From 0f54df53d26e4c293871fb30bce88511e1d61d6c Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 6 Apr 2021 11:30:42 +0100 Subject: [PATCH] build: Remove gnulib. As part of our efforts to clean up and simplify libguestfs, removing gnulib deletes a large dependency that we mostly no longer use and causes problems for new users trying to build the library from source. A few modules from gnulib are still used (under a compatible license) and these are copied into gnulib/lib/ --- .gitignore | 3 - .gitmodules | 3 - .gnulib | 1 - Makefile.am | 15 +- autogen.sh | 51 -- bootstrap | 174 ------ cfg.mk | 141 ----- configure.ac | 6 - daemon/command.c | 5 +- daemon/guestfsd.c | 1 - daemon/link.c | 34 +- docs/guestfs-building.pod | 12 +- docs/guestfs-faq.pod | 4 +- docs/guestfs-hacking.pod | 15 +- fish/fish.c | 5 +- fuse/guestmount.c | 1 + fuse/guestunmount.c | 1 + gnulib/lib/Makefile.am | 57 ++ gnulib/lib/base64.c | 612 ++++++++++++++++++ gnulib/lib/base64.h | 75 +++ gnulib/lib/bitrotate.h | 135 ++++ gnulib/lib/c-ctype.h | 363 +++++++++++ gnulib/lib/cloexec.c | 88 +++ gnulib/lib/cloexec.h | 43 ++ gnulib/lib/full-read.c | 25 + gnulib/lib/full-read.h | 30 + gnulib/lib/full-write.c | 86 +++ gnulib/lib/full-write.h | 41 ++ gnulib/lib/getprogname.h | 30 + gnulib/lib/hash-pjw.c | 40 ++ gnulib/lib/hash-pjw.h | 23 + gnulib/lib/hash.c | 1113 +++++++++++++++++++++++++++++++++ gnulib/lib/hash.h | 256 ++++++++ gnulib/lib/ignore-value.h | 58 ++ gnulib/lib/nonblocking.c | 170 +++++ gnulib/lib/nonblocking.h | 69 ++ gnulib/lib/safe-read.c | 79 +++ gnulib/lib/safe-read.h | 54 ++ gnulib/lib/safe-write.c | 25 + gnulib/lib/safe-write.h | 44 ++ gnulib/lib/xalloc-oversized.h | 67 ++ gnulib/lib/xstrtol.c | 236 +++++++ gnulib/lib/xstrtol.h | 50 ++ gnulib/lib/xstrtoll.c | 10 + gnulib/lib/xstrtoul.c | 6 + gnulib/lib/xstrtoull.c | 6 + gnulib/lib/xstrtoumax.c | 6 + m4/.gitignore | 251 -------- m4/guestfs-c.m4 | 131 +--- rescue/rescue.c | 11 +- 50 files changed, 3964 insertions(+), 798 deletions(-) delete mode 160000 .gnulib delete mode 100755 autogen.sh delete mode 100755 bootstrap delete mode 100644 cfg.mk create mode 100644 gnulib/lib/Makefile.am create mode 100644 gnulib/lib/base64.c create mode 100644 gnulib/lib/base64.h create mode 100755 gnulib/lib/bitrotate.h create mode 100755 gnulib/lib/c-ctype.h create mode 100644 gnulib/lib/cloexec.c create mode 100644 gnulib/lib/cloexec.h create mode 100644 gnulib/lib/full-read.c create mode 100644 gnulib/lib/full-read.h create mode 100644 gnulib/lib/full-write.c create mode 100644 gnulib/lib/full-write.h create mode 100644 gnulib/lib/getprogname.h create mode 100644 gnulib/lib/hash-pjw.c create mode 100644 gnulib/lib/hash-pjw.h create mode 100755 gnulib/lib/hash.c create mode 100755 gnulib/lib/hash.h create mode 100755 gnulib/lib/ignore-value.h create mode 100644 gnulib/lib/nonblocking.c create mode 100644 gnulib/lib/nonblocking.h create mode 100644 gnulib/lib/safe-read.c create mode 100644 gnulib/lib/safe-read.h create mode 100644 gnulib/lib/safe-write.c create mode 100644 gnulib/lib/safe-write.h create mode 100755 gnulib/lib/xalloc-oversized.h create mode 100644 gnulib/lib/xstrtol.c create mode 100644 gnulib/lib/xstrtol.h create mode 100644 gnulib/lib/xstrtoll.c create mode 100644 gnulib/lib/xstrtoul.c create mode 100644 gnulib/lib/xstrtoull.c create mode 100644 gnulib/lib/xstrtoumax.c delete mode 100644 m4/.gitignore diff --git a/.gitignore b/.gitignore index de4abff58..c1149cb5b 100644 --- a/.gitignore +++ b/.gitignore @@ -205,8 +205,6 @@ Makefile.in /generator/stamp-generator /.gitattributes /.git-module-status -/gnulib -/GNUmakefile /gobject/bindtests.js /gobject/Guestfs-1.0.gir /gobject/Guestfs-1.0.typelib @@ -266,7 +264,6 @@ Makefile.in /lua/guestfs.so /lua/lua-guestfs.c /m4/ChangeLog -/m4/gnulib-cache.m4 /m4/intmax.m4 /m4/libtool.m4 /m4/lt~obsolete.m4 diff --git a/.gitmodules b/.gitmodules index fb96af879..134314212 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "gnulib"] - path = .gnulib - url = https://git.savannah.gnu.org/git/gnulib.git [submodule "common"] path = common url = https://github.com/libguestfs/libguestfs-common diff --git a/.gnulib b/.gnulib deleted file mode 160000 index 25a7d014d..000000000 --- a/.gnulib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 25a7d014d697f7c1b7af2eb6f4b144c7aa3baf13 diff --git a/Makefile.am b/Makefile.am index 743d2fbad..11100b407 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,13 +26,8 @@ SUBDIRS = common/mlstdutils generator # tests run. SUBDIRS += test-data -# Gnulib - must be built and tested before the library. -SUBDIRS += gnulib/lib -if ENABLE_GNULIB_TESTS -SUBDIRS += gnulib/tests -endif - # Basic source for the library. +SUBDIRS += gnulib/lib SUBDIRS += common/errnostring common/protocol common/qemuopts SUBDIRS += common/utils SUBDIRS += common/structs @@ -126,12 +121,9 @@ EXTRA_DIST = \ .gitignore \ .lvimrc \ .mailmap \ - bootstrap \ bugs-in-changelog.sh \ - autogen.sh \ bindtests \ build-aux/config.rpath \ - cfg.mk \ check-mli.sh \ common/.gitignore \ common/README \ @@ -234,7 +226,7 @@ BUGS: configure.ac docs/C_SOURCE_FILES: configure.ac rm -f $@ $@-t find $(DIST_SUBDIRS) -name '*.[ch]' | \ - grep -v -E '^(builder/index-parse\.|builder/index-scan\.|examples/|gnulib/|gobject/|java/com_redhat_et_libguestfs|perl/|php/extension/config\.h|ruby/ext/guestfs/extconf\.h|tests/|test-data/|bundled/)' | \ + grep -v -E '^(builder/index-parse\.|builder/index-scan\.|examples/|gobject/|java/com_redhat_et_libguestfs|perl/|php/extension/config\.h|ruby/ext/guestfs/extconf\.h|tests/|test-data/|bundled/)' | \ grep -v -E '/(guestfs|rc)_protocol\.' | \ grep -v -E '.*/errnostring\.' | \ grep -v -E '.*-gperf\.' | \ @@ -247,7 +239,7 @@ po/POTFILES: configure.ac rm -f $@ $@-t cd $(srcdir); \ find $(DIST_SUBDIRS) -name '*.c' | \ - grep -v -E '^(examples|gnulib|perl/(blib|examples)|po-docs|tests|test-data|bundled)/' | \ + grep -v -E '^(examples|perl/(blib|examples)|po-docs|tests|test-data|bundled)/' | \ grep -v -E '/((guestfs|rc)_protocol\.c|dummy\.c)$$' | \ grep -v -E '^python/utils\.c$$' | \ grep -v -E '^perl/lib/Sys/Guestfs\.c$$' | \ @@ -490,7 +482,6 @@ maintainer-check-extra-dist: grep -v '^common/mlxml/' | \ grep -v '^intltool-.*\.in' | \ grep -v '^\.gitmodules' | \ - grep -v '^\.gnulib' | \ sort > tmp/gitfiles comm -13 tmp/tarfiles tmp/gitfiles > tmp/comm-out @echo Checking for differences between EXTRA_DIST and git ... diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 6f0cbbcfe..000000000 --- a/autogen.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# libguestfs -# Copyright (C) 2009 Red Hat Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any 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. -# -# Rebuild the autotools environment. - -set -e -set -v - -# Ensure that whenever we pull in a gnulib update or otherwise change to a -# different version (i.e., when switching branches), we also rerun ./bootstrap. -curr_status=.git-module-status -t=$(git submodule status | grep gnulib | sed 's/^[ +-]//;s/ .*//') -if test "$t" = "$(cat $curr_status 2>/dev/null)"; then - : # good, it's up to date -else - echo running bootstrap... - ./bootstrap && echo "$t" > $curr_status -fi - -CONFIGUREDIR=. - -# Run configure in BUILDDIR if it's set -if [ ! -z "$BUILDDIR" ]; then - mkdir -p $BUILDDIR - cd $BUILDDIR - - CONFIGUREDIR=.. -fi - -# If no arguments were specified and configure has run before, use the previous -# arguments -if test $# -eq 0 && test -x ./config.status; then - ./config.status --recheck -else - $CONFIGUREDIR/configure "$@" -fi diff --git a/bootstrap b/bootstrap deleted file mode 100755 index 9e5a8b72d..000000000 --- a/bootstrap +++ /dev/null @@ -1,174 +0,0 @@ -#!/bin/sh - -usage() { - echo >&2 "\ -Usage: $0 [OPTION]... -Bootstrap this package from the checked-out sources. - -Options: - --gnulib-srcdir=DIRNAME specify the local directory where gnulib - sources reside. Use this if you already - have gnulib sources on your machine, and - do not want to waste your bandwidth downloading - them again. Defaults to \$GNULIB_SRCDIR -" -} - -for option -do - case $option in - --help) - usage - exit;; - --gnulib-srcdir=*) - GNULIB_SRCDIR=${option#--gnulib-srcdir=};; - *) - echo >&2 "$0: $option: unknown option" - exit 1;; - esac -done - -cleanup_gnulib() { - status=$? - rm -fr "$gnulib_path" - exit $status -} - -git_modules_config () { - test -f .gitmodules && git config --file .gitmodules "$@" -} - -gnulib_path=$(git_modules_config submodule.gnulib.path) -test -z "$gnulib_path" && gnulib_path=gnulib - -# Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a -# submodule, for use in the rest of the script. - -case ${GNULIB_SRCDIR--} in --) - if git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - git submodule init -- "$gnulib_path" || exit $? - git submodule update -- "$gnulib_path" || exit $? - - elif [ ! -d "$gnulib_path" ]; then - echo "$0: getting gnulib files..." - - trap cleanup_gnulib 1 2 13 15 - - shallow= - git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' - git clone $shallow git://git.sv.gnu.org/gnulib "$gnulib_path" || - cleanup_gnulib - - trap - 1 2 13 15 - fi - GNULIB_SRCDIR=$gnulib_path - ;; -*) - # Use GNULIB_SRCDIR directly or as a reference. - if test -d "$GNULIB_SRCDIR"/.git && \ - git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - if git submodule -h|grep -- --reference > /dev/null; then - # Prefer the one-liner available in git 1.6.4 or newer. - git submodule update --init --reference "$GNULIB_SRCDIR" \ - "$gnulib_path" || exit $? - else - # This fallback allows at least git 1.5.5. - if test -f "$gnulib_path"/gnulib-tool; then - # Since file already exists, assume submodule init already complete. - git submodule update -- "$gnulib_path" || exit $? - else - # Older git can't clone into an empty directory. - rmdir "$gnulib_path" 2>/dev/null - git clone --reference "$GNULIB_SRCDIR" \ - "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ - && git submodule init -- "$gnulib_path" \ - && git submodule update -- "$gnulib_path" \ - || exit $? - fi - fi - GNULIB_SRCDIR=$gnulib_path - fi - ;; -esac - -# Autoreconf runs aclocal before libtoolize, which causes spurious -# warnings if the initial aclocal is confused by the libtoolized -# (or worse out-of-date) macro directory. -libtoolize --copy --install - -gnulib_tool=$GNULIB_SRCDIR/gnulib-tool -<$gnulib_tool || exit - -modules=' -accept4 -areadlinkat -base64 -byteswap -c-ctype -cloexec -closeout -connect -error -fstatat -full-read -full-write -futimens -getline -getprogname -gitlog-to-changelog -glob -gnumakefile -hash -hash-pjw -human -ignore-value -intprops -lock -maintainer-makefile -manywarnings -memmem -mkdtemp -mkstemps -netdb -nonblocking -perror -pipe2 -pread -readlink -select -setenv -sleep -socket -strchrnul -strerror -strndup -sys_select -sys_types -sys_wait -tls -vasprintf -vc-list-files -warnings -xstrtol -xstrtoll -xvasprintf -' - -# If any tests fail, avoid including them by adding them to -# this list. -avoid="--avoid=dummy --avoid=getlogin_r-tests" - -$gnulib_tool \ - $avoid \ - --with-tests \ - --m4-base=m4 \ - --source-base=gnulib/lib \ - --tests-base=gnulib/tests \ - --libtool \ - --import $modules - -# Disable autopoint and libtoolize, since they were already done above. -AUTOPOINT=true LIBTOOLIZE=true autoreconf --verbose --install diff --git a/cfg.mk b/cfg.mk deleted file mode 100644 index a303ee728..000000000 --- a/cfg.mk +++ /dev/null @@ -1,141 +0,0 @@ -# Customize Makefile.maint. -*- makefile -*- -# Copyright (C) 2003-2012 Free Software Foundation, Inc. - -# This program is free software: you can 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 . - -# Use alpha.gnu.org for alpha and beta releases. -# Use ftp.gnu.org for major releases. -gnu_ftp_host-alpha = alpha.gnu.org -gnu_ftp_host-beta = alpha.gnu.org -gnu_ftp_host-major = ftp.gnu.org -gnu_rel_host = $(gnu_ftp_host-$(RELEASE_TYPE)) - -url_dir_list = \ - ftp://$(gnu_rel_host)/gnu/coreutils - -# Exclude some filenames. -exclude_file_name_regexp--sc_bindtextdomain = ^(daemon|erlang|examples|tests)/|/test- -exclude_file_name_regexp--sc_error_message_period = ^(generator|php|po-docs)/ -exclude_file_name_regexp--sc_prohibit_always-defined_macros = ^examples/|^tests/xml/fake-libvirt-xml\.c|^daemon/guestfsd\.c -exclude_file_name_regexp--sc_prohibit_doubled_word = ^po/ -exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = ^test-data/|\.patch$|\.png$ -exclude_file_name_regexp--sc_prohibit_magic_number_exit = ^(po|po-docs)/|\.pod$|^fuse/guestunmount\.c -exclude_file_name_regexp--sc_prohibit_strcmp = ^(examples|po-docs)/|\.pod$ -exclude_file_name_regexp--sc_prohibit_strcmp_and_strncmp = ^(examples|po-docs)/|\.pod$ -exclude_file_name_regexp--sc_prohibit_strncpy = ^lib/launch-.*\.c$ -exclude_file_name_regexp--sc_require_config_h = ^examples/|^tests/c-api/test-just-header\.c$ -exclude_file_name_regexp--sc_require_config_h_first = ^examples/|^tests/c-api/test-just-header\.c$|^python/guestfs-py-byhand\.c$ - -# Tests not to run as part of "make distcheck". -local-checks-to-skip = \ - sc_po_check \ - sc_GPL_version \ - sc_error_exit_success \ - sc_file_system \ - sc_makefile_path_separator_check \ - sc_obsolete_symbols \ - sc_prohibit_atoi_atof \ - sc_prohibit_quote_without_use \ - sc_prohibit_quotearg_without_use \ - sc_prohibit_stat_st_blocks \ - sc_space_tab \ - sc_two_space_separator_in_usage \ - sc_error_message_uppercase \ - sc_program_name \ - $(disable_temporarily) \ - sc_useless_cpp_parens \ - sc_cast_of_argument_to_free - -disable_temporarily = \ - sc_makefile_check \ - sc_unmarked_diagnostics \ - sc_prohibit_ctype_h \ - sc_prohibit_asprintf \ - sc_avoid_write - -# Avoid uses of write(2). Either switch to streams (fwrite), or use -# the safewrite wrapper. -sc_avoid_write: - @if $(VC_LIST_EXCEPT) | grep '\.c$$' > /dev/null; then \ - grep '\&2; exit 1; } || :; \ - else :; \ - fi - -# Use STREQ rather than comparing strcmp == 0, or != 0. -# Similarly, use STREQLEN or STRPREFIX rather than strncmp. -sc_prohibit_strcmp_and_strncmp: - @grep -nE '! *strn?cmp *\(|\&2; exit 1; } || : - -# Use virAsprintf rather than a'sprintf since *strp is undefined on error. -sc_prohibit_asprintf: - @re='\<[a]sprintf\>' \ - msg='use virAsprintf, not a'sprintf \ - $(_prohibit_regexp) - -# Prohibit the inclusion of . -sc_prohibit_ctype_h: - @grep -E '^# *include *' $$($(VC_LIST_EXCEPT)) && \ - { echo "$(ME): don't use ctype.h; instead, use c-ctype.h" \ - 1>&2; exit 1; } || : - -ctype_re = isalnum|isalpha|isascii|isblank|iscntrl|isdigit|isgraph|islower\ -|isprint|ispunct|isspace|isupper|isxdigit|tolower|toupper - -sc_avoid_ctype_macros: - @grep -E '\b($(ctype_re)) *\(' /dev/null \ - $$($(VC_LIST_EXCEPT)) && \ - { echo "$(ME): don't use ctype macros (use c-ctype.h)" \ - 1>&2; exit 1; } || : - -sc_prohibit_virBufferAdd_with_string_literal: - @prohibit='\ #include +#include #include #include #include +#include +#include #include #include #include -#include -#include #include "ignore-value.h" diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index e5293d08c..a1efee20d 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -49,7 +49,6 @@ #include "c-ctype.h" #include "ignore-value.h" -#include "sockets.h" #include "daemon.h" diff --git a/daemon/link.c b/daemon/link.c index 53f4719ed..acc46891a 100644 --- a/daemon/link.c +++ b/daemon/link.c @@ -24,8 +24,8 @@ #include #include #include - -#include "areadlink.h" +#include +#include #include "daemon.h" #include "actions.h" @@ -47,20 +47,36 @@ do_internal_readlinklist (const char *path, char *const *names) } for (i = 0; names[i] != NULL; ++i) { + CLEANUP_FREE char *link = NULL; + struct stat statbuf; + size_t n; + ssize_t r; + /* Because of the way this function is intended to be used, * we actually expect to see errors here, and they are not fatal. */ - CLEANUP_FREE char *link = areadlinkat (fd_cwd, names[i]); - if (link != NULL) { - if (add_string (&ret, link) == -1) { - add_string_failed: + if (fstatat (fd_cwd, names[i], &statbuf, AT_SYMLINK_NOFOLLOW) == -1) { + add_empty_string: + if (add_string (&ret, "") == -1) { close (fd_cwd); return NULL; } - } else { - if (add_string (&ret, "") == -1) - goto add_string_failed; + } + if (!S_ISLNK (statbuf.st_mode)) + goto add_empty_string; + n = statbuf.st_size; + link = malloc (n+1); + if (link == NULL) + goto add_empty_string; + r = readlinkat (fd_cwd, names[i], link, n); + if (r == -1 || r != n) + goto add_empty_string; + link[n] = '\0'; + + if (add_string (&ret, link) == -1) { + close (fd_cwd); + return NULL; } } diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index 4d0e943cb..1f872700e 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -402,7 +402,8 @@ git. git clone https://github.com/libguestfs/libguestfs cd libguestfs git submodule update --init - CFLAGS=-fPIC ./autogen.sh + autoreconf -i + ./configure CFLAGS=-fPIC make =head1 BUILDING FROM TARBALLS @@ -497,11 +498,11 @@ are ignored by git. These files can contain local configuration or scripts that you need to build libguestfs. I have a file called F which is a simple wrapper -around F containing local configure customizations that I +around F containing local configure customizations that I need. It looks like this: . localenv - ./autogen.sh \ + ./configure.sh \ -C \ --enable-werror \ "$@" @@ -570,11 +571,6 @@ OCaml tools. Disable FUSE support in the API and the L tool. -=item B<--disable-gnulib-tests> - -On some platforms the GNUlib test suite can be flaky. This disables -it, since errors in the GNUlib test suite are often not important. - =item B<--disable-static> Don’t build a static linked version of the libguestfs library. diff --git a/docs/guestfs-faq.pod b/docs/guestfs-faq.pod index d179d3852..bea609591 100644 --- a/docs/guestfs-faq.pod +++ b/docs/guestfs-faq.pod @@ -403,7 +403,7 @@ should contain: source localenv #export PATH=/tmp/qemu/x86_64-softmmu:$PATH - ./autogen.sh --prefix /usr "$@" + ./configure --prefix /usr "$@" Make C executable. @@ -431,7 +431,7 @@ enough supermin installed, then see the previous question. If supermin 5 doesn't support your distro at all, you will need to use the "fixed appliance method" where you use a pre-compiled binary appliance. To build libguestfs without supermin, you need to pass -C<--disable-appliance --disable-daemon> to either F<./autogen.sh> or +C<--disable-appliance --disable-daemon> to either F<./configure> or F<./configure> (depending whether you are building respectively from git or from tarballs). Then, when using libguestfs, you B set the C environment variable to the directory of a diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index 90923f450..1527c4fbd 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -14,7 +14,7 @@ L Large amounts of boilerplate code in libguestfs (RPC, bindings, documentation) are generated. This means that many source files will appear to be missing from a straightforward git checkout. You have to -run the generator (C<./autogen.sh && make -C generator>) in order to +run the generator (C<./configure && make -C generator>) in order to create those files. Libguestfs uses an autotools-based build system, with the main files @@ -250,11 +250,6 @@ large amounts of boilerplate C code for things like RPC and bindings. L command and documentation. -=item F - -Gnulib is used as a portability library. A copy of gnulib is included -under here. - =item F L, the virtual machine image inspector. @@ -799,7 +794,7 @@ the libguestfs source tree must be the same. Do: - ./autogen.sh + ./configure make clean ||: make make installcheck @@ -921,7 +916,7 @@ command, the list of objects and the C<-cclib> libraries in the correct order otherwise. virt_customize_LINK = \ - $(top_builddir)/ocaml-link.sh -cclib '-lutils -lgnu' -- ... + $(top_builddir)/ocaml-link.sh -cclib '-lutils' -- ... The actual rules, which you can examine in F, are a little bit more complicated than this because they have to handle: @@ -1029,10 +1024,6 @@ Finalize F =item * -Consider updating gnulib to latest upstream version. - -=item * - Create new stable and development directories under L. diff --git a/fish/fish.c b/fish/fish.c index d6ac99032..3881647c1 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,6 @@ #include "progress.h" #include "c-ctype.h" -#include "closeout.h" #include "ignore-value.h" #include "getprogname.h" @@ -178,9 +178,6 @@ usage (int status) int main (int argc, char *argv[]) { - /* Initialize gnulib closeout module. */ - atexit (close_stdout); - setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEBASEDIR); textdomain (PACKAGE); diff --git a/fuse/guestmount.c b/fuse/guestmount.c index b52ae670a..d2f655729 100644 --- a/fuse/guestmount.c +++ b/fuse/guestmount.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/fuse/guestunmount.c b/fuse/guestunmount.c index 9560fe740..c7152d076 100644 --- a/fuse/guestunmount.c +++ b/fuse/guestunmount.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/gnulib/lib/Makefile.am b/gnulib/lib/Makefile.am new file mode 100644 index 000000000..20bbb18ff --- /dev/null +++ b/gnulib/lib/Makefile.am @@ -0,0 +1,57 @@ +# libguestfs +# Copyright (C) 2017-2021 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any 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. + +# This directory contains some dependencies originally from gnulib. +# The aim is for everything in this directory to eventually go away, +# probably being moved to common/utils. + +include $(top_srcdir)/subdir-rules.mk + +noinst_LTLIBRARIES = libgnu.la +libgnu_la_SOURCES = \ + base64.c \ + base64.h \ + bitrotate.h \ + c-ctype.h \ + cloexec.c \ + cloexec.h \ + full-read.c \ + full-read.h \ + full-write.c \ + full-write.h \ + getprogname.h \ + hash.c \ + hash.h \ + hash-pjw.c \ + hash-pjw.h \ + ignore-value.h \ + nonblocking.c \ + nonblocking.h \ + safe-read.c \ + safe-read.h \ + safe-write.c \ + safe-write.h \ + xalloc-oversized.h \ + xstrtol.c \ + xstrtol.h \ + xstrtoll.c \ + xstrtoul.c \ + xstrtoull.c \ + xstrtoumax.c +libutils_la_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(GCC_VISIBILITY_HIDDEN) diff --git a/gnulib/lib/base64.c b/gnulib/lib/base64.c new file mode 100644 index 000000000..6948234f1 --- /dev/null +++ b/gnulib/lib/base64.c @@ -0,0 +1,612 @@ +/* base64.c -- Encode binary data using printable characters. + Copyright (C) 1999-2001, 2004-2006, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* 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 . + * + * 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 + +/* Get prototype. */ +#include "base64.h" + +/* Get malloc. */ +#include + +/* Get UCHAR_MAX. */ +#include + +#include + +/* 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; +} diff --git a/gnulib/lib/base64.h b/gnulib/lib/base64.h new file mode 100644 index 000000000..c20f7c791 --- /dev/null +++ b/gnulib/lib/base64.h @@ -0,0 +1,75 @@ +/* base64.h -- Encode binary data using printable characters. + Copyright (C) 2004-2006, 2009-2021 Free Software Foundation, Inc. + Written by Simon Josefsson. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BASE64_H +# define BASE64_H + +/* Get size_t. */ +# include + +/* Get bool. */ +# include + +# 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); + +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 */ diff --git a/gnulib/lib/bitrotate.h b/gnulib/lib/bitrotate.h new file mode 100755 index 000000000..a0dbc70e0 --- /dev/null +++ b/gnulib/lib/bitrotate.h @@ -0,0 +1,135 @@ +/* bitrotate.h - Rotate bits in integers + Copyright (C) 2008-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Written by Simon Josefsson , 2008. */ + +#ifndef _GL_BITROTATE_H +#define _GL_BITROTATE_H + +#include +#include +#include + +#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. */ +static 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.*/ +static 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. */ +static 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.*/ +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static 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. */ +static inline uint8_t +rotr8 (uint8_t x, int n) +{ + return (((unsigned int) x >> n) | ((unsigned int) x << (8 - n))) & UINT8_MAX; +} + +#endif /* _GL_BITROTATE_H */ diff --git a/gnulib/lib/c-ctype.h b/gnulib/lib/c-ctype.h new file mode 100755 index 000000000..6c2030ae1 --- /dev/null +++ b/gnulib/lib/c-ctype.h @@ -0,0 +1,363 @@ +/* Character handling in C locale. + + These functions work like the corresponding functions in , + except that they have the C (POSIX) locale hardwired, whereas the + functions' behaviour depends on the current locale set via + setlocale. + + Copyright (C) 2000-2003, 2006, 2008-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef C_CTYPE_H +#define C_CTYPE_H + +#include + +#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 , 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 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)) ... + */ + +static inline bool +c_isalnum (int c) +{ + switch (c) + { + _C_CTYPE_DIGIT: + _C_CTYPE_LOWER: + _C_CTYPE_UPPER: + return true; + default: + return false; + } +} + +static 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. */ +static 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; + } +} + +static inline bool +c_isblank (int c) +{ + return c == ' ' || c == '\t'; +} + +static inline bool +c_iscntrl (int c) +{ + switch (c) + { + _C_CTYPE_CNTRL: + return true; + default: + return false; + } +} + +static inline bool +c_isdigit (int c) +{ + switch (c) + { + _C_CTYPE_DIGIT: + return true; + default: + return false; + } +} + +static 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; + } +} + +static inline bool +c_islower (int c) +{ + switch (c) + { + _C_CTYPE_LOWER: + return true; + default: + return false; + } +} + +static 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; + } +} + +static inline bool +c_ispunct (int c) +{ + switch (c) + { + _C_CTYPE_PUNCT: + return true; + default: + return false; + } +} + +static inline bool +c_isspace (int c) +{ + switch (c) + { + case ' ': case '\t': case '\n': case '\v': case '\f': case '\r': + return true; + default: + return false; + } +} + +static inline bool +c_isupper (int c) +{ + switch (c) + { + _C_CTYPE_UPPER: + return true; + default: + return false; + } +} + +static inline bool +c_isxdigit (int c) +{ + switch (c) + { + _C_CTYPE_DIGIT: + _C_CTYPE_A_THRU_F: + return true; + default: + return false; + } +} + +static inline int +c_tolower (int c) +{ + switch (c) + { + _C_CTYPE_UPPER: + return c - 'A' + 'a'; + default: + return c; + } +} + +static inline int +c_toupper (int c) +{ + switch (c) + { + _C_CTYPE_LOWER: + return c - 'a' + 'A'; + default: + return c; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* C_CTYPE_H */ diff --git a/gnulib/lib/cloexec.c b/gnulib/lib/cloexec.c new file mode 100644 index 000000000..25486250e --- /dev/null +++ b/gnulib/lib/cloexec.c @@ -0,0 +1,88 @@ +/* cloexec.c - set or clear the close-on-exec descriptor flag + + Copyright (C) 1991, 2004-2006, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "cloexec.h" + +#include +#include +#include + +/* 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); +} diff --git a/gnulib/lib/cloexec.h b/gnulib/lib/cloexec.h new file mode 100644 index 000000000..4708343ac --- /dev/null +++ b/gnulib/lib/cloexec.h @@ -0,0 +1,43 @@ +/* cloexec.c - set or clear the close-on-exec descriptor flag + + Copyright (C) 2004, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/* 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); diff --git a/gnulib/lib/full-read.c b/gnulib/lib/full-read.c new file mode 100644 index 000000000..b9f584a6c --- /dev/null +++ b/gnulib/lib/full-read.c @@ -0,0 +1,25 @@ +/* An interface to read that retries after partial reads and interrupts. + Copyright (C) 2002-2003, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FULL_READ +#include "full-write.c" diff --git a/gnulib/lib/full-read.h b/gnulib/lib/full-read.h new file mode 100644 index 000000000..4d2e62946 --- /dev/null +++ b/gnulib/lib/full-read.h @@ -0,0 +1,30 @@ +/* An interface to read() that reads all it is asked to read. + + Copyright (C) 2002, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/* Read COUNT bytes at BUF to descriptor FD, retrying if interrupted + or if partial reads occur. Return the number of bytes successfully + read, setting errno if that is less than COUNT. errno = 0 means EOF. */ +extern size_t full_read (int fd, void *buf, size_t count); diff --git a/gnulib/lib/full-write.c b/gnulib/lib/full-write.c new file mode 100644 index 000000000..aa13732f4 --- /dev/null +++ b/gnulib/lib/full-write.c @@ -0,0 +1,86 @@ +/* An interface to read and write that retries (if necessary) until complete. + + Copyright (C) 1993-1994, 1997-2006, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/* Specification. */ +#ifdef FULL_READ +# include "full-read.h" +#else +# include "full-write.h" +#endif + +#include + +#ifdef FULL_READ +# include "safe-read.h" +# define safe_rw safe_read +# define full_rw full_read +# undef const +# define const /* empty */ +#else +# include "safe-write.h" +# define safe_rw safe_write +# define full_rw full_write +#endif + +#ifdef FULL_READ +/* Set errno to zero upon EOF. */ +# define ZERO_BYTE_TRANSFER_ERRNO 0 +#else +/* Some buggy drivers return 0 when one tries to write beyond + a device's end. (Example: Linux 1.2.13 on /dev/fd0.) + Set errno to ENOSPC so they get a sensible diagnostic. */ +# define ZERO_BYTE_TRANSFER_ERRNO ENOSPC +#endif + +/* Write(read) COUNT bytes at BUF to(from) descriptor FD, retrying if + interrupted or if a partial write(read) occurs. Return the number + of bytes transferred. + When writing, set errno if fewer than COUNT bytes are written. + When reading, if fewer than COUNT bytes are read, you must examine + errno to distinguish failure from EOF (errno == 0). */ +size_t +full_rw (int fd, const void *buf, size_t count) +{ + size_t total = 0; + const char *ptr = (const char *) buf; + + while (count > 0) + { + size_t n_rw = safe_rw (fd, ptr, count); + if (n_rw == (size_t) -1) + break; + if (n_rw == 0) + { + errno = ZERO_BYTE_TRANSFER_ERRNO; + break; + } + total += n_rw; + ptr += n_rw; + count -= n_rw; + } + + return total; +} diff --git a/gnulib/lib/full-write.h b/gnulib/lib/full-write.h new file mode 100644 index 000000000..3d345635e --- /dev/null +++ b/gnulib/lib/full-write.h @@ -0,0 +1,41 @@ +/* An interface to write() that writes all it is asked to write. + + Copyright (C) 2002-2003, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Write COUNT bytes at BUF to descriptor FD, retrying if interrupted + or if partial writes occur. Return the number of bytes successfully + written, setting errno if that is less than COUNT. */ +extern size_t full_write (int fd, const void *buf, size_t count); + + +#ifdef __cplusplus +} +#endif diff --git a/gnulib/lib/getprogname.h b/gnulib/lib/getprogname.h new file mode 100644 index 000000000..68a4cb2d4 --- /dev/null +++ b/gnulib/lib/getprogname.h @@ -0,0 +1,30 @@ +/* libguestfs + * Copyright (C) 2013-2020 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GUESTFS_GETPROGNAME +#define GUESTFS_GETPROGNAME + +#include + +static inline char const * +getprogname (void) +{ + return program_invocation_short_name; +} + +#endif /* GUESTFS_GETPROGNAME */ diff --git a/gnulib/lib/hash-pjw.c b/gnulib/lib/hash-pjw.c new file mode 100644 index 000000000..f725db099 --- /dev/null +++ b/gnulib/lib/hash-pjw.c @@ -0,0 +1,40 @@ +/* hash-pjw.c -- compute a hash value from a NUL-terminated string. + + Copyright (C) 2001, 2003, 2006, 2009-2021 Free Software Foundation, Inc. + + This program is free software: you can 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 . */ + +#include + +#include "hash-pjw.h" + +#include + +#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See https://www.haible.de/bruno/hashfunc.html. */ + +size_t +hash_pjw (const void *x, size_t tablesize) +{ + const char *s; + size_t h = 0; + + for (s = x; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h % tablesize; +} diff --git a/gnulib/lib/hash-pjw.h b/gnulib/lib/hash-pjw.h new file mode 100644 index 000000000..778fdfca3 --- /dev/null +++ b/gnulib/lib/hash-pjw.h @@ -0,0 +1,23 @@ +/* hash-pjw.h -- declaration for a simple hash function + Copyright (C) 2001, 2003, 2009-2021 Free Software Foundation, Inc. + + This program is free software: you can 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 . */ + +#include + +/* Compute a hash code for a NUL-terminated string starting at X, + and return the hash code modulo TABLESIZE. + The result is platform dependent: it depends on the size of the 'size_t' + type and on the signedness of the 'char' type. */ +extern size_t hash_pjw (void const *x, size_t tablesize); diff --git a/gnulib/lib/hash.c b/gnulib/lib/hash.c new file mode 100755 index 000000000..f6c7518ea --- /dev/null +++ b/gnulib/lib/hash.c @@ -0,0 +1,1113 @@ +/* hash - hashing table processing. + + Copyright (C) 1998-2004, 2006-2007, 2009-2021 Free Software Foundation, Inc. + + Written by Jim Meyering, 1992. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* A generic hash table package. */ + +/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead + of malloc. If you change USE_OBSTACK, you have to recompile! */ + +#include + +#include "hash.h" + +#include "bitrotate.h" +#include "xalloc-oversized.h" + +#include +#include +#include + +#if USE_OBSTACK +# include "obstack.h" +# ifndef obstack_chunk_alloc +# define obstack_chunk_alloc malloc +# endif +# ifndef obstack_chunk_free +# define obstack_chunk_free free +# endif +#endif + +struct hash_entry + { + void *data; + struct hash_entry *next; + }; + +struct hash_table + { + /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1, + for a possibility of N_BUCKETS. Among those, N_BUCKETS_USED buckets + are not empty, there are N_ENTRIES active entries in the table. */ + struct hash_entry *bucket; + struct hash_entry const *bucket_limit; + size_t n_buckets; + size_t n_buckets_used; + size_t n_entries; + + /* Tuning arguments, kept in a physically separate structure. */ + const Hash_tuning *tuning; + + /* Three functions are given to 'hash_initialize', see the documentation + block for this function. In a word, HASHER randomizes a user entry + into a number up from 0 up to some maximum minus 1; COMPARATOR returns + true if two user entries compare equally; and DATA_FREER is the cleanup + function for a user entry. */ + Hash_hasher hasher; + Hash_comparator comparator; + Hash_data_freer data_freer; + + /* A linked list of freed struct hash_entry structs. */ + struct hash_entry *free_entry_list; + +#if USE_OBSTACK + /* Whenever obstacks are used, it is possible to allocate all overflowed + entries into a single stack, so they all can be freed in a single + operation. It is not clear if the speedup is worth the trouble. */ + struct obstack entry_stack; +#endif + }; + +/* A hash table contains many internal entries, each holding a pointer to + some user-provided data (also called a user entry). An entry indistinctly + refers to both the internal entry and its associated user entry. A user + entry contents may be hashed by a randomization function (the hashing + function, or just "hasher" for short) into a number (or "slot") between 0 + and the current table size. At each slot position in the hash table, + starts a linked chain of entries for which the user data all hash to this + slot. A bucket is the collection of all entries hashing to the same slot. + + A good "hasher" function will distribute entries rather evenly in buckets. + In the ideal case, the length of each bucket is roughly the number of + entries divided by the table size. Finding the slot for a data is usually + done in constant time by the "hasher", and the later finding of a precise + entry is linear in time with the size of the bucket. Consequently, a + larger hash table size (that is, a larger number of buckets) is prone to + yielding shorter chains, *given* the "hasher" function behaves properly. + + Long buckets slow down the lookup algorithm. One might use big hash table + sizes in hope to reduce the average length of buckets, but this might + become inordinate, as unused slots in the hash table take some space. The + best bet is to make sure you are using a good "hasher" function (beware + that those are not that easy to write! :-), and to use a table size + larger than the actual number of entries. */ + +/* If an insertion makes the ratio of nonempty buckets to table size larger + than the growth threshold (a number between 0.0 and 1.0), then increase + the table size by multiplying by the growth factor (a number greater than + 1.0). The growth threshold defaults to 0.8, and the growth factor + defaults to 1.414, meaning that the table will have doubled its size + every second time 80% of the buckets get used. */ +#define DEFAULT_GROWTH_THRESHOLD 0.8f +#define DEFAULT_GROWTH_FACTOR 1.414f + +/* If a deletion empties a bucket and causes the ratio of used buckets to + table size to become smaller than the shrink threshold (a number between + 0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a + number greater than the shrink threshold but smaller than 1.0). The shrink + threshold and factor default to 0.0 and 1.0, meaning that the table never + shrinks. */ +#define DEFAULT_SHRINK_THRESHOLD 0.0f +#define DEFAULT_SHRINK_FACTOR 1.0f + +/* Use this to initialize or reset a TUNING structure to + some sensible values. */ +static const Hash_tuning default_tuning = + { + DEFAULT_SHRINK_THRESHOLD, + DEFAULT_SHRINK_FACTOR, + DEFAULT_GROWTH_THRESHOLD, + DEFAULT_GROWTH_FACTOR, + false + }; + +/* Information and lookup. */ + +size_t +hash_get_n_buckets (const Hash_table *table) +{ + return table->n_buckets; +} + +size_t +hash_get_n_buckets_used (const Hash_table *table) +{ + return table->n_buckets_used; +} + +size_t +hash_get_n_entries (const Hash_table *table) +{ + return table->n_entries; +} + +size_t +hash_get_max_bucket_length (const Hash_table *table) +{ + struct hash_entry const *bucket; + size_t max_bucket_length = 0; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry const *cursor = bucket; + size_t bucket_length = 1; + + while (cursor = cursor->next, cursor) + bucket_length++; + + if (bucket_length > max_bucket_length) + max_bucket_length = bucket_length; + } + } + + return max_bucket_length; +} + +bool +hash_table_ok (const Hash_table *table) +{ + struct hash_entry const *bucket; + size_t n_buckets_used = 0; + size_t n_entries = 0; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry const *cursor = bucket; + + /* Count bucket head. */ + n_buckets_used++; + n_entries++; + + /* Count bucket overflow. */ + while (cursor = cursor->next, cursor) + n_entries++; + } + } + + if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries) + return true; + + return false; +} + +void +hash_print_statistics (const Hash_table *table, FILE *stream) +{ + size_t n_entries = hash_get_n_entries (table); + size_t n_buckets = hash_get_n_buckets (table); + size_t n_buckets_used = hash_get_n_buckets_used (table); + size_t max_bucket_length = hash_get_max_bucket_length (table); + + fprintf (stream, "# entries: %lu\n", (unsigned long int) n_entries); + fprintf (stream, "# buckets: %lu\n", (unsigned long int) n_buckets); + fprintf (stream, "# buckets used: %lu (%.2f%%)\n", + (unsigned long int) n_buckets_used, + (100.0 * n_buckets_used) / n_buckets); + fprintf (stream, "max bucket length: %lu\n", + (unsigned long int) max_bucket_length); +} + +/* Hash KEY and return a pointer to the selected bucket. + If TABLE->hasher misbehaves, abort. */ +static struct hash_entry * +safe_hasher (const Hash_table *table, const void *key) +{ + size_t n = table->hasher (key, table->n_buckets); + if (! (n < table->n_buckets)) + abort (); + return table->bucket + n; +} + +void * +hash_lookup (const Hash_table *table, const void *entry) +{ + struct hash_entry const *bucket = safe_hasher (table, entry); + struct hash_entry const *cursor; + + if (bucket->data == NULL) + return NULL; + + for (cursor = bucket; cursor; cursor = cursor->next) + if (entry == cursor->data || table->comparator (entry, cursor->data)) + return cursor->data; + + return NULL; +} + +/* Walking. */ + +void * +hash_get_first (const Hash_table *table) +{ + struct hash_entry const *bucket; + + if (table->n_entries == 0) + return NULL; + + for (bucket = table->bucket; ; bucket++) + if (! (bucket < table->bucket_limit)) + abort (); + else if (bucket->data) + return bucket->data; +} + +void * +hash_get_next (const Hash_table *table, const void *entry) +{ + struct hash_entry const *bucket = safe_hasher (table, entry); + struct hash_entry const *cursor; + + /* Find next entry in the same bucket. */ + cursor = bucket; + do + { + if (cursor->data == entry && cursor->next) + return cursor->next->data; + cursor = cursor->next; + } + while (cursor != NULL); + + /* Find first entry in any subsequent bucket. */ + while (++bucket < table->bucket_limit) + if (bucket->data) + return bucket->data; + + /* None found. */ + return NULL; +} + +size_t +hash_get_entries (const Hash_table *table, void **buffer, + size_t buffer_size) +{ + size_t counter = 0; + struct hash_entry const *bucket; + struct hash_entry const *cursor; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + { + if (counter >= buffer_size) + return counter; + buffer[counter++] = cursor->data; + } + } + } + + return counter; +} + +size_t +hash_do_for_each (const Hash_table *table, Hash_processor processor, + void *processor_data) +{ + size_t counter = 0; + struct hash_entry const *bucket; + struct hash_entry const *cursor; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + { + if (! processor (cursor->data, processor_data)) + return counter; + counter++; + } + } + } + + return counter; +} + +/* Allocation and clean-up. */ + +#if USE_DIFF_HASH + +/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see + B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm, + Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash + algorithms tend to be domain-specific, so what's good for [diffutils'] io.c + may not be good for your application." */ + +size_t +hash_string (const char *string, size_t n_buckets) +{ +# define HASH_ONE_CHAR(Value, Byte) \ + ((Byte) + rotl_sz (Value, 7)) + + size_t value = 0; + unsigned char ch; + + for (; (ch = *string); string++) + value = HASH_ONE_CHAR (value, ch); + return value % n_buckets; + +# undef HASH_ONE_CHAR +} + +#else /* not USE_DIFF_HASH */ + +/* This one comes from 'recode', and performs a bit better than the above as + per a few experiments. It is inspired from a hashing routine found in the + very old Cyber 'snoop', itself written in typical Greg Mansfield style. + (By the way, what happened to this excellent man? Is he still alive?) */ + +size_t +hash_string (const char *string, size_t n_buckets) +{ + size_t value = 0; + unsigned char ch; + + for (; (ch = *string); string++) + value = (value * 31 + ch) % n_buckets; + return value; +} + +#endif /* not USE_DIFF_HASH */ + +/* Return true if CANDIDATE is a prime number. CANDIDATE should be an odd + number at least equal to 11. */ + +static bool +is_prime (size_t candidate) +{ + size_t divisor = 3; + size_t square = divisor * divisor; + + while (square < candidate && (candidate % divisor)) + { + divisor++; + square += 4 * divisor; + divisor++; + } + + return (candidate % divisor ? true : false); +} + +/* Round a given CANDIDATE number up to the nearest prime, and return that + prime. Primes lower than 10 are merely skipped. */ + +static size_t +next_prime (size_t candidate) +{ + /* Skip small primes. */ + if (candidate < 10) + candidate = 10; + + /* Make it definitely odd. */ + candidate |= 1; + + while (SIZE_MAX != candidate && !is_prime (candidate)) + candidate += 2; + + return candidate; +} + +void +hash_reset_tuning (Hash_tuning *tuning) +{ + *tuning = default_tuning; +} + +/* If the user passes a NULL hasher, we hash the raw pointer. */ +static size_t +raw_hasher (const void *data, size_t n) +{ + /* When hashing unique pointers, it is often the case that they were + generated by malloc and thus have the property that the low-order + bits are 0. As this tends to give poorer performance with small + tables, we rotate the pointer value before performing division, + in an attempt to improve hash quality. */ + size_t val = rotr_sz ((size_t) data, 3); + return val % n; +} + +/* If the user passes a NULL comparator, we use pointer comparison. */ +static bool +raw_comparator (const void *a, const void *b) +{ + return a == b; +} + + +/* For the given hash TABLE, check the user supplied tuning structure for + reasonable values, and return true if there is no gross error with it. + Otherwise, definitively reset the TUNING field to some acceptable default + in the hash table (that is, the user loses the right of further modifying + tuning arguments), and return false. */ + +static bool +check_tuning (Hash_table *table) +{ + const Hash_tuning *tuning = table->tuning; + float epsilon; + if (tuning == &default_tuning) + return true; + + /* Be a bit stricter than mathematics would require, so that + rounding errors in size calculations do not cause allocations to + fail to grow or shrink as they should. The smallest allocation + is 11 (due to next_prime's algorithm), so an epsilon of 0.1 + should be good enough. */ + epsilon = 0.1f; + + if (epsilon < tuning->growth_threshold + && tuning->growth_threshold < 1 - epsilon + && 1 + epsilon < tuning->growth_factor + && 0 <= tuning->shrink_threshold + && tuning->shrink_threshold + epsilon < tuning->shrink_factor + && tuning->shrink_factor <= 1 + && tuning->shrink_threshold + epsilon < tuning->growth_threshold) + return true; + + table->tuning = &default_tuning; + return false; +} + +/* Compute the size of the bucket array for the given CANDIDATE and + TUNING, or return 0 if there is no possible way to allocate that + many entries. */ + +static size_t +compute_bucket_size (size_t candidate, const Hash_tuning *tuning) +{ + if (!tuning->is_n_buckets) + { + float new_candidate = candidate / tuning->growth_threshold; + if ((float) SIZE_MAX <= new_candidate) + return 0; + candidate = new_candidate; + } + candidate = next_prime (candidate); + if (xalloc_oversized (candidate, sizeof (struct hash_entry *))) + return 0; + return candidate; +} + +Hash_table * +hash_initialize (size_t candidate, const Hash_tuning *tuning, + Hash_hasher hasher, Hash_comparator comparator, + Hash_data_freer data_freer) +{ + Hash_table *table; + + if (hasher == NULL) + hasher = raw_hasher; + if (comparator == NULL) + comparator = raw_comparator; + + table = malloc (sizeof *table); + if (table == NULL) + return NULL; + + if (!tuning) + tuning = &default_tuning; + table->tuning = tuning; + if (!check_tuning (table)) + { + /* Fail if the tuning options are invalid. This is the only occasion + when the user gets some feedback about it. Once the table is created, + if the user provides invalid tuning options, we silently revert to + using the defaults, and ignore further request to change the tuning + options. */ + goto fail; + } + + table->n_buckets = compute_bucket_size (candidate, tuning); + if (!table->n_buckets) + goto fail; + + table->bucket = calloc (table->n_buckets, sizeof *table->bucket); + if (table->bucket == NULL) + goto fail; + table->bucket_limit = table->bucket + table->n_buckets; + table->n_buckets_used = 0; + table->n_entries = 0; + + table->hasher = hasher; + table->comparator = comparator; + table->data_freer = data_freer; + + table->free_entry_list = NULL; +#if USE_OBSTACK + obstack_init (&table->entry_stack); +#endif + return table; + + fail: + free (table); + return NULL; +} + +void +hash_clear (Hash_table *table) +{ + struct hash_entry *bucket; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry *cursor; + struct hash_entry *next; + + /* Free the bucket overflow. */ + for (cursor = bucket->next; cursor; cursor = next) + { + if (table->data_freer) + table->data_freer (cursor->data); + cursor->data = NULL; + + next = cursor->next; + /* Relinking is done one entry at a time, as it is to be expected + that overflows are either rare or short. */ + cursor->next = table->free_entry_list; + table->free_entry_list = cursor; + } + + /* Free the bucket head. */ + if (table->data_freer) + table->data_freer (bucket->data); + bucket->data = NULL; + bucket->next = NULL; + } + } + + table->n_buckets_used = 0; + table->n_entries = 0; +} + +void +hash_free (Hash_table *table) +{ + struct hash_entry *bucket; + struct hash_entry *cursor; + struct hash_entry *next; + + /* Call the user data_freer function. */ + if (table->data_freer && table->n_entries) + { + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + table->data_freer (cursor->data); + } + } + } + +#if USE_OBSTACK + + obstack_free (&table->entry_stack, NULL); + +#else + + /* Free all bucket overflowed entries. */ + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + for (cursor = bucket->next; cursor; cursor = next) + { + next = cursor->next; + free (cursor); + } + } + + /* Also reclaim the internal list of previously freed entries. */ + for (cursor = table->free_entry_list; cursor; cursor = next) + { + next = cursor->next; + free (cursor); + } + +#endif + + /* Free the remainder of the hash table structure. */ + free (table->bucket); + free (table); +} + +/* Insertion and deletion. */ + +/* Get a new hash entry for a bucket overflow, possibly by recycling a + previously freed one. If this is not possible, allocate a new one. */ + +static struct hash_entry * +allocate_entry (Hash_table *table) +{ + struct hash_entry *new; + + if (table->free_entry_list) + { + new = table->free_entry_list; + table->free_entry_list = new->next; + } + else + { +#if USE_OBSTACK + new = obstack_alloc (&table->entry_stack, sizeof *new); +#else + new = malloc (sizeof *new); +#endif + } + + return new; +} + +/* Free a hash entry which was part of some bucket overflow, + saving it for later recycling. */ + +static void +free_entry (Hash_table *table, struct hash_entry *entry) +{ + entry->data = NULL; + entry->next = table->free_entry_list; + table->free_entry_list = entry; +} + +/* This private function is used to help with insertion and deletion. When + ENTRY matches an entry in the table, return a pointer to the corresponding + user data and set *BUCKET_HEAD to the head of the selected bucket. + Otherwise, return NULL. When DELETE is true and ENTRY matches an entry in + the table, unlink the matching entry. */ + +static void * +hash_find_entry (Hash_table *table, const void *entry, + struct hash_entry **bucket_head, bool delete) +{ + struct hash_entry *bucket = safe_hasher (table, entry); + struct hash_entry *cursor; + + *bucket_head = bucket; + + /* Test for empty bucket. */ + if (bucket->data == NULL) + return NULL; + + /* See if the entry is the first in the bucket. */ + if (entry == bucket->data || table->comparator (entry, bucket->data)) + { + void *data = bucket->data; + + if (delete) + { + if (bucket->next) + { + struct hash_entry *next = bucket->next; + + /* Bump the first overflow entry into the bucket head, then save + the previous first overflow entry for later recycling. */ + *bucket = *next; + free_entry (table, next); + } + else + { + bucket->data = NULL; + } + } + + return data; + } + + /* Scan the bucket overflow. */ + for (cursor = bucket; cursor->next; cursor = cursor->next) + { + if (entry == cursor->next->data + || table->comparator (entry, cursor->next->data)) + { + void *data = cursor->next->data; + + if (delete) + { + struct hash_entry *next = cursor->next; + + /* Unlink the entry to delete, then save the freed entry for later + recycling. */ + cursor->next = next->next; + free_entry (table, next); + } + + return data; + } + } + + /* No entry found. */ + return NULL; +} + +/* Internal helper, to move entries from SRC to DST. Both tables must + share the same free entry list. If SAFE, only move overflow + entries, saving bucket heads for later, so that no allocations will + occur. Return false if the free entry list is exhausted and an + allocation fails. */ + +static bool +transfer_entries (Hash_table *dst, Hash_table *src, bool safe) +{ + struct hash_entry *bucket; + struct hash_entry *cursor; + struct hash_entry *next; + for (bucket = src->bucket; bucket < src->bucket_limit; bucket++) + if (bucket->data) + { + void *data; + struct hash_entry *new_bucket; + + /* Within each bucket, transfer overflow entries first and + then the bucket head, to minimize memory pressure. After + all, the only time we might allocate is when moving the + bucket head, but moving overflow entries first may create + free entries that can be recycled by the time we finally + get to the bucket head. */ + for (cursor = bucket->next; cursor; cursor = next) + { + data = cursor->data; + new_bucket = safe_hasher (dst, data); + + next = cursor->next; + + if (new_bucket->data) + { + /* Merely relink an existing entry, when moving from a + bucket overflow into a bucket overflow. */ + cursor->next = new_bucket->next; + new_bucket->next = cursor; + } + else + { + /* Free an existing entry, when moving from a bucket + overflow into a bucket header. */ + new_bucket->data = data; + dst->n_buckets_used++; + free_entry (dst, cursor); + } + } + /* Now move the bucket head. Be sure that if we fail due to + allocation failure that the src table is in a consistent + state. */ + data = bucket->data; + bucket->next = NULL; + if (safe) + continue; + new_bucket = safe_hasher (dst, data); + + if (new_bucket->data) + { + /* Allocate or recycle an entry, when moving from a bucket + header into a bucket overflow. */ + struct hash_entry *new_entry = allocate_entry (dst); + + if (new_entry == NULL) + return false; + + new_entry->data = data; + new_entry->next = new_bucket->next; + new_bucket->next = new_entry; + } + else + { + /* Move from one bucket header to another. */ + new_bucket->data = data; + dst->n_buckets_used++; + } + bucket->data = NULL; + src->n_buckets_used--; + } + return true; +} + +bool +hash_rehash (Hash_table *table, size_t candidate) +{ + Hash_table storage; + Hash_table *new_table; + size_t new_size = compute_bucket_size (candidate, table->tuning); + + if (!new_size) + return false; + if (new_size == table->n_buckets) + return true; + new_table = &storage; + new_table->bucket = calloc (new_size, sizeof *new_table->bucket); + if (new_table->bucket == NULL) + return false; + new_table->n_buckets = new_size; + new_table->bucket_limit = new_table->bucket + new_size; + new_table->n_buckets_used = 0; + new_table->n_entries = 0; + new_table->tuning = table->tuning; + new_table->hasher = table->hasher; + new_table->comparator = table->comparator; + new_table->data_freer = table->data_freer; + + /* In order for the transfer to successfully complete, we need + additional overflow entries when distinct buckets in the old + table collide into a common bucket in the new table. The worst + case possible is a hasher that gives a good spread with the old + size, but returns a constant with the new size; if we were to + guarantee table->n_buckets_used-1 free entries in advance, then + the transfer would be guaranteed to not allocate memory. + However, for large tables, a guarantee of no further allocation + introduces a lot of extra memory pressure, all for an unlikely + corner case (most rehashes reduce, rather than increase, the + number of overflow entries needed). So, we instead ensure that + the transfer process can be reversed if we hit a memory + allocation failure mid-transfer. */ + + /* Merely reuse the extra old space into the new table. */ +#if USE_OBSTACK + new_table->entry_stack = table->entry_stack; +#endif + new_table->free_entry_list = table->free_entry_list; + + if (transfer_entries (new_table, table, false)) + { + /* Entries transferred successfully; tie up the loose ends. */ + free (table->bucket); + table->bucket = new_table->bucket; + table->bucket_limit = new_table->bucket_limit; + table->n_buckets = new_table->n_buckets; + table->n_buckets_used = new_table->n_buckets_used; + table->free_entry_list = new_table->free_entry_list; + /* table->n_entries and table->entry_stack already hold their value. */ + return true; + } + + /* We've allocated new_table->bucket (and possibly some entries), + exhausted the free list, and moved some but not all entries into + new_table. We must undo the partial move before returning + failure. The only way to get into this situation is if new_table + uses fewer buckets than the old table, so we will reclaim some + free entries as overflows in the new table are put back into + distinct buckets in the old table. + + There are some pathological cases where a single pass through the + table requires more intermediate overflow entries than using two + passes. Two passes give worse cache performance and takes + longer, but at this point, we're already out of memory, so slow + and safe is better than failure. */ + table->free_entry_list = new_table->free_entry_list; + if (! (transfer_entries (table, new_table, true) + && transfer_entries (table, new_table, false))) + abort (); + /* table->n_entries already holds its value. */ + free (new_table->bucket); + return false; +} + +int +hash_insert_if_absent (Hash_table *table, void const *entry, + void const **matched_ent) +{ + void *data; + struct hash_entry *bucket; + + /* The caller cannot insert a NULL entry, since hash_lookup returns NULL + to indicate "not found", and hash_find_entry uses "bucket->data == NULL" + to indicate an empty bucket. */ + if (! entry) + abort (); + + /* If there's a matching entry already in the table, return that. */ + if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL) + { + if (matched_ent) + *matched_ent = data; + return 0; + } + + /* If the growth threshold of the buckets in use has been reached, increase + the table size and rehash. There's no point in checking the number of + entries: if the hashing function is ill-conditioned, rehashing is not + likely to improve it. */ + + if (table->n_buckets_used + > table->tuning->growth_threshold * table->n_buckets) + { + /* Check more fully, before starting real work. If tuning arguments + became invalid, the second check will rely on proper defaults. */ + check_tuning (table); + if (table->n_buckets_used + > table->tuning->growth_threshold * table->n_buckets) + { + const Hash_tuning *tuning = table->tuning; + float candidate = + (tuning->is_n_buckets + ? (table->n_buckets * tuning->growth_factor) + : (table->n_buckets * tuning->growth_factor + * tuning->growth_threshold)); + + if ((float) SIZE_MAX <= candidate) + return -1; + + /* If the rehash fails, arrange to return NULL. */ + if (!hash_rehash (table, candidate)) + return -1; + + /* Update the bucket we are interested in. */ + if (hash_find_entry (table, entry, &bucket, false) != NULL) + abort (); + } + } + + /* ENTRY is not matched, it should be inserted. */ + + if (bucket->data) + { + struct hash_entry *new_entry = allocate_entry (table); + + if (new_entry == NULL) + return -1; + + /* Add ENTRY in the overflow of the bucket. */ + + new_entry->data = (void *) entry; + new_entry->next = bucket->next; + bucket->next = new_entry; + table->n_entries++; + return 1; + } + + /* Add ENTRY right in the bucket head. */ + + bucket->data = (void *) entry; + table->n_entries++; + table->n_buckets_used++; + + return 1; +} + +void * +hash_insert (Hash_table *table, void const *entry) +{ + void const *matched_ent; + int err = hash_insert_if_absent (table, entry, &matched_ent); + return (err == -1 + ? NULL + : (void *) (err == 0 ? matched_ent : entry)); +} + +void * +hash_remove (Hash_table *table, const void *entry) +{ + void *data; + struct hash_entry *bucket; + + data = hash_find_entry (table, entry, &bucket, true); + if (!data) + return NULL; + + table->n_entries--; + if (!bucket->data) + { + table->n_buckets_used--; + + /* If the shrink threshold of the buckets in use has been reached, + rehash into a smaller table. */ + + if (table->n_buckets_used + < table->tuning->shrink_threshold * table->n_buckets) + { + /* Check more fully, before starting real work. If tuning arguments + became invalid, the second check will rely on proper defaults. */ + check_tuning (table); + if (table->n_buckets_used + < table->tuning->shrink_threshold * table->n_buckets) + { + const Hash_tuning *tuning = table->tuning; + size_t candidate = + (tuning->is_n_buckets + ? table->n_buckets * tuning->shrink_factor + : (table->n_buckets * tuning->shrink_factor + * tuning->growth_threshold)); + + if (!hash_rehash (table, candidate)) + { + /* Failure to allocate memory in an attempt to + shrink the table is not fatal. But since memory + is low, we can at least be kind and free any + spare entries, rather than keeping them tied up + in the free entry list. */ +#if ! USE_OBSTACK + struct hash_entry *cursor = table->free_entry_list; + struct hash_entry *next; + while (cursor) + { + next = cursor->next; + free (cursor); + cursor = next; + } + table->free_entry_list = NULL; +#endif + } + } + } + } + + return data; +} + +void * +hash_delete (Hash_table *table, const void *entry) +{ + return hash_remove (table, entry); +} + +/* Testing. */ + +#if TESTING + +void +hash_print (const Hash_table *table) +{ + struct hash_entry *bucket = (struct hash_entry *) table->bucket; + + for ( ; bucket < table->bucket_limit; bucket++) + { + struct hash_entry *cursor; + + if (bucket) + printf ("%lu:\n", (unsigned long int) (bucket - table->bucket)); + + for (cursor = bucket; cursor; cursor = cursor->next) + { + char const *s = cursor->data; + /* FIXME */ + if (s) + printf (" %s\n", s); + } + } +} + +#endif /* TESTING */ diff --git a/gnulib/lib/hash.h b/gnulib/lib/hash.h new file mode 100755 index 000000000..8db7c3487 --- /dev/null +++ b/gnulib/lib/hash.h @@ -0,0 +1,256 @@ +/* hash - hashing table processing. + Copyright (C) 1998-1999, 2001, 2003, 2009-2021 Free Software Foundation, + Inc. + Written by Jim Meyering , 1998. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* A generic hash table package. */ + +/* Make sure USE_OBSTACK is defined to 1 if you want the allocator to use + obstacks instead of malloc, and recompile 'hash.c' with same setting. */ + +#ifndef HASH_H_ +# define HASH_H_ + +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +struct hash_tuning + { + /* This structure is mainly used for 'hash_initialize', see the block + documentation of 'hash_reset_tuning' for more complete comments. */ + + float shrink_threshold; /* ratio of used buckets to trigger a shrink */ + float shrink_factor; /* ratio of new smaller size to original size */ + float growth_threshold; /* ratio of used buckets to trigger a growth */ + float growth_factor; /* ratio of new bigger size to original size */ + bool is_n_buckets; /* if CANDIDATE really means table size */ + }; + +typedef struct hash_tuning Hash_tuning; + +struct hash_table; + +typedef struct hash_table Hash_table; + +/* + * Information and lookup. + */ + +/* The following few functions provide information about the overall hash + table organization: the number of entries, number of buckets and maximum + length of buckets. */ + +/* Return the number of buckets in the hash table. The table size, the total + number of buckets (used plus unused), or the maximum number of slots, are + the same quantity. */ +extern size_t hash_get_n_buckets (const Hash_table *table); + +/* Return the number of slots in use (non-empty buckets). */ +extern size_t hash_get_n_buckets_used (const Hash_table *table); + +/* Return the number of active entries. */ +extern size_t hash_get_n_entries (const Hash_table *table); + +/* Return the length of the longest chain (bucket). */ +extern size_t hash_get_max_bucket_length (const Hash_table *table); + +/* Do a mild validation of a hash table, by traversing it and checking two + statistics. */ +extern bool hash_table_ok (const Hash_table *table); + +extern void hash_print_statistics (const Hash_table *table, FILE *stream); + +/* If ENTRY matches an entry already in the hash table, return the + entry from the table. Otherwise, return NULL. */ +extern void *hash_lookup (const Hash_table *table, const void *entry); + +/* + * Walking. + */ + +/* The functions in this page traverse the hash table and process the + contained entries. For the traversal to work properly, the hash table + should not be resized nor modified while any particular entry is being + processed. In particular, entries should not be added, and an entry + may be removed only if there is no shrink threshold and the entry being + removed has already been passed to hash_get_next. */ + +/* Return the first data in the table, or NULL if the table is empty. */ +extern void *hash_get_first (const Hash_table *table); + +/* Return the user data for the entry following ENTRY, where ENTRY has been + returned by a previous call to either 'hash_get_first' or 'hash_get_next'. + Return NULL if there are no more entries. */ +extern void *hash_get_next (const Hash_table *table, const void *entry); + +/* Fill BUFFER with pointers to active user entries in the hash table, then + return the number of pointers copied. Do not copy more than BUFFER_SIZE + pointers. */ +extern size_t hash_get_entries (const Hash_table *table, void **buffer, + size_t buffer_size); + +typedef bool (*Hash_processor) (void *entry, void *processor_data); + +/* Call a PROCESSOR function for each entry of a hash table, and return the + number of entries for which the processor function returned success. A + pointer to some PROCESSOR_DATA which will be made available to each call to + the processor function. The PROCESSOR accepts two arguments: the first is + the user entry being walked into, the second is the value of PROCESSOR_DATA + as received. The walking continue for as long as the PROCESSOR function + returns nonzero. When it returns zero, the walking is interrupted. */ +extern size_t hash_do_for_each (const Hash_table *table, + Hash_processor processor, void *processor_data); + +/* + * Allocation and clean-up. + */ + +/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1. + This is a convenience routine for constructing other hashing functions. */ +extern size_t hash_string (const char *string, size_t n_buckets); + +extern void hash_reset_tuning (Hash_tuning *tuning); + +typedef size_t (*Hash_hasher) (const void *entry, size_t table_size); +typedef bool (*Hash_comparator) (const void *entry1, const void *entry2); +typedef void (*Hash_data_freer) (void *entry); + +/* Allocate and return a new hash table, or NULL upon failure. The initial + number of buckets is automatically selected so as to _guarantee_ that you + may insert at least CANDIDATE different user entries before any growth of + the hash table size occurs. So, if have a reasonably tight a-priori upper + bound on the number of entries you intend to insert in the hash table, you + may save some table memory and insertion time, by specifying it here. If + the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE + argument has its meaning changed to the wanted number of buckets. + + TUNING points to a structure of user-supplied values, in case some fine + tuning is wanted over the default behavior of the hasher. If TUNING is + NULL, the default tuning parameters are used instead. If TUNING is + provided but the values requested are out of bounds or might cause + rounding errors, return NULL. + + The user-supplied HASHER function, when not NULL, accepts two + arguments ENTRY and TABLE_SIZE. It computes, by hashing ENTRY contents, a + slot number for that entry which should be in the range 0..TABLE_SIZE-1. + This slot number is then returned. + + The user-supplied COMPARATOR function, when not NULL, accepts two + arguments pointing to user data, it then returns true for a pair of entries + that compare equal, or false otherwise. This function is internally called + on entries which are already known to hash to the same bucket index, + but which are distinct pointers. + + The user-supplied DATA_FREER function, when not NULL, may be later called + with the user data as an argument, just before the entry containing the + data gets freed. This happens from within 'hash_free' or 'hash_clear'. + You should specify this function only if you want these functions to free + all of your 'data' data. This is typically the case when your data is + simply an auxiliary struct that you have malloc'd to aggregate several + values. */ +extern Hash_table *hash_initialize (size_t candidate, + const Hash_tuning *tuning, + Hash_hasher hasher, + Hash_comparator comparator, + Hash_data_freer data_freer); + +/* Same as hash_initialize, but invokes xalloc_die on memory exhaustion. */ +/* This function is defined by module 'xhash'. */ +extern Hash_table *hash_xinitialize (size_t candidate, + const Hash_tuning *tuning, + Hash_hasher hasher, + Hash_comparator comparator, + Hash_data_freer data_freer); + +/* Make all buckets empty, placing any chained entries on the free list. + Apply the user-specified function data_freer (if any) to the datas of any + affected entries. */ +extern void hash_clear (Hash_table *table); + +/* Reclaim all storage associated with a hash table. If a data_freer + function has been supplied by the user when the hash table was created, + this function applies it to the data of each entry before freeing that + entry. */ +extern void hash_free (Hash_table *table); + +/* + * Insertion and deletion. + */ + +/* For an already existing hash table, change the number of buckets through + specifying CANDIDATE. The contents of the hash table are preserved. The + new number of buckets is automatically selected so as to _guarantee_ that + the table may receive at least CANDIDATE different user entries, including + those already in the table, before any other growth of the hash table size + occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the + exact number of buckets desired. Return true iff the rehash succeeded. */ +extern bool hash_rehash (Hash_table *table, size_t candidate); + +/* If ENTRY matches an entry already in the hash table, return the pointer + to the entry from the table. Otherwise, insert ENTRY and return ENTRY. + Return NULL if the storage required for insertion cannot be allocated. + This implementation does not support duplicate entries or insertion of + NULL. */ +extern void *hash_insert (Hash_table *table, const void *entry); + +/* Same as hash_insert, but invokes xalloc_die on memory exhaustion. */ +/* This function is defined by module 'xhash'. */ +extern void *hash_xinsert (Hash_table *table, const void *entry); + +/* Insert ENTRY into hash TABLE if there is not already a matching entry. + + Return -1 upon memory allocation failure. + Return 1 if insertion succeeded. + Return 0 if there is already a matching entry in the table, + and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT + to that entry. + + This interface is easier to use than hash_insert when you must + distinguish between the latter two cases. More importantly, + hash_insert is unusable for some types of ENTRY values. When using + hash_insert, the only way to distinguish those cases is to compare + the return value and ENTRY. That works only when you can have two + different ENTRY values that point to data that compares "equal". Thus, + when the ENTRY value is a simple scalar, you must use + hash_insert_if_absent. ENTRY must not be NULL. */ +extern int hash_insert_if_absent (Hash_table *table, const void *entry, + const void **matched_ent); + +/* If ENTRY is already in the table, remove it and return the just-deleted + data (the user may want to deallocate its storage). If ENTRY is not in the + table, don't modify the table and return NULL. */ +extern void *hash_remove (Hash_table *table, const void *entry); + +/* Same as hash_remove. This interface is deprecated. + FIXME: Remove in 2022. */ +extern void *hash_delete (Hash_table *table, const void *entry); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/gnulib/lib/ignore-value.h b/gnulib/lib/ignore-value.h new file mode 100755 index 000000000..dac2d1a23 --- /dev/null +++ b/gnulib/lib/ignore-value.h @@ -0,0 +1,58 @@ +/* ignore a function return without a compiler warning. -*- coding: utf-8 -*- + + Copyright (C) 2008-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Written by Jim Meyering, Eric Blake and Pádraig Brady. */ + +/* Use "ignore_value" to avoid a warning when using a function declared with + gcc's warn_unused_result attribute, but for which you really do want to + ignore the result. Traditionally, people have used a "(void)" cast to + indicate that a function's return value is deliberately unused. However, + if the function is declared with __attribute__((warn_unused_result)), + gcc issues a warning even with the cast. + + Caution: most of the time, you really should heed gcc's warning, and + check the return value. However, in those exceptional cases in which + you're sure you know what you're doing, use this function. + + For the record, here's one of the ignorable warnings: + "copy.c:233: warning: ignoring return value of 'fchown', + declared with attribute warn_unused_result". */ + +#ifndef _GL_IGNORE_VALUE_H +#define _GL_IGNORE_VALUE_H + +/* Normally casting an expression to void discards its value, but GCC + versions 3.4 and newer have __attribute__ ((__warn_unused_result__)) + which may cause unwanted diagnostics in that case. Use __typeof__ + and __extension__ to work around the problem, if the workaround is + known to be needed. + The workaround is not needed with clang. */ +#if (3 < __GNUC__ + (4 <= __GNUC_MINOR__)) && !defined __clang__ +# define ignore_value(x) \ + (__extension__ ({ __typeof__ (x) __x = (x); (void) __x; })) +#else +# define ignore_value(x) ((void) (x)) +#endif + +#endif diff --git a/gnulib/lib/nonblocking.c b/gnulib/lib/nonblocking.c new file mode 100644 index 000000000..d085ecdb2 --- /dev/null +++ b/gnulib/lib/nonblocking.c @@ -0,0 +1,170 @@ +/* Non-blocking I/O for pipe or socket descriptors. + Copyright (C) 2011-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/* Specification. */ +#include "nonblocking.h" + +#include + +#if defined _WIN32 && ! defined __CYGWIN__ +/* Native Windows API. */ + +# include +# include +# include + +/* Get declarations of the native Windows API functions. */ +# define WIN32_LEAN_AND_MEAN +# include + +# if GNULIB_MSVC_NOTHROW +# include "msvc-nothrow.h" +# else +# include +# endif + +/* Don't assume that UNICODE is not defined. */ +# undef GetNamedPipeHandleState +# define GetNamedPipeHandleState GetNamedPipeHandleStateA + +int +get_nonblocking_flag (int desc) +{ + HANDLE h = (HANDLE) _get_osfhandle (desc); + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + if (GetFileType (h) == FILE_TYPE_PIPE) + { + /* h is a pipe or socket. */ + DWORD state; + if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0)) + /* h is a pipe. */ + return (state & PIPE_NOWAIT) != 0; + else + /* h is a socket. */ + errno = ENOSYS; + return -1; + } + else + /* The native Windows API does not support non-blocking on regular + files. */ + return 0; +} + +int +set_nonblocking_flag (int desc, bool value) +{ + HANDLE h = (HANDLE) _get_osfhandle (desc); + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + if (GetFileType (h) == FILE_TYPE_PIPE) + { + /* h is a pipe or socket. */ + DWORD state; + if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0)) + { + /* h is a pipe. */ + if ((state & PIPE_NOWAIT) != 0) + { + if (value) + return 0; + state &= ~PIPE_NOWAIT; + } + else + { + if (!value) + return 0; + state |= PIPE_NOWAIT; + } + if (SetNamedPipeHandleState (h, &state, NULL, NULL)) + return 0; + errno = EINVAL; + return -1; + } + else + { + /* h is a socket. */ + int v = value; + return ioctl (desc, FIONBIO, &v); + } + } + else + { + /* The native Windows API does not support non-blocking on regular + files. */ + if (!value) + return 0; + errno = ENOTSUP; + return -1; + } +} + +#else +/* Unix API. */ + +# include + +# if GNULIB_defined_O_NONBLOCK +# error Please port nonblocking to your platform +# endif + +/* We don't need the gnulib replacement of fcntl() here. */ +# undef fcntl + +int +get_nonblocking_flag (int desc) +{ + int fcntl_flags; + + fcntl_flags = fcntl (desc, F_GETFL, 0); + if (fcntl_flags < 0) + return -1; + return (fcntl_flags & O_NONBLOCK) != 0; +} + +int +set_nonblocking_flag (int desc, bool value) +{ + int fcntl_flags; + + fcntl_flags = fcntl (desc, F_GETFL, 0); + if (fcntl_flags < 0) + return -1; + if (((fcntl_flags & O_NONBLOCK) != 0) == value) + return 0; + if (value) + fcntl_flags |= O_NONBLOCK; + else + fcntl_flags &= ~O_NONBLOCK; + return fcntl (desc, F_SETFL, fcntl_flags); +} + +#endif diff --git a/gnulib/lib/nonblocking.h b/gnulib/lib/nonblocking.h new file mode 100644 index 000000000..34f9c0628 --- /dev/null +++ b/gnulib/lib/nonblocking.h @@ -0,0 +1,69 @@ +/* Non-blocking I/O for pipe or socket descriptors. + Copyright (C) 2011-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _NONBLOCKING_H +#define _NONBLOCKING_H + +#include + +/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid + blocking the current thread. When non-blocking is enabled: + - A read() call returns -1 with errno set to EAGAIN when no data or EOF + information is immediately available. + - A write() call returns -1 with errno set to EAGAIN when it cannot + transport the requested amount of data (but at most one pipe buffer) + without blocking. + Non-blocking I/O is most useful for character devices, pipes, and sockets. + Whether it also works on regular files and block devices is platform + dependent. + + There are three modern alternatives to non-blocking I/O: + - use select() or poll() followed by read() or write() if the descriptor + is ready, + - call read() or write() in separate threads, + - use interfaces. */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Return 1 if I/O to the descriptor DESC is currently non-blocking, 0 + it is blocking, or -1 with errno set if fd is invalid or blocking + status cannot be determined (such as with sockets on mingw). */ +extern int get_nonblocking_flag (int desc); + +/* Specify the non-blocking flag for the descriptor DESC. + Return 0 upon success, or -1 with errno set upon failure. + The default depends on the presence of the O_NONBLOCK flag for files + or pipes opened with open() or on the presence of the SOCK_NONBLOCK + flag for sockets. */ +extern int set_nonblocking_flag (int desc, bool value); + + +#ifdef __cplusplus +} +#endif + +#endif /* _NONBLOCKING_H */ diff --git a/gnulib/lib/safe-read.c b/gnulib/lib/safe-read.c new file mode 100644 index 000000000..ffabb94b7 --- /dev/null +++ b/gnulib/lib/safe-read.c @@ -0,0 +1,79 @@ +/* An interface to read and write that retries after interrupts. + + Copyright (C) 1993-1994, 1998, 2002-2006, 2009-2021 Free Software + Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/* Specification. */ +#ifdef SAFE_WRITE +# include "safe-write.h" +#else +# include "safe-read.h" +#endif + +/* Get ssize_t. */ +#include +#include +#include + +#include + +#ifdef EINTR +# define IS_EINTR(x) ((x) == EINTR) +#else +# define IS_EINTR(x) 0 +#endif + +enum { SYS_BUFSIZE_MAX = INT_MAX >> 20 << 20 }; + +#ifdef SAFE_WRITE +# define safe_rw safe_write +# define rw write +#else +# define safe_rw safe_read +# define rw read +# undef const +# define const /* empty */ +#endif + +/* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if + interrupted. Return the actual number of bytes read(written), zero for EOF, + or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */ +size_t +safe_rw (int fd, void const *buf, size_t count) +{ + for (;;) + { + ssize_t result = rw (fd, buf, count); + + if (0 <= result) + return result; + else if (IS_EINTR (errno)) + continue; + else if (errno == EINVAL && SYS_BUFSIZE_MAX < count) + count = SYS_BUFSIZE_MAX; + else + return result; + } +} diff --git a/gnulib/lib/safe-read.h b/gnulib/lib/safe-read.h new file mode 100644 index 000000000..085bcb118 --- /dev/null +++ b/gnulib/lib/safe-read.h @@ -0,0 +1,54 @@ +/* An interface to read() that retries after interrupts. + Copyright (C) 2002, 2006, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Some system calls may be interrupted and fail with errno = EINTR in the + following situations: + - The process is stopped and restarted (signal SIGSTOP and SIGCONT, user + types Ctrl-Z) on some platforms: Mac OS X. + - The process receives a signal for which a signal handler was installed + with sigaction() with an sa_flags field that does not contain + SA_RESTART. + - The process receives a signal for which a signal handler was installed + with signal() and for which no call to siginterrupt(sig,0) was done, + on some platforms: AIX, HP-UX, IRIX, OSF/1, Solaris. + + This module provides a wrapper around read() that handles EINTR. */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SAFE_READ_ERROR ((size_t) -1) + +/* Read up to COUNT bytes at BUF from descriptor FD, retrying if interrupted. + Return the actual number of bytes read, zero for EOF, or SAFE_READ_ERROR + upon error. */ +extern size_t safe_read (int fd, void *buf, size_t count); + + +#ifdef __cplusplus +} +#endif diff --git a/gnulib/lib/safe-write.c b/gnulib/lib/safe-write.c new file mode 100644 index 000000000..20148a16a --- /dev/null +++ b/gnulib/lib/safe-write.c @@ -0,0 +1,25 @@ +/* An interface to write that retries after interrupts. + Copyright (C) 2002, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define SAFE_WRITE +#include "safe-read.c" diff --git a/gnulib/lib/safe-write.h b/gnulib/lib/safe-write.h new file mode 100644 index 000000000..9adba907a --- /dev/null +++ b/gnulib/lib/safe-write.h @@ -0,0 +1,44 @@ +/* An interface to write() that retries after interrupts. + Copyright (C) 2002, 2009-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Some system calls may be interrupted and fail with errno = EINTR in the + following situations: + - The process is stopped and restarted (signal SIGSTOP and SIGCONT, user + types Ctrl-Z) on some platforms: Mac OS X. + - The process receives a signal for which a signal handler was installed + with sigaction() with an sa_flags field that does not contain + SA_RESTART. + - The process receives a signal for which a signal handler was installed + with signal() and for which no call to siginterrupt(sig,0) was done, + on some platforms: AIX, HP-UX, IRIX, OSF/1, Solaris. + + This module provides a wrapper around write() that handles EINTR. */ + +#include + +#define SAFE_WRITE_ERROR ((size_t) -1) + +/* Write up to COUNT bytes at BUF to descriptor FD, retrying if interrupted. + Return the actual number of bytes written, zero for EOF, or SAFE_WRITE_ERROR + upon error. */ +extern size_t safe_write (int fd, const void *buf, size_t count); diff --git a/gnulib/lib/xalloc-oversized.h b/gnulib/lib/xalloc-oversized.h new file mode 100755 index 000000000..1e6acce84 --- /dev/null +++ b/gnulib/lib/xalloc-oversized.h @@ -0,0 +1,67 @@ +/* xalloc-oversized.h -- memory allocation size checking + + Copyright (C) 1990-2000, 2003-2004, 2006-2021 Free Software Foundation, Inc. + + (NB: I modified the original GPL boilerplate here to LGPLv2+. This + is because of the weird way that gnulib uses licenses, where the + real license is covered in the modules/X file. The real license + for this file is LGPLv2+, not GPL. - RWMJ) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XALLOC_OVERSIZED_H_ +#define XALLOC_OVERSIZED_H_ + +#include +#include + +/* True if N * S would overflow in a size_t calculation, + or would generate a value larger than PTRDIFF_MAX. + This expands to a constant expression if N and S are both constants. + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative size_t-based dividend to use here + is SIZE_MAX - 1. */ +#define __xalloc_oversized(n, s) \ + ((size_t) (PTRDIFF_MAX < SIZE_MAX ? PTRDIFF_MAX : SIZE_MAX - 1) / (s) < (n)) + +#if PTRDIFF_MAX < SIZE_MAX +typedef ptrdiff_t __xalloc_count_type; +#else +typedef size_t __xalloc_count_type; +#endif + +/* Return 1 if an array of N objects, each of size S, cannot exist + reliably due to size or ptrdiff_t arithmetic overflow. S must be + positive and N must be nonnegative. This is a macro, not a + function, so that it works correctly even when SIZE_MAX < N. */ + +#if 7 <= __GNUC__ && !defined __clang__ +# define xalloc_oversized(n, s) \ + __builtin_mul_overflow_p (n, s, (__xalloc_count_type) 1) +#elif 5 <= __GNUC__ && !defined __ICC && !__STRICT_ANSI__ +# define xalloc_oversized(n, s) \ + (__builtin_constant_p (n) && __builtin_constant_p (s) \ + ? __xalloc_oversized (n, s) \ + : ({ __xalloc_count_type __xalloc_count; \ + __builtin_mul_overflow (n, s, &__xalloc_count); })) + +/* Other compilers use integer division; this may be slower but is + more portable. */ +#else +# define xalloc_oversized(n, s) __xalloc_oversized (n, s) +#endif + +#endif /* !XALLOC_OVERSIZED_H_ */ diff --git a/gnulib/lib/xstrtol.c b/gnulib/lib/xstrtol.c new file mode 100644 index 000000000..ee2f2caee --- /dev/null +++ b/gnulib/lib/xstrtol.c @@ -0,0 +1,236 @@ +/* A more useful interface to strtol. + + Copyright (C) 1995-1996, 1998-2001, 2003-2007, 2009-2021 Free Software + Foundation, Inc. + + This program is free software: you can 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 . */ + +/* Written by Jim Meyering. */ + +#ifndef __strtol +# define __strtol strtol +# define __strtol_t long int +# define __xstrtol xstrtol +# define STRTOL_T_MINIMUM LONG_MIN +# define STRTOL_T_MAXIMUM LONG_MAX +#endif + +#include + +#include "xstrtol.h" + +/* Some pre-ANSI implementations (e.g. SunOS 4) + need stderr defined if assertion checking is enabled. */ +#include + +#include +#include +#include +#include +#include +#include + +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +static strtol_error +bkm_scale (__strtol_t *x, int scale_factor) +{ + if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor) + { + *x = STRTOL_T_MINIMUM; + return LONGINT_OVERFLOW; + } + if (STRTOL_T_MAXIMUM / scale_factor < *x) + { + *x = STRTOL_T_MAXIMUM; + return LONGINT_OVERFLOW; + } + *x *= scale_factor; + return LONGINT_OK; +} + +static strtol_error +bkm_scale_by_power (__strtol_t *x, int base, int power) +{ + strtol_error err = LONGINT_OK; + while (power--) + err |= bkm_scale (x, base); + return err; +} + +/* FIXME: comment. */ + +strtol_error +__xstrtol (const char *s, char **ptr, int strtol_base, + __strtol_t *val, const char *valid_suffixes) +{ + char *t_ptr; + char **p; + __strtol_t tmp; + strtol_error err = LONGINT_OK; + + assert (0 <= strtol_base && strtol_base <= 36); + + p = (ptr ? ptr : &t_ptr); + + errno = 0; + + if (! TYPE_SIGNED (__strtol_t)) + { + const char *q = s; + unsigned char ch = *q; + while (isspace (ch)) + ch = *++q; + if (ch == '-') + return LONGINT_INVALID; + } + + tmp = __strtol (s, p, strtol_base); + + if (*p == s) + { + /* If there is no number but there is a valid suffix, assume the + number is 1. The string is invalid otherwise. */ + if (valid_suffixes && **p && strchr (valid_suffixes, **p)) + tmp = 1; + else + return LONGINT_INVALID; + } + else if (errno != 0) + { + if (errno != ERANGE) + return LONGINT_INVALID; + err = LONGINT_OVERFLOW; + } + + /* Let valid_suffixes == NULL mean "allow any suffix". */ + /* FIXME: update all callers except the ones that allow suffixes + after the number, changing last parameter NULL to "". */ + if (!valid_suffixes) + { + *val = tmp; + return err; + } + + if (**p != '\0') + { + int base = 1024; + int suffixes = 1; + strtol_error overflow; + + if (!strchr (valid_suffixes, **p)) + { + *val = tmp; + return err | LONGINT_INVALID_SUFFIX_CHAR; + } + + switch (**p) + { + case 'E': case 'G': case 'g': case 'k': case 'K': case 'M': case 'm': + case 'P': case 'T': case 't': case 'Y': case 'Z': + + /* The "valid suffix" '0' is a special flag meaning that + an optional second suffix is allowed, which can change + the base. A suffix "B" (e.g. "100MB") stands for a power + of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for + a power of 1024. If no suffix (e.g. "100M"), assume + power-of-1024. */ + + if (strchr (valid_suffixes, '0')) + switch (p[0][1]) + { + case 'i': + if (p[0][2] == 'B') + suffixes += 2; + break; + + case 'B': + case 'D': /* 'D' is obsolescent */ + base = 1000; + suffixes++; + break; + } + } + + switch (**p) + { + case 'b': + overflow = bkm_scale (&tmp, 512); + break; + + case 'B': + /* This obsolescent first suffix is distinct from the 'B' + second suffix above. E.g., 'tar -L 1000B' means change + the tape after writing 1000 KiB of data. */ + overflow = bkm_scale (&tmp, 1024); + break; + + case 'c': + overflow = LONGINT_OK; + break; + + case 'E': /* exa or exbi */ + overflow = bkm_scale_by_power (&tmp, base, 6); + break; + + case 'G': /* giga or gibi */ + case 'g': /* 'g' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 3); + break; + + case 'k': /* kilo */ + case 'K': /* kibi */ + overflow = bkm_scale_by_power (&tmp, base, 1); + break; + + case 'M': /* mega or mebi */ + case 'm': /* 'm' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 2); + break; + + case 'P': /* peta or pebi */ + overflow = bkm_scale_by_power (&tmp, base, 5); + break; + + case 'T': /* tera or tebi */ + case 't': /* 't' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 4); + break; + + case 'w': + overflow = bkm_scale (&tmp, 2); + break; + + case 'Y': /* yotta or 2**80 */ + overflow = bkm_scale_by_power (&tmp, base, 8); + break; + + case 'Z': /* zetta or 2**70 */ + overflow = bkm_scale_by_power (&tmp, base, 7); + break; + + default: + *val = tmp; + return err | LONGINT_INVALID_SUFFIX_CHAR; + } + + err |= overflow; + *p += suffixes; + if (**p) + err |= LONGINT_INVALID_SUFFIX_CHAR; + } + + *val = tmp; + return err; +} diff --git a/gnulib/lib/xstrtol.h b/gnulib/lib/xstrtol.h new file mode 100644 index 000000000..b873033f4 --- /dev/null +++ b/gnulib/lib/xstrtol.h @@ -0,0 +1,50 @@ +/* A more useful interface to strtol. + + Copyright (C) 1995-1996, 1998-1999, 2001-2004, 2006-2021 Free Software + Foundation, Inc. + + This program is free software: you can 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 . */ + +#ifndef XSTRTOL_H_ +# define XSTRTOL_H_ 1 + +# include + +# ifndef _STRTOL_ERROR +enum strtol_error + { + LONGINT_OK = 0, + + /* These two values can be ORed together, to indicate that both + errors occurred. */ + LONGINT_OVERFLOW = 1, + LONGINT_INVALID_SUFFIX_CHAR = 2, + + LONGINT_INVALID_SUFFIX_CHAR_WITH_OVERFLOW = (LONGINT_INVALID_SUFFIX_CHAR + | LONGINT_OVERFLOW), + LONGINT_INVALID = 4 + }; +typedef enum strtol_error strtol_error; +# endif + +# define _DECLARE_XSTRTOL(name, type) \ + strtol_error name (const char *, char **, int, type *, const char *); +_DECLARE_XSTRTOL (xstrtol, long int) +_DECLARE_XSTRTOL (xstrtoul, unsigned long int) +_DECLARE_XSTRTOL (xstrtoll, long long int) +_DECLARE_XSTRTOL (xstrtoull, unsigned long long int) +_DECLARE_XSTRTOL (xstrtoimax, intmax_t) +_DECLARE_XSTRTOL (xstrtoumax, uintmax_t) + +#endif /* not XSTRTOL_H_ */ diff --git a/gnulib/lib/xstrtoll.c b/gnulib/lib/xstrtoll.c new file mode 100644 index 000000000..0e5572782 --- /dev/null +++ b/gnulib/lib/xstrtoll.c @@ -0,0 +1,10 @@ +/* Note the license of this file is "GPL". It is used in the daemon + * and in guestfish which have a compatible license. + */ + +#define __strtol strtoll +#define __strtol_t long long int +#define __xstrtol xstrtoll +#define STRTOL_T_MINIMUM LLONG_MIN +#define STRTOL_T_MAXIMUM LLONG_MAX +#include "xstrtol.c" diff --git a/gnulib/lib/xstrtoul.c b/gnulib/lib/xstrtoul.c new file mode 100644 index 000000000..285f7b96f --- /dev/null +++ b/gnulib/lib/xstrtoul.c @@ -0,0 +1,6 @@ +#define __strtol strtoul +#define __strtol_t unsigned long int +#define __xstrtol xstrtoul +#define STRTOL_T_MINIMUM 0 +#define STRTOL_T_MAXIMUM ULONG_MAX +#include "xstrtol.c" diff --git a/gnulib/lib/xstrtoull.c b/gnulib/lib/xstrtoull.c new file mode 100644 index 000000000..10dda5044 --- /dev/null +++ b/gnulib/lib/xstrtoull.c @@ -0,0 +1,6 @@ +#define __strtol strtoull +#define __strtol_t unsigned long long int +#define __xstrtol xstrtoull +#define STRTOL_T_MINIMUM 0 +#define STRTOL_T_MAXIMUM ULLONG_MAX +#include "xstrtol.c" diff --git a/gnulib/lib/xstrtoumax.c b/gnulib/lib/xstrtoumax.c new file mode 100644 index 000000000..9a2349f00 --- /dev/null +++ b/gnulib/lib/xstrtoumax.c @@ -0,0 +1,6 @@ +#define __strtol strtoumax +#define __strtol_t uintmax_t +#define __xstrtol xstrtoumax +#define STRTOL_T_MINIMUM 0 +#define STRTOL_T_MAXIMUM UINTMAX_MAX +#include "xstrtol.c" diff --git a/m4/.gitignore b/m4/.gitignore deleted file mode 100644 index 854212db7..000000000 --- a/m4/.gitignore +++ /dev/null @@ -1,251 +0,0 @@ -/00gnulib.m4 -/absolute-header.m4 -/accept4.m4 -/access.m4 -/alloca.m4 -/arpa_inet_h.m4 -/asm-underscore.m4 -/base64.m4 -/btowc.m4 -/builtin-expect.m4 -/byteswap.m4 -/chdir-long.m4 -/clock_time.m4 -/closedir.m4 -/close.m4 -/codeset.m4 -/creat.m4 -/ctype.m4 -/dirent_h.m4 -/dirfd.m4 -/double-slash-root.m4 -/d-type.m4 -/dup2.m4 -/dup.m4 -/eaccess.m4 -/eealloc.m4 -/environ.m4 -/errno_h.m4 -/error.m4 -/exponentd.m4 -/extensions.m4 -/extern-inline.m4 -/fatal-signal.m4 -/fchdir.m4 -/fcntl_h.m4 -/fcntl.m4 -/fcntl-o.m4 -/fdopen.m4 -/filenamecat.m4 -/findprog-in.m4 -/flexmember.m4 -/float_h.m4 -/fnmatch_h.m4 -/fnmatch.m4 -/fpending.m4 -/fpieee.m4 -/fstatat.m4 -/fstat.m4 -/ftruncate.m4 -/futimens.m4 -/getcwd.m4 -/getdelim.m4 -/getdtablesize.m4 -/getline.m4 -/getlogin.m4 -/getlogin_r.m4 -/getopt.m4 -/getpagesize.m4 -/getprogname.m4 -/getrandom.m4 -/gettime.m4 -/gettimeofday.m4 -/glob_h.m4 -/glob.m4 -/gnulib-common.m4 -/gnulib-comp.m4 -/gnulib-tool.m4 -/human.m4 -/include_next.m4 -/inet_pton.m4 -/__inline.m4 -/intlmacosx.m4 -/intl-thread-locale.m4 -/intmax_t.m4 -/inttostr.m4 -/inttypes_h.m4 -/inttypes.m4 -/ioctl.m4 -/isblank.m4 -/langinfo_h.m4 -/largefile.m4 -/lcmessage.m4 -/limits-h.m4 -/localcharset.m4 -/localeconv.m4 -/locale-fr.m4 -/locale_h.m4 -/locale-ja.m4 -/localename.m4 -/locale-tr.m4 -/locale-zh.m4 -/lock.m4 -/lseek.m4 -/lstat.m4 -/malloca.m4 -/malloc.m4 -/manywarnings-c++.m4 -/manywarnings.m4 -/math_h.m4 -/mbrtowc.m4 -/mbsinit.m4 -/mbsrtowcs.m4 -/mbstate_t.m4 -/mbtowc.m4 -/memchr.m4 -/memmem.m4 -/mempcpy.m4 -/memrchr.m4 -/minmax.m4 -/mkdir.m4 -/mkdtemp.m4 -/mkstemps.m4 -/mmap-anon.m4 -/mode_t.m4 -/msvc-inval.m4 -/msvc-nothrow.m4 -/multiarch.m4 -/musl.m4 -/nanosleep.m4 -/netdb_h.m4 -/netinet_in_h.m4 -/nocrash.m4 -/nonblocking.m4 -/off_t.m4 -/openat.m4 -/open-cloexec.m4 -/opendir.m4 -/open.m4 -/open-slash.m4 -/pathmax.m4 -/perror.m4 -/pid_t.m4 -/pipe2.m4 -/pipe.m4 -/posix_spawn.m4 -/pread.m4 -/printf.m4 -/priv-set.m4 -/pthread_h.m4 -/pthread_rwlock_rdlock.m4 -/pthread_sigmask.m4 -/pthread-thread.m4 -/putenv.m4 -/quotearg.m4 -/quote.m4 -/raise.m4 -/rawmemchr.m4 -/readdir.m4 -/readlinkat.m4 -/readlink.m4 -/read.m4 -/realloc.m4 -/rmdir.m4 -/safe-read.m4 -/safe-write.m4 -/save-cwd.m4 -/sched_h.m4 -/select.m4 -/semaphore.m4 -/setenv.m4 -/setlocale.m4 -/setlocale_null.m4 -/sh-filename.m4 -/sigaction.m4 -/sig_atomic_t.m4 -/signalblocking.m4 -/signal_h.m4 -/size_max.m4 -/sleep.m4 -/snprintf.m4 -/socketlib.m4 -/sockets.m4 -/socklen.m4 -/sockpfaf.m4 -/spawn_h.m4 -/ssize_t.m4 -/stat.m4 -/stat-time.m4 -/stdalign.m4 -/stdarg.m4 -/stdbool.m4 -/stddef_h.m4 -/std-gnu11.m4 -/stdint_h.m4 -/stdint.m4 -/stdio_h.m4 -/stdlib_h.m4 -/stpcpy.m4 -/strchrnul.m4 -/strdup.m4 -/strerror.m4 -/strerror_r.m4 -/string_h.m4 -/strndup.m4 -/strnlen.m4 -/strtoll.m4 -/strtoull.m4 -/strtoumax.m4 -/symlinkat.m4 -/symlink.m4 -/sys_ioctl_h.m4 -/sys_random_h.m4 -/sys_select_h.m4 -/sys_socket_h.m4 -/sys_stat_h.m4 -/sys_time_h.m4 -/sys_types_h.m4 -/sys_uio_h.m4 -/sys_wait_h.m4 -/tempname.m4 -/threadlib.m4 -/thread.m4 -/time_h.m4 -/timespec.m4 -/tls.m4 -/unistd_h.m4 -/unistd-safer.m4 -/unlinkat.m4 -/unlinkdir.m4 -/unlink.m4 -/usleep.m4 -/utimecmp.m4 -/utime_h.m4 -/utime.m4 -/utimensat.m4 -/utimens.m4 -/utimes.m4 -/vasnprintf.m4 -/vasprintf.m4 -/visibility.m4 -/vsnprintf.m4 -/waitpid.m4 -/wait-process.m4 -/warnings.m4 -/warn-on-use.m4 -/wchar_h.m4 -/wchar_t.m4 -/wcrtomb.m4 -/wctob.m4 -/wctomb.m4 -/wctype_h.m4 -/wint_t.m4 -/wmemchr.m4 -/wmempcpy.m4 -/write.m4 -/xalloc.m4 -/xsize.m4 -/xstrtol.m4 -/xvasprintf.m4 -/yield.m4 -/zzgnulib.m4 diff --git a/m4/guestfs-c.m4 b/m4/guestfs-c.m4 index bbb4db464..08fd4e1b5 100644 --- a/m4/guestfs-c.m4 +++ b/m4/guestfs-c.m4 @@ -24,115 +24,28 @@ AC_PROG_CC_STDC AC_PROG_INSTALL AC_PROG_CPP -AC_ARG_ENABLE([werror], - [AS_HELP_STRING([--enable-werror], - [turn GCC warnings into errors (for developers)])], - [case $enableval in - yes|no) ;; - *) AC_MSG_ERROR([bad value $enableval for werror option]) ;; - esac - gl_gcc_werror=$enableval], - [gl_gcc_werror=no] -) - -if test "$gl_gcc_werror" = yes; then - gl_WARN_ADD([-Werror], [WERROR_CFLAGS]) - AC_SUBST([WERROR_CFLAGS]) -fi - -dnl This, $nw, is the list of warnings we disable. -nw= -nw="$nw -Waggregate-return" # anachronistic -nw="$nw -Wundef" # Warns on '#if GNULIB_FOO' etc in gnulib -nw="$nw -Wtraditional" # Warns on #elif which we use often -nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings -nw="$nw -Wpadded" # Our structs are not padded -nw="$nw -Wvla" # Allow variable length arrays. -nw="$nw -Wvla-larger-than=4031" -nw="$nw -Winline" # inline functions in Python binding -nw="$nw -Wshadow" # Not useful, as it applies to global vars -nw="$nw -Wunsafe-loop-optimizations" # just a warning that an optimization - # was not possible, safe to ignore -nw="$nw -Wstack-protector" # Useless warning when stack protector - # cannot being used in a function. -nw="$nw -Wcast-align" # Useless warning on arm >= 7, intel -nw="$nw -Wabi" # Broken in GCC 8.1. -dnl things I might fix soon: -nw="$nw -Wpacked" # Allow attribute((packed)) on structs -nw="$nw -Wlong-long" # Allow long long since it's required - # by Python, Ruby and xstrtoll. -nw="$nw -Wsuggest-attribute=pure" # Don't suggest pure functions. -nw="$nw -Wsuggest-attribute=const" # Don't suggest const functions. -nw="$nw -Wsuggest-attribute=malloc" # Don't suggest malloc functions. -nw="$nw -Wunsuffixed-float-constants" # Don't care about these. -nw="$nw -Wswitch-default" # This warning is actively dangerous. -nw="$nw -Woverlength-strings" # Who cares about stupid ISO C99 limit. - -gl_MANYWARN_ALL_GCC([ws]) -gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw]) -for w in $ws; do - gl_WARN_ADD([$w]) -done - -dnl Normally we disable warnings in $nw above. However $nw only -dnl filters out exact matching warning strings from a list inside -dnl gnulib (see m4/manywarnings.m4). So we need to explicitly list a -dnl few disabled warnings below. - -dnl Unused parameters are not a bug. -gl_WARN_ADD([-Wno-unused-parameter]) - -dnl Missing field initializers is not a bug in C. -gl_WARN_ADD([-Wno-missing-field-initializers]) - -dnl Display the name of the warning option with the warning. -gl_WARN_ADD([-fdiagnostics-show-option]) - -dnl Now some warnings we want to enable and/or customize ... - -dnl Warn about large stack frames. This does not include alloca and -dnl variable length arrays. Coverity warns about 10000 byte frames. -gl_WARN_ADD([-Wframe-larger-than=6000]) - -dnl Warn about large stack frames, including estimates for alloca -dnl and variable length arrays. -gl_WARN_ADD([-Wstack-usage=10000]) - -dnl Warn about implicit fallthrough in case statements, but suppress -dnl the warning if /*FALLTHROUGH*/ comment is used. -gl_WARN_ADD([-Wimplicit-fallthrough=4]) - -dnl GCC level 2 gives incorrect warnings, so use level 1. -gl_WARN_ADD([-Wformat-truncation=1]) - -dnl GCC 9 at level 2 gives apparently bogus errors when %.*s is used. -gl_WARN_ADD([-Wformat-overflow=1]) - -dnl GCC < 11 gives warnings when disabling GCC 11 warnings. -gl_WARN_ADD([-Wno-pragmas]) - -AC_SUBST([WARN_CFLAGS]) - -NO_SNV_CFLAGS= -gl_COMPILER_OPTION_IF([-Wno-shift-negative-value],[ - NO_SNV_CFLAGS="-Wno-shift-negative-value" -]) -AC_SUBST([NO_SNV_CFLAGS]) - -NO_UM_CFLAGS= -gl_COMPILER_OPTION_IF([-Wno-unused-macros],[ - NO_UM_CFLAGS="-Wno-unused-macros" -]) -AC_SUBST([NO_UM_CFLAGS]) - -AC_DEFINE([lint], [1], [Define to 1 if the compiler is checking for lint.]) -AC_DEFINE([GNULIB_PORTCHECK], [1], [Enable some gnulib portability checks.]) - AC_C_PROTOTYPES test "x$U" != "x" && AC_MSG_ERROR([Compiler not ANSI compliant]) AM_PROG_CC_C_O +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--enable-error], + [turn on lots of GCC warnings (for developers)])], + [case $enableval in + yes|no) ;; + *) AC_MSG_ERROR([bad value $enableval for werror option]) ;; + esac + gcc_warnings=$enableval], + [gcc_warnings=no] +) +WARN_CFLAGS="-Wall" +AC_SUBST([WARN_CFLAGS]) +if test "x$gcc_warnings" = "xyes"; then + WERROR_CFLAGS="-Werror" +fi +AC_SUBST([WERROR_CFLAGS]) + # Provide a global place to set CFLAGS. (Note that setting AM_CFLAGS # is no use because it doesn't override target_CFLAGS). #--- @@ -205,16 +118,6 @@ resources when it runs.])]) dnl restore CFLAGS CFLAGS="${acx_nbdkit_save_CFLAGS}" -dnl Should we run the gnulib tests? -AC_MSG_CHECKING([if we should run the GNUlib tests]) -AC_ARG_ENABLE([gnulib-tests], - [AS_HELP_STRING([--disable-gnulib-tests], - [disable running GNU Portability library tests @<:@default=yes@:>@])], - [ENABLE_GNULIB_TESTS="$enableval"], - [ENABLE_GNULIB_TESTS=yes]) -AM_CONDITIONAL([ENABLE_GNULIB_TESTS],[test "x$ENABLE_GNULIB_TESTS" = "xyes"]) -AC_MSG_RESULT([$ENABLE_GNULIB_TESTS]) - dnl Define this so that include/guestfs.h is included dnl instead of the possibly installed . This is dnl only needed when compiling libguestfs itself. It is diff --git a/rescue/rescue.c b/rescue/rescue.c index c2e4e5828..048408f33 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,6 @@ #include "getprogname.h" #include "ignore-value.h" #include "nonblocking.h" -#include "xvasprintf.h" #include "guestfs.h" #include "guestfs-utils.h" @@ -364,9 +364,12 @@ main (int argc, char *argv[]) /* Kernel command line must include guestfs_rescue=1 (see * appliance/init) as well as other options. */ - append_full = xasprintf ("guestfs_rescue=1%s%s", - append ? " " : "", - append ? append : ""); + if (asprintf (&append_full, "guestfs_rescue=1%s%s", + append ? " " : "", + append ? append : "") == -1) { + perror ("asprintf"); + exit (EXIT_FAILURE); + } if (guestfs_set_append (g, append_full) == -1) exit (EXIT_FAILURE); free (append_full);