based on coreutils-8.32
This commit is contained in:
parent
90bc173c8c
commit
94bccd8e8d
17
Makefile
17
Makefile
@ -1,17 +0,0 @@
|
||||
CROSS_COMPILE ?=
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
STRIP := $(CROSS_COMPILE)strip
|
||||
CFLAGS += -g -O2 -Wall
|
||||
LIBS = -static
|
||||
OBJ := rm_
|
||||
|
||||
all: rm_.o
|
||||
$(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS)
|
||||
$(STRIP) $(OBJ)
|
||||
-chmod a+x $(OBJ)
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $< $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf *.o
|
||||
rm $(OBJ)
|
19
README.md
19
README.md
@ -1,19 +1,16 @@
|
||||
# rm_
|
||||
Linux代替rm命令防止错误删除文件
|
||||
基于coreutils-8.32 mv源代码
|
||||
|
||||
# Build
|
||||
git clone https://github.com/niuyuling/rm_.git
|
||||
cd rm_
|
||||
make clean; make
|
||||
bash build_mv.sh
|
||||
|
||||
# Help Information
|
||||
root@niuyuling:/mnt/c/Users/niuyuling/Desktop/rm# touch a.txt b.txt ../c.txt
|
||||
root@niuyuling:/mnt/c/Users/niuyuling/Desktop/rm# ./rm_ a.txt b.txt ../c.txt
|
||||
/delete/ 垃圾桶目录存在
|
||||
移动到 /delete/a.txt
|
||||
移动到 /delete/b.txt
|
||||
移动到 /delete/c.txt
|
||||
a.txt 删除成功
|
||||
b.txt 删除成功
|
||||
../c.txt 删除成功
|
||||
root@niuyuling:/mnt/c/Users/niuyuling/Desktop/rm#
|
||||
root@niuyuling:/mnt/c/Users/niuyuling/Desktop/Arm-tool# rm_ busybox-1.31.1
|
||||
rm_
|
||||
busybox-1.31.1
|
||||
/tmp/
|
||||
3
|
||||
root@niuyuling:/mnt/c/Users/niuyuling/Desktop/Arm-tool#
|
48
build_mv.sh
Normal file
48
build_mv.sh
Normal file
@ -0,0 +1,48 @@
|
||||
#!/bin/bsh
|
||||
#
|
||||
# Build MV command
|
||||
# based on coreutils-8.32
|
||||
#
|
||||
|
||||
function init() {
|
||||
SHELL_FOLDER=$(dirname $(readlink -f "$0"));
|
||||
set -x
|
||||
}
|
||||
|
||||
function LIB() {
|
||||
S=(copy-acl.c set-acl.c acl-errno-valid.c acl-internal.c get-permissions.c set-permissions.c allocator.c areadlink.c areadlink-with-size.c areadlinkat.c argmatch.c argv-iter.c openat-proc.c backupfile.c backup-rename.c backupfile.c backup-find.c base32.c base64.c binary-io.c bitrotate.c buffer-lcm.c c-ctype.c c-strcasecmp.c c-strncasecmp.c c-strtod.c c-strtold.c canon-host.c canonicalize.c careadlinkat.c chmodat.c chownat.c cl-strtod.c cl-strtold.c cloexec.c close-stream.c closein.c closeout.c count-leading-zeros.c af_alg.c md5.c sha1.c sha256.c sha512.c cycle-check.c di-set.c diacrit.c opendir-safer.c dirname.c basename.c dirname-lgpl.c basename-lgpl.c stripslash.c dtoastr.c dtotimespec.c exclude.c exitfail.c fadvise.c creat-safer.c open-safer.c fd-hook.c fd-reopen.c fd-safer-flag.c dup-safer-flag.c fdutimensat.c file-has-acl.c file-set.c file-type.c filemode.c filenamecat.c filenamecat-lgpl.c filevercmp.c fopen-safer.c fprintftime.c freading.c freadseek.c freopen-safer.c ftoastr.c full-read.c full-write.c gethrxtime.c xtime.c getndelim2.c getprogname.c gettime.c getugroups.c hard-locale.c hash.c hash-pjw.c hash-triple.c heap.c human.c i-ring.c idcache.c ino-map.c imaxtostr.c inttostr.c offtostr.c uinttostr.c umaxtostr.c ldtoastr.c linebuffer.c localcharset.c glthread/lock.c long-options.c malloca.c math.c mbchar.c mbiter.c mbsalign.c mbscasecmp.c mbschr.c mbslen.c mbsstr.c mbswidth.c mbuiter.c memcasecmp.c memchr2.c memcmp2.c memcoll.c mgetgroups.c mkancesdirs.c dirchownmod.c mkdir-p.c modechange.c mpsort.c nproc.c nstrftime.c openat-die.c openat-safer.c opendirat.c parse-datetime.c physmem.c pipe2.c posixtm.c posixver.c printf-frexp.c printf-frexpl.c priv-set.c progname.c propername.c qcopy-acl.c qset-acl.c quotearg.c randint.c randperm.c randread.c rand-isaac.c read-file.c readtokens.c readtokens0.c renameatu.c root-dev-ino.c safe-read.c safe-write.c same.c save-cwd.c savedir.c savewd.c selinux-at.c se-context.c se-selinux.c setlocale_null.c settime.c sig-handler.c sockets.c stat-time.c statat.c mkstemp-safer.c striconv.c strnlen1.c strintcmp.c strnumcmp.c sys_socket.c tempname.c glthread/threadlib.c timespec.c glthread/tls.c trim.c u64.c unicodeio.c unistd.c dup-safer.c fd-safer.c pipe-safer.c unistr/u8-mbtoucr.c unistr/u8-uctomb.c unistr/u8-uctomb-aux.c uniwidth/width.c unlinkdir.c userspec.c utimecmp.c utimens.c verror.c version-etc.c version-etc-fsf.c wctype-h.c write-any-file.c xmalloc.c xalloc-die.c xbinary-io.c xdectoimax.c xdectoumax.c xfts.c xgetcwd.c xgetgroups.c xgethostname.c xmemcoll.c xnanosleep.c xprintf.c xreadlink.c xsize.c xstriconv.c xstrndup.c xstrtod.c xstrtoimax.c xstrtol.c xstrtoul.c xstrtol-error.c xstrtold.c xstrtoumax.c xvasprintf.c xasprintf.c yesno.c asnprintf.c chdir-long.c fchmodat.c fclose.c fcntl.c fflush.c fpurge.c freadahead.c freadptr.c fseek.c fseeko.c fseterr.c fsusage.c fts.c getfilecon.c isapipe.c lchmod.c localtime-buffer.c mbrlen.c mbrtowc.c mknod.c mkstemp.c mktime.c mountlist.c nanosleep.c obstack.c printf-args.c printf-parse.c readutmp.c regex.c sig2str.c time_rz.c vasnprintf.c)
|
||||
O="copy-acl.o set-acl.o acl-errno-valid.o acl-internal.o get-permissions.o set-permissions.o allocator.o areadlink.o areadlink-with-size.o areadlinkat.o argmatch.o argv-iter.o openat-proc.o backupfile.o backup-rename.o backupfile.o backup-find.o base32.o base64.o binary-io.o bitrotate.o buffer-lcm.o c-ctype.o c-strcasecmp.o c-strncasecmp.o c-strtod.o c-strtold.o canon-host.o canonicalize.o careadlinkat.o chmodat.o chownat.o cl-strtod.o cl-strtold.o cloexec.o close-stream.o closein.o closeout.o count-leading-zeros.o af_alg.o md5.o sha1.o sha256.o sha512.o cycle-check.o di-set.o diacrit.o opendir-safer.o dirname.o basename.o dirname-lgpl.o basename-lgpl.o stripslash.o dtoastr.o dtotimespec.o exclude.o exitfail.o fadvise.o creat-safer.o open-safer.o fd-hook.o fd-reopen.o fd-safer-flag.o dup-safer-flag.o fdutimensat.o file-has-acl.o file-set.o file-type.o filemode.o filenamecat.o filenamecat-lgpl.o filevercmp.o fopen-safer.o fprintftime.o freading.o freadseek.o freopen-safer.o ftoastr.o full-read.o full-write.o gethrxtime.o xtime.o getndelim2.o getprogname.o gettime.o getugroups.o hard-locale.o hash.o hash-pjw.o hash-triple.o heap.o human.o i-ring.o idcache.o ino-map.o imaxtostr.o inttostr.o offtostr.o uinttostr.o umaxtostr.o ldtoastr.o linebuffer.o localcharset.o glthread/lock.o long-options.o malloca.o math.o mbchar.o mbiter.o mbsalign.o mbscasecmp.o mbschr.o mbslen.o mbsstr.o mbswidth.o mbuiter.o memcasecmp.o memchr2.o memcmp2.o memcoll.o mgetgroups.o mkancesdirs.o dirchownmod.o mkdir-p.o modechange.o mpsort.o nproc.o nstrftime.o openat-die.o openat-safer.o opendirat.o parse-datetime.o physmem.o pipe2.o posixtm.o posixver.o printf-frexp.o printf-frexpl.o priv-set.o progname.o propername.o qcopy-acl.o qset-acl.o quotearg.o randint.o randperm.o randread.o rand-isaac.o read-file.o readtokens.o readtokens0.o renameatu.o root-dev-ino.o safe-read.o safe-write.o same.o save-cwd.o savedir.o savewd.o selinux-at.o se-context.o se-selinux.o setlocale_null.o settime.o sig-handler.o sockets.o stat-time.o statat.o mkstemp-safer.o striconv.o strnlen1.o strintcmp.o strnumcmp.o sys_socket.o tempname.o glthread/threadlib.o timespec.o glthread/tls.o trim.o u64.o unicodeio.o unistd.o dup-safer.o fd-safer.o pipe-safer.o unistr/u8-mbtoucr.o unistr/u8-uctomb.o unistr/u8-uctomb-aux.o uniwidth/width.o unlinkdir.o userspec.o utimecmp.o utimens.o verror.o version-etc.o version-etc-fsf.o wctype-h.o write-any-file.o xmalloc.o xalloc-die.o xbinary-io.o xdectoimax.o xdectoumax.o xfts.o xgetcwd.o xgetgroups.o xgethostname.o xmemcoll.o xnanosleep.o xprintf.o xreadlink.o xsize.o xstriconv.o xstrndup.o xstrtod.o xstrtoimax.o xstrtol.o xstrtoul.o xstrtol-error.o xstrtold.o xstrtoumax.o xvasprintf.o xasprintf.o yesno.o asnprintf.o chdir-long.o fchmodat.o fclose.o fcntl.o fflush.o fpurge.o freadahead.o freadptr.o fseek.o fseeko.o fseterr.o fsusage.o fts.o getfilecon.o isapipe.o lchmod.o localtime-buffer.o mbrlen.o mbrtowc.o mknod.o mkstemp.o mktime.o mountlist.o nanosleep.o obstack.o printf-args.o printf-parse.o readutmp.o regex.o sig2str.o time_rz.o vasnprintf.o"
|
||||
|
||||
for i in ${S[@]}; do
|
||||
echo $i
|
||||
o=$(echo $i | sed 's/\.c/\.o/');
|
||||
echo $o;
|
||||
gcc -I. -I./lib -Ilib -I./lib -g -O2 -c -o lib/${o} lib/${i}
|
||||
done
|
||||
|
||||
cd ${SHELL_FOLDER}/lib;
|
||||
ar cr libcoreutils.a ${O};
|
||||
}
|
||||
|
||||
function MV() {
|
||||
cd ${SHELL_FOLDER};
|
||||
gcc -I./lib -c -o version.o version.c
|
||||
ar cr libver.a version.o
|
||||
gcc -I. -I./lib -g -O2 -c -o remove.o remove.c
|
||||
gcc -I. -I./lib -g -O2 -c -o copy.o copy.c
|
||||
gcc -I. -I./lib -g -O2 -c -o cp-hash.o cp-hash.c
|
||||
gcc -I. -I./lib -g -O2 -c -o extent-scan.o extent-scan.c
|
||||
gcc -I. -I./lib -g -O2 -c -o force-link.o force-link.c
|
||||
gcc -I. -I./lib -g -O2 -c -o selinux.o selinux.c
|
||||
gcc -I. -I./lib -g -O2 -c -o mv.o mv.c
|
||||
|
||||
gcc -g -O2 -Wl,--as-needed -o mv \
|
||||
mv.o remove.o copy.o cp-hash.o extent-scan.o force-link.o selinux.o libver.a lib/libcoreutils.a lib/libcoreutils.a \
|
||||
-lselinux -lacl -lattr -pthread -lpcre2-8 -ldl -static
|
||||
|
||||
mv ./mv rm_
|
||||
}
|
||||
|
||||
init
|
||||
#LIB
|
||||
MV
|
63
build_mv.txt
Normal file
63
build_mv.txt
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
|
||||
# MV
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/mv.o -MD -MP -MF src/.deps/mv.Tpo -c -o src/mv.o src/mv.c
|
||||
gcc -g -O2 -Wl,--as-needed -o src/mv \
|
||||
src/mv.o src/remove.o src/copy.o src/cp-hash.o src/extent-scan.o src/force-link.o src/selinux.o src/libver.a lib/libcoreutils.a lib/libcoreutils.a \
|
||||
-lselinux -lacl -lattr -pthread -lpcre2-8 -ldl -static
|
||||
|
||||
|
||||
# remove.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/remove.o -MD -MP -MF src/.deps/remove.Tpo -c -o src/remove.o src/remove.c
|
||||
|
||||
# copy.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/copy.o -MD -MP -MF src/.deps/copy.Tpo -c -o src/copy.o src/copy.c
|
||||
|
||||
# cp-hash.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/cp-hash.o -MD -MP -MF src/.deps/cp-hash.Tpo -c -o src/cp-hash.o src/cp-hash.c
|
||||
|
||||
# extent-scan.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/extent-scan.o -MD -MP -MF src/.deps/extent-scan.Tpo -c -o src/extent-scan.o src/extent-scan.c
|
||||
|
||||
# force-link.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/force-link.o -MD -MP -MF src/.deps/force-link.Tpo -c -o src/force-link.o src/force-link.c
|
||||
|
||||
# selinux.o
|
||||
gcc -I. -I./lib -Ilib -I./lib -Isrc -I./src -g -O2 -MT src/selinux.o -MD -MP -MF src/.deps/selinux.Tpo -c -o src/selinux.o src/selinux.c
|
||||
|
||||
# libver.a
|
||||
ar cr src/libver.a src/version.o
|
||||
|
||||
# libcoreutils.a
|
||||
ar cr lib/libcoreutils.a lib/copy-acl.o lib/set-acl.o lib/acl-errno-valid.o lib/acl-internal.o lib/get-permissions.o lib/set-permissions.o \
|
||||
lib/allocator.o lib/areadlink.o lib/areadlink-with-size.o lib/areadlinkat.o lib/argmatch.o lib/argv-iter.o lib/openat-proc.o lib/backupfile.o \
|
||||
lib/backup-rename.o lib/backupfile.o lib/backup-find.o lib/base32.o lib/base64.o lib/binary-io.o lib/bitrotate.o lib/buffer-lcm.o lib/c-ctype.o \
|
||||
lib/c-strcasecmp.o lib/c-strncasecmp.o lib/c-strtod.o lib/c-strtold.o lib/canon-host.o lib/canonicalize.o lib/careadlinkat.o lib/chmodat.o lib/chownat.o \
|
||||
lib/cl-strtod.o lib/cl-strtold.o lib/cloexec.o lib/close-stream.o lib/closein.o lib/closeout.o lib/count-leading-zeros.o lib/af_alg.o lib/md5.o lib/sha1.o \
|
||||
lib/sha256.o lib/sha512.o lib/cycle-check.o lib/di-set.o lib/diacrit.o lib/opendir-safer.o lib/dirname.o lib/basename.o lib/dirname-lgpl.o lib/basename-lgpl.o \
|
||||
lib/stripslash.o lib/dtoastr.o lib/dtotimespec.o lib/exclude.o lib/exitfail.o lib/fadvise.o lib/creat-safer.o lib/open-safer.o lib/fd-hook.o lib/fd-reopen.o \
|
||||
lib/fd-safer-flag.o lib/dup-safer-flag.o lib/fdutimensat.o lib/file-has-acl.o lib/file-set.o lib/file-type.o lib/filemode.o lib/filenamecat.o lib/filenamecat-lgpl.o \
|
||||
lib/filevercmp.o lib/fopen-safer.o lib/fprintftime.o lib/freading.o lib/freadseek.o lib/freopen-safer.o lib/ftoastr.o lib/full-read.o lib/full-write.o lib/gethrxtime.o \
|
||||
lib/xtime.o lib/getndelim2.o lib/getprogname.o lib/gettime.o lib/getugroups.o lib/hard-locale.o lib/hash.o lib/hash-pjw.o lib/hash-triple.o lib/heap.o lib/human.o lib/i-ring.o \
|
||||
lib/idcache.o lib/ino-map.o lib/imaxtostr.o lib/inttostr.o lib/offtostr.o lib/uinttostr.o lib/umaxtostr.o lib/ldtoastr.o lib/linebuffer.o lib/localcharset.o \
|
||||
lib/glthread/lock.o lib/long-options.o lib/malloca.o lib/math.o lib/mbchar.o lib/mbiter.o lib/mbsalign.o lib/mbscasecmp.o lib/mbschr.o lib/mbslen.o lib/mbsstr.o \
|
||||
lib/mbswidth.o lib/mbuiter.o lib/memcasecmp.o lib/memchr2.o lib/memcmp2.o lib/memcoll.o lib/mgetgroups.o lib/mkancesdirs.o lib/dirchownmod.o lib/mkdir-p.o lib/modechange.o \
|
||||
lib/mpsort.o lib/nproc.o lib/nstrftime.o lib/openat-die.o lib/openat-safer.o lib/opendirat.o lib/parse-datetime.o lib/physmem.o lib/pipe2.o lib/posixtm.o lib/posixver.o \
|
||||
lib/printf-frexp.o lib/printf-frexpl.o lib/priv-set.o lib/progname.o lib/propername.o lib/qcopy-acl.o lib/qset-acl.o lib/quotearg.o lib/randint.o lib/randperm.o \
|
||||
lib/randread.o lib/rand-isaac.o lib/read-file.o lib/readtokens.o lib/readtokens0.o lib/renameatu.o lib/root-dev-ino.o lib/safe-read.o lib/safe-write.o lib/same.o \
|
||||
lib/save-cwd.o lib/savedir.o lib/savewd.o lib/selinux-at.o lib/se-context.o lib/se-selinux.o lib/setlocale_null.o lib/settime.o lib/sig-handler.o lib/sockets.o \
|
||||
lib/stat-time.o lib/statat.o lib/mkstemp-safer.o lib/striconv.o lib/strnlen1.o lib/strintcmp.o lib/strnumcmp.o lib/sys_socket.o lib/tempname.o lib/glthread/threadlib.o \
|
||||
lib/timespec.o lib/glthread/tls.o lib/trim.o lib/u64.o lib/unicodeio.o lib/unistd.o lib/dup-safer.o lib/fd-safer.o lib/pipe-safer.o lib/unistr/u8-mbtoucr.o \
|
||||
lib/unistr/u8-uctomb.o lib/unistr/u8-uctomb-aux.o lib/uniwidth/width.o lib/unlinkdir.o lib/userspec.o lib/utimecmp.o lib/utimens.o lib/verror.o lib/version-etc.o \
|
||||
lib/version-etc-fsf.o lib/wctype-h.o lib/write-any-file.o lib/xmalloc.o lib/xalloc-die.o lib/xbinary-io.o lib/xdectoimax.o lib/xdectoumax.o lib/xfts.o lib/xgetcwd.o \
|
||||
lib/xgetgroups.o lib/xgethostname.o lib/xmemcoll.o lib/xnanosleep.o lib/xprintf.o lib/xreadlink.o lib/xsize.o lib/xstriconv.o lib/xstrndup.o lib/xstrtod.o \
|
||||
lib/xstrtoimax.o lib/xstrtol.o lib/xstrtoul.o lib/xstrtol-error.o lib/xstrtold.o lib/xstrtoumax.o lib/xvasprintf.o lib/xasprintf.o lib/yesno.o \
|
||||
lib/asnprintf.o lib/chdir-long.o lib/fchmodat.o lib/fclose.o lib/fcntl.o lib/fflush.o lib/fpurge.o lib/freadahead.o lib/freadptr.o lib/fseek.o \
|
||||
lib/fseeko.o lib/fseterr.o lib/fsusage.o lib/fts.o lib/getfilecon.o lib/isapipe.o lib/lchmod.o lib/localtime-buffer.o lib/mbrlen.o lib/mbrtowc.o \
|
||||
lib/mknod.o lib/mkstemp.o lib/mktime.o lib/mountlist.o lib/nanosleep.o lib/obstack.o lib/printf-args.o lib/printf-parse.o lib/readutmp.o lib/regex.o \
|
||||
lib/sig2str.o lib/time_rz.o lib/vasnprintf.o
|
||||
|
||||
|
||||
|
||||
|
||||
|
307
copy.h
Normal file
307
copy.h
Normal file
@ -0,0 +1,307 @@
|
||||
/* core functions for copying files and directories
|
||||
Copyright (C) 1989-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Extracted from cp.c and librarified by Jim Meyering. */
|
||||
|
||||
#ifndef COPY_H
|
||||
# define COPY_H
|
||||
|
||||
# include <stdbool.h>
|
||||
# include "hash.h"
|
||||
|
||||
/* Control creation of sparse files (files with holes). */
|
||||
enum Sparse_type
|
||||
{
|
||||
SPARSE_UNUSED,
|
||||
|
||||
/* Never create holes in DEST. */
|
||||
SPARSE_NEVER,
|
||||
|
||||
/* This is the default. Use a crude (and sometimes inaccurate)
|
||||
heuristic to determine if SOURCE has holes. If so, try to create
|
||||
holes in DEST. */
|
||||
SPARSE_AUTO,
|
||||
|
||||
/* For every sufficiently long sequence of bytes in SOURCE, try to
|
||||
create a corresponding hole in DEST. There is a performance penalty
|
||||
here because CP has to search for holes in SRC. But if the holes are
|
||||
big enough, that penalty can be offset by the decrease in the amount
|
||||
of data written to disk. */
|
||||
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
|
||||
{
|
||||
I_ALWAYS_YES = 1,
|
||||
I_ALWAYS_NO,
|
||||
I_ASK_USER,
|
||||
I_UNSPECIFIED
|
||||
};
|
||||
|
||||
/* How to handle symbolic links. */
|
||||
enum Dereference_symlink
|
||||
{
|
||||
DEREF_UNDEFINED = 1,
|
||||
|
||||
/* Copy the symbolic link itself. -P */
|
||||
DEREF_NEVER,
|
||||
|
||||
/* If the symbolic is a command line argument, then copy
|
||||
its referent. Otherwise, copy the symbolic link itself. -H */
|
||||
DEREF_COMMAND_LINE_ARGUMENTS,
|
||||
|
||||
/* Copy the referent of the symbolic link. -L */
|
||||
DEREF_ALWAYS
|
||||
};
|
||||
|
||||
# define VALID_SPARSE_MODE(Mode) \
|
||||
((Mode) == SPARSE_NEVER \
|
||||
|| (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
|
||||
mv.c, cp.c, and install.c. */
|
||||
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;
|
||||
|
||||
/* 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
|
||||
any destination is sure to be removed before the open. */
|
||||
bool unlink_dest_after_failed_open;
|
||||
|
||||
/* If true, create hard links instead of copying files.
|
||||
Create destination directories as usual. */
|
||||
bool hard_link;
|
||||
|
||||
/* If true, rather than copying, first attempt to use rename.
|
||||
If that fails, then resort to copying. */
|
||||
bool move_mode;
|
||||
|
||||
/* If true, install(1) is the caller. */
|
||||
bool install_mode;
|
||||
|
||||
/* Whether this process has appropriate privileges to chown a file
|
||||
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;
|
||||
|
||||
/* If true, attempt to give the copies the original files' permissions,
|
||||
owner, group, and timestamps. */
|
||||
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
|
||||
logical hard links between the source files. If used with cp's
|
||||
--no-dereference option, and copying two hard-linked files,
|
||||
the two corresponding destination files will also be hard linked.
|
||||
|
||||
If used with cp's --dereference (-L) option, then, as that option implies,
|
||||
hard links are *not* preserved. However, when copying a file F and
|
||||
a symlink S to F, the resulting S and F in the destination directory
|
||||
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. 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;
|
||||
|
||||
/* If true, set file mode to value of MODE. Otherwise,
|
||||
set it based on current umask modified by UMASK_KILL. */
|
||||
bool set_mode;
|
||||
|
||||
/* If true, create symbolic links instead of copying files.
|
||||
Create destination directories as usual. */
|
||||
bool symbolic_link;
|
||||
|
||||
/* If true, do not copy a nondirectory that has an existing destination
|
||||
with the same or newer modification time. */
|
||||
bool update;
|
||||
|
||||
/* If true, display the names of the files before copying them. */
|
||||
bool verbose;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* If true, this is the last filed to be copied. mv uses this to
|
||||
avoid some unnecessary work. */
|
||||
bool last_file;
|
||||
|
||||
/* Zero if the source has already been renamed to the destination; a
|
||||
positive errno number if this failed with the given errno; -1 if
|
||||
no attempt has been made to rename. Always -1, except for mv. */
|
||||
int rename_errno;
|
||||
|
||||
/* 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).
|
||||
When renaming, it protects all non-directories.
|
||||
Use dest_info_init to initialize it, or set it to NULL to disable
|
||||
this feature. */
|
||||
Hash_table *dest_info;
|
||||
|
||||
/* FIXME */
|
||||
Hash_table *src_info;
|
||||
};
|
||||
|
||||
/* Arrange to make rename calls go through the wrapper function
|
||||
on systems with a rename function that fails for a source file name
|
||||
specified with a trailing slash. */
|
||||
# if RENAME_TRAILING_SLASH_BUG
|
||||
int rpl_rename (const char *, const char *);
|
||||
# undef rename
|
||||
# define rename rpl_rename
|
||||
# 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);
|
||||
|
||||
extern bool set_process_security_ctx (char const *src_name,
|
||||
char const *dst_name,
|
||||
mode_t mode, bool new_dst,
|
||||
const struct cp_options *x);
|
||||
|
||||
extern bool set_file_security_ctx (char const *dst_name, bool process_local,
|
||||
bool recurse, const struct cp_options *x);
|
||||
|
||||
void dest_info_init (struct cp_options *);
|
||||
void src_info_init (struct cp_options *);
|
||||
|
||||
void cp_options_default (struct cp_options *);
|
||||
bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE;
|
||||
mode_t cached_umask (void);
|
||||
|
||||
#endif
|
103260
coreutils.txt
Normal file
103260
coreutils.txt
Normal file
File diff suppressed because it is too large
Load Diff
164
cp-hash.c
Normal file
164
cp-hash.c
Normal file
@ -0,0 +1,164 @@
|
||||
/* cp-hash.c -- file copying (hash search routines)
|
||||
Copyright (C) 1989-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Torbjorn Granlund, Sweden (tege@sics.se).
|
||||
Rewritten to use lib/hash.c by Jim Meyering. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "system.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "cp-hash.h"
|
||||
|
||||
/* Use ST_DEV and ST_INO as the key, FILENAME as the value.
|
||||
These are used e.g., in copy.c to associate the destination name with
|
||||
the source device/inode pair so that if we encounter a matching dev/ino
|
||||
pair in the source tree we can arrange to create a hard link between
|
||||
the corresponding names in the destination tree. */
|
||||
struct Src_to_dest
|
||||
{
|
||||
ino_t st_ino;
|
||||
dev_t st_dev;
|
||||
/* Destination file name (of non-directory or pre-existing directory)
|
||||
corresponding to the dev/ino of a copied file, or the destination file
|
||||
name corresponding to a dev/ino pair for a newly-created directory. */
|
||||
char *name;
|
||||
};
|
||||
|
||||
/* This table maps source dev/ino to destination file name.
|
||||
We use it to preserve hard links when copying. */
|
||||
static Hash_table *src_to_dest;
|
||||
|
||||
/* Initial size of the above hash table. */
|
||||
#define INITIAL_TABLE_SIZE 103
|
||||
|
||||
static size_t
|
||||
src_to_dest_hash (void const *x, size_t table_size)
|
||||
{
|
||||
struct Src_to_dest 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 Src_to_dest entries.
|
||||
Return true if their keys are judged 'equal'. */
|
||||
static bool
|
||||
src_to_dest_compare (void const *x, void const *y)
|
||||
{
|
||||
struct Src_to_dest const *a = x;
|
||||
struct Src_to_dest const *b = y;
|
||||
return SAME_INODE (*a, *b) ? true : false;
|
||||
}
|
||||
|
||||
static void
|
||||
src_to_dest_free (void *x)
|
||||
{
|
||||
struct Src_to_dest *a = x;
|
||||
free (a->name);
|
||||
free (x);
|
||||
}
|
||||
|
||||
/* Remove the entry matching INO/DEV from the table
|
||||
that maps source ino/dev to destination file name. */
|
||||
extern void
|
||||
forget_created (ino_t ino, dev_t dev)
|
||||
{
|
||||
struct Src_to_dest probe;
|
||||
struct Src_to_dest *ent;
|
||||
|
||||
probe.st_ino = ino;
|
||||
probe.st_dev = dev;
|
||||
probe.name = NULL;
|
||||
|
||||
ent = hash_delete (src_to_dest, &probe);
|
||||
if (ent)
|
||||
src_to_dest_free (ent);
|
||||
}
|
||||
|
||||
/* If INO/DEV correspond to an already-copied source file, return the
|
||||
name of the corresponding destination file. Otherwise, return NULL. */
|
||||
|
||||
extern char *
|
||||
src_to_dest_lookup (ino_t ino, dev_t dev)
|
||||
{
|
||||
struct Src_to_dest ent;
|
||||
struct Src_to_dest const *e;
|
||||
ent.st_ino = ino;
|
||||
ent.st_dev = dev;
|
||||
e = hash_lookup (src_to_dest, &ent);
|
||||
return e ? e->name : NULL;
|
||||
}
|
||||
|
||||
/* Add file NAME, copied from inode number INO and device number DEV,
|
||||
to the list of files we have copied.
|
||||
Return NULL if inserted, otherwise non-NULL. */
|
||||
|
||||
extern char *
|
||||
remember_copied (const char *name, ino_t ino, dev_t dev)
|
||||
{
|
||||
struct Src_to_dest *ent;
|
||||
struct Src_to_dest *ent_from_table;
|
||||
|
||||
ent = xmalloc (sizeof *ent);
|
||||
ent->name = xstrdup (name);
|
||||
ent->st_ino = ino;
|
||||
ent->st_dev = dev;
|
||||
|
||||
ent_from_table = hash_insert (src_to_dest, ent);
|
||||
if (ent_from_table == NULL)
|
||||
{
|
||||
/* Insertion failed due to lack of memory. */
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
if (ent_from_table != ent)
|
||||
{
|
||||
src_to_dest_free (ent);
|
||||
return (char *) ent_from_table->name;
|
||||
}
|
||||
|
||||
/* New key; insertion succeeded. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize the hash table. */
|
||||
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);
|
||||
if (src_to_dest == NULL)
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
/* Reset the hash structure in the global variable 'htab' to
|
||||
contain no entries. */
|
||||
|
||||
extern void
|
||||
forget_all (void)
|
||||
{
|
||||
hash_free (src_to_dest);
|
||||
}
|
5
cp-hash.h
Normal file
5
cp-hash.h
Normal file
@ -0,0 +1,5 @@
|
||||
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);
|
||||
char *src_to_dest_lookup (ino_t ino, dev_t dev);
|
31
die.h
Normal file
31
die.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* Report an error and exit.
|
||||
Copyright 2016-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, 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. */
|
||||
|
||||
#ifndef DIE_H
|
||||
# define DIE_H
|
||||
|
||||
# include <error.h>
|
||||
# include <stdbool.h>
|
||||
# include <verify.h>
|
||||
|
||||
/* Like 'error (STATUS, ...)', except STATUS must be a nonzero constant.
|
||||
This may pacify the compiler or help it generate better code. */
|
||||
# define die(status, ...) \
|
||||
verify_expr (status, (error (status, __VA_ARGS__), assume (false)))
|
||||
|
||||
#endif /* DIE_H */
|
228
extent-scan.c
Normal file
228
extent-scan.c
Normal file
@ -0,0 +1,228 @@
|
||||
/* extent-scan.c -- core functions for scanning extents
|
||||
Copyright (C) 2010-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by 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 { headersize = offsetof (struct fiemap, fm_extents) };
|
||||
enum { count = (sizeof fiemap_buf - headersize) / 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
|
73
extent-scan.h
Normal file
73
extent-scan.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* core functions for efficient reading sparse files
|
||||
Copyright (C) 2010-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by 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 */
|
102
fiemap.h
Normal file
102
fiemap.h
Normal file
@ -0,0 +1,102 @@
|
||||
/* 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). */
|
||||
struct fiemap_extent fm_extents[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
|
||||
/* 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
|
184
force-link.c
Normal file
184
force-link.c
Normal file
@ -0,0 +1,184 @@
|
||||
/* Implement ln -f "atomically"
|
||||
|
||||
Copyright 2017-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
/* A naive "ln -f A B" unlinks B and then links A to B. This module
|
||||
instead links A to a randomly-named temporary T in B's directory,
|
||||
and then renames T to B. This approach has a window with a
|
||||
randomly-named temporary, which is safer for many applications than
|
||||
a window where B does not exist. */
|
||||
|
||||
#include <config.h>
|
||||
#include "system.h"
|
||||
|
||||
#include "force-link.h"
|
||||
|
||||
#include <tempname.h>
|
||||
|
||||
/* A basename pattern suitable for a temporary file. It should work
|
||||
even on file systems like FAT that support only short names.
|
||||
"Cu" is short for "Coreutils" or for "Changeable unstable",
|
||||
take your pick.... */
|
||||
|
||||
static char const simple_pattern[] = "CuXXXXXX";
|
||||
enum { x_suffix_len = sizeof "XXXXXX" - 1 };
|
||||
|
||||
/* A size for smallish buffers containing file names. Longer file
|
||||
names can use malloc. */
|
||||
|
||||
enum { smallsize = 256 };
|
||||
|
||||
/* Return a template for a file in the same directory as DSTNAME.
|
||||
Use BUF if the template fits, otherwise use malloc and return NULL
|
||||
(setting errno) if unsuccessful. */
|
||||
|
||||
static char *
|
||||
samedir_template (char const *dstname, char buf[smallsize])
|
||||
{
|
||||
ptrdiff_t dstdirlen = last_component (dstname) - dstname;
|
||||
size_t dsttmpsize = dstdirlen + sizeof simple_pattern;
|
||||
char *dsttmp;
|
||||
if (dsttmpsize <= smallsize)
|
||||
dsttmp = buf;
|
||||
else
|
||||
{
|
||||
dsttmp = malloc (dsttmpsize);
|
||||
if (!dsttmp)
|
||||
return dsttmp;
|
||||
}
|
||||
strcpy (mempcpy (dsttmp, dstname, dstdirlen), simple_pattern);
|
||||
return dsttmp;
|
||||
}
|
||||
|
||||
|
||||
/* Auxiliaries for force_linkat. */
|
||||
|
||||
struct link_arg
|
||||
{
|
||||
int srcdir;
|
||||
char const *srcname;
|
||||
int dstdir;
|
||||
int flags;
|
||||
};
|
||||
|
||||
static int
|
||||
try_link (char *dest, void *arg)
|
||||
{
|
||||
struct link_arg *a = arg;
|
||||
return linkat (a->srcdir, a->srcname, a->dstdir, dest, a->flags);
|
||||
}
|
||||
|
||||
/* Hard-link directory SRCDIR's file SRCNAME to directory DSTDIR's
|
||||
file DSTNAME, using linkat-style FLAGS to control the linking.
|
||||
If FORCE and DSTNAME already exists, replace it atomically.
|
||||
If LINKAT_ERRNO is 0, the hard link is already done; if positive,
|
||||
the hard link was tried and failed with errno == LINKAT_ERRNO. Return
|
||||
-1 if successful and DSTNAME already existed,
|
||||
0 if successful and DSTNAME did not already exist, and
|
||||
a positive errno value on failure. */
|
||||
extern int
|
||||
force_linkat (int srcdir, char const *srcname,
|
||||
int dstdir, char const *dstname, int flags, bool force,
|
||||
int linkat_errno)
|
||||
{
|
||||
if (linkat_errno < 0)
|
||||
linkat_errno = (linkat (srcdir, srcname, dstdir, dstname, flags) == 0
|
||||
? 0 : errno);
|
||||
if (!force || linkat_errno != EEXIST)
|
||||
return linkat_errno;
|
||||
|
||||
char buf[smallsize];
|
||||
char *dsttmp = samedir_template (dstname, buf);
|
||||
if (! dsttmp)
|
||||
return errno;
|
||||
struct link_arg arg = { srcdir, srcname, dstdir, flags };
|
||||
int err;
|
||||
|
||||
if (try_tempname_len (dsttmp, 0, &arg, try_link, x_suffix_len) != 0)
|
||||
err = errno;
|
||||
else
|
||||
{
|
||||
err = renameat (dstdir, dsttmp, dstdir, dstname) == 0 ? -1 : errno;
|
||||
/* Unlink DSTTMP even if renameat succeeded, in case DSTTMP
|
||||
and DSTNAME were already the same hard link and renameat
|
||||
was a no-op. */
|
||||
unlinkat (dstdir, dsttmp, 0);
|
||||
}
|
||||
|
||||
if (dsttmp != buf)
|
||||
free (dsttmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Auxiliaries for force_symlinkat. */
|
||||
|
||||
struct symlink_arg
|
||||
{
|
||||
char const *srcname;
|
||||
int dstdir;
|
||||
};
|
||||
|
||||
static int
|
||||
try_symlink (char *dest, void *arg)
|
||||
{
|
||||
struct symlink_arg *a = arg;
|
||||
return symlinkat (a->srcname, a->dstdir, dest);
|
||||
}
|
||||
|
||||
/* Create a symlink containing SRCNAME in directory DSTDIR's file DSTNAME.
|
||||
If FORCE and DSTNAME already exists, replace it atomically.
|
||||
If SYMLINKAT_ERRNO is 0, the symlink is already done; if positive,
|
||||
the symlink was tried and failed with errno == SYMLINKAT_ERRNO. Return
|
||||
-1 if successful and DSTNAME already existed,
|
||||
0 if successful and DSTNAME did not already exist, and
|
||||
a positive errno value on failure. */
|
||||
extern int
|
||||
force_symlinkat (char const *srcname, int dstdir, char const *dstname,
|
||||
bool force, int symlinkat_errno)
|
||||
{
|
||||
if (symlinkat_errno < 0)
|
||||
symlinkat_errno = symlinkat (srcname, dstdir, dstname) == 0 ? 0 : errno;
|
||||
if (!force || symlinkat_errno != EEXIST)
|
||||
return symlinkat_errno;
|
||||
|
||||
char buf[smallsize];
|
||||
char *dsttmp = samedir_template (dstname, buf);
|
||||
if (!dsttmp)
|
||||
return errno;
|
||||
struct symlink_arg arg = { srcname, dstdir };
|
||||
int err;
|
||||
|
||||
if (try_tempname_len (dsttmp, 0, &arg, try_symlink, x_suffix_len) != 0)
|
||||
err = errno;
|
||||
else if (renameat (dstdir, dsttmp, dstdir, dstname) != 0)
|
||||
{
|
||||
err = errno;
|
||||
unlinkat (dstdir, dsttmp, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't worry about renameat being a no-op, since DSTTMP is
|
||||
newly created. */
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (dsttmp != buf)
|
||||
free (dsttmp);
|
||||
return err;
|
||||
}
|
2
force-link.h
Normal file
2
force-link.h
Normal file
@ -0,0 +1,2 @@
|
||||
extern int force_linkat (int, char const *, int, char const *, int, bool, int);
|
||||
extern int force_symlinkat (char const *, int, char const *, bool, int);
|
78
ioblksize.h
Normal file
78
ioblksize.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* I/O block size definitions for coreutils
|
||||
Copyright (C) 1989-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Include 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));
|
||||
}
|
0
lib/.dirstamp
Normal file
0
lib/.dirstamp
Normal file
43
lib/_Noreturn.h
Normal file
43
lib/_Noreturn.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* A C macro for declaring that a function does not return.
|
||||
Copyright (C) 2011-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _Noreturn
|
||||
# if (defined __cplusplus \
|
||||
&& ((201103 <= __cplusplus && !(__GNUC__ == 4 && __GNUC_MINOR__ == 7)) \
|
||||
|| (defined _MSC_VER && 1900 <= _MSC_VER)) \
|
||||
&& 0)
|
||||
/* [[noreturn]] is not practically usable, because with it the syntax
|
||||
extern _Noreturn void func (...);
|
||||
would not be valid; such a declaration would only be valid with 'extern'
|
||||
and '_Noreturn' swapped, or without the 'extern' keyword. However, some
|
||||
AIX system header files and several gnulib header files use precisely
|
||||
this syntax with 'extern'. */
|
||||
# define _Noreturn [[noreturn]]
|
||||
# elif ((!defined __cplusplus || defined __clang__) \
|
||||
&& (201112 <= (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) \
|
||||
|| 4 < __GNUC__ + (7 <= __GNUC_MINOR__) \
|
||||
|| (defined __apple_build_version__ \
|
||||
? 6000000 <= __apple_build_version__ \
|
||||
: 3 < __clang_major__ + (5 <= __clang_minor__))))
|
||||
/* _Noreturn works as-is. */
|
||||
# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__) || 0x5110 <= __SUNPRO_C
|
||||
# define _Noreturn __attribute__ ((__noreturn__))
|
||||
# elif 1200 <= (defined _MSC_VER ? _MSC_VER : 0)
|
||||
# define _Noreturn __declspec (noreturn)
|
||||
# else
|
||||
# define _Noreturn
|
||||
# endif
|
||||
#endif
|
52
lib/acl-errno-valid.c
Normal file
52
lib/acl-errno-valid.c
Normal file
@ -0,0 +1,52 @@
|
||||
/* Test whether ACLs are well supported on this system.
|
||||
|
||||
Copyright 2013-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <acl.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* Return true if errno value ERRNUM indicates that ACLs are well
|
||||
supported on this system. ERRNUM should be an errno value obtained
|
||||
after an ACL-related system call fails. */
|
||||
bool
|
||||
acl_errno_valid (int errnum)
|
||||
{
|
||||
/* Recognize some common errors such as from an NFS mount that does
|
||||
not support ACLs, even when local drives do. */
|
||||
switch (errnum)
|
||||
{
|
||||
case EBUSY: return false;
|
||||
case EINVAL: return false;
|
||||
#if defined __APPLE__ && defined __MACH__
|
||||
case ENOENT: return false;
|
||||
#endif
|
||||
case ENOSYS: return false;
|
||||
|
||||
#if defined ENOTSUP && ENOTSUP != EOPNOTSUPP
|
||||
# if ENOTSUP != ENOSYS /* Needed for the MS-Windows port of GNU Emacs. */
|
||||
case ENOTSUP: return false;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
case EOPNOTSUPP: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
507
lib/acl-internal.c
Normal file
507
lib/acl-internal.c
Normal file
@ -0,0 +1,507 @@
|
||||
/* Test whether a file has a nontrivial ACL. -*- coding: utf-8 -*-
|
||||
|
||||
Copyright (C) 2002-2003, 2005-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "acl.h"
|
||||
|
||||
#include "acl-internal.h"
|
||||
|
||||
#if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
|
||||
|
||||
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
|
||||
|
||||
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
|
||||
Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial. */
|
||||
int
|
||||
acl_extended_nontrivial (acl_t acl)
|
||||
{
|
||||
/* acl is non-trivial if it is non-empty. */
|
||||
return (acl_entries (acl) > 0);
|
||||
}
|
||||
|
||||
# else /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
|
||||
|
||||
/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS.
|
||||
Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.
|
||||
Return -1 and set errno upon failure to determine it. */
|
||||
int
|
||||
acl_access_nontrivial (acl_t acl)
|
||||
{
|
||||
/* acl is non-trivial if it has some entries other than for "user::",
|
||||
"group::", and "other::". Normally these three should be present
|
||||
at least, allowing us to write
|
||||
return (3 < acl_entries (acl));
|
||||
but the following code is more robust. */
|
||||
# if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD, Cygwin >= 2.5 */
|
||||
|
||||
acl_entry_t ace;
|
||||
int got_one;
|
||||
|
||||
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
|
||||
got_one > 0;
|
||||
got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
|
||||
{
|
||||
acl_tag_t tag;
|
||||
if (acl_get_tag_type (ace, &tag) < 0)
|
||||
return -1;
|
||||
if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER))
|
||||
return 1;
|
||||
}
|
||||
return got_one;
|
||||
|
||||
# elif HAVE_ACL_TO_SHORT_TEXT /* IRIX */
|
||||
/* Don't use acl_get_entry: it is undocumented. */
|
||||
|
||||
int count = acl->acl_cnt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
acl_entry_t ace = &acl->acl_entry[i];
|
||||
acl_tag_t tag = ace->ae_tag;
|
||||
|
||||
if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ
|
||||
|| tag == ACL_OTHER_OBJ))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
# elif HAVE_ACL_FREE_TEXT /* Tru64 */
|
||||
/* Don't use acl_get_entry: it takes only one argument and does not work. */
|
||||
|
||||
int count = acl->acl_num;
|
||||
acl_entry_t ace;
|
||||
|
||||
for (ace = acl->acl_first; count > 0; ace = ace->next, count--)
|
||||
{
|
||||
acl_tag_t tag;
|
||||
acl_perm_t perm;
|
||||
|
||||
tag = ace->entry->acl_type;
|
||||
if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER))
|
||||
return 1;
|
||||
|
||||
perm = ace->entry->acl_perm;
|
||||
/* On Tru64, perm can also contain non-standard bits such as
|
||||
PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... */
|
||||
if ((perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
# else
|
||||
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
int
|
||||
acl_default_nontrivial (acl_t acl)
|
||||
{
|
||||
/* acl is non-trivial if it is non-empty. */
|
||||
return (acl_entries (acl) > 0);
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
|
||||
|
||||
/* Test an ACL retrieved with GETACL.
|
||||
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_nontrivial (int count, aclent_t *entries)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
aclent_t *ace = &entries[i];
|
||||
|
||||
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
|
||||
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
|
||||
We don't need to check ace->a_id in these cases. */
|
||||
if (!(ace->a_type == USER_OBJ
|
||||
|| ace->a_type == GROUP_OBJ
|
||||
|| ace->a_type == OTHER_OBJ
|
||||
/* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry
|
||||
sometimes. */
|
||||
|| ace->a_type == CLASS_OBJ))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ifdef ACE_GETACL
|
||||
|
||||
/* A shortcut for a bitmask. */
|
||||
# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA)
|
||||
|
||||
/* Test an ACL retrieved with ACE_GETACL.
|
||||
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_ace_nontrivial (int count, ace_t *entries)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* The flags in the ace_t structure changed in a binary incompatible way
|
||||
when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
|
||||
How to distinguish the two conventions at runtime?
|
||||
In the old convention, usually three ACEs have a_flags = ACE_OWNER /
|
||||
ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new
|
||||
convention, these values are not used. */
|
||||
int old_convention = 0;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
|
||||
{
|
||||
old_convention = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_convention)
|
||||
/* Running on Solaris 10. */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ace_t *ace = &entries[i];
|
||||
|
||||
/* Note:
|
||||
If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat().
|
||||
If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat().
|
||||
We don't need to check ace->a_who in these cases. */
|
||||
if (!(ace->a_type == OLD_ALLOW
|
||||
&& (ace->a_flags == OLD_ACE_OWNER
|
||||
|| ace->a_flags == OLD_ACE_GROUP
|
||||
|| ace->a_flags == OLD_ACE_OTHER)))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Running on Solaris 10 (newer version) or Solaris 11. */
|
||||
unsigned int access_masks[6] =
|
||||
{
|
||||
0, /* owner@ deny */
|
||||
0, /* owner@ allow */
|
||||
0, /* group@ deny */
|
||||
0, /* group@ allow */
|
||||
0, /* everyone@ deny */
|
||||
0 /* everyone@ allow */
|
||||
};
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ace_t *ace = &entries[i];
|
||||
unsigned int index1;
|
||||
unsigned int index2;
|
||||
|
||||
if (ace->a_type == NEW_ACE_ACCESS_ALLOWED_ACE_TYPE)
|
||||
index1 = 1;
|
||||
else if (ace->a_type == NEW_ACE_ACCESS_DENIED_ACE_TYPE)
|
||||
index1 = 0;
|
||||
else
|
||||
return 1;
|
||||
|
||||
if (ace->a_flags == NEW_ACE_OWNER)
|
||||
index2 = 0;
|
||||
else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP))
|
||||
index2 = 2;
|
||||
else if (ace->a_flags == NEW_ACE_EVERYONE)
|
||||
index2 = 4;
|
||||
else
|
||||
return 1;
|
||||
|
||||
access_masks[index1 + index2] |= ace->a_access_mask;
|
||||
}
|
||||
|
||||
/* The same bit shouldn't be both allowed and denied. */
|
||||
if (access_masks[0] & access_masks[1])
|
||||
return 1;
|
||||
if (access_masks[2] & access_masks[3])
|
||||
return 1;
|
||||
if (access_masks[4] & access_masks[5])
|
||||
return 1;
|
||||
|
||||
/* Check minimum masks. */
|
||||
if ((NEW_ACE_WRITE_NAMED_ATTRS
|
||||
| NEW_ACE_WRITE_ATTRIBUTES
|
||||
| NEW_ACE_WRITE_ACL
|
||||
| NEW_ACE_WRITE_OWNER)
|
||||
& ~ access_masks[1])
|
||||
return 1;
|
||||
access_masks[1] &= ~(NEW_ACE_WRITE_NAMED_ATTRS
|
||||
| NEW_ACE_WRITE_ATTRIBUTES
|
||||
| NEW_ACE_WRITE_ACL
|
||||
| NEW_ACE_WRITE_OWNER);
|
||||
if ((NEW_ACE_READ_NAMED_ATTRS
|
||||
| NEW_ACE_READ_ATTRIBUTES
|
||||
| NEW_ACE_READ_ACL
|
||||
| NEW_ACE_SYNCHRONIZE)
|
||||
& ~ access_masks[5])
|
||||
return 1;
|
||||
access_masks[5] &= ~(NEW_ACE_READ_NAMED_ATTRS
|
||||
| NEW_ACE_READ_ATTRIBUTES
|
||||
| NEW_ACE_READ_ACL
|
||||
| NEW_ACE_SYNCHRONIZE);
|
||||
|
||||
/* Check the allowed or denied bits. */
|
||||
switch ((access_masks[0] | access_masks[1])
|
||||
& ~(NEW_ACE_READ_NAMED_ATTRS
|
||||
| NEW_ACE_READ_ATTRIBUTES
|
||||
| NEW_ACE_READ_ACL
|
||||
| NEW_ACE_SYNCHRONIZE))
|
||||
{
|
||||
case 0:
|
||||
case NEW_ACE_READ_DATA:
|
||||
case NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
switch ((access_masks[2] | access_masks[3])
|
||||
& ~(NEW_ACE_READ_NAMED_ATTRS
|
||||
| NEW_ACE_READ_ATTRIBUTES
|
||||
| NEW_ACE_READ_ACL
|
||||
| NEW_ACE_SYNCHRONIZE))
|
||||
{
|
||||
case 0:
|
||||
case NEW_ACE_READ_DATA:
|
||||
case NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
switch ((access_masks[4] | access_masks[5])
|
||||
& ~(NEW_ACE_WRITE_NAMED_ATTRS
|
||||
| NEW_ACE_WRITE_ATTRIBUTES
|
||||
| NEW_ACE_WRITE_ACL
|
||||
| NEW_ACE_WRITE_OWNER))
|
||||
{
|
||||
case 0:
|
||||
case NEW_ACE_READ_DATA:
|
||||
case NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
|
||||
case NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are
|
||||
either both allowed or both denied. */
|
||||
if (((access_masks[0] & NEW_ACE_WRITE_DATA) != 0)
|
||||
!= ((access_masks[0] & NEW_ACE_APPEND_DATA) != 0))
|
||||
return 1;
|
||||
if (((access_masks[2] & NEW_ACE_WRITE_DATA) != 0)
|
||||
!= ((access_masks[2] & NEW_ACE_APPEND_DATA) != 0))
|
||||
return 1;
|
||||
if (((access_masks[4] & NEW_ACE_WRITE_DATA) != 0)
|
||||
!= ((access_masks[4] & NEW_ACE_APPEND_DATA) != 0))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#elif USE_ACL && HAVE_GETACL /* HP-UX */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_nontrivial (int count, struct acl_entry *entries)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (count > 3)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
struct acl_entry *ace = &entries[i];
|
||||
|
||||
if (ace->uid != ACL_NSUSER && ace->gid != ACL_NSGROUP)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
aclv_nontrivial (int count, struct acl *entries)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
struct acl *ace = &entries[i];
|
||||
|
||||
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
|
||||
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
|
||||
We don't need to check ace->a_id in these cases. */
|
||||
if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */
|
||||
|| ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */
|
||||
|| ace->a_type == CLASS_OBJ
|
||||
|| ace->a_type == OTHER_OBJ))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#elif USE_ACL && (HAVE_ACLX_GET || HAVE_STATACL) /* AIX */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_nontrivial (struct acl *a)
|
||||
{
|
||||
/* The normal way to iterate through an ACL is like this:
|
||||
struct acl_entry *ace;
|
||||
for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace))
|
||||
{
|
||||
struct ace_id *aei;
|
||||
switch (ace->ace_type)
|
||||
{
|
||||
case ACC_PERMIT:
|
||||
case ACC_DENY:
|
||||
case ACC_SPECIFY:
|
||||
...;
|
||||
}
|
||||
for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei))
|
||||
...
|
||||
}
|
||||
*/
|
||||
return (acl_last (a) != a->acl_ext ? 1 : 0);
|
||||
}
|
||||
|
||||
# if HAVE_ACLX_GET && defined ACL_AIX_WIP /* newer AIX */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_nfs4_nontrivial (nfs4_acl_int_t *a)
|
||||
{
|
||||
# if 1 /* let's try this first */
|
||||
return (a->aclEntryN > 0 ? 1 : 0);
|
||||
# else
|
||||
int count = a->aclEntryN;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
nfs4_ace_int_t *ace = &a->aclEntry[i];
|
||||
|
||||
if (!((ace->flags & ACE4_ID_SPECIAL) != 0
|
||||
&& (ace->aceWho.special_whoid == ACE4_WHO_OWNER
|
||||
|| ace->aceWho.special_whoid == ACE4_WHO_GROUP
|
||||
|| ace->aceWho.special_whoid == ACE4_WHO_EVERYONE)
|
||||
&& ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE
|
||||
&& ace->aceFlags == 0
|
||||
&& (ace->aceMask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY
|
||||
| ACE4_WRITE_DATA | ACE4_ADD_FILE
|
||||
| ACE4_EXECUTE)) == 0))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
|
||||
|
||||
/* Test an ACL retrieved with ACL_GET.
|
||||
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
int
|
||||
acl_nontrivial (int count, struct acl *entries)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
struct acl *ace = &entries[i];
|
||||
|
||||
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
|
||||
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
|
||||
We don't need to check ace->a_id in these cases. */
|
||||
if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */
|
||||
|| ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */
|
||||
|| ace->a_type == CLASS_OBJ
|
||||
|| ace->a_type == OTHER_OBJ))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
free_permission_context (struct permission_context *ctx)
|
||||
{
|
||||
#if USE_ACL
|
||||
# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
|
||||
if (ctx->acl)
|
||||
acl_free (ctx->acl);
|
||||
# if !HAVE_ACL_TYPE_EXTENDED
|
||||
if (ctx->default_acl)
|
||||
acl_free (ctx->default_acl);
|
||||
# endif
|
||||
|
||||
# elif defined GETACL /* Solaris, Cygwin < 2.5 */
|
||||
free (ctx->entries);
|
||||
# ifdef ACE_GETACL
|
||||
free (ctx->ace_entries);
|
||||
# endif
|
||||
|
||||
# elif HAVE_GETACL /* HP-UX */
|
||||
|
||||
# if HAVE_ACLV_H
|
||||
# endif
|
||||
|
||||
# elif HAVE_STATACL /* older AIX */
|
||||
|
||||
# elif HAVE_ACLSORT /* NonStop Kernel */
|
||||
|
||||
# endif
|
||||
#endif
|
||||
}
|
302
lib/acl-internal.h
Normal file
302
lib/acl-internal.h
Normal file
@ -0,0 +1,302 @@
|
||||
/* Internal implementation of access control lists. -*- coding: utf-8 -*-
|
||||
|
||||
Copyright (C) 2002-2003, 2005-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
|
||||
|
||||
#include "acl.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* All systems define the ACL related API in <sys/acl.h>. */
|
||||
#if HAVE_SYS_ACL_H
|
||||
# include <sys/acl.h>
|
||||
#endif
|
||||
#if defined HAVE_FACL && ! defined GETACLCNT && defined ACL_CNT
|
||||
# define GETACLCNT ACL_CNT
|
||||
#endif
|
||||
|
||||
/* On Linux and Cygwin >= 2.5, additional ACL related API is available in
|
||||
<acl/libacl.h>. */
|
||||
#ifdef HAVE_ACL_LIBACL_H
|
||||
# include <acl/libacl.h>
|
||||
#endif
|
||||
|
||||
/* On HP-UX >= 11.11, additional ACL API is available in <aclv.h>. */
|
||||
#if HAVE_ACLV_H
|
||||
# include <sys/types.h>
|
||||
# include <aclv.h>
|
||||
/* HP-UX 11.11 lacks these declarations. */
|
||||
extern int acl (char *, int, int, struct acl *);
|
||||
extern int aclsort (int, int, struct acl *);
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <limits.h>
|
||||
#ifndef MIN
|
||||
# define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FCHMOD
|
||||
# define HAVE_FCHMOD false
|
||||
# define fchmod(fd, mode) (-1)
|
||||
#endif
|
||||
|
||||
#ifndef _GL_INLINE_HEADER_BEGIN
|
||||
#error "Please include config.h first."
|
||||
#endif
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef ACL_INTERNAL_INLINE
|
||||
# define ACL_INTERNAL_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
#if USE_ACL
|
||||
|
||||
# if HAVE_ACL_GET_FILE
|
||||
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
|
||||
/* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
|
||||
|
||||
# ifndef MIN_ACL_ENTRIES
|
||||
# define MIN_ACL_ENTRIES 4
|
||||
# endif
|
||||
|
||||
/* POSIX 1003.1e (draft 17) */
|
||||
# ifdef HAVE_ACL_GET_FD
|
||||
/* Most platforms have a 1-argument acl_get_fd, only OSF/1 has a 2-argument
|
||||
macro(!). */
|
||||
# if HAVE_ACL_FREE_TEXT /* OSF/1 */
|
||||
ACL_INTERNAL_INLINE acl_t
|
||||
rpl_acl_get_fd (int fd)
|
||||
{
|
||||
return acl_get_fd (fd, ACL_TYPE_ACCESS);
|
||||
}
|
||||
# undef acl_get_fd
|
||||
# define acl_get_fd rpl_acl_get_fd
|
||||
# endif
|
||||
# else
|
||||
# define HAVE_ACL_GET_FD false
|
||||
# undef acl_get_fd
|
||||
# define acl_get_fd(fd) (NULL)
|
||||
# endif
|
||||
|
||||
/* POSIX 1003.1e (draft 17) */
|
||||
# ifdef HAVE_ACL_SET_FD
|
||||
/* Most platforms have a 2-argument acl_set_fd, only OSF/1 has a 3-argument
|
||||
macro(!). */
|
||||
# if HAVE_ACL_FREE_TEXT /* OSF/1 */
|
||||
ACL_INTERNAL_INLINE int
|
||||
rpl_acl_set_fd (int fd, acl_t acl)
|
||||
{
|
||||
return acl_set_fd (fd, ACL_TYPE_ACCESS, acl);
|
||||
}
|
||||
# undef acl_set_fd
|
||||
# define acl_set_fd rpl_acl_set_fd
|
||||
# endif
|
||||
# else
|
||||
# define HAVE_ACL_SET_FD false
|
||||
# undef acl_set_fd
|
||||
# define acl_set_fd(fd, acl) (-1)
|
||||
# endif
|
||||
|
||||
/* POSIX 1003.1e (draft 13) */
|
||||
# if ! HAVE_ACL_FREE_TEXT
|
||||
# define acl_free_text(buf) acl_free (buf)
|
||||
# endif
|
||||
|
||||
/* Linux-specific */
|
||||
/* Cygwin >= 2.5 implements this function, but it returns 1 for all
|
||||
directories, thus is unusable. */
|
||||
# if !defined HAVE_ACL_EXTENDED_FILE || defined __CYGWIN__
|
||||
# undef HAVE_ACL_EXTENDED_FILE
|
||||
# define HAVE_ACL_EXTENDED_FILE false
|
||||
# define acl_extended_file(name) (-1)
|
||||
# endif
|
||||
|
||||
# if ! defined HAVE_ACL_FROM_MODE && ! defined HAVE_ACL_FROM_TEXT
|
||||
# define acl_from_mode (NULL)
|
||||
# endif
|
||||
|
||||
/* Set to 0 if a file's mode is stored independently from the ACL. */
|
||||
# if (HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP) || defined __sgi /* Mac OS X, IRIX */
|
||||
# define MODE_INSIDE_ACL 0
|
||||
# endif
|
||||
|
||||
/* Return the number of entries in ACL.
|
||||
Return -1 and set errno upon failure to determine it. */
|
||||
/* Define a replacement for acl_entries if needed. (Only Linux has it.) */
|
||||
# if !HAVE_ACL_ENTRIES
|
||||
# define acl_entries rpl_acl_entries
|
||||
extern int acl_entries (acl_t);
|
||||
# endif
|
||||
|
||||
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
|
||||
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
|
||||
Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial. */
|
||||
extern int acl_extended_nontrivial (acl_t);
|
||||
# else
|
||||
/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS.
|
||||
Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.
|
||||
Return -1 and set errno upon failure to determine it. */
|
||||
extern int acl_access_nontrivial (acl_t);
|
||||
|
||||
/* ACL is an ACL, from a file, stored as type ACL_TYPE_DEFAULT.
|
||||
Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.
|
||||
Return -1 and set errno upon failure to determine it. */
|
||||
extern int acl_default_nontrivial (acl_t);
|
||||
# endif
|
||||
|
||||
# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
|
||||
|
||||
/* Set to 0 if a file's mode is stored independently from the ACL. */
|
||||
# if defined __CYGWIN__ /* Cygwin */
|
||||
# define MODE_INSIDE_ACL 0
|
||||
# endif
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int acl_nontrivial (int count, aclent_t *entries) _GL_ATTRIBUTE_PURE;
|
||||
|
||||
# ifdef ACE_GETACL /* Solaris 10 */
|
||||
|
||||
/* Test an ACL retrieved with ACE_GETACL.
|
||||
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int acl_ace_nontrivial (int count, ace_t *entries) _GL_ATTRIBUTE_PURE;
|
||||
|
||||
/* Definitions for when the built executable is executed on Solaris 10
|
||||
(newer version) or Solaris 11. */
|
||||
/* For a_type. */
|
||||
# define OLD_ALLOW 0
|
||||
# define OLD_DENY 1
|
||||
# define NEW_ACE_ACCESS_ALLOWED_ACE_TYPE 0 /* replaces ALLOW */
|
||||
# define NEW_ACE_ACCESS_DENIED_ACE_TYPE 1 /* replaces DENY */
|
||||
/* For a_flags. */
|
||||
# define OLD_ACE_OWNER 0x0100
|
||||
# define OLD_ACE_GROUP 0x0200
|
||||
# define OLD_ACE_OTHER 0x0400
|
||||
# define NEW_ACE_OWNER 0x1000
|
||||
# define NEW_ACE_GROUP 0x2000
|
||||
# define NEW_ACE_IDENTIFIER_GROUP 0x0040
|
||||
# define NEW_ACE_EVERYONE 0x4000
|
||||
/* For a_access_mask. */
|
||||
# define NEW_ACE_READ_DATA 0x001 /* corresponds to 'r' */
|
||||
# define NEW_ACE_WRITE_DATA 0x002 /* corresponds to 'w' */
|
||||
# define NEW_ACE_APPEND_DATA 0x004
|
||||
# define NEW_ACE_READ_NAMED_ATTRS 0x008
|
||||
# define NEW_ACE_WRITE_NAMED_ATTRS 0x010
|
||||
# define NEW_ACE_EXECUTE 0x020
|
||||
# define NEW_ACE_DELETE_CHILD 0x040
|
||||
# define NEW_ACE_READ_ATTRIBUTES 0x080
|
||||
# define NEW_ACE_WRITE_ATTRIBUTES 0x100
|
||||
# define NEW_ACE_DELETE 0x10000
|
||||
# define NEW_ACE_READ_ACL 0x20000
|
||||
# define NEW_ACE_WRITE_ACL 0x40000
|
||||
# define NEW_ACE_WRITE_OWNER 0x80000
|
||||
# define NEW_ACE_SYNCHRONIZE 0x100000
|
||||
|
||||
# endif
|
||||
|
||||
# elif HAVE_GETACL /* HP-UX */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int acl_nontrivial (int count, struct acl_entry *entries);
|
||||
|
||||
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int aclv_nontrivial (int count, struct acl *entries);
|
||||
|
||||
# endif
|
||||
|
||||
# elif HAVE_ACLX_GET && 0 /* AIX */
|
||||
|
||||
/* TODO */
|
||||
|
||||
# elif HAVE_STATACL /* older AIX */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int acl_nontrivial (struct acl *a);
|
||||
|
||||
# elif HAVE_ACLSORT /* NonStop Kernel */
|
||||
|
||||
/* Return 1 if the given ACL is non-trivial.
|
||||
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
|
||||
extern int acl_nontrivial (int count, struct acl *entries);
|
||||
|
||||
# endif
|
||||
|
||||
/* Set to 1 if a file's mode is implicit by the ACL. */
|
||||
# ifndef MODE_INSIDE_ACL
|
||||
# define MODE_INSIDE_ACL 1
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
struct permission_context {
|
||||
mode_t mode;
|
||||
#if USE_ACL
|
||||
# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
|
||||
acl_t acl;
|
||||
# if !HAVE_ACL_TYPE_EXTENDED
|
||||
acl_t default_acl;
|
||||
# endif
|
||||
bool acls_not_supported;
|
||||
|
||||
# elif defined GETACL /* Solaris, Cygwin < 2.5 */
|
||||
int count;
|
||||
aclent_t *entries;
|
||||
# ifdef ACE_GETACL
|
||||
int ace_count;
|
||||
ace_t *ace_entries;
|
||||
# endif
|
||||
|
||||
# elif HAVE_GETACL /* HP-UX */
|
||||
struct acl_entry entries[NACLENTRIES];
|
||||
int count;
|
||||
# if HAVE_ACLV_H
|
||||
struct acl aclv_entries[NACLVENTRIES];
|
||||
int aclv_count;
|
||||
# endif
|
||||
|
||||
# elif HAVE_STATACL /* older AIX */
|
||||
union { struct acl a; char room[4096]; } u;
|
||||
bool have_u;
|
||||
|
||||
# elif HAVE_ACLSORT /* NonStop Kernel */
|
||||
struct acl entries[NACLENTRIES];
|
||||
int count;
|
||||
|
||||
# endif
|
||||
#endif
|
||||
};
|
||||
|
||||
int get_permissions (const char *, int, mode_t, struct permission_context *);
|
||||
int set_permissions (struct permission_context *, const char *, int);
|
||||
void free_permission_context (struct permission_context *);
|
||||
|
||||
_GL_INLINE_HEADER_END
|
35
lib/acl.h
Normal file
35
lib/acl.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* acl.c - access control lists
|
||||
|
||||
Copyright (C) 2002, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert. */
|
||||
|
||||
#ifndef _GL_ACL_H
|
||||
#define _GL_ACL_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
|
||||
int file_has_acl (char const *, struct stat const *);
|
||||
int qset_acl (char const *, int, mode_t);
|
||||
int set_acl (char const *, int, mode_t);
|
||||
int qcopy_acl (char const *, int, char const *, int, mode_t);
|
||||
int copy_acl (char const *, int, char const *, int, mode_t);
|
||||
int chmod_or_fchmod (char const *, int, mode_t);
|
||||
|
||||
#endif
|
75
lib/acl_entries.c
Normal file
75
lib/acl_entries.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* Return the number of entries in an ACL.
|
||||
|
||||
Copyright (C) 2002-2003, 2005-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert and Andreas Gruenbacher. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "acl-internal.h"
|
||||
|
||||
/* This file assumes POSIX-draft like ACLs
|
||||
(Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5). */
|
||||
|
||||
/* Return the number of entries in ACL.
|
||||
Return -1 and set errno upon failure to determine it. */
|
||||
|
||||
int
|
||||
acl_entries (acl_t acl)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (acl != NULL)
|
||||
{
|
||||
#if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD, Mac OS X, Cygwin >= 2.5 */
|
||||
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
|
||||
/* acl_get_entry returns 0 when it successfully fetches an entry,
|
||||
and -1/EINVAL at the end. */
|
||||
acl_entry_t ace;
|
||||
int got_one;
|
||||
|
||||
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
|
||||
got_one >= 0;
|
||||
got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
|
||||
count++;
|
||||
# else /* Linux, FreeBSD, Cygwin >= 2.5 */
|
||||
/* acl_get_entry returns 1 when it successfully fetches an entry,
|
||||
and 0 at the end. */
|
||||
acl_entry_t ace;
|
||||
int got_one;
|
||||
|
||||
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
|
||||
got_one > 0;
|
||||
got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
|
||||
count++;
|
||||
if (got_one < 0)
|
||||
return -1;
|
||||
# endif
|
||||
#else /* IRIX, Tru64 */
|
||||
# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */
|
||||
/* Don't use acl_get_entry: it is undocumented. */
|
||||
count = acl->acl_cnt;
|
||||
# endif
|
||||
# if HAVE_ACL_FREE_TEXT /* Tru64 */
|
||||
/* Don't use acl_get_entry: it takes only one argument and does not
|
||||
work. */
|
||||
count = acl->acl_num;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
213
lib/af_alg.c
Normal file
213
lib/af_alg.c
Normal file
@ -0,0 +1,213 @@
|
||||
/* af_alg.c - Compute message digests from file streams and buffers.
|
||||
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Matteo Croce <mcroce@redhat.com>, 2018. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "af_alg.h"
|
||||
|
||||
#if USE_LINUX_CRYPTO_API
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <linux/if_alg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "sys-limits.h"
|
||||
|
||||
#define BLOCKSIZE 32768
|
||||
|
||||
/* Return a newly created socket for ALG.
|
||||
On error, return a negative error number. */
|
||||
static int
|
||||
alg_socket (char const *alg)
|
||||
{
|
||||
struct sockaddr_alg salg = {
|
||||
.salg_family = AF_ALG,
|
||||
.salg_type = "hash",
|
||||
};
|
||||
/* Copy alg into salg.salg_name, without calling strcpy nor strlen. */
|
||||
for (size_t i = 0; (salg.salg_name[i] = alg[i]) != '\0'; i++)
|
||||
if (i == sizeof salg.salg_name - 1)
|
||||
/* alg is too long. */
|
||||
return -EINVAL;
|
||||
|
||||
int cfd = socket (AF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||
if (cfd < 0)
|
||||
return -EAFNOSUPPORT;
|
||||
int ofd = (bind (cfd, (struct sockaddr *) &salg, sizeof salg) == 0
|
||||
? accept4 (cfd, NULL, 0, SOCK_CLOEXEC)
|
||||
: -1);
|
||||
close (cfd);
|
||||
return ofd < 0 ? -EAFNOSUPPORT : ofd;
|
||||
}
|
||||
|
||||
int
|
||||
afalg_buffer (const char *buffer, size_t len, const char *alg,
|
||||
void *resblock, ssize_t hashlen)
|
||||
{
|
||||
/* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
|
||||
See <https://patchwork.kernel.org/patch/9308641/>.
|
||||
This was not fixed properly until November 2016,
|
||||
see <https://patchwork.kernel.org/patch/9434741/>. */
|
||||
if (len == 0)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
int ofd = alg_socket (alg);
|
||||
if (ofd < 0)
|
||||
return ofd;
|
||||
|
||||
int result;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len);
|
||||
if (send (ofd, buffer, size, MSG_MORE) != size)
|
||||
{
|
||||
result = -EAFNOSUPPORT;
|
||||
break;
|
||||
}
|
||||
buffer += size;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
{
|
||||
result = read (ofd, resblock, hashlen) == hashlen ? 0 : -EAFNOSUPPORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close (ofd);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
afalg_stream (FILE *stream, const char *alg,
|
||||
void *resblock, ssize_t hashlen)
|
||||
{
|
||||
int ofd = alg_socket (alg);
|
||||
if (ofd < 0)
|
||||
return ofd;
|
||||
|
||||
/* If STREAM's size is known and nonzero and not too large, attempt
|
||||
sendfile to pipe the data. The nonzero restriction avoids issues
|
||||
with /proc files that pretend to be empty, and lets the classic
|
||||
read-write loop work around an empty-input bug noted below. */
|
||||
int fd = fileno (stream);
|
||||
int result;
|
||||
struct stat st;
|
||||
off_t off = ftello (stream);
|
||||
if (0 <= off && fstat (fd, &st) == 0
|
||||
&& (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
|
||||
&& off < st.st_size && st.st_size - off < SYS_BUFSIZE_MAX)
|
||||
{
|
||||
/* Make sure the offset of fileno (stream) reflects how many bytes
|
||||
have been read from stream before this function got invoked.
|
||||
Note: fflush on an input stream after ungetc does not work as expected
|
||||
on some platforms. Therefore this situation is not supported here. */
|
||||
if (fflush (stream))
|
||||
result = -EIO;
|
||||
else
|
||||
{
|
||||
off_t nbytes = st.st_size - off;
|
||||
if (sendfile (ofd, fd, &off, nbytes) == nbytes)
|
||||
{
|
||||
if (read (ofd, resblock, hashlen) == hashlen)
|
||||
{
|
||||
/* The input buffers of stream are no longer valid. */
|
||||
if (lseek (fd, off, SEEK_SET) != (off_t)-1)
|
||||
result = 0;
|
||||
else
|
||||
/* The file position of fd has not changed. */
|
||||
result = -EAFNOSUPPORT;
|
||||
}
|
||||
else
|
||||
/* The file position of fd has not changed. */
|
||||
result = -EAFNOSUPPORT;
|
||||
}
|
||||
else
|
||||
/* The file position of fd has not changed. */
|
||||
result = -EAFNOSUPPORT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* sendfile not possible, do a classic read-write loop. */
|
||||
|
||||
/* Number of bytes to seek (backwards) in case of error. */
|
||||
off_t nseek = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buf[BLOCKSIZE];
|
||||
/* When the stream is not seekable, start with a single-byte block,
|
||||
so that we can use ungetc() in the case that send() fails. */
|
||||
size_t blocksize = (nseek == 0 && off < 0 ? 1 : BLOCKSIZE);
|
||||
ssize_t size = fread (buf, 1, blocksize, stream);
|
||||
if (size == 0)
|
||||
{
|
||||
/* On Linux < 4.9, the value for an empty stream is wrong (all 0).
|
||||
See <https://patchwork.kernel.org/patch/9308641/>.
|
||||
This was not fixed properly until November 2016,
|
||||
see <https://patchwork.kernel.org/patch/9434741/>. */
|
||||
result = ferror (stream) ? -EIO : nseek == 0 ? -EAFNOSUPPORT : 0;
|
||||
break;
|
||||
}
|
||||
nseek -= size;
|
||||
if (send (ofd, buf, size, MSG_MORE) != size)
|
||||
{
|
||||
if (nseek == -1)
|
||||
{
|
||||
/* 1 byte of pushback buffer is guaranteed on stream, even
|
||||
if stream is not seekable. */
|
||||
ungetc ((unsigned char) buf[0], stream);
|
||||
result = -EAFNOSUPPORT;
|
||||
}
|
||||
else if (fseeko (stream, nseek, SEEK_CUR) == 0)
|
||||
/* The position of stream has been restored. */
|
||||
result = -EAFNOSUPPORT;
|
||||
else
|
||||
result = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Don't assume that EOF is sticky. See:
|
||||
<https://sourceware.org/bugzilla/show_bug.cgi?id=19476>. */
|
||||
if (feof (stream))
|
||||
{
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0 && read (ofd, resblock, hashlen) != hashlen)
|
||||
{
|
||||
if (nseek == 0 || fseeko (stream, nseek, SEEK_CUR) == 0)
|
||||
/* The position of stream has been restored. */
|
||||
result = -EAFNOSUPPORT;
|
||||
else
|
||||
result = -EIO;
|
||||
}
|
||||
}
|
||||
close (ofd);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
115
lib/af_alg.h
Normal file
115
lib/af_alg.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* af_alg.h - Compute message digests from file streams and buffers.
|
||||
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Matteo Croce <mcroce@redhat.com>, 2018.
|
||||
Documentation by Bruno Haible <bruno@clisp.org>, 2018. */
|
||||
|
||||
/* Declare specific functions for computing message digests
|
||||
using the Linux kernel crypto API, if available. This kernel API gives
|
||||
access to specialized crypto instructions (that would also be available
|
||||
in user space) or to crypto devices (not directly available in user space).
|
||||
|
||||
For a more complete set of facilities that use the Linux kernel crypto API,
|
||||
look at libkcapi. */
|
||||
|
||||
#ifndef AF_ALG_H
|
||||
# define AF_ALG_H 1
|
||||
|
||||
# include <stdio.h>
|
||||
# include <errno.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
# if USE_LINUX_CRYPTO_API
|
||||
|
||||
/* Compute a message digest of a memory region.
|
||||
|
||||
The memory region starts at BUFFER and is LEN bytes long.
|
||||
|
||||
ALG is the message digest algorithm; see the file /proc/crypto.
|
||||
|
||||
RESBLOCK points to a block of HASHLEN bytes, for the result.
|
||||
HASHLEN must be the length of the message digest, in bytes, in particular:
|
||||
|
||||
alg | hashlen
|
||||
-------+--------
|
||||
md5 | 16
|
||||
sha1 | 20
|
||||
sha224 | 28
|
||||
sha256 | 32
|
||||
sha384 | 48
|
||||
sha512 | 64
|
||||
|
||||
If successful, fill RESBLOCK and return 0.
|
||||
Upon failure, return a negated error number. */
|
||||
int
|
||||
afalg_buffer (const char *buffer, size_t len, const char *alg,
|
||||
void *resblock, ssize_t hashlen);
|
||||
|
||||
/* Compute a message digest of data read from STREAM.
|
||||
|
||||
STREAM is an open file stream. The last operation on STREAM should
|
||||
not be 'ungetc', and if STREAM is also open for writing it should
|
||||
have been fflushed since its last write. Read from the current
|
||||
position to the end of STREAM. Handle regular files efficiently.
|
||||
|
||||
ALG is the message digest algorithm; see the file /proc/crypto.
|
||||
|
||||
RESBLOCK points to a block of HASHLEN bytes, for the result.
|
||||
HASHLEN must be the length of the message digest, in bytes, in particular:
|
||||
|
||||
alg | hashlen
|
||||
-------+--------
|
||||
md5 | 16
|
||||
sha1 | 20
|
||||
sha224 | 28
|
||||
sha256 | 32
|
||||
sha384 | 48
|
||||
sha512 | 64
|
||||
|
||||
If successful, fill RESBLOCK and return 0.
|
||||
Upon failure, return a negated error number.
|
||||
Unless returning 0 or -EIO, restore STREAM's file position so that
|
||||
the caller can fall back on some other method. */
|
||||
int
|
||||
afalg_stream (FILE *stream, const char *alg,
|
||||
void *resblock, ssize_t hashlen);
|
||||
|
||||
# else
|
||||
|
||||
static inline int
|
||||
afalg_buffer (const char *buffer, size_t len, const char *alg,
|
||||
void *resblock, ssize_t hashlen)
|
||||
{
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
static inline int
|
||||
afalg_stream (FILE *stream, const char *alg,
|
||||
void *resblock, ssize_t hashlen)
|
||||
{
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif /* AF_ALG_H */
|
50
lib/alignof.h
Normal file
50
lib/alignof.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* Determine alignment of types.
|
||||
Copyright (C) 2003-2004, 2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _ALIGNOF_H
|
||||
#define _ALIGNOF_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* alignof_slot (TYPE)
|
||||
Determine the alignment of a structure slot (field) of a given type,
|
||||
at compile time. Note that the result depends on the ABI.
|
||||
This is the same as alignof (TYPE) and _Alignof (TYPE), defined in
|
||||
<stdalign.h> if __alignof_is_defined is 1.
|
||||
Note: The result cannot be used as a value for an 'enum' constant,
|
||||
due to bugs in HP-UX 10.20 cc and AIX 3.2.5 xlc. */
|
||||
#if defined __cplusplus
|
||||
template <class type> struct alignof_helper { char __slot1; type __slot2; };
|
||||
# define alignof_slot(type) offsetof (alignof_helper<type>, __slot2)
|
||||
#else
|
||||
# define alignof_slot(type) offsetof (struct { char __slot1; type __slot2; }, __slot2)
|
||||
#endif
|
||||
|
||||
/* alignof_type (TYPE)
|
||||
Determine the good alignment of an object of the given type at compile time.
|
||||
Note that this is not necessarily the same as alignof_slot(type).
|
||||
For example, with GNU C on x86 platforms: alignof_type(double) = 8, but
|
||||
- when -malign-double is not specified: alignof_slot(double) = 4,
|
||||
- when -malign-double is specified: alignof_slot(double) = 8.
|
||||
Note: The result cannot be used as a value for an 'enum' constant,
|
||||
due to bugs in HP-UX 10.20 cc and AIX 3.2.5 xlc. */
|
||||
#if defined __GNUC__ || defined __IBM__ALIGNOF__
|
||||
# define alignof_type __alignof__
|
||||
#else
|
||||
# define alignof_type alignof_slot
|
||||
#endif
|
||||
|
||||
#endif /* _ALIGNOF_H */
|
478
lib/alloca.c
Normal file
478
lib/alloca.c
Normal file
@ -0,0 +1,478 @@
|
||||
/* alloca.c -- allocate automatically reclaimed memory
|
||||
(Mostly) portable public-domain implementation -- D A Gwyn
|
||||
|
||||
This implementation of the PWB library alloca function,
|
||||
which is used to allocate space off the run-time stack so
|
||||
that it is automatically reclaimed upon procedure exit,
|
||||
was inspired by discussions with J. Q. Johnson of Cornell.
|
||||
J.Otto Tennant <jot@cray.com> contributed the Cray support.
|
||||
|
||||
There are some preprocessor constants that can
|
||||
be defined when compiling for your specific system, for
|
||||
improved efficiency; however, the defaults should be okay.
|
||||
|
||||
The general concept of this implementation is to keep
|
||||
track of all alloca-allocated blocks, and reclaim any
|
||||
that are found to be deeper in the stack than the current
|
||||
invocation. This heuristic does not reclaim storage as
|
||||
soon as it becomes invalid, but it will do so eventually.
|
||||
|
||||
As a special case, alloca(0) reclaims storage without
|
||||
allocating any. It is a good idea to use alloca(0) in
|
||||
your main control loop, etc. to force garbage collection. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <alloca.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef emacs
|
||||
# include "lisp.h"
|
||||
# include "blockinput.h"
|
||||
# ifdef EMACS_FREE
|
||||
# undef free
|
||||
# define free EMACS_FREE
|
||||
# endif
|
||||
#else
|
||||
# define memory_full() abort ()
|
||||
#endif
|
||||
|
||||
/* If compiling with GCC 2, this file's not needed. */
|
||||
#if !defined (__GNUC__) || __GNUC__ < 2
|
||||
|
||||
/* If someone has defined alloca as a macro,
|
||||
there must be some other way alloca is supposed to work. */
|
||||
# ifndef alloca
|
||||
|
||||
# ifdef emacs
|
||||
# ifdef static
|
||||
/* actually, only want this if static is defined as ""
|
||||
-- this is for usg, in which emacs must undefine static
|
||||
in order to make unexec workable
|
||||
*/
|
||||
# ifndef STACK_DIRECTION
|
||||
you
|
||||
lose
|
||||
-- must know STACK_DIRECTION at compile-time
|
||||
/* Using #error here is not wise since this file should work for
|
||||
old and obscure compilers. */
|
||||
# endif /* STACK_DIRECTION undefined */
|
||||
# endif /* static */
|
||||
# endif /* emacs */
|
||||
|
||||
/* If your stack is a linked list of frames, you have to
|
||||
provide an "address metric" ADDRESS_FUNCTION macro. */
|
||||
|
||||
# if defined (CRAY) && defined (CRAY_STACKSEG_END)
|
||||
long i00afunc ();
|
||||
# define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
|
||||
# else
|
||||
# define ADDRESS_FUNCTION(arg) &(arg)
|
||||
# endif
|
||||
|
||||
/* Define STACK_DIRECTION if you know the direction of stack
|
||||
growth for your system; otherwise it will be automatically
|
||||
deduced at run-time.
|
||||
|
||||
STACK_DIRECTION > 0 => grows toward higher addresses
|
||||
STACK_DIRECTION < 0 => grows toward lower addresses
|
||||
STACK_DIRECTION = 0 => direction of growth unknown */
|
||||
|
||||
# ifndef STACK_DIRECTION
|
||||
# define STACK_DIRECTION 0 /* Direction unknown. */
|
||||
# endif
|
||||
|
||||
# if STACK_DIRECTION != 0
|
||||
|
||||
# define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
|
||||
|
||||
# else /* STACK_DIRECTION == 0; need run-time code. */
|
||||
|
||||
static int stack_dir; /* 1 or -1 once known. */
|
||||
# define STACK_DIR stack_dir
|
||||
|
||||
static int
|
||||
find_stack_direction (int *addr, int depth)
|
||||
{
|
||||
int dir, dummy = 0;
|
||||
if (! addr)
|
||||
addr = &dummy;
|
||||
*addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1;
|
||||
dir = depth ? find_stack_direction (addr, depth - 1) : 0;
|
||||
return dir + dummy;
|
||||
}
|
||||
|
||||
# endif /* STACK_DIRECTION == 0 */
|
||||
|
||||
/* An "alloca header" is used to:
|
||||
(a) chain together all alloca'ed blocks;
|
||||
(b) keep track of stack depth.
|
||||
|
||||
It is very important that sizeof(header) agree with malloc
|
||||
alignment chunk size. The following default should work okay. */
|
||||
|
||||
# ifndef ALIGN_SIZE
|
||||
# define ALIGN_SIZE sizeof(double)
|
||||
# endif
|
||||
|
||||
typedef union hdr
|
||||
{
|
||||
char align[ALIGN_SIZE]; /* To force sizeof(header). */
|
||||
struct
|
||||
{
|
||||
union hdr *next; /* For chaining headers. */
|
||||
char *deep; /* For stack depth measure. */
|
||||
} h;
|
||||
} header;
|
||||
|
||||
static header *last_alloca_header = NULL; /* -> last alloca header. */
|
||||
|
||||
/* Return a pointer to at least SIZE bytes of storage,
|
||||
which will be automatically reclaimed upon exit from
|
||||
the procedure that called alloca. Originally, this space
|
||||
was supposed to be taken from the current stack frame of the
|
||||
caller, but that method cannot be made to work for some
|
||||
implementations of C, for example under Gould's UTX/32. */
|
||||
|
||||
void *
|
||||
alloca (size_t size)
|
||||
{
|
||||
auto char probe; /* Probes stack depth: */
|
||||
register char *depth = ADDRESS_FUNCTION (probe);
|
||||
|
||||
# if STACK_DIRECTION == 0
|
||||
if (STACK_DIR == 0) /* Unknown growth direction. */
|
||||
STACK_DIR = find_stack_direction (NULL, (size & 1) + 20);
|
||||
# endif
|
||||
|
||||
/* Reclaim garbage, defined as all alloca'd storage that
|
||||
was allocated from deeper in the stack than currently. */
|
||||
|
||||
{
|
||||
register header *hp; /* Traverses linked list. */
|
||||
|
||||
# ifdef emacs
|
||||
BLOCK_INPUT;
|
||||
# endif
|
||||
|
||||
for (hp = last_alloca_header; hp != NULL;)
|
||||
if ((STACK_DIR > 0 && hp->h.deep > depth)
|
||||
|| (STACK_DIR < 0 && hp->h.deep < depth))
|
||||
{
|
||||
register header *np = hp->h.next;
|
||||
|
||||
free (hp); /* Collect garbage. */
|
||||
|
||||
hp = np; /* -> next header. */
|
||||
}
|
||||
else
|
||||
break; /* Rest are not deeper. */
|
||||
|
||||
last_alloca_header = hp; /* -> last valid storage. */
|
||||
|
||||
# ifdef emacs
|
||||
UNBLOCK_INPUT;
|
||||
# endif
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
return NULL; /* No allocation required. */
|
||||
|
||||
/* Allocate combined header + user data storage. */
|
||||
|
||||
{
|
||||
/* Address of header. */
|
||||
register header *new;
|
||||
|
||||
size_t combined_size = sizeof (header) + size;
|
||||
if (combined_size < sizeof (header))
|
||||
memory_full ();
|
||||
|
||||
new = malloc (combined_size);
|
||||
|
||||
if (! new)
|
||||
memory_full ();
|
||||
|
||||
new->h.next = last_alloca_header;
|
||||
new->h.deep = depth;
|
||||
|
||||
last_alloca_header = new;
|
||||
|
||||
/* User storage begins just after header. */
|
||||
|
||||
return (void *) (new + 1);
|
||||
}
|
||||
}
|
||||
|
||||
# if defined (CRAY) && defined (CRAY_STACKSEG_END)
|
||||
|
||||
# ifdef DEBUG_I00AFUNC
|
||||
# include <stdio.h>
|
||||
# endif
|
||||
|
||||
# ifndef CRAY_STACK
|
||||
# define CRAY_STACK
|
||||
# ifndef CRAY2
|
||||
/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
|
||||
struct stack_control_header
|
||||
{
|
||||
long shgrow:32; /* Number of times stack has grown. */
|
||||
long shaseg:32; /* Size of increments to stack. */
|
||||
long shhwm:32; /* High water mark of stack. */
|
||||
long shsize:32; /* Current size of stack (all segments). */
|
||||
};
|
||||
|
||||
/* The stack segment linkage control information occurs at
|
||||
the high-address end of a stack segment. (The stack
|
||||
grows from low addresses to high addresses.) The initial
|
||||
part of the stack segment linkage control information is
|
||||
0200 (octal) words. This provides for register storage
|
||||
for the routine which overflows the stack. */
|
||||
|
||||
struct stack_segment_linkage
|
||||
{
|
||||
long ss[0200]; /* 0200 overflow words. */
|
||||
long sssize:32; /* Number of words in this segment. */
|
||||
long ssbase:32; /* Offset to stack base. */
|
||||
long:32;
|
||||
long sspseg:32; /* Offset to linkage control of previous
|
||||
segment of stack. */
|
||||
long:32;
|
||||
long sstcpt:32; /* Pointer to task common address block. */
|
||||
long sscsnm; /* Private control structure number for
|
||||
microtasking. */
|
||||
long ssusr1; /* Reserved for user. */
|
||||
long ssusr2; /* Reserved for user. */
|
||||
long sstpid; /* Process ID for pid based multi-tasking. */
|
||||
long ssgvup; /* Pointer to multitasking thread giveup. */
|
||||
long sscray[7]; /* Reserved for Cray Research. */
|
||||
long ssa0;
|
||||
long ssa1;
|
||||
long ssa2;
|
||||
long ssa3;
|
||||
long ssa4;
|
||||
long ssa5;
|
||||
long ssa6;
|
||||
long ssa7;
|
||||
long sss0;
|
||||
long sss1;
|
||||
long sss2;
|
||||
long sss3;
|
||||
long sss4;
|
||||
long sss5;
|
||||
long sss6;
|
||||
long sss7;
|
||||
};
|
||||
|
||||
# else /* CRAY2 */
|
||||
/* The following structure defines the vector of words
|
||||
returned by the STKSTAT library routine. */
|
||||
struct stk_stat
|
||||
{
|
||||
long now; /* Current total stack size. */
|
||||
long maxc; /* Amount of contiguous space which would
|
||||
be required to satisfy the maximum
|
||||
stack demand to date. */
|
||||
long high_water; /* Stack high-water mark. */
|
||||
long overflows; /* Number of stack overflow ($STKOFEN) calls. */
|
||||
long hits; /* Number of internal buffer hits. */
|
||||
long extends; /* Number of block extensions. */
|
||||
long stko_mallocs; /* Block allocations by $STKOFEN. */
|
||||
long underflows; /* Number of stack underflow calls ($STKRETN). */
|
||||
long stko_free; /* Number of deallocations by $STKRETN. */
|
||||
long stkm_free; /* Number of deallocations by $STKMRET. */
|
||||
long segments; /* Current number of stack segments. */
|
||||
long maxs; /* Maximum number of stack segments so far. */
|
||||
long pad_size; /* Stack pad size. */
|
||||
long current_address; /* Current stack segment address. */
|
||||
long current_size; /* Current stack segment size. This
|
||||
number is actually corrupted by STKSTAT to
|
||||
include the fifteen word trailer area. */
|
||||
long initial_address; /* Address of initial segment. */
|
||||
long initial_size; /* Size of initial segment. */
|
||||
};
|
||||
|
||||
/* The following structure describes the data structure which trails
|
||||
any stack segment. I think that the description in 'asdef' is
|
||||
out of date. I only describe the parts that I am sure about. */
|
||||
|
||||
struct stk_trailer
|
||||
{
|
||||
long this_address; /* Address of this block. */
|
||||
long this_size; /* Size of this block (does not include
|
||||
this trailer). */
|
||||
long unknown2;
|
||||
long unknown3;
|
||||
long link; /* Address of trailer block of previous
|
||||
segment. */
|
||||
long unknown5;
|
||||
long unknown6;
|
||||
long unknown7;
|
||||
long unknown8;
|
||||
long unknown9;
|
||||
long unknown10;
|
||||
long unknown11;
|
||||
long unknown12;
|
||||
long unknown13;
|
||||
long unknown14;
|
||||
};
|
||||
|
||||
# endif /* CRAY2 */
|
||||
# endif /* not CRAY_STACK */
|
||||
|
||||
# ifdef CRAY2
|
||||
/* Determine a "stack measure" for an arbitrary ADDRESS.
|
||||
I doubt that "lint" will like this much. */
|
||||
|
||||
static long
|
||||
i00afunc (long *address)
|
||||
{
|
||||
struct stk_stat status;
|
||||
struct stk_trailer *trailer;
|
||||
long *block, size;
|
||||
long result = 0;
|
||||
|
||||
/* We want to iterate through all of the segments. The first
|
||||
step is to get the stack status structure. We could do this
|
||||
more quickly and more directly, perhaps, by referencing the
|
||||
$LM00 common block, but I know that this works. */
|
||||
|
||||
STKSTAT (&status);
|
||||
|
||||
/* Set up the iteration. */
|
||||
|
||||
trailer = (struct stk_trailer *) (status.current_address
|
||||
+ status.current_size
|
||||
- 15);
|
||||
|
||||
/* There must be at least one stack segment. Therefore it is
|
||||
a fatal error if "trailer" is null. */
|
||||
|
||||
if (trailer == NULL)
|
||||
abort ();
|
||||
|
||||
/* Discard segments that do not contain our argument address. */
|
||||
|
||||
while (trailer != NULL)
|
||||
{
|
||||
block = (long *) trailer->this_address;
|
||||
size = trailer->this_size;
|
||||
if (block == NULL || size == 0)
|
||||
abort ();
|
||||
trailer = (struct stk_trailer *) trailer->link;
|
||||
if ((block <= address) && (address < (block + size)))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the result to the offset in this segment and add the sizes
|
||||
of all predecessor segments. */
|
||||
|
||||
result = address - block;
|
||||
|
||||
if (trailer == NULL)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (trailer->this_size <= 0)
|
||||
abort ();
|
||||
result += trailer->this_size;
|
||||
trailer = (struct stk_trailer *) trailer->link;
|
||||
}
|
||||
while (trailer != NULL);
|
||||
|
||||
/* We are done. Note that if you present a bogus address (one
|
||||
not in any segment), you will get a different number back, formed
|
||||
from subtracting the address of the first block. This is probably
|
||||
not what you want. */
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
# else /* not CRAY2 */
|
||||
/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
|
||||
Determine the number of the cell within the stack,
|
||||
given the address of the cell. The purpose of this
|
||||
routine is to linearize, in some sense, stack addresses
|
||||
for alloca. */
|
||||
|
||||
static long
|
||||
i00afunc (long address)
|
||||
{
|
||||
long stkl = 0;
|
||||
|
||||
long size, pseg, this_segment, stack;
|
||||
long result = 0;
|
||||
|
||||
struct stack_segment_linkage *ssptr;
|
||||
|
||||
/* Register B67 contains the address of the end of the
|
||||
current stack segment. If you (as a subprogram) store
|
||||
your registers on the stack and find that you are past
|
||||
the contents of B67, you have overflowed the segment.
|
||||
|
||||
B67 also points to the stack segment linkage control
|
||||
area, which is what we are really interested in. */
|
||||
|
||||
stkl = CRAY_STACKSEG_END ();
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
|
||||
/* If one subtracts 'size' from the end of the segment,
|
||||
one has the address of the first word of the segment.
|
||||
|
||||
If this is not the first segment, 'pseg' will be
|
||||
nonzero. */
|
||||
|
||||
pseg = ssptr->sspseg;
|
||||
size = ssptr->sssize;
|
||||
|
||||
this_segment = stkl - size;
|
||||
|
||||
/* It is possible that calling this routine itself caused
|
||||
a stack overflow. Discard stack segments which do not
|
||||
contain the target address. */
|
||||
|
||||
while (!(this_segment <= address && address <= stkl))
|
||||
{
|
||||
# ifdef DEBUG_I00AFUNC
|
||||
fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
|
||||
# endif
|
||||
if (pseg == 0)
|
||||
break;
|
||||
stkl = stkl - pseg;
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
size = ssptr->sssize;
|
||||
pseg = ssptr->sspseg;
|
||||
this_segment = stkl - size;
|
||||
}
|
||||
|
||||
result = address - this_segment;
|
||||
|
||||
/* If you subtract pseg from the current end of the stack,
|
||||
you get the address of the previous stack segment's end.
|
||||
This seems a little convoluted to me, but I'll bet you save
|
||||
a cycle somewhere. */
|
||||
|
||||
while (pseg != 0)
|
||||
{
|
||||
# ifdef DEBUG_I00AFUNC
|
||||
fprintf (stderr, "%011o %011o\n", pseg, size);
|
||||
# endif
|
||||
stkl = stkl - pseg;
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
size = ssptr->sssize;
|
||||
pseg = ssptr->sspseg;
|
||||
result += size;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
# endif /* not CRAY2 */
|
||||
# endif /* CRAY */
|
||||
|
||||
# endif /* no alloca */
|
||||
#endif /* not GCC 2 */
|
72
lib/alloca.h
Normal file
72
lib/alloca.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
|
||||
/* Memory allocation on the stack.
|
||||
|
||||
Copyright (C) 1995, 1999, 2001-2004, 2006-2020 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H
|
||||
means there is a real alloca function. */
|
||||
#ifndef _GL_ALLOCA_H
|
||||
#define _GL_ALLOCA_H
|
||||
|
||||
/* alloca (N) returns a pointer to N bytes of memory
|
||||
allocated on the stack, which will last until the function returns.
|
||||
Use of alloca should be avoided:
|
||||
- inside arguments of function calls - undefined behaviour,
|
||||
- in inline functions - the allocation may actually last until the
|
||||
calling function returns,
|
||||
- for huge N (say, N >= 65536) - you never know how large (or small)
|
||||
the stack is, and when the stack cannot fulfill the memory allocation
|
||||
request, the program just crashes.
|
||||
*/
|
||||
|
||||
#ifndef alloca
|
||||
# ifdef __GNUC__
|
||||
/* Some version of mingw have an <alloca.h> that causes trouble when
|
||||
included after 'alloca' gets defined as a macro. As a workaround, include
|
||||
this <alloca.h> first and define 'alloca' as a macro afterwards. */
|
||||
# if (defined _WIN32 && ! defined __CYGWIN__) && 1
|
||||
# include_next <alloca.h>
|
||||
# endif
|
||||
# define alloca __builtin_alloca
|
||||
# elif defined _AIX
|
||||
# define alloca __alloca
|
||||
# elif defined _MSC_VER
|
||||
# include <malloc.h>
|
||||
# define alloca _alloca
|
||||
# elif defined __DECC && defined __VMS
|
||||
# define alloca __ALLOCA
|
||||
# elif defined __TANDEM && defined _TNS_E_TARGET
|
||||
# ifdef __cplusplus
|
||||
extern "C"
|
||||
# endif
|
||||
void *_alloca (unsigned short);
|
||||
# pragma intrinsic (_alloca)
|
||||
# define alloca _alloca
|
||||
# elif defined __MVS__
|
||||
# include <stdlib.h>
|
||||
# else
|
||||
# include <stddef.h>
|
||||
# ifdef __cplusplus
|
||||
extern "C"
|
||||
# endif
|
||||
void *alloca (size_t);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _GL_ALLOCA_H */
|
71
lib/alloca.in.h
Normal file
71
lib/alloca.in.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* Memory allocation on the stack.
|
||||
|
||||
Copyright (C) 1995, 1999, 2001-2004, 2006-2020 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H
|
||||
means there is a real alloca function. */
|
||||
#ifndef _GL_ALLOCA_H
|
||||
#define _GL_ALLOCA_H
|
||||
|
||||
/* alloca (N) returns a pointer to N bytes of memory
|
||||
allocated on the stack, which will last until the function returns.
|
||||
Use of alloca should be avoided:
|
||||
- inside arguments of function calls - undefined behaviour,
|
||||
- in inline functions - the allocation may actually last until the
|
||||
calling function returns,
|
||||
- for huge N (say, N >= 65536) - you never know how large (or small)
|
||||
the stack is, and when the stack cannot fulfill the memory allocation
|
||||
request, the program just crashes.
|
||||
*/
|
||||
|
||||
#ifndef alloca
|
||||
# ifdef __GNUC__
|
||||
/* Some version of mingw have an <alloca.h> that causes trouble when
|
||||
included after 'alloca' gets defined as a macro. As a workaround, include
|
||||
this <alloca.h> first and define 'alloca' as a macro afterwards. */
|
||||
# if (defined _WIN32 && ! defined __CYGWIN__) && @HAVE_ALLOCA_H@
|
||||
# include_next <alloca.h>
|
||||
# endif
|
||||
# define alloca __builtin_alloca
|
||||
# elif defined _AIX
|
||||
# define alloca __alloca
|
||||
# elif defined _MSC_VER
|
||||
# include <malloc.h>
|
||||
# define alloca _alloca
|
||||
# elif defined __DECC && defined __VMS
|
||||
# define alloca __ALLOCA
|
||||
# elif defined __TANDEM && defined _TNS_E_TARGET
|
||||
# ifdef __cplusplus
|
||||
extern "C"
|
||||
# endif
|
||||
void *_alloca (unsigned short);
|
||||
# pragma intrinsic (_alloca)
|
||||
# define alloca _alloca
|
||||
# elif defined __MVS__
|
||||
# include <stdlib.h>
|
||||
# else
|
||||
# include <stddef.h>
|
||||
# ifdef __cplusplus
|
||||
extern "C"
|
||||
# endif
|
||||
void *alloca (size_t);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _GL_ALLOCA_H */
|
5
lib/allocator.c
Normal file
5
lib/allocator.c
Normal file
@ -0,0 +1,5 @@
|
||||
#define _GL_USE_STDLIB_ALLOC 1
|
||||
#include <config.h>
|
||||
#include "allocator.h"
|
||||
#include <stdlib.h>
|
||||
struct allocator const stdlib_allocator = { malloc, realloc, free, NULL };
|
58
lib/allocator.h
Normal file
58
lib/allocator.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* Memory allocators such as malloc+free.
|
||||
|
||||
Copyright (C) 2011-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#ifndef _GL_ALLOCATOR_H
|
||||
#define _GL_ALLOCATOR_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* An object describing a memory allocator family. */
|
||||
|
||||
struct allocator
|
||||
{
|
||||
/* Do not use GCC attributes such as __attribute__ ((malloc)) with
|
||||
the function types pointed at by these members, because these
|
||||
attributes do not work with pointers to functions. See
|
||||
<https://lists.gnu.org/r/bug-gnulib/2011-04/msg00007.html>. */
|
||||
|
||||
/* Call ALLOCATE to allocate memory, like 'malloc'. On failure ALLOCATE
|
||||
should return NULL, though not necessarily set errno. When given
|
||||
a zero size it may return NULL even if successful. */
|
||||
void *(*allocate) (size_t);
|
||||
|
||||
/* If nonnull, call REALLOCATE to reallocate memory, like 'realloc'.
|
||||
On failure REALLOCATE should return NULL, though not necessarily set
|
||||
errno. When given a zero size it may return NULL even if
|
||||
successful. */
|
||||
void *(*reallocate) (void *, size_t);
|
||||
|
||||
/* Call FREE to free memory, like 'free'. */
|
||||
void (*free) (void *);
|
||||
|
||||
/* If nonnull, call DIE (SIZE) if MALLOC (SIZE) or REALLOC (...,
|
||||
SIZE) fails. DIE should not return. SIZE should equal SIZE_MAX
|
||||
if size_t overflow was detected while calculating sizes to be
|
||||
passed to MALLOC or REALLOC. */
|
||||
void (*die) (size_t);
|
||||
};
|
||||
|
||||
/* An allocator using the stdlib functions and a null DIE function. */
|
||||
extern struct allocator const stdlib_allocator;
|
||||
|
||||
#endif /* _GL_ALLOCATOR_H */
|
57
lib/anytostr.c
Normal file
57
lib/anytostr.c
Normal file
@ -0,0 +1,57 @@
|
||||
/* anytostr.c -- convert integers to printable strings
|
||||
|
||||
Copyright (C) 2001, 2006, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert */
|
||||
|
||||
/* Tell gcc not to warn about the (i < 0) test, below. */
|
||||
#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#elif defined __clang__
|
||||
# pragma clang diagnostic ignored "-Wtautological-compare"
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "inttostr.h"
|
||||
|
||||
/* Convert I to a printable string in BUF, which must be at least
|
||||
INT_BUFSIZE_BOUND (INTTYPE) bytes long. Return the address of the
|
||||
printable string, which need not start at BUF. */
|
||||
|
||||
char * __attribute_warn_unused_result__
|
||||
anytostr (inttype i, char *buf)
|
||||
{
|
||||
char *p = buf + INT_STRLEN_BOUND (inttype);
|
||||
*p = 0;
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
do
|
||||
*--p = '0' - i % 10;
|
||||
while ((i /= 10) != 0);
|
||||
|
||||
*--p = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
*--p = '0' + i % 10;
|
||||
while ((i /= 10) != 0);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
128
lib/areadlink-with-size.c
Normal file
128
lib/areadlink-with-size.c
Normal file
@ -0,0 +1,128 @@
|
||||
/* readlink wrapper to return the link name in malloc'd storage.
|
||||
Unlike xreadlink and xreadlink_with_size, don't ever call exit.
|
||||
|
||||
Copyright (C) 2001, 2003-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering <jim@meyering.net> */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "areadlink.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
|
||||
#endif
|
||||
|
||||
/* SYMLINK_MAX is used only for an initial memory-allocation sanity
|
||||
check, so it's OK to guess too small on hosts where there is no
|
||||
arbitrary limit to symbolic link length. */
|
||||
#ifndef SYMLINK_MAX
|
||||
# define SYMLINK_MAX 1024
|
||||
#endif
|
||||
|
||||
#define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
|
||||
|
||||
/* Call readlink to get the symbolic link value of FILE.
|
||||
SIZE is a hint as to how long the link is expected to be;
|
||||
typically it is taken from st_size. It need not be correct.
|
||||
Return a pointer to that NUL-terminated string in malloc'd storage.
|
||||
If readlink fails, malloc fails, or if the link value is longer
|
||||
than SSIZE_MAX, return NULL (caller may use errno to diagnose). */
|
||||
|
||||
char *
|
||||
areadlink_with_size (char const *file, size_t size)
|
||||
{
|
||||
/* Some buggy file systems report garbage in st_size. Defend
|
||||
against them by ignoring outlandish st_size values in the initial
|
||||
memory allocation. */
|
||||
size_t symlink_max = SYMLINK_MAX;
|
||||
size_t INITIAL_LIMIT_BOUND = 8 * 1024;
|
||||
size_t initial_limit = (symlink_max < INITIAL_LIMIT_BOUND
|
||||
? symlink_max + 1
|
||||
: INITIAL_LIMIT_BOUND);
|
||||
|
||||
enum { stackbuf_size = 128 };
|
||||
|
||||
/* The initial buffer size for the link value. */
|
||||
size_t buf_size = (size == 0 ? stackbuf_size
|
||||
: size < initial_limit ? size + 1 : initial_limit);
|
||||
|
||||
while (1)
|
||||
{
|
||||
ssize_t r;
|
||||
size_t link_length;
|
||||
char stackbuf[stackbuf_size];
|
||||
char *buf = stackbuf;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (! (size == 0 && buf_size == stackbuf_size))
|
||||
{
|
||||
buf = buffer = malloc (buf_size);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = readlink (file, buf, buf_size);
|
||||
link_length = r;
|
||||
|
||||
/* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
|
||||
with errno == ERANGE if the buffer is too small. */
|
||||
if (r < 0 && errno != ERANGE)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
free (buffer);
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (link_length < buf_size)
|
||||
{
|
||||
buf[link_length] = 0;
|
||||
if (!buffer)
|
||||
{
|
||||
buffer = malloc (link_length + 1);
|
||||
if (buffer)
|
||||
return memcpy (buffer, buf, link_length + 1);
|
||||
}
|
||||
else if (link_length + 1 < buf_size)
|
||||
{
|
||||
/* Shrink BUFFER before returning it. */
|
||||
char *shrinked_buffer = realloc (buffer, link_length + 1);
|
||||
if (shrinked_buffer != NULL)
|
||||
buffer = shrinked_buffer;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
free (buffer);
|
||||
if (buf_size <= MAXSIZE / 2)
|
||||
buf_size *= 2;
|
||||
else if (buf_size < MAXSIZE)
|
||||
buf_size = MAXSIZE;
|
||||
else
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
56
lib/areadlink.c
Normal file
56
lib/areadlink.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* areadlink.c -- readlink wrapper to return the link name in malloc'd storage
|
||||
Unlike xreadlink and xreadlink_with_size, don't ever call exit.
|
||||
|
||||
Copyright (C) 2001, 2003-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering <jim@meyering.net>
|
||||
and Bruno Haible <bruno@clisp.org>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "areadlink.h"
|
||||
|
||||
#include "careadlinkat.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Get the symbolic link value of FILENAME and put it into BUFFER, with
|
||||
size BUFFER_SIZE. This function acts like readlink but has
|
||||
readlinkat's signature. */
|
||||
static ssize_t
|
||||
careadlinkatcwd (int fd, char const *filename, char *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
/* FD must be AT_FDCWD here, otherwise the caller is using this
|
||||
function in contexts it was not meant for. */
|
||||
if (fd != AT_FDCWD)
|
||||
abort ();
|
||||
return readlink (filename, buffer, buffer_size);
|
||||
}
|
||||
|
||||
/* Call readlink to get the symbolic link value of FILENAME.
|
||||
Return a pointer to that NUL-terminated string in malloc'd storage.
|
||||
If readlink fails, return NULL and set errno.
|
||||
If allocation fails, or if the link value is longer than SIZE_MAX :-),
|
||||
return NULL and set errno to ENOMEM. */
|
||||
|
||||
char *
|
||||
areadlink (char const *filename)
|
||||
{
|
||||
return careadlinkat (AT_FDCWD, filename, NULL, 0, NULL, careadlinkatcwd);
|
||||
}
|
33
lib/areadlink.h
Normal file
33
lib/areadlink.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Read symbolic links without size limitation.
|
||||
|
||||
Copyright (C) 2001, 2003-2004, 2007, 2009-2020 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering <jim@meyering.net> */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern char *areadlink (char const *filename);
|
||||
extern char *areadlink_with_size (char const *filename, size_t size_hint);
|
||||
|
||||
#if GNULIB_AREADLINKAT
|
||||
extern char *areadlinkat (int fd, char const *filename);
|
||||
#endif
|
||||
|
||||
#if GNULIB_AREADLINKAT_WITH_SIZE
|
||||
extern char *areadlinkat_with_size (int fd, char const *filename,
|
||||
size_t size_hint);
|
||||
#endif
|
67
lib/areadlinkat.c
Normal file
67
lib/areadlinkat.c
Normal file
@ -0,0 +1,67 @@
|
||||
/* areadlinkat.c -- readlinkat wrapper to return malloc'd link name
|
||||
Unlike xreadlinkat, only call exit on failure to change directory.
|
||||
|
||||
Copyright (C) 2001, 2003-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering <jim@meyering.net>,
|
||||
and Bruno Haible <bruno@clisp.org>,
|
||||
and Eric Blake <ebb9@byu.net>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "areadlink.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "careadlinkat.h"
|
||||
|
||||
#if HAVE_READLINKAT
|
||||
|
||||
/* Call readlinkat to get the symbolic link value of FILENAME relative to FD.
|
||||
Return a pointer to that NUL-terminated string in malloc'd storage.
|
||||
If readlinkat fails, return NULL and set errno (although failure to
|
||||
change directory will issue a diagnostic and exit).
|
||||
If allocation fails, or if the link value is longer than SIZE_MAX :-),
|
||||
return NULL and set errno to ENOMEM. */
|
||||
|
||||
char *
|
||||
areadlinkat (int fd, char const *filename)
|
||||
{
|
||||
return careadlinkat (fd, filename, NULL, 0, NULL, readlinkat);
|
||||
}
|
||||
|
||||
#else /* !HAVE_READLINKAT */
|
||||
|
||||
/* It is more efficient to change directories only once and call
|
||||
areadlink, rather than repeatedly call the replacement
|
||||
readlinkat. */
|
||||
|
||||
# define AT_FUNC_NAME areadlinkat
|
||||
# define AT_FUNC_F1 areadlink
|
||||
# define AT_FUNC_POST_FILE_PARAM_DECLS /* empty */
|
||||
# define AT_FUNC_POST_FILE_ARGS /* empty */
|
||||
# define AT_FUNC_RESULT char *
|
||||
# define AT_FUNC_FAIL NULL
|
||||
# include "at-func.c"
|
||||
# undef AT_FUNC_NAME
|
||||
# undef AT_FUNC_F1
|
||||
# undef AT_FUNC_POST_FILE_PARAM_DECLS
|
||||
# undef AT_FUNC_POST_FILE_ARGS
|
||||
# undef AT_FUNC_RESULT
|
||||
# undef AT_FUNC_FAIL
|
||||
|
||||
#endif /* !HAVE_READLINKAT */
|
26
lib/arg-nonnull.h
Normal file
26
lib/arg-nonnull.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* A C macro for declaring that specific arguments must not be NULL.
|
||||
Copyright (C) 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* _GL_ARG_NONNULL((n,...,m)) tells the compiler and static analyzer tools
|
||||
that the values passed as arguments n, ..., m must be non-NULL pointers.
|
||||
n = 1 stands for the first argument, n = 2 for the second argument etc. */
|
||||
#ifndef _GL_ARG_NONNULL
|
||||
# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ > 3
|
||||
# define _GL_ARG_NONNULL(params) __attribute__ ((__nonnull__ params))
|
||||
# else
|
||||
# define _GL_ARG_NONNULL(params)
|
||||
# endif
|
||||
#endif
|
273
lib/argmatch.c
Normal file
273
lib/argmatch.c
Normal file
@ -0,0 +1,273 @@
|
||||
/* argmatch.c -- find a match for a string in an array
|
||||
|
||||
Copyright (C) 1990, 1998-1999, 2001-2007, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by David MacKenzie <djm@ai.mit.edu>
|
||||
Modified by Akim Demaille <demaille@inf.enst.fr> */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "argmatch.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define _(msgid) gettext (msgid)
|
||||
|
||||
#include "error.h"
|
||||
#include "quotearg.h"
|
||||
#include "getprogname.h"
|
||||
|
||||
#if USE_UNLOCKED_IO
|
||||
# include "unlocked-io.h"
|
||||
#endif
|
||||
|
||||
/* When reporting an invalid argument, show nonprinting characters
|
||||
by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use
|
||||
literal_quoting_style. */
|
||||
#ifndef ARGMATCH_QUOTING_STYLE
|
||||
# define ARGMATCH_QUOTING_STYLE locale_quoting_style
|
||||
#endif
|
||||
|
||||
/* Non failing version of argmatch call this function after failing. */
|
||||
#ifndef ARGMATCH_DIE
|
||||
# include "exitfail.h"
|
||||
# define ARGMATCH_DIE exit (exit_failure)
|
||||
#endif
|
||||
|
||||
#ifdef ARGMATCH_DIE_DECL
|
||||
ARGMATCH_DIE_DECL;
|
||||
#endif
|
||||
|
||||
static void
|
||||
__argmatch_die (void)
|
||||
{
|
||||
ARGMATCH_DIE;
|
||||
}
|
||||
|
||||
/* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h.
|
||||
Default to __argmatch_die, but allow caller to change this at run-time. */
|
||||
argmatch_exit_fn argmatch_die = __argmatch_die;
|
||||
|
||||
|
||||
/* If ARG is an unambiguous match for an element of the
|
||||
NULL-terminated array ARGLIST, return the index in ARGLIST
|
||||
of the matched element, else -1 if it does not match any element
|
||||
or -2 if it is ambiguous (is a prefix of more than one element).
|
||||
|
||||
If VALLIST is none null, use it to resolve ambiguities limited to
|
||||
synonyms, i.e., for
|
||||
"yes", "yop" -> 0
|
||||
"no", "nope" -> 1
|
||||
"y" is a valid argument, for 0, and "n" for 1. */
|
||||
|
||||
ptrdiff_t
|
||||
argmatch (const char *arg, const char *const *arglist,
|
||||
const void *vallist, size_t valsize)
|
||||
{
|
||||
size_t i; /* Temporary index in ARGLIST. */
|
||||
size_t arglen; /* Length of ARG. */
|
||||
ptrdiff_t matchind = -1; /* Index of first nonexact match. */
|
||||
bool ambiguous = false; /* If true, multiple nonexact match(es). */
|
||||
|
||||
arglen = strlen (arg);
|
||||
|
||||
/* Test all elements for either exact match or abbreviated matches. */
|
||||
for (i = 0; arglist[i]; i++)
|
||||
{
|
||||
if (!strncmp (arglist[i], arg, arglen))
|
||||
{
|
||||
if (strlen (arglist[i]) == arglen)
|
||||
/* Exact match found. */
|
||||
return i;
|
||||
else if (matchind == -1)
|
||||
/* First nonexact match found. */
|
||||
matchind = i;
|
||||
else
|
||||
{
|
||||
/* Second nonexact match found. */
|
||||
if (vallist == NULL
|
||||
|| memcmp ((char const *) vallist + valsize * matchind,
|
||||
(char const *) vallist + valsize * i, valsize))
|
||||
{
|
||||
/* There is a real ambiguity, or we could not
|
||||
disambiguate. */
|
||||
ambiguous = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ambiguous)
|
||||
return -2;
|
||||
else
|
||||
return matchind;
|
||||
}
|
||||
|
||||
/* Error reporting for argmatch.
|
||||
CONTEXT is a description of the type of entity that was being matched.
|
||||
VALUE is the invalid value that was given.
|
||||
PROBLEM is the return value from argmatch. */
|
||||
|
||||
void
|
||||
argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
|
||||
{
|
||||
char const *format = (problem == -1
|
||||
? _("invalid argument %s for %s")
|
||||
: _("ambiguous argument %s for %s"));
|
||||
|
||||
error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
|
||||
quote_n (1, context));
|
||||
}
|
||||
|
||||
/* List the valid arguments for argmatch.
|
||||
ARGLIST is the same as in argmatch.
|
||||
VALLIST is a pointer to an array of values.
|
||||
VALSIZE is the size of the elements of VALLIST */
|
||||
void
|
||||
argmatch_valid (const char *const *arglist,
|
||||
const void *vallist, size_t valsize)
|
||||
{
|
||||
size_t i;
|
||||
const char *last_val = NULL;
|
||||
|
||||
/* We try to put synonyms on the same line. The assumption is that
|
||||
synonyms follow each other */
|
||||
fputs (_("Valid arguments are:"), stderr);
|
||||
for (i = 0; arglist[i]; i++)
|
||||
if ((i == 0)
|
||||
|| memcmp (last_val, (char const *) vallist + valsize * i, valsize))
|
||||
{
|
||||
fprintf (stderr, "\n - %s", quote (arglist[i]));
|
||||
last_val = (char const *) vallist + valsize * i;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, ", %s", quote (arglist[i]));
|
||||
}
|
||||
putc ('\n', stderr);
|
||||
}
|
||||
|
||||
/* Never failing versions of the previous functions.
|
||||
|
||||
CONTEXT is the context for which argmatch is called (e.g.,
|
||||
"--version-control", or "$VERSION_CONTROL" etc.). Upon failure,
|
||||
calls the (supposed never to return) function EXIT_FN. */
|
||||
|
||||
ptrdiff_t
|
||||
__xargmatch_internal (const char *context,
|
||||
const char *arg, const char *const *arglist,
|
||||
const void *vallist, size_t valsize,
|
||||
argmatch_exit_fn exit_fn)
|
||||
{
|
||||
ptrdiff_t res = argmatch (arg, arglist, vallist, valsize);
|
||||
if (res >= 0)
|
||||
/* Success. */
|
||||
return res;
|
||||
|
||||
/* We failed. Explain why. */
|
||||
argmatch_invalid (context, arg, res);
|
||||
argmatch_valid (arglist, vallist, valsize);
|
||||
(*exit_fn) ();
|
||||
|
||||
return -1; /* To please the compilers. */
|
||||
}
|
||||
|
||||
/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
|
||||
return the first corresponding argument in ARGLIST */
|
||||
const char *
|
||||
argmatch_to_argument (const void *value,
|
||||
const char *const *arglist,
|
||||
const void *vallist, size_t valsize)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; arglist[i]; i++)
|
||||
if (!memcmp (value, (char const *) vallist + valsize * i, valsize))
|
||||
return arglist[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
/*
|
||||
* Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
|
||||
*/
|
||||
|
||||
/* When to make backup files. */
|
||||
enum backup_type
|
||||
{
|
||||
/* Never make backups. */
|
||||
no_backups,
|
||||
|
||||
/* Make simple backups of every file. */
|
||||
simple_backups,
|
||||
|
||||
/* Make numbered backups of files that already have numbered backups,
|
||||
and simple backups of the others. */
|
||||
numbered_existing_backups,
|
||||
|
||||
/* Make numbered backups of every file. */
|
||||
numbered_backups
|
||||
};
|
||||
|
||||
/* Two tables describing arguments (keys) and their corresponding
|
||||
values */
|
||||
static const char *const backup_args[] =
|
||||
{
|
||||
"no", "none", "off",
|
||||
"simple", "never",
|
||||
"existing", "nil",
|
||||
"numbered", "t",
|
||||
0
|
||||
};
|
||||
|
||||
static const enum backup_type backup_vals[] =
|
||||
{
|
||||
no_backups, no_backups, no_backups,
|
||||
simple_backups, simple_backups,
|
||||
numbered_existing_backups, numbered_existing_backups,
|
||||
numbered_backups, numbered_backups
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, const char *const *argv)
|
||||
{
|
||||
const char *cp;
|
||||
enum backup_type backup_type = no_backups;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", getprogname ());
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if ((cp = getenv ("VERSION_CONTROL")))
|
||||
backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
|
||||
backup_args, backup_vals);
|
||||
|
||||
if (argc == 2)
|
||||
backup_type = XARGMATCH (getprogname (), argv[1],
|
||||
backup_args, backup_vals);
|
||||
|
||||
printf ("The version control is '%s'\n",
|
||||
ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
331
lib/argmatch.h
Normal file
331
lib/argmatch.h
Normal file
@ -0,0 +1,331 @@
|
||||
/* argmatch.h -- definitions and prototypes for argmatch.c
|
||||
|
||||
Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by David MacKenzie <djm@ai.mit.edu>
|
||||
Modified by Akim Demaille <demaille@inf.enst.fr> */
|
||||
|
||||
#ifndef ARGMATCH_H_
|
||||
# define ARGMATCH_H_ 1
|
||||
|
||||
# include <limits.h>
|
||||
# include <stdbool.h>
|
||||
# include <stddef.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h> /* memcmp */
|
||||
|
||||
# include "gettext.h"
|
||||
# include "quote.h"
|
||||
# include "verify.h"
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
|
||||
|
||||
/* Assert there are as many real arguments as there are values
|
||||
(argument list ends with a NULL guard). */
|
||||
|
||||
# define ARGMATCH_VERIFY(Arglist, Vallist) \
|
||||
verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)
|
||||
|
||||
/* Return the index of the element of ARGLIST (NULL terminated) that
|
||||
matches with ARG. If VALLIST is not NULL, then use it to resolve
|
||||
false ambiguities (i.e., different matches of ARG but corresponding
|
||||
to the same values in VALLIST). */
|
||||
|
||||
ptrdiff_t argmatch (char const *arg, char const *const *arglist,
|
||||
void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE;
|
||||
|
||||
# define ARGMATCH(Arg, Arglist, Vallist) \
|
||||
argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
|
||||
|
||||
/* xargmatch calls this function when it fails. This function should not
|
||||
return. By default, this is a function that calls ARGMATCH_DIE which
|
||||
in turn defaults to 'exit (exit_failure)'. */
|
||||
typedef void (*argmatch_exit_fn) (void);
|
||||
extern argmatch_exit_fn argmatch_die;
|
||||
|
||||
/* Report on stderr why argmatch failed. Report correct values. */
|
||||
|
||||
void argmatch_invalid (char const *context, char const *value,
|
||||
ptrdiff_t problem);
|
||||
|
||||
/* Left for compatibility with the old name invalid_arg */
|
||||
|
||||
# define invalid_arg(Context, Value, Problem) \
|
||||
argmatch_invalid (Context, Value, Problem)
|
||||
|
||||
|
||||
|
||||
/* Report on stderr the list of possible arguments. */
|
||||
|
||||
void argmatch_valid (char const *const *arglist,
|
||||
void const *vallist, size_t valsize);
|
||||
|
||||
# define ARGMATCH_VALID(Arglist, Vallist) \
|
||||
argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
|
||||
|
||||
|
||||
|
||||
/* Same as argmatch, but upon failure, report an explanation of the
|
||||
failure, and exit using the function EXIT_FN. */
|
||||
|
||||
ptrdiff_t __xargmatch_internal (char const *context,
|
||||
char const *arg, char const *const *arglist,
|
||||
void const *vallist, size_t valsize,
|
||||
argmatch_exit_fn exit_fn);
|
||||
|
||||
/* Programmer friendly interface to __xargmatch_internal. */
|
||||
|
||||
# define XARGMATCH(Context, Arg, Arglist, Vallist) \
|
||||
((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
|
||||
(void const *) (Vallist), \
|
||||
sizeof *(Vallist), \
|
||||
argmatch_die)])
|
||||
|
||||
/* Convert a value into a corresponding argument. */
|
||||
|
||||
char const *argmatch_to_argument (void const *value,
|
||||
char const *const *arglist,
|
||||
void const *vallist, size_t valsize)
|
||||
_GL_ATTRIBUTE_PURE;
|
||||
|
||||
# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \
|
||||
argmatch_to_argument (Value, Arglist, \
|
||||
(void const *) (Vallist), sizeof *(Vallist))
|
||||
|
||||
# define ARGMATCH_DEFINE_GROUP(Name, Type) \
|
||||
/* The type of the values of this group. */ \
|
||||
typedef Type argmatch_##Name##_type; \
|
||||
\
|
||||
/* The size of the type of the values of this group. */ \
|
||||
enum argmatch_##Name##_size_enum \
|
||||
{ \
|
||||
argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \
|
||||
}; \
|
||||
\
|
||||
/* Argument mapping of this group. */ \
|
||||
typedef struct \
|
||||
{ \
|
||||
/* Argument (e.g., "simple"). */ \
|
||||
const char *arg; \
|
||||
/* Value (e.g., simple_backups). */ \
|
||||
const argmatch_##Name##_type val; \
|
||||
} argmatch_##Name##_arg; \
|
||||
\
|
||||
/* Documentation of this group. */ \
|
||||
typedef struct \
|
||||
{ \
|
||||
/* Argument (e.g., "simple"). */ \
|
||||
const char *arg; \
|
||||
/* Documentation (e.g., N_("always make simple backups")). */ \
|
||||
const char *doc; \
|
||||
} argmatch_##Name##_doc; \
|
||||
\
|
||||
/* All the features of an argmatch group. */ \
|
||||
typedef struct \
|
||||
{ \
|
||||
const argmatch_##Name##_arg* args; \
|
||||
const argmatch_##Name##_doc* docs; \
|
||||
\
|
||||
/* Printed before the usage message. */ \
|
||||
const char *doc_pre; \
|
||||
/* Printed after the usage message. */ \
|
||||
const char *doc_post; \
|
||||
} argmatch_##Name##_group_type; \
|
||||
\
|
||||
/* The structure the user must build. */ \
|
||||
extern const argmatch_##Name##_group_type argmatch_##Name##_group; \
|
||||
\
|
||||
/* Print the documentation of this group. */ \
|
||||
void argmatch_##Name##_usage (FILE *out); \
|
||||
\
|
||||
/* If nonnegative, the index I of ARG in ARGS, i.e, \
|
||||
ARGS[I] == ARG. \
|
||||
Return -1 for invalid argument, -2 for ambiguous argument. */ \
|
||||
ptrdiff_t argmatch_##Name##_choice (const char *arg); \
|
||||
\
|
||||
/* A pointer to the corresponding value if it exists, or \
|
||||
report an error and exit with failure if the argument was \
|
||||
not recognized. */ \
|
||||
const argmatch_##Name##_type* \
|
||||
argmatch_##Name##_value (const char *context, const char *arg); \
|
||||
\
|
||||
/* The first argument in ARGS that matches this value, or NULL. */ \
|
||||
const char * \
|
||||
argmatch_##Name##_argument (const argmatch_##Name##_type *val); \
|
||||
\
|
||||
ptrdiff_t \
|
||||
argmatch_##Name##_choice (const char *arg) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
size_t size = argmatch_##Name##_size; \
|
||||
ptrdiff_t res = -1; /* Index of first nonexact match. */ \
|
||||
bool ambiguous = false; /* Whether multiple nonexact match(es). */ \
|
||||
size_t arglen = strlen (arg); \
|
||||
\
|
||||
/* Test all elements for either exact match or abbreviated \
|
||||
matches. */ \
|
||||
for (size_t i = 0; g->args[i].arg; i++) \
|
||||
if (!strncmp (g->args[i].arg, arg, arglen)) \
|
||||
{ \
|
||||
if (strlen (g->args[i].arg) == arglen) \
|
||||
/* Exact match found. */ \
|
||||
return i; \
|
||||
else if (res == -1) \
|
||||
/* First nonexact match found. */ \
|
||||
res = i; \
|
||||
else if (memcmp (&g->args[res].val, &g->args[i].val, size)) \
|
||||
/* Second nonexact match found. */ \
|
||||
/* There is a real ambiguity, or we could not \
|
||||
disambiguate. */ \
|
||||
ambiguous = true; \
|
||||
} \
|
||||
return ambiguous ? -2 : res; \
|
||||
} \
|
||||
\
|
||||
const char * \
|
||||
argmatch_##Name##_argument (const argmatch_##Name##_type *val) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
size_t size = argmatch_##Name##_size; \
|
||||
for (size_t i = 0; g->args[i].arg; i++) \
|
||||
if (!memcmp (val, &g->args[i].val, size)) \
|
||||
return g->args[i].arg; \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
/* List the valid values of this group. */ \
|
||||
static void \
|
||||
argmatch_##Name##_valid (FILE *out) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
size_t size = argmatch_##Name##_size; \
|
||||
\
|
||||
/* Try to put synonyms on the same line. Synonyms are expected \
|
||||
to follow each other. */ \
|
||||
fputs (gettext ("Valid arguments are:"), out); \
|
||||
for (int i = 0; g->args[i].arg; i++) \
|
||||
if (i == 0 \
|
||||
|| memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
|
||||
fprintf (out, "\n - %s", quote (g->args[i].arg)); \
|
||||
else \
|
||||
fprintf (out, ", %s", quote (g->args[i].arg)); \
|
||||
putc ('\n', out); \
|
||||
} \
|
||||
\
|
||||
const argmatch_##Name##_type* \
|
||||
argmatch_##Name##_value (const char *context, const char *arg) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
ptrdiff_t res = argmatch_##Name##_choice (arg); \
|
||||
if (res < 0) \
|
||||
{ \
|
||||
argmatch_invalid (context, arg, res); \
|
||||
argmatch_##Name##_valid (stderr); \
|
||||
argmatch_die (); \
|
||||
} \
|
||||
return &g->args[res].val; \
|
||||
} \
|
||||
\
|
||||
/* The column in which the documentation is displayed. \
|
||||
The leftmost possible, but no more than 20. */ \
|
||||
static int \
|
||||
argmatch_##Name##_doc_col (void) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
size_t size = argmatch_##Name##_size; \
|
||||
int res = 0; \
|
||||
for (int i = 0; g->docs[i].arg; ++i) \
|
||||
{ \
|
||||
int col = 4; \
|
||||
int ival = argmatch_##Name##_choice (g->docs[i].arg); \
|
||||
if (ival < 0) \
|
||||
/* Pseudo argument, display it. */ \
|
||||
col += strlen (g->docs[i].arg); \
|
||||
else \
|
||||
/* Genuine argument, display it with its synonyms. */ \
|
||||
for (int j = 0; g->args[j].arg; ++j) \
|
||||
if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
|
||||
col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg); \
|
||||
if (res <= col) \
|
||||
res = col <= 20 ? col : 20; \
|
||||
} \
|
||||
return res ? res : 20; \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
argmatch_##Name##_usage (FILE *out) \
|
||||
{ \
|
||||
const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
|
||||
size_t size = argmatch_##Name##_size; \
|
||||
/* Width of the screen. Help2man does not seem to support \
|
||||
arguments on several lines, so in that case pretend a very \
|
||||
large width. */ \
|
||||
const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
|
||||
if (g->doc_pre) \
|
||||
fprintf (out, "%s\n", gettext (g->doc_pre)); \
|
||||
int doc_col = argmatch_##Name##_doc_col (); \
|
||||
for (int i = 0; g->docs[i].arg; ++i) \
|
||||
{ \
|
||||
int col = 0; \
|
||||
bool first = true; \
|
||||
int ival = argmatch_##Name##_choice (g->docs[i].arg); \
|
||||
if (ival < 0) \
|
||||
/* Pseudo argument, display it. */ \
|
||||
col += fprintf (out, " %s", g->docs[i].arg); \
|
||||
else \
|
||||
/* Genuine argument, display it with its synonyms. */ \
|
||||
for (int j = 0; g->args[j].arg; ++j) \
|
||||
if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
|
||||
{ \
|
||||
if (!first \
|
||||
&& screen_width < col + 2 + strlen (g->args[j].arg)) \
|
||||
{ \
|
||||
fprintf (out, ",\n"); \
|
||||
col = 0; \
|
||||
first = true; \
|
||||
} \
|
||||
if (first) \
|
||||
{ \
|
||||
col += fprintf (out, " "); \
|
||||
first = false; \
|
||||
} \
|
||||
else \
|
||||
col += fprintf (out, ","); \
|
||||
col += fprintf (out, " %s", g->args[j].arg); \
|
||||
} \
|
||||
/* The doc. Separated by at least two spaces. */ \
|
||||
if (doc_col < col + 2) \
|
||||
{ \
|
||||
fprintf (out, "\n"); \
|
||||
col = 0; \
|
||||
} \
|
||||
fprintf (out, "%*s%s\n", \
|
||||
doc_col - col, "", gettext (g->docs[i].doc)); \
|
||||
} \
|
||||
if (g->doc_post) \
|
||||
fprintf (out, "%s\n", gettext (g->doc_post)); \
|
||||
}
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif /* ARGMATCH_H_ */
|
111
lib/argv-iter.c
Normal file
111
lib/argv-iter.c
Normal file
@ -0,0 +1,111 @@
|
||||
/* Iterate over arguments from argv or --files0-from=FILE
|
||||
Copyright (C) 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering. */
|
||||
|
||||
#include <config.h>
|
||||
#include "argv-iter.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct argv_iterator
|
||||
{
|
||||
/* Test FP to determine whether in read-mode or argv-mode. */
|
||||
/* file-mode: fp records position */
|
||||
FILE *fp;
|
||||
size_t item_idx;
|
||||
char *tok;
|
||||
size_t buf_len;
|
||||
|
||||
/* argv-mode: record just argv and current pointer */
|
||||
char **arg_list;
|
||||
char **p;
|
||||
};
|
||||
|
||||
struct argv_iterator *
|
||||
argv_iter_init_argv (char **argv)
|
||||
{
|
||||
struct argv_iterator *ai = malloc (sizeof *ai);
|
||||
if (!ai)
|
||||
return NULL;
|
||||
ai->fp = NULL;
|
||||
ai->arg_list = argv;
|
||||
ai->p = argv;
|
||||
return ai;
|
||||
}
|
||||
|
||||
/* Initialize to read from the stream, FP.
|
||||
The input is expected to contain a list of NUL-delimited tokens. */
|
||||
struct argv_iterator *
|
||||
argv_iter_init_stream (FILE *fp)
|
||||
{
|
||||
struct argv_iterator *ai = malloc (sizeof *ai);
|
||||
if (!ai)
|
||||
return NULL;
|
||||
ai->fp = fp;
|
||||
ai->tok = NULL;
|
||||
ai->buf_len = 0;
|
||||
|
||||
ai->item_idx = 0;
|
||||
ai->arg_list = NULL;
|
||||
return ai;
|
||||
}
|
||||
|
||||
char *
|
||||
argv_iter (struct argv_iterator *ai, enum argv_iter_err *err)
|
||||
{
|
||||
if (ai->fp)
|
||||
{
|
||||
ssize_t len = getdelim (&ai->tok, &ai->buf_len, '\0', ai->fp);
|
||||
if (len < 0)
|
||||
{
|
||||
*err = feof (ai->fp) ? AI_ERR_EOF : AI_ERR_READ;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*err = AI_ERR_OK;
|
||||
ai->item_idx++;
|
||||
return ai->tok;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*(ai->p) == NULL)
|
||||
{
|
||||
*err = AI_ERR_EOF;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = AI_ERR_OK;
|
||||
return *(ai->p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
argv_iter_n_args (struct argv_iterator const *ai)
|
||||
{
|
||||
return ai->fp ? ai->item_idx : ai->p - ai->arg_list;
|
||||
}
|
||||
|
||||
void
|
||||
argv_iter_free (struct argv_iterator *ai)
|
||||
{
|
||||
if (ai->fp)
|
||||
free (ai->tok);
|
||||
free (ai);
|
||||
}
|
42
lib/argv-iter.h
Normal file
42
lib/argv-iter.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* Iterate over arguments from argv or --files0-from=FILE
|
||||
Copyright (C) 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Definition of _GL_ARG_NONNULL. */
|
||||
#include "arg-nonnull.h"
|
||||
|
||||
struct argv_iterator;
|
||||
|
||||
enum argv_iter_err
|
||||
{
|
||||
AI_ERR_OK = 1,
|
||||
AI_ERR_EOF,
|
||||
AI_ERR_MEM,
|
||||
AI_ERR_READ
|
||||
};
|
||||
|
||||
struct argv_iterator *argv_iter_init_argv (char **argv)
|
||||
_GL_ARG_NONNULL ((1));
|
||||
struct argv_iterator *argv_iter_init_stream (FILE *fp)
|
||||
_GL_ARG_NONNULL ((1));
|
||||
char *argv_iter (struct argv_iterator *, enum argv_iter_err *)
|
||||
_GL_ARG_NONNULL ((1, 2));
|
||||
size_t argv_iter_n_args (struct argv_iterator const *)
|
||||
_GL_ATTRIBUTE_PURE _GL_ARG_NONNULL ((1));
|
||||
void argv_iter_free (struct argv_iterator *)
|
||||
_GL_ARG_NONNULL ((1));
|
621
lib/arpa/inet.h
Normal file
621
lib/arpa/inet.h
Normal file
@ -0,0 +1,621 @@
|
||||
/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
|
||||
/* A GNU-like <arpa/inet.h>.
|
||||
|
||||
Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _GL_ARPA_INET_H
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
|
||||
#if 1
|
||||
# include <features.h> /* for __GLIBC__ */
|
||||
#endif
|
||||
|
||||
/* Gnulib's sys/socket.h is responsible for defining socklen_t (used below) and
|
||||
for pulling in winsock2.h etc. under MinGW.
|
||||
But avoid namespace pollution on glibc systems. */
|
||||
#ifndef __GLIBC__
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* On NonStop Kernel, inet_ntop and inet_pton are declared in <netdb.h>.
|
||||
But avoid namespace pollution on glibc systems. */
|
||||
#if defined __TANDEM && !defined __GLIBC__
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
|
||||
/* The include_next requires a split double-inclusion guard. */
|
||||
# include_next <arpa/inet.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef _GL_ARPA_INET_H
|
||||
#define _GL_ARPA_INET_H
|
||||
|
||||
/* Get all possible declarations of inet_ntop() and inet_pton(). */
|
||||
#if (1 || IN_COREUTILS_GNULIB_TESTS || defined GNULIB_POSIXCHECK) \
|
||||
&& 0
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
|
||||
/* C++ compatible function declaration macros.
|
||||
Copyright (C) 2010-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _GL_CXXDEFS_H
|
||||
#define _GL_CXXDEFS_H
|
||||
|
||||
/* Begin/end the GNULIB_NAMESPACE namespace. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_BEGIN_NAMESPACE namespace GNULIB_NAMESPACE {
|
||||
# define _GL_END_NAMESPACE }
|
||||
#else
|
||||
# define _GL_BEGIN_NAMESPACE
|
||||
# define _GL_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
/* The three most frequent use cases of these macros are:
|
||||
|
||||
* For providing a substitute for a function that is missing on some
|
||||
platforms, but is declared and works fine on the platforms on which
|
||||
it exists:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if !@HAVE_FOO@
|
||||
_GL_FUNCDECL_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
|
||||
* For providing a replacement for a function that exists on all platforms,
|
||||
but is broken/insufficient and needs to be replaced on some platforms:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if @REPLACE_FOO@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef foo
|
||||
# define foo rpl_foo
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (foo, ...);
|
||||
_GL_CXXALIAS_RPL (foo, ...);
|
||||
# else
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
|
||||
* For providing a replacement for a function that exists on some platforms
|
||||
but is broken/insufficient and needs to be replaced on some of them and
|
||||
is additionally either missing or undeclared on some other platforms:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if @REPLACE_FOO@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef foo
|
||||
# define foo rpl_foo
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (foo, ...);
|
||||
_GL_CXXALIAS_RPL (foo, ...);
|
||||
# else
|
||||
# if !@HAVE_FOO@ or if !@HAVE_DECL_FOO@
|
||||
_GL_FUNCDECL_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
*/
|
||||
|
||||
/* _GL_EXTERN_C declaration;
|
||||
performs the declaration with C linkage. */
|
||||
#if defined __cplusplus
|
||||
# define _GL_EXTERN_C extern "C"
|
||||
#else
|
||||
# define _GL_EXTERN_C extern
|
||||
#endif
|
||||
|
||||
/* _GL_FUNCDECL_RPL (func, rettype, parameters_and_attributes);
|
||||
declares a replacement function, named rpl_func, with the given prototype,
|
||||
consisting of return type, parameters, and attributes.
|
||||
Example:
|
||||
_GL_FUNCDECL_RPL (open, int, (const char *filename, int flags, ...)
|
||||
_GL_ARG_NONNULL ((1)));
|
||||
*/
|
||||
#define _GL_FUNCDECL_RPL(func,rettype,parameters_and_attributes) \
|
||||
_GL_FUNCDECL_RPL_1 (rpl_##func, rettype, parameters_and_attributes)
|
||||
#define _GL_FUNCDECL_RPL_1(rpl_func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C rettype rpl_func parameters_and_attributes
|
||||
|
||||
/* _GL_FUNCDECL_SYS (func, rettype, parameters_and_attributes);
|
||||
declares the system function, named func, with the given prototype,
|
||||
consisting of return type, parameters, and attributes.
|
||||
Example:
|
||||
_GL_FUNCDECL_SYS (open, int, (const char *filename, int flags, ...)
|
||||
_GL_ARG_NONNULL ((1)));
|
||||
*/
|
||||
#define _GL_FUNCDECL_SYS(func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C rettype func parameters_and_attributes
|
||||
|
||||
/* _GL_CXXALIAS_RPL (func, rettype, parameters);
|
||||
declares a C++ alias called GNULIB_NAMESPACE::func
|
||||
that redirects to rpl_func, if GNULIB_NAMESPACE is defined.
|
||||
Example:
|
||||
_GL_CXXALIAS_RPL (open, int, (const char *filename, int flags, ...));
|
||||
|
||||
Wrapping rpl_func in an object with an inline conversion operator
|
||||
avoids a reference to rpl_func unless GNULIB_NAMESPACE::func is
|
||||
actually used in the program. */
|
||||
#define _GL_CXXALIAS_RPL(func,rettype,parameters) \
|
||||
_GL_CXXALIAS_RPL_1 (func, rpl_##func, rettype, parameters)
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return ::rpl_func; \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_RPL_CAST_1 (func, rpl_func, rettype, parameters);
|
||||
is like _GL_CXXALIAS_RPL_1 (func, rpl_func, rettype, parameters);
|
||||
except that the C function rpl_func may have a slightly different
|
||||
declaration. A cast is used to silence the "invalid conversion" error
|
||||
that would otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>(::rpl_func); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
declares a C++ alias called GNULIB_NAMESPACE::func
|
||||
that redirects to the system provided function func, if GNULIB_NAMESPACE
|
||||
is defined.
|
||||
Example:
|
||||
_GL_CXXALIAS_SYS (open, int, (const char *filename, int flags, ...));
|
||||
|
||||
Wrapping func in an object with an inline conversion operator
|
||||
avoids a reference to func unless GNULIB_NAMESPACE::func is
|
||||
actually used in the program. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return ::func; \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS_CAST (func, rettype, parameters);
|
||||
is like _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
except that the C function func may have a slightly different declaration.
|
||||
A cast is used to silence the "invalid conversion" error that would
|
||||
otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>(::func); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS_CAST2 (func, rettype, parameters, rettype2, parameters2);
|
||||
is like _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
except that the C function is picked among a set of overloaded functions,
|
||||
namely the one with rettype2 and parameters2. Two consecutive casts
|
||||
are used to silence the "cannot find a match" and "invalid conversion"
|
||||
errors that would otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
/* The outer cast must be a reinterpret_cast.
|
||||
The inner cast: When the function is defined as a set of overloaded
|
||||
functions, it works as a static_cast<>, choosing the designated variant.
|
||||
When the function is defined as a single variant, it works as a
|
||||
reinterpret_cast<>. The parenthesized cast syntax works both ways. */
|
||||
# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>((rettype2 (*) parameters2)(::func)); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIASWARN (func);
|
||||
causes a warning to be emitted when ::func is used but not when
|
||||
GNULIB_NAMESPACE::func is used. func must be defined without overloaded
|
||||
variants. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIASWARN(func) \
|
||||
_GL_CXXALIASWARN_1 (func, GNULIB_NAMESPACE)
|
||||
# define _GL_CXXALIASWARN_1(func,namespace) \
|
||||
_GL_CXXALIASWARN_2 (func, namespace)
|
||||
/* To work around GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
|
||||
we enable the warning only when not optimizing. */
|
||||
# if !__OPTIMIZE__
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
_GL_WARN_ON_USE (func, \
|
||||
"The symbol ::" #func " refers to the system function. " \
|
||||
"Use " #namespace "::" #func " instead.")
|
||||
# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
extern __typeof__ (func) func
|
||||
# else
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
# endif
|
||||
#else
|
||||
# define _GL_CXXALIASWARN(func) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIASWARN1 (func, rettype, parameters_and_attributes);
|
||||
causes a warning to be emitted when the given overloaded variant of ::func
|
||||
is used but not when GNULIB_NAMESPACE::func is used. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
|
||||
_GL_CXXALIASWARN1_1 (func, rettype, parameters_and_attributes, \
|
||||
GNULIB_NAMESPACE)
|
||||
# define _GL_CXXALIASWARN1_1(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_CXXALIASWARN1_2 (func, rettype, parameters_and_attributes, namespace)
|
||||
/* To work around GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
|
||||
we enable the warning only when not optimizing. */
|
||||
# if !__OPTIMIZE__
|
||||
# define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_WARN_ON_USE_CXX (func, rettype, parameters_and_attributes, \
|
||||
"The symbol ::" #func " refers to the system function. " \
|
||||
"Use " #namespace "::" #func " instead.")
|
||||
# else
|
||||
# define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
# endif
|
||||
#else
|
||||
# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
#endif /* _GL_CXXDEFS_H */
|
||||
|
||||
/* The definition of _GL_ARG_NONNULL is copied here. */
|
||||
/* A C macro for declaring that specific arguments must not be NULL.
|
||||
Copyright (C) 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* _GL_ARG_NONNULL((n,...,m)) tells the compiler and static analyzer tools
|
||||
that the values passed as arguments n, ..., m must be non-NULL pointers.
|
||||
n = 1 stands for the first argument, n = 2 for the second argument etc. */
|
||||
#ifndef _GL_ARG_NONNULL
|
||||
# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ > 3
|
||||
# define _GL_ARG_NONNULL(params) __attribute__ ((__nonnull__ params))
|
||||
# else
|
||||
# define _GL_ARG_NONNULL(params)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* The definition of _GL_WARN_ON_USE is copied here. */
|
||||
/* A C macro for emitting warnings if a function is used.
|
||||
Copyright (C) 2010-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* _GL_WARN_ON_USE (function, "literal string") issues a declaration
|
||||
for FUNCTION which will then trigger a compiler warning containing
|
||||
the text of "literal string" anywhere that function is called, if
|
||||
supported by the compiler. If the compiler does not support this
|
||||
feature, the macro expands to an unused extern declaration.
|
||||
|
||||
_GL_WARN_ON_USE_ATTRIBUTE ("literal string") expands to the
|
||||
attribute used in _GL_WARN_ON_USE. If the compiler does not support
|
||||
this feature, it expands to empty.
|
||||
|
||||
These macros are useful for marking a function as a potential
|
||||
portability trap, with the intent that "literal string" include
|
||||
instructions on the replacement function that should be used
|
||||
instead.
|
||||
_GL_WARN_ON_USE is for functions with 'extern' linkage.
|
||||
_GL_WARN_ON_USE_ATTRIBUTE is for functions with 'static' or 'inline'
|
||||
linkage.
|
||||
|
||||
However, one of the reasons that a function is a portability trap is
|
||||
if it has the wrong signature. Declaring FUNCTION with a different
|
||||
signature in C is a compilation error, so this macro must use the
|
||||
same type as any existing declaration so that programs that avoid
|
||||
the problematic FUNCTION do not fail to compile merely because they
|
||||
included a header that poisoned the function. But this implies that
|
||||
_GL_WARN_ON_USE is only safe to use if FUNCTION is known to already
|
||||
have a declaration. Use of this macro implies that there must not
|
||||
be any other macro hiding the declaration of FUNCTION; but
|
||||
undefining FUNCTION first is part of the poisoning process anyway
|
||||
(although for symbols that are provided only via a macro, the result
|
||||
is a compilation error rather than a warning containing
|
||||
"literal string"). Also note that in C++, it is only safe to use if
|
||||
FUNCTION has no overloads.
|
||||
|
||||
For an example, it is possible to poison 'getline' by:
|
||||
- adding a call to gl_WARN_ON_USE_PREPARE([[#include <stdio.h>]],
|
||||
[getline]) in configure.ac, which potentially defines
|
||||
HAVE_RAW_DECL_GETLINE
|
||||
- adding this code to a header that wraps the system <stdio.h>:
|
||||
#undef getline
|
||||
#if HAVE_RAW_DECL_GETLINE
|
||||
_GL_WARN_ON_USE (getline, "getline is required by POSIX 2008, but"
|
||||
"not universally present; use the gnulib module getline");
|
||||
#endif
|
||||
|
||||
It is not possible to directly poison global variables. But it is
|
||||
possible to write a wrapper accessor function, and poison that
|
||||
(less common usage, like &environ, will cause a compilation error
|
||||
rather than issue the nice warning, but the end result of informing
|
||||
the developer about their portability problem is still achieved):
|
||||
#if HAVE_RAW_DECL_ENVIRON
|
||||
static char ***
|
||||
rpl_environ (void) { return &environ; }
|
||||
_GL_WARN_ON_USE (rpl_environ, "environ is not always properly declared");
|
||||
# undef environ
|
||||
# define environ (*rpl_environ ())
|
||||
#endif
|
||||
or better (avoiding contradictory use of 'static' and 'extern'):
|
||||
#if HAVE_RAW_DECL_ENVIRON
|
||||
static char ***
|
||||
_GL_WARN_ON_USE_ATTRIBUTE ("environ is not always properly declared")
|
||||
rpl_environ (void) { return &environ; }
|
||||
# undef environ
|
||||
# define environ (*rpl_environ ())
|
||||
#endif
|
||||
*/
|
||||
#ifndef _GL_WARN_ON_USE
|
||||
|
||||
# if 4 < __GNUC__ || (__GNUC__ == 4 && 3 <= __GNUC_MINOR__)
|
||||
/* A compiler attribute is available in gcc versions 4.3.0 and later. */
|
||||
# define _GL_WARN_ON_USE(function, message) \
|
||||
extern __typeof__ (function) function __attribute__ ((__warning__ (message)))
|
||||
# define _GL_WARN_ON_USE_ATTRIBUTE(message) \
|
||||
__attribute__ ((__warning__ (message)))
|
||||
# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
|
||||
/* Verify the existence of the function. */
|
||||
# define _GL_WARN_ON_USE(function, message) \
|
||||
extern __typeof__ (function) function
|
||||
# define _GL_WARN_ON_USE_ATTRIBUTE(message)
|
||||
# else /* Unsupported. */
|
||||
# define _GL_WARN_ON_USE(function, message) \
|
||||
_GL_WARN_EXTERN_C int _gl_warn_on_use
|
||||
# define _GL_WARN_ON_USE_ATTRIBUTE(message)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* _GL_WARN_ON_USE_CXX (function, rettype, parameters_and_attributes, "string")
|
||||
is like _GL_WARN_ON_USE (function, "string"), except that the function is
|
||||
declared with the given prototype, consisting of return type, parameters,
|
||||
and attributes.
|
||||
This variant is useful for overloaded functions in C++. _GL_WARN_ON_USE does
|
||||
not work in this case. */
|
||||
#ifndef _GL_WARN_ON_USE_CXX
|
||||
# if 4 < __GNUC__ || (__GNUC__ == 4 && 3 <= __GNUC_MINOR__)
|
||||
# define _GL_WARN_ON_USE_CXX(function,rettype,parameters_and_attributes,msg) \
|
||||
extern rettype function parameters_and_attributes \
|
||||
__attribute__ ((__warning__ (msg)))
|
||||
# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
|
||||
/* Verify the existence of the function. */
|
||||
# define _GL_WARN_ON_USE_CXX(function,rettype,parameters_and_attributes,msg) \
|
||||
extern rettype function parameters_and_attributes
|
||||
# else /* Unsupported. */
|
||||
# define _GL_WARN_ON_USE_CXX(function,rettype,parameters_and_attributes,msg) \
|
||||
_GL_WARN_EXTERN_C int _gl_warn_on_use
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* _GL_WARN_EXTERN_C declaration;
|
||||
performs the declaration with C linkage. */
|
||||
#ifndef _GL_WARN_EXTERN_C
|
||||
# if defined __cplusplus
|
||||
# define _GL_WARN_EXTERN_C extern "C"
|
||||
# else
|
||||
# define _GL_WARN_EXTERN_C extern
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if 1
|
||||
/* Converts an internet address from internal format to a printable,
|
||||
presentable format.
|
||||
AF is an internet address family, such as AF_INET or AF_INET6.
|
||||
SRC points to a 'struct in_addr' (for AF_INET) or 'struct in6_addr'
|
||||
(for AF_INET6).
|
||||
DST points to a buffer having room for CNT bytes.
|
||||
The printable representation of the address (in numeric form, not
|
||||
surrounded by [...], no reverse DNS is done) is placed in DST, and
|
||||
DST is returned. If an error occurs, the return value is NULL and
|
||||
errno is set. If CNT bytes are not sufficient to hold the result,
|
||||
the return value is NULL and errno is set to ENOSPC. A good value
|
||||
for CNT is 46.
|
||||
|
||||
For more details, see the POSIX:2008 specification
|
||||
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html>. */
|
||||
# if 0
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef inet_ntop
|
||||
# define inet_ntop rpl_inet_ntop
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
_GL_CXXALIAS_RPL (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt));
|
||||
# else
|
||||
# if !1
|
||||
_GL_FUNCDECL_SYS (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
# endif
|
||||
/* Need to cast, because on NonStop Kernel, the fourth parameter is
|
||||
size_t cnt. */
|
||||
_GL_CXXALIAS_SYS_CAST (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt));
|
||||
# endif
|
||||
# if __GLIBC__ >= 2
|
||||
_GL_CXXALIASWARN (inet_ntop);
|
||||
# endif
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
# undef inet_ntop
|
||||
# if HAVE_RAW_DECL_INET_NTOP
|
||||
_GL_WARN_ON_USE (inet_ntop, "inet_ntop is unportable - "
|
||||
"use gnulib module inet_ntop for portability");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if IN_COREUTILS_GNULIB_TESTS
|
||||
# if 0
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef inet_pton
|
||||
# define inet_pton rpl_inet_pton
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
_GL_CXXALIAS_RPL (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst));
|
||||
# else
|
||||
# if !1
|
||||
_GL_FUNCDECL_SYS (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst));
|
||||
# endif
|
||||
# if __GLIBC__ >= 2
|
||||
_GL_CXXALIASWARN (inet_pton);
|
||||
# endif
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
# undef inet_pton
|
||||
# if HAVE_RAW_DECL_INET_PTON
|
||||
_GL_WARN_ON_USE (inet_pton, "inet_pton is unportable - "
|
||||
"use gnulib module inet_pton for portability");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _GL_ARPA_INET_H */
|
||||
#endif /* _GL_ARPA_INET_H */
|
150
lib/arpa_inet.in.h
Normal file
150
lib/arpa_inet.in.h
Normal file
@ -0,0 +1,150 @@
|
||||
/* A GNU-like <arpa/inet.h>.
|
||||
|
||||
Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _@GUARD_PREFIX@_ARPA_INET_H
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
@PRAGMA_SYSTEM_HEADER@
|
||||
#endif
|
||||
@PRAGMA_COLUMNS@
|
||||
|
||||
#if @HAVE_FEATURES_H@
|
||||
# include <features.h> /* for __GLIBC__ */
|
||||
#endif
|
||||
|
||||
/* Gnulib's sys/socket.h is responsible for defining socklen_t (used below) and
|
||||
for pulling in winsock2.h etc. under MinGW.
|
||||
But avoid namespace pollution on glibc systems. */
|
||||
#ifndef __GLIBC__
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* On NonStop Kernel, inet_ntop and inet_pton are declared in <netdb.h>.
|
||||
But avoid namespace pollution on glibc systems. */
|
||||
#if defined __TANDEM && !defined __GLIBC__
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if @HAVE_ARPA_INET_H@
|
||||
|
||||
/* The include_next requires a split double-inclusion guard. */
|
||||
# @INCLUDE_NEXT@ @NEXT_ARPA_INET_H@
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef _@GUARD_PREFIX@_ARPA_INET_H
|
||||
#define _@GUARD_PREFIX@_ARPA_INET_H
|
||||
|
||||
/* Get all possible declarations of inet_ntop() and inet_pton(). */
|
||||
#if (@GNULIB_INET_NTOP@ || @GNULIB_INET_PTON@ || defined GNULIB_POSIXCHECK) \
|
||||
&& @HAVE_WS2TCPIP_H@
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
|
||||
|
||||
/* The definition of _GL_ARG_NONNULL is copied here. */
|
||||
|
||||
/* The definition of _GL_WARN_ON_USE is copied here. */
|
||||
|
||||
|
||||
#if @GNULIB_INET_NTOP@
|
||||
/* Converts an internet address from internal format to a printable,
|
||||
presentable format.
|
||||
AF is an internet address family, such as AF_INET or AF_INET6.
|
||||
SRC points to a 'struct in_addr' (for AF_INET) or 'struct in6_addr'
|
||||
(for AF_INET6).
|
||||
DST points to a buffer having room for CNT bytes.
|
||||
The printable representation of the address (in numeric form, not
|
||||
surrounded by [...], no reverse DNS is done) is placed in DST, and
|
||||
DST is returned. If an error occurs, the return value is NULL and
|
||||
errno is set. If CNT bytes are not sufficient to hold the result,
|
||||
the return value is NULL and errno is set to ENOSPC. A good value
|
||||
for CNT is 46.
|
||||
|
||||
For more details, see the POSIX:2008 specification
|
||||
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html>. */
|
||||
# if @REPLACE_INET_NTOP@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef inet_ntop
|
||||
# define inet_ntop rpl_inet_ntop
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
_GL_CXXALIAS_RPL (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt));
|
||||
# else
|
||||
# if !@HAVE_DECL_INET_NTOP@
|
||||
_GL_FUNCDECL_SYS (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
# endif
|
||||
/* Need to cast, because on NonStop Kernel, the fourth parameter is
|
||||
size_t cnt. */
|
||||
_GL_CXXALIAS_SYS_CAST (inet_ntop, const char *,
|
||||
(int af, const void *restrict src,
|
||||
char *restrict dst, socklen_t cnt));
|
||||
# endif
|
||||
# if __GLIBC__ >= 2
|
||||
_GL_CXXALIASWARN (inet_ntop);
|
||||
# endif
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
# undef inet_ntop
|
||||
# if HAVE_RAW_DECL_INET_NTOP
|
||||
_GL_WARN_ON_USE (inet_ntop, "inet_ntop is unportable - "
|
||||
"use gnulib module inet_ntop for portability");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if @GNULIB_INET_PTON@
|
||||
# if @REPLACE_INET_PTON@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef inet_pton
|
||||
# define inet_pton rpl_inet_pton
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
_GL_CXXALIAS_RPL (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst));
|
||||
# else
|
||||
# if !@HAVE_DECL_INET_PTON@
|
||||
_GL_FUNCDECL_SYS (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst)
|
||||
_GL_ARG_NONNULL ((2, 3)));
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (inet_pton, int,
|
||||
(int af, const char *restrict src, void *restrict dst));
|
||||
# endif
|
||||
# if __GLIBC__ >= 2
|
||||
_GL_CXXALIASWARN (inet_pton);
|
||||
# endif
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
# undef inet_pton
|
||||
# if HAVE_RAW_DECL_INET_PTON
|
||||
_GL_WARN_ON_USE (inet_pton, "inet_pton is unportable - "
|
||||
"use gnulib module inet_pton for portability");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _@GUARD_PREFIX@_ARPA_INET_H */
|
||||
#endif /* _@GUARD_PREFIX@_ARPA_INET_H */
|
34
lib/asnprintf.c
Normal file
34
lib/asnprintf.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Formatted output to strings.
|
||||
Copyright (C) 1999, 2002, 2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "vasnprintf.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
char *
|
||||
asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *result;
|
||||
|
||||
va_start (args, format);
|
||||
result = vasnprintf (resultbuf, lengthp, format, args);
|
||||
va_end (args);
|
||||
return result;
|
||||
}
|
39
lib/asprintf.c
Normal file
39
lib/asprintf.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* Formatted output to strings.
|
||||
Copyright (C) 1999, 2002, 2006-2007, 2009-2020 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#ifdef IN_LIBASPRINTF
|
||||
# include "vasprintf.h"
|
||||
#else
|
||||
# include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int
|
||||
asprintf (char **resultp, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int result;
|
||||
|
||||
va_start (args, format);
|
||||
result = vasprintf (resultp, format, args);
|
||||
va_end (args);
|
||||
return result;
|
||||
}
|
37
lib/assure.h
Normal file
37
lib/assure.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* Run-time assert-like macros.
|
||||
|
||||
Copyright (C) 2014-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#ifndef _GL_ASSURE_H
|
||||
#define _GL_ASSURE_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* Check E's value at runtime, and report an error and abort if not.
|
||||
However, do nothing if NDEBUG is defined.
|
||||
|
||||
Unlike standard 'assert', this macro always compiles E even when NDEBUG
|
||||
is defined, so as to catch typos and avoid some GCC warnings. */
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define assure(E) ((void) (0 && (E)))
|
||||
#else
|
||||
# define assure(E) assert (E)
|
||||
#endif
|
||||
|
||||
#endif
|
146
lib/at-func.c
Normal file
146
lib/at-func.c
Normal file
@ -0,0 +1,146 @@
|
||||
/* Define at-style functions like fstatat, unlinkat, fchownat, etc.
|
||||
Copyright (C) 2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Jim Meyering */
|
||||
|
||||
#include "dosname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
|
||||
|
||||
#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
|
||||
# include <errno.h>
|
||||
# ifndef ENOTSUP
|
||||
# define ENOTSUP EINVAL
|
||||
# endif
|
||||
#else
|
||||
# include "openat.h"
|
||||
# include "openat-priv.h"
|
||||
# include "save-cwd.h"
|
||||
#endif
|
||||
|
||||
#ifdef AT_FUNC_USE_F1_COND
|
||||
# define CALL_FUNC(F) \
|
||||
(flag == AT_FUNC_USE_F1_COND \
|
||||
? AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS) \
|
||||
: AT_FUNC_F2 (F AT_FUNC_POST_FILE_ARGS))
|
||||
# define VALIDATE_FLAG(F) \
|
||||
if (flag & ~AT_FUNC_USE_F1_COND) \
|
||||
{ \
|
||||
errno = EINVAL; \
|
||||
return FUNC_FAIL; \
|
||||
}
|
||||
#else
|
||||
# define CALL_FUNC(F) (AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS))
|
||||
# define VALIDATE_FLAG(F) /* empty */
|
||||
#endif
|
||||
|
||||
#ifdef AT_FUNC_RESULT
|
||||
# define FUNC_RESULT AT_FUNC_RESULT
|
||||
#else
|
||||
# define FUNC_RESULT int
|
||||
#endif
|
||||
|
||||
#ifdef AT_FUNC_FAIL
|
||||
# define FUNC_FAIL AT_FUNC_FAIL
|
||||
#else
|
||||
# define FUNC_FAIL -1
|
||||
#endif
|
||||
|
||||
/* Call AT_FUNC_F1 to operate on FILE, which is in the directory
|
||||
open on descriptor FD. If AT_FUNC_USE_F1_COND is defined to a value,
|
||||
AT_FUNC_POST_FILE_PARAM_DECLS must include a parameter named flag;
|
||||
call AT_FUNC_F2 if FLAG is 0 or fail if FLAG contains more bits than
|
||||
AT_FUNC_USE_F1_COND. Return int and fail with -1 unless AT_FUNC_RESULT
|
||||
or AT_FUNC_FAIL are defined. If possible, do it without changing the
|
||||
working directory. Otherwise, resort to using save_cwd/fchdir,
|
||||
then AT_FUNC_F?/restore_cwd. If either the save_cwd or the restore_cwd
|
||||
fails, then give a diagnostic and exit nonzero. */
|
||||
FUNC_RESULT
|
||||
AT_FUNC_NAME (int fd, char const *file AT_FUNC_POST_FILE_PARAM_DECLS)
|
||||
{
|
||||
VALIDATE_FLAG (flag);
|
||||
|
||||
if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
|
||||
return CALL_FUNC (file);
|
||||
|
||||
#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
|
||||
errno = ENOTSUP;
|
||||
return FUNC_FAIL;
|
||||
#else
|
||||
{
|
||||
/* Be careful to choose names unlikely to conflict with
|
||||
AT_FUNC_POST_FILE_PARAM_DECLS. */
|
||||
struct saved_cwd saved_cwd;
|
||||
int saved_errno;
|
||||
FUNC_RESULT err;
|
||||
|
||||
{
|
||||
char proc_buf[OPENAT_BUFFER_SIZE];
|
||||
char *proc_file = openat_proc_name (proc_buf, fd, file);
|
||||
if (proc_file)
|
||||
{
|
||||
FUNC_RESULT proc_result = CALL_FUNC (proc_file);
|
||||
int proc_errno = errno;
|
||||
if (proc_file != proc_buf)
|
||||
free (proc_file);
|
||||
/* If the syscall succeeds, or if it fails with an unexpected
|
||||
errno value, then return right away. Otherwise, fall through
|
||||
and resort to using save_cwd/restore_cwd. */
|
||||
if (FUNC_FAIL != proc_result)
|
||||
return proc_result;
|
||||
if (! EXPECTED_ERRNO (proc_errno))
|
||||
{
|
||||
errno = proc_errno;
|
||||
return proc_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (save_cwd (&saved_cwd) != 0)
|
||||
openat_save_fail (errno);
|
||||
if (0 <= fd && fd == saved_cwd.desc)
|
||||
{
|
||||
/* If saving the working directory collides with the user's
|
||||
requested fd, then the user's fd must have been closed to
|
||||
begin with. */
|
||||
free_cwd (&saved_cwd);
|
||||
errno = EBADF;
|
||||
return FUNC_FAIL;
|
||||
}
|
||||
|
||||
if (fchdir (fd) != 0)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return FUNC_FAIL;
|
||||
}
|
||||
|
||||
err = CALL_FUNC (file);
|
||||
saved_errno = (err == FUNC_FAIL ? errno : 0);
|
||||
|
||||
if (restore_cwd (&saved_cwd) != 0)
|
||||
openat_restore_fail (errno);
|
||||
|
||||
free_cwd (&saved_cwd);
|
||||
|
||||
if (saved_errno)
|
||||
errno = saved_errno;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#undef CALL_FUNC
|
||||
#undef FUNC_RESULT
|
||||
#undef FUNC_FAIL
|
289
lib/at-func2.c
Normal file
289
lib/at-func2.c
Normal file
@ -0,0 +1,289 @@
|
||||
/* Define 2-FD at-style functions like linkat or renameat.
|
||||
Copyright (C) 2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Jim Meyering and Eric Blake */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "openat-priv.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dosname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
|
||||
#include "filenamecat.h"
|
||||
#include "openat.h"
|
||||
#include "same-inode.h"
|
||||
#include "save-cwd.h"
|
||||
|
||||
/* Call FUNC to operate on a pair of files, where FILE1 is relative to FD1,
|
||||
and FILE2 is relative to FD2. If possible, do it without changing the
|
||||
working directory. Otherwise, resort to using save_cwd/fchdir,
|
||||
FUNC, restore_cwd (up to two times). If either the save_cwd or the
|
||||
restore_cwd fails, then give a diagnostic and exit nonzero. */
|
||||
int
|
||||
at_func2 (int fd1, char const *file1,
|
||||
int fd2, char const *file2,
|
||||
int (*func) (char const *file1, char const *file2))
|
||||
{
|
||||
struct saved_cwd saved_cwd;
|
||||
int saved_errno;
|
||||
int err;
|
||||
char *file1_alt;
|
||||
char *file2_alt;
|
||||
struct stat st1;
|
||||
struct stat st2;
|
||||
|
||||
/* There are 16 possible scenarios, based on whether an fd is
|
||||
AT_FDCWD or real, and whether a file is absolute or relative:
|
||||
|
||||
fd1 file1 fd2 file2 action
|
||||
0 cwd abs cwd abs direct call
|
||||
1 cwd abs cwd rel direct call
|
||||
2 cwd abs fd abs direct call
|
||||
3 cwd abs fd rel chdir to fd2
|
||||
4 cwd rel cwd abs direct call
|
||||
5 cwd rel cwd rel direct call
|
||||
6 cwd rel fd abs direct call
|
||||
7 cwd rel fd rel convert file1 to abs, then case 3
|
||||
8 fd abs cwd abs direct call
|
||||
9 fd abs cwd rel direct call
|
||||
10 fd abs fd abs direct call
|
||||
11 fd abs fd rel chdir to fd2
|
||||
12 fd rel cwd abs chdir to fd1
|
||||
13 fd rel cwd rel convert file2 to abs, then case 12
|
||||
14 fd rel fd abs chdir to fd1
|
||||
15a fd1 rel fd1 rel chdir to fd1
|
||||
15b fd1 rel fd2 rel chdir to fd1, then case 7
|
||||
|
||||
Try some optimizations to reduce fd to AT_FDCWD, or to at least
|
||||
avoid converting an absolute name or doing a double chdir. */
|
||||
|
||||
if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
|
||||
&& (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2)))
|
||||
return func (file1, file2); /* Case 0-2, 4-6, 8-10. */
|
||||
|
||||
/* If /proc/self/fd works, we don't need any stat or chdir. */
|
||||
{
|
||||
char proc_buf1[OPENAT_BUFFER_SIZE];
|
||||
char *proc_file1 = ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
|
||||
? (char *) file1
|
||||
: openat_proc_name (proc_buf1, fd1, file1));
|
||||
if (proc_file1)
|
||||
{
|
||||
char proc_buf2[OPENAT_BUFFER_SIZE];
|
||||
char *proc_file2 = ((fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))
|
||||
? (char *) file2
|
||||
: openat_proc_name (proc_buf2, fd2, file2));
|
||||
if (proc_file2)
|
||||
{
|
||||
int proc_result = func (proc_file1, proc_file2);
|
||||
int proc_errno = errno;
|
||||
if (proc_file1 != proc_buf1 && proc_file1 != file1)
|
||||
free (proc_file1);
|
||||
if (proc_file2 != proc_buf2 && proc_file2 != file2)
|
||||
free (proc_file2);
|
||||
/* If the syscall succeeds, or if it fails with an unexpected
|
||||
errno value, then return right away. Otherwise, fall through
|
||||
and resort to using save_cwd/restore_cwd. */
|
||||
if (0 <= proc_result)
|
||||
return proc_result;
|
||||
if (! EXPECTED_ERRNO (proc_errno))
|
||||
{
|
||||
errno = proc_errno;
|
||||
return proc_result;
|
||||
}
|
||||
}
|
||||
else if (proc_file1 != proc_buf1 && proc_file1 != file1)
|
||||
free (proc_file1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cases 3, 7, 11-15 remain. Time to normalize directory fds, if
|
||||
possible. */
|
||||
if (IS_ABSOLUTE_FILE_NAME (file1))
|
||||
fd1 = AT_FDCWD; /* Case 11 reduced to 3. */
|
||||
else if (IS_ABSOLUTE_FILE_NAME (file2))
|
||||
fd2 = AT_FDCWD; /* Case 14 reduced to 12. */
|
||||
|
||||
/* Cases 3, 7, 12, 13, 15 remain. */
|
||||
|
||||
if (fd1 == AT_FDCWD) /* Cases 3, 7. */
|
||||
{
|
||||
if (stat (".", &st1) == -1 || fstat (fd2, &st2) == -1)
|
||||
return -1;
|
||||
if (!S_ISDIR (st2.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
if (SAME_INODE (st1, st2)) /* Reduced to cases 1, 5. */
|
||||
return func (file1, file2);
|
||||
}
|
||||
else if (fd2 == AT_FDCWD) /* Cases 12, 13. */
|
||||
{
|
||||
if (stat (".", &st2) == -1 || fstat (fd1, &st1) == -1)
|
||||
return -1;
|
||||
if (!S_ISDIR (st1.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
if (SAME_INODE (st1, st2)) /* Reduced to cases 4, 5. */
|
||||
return func (file1, file2);
|
||||
}
|
||||
else if (fd1 != fd2) /* Case 15b. */
|
||||
{
|
||||
if (fstat (fd1, &st1) == -1 || fstat (fd2, &st2) == -1)
|
||||
return -1;
|
||||
if (!S_ISDIR (st1.st_mode) || !S_ISDIR (st2.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
if (SAME_INODE (st1, st2)) /* Reduced to case 15a. */
|
||||
{
|
||||
fd2 = fd1;
|
||||
if (stat (".", &st1) == 0 && SAME_INODE (st1, st2))
|
||||
return func (file1, file2); /* Further reduced to case 5. */
|
||||
}
|
||||
}
|
||||
else /* Case 15a. */
|
||||
{
|
||||
if (fstat (fd1, &st1) == -1)
|
||||
return -1;
|
||||
if (!S_ISDIR (st1.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
if (stat (".", &st2) == 0 && SAME_INODE (st1, st2))
|
||||
return func (file1, file2); /* Reduced to case 5. */
|
||||
}
|
||||
|
||||
/* Catch invalid arguments before changing directories. */
|
||||
if (file1[0] == '\0' || file2[0] == '\0')
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cases 3, 7, 12, 13, 15a, 15b remain. With all reductions in
|
||||
place, it is time to start changing directories. */
|
||||
|
||||
if (save_cwd (&saved_cwd) != 0)
|
||||
openat_save_fail (errno);
|
||||
|
||||
if (fd1 != AT_FDCWD && fd2 != AT_FDCWD && fd1 != fd2) /* Case 15b. */
|
||||
{
|
||||
if (fchdir (fd1) != 0)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
fd1 = AT_FDCWD; /* Reduced to case 7. */
|
||||
}
|
||||
|
||||
/* Cases 3, 7, 12, 13, 15a remain. Convert one relative name to
|
||||
absolute, if necessary. */
|
||||
|
||||
file1_alt = (char *) file1;
|
||||
file2_alt = (char *) file2;
|
||||
|
||||
if (fd1 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file1)) /* Case 7. */
|
||||
{
|
||||
/* It would be nicer to use:
|
||||
file1_alt = file_name_concat (xgetcwd (), file1, NULL);
|
||||
but libraries should not call xalloc_die. */
|
||||
char *cwd = getcwd (NULL, 0);
|
||||
if (!cwd)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
file1_alt = mfile_name_concat (cwd, file1, NULL);
|
||||
if (!file1_alt)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free (cwd);
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
free (cwd); /* Reduced to case 3. */
|
||||
}
|
||||
else if (fd2 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file2)) /* Case 13. */
|
||||
{
|
||||
char *cwd = getcwd (NULL, 0);
|
||||
if (!cwd)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
file2_alt = mfile_name_concat (cwd, file2, NULL);
|
||||
if (!file2_alt)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free (cwd);
|
||||
free_cwd (&saved_cwd);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
free (cwd); /* Reduced to case 12. */
|
||||
}
|
||||
|
||||
/* Cases 3, 12, 15a remain. Change to the correct directory. */
|
||||
if (fchdir (fd1 == AT_FDCWD ? fd2 : fd1) != 0)
|
||||
{
|
||||
saved_errno = errno;
|
||||
free_cwd (&saved_cwd);
|
||||
if (file1 != file1_alt)
|
||||
free (file1_alt);
|
||||
else if (file2 != file2_alt)
|
||||
free (file2_alt);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Finally safe to perform the user's function, then clean up. */
|
||||
|
||||
err = func (file1_alt, file2_alt);
|
||||
saved_errno = (err < 0 ? errno : 0);
|
||||
|
||||
if (file1 != file1_alt)
|
||||
free (file1_alt);
|
||||
else if (file2 != file2_alt)
|
||||
free (file2_alt);
|
||||
|
||||
if (restore_cwd (&saved_cwd) != 0)
|
||||
openat_restore_fail (errno);
|
||||
|
||||
free_cwd (&saved_cwd);
|
||||
|
||||
if (saved_errno)
|
||||
errno = saved_errno;
|
||||
return err;
|
||||
}
|
||||
#undef CALL_FUNC
|
||||
#undef FUNC_RESULT
|
93
lib/backup-find.c
Normal file
93
lib/backup-find.c
Normal file
@ -0,0 +1,93 @@
|
||||
/* backupfile.c -- make Emacs style backup file names
|
||||
|
||||
Copyright 2017-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "backup-internal.h"
|
||||
|
||||
#include "argmatch.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Relative to DIR_FD, return the name of a backup file for the
|
||||
existing file FILE, allocated with malloc. Report an error and
|
||||
exit if out of memory. Do not call this function if
|
||||
backup_type == no_backups. */
|
||||
|
||||
char *
|
||||
find_backup_file_name (int dir_fd, char const *file,
|
||||
enum backup_type backup_type)
|
||||
{
|
||||
char *result = backupfile_internal (dir_fd, file, backup_type, false);
|
||||
if (!result)
|
||||
xalloc_die ();
|
||||
return result;
|
||||
}
|
||||
|
||||
static char const *const backup_args[] =
|
||||
{
|
||||
/* In a series of synonyms, present the most meaningful first, so
|
||||
that argmatch_valid be more readable. */
|
||||
"none", "off",
|
||||
"simple", "never",
|
||||
"existing", "nil",
|
||||
"numbered", "t",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const enum backup_type backup_types[] =
|
||||
{
|
||||
no_backups, no_backups,
|
||||
simple_backups, simple_backups,
|
||||
numbered_existing_backups, numbered_existing_backups,
|
||||
numbered_backups, numbered_backups
|
||||
};
|
||||
|
||||
/* Ensure that these two vectors have the same number of elements,
|
||||
not counting the final NULL in the first one. */
|
||||
ARGMATCH_VERIFY (backup_args, backup_types);
|
||||
|
||||
/* Return the type of backup specified by VERSION.
|
||||
If VERSION is NULL or the empty string, return numbered_existing_backups.
|
||||
If VERSION is invalid or ambiguous, fail with a diagnostic appropriate
|
||||
for the specified CONTEXT. Unambiguous abbreviations are accepted. */
|
||||
|
||||
enum backup_type
|
||||
get_version (char const *context, char const *version)
|
||||
{
|
||||
if (version == 0 || *version == 0)
|
||||
return numbered_existing_backups;
|
||||
else
|
||||
return XARGMATCH (context, version, backup_args, backup_types);
|
||||
}
|
||||
|
||||
|
||||
/* Return the type of backup specified by VERSION.
|
||||
If VERSION is NULL, use the value of the envvar VERSION_CONTROL.
|
||||
If the specified string is invalid or ambiguous, fail with a diagnostic
|
||||
appropriate for the specified CONTEXT.
|
||||
Unambiguous abbreviations are accepted. */
|
||||
|
||||
enum backup_type
|
||||
xget_version (char const *context, char const *version)
|
||||
{
|
||||
if (version && *version)
|
||||
return get_version (context, version);
|
||||
else
|
||||
return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL"));
|
||||
}
|
3
lib/backup-internal.h
Normal file
3
lib/backup-internal.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include "backupfile.h"
|
||||
#include <stdbool.h>
|
||||
extern char *backupfile_internal (int, char const *, enum backup_type, bool);
|
31
lib/backup-rename.c
Normal file
31
lib/backup-rename.c
Normal file
@ -0,0 +1,31 @@
|
||||
/* Rename a file to a backup name, Emacs style.
|
||||
|
||||
Copyright 2017-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "backup-internal.h"
|
||||
|
||||
/* Relative to DIR_FD, rename the existing file FILE to a backup name,
|
||||
allocated with malloc, and return the backup name. On failure
|
||||
return a null pointer, setting errno. Do not call this function if
|
||||
backup_type == no_backups. */
|
||||
|
||||
char *
|
||||
backup_file_rename (int dir_fd, char const *file, enum backup_type backup_type)
|
||||
{
|
||||
return backupfile_internal (dir_fd, file, backup_type, true);
|
||||
}
|
397
lib/backupfile.c
Normal file
397
lib/backupfile.c
Normal file
@ -0,0 +1,397 @@
|
||||
/* backupfile.c -- make Emacs style backup file names
|
||||
|
||||
Copyright (C) 1990-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert and David MacKenzie.
|
||||
Some algorithms adapted from GNU Emacs. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "backup-internal.h"
|
||||
|
||||
#include "dirname.h"
|
||||
#include "opendirat.h"
|
||||
#include "renameatu.h"
|
||||
#include "xalloc-oversized.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef FALLTHROUGH
|
||||
# if __GNUC__ < 7
|
||||
# define FALLTHROUGH ((void) 0)
|
||||
# else
|
||||
# define FALLTHROUGH __attribute__ ((__fallthrough__))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef _D_EXACT_NAMLEN
|
||||
# define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
|
||||
#endif
|
||||
|
||||
#if ! (HAVE_PATHCONF && defined _PC_NAME_MAX)
|
||||
# define pathconf(file, option) (errno = -1)
|
||||
#endif
|
||||
|
||||
#ifndef _POSIX_NAME_MAX
|
||||
# define _POSIX_NAME_MAX 14
|
||||
#endif
|
||||
|
||||
#if defined _XOPEN_NAME_MAX
|
||||
# define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
|
||||
#else
|
||||
# define NAME_MAX_MINIMUM _POSIX_NAME_MAX
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_DOS_FILE_NAMES
|
||||
# define HAVE_DOS_FILE_NAMES 0
|
||||
#endif
|
||||
#ifndef HAVE_LONG_FILE_NAMES
|
||||
# define HAVE_LONG_FILE_NAMES 0
|
||||
#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. */
|
||||
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
|
||||
|
||||
/* The extension added to file names to produce a simple (as opposed
|
||||
to numbered) backup file name. */
|
||||
char const *simple_backup_suffix = NULL;
|
||||
|
||||
/* Set SIMPLE_BACKUP_SUFFIX to S, or to a default specified by the
|
||||
environment if S is null. If S or the environment does not specify
|
||||
a valid backup suffix, use "~". */
|
||||
void
|
||||
set_simple_backup_suffix (char const *s)
|
||||
{
|
||||
if (!s)
|
||||
s = getenv ("SIMPLE_BACKUP_SUFFIX");
|
||||
simple_backup_suffix = s && *s && s == last_component (s) ? s : "~";
|
||||
}
|
||||
|
||||
/* If FILE (which was of length FILELEN before an extension was
|
||||
appended to it) is too long, replace the extension with the single
|
||||
char E. If the result is still too long, remove the char just
|
||||
before E.
|
||||
|
||||
If DIR_FD is nonnegative, it is a file descriptor for FILE's parent.
|
||||
*NAME_MAX is either 0, or the cached result of a previous call for
|
||||
FILE's parent's _PC_NAME_MAX. */
|
||||
|
||||
static void
|
||||
check_extension (char *file, size_t filelen, char e,
|
||||
int dir_fd, size_t *base_max)
|
||||
{
|
||||
char *base = last_component (file);
|
||||
size_t baselen = base_len (base);
|
||||
size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
|
||||
|
||||
if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
|
||||
{
|
||||
/* The new base name is long enough to require a pathconf check. */
|
||||
if (*base_max == 0)
|
||||
{
|
||||
long name_max;
|
||||
if (dir_fd < 0)
|
||||
{
|
||||
/* Temporarily modify the buffer into its parent
|
||||
directory name, invoke pathconf on the directory, and
|
||||
then restore the buffer. */
|
||||
char tmp[sizeof "."];
|
||||
memcpy (tmp, base, sizeof ".");
|
||||
strcpy (base, ".");
|
||||
errno = 0;
|
||||
name_max = pathconf (file, _PC_NAME_MAX);
|
||||
name_max -= !errno;
|
||||
memcpy (base, tmp, sizeof ".");
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = 0;
|
||||
name_max = fpathconf (dir_fd, _PC_NAME_MAX);
|
||||
name_max -= !errno;
|
||||
}
|
||||
|
||||
*base_max = (0 <= name_max && name_max <= SIZE_MAX ? name_max
|
||||
: name_max < -1 ? NAME_MAX_MINIMUM : SIZE_MAX);
|
||||
}
|
||||
|
||||
baselen_max = *base_max;
|
||||
}
|
||||
|
||||
if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
|
||||
{
|
||||
/* Live within DOS's 8.3 limit. */
|
||||
char *dot = strchr (base, '.');
|
||||
if (!dot)
|
||||
baselen_max = 8;
|
||||
else
|
||||
{
|
||||
char const *second_dot = strchr (dot + 1, '.');
|
||||
baselen_max = (second_dot
|
||||
? second_dot - base
|
||||
: dot + 1 - base + 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (baselen_max < baselen)
|
||||
{
|
||||
baselen = file + filelen - base;
|
||||
if (baselen_max <= baselen)
|
||||
baselen = baselen_max - 1;
|
||||
base[baselen] = e;
|
||||
base[baselen + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Returned values for NUMBERED_BACKUP. */
|
||||
|
||||
enum numbered_backup_result
|
||||
{
|
||||
/* The new backup name is the same length as an existing backup
|
||||
name, so it's valid for that directory. */
|
||||
BACKUP_IS_SAME_LENGTH,
|
||||
|
||||
/* Some backup names already exist, but the returned name is longer
|
||||
than any of them, and its length should be checked. */
|
||||
BACKUP_IS_LONGER,
|
||||
|
||||
/* There are no existing backup names. The new name's length
|
||||
should be checked. */
|
||||
BACKUP_IS_NEW,
|
||||
|
||||
/* Memory allocation failure. */
|
||||
BACKUP_NOMEM
|
||||
};
|
||||
|
||||
/* Relative to DIR_FD, *BUFFER contains a file name.
|
||||
Store into *BUFFER the next backup name for the named file,
|
||||
with a version number greater than all the
|
||||
existing numbered backups. Reallocate *BUFFER as necessary; its
|
||||
initial allocated size is BUFFER_SIZE, which must be at least 4
|
||||
bytes longer than the file name to make room for the initially
|
||||
appended ".~1". FILELEN is the length of the original file name.
|
||||
BASE_OFFSET is the offset of the basename in *BUFFER.
|
||||
The returned value indicates what kind of backup was found. If an
|
||||
I/O or other read error occurs, use the highest backup number that
|
||||
was found.
|
||||
|
||||
*DIRPP is the destination directory. If *DIRPP is null, open the
|
||||
destination directory and store the resulting stream into *DIRPP
|
||||
and its file descriptor into *PNEW_FD without closing the stream. */
|
||||
|
||||
static enum numbered_backup_result
|
||||
numbered_backup (int dir_fd, char **buffer, size_t buffer_size, size_t filelen,
|
||||
ptrdiff_t base_offset, DIR **dirpp, int *pnew_fd)
|
||||
{
|
||||
enum numbered_backup_result result = BACKUP_IS_NEW;
|
||||
DIR *dirp = *dirpp;
|
||||
struct dirent *dp;
|
||||
char *buf = *buffer;
|
||||
size_t versionlenmax = 1;
|
||||
char *base = buf + base_offset;
|
||||
size_t baselen = base_len (base);
|
||||
|
||||
if (dirp)
|
||||
rewinddir (dirp);
|
||||
else
|
||||
{
|
||||
/* Temporarily modify the buffer into its parent directory name,
|
||||
open the directory, and then restore the buffer. */
|
||||
char tmp[sizeof "."];
|
||||
memcpy (tmp, base, sizeof ".");
|
||||
strcpy (base, ".");
|
||||
dirp = opendirat (dir_fd, buf, 0, pnew_fd);
|
||||
if (!dirp && errno == ENOMEM)
|
||||
result = BACKUP_NOMEM;
|
||||
memcpy (base, tmp, sizeof ".");
|
||||
strcpy (base + baselen, ".~1~");
|
||||
if (!dirp)
|
||||
return result;
|
||||
*dirpp = dirp;
|
||||
}
|
||||
|
||||
while ((dp = readdir (dirp)) != NULL)
|
||||
{
|
||||
char const *p;
|
||||
char *q;
|
||||
bool all_9s;
|
||||
size_t versionlen;
|
||||
|
||||
if (_D_EXACT_NAMLEN (dp) < baselen + 4)
|
||||
continue;
|
||||
|
||||
if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0)
|
||||
continue;
|
||||
|
||||
p = dp->d_name + baselen + 2;
|
||||
|
||||
/* Check whether this file has a version number and if so,
|
||||
whether it is larger. Use string operations rather than
|
||||
integer arithmetic, to avoid problems with integer overflow. */
|
||||
|
||||
if (! ('1' <= *p && *p <= '9'))
|
||||
continue;
|
||||
all_9s = (*p == '9');
|
||||
for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++)
|
||||
all_9s &= (p[versionlen] == '9');
|
||||
|
||||
if (! (p[versionlen] == '~' && !p[versionlen + 1]
|
||||
&& (versionlenmax < versionlen
|
||||
|| (versionlenmax == versionlen
|
||||
&& memcmp (buf + filelen + 2, p, versionlen) <= 0))))
|
||||
continue;
|
||||
|
||||
/* This entry has the largest version number seen so far.
|
||||
Append this highest numbered extension to the file name,
|
||||
prepending '0' to the number if it is all 9s. */
|
||||
|
||||
versionlenmax = all_9s + versionlen;
|
||||
result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH);
|
||||
size_t new_buffer_size = filelen + 2 + versionlenmax + 2;
|
||||
if (buffer_size < new_buffer_size)
|
||||
{
|
||||
if (! xalloc_oversized (new_buffer_size, 2))
|
||||
new_buffer_size *= 2;
|
||||
char *new_buf = realloc (buf, new_buffer_size);
|
||||
if (!new_buf)
|
||||
{
|
||||
*buffer = buf;
|
||||
return BACKUP_NOMEM;
|
||||
}
|
||||
buf = new_buf;
|
||||
buffer_size = new_buffer_size;
|
||||
}
|
||||
q = buf + filelen;
|
||||
*q++ = '.';
|
||||
*q++ = '~';
|
||||
*q = '0';
|
||||
q += all_9s;
|
||||
memcpy (q, p, versionlen + 2);
|
||||
|
||||
/* Add 1 to the version number. */
|
||||
|
||||
q += versionlen;
|
||||
while (*--q == '9')
|
||||
*q = '0';
|
||||
++*q;
|
||||
}
|
||||
|
||||
*buffer = buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Relative to DIR_FD, return the name of the new backup file for the
|
||||
existing file FILE, allocated with malloc.
|
||||
If RENAME, also rename FILE to the new name.
|
||||
On failure, return NULL and set errno.
|
||||
Do not call this function if backup_type == no_backups. */
|
||||
|
||||
char *
|
||||
backupfile_internal (int dir_fd, char const *file,
|
||||
enum backup_type backup_type, bool rename)
|
||||
{
|
||||
ptrdiff_t base_offset = last_component (file) - file;
|
||||
size_t filelen = base_offset + strlen (file + base_offset);
|
||||
|
||||
if (! simple_backup_suffix)
|
||||
set_simple_backup_suffix (NULL);
|
||||
|
||||
/* Allow room for simple or ".~N~" backups. The guess must be at
|
||||
least sizeof ".~1~", but otherwise will be adjusted as needed. */
|
||||
size_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1;
|
||||
size_t backup_suffix_size_guess = simple_backup_suffix_size;
|
||||
enum { GUESS = sizeof ".~12345~" };
|
||||
if (backup_suffix_size_guess < GUESS)
|
||||
backup_suffix_size_guess = GUESS;
|
||||
|
||||
ssize_t ssize = filelen + backup_suffix_size_guess + 1;
|
||||
char *s = malloc (ssize);
|
||||
if (!s)
|
||||
return s;
|
||||
|
||||
DIR *dirp = NULL;
|
||||
int sdir = -1;
|
||||
size_t base_max = 0;
|
||||
while (true)
|
||||
{
|
||||
memcpy (s, file, filelen + 1);
|
||||
|
||||
if (backup_type == simple_backups)
|
||||
memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
|
||||
else
|
||||
switch (numbered_backup (dir_fd, &s, ssize, filelen, base_offset,
|
||||
&dirp, &sdir))
|
||||
{
|
||||
case BACKUP_IS_SAME_LENGTH:
|
||||
break;
|
||||
|
||||
case BACKUP_IS_NEW:
|
||||
if (backup_type == numbered_existing_backups)
|
||||
{
|
||||
backup_type = simple_backups;
|
||||
memcpy (s + filelen, simple_backup_suffix,
|
||||
simple_backup_suffix_size);
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case BACKUP_IS_LONGER:
|
||||
check_extension (s, filelen, '~', sdir, &base_max);
|
||||
break;
|
||||
|
||||
case BACKUP_NOMEM:
|
||||
if (dirp)
|
||||
closedir (dirp);
|
||||
free (s);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (! rename)
|
||||
break;
|
||||
|
||||
if (sdir < 0)
|
||||
{
|
||||
sdir = AT_FDCWD;
|
||||
base_offset = 0;
|
||||
}
|
||||
unsigned flags = backup_type == simple_backups ? 0 : RENAME_NOREPLACE;
|
||||
if (renameatu (AT_FDCWD, file, sdir, s + base_offset, flags) == 0)
|
||||
break;
|
||||
int e = errno;
|
||||
if (e != EEXIST)
|
||||
{
|
||||
if (dirp)
|
||||
closedir (dirp);
|
||||
free (s);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirp)
|
||||
closedir (dirp);
|
||||
return s;
|
||||
}
|
62
lib/backupfile.h
Normal file
62
lib/backupfile.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* backupfile.h -- declarations for making Emacs style backup file names
|
||||
|
||||
Copyright (C) 1990-1992, 1997-1999, 2003-2004, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef BACKUPFILE_H_
|
||||
#define BACKUPFILE_H_
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* When to make backup files. */
|
||||
enum backup_type
|
||||
{
|
||||
/* Never make backups. */
|
||||
no_backups,
|
||||
|
||||
/* Make simple backups of every file. */
|
||||
simple_backups,
|
||||
|
||||
/* Make numbered backups of files that already have numbered backups,
|
||||
and simple backups of the others. */
|
||||
numbered_existing_backups,
|
||||
|
||||
/* Make numbered backups of every file. */
|
||||
numbered_backups
|
||||
};
|
||||
|
||||
#define VALID_BACKUP_TYPE(Type) \
|
||||
((unsigned int) (Type) <= numbered_backups)
|
||||
|
||||
extern char const *simple_backup_suffix;
|
||||
|
||||
void set_simple_backup_suffix (char const *);
|
||||
char *backup_file_rename (int, char const *, enum backup_type);
|
||||
char *find_backup_file_name (int, char const *, enum backup_type);
|
||||
enum backup_type get_version (char const *context, char const *arg);
|
||||
enum backup_type xget_version (char const *context, char const *arg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ! BACKUPFILE_H_ */
|
587
lib/base32.c
Normal file
587
lib/base32.c
Normal file
@ -0,0 +1,587 @@
|
||||
/* base32.c -- Encode binary data using printable characters.
|
||||
Copyright (C) 1999-2001, 2004-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Adapted from Simon Josefsson's base64 code by Gijs van Tulder.
|
||||
*
|
||||
* See also RFC 4648 <https://www.ietf.org/rfc/rfc4648.txt>.
|
||||
*
|
||||
* Be careful with error checking. Here is how you would typically
|
||||
* use these functions:
|
||||
*
|
||||
* bool ok = base32_decode_alloc (in, inlen, &out, &outlen);
|
||||
* if (!ok)
|
||||
* FAIL: input was not valid base32
|
||||
* if (out == NULL)
|
||||
* FAIL: memory allocation error
|
||||
* OK: data in OUT/OUTLEN
|
||||
*
|
||||
* size_t outlen = base32_encode_alloc (in, inlen, &out);
|
||||
* if (out == NULL && outlen == 0 && inlen != 0)
|
||||
* FAIL: input too long
|
||||
* if (out == NULL)
|
||||
* FAIL: memory allocation error
|
||||
* OK: data in OUT/OUTLEN.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Get prototype. */
|
||||
#include "base32.h"
|
||||
|
||||
/* Get malloc. */
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Get UCHAR_MAX. */
|
||||
#include <limits.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* C89 compliant way to cast 'char' to 'unsigned char'. */
|
||||
static unsigned char
|
||||
to_uchar (char ch)
|
||||
{
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* Base32 encode IN array of size INLEN into OUT array of size OUTLEN.
|
||||
If OUTLEN is less than BASE32_LENGTH(INLEN), write as many bytes as
|
||||
possible. If OUTLEN is larger than BASE32_LENGTH(INLEN), also zero
|
||||
terminate the output buffer. */
|
||||
void
|
||||
base32_encode (const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t outlen)
|
||||
{
|
||||
static const char b32str[32] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
while (inlen && outlen)
|
||||
{
|
||||
*out++ = b32str[(to_uchar (in[0]) >> 3) & 0x1f];
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ = b32str[((to_uchar (in[0]) << 2)
|
||||
+ (--inlen ? to_uchar (in[1]) >> 6 : 0))
|
||||
& 0x1f];
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b32str[(to_uchar (in[1]) >> 1) & 0x1f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b32str[((to_uchar (in[1]) << 4)
|
||||
+ (--inlen ? to_uchar (in[2]) >> 4 : 0))
|
||||
& 0x1f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b32str[((to_uchar (in[2]) << 1)
|
||||
+ (--inlen ? to_uchar (in[3]) >> 7 : 0))
|
||||
& 0x1f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b32str[(to_uchar (in[3]) >> 2) & 0x1f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b32str[((to_uchar (in[3]) << 3)
|
||||
+ (--inlen ? to_uchar (in[4]) >> 5 : 0))
|
||||
& 0x1f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ = inlen ? b32str[to_uchar (in[4]) & 0x1f] : '=';
|
||||
if (!--outlen)
|
||||
break;
|
||||
if (inlen)
|
||||
inlen--;
|
||||
if (inlen)
|
||||
in += 5;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
/* Allocate a buffer and store zero terminated base32 encoded data
|
||||
from array IN of size INLEN, returning BASE32_LENGTH(INLEN), i.e.,
|
||||
the length of the encoded data, excluding the terminating zero. On
|
||||
return, the OUT variable will hold a pointer to newly allocated
|
||||
memory that must be deallocated by the caller. If output string
|
||||
length would overflow, 0 is returned and OUT is set to NULL. If
|
||||
memory allocation failed, OUT is set to NULL, and the return value
|
||||
indicates length of the requested memory block, i.e.,
|
||||
BASE32_LENGTH(inlen) + 1. */
|
||||
size_t
|
||||
base32_encode_alloc (const char *in, size_t inlen, char **out)
|
||||
{
|
||||
size_t outlen = 1 + BASE32_LENGTH (inlen);
|
||||
|
||||
/* Check for overflow in outlen computation.
|
||||
*
|
||||
* If there is no overflow, outlen >= inlen.
|
||||
*
|
||||
* TODO Is this a sufficient check? (See the notes in base64.c.)
|
||||
*/
|
||||
if (inlen > outlen)
|
||||
{
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out = malloc (outlen);
|
||||
if (!*out)
|
||||
return outlen;
|
||||
|
||||
base32_encode (in, inlen, *out, outlen);
|
||||
|
||||
return outlen - 1;
|
||||
}
|
||||
|
||||
/* With this approach this file works independent of the charset used
|
||||
(think EBCDIC). However, it does assume that the characters in the
|
||||
Base32 alphabet (A-Z2-7) are encoded in 0..255. POSIX
|
||||
1003.1-2001 require that char and unsigned char are 8-bit
|
||||
quantities, though, taking care of that problem. But this may be a
|
||||
potential problem on non-POSIX C99 platforms.
|
||||
|
||||
IBM C V6 for AIX mishandles "#define B32(x) ...'x'...", so use "_"
|
||||
as the formal parameter rather than "x". */
|
||||
#define B32(_) \
|
||||
((_) == 'A' ? 0 \
|
||||
: (_) == 'B' ? 1 \
|
||||
: (_) == 'C' ? 2 \
|
||||
: (_) == 'D' ? 3 \
|
||||
: (_) == 'E' ? 4 \
|
||||
: (_) == 'F' ? 5 \
|
||||
: (_) == 'G' ? 6 \
|
||||
: (_) == 'H' ? 7 \
|
||||
: (_) == 'I' ? 8 \
|
||||
: (_) == 'J' ? 9 \
|
||||
: (_) == 'K' ? 10 \
|
||||
: (_) == 'L' ? 11 \
|
||||
: (_) == 'M' ? 12 \
|
||||
: (_) == 'N' ? 13 \
|
||||
: (_) == 'O' ? 14 \
|
||||
: (_) == 'P' ? 15 \
|
||||
: (_) == 'Q' ? 16 \
|
||||
: (_) == 'R' ? 17 \
|
||||
: (_) == 'S' ? 18 \
|
||||
: (_) == 'T' ? 19 \
|
||||
: (_) == 'U' ? 20 \
|
||||
: (_) == 'V' ? 21 \
|
||||
: (_) == 'W' ? 22 \
|
||||
: (_) == 'X' ? 23 \
|
||||
: (_) == 'Y' ? 24 \
|
||||
: (_) == 'Z' ? 25 \
|
||||
: (_) == '2' ? 26 \
|
||||
: (_) == '3' ? 27 \
|
||||
: (_) == '4' ? 28 \
|
||||
: (_) == '5' ? 29 \
|
||||
: (_) == '6' ? 30 \
|
||||
: (_) == '7' ? 31 \
|
||||
: -1)
|
||||
|
||||
static const signed char b32[0x100] = {
|
||||
B32 (0), B32 (1), B32 (2), B32 (3),
|
||||
B32 (4), B32 (5), B32 (6), B32 (7),
|
||||
B32 (8), B32 (9), B32 (10), B32 (11),
|
||||
B32 (12), B32 (13), B32 (14), B32 (15),
|
||||
B32 (16), B32 (17), B32 (18), B32 (19),
|
||||
B32 (20), B32 (21), B32 (22), B32 (23),
|
||||
B32 (24), B32 (25), B32 (26), B32 (27),
|
||||
B32 (28), B32 (29), B32 (30), B32 (31),
|
||||
B32 (32), B32 (33), B32 (34), B32 (35),
|
||||
B32 (36), B32 (37), B32 (38), B32 (39),
|
||||
B32 (40), B32 (41), B32 (42), B32 (43),
|
||||
B32 (44), B32 (45), B32 (46), B32 (47),
|
||||
B32 (48), B32 (49), B32 (50), B32 (51),
|
||||
B32 (52), B32 (53), B32 (54), B32 (55),
|
||||
B32 (56), B32 (57), B32 (58), B32 (59),
|
||||
B32 (60), B32 (61), B32 (62), B32 (63),
|
||||
B32 (32), B32 (65), B32 (66), B32 (67),
|
||||
B32 (68), B32 (69), B32 (70), B32 (71),
|
||||
B32 (72), B32 (73), B32 (74), B32 (75),
|
||||
B32 (76), B32 (77), B32 (78), B32 (79),
|
||||
B32 (80), B32 (81), B32 (82), B32 (83),
|
||||
B32 (84), B32 (85), B32 (86), B32 (87),
|
||||
B32 (88), B32 (89), B32 (90), B32 (91),
|
||||
B32 (92), B32 (93), B32 (94), B32 (95),
|
||||
B32 (96), B32 (97), B32 (98), B32 (99),
|
||||
B32 (100), B32 (101), B32 (102), B32 (103),
|
||||
B32 (104), B32 (105), B32 (106), B32 (107),
|
||||
B32 (108), B32 (109), B32 (110), B32 (111),
|
||||
B32 (112), B32 (113), B32 (114), B32 (115),
|
||||
B32 (116), B32 (117), B32 (118), B32 (119),
|
||||
B32 (120), B32 (121), B32 (122), B32 (123),
|
||||
B32 (124), B32 (125), B32 (126), B32 (127),
|
||||
B32 (128), B32 (129), B32 (130), B32 (131),
|
||||
B32 (132), B32 (133), B32 (134), B32 (135),
|
||||
B32 (136), B32 (137), B32 (138), B32 (139),
|
||||
B32 (140), B32 (141), B32 (142), B32 (143),
|
||||
B32 (144), B32 (145), B32 (146), B32 (147),
|
||||
B32 (148), B32 (149), B32 (150), B32 (151),
|
||||
B32 (152), B32 (153), B32 (154), B32 (155),
|
||||
B32 (156), B32 (157), B32 (158), B32 (159),
|
||||
B32 (160), B32 (161), B32 (162), B32 (163),
|
||||
B32 (132), B32 (165), B32 (166), B32 (167),
|
||||
B32 (168), B32 (169), B32 (170), B32 (171),
|
||||
B32 (172), B32 (173), B32 (174), B32 (175),
|
||||
B32 (176), B32 (177), B32 (178), B32 (179),
|
||||
B32 (180), B32 (181), B32 (182), B32 (183),
|
||||
B32 (184), B32 (185), B32 (186), B32 (187),
|
||||
B32 (188), B32 (189), B32 (190), B32 (191),
|
||||
B32 (192), B32 (193), B32 (194), B32 (195),
|
||||
B32 (196), B32 (197), B32 (198), B32 (199),
|
||||
B32 (200), B32 (201), B32 (202), B32 (203),
|
||||
B32 (204), B32 (205), B32 (206), B32 (207),
|
||||
B32 (208), B32 (209), B32 (210), B32 (211),
|
||||
B32 (212), B32 (213), B32 (214), B32 (215),
|
||||
B32 (216), B32 (217), B32 (218), B32 (219),
|
||||
B32 (220), B32 (221), B32 (222), B32 (223),
|
||||
B32 (224), B32 (225), B32 (226), B32 (227),
|
||||
B32 (228), B32 (229), B32 (230), B32 (231),
|
||||
B32 (232), B32 (233), B32 (234), B32 (235),
|
||||
B32 (236), B32 (237), B32 (238), B32 (239),
|
||||
B32 (240), B32 (241), B32 (242), B32 (243),
|
||||
B32 (244), B32 (245), B32 (246), B32 (247),
|
||||
B32 (248), B32 (249), B32 (250), B32 (251),
|
||||
B32 (252), B32 (253), B32 (254), B32 (255)
|
||||
};
|
||||
|
||||
#if UCHAR_MAX == 255
|
||||
# define uchar_in_range(c) true
|
||||
#else
|
||||
# define uchar_in_range(c) ((c) <= 255)
|
||||
#endif
|
||||
|
||||
/* Return true if CH is a character from the Base32 alphabet, and
|
||||
false otherwise. Note that '=' is padding and not considered to be
|
||||
part of the alphabet. */
|
||||
bool
|
||||
isbase32 (char ch)
|
||||
{
|
||||
return uchar_in_range (to_uchar (ch)) && 0 <= b32[to_uchar (ch)];
|
||||
}
|
||||
|
||||
/* Initialize decode-context buffer, CTX. */
|
||||
void
|
||||
base32_decode_ctx_init (struct base32_decode_context *ctx)
|
||||
{
|
||||
ctx->i = 0;
|
||||
}
|
||||
|
||||
/* If CTX->i is 0 or 8, there are eight or more bytes in [*IN..IN_END), and
|
||||
none of those eight is a newline, then return *IN. Otherwise, copy up to
|
||||
4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
|
||||
index CTX->i and setting CTX->i to reflect the number of bytes copied,
|
||||
and return CTX->buf. In either case, advance *IN to point to the byte
|
||||
after the last one processed, and set *N_NON_NEWLINE to the number of
|
||||
verified non-newline bytes accessible through the returned pointer. */
|
||||
static char *
|
||||
get_8 (struct base32_decode_context *ctx,
|
||||
char const *restrict *in, char const *restrict in_end,
|
||||
size_t *n_non_newline)
|
||||
{
|
||||
if (ctx->i == 8)
|
||||
ctx->i = 0;
|
||||
|
||||
if (ctx->i == 0)
|
||||
{
|
||||
char const *t = *in;
|
||||
if (8 <= in_end - *in && memchr (t, '\n', 8) == NULL)
|
||||
{
|
||||
/* This is the common case: no newline. */
|
||||
*in += 8;
|
||||
*n_non_newline = 8;
|
||||
return (char *) t;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Copy non-newline bytes into BUF. */
|
||||
char const *p = *in;
|
||||
while (p < in_end)
|
||||
{
|
||||
char c = *p++;
|
||||
if (c != '\n')
|
||||
{
|
||||
ctx->buf[ctx->i++] = c;
|
||||
if (ctx->i == 8)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*in = p;
|
||||
*n_non_newline = ctx->i;
|
||||
return ctx->buf;
|
||||
}
|
||||
}
|
||||
|
||||
#define return_false \
|
||||
do \
|
||||
{ \
|
||||
*outp = out; \
|
||||
return false; \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
/* Decode eight bytes of base32-encoded data, IN, of length INLEN
|
||||
into the output buffer, *OUT, of size *OUTLEN bytes. Return true if
|
||||
decoding is successful, false otherwise. If *OUTLEN is too small,
|
||||
as many bytes as possible are written to *OUT. On return, advance
|
||||
*OUT to point to the byte after the last one written, and decrement
|
||||
*OUTLEN to reflect the number of bytes remaining in *OUT. */
|
||||
static bool
|
||||
decode_8 (char const *restrict in, size_t inlen,
|
||||
char *restrict *outp, size_t *outleft)
|
||||
{
|
||||
char *out = *outp;
|
||||
if (inlen < 8)
|
||||
return false;
|
||||
|
||||
if (!isbase32 (in[0]) || !isbase32 (in[1]) )
|
||||
return false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b32[to_uchar (in[0])] << 3)
|
||||
| (b32[to_uchar (in[1])] >> 2));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (in[2] == '=')
|
||||
{
|
||||
if (in[3] != '=' || in[4] != '=' || in[5] != '='
|
||||
|| in[6] != '=' || in[7] != '=')
|
||||
return_false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isbase32 (in[2]) || !isbase32 (in[3]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b32[to_uchar (in[1])] << 6)
|
||||
| (b32[to_uchar (in[2])] << 1)
|
||||
| (b32[to_uchar (in[3])] >> 4));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (in[4] == '=')
|
||||
{
|
||||
if (in[5] != '=' || in[6] != '=' || in[7] != '=')
|
||||
return_false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isbase32 (in[4]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b32[to_uchar (in[3])] << 4)
|
||||
| (b32[to_uchar (in[4])] >> 1));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (in[5] == '=')
|
||||
{
|
||||
if (in[6] != '=' || in[7] != '=')
|
||||
return_false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isbase32 (in[5]) || !isbase32 (in[6]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b32[to_uchar (in[4])] << 7)
|
||||
| (b32[to_uchar (in[5])] << 2)
|
||||
| (b32[to_uchar (in[6])] >> 3));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (in[7] != '=')
|
||||
{
|
||||
if (!isbase32 (in[7]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b32[to_uchar (in[6])] << 5)
|
||||
| (b32[to_uchar (in[7])]));
|
||||
--*outleft;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*outp = out;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Decode base32-encoded input array IN of length INLEN to output array
|
||||
OUT that can hold *OUTLEN bytes. The input data may be interspersed
|
||||
with newlines. Return true if decoding was successful, i.e. if the
|
||||
input was valid base32 data, false otherwise. If *OUTLEN is too
|
||||
small, as many bytes as possible will be written to OUT. On return,
|
||||
*OUTLEN holds the length of decoded bytes in OUT. Note that as soon
|
||||
as any non-alphabet, non-newline character is encountered, decoding
|
||||
is stopped and false is returned. If INLEN is zero, then process
|
||||
only whatever data is stored in CTX.
|
||||
|
||||
Initially, CTX must have been initialized via base32_decode_ctx_init.
|
||||
Subsequent calls to this function must reuse whatever state is recorded
|
||||
in that buffer. It is necessary for when a octuple of base32 input
|
||||
bytes spans two input buffers.
|
||||
|
||||
If CTX is NULL then newlines are treated as garbage and the input
|
||||
buffer is processed as a unit. */
|
||||
|
||||
bool
|
||||
base32_decode_ctx (struct base32_decode_context *ctx,
|
||||
const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t *outlen)
|
||||
{
|
||||
size_t outleft = *outlen;
|
||||
bool ignore_newlines = ctx != NULL;
|
||||
bool flush_ctx = false;
|
||||
unsigned int ctx_i = 0;
|
||||
|
||||
if (ignore_newlines)
|
||||
{
|
||||
ctx_i = ctx->i;
|
||||
flush_ctx = inlen == 0;
|
||||
}
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t outleft_save = outleft;
|
||||
if (ctx_i == 0 && !flush_ctx)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
/* Save a copy of outleft, in case we need to re-parse this
|
||||
block of four bytes. */
|
||||
outleft_save = outleft;
|
||||
if (!decode_8 (in, inlen, &out, &outleft))
|
||||
break;
|
||||
|
||||
in += 8;
|
||||
inlen -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (inlen == 0 && !flush_ctx)
|
||||
break;
|
||||
|
||||
/* Handle the common case of 72-byte wrapped lines.
|
||||
This also handles any other multiple-of-8-byte wrapping. */
|
||||
if (inlen && *in == '\n' && ignore_newlines)
|
||||
{
|
||||
++in;
|
||||
--inlen;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Restore OUT and OUTLEFT. */
|
||||
out -= outleft_save - outleft;
|
||||
outleft = outleft_save;
|
||||
|
||||
{
|
||||
char const *in_end = in + inlen;
|
||||
char const *non_nl;
|
||||
|
||||
if (ignore_newlines)
|
||||
non_nl = get_8 (ctx, &in, in_end, &inlen);
|
||||
else
|
||||
non_nl = in; /* Might have nl in this case. */
|
||||
|
||||
/* If the input is empty or consists solely of newlines (0 non-newlines),
|
||||
then we're done. Likewise if there are fewer than 8 bytes when not
|
||||
flushing context and not treating newlines as garbage. */
|
||||
if (inlen == 0 || (inlen < 8 && !flush_ctx && ignore_newlines))
|
||||
{
|
||||
inlen = 0;
|
||||
break;
|
||||
}
|
||||
if (!decode_8 (non_nl, inlen, &out, &outleft))
|
||||
break;
|
||||
|
||||
inlen = in_end - in;
|
||||
}
|
||||
}
|
||||
|
||||
*outlen -= outleft;
|
||||
|
||||
return inlen == 0;
|
||||
}
|
||||
|
||||
/* Allocate an output buffer in *OUT, and decode the base32 encoded
|
||||
data stored in IN of size INLEN to the *OUT buffer. On return, the
|
||||
size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
|
||||
if the caller is not interested in the decoded length. *OUT may be
|
||||
NULL to indicate an out of memory error, in which case *OUTLEN
|
||||
contains the size of the memory block needed. The function returns
|
||||
true on successful decoding and memory allocation errors. (Use the
|
||||
*OUT and *OUTLEN parameters to differentiate between successful
|
||||
decoding and memory error.) The function returns false if the
|
||||
input was invalid, in which case *OUT is NULL and *OUTLEN is
|
||||
undefined. */
|
||||
bool
|
||||
base32_decode_alloc_ctx (struct base32_decode_context *ctx,
|
||||
const char *in, size_t inlen, char **out,
|
||||
size_t *outlen)
|
||||
{
|
||||
/* This may allocate a few bytes too many, depending on input,
|
||||
but it's not worth the extra CPU time to compute the exact size.
|
||||
The exact size is 5 * inlen / 8, minus one or more bytes if the
|
||||
input is padded with one or more "=".
|
||||
Dividing before multiplying avoids the possibility of overflow. */
|
||||
size_t needlen = 5 * (inlen / 8) + 5;
|
||||
|
||||
*out = malloc (needlen);
|
||||
if (!*out)
|
||||
return true;
|
||||
|
||||
if (!base32_decode_ctx (ctx, in, inlen, *out, &needlen))
|
||||
{
|
||||
free (*out);
|
||||
*out = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*outlen = needlen;
|
||||
|
||||
return true;
|
||||
}
|
60
lib/base32.h
Normal file
60
lib/base32.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* base32.h -- Encode binary data using printable characters.
|
||||
Copyright (C) 2004-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
Adapted from Simon Josefsson's base64 code by Gijs van Tulder.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef BASE32_H
|
||||
# define BASE32_H
|
||||
|
||||
/* Get size_t. */
|
||||
# include <stddef.h>
|
||||
|
||||
/* Get bool. */
|
||||
# include <stdbool.h>
|
||||
|
||||
/* This uses that the expression (n+(k-1))/k means the smallest
|
||||
integer >= n/k, i.e., the ceiling of n/k. */
|
||||
# define BASE32_LENGTH(inlen) ((((inlen) + 4) / 5) * 8)
|
||||
|
||||
struct base32_decode_context
|
||||
{
|
||||
unsigned int i;
|
||||
char buf[8];
|
||||
};
|
||||
|
||||
extern bool isbase32 (char ch) _GL_ATTRIBUTE_CONST;
|
||||
|
||||
extern void base32_encode (const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t outlen);
|
||||
|
||||
extern size_t base32_encode_alloc (const char *in, size_t inlen, char **out);
|
||||
|
||||
extern void base32_decode_ctx_init (struct base32_decode_context *ctx);
|
||||
|
||||
extern bool base32_decode_ctx (struct base32_decode_context *ctx,
|
||||
const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t *outlen);
|
||||
|
||||
extern bool base32_decode_alloc_ctx (struct base32_decode_context *ctx,
|
||||
const char *in, size_t inlen,
|
||||
char **out, size_t *outlen);
|
||||
|
||||
#define base32_decode(in, inlen, out, outlen) \
|
||||
base32_decode_ctx (NULL, in, inlen, out, outlen)
|
||||
|
||||
#define base32_decode_alloc(in, inlen, out, outlen) \
|
||||
base32_decode_alloc_ctx (NULL, in, inlen, out, outlen)
|
||||
|
||||
#endif /* BASE32_H */
|
605
lib/base64.c
Normal file
605
lib/base64.c
Normal file
@ -0,0 +1,605 @@
|
||||
/* base64.c -- Encode binary data using printable characters.
|
||||
Copyright (C) 1999-2001, 2004-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Simon Josefsson. Partially adapted from GNU MailUtils
|
||||
* (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
|
||||
* from Paul Eggert, Bruno Haible, and Stepan Kasal.
|
||||
*
|
||||
* See also RFC 4648 <https://www.ietf.org/rfc/rfc4648.txt>.
|
||||
*
|
||||
* Be careful with error checking. Here is how you would typically
|
||||
* use these functions:
|
||||
*
|
||||
* bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
|
||||
* if (!ok)
|
||||
* FAIL: input was not valid base64
|
||||
* if (out == NULL)
|
||||
* FAIL: memory allocation error
|
||||
* OK: data in OUT/OUTLEN
|
||||
*
|
||||
* size_t outlen = base64_encode_alloc (in, inlen, &out);
|
||||
* if (out == NULL && outlen == 0 && inlen != 0)
|
||||
* FAIL: input too long
|
||||
* if (out == NULL)
|
||||
* FAIL: memory allocation error
|
||||
* OK: data in OUT/OUTLEN.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Get prototype. */
|
||||
#include "base64.h"
|
||||
|
||||
/* Get malloc. */
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Get UCHAR_MAX. */
|
||||
#include <limits.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* C89 compliant way to cast 'char' to 'unsigned char'. */
|
||||
static unsigned char
|
||||
to_uchar (char ch)
|
||||
{
|
||||
return ch;
|
||||
}
|
||||
|
||||
static const char b64c[64] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* Base64 encode IN array of size INLEN into OUT array. OUT needs
|
||||
to be of length >= BASE64_LENGTH(INLEN), and INLEN needs to be
|
||||
a multiple of 3. */
|
||||
static void
|
||||
base64_encode_fast (const char *restrict in, size_t inlen, char *restrict out)
|
||||
{
|
||||
while (inlen)
|
||||
{
|
||||
*out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f];
|
||||
*out++ = b64c[((to_uchar (in[0]) << 4) + (to_uchar (in[1]) >> 4)) & 0x3f];
|
||||
*out++ = b64c[((to_uchar (in[1]) << 2) + (to_uchar (in[2]) >> 6)) & 0x3f];
|
||||
*out++ = b64c[to_uchar (in[2]) & 0x3f];
|
||||
|
||||
inlen -= 3;
|
||||
in += 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
|
||||
If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
|
||||
possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
|
||||
terminate the output buffer. */
|
||||
void
|
||||
base64_encode (const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t outlen)
|
||||
{
|
||||
/* Note this outlen constraint can be enforced at compile time.
|
||||
I.E. that the output buffer is exactly large enough to hold
|
||||
the encoded inlen bytes. The inlen constraints (of corresponding
|
||||
to outlen, and being a multiple of 3) can change at runtime
|
||||
at the end of input. However the common case when reading
|
||||
large inputs is to have both constraints satisfied, so we depend
|
||||
on both in base_encode_fast(). */
|
||||
if (outlen % 4 == 0 && inlen == outlen / 4 * 3)
|
||||
{
|
||||
base64_encode_fast (in, inlen, out);
|
||||
return;
|
||||
}
|
||||
|
||||
while (inlen && outlen)
|
||||
{
|
||||
*out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f];
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ = b64c[((to_uchar (in[0]) << 4)
|
||||
+ (--inlen ? to_uchar (in[1]) >> 4 : 0))
|
||||
& 0x3f];
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ =
|
||||
(inlen
|
||||
? b64c[((to_uchar (in[1]) << 2)
|
||||
+ (--inlen ? to_uchar (in[2]) >> 6 : 0))
|
||||
& 0x3f]
|
||||
: '=');
|
||||
if (!--outlen)
|
||||
break;
|
||||
*out++ = inlen ? b64c[to_uchar (in[2]) & 0x3f] : '=';
|
||||
if (!--outlen)
|
||||
break;
|
||||
if (inlen)
|
||||
inlen--;
|
||||
if (inlen)
|
||||
in += 3;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
/* Allocate a buffer and store zero terminated base64 encoded data
|
||||
from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
|
||||
the length of the encoded data, excluding the terminating zero. On
|
||||
return, the OUT variable will hold a pointer to newly allocated
|
||||
memory that must be deallocated by the caller. If output string
|
||||
length would overflow, 0 is returned and OUT is set to NULL. If
|
||||
memory allocation failed, OUT is set to NULL, and the return value
|
||||
indicates length of the requested memory block, i.e.,
|
||||
BASE64_LENGTH(inlen) + 1. */
|
||||
size_t
|
||||
base64_encode_alloc (const char *in, size_t inlen, char **out)
|
||||
{
|
||||
size_t outlen = 1 + BASE64_LENGTH (inlen);
|
||||
|
||||
/* Check for overflow in outlen computation.
|
||||
*
|
||||
* If there is no overflow, outlen >= inlen.
|
||||
*
|
||||
* If the operation (inlen + 2) overflows then it yields at most +1, so
|
||||
* outlen is 0.
|
||||
*
|
||||
* If the multiplication overflows, we lose at least half of the
|
||||
* correct value, so the result is < ((inlen + 2) / 3) * 2, which is
|
||||
* less than (inlen + 2) * 0.66667, which is less than inlen as soon as
|
||||
* (inlen > 4).
|
||||
*/
|
||||
if (inlen > outlen)
|
||||
{
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out = malloc (outlen);
|
||||
if (!*out)
|
||||
return outlen;
|
||||
|
||||
base64_encode (in, inlen, *out, outlen);
|
||||
|
||||
return outlen - 1;
|
||||
}
|
||||
|
||||
/* With this approach this file works independent of the charset used
|
||||
(think EBCDIC). However, it does assume that the characters in the
|
||||
Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX
|
||||
1003.1-2001 require that char and unsigned char are 8-bit
|
||||
quantities, though, taking care of that problem. But this may be a
|
||||
potential problem on non-POSIX C99 platforms.
|
||||
|
||||
IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
|
||||
as the formal parameter rather than "x". */
|
||||
#define B64(_) \
|
||||
((_) == 'A' ? 0 \
|
||||
: (_) == 'B' ? 1 \
|
||||
: (_) == 'C' ? 2 \
|
||||
: (_) == 'D' ? 3 \
|
||||
: (_) == 'E' ? 4 \
|
||||
: (_) == 'F' ? 5 \
|
||||
: (_) == 'G' ? 6 \
|
||||
: (_) == 'H' ? 7 \
|
||||
: (_) == 'I' ? 8 \
|
||||
: (_) == 'J' ? 9 \
|
||||
: (_) == 'K' ? 10 \
|
||||
: (_) == 'L' ? 11 \
|
||||
: (_) == 'M' ? 12 \
|
||||
: (_) == 'N' ? 13 \
|
||||
: (_) == 'O' ? 14 \
|
||||
: (_) == 'P' ? 15 \
|
||||
: (_) == 'Q' ? 16 \
|
||||
: (_) == 'R' ? 17 \
|
||||
: (_) == 'S' ? 18 \
|
||||
: (_) == 'T' ? 19 \
|
||||
: (_) == 'U' ? 20 \
|
||||
: (_) == 'V' ? 21 \
|
||||
: (_) == 'W' ? 22 \
|
||||
: (_) == 'X' ? 23 \
|
||||
: (_) == 'Y' ? 24 \
|
||||
: (_) == 'Z' ? 25 \
|
||||
: (_) == 'a' ? 26 \
|
||||
: (_) == 'b' ? 27 \
|
||||
: (_) == 'c' ? 28 \
|
||||
: (_) == 'd' ? 29 \
|
||||
: (_) == 'e' ? 30 \
|
||||
: (_) == 'f' ? 31 \
|
||||
: (_) == 'g' ? 32 \
|
||||
: (_) == 'h' ? 33 \
|
||||
: (_) == 'i' ? 34 \
|
||||
: (_) == 'j' ? 35 \
|
||||
: (_) == 'k' ? 36 \
|
||||
: (_) == 'l' ? 37 \
|
||||
: (_) == 'm' ? 38 \
|
||||
: (_) == 'n' ? 39 \
|
||||
: (_) == 'o' ? 40 \
|
||||
: (_) == 'p' ? 41 \
|
||||
: (_) == 'q' ? 42 \
|
||||
: (_) == 'r' ? 43 \
|
||||
: (_) == 's' ? 44 \
|
||||
: (_) == 't' ? 45 \
|
||||
: (_) == 'u' ? 46 \
|
||||
: (_) == 'v' ? 47 \
|
||||
: (_) == 'w' ? 48 \
|
||||
: (_) == 'x' ? 49 \
|
||||
: (_) == 'y' ? 50 \
|
||||
: (_) == 'z' ? 51 \
|
||||
: (_) == '0' ? 52 \
|
||||
: (_) == '1' ? 53 \
|
||||
: (_) == '2' ? 54 \
|
||||
: (_) == '3' ? 55 \
|
||||
: (_) == '4' ? 56 \
|
||||
: (_) == '5' ? 57 \
|
||||
: (_) == '6' ? 58 \
|
||||
: (_) == '7' ? 59 \
|
||||
: (_) == '8' ? 60 \
|
||||
: (_) == '9' ? 61 \
|
||||
: (_) == '+' ? 62 \
|
||||
: (_) == '/' ? 63 \
|
||||
: -1)
|
||||
|
||||
static const signed char b64[0x100] = {
|
||||
B64 (0), B64 (1), B64 (2), B64 (3),
|
||||
B64 (4), B64 (5), B64 (6), B64 (7),
|
||||
B64 (8), B64 (9), B64 (10), B64 (11),
|
||||
B64 (12), B64 (13), B64 (14), B64 (15),
|
||||
B64 (16), B64 (17), B64 (18), B64 (19),
|
||||
B64 (20), B64 (21), B64 (22), B64 (23),
|
||||
B64 (24), B64 (25), B64 (26), B64 (27),
|
||||
B64 (28), B64 (29), B64 (30), B64 (31),
|
||||
B64 (32), B64 (33), B64 (34), B64 (35),
|
||||
B64 (36), B64 (37), B64 (38), B64 (39),
|
||||
B64 (40), B64 (41), B64 (42), B64 (43),
|
||||
B64 (44), B64 (45), B64 (46), B64 (47),
|
||||
B64 (48), B64 (49), B64 (50), B64 (51),
|
||||
B64 (52), B64 (53), B64 (54), B64 (55),
|
||||
B64 (56), B64 (57), B64 (58), B64 (59),
|
||||
B64 (60), B64 (61), B64 (62), B64 (63),
|
||||
B64 (64), B64 (65), B64 (66), B64 (67),
|
||||
B64 (68), B64 (69), B64 (70), B64 (71),
|
||||
B64 (72), B64 (73), B64 (74), B64 (75),
|
||||
B64 (76), B64 (77), B64 (78), B64 (79),
|
||||
B64 (80), B64 (81), B64 (82), B64 (83),
|
||||
B64 (84), B64 (85), B64 (86), B64 (87),
|
||||
B64 (88), B64 (89), B64 (90), B64 (91),
|
||||
B64 (92), B64 (93), B64 (94), B64 (95),
|
||||
B64 (96), B64 (97), B64 (98), B64 (99),
|
||||
B64 (100), B64 (101), B64 (102), B64 (103),
|
||||
B64 (104), B64 (105), B64 (106), B64 (107),
|
||||
B64 (108), B64 (109), B64 (110), B64 (111),
|
||||
B64 (112), B64 (113), B64 (114), B64 (115),
|
||||
B64 (116), B64 (117), B64 (118), B64 (119),
|
||||
B64 (120), B64 (121), B64 (122), B64 (123),
|
||||
B64 (124), B64 (125), B64 (126), B64 (127),
|
||||
B64 (128), B64 (129), B64 (130), B64 (131),
|
||||
B64 (132), B64 (133), B64 (134), B64 (135),
|
||||
B64 (136), B64 (137), B64 (138), B64 (139),
|
||||
B64 (140), B64 (141), B64 (142), B64 (143),
|
||||
B64 (144), B64 (145), B64 (146), B64 (147),
|
||||
B64 (148), B64 (149), B64 (150), B64 (151),
|
||||
B64 (152), B64 (153), B64 (154), B64 (155),
|
||||
B64 (156), B64 (157), B64 (158), B64 (159),
|
||||
B64 (160), B64 (161), B64 (162), B64 (163),
|
||||
B64 (164), B64 (165), B64 (166), B64 (167),
|
||||
B64 (168), B64 (169), B64 (170), B64 (171),
|
||||
B64 (172), B64 (173), B64 (174), B64 (175),
|
||||
B64 (176), B64 (177), B64 (178), B64 (179),
|
||||
B64 (180), B64 (181), B64 (182), B64 (183),
|
||||
B64 (184), B64 (185), B64 (186), B64 (187),
|
||||
B64 (188), B64 (189), B64 (190), B64 (191),
|
||||
B64 (192), B64 (193), B64 (194), B64 (195),
|
||||
B64 (196), B64 (197), B64 (198), B64 (199),
|
||||
B64 (200), B64 (201), B64 (202), B64 (203),
|
||||
B64 (204), B64 (205), B64 (206), B64 (207),
|
||||
B64 (208), B64 (209), B64 (210), B64 (211),
|
||||
B64 (212), B64 (213), B64 (214), B64 (215),
|
||||
B64 (216), B64 (217), B64 (218), B64 (219),
|
||||
B64 (220), B64 (221), B64 (222), B64 (223),
|
||||
B64 (224), B64 (225), B64 (226), B64 (227),
|
||||
B64 (228), B64 (229), B64 (230), B64 (231),
|
||||
B64 (232), B64 (233), B64 (234), B64 (235),
|
||||
B64 (236), B64 (237), B64 (238), B64 (239),
|
||||
B64 (240), B64 (241), B64 (242), B64 (243),
|
||||
B64 (244), B64 (245), B64 (246), B64 (247),
|
||||
B64 (248), B64 (249), B64 (250), B64 (251),
|
||||
B64 (252), B64 (253), B64 (254), B64 (255)
|
||||
};
|
||||
|
||||
#if UCHAR_MAX == 255
|
||||
# define uchar_in_range(c) true
|
||||
#else
|
||||
# define uchar_in_range(c) ((c) <= 255)
|
||||
#endif
|
||||
|
||||
/* Return true if CH is a character from the Base64 alphabet, and
|
||||
false otherwise. Note that '=' is padding and not considered to be
|
||||
part of the alphabet. */
|
||||
bool
|
||||
isbase64 (char ch)
|
||||
{
|
||||
return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
|
||||
}
|
||||
|
||||
/* Initialize decode-context buffer, CTX. */
|
||||
void
|
||||
base64_decode_ctx_init (struct base64_decode_context *ctx)
|
||||
{
|
||||
ctx->i = 0;
|
||||
}
|
||||
|
||||
/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
|
||||
none of those four is a newline, then return *IN. Otherwise, copy up to
|
||||
4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
|
||||
index CTX->i and setting CTX->i to reflect the number of bytes copied,
|
||||
and return CTX->buf. In either case, advance *IN to point to the byte
|
||||
after the last one processed, and set *N_NON_NEWLINE to the number of
|
||||
verified non-newline bytes accessible through the returned pointer. */
|
||||
static char *
|
||||
get_4 (struct base64_decode_context *ctx,
|
||||
char const *restrict *in, char const *restrict in_end,
|
||||
size_t *n_non_newline)
|
||||
{
|
||||
if (ctx->i == 4)
|
||||
ctx->i = 0;
|
||||
|
||||
if (ctx->i == 0)
|
||||
{
|
||||
char const *t = *in;
|
||||
if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
|
||||
{
|
||||
/* This is the common case: no newline. */
|
||||
*in += 4;
|
||||
*n_non_newline = 4;
|
||||
return (char *) t;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Copy non-newline bytes into BUF. */
|
||||
char const *p = *in;
|
||||
while (p < in_end)
|
||||
{
|
||||
char c = *p++;
|
||||
if (c != '\n')
|
||||
{
|
||||
ctx->buf[ctx->i++] = c;
|
||||
if (ctx->i == 4)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*in = p;
|
||||
*n_non_newline = ctx->i;
|
||||
return ctx->buf;
|
||||
}
|
||||
}
|
||||
|
||||
#define return_false \
|
||||
do \
|
||||
{ \
|
||||
*outp = out; \
|
||||
return false; \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
|
||||
into the output buffer, *OUT, of size *OUTLEN bytes. Return true if
|
||||
decoding is successful, false otherwise. If *OUTLEN is too small,
|
||||
as many bytes as possible are written to *OUT. On return, advance
|
||||
*OUT to point to the byte after the last one written, and decrement
|
||||
*OUTLEN to reflect the number of bytes remaining in *OUT. */
|
||||
static bool
|
||||
decode_4 (char const *restrict in, size_t inlen,
|
||||
char *restrict *outp, size_t *outleft)
|
||||
{
|
||||
char *out = *outp;
|
||||
if (inlen < 2)
|
||||
return false;
|
||||
|
||||
if (!isbase64 (in[0]) || !isbase64 (in[1]))
|
||||
return false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = ((b64[to_uchar (in[0])] << 2)
|
||||
| (b64[to_uchar (in[1])] >> 4));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (inlen == 2)
|
||||
return_false;
|
||||
|
||||
if (in[2] == '=')
|
||||
{
|
||||
if (inlen != 4)
|
||||
return_false;
|
||||
|
||||
if (in[3] != '=')
|
||||
return_false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isbase64 (in[2]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
|
||||
| (b64[to_uchar (in[2])] >> 2));
|
||||
--*outleft;
|
||||
}
|
||||
|
||||
if (inlen == 3)
|
||||
return_false;
|
||||
|
||||
if (in[3] == '=')
|
||||
{
|
||||
if (inlen != 4)
|
||||
return_false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isbase64 (in[3]))
|
||||
return_false;
|
||||
|
||||
if (*outleft)
|
||||
{
|
||||
*out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
|
||||
| b64[to_uchar (in[3])]);
|
||||
--*outleft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*outp = out;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Decode base64-encoded input array IN of length INLEN to output array
|
||||
OUT that can hold *OUTLEN bytes. The input data may be interspersed
|
||||
with newlines. Return true if decoding was successful, i.e. if the
|
||||
input was valid base64 data, false otherwise. If *OUTLEN is too
|
||||
small, as many bytes as possible will be written to OUT. On return,
|
||||
*OUTLEN holds the length of decoded bytes in OUT. Note that as soon
|
||||
as any non-alphabet, non-newline character is encountered, decoding
|
||||
is stopped and false is returned. If INLEN is zero, then process
|
||||
only whatever data is stored in CTX.
|
||||
|
||||
Initially, CTX must have been initialized via base64_decode_ctx_init.
|
||||
Subsequent calls to this function must reuse whatever state is recorded
|
||||
in that buffer. It is necessary for when a quadruple of base64 input
|
||||
bytes spans two input buffers.
|
||||
|
||||
If CTX is NULL then newlines are treated as garbage and the input
|
||||
buffer is processed as a unit. */
|
||||
|
||||
bool
|
||||
base64_decode_ctx (struct base64_decode_context *ctx,
|
||||
const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t *outlen)
|
||||
{
|
||||
size_t outleft = *outlen;
|
||||
bool ignore_newlines = ctx != NULL;
|
||||
bool flush_ctx = false;
|
||||
unsigned int ctx_i = 0;
|
||||
|
||||
if (ignore_newlines)
|
||||
{
|
||||
ctx_i = ctx->i;
|
||||
flush_ctx = inlen == 0;
|
||||
}
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t outleft_save = outleft;
|
||||
if (ctx_i == 0 && !flush_ctx)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
/* Save a copy of outleft, in case we need to re-parse this
|
||||
block of four bytes. */
|
||||
outleft_save = outleft;
|
||||
if (!decode_4 (in, inlen, &out, &outleft))
|
||||
break;
|
||||
|
||||
in += 4;
|
||||
inlen -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (inlen == 0 && !flush_ctx)
|
||||
break;
|
||||
|
||||
/* Handle the common case of 72-byte wrapped lines.
|
||||
This also handles any other multiple-of-4-byte wrapping. */
|
||||
if (inlen && *in == '\n' && ignore_newlines)
|
||||
{
|
||||
++in;
|
||||
--inlen;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Restore OUT and OUTLEFT. */
|
||||
out -= outleft_save - outleft;
|
||||
outleft = outleft_save;
|
||||
|
||||
{
|
||||
char const *in_end = in + inlen;
|
||||
char const *non_nl;
|
||||
|
||||
if (ignore_newlines)
|
||||
non_nl = get_4 (ctx, &in, in_end, &inlen);
|
||||
else
|
||||
non_nl = in; /* Might have nl in this case. */
|
||||
|
||||
/* If the input is empty or consists solely of newlines (0 non-newlines),
|
||||
then we're done. Likewise if there are fewer than 4 bytes when not
|
||||
flushing context and not treating newlines as garbage. */
|
||||
if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines))
|
||||
{
|
||||
inlen = 0;
|
||||
break;
|
||||
}
|
||||
if (!decode_4 (non_nl, inlen, &out, &outleft))
|
||||
break;
|
||||
|
||||
inlen = in_end - in;
|
||||
}
|
||||
}
|
||||
|
||||
*outlen -= outleft;
|
||||
|
||||
return inlen == 0;
|
||||
}
|
||||
|
||||
/* Allocate an output buffer in *OUT, and decode the base64 encoded
|
||||
data stored in IN of size INLEN to the *OUT buffer. On return, the
|
||||
size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
|
||||
if the caller is not interested in the decoded length. *OUT may be
|
||||
NULL to indicate an out of memory error, in which case *OUTLEN
|
||||
contains the size of the memory block needed. The function returns
|
||||
true on successful decoding and memory allocation errors. (Use the
|
||||
*OUT and *OUTLEN parameters to differentiate between successful
|
||||
decoding and memory error.) The function returns false if the
|
||||
input was invalid, in which case *OUT is NULL and *OUTLEN is
|
||||
undefined. */
|
||||
bool
|
||||
base64_decode_alloc_ctx (struct base64_decode_context *ctx,
|
||||
const char *in, size_t inlen, char **out,
|
||||
size_t *outlen)
|
||||
{
|
||||
/* This may allocate a few bytes too many, depending on input,
|
||||
but it's not worth the extra CPU time to compute the exact size.
|
||||
The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
|
||||
input ends with "=" and minus another 1 if the input ends with "==".
|
||||
Dividing before multiplying avoids the possibility of overflow. */
|
||||
size_t needlen = 3 * (inlen / 4) + 3;
|
||||
|
||||
*out = malloc (needlen);
|
||||
if (!*out)
|
||||
return true;
|
||||
|
||||
if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
|
||||
{
|
||||
free (*out);
|
||||
*out = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*outlen = needlen;
|
||||
|
||||
return true;
|
||||
}
|
68
lib/base64.h
Normal file
68
lib/base64.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* base64.h -- Encode binary data using printable characters.
|
||||
Copyright (C) 2004-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
Written by Simon Josefsson.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef BASE64_H
|
||||
# define BASE64_H
|
||||
|
||||
/* Get size_t. */
|
||||
# include <stddef.h>
|
||||
|
||||
/* Get bool. */
|
||||
# include <stdbool.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/* This uses that the expression (n+(k-1))/k means the smallest
|
||||
integer >= n/k, i.e., the ceiling of n/k. */
|
||||
# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
|
||||
|
||||
struct base64_decode_context
|
||||
{
|
||||
unsigned int i;
|
||||
char buf[4];
|
||||
};
|
||||
|
||||
extern bool isbase64 (char ch) _GL_ATTRIBUTE_CONST;
|
||||
|
||||
extern void base64_encode (const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t outlen);
|
||||
|
||||
extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
|
||||
|
||||
extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
|
||||
|
||||
extern bool base64_decode_ctx (struct base64_decode_context *ctx,
|
||||
const char *restrict in, size_t inlen,
|
||||
char *restrict out, size_t *outlen);
|
||||
|
||||
extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
|
||||
const char *in, size_t inlen,
|
||||
char **out, size_t *outlen);
|
||||
|
||||
#define base64_decode(in, inlen, out, outlen) \
|
||||
base64_decode_ctx (NULL, in, inlen, out, outlen)
|
||||
|
||||
#define base64_decode_alloc(in, inlen, out, outlen) \
|
||||
base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif /* BASE64_H */
|
75
lib/basename-lgpl.c
Normal file
75
lib/basename-lgpl.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* basename.c -- return the last element in a file name
|
||||
|
||||
Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "dirname.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Return the address of the last file name component of NAME. If
|
||||
NAME has no relative file name components because it is a file
|
||||
system root, return the empty string. */
|
||||
|
||||
char *
|
||||
last_component (char const *name)
|
||||
{
|
||||
char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
|
||||
char const *p;
|
||||
bool saw_slash = false;
|
||||
|
||||
while (ISSLASH (*base))
|
||||
base++;
|
||||
|
||||
for (p = base; *p; p++)
|
||||
{
|
||||
if (ISSLASH (*p))
|
||||
saw_slash = true;
|
||||
else if (saw_slash)
|
||||
{
|
||||
base = p;
|
||||
saw_slash = false;
|
||||
}
|
||||
}
|
||||
|
||||
return (char *) base;
|
||||
}
|
||||
|
||||
/* Return the length of the basename NAME. Typically NAME is the
|
||||
value returned by base_name or last_component. Act like strlen
|
||||
(NAME), except omit all trailing slashes. */
|
||||
|
||||
size_t
|
||||
base_len (char const *name)
|
||||
{
|
||||
size_t len;
|
||||
size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
|
||||
|
||||
for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--)
|
||||
continue;
|
||||
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1
|
||||
&& ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2])
|
||||
return 2;
|
||||
|
||||
if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len
|
||||
&& len == prefix_len && ISSLASH (name[prefix_len]))
|
||||
return prefix_len + 1;
|
||||
|
||||
return len;
|
||||
}
|
58
lib/basename.c
Normal file
58
lib/basename.c
Normal file
@ -0,0 +1,58 @@
|
||||
/* basename.c -- return the last element in a file name
|
||||
|
||||
Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "dirname.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "xalloc.h"
|
||||
#include "xstrndup.h"
|
||||
|
||||
char *
|
||||
base_name (char const *name)
|
||||
{
|
||||
char const *base = last_component (name);
|
||||
size_t length;
|
||||
|
||||
/* If there is no last component, then name is a file system root or the
|
||||
empty string. */
|
||||
if (! *base)
|
||||
return xstrndup (name, base_len (name));
|
||||
|
||||
/* Collapse a sequence of trailing slashes into one. */
|
||||
length = base_len (base);
|
||||
if (ISSLASH (base[length]))
|
||||
length++;
|
||||
|
||||
/* On systems with drive letters, "a/b:c" must return "./b:c" rather
|
||||
than "b:c" to avoid confusion with a drive letter. On systems
|
||||
with pure POSIX semantics, this is not an issue. */
|
||||
if (FILE_SYSTEM_PREFIX_LEN (base))
|
||||
{
|
||||
char *p = xmalloc (length + 3);
|
||||
p[0] = '.';
|
||||
p[1] = '/';
|
||||
memcpy (p + 2, base, length);
|
||||
p[length + 2] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Finally, copy the basename. */
|
||||
return xstrndup (base, length);
|
||||
}
|
39
lib/binary-io.c
Normal file
39
lib/binary-io.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* Binary mode I/O.
|
||||
Copyright 2017-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define BINARY_IO_INLINE _GL_EXTERN_INLINE
|
||||
#include "binary-io.h"
|
||||
|
||||
#if defined __DJGPP__ || defined __EMX__
|
||||
# include <unistd.h>
|
||||
|
||||
int
|
||||
set_binary_mode (int fd, int mode)
|
||||
{
|
||||
if (isatty (fd))
|
||||
/* If FD refers to a console (not a pipe, not a regular file),
|
||||
O_TEXT is the only reasonable mode, both on input and on output.
|
||||
Silently ignore the request. If we were to return -1 here,
|
||||
all programs that use xset_binary_mode would fail when run
|
||||
with console input or console output. */
|
||||
return O_TEXT;
|
||||
else
|
||||
return __gl_setmode (fd, mode);
|
||||
}
|
||||
|
||||
#endif
|
77
lib/binary-io.h
Normal file
77
lib/binary-io.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* Binary mode I/O.
|
||||
Copyright (C) 2001, 2003, 2005, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _BINARY_H
|
||||
#define _BINARY_H
|
||||
|
||||
/* For systems that distinguish between text and binary I/O.
|
||||
O_BINARY is guaranteed by the gnulib <fcntl.h>. */
|
||||
#include <fcntl.h>
|
||||
|
||||
/* The MSVC7 <stdio.h> doesn't like to be included after '#define fileno ...',
|
||||
so we include it here first. */
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _GL_INLINE_HEADER_BEGIN
|
||||
#error "Please include config.h first."
|
||||
#endif
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef BINARY_IO_INLINE
|
||||
# define BINARY_IO_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
#if O_BINARY
|
||||
# if defined __EMX__ || defined __DJGPP__ || defined __CYGWIN__
|
||||
# include <io.h> /* declares setmode() */
|
||||
# define __gl_setmode setmode
|
||||
# else
|
||||
# define __gl_setmode _setmode
|
||||
# undef fileno
|
||||
# define fileno _fileno
|
||||
# endif
|
||||
#else
|
||||
/* On reasonable systems, binary I/O is the only choice. */
|
||||
/* Use a function rather than a macro, to avoid gcc warnings
|
||||
"warning: statement with no effect". */
|
||||
BINARY_IO_INLINE int
|
||||
__gl_setmode (int fd _GL_UNUSED, int mode _GL_UNUSED)
|
||||
{
|
||||
return O_BINARY;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set FD's mode to MODE, which should be either O_TEXT or O_BINARY.
|
||||
Return the old mode if successful, -1 (setting errno) on failure.
|
||||
Ordinarily this function would be called 'setmode', since that is
|
||||
its name on MS-Windows, but it is called 'set_binary_mode' here
|
||||
to avoid colliding with a BSD function of another name. */
|
||||
|
||||
#if defined __DJGPP__ || defined __EMX__
|
||||
extern int set_binary_mode (int fd, int mode);
|
||||
#else
|
||||
BINARY_IO_INLINE int
|
||||
set_binary_mode (int fd, int mode)
|
||||
{
|
||||
return __gl_setmode (fd, mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This macro is obsolescent. */
|
||||
#define SET_BINARY(fd) ((void) set_binary_mode (fd, O_BINARY))
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
#endif /* _BINARY_H */
|
3
lib/bitrotate.c
Normal file
3
lib/bitrotate.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include <config.h>
|
||||
#define BITROTATE_INLINE _GL_EXTERN_INLINE
|
||||
#include "bitrotate.h"
|
138
lib/bitrotate.h
Normal file
138
lib/bitrotate.h
Normal file
@ -0,0 +1,138 @@
|
||||
/* bitrotate.h - Rotate bits in integers
|
||||
Copyright (C) 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Simon Josefsson <simon@josefsson.org>, 2008. */
|
||||
|
||||
#ifndef _GL_BITROTATE_H
|
||||
#define _GL_BITROTATE_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _GL_INLINE_HEADER_BEGIN
|
||||
#error "Please include config.h first."
|
||||
#endif
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef BITROTATE_INLINE
|
||||
# define BITROTATE_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
#ifdef UINT64_MAX
|
||||
/* Given an unsigned 64-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the left. N must be between 1 and
|
||||
63 inclusive. */
|
||||
BITROTATE_INLINE uint64_t
|
||||
rotl64 (uint64_t x, int n)
|
||||
{
|
||||
return ((x << n) | (x >> (64 - n))) & UINT64_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 64-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the right. N must be between 1 to
|
||||
63 inclusive.*/
|
||||
BITROTATE_INLINE uint64_t
|
||||
rotr64 (uint64_t x, int n)
|
||||
{
|
||||
return ((x >> n) | (x << (64 - n))) & UINT64_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Given an unsigned 32-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the left. N must be between 1 and
|
||||
31 inclusive. */
|
||||
BITROTATE_INLINE uint32_t
|
||||
rotl32 (uint32_t x, int n)
|
||||
{
|
||||
return ((x << n) | (x >> (32 - n))) & UINT32_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 32-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the right. N must be between 1 to
|
||||
31 inclusive.*/
|
||||
BITROTATE_INLINE uint32_t
|
||||
rotr32 (uint32_t x, int n)
|
||||
{
|
||||
return ((x >> n) | (x << (32 - n))) & UINT32_MAX;
|
||||
}
|
||||
|
||||
/* Given a size_t argument X, return the value corresponding
|
||||
to rotating the bits N steps to the left. N must be between 1 and
|
||||
(CHAR_BIT * sizeof (size_t) - 1) inclusive. */
|
||||
BITROTATE_INLINE size_t
|
||||
rotl_sz (size_t x, int n)
|
||||
{
|
||||
return ((x << n) | (x >> ((CHAR_BIT * sizeof x) - n))) & SIZE_MAX;
|
||||
}
|
||||
|
||||
/* Given a size_t argument X, return the value corresponding
|
||||
to rotating the bits N steps to the right. N must be between 1 to
|
||||
(CHAR_BIT * sizeof (size_t) - 1) inclusive. */
|
||||
BITROTATE_INLINE size_t
|
||||
rotr_sz (size_t x, int n)
|
||||
{
|
||||
return ((x >> n) | (x << ((CHAR_BIT * sizeof x) - n))) & SIZE_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 16-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the left. N must be between 1 to
|
||||
15 inclusive, but on most relevant targets N can also be 0 and 16
|
||||
because 'int' is at least 32 bits and the arguments must widen
|
||||
before shifting. */
|
||||
BITROTATE_INLINE uint16_t
|
||||
rotl16 (uint16_t x, int n)
|
||||
{
|
||||
return (((unsigned int) x << n) | ((unsigned int) x >> (16 - n)))
|
||||
& UINT16_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 16-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the right. N must be in 1 to 15
|
||||
inclusive, but on most relevant targets N can also be 0 and 16
|
||||
because 'int' is at least 32 bits and the arguments must widen
|
||||
before shifting. */
|
||||
BITROTATE_INLINE uint16_t
|
||||
rotr16 (uint16_t x, int n)
|
||||
{
|
||||
return (((unsigned int) x >> n) | ((unsigned int) x << (16 - n)))
|
||||
& UINT16_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 8-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the left. N must be between 1 to 7
|
||||
inclusive, but on most relevant targets N can also be 0 and 8
|
||||
because 'int' is at least 32 bits and the arguments must widen
|
||||
before shifting. */
|
||||
BITROTATE_INLINE uint8_t
|
||||
rotl8 (uint8_t x, int n)
|
||||
{
|
||||
return (((unsigned int) x << n) | ((unsigned int) x >> (8 - n))) & UINT8_MAX;
|
||||
}
|
||||
|
||||
/* Given an unsigned 8-bit argument X, return the value corresponding
|
||||
to rotating the bits N steps to the right. N must be in 1 to 7
|
||||
inclusive, but on most relevant targets N can also be 0 and 8
|
||||
because 'int' is at least 32 bits and the arguments must widen
|
||||
before shifting. */
|
||||
BITROTATE_INLINE uint8_t
|
||||
rotr8 (uint8_t x, int n)
|
||||
{
|
||||
return (((unsigned int) x >> n) | ((unsigned int) x << (8 - n))) & UINT8_MAX;
|
||||
}
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
#endif /* _GL_BITROTATE_H */
|
39
lib/btowc.c
Normal file
39
lib/btowc.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* Convert unibyte character to wide character.
|
||||
Copyright (C) 2008, 2010-2020 Free Software Foundation, Inc.
|
||||
Written by Bruno Haible <bruno@clisp.org>, 2008.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include <wchar.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
wint_t
|
||||
btowc (int c)
|
||||
{
|
||||
if (c != EOF)
|
||||
{
|
||||
char buf[1];
|
||||
wchar_t wc;
|
||||
|
||||
buf[0] = c;
|
||||
if (mbtowc (&wc, buf, 1) >= 0)
|
||||
return wc;
|
||||
}
|
||||
return WEOF;
|
||||
}
|
59
lib/buffer-lcm.c
Normal file
59
lib/buffer-lcm.c
Normal file
@ -0,0 +1,59 @@
|
||||
/* buffer-lcm.c - compute a good buffer size for dealing with two files
|
||||
|
||||
Copyright (C) 2002-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#include <config.h>
|
||||
#include "buffer-lcm.h"
|
||||
|
||||
/* Return a buffer size suitable for doing I/O with files whose block
|
||||
sizes are A and B. However, never return a value greater than
|
||||
LCM_MAX. */
|
||||
|
||||
size_t
|
||||
buffer_lcm (size_t a, size_t b, size_t lcm_max)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
/* Use reasonable values if buffer sizes are zero. */
|
||||
if (!a)
|
||||
size = b ? b : 8 * 1024;
|
||||
else
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
/* Return lcm (A, B) if it is in range; otherwise, fall back
|
||||
on A. */
|
||||
|
||||
size_t lcm, m, n, q, r;
|
||||
|
||||
/* N = gcd (A, B). */
|
||||
for (m = a, n = b; (r = m % n) != 0; m = n, n = r)
|
||||
continue;
|
||||
|
||||
/* LCM = lcm (A, B), if in range. */
|
||||
q = a / n;
|
||||
lcm = q * b;
|
||||
if (lcm <= lcm_max && lcm / b == q)
|
||||
return lcm;
|
||||
}
|
||||
|
||||
size = a;
|
||||
}
|
||||
|
||||
return size <= lcm_max ? size : lcm_max;
|
||||
}
|
2
lib/buffer-lcm.h
Normal file
2
lib/buffer-lcm.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include <stddef.h>
|
||||
size_t buffer_lcm (size_t, size_t, size_t) _GL_ATTRIBUTE_CONST;
|
44
lib/byteswap.in.h
Normal file
44
lib/byteswap.in.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* byteswap.h - Byte swapping
|
||||
Copyright (C) 2005, 2007, 2009-2020 Free Software Foundation, Inc.
|
||||
Written by Oskar Liljeblad <oskar@osk.mine.nu>, 2005.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _GL_BYTESWAP_H
|
||||
#define _GL_BYTESWAP_H
|
||||
|
||||
/* Given an unsigned 16-bit argument X, return the value corresponding to
|
||||
X with reversed byte order. */
|
||||
#define bswap_16(x) ((((x) & 0x00FF) << 8) | \
|
||||
(((x) & 0xFF00) >> 8))
|
||||
|
||||
/* Given an unsigned 32-bit argument X, return the value corresponding to
|
||||
X with reversed byte order. */
|
||||
#define bswap_32(x) ((((x) & 0x000000FF) << 24) | \
|
||||
(((x) & 0x0000FF00) << 8) | \
|
||||
(((x) & 0x00FF0000) >> 8) | \
|
||||
(((x) & 0xFF000000) >> 24))
|
||||
|
||||
/* Given an unsigned 64-bit argument X, return the value corresponding to
|
||||
X with reversed byte order. */
|
||||
#define bswap_64(x) ((((x) & 0x00000000000000FFULL) << 56) | \
|
||||
(((x) & 0x000000000000FF00ULL) << 40) | \
|
||||
(((x) & 0x0000000000FF0000ULL) << 24) | \
|
||||
(((x) & 0x00000000FF000000ULL) << 8) | \
|
||||
(((x) & 0x000000FF00000000ULL) >> 8) | \
|
||||
(((x) & 0x0000FF0000000000ULL) >> 24) | \
|
||||
(((x) & 0x00FF000000000000ULL) >> 40) | \
|
||||
(((x) & 0xFF00000000000000ULL) >> 56))
|
||||
|
||||
#endif /* _GL_BYTESWAP_H */
|
313
lib/c++defs.h
Normal file
313
lib/c++defs.h
Normal file
@ -0,0 +1,313 @@
|
||||
/* C++ compatible function declaration macros.
|
||||
Copyright (C) 2010-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _GL_CXXDEFS_H
|
||||
#define _GL_CXXDEFS_H
|
||||
|
||||
/* Begin/end the GNULIB_NAMESPACE namespace. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_BEGIN_NAMESPACE namespace GNULIB_NAMESPACE {
|
||||
# define _GL_END_NAMESPACE }
|
||||
#else
|
||||
# define _GL_BEGIN_NAMESPACE
|
||||
# define _GL_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
/* The three most frequent use cases of these macros are:
|
||||
|
||||
* For providing a substitute for a function that is missing on some
|
||||
platforms, but is declared and works fine on the platforms on which
|
||||
it exists:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if !@HAVE_FOO@
|
||||
_GL_FUNCDECL_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
|
||||
* For providing a replacement for a function that exists on all platforms,
|
||||
but is broken/insufficient and needs to be replaced on some platforms:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if @REPLACE_FOO@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef foo
|
||||
# define foo rpl_foo
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (foo, ...);
|
||||
_GL_CXXALIAS_RPL (foo, ...);
|
||||
# else
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
|
||||
* For providing a replacement for a function that exists on some platforms
|
||||
but is broken/insufficient and needs to be replaced on some of them and
|
||||
is additionally either missing or undeclared on some other platforms:
|
||||
|
||||
#if @GNULIB_FOO@
|
||||
# if @REPLACE_FOO@
|
||||
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
|
||||
# undef foo
|
||||
# define foo rpl_foo
|
||||
# endif
|
||||
_GL_FUNCDECL_RPL (foo, ...);
|
||||
_GL_CXXALIAS_RPL (foo, ...);
|
||||
# else
|
||||
# if !@HAVE_FOO@ or if !@HAVE_DECL_FOO@
|
||||
_GL_FUNCDECL_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIAS_SYS (foo, ...);
|
||||
# endif
|
||||
_GL_CXXALIASWARN (foo);
|
||||
#elif defined GNULIB_POSIXCHECK
|
||||
...
|
||||
#endif
|
||||
*/
|
||||
|
||||
/* _GL_EXTERN_C declaration;
|
||||
performs the declaration with C linkage. */
|
||||
#if defined __cplusplus
|
||||
# define _GL_EXTERN_C extern "C"
|
||||
#else
|
||||
# define _GL_EXTERN_C extern
|
||||
#endif
|
||||
|
||||
/* _GL_FUNCDECL_RPL (func, rettype, parameters_and_attributes);
|
||||
declares a replacement function, named rpl_func, with the given prototype,
|
||||
consisting of return type, parameters, and attributes.
|
||||
Example:
|
||||
_GL_FUNCDECL_RPL (open, int, (const char *filename, int flags, ...)
|
||||
_GL_ARG_NONNULL ((1)));
|
||||
*/
|
||||
#define _GL_FUNCDECL_RPL(func,rettype,parameters_and_attributes) \
|
||||
_GL_FUNCDECL_RPL_1 (rpl_##func, rettype, parameters_and_attributes)
|
||||
#define _GL_FUNCDECL_RPL_1(rpl_func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C rettype rpl_func parameters_and_attributes
|
||||
|
||||
/* _GL_FUNCDECL_SYS (func, rettype, parameters_and_attributes);
|
||||
declares the system function, named func, with the given prototype,
|
||||
consisting of return type, parameters, and attributes.
|
||||
Example:
|
||||
_GL_FUNCDECL_SYS (open, int, (const char *filename, int flags, ...)
|
||||
_GL_ARG_NONNULL ((1)));
|
||||
*/
|
||||
#define _GL_FUNCDECL_SYS(func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C rettype func parameters_and_attributes
|
||||
|
||||
/* _GL_CXXALIAS_RPL (func, rettype, parameters);
|
||||
declares a C++ alias called GNULIB_NAMESPACE::func
|
||||
that redirects to rpl_func, if GNULIB_NAMESPACE is defined.
|
||||
Example:
|
||||
_GL_CXXALIAS_RPL (open, int, (const char *filename, int flags, ...));
|
||||
|
||||
Wrapping rpl_func in an object with an inline conversion operator
|
||||
avoids a reference to rpl_func unless GNULIB_NAMESPACE::func is
|
||||
actually used in the program. */
|
||||
#define _GL_CXXALIAS_RPL(func,rettype,parameters) \
|
||||
_GL_CXXALIAS_RPL_1 (func, rpl_##func, rettype, parameters)
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return ::rpl_func; \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_RPL_CAST_1 (func, rpl_func, rettype, parameters);
|
||||
is like _GL_CXXALIAS_RPL_1 (func, rpl_func, rettype, parameters);
|
||||
except that the C function rpl_func may have a slightly different
|
||||
declaration. A cast is used to silence the "invalid conversion" error
|
||||
that would otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>(::rpl_func); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
declares a C++ alias called GNULIB_NAMESPACE::func
|
||||
that redirects to the system provided function func, if GNULIB_NAMESPACE
|
||||
is defined.
|
||||
Example:
|
||||
_GL_CXXALIAS_SYS (open, int, (const char *filename, int flags, ...));
|
||||
|
||||
Wrapping func in an object with an inline conversion operator
|
||||
avoids a reference to func unless GNULIB_NAMESPACE::func is
|
||||
actually used in the program. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return ::func; \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS_CAST (func, rettype, parameters);
|
||||
is like _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
except that the C function func may have a slightly different declaration.
|
||||
A cast is used to silence the "invalid conversion" error that would
|
||||
otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>(::func); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIAS_SYS_CAST2 (func, rettype, parameters, rettype2, parameters2);
|
||||
is like _GL_CXXALIAS_SYS (func, rettype, parameters);
|
||||
except that the C function is picked among a set of overloaded functions,
|
||||
namely the one with rettype2 and parameters2. Two consecutive casts
|
||||
are used to silence the "cannot find a match" and "invalid conversion"
|
||||
errors that would otherwise occur. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
/* The outer cast must be a reinterpret_cast.
|
||||
The inner cast: When the function is defined as a set of overloaded
|
||||
functions, it works as a static_cast<>, choosing the designated variant.
|
||||
When the function is defined as a single variant, it works as a
|
||||
reinterpret_cast<>. The parenthesized cast syntax works both ways. */
|
||||
# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
|
||||
namespace GNULIB_NAMESPACE \
|
||||
{ \
|
||||
static const struct _gl_ ## func ## _wrapper \
|
||||
{ \
|
||||
typedef rettype (*type) parameters; \
|
||||
\
|
||||
inline operator type () const \
|
||||
{ \
|
||||
return reinterpret_cast<type>((rettype2 (*) parameters2)(::func)); \
|
||||
} \
|
||||
} func = {}; \
|
||||
} \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#else
|
||||
# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIASWARN (func);
|
||||
causes a warning to be emitted when ::func is used but not when
|
||||
GNULIB_NAMESPACE::func is used. func must be defined without overloaded
|
||||
variants. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIASWARN(func) \
|
||||
_GL_CXXALIASWARN_1 (func, GNULIB_NAMESPACE)
|
||||
# define _GL_CXXALIASWARN_1(func,namespace) \
|
||||
_GL_CXXALIASWARN_2 (func, namespace)
|
||||
/* To work around GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
|
||||
we enable the warning only when not optimizing. */
|
||||
# if !__OPTIMIZE__
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
_GL_WARN_ON_USE (func, \
|
||||
"The symbol ::" #func " refers to the system function. " \
|
||||
"Use " #namespace "::" #func " instead.")
|
||||
# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
extern __typeof__ (func) func
|
||||
# else
|
||||
# define _GL_CXXALIASWARN_2(func,namespace) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
# endif
|
||||
#else
|
||||
# define _GL_CXXALIASWARN(func) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
/* _GL_CXXALIASWARN1 (func, rettype, parameters_and_attributes);
|
||||
causes a warning to be emitted when the given overloaded variant of ::func
|
||||
is used but not when GNULIB_NAMESPACE::func is used. */
|
||||
#if defined __cplusplus && defined GNULIB_NAMESPACE
|
||||
# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
|
||||
_GL_CXXALIASWARN1_1 (func, rettype, parameters_and_attributes, \
|
||||
GNULIB_NAMESPACE)
|
||||
# define _GL_CXXALIASWARN1_1(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_CXXALIASWARN1_2 (func, rettype, parameters_and_attributes, namespace)
|
||||
/* To work around GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
|
||||
we enable the warning only when not optimizing. */
|
||||
# if !__OPTIMIZE__
|
||||
# define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_WARN_ON_USE_CXX (func, rettype, parameters_and_attributes, \
|
||||
"The symbol ::" #func " refers to the system function. " \
|
||||
"Use " #namespace "::" #func " instead.")
|
||||
# else
|
||||
# define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
# endif
|
||||
#else
|
||||
# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
|
||||
_GL_EXTERN_C int _gl_cxxalias_dummy
|
||||
#endif
|
||||
|
||||
#endif /* _GL_CXXDEFS_H */
|
3
lib/c-ctype.c
Normal file
3
lib/c-ctype.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include <config.h>
|
||||
#define C_CTYPE_INLINE _GL_EXTERN_INLINE
|
||||
#include "c-ctype.h"
|
366
lib/c-ctype.h
Normal file
366
lib/c-ctype.h
Normal file
@ -0,0 +1,366 @@
|
||||
/* Character handling in C locale.
|
||||
|
||||
These functions work like the corresponding functions in <ctype.h>,
|
||||
except that they have the C (POSIX) locale hardwired, whereas the
|
||||
<ctype.h> functions' behaviour depends on the current locale set via
|
||||
setlocale.
|
||||
|
||||
Copyright (C) 2000-2003, 2006, 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef C_CTYPE_H
|
||||
#define C_CTYPE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef _GL_INLINE_HEADER_BEGIN
|
||||
#error "Please include config.h first."
|
||||
#endif
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef C_CTYPE_INLINE
|
||||
# define C_CTYPE_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* The functions defined in this file assume the "C" locale and a character
|
||||
set without diacritics (ASCII-US or EBCDIC-US or something like that).
|
||||
Even if the "C" locale on a particular system is an extension of the ASCII
|
||||
character set (like on BeOS, where it is UTF-8, or on AmigaOS, where it
|
||||
is ISO-8859-1), the functions in this file recognize only the ASCII
|
||||
characters. */
|
||||
|
||||
|
||||
#if (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
|
||||
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
|
||||
&& (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
|
||||
&& ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
|
||||
&& ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
|
||||
&& ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
|
||||
&& ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
|
||||
&& ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
|
||||
&& ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
|
||||
&& ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
|
||||
&& ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
|
||||
&& ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
|
||||
&& ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
|
||||
&& ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
|
||||
&& ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
|
||||
&& ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
|
||||
&& ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
|
||||
&& ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
|
||||
&& ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
|
||||
&& ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
|
||||
&& ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
|
||||
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
|
||||
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)
|
||||
/* The character set is ASCII or one of its variants or extensions, not EBCDIC.
|
||||
Testing the value of '\n' and '\r' is not relevant. */
|
||||
# define C_CTYPE_ASCII 1
|
||||
#elif ! (' ' == '\x40' && '0' == '\xf0' \
|
||||
&& 'A' == '\xc1' && 'J' == '\xd1' && 'S' == '\xe2' \
|
||||
&& 'a' == '\x81' && 'j' == '\x91' && 's' == '\xa2')
|
||||
# error "Only ASCII and EBCDIC are supported"
|
||||
#endif
|
||||
|
||||
#if 'A' < 0
|
||||
# error "EBCDIC and char is signed -- not supported"
|
||||
#endif
|
||||
|
||||
/* Cases for control characters. */
|
||||
|
||||
#define _C_CTYPE_CNTRL \
|
||||
case '\a': case '\b': case '\f': case '\n': \
|
||||
case '\r': case '\t': case '\v': \
|
||||
_C_CTYPE_OTHER_CNTRL
|
||||
|
||||
/* ASCII control characters other than those with \-letter escapes. */
|
||||
|
||||
#if C_CTYPE_ASCII
|
||||
# define _C_CTYPE_OTHER_CNTRL \
|
||||
case '\x00': case '\x01': case '\x02': case '\x03': \
|
||||
case '\x04': case '\x05': case '\x06': case '\x0e': \
|
||||
case '\x0f': case '\x10': case '\x11': case '\x12': \
|
||||
case '\x13': case '\x14': case '\x15': case '\x16': \
|
||||
case '\x17': case '\x18': case '\x19': case '\x1a': \
|
||||
case '\x1b': case '\x1c': case '\x1d': case '\x1e': \
|
||||
case '\x1f': case '\x7f'
|
||||
#else
|
||||
/* Use EBCDIC code page 1047's assignments for ASCII control chars;
|
||||
assume all EBCDIC code pages agree about these assignments. */
|
||||
# define _C_CTYPE_OTHER_CNTRL \
|
||||
case '\x00': case '\x01': case '\x02': case '\x03': \
|
||||
case '\x07': case '\x0e': case '\x0f': case '\x10': \
|
||||
case '\x11': case '\x12': case '\x13': case '\x18': \
|
||||
case '\x19': case '\x1c': case '\x1d': case '\x1e': \
|
||||
case '\x1f': case '\x26': case '\x27': case '\x2d': \
|
||||
case '\x2e': case '\x32': case '\x37': case '\x3c': \
|
||||
case '\x3d': case '\x3f'
|
||||
#endif
|
||||
|
||||
/* Cases for lowercase hex letters, and lowercase letters, all offset by N. */
|
||||
|
||||
#define _C_CTYPE_LOWER_A_THRU_F_N(N) \
|
||||
case 'a' + (N): case 'b' + (N): case 'c' + (N): case 'd' + (N): \
|
||||
case 'e' + (N): case 'f' + (N)
|
||||
#define _C_CTYPE_LOWER_N(N) \
|
||||
_C_CTYPE_LOWER_A_THRU_F_N(N): \
|
||||
case 'g' + (N): case 'h' + (N): case 'i' + (N): case 'j' + (N): \
|
||||
case 'k' + (N): case 'l' + (N): case 'm' + (N): case 'n' + (N): \
|
||||
case 'o' + (N): case 'p' + (N): case 'q' + (N): case 'r' + (N): \
|
||||
case 's' + (N): case 't' + (N): case 'u' + (N): case 'v' + (N): \
|
||||
case 'w' + (N): case 'x' + (N): case 'y' + (N): case 'z' + (N)
|
||||
|
||||
/* Cases for hex letters, digits, lower, punct, and upper. */
|
||||
|
||||
#define _C_CTYPE_A_THRU_F \
|
||||
_C_CTYPE_LOWER_A_THRU_F_N (0): \
|
||||
_C_CTYPE_LOWER_A_THRU_F_N ('A' - 'a')
|
||||
#define _C_CTYPE_DIGIT \
|
||||
case '0': case '1': case '2': case '3': \
|
||||
case '4': case '5': case '6': case '7': \
|
||||
case '8': case '9'
|
||||
#define _C_CTYPE_LOWER _C_CTYPE_LOWER_N (0)
|
||||
#define _C_CTYPE_PUNCT \
|
||||
case '!': case '"': case '#': case '$': \
|
||||
case '%': case '&': case '\'': case '(': \
|
||||
case ')': case '*': case '+': case ',': \
|
||||
case '-': case '.': case '/': case ':': \
|
||||
case ';': case '<': case '=': case '>': \
|
||||
case '?': case '@': case '[': case '\\': \
|
||||
case ']': case '^': case '_': case '`': \
|
||||
case '{': case '|': case '}': case '~'
|
||||
#define _C_CTYPE_UPPER _C_CTYPE_LOWER_N ('A' - 'a')
|
||||
|
||||
|
||||
/* Function definitions. */
|
||||
|
||||
/* Unlike the functions in <ctype.h>, which require an argument in the range
|
||||
of the 'unsigned char' type, the functions here operate on values that are
|
||||
in the 'unsigned char' range or in the 'char' range. In other words,
|
||||
when you have a 'char' value, you need to cast it before using it as
|
||||
argument to a <ctype.h> function:
|
||||
|
||||
const char *s = ...;
|
||||
if (isalpha ((unsigned char) *s)) ...
|
||||
|
||||
but you don't need to cast it for the functions defined in this file:
|
||||
|
||||
const char *s = ...;
|
||||
if (c_isalpha (*s)) ...
|
||||
*/
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isalnum (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_DIGIT:
|
||||
_C_CTYPE_LOWER:
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isalpha (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_LOWER:
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* The function isascii is not locale dependent.
|
||||
Its use in EBCDIC is questionable. */
|
||||
C_CTYPE_INLINE bool
|
||||
c_isascii (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
_C_CTYPE_CNTRL:
|
||||
_C_CTYPE_DIGIT:
|
||||
_C_CTYPE_LOWER:
|
||||
_C_CTYPE_PUNCT:
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isblank (int c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_iscntrl (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_CNTRL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isdigit (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_DIGIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isgraph (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_DIGIT:
|
||||
_C_CTYPE_LOWER:
|
||||
_C_CTYPE_PUNCT:
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_islower (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_LOWER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isprint (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
_C_CTYPE_DIGIT:
|
||||
_C_CTYPE_LOWER:
|
||||
_C_CTYPE_PUNCT:
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_ispunct (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_PUNCT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isspace (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isupper (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_UPPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE bool
|
||||
c_isxdigit (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_DIGIT:
|
||||
_C_CTYPE_A_THRU_F:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE int
|
||||
c_tolower (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_UPPER:
|
||||
return c - 'A' + 'a';
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
C_CTYPE_INLINE int
|
||||
c_toupper (int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
_C_CTYPE_LOWER:
|
||||
return c - 'a' + 'A';
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
#endif /* C_CTYPE_H */
|
56
lib/c-strcase.h
Normal file
56
lib/c-strcase.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Case-insensitive string comparison functions in C locale.
|
||||
Copyright (C) 1995-1996, 2001, 2003, 2005, 2009-2020 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef C_STRCASE_H
|
||||
#define C_STRCASE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/* The functions defined in this file assume the "C" locale and a character
|
||||
set without diacritics (ASCII-US or EBCDIC-US or something like that).
|
||||
Even if the "C" locale on a particular system is an extension of the ASCII
|
||||
character set (like on BeOS, where it is UTF-8, or on AmigaOS, where it
|
||||
is ISO-8859-1), the functions in this file recognize only the ASCII
|
||||
characters. More precisely, one of the string arguments must be an ASCII
|
||||
string; the other one can also contain non-ASCII characters (but then
|
||||
the comparison result will be nonzero). */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Compare strings S1 and S2, ignoring case, returning less than, equal to or
|
||||
greater than zero if S1 is lexicographically less than, equal to or greater
|
||||
than S2. */
|
||||
extern int c_strcasecmp (const char *s1, const char *s2) _GL_ATTRIBUTE_PURE;
|
||||
|
||||
/* Compare no more than N characters of strings S1 and S2, ignoring case,
|
||||
returning less than, equal to or greater than zero if S1 is
|
||||
lexicographically less than, equal to or greater than S2. */
|
||||
extern int c_strncasecmp (const char *s1, const char *s2, size_t n)
|
||||
_GL_ATTRIBUTE_PURE;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* C_STRCASE_H */
|
56
lib/c-strcasecmp.c
Normal file
56
lib/c-strcasecmp.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* c-strcasecmp.c -- case insensitive string comparator in C locale
|
||||
Copyright (C) 1998-1999, 2005-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "c-strcase.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "c-ctype.h"
|
||||
|
||||
int
|
||||
c_strcasecmp (const char *s1, const char *s2)
|
||||
{
|
||||
register const unsigned char *p1 = (const unsigned char *) s1;
|
||||
register const unsigned char *p2 = (const unsigned char *) s2;
|
||||
unsigned char c1, c2;
|
||||
|
||||
if (p1 == p2)
|
||||
return 0;
|
||||
|
||||
do
|
||||
{
|
||||
c1 = c_tolower (*p1);
|
||||
c2 = c_tolower (*p2);
|
||||
|
||||
if (c1 == '\0')
|
||||
break;
|
||||
|
||||
++p1;
|
||||
++p2;
|
||||
}
|
||||
while (c1 == c2);
|
||||
|
||||
if (UCHAR_MAX <= INT_MAX)
|
||||
return c1 - c2;
|
||||
else
|
||||
/* On machines where 'char' and 'int' are types of the same size, the
|
||||
difference of two 'unsigned char' values - including the sign bit -
|
||||
doesn't fit in an 'int'. */
|
||||
return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0);
|
||||
}
|
181
lib/c-strcaseeq.h
Normal file
181
lib/c-strcaseeq.h
Normal file
@ -0,0 +1,181 @@
|
||||
/* Optimized case-insensitive string comparison in C locale.
|
||||
Copyright (C) 2001-2002, 2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>. */
|
||||
|
||||
#include "c-strcase.h"
|
||||
#include "c-ctype.h"
|
||||
|
||||
/* STRCASEEQ allows to optimize string comparison with a small literal string.
|
||||
STRCASEEQ (s, "UTF-8", 'U','T','F','-','8',0,0,0,0)
|
||||
is semantically equivalent to
|
||||
c_strcasecmp (s, "UTF-8") == 0
|
||||
just faster. */
|
||||
|
||||
/* Help GCC to generate good code for string comparisons with
|
||||
immediate strings. */
|
||||
#if defined (__GNUC__) && defined (__OPTIMIZE__)
|
||||
|
||||
/* Case insensitive comparison of ASCII characters. */
|
||||
# if C_CTYPE_ASCII
|
||||
# define CASEEQ(other,upper) \
|
||||
(c_isupper (upper) ? ((other) & ~0x20) == (upper) : (other) == (upper))
|
||||
# else
|
||||
# define CASEEQ(other,upper) \
|
||||
(c_toupper (other) == (upper))
|
||||
# endif
|
||||
|
||||
static inline int
|
||||
strcaseeq9 (const char *s1, const char *s2)
|
||||
{
|
||||
return c_strcasecmp (s1 + 9, s2 + 9) == 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq8 (const char *s1, const char *s2, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[8], s28))
|
||||
{
|
||||
if (s28 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq9 (s1, s2);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq7 (const char *s1, const char *s2, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[7], s27))
|
||||
{
|
||||
if (s27 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq8 (s1, s2, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq6 (const char *s1, const char *s2, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[6], s26))
|
||||
{
|
||||
if (s26 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq7 (s1, s2, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq5 (const char *s1, const char *s2, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[5], s25))
|
||||
{
|
||||
if (s25 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq6 (s1, s2, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq4 (const char *s1, const char *s2, char s24, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[4], s24))
|
||||
{
|
||||
if (s24 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq5 (s1, s2, s25, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq3 (const char *s1, const char *s2, char s23, char s24, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[3], s23))
|
||||
{
|
||||
if (s23 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq4 (s1, s2, s24, s25, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq2 (const char *s1, const char *s2, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[2], s22))
|
||||
{
|
||||
if (s22 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq3 (s1, s2, s23, s24, s25, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq1 (const char *s1, const char *s2, char s21, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[1], s21))
|
||||
{
|
||||
if (s21 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq2 (s1, s2, s22, s23, s24, s25, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
strcaseeq0 (const char *s1, const char *s2, char s20, char s21, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
|
||||
{
|
||||
if (CASEEQ (s1[0], s20))
|
||||
{
|
||||
if (s20 == 0)
|
||||
return 1;
|
||||
else
|
||||
return strcaseeq1 (s1, s2, s21, s22, s23, s24, s25, s26, s27, s28);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STRCASEEQ(s1,s2,s20,s21,s22,s23,s24,s25,s26,s27,s28) \
|
||||
strcaseeq0 (s1, s2, s20, s21, s22, s23, s24, s25, s26, s27, s28)
|
||||
|
||||
#else
|
||||
|
||||
#define STRCASEEQ(s1,s2,s20,s21,s22,s23,s24,s25,s26,s27,s28) \
|
||||
(c_strcasecmp (s1, s2) == 0)
|
||||
|
||||
#endif
|
56
lib/c-strncasecmp.c
Normal file
56
lib/c-strncasecmp.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* c-strncasecmp.c -- case insensitive string comparator in C locale
|
||||
Copyright (C) 1998-1999, 2005-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "c-strcase.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "c-ctype.h"
|
||||
|
||||
int
|
||||
c_strncasecmp (const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
register const unsigned char *p1 = (const unsigned char *) s1;
|
||||
register const unsigned char *p2 = (const unsigned char *) s2;
|
||||
unsigned char c1, c2;
|
||||
|
||||
if (p1 == p2 || n == 0)
|
||||
return 0;
|
||||
|
||||
do
|
||||
{
|
||||
c1 = c_tolower (*p1);
|
||||
c2 = c_tolower (*p2);
|
||||
|
||||
if (--n == 0 || c1 == '\0')
|
||||
break;
|
||||
|
||||
++p1;
|
||||
++p2;
|
||||
}
|
||||
while (c1 == c2);
|
||||
|
||||
if (UCHAR_MAX <= INT_MAX)
|
||||
return c1 - c2;
|
||||
else
|
||||
/* On machines where 'char' and 'int' are types of the same size, the
|
||||
difference of two 'unsigned char' values - including the sign bit -
|
||||
doesn't fit in an 'int'. */
|
||||
return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0);
|
||||
}
|
135
lib/c-strtod.c
Normal file
135
lib/c-strtod.c
Normal file
@ -0,0 +1,135 @@
|
||||
/* Convert string to double, using the C locale.
|
||||
|
||||
Copyright (C) 2003-2004, 2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "c-strtod.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if LONG
|
||||
# define C_STRTOD c_strtold
|
||||
# define DOUBLE long double
|
||||
# define STRTOD_L strtold_l
|
||||
# define HAVE_GOOD_STRTOD_L (HAVE_STRTOLD_L && !GNULIB_defined_strtold_function)
|
||||
# define STRTOD strtold
|
||||
#else
|
||||
# define C_STRTOD c_strtod
|
||||
# define DOUBLE double
|
||||
# define STRTOD_L strtod_l
|
||||
# define HAVE_GOOD_STRTOD_L (HAVE_STRTOD_L && !GNULIB_defined_strtod_function)
|
||||
# define STRTOD strtod
|
||||
#endif
|
||||
|
||||
#if defined LC_ALL_MASK \
|
||||
&& ((LONG ? HAVE_GOOD_STRTOLD_L : HAVE_GOOD_STRTOD_L) \
|
||||
|| HAVE_WORKING_USELOCALE)
|
||||
|
||||
/* Cache for the C locale object.
|
||||
Marked volatile so that different threads see the same value
|
||||
(avoids locking). */
|
||||
static volatile locale_t c_locale_cache;
|
||||
|
||||
/* Return the C locale object, or (locale_t) 0 with errno set
|
||||
if it cannot be created. */
|
||||
static locale_t
|
||||
c_locale (void)
|
||||
{
|
||||
if (!c_locale_cache)
|
||||
c_locale_cache = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
|
||||
return c_locale_cache;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DOUBLE
|
||||
C_STRTOD (char const *nptr, char **endptr)
|
||||
{
|
||||
DOUBLE r;
|
||||
|
||||
#if defined LC_ALL_MASK \
|
||||
&& ((LONG ? HAVE_GOOD_STRTOLD_L : HAVE_GOOD_STRTOD_L) \
|
||||
|| HAVE_WORKING_USELOCALE)
|
||||
|
||||
locale_t locale = c_locale ();
|
||||
if (!locale)
|
||||
{
|
||||
if (endptr)
|
||||
*endptr = (char *) nptr;
|
||||
return 0; /* errno is set here */
|
||||
}
|
||||
|
||||
# if (LONG ? HAVE_GOOD_STRTOLD_L : HAVE_GOOD_STRTOD_L)
|
||||
|
||||
r = STRTOD_L (nptr, endptr, locale);
|
||||
|
||||
# else /* HAVE_WORKING_USELOCALE */
|
||||
|
||||
locale_t old_locale = uselocale (locale);
|
||||
if (old_locale == (locale_t)0)
|
||||
{
|
||||
if (endptr)
|
||||
*endptr = (char *) nptr;
|
||||
return 0; /* errno is set here */
|
||||
}
|
||||
|
||||
r = STRTOD (nptr, endptr);
|
||||
|
||||
int saved_errno = errno;
|
||||
if (uselocale (old_locale) == (locale_t)0)
|
||||
/* We can't switch back to the old locale. The thread is hosed. */
|
||||
abort ();
|
||||
errno = saved_errno;
|
||||
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
char *saved_locale = setlocale (LC_NUMERIC, NULL);
|
||||
|
||||
if (saved_locale)
|
||||
{
|
||||
saved_locale = strdup (saved_locale);
|
||||
if (saved_locale == NULL)
|
||||
{
|
||||
if (endptr)
|
||||
*endptr = (char *) nptr;
|
||||
return 0; /* errno is set here */
|
||||
}
|
||||
setlocale (LC_NUMERIC, "C");
|
||||
}
|
||||
|
||||
r = STRTOD (nptr, endptr);
|
||||
|
||||
if (saved_locale)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
|
||||
setlocale (LC_NUMERIC, saved_locale);
|
||||
free (saved_locale);
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
45
lib/c-strtod.h
Normal file
45
lib/c-strtod.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* Convert string to double, using the C locale. -*- coding: utf-8 -*-
|
||||
|
||||
Copyright (C) 2003-2004, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Parse the initial portion of the string pointed to by NPTR as a floating-
|
||||
point number (in decimal or hexadecimal notation), like in the C locale:
|
||||
accepting only the ASCII digits '0'..'9', and only '.' as decimal point
|
||||
character.
|
||||
If ENDPTR is not NULL, set *ENDPTR to point to the first byte beyond the
|
||||
parsed number or to NPTR if the string does not start with a parsable
|
||||
number.
|
||||
Return value:
|
||||
- If successful, return the value as a double or 'long double',
|
||||
respectively, and don't modify errno.
|
||||
- In case of overflow, return ±HUGE_VAL or ±HUGE_VALL, respectively, and
|
||||
set errno to ERANGE.
|
||||
- In case of underflow, return a value very near to 0 and set errno to
|
||||
ERANGE.
|
||||
- If the string does not start with a number at all, return 0 (and recall
|
||||
that if ENDPTR != NULL, *ENDPTR is set to NPTR), and maybe set errno to
|
||||
EINVAL.
|
||||
- In case of other error, return 0 and set errno, for example to ENOMEM. */
|
||||
extern double c_strtod (char const *nptr, char **endptr);
|
||||
extern long double c_strtold (char const *nptr, char **endptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
2
lib/c-strtold.c
Normal file
2
lib/c-strtold.c
Normal file
@ -0,0 +1,2 @@
|
||||
#define LONG 1
|
||||
#include "c-strtod.c"
|
73
lib/calloc.c
Normal file
73
lib/calloc.c
Normal file
@ -0,0 +1,73 @@
|
||||
/* calloc() function that is glibc compatible.
|
||||
This wrapper function is required at least on Tru64 UNIX 5.1 and mingw.
|
||||
Copyright (C) 2004-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Jim Meyering and Bruno Haible */
|
||||
|
||||
#include <config.h>
|
||||
/* Only the AC_FUNC_CALLOC macro defines 'calloc' already in config.h. */
|
||||
#ifdef calloc
|
||||
# define NEED_CALLOC_GNU 1
|
||||
# undef calloc
|
||||
/* Whereas the gnulib module 'calloc-gnu' defines HAVE_CALLOC_GNU. */
|
||||
#elif GNULIB_CALLOC_GNU && !HAVE_CALLOC_GNU
|
||||
# define NEED_CALLOC_GNU 1
|
||||
#endif
|
||||
|
||||
/* Specification. */
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* Call the system's calloc below. */
|
||||
#undef calloc
|
||||
|
||||
/* Allocate and zero-fill an NxS-byte block of memory from the heap.
|
||||
If N or S is zero, allocate and zero-fill a 1-byte block. */
|
||||
|
||||
void *
|
||||
rpl_calloc (size_t n, size_t s)
|
||||
{
|
||||
void *result;
|
||||
|
||||
#if NEED_CALLOC_GNU
|
||||
if (n == 0 || s == 0)
|
||||
{
|
||||
n = 1;
|
||||
s = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Defend against buggy calloc implementations that mishandle
|
||||
size_t overflow. */
|
||||
size_t bytes = n * s;
|
||||
if (bytes / s != n)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
result = calloc (n, s);
|
||||
|
||||
#if !HAVE_CALLOC_POSIX
|
||||
if (result == NULL)
|
||||
errno = ENOMEM;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
91
lib/canon-host.c
Normal file
91
lib/canon-host.c
Normal file
@ -0,0 +1,91 @@
|
||||
/* Host name canonicalization
|
||||
|
||||
Copyright (C) 2005-2020 Free Software Foundation, Inc.
|
||||
|
||||
Written by Derek Price <derek@ximbiot.com>.
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "canon-host.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
|
||||
/* Store the last error for the single-threaded version of this function. */
|
||||
static int last_cherror;
|
||||
|
||||
/* Single-threaded of wrapper for canon_host_r. After a NULL return, error
|
||||
messages may be retrieved via ch_strerror(). */
|
||||
char *
|
||||
canon_host (const char *host)
|
||||
{
|
||||
return canon_host_r (host, &last_cherror);
|
||||
}
|
||||
|
||||
/* Return a malloc'd string containing the canonical hostname associated with
|
||||
HOST, or NULL if a canonical name cannot be determined. On NULL return,
|
||||
if CHERROR is not NULL, set *CHERROR to an error code as returned by
|
||||
getaddrinfo(). Use ch_strerror_r() or gai_strerror() to convert a *CHERROR
|
||||
value to a string suitable for error messages.
|
||||
|
||||
WARNINGS
|
||||
HOST must be a string representation of a resolvable name for this host.
|
||||
Strings containing an IP address in dotted decimal notation will be
|
||||
returned as-is, without further resolution.
|
||||
|
||||
The use of the word "canonical" in this context is unfortunate but
|
||||
entrenched. The value returned by this function will be the end result
|
||||
of the resolution of any CNAME chains in the DNS. There may only be one
|
||||
such value for any given hostname, though the actual IP address
|
||||
referenced by this value and the device using that IP address may each
|
||||
actually have any number of such "canonical" hostnames. See the POSIX
|
||||
getaddrinfo spec
|
||||
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>,
|
||||
RFC 1034 <https://www.ietf.org/rfc/rfc1034.txt>, & RFC 2181
|
||||
<https://www.ietf.org/rfc/rfc2181.txt> for more on what this confusing
|
||||
term really refers to. */
|
||||
char *
|
||||
canon_host_r (char const *host, int *cherror)
|
||||
{
|
||||
char *retval = NULL;
|
||||
static struct addrinfo hints;
|
||||
struct addrinfo *res = NULL;
|
||||
int status;
|
||||
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
status = getaddrinfo (host, NULL, &hints, &res);
|
||||
if (!status)
|
||||
{
|
||||
/* https://lists.gnu.org/r/bug-coreutils/2006-09/msg00300.html
|
||||
says Darwin 7.9.0 getaddrinfo returns 0 but sets
|
||||
res->ai_canonname to NULL. */
|
||||
retval = strdup (res->ai_canonname ? res->ai_canonname : host);
|
||||
if (!retval && cherror)
|
||||
*cherror = EAI_MEMORY;
|
||||
freeaddrinfo (res);
|
||||
}
|
||||
else if (cherror)
|
||||
*cherror = status;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Return a string describing the last error encountered by canon_host. */
|
||||
const char *
|
||||
ch_strerror (void)
|
||||
{
|
||||
return gai_strerror (last_cherror);
|
||||
}
|
29
lib/canon-host.h
Normal file
29
lib/canon-host.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* Host name canonicalization
|
||||
|
||||
Copyright (C) 2005, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
Written by Derek Price <derek@ximbiot.com>
|
||||
|
||||
This 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef CANON_HOST_H
|
||||
# define CANON_HOST_H 1
|
||||
|
||||
char *canon_host (char const *host) _GL_ATTRIBUTE_MALLOC;
|
||||
char *canon_host_r (char const *host, int *cherror) _GL_ATTRIBUTE_MALLOC;
|
||||
|
||||
const char *ch_strerror (void);
|
||||
# define ch_strerror_r(cherror) gai_strerror (cherror);
|
||||
|
||||
#endif /* !CANON_HOST_H */
|
355
lib/canonicalize.c
Normal file
355
lib/canonicalize.c
Normal file
@ -0,0 +1,355 @@
|
||||
/* Return the canonical absolute name of a given file.
|
||||
Copyright (C) 1996-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "canonicalize.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "areadlink.h"
|
||||
#include "file-set.h"
|
||||
#include "hash-triple.h"
|
||||
#include "pathmax.h"
|
||||
#include "xalloc.h"
|
||||
#include "xgetcwd.h"
|
||||
#include "dosname.h"
|
||||
|
||||
#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
|
||||
|
||||
/* In this file, we cannot handle file names longer than PATH_MAX.
|
||||
On systems with no file name length limit, use a fallback. */
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 8192
|
||||
#endif
|
||||
|
||||
#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
|
||||
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
|
||||
#endif
|
||||
|
||||
#if ISSLASH ('\\')
|
||||
# define SLASHES "/\\"
|
||||
#else
|
||||
# define SLASHES "/"
|
||||
#endif
|
||||
|
||||
#if !((HAVE_CANONICALIZE_FILE_NAME && FUNC_REALPATH_WORKS) \
|
||||
|| GNULIB_CANONICALIZE_LGPL)
|
||||
/* Return the canonical absolute name of file NAME. A canonical name
|
||||
does not contain any ".", ".." components nor any repeated file name
|
||||
separators ('/') or symlinks. All components must exist.
|
||||
The result is malloc'd. */
|
||||
|
||||
char *
|
||||
canonicalize_file_name (const char *name)
|
||||
{
|
||||
return canonicalize_filename_mode (name, CAN_EXISTING);
|
||||
}
|
||||
#endif /* !HAVE_CANONICALIZE_FILE_NAME */
|
||||
|
||||
/* Return true if we've already seen the triple, <FILENAME, dev, ino>.
|
||||
If *HT is not initialized, initialize it. */
|
||||
static bool
|
||||
seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
|
||||
{
|
||||
if (*ht == NULL)
|
||||
{
|
||||
size_t initial_capacity = 7;
|
||||
*ht = hash_initialize (initial_capacity,
|
||||
NULL,
|
||||
triple_hash,
|
||||
triple_compare_ino_str,
|
||||
triple_free);
|
||||
if (*ht == NULL)
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
if (seen_file (*ht, filename, st))
|
||||
return true;
|
||||
|
||||
record_file (*ht, filename, st);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the canonical absolute name of file NAME, while treating
|
||||
missing elements according to CAN_MODE. A canonical name
|
||||
does not contain any ".", ".." components nor any repeated file name
|
||||
separators ('/') or, depending on other CAN_MODE flags, symlinks.
|
||||
Whether components must exist or not depends on canonicalize mode.
|
||||
The result is malloc'd. */
|
||||
|
||||
char *
|
||||
canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
|
||||
{
|
||||
char *rname, *dest, *extra_buf = NULL;
|
||||
char const *start;
|
||||
char const *end;
|
||||
char const *rname_limit;
|
||||
size_t extra_len = 0;
|
||||
Hash_table *ht = NULL;
|
||||
int saved_errno;
|
||||
int can_flags = can_mode & ~CAN_MODE_MASK;
|
||||
bool logical = can_flags & CAN_NOLINKS;
|
||||
size_t prefix_len;
|
||||
|
||||
can_mode &= CAN_MODE_MASK;
|
||||
|
||||
if (MULTIPLE_BITS_SET (can_mode))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (name[0] == '\0')
|
||||
{
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is always zero for Posix hosts, but can be 2 for MS-Windows
|
||||
and MS-DOS X:/foo/bar file names. */
|
||||
prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
|
||||
|
||||
if (!IS_ABSOLUTE_FILE_NAME (name))
|
||||
{
|
||||
rname = xgetcwd ();
|
||||
if (!rname)
|
||||
return NULL;
|
||||
dest = strchr (rname, '\0');
|
||||
if (dest - rname < PATH_MAX)
|
||||
{
|
||||
char *p = xrealloc (rname, PATH_MAX);
|
||||
dest = p + (dest - rname);
|
||||
rname = p;
|
||||
rname_limit = rname + PATH_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
rname_limit = dest;
|
||||
}
|
||||
start = name;
|
||||
prefix_len = FILE_SYSTEM_PREFIX_LEN (rname);
|
||||
}
|
||||
else
|
||||
{
|
||||
rname = xmalloc (PATH_MAX);
|
||||
rname_limit = rname + PATH_MAX;
|
||||
dest = rname;
|
||||
if (prefix_len)
|
||||
{
|
||||
memcpy (rname, name, prefix_len);
|
||||
dest += prefix_len;
|
||||
}
|
||||
*dest++ = '/';
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
|
||||
{
|
||||
if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len)
|
||||
*dest++ = '/';
|
||||
*dest = '\0';
|
||||
}
|
||||
start = name + prefix_len;
|
||||
}
|
||||
|
||||
for ( ; *start; start = end)
|
||||
{
|
||||
/* Skip sequence of multiple file name separators. */
|
||||
while (ISSLASH (*start))
|
||||
++start;
|
||||
|
||||
/* Find end of component. */
|
||||
for (end = start; *end && !ISSLASH (*end); ++end)
|
||||
/* Nothing. */;
|
||||
|
||||
if (end - start == 0)
|
||||
break;
|
||||
else if (end - start == 1 && start[0] == '.')
|
||||
/* nothing */;
|
||||
else if (end - start == 2 && start[0] == '.' && start[1] == '.')
|
||||
{
|
||||
/* Back up to previous component, ignore if at root already. */
|
||||
if (dest > rname + prefix_len + 1)
|
||||
for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest)
|
||||
continue;
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
|
||||
&& !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1]))
|
||||
dest++;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (!ISSLASH (dest[-1]))
|
||||
*dest++ = '/';
|
||||
|
||||
if (dest + (end - start) >= rname_limit)
|
||||
{
|
||||
ptrdiff_t dest_offset = dest - rname;
|
||||
size_t new_size = rname_limit - rname;
|
||||
|
||||
if (end - start + 1 > PATH_MAX)
|
||||
new_size += end - start + 1;
|
||||
else
|
||||
new_size += PATH_MAX;
|
||||
rname = xrealloc (rname, new_size);
|
||||
rname_limit = rname + new_size;
|
||||
|
||||
dest = rname + dest_offset;
|
||||
}
|
||||
|
||||
dest = memcpy (dest, start, end - start);
|
||||
dest += end - start;
|
||||
*dest = '\0';
|
||||
|
||||
if (logical && (can_mode == CAN_MISSING))
|
||||
{
|
||||
/* Avoid the stat in this case as it's inconsequential.
|
||||
i.e. we're neither resolving symlinks or testing
|
||||
component existence. */
|
||||
st.st_mode = 0;
|
||||
}
|
||||
else if ((logical ? stat (rname, &st) : lstat (rname, &st)) != 0)
|
||||
{
|
||||
/* FIXME: If errno == EOVERFLOW here, the entry exists. */
|
||||
saved_errno = errno;
|
||||
if (can_mode == CAN_EXISTING)
|
||||
goto error;
|
||||
if (can_mode == CAN_ALL_BUT_LAST)
|
||||
{
|
||||
if (end[strspn (end, SLASHES)] || saved_errno != ENOENT)
|
||||
goto error;
|
||||
continue;
|
||||
}
|
||||
st.st_mode = 0;
|
||||
}
|
||||
|
||||
if (S_ISLNK (st.st_mode))
|
||||
{
|
||||
char *buf;
|
||||
size_t n, len;
|
||||
|
||||
/* Detect loops. We cannot use the cycle-check module here,
|
||||
since it's actually possible to encounter the same symlink
|
||||
more than once in a given traversal. However, encountering
|
||||
the same symlink,NAME pair twice does indicate a loop. */
|
||||
if (seen_triple (&ht, name, &st))
|
||||
{
|
||||
if (can_mode == CAN_MISSING)
|
||||
continue;
|
||||
saved_errno = ELOOP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
buf = areadlink_with_size (rname, st.st_size);
|
||||
if (!buf)
|
||||
{
|
||||
if (can_mode == CAN_MISSING && errno != ENOMEM)
|
||||
continue;
|
||||
saved_errno = errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
n = strlen (buf);
|
||||
len = strlen (end);
|
||||
|
||||
if (!extra_len)
|
||||
{
|
||||
extra_len =
|
||||
((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
|
||||
extra_buf = xmalloc (extra_len);
|
||||
}
|
||||
else if ((n + len + 1) > extra_len)
|
||||
{
|
||||
extra_len = n + len + 1;
|
||||
extra_buf = xrealloc (extra_buf, extra_len);
|
||||
}
|
||||
|
||||
/* Careful here, end may be a pointer into extra_buf... */
|
||||
memmove (&extra_buf[n], end, len + 1);
|
||||
name = end = memcpy (extra_buf, buf, n);
|
||||
|
||||
if (IS_ABSOLUTE_FILE_NAME (buf))
|
||||
{
|
||||
size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
|
||||
|
||||
if (pfxlen)
|
||||
memcpy (rname, buf, pfxlen);
|
||||
dest = rname + pfxlen;
|
||||
*dest++ = '/'; /* It's an absolute symlink */
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
|
||||
{
|
||||
if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen)
|
||||
*dest++ = '/';
|
||||
*dest = '\0';
|
||||
}
|
||||
/* Install the new prefix to be in effect hereafter. */
|
||||
prefix_len = pfxlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Back up to previous component, ignore if at root
|
||||
already: */
|
||||
if (dest > rname + prefix_len + 1)
|
||||
for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest)
|
||||
continue;
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
|
||||
&& ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)
|
||||
dest++;
|
||||
}
|
||||
|
||||
free (buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
|
||||
{
|
||||
saved_errno = ENOTDIR;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dest > rname + prefix_len + 1 && ISSLASH (dest[-1]))
|
||||
--dest;
|
||||
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len
|
||||
&& ISSLASH (*dest) && !ISSLASH (dest[1]))
|
||||
dest++;
|
||||
*dest = '\0';
|
||||
if (rname_limit != dest + 1)
|
||||
rname = xrealloc (rname, dest - rname + 1);
|
||||
|
||||
free (extra_buf);
|
||||
if (ht)
|
||||
hash_free (ht);
|
||||
return rname;
|
||||
|
||||
error:
|
||||
free (extra_buf);
|
||||
free (rname);
|
||||
if (ht)
|
||||
hash_free (ht);
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
56
lib/canonicalize.h
Normal file
56
lib/canonicalize.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Return the canonical absolute name of a given file.
|
||||
Copyright (C) 1996-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef CANONICALIZE_H_
|
||||
# define CANONICALIZE_H_
|
||||
|
||||
#include <stdlib.h> /* for canonicalize_file_name */
|
||||
|
||||
#define CAN_MODE_MASK (CAN_EXISTING | CAN_ALL_BUT_LAST | CAN_MISSING)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum canonicalize_mode_t
|
||||
{
|
||||
/* All components must exist. */
|
||||
CAN_EXISTING = 0,
|
||||
|
||||
/* All components excluding last one must exist. */
|
||||
CAN_ALL_BUT_LAST = 1,
|
||||
|
||||
/* No requirements on components existence. */
|
||||
CAN_MISSING = 2,
|
||||
|
||||
/* Don't expand symlinks. */
|
||||
CAN_NOLINKS = 4
|
||||
};
|
||||
typedef enum canonicalize_mode_t canonicalize_mode_t;
|
||||
|
||||
/* Return the canonical absolute name of file NAME, while treating
|
||||
missing elements according to CAN_MODE. A canonical name
|
||||
does not contain any `.', `..' components nor any repeated file name
|
||||
separators ('/') or, depending on other CAN_MODE flags, symlinks.
|
||||
Whether components must exist or not depends on canonicalize mode.
|
||||
The result is malloc'd. */
|
||||
char *canonicalize_filename_mode (const char *, canonicalize_mode_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !CANONICALIZE_H_ */
|
160
lib/careadlinkat.c
Normal file
160
lib/careadlinkat.c
Normal file
@ -0,0 +1,160 @@
|
||||
/* Read symbolic links into a buffer without size limitation, relative to fd.
|
||||
|
||||
Copyright (C) 2001, 2003-2004, 2007, 2009-2020 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "careadlinkat.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Define this independently so that stdint.h is not a prerequisite. */
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
|
||||
#endif
|
||||
|
||||
#include "allocator.h"
|
||||
|
||||
/* Assuming the current directory is FD, get the symbolic link value
|
||||
of FILENAME as a null-terminated string and put it into a buffer.
|
||||
If FD is AT_FDCWD, FILENAME is interpreted relative to the current
|
||||
working directory, as in openat.
|
||||
|
||||
If the link is small enough to fit into BUFFER put it there.
|
||||
BUFFER's size is BUFFER_SIZE, and BUFFER can be null
|
||||
if BUFFER_SIZE is zero.
|
||||
|
||||
If the link is not small, put it into a dynamically allocated
|
||||
buffer managed by ALLOC. It is the caller's responsibility to free
|
||||
the returned value if it is nonnull and is not BUFFER. A null
|
||||
ALLOC stands for the standard allocator.
|
||||
|
||||
The PREADLINKAT function specifies how to read links. It operates
|
||||
like POSIX readlinkat()
|
||||
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
|
||||
but can assume that its first argument is the same as FD.
|
||||
|
||||
If successful, return the buffer address; otherwise return NULL and
|
||||
set errno. */
|
||||
|
||||
char *
|
||||
careadlinkat (int fd, char const *filename,
|
||||
char *buffer, size_t buffer_size,
|
||||
struct allocator const *alloc,
|
||||
ssize_t (*preadlinkat) (int, char const *, char *, size_t))
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t buf_size_max =
|
||||
SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
|
||||
char stack_buf[1024];
|
||||
|
||||
if (! alloc)
|
||||
alloc = &stdlib_allocator;
|
||||
|
||||
if (! buffer_size)
|
||||
{
|
||||
/* Allocate the initial buffer on the stack. This way, in the
|
||||
common case of a symlink of small size, we get away with a
|
||||
single small malloc() instead of a big malloc() followed by a
|
||||
shrinking realloc(). */
|
||||
buffer = stack_buf;
|
||||
buffer_size = sizeof stack_buf;
|
||||
}
|
||||
|
||||
buf = buffer;
|
||||
buf_size = buffer_size;
|
||||
|
||||
do
|
||||
{
|
||||
/* Attempt to read the link into the current buffer. */
|
||||
ssize_t link_length = preadlinkat (fd, filename, buf, buf_size);
|
||||
size_t link_size;
|
||||
if (link_length < 0)
|
||||
{
|
||||
/* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
|
||||
with errno == ERANGE if the buffer is too small. */
|
||||
int readlinkat_errno = errno;
|
||||
if (readlinkat_errno != ERANGE)
|
||||
{
|
||||
if (buf != buffer)
|
||||
{
|
||||
alloc->free (buf);
|
||||
errno = readlinkat_errno;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
link_size = link_length;
|
||||
|
||||
if (link_size < buf_size)
|
||||
{
|
||||
buf[link_size++] = '\0';
|
||||
|
||||
if (buf == stack_buf)
|
||||
{
|
||||
char *b = (char *) alloc->allocate (link_size);
|
||||
buf_size = link_size;
|
||||
if (! b)
|
||||
break;
|
||||
memcpy (b, buf, link_size);
|
||||
buf = b;
|
||||
}
|
||||
else if (link_size < buf_size && buf != buffer && alloc->reallocate)
|
||||
{
|
||||
/* Shrink BUF before returning it. */
|
||||
char *b = (char *) alloc->reallocate (buf, link_size);
|
||||
if (b)
|
||||
buf = b;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (buf != buffer)
|
||||
alloc->free (buf);
|
||||
|
||||
if (buf_size <= buf_size_max / 2)
|
||||
buf_size *= 2;
|
||||
else if (buf_size < buf_size_max)
|
||||
buf_size = buf_size_max;
|
||||
else if (buf_size_max < SIZE_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
break;
|
||||
buf = (char *) alloc->allocate (buf_size);
|
||||
}
|
||||
while (buf);
|
||||
|
||||
if (alloc->die)
|
||||
alloc->die (buf_size);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
67
lib/careadlinkat.h
Normal file
67
lib/careadlinkat.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* Read symbolic links into a buffer without size limitation, relative to fd.
|
||||
|
||||
Copyright (C) 2011-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */
|
||||
|
||||
#ifndef _GL_CAREADLINKAT_H
|
||||
#define _GL_CAREADLINKAT_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct allocator;
|
||||
|
||||
/* Assuming the current directory is FD, get the symbolic link value
|
||||
of FILENAME as a null-terminated string and put it into a buffer.
|
||||
If FD is AT_FDCWD, FILENAME is interpreted relative to the current
|
||||
working directory, as in openat.
|
||||
|
||||
If the link is small enough to fit into BUFFER put it there.
|
||||
BUFFER's size is BUFFER_SIZE, and BUFFER can be null
|
||||
if BUFFER_SIZE is zero.
|
||||
|
||||
If the link is not small, put it into a dynamically allocated
|
||||
buffer managed by ALLOC. It is the caller's responsibility to free
|
||||
the returned value if it is nonnull and is not BUFFER.
|
||||
|
||||
The PREADLINKAT function specifies how to read links. It operates
|
||||
like POSIX readlinkat()
|
||||
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
|
||||
but can assume that its first argument is the same as FD.
|
||||
|
||||
If successful, return the buffer address; otherwise return NULL and
|
||||
set errno. */
|
||||
|
||||
char *careadlinkat (int fd, char const *filename,
|
||||
char *restrict buffer, size_t buffer_size,
|
||||
struct allocator const *alloc,
|
||||
ssize_t (*preadlinkat) (int, char const *,
|
||||
char *, size_t));
|
||||
|
||||
/* Suitable value for careadlinkat's FD argument. */
|
||||
#if HAVE_READLINKAT
|
||||
/* AT_FDCWD is declared in <fcntl.h>. */
|
||||
#else
|
||||
/* Define AT_FDCWD independently, so that the careadlinkat module does
|
||||
not depend on the fcntl-h module. We might as well use the same value
|
||||
as fcntl-h. */
|
||||
# ifndef AT_FDCWD
|
||||
# define AT_FDCWD (-3041965)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _GL_CAREADLINKAT_H */
|
514
lib/cdefs.h
Normal file
514
lib/cdefs.h
Normal file
@ -0,0 +1,514 @@
|
||||
/* Copyright (C) 1992-2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library 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.
|
||||
|
||||
The GNU C Library 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 the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _SYS_CDEFS_H
|
||||
#define _SYS_CDEFS_H 1
|
||||
|
||||
/* We are almost always included from features.h. */
|
||||
#ifndef _FEATURES_H
|
||||
# include <features.h>
|
||||
#endif
|
||||
|
||||
/* The GNU libc does not support any K&R compilers or the traditional mode
|
||||
of ISO C compilers anymore. Check for some of the combinations not
|
||||
anymore supported. */
|
||||
#if defined __GNUC__ && !defined __STDC__
|
||||
# error "You need a ISO C conforming compiler to use the glibc headers"
|
||||
#endif
|
||||
|
||||
/* Some user header file might have defined this before. */
|
||||
#undef __P
|
||||
#undef __PMT
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
/* All functions, except those with callbacks or those that
|
||||
synchronize memory, are leaf functions. */
|
||||
# if __GNUC_PREREQ (4, 6) && !defined _LIBC
|
||||
# define __LEAF , __leaf__
|
||||
# define __LEAF_ATTR __attribute__ ((__leaf__))
|
||||
# else
|
||||
# define __LEAF
|
||||
# define __LEAF_ATTR
|
||||
# endif
|
||||
|
||||
/* GCC can always grok prototypes. For C++ programs we add throw()
|
||||
to help it optimize the function calls. But this works only with
|
||||
gcc 2.8.x and egcs. For gcc 3.2 and up we even mark C functions
|
||||
as non-throwing using a function attribute since programs can use
|
||||
the -fexceptions options for C code as well. */
|
||||
# if !defined __cplusplus && __GNUC_PREREQ (3, 3)
|
||||
# define __THROW __attribute__ ((__nothrow__ __LEAF))
|
||||
# define __THROWNL __attribute__ ((__nothrow__))
|
||||
# define __NTH(fct) __attribute__ ((__nothrow__ __LEAF)) fct
|
||||
# define __NTHNL(fct) __attribute__ ((__nothrow__)) fct
|
||||
# else
|
||||
# if defined __cplusplus && __GNUC_PREREQ (2,8)
|
||||
# define __THROW throw ()
|
||||
# define __THROWNL throw ()
|
||||
# define __NTH(fct) __LEAF_ATTR fct throw ()
|
||||
# define __NTHNL(fct) fct throw ()
|
||||
# else
|
||||
# define __THROW
|
||||
# define __THROWNL
|
||||
# define __NTH(fct) fct
|
||||
# define __NTHNL(fct) fct
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#else /* Not GCC. */
|
||||
|
||||
# if (defined __cplusplus \
|
||||
|| (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
|
||||
# define __inline inline
|
||||
# else
|
||||
# define __inline /* No inline functions. */
|
||||
# endif
|
||||
|
||||
# define __THROW
|
||||
# define __THROWNL
|
||||
# define __NTH(fct) fct
|
||||
|
||||
#endif /* GCC. */
|
||||
|
||||
/* Compilers that are not clang may object to
|
||||
#if defined __clang__ && __has_extension(...)
|
||||
even though they do not need to evaluate the right-hand side of the &&. */
|
||||
#if defined __clang__ && defined __has_extension
|
||||
# define __glibc_clang_has_extension(ext) __has_extension (ext)
|
||||
#else
|
||||
# define __glibc_clang_has_extension(ext) 0
|
||||
#endif
|
||||
|
||||
/* These two macros are not used in glibc anymore. They are kept here
|
||||
only because some other projects expect the macros to be defined. */
|
||||
#define __P(args) args
|
||||
#define __PMT(args) args
|
||||
|
||||
/* For these things, GCC behaves the ANSI way normally,
|
||||
and the non-ANSI way under -traditional. */
|
||||
|
||||
#define __CONCAT(x,y) x ## y
|
||||
#define __STRING(x) #x
|
||||
|
||||
/* This is not a typedef so `const __ptr_t' does the right thing. */
|
||||
#define __ptr_t void *
|
||||
|
||||
|
||||
/* C++ needs to know that types and declarations are C, not C++. */
|
||||
#ifdef __cplusplus
|
||||
# define __BEGIN_DECLS extern "C" {
|
||||
# define __END_DECLS }
|
||||
#else
|
||||
# define __BEGIN_DECLS
|
||||
# define __END_DECLS
|
||||
#endif
|
||||
|
||||
|
||||
/* Fortify support. */
|
||||
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
|
||||
#define __bos0(ptr) __builtin_object_size (ptr, 0)
|
||||
|
||||
#if __GNUC_PREREQ (4,3)
|
||||
# define __warndecl(name, msg) \
|
||||
extern void name (void) __attribute__((__warning__ (msg)))
|
||||
# define __warnattr(msg) __attribute__((__warning__ (msg)))
|
||||
# define __errordecl(name, msg) \
|
||||
extern void name (void) __attribute__((__error__ (msg)))
|
||||
#else
|
||||
# define __warndecl(name, msg) extern void name (void)
|
||||
# define __warnattr(msg)
|
||||
# define __errordecl(name, msg) extern void name (void)
|
||||
#endif
|
||||
|
||||
/* Support for flexible arrays.
|
||||
Headers that should use flexible arrays only if they're "real"
|
||||
(e.g. only if they won't affect sizeof()) should test
|
||||
#if __glibc_c99_flexarr_available. */
|
||||
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L && !defined __HP_cc
|
||||
# define __flexarr []
|
||||
# define __glibc_c99_flexarr_available 1
|
||||
#elif __GNUC_PREREQ (2,97)
|
||||
/* GCC 2.97 supports C99 flexible array members as an extension,
|
||||
even when in C89 mode or compiling C++ (any version). */
|
||||
# define __flexarr []
|
||||
# define __glibc_c99_flexarr_available 1
|
||||
#elif defined __GNUC__
|
||||
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
|
||||
an equivalent extension with slightly different notation. */
|
||||
# define __flexarr [0]
|
||||
# define __glibc_c99_flexarr_available 1
|
||||
#else
|
||||
/* Some other non-C99 compiler. Approximate with [1]. */
|
||||
# define __flexarr [1]
|
||||
# define __glibc_c99_flexarr_available 0
|
||||
#endif
|
||||
|
||||
|
||||
/* __asm__ ("xyz") is used throughout the headers to rename functions
|
||||
at the assembly language level. This is wrapped by the __REDIRECT
|
||||
macro, in order to support compilers that can do this some other
|
||||
way. When compilers don't support asm-names at all, we have to do
|
||||
preprocessor tricks instead (which don't have exactly the right
|
||||
semantics, but it's the best we can do).
|
||||
|
||||
Example:
|
||||
int __REDIRECT(setpgrp, (__pid_t pid, __pid_t pgrp), setpgid); */
|
||||
|
||||
#if defined __GNUC__ && __GNUC__ >= 2
|
||||
|
||||
# define __REDIRECT(name, proto, alias) name proto __asm__ (__ASMNAME (#alias))
|
||||
# ifdef __cplusplus
|
||||
# define __REDIRECT_NTH(name, proto, alias) \
|
||||
name proto __THROW __asm__ (__ASMNAME (#alias))
|
||||
# define __REDIRECT_NTHNL(name, proto, alias) \
|
||||
name proto __THROWNL __asm__ (__ASMNAME (#alias))
|
||||
# else
|
||||
# define __REDIRECT_NTH(name, proto, alias) \
|
||||
name proto __asm__ (__ASMNAME (#alias)) __THROW
|
||||
# define __REDIRECT_NTHNL(name, proto, alias) \
|
||||
name proto __asm__ (__ASMNAME (#alias)) __THROWNL
|
||||
# endif
|
||||
# define __ASMNAME(cname) __ASMNAME2 (__USER_LABEL_PREFIX__, cname)
|
||||
# define __ASMNAME2(prefix, cname) __STRING (prefix) cname
|
||||
|
||||
/*
|
||||
#elif __SOME_OTHER_COMPILER__
|
||||
|
||||
# define __REDIRECT(name, proto, alias) name proto; \
|
||||
_Pragma("let " #name " = " #alias)
|
||||
*/
|
||||
#endif
|
||||
|
||||
/* GCC has various useful declarations that can be made with the
|
||||
`__attribute__' syntax. All of the ways we use this do fine if
|
||||
they are omitted for compilers that don't understand it. */
|
||||
#if !defined __GNUC__ || __GNUC__ < 2
|
||||
# define __attribute__(xyz) /* Ignore */
|
||||
#endif
|
||||
|
||||
/* At some point during the gcc 2.96 development the `malloc' attribute
|
||||
for functions was introduced. We don't want to use it unconditionally
|
||||
(although this would be possible) since it generates warnings. */
|
||||
#if __GNUC_PREREQ (2,96)
|
||||
# define __attribute_malloc__ __attribute__ ((__malloc__))
|
||||
#else
|
||||
# define __attribute_malloc__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* Tell the compiler which arguments to an allocation function
|
||||
indicate the size of the allocation. */
|
||||
#if __GNUC_PREREQ (4, 3)
|
||||
# define __attribute_alloc_size__(params) \
|
||||
__attribute__ ((__alloc_size__ params))
|
||||
#else
|
||||
# define __attribute_alloc_size__(params) /* Ignore. */
|
||||
#endif
|
||||
|
||||
/* At some point during the gcc 2.96 development the `pure' attribute
|
||||
for functions was introduced. We don't want to use it unconditionally
|
||||
(although this would be possible) since it generates warnings. */
|
||||
#if __GNUC_PREREQ (2,96)
|
||||
# define __attribute_pure__ __attribute__ ((__pure__))
|
||||
#else
|
||||
# define __attribute_pure__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* This declaration tells the compiler that the value is constant. */
|
||||
#if __GNUC_PREREQ (2,5)
|
||||
# define __attribute_const__ __attribute__ ((__const__))
|
||||
#else
|
||||
# define __attribute_const__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* At some point during the gcc 3.1 development the `used' attribute
|
||||
for functions was introduced. We don't want to use it unconditionally
|
||||
(although this would be possible) since it generates warnings. */
|
||||
#if __GNUC_PREREQ (3,1)
|
||||
# define __attribute_used__ __attribute__ ((__used__))
|
||||
# define __attribute_noinline__ __attribute__ ((__noinline__))
|
||||
#else
|
||||
# define __attribute_used__ __attribute__ ((__unused__))
|
||||
# define __attribute_noinline__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* Since version 3.2, gcc allows marking deprecated functions. */
|
||||
#if __GNUC_PREREQ (3,2)
|
||||
# define __attribute_deprecated__ __attribute__ ((__deprecated__))
|
||||
#else
|
||||
# define __attribute_deprecated__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* Since version 4.5, gcc also allows one to specify the message printed
|
||||
when a deprecated function is used. clang claims to be gcc 4.2, but
|
||||
may also support this feature. */
|
||||
#if __GNUC_PREREQ (4,5) || \
|
||||
__glibc_clang_has_extension (__attribute_deprecated_with_message__)
|
||||
# define __attribute_deprecated_msg__(msg) \
|
||||
__attribute__ ((__deprecated__ (msg)))
|
||||
#else
|
||||
# define __attribute_deprecated_msg__(msg) __attribute_deprecated__
|
||||
#endif
|
||||
|
||||
/* At some point during the gcc 2.8 development the `format_arg' attribute
|
||||
for functions was introduced. We don't want to use it unconditionally
|
||||
(although this would be possible) since it generates warnings.
|
||||
If several `format_arg' attributes are given for the same function, in
|
||||
gcc-3.0 and older, all but the last one are ignored. In newer gccs,
|
||||
all designated arguments are considered. */
|
||||
#if __GNUC_PREREQ (2,8)
|
||||
# define __attribute_format_arg__(x) __attribute__ ((__format_arg__ (x)))
|
||||
#else
|
||||
# define __attribute_format_arg__(x) /* Ignore */
|
||||
#endif
|
||||
|
||||
/* At some point during the gcc 2.97 development the `strfmon' format
|
||||
attribute for functions was introduced. We don't want to use it
|
||||
unconditionally (although this would be possible) since it
|
||||
generates warnings. */
|
||||
#if __GNUC_PREREQ (2,97)
|
||||
# define __attribute_format_strfmon__(a,b) \
|
||||
__attribute__ ((__format__ (__strfmon__, a, b)))
|
||||
#else
|
||||
# define __attribute_format_strfmon__(a,b) /* Ignore */
|
||||
#endif
|
||||
|
||||
/* The nonnull function attribute marks pointer parameters that
|
||||
must not be NULL. Do not define __nonnull if it is already defined,
|
||||
for portability when this file is used in Gnulib. */
|
||||
#ifndef __nonnull
|
||||
# if __GNUC_PREREQ (3,3)
|
||||
# define __nonnull(params) __attribute__ ((__nonnull__ params))
|
||||
# else
|
||||
# define __nonnull(params)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* If fortification mode, we warn about unused results of certain
|
||||
function calls which can lead to problems. */
|
||||
#if __GNUC_PREREQ (3,4)
|
||||
# define __attribute_warn_unused_result__ \
|
||||
__attribute__ ((__warn_unused_result__))
|
||||
# if defined __USE_FORTIFY_LEVEL && __USE_FORTIFY_LEVEL > 0
|
||||
# define __wur __attribute_warn_unused_result__
|
||||
# endif
|
||||
#else
|
||||
# define __attribute_warn_unused_result__ /* empty */
|
||||
#endif
|
||||
#ifndef __wur
|
||||
# define __wur /* Ignore */
|
||||
#endif
|
||||
|
||||
/* Forces a function to be always inlined. */
|
||||
#if __GNUC_PREREQ (3,2)
|
||||
/* The Linux kernel defines __always_inline in stddef.h (283d7573), and
|
||||
it conflicts with this definition. Therefore undefine it first to
|
||||
allow either header to be included first. */
|
||||
# undef __always_inline
|
||||
# define __always_inline __inline __attribute__ ((__always_inline__))
|
||||
#else
|
||||
# undef __always_inline
|
||||
# define __always_inline __inline
|
||||
#endif
|
||||
|
||||
/* Associate error messages with the source location of the call site rather
|
||||
than with the source location inside the function. */
|
||||
#if __GNUC_PREREQ (4,3)
|
||||
# define __attribute_artificial__ __attribute__ ((__artificial__))
|
||||
#else
|
||||
# define __attribute_artificial__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99
|
||||
inline semantics, unless -fgnu89-inline is used. Using __GNUC_STDC_INLINE__
|
||||
or __GNUC_GNU_INLINE is not a good enough check for gcc because gcc versions
|
||||
older than 4.3 may define these macros and still not guarantee GNU inlining
|
||||
semantics.
|
||||
|
||||
clang++ identifies itself as gcc-4.2, but has support for GNU inlining
|
||||
semantics, that can be checked for by using the __GNUC_STDC_INLINE_ and
|
||||
__GNUC_GNU_INLINE__ macro definitions. */
|
||||
#if (!defined __cplusplus || __GNUC_PREREQ (4,3) \
|
||||
|| (defined __clang__ && (defined __GNUC_STDC_INLINE__ \
|
||||
|| defined __GNUC_GNU_INLINE__)))
|
||||
# if defined __GNUC_STDC_INLINE__ || defined __cplusplus
|
||||
# define __extern_inline extern __inline __attribute__ ((__gnu_inline__))
|
||||
# define __extern_always_inline \
|
||||
extern __always_inline __attribute__ ((__gnu_inline__))
|
||||
# else
|
||||
# define __extern_inline extern __inline
|
||||
# define __extern_always_inline extern __always_inline
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __extern_always_inline
|
||||
# define __fortify_function __extern_always_inline __attribute_artificial__
|
||||
#endif
|
||||
|
||||
/* GCC 4.3 and above allow passing all anonymous arguments of an
|
||||
__extern_always_inline function to some other vararg function. */
|
||||
#if __GNUC_PREREQ (4,3)
|
||||
# define __va_arg_pack() __builtin_va_arg_pack ()
|
||||
# define __va_arg_pack_len() __builtin_va_arg_pack_len ()
|
||||
#endif
|
||||
|
||||
/* It is possible to compile containing GCC extensions even if GCC is
|
||||
run in pedantic mode if the uses are carefully marked using the
|
||||
`__extension__' keyword. But this is not generally available before
|
||||
version 2.8. */
|
||||
#if !__GNUC_PREREQ (2,8)
|
||||
# define __extension__ /* Ignore */
|
||||
#endif
|
||||
|
||||
/* __restrict is known in EGCS 1.2 and above. */
|
||||
#if !__GNUC_PREREQ (2,92)
|
||||
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
|
||||
# define __restrict restrict
|
||||
# else
|
||||
# define __restrict /* Ignore */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ISO C99 also allows to declare arrays as non-overlapping. The syntax is
|
||||
array_name[restrict]
|
||||
GCC 3.1 supports this. */
|
||||
#if __GNUC_PREREQ (3,1) && !defined __GNUG__
|
||||
# define __restrict_arr __restrict
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# define __restrict_arr /* Not supported in old GCC. */
|
||||
# else
|
||||
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
|
||||
# define __restrict_arr restrict
|
||||
# else
|
||||
/* Some other non-C99 compiler. */
|
||||
# define __restrict_arr /* Not supported. */
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
# define __glibc_unlikely(cond) __builtin_expect ((cond), 0)
|
||||
# define __glibc_likely(cond) __builtin_expect ((cond), 1)
|
||||
#else
|
||||
# define __glibc_unlikely(cond) (cond)
|
||||
# define __glibc_likely(cond) (cond)
|
||||
#endif
|
||||
|
||||
#ifdef __has_attribute
|
||||
# define __glibc_has_attribute(attr) __has_attribute (attr)
|
||||
#else
|
||||
# define __glibc_has_attribute(attr) 0
|
||||
#endif
|
||||
|
||||
#if (!defined _Noreturn \
|
||||
&& (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 201112 \
|
||||
&& !__GNUC_PREREQ (4,7))
|
||||
# if __GNUC_PREREQ (2,8)
|
||||
# define _Noreturn __attribute__ ((__noreturn__))
|
||||
# else
|
||||
# define _Noreturn
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if __GNUC_PREREQ (8, 0)
|
||||
/* Describes a char array whose address can safely be passed as the first
|
||||
argument to strncpy and strncat, as the char array is not necessarily
|
||||
a NUL-terminated string. */
|
||||
# define __attribute_nonstring__ __attribute__ ((__nonstring__))
|
||||
#else
|
||||
# define __attribute_nonstring__
|
||||
#endif
|
||||
|
||||
#if (!defined _Static_assert && !defined __cplusplus \
|
||||
&& (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 201112 \
|
||||
&& (!__GNUC_PREREQ (4, 6) || defined __STRICT_ANSI__))
|
||||
# define _Static_assert(expr, diagnostic) \
|
||||
extern int (*__Static_assert_function (void)) \
|
||||
[!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]
|
||||
#endif
|
||||
|
||||
/* The #ifndef lets Gnulib avoid including these on non-glibc
|
||||
platforms, where the includes typically do not exist. */
|
||||
#ifndef __WORDSIZE
|
||||
# include <bits/wordsize.h>
|
||||
# include <bits/long-double.h>
|
||||
#endif
|
||||
|
||||
#if defined __LONG_DOUBLE_MATH_OPTIONAL && defined __NO_LONG_DOUBLE_MATH
|
||||
# define __LDBL_COMPAT 1
|
||||
# ifdef __REDIRECT
|
||||
# define __LDBL_REDIR1(name, proto, alias) __REDIRECT (name, proto, alias)
|
||||
# define __LDBL_REDIR(name, proto) \
|
||||
__LDBL_REDIR1 (name, proto, __nldbl_##name)
|
||||
# define __LDBL_REDIR1_NTH(name, proto, alias) __REDIRECT_NTH (name, proto, alias)
|
||||
# define __LDBL_REDIR_NTH(name, proto) \
|
||||
__LDBL_REDIR1_NTH (name, proto, __nldbl_##name)
|
||||
# define __LDBL_REDIR1_DECL(name, alias) \
|
||||
extern __typeof (name) name __asm (__ASMNAME (#alias));
|
||||
# define __LDBL_REDIR_DECL(name) \
|
||||
extern __typeof (name) name __asm (__ASMNAME ("__nldbl_" #name));
|
||||
# define __REDIRECT_LDBL(name, proto, alias) \
|
||||
__LDBL_REDIR1 (name, proto, __nldbl_##alias)
|
||||
# define __REDIRECT_NTH_LDBL(name, proto, alias) \
|
||||
__LDBL_REDIR1_NTH (name, proto, __nldbl_##alias)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined __LDBL_COMPAT || !defined __REDIRECT
|
||||
# define __LDBL_REDIR1(name, proto, alias) name proto
|
||||
# define __LDBL_REDIR(name, proto) name proto
|
||||
# define __LDBL_REDIR1_NTH(name, proto, alias) name proto __THROW
|
||||
# define __LDBL_REDIR_NTH(name, proto) name proto __THROW
|
||||
# define __LDBL_REDIR_DECL(name)
|
||||
# ifdef __REDIRECT
|
||||
# define __REDIRECT_LDBL(name, proto, alias) __REDIRECT (name, proto, alias)
|
||||
# define __REDIRECT_NTH_LDBL(name, proto, alias) \
|
||||
__REDIRECT_NTH (name, proto, alias)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* __glibc_macro_warning (MESSAGE) issues warning MESSAGE. This is
|
||||
intended for use in preprocessor macros.
|
||||
|
||||
Note: MESSAGE must be a _single_ string; concatenation of string
|
||||
literals is not supported. */
|
||||
#if __GNUC_PREREQ (4,8) || __glibc_clang_prereq (3,5)
|
||||
# define __glibc_macro_warning1(message) _Pragma (#message)
|
||||
# define __glibc_macro_warning(message) \
|
||||
__glibc_macro_warning1 (GCC warning message)
|
||||
#else
|
||||
# define __glibc_macro_warning(msg)
|
||||
#endif
|
||||
|
||||
/* Generic selection (ISO C11) is a C-only feature, available in GCC
|
||||
since version 4.9. Previous versions do not provide generic
|
||||
selection, even though they might set __STDC_VERSION__ to 201112L,
|
||||
when in -std=c11 mode. Thus, we must check for !defined __GNUC__
|
||||
when testing __STDC_VERSION__ for generic selection support.
|
||||
On the other hand, Clang also defines __GNUC__, so a clang-specific
|
||||
check is required to enable the use of generic selection. */
|
||||
#if !defined __cplusplus \
|
||||
&& (__GNUC_PREREQ (4, 9) \
|
||||
|| __glibc_clang_has_extension (c_generic_selections) \
|
||||
|| (!defined __GNUC__ && defined __STDC_VERSION__ \
|
||||
&& __STDC_VERSION__ >= 201112L))
|
||||
# define __HAVE_GENERIC_SELECTION 1
|
||||
#else
|
||||
# define __HAVE_GENERIC_SELECTION 0
|
||||
#endif
|
||||
|
||||
#endif /* sys/cdefs.h */
|
264
lib/chdir-long.c
Normal file
264
lib/chdir-long.c
Normal file
@ -0,0 +1,264 @@
|
||||
/* provide a chdir function that tries not to fail due to ENAMETOOLONG
|
||||
Copyright (C) 2004-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Jim Meyering */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "chdir-long.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "assure.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
# error "compile this file only if your system defines PATH_MAX"
|
||||
#endif
|
||||
|
||||
/* The results of openat() in this file are not leaked to any
|
||||
single-threaded code that could use stdio.
|
||||
FIXME - if the kernel ever adds support for multi-thread safety for
|
||||
avoiding standard fds, then we should use openat_safer. */
|
||||
|
||||
struct cd_buf
|
||||
{
|
||||
int fd;
|
||||
};
|
||||
|
||||
static void
|
||||
cdb_init (struct cd_buf *cdb)
|
||||
{
|
||||
cdb->fd = AT_FDCWD;
|
||||
}
|
||||
|
||||
static int
|
||||
cdb_fchdir (struct cd_buf const *cdb)
|
||||
{
|
||||
return fchdir (cdb->fd);
|
||||
}
|
||||
|
||||
static void
|
||||
cdb_free (struct cd_buf const *cdb)
|
||||
{
|
||||
if (0 <= cdb->fd)
|
||||
{
|
||||
bool close_fail = close (cdb->fd);
|
||||
assure (! close_fail);
|
||||
}
|
||||
}
|
||||
|
||||
/* Given a file descriptor of an open directory (or AT_FDCWD), CDB->fd,
|
||||
try to open the CDB->fd-relative directory, DIR. If the open succeeds,
|
||||
update CDB->fd with the resulting descriptor, close the incoming file
|
||||
descriptor, and return zero. Upon failure, return -1 and set errno. */
|
||||
static int
|
||||
cdb_advance_fd (struct cd_buf *cdb, char const *dir)
|
||||
{
|
||||
int new_fd = openat (cdb->fd, dir,
|
||||
O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
|
||||
if (new_fd < 0)
|
||||
return -1;
|
||||
|
||||
cdb_free (cdb);
|
||||
cdb->fd = new_fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a pointer to the first non-slash in S. */
|
||||
static char * _GL_ATTRIBUTE_PURE
|
||||
find_non_slash (char const *s)
|
||||
{
|
||||
size_t n_slash = strspn (s, "/");
|
||||
return (char *) s + n_slash;
|
||||
}
|
||||
|
||||
/* This is a function much like chdir, but without the PATH_MAX limitation
|
||||
on the length of the directory name. A significant difference is that
|
||||
it must be able to modify (albeit only temporarily) the directory
|
||||
name. It handles an arbitrarily long directory name by operating
|
||||
on manageable portions of the name. On systems without the openat
|
||||
syscall, this means changing the working directory to more and more
|
||||
"distant" points along the long directory name and then restoring
|
||||
the working directory. If any of those attempts to save or restore
|
||||
the working directory fails, this function exits nonzero.
|
||||
|
||||
Note that this function may still fail with errno == ENAMETOOLONG, but
|
||||
only if the specified directory name contains a component that is long
|
||||
enough to provoke such a failure all by itself (e.g. if the component
|
||||
has length PATH_MAX or greater on systems that define PATH_MAX). */
|
||||
|
||||
int
|
||||
chdir_long (char *dir)
|
||||
{
|
||||
int e = chdir (dir);
|
||||
if (e == 0 || errno != ENAMETOOLONG)
|
||||
return e;
|
||||
|
||||
{
|
||||
size_t len = strlen (dir);
|
||||
char *dir_end = dir + len;
|
||||
struct cd_buf cdb;
|
||||
size_t n_leading_slash;
|
||||
|
||||
cdb_init (&cdb);
|
||||
|
||||
/* If DIR is the empty string, then the chdir above
|
||||
must have failed and set errno to ENOENT. */
|
||||
assure (0 < len);
|
||||
assure (PATH_MAX <= len);
|
||||
|
||||
/* Count leading slashes. */
|
||||
n_leading_slash = strspn (dir, "/");
|
||||
|
||||
/* Handle any leading slashes as well as any name that matches
|
||||
the regular expression, m!^//hostname[/]*! . Handling this
|
||||
prefix separately usually results in a single additional
|
||||
cdb_advance_fd call, but it's worthwhile, since it makes the
|
||||
code in the following loop cleaner. */
|
||||
if (n_leading_slash == 2)
|
||||
{
|
||||
int err;
|
||||
/* Find next slash.
|
||||
We already know that dir[2] is neither a slash nor '\0'. */
|
||||
char *slash = memchr (dir + 3, '/', dir_end - (dir + 3));
|
||||
if (slash == NULL)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
*slash = '\0';
|
||||
err = cdb_advance_fd (&cdb, dir);
|
||||
*slash = '/';
|
||||
if (err != 0)
|
||||
goto Fail;
|
||||
dir = find_non_slash (slash + 1);
|
||||
}
|
||||
else if (n_leading_slash)
|
||||
{
|
||||
if (cdb_advance_fd (&cdb, "/") != 0)
|
||||
goto Fail;
|
||||
dir += n_leading_slash;
|
||||
}
|
||||
|
||||
assure (*dir != '/');
|
||||
assure (dir <= dir_end);
|
||||
|
||||
while (PATH_MAX <= dir_end - dir)
|
||||
{
|
||||
int err;
|
||||
/* Find a slash that is PATH_MAX or fewer bytes away from dir.
|
||||
I.e. see if there is a slash that will give us a name of
|
||||
length PATH_MAX-1 or less. */
|
||||
char *slash = memrchr (dir, '/', PATH_MAX);
|
||||
if (slash == NULL)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*slash = '\0';
|
||||
assure (slash - dir < PATH_MAX);
|
||||
err = cdb_advance_fd (&cdb, dir);
|
||||
*slash = '/';
|
||||
if (err != 0)
|
||||
goto Fail;
|
||||
|
||||
dir = find_non_slash (slash + 1);
|
||||
}
|
||||
|
||||
if (dir < dir_end)
|
||||
{
|
||||
if (cdb_advance_fd (&cdb, dir) != 0)
|
||||
goto Fail;
|
||||
}
|
||||
|
||||
if (cdb_fchdir (&cdb) != 0)
|
||||
goto Fail;
|
||||
|
||||
cdb_free (&cdb);
|
||||
return 0;
|
||||
|
||||
Fail:
|
||||
{
|
||||
int saved_errno = errno;
|
||||
cdb_free (&cdb);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TEST_CHDIR
|
||||
|
||||
# include "closeout.h"
|
||||
# include "error.h"
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t n = 0;
|
||||
int len;
|
||||
|
||||
atexit (close_stdout);
|
||||
|
||||
len = getline (&line, &n, stdin);
|
||||
if (len < 0)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
if (feof (stdin))
|
||||
exit (0);
|
||||
|
||||
error (EXIT_FAILURE, saved_errno,
|
||||
"reading standard input");
|
||||
}
|
||||
else if (len == 0)
|
||||
exit (0);
|
||||
|
||||
if (line[len-1] == '\n')
|
||||
line[len-1] = '\0';
|
||||
|
||||
if (chdir_long (line) != 0)
|
||||
error (EXIT_FAILURE, errno,
|
||||
"chdir_long failed: %s", line);
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
/* Using 'pwd' here makes sense only if it is a robust implementation,
|
||||
like the one in coreutils after the 2004-04-19 changes. */
|
||||
char const *cmd = "pwd";
|
||||
execlp (cmd, (char *) NULL);
|
||||
error (EXIT_FAILURE, errno, "%s", cmd);
|
||||
}
|
||||
|
||||
fclose (stdin);
|
||||
fclose (stderr);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
compile-command: "gcc -DTEST_CHDIR=1 -g -O -W -Wall chdir-long.c libcoreutils.a"
|
||||
End:
|
||||
*/
|
30
lib/chdir-long.h
Normal file
30
lib/chdir-long.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* provide a chdir function that tries not to fail due to ENAMETOOLONG
|
||||
Copyright (C) 2004-2005, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering. */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pathmax.h"
|
||||
|
||||
/* On systems without PATH_MAX, presume that chdir accepts
|
||||
arbitrarily long directory names. */
|
||||
#ifndef PATH_MAX
|
||||
# define chdir_long(Dir) chdir (Dir)
|
||||
#else
|
||||
int chdir_long (char *dir);
|
||||
#endif
|
3
lib/chmodat.c
Normal file
3
lib/chmodat.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include <config.h>
|
||||
#define CHMODAT_INLINE _GL_EXTERN_INLINE
|
||||
#include "openat.h"
|
151
lib/chown.c
Normal file
151
lib/chown.c
Normal file
@ -0,0 +1,151 @@
|
||||
/* provide consistent interface to chown for systems that don't interpret
|
||||
an ID of -1 as meaning "don't change the corresponding ID".
|
||||
|
||||
Copyright (C) 1997, 2004-2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Jim Meyering */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if !HAVE_CHOWN
|
||||
|
||||
/* Simple stub that always fails with ENOSYS, for mingw. */
|
||||
int
|
||||
chown (const char *file _GL_UNUSED, uid_t uid _GL_UNUSED,
|
||||
gid_t gid _GL_UNUSED)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else /* HAVE_CHOWN */
|
||||
|
||||
/* Below we refer to the system's chown(). */
|
||||
# undef chown
|
||||
|
||||
/* Provide a more-closely POSIX-conforming version of chown on
|
||||
systems with one or both of the following problems:
|
||||
- chown doesn't treat an ID of -1 as meaning
|
||||
"don't change the corresponding ID".
|
||||
- chown doesn't dereference symlinks. */
|
||||
|
||||
int
|
||||
rpl_chown (const char *file, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat st;
|
||||
bool stat_valid = false;
|
||||
int result;
|
||||
|
||||
# if CHOWN_CHANGE_TIME_BUG
|
||||
if (gid != (gid_t) -1 || uid != (uid_t) -1)
|
||||
{
|
||||
if (stat (file, &st))
|
||||
return -1;
|
||||
stat_valid = true;
|
||||
}
|
||||
# endif
|
||||
|
||||
# if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
|
||||
if (gid == (gid_t) -1 || uid == (uid_t) -1)
|
||||
{
|
||||
/* Stat file to get id(s) that should remain unchanged. */
|
||||
if (!stat_valid && stat (file, &st))
|
||||
return -1;
|
||||
if (gid == (gid_t) -1)
|
||||
gid = st.st_gid;
|
||||
if (uid == (uid_t) -1)
|
||||
uid = st.st_uid;
|
||||
}
|
||||
# endif
|
||||
|
||||
# if CHOWN_MODIFIES_SYMLINK
|
||||
{
|
||||
/* Handle the case in which the system-supplied chown function
|
||||
does *not* follow symlinks. Instead, it changes permissions
|
||||
on the symlink itself. To work around that, we open the
|
||||
file (but this can fail due to lack of read or write permission) and
|
||||
use fchown on the resulting descriptor. */
|
||||
int open_flags = O_NONBLOCK | O_NOCTTY;
|
||||
int fd = open (file, O_RDONLY | open_flags);
|
||||
if (0 <= fd
|
||||
|| (errno == EACCES
|
||||
&& 0 <= (fd = open (file, O_WRONLY | open_flags))))
|
||||
{
|
||||
int saved_errno;
|
||||
bool fchown_socket_failure;
|
||||
|
||||
result = fchown (fd, uid, gid);
|
||||
saved_errno = errno;
|
||||
|
||||
/* POSIX says fchown can fail with errno == EINVAL on sockets
|
||||
and pipes, so fall back on chown in that case. */
|
||||
fchown_socket_failure =
|
||||
(result != 0 && saved_errno == EINVAL
|
||||
&& fstat (fd, &st) == 0
|
||||
&& (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode)));
|
||||
|
||||
close (fd);
|
||||
|
||||
if (! fchown_socket_failure)
|
||||
{
|
||||
errno = saved_errno;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (errno != EACCES)
|
||||
return -1;
|
||||
}
|
||||
# endif
|
||||
|
||||
# if CHOWN_TRAILING_SLASH_BUG
|
||||
if (!stat_valid)
|
||||
{
|
||||
size_t len = strlen (file);
|
||||
if (len && file[len - 1] == '/' && stat (file, &st))
|
||||
return -1;
|
||||
}
|
||||
# endif
|
||||
|
||||
result = chown (file, uid, gid);
|
||||
|
||||
# if CHOWN_CHANGE_TIME_BUG
|
||||
if (result == 0 && stat_valid
|
||||
&& (uid == st.st_uid || uid == (uid_t) -1)
|
||||
&& (gid == st.st_gid || gid == (gid_t) -1))
|
||||
{
|
||||
/* No change in ownership, but at least one argument was not -1,
|
||||
so we are required to update ctime. Since chown succeeded,
|
||||
we assume that chmod will do likewise. Fortunately, on all
|
||||
known systems where a 'no-op' chown skips the ctime update, a
|
||||
'no-op' chmod still does the trick. */
|
||||
result = chmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO
|
||||
| S_ISUID | S_ISGID | S_ISVTX));
|
||||
}
|
||||
# endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* HAVE_CHOWN */
|
3
lib/chownat.c
Normal file
3
lib/chownat.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include <config.h>
|
||||
#define CHOWNAT_INLINE _GL_EXTERN_INLINE
|
||||
#include "openat.h"
|
76
lib/cl-strtod.c
Normal file
76
lib/cl-strtod.c
Normal file
@ -0,0 +1,76 @@
|
||||
/* Convert string to double in the current locale, falling back on the C locale.
|
||||
|
||||
Copyright 2019-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "cl-strtod.h"
|
||||
|
||||
#include <c-strtod.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if LONG
|
||||
# define CL_STRTOD cl_strtold
|
||||
# define DOUBLE long double
|
||||
# define C_STRTOD c_strtold
|
||||
# define STRTOD strtold
|
||||
#else
|
||||
# define CL_STRTOD cl_strtod
|
||||
# define DOUBLE double
|
||||
# define C_STRTOD c_strtod
|
||||
# define STRTOD strtod
|
||||
#endif
|
||||
|
||||
/* This function acts like strtod or strtold, except that it falls
|
||||
back on the C locale if the initial prefix is not parsable in
|
||||
the current locale. If the prefix is parsable in both locales,
|
||||
it uses the longer parse, breaking ties in favor of the current locale.
|
||||
|
||||
Parse the initial prefix of NPTR as a floating-point number in the
|
||||
current locale or in the C locale (preferring the locale that
|
||||
yields the longer parse, or the current locale if there is a tie).
|
||||
If ENDPTR is not NULL, set *ENDPTR to the first unused byte, or to
|
||||
NPTR if the prefix cannot be parsed.
|
||||
|
||||
If successful, return a number without changing errno.
|
||||
If the prefix cannot be parsed, return 0 and possibly set errno to EINVAL.
|
||||
If the number overflows, return an extreme value and set errno to ERANGE.
|
||||
If the number underflows, return a value close to 0 and set errno to ERANGE.
|
||||
If there is some other error, return 0 and set errno. */
|
||||
|
||||
DOUBLE
|
||||
CL_STRTOD (char const *nptr, char **restrict endptr)
|
||||
{
|
||||
char *end;
|
||||
DOUBLE d = STRTOD (nptr, &end);
|
||||
if (*end)
|
||||
{
|
||||
int strtod_errno = errno;
|
||||
char *c_end;
|
||||
DOUBLE c = C_STRTOD (nptr, &c_end);
|
||||
if (end < c_end)
|
||||
d = c, end = c_end;
|
||||
else
|
||||
errno = strtod_errno;
|
||||
}
|
||||
if (endptr)
|
||||
*endptr = end;
|
||||
return d;
|
||||
}
|
2
lib/cl-strtod.h
Normal file
2
lib/cl-strtod.h
Normal file
@ -0,0 +1,2 @@
|
||||
double cl_strtod (char const *, char **restrict);
|
||||
long double cl_strtold (char const *, char **restrict);
|
2
lib/cl-strtold.c
Normal file
2
lib/cl-strtold.c
Normal file
@ -0,0 +1,2 @@
|
||||
#define LONG 1
|
||||
#include "cl-strtod.c"
|
83
lib/cloexec.c
Normal file
83
lib/cloexec.c
Normal file
@ -0,0 +1,83 @@
|
||||
/* cloexec.c - set or clear the close-on-exec descriptor flag
|
||||
|
||||
Copyright (C) 1991, 2004-2006, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
The code is taken from glibc/manual/llio.texi */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "cloexec.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Set the 'FD_CLOEXEC' flag of DESC if VALUE is true,
|
||||
or clear the flag if VALUE is false.
|
||||
Return 0 on success, or -1 on error with 'errno' set.
|
||||
|
||||
Note that on MingW, this function does NOT protect DESC from being
|
||||
inherited into spawned children. Instead, either use dup_cloexec
|
||||
followed by closing the original DESC, or use interfaces such as
|
||||
open or pipe2 that accept flags like O_CLOEXEC to create DESC
|
||||
non-inheritable in the first place. */
|
||||
|
||||
int
|
||||
set_cloexec_flag (int desc, bool value)
|
||||
{
|
||||
#ifdef F_SETFD
|
||||
|
||||
int flags = fcntl (desc, F_GETFD, 0);
|
||||
|
||||
if (0 <= flags)
|
||||
{
|
||||
int newflags = (value ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC);
|
||||
|
||||
if (flags == newflags
|
||||
|| fcntl (desc, F_SETFD, newflags) != -1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
#else /* !F_SETFD */
|
||||
|
||||
/* Use dup2 to reject invalid file descriptors; the cloexec flag
|
||||
will be unaffected. */
|
||||
if (desc < 0)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (dup2 (desc, desc) < 0)
|
||||
/* errno is EBADF here. */
|
||||
return -1;
|
||||
|
||||
/* There is nothing we can do on this kind of platform. Punt. */
|
||||
return 0;
|
||||
#endif /* !F_SETFD */
|
||||
}
|
||||
|
||||
|
||||
/* Duplicates a file handle FD, while marking the copy to be closed
|
||||
prior to exec or spawn. Returns -1 and sets errno if FD could not
|
||||
be duplicated. */
|
||||
|
||||
int
|
||||
dup_cloexec (int fd)
|
||||
{
|
||||
return fcntl (fd, F_DUPFD_CLOEXEC, 0);
|
||||
}
|
38
lib/cloexec.h
Normal file
38
lib/cloexec.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* cloexec.c - set or clear the close-on-exec descriptor flag
|
||||
|
||||
Copyright (C) 2004, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Set the 'FD_CLOEXEC' flag of DESC if VALUE is true,
|
||||
or clear the flag if VALUE is false.
|
||||
Return 0 on success, or -1 on error with 'errno' set.
|
||||
|
||||
Note that on MingW, this function does NOT protect DESC from being
|
||||
inherited into spawned children. Instead, either use dup_cloexec
|
||||
followed by closing the original DESC, or use interfaces such as
|
||||
open or pipe2 that accept flags like O_CLOEXEC to create DESC
|
||||
non-inheritable in the first place. */
|
||||
|
||||
int set_cloexec_flag (int desc, bool value);
|
||||
|
||||
/* Duplicates a file handle FD, while marking the copy to be closed
|
||||
prior to exec or spawn. Returns -1 and sets errno if FD could not
|
||||
be duplicated. */
|
||||
|
||||
int dup_cloexec (int fd);
|
78
lib/close-stream.c
Normal file
78
lib/close-stream.c
Normal file
@ -0,0 +1,78 @@
|
||||
/* Close a stream, with nicer error checking than fclose's.
|
||||
|
||||
Copyright (C) 1998-2002, 2004, 2006-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "close-stream.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "fpending.h"
|
||||
|
||||
#if USE_UNLOCKED_IO
|
||||
# include "unlocked-io.h"
|
||||
#endif
|
||||
|
||||
/* Close STREAM. Return 0 if successful, EOF (setting errno)
|
||||
otherwise. A failure might set errno to 0 if the error number
|
||||
cannot be determined.
|
||||
|
||||
A failure with errno set to EPIPE may or may not indicate an error
|
||||
situation worth signaling to the user. See the documentation of the
|
||||
close_stdout_set_ignore_EPIPE function for details.
|
||||
|
||||
If a program writes *anything* to STREAM, that program should close
|
||||
STREAM and make sure that it succeeds before exiting. Otherwise,
|
||||
suppose that you go to the extreme of checking the return status
|
||||
of every function that does an explicit write to STREAM. The last
|
||||
printf can succeed in writing to the internal stream buffer, and yet
|
||||
the fclose(STREAM) could still fail (due e.g., to a disk full error)
|
||||
when it tries to write out that buffered data. Thus, you would be
|
||||
left with an incomplete output file and the offending program would
|
||||
exit successfully. Even calling fflush is not always sufficient,
|
||||
since some file systems (NFS and CODA) buffer written/flushed data
|
||||
until an actual close call.
|
||||
|
||||
Besides, it's wasteful to check the return value from every call
|
||||
that writes to STREAM -- just let the internal stream state record
|
||||
the failure. That's what the ferror test is checking below. */
|
||||
|
||||
int
|
||||
close_stream (FILE *stream)
|
||||
{
|
||||
const bool some_pending = (__fpending (stream) != 0);
|
||||
const bool prev_fail = (ferror (stream) != 0);
|
||||
const bool fclose_fail = (fclose (stream) != 0);
|
||||
|
||||
/* Return an error indication if there was a previous failure or if
|
||||
fclose failed, with one exception: ignore an fclose failure if
|
||||
there was no previous error, no data remains to be flushed, and
|
||||
fclose failed with EBADF. That can happen when a program like cp
|
||||
is invoked like this 'cp a b >&-' (i.e., with standard output
|
||||
closed) and doesn't generate any output (hence no previous error
|
||||
and nothing to be flushed). */
|
||||
|
||||
if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
|
||||
{
|
||||
if (! fclose_fail)
|
||||
errno = 0;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
2
lib/close-stream.h
Normal file
2
lib/close-stream.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include <stdio.h>
|
||||
int close_stream (FILE *stream);
|
71
lib/close.c
Normal file
71
lib/close.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* close replacement.
|
||||
Copyright (C) 2008-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "fd-hook.h"
|
||||
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
|
||||
# include "msvc-inval.h"
|
||||
#endif
|
||||
|
||||
#undef close
|
||||
|
||||
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
|
||||
static int
|
||||
close_nothrow (int fd)
|
||||
{
|
||||
int result;
|
||||
|
||||
TRY_MSVC_INVAL
|
||||
{
|
||||
result = close (fd);
|
||||
}
|
||||
CATCH_MSVC_INVAL
|
||||
{
|
||||
result = -1;
|
||||
errno = EBADF;
|
||||
}
|
||||
DONE_MSVC_INVAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
# define close_nothrow close
|
||||
#endif
|
||||
|
||||
/* Override close() to call into other gnulib modules. */
|
||||
|
||||
int
|
||||
rpl_close (int fd)
|
||||
{
|
||||
#if WINDOWS_SOCKETS
|
||||
int retval = execute_all_close_hooks (close_nothrow, fd);
|
||||
#else
|
||||
int retval = close_nothrow (fd);
|
||||
#endif
|
||||
|
||||
#if REPLACE_FCHDIR
|
||||
if (retval >= 0)
|
||||
_gl_unregister_fd (fd);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
71
lib/closedir.c
Normal file
71
lib/closedir.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* Stop reading the entries of a directory.
|
||||
Copyright (C) 2006-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include <dirent.h>
|
||||
|
||||
#if REPLACE_FCHDIR
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_CLOSEDIR
|
||||
|
||||
/* Override closedir(), to keep track of the open file descriptors.
|
||||
Needed because there is a function dirfd(). */
|
||||
|
||||
#else
|
||||
|
||||
# include <stdlib.h>
|
||||
|
||||
# include "dirent-private.h"
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
closedir (DIR *dirp)
|
||||
{
|
||||
# if REPLACE_FCHDIR || REPLACE_DIRFD
|
||||
int fd = dirfd (dirp);
|
||||
# endif
|
||||
int retval;
|
||||
|
||||
#if HAVE_CLOSEDIR
|
||||
# undef closedir
|
||||
|
||||
retval = closedir (dirp);
|
||||
|
||||
# ifdef __KLIBC__
|
||||
if (!retval)
|
||||
_gl_unregister_dirp_fd (fd);
|
||||
# endif
|
||||
#else
|
||||
|
||||
if (dirp->current != INVALID_HANDLE_VALUE)
|
||||
FindClose (dirp->current);
|
||||
free (dirp);
|
||||
|
||||
retval = 0;
|
||||
|
||||
#endif
|
||||
|
||||
#if REPLACE_FCHDIR
|
||||
if (retval >= 0)
|
||||
_gl_unregister_fd (fd);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
112
lib/closein.c
Normal file
112
lib/closein.c
Normal file
@ -0,0 +1,112 @@
|
||||
/* Close standard input, rewinding seekable stdin if necessary.
|
||||
|
||||
Copyright (C) 2007, 2009-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "closein.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "gettext.h"
|
||||
#define _(msgid) gettext (msgid)
|
||||
|
||||
#include "close-stream.h"
|
||||
#include "closeout.h"
|
||||
#include "error.h"
|
||||
#include "exitfail.h"
|
||||
#include "freadahead.h"
|
||||
#include "quotearg.h"
|
||||
|
||||
static const char *file_name;
|
||||
|
||||
/* Set the file name to be reported in the event an error is detected
|
||||
on stdin by close_stdin. See also close_stdout_set_file_name, if
|
||||
an error is detected when closing stdout. */
|
||||
void
|
||||
close_stdin_set_file_name (const char *file)
|
||||
{
|
||||
file_name = file;
|
||||
}
|
||||
|
||||
/* Close standard input, rewinding any unused input if stdin is
|
||||
seekable. On error, issue a diagnostic and _exit with status
|
||||
'exit_failure'. Then call close_stdout.
|
||||
|
||||
Most programs can get by with close_stdout. close_stdin is only
|
||||
needed when a program wants to guarantee that partially read input
|
||||
from seekable stdin is not consumed, for any subsequent clients.
|
||||
For example, POSIX requires that these two commands behave alike:
|
||||
|
||||
(sed -ne 1q; cat) < file
|
||||
tail -n +2 file
|
||||
|
||||
Since close_stdin is commonly registered via 'atexit', POSIX
|
||||
and the C standard both say that it should not call 'exit',
|
||||
because the behavior is undefined if 'exit' is called more than
|
||||
once. So it calls '_exit' instead of 'exit'. If close_stdin
|
||||
is registered via atexit before other functions are registered,
|
||||
the other functions can act before this _exit is invoked.
|
||||
|
||||
Applications that use close_stdout should flush any streams other
|
||||
than stdin, stdout, and stderr before exiting, since the call to
|
||||
_exit will bypass other buffer flushing. Applications should be
|
||||
flushing and closing other streams anyway, to check for I/O errors.
|
||||
Also, applications should not use tmpfile, since _exit can bypass
|
||||
the removal of these files.
|
||||
|
||||
It's important to detect such failures and exit nonzero because many
|
||||
tools (most notably 'make' and other build-management systems) depend
|
||||
on being able to detect failure in other tools via their exit status. */
|
||||
|
||||
void
|
||||
close_stdin (void)
|
||||
{
|
||||
bool fail = false;
|
||||
|
||||
/* There is no need to flush stdin if we can determine quickly that stdin's
|
||||
input buffer is empty; in this case we know that if stdin is seekable,
|
||||
(fseeko (stdin, 0, SEEK_CUR), ftello (stdin))
|
||||
== lseek (0, 0, SEEK_CUR). */
|
||||
if (freadahead (stdin) > 0)
|
||||
{
|
||||
/* Only attempt flush if stdin is seekable, as fflush is entitled to
|
||||
fail on non-seekable streams. */
|
||||
if (fseeko (stdin, 0, SEEK_CUR) == 0 && fflush (stdin) != 0)
|
||||
fail = true;
|
||||
}
|
||||
if (close_stream (stdin) != 0)
|
||||
fail = true;
|
||||
if (fail)
|
||||
{
|
||||
/* Report failure, but defer exit until after closing stdout,
|
||||
since the failure report should still be flushed. */
|
||||
char const *close_error = _("error closing file");
|
||||
if (file_name)
|
||||
error (0, errno, "%s: %s", quotearg_colon (file_name),
|
||||
close_error);
|
||||
else
|
||||
error (0, errno, "%s", close_error);
|
||||
}
|
||||
|
||||
close_stdout ();
|
||||
|
||||
if (fail)
|
||||
_exit (exit_failure);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user