summaryrefslogtreecommitdiffstats
path: root/lib/sh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sh')
-rw-r--r--lib/sh/Makefile.in657
-rw-r--r--lib/sh/casemod.c271
-rw-r--r--lib/sh/clktck.c61
-rw-r--r--lib/sh/clock.c87
-rw-r--r--lib/sh/dprintf.c70
-rw-r--r--lib/sh/eaccess.c244
-rw-r--r--lib/sh/fmtullong.c31
-rw-r--r--lib/sh/fmtulong.c191
-rw-r--r--lib/sh/fmtumax.c27
-rw-r--r--lib/sh/fnxform.c199
-rw-r--r--lib/sh/fpurge.c232
-rw-r--r--lib/sh/getcwd.c356
-rw-r--r--lib/sh/getenv.c233
-rw-r--r--lib/sh/gettimeofday.c35
-rw-r--r--lib/sh/inet_aton.c214
-rw-r--r--lib/sh/input_avail.c165
-rw-r--r--lib/sh/itos.c84
-rw-r--r--lib/sh/mailstat.c159
-rw-r--r--lib/sh/makepath.c128
-rw-r--r--lib/sh/mbscasecmp.c79
-rw-r--r--lib/sh/mbschr.c91
-rw-r--r--lib/sh/mbscmp.c77
-rw-r--r--lib/sh/memset.c29
-rw-r--r--lib/sh/mktime.c438
-rw-r--r--lib/sh/netconn.c82
-rw-r--r--lib/sh/netopen.c351
-rw-r--r--lib/sh/oslib.c301
-rw-r--r--lib/sh/pathcanon.c234
-rw-r--r--lib/sh/pathphys.c296
-rw-r--r--lib/sh/random.c240
-rw-r--r--lib/sh/rename.c76
-rw-r--r--lib/sh/setlinebuf.c66
-rw-r--r--lib/sh/shmatch.c132
-rw-r--r--lib/sh/shmbchar.c137
-rw-r--r--lib/sh/shquote.c432
-rw-r--r--lib/sh/shtty.c330
-rw-r--r--lib/sh/snprintf.c2221
-rw-r--r--lib/sh/spell.c212
-rw-r--r--lib/sh/strcasecmp.c84
-rw-r--r--lib/sh/strcasestr.c46
-rw-r--r--lib/sh/strchrnul.c35
-rw-r--r--lib/sh/strdup.c41
-rw-r--r--lib/sh/strerror.c74
-rw-r--r--lib/sh/strftime.c1012
-rw-r--r--lib/sh/stringlist.c297
-rw-r--r--lib/sh/stringvec.c272
-rw-r--r--lib/sh/strnlen.c49
-rw-r--r--lib/sh/strpbrk.c49
-rw-r--r--lib/sh/strstr.c125
-rw-r--r--lib/sh/strtod.c207
-rw-r--r--lib/sh/strtoimax.c113
-rw-r--r--lib/sh/strtol.c259
-rw-r--r--lib/sh/strtoll.c30
-rw-r--r--lib/sh/strtoul.c30
-rw-r--r--lib/sh/strtoull.c31
-rw-r--r--lib/sh/strtoumax.c113
-rw-r--r--lib/sh/strtrans.c400
-rw-r--r--lib/sh/strvis.c154
-rw-r--r--lib/sh/timers.c262
-rw-r--r--lib/sh/times.c77
-rw-r--r--lib/sh/timeval.c179
-rw-r--r--lib/sh/tmpfile.c311
-rw-r--r--lib/sh/uconvert.c124
-rw-r--r--lib/sh/ufuncs.c140
-rw-r--r--lib/sh/unicode.c339
-rw-r--r--lib/sh/utf8.c196
-rw-r--r--lib/sh/vprint.c85
-rw-r--r--lib/sh/wcsdup.c44
-rw-r--r--lib/sh/wcsnwidth.c56
-rw-r--r--lib/sh/wcswidth.c46
-rw-r--r--lib/sh/winsize.c104
-rw-r--r--lib/sh/zcatfd.c74
-rw-r--r--lib/sh/zgetline.c126
-rw-r--r--lib/sh/zmapfd.c93
-rw-r--r--lib/sh/zread.c228
-rw-r--r--lib/sh/zwrite.c64
76 files changed, 15237 insertions, 0 deletions
diff --git a/lib/sh/Makefile.in b/lib/sh/Makefile.in
new file mode 100644
index 0000000..8c42c73
--- /dev/null
+++ b/lib/sh/Makefile.in
@@ -0,0 +1,657 @@
+#
+# Makefile for the Bash library
+#
+#
+# Copyright (C) 1998-2022 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+LIBBUILD = ${BUILD_DIR}/lib
+
+BASHINCDIR = ${topdir}/include
+
+INTL_LIBSRC = ${topdir}/lib/intl
+INTL_BUILDDIR = ${LIBBUILD}/intl
+INTL_INC = @INTL_INC@
+LIBINTL_H = @LIBINTL_H@
+
+datarootdir = @datarootdir@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm -f
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@ ${DEBUG}
+STYLE_CFLAGS = @STYLE_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@
+
+PROFILE_FLAGS = @PROFILE_FLAGS@
+
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+INCLUDES = -I. -I../.. -I$(topdir) -I$(topdir)/lib -I$(BASHINCDIR) -I$(srcdir) $(INTL_INC)
+
+CCFLAGS = ${ADDON_CFLAGS} ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) \
+ $(LOCAL_CFLAGS) $(STYLE_CFLAGS) $(CFLAGS) $(CPPFLAGS)
+
+GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wstrict-prototypes -Wconversion \
+ -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic
+
+.c.o:
+ $(CC) -c $(CCFLAGS) $<
+
+# The name of the library target.
+LIBRARY_NAME = libsh.a
+
+# The C code source files for this library.
+CSOURCES = clktck.c clock.c getcwd.c getenv.c oslib.c setlinebuf.c \
+ strcasecmp.c strerror.c strtod.c strtol.c strtoul.c \
+ vprint.c itos.c rename.c zread.c zwrite.c shtty.c \
+ inet_aton.c netconn.c netopen.c strpbrk.c timeval.c makepath.c \
+ pathcanon.c pathphys.c tmpfile.c stringlist.c stringvec.c spell.c \
+ shquote.c strtrans.c strcasestr.c snprintf.c mailstat.c \
+ fmtulong.c fmtullong.c fmtumax.c shmatch.c strnlen.c \
+ strtoll.c strtoull.c strtoimax.c strtoumax.c memset.c strstr.c \
+ mktime.c strftime.c mbschr.c zcatfd.c zmapfd.c winsize.c eaccess.c \
+ wcsdup.c fpurge.c zgetline.c mbscmp.c uconvert.c ufuncs.c \
+ casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \
+ strchrnul.c unicode.c wcswidth.c wcsnwidth.c shmbchar.c strdup.c \
+ strvis.c utf8.c random.c gettimeofday.c timers.c
+
+# The header files for this library.
+HSOURCES =
+
+# The object files contained in $(LIBRARY_NAME)
+LIBOBJS = @LIBOBJS@
+OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \
+ itos.o zread.o zwrite.o shtty.o shmatch.o eaccess.o \
+ netconn.o netopen.o timeval.o makepath.o pathcanon.o \
+ pathphys.o tmpfile.o stringlist.o stringvec.o spell.o shquote.o \
+ strtrans.o snprintf.o mailstat.o fmtulong.o \
+ fmtullong.o fmtumax.o zcatfd.o zmapfd.o winsize.o wcsdup.o \
+ fpurge.o zgetline.o mbscmp.o uconvert.o ufuncs.o casemod.o \
+ input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o strvis.o \
+ utf8.o random.o gettimeofday.o timers.o wcsnwidth.o ${LIBOBJS}
+
+SUPPORT = Makefile
+
+all: $(LIBRARY_NAME)
+
+$(LIBRARY_NAME): $(OBJECTS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+force:
+
+# The rule for 'includes' is written funny so that the if statement
+# always returns TRUE unless there really was an error installing the
+# include files.
+install:
+
+clean:
+ $(RM) $(OBJECTS) $(LIBRARY_NAME)
+
+realclean distclean maintainer-clean: clean
+ $(RM) Makefile
+
+mostlyclean: clean
+
+# Dependencies
+
+${BUILD_DIR}/version.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile
+ -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} version.h )
+
+${BUILD_DIR}/pathnames.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile
+ -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} pathnames.h )
+
+# rules for losing makes, like SunOS
+casemod.o: casemod.c
+clktck.o: clktck.c
+clock.o: clock.c
+eaccess.o: eaccess.c
+dprintf.o: dprintf.c
+fmtullong.o: fmtullong.c
+fmtulong.o: fmtulong.c
+fmtumax.o: fmtumax.c
+fnxform.o: fnxform.c
+fpurge.o: fpurge.c
+getcwd.o: getcwd.c
+getenv.o: getenv.c
+gettimeofday.o: gettimeofday.c
+inet_aton.o: inet_aton.c
+input_avail.o: input_avail.c
+itos.o: itos.c
+mailstat.o: mailstat.c
+makepath.o: makepath.c
+mbscasecmp.o: mbscasecmp.c
+mbschr.o: mbschr.c
+mbscmp.o: mbscmp.c
+memset.o: memset.c
+mktime.o: mktime.c
+netconn.o: netconn.c
+netopen.o: netopen.c
+oslib.o: oslib.c
+pathcanon.o: pathcanon.c
+pathphys.o: pathphys.c
+random.o: random.c
+rename.o: rename.c
+setlinebuf.o: setlinebuf.c
+shmatch.o: shmatch.c
+shmbchar.o: shmbchar.c
+shquote.o: shquote.c
+shtty.o: shtty.c
+snprintf.o: snprintf.c
+spell.o: spell.c
+strcasecmp.o: strcasecmp.c
+strchrnul.o: strchrnul.c
+strerror.o: strerror.c
+strftime.o: strftime.c
+strcasestr.o: strcasestr.c
+stringlist.o: stringlist.c
+stringvec.o: stringvec.c
+strnlen.o: strnlen.c
+strpbrk.o: strpbrk.c
+strtod.o: strtod.c
+strtoimax.o: strtoimax.c
+strtol.o: strtol.c
+strtoll.o: strtoll.c
+strtoul.o: strtoul.c
+strtoull.o: strtoull.c
+strtoumax.o: strtoumax.c
+strtrans.o: strtrans.c
+strvis.o: strvis.c
+timers.o: timers.c
+times.o: times.c
+timeval.o: timeval.c
+tmpfile.o: tmpfile.c
+uconvert.o: uconvert.c
+ufuncs.o: ufuncs.c
+unicode.o: unicode.c
+utf8.o: utf8.c
+vprint.o: vprint.c
+wcsdup.o: wcsdup.c
+wcsnwidth.o: wcsnwidth.c
+wcswidth.o: wcswidth.c
+winsize.o: winsize.c
+zcatfd.o: zcatfd.c
+zmapfd.o: zmapfd.c
+zgetline.o: zgetline.c
+zread.o: zread.c
+zwrite.o: zwrite.c
+
+# dependencies for c files that include other c files
+fmtullong.o: fmtulong.c
+fmtumax.o: fmtulong.c
+strtoll.o: strtol.c
+strtoul.o: strtol.c
+strtoull.o: strtol.c
+
+# all files in the library depend on config.h
+casemod.o: ${BUILD_DIR}/config.h
+clktck.o: ${BUILD_DIR}/config.h
+clock.o: ${BUILD_DIR}/config.h
+eaccess.o: ${BUILD_DIR}/config.h
+dprintf.o: ${BUILD_DIR}/config.h
+fmtullong.o: ${BUILD_DIR}/config.h
+fmtulong.o: ${BUILD_DIR}/config.h
+fmtumax.o: ${BUILD_DIR}/config.h
+fnxform.o: ${BUILD_DIR}/config.h
+fpurge.o: ${BUILD_DIR}/config.h
+getcwd.o: ${BUILD_DIR}/config.h
+getenv.o: ${BUILD_DIR}/config.h
+gettimeofday.o: ${BUILD_DIR}/config.h
+inet_aton.o: ${BUILD_DIR}/config.h
+input_avail.o: ${BUILD_DIR}/config.h
+itos.o: ${BUILD_DIR}/config.h
+mailstat.o: ${BUILD_DIR}/config.h
+makepath.o: ${BUILD_DIR}/config.h
+mbscasecmp.o: ${BUILD_DIR}/config.h
+mbschr.o: ${BUILD_DIR}/config.h
+mbscmp.o: ${BUILD_DIR}/config.h
+memset.o: ${BUILD_DIR}/config.h
+mktime.o: ${BUILD_DIR}/config.h
+netconn.o: ${BUILD_DIR}/config.h
+netopen.o: ${BUILD_DIR}/config.h
+oslib.o: ${BUILD_DIR}/config.h
+pathcanon.o: ${BUILD_DIR}/config.h
+pathphys.o: ${BUILD_DIR}/config.h
+random.o: ${BUILD_DIR}/config.h
+rename.o: ${BUILD_DIR}/config.h
+setlinebuf.o: ${BUILD_DIR}/config.h
+shmatch.o: ${BUILD_DIR}/config.h
+shmbchar.o: ${BUILD_DIR}/config.h
+shquote.o: ${BUILD_DIR}/config.h
+shtty.o: ${BUILD_DIR}/config.h
+snprintf.o: ${BUILD_DIR}/config.h
+spell.o: ${BUILD_DIR}/config.h
+strcasecmp.o: ${BUILD_DIR}/config.h
+strchrnul.o: ${BUILD_DIR}/config.h
+strerror.o: ${BUILD_DIR}/config.h
+strftime.o: ${BUILD_DIR}/config.h
+strcasestr.o: ${BUILD_DIR}/config.h
+stringlist.o: ${BUILD_DIR}/config.h
+stringvec.o: ${BUILD_DIR}/config.h
+strnlen.o: ${BUILD_DIR}/config.h
+strpbrk.o: ${BUILD_DIR}/config.h
+strtod.o: ${BUILD_DIR}/config.h
+strtoimax.o: ${BUILD_DIR}/config.h
+strtol.o: ${BUILD_DIR}/config.h
+strtoll.o: ${BUILD_DIR}/config.h
+strtoul.o: ${BUILD_DIR}/config.h
+strtoull.o: ${BUILD_DIR}/config.h
+strtoumax.o: ${BUILD_DIR}/config.h
+strtrans.o: ${BUILD_DIR}/config.h
+strvis.o: ${BUILD_DIR}/config.h
+timers.o: ${BUILD_DIR}/config.h
+times.o: ${BUILD_DIR}/config.h
+timeval.o: ${BUILD_DIR}/config.h
+tmpfile.o: ${BUILD_DIR}/config.h ${topdir}/config-top.h
+uconvert.o: ${BUILD_DIR}/config.h
+ufuncs.o: ${BUILD_DIR}/config.h
+unicode.o: ${BUILD_DIR}/config.h
+utf8.o: ${BUILD_DIR}/config.h
+vprint.o: ${BUILD_DIR}/config.h
+wcsdup.o: ${BUILD_DIR}/config.h
+wcsnwidth.o: ${BUILD_DIR}/config.h
+wcswidth.o: ${BUILD_DIR}/config.h
+winsize.o: ${BUILD_DIR}/config.h
+zcatfd.o: ${BUILD_DIR}/config.h
+zgetline.o: ${BUILD_DIR}/config.h
+zmapfd.o: ${BUILD_DIR}/config.h
+zread.o: ${BUILD_DIR}/config.h
+zwrite.o: ${BUILD_DIR}/config.h
+
+clktck.o: ${topdir}/bashtypes.h
+
+getcwd.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h
+getcwd.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/posixdir.h
+getcwd.o: ${BASHINCDIR}/memalloc.h ${BASHINCDIR}/ansi_stdlib.h
+
+getenv.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+getenv.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+getenv.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+getenv.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+getenv.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+getenv.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+getenv.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+getenv.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#getenv.o: ${BUILD_DIR}/version.h
+
+inet_aton.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+inet_aton.o: ${BASHINCDIR}/stdc.h
+
+itos.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+itos.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+itos.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+itos.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+itos.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+itos.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+itos.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+itos.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#itos.o: ${BUILD_DIR}/version.h
+
+makepath.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+makepath.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+makepath.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+makepath.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+makepath.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+makepath.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+makepath.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+makepath.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#makepath.o: ${BUILD_DIR}/version.h
+
+netconn.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
+netconn.o: ${topdir}/bashtypes.h
+
+netopen.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h
+netopen.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+netopen.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+netopen.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+netopen.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+netopen.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+netopen.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+netopen.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+netopen.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+#netopen.o: ${BUILD_DIR}/version.h
+
+oslib.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h
+oslib.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+oslib.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+oslib.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+oslib.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+oslib.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+oslib.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+oslib.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+oslib.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
+oslib.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+#oslib.o: ${BUILD_DIR}/version.h
+
+pathcanon.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h
+pathcanon.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+pathcanon.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+pathcanon.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+pathcanon.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+pathcanon.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+pathcanon.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+pathcanon.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+pathcanon.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
+pathcanon.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+#pathcanon.o: ${BUILD_DIR}/version.h
+
+pathphys.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h
+pathphys.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+pathphys.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+pathphys.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+pathphys.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+pathphys.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+pathphys.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+pathphys.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+pathphys.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
+pathphys.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+#pathphys.o: ${BUILD_DIR}/version.h
+
+random.o: ${topdir}/bashtypes.h ${BASHINCDIR}/stdc.h
+random.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+random.o: ${BASHINCDIR}/filecntl.h
+
+rename.o: ${topdir}/bashtypes.h ${BASHINCDIR}/stdc.h
+rename.o: ${BASHINCDIR}/posixstat.h
+
+setlinebuf.o: ${topdir}/xmalloc.h ${topdir}/bashansi.h
+setlinebuf.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/stdc.h
+
+eaccess.o: ${topdir}/bashtypes.h
+eaccess.o: ${BASHINCDIR}/posixstat.h
+eaccess.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+eaccess.o: ${BASHINCDIR}/filecntl.h
+eaccess.o: ${BASHINCDIR}/stdc.h
+eaccess.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+eaccess.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+eaccess.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+eaccess.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+eaccess.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+eaccess.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+eaccess.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#eaccess.o: ${BUILD_DIR}/version.h
+
+shmatch.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h
+shmatch.o: ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h
+shmatch.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+shmatch.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+shmatch.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+shmatch.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+shmatch.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+shmatch.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+shmatch.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+
+shquote.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h
+shquote.o: ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h
+shquote.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+
+shtty.o: ${BASHINCDIR}/shtty.h
+shtty.o: ${BASHINCDIR}/stdc.h
+
+snprintf.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h ${topdir}/xmalloc.h
+snprintf.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+snprintf.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+snprintf.o: ${BASHINCDIR}/typemax.h
+
+spell.o: ${topdir}/bashtypes.h
+spell.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/posixdir.h
+spell.o: ${BASHINCDIR}/ansi_stdlib.h
+
+strcasecmp.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h
+strcasecmp.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+
+strerror.o: ${topdir}/bashtypes.h
+strerror.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+strerror.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+strerror.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+strerror.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+strerror.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+strerror.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+strerror.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#strerror.o: ${BUILD_DIR}/version.h
+
+strcasestr.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h
+strcasestr.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+
+stringlist.o: ${topdir}/bashansi.h
+stringlist.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+stringlist.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+stringlist.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+stringlist.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+stringlist.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+stringlist.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+stringlist.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#stringlist.o: ${BUILD_DIR}/version.h
+
+stringvec.o: ${topdir}/bashansi.h ${BASHINCDIR}/chartypes.h
+stringvec.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+stringvec.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+stringvec.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+stringvec.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+stringvec.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+stringvec.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+stringvec.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+#stringvec.o: ${BUILD_DIR}/version.h
+
+strnlen.o: ${BASHINCDIR}/stdc.h
+
+strpbrk.o: ${BASHINCDIR}/stdc.h
+
+strtod.o: ${topdir}/bashansi.h
+strtod.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+
+strtoimax.o: ${BASHINCDIR}/stdc.h
+
+strtol.o: ${topdir}/bashansi.h
+strtol.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strtol.o: ${BASHINCDIR}/typemax.h
+
+strtoll.o: ${topdir}/bashansi.h
+strtoll.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strtoll.o: ${BASHINCDIR}/typemax.h
+
+strtoul.o: ${topdir}/bashansi.h
+strtoul.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strtoul.o: ${BASHINCDIR}/typemax.h
+
+strtoull.o: ${topdir}/bashansi.h
+strtoull.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strtoull.o: ${BASHINCDIR}/typemax.h
+
+strtoumax.o: ${BASHINCDIR}/stdc.h
+
+strtrans.o: ${topdir}/bashansi.h
+strtrans.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strtrans.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+strtrans.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+strtrans.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+strtrans.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+strtrans.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+strtrans.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+strtrans.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+strtrans.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+#strtrans.o: ${BUILD_DIR}/version.h
+
+strvis.o: ${topdir}/bashansi.h
+strvis.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strvis.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+strvis.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+
+times.o: ${BASHINCDIR}/systimes.h
+times.o: ${BASHINCDIR}/posixtime.h
+
+timeval.o: ${BASHINCDIR}/posixtime.h
+gettimeofday.o: ${BASHINCDIR}/posixtime.h
+
+tmpfile.o: ${topdir}/bashtypes.h
+tmpfile.o: ${BASHINCDIR}/chartypes.h
+tmpfile.o: ${BASHINCDIR}/posixstat.h
+tmpfile.o: ${BASHINCDIR}/filecntl.h
+tmpfile.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+tmpfile.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+tmpfile.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+tmpfile.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+tmpfile.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+tmpfile.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+tmpfile.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+
+uconvert.o: ${topdir}/bashtypes.h
+uconvert.o: ${BASHINCDIR}/chartypes.h
+uconvert.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+uconvert.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
+uconvert.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h
+uconvert.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h
+uconvert.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
+uconvert.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
+uconvert.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
+
+ufuncs.o: ${topdir}/bashtypes.h
+
+clock.o: ${BASHINCDIR}/posixtime.h
+
+mailstat.o: ${topdir}/bashansi.h
+mailstat.o: ${topdir}/bashtypes.h
+mailstat.o: ${BASHINCDIR}/ansi_stdlib.h
+mailstat.o: ${BASHINCDIR}/posixstat.h
+mailstat.o: ${BASHINCDIR}/posixdir.h
+mailstat.o: ${BASHINCDIR}/maxpath.h
+
+fmtulong.o: ${topdir}/bashansi.h
+fmtulong.o: ${BASHINCDIR}/ansi_stdlib.h
+fmtulong.o: ${BASHINCDIR}/chartypes.h
+fmtulong.o: ${BASHINCDIR}/stdc.h
+fmtulong.o: ${BASHINCDIR}/typemax.h
+fmtulong.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+fmtullong.o: ${topdir}/bashansi.h
+fmtullong.o: ${BASHINCDIR}/ansi_stdlib.h
+fmtullong.o: ${BASHINCDIR}/chartypes.h
+fmtullong.o: ${BASHINCDIR}/stdc.h
+fmtullong.o: ${BASHINCDIR}/typemax.h
+fmtullong.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+fmtumax.o: ${topdir}/bashansi.h
+fmtumax.o: ${BASHINCDIR}/ansi_stdlib.h
+fmtumax.o: ${BASHINCDIR}/chartypes.h
+fmtumax.o: ${BASHINCDIR}/stdc.h
+fmtumax.o: ${BASHINCDIR}/typemax.h
+fmtumax.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+wcsdup.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+wcsdup.o: ${BASHINCDIR}/stdc.h
+wcsdup.o: ${topdir}/xmalloc.h
+
+wcsnwidth.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+wcsnwidth.o: ${BASHINCDIR}/stdc.h
+
+wcswidth.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+wcswidth.o: ${BASHINCDIR}/stdc.h
+
+mbschr.o: ${topdir}/bashansi.h
+mbschr.o: ${BASHINCDIR}/ansi_stdlib.h
+mbschr.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+
+zgetline.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+zgetline.o: ${BASHINCDIR}/stdc.h
+zgetline.o: ${topdir}/xmalloc.h
+zgetline.o: ${topdir}/bashtypes.h
+
+mbscasecmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+mbscasecmp.o: ${BASHINCDIR}/stdc.h
+mbscasecmp.o: ${topdir}/xmalloc.h
+
+mbscmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+mbscmp.o: ${BASHINCDIR}/stdc.h
+mbscmp.o: ${topdir}/xmalloc.h
+
+casemod.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+casemod.o: ${BASHINCDIR}/stdc.h
+casemod.o: ${topdir}/xmalloc.h
+casemod.o: ${topdir}/bashtypes.h
+casemod.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+casemod.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+dprintf.o: ${BASHINCDIR}/stdc.h
+
+input_avail.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+input_avail.o: ${BASHINCDIR}/stdc.h
+input_avail.o: ${topdir}/xmalloc.h ${BASHINCDIR}/posixselect.h
+
+mktime.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+mktime.o: ${BASHINCDIR}/stdc.h
+
+fnxform.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+fnxform.o: ${BASHINCDIR}/stdc.h
+fnxform.o: ${topdir}/bashtypes.h
+fnxform.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+shmbchar.o: ${BASHINCDIR}/shmbchar.h
+shmbchar.o: ${BASHINCDIR}/shmbutil.h
+
+timers.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+timers.o: ${BASHINCDIR}/stdc.h
+timers.o: ${topdir}/xmalloc.h ${topdir}/sig.h
+timers.o: ${BASHINCDIR}/posixtime.h ${BASHINCDIR}/stat-time.h
+timers.o: ${BASHINCDIR}/posixselect.h
+timers.o: ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
+timers.o: ${BASHINCDIR}/timer.h
+
+unicode.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+unicode.o: ${BASHINCDIR}/stdc.h
+unicode.o: ${topdir}/xmalloc.h
+
+utf8.o: ${topdir}/bashansi.h
+utf8.o: ${BASHINCDIR}/ansi_stdlib.h
+utf8.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+
+winsize.o: ${BASHINCDIR}/stdc.h
+winsize.o: ${topdir}/xmalloc.h
+winsize.o: ${topdir}/bashtypes.h
+
+zmapfd.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+zmapfd.o: ${BASHINCDIR}/stdc.h
+zmapfd.o: ${topdir}/command.h
+zmapfd.o: ${topdir}/general.h
+zmapfd.o: ${topdir}/bashtypes.h ${BASHINCDIR}/chartypes.h ${topdir}/xmalloc.h
diff --git a/lib/sh/casemod.c b/lib/sh/casemod.c
new file mode 100644
index 0000000..0403f91
--- /dev/null
+++ b/lib/sh/casemod.c
@@ -0,0 +1,271 @@
+/* casemod.c -- functions to change case of strings */
+
+/* Copyright (C) 2008-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <stdc.h>
+
+#include <bashansi.h>
+#include <bashintl.h>
+#include <bashtypes.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <xmalloc.h>
+
+#include <shmbchar.h>
+#include <shmbutil.h>
+#include <chartypes.h>
+#include <typemax.h>
+
+#include <glob/strmatch.h>
+
+#define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
+#define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc))
+
+#if !defined (HANDLE_MULTIBYTE)
+# define cval(s, i, l) ((s)[(i)])
+# define iswalnum(c) (isalnum(c))
+# define TOGGLE(x) (ISUPPER (x) ? tolower ((unsigned char)x) : (TOUPPER (x)))
+#else
+# define TOGGLE(x) (iswupper (x) ? towlower (x) : (_to_wupper(x)))
+#endif
+
+/* These must agree with the defines in externs.h */
+#define CASE_NOOP 0x0000
+#define CASE_LOWER 0x0001
+#define CASE_UPPER 0x0002
+#define CASE_CAPITALIZE 0x0004
+#define CASE_UNCAP 0x0008
+#define CASE_TOGGLE 0x0010
+#define CASE_TOGGLEALL 0x0020
+#define CASE_UPFIRST 0x0040
+#define CASE_LOWFIRST 0x0080
+
+#define CASE_USEWORDS 0x1000 /* modify behavior to act on words in passed string */
+
+extern char *substring PARAMS((char *, int, int));
+
+#ifndef UCHAR_MAX
+# define UCHAR_MAX TYPE_MAXIMUM(unsigned char)
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+static wchar_t
+cval (s, i, l)
+ char *s;
+ int i, l;
+{
+ size_t tmp;
+ wchar_t wc;
+ mbstate_t mps;
+
+ if (MB_CUR_MAX == 1 || is_basic (s[i]))
+ return ((wchar_t)s[i]);
+ if (i >= (l - 1))
+ return ((wchar_t)s[i]);
+ memset (&mps, 0, sizeof (mbstate_t));
+ tmp = mbrtowc (&wc, s + i, l - i, &mps);
+ if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
+ return ((wchar_t)s[i]);
+ return wc;
+}
+#endif
+
+/* Modify the case of characters in STRING matching PAT based on the value of
+ FLAGS. If PAT is null, modify the case of each character */
+char *
+sh_modcase (string, pat, flags)
+ const char *string;
+ char *pat;
+ int flags;
+{
+ int start, next, end, retind;
+ int inword, c, nc, nop, match, usewords;
+ char *ret, *s;
+ wchar_t wc;
+ int mb_cur_max;
+#if defined (HANDLE_MULTIBYTE)
+ wchar_t nwc;
+ char mb[MB_LEN_MAX+1];
+ int mlen;
+ size_t m;
+ mbstate_t state;
+#endif
+
+ if (string == 0 || *string == 0)
+ {
+ ret = (char *)xmalloc (1);
+ ret[0] = '\0';
+ return ret;
+ }
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (&state, 0, sizeof (mbstate_t));
+#endif
+
+ start = 0;
+ end = strlen (string);
+ mb_cur_max = MB_CUR_MAX;
+
+ ret = (char *)xmalloc (2*end + 1);
+ retind = 0;
+
+ /* See if we are supposed to split on alphanumerics and operate on each word */
+ usewords = (flags & CASE_USEWORDS);
+ flags &= ~CASE_USEWORDS;
+
+ inword = 0;
+ while (start < end)
+ {
+ wc = cval ((char *)string, start, end);
+
+ if (iswalnum (wc) == 0)
+ inword = 0;
+
+ if (pat)
+ {
+ next = start;
+ ADVANCE_CHAR (string, end, next);
+ s = substring ((char *)string, start, next);
+ match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
+ free (s);
+ if (match == 0)
+ {
+ /* copy unmatched portion */
+ memcpy (ret + retind, string + start, next - start);
+ retind += next - start;
+ start = next;
+ inword = 1;
+ continue;
+ }
+ }
+
+ /* XXX - for now, the toggling operators work on the individual
+ words in the string, breaking on alphanumerics. Should I
+ leave the capitalization operators to do that also? */
+ if (flags == CASE_CAPITALIZE)
+ {
+ if (usewords)
+ nop = inword ? CASE_LOWER : CASE_UPPER;
+ else
+ nop = (start > 0) ? CASE_LOWER : CASE_UPPER;
+ inword = 1;
+ }
+ else if (flags == CASE_UNCAP)
+ {
+ if (usewords)
+ nop = inword ? CASE_UPPER : CASE_LOWER;
+ else
+ nop = (start > 0) ? CASE_UPPER : CASE_LOWER;
+ inword = 1;
+ }
+ else if (flags == CASE_UPFIRST)
+ {
+ if (usewords)
+ nop = inword ? CASE_NOOP : CASE_UPPER;
+ else
+ nop = (start > 0) ? CASE_NOOP : CASE_UPPER;
+ inword = 1;
+ }
+ else if (flags == CASE_LOWFIRST)
+ {
+ if (usewords)
+ nop = inword ? CASE_NOOP : CASE_LOWER;
+ else
+ nop = (start > 0) ? CASE_NOOP : CASE_LOWER;
+ inword = 1;
+ }
+ else if (flags == CASE_TOGGLE)
+ {
+ nop = inword ? CASE_NOOP : CASE_TOGGLE;
+ inword = 1;
+ }
+ else
+ nop = flags;
+
+ /* Can't short-circuit, some locales have multibyte upper and lower
+ case equivalents of single-byte ascii characters (e.g., Turkish) */
+ if (mb_cur_max == 1)
+ {
+singlebyte:
+ switch (nop)
+ {
+ default:
+ case CASE_NOOP: nc = wc; break;
+ case CASE_UPPER: nc = TOUPPER (wc); break;
+ case CASE_LOWER: nc = TOLOWER (wc); break;
+ case CASE_TOGGLEALL:
+ case CASE_TOGGLE: nc = TOGGLE (wc); break;
+ }
+ ret[retind++] = nc;
+ }
+#if defined (HANDLE_MULTIBYTE)
+ else
+ {
+ m = mbrtowc (&wc, string + start, end - start, &state);
+ /* Have to go through wide case conversion even for single-byte
+ chars, to accommodate single-byte characters where the
+ corresponding upper or lower case equivalent is multibyte. */
+ if (MB_INVALIDCH (m))
+ {
+ wc = (unsigned char)string[start];
+ goto singlebyte;
+ }
+ else if (MB_NULLWCH (m))
+ wc = L'\0';
+ switch (nop)
+ {
+ default:
+ case CASE_NOOP: nwc = wc; break;
+ case CASE_UPPER: nwc = _to_wupper (wc); break;
+ case CASE_LOWER: nwc = _to_wlower (wc); break;
+ case CASE_TOGGLEALL:
+ case CASE_TOGGLE: nwc = TOGGLE (wc); break;
+ }
+
+ /* We don't have to convert `wide' characters that are in the
+ unsigned char range back to single-byte `multibyte' characters. */
+ if ((int)nwc <= UCHAR_MAX && is_basic ((int)nwc))
+ ret[retind++] = nwc;
+ else
+ {
+ mlen = wcrtomb (mb, nwc, &state);
+ if (mlen > 0)
+ mb[mlen] = '\0';
+ /* Don't assume the same width */
+ strncpy (ret + retind, mb, mlen);
+ retind += mlen;
+ }
+ }
+#endif
+
+ ADVANCE_CHAR (string, end, start);
+ }
+
+ ret[retind] = '\0';
+ return ret;
+}
diff --git a/lib/sh/clktck.c b/lib/sh/clktck.c
new file mode 100644
index 0000000..8b9b5b3
--- /dev/null
+++ b/lib/sh/clktck.c
@@ -0,0 +1,61 @@
+/* clktck.c - get the value of CLK_TCK. */
+
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
+# if !defined (CLK_TCK)
+# if defined (HZ)
+# define CLK_TCK HZ
+# else
+# define CLK_TCK 60
+# endif
+# endif /* !CLK_TCK */
+#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */
+
+long
+get_clk_tck ()
+{
+ static long retval = 0;
+
+ if (retval != 0)
+ return (retval);
+
+#if defined (HAVE_SYSCONF) && defined (_SC_CLK_TCK)
+ retval = sysconf (_SC_CLK_TCK);
+#else /* !SYSCONF || !_SC_CLK_TCK */
+ retval = CLK_TCK;
+#endif /* !SYSCONF || !_SC_CLK_TCK */
+
+ return (retval);
+}
diff --git a/lib/sh/clock.c b/lib/sh/clock.c
new file mode 100644
index 0000000..c6c52bf
--- /dev/null
+++ b/lib/sh/clock.c
@@ -0,0 +1,87 @@
+/* clock.c - operations on struct tms and clock_t's */
+
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_TIMES)
+
+#include <sys/types.h>
+#include <posixtime.h>
+
+#if defined (HAVE_SYS_TIMES_H)
+# include <sys/times.h>
+#endif
+
+#include <stdio.h>
+#include <stdc.h>
+
+#include <bashintl.h>
+
+#ifndef locale_decpoint
+extern int locale_decpoint PARAMS((void));
+#endif
+
+extern long get_clk_tck PARAMS((void));
+
+void
+clock_t_to_secs (t, sp, sfp)
+ clock_t t;
+ time_t *sp;
+ int *sfp;
+{
+ static long clk_tck = -1;
+
+ if (clk_tck == -1)
+ clk_tck = get_clk_tck ();
+
+ *sfp = t % clk_tck;
+ *sfp = (*sfp * 1000) / clk_tck;
+
+ *sp = t / clk_tck;
+
+ /* Sanity check */
+ if (*sfp >= 1000)
+ {
+ *sp += 1;
+ *sfp -= 1000;
+ }
+}
+
+/* Print the time defined by a clock_t (returned by the `times' and `time'
+ system calls) in a standard way to stdio stream FP. This is scaled in
+ terms of the value of CLK_TCK, which is what is returned by the
+ `times' call. */
+void
+print_clock_t (fp, t)
+ FILE *fp;
+ clock_t t;
+{
+ time_t timestamp;
+ long minutes;
+ int seconds, seconds_fraction;
+
+ clock_t_to_secs (t, &timestamp, &seconds_fraction);
+
+ minutes = timestamp / 60;
+ seconds = timestamp % 60;
+
+ fprintf (fp, "%ldm%d%c%03ds", minutes, seconds, locale_decpoint(), seconds_fraction);
+}
+#endif /* HAVE_TIMES */
diff --git a/lib/sh/dprintf.c b/lib/sh/dprintf.c
new file mode 100644
index 0000000..b3b5d64
--- /dev/null
+++ b/lib/sh/dprintf.c
@@ -0,0 +1,70 @@
+/* dprintf -- printf to a file descriptor */
+
+/* Copyright (C) 2008-2010 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdc.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (PREFER_STDARG)
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include <stdio.h>
+
+int
+#if defined (PREFER_STDARG)
+dprintf(int fd, const char *format, ...)
+#else
+dprintf(fd, format, va_alist)
+ int fd;
+ const char *format;
+ va_dcl
+#endif
+{
+ FILE *fp;
+ int fd2, rc, r2;
+ va_list args;
+
+ if ((fd2 = dup(fd)) < 0)
+ return -1;
+ fp = fdopen (fd2, "w");
+ if (fp == 0)
+ {
+ close (fd2);
+ return -1;
+ }
+
+ SH_VA_START (args, format);
+ rc = vfprintf (fp, format, args);
+ fflush (fp);
+ va_end (args);
+
+ r2 = fclose (fp); /* check here */
+
+ return rc;
+}
diff --git a/lib/sh/eaccess.c b/lib/sh/eaccess.c
new file mode 100644
index 0000000..c3043ec
--- /dev/null
+++ b/lib/sh/eaccess.c
@@ -0,0 +1,244 @@
+/* eaccess.c - eaccess replacement for the shell, plus other access functions. */
+
+/* Copyright (C) 2006-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif /* !_POSIX_VERSION */
+#include "posixstat.h"
+#include "filecntl.h"
+
+#include "shell.h"
+
+#if !defined (R_OK)
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif /* R_OK */
+
+static int path_is_devfd PARAMS((const char *));
+static int sh_stataccess PARAMS((const char *, int));
+#if HAVE_DECL_SETREGID
+static int sh_euidaccess PARAMS((const char *, int));
+#endif
+
+static int
+path_is_devfd (path)
+ const char *path;
+{
+ if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
+ return 1;
+ else if (STREQN (path, "/dev/std", 8))
+ {
+ if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err"))
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+/* A wrapper for stat () which disallows pathnames that are empty strings
+ and handles /dev/fd emulation on systems that don't have it. */
+int
+sh_stat (path, finfo)
+ const char *path;
+ struct stat *finfo;
+{
+ static char *pbuf = 0;
+
+ if (*path == '\0')
+ {
+ errno = ENOENT;
+ return (-1);
+ }
+ if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
+ {
+ /* If stating /dev/fd/n doesn't produce the same results as fstat of
+ FD N, then define DEV_FD_STAT_BROKEN */
+#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
+ intmax_t fd;
+ int r;
+
+ if (legal_number (path + 8, &fd) && fd == (int)fd)
+ {
+ r = fstat ((int)fd, finfo);
+ if (r == 0 || errno != EBADF)
+ return (r);
+ }
+ errno = ENOENT;
+ return (-1);
+#else
+ /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
+ trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
+ On most systems, with the notable exception of linux, this is
+ effectively a no-op. */
+ pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8));
+ strcpy (pbuf, DEV_FD_PREFIX);
+ strcat (pbuf, path + 8);
+ return (stat (pbuf, finfo));
+#endif /* !HAVE_DEV_FD */
+ }
+#if !defined (HAVE_DEV_STDIN)
+ else if (STREQN (path, "/dev/std", 8))
+ {
+ if (STREQ (path+8, "in"))
+ return (fstat (0, finfo));
+ else if (STREQ (path+8, "out"))
+ return (fstat (1, finfo));
+ else if (STREQ (path+8, "err"))
+ return (fstat (2, finfo));
+ else
+ return (stat (path, finfo));
+ }
+#endif /* !HAVE_DEV_STDIN */
+ return (stat (path, finfo));
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+ and don't make the mistake of telling root that any file is
+ executable. This version uses stat(2). */
+static int
+sh_stataccess (path, mode)
+ const char *path;
+ int mode;
+{
+ struct stat st;
+
+ if (sh_stat (path, &st) < 0)
+ return (-1);
+
+ if (current_user.euid == 0)
+ {
+ /* Root can read or write any file. */
+ if ((mode & X_OK) == 0)
+ return (0);
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return (0);
+ }
+
+ if (st.st_uid == current_user.euid) /* owner */
+ mode <<= 6;
+ else if (group_member (st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode)
+ return (0);
+
+ errno = EACCES;
+ return (-1);
+}
+
+#if HAVE_DECL_SETREGID
+/* Version to call when uid != euid or gid != egid. We temporarily swap
+ the effective and real uid and gid as appropriate. */
+static int
+sh_euidaccess (path, mode)
+ const char *path;
+ int mode;
+{
+ int r, e;
+
+ if (current_user.uid != current_user.euid)
+ setreuid (current_user.euid, current_user.uid);
+ if (current_user.gid != current_user.egid)
+ setregid (current_user.egid, current_user.gid);
+
+ r = access (path, mode);
+ e = errno;
+
+ if (current_user.uid != current_user.euid)
+ setreuid (current_user.uid, current_user.euid);
+ if (current_user.gid != current_user.egid)
+ setregid (current_user.gid, current_user.egid);
+
+ errno = e;
+ return r;
+}
+#endif
+
+int
+sh_eaccess (path, mode)
+ const char *path;
+ int mode;
+{
+ int ret;
+
+ if (path_is_devfd (path))
+ return (sh_stataccess (path, mode));
+
+#if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS)
+# if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
+ ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS);
+# else /* HAVE_EACCESS */ /* FreeBSD */
+ ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
+# endif /* HAVE_EACCESS */
+# if defined (__FreeBSD__) || defined (SOLARIS) || defined (_AIX)
+ if (ret == 0 && current_user.euid == 0 && mode == X_OK)
+ return (sh_stataccess (path, mode));
+# endif /* __FreeBSD__ || SOLARIS || _AIX */
+ return ret;
+#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
+ return access (path, mode|EFF_ONLY_OK);
+#else
+ if (mode == F_OK)
+ return (sh_stataccess (path, mode));
+
+# if HAVE_DECL_SETREGID
+ if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
+ return (sh_euidaccess (path, mode));
+# endif
+
+ if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
+ {
+ ret = access (path, mode);
+#if defined (__FreeBSD__) || defined (SOLARIS)
+ if (ret == 0 && current_user.euid == 0 && mode == X_OK)
+ return (sh_stataccess (path, mode));
+#endif
+ return ret;
+ }
+
+ return (sh_stataccess (path, mode));
+#endif
+}
diff --git a/lib/sh/fmtullong.c b/lib/sh/fmtullong.c
new file mode 100644
index 0000000..46eaf50
--- /dev/null
+++ b/lib/sh/fmtullong.c
@@ -0,0 +1,31 @@
+/* fmtullong.c - convert `long long int' to string */
+
+/* Copyright (C) 2001-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifdef HAVE_LONG_LONG_INT
+
+#define LONG long long
+#define UNSIGNED_LONG unsigned long long
+#define fmtulong fmtullong
+
+#include "fmtulong.c"
+
+#endif
diff --git a/lib/sh/fmtulong.c b/lib/sh/fmtulong.c
new file mode 100644
index 0000000..0ccc22b
--- /dev/null
+++ b/lib/sh/fmtulong.c
@@ -0,0 +1,191 @@
+/* fmtulong.c -- Convert unsigned long int to string. */
+
+/* Copyright (C) 1998-2011 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#include <bashansi.h>
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#include <chartypes.h>
+#include <errno.h>
+
+#include <bashintl.h>
+
+#include "stdc.h"
+
+#include <typemax.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#define x_digs "0123456789abcdef"
+#define X_digs "0123456789ABCDEF"
+
+/* XXX -- assumes uppercase letters, lowercase letters, and digits are
+ contiguous */
+#define FMTCHAR(x) \
+ ((x) < 10) ? (x) + '0' \
+ : (((x) < 36) ? (x) - 10 + 'a' \
+ : (((x) < 62) ? (x) - 36 + 'A' \
+ : (((x) == 62) ? '@' : '_')))
+
+#ifndef FL_PREFIX
+# define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */
+# define FL_ADDBASE 0x02 /* add base# prefix to converted value */
+# define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */
+# define FL_UNSIGNED 0x08 /* don't add any sign */
+#endif
+
+#ifndef LONG
+# define LONG long
+# define UNSIGNED_LONG unsigned long
+#endif
+
+/* `unsigned long' (or unsigned long long) to string conversion for a given
+ base. The caller passes the output buffer and the size. This should
+ check for buffer underflow, but currently does not. */
+char *
+fmtulong (ui, base, buf, len, flags)
+ UNSIGNED_LONG ui;
+ int base;
+ char *buf;
+ size_t len;
+ int flags;
+{
+ char *p;
+ int sign;
+ LONG si;
+
+ if (base == 0)
+ base = 10;
+
+ if (base < 2 || base > 64)
+ {
+#if 1
+ /* XXX - truncation possible with long translation */
+ strncpy (buf, _("invalid base"), len - 1);
+ buf[len-1] = '\0';
+ errno = EINVAL;
+ return (p = buf);
+#else
+ base = 10;
+#endif
+ }
+
+ sign = 0;
+ if ((flags & FL_UNSIGNED) == 0 && (LONG)ui < 0)
+ {
+ ui = -ui;
+ sign = '-';
+ }
+
+ p = buf + len - 2;
+ p[1] = '\0';
+
+ /* handle common cases explicitly */
+ switch (base)
+ {
+ case 10:
+ if (ui < 10)
+ {
+ *p-- = TOCHAR (ui);
+ break;
+ }
+ /* Favor signed arithmetic over unsigned arithmetic; it is faster on
+ many machines. */
+ if ((LONG)ui < 0)
+ {
+ *p-- = TOCHAR (ui % 10);
+ si = ui / 10;
+ }
+ else
+ si = ui;
+ do
+ *p-- = TOCHAR (si % 10);
+ while (si /= 10);
+ break;
+
+ case 8:
+ do
+ *p-- = TOCHAR (ui & 7);
+ while (ui >>= 3);
+ break;
+
+ case 16:
+ do
+ *p-- = (flags & FL_HEXUPPER) ? X_digs[ui & 15] : x_digs[ui & 15];
+ while (ui >>= 4);
+ break;
+
+ case 2:
+ do
+ *p-- = TOCHAR (ui & 1);
+ while (ui >>= 1);
+ break;
+
+ default:
+ do
+ *p-- = FMTCHAR (ui % base);
+ while (ui /= base);
+ break;
+ }
+
+ if ((flags & FL_PREFIX) && (base == 8 || base == 16))
+ {
+ if (base == 16)
+ {
+ *p-- = (flags & FL_HEXUPPER) ? 'X' : 'x';
+ *p-- = '0';
+ }
+ else if (p[1] != '0')
+ *p-- = '0';
+ }
+ else if ((flags & FL_ADDBASE) && base != 10)
+ {
+ *p-- = '#';
+ *p-- = TOCHAR (base % 10);
+ if (base > 10)
+ *p-- = TOCHAR (base / 10);
+ }
+
+ if (sign)
+ *p-- = '-';
+
+ return (p + 1);
+}
diff --git a/lib/sh/fmtumax.c b/lib/sh/fmtumax.c
new file mode 100644
index 0000000..f2786b5
--- /dev/null
+++ b/lib/sh/fmtumax.c
@@ -0,0 +1,27 @@
+/* fmtumax.c -- Convert uintmax_t to string. */
+
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#define LONG intmax_t
+#define UNSIGNED_LONG uintmax_t
+#define fmtulong fmtumax
+
+#include "fmtulong.c"
diff --git a/lib/sh/fnxform.c b/lib/sh/fnxform.c
new file mode 100644
index 0000000..35d7e73
--- /dev/null
+++ b/lib/sh/fnxform.c
@@ -0,0 +1,199 @@
+/* fnxform - use iconv(3) to transform strings to and from "filename" format */
+
+/* Copyright (C) 2009-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <stdio.h>
+#include "bashtypes.h"
+
+#include "stdc.h"
+#include "bashintl.h"
+#include <xmalloc.h>
+
+#if defined (HAVE_ICONV)
+# include <iconv.h>
+#endif
+
+#if defined (HAVE_LOCALE_CHARSET)
+extern const char *locale_charset PARAMS((void));
+#else
+extern char *get_locale_var PARAMS((char *));
+#endif
+
+#if defined (HAVE_ICONV)
+static iconv_t conv_fromfs = (iconv_t)-1;
+static iconv_t conv_tofs = (iconv_t)-1;
+
+#define OUTLEN_MAX 4096
+
+static char *outbuf = 0;
+static size_t outlen = 0;
+
+static char *curencoding PARAMS((void));
+static void init_tofs PARAMS((void));
+static void init_fromfs PARAMS((void));
+
+static char *
+curencoding ()
+{
+ char *loc;
+#if defined (HAVE_LOCALE_CHARSET)
+ loc = (char *)locale_charset ();
+ return loc;
+#else
+ char *dot, *mod;
+
+ loc = get_locale_var ("LC_CTYPE");
+ if (loc == 0 || *loc == 0)
+ return "";
+ dot = strchr (loc, '.');
+ if (dot == 0)
+ return loc;
+ mod = strchr (dot, '@');
+ if (mod)
+ *mod = '\0';
+ return ++dot;
+#endif
+}
+
+static void
+init_tofs ()
+{
+ char *cur;
+
+ cur = curencoding ();
+ conv_tofs = iconv_open ("UTF-8-MAC", cur);
+}
+
+static void
+init_fromfs ()
+{
+ char *cur;
+
+ cur = curencoding ();
+ conv_fromfs = iconv_open (cur, "UTF-8-MAC");
+}
+
+char *
+fnx_tofs (string, len)
+ char *string;
+ size_t len;
+{
+#ifdef MACOSX
+ ICONV_CONST char *inbuf;
+ char *tempbuf;
+ size_t templen;
+
+ if (conv_tofs == (iconv_t)-1)
+ init_tofs ();
+ if (conv_tofs == (iconv_t)-1)
+ return string;
+
+ /* Free and reallocate outbuf if it's *too* big */
+ if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8)
+ {
+ free (outbuf);
+ outbuf = 0;
+ outlen = 0;
+ }
+
+ inbuf = string;
+ if (outbuf == 0 || outlen < len + 8)
+ {
+ outlen = len + 8;
+ outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1);
+ }
+ tempbuf = outbuf;
+ templen = outlen;
+
+ iconv (conv_tofs, NULL, NULL, NULL, NULL);
+
+ if (iconv (conv_tofs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1)
+ return string;
+
+ *tempbuf = '\0';
+ return outbuf;
+#else
+ return string;
+#endif
+}
+
+char *
+fnx_fromfs (string, len)
+ char *string;
+ size_t len;
+{
+#ifdef MACOSX
+ ICONV_CONST char *inbuf;
+ char *tempbuf;
+ size_t templen;
+
+ if (conv_fromfs == (iconv_t)-1)
+ init_fromfs ();
+ if (conv_fromfs == (iconv_t)-1)
+ return string;
+
+ /* Free and reallocate outbuf if it's *too* big */
+ if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8)
+ {
+ free (outbuf);
+ outbuf = 0;
+ outlen = 0;
+ }
+
+ inbuf = string;
+ if (outbuf == 0 || outlen < (len + 8))
+ {
+ outlen = len + 8;
+ outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1);
+ }
+ tempbuf = outbuf;
+ templen = outlen;
+
+ iconv (conv_fromfs, NULL, NULL, NULL, NULL);
+
+ if (iconv (conv_fromfs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1)
+ return string;
+
+ *tempbuf = '\0';
+ return outbuf;
+#else
+ return string;
+#endif
+}
+
+#else
+char *
+fnx_tofs (string)
+ char *string;
+{
+ return string;
+}
+
+char *
+fnx_fromfs (string)
+ char *string;
+{
+ return string;
+}
+#endif
diff --git a/lib/sh/fpurge.c b/lib/sh/fpurge.c
new file mode 100644
index 0000000..8cd4e36
--- /dev/null
+++ b/lib/sh/fpurge.c
@@ -0,0 +1,232 @@
+/* fpurge - Flushing buffers of a FILE stream. */
+
+/* Copyright (C) 2007-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include "stdc.h"
+
+#include <stdio.h>
+
+/* Specification. Same as in ../../externs.h. */
+#define NEED_FPURGE_DECL
+#if HAVE_FPURGE
+# define fpurge _bash_fpurge
+#endif
+extern int fpurge PARAMS((FILE *stream));
+
+#if HAVE___FPURGE /* glibc >= 2.2, Haiku, Solaris >= 7 */
+# include <stdio_ext.h>
+#endif
+#include <stdlib.h>
+
+/* Inline contents of gnulib:stdio-impl.h */
+
+/* Many stdio implementations have the same logic and therefore can share
+ the same implementation of stdio extension API, except that some fields
+ have different naming conventions, or their access requires some casts. */
+
+/* BSD stdio derived implementations. */
+
+#if defined __NetBSD__ /* NetBSD */
+/* Get __NetBSD_Version__. */
+# include <sys/param.h>
+#endif
+
+#if defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */
+
+# if defined __DragonFly__ /* DragonFly */
+ /* See <http://www.dragonflybsd.org/cvsweb/src/lib/libc/stdio/priv_stdio.h?rev=HEAD&content-type=text/x-cvsweb-markup>. */
+# define fp_ ((struct { struct __FILE_public pub; \
+ struct { unsigned char *_base; int _size; } _bf; \
+ void *cookie; \
+ void *_close; \
+ void *_read; \
+ void *_seek; \
+ void *_write; \
+ struct { unsigned char *_base; int _size; } _ub; \
+ int _ur; \
+ unsigned char _ubuf[3]; \
+ unsigned char _nbuf[1]; \
+ struct { unsigned char *_base; int _size; } _lb; \
+ int _blksize; \
+ fpos_t _offset; \
+ /* More fields, not relevant here. */ \
+ } *) fp)
+ /* See <http://www.dragonflybsd.org/cvsweb/src/include/stdio.h?rev=HEAD&content-type=text/x-cvsweb-markup>. */
+# define _p pub._p
+# define _flags pub._flags
+# define _r pub._r
+# define _w pub._w
+# else
+# define fp_ fp
+# endif
+
+# if (defined __NetBSD__ && __NetBSD_Version__ >= 105270000) || defined __OpenBSD__ /* NetBSD >= 1.5ZA, OpenBSD */
+ /* See <http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdio/fileext.h?rev=HEAD&content-type=text/x-cvsweb-markup>
+ and <http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/fileext.h?rev=HEAD&content-type=text/x-cvsweb-markup> */
+ struct __sfileext
+ {
+ struct __sbuf _ub; /* ungetc buffer */
+ /* More fields, not relevant here. */
+ };
+# define fp_ub ((struct __sfileext *) fp->_ext._base)->_ub
+# else /* FreeBSD, NetBSD <= 1.5Z, DragonFly, MacOS X, Cygwin */
+# define fp_ub fp_->_ub
+# endif
+
+# define HASUB(fp) (fp_ub._base != NULL)
+
+#endif
+
+/* SystemV derived implementations. */
+
+#if defined _IOERR
+
+# if defined __sun && defined _LP64 /* Solaris/{SPARC,AMD64} 64-bit */
+# define fp_ ((struct { unsigned char *_ptr; \
+ unsigned char *_base; \
+ unsigned char *_end; \
+ long _cnt; \
+ int _file; \
+ unsigned int _flag; \
+ } *) fp)
+# else
+# define fp_ fp
+# endif
+
+# if defined _SCO_DS /* OpenServer */
+# define _cnt __cnt
+# define _ptr __ptr
+# define _base __base
+# define _flag __flag
+# endif
+
+#endif
+
+int
+fpurge (FILE *fp)
+{
+#if HAVE___FPURGE /* glibc >= 2.2, Haiku, Solaris >= 7 */
+
+ __fpurge (fp);
+ /* The __fpurge function does not have a return value. */
+ return 0;
+
+#elif HAVE_FPURGE /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin 1.7 */
+
+ /* Call the system's fpurge function. */
+# undef fpurge
+# if !HAVE_DECL_FPURGE
+ extern int fpurge (FILE *);
+# endif
+ int result = fpurge (fp);
+# if defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */
+ if (result == 0)
+ /* Correct the invariants that fpurge broke.
+ <stdio.h> on BSD systems says:
+ "The following always hold: if _flags & __SRD, _w is 0."
+ If this invariant is not fulfilled and the stream is read-write but
+ currently reading, subsequent putc or fputc calls will write directly
+ into the buffer, although they shouldn't be allowed to. */
+ if ((fp_->_flags & __SRD) != 0)
+ fp_->_w = 0;
+# endif
+ return result;
+
+#else
+
+ /* Most systems provide FILE as a struct and the necessary bitmask in
+ <stdio.h>, because they need it for implementing getc() and putc() as
+ fast macros. */
+# if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
+ fp->_IO_read_end = fp->_IO_read_ptr;
+ fp->_IO_write_ptr = fp->_IO_write_base;
+ /* Avoid memory leak when there is an active ungetc buffer. */
+ if (fp->_IO_save_base != NULL)
+ {
+ free (fp->_IO_save_base);
+ fp->_IO_save_base = NULL;
+ }
+ return 0;
+# elif defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */
+ fp_->_p = fp_->_bf._base;
+ fp_->_r = 0;
+ fp_->_w = ((fp_->_flags & (__SLBF | __SNBF | __SRD)) == 0 /* fully buffered and not currently reading? */
+ ? fp_->_bf._size
+ : 0);
+ /* Avoid memory leak when there is an active ungetc buffer. */
+ if (fp_ub._base != NULL)
+ {
+ if (fp_ub._base != fp_->_ubuf)
+ free (fp_ub._base);
+ fp_ub._base = NULL;
+ }
+ return 0;
+# elif defined __EMX__ /* emx+gcc */
+ fp->_ptr = fp->_buffer;
+ fp->_rcount = 0;
+ fp->_wcount = 0;
+ fp->_ungetc_count = 0;
+ return 0;
+# elif defined _IOERR || defined __TANDEM /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, mingw */
+ fp->_ptr = fp->_base;
+ if (fp->_ptr != NULL)
+ fp->_cnt = 0;
+ return 0;
+# elif defined __UCLIBC__ /* uClibc */
+# ifdef __STDIO_BUFFERS
+ if (fp->__modeflags & __FLAG_WRITING)
+ fp->__bufpos = fp->__bufstart;
+ else if (fp->__modeflags & (__FLAG_READONLY | __FLAG_READING))
+ fp->__bufpos = fp->__bufread;
+# endif
+ return 0;
+# elif defined __QNX__ /* QNX */
+ fp->_Rback = fp->_Back + sizeof (fp->_Back);
+ fp->_Rsave = NULL;
+ if (fp->_Mode & 0x2000 /* _MWRITE */)
+ /* fp->_Buf <= fp->_Next <= fp->_Wend */
+ fp->_Next = fp->_Buf;
+ else
+ /* fp->_Buf <= fp->_Next <= fp->_Rend */
+ fp->_Rend = fp->_Next;
+ return 0;
+# elif defined __MINT__ /* Atari FreeMiNT */
+ if (fp->__pushed_back)
+ {
+ fp->__bufp = fp->__pushback_bufp;
+ fp->__pushed_back = 0;
+ }
+ /* Preserve the current file position. */
+ if (fp->__target != -1)
+ fp->__target += fp->__bufp - fp->__buffer;
+ fp->__bufp = fp->__buffer;
+ /* Nothing in the buffer, next getc is nontrivial. */
+ fp->__get_limit = fp->__bufp;
+ /* Nothing in the buffer, next putc is nontrivial. */
+ fp->__put_limit = fp->__buffer;
+ return 0;
+# else
+# warning "Please port gnulib fpurge.c to your platform! Look at the definitions of fflush, setvbuf and ungetc on your system, then report this to bug-gnulib."
+ return 0;
+# endif
+
+#endif
+}
diff --git a/lib/sh/getcwd.c b/lib/sh/getcwd.c
new file mode 100644
index 0000000..d7bd241
--- /dev/null
+++ b/lib/sh/getcwd.c
@@ -0,0 +1,356 @@
+/* getcwd.c -- get pathname of current directory */
+
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_GETCWD)
+
+#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
+ #pragma alloca
+#endif /* _AIX && RISC6000 && !__GNUC__ */
+
+#if defined (__QNX__)
+# undef HAVE_LSTAT
+#endif
+
+#include <bashtypes.h>
+#include <errno.h>
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <posixdir.h>
+#include <posixstat.h>
+#include <maxpath.h>
+#include <memalloc.h>
+
+#include <bashansi.h>
+
+#if !defined (D_FILENO_AVAILABLE)
+# include "command.h"
+# include "general.h"
+# include "externs.h"
+#endif
+
+#include <xmalloc.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (HAVE_LSTAT)
+# define lstat stat
+#endif
+
+#if !defined (NULL)
+# define NULL 0
+#endif
+
+/* If the d_fileno member of a struct dirent doesn't return anything useful,
+ we need to check inode number equivalence the hard way. Return 1 if
+ the inode corresponding to PATH/DIR is identical to THISINO. */
+#if !defined (D_FILENO_AVAILABLE)
+static int
+_path_checkino (dotp, name, thisino)
+ char *dotp;
+ char *name;
+ ino_t thisino;
+{
+ char *fullpath;
+ int r, e;
+ struct stat st;
+
+ e = errno;
+ fullpath = sh_makepath (dotp, name, MP_RMDOT);
+ if (stat (fullpath, &st) < 0)
+ {
+ errno = e;
+ return 0;
+ }
+ free (fullpath);
+ errno = e;
+ return (st.st_ino == thisino);
+}
+#endif
+
+/* Get the pathname of the current working directory,
+ and put it in SIZE bytes of BUF. Returns NULL if the
+ directory couldn't be determined or SIZE was too small.
+ If successful, returns BUF. In GNU, if BUF is NULL,
+ an array is allocated with `malloc'; the array is SIZE
+ bytes long, unless SIZE <= 0, in which case it is as
+ big as necessary. */
+#if defined (__STDC__)
+char *
+getcwd (char *buf, size_t size)
+#else /* !__STDC__ */
+char *
+getcwd (buf, size)
+ char *buf;
+ size_t size;
+#endif /* !__STDC__ */
+{
+ static const char dots[]
+ = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+ const char *dotp, *dotlist;
+ size_t dotsize;
+ dev_t rootdev, thisdev;
+ ino_t rootino, thisino;
+ char path[PATH_MAX + 1];
+ register char *pathp;
+ char *pathbuf;
+ size_t pathsize;
+ struct stat st;
+ int saved_errno;
+
+ if (buf != NULL && size == 0)
+ {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ pathsize = sizeof (path);
+ pathp = &path[pathsize];
+ *--pathp = '\0';
+ pathbuf = path;
+
+ if (stat (".", &st) < 0)
+ return ((char *)NULL);
+ thisdev = st.st_dev;
+ thisino = st.st_ino;
+
+ if (stat ("/", &st) < 0)
+ return ((char *)NULL);
+ rootdev = st.st_dev;
+ rootino = st.st_ino;
+
+ saved_errno = 0;
+
+ dotsize = sizeof (dots) - 1;
+ dotp = &dots[sizeof (dots)];
+ dotlist = dots;
+ while (!(thisdev == rootdev && thisino == rootino))
+ {
+ register DIR *dirstream;
+ register struct dirent *d;
+ dev_t dotdev;
+ ino_t dotino;
+ char mount_point;
+ int namlen;
+
+ /* Look at the parent directory. */
+ if (dotp == dotlist)
+ {
+ /* My, what a deep directory tree you have, Grandma. */
+ char *new;
+ if (dotlist == dots)
+ {
+ new = (char *)malloc (dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ memcpy (new, dots, dotsize);
+ }
+ else
+ {
+ new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ }
+ memcpy (&new[dotsize], new, dotsize);
+ dotp = &new[dotsize];
+ dotsize *= 2;
+ new[dotsize] = '\0';
+ dotlist = new;
+ }
+
+ dotp -= 3;
+
+ /* Figure out if this directory is a mount point. */
+ if (stat (dotp, &st) < 0)
+ goto lose;
+ dotdev = st.st_dev;
+ dotino = st.st_ino;
+ mount_point = dotdev != thisdev;
+
+ /* Search for the last directory. */
+ dirstream = opendir (dotp);
+ if (dirstream == NULL)
+ goto lose;
+ while ((d = readdir (dirstream)) != NULL)
+ {
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+#if defined (D_FILENO_AVAILABLE)
+ if (mount_point || d->d_fileno == thisino)
+#else
+ if (mount_point || _path_checkino (dotp, d->d_name, thisino))
+#endif
+ {
+ char *name;
+
+ namlen = D_NAMLEN(d);
+ name = (char *)
+ alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
+ memcpy (name, dotp, dotlist + dotsize - dotp);
+ name[dotlist + dotsize - dotp] = '/';
+ memcpy (&name[dotlist + dotsize - dotp + 1],
+ d->d_name, namlen + 1);
+ if (lstat (name, &st) < 0)
+ {
+#if 0
+ int save = errno;
+ (void) closedir (dirstream);
+ errno = save;
+ goto lose;
+#else
+ saved_errno = errno;
+#endif
+ }
+ if (st.st_dev == thisdev && st.st_ino == thisino)
+ break;
+ }
+ }
+ if (d == NULL)
+ {
+#if 0
+ int save = errno;
+#else
+ int save = errno ? errno : saved_errno;
+#endif
+ (void) closedir (dirstream);
+ errno = save;
+ goto lose;
+ }
+ else
+ {
+ size_t space;
+
+ while ((space = pathp - pathbuf) <= namlen)
+ {
+ char *new;
+
+ if (pathbuf == path)
+ {
+ new = (char *)malloc (pathsize * 2);
+ if (!new)
+ goto lose;
+ }
+ else
+ {
+ new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
+ if (!new)
+ goto lose;
+ pathp = new + space;
+ }
+ (void) memcpy (new + pathsize + space, pathp, pathsize - space);
+ pathp = new + pathsize + space;
+ pathbuf = new;
+ pathsize *= 2;
+ }
+
+ pathp -= namlen;
+ (void) memcpy (pathp, d->d_name, namlen);
+ *--pathp = '/';
+ (void) closedir (dirstream);
+ }
+
+ thisdev = dotdev;
+ thisino = dotino;
+ }
+
+ if (pathp == &path[sizeof(path) - 1])
+ *--pathp = '/';
+
+ if (dotlist != dots)
+ free ((PTR_T) dotlist);
+
+ {
+ size_t len = pathbuf + pathsize - pathp;
+ if (buf == NULL && size <= 0)
+ size = len;
+
+ if ((size_t) size < len)
+ {
+ errno = ERANGE;
+ goto lose2;
+ }
+ if (buf == NULL)
+ {
+ buf = (char *) malloc (size);
+ if (buf == NULL)
+ goto lose2;
+ }
+
+ (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
+ }
+
+ if (pathbuf != path)
+ free (pathbuf);
+
+ return (buf);
+
+ lose:
+ if ((dotlist != dots) && dotlist)
+ {
+ int e = errno;
+ free ((PTR_T) dotlist);
+ errno = e;
+ }
+
+ lose2:
+ if ((pathbuf != path) && pathbuf)
+ {
+ int e = errno;
+ free ((PTR_T) pathbuf);
+ errno = e;
+ }
+ return ((char *)NULL);
+}
+
+#if defined (TEST)
+# include <stdio.h>
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char b[PATH_MAX];
+
+ if (getcwd(b, sizeof(b)))
+ {
+ printf ("%s\n", b);
+ exit (0);
+ }
+ else
+ {
+ perror ("cwd: getcwd");
+ exit (1);
+ }
+}
+#endif /* TEST */
+#endif /* !HAVE_GETCWD */
diff --git a/lib/sh/getenv.c b/lib/sh/getenv.c
new file mode 100644
index 0000000..1e682ae
--- /dev/null
+++ b/lib/sh/getenv.c
@@ -0,0 +1,233 @@
+/* getenv.c - get environment variable value from the shell's variable
+ list. */
+
+/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (CAN_REDEFINE_GETENV)
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include <errno.h>
+#include <shell.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char **environ;
+
+/* We supply our own version of getenv () because we want library
+ routines to get the changed values of exported variables. */
+
+/* The NeXT C library has getenv () defined and used in the same file.
+ This screws our scheme. However, Bash will run on the NeXT using
+ the C library getenv (), since right now the only environment variable
+ that we care about is HOME, and that is already defined. */
+static char *last_tempenv_value = (char *)NULL;
+
+char *
+getenv (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ if (name == 0 || *name == '\0')
+ return ((char *)NULL);
+
+ var = find_tempenv_variable ((char *)name);
+ if (var)
+ {
+ FREE (last_tempenv_value);
+
+ last_tempenv_value = value_cell (var) ? savestring (value_cell (var)) : (char *)NULL;
+ return (last_tempenv_value);
+ }
+ else if (shell_variables)
+ {
+ var = find_variable ((char *)name);
+ if (var && exported_p (var))
+ return (value_cell (var));
+ }
+ else if (environ)
+ {
+ register int i, len;
+
+ /* In some cases, s5r3 invokes getenv() before main(); BSD systems
+ using gprof also exhibit this behavior. This means that
+ shell_variables will be 0 when this is invoked. We look up the
+ variable in the real environment in that case. */
+
+ for (i = 0, len = strlen (name); environ[i]; i++)
+ {
+ if ((STREQN (environ[i], name, len)) && (environ[i][len] == '='))
+ return (environ[i] + len + 1);
+ }
+ }
+
+ return ((char *)NULL);
+}
+
+/* Some versions of Unix use _getenv instead. */
+char *
+_getenv (name)
+ const char *name;
+{
+ return (getenv (name));
+}
+
+/* SUSv3 says argument is a `char *'; BSD implementations disagree */
+int
+putenv (str)
+#ifndef HAVE_STD_PUTENV
+ const char *str;
+#else
+ char *str;
+#endif
+{
+ SHELL_VAR *var;
+ char *name, *value;
+ int offset;
+
+ if (str == 0 || *str == '\0')
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ offset = assignment (str, 0);
+ if (str[offset] != '=')
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ name = savestring (str);
+ name[offset] = 0;
+
+ value = name + offset + 1;
+
+ /* XXX - should we worry about readonly here? */
+ var = bind_variable (name, value, 0);
+ if (var == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ VUNSETATTR (var, att_invisible);
+ VSETATTR (var, att_exported);
+
+ return 0;
+}
+
+#if 0
+int
+_putenv (name)
+#ifndef HAVE_STD_PUTENV
+ const char *name;
+#else
+ char *name;
+#endif
+{
+ return putenv (name);
+}
+#endif
+
+int
+setenv (name, value, rewrite)
+ const char *name;
+ const char *value;
+ int rewrite;
+{
+ SHELL_VAR *var;
+ char *v;
+
+ if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ var = 0;
+ v = (char *)value; /* some compilers need explicit cast */
+ /* XXX - should we worry about readonly here? */
+ if (rewrite == 0)
+ var = find_variable (name);
+
+ if (var == 0)
+ var = bind_variable (name, v, 0);
+
+ if (var == 0)
+ return -1;
+
+ VUNSETATTR (var, att_invisible);
+ VSETATTR (var, att_exported);
+
+ return 0;
+}
+
+#if 0
+int
+_setenv (name, value, rewrite)
+ const char *name;
+ const char *value;
+ int rewrite;
+{
+ return setenv (name, value, rewrite);
+}
+#endif
+
+/* SUSv3 says unsetenv returns int; existing implementations (BSD) disagree. */
+
+#ifdef HAVE_STD_UNSETENV
+#define UNSETENV_RETURN(N) return(N)
+#define UNSETENV_RETTYPE int
+#else
+#define UNSETENV_RETURN(N) return
+#define UNSETENV_RETTYPE void
+#endif
+
+UNSETENV_RETTYPE
+unsetenv (name)
+ const char *name;
+{
+ if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
+ {
+ errno = EINVAL;
+ UNSETENV_RETURN(-1);
+ }
+
+ /* XXX - should we just remove the export attribute here? */
+#if 1
+ unbind_variable (name);
+#else
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v)
+ VUNSETATTR (v, att_exported);
+#endif
+
+ UNSETENV_RETURN(0);
+}
+#endif /* CAN_REDEFINE_GETENV */
diff --git a/lib/sh/gettimeofday.c b/lib/sh/gettimeofday.c
new file mode 100644
index 0000000..b654c15
--- /dev/null
+++ b/lib/sh/gettimeofday.c
@@ -0,0 +1,35 @@
+/* gettimeofday.c - gettimeofday replacement using time() */
+
+/* Copyright (C) 2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#if !defined (HAVE_GETTIMEOFDAY)
+
+#include "posixtime.h"
+
+/* A version of gettimeofday that just sets tv_sec from time(3) */
+int
+gettimeofday (struct timeval *tv, void *tz)
+{
+ tv->tv_sec = (time_t) time ((time_t *)0);
+ tv->tv_usec = 0;
+ return 0;
+}
+#endif
diff --git a/lib/sh/inet_aton.c b/lib/sh/inet_aton.c
new file mode 100644
index 0000000..e377178
--- /dev/null
+++ b/lib/sh/inet_aton.c
@@ -0,0 +1,214 @@
+/* inet_aton - convert string to numeric IP address */
+
+/* Snagged from GNU C library, version 2.0.3. */
+
+/*
+ * ++Copyright++ 1983, 1990, 1993
+ * -
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93";
+static char rcsid[] = "$Id: inet_addr.c,v 1.5 1996/08/14 03:48:37 drepper Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <config.h>
+
+#if !defined (HAVE_INET_ATON) && defined (HAVE_NETWORK) && defined (HAVE_NETINET_IN_H) && defined (HAVE_ARPA_INET_H)
+
+#include <sys/types.h>
+#if defined (HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include <ctype.h>
+#include <stdc.h>
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif
+
+/* these are compatibility routines, not needed on recent BSD releases */
+
+#if 0
+/* Not used, not needed. */
+/*
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ */
+u_long
+inet_addr(cp)
+ register const char *cp;
+{
+ struct in_addr val;
+
+ if (inet_aton(cp, &val))
+ return (val.s_addr);
+ return (INADDR_NONE);
+}
+#endif
+
+/*
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ */
+int
+inet_aton(cp, addr)
+ register const char *cp;
+ struct in_addr *addr;
+{
+ register u_bits32_t val;
+ register int base, n;
+ register unsigned char c;
+ u_int parts[4];
+ register u_int *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+#if 0
+ if (!isdigit(c))
+#else
+ if (c != '0' && c != '1' && c != '2' && c != '3' && c != '4' &&
+ c != '5' && c != '6' && c != '7' && c != '8' && c != '9')
+#endif
+ return (0);
+ val = 0; base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X')
+ base = 16, c = *++cp;
+ else
+ base = 8;
+ }
+ for (;;) {
+ if (isascii(c) && isdigit(c)) {
+ val = (val * base) + (c - '0');
+ c = *++cp;
+ } else if (base == 16 && isascii(c) && isxdigit(c)) {
+ val = (val << 4) |
+ (c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3)
+ return (0);
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && (!isascii(c) || !isspace(c)))
+ return (0);
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+ if (addr)
+ addr->s_addr = htonl(val);
+ return (1);
+}
+
+#endif /* !HAVE_INET_ATON */
diff --git a/lib/sh/input_avail.c b/lib/sh/input_avail.c
new file mode 100644
index 0000000..2ac4461
--- /dev/null
+++ b/lib/sh/input_avail.c
@@ -0,0 +1,165 @@
+/* input_avail.c -- check whether or not data is available for reading on a
+ specified file descriptor. */
+
+/* Copyright (C) 2008,2009-2019 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined (__TANDEM)
+# include <floss.h>
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+#if defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (HAVE_PSELECT)
+# include <signal.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include "bashansi.h"
+
+#include "posixselect.h"
+
+#if defined (FIONREAD_IN_SYS_IOCTL)
+# include <sys/ioctl.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (O_NDELAY) && defined (O_NONBLOCK)
+# define O_NDELAY O_NONBLOCK /* Posix style */
+#endif
+
+/* Return >= 1 if select/FIONREAD indicates data available for reading on
+ file descriptor FD; 0 if no data available. Return -1 on error. */
+int
+input_avail (fd)
+ int fd;
+{
+ int result, chars_avail;
+#if defined(HAVE_SELECT)
+ fd_set readfds, exceptfds;
+ struct timeval timeout;
+#endif
+
+ if (fd < 0)
+ return -1;
+
+ chars_avail = 0;
+
+#if defined (HAVE_SELECT)
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (fd, &readfds);
+ FD_SET (fd, &exceptfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ result = select (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
+ return ((result <= 0) ? 0 : 1);
+#endif
+
+#if defined (FIONREAD)
+ errno = 0;
+ result = ioctl (fd, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+ return (chars_avail);
+#endif
+
+ return 0;
+}
+
+/* Wait until NCHARS are available for reading on file descriptor FD.
+ This can wait indefinitely. Return -1 on error. */
+int
+nchars_avail (fd, nchars)
+ int fd;
+ int nchars;
+{
+ int result, chars_avail;
+#if defined(HAVE_SELECT)
+ fd_set readfds, exceptfds;
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ sigset_t set, oset;
+#endif
+
+ if (fd < 0 || nchars < 0)
+ return -1;
+ if (nchars == 0)
+ return (input_avail (fd));
+
+ chars_avail = 0;
+
+#if defined (HAVE_SELECT)
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (fd, &readfds);
+ FD_SET (fd, &exceptfds);
+#endif
+#if defined (HAVE_SELECT) || defined (HAVE_PSELECT)
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &set);
+# ifdef SIGCHLD
+ sigaddset (&set, SIGCHLD);
+# endif
+ sigemptyset (&oset);
+#endif
+
+ while (1)
+ {
+ result = 0;
+#if defined (HAVE_PSELECT)
+ /* XXX - use pselect(2) to block SIGCHLD atomically */
+ result = pselect (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, (struct timespec *)NULL, &set);
+#elif defined (HAVE_SELECT)
+ sigprocmask (SIG_BLOCK, &set, &oset);
+ result = select (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, (struct timeval *)NULL);
+ sigprocmask (SIG_BLOCK, &oset, (sigset_t *)NULL);
+#endif
+ if (result < 0)
+ return -1;
+
+#if defined (FIONREAD)
+ errno = 0;
+ result = ioctl (fd, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+ if (chars_avail >= nchars)
+ break;
+#else
+ break;
+#endif
+ }
+
+ return 0;
+}
diff --git a/lib/sh/itos.c b/lib/sh/itos.c
new file mode 100644
index 0000000..cd36ef3
--- /dev/null
+++ b/lib/sh/itos.c
@@ -0,0 +1,84 @@
+/* itos.c -- Convert integer to string. */
+
+/* Copyright (C) 1998-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include "shell.h"
+
+char *
+inttostr (i, buf, len)
+ intmax_t i;
+ char *buf;
+ size_t len;
+{
+ return (fmtumax (i, 10, buf, len, 0));
+}
+
+/* Integer to string conversion. This conses the string; the
+ caller should free it. */
+char *
+itos (i)
+ intmax_t i;
+{
+ char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1];
+
+ p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0);
+ return (savestring (p));
+}
+
+/* Integer to string conversion. This conses the string using strdup;
+ caller should free it and be prepared to deal with NULL return. */
+char *
+mitos (i)
+ intmax_t i;
+{
+ char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1];
+
+ p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0);
+ return (strdup (p));
+}
+
+char *
+uinttostr (i, buf, len)
+ uintmax_t i;
+ char *buf;
+ size_t len;
+{
+ return (fmtumax (i, 10, buf, len, FL_UNSIGNED));
+}
+
+/* Integer to string conversion. This conses the string; the
+ caller should free it. */
+char *
+uitos (i)
+ uintmax_t i;
+{
+ char *p, lbuf[INT_STRLEN_BOUND(uintmax_t) + 1];
+
+ p = fmtumax (i, 10, lbuf, sizeof(lbuf), FL_UNSIGNED);
+ return (savestring (p));
+}
diff --git a/lib/sh/mailstat.c b/lib/sh/mailstat.c
new file mode 100644
index 0000000..bd5c25f
--- /dev/null
+++ b/lib/sh/mailstat.c
@@ -0,0 +1,159 @@
+/* mailstat.c -- stat a mailbox file, handling maildir-type mail directories */
+
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <bashtypes.h>
+#include <posixstat.h>
+#include <posixdir.h>
+#include <bashansi.h>
+
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+
+#include <maxpath.h>
+
+/*
+ * Stat a file. If it's a maildir, check all messages
+ * in the maildir and present the grand total as a file.
+ * The fields in the 'struct stat' are from the mail directory.
+ * The following fields are emulated:
+ *
+ * st_nlink always 1, unless st_blocks is not present, in which case it's
+ * the total number of messages
+ * st_size total number of bytes in all files
+ * st_blocks total number of messages, if present in struct stat
+ * st_atime access time of newest file in maildir
+ * st_mtime modify time of newest file in maildir
+ * st_mode S_IFDIR changed to S_IFREG
+ *
+ * This is good enough for most mail-checking applications.
+ */
+
+int
+mailstat(path, st)
+ const char *path;
+ struct stat *st;
+{
+ static struct stat st_new_last, st_ret_last;
+ struct stat st_ret, st_tmp;
+ DIR *dd;
+ struct dirent *fn;
+ char dir[PATH_MAX * 2], file[PATH_MAX * 2 + 1];
+ int i, l;
+ time_t atime, mtime;
+
+ atime = mtime = 0;
+
+ /* First see if it's a directory. */
+ if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0)
+ return i;
+
+ if (strlen(path) > sizeof(dir) - 5)
+ {
+#ifdef ENAMETOOLONG
+ errno = ENAMETOOLONG;
+#else
+ errno = EINVAL;
+#endif
+ return -1;
+ }
+
+ st_ret = *st;
+ st_ret.st_nlink = 1;
+ st_ret.st_size = 0;
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+ st_ret.st_blocks = 0;
+#else
+ st_ret.st_nlink = 0;
+#endif
+ st_ret.st_mode &= ~S_IFDIR;
+ st_ret.st_mode |= S_IFREG;
+
+ /* See if cur/ is present */
+ sprintf(dir, "%s/cur", path);
+ if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
+ return 0;
+ st_ret.st_atime = st_tmp.st_atime;
+
+ /* See if tmp/ is present */
+ sprintf(dir, "%s/tmp", path);
+ if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
+ return 0;
+ st_ret.st_mtime = st_tmp.st_mtime;
+
+ /* And new/ */
+ sprintf(dir, "%s/new", path);
+ if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
+ return 0;
+ st_ret.st_mtime = st_tmp.st_mtime;
+
+ /* Optimization - if new/ didn't change, nothing else did. */
+ if (st_tmp.st_dev == st_new_last.st_dev &&
+ st_tmp.st_ino == st_new_last.st_ino &&
+ st_tmp.st_atime == st_new_last.st_atime &&
+ st_tmp.st_mtime == st_new_last.st_mtime)
+ {
+ *st = st_ret_last;
+ return 0;
+ }
+ st_new_last = st_tmp;
+
+ /* Loop over new/ and cur/ */
+ for (i = 0; i < 2; i++)
+ {
+ sprintf(dir, "%s/%s", path, i ? "cur" : "new");
+ sprintf(file, "%s/", dir);
+ l = strlen(file);
+ if ((dd = opendir(dir)) == NULL)
+ return 0;
+ while ((fn = readdir(dd)) != NULL)
+ {
+ if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file))
+ continue;
+ strcpy(file + l, fn->d_name);
+ if (stat(file, &st_tmp) != 0)
+ continue;
+ st_ret.st_size += st_tmp.st_size;
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+ st_ret.st_blocks++;
+#else
+ st_ret.st_nlink++;
+#endif
+ if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime)
+ atime = st_tmp.st_atime;
+ if (st_tmp.st_mtime > mtime)
+ mtime = st_tmp.st_mtime;
+ }
+ closedir(dd);
+ }
+
+/* if (atime) */ /* Set atime even if cur/ is empty */
+ st_ret.st_atime = atime;
+ if (mtime)
+ st_ret.st_mtime = mtime;
+
+ *st = st_ret_last = st_ret;
+ return 0;
+}
diff --git a/lib/sh/makepath.c b/lib/sh/makepath.c
new file mode 100644
index 0000000..ab46c96
--- /dev/null
+++ b/lib/sh/makepath.c
@@ -0,0 +1,128 @@
+/* makepath.c - glue PATH and DIR together into a full pathname. */
+
+/* Copyright (C) 1987-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include "shell.h"
+
+#include <tilde/tilde.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/* MAKE SURE THESE AGREE WITH ../../externs.h. */
+
+#ifndef MP_DOTILDE
+# define MP_DOTILDE 0x01
+# define MP_DOCWD 0x02
+# define MP_RMDOT 0x04
+# define MP_IGNDOT 0x08
+#endif
+
+extern char *get_working_directory PARAMS((char *));
+
+static char *nullpath = "";
+
+/* Take PATH, an element from, e.g., $CDPATH, and DIR, a directory name,
+ and paste them together into PATH/DIR. Tilde expansion is performed on
+ PATH if (flags & MP_DOTILDE) is non-zero. If PATH is NULL or the empty
+ string, it is converted to the current directory. A full pathname is
+ used if (flags & MP_DOCWD) is non-zero, otherwise `./' is used. If
+ (flags & MP_RMDOT) is non-zero, any `./' is removed from the beginning
+ of DIR. If (flags & MP_IGNDOT) is non-zero, a PATH that is "." or "./"
+ is ignored. */
+
+#define MAKEDOT() \
+ do { \
+ xpath = (char *)xmalloc (2); \
+ xpath[0] = '.'; \
+ xpath[1] = '\0'; \
+ pathlen = 1; \
+ } while (0)
+
+char *
+sh_makepath (path, dir, flags)
+ const char *path, *dir;
+ int flags;
+{
+ int dirlen, pathlen;
+ char *ret, *xpath, *xdir, *r, *s;
+
+ if (path == 0 || *path == '\0')
+ {
+ if (flags & MP_DOCWD)
+ {
+ xpath = get_working_directory ("sh_makepath");
+ if (xpath == 0)
+ {
+ ret = get_string_value ("PWD");
+ if (ret)
+ xpath = savestring (ret);
+ }
+ if (xpath == 0)
+ MAKEDOT();
+ else
+ pathlen = strlen (xpath);
+ }
+ else
+ MAKEDOT();
+ }
+ else if ((flags & MP_IGNDOT) && path[0] == '.' && (path[1] == '\0' ||
+ (path[1] == '/' && path[2] == '\0')))
+ {
+ xpath = nullpath;
+ pathlen = 0;
+ }
+ else
+ {
+ xpath = ((flags & MP_DOTILDE) && *path == '~') ? bash_tilde_expand (path, 0) : (char *)path;
+ pathlen = strlen (xpath);
+ }
+
+ xdir = (char *)dir;
+ dirlen = strlen (xdir);
+ if ((flags & MP_RMDOT) && dir[0] == '.' && dir[1] == '/')
+ {
+ xdir += 2;
+ dirlen -= 2;
+ }
+
+ r = ret = (char *)xmalloc (2 + dirlen + pathlen);
+ s = xpath;
+ while (*s)
+ *r++ = *s++;
+ if (s > xpath && s[-1] != '/')
+ *r++ = '/';
+ s = xdir;
+ while (*r++ = *s++)
+ ;
+ if (xpath != path && xpath != nullpath)
+ free (xpath);
+ return (ret);
+}
diff --git a/lib/sh/mbscasecmp.c b/lib/sh/mbscasecmp.c
new file mode 100644
index 0000000..0ab9560
--- /dev/null
+++ b/lib/sh/mbscasecmp.c
@@ -0,0 +1,79 @@
+/* mbscasecmp - case-insensitive multibyte string comparison. */
+
+/* Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_MBSCASECMP) && defined (HANDLE_MULTIBYTE)
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <wchar.h>
+#include <wctype.h>
+
+/* Compare MBS1 and MBS2 without regard to case. */
+int
+mbscasecmp (mbs1, mbs2)
+ const char *mbs1;
+ const char *mbs2;
+{
+ int len1, len2, mb_cur_max;
+ wchar_t c1, c2, l1, l2;
+
+ len1 = len2 = 0;
+ /* Reset multibyte characters to their initial state. */
+ (void) mblen ((char *) NULL, 0);
+
+ mb_cur_max = MB_CUR_MAX;
+ do
+ {
+ len1 = mbtowc (&c1, mbs1, mb_cur_max);
+ len2 = mbtowc (&c2, mbs2, mb_cur_max);
+
+ if (len1 == 0)
+ return len2 == 0 ? 0 : -1;
+ else if (len2 == 0)
+ return 1;
+ else if (len1 > 0 && len2 < 0)
+ return -1;
+ else if (len1 < 0 && len2 > 0)
+ return 1;
+ else if (len1 < 0 && len2 < 0)
+ {
+ len1 = strlen (mbs1);
+ len2 = strlen (mbs2);
+ return (len1 == len2 ? memcmp (mbs1, mbs2, len1)
+ : ((len1 < len2) ? (memcmp (mbs1, mbs2, len1) > 0 ? 1 : -1)
+ : (memcmp (mbs1, mbs2, len2) >= 0 ? 1 : -1)));
+ }
+
+ l1 = towlower (c1);
+ l2 = towlower (c2);
+
+ mbs1 += len1;
+ mbs2 += len2;
+ }
+ while (l1 == l2);
+
+ return l1 - l2;
+}
+
+#endif
diff --git a/lib/sh/mbschr.c b/lib/sh/mbschr.c
new file mode 100644
index 0000000..639962d
--- /dev/null
+++ b/lib/sh/mbschr.c
@@ -0,0 +1,91 @@
+/* mbschr.c - strchr(3) that handles multibyte characters. */
+
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "bashansi.h"
+#include "shmbutil.h"
+
+extern int locale_mb_cur_max;
+extern int locale_utf8locale;
+
+#undef mbschr
+
+extern char *utf8_mbschr (const char *, int); /* XXX */
+
+/* In some locales, the non-first byte of some multibyte characters have
+ the same value as some ascii character. Faced with these strings, a
+ legacy strchr() might return the wrong value. */
+
+char *
+#if defined (PROTOTYPES)
+mbschr (const char *s, int c)
+#else
+mbschr (s, c)
+ const char *s;
+ int c;
+#endif
+{
+#if HANDLE_MULTIBYTE
+ char *pos;
+ mbstate_t state;
+ size_t strlength, mblength;
+
+ if (locale_utf8locale && c < 0x80)
+ return (utf8_mbschr (s, c)); /* XXX */
+
+ /* The locale encodings with said weird property are BIG5, BIG5-HKSCS,
+ GBK, GB18030, SHIFT_JIS, and JOHAB. They exhibit the problem only
+ when c >= 0x30. We can therefore use the faster bytewise search if
+ c <= 0x30. */
+ if ((unsigned char)c >= '0' && locale_mb_cur_max > 1)
+ {
+ pos = (char *)s;
+ memset (&state, '\0', sizeof(mbstate_t));
+ strlength = strlen (s);
+
+ while (strlength > 0)
+ {
+ if (is_basic (*pos))
+ mblength = 1;
+ else
+ {
+ mblength = mbrlen (pos, strlength, &state);
+ if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
+ mblength = 1;
+ }
+
+ if (mblength == 1 && c == (unsigned char)*pos)
+ return pos;
+
+ strlength -= mblength;
+ pos += mblength;
+ }
+
+ return ((char *)NULL);
+ }
+ else
+#endif
+ return (strchr (s, c));
+}
diff --git a/lib/sh/mbscmp.c b/lib/sh/mbscmp.c
new file mode 100644
index 0000000..c7c8443
--- /dev/null
+++ b/lib/sh/mbscmp.c
@@ -0,0 +1,77 @@
+/* mbscmp - multibyte string comparison. */
+
+/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_MBSCMP) && defined (HANDLE_MULTIBYTE)
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+extern int locale_utf8locale;
+
+extern int utf8_mbscmp (const char *, const char *);
+
+/* Compare MBS1 and MBS2. */
+int
+mbscmp (mbs1, mbs2)
+ const char *mbs1;
+ const char *mbs2;
+{
+ int len1, len2, mb_cur_max;
+ wchar_t c1, c2;
+
+ len1 = len2 = 0;
+ /* Reset multibyte characters to their initial state. */
+ (void) mblen ((char *) NULL, 0);
+
+ mb_cur_max = MB_CUR_MAX;
+ do
+ {
+ len1 = mbtowc (&c1, mbs1, mb_cur_max);
+ len2 = mbtowc (&c2, mbs2, mb_cur_max);
+
+ if (len1 == 0)
+ return len2 == 0 ? 0 : -1;
+ else if (len2 == 0)
+ return 1;
+ else if (len1 > 0 && len2 < 0)
+ return -1;
+ else if (len1 < 0 && len2 > 0)
+ return 1;
+ else if (len1 < 0 && len2 < 0)
+ {
+ len1 = strlen (mbs1);
+ len2 = strlen (mbs2);
+ return (len1 == len2 ? memcmp (mbs1, mbs2, len1)
+ : ((len1 < len2) ? (memcmp (mbs1, mbs2, len1) > 0 ? 1 : -1)
+ : (memcmp (mbs1, mbs2, len2) >= 0 ? 1 : -1)));
+ }
+
+ mbs1 += len1;
+ mbs2 += len2;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
+
+#endif
diff --git a/lib/sh/memset.c b/lib/sh/memset.c
new file mode 100644
index 0000000..4ebc418
--- /dev/null
+++ b/lib/sh/memset.c
@@ -0,0 +1,29 @@
+/* memset.c -- set an area of memory to a given value */
+
+/* Copyright (C) 1991-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+char *
+memset (char *str, int c, unsigned int len)
+{
+ register char *st = str;
+
+ while (len-- > 0)
+ *st++ = c;
+ return str;
+}
diff --git a/lib/sh/mktime.c b/lib/sh/mktime.c
new file mode 100644
index 0000000..9ee675b
--- /dev/null
+++ b/lib/sh/mktime.c
@@ -0,0 +1,438 @@
+/* mktime - convert struct tm to a time_t value */
+
+/* Copyright (C) 1993-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+ Contributed by Paul Eggert (eggert@twinsun.com).
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+/* Define this to have a standalone program to test this implementation of
+ mktime. */
+/* #define DEBUG 1 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _LIBC
+# define HAVE_LIMITS_H 1
+# define HAVE_LOCALTIME_R 1
+# define STDC_HEADERS 1
+#endif
+
+/* Assume that leap seconds are possible, unless told otherwise.
+ If the host has a `zic' command with a `-L leapsecondfilename' option,
+ then it supports leap seconds; otherwise it probably doesn't. */
+#ifndef LEAP_SECONDS_POSSIBLE
+#define LEAP_SECONDS_POSSIBLE 1
+#endif
+
+#ifndef VMS
+#include <sys/types.h> /* Some systems define `time_t' here. */
+#endif
+#include <time.h>
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "bashansi.h"
+
+#if DEBUG_MKTIME
+#include <stdio.h>
+/* Make it work even if the system's libc has its own mktime routine. */
+#define mktime my_mktime
+#endif /* DEBUG_MKTIME */
+
+#ifndef PARAMS
+#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#define PARAMS(args) args
+#else
+#define PARAMS(args) ()
+#endif /* GCC. */
+#endif /* Not PARAMS. */
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef INT_MIN
+#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
+#endif
+#ifndef INT_MAX
+#define INT_MAX (~0 - INT_MIN)
+#endif
+
+/* True if the arithmetic type T is signed. */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+/* The maximum and minimum values for the integer type T. These
+ macros have undefined behavior if T is signed and has padding bits.
+ If this is a problem for you, please let us know how to fix it for
+ your host. */
+#define TYPE_MINIMUM(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) 0 \
+ : ~ TYPE_MAXIMUM (t)))
+#define TYPE_MAXIMUM(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) -1 \
+ : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
+
+#ifndef TIME_T_MIN
+# define TIME_T_MIN TYPE_MINIMUM (time_t)
+#endif
+#ifndef TIME_T_MAX
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
+#endif
+
+#define TM_YEAR_BASE 1900
+#define EPOCH_YEAR 1970
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+ except every 100th isn't, and every 400th is). */
+#define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#endif
+
+/* How many days come before each month (0-12). */
+const unsigned short int __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+
+static time_t ydhms_tm_diff PARAMS ((int, int, int, int, int, const struct tm *));
+time_t __mktime_internal PARAMS ((struct tm *,
+ struct tm *(*) (const time_t *, struct tm *),
+ time_t *));
+
+
+static struct tm *my_localtime_r PARAMS ((const time_t *, struct tm *));
+static struct tm *
+my_localtime_r (t, tp)
+ const time_t *t;
+ struct tm *tp;
+{
+ struct tm *l = localtime (t);
+ if (! l)
+ return 0;
+ *tp = *l;
+ return tp;
+}
+
+
+/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
+ measured in seconds, ignoring leap seconds.
+ YEAR uses the same numbering as TM->tm_year.
+ All values are in range, except possibly YEAR.
+ If overflow occurs, yield the low order bits of the correct answer. */
+static time_t
+ydhms_tm_diff (year, yday, hour, min, sec, tp)
+ int year, yday, hour, min, sec;
+ const struct tm *tp;
+{
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow. time_t overflow is OK, since
+ only the low order bits of the correct time_t answer are needed.
+ Don't convert to time_t until after all divisions are done, since
+ time_t might be unsigned. */
+ int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
+ int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ time_t years = year - (time_t) tp->tm_year;
+ time_t days = (365 * years + intervening_leap_days
+ + (yday - tp->tm_yday));
+ return (60 * (60 * (24 * days + (hour - tp->tm_hour))
+ + (min - tp->tm_min))
+ + (sec - tp->tm_sec));
+}
+
+
+static time_t localtime_offset;
+
+/* Convert *TP to a time_t value. */
+time_t
+mktime (tp)
+ struct tm *tp;
+{
+#ifdef _LIBC
+ /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
+ time zone names contained in the external variable `tzname' shall
+ be set as if the tzset() function had been called. */
+ __tzset ();
+#endif
+
+ return __mktime_internal (tp, my_localtime_r, &localtime_offset);
+}
+
+/* Convert *TP to a time_t value, inverting
+ the monotonic and mostly-unit-linear conversion function CONVERT.
+ Use *OFFSET to keep track of a guess at the offset of the result,
+ compared to what the result would be for UTC without leap seconds.
+ If *OFFSET's guess is correct, only one CONVERT call is needed. */
+time_t
+__mktime_internal (tp, convert, offset)
+ struct tm *tp;
+ struct tm *(*convert) PARAMS ((const time_t *, struct tm *));
+ time_t *offset;
+{
+ time_t t, dt, t0;
+ struct tm tm;
+
+ /* The maximum number of probes (calls to CONVERT) should be enough
+ to handle any combinations of time zone rule changes, solar time,
+ and leap seconds. Posix.1 prohibits leap seconds, but some hosts
+ have them anyway. */
+ int remaining_probes = 4;
+
+ /* Time requested. Copy it in case CONVERT modifies *TP; this can
+ occur if TP is localtime's returned value and CONVERT is localtime. */
+ int sec = tp->tm_sec;
+ int min = tp->tm_min;
+ int hour = tp->tm_hour;
+ int mday = tp->tm_mday;
+ int mon = tp->tm_mon;
+ int year_requested = tp->tm_year;
+ int isdst = tp->tm_isdst;
+
+ /* Ensure that mon is in range, and set year accordingly. */
+ int mon_remainder = mon % 12;
+ int negative_mon_remainder = mon_remainder < 0;
+ int mon_years = mon / 12 - negative_mon_remainder;
+ int year = year_requested + mon_years;
+
+ /* The other values need not be in range:
+ the remaining code handles minor overflows correctly,
+ assuming int and time_t arithmetic wraps around.
+ Major overflows are caught at the end. */
+
+ /* Calculate day of year from year, month, and day of month.
+ The result need not be in range. */
+ int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
+ [mon_remainder + 12 * negative_mon_remainder])
+ + mday - 1);
+
+#if LEAP_SECONDS_POSSIBLE
+ /* Handle out-of-range seconds specially,
+ since ydhms_tm_diff assumes every minute has 60 seconds. */
+ int sec_requested = sec;
+ if (sec < 0)
+ sec = 0;
+ if (59 < sec)
+ sec = 59;
+#endif
+
+ /* Invert CONVERT by probing. First assume the same offset as last time.
+ Then repeatedly use the error to improve the guess. */
+
+ tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
+ tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
+
+ for (t = t0 + *offset;
+ (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
+ t += dt)
+ if (--remaining_probes == 0)
+ return -1;
+
+ /* Check whether tm.tm_isdst has the requested value, if any. */
+ if (0 <= isdst && 0 <= tm.tm_isdst)
+ {
+ int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
+ if (dst_diff)
+ {
+ /* Move two hours in the direction indicated by the disagreement,
+ probe some more, and switch to a new time if found.
+ The largest known fallback due to daylight savings is two hours:
+ once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
+ time_t ot = t - 2 * 60 * 60 * dst_diff;
+ while (--remaining_probes != 0)
+ {
+ struct tm otm;
+ if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
+ (*convert) (&ot, &otm))))
+ {
+ t = ot;
+ tm = otm;
+ break;
+ }
+ if ((ot += dt) == t)
+ break; /* Avoid a redundant probe. */
+ }
+ }
+ }
+
+ *offset = t - t0;
+
+#if LEAP_SECONDS_POSSIBLE
+ if (sec_requested != tm.tm_sec)
+ {
+ /* Adjust time to reflect the tm_sec requested, not the normalized value.
+ Also, repair any damage from a false match due to a leap second. */
+ t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
+ (*convert) (&t, &tm);
+ }
+#endif
+
+ if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
+ {
+ /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
+ so check for major overflows. A gross check suffices,
+ since if t has overflowed, it is off by a multiple of
+ TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
+ the difference that is bounded by a small value. */
+
+ double dyear = (double) year_requested + mon_years - tm.tm_year;
+ double dday = 366 * dyear + mday;
+ double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
+
+ if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
+ return -1;
+ }
+
+ *tp = tm;
+ return t;
+}
+
+#ifdef weak_alias
+weak_alias (mktime, timelocal)
+#endif
+
+#if DEBUG_MKTIME
+
+static int
+not_equal_tm (a, b)
+ struct tm *a;
+ struct tm *b;
+{
+ return ((a->tm_sec ^ b->tm_sec)
+ | (a->tm_min ^ b->tm_min)
+ | (a->tm_hour ^ b->tm_hour)
+ | (a->tm_mday ^ b->tm_mday)
+ | (a->tm_mon ^ b->tm_mon)
+ | (a->tm_year ^ b->tm_year)
+ | (a->tm_mday ^ b->tm_mday)
+ | (a->tm_yday ^ b->tm_yday)
+ | (a->tm_isdst ^ b->tm_isdst));
+}
+
+static void
+print_tm (tp)
+ struct tm *tp;
+{
+ printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
+ tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec,
+ tp->tm_yday, tp->tm_wday, tp->tm_isdst);
+}
+
+static int
+check_result (tk, tmk, tl, tml)
+ time_t tk;
+ struct tm tmk;
+ time_t tl;
+ struct tm tml;
+{
+ if (tk != tl || not_equal_tm (&tmk, &tml))
+ {
+ printf ("mktime (");
+ print_tm (&tmk);
+ printf (")\nyields (");
+ print_tm (&tml);
+ printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int status = 0;
+ struct tm tm, tmk, tml;
+ time_t tk, tl;
+ char trailer;
+
+ if ((argc == 3 || argc == 4)
+ && (sscanf (argv[1], "%d-%d-%d%c",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
+ == 3)
+ && (sscanf (argv[2], "%d:%d:%d%c",
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
+ == 3))
+ {
+ tm.tm_year -= TM_YEAR_BASE;
+ tm.tm_mon--;
+ tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
+ tmk = tm;
+ tl = mktime (&tmk);
+ tml = *localtime (&tl);
+ printf ("mktime returns %ld == ", (long) tl);
+ print_tm (&tmk);
+ printf ("\n");
+ status = check_result (tl, tmk, tl, tml);
+ }
+ else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
+ {
+ time_t from = atol (argv[1]);
+ time_t by = atol (argv[2]);
+ time_t to = atol (argv[3]);
+
+ if (argc == 4)
+ for (tl = from; tl <= to; tl += by)
+ {
+ tml = *localtime (&tl);
+ tmk = tml;
+ tk = mktime (&tmk);
+ status |= check_result (tk, tmk, tl, tml);
+ }
+ else
+ for (tl = from; tl <= to; tl += by)
+ {
+ /* Null benchmark. */
+ tml = *localtime (&tl);
+ tmk = tml;
+ tk = tl;
+ status |= check_result (tk, tmk, tl, tml);
+ }
+ }
+ else
+ printf ("Usage:\
+\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
+\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
+\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
+ argv[0], argv[0], argv[0]);
+
+ return status;
+}
+
+#endif /* DEBUG_MKTIME */
+
+/*
+Local Variables:
+compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
+End:
+*/
diff --git a/lib/sh/netconn.c b/lib/sh/netconn.c
new file mode 100644
index 0000000..e20f104
--- /dev/null
+++ b/lib/sh/netconn.c
@@ -0,0 +1,82 @@
+/* netconn.c -- is a particular file descriptor a network connection?. */
+
+/* Copyright (C) 2002-2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include <posixstat.h>
+#include <filecntl.h>
+
+#include <errno.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+/* The second and subsequent conditions must match those used to decide
+ whether or not to call getpeername() in isnetconn(). */
+#if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2)
+# include <sys/socket.h>
+#endif
+
+/* Is FD a socket or network connection? */
+int
+isnetconn (fd)
+ int fd;
+{
+#if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2) && !defined (__BEOS__)
+ int rv;
+ socklen_t l;
+ struct sockaddr sa;
+
+ l = sizeof(sa);
+ rv = getpeername(fd, &sa, &l);
+ /* Posix.2 says getpeername can return these errors. */
+ return ((rv < 0 && (errno == ENOTSOCK || errno == ENOTCONN || errno == EINVAL || errno == EBADF)) ? 0 : 1);
+#else /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
+# if defined (SVR4) || defined (SVR4_2)
+ /* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */
+ struct stat sb;
+
+ if (isatty (fd))
+ return (0);
+ if (fstat (fd, &sb) < 0)
+ return (0);
+# if defined (S_ISFIFO)
+ if (S_ISFIFO (sb.st_mode))
+ return (0);
+# endif /* S_ISFIFO */
+ return (S_ISCHR (sb.st_mode));
+# else /* !SVR4 && !SVR4_2 */
+# if defined (S_ISSOCK) && !defined (__BEOS__)
+ struct stat sb;
+
+ if (fstat (fd, &sb) < 0)
+ return (0);
+ return (S_ISSOCK (sb.st_mode));
+# else /* !S_ISSOCK || __BEOS__ */
+ return (0);
+# endif /* !S_ISSOCK || __BEOS__ */
+# endif /* !SVR4 && !SVR4_2 */
+#endif /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
+}
diff --git a/lib/sh/netopen.c b/lib/sh/netopen.c
new file mode 100644
index 0000000..ee0baf6
--- /dev/null
+++ b/lib/sh/netopen.c
@@ -0,0 +1,351 @@
+/*
+ * netopen.c -- functions to make tcp/udp connections
+ *
+ * Chet Ramey
+ * chet@ins.CWRU.Edu
+ */
+
+/* Copyright (C) 1987-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_NETWORK)
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#if defined (HAVE_SYS_SOCKET_H)
+# include <sys/socket.h>
+#endif
+
+#if defined (HAVE_NETINET_IN_H)
+# include <netinet/in.h>
+#endif
+
+#if defined (HAVE_NETDB_H)
+# include <netdb.h>
+#endif
+
+#if defined (HAVE_ARPA_INET_H)
+# include <arpa/inet.h>
+#endif
+
+#include <bashansi.h>
+#include <bashintl.h>
+
+#include <errno.h>
+
+#include <shell.h>
+#include <xmalloc.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#if !defined (HAVE_INET_ATON)
+extern int inet_aton PARAMS((const char *, struct in_addr *));
+#endif
+
+#ifndef HAVE_GETADDRINFO
+static int _getaddr PARAMS((char *, struct in_addr *));
+static int _getserv PARAMS((char *, int, unsigned short *));
+static int _netopen4 PARAMS((char *, char *, int));
+#else /* HAVE_GETADDRINFO */
+static int _netopen6 PARAMS((char *, char *, int));
+#endif
+
+static int _netopen PARAMS((char *, char *, int));
+
+#ifndef HAVE_GETADDRINFO
+/* Stuff the internet address corresponding to HOST into AP, in network
+ byte order. Return 1 on success, 0 on failure. */
+
+static int
+_getaddr (host, ap)
+ char *host;
+ struct in_addr *ap;
+{
+ struct hostent *h;
+ int r;
+
+ r = 0;
+ if (host[0] >= '0' && host[0] <= '9')
+ {
+ /* If the first character is a digit, guess that it's an
+ Internet address and return immediately if inet_aton succeeds. */
+ r = inet_aton (host, ap);
+ if (r)
+ return r;
+ }
+#if !defined (HAVE_GETHOSTBYNAME)
+ return 0;
+#else
+ h = gethostbyname (host);
+ if (h && h->h_addr)
+ {
+ bcopy(h->h_addr, (char *)ap, h->h_length);
+ return 1;
+ }
+#endif
+ return 0;
+
+}
+
+/* Return 1 if SERV is a valid port number and stuff the converted value into
+ PP in network byte order. */
+static int
+_getserv (serv, proto, pp)
+ char *serv;
+ int proto;
+ unsigned short *pp;
+{
+ intmax_t l;
+ unsigned short s;
+
+ if (legal_number (serv, &l))
+ {
+ s = (unsigned short)(l & 0xFFFF);
+ if (s != l)
+ return (0);
+ s = htons (s);
+ if (pp)
+ *pp = s;
+ return 1;
+ }
+ else
+#if defined (HAVE_GETSERVBYNAME)
+ {
+ struct servent *se;
+
+ se = getservbyname (serv, (proto == 't') ? "tcp" : "udp");
+ if (se == 0)
+ return 0;
+ if (pp)
+ *pp = se->s_port; /* ports returned in network byte order */
+ return 1;
+ }
+#else /* !HAVE_GETSERVBYNAME */
+ return 0;
+#endif /* !HAVE_GETSERVBYNAME */
+}
+
+/*
+ * Open a TCP or UDP connection to HOST on port SERV. Uses the
+ * traditional BSD mechanisms. Returns the connected socket or -1 on error.
+ */
+static int
+_netopen4(host, serv, typ)
+ char *host, *serv;
+ int typ;
+{
+ struct in_addr ina;
+ struct sockaddr_in sin;
+ unsigned short p;
+ int s, e;
+
+ if (_getaddr(host, &ina) == 0)
+ {
+ internal_error (_("%s: host unknown"), host);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (_getserv(serv, typ, &p) == 0)
+ {
+ internal_error(_("%s: invalid service"), serv);
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset ((char *)&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = p;
+ sin.sin_addr = ina;
+
+ s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0);
+ if (s < 0)
+ {
+ sys_error ("socket");
+ return (-1);
+ }
+
+ if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0)
+ {
+ e = errno;
+ sys_error("connect");
+ close(s);
+ errno = e;
+ return (-1);
+ }
+
+ return(s);
+}
+#endif /* ! HAVE_GETADDRINFO */
+
+#ifdef HAVE_GETADDRINFO
+/*
+ * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3)
+ * which provides support for IPv6. Returns the connected socket or -1
+ * on error.
+ */
+static int
+_netopen6 (host, serv, typ)
+ char *host, *serv;
+ int typ;
+{
+ int s, e;
+ struct addrinfo hints, *res, *res0;
+ int gerr;
+
+ memset ((char *)&hints, 0, sizeof (hints));
+ /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */
+#ifdef DEBUG /* PF_INET is the one that works for me */
+ hints.ai_family = PF_INET;
+#else
+ hints.ai_family = PF_UNSPEC;
+#endif
+ hints.ai_socktype = (typ == 't') ? SOCK_STREAM : SOCK_DGRAM;
+
+ gerr = getaddrinfo (host, serv, &hints, &res0);
+ if (gerr)
+ {
+ if (gerr == EAI_SERVICE)
+ internal_error ("%s: %s", serv, gai_strerror (gerr));
+ else
+ internal_error ("%s: %s", host, gai_strerror (gerr));
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (res = res0; res; res = res->ai_next)
+ {
+ if ((s = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
+ {
+ if (res->ai_next)
+ continue;
+ sys_error ("socket");
+ freeaddrinfo (res0);
+ return -1;
+ }
+ if (connect (s, res->ai_addr, res->ai_addrlen) < 0)
+ {
+ if (res->ai_next)
+ {
+ close (s);
+ continue;
+ }
+ e = errno;
+ sys_error ("connect");
+ close (s);
+ freeaddrinfo (res0);
+ errno = e;
+ return -1;
+ }
+ freeaddrinfo (res0);
+ break;
+ }
+ return s;
+}
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3)
+ * if available, falling back to the traditional BSD mechanisms otherwise.
+ * Returns the connected socket or -1 on error.
+ */
+static int
+_netopen(host, serv, typ)
+ char *host, *serv;
+ int typ;
+{
+#ifdef HAVE_GETADDRINFO
+ return (_netopen6 (host, serv, typ));
+#else
+ return (_netopen4 (host, serv, typ));
+#endif
+}
+
+/*
+ * Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to
+ * host `host' on port `port' and return the connected socket.
+ */
+int
+netopen (path)
+ char *path;
+{
+ char *np, *s, *t;
+ int fd;
+
+ np = (char *)xmalloc (strlen (path) + 1);
+ strcpy (np, path);
+
+ s = np + 9;
+ t = strchr (s, '/');
+ if (t == 0)
+ {
+ internal_error (_("%s: bad network path specification"), path);
+ free (np);
+ return -1;
+ }
+ *t++ = '\0';
+ fd = _netopen (s, t, path[5]);
+ free (np);
+
+ return fd;
+}
+
+#if 0
+/*
+ * Open a TCP connection to host `host' on the port defined for service
+ * `serv' and return the connected socket.
+ */
+int
+tcpopen (host, serv)
+ char *host, *serv;
+{
+ return (_netopen (host, serv, 't'));
+}
+
+/*
+ * Open a UDP connection to host `host' on the port defined for service
+ * `serv' and return the connected socket.
+ */
+int
+udpopen (host, serv)
+ char *host, *serv;
+{
+ return _netopen (host, serv, 'u');
+}
+#endif
+
+#else /* !HAVE_NETWORK */
+
+int
+netopen (path)
+ char *path;
+{
+ internal_error (_("network operations not supported"));
+ return -1;
+}
+
+#endif /* !HAVE_NETWORK */
diff --git a/lib/sh/oslib.c b/lib/sh/oslib.c
new file mode 100644
index 0000000..edc5c6f
--- /dev/null
+++ b/lib/sh/oslib.c
@@ -0,0 +1,301 @@
+/* oslib.c - functions present only in some unix versions. */
+
+/* Copyright (C) 1995,2010 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#include <posixstat.h>
+#include <filecntl.h>
+#include <bashansi.h>
+
+#if !defined (HAVE_KILLPG)
+# include <signal.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <chartypes.h>
+
+#include <shell.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* Make the functions strchr and strrchr if they do not exist. */
+#if !defined (HAVE_STRCHR)
+char *
+strchr (string, c)
+ char *string;
+ int c;
+{
+ register char *s;
+
+ for (s = string; s && *s; s++)
+ if (*s == c)
+ return (s);
+
+ return ((char *) NULL);
+}
+
+char *
+strrchr (string, c)
+ char *string;
+ int c;
+{
+ register char *s, *t;
+
+ for (s = string, t = (char *)NULL; s && *s; s++)
+ if (*s == c)
+ t = s;
+ return (t);
+}
+#endif /* !HAVE_STRCHR */
+
+#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
+/* Replacement for dup2 (), for those systems which either don't have it,
+ or supply one with broken behaviour. */
+int
+dup2 (fd1, fd2)
+ int fd1, fd2;
+{
+ int saved_errno, r;
+
+ /* If FD1 is not a valid file descriptor, then return immediately with
+ an error. */
+ if (fcntl (fd1, F_GETFL, 0) == -1)
+ return (-1);
+
+ if (fd2 < 0 || fd2 >= getdtablesize ())
+ {
+ errno = EBADF;
+ return (-1);
+ }
+
+ if (fd1 == fd2)
+ return (0);
+
+ saved_errno = errno;
+
+ (void) close (fd2);
+ r = fcntl (fd1, F_DUPFD, fd2);
+
+ if (r >= 0)
+ errno = saved_errno;
+ else
+ if (errno == EINVAL)
+ errno = EBADF;
+
+ /* Force the new file descriptor to remain open across exec () calls. */
+ SET_OPEN_ON_EXEC (fd2);
+ return (r);
+}
+#endif /* !HAVE_DUP2 */
+
+/*
+ * Return the total number of available file descriptors.
+ *
+ * On some systems, like 4.2BSD and its descendants, there is a system call
+ * that returns the size of the descriptor table: getdtablesize(). There are
+ * lots of ways to emulate this on non-BSD systems.
+ *
+ * On System V.3, this can be obtained via a call to ulimit:
+ * return (ulimit(4, 0L));
+ *
+ * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
+ * (this is what we assume below), so we can simply use it:
+ * return (NOFILE);
+ *
+ * On POSIX systems, there are specific functions for retrieving various
+ * configuration parameters:
+ * return (sysconf(_SC_OPEN_MAX));
+ *
+ */
+
+#if !defined (HAVE_GETDTABLESIZE)
+int
+getdtablesize ()
+{
+# if defined (_POSIX_VERSION) && defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
+ return (sysconf(_SC_OPEN_MAX)); /* Posix systems use sysconf */
+# else /* ! (_POSIX_VERSION && HAVE_SYSCONF && _SC_OPEN_MAX) */
+# if defined (ULIMIT_MAXFDS)
+ return (ulimit (4, 0L)); /* System V.3 systems use ulimit(4, 0L) */
+# else /* !ULIMIT_MAXFDS */
+# if defined (NOFILE) /* Other systems use NOFILE */
+ return (NOFILE);
+# else /* !NOFILE */
+ return (20); /* XXX - traditional value is 20 */
+# endif /* !NOFILE */
+# endif /* !ULIMIT_MAXFDS */
+# endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
+}
+#endif /* !HAVE_GETDTABLESIZE */
+
+#if !defined (HAVE_BCOPY)
+# if defined (bcopy)
+# undef bcopy
+# endif
+void
+bcopy (s,d,n)
+ void *d, *s;
+ size_t n;
+{
+ FASTCOPY (s, d, n);
+}
+#endif /* !HAVE_BCOPY */
+
+#if !defined (HAVE_BZERO)
+# if defined (bzero)
+# undef bzero
+# endif
+void
+bzero (s, n)
+ void *s;
+ size_t n;
+{
+ register int i;
+ register char *r;
+
+ for (i = 0, r = s; i < n; i++)
+ *r++ = '\0';
+}
+#endif
+
+#if !defined (HAVE_GETHOSTNAME)
+# if defined (HAVE_UNAME)
+# include <sys/utsname.h>
+int
+gethostname (name, namelen)
+ char *name;
+ size_t namelen;
+{
+ int i;
+ struct utsname ut;
+
+ --namelen;
+
+ uname (&ut);
+ i = strlen (ut.nodename) + 1;
+ strncpy (name, ut.nodename, i < namelen ? i : namelen);
+ name[namelen] = '\0';
+ return (0);
+}
+# else /* !HAVE_UNAME */
+int
+gethostname (name, namelen)
+ char *name;
+ size_t namelen;
+{
+ strncpy (name, "unknown", namelen);
+ name[namelen] = '\0';
+ return 0;
+}
+# endif /* !HAVE_UNAME */
+#endif /* !HAVE_GETHOSTNAME */
+
+#if !defined (HAVE_KILLPG)
+int
+killpg (pgrp, sig)
+ pid_t pgrp;
+ int sig;
+{
+ return (kill (-pgrp, sig));
+}
+#endif /* !HAVE_KILLPG */
+
+#if !defined (HAVE_MKFIFO) && defined (PROCESS_SUBSTITUTION)
+int
+mkfifo (path, mode)
+ char *path;
+ mode_t mode;
+{
+#if defined (S_IFIFO)
+ return (mknod (path, (mode | S_IFIFO), 0));
+#else /* !S_IFIFO */
+ return (-1);
+#endif /* !S_IFIFO */
+}
+#endif /* !HAVE_MKFIFO && PROCESS_SUBSTITUTION */
+
+#define DEFAULT_MAXGROUPS 64
+
+int
+getmaxgroups ()
+{
+ static int maxgroups = -1;
+
+ if (maxgroups > 0)
+ return maxgroups;
+
+#if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
+ maxgroups = sysconf (_SC_NGROUPS_MAX);
+#else
+# if defined (NGROUPS_MAX)
+ maxgroups = NGROUPS_MAX;
+# else /* !NGROUPS_MAX */
+# if defined (NGROUPS)
+ maxgroups = NGROUPS;
+# else /* !NGROUPS */
+ maxgroups = DEFAULT_MAXGROUPS;
+# endif /* !NGROUPS */
+# endif /* !NGROUPS_MAX */
+#endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */
+
+ if (maxgroups <= 0)
+ maxgroups = DEFAULT_MAXGROUPS;
+
+ return maxgroups;
+}
+
+long
+getmaxchild ()
+{
+ static long maxchild = -1L;
+
+ if (maxchild > 0)
+ return maxchild;
+
+#if defined (HAVE_SYSCONF) && defined (_SC_CHILD_MAX)
+ maxchild = sysconf (_SC_CHILD_MAX);
+#else
+# if defined (CHILD_MAX)
+ maxchild = CHILD_MAX;
+# else
+# if defined (MAXUPRC)
+ maxchild = MAXUPRC;
+# endif /* MAXUPRC */
+# endif /* CHILD_MAX */
+#endif /* !HAVE_SYSCONF || !_SC_CHILD_MAX */
+
+ return (maxchild);
+}
diff --git a/lib/sh/pathcanon.c b/lib/sh/pathcanon.c
new file mode 100644
index 0000000..7d0df9f
--- /dev/null
+++ b/lib/sh/pathcanon.c
@@ -0,0 +1,234 @@
+/* pathcanon.c -- canonicalize and manipulate pathnames. */
+
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+#include <posixstat.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <filecntl.h>
+#include <bashansi.h>
+#include <stdio.h>
+#include <chartypes.h>
+#include <errno.h>
+
+#include "shell.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if defined (__CYGWIN__)
+#include <sys/cygwin.h>
+
+static int
+_is_cygdrive (path)
+ char *path;
+{
+ static char user[MAXPATHLEN];
+ static char system[MAXPATHLEN];
+ static int first_time = 1;
+
+ /* If the path is the first part of a network path, treat it as
+ existing. */
+ if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/'))
+ return 1;
+ /* Otherwise check for /cygdrive prefix. */
+ if (first_time)
+ {
+ char user_flags[MAXPATHLEN];
+ char system_flags[MAXPATHLEN];
+ /* Get the cygdrive info */
+ cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags);
+ first_time = 0;
+ }
+ return !strcasecmp (path, user) || !strcasecmp (path, system);
+}
+#endif /* __CYGWIN__ */
+
+/* Return 1 if PATH corresponds to a directory. A function for debugging. */
+static int
+_path_isdir (path)
+ char *path;
+{
+ int l;
+ struct stat sb;
+
+ /* This should leave errno set to the correct value. */
+ errno = 0;
+ l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode);
+#if defined (__CYGWIN__)
+ if (l == 0)
+ l = _is_cygdrive (path);
+#endif
+ return l;
+}
+
+/* Canonicalize PATH, and return a new path. The new path differs from PATH
+ in that:
+ Multiple `/'s are collapsed to a single `/'.
+ Leading `./'s and trailing `/.'s are removed.
+ Trailing `/'s are removed.
+ Non-leading `../'s and trailing `..'s are handled by removing
+ portions of the path. */
+
+/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
+
+#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
+
+char *
+sh_canonpath (path, flags)
+ char *path;
+ int flags;
+{
+ char stub_char;
+ char *result, *p, *q, *base, *dotdot;
+ int rooted, double_slash_path;
+
+ /* The result cannot be larger than the input PATH. */
+ result = (flags & PATH_NOALLOC) ? path : savestring (path);
+
+ /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
+ leading `x:' (dos drive name). */
+ if (rooted = ROOTEDPATH(path))
+ {
+ stub_char = DIRSEP;
+#if defined (__CYGWIN__)
+ base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1;
+#else
+ base = result + 1;
+#endif
+ double_slash_path = DOUBLE_SLASH (path);
+ base += double_slash_path;
+ }
+ else
+ {
+ stub_char = '.';
+#if defined (__CYGWIN__)
+ base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result;
+#else
+ base = result;
+#endif
+ double_slash_path = 0;
+ }
+
+ /*
+ * invariants:
+ * base points to the portion of the path we want to modify
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ * dotdot points just past the point where .. cannot backtrack
+ * any further (no slash).
+ */
+ p = q = dotdot = base;
+
+ while (*p)
+ {
+ if (ISDIRSEP(p[0])) /* null element */
+ p++;
+ else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
+ p += 1; /* don't count the separator in case it is nul */
+ else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
+ {
+ p += 2; /* skip `..' */
+ if (q > dotdot) /* can backtrack */
+ {
+ if (flags & PATH_CHECKDOTDOT)
+ {
+ char c;
+
+ /* Make sure what we have so far corresponds to a valid
+ path before we chop some of it off. */
+ c = *q;
+ *q = '\0';
+ if (_path_isdir (result) == 0)
+ {
+ if ((flags & PATH_NOALLOC) == 0)
+ free (result);
+ return ((char *)NULL);
+ }
+ *q = c;
+ }
+
+ while (--q > dotdot && ISDIRSEP(*q) == 0)
+ ;
+ }
+ else if (rooted == 0)
+ {
+ /* /.. is / but ./../ is .. */
+ if (q != base)
+ *q++ = DIRSEP;
+ *q++ = '.';
+ *q++ = '.';
+ dotdot = q;
+ }
+ }
+ else /* real path element */
+ {
+ /* add separator if not at start of work portion of result */
+ if (q != base)
+ *q++ = DIRSEP;
+ while (*p && (ISDIRSEP(*p) == 0))
+ *q++ = *p++;
+ /* Check here for a valid directory with _path_isdir. */
+ if (flags & PATH_CHECKEXISTS)
+ {
+ char c;
+
+ /* Make sure what we have so far corresponds to a valid
+ path before we chop some of it off. */
+ c = *q;
+ *q = '\0';
+ if (_path_isdir (result) == 0)
+ {
+ if ((flags & PATH_NOALLOC) == 0)
+ free (result);
+ return ((char *)NULL);
+ }
+ *q = c;
+ }
+ }
+ }
+
+ /* Empty string is really ``.'' or `/', depending on what we started with. */
+ if (q == result)
+ *q++ = stub_char;
+ *q = '\0';
+
+ /* If the result starts with `//', but the original path does not, we
+ can turn the // into /. Because of how we set `base', this should never
+ be true, but it's a sanity check. */
+ if (DOUBLE_SLASH(result) && double_slash_path == 0)
+ {
+ if (result[2] == '\0') /* short-circuit for bare `//' */
+ result[1] = '\0';
+ else
+ memmove (result, result + 1, strlen (result + 1) + 1);
+ }
+
+ return (result);
+}
diff --git a/lib/sh/pathphys.c b/lib/sh/pathphys.c
new file mode 100644
index 0000000..95b72f1
--- /dev/null
+++ b/lib/sh/pathphys.c
@@ -0,0 +1,296 @@
+/* pathphys.c -- return pathname with all symlinks expanded. */
+
+/* Copyright (C) 2000-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+#include <posixstat.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <filecntl.h>
+#include <bashansi.h>
+#include <stdio.h>
+#include <chartypes.h>
+#include <errno.h>
+
+#include "shell.h"
+
+#if !defined (MAXSYMLINKS)
+# define MAXSYMLINKS 32
+#endif
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern char *get_working_directory PARAMS((char *));
+
+static int
+_path_readlink (path, buf, bufsiz)
+ char *path;
+ char *buf;
+ int bufsiz;
+{
+#ifdef HAVE_READLINK
+ return readlink (path, buf, bufsiz);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
+
+#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
+
+/*
+ * Return PATH with all symlinks expanded in newly-allocated memory.
+ * This always gets an absolute pathname.
+ */
+
+char *
+sh_physpath (path, flags)
+ char *path;
+ int flags;
+{
+ char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
+ char *result, *p, *q, *qsave, *qbase, *workpath;
+ int double_slash_path, linklen, nlink;
+
+ linklen = strlen (path);
+
+#if 0
+ /* First sanity check -- punt immediately if the name is too long. */
+ if (linklen >= PATH_MAX)
+ return (savestring (path));
+#endif
+
+ nlink = 0;
+ q = result = (char *)xmalloc (PATH_MAX + 1);
+
+ /* Even if we get something longer than PATH_MAX, we might be able to
+ shorten it, so we try. */
+ if (linklen >= PATH_MAX)
+ workpath = savestring (path);
+ else
+ {
+ workpath = (char *)xmalloc (PATH_MAX + 1);
+ strcpy (workpath, path);
+ }
+
+ /* This always gets an absolute pathname. */
+
+ /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
+ leading `x:' (dos drive name). */
+#if defined (__CYGWIN__)
+ qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
+#else
+ qbase = workpath + 1;
+#endif
+ double_slash_path = DOUBLE_SLASH (workpath);
+ qbase += double_slash_path;
+
+ for (p = workpath; p < qbase; )
+ *q++ = *p++;
+ qbase = q;
+
+ /*
+ * invariants:
+ * qbase points to the portion of the result path we want to modify
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ *
+ * XXX -- need to fix error checking for too-long pathnames
+ */
+
+ while (*p)
+ {
+ if (ISDIRSEP(p[0])) /* null element */
+ p++;
+ else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
+ p += 1; /* don't count the separator in case it is nul */
+ else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
+ {
+ p += 2; /* skip `..' */
+ if (q > qbase)
+ {
+ while (--q > qbase && ISDIRSEP(*q) == 0)
+ ;
+ }
+ }
+ else /* real path element */
+ {
+ /* add separator if not at start of work portion of result */
+ qsave = q;
+ if (q != qbase)
+ *q++ = DIRSEP;
+ while (*p && (ISDIRSEP(*p) == 0))
+ {
+ if (q - result >= PATH_MAX)
+ {
+#ifdef ENAMETOOLONG
+ errno = ENAMETOOLONG;
+#else
+ errno = EINVAL;
+#endif
+ goto error;
+ }
+
+ *q++ = *p++;
+ }
+
+ *q = '\0';
+
+ linklen = _path_readlink (result, linkbuf, PATH_MAX);
+ if (linklen < 0) /* if errno == EINVAL, it's not a symlink */
+ {
+ if (errno != EINVAL)
+ goto error;
+ continue;
+ }
+
+ /* It's a symlink, and the value is in LINKBUF. */
+ nlink++;
+ if (nlink > MAXSYMLINKS)
+ {
+#ifdef ELOOP
+ errno = ELOOP;
+#else
+ errno = EINVAL;
+#endif
+error:
+ free (result);
+ free (workpath);
+ return ((char *)NULL);
+ }
+
+ linkbuf[linklen] = '\0';
+
+ /* If the new path length would overrun PATH_MAX, punt now. */
+ if ((strlen (p) + linklen + 2) >= PATH_MAX)
+ {
+#ifdef ENAMETOOLONG
+ errno = ENAMETOOLONG;
+#else
+ errno = EINVAL;
+#endif
+ goto error;
+ }
+
+ /* Form the new pathname by copying the link value to a temporary
+ buffer and appending the rest of `workpath'. Reset p to point
+ to the start of the rest of the path. If the link value is an
+ absolute pathname, reset p, q, and qbase. If not, reset p
+ and q. */
+ strcpy (tbuf, linkbuf);
+ tbuf[linklen] = '/';
+ strcpy (tbuf + linklen, p);
+ strcpy (workpath, tbuf);
+
+ if (ABSPATH(linkbuf))
+ {
+ q = result;
+ /* Duplicating some code here... */
+#if defined (__CYGWIN__)
+ qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
+#else
+ qbase = workpath + 1;
+#endif
+ double_slash_path = DOUBLE_SLASH (workpath);
+ qbase += double_slash_path;
+
+ for (p = workpath; p < qbase; )
+ *q++ = *p++;
+ qbase = q;
+ }
+ else
+ {
+ p = workpath;
+ q = qsave;
+ }
+ }
+ }
+
+ *q = '\0';
+ free (workpath);
+
+ /* If the result starts with `//', but the original path does not, we
+ can turn the // into /. Because of how we set `qbase', this should never
+ be true, but it's a sanity check. */
+ if (DOUBLE_SLASH(result) && double_slash_path == 0)
+ {
+ if (result[2] == '\0') /* short-circuit for bare `//' */
+ result[1] = '\0';
+ else
+ memmove (result, result + 1, strlen (result + 1) + 1);
+ }
+
+ return (result);
+}
+
+char *
+sh_realpath (pathname, resolved)
+ const char *pathname;
+ char *resolved;
+{
+ char *tdir, *wd;
+
+ if (pathname == 0 || *pathname == '\0')
+ {
+ errno = (pathname == 0) ? EINVAL : ENOENT;
+ return ((char *)NULL);
+ }
+
+ if (ABSPATH (pathname) == 0)
+ {
+ wd = get_working_directory ("sh_realpath");
+ if (wd == 0)
+ return ((char *)NULL);
+ tdir = sh_makepath (wd, (char *)pathname, 0);
+ free (wd);
+ }
+ else
+ tdir = savestring (pathname);
+
+ wd = sh_physpath (tdir, 0);
+ free (tdir);
+
+ if (resolved == 0)
+ return (wd);
+
+ if (wd)
+ {
+ strncpy (resolved, wd, PATH_MAX - 1);
+ resolved[PATH_MAX - 1] = '\0';
+ free (wd);
+ return resolved;
+ }
+ else
+ {
+ resolved[0] = '\0';
+ return wd;
+ }
+}
diff --git a/lib/sh/random.c b/lib/sh/random.c
new file mode 100644
index 0000000..1eaa71a
--- /dev/null
+++ b/lib/sh/random.c
@@ -0,0 +1,240 @@
+/* random.c -- Functions for managing 16-bit and 32-bit random numbers. */
+
+/* Copyright (C) 2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_SYS_RANDOM_H)
+# include <sys/random.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include "filecntl.h"
+
+#include <stdio.h>
+#include "bashansi.h"
+
+#include "shell.h"
+
+extern time_t shell_start_time;
+
+extern int last_random_value;
+
+static u_bits32_t intrand32 PARAMS((u_bits32_t));
+static u_bits32_t genseed PARAMS((void));
+
+static u_bits32_t brand32 PARAMS((void));
+static void sbrand32 PARAMS((u_bits32_t));
+static void perturb_rand32 PARAMS((void));
+
+/* The random number seed. You can change this by setting RANDOM. */
+static u_bits32_t rseed = 1;
+
+/* Returns a 32-bit pseudo-random number. */
+static u_bits32_t
+intrand32 (last)
+ u_bits32_t last;
+{
+ /* Minimal Standard generator from
+ "Random number generators: good ones are hard to find",
+ Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ October 1988, p. 1195. Filtered through FreeBSD.
+
+ x(n+1) = 16807 * x(n) mod (m).
+
+ We split up the calculations to avoid overflow.
+
+ h = last / q; l = x - h * q; t = a * l - h * r
+ m = 2147483647, a = 16807, q = 127773, r = 2836
+
+ There are lots of other combinations of constants to use; look at
+ https://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html#Other-random-number-generators */
+
+ bits32_t h, l, t;
+ u_bits32_t ret;
+
+ /* Can't seed with 0. */
+ ret = (last == 0) ? 123459876 : last;
+ h = ret / 127773;
+ l = ret - (127773 * h);
+ t = 16807 * l - 2836 * h;
+ ret = (t < 0) ? t + 0x7fffffff : t;
+
+ return (ret);
+}
+
+static u_bits32_t
+genseed ()
+{
+ struct timeval tv;
+ u_bits32_t iv;
+
+ gettimeofday (&tv, NULL);
+ iv = (u_bits32_t)seedrand; /* let the compiler truncate */
+ iv = tv.tv_sec ^ tv.tv_usec ^ getpid () ^ getppid () ^ current_user.uid ^ iv;
+ return (iv);
+}
+
+#define BASH_RAND_MAX 32767 /* 0x7fff - 16 bits */
+
+/* Returns a pseudo-random number between 0 and 32767. */
+int
+brand ()
+{
+ unsigned int ret;
+
+ rseed = intrand32 (rseed);
+ if (shell_compatibility_level > 50)
+ ret = (rseed >> 16) ^ (rseed & 65535);
+ else
+ ret = rseed;
+ return (ret & BASH_RAND_MAX);
+}
+
+/* Set the random number generator seed to SEED. */
+void
+sbrand (seed)
+ unsigned long seed;
+{
+ rseed = seed;
+ last_random_value = 0;
+}
+
+void
+seedrand ()
+{
+ u_bits32_t iv;
+
+ iv = genseed ();
+ sbrand (iv);
+}
+
+static u_bits32_t rseed32 = 1073741823;
+static int last_rand32;
+
+static int urandfd = -1;
+
+#define BASH_RAND32_MAX 0x7fffffff /* 32 bits */
+
+/* Returns a 32-bit pseudo-random number between 0 and 4294967295. */
+static u_bits32_t
+brand32 ()
+{
+ u_bits32_t ret;
+
+ rseed32 = intrand32 (rseed32);
+ return (rseed32 & BASH_RAND32_MAX);
+}
+
+static void
+sbrand32 (seed)
+ u_bits32_t seed;
+{
+ last_rand32 = rseed32 = seed;
+}
+
+void
+seedrand32 ()
+{
+ u_bits32_t iv;
+
+ iv = genseed ();
+ sbrand32 (iv);
+}
+
+static void
+perturb_rand32 ()
+{
+ rseed32 ^= genseed ();
+}
+
+/* Force another attempt to open /dev/urandom on the next call to get_urandom32 */
+void
+urandom_close ()
+{
+ if (urandfd >= 0)
+ close (urandfd);
+ urandfd = -1;
+}
+
+#if !defined (HAVE_GETRANDOM)
+/* Imperfect emulation of getrandom(2). */
+#ifndef GRND_NONBLOCK
+# define GRND_NONBLOCK 1
+# define GRND_RANDOM 2
+#endif
+
+static ssize_t
+getrandom (buf, len, flags)
+ void *buf;
+ size_t len;
+ unsigned int flags;
+{
+ int oflags;
+ ssize_t r;
+ static int urand_unavail = 0;
+
+#if HAVE_GETENTROPY
+ r = getentropy (buf, len);
+ return (r == 0) ? len : -1;
+#endif
+
+ if (urandfd == -1 && urand_unavail == 0)
+ {
+ oflags = O_RDONLY;
+ if (flags & GRND_NONBLOCK)
+ oflags |= O_NONBLOCK;
+ urandfd = open ("/dev/urandom", oflags, 0);
+ if (urandfd >= 0)
+ SET_CLOSE_ON_EXEC (urandfd);
+ else
+ {
+ urand_unavail = 1;
+ return -1;
+ }
+ }
+ if (urandfd >= 0 && (r = read (urandfd, buf, len)) == len)
+ return (r);
+ return -1;
+}
+#endif
+
+u_bits32_t
+get_urandom32 ()
+{
+ u_bits32_t ret;
+
+ if (getrandom ((void *)&ret, sizeof (ret), GRND_NONBLOCK) == sizeof (ret))
+ return (last_rand32 = ret);
+
+#if defined (HAVE_ARC4RANDOM)
+ ret = arc4random ();
+#else
+ if (subshell_environment)
+ perturb_rand32 ();
+ do
+ ret = brand32 ();
+ while (ret == last_rand32);
+#endif
+ return (last_rand32 = ret);
+}
diff --git a/lib/sh/rename.c b/lib/sh/rename.c
new file mode 100644
index 0000000..e410b5e
--- /dev/null
+++ b/lib/sh/rename.c
@@ -0,0 +1,76 @@
+/*
+ * rename - rename a file
+ */
+
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_RENAME)
+
+#include <bashtypes.h>
+#include <posixstat.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include <errno.h>
+
+#include <stdc.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+int
+rename (from, to)
+ const char *from, *to;
+{
+ struct stat fb, tb;
+
+ if (stat (from, &fb) < 0)
+ return -1;
+
+ if (stat (to, &tb) < 0)
+ {
+ if (errno != ENOENT)
+ return -1;
+ }
+ else
+ {
+ if (fb.st_dev == tb.st_dev && fb.st_ino == tb.st_ino)
+ return 0; /* same file */
+ if (unlink (to) < 0 && errno != ENOENT)
+ return -1;
+ }
+
+ if (link (from, to) < 0)
+ return (-1);
+
+ if (unlink (from) < 0 && errno != ENOENT)
+ {
+ int e = errno;
+ unlink (to);
+ errno = e;
+ return (-1);
+ }
+
+ return (0);
+}
+#endif /* !HAVE_RENAME */
diff --git a/lib/sh/setlinebuf.c b/lib/sh/setlinebuf.c
new file mode 100644
index 0000000..dd76e9f
--- /dev/null
+++ b/lib/sh/setlinebuf.c
@@ -0,0 +1,66 @@
+/* setlinebuf.c - line-buffer a stdio stream. */
+
+/* Copyright (C) 1997,2022 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <xmalloc.h>
+
+#if defined (USING_BASH_MALLOC)
+# define LBUF_BUFSIZE 2016
+#else
+# define LBUF_BUFSIZE BUFSIZ
+#endif
+
+static char *stdoutbuf = 0;
+static char *stderrbuf = 0;
+
+/* Cause STREAM to buffer lines as opposed to characters or blocks. */
+int
+sh_setlinebuf (stream)
+ FILE *stream;
+{
+#if !defined (HAVE_SETLINEBUF) && !defined (HAVE_SETVBUF)
+ return (0);
+#endif
+
+#if defined (HAVE_SETVBUF)
+ char *local_linebuf;
+
+#if defined (USING_BASH_MALLOC)
+ if (stream == stdout && stdoutbuf == 0)
+ local_linebuf = stdoutbuf = (char *)xmalloc (LBUF_BUFSIZE);
+ else if (stream == stderr && stderrbuf == 0)
+ local_linebuf = stderrbuf = (char *)xmalloc (LBUF_BUFSIZE);
+ else
+ local_linebuf = (char *)NULL; /* let stdio handle it */
+#else
+ local_linebuf = (char *)NULL;
+#endif
+
+ return (setvbuf (stream, local_linebuf, _IOLBF, LBUF_BUFSIZE));
+#else /* !HAVE_SETVBUF */
+
+ setlinebuf (stream);
+ return (0);
+
+#endif /* !HAVE_SETVBUF */
+}
diff --git a/lib/sh/shmatch.c b/lib/sh/shmatch.c
new file mode 100644
index 0000000..a717d45
--- /dev/null
+++ b/lib/sh/shmatch.c
@@ -0,0 +1,132 @@
+/*
+ * shmatch.c -- shell interface to posix regular expression matching.
+ */
+
+/* Copyright (C) 2003-2022 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined (HAVE_POSIX_REGEXP)
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#include <stdio.h>
+#include <regex.h>
+
+#include "shell.h"
+#include "variables.h"
+#include "externs.h"
+
+extern int glob_ignore_case, match_ignore_case;
+
+#if defined (ARRAY_VARS)
+extern SHELL_VAR *builtin_find_indexed_array (char *, int);
+#endif
+
+int
+sh_regmatch (string, pattern, flags)
+ const char *string;
+ const char *pattern;
+ int flags;
+{
+ regex_t regex = { 0 };
+ regmatch_t *matches;
+ int rflags;
+#if defined (ARRAY_VARS)
+ SHELL_VAR *rematch;
+ ARRAY *amatch;
+ int subexp_ind;
+ char *subexp_str;
+ int subexp_len;
+#endif
+ int result;
+
+#if defined (ARRAY_VARS)
+ rematch = (SHELL_VAR *)NULL;
+#endif
+
+ rflags = REG_EXTENDED;
+ if (match_ignore_case)
+ rflags |= REG_ICASE;
+#if !defined (ARRAY_VARS)
+ rflags |= REG_NOSUB;
+#endif
+
+ if (regcomp (&regex, pattern, rflags))
+ return 2; /* flag for printing a warning here. */
+
+#if defined (ARRAY_VARS)
+ matches = (regmatch_t *)malloc (sizeof (regmatch_t) * (regex.re_nsub + 1));
+#else
+ matches = NULL;
+#endif
+
+ /* man regexec: NULL PMATCH ignored if NMATCH == 0 */
+ if (regexec (&regex, string, matches ? regex.re_nsub + 1 : 0, matches, 0))
+ result = EXECUTION_FAILURE;
+ else
+ result = EXECUTION_SUCCESS; /* match */
+
+#if defined (ARRAY_VARS)
+ subexp_len = strlen (string) + 10;
+ subexp_str = malloc (subexp_len + 1);
+
+ /* Store the parenthesized subexpressions in the array BASH_REMATCH.
+ Element 0 is the portion that matched the entire regexp. Element 1
+ is the part that matched the first subexpression, and so on. */
+#if 1
+ unbind_global_variable_noref ("BASH_REMATCH");
+ rematch = make_new_array_variable ("BASH_REMATCH");
+#else
+ /* TAG:bash-5.3 */
+ rematch = builtin_find_indexed_array ("BASH_REMATCH", 1);
+#endif
+ amatch = rematch ? array_cell (rematch) : (ARRAY *)0;
+
+ if (matches && amatch && (flags & SHMAT_SUBEXP) && result == EXECUTION_SUCCESS && subexp_str)
+ {
+ for (subexp_ind = 0; subexp_ind <= regex.re_nsub; subexp_ind++)
+ {
+ memset (subexp_str, 0, subexp_len);
+ strncpy (subexp_str, string + matches[subexp_ind].rm_so,
+ matches[subexp_ind].rm_eo - matches[subexp_ind].rm_so);
+ array_insert (amatch, subexp_ind, subexp_str);
+ }
+ }
+
+#if 0
+ VSETATTR (rematch, att_readonly);
+#endif
+
+ free (subexp_str);
+ free (matches);
+#endif /* ARRAY_VARS */
+
+ regfree (&regex);
+
+ return result;
+}
+
+#endif /* HAVE_POSIX_REGEXP */
diff --git a/lib/sh/shmbchar.c b/lib/sh/shmbchar.c
new file mode 100644
index 0000000..f2f2582
--- /dev/null
+++ b/lib/sh/shmbchar.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2001, 2006, 2009, 2010, 2012, 2015-2018 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include <config.h>
+
+#if defined (HANDLE_MULTIBYTE)
+#include <stdlib.h>
+#include <limits.h>
+
+#include <errno.h>
+
+#include <shmbutil.h>
+#include <shmbchar.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#if IS_BASIC_ASCII
+
+/* Bit table of characters in the ISO C "basic character set". */
+const unsigned int is_basic_table [UCHAR_MAX / 32 + 1] =
+{
+ 0x00001a00, /* '\t' '\v' '\f' */
+ 0xffffffef, /* ' '...'#' '%'...'?' */
+ 0xfffffffe, /* 'A'...'Z' '[' '\\' ']' '^' '_' */
+ 0x7ffffffe /* 'a'...'z' '{' '|' '}' '~' */
+ /* The remaining bits are 0. */
+};
+
+#endif /* IS_BASIC_ASCII */
+
+extern int locale_utf8locale;
+
+extern char *utf8_mbsmbchar (const char *);
+extern int utf8_mblen (const char *, size_t);
+
+/* Count the number of characters in S, counting multi-byte characters as a
+ single character. */
+size_t
+mbstrlen (s)
+ const char *s;
+{
+ size_t clen, nc;
+ mbstate_t mbs = { 0 }, mbsbak = { 0 };
+ int f, mb_cur_max;
+
+ nc = 0;
+ mb_cur_max = MB_CUR_MAX;
+ while (*s && (clen = (f = is_basic (*s)) ? 1 : mbrlen(s, mb_cur_max, &mbs)) != 0)
+ {
+ if (MB_INVALIDCH(clen))
+ {
+ clen = 1; /* assume single byte */
+ mbs = mbsbak;
+ }
+
+ if (f == 0)
+ mbsbak = mbs;
+
+ s += clen;
+ nc++;
+ }
+ return nc;
+}
+
+/* Return pointer to first multibyte char in S, or NULL if none. */
+/* XXX - if we know that the locale is UTF-8, we can just check whether or
+ not any byte has the eighth bit turned on */
+char *
+mbsmbchar (s)
+ const char *s;
+{
+ char *t;
+ size_t clen;
+ mbstate_t mbs = { 0 };
+ int mb_cur_max;
+
+ if (locale_utf8locale)
+ return (utf8_mbsmbchar (s)); /* XXX */
+
+ mb_cur_max = MB_CUR_MAX;
+ for (t = (char *)s; *t; t++)
+ {
+ if (is_basic (*t))
+ continue;
+
+ if (locale_utf8locale) /* not used if above code active */
+ clen = utf8_mblen (t, mb_cur_max);
+ else
+ clen = mbrlen (t, mb_cur_max, &mbs);
+
+ if (clen == 0)
+ return 0;
+ if (MB_INVALIDCH(clen))
+ continue;
+
+ if (clen > 1)
+ return t;
+ }
+ return 0;
+}
+
+int
+sh_mbsnlen(src, srclen, maxlen)
+ const char *src;
+ size_t srclen;
+ int maxlen;
+{
+ int count;
+ int sind;
+ DECLARE_MBSTATE;
+
+ for (sind = count = 0; src[sind]; )
+ {
+ count++; /* number of multibyte characters */
+ ADVANCE_CHAR (src, srclen, sind);
+ if (sind > maxlen)
+ break;
+ }
+
+ return count;
+}
+#endif
diff --git a/lib/sh/shquote.c b/lib/sh/shquote.c
new file mode 100644
index 0000000..26fe018
--- /dev/null
+++ b/lib/sh/shquote.c
@@ -0,0 +1,432 @@
+/* shquote - functions to quote and dequote strings */
+
+/* Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdc.h>
+
+#include "syntax.h"
+#include <xmalloc.h>
+
+#include "shmbchar.h"
+#include "shmbutil.h"
+
+extern char *ansic_quote PARAMS((char *, int, int *));
+extern int ansic_shouldquote PARAMS((const char *));
+
+/* Default set of characters that should be backslash-quoted in strings */
+static const char bstab[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, /* TAB, NL */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 1, 1, 1, 0, 1, 0, 1, 1, /* SPACE, !, DQUOTE, DOL, AMP, SQUOTE */
+ 1, 1, 1, 0, 1, 0, 0, 0, /* LPAR, RPAR, STAR, COMMA */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 0, 1, 1, /* SEMI, LESSTHAN, GREATERTHAN, QUEST */
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 0, /* LBRACK, BS, RBRACK, CARAT */
+
+ 1, 0, 0, 0, 0, 0, 0, 0, /* BACKQ */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 0, 0, /* LBRACE, BAR, RBRACE */
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+/* **************************************************************** */
+/* */
+/* Functions for quoting strings to be re-read as input */
+/* */
+/* **************************************************************** */
+
+/* Return a new string which is the single-quoted version of STRING.
+ Used by alias and trap, among others. */
+char *
+sh_single_quote (string)
+ const char *string;
+{
+ register int c;
+ char *result, *r;
+ const char *s;
+
+ result = (char *)xmalloc (3 + (4 * strlen (string)));
+ r = result;
+
+ if (string[0] == '\'' && string[1] == 0)
+ {
+ *r++ = '\\';
+ *r++ = '\'';
+ *r++ = 0;
+ return result;
+ }
+
+ *r++ = '\'';
+
+ for (s = string; s && (c = *s); s++)
+ {
+ *r++ = c;
+
+ if (c == '\'')
+ {
+ *r++ = '\\'; /* insert escaped single quote */
+ *r++ = '\'';
+ *r++ = '\''; /* start new quoted string */
+ }
+ }
+
+ *r++ = '\'';
+ *r = '\0';
+
+ return (result);
+}
+
+/* Quote STRING using double quotes. Return a new string. */
+char *
+sh_double_quote (string)
+ const char *string;
+{
+ register unsigned char c;
+ int mb_cur_max;
+ char *result, *r;
+ size_t slen;
+ const char *s, *send;
+ DECLARE_MBSTATE;
+
+ slen = strlen (string);
+ send = string + slen;
+ mb_cur_max = MB_CUR_MAX;
+
+ result = (char *)xmalloc (3 + (2 * strlen (string)));
+ r = result;
+ *r++ = '"';
+
+ for (s = string; s && (c = *s); s++)
+ {
+ /* Backslash-newline disappears within double quotes, so don't add one. */
+ if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
+ *r++ = '\\';
+
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
+ {
+ COPY_CHAR_P (r, s, send);
+ s--; /* compensate for auto-increment in loop above */
+ continue;
+ }
+#endif
+
+ /* Assume that the string will not be further expanded, so no need to
+ add CTLESC to protect CTLESC or CTLNUL. */
+ *r++ = c;
+ }
+
+ *r++ = '"';
+ *r = '\0';
+
+ return (result);
+}
+
+/* Turn S into a simple double-quoted string. If FLAGS is non-zero, quote
+ double quote characters in S with backslashes. */
+char *
+sh_mkdoublequoted (s, slen, flags)
+ const char *s;
+ int slen, flags;
+{
+ char *r, *ret;
+ const char *send;
+ int rlen, mb_cur_max;
+ DECLARE_MBSTATE;
+
+ send = s + slen;
+ mb_cur_max = flags ? MB_CUR_MAX : 1;
+ rlen = (flags == 0) ? slen + 3 : (2 * slen) + 1;
+ ret = r = (char *)xmalloc (rlen);
+
+ *r++ = '"';
+ while (*s)
+ {
+ if (flags && *s == '"')
+ *r++ = '\\';
+
+#if defined (HANDLE_MULTIBYTE)
+ if (flags && ((locale_utf8locale && (*s & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (*s) == 0)))
+ {
+ COPY_CHAR_P (r, s, send);
+ continue;
+ }
+#endif
+ *r++ = *s++;
+ }
+ *r++ = '"';
+ *r = '\0';
+
+ return ret;
+}
+
+/* Remove backslashes that are quoting characters that are special between
+ double quotes. Return a new string. XXX - should this handle CTLESC
+ and CTLNUL? */
+char *
+sh_un_double_quote (string)
+ char *string;
+{
+ register int c, pass_next;
+ char *result, *r, *s;
+
+ r = result = (char *)xmalloc (strlen (string) + 1);
+
+ for (pass_next = 0, s = string; s && (c = *s); s++)
+ {
+ if (pass_next)
+ {
+ *r++ = c;
+ pass_next = 0;
+ continue;
+ }
+ if (c == '\\' && (sh_syntaxtab[(unsigned char) s[1]] & CBSDQUOTE))
+ {
+ pass_next = 1;
+ continue;
+ }
+ *r++ = c;
+ }
+
+ *r = '\0';
+ return result;
+}
+
+/* Quote special characters in STRING using backslashes. Return a new
+ string. NOTE: if the string is to be further expanded, we need a
+ way to protect the CTLESC and CTLNUL characters. As I write this,
+ the current callers will never cause the string to be expanded without
+ going through the shell parser, which will protect the internal
+ quoting characters. TABLE, if set, points to a map of the ascii code
+ set with char needing to be backslash-quoted if table[char]==1. FLAGS,
+ if 1, causes tildes to be quoted as well. If FLAGS&2, backslash-quote
+ other shell blank characters. */
+
+char *
+sh_backslash_quote (string, table, flags)
+ char *string;
+ char *table;
+ int flags;
+{
+ int c, mb_cur_max;
+ size_t slen;
+ char *result, *r, *s, *backslash_table, *send;
+ DECLARE_MBSTATE;
+
+ slen = strlen (string);
+ send = string + slen;
+ result = (char *)xmalloc (2 * slen + 1);
+
+ backslash_table = table ? table : (char *)bstab;
+ mb_cur_max = MB_CUR_MAX;
+
+ for (r = result, s = string; s && (c = *s); s++)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ /* XXX - isascii, even if is_basic(c) == 0 - works in most cases. */
+ if (c >= 0 && c <= 127 && backslash_table[(unsigned char)c] == 1)
+ {
+ *r++ = '\\';
+ *r++ = c;
+ continue;
+ }
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
+ {
+ COPY_CHAR_P (r, s, send);
+ s--; /* compensate for auto-increment in loop above */
+ continue;
+ }
+#endif
+ if (backslash_table[(unsigned char)c] == 1)
+ *r++ = '\\';
+ else if (c == '#' && s == string) /* comment char */
+ *r++ = '\\';
+ else if ((flags&1) && c == '~' && (s == string || s[-1] == ':' || s[-1] == '='))
+ /* Tildes are special at the start of a word or after a `:' or `='
+ (technically unquoted, but it doesn't make a difference in practice) */
+ *r++ = '\\';
+ else if ((flags&2) && shellblank((unsigned char)c))
+ *r++ = '\\';
+ *r++ = c;
+ }
+
+ *r = '\0';
+ return (result);
+}
+
+#if defined (PROMPT_STRING_DECODE) || defined (TRANSLATABLE_STRINGS)
+/* Quote characters that get special treatment when in double quotes in STRING
+ using backslashes. FLAGS is reserved for future use. Return a new string. */
+char *
+sh_backslash_quote_for_double_quotes (string, flags)
+ char *string;
+ int flags;
+{
+ unsigned char c;
+ char *result, *r, *s, *send;
+ size_t slen;
+ int mb_cur_max;
+ DECLARE_MBSTATE;
+
+ slen = strlen (string);
+ send = string + slen;
+ mb_cur_max = MB_CUR_MAX;
+ result = (char *)xmalloc (2 * slen + 1);
+
+ for (r = result, s = string; s && (c = *s); s++)
+ {
+ /* Backslash-newline disappears within double quotes, so don't add one. */
+ if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
+ *r++ = '\\';
+ /* I should probably use the CSPECL flag for these in sh_syntaxtab[] */
+ else if (c == CTLESC || c == CTLNUL)
+ *r++ = CTLESC; /* could be '\\'? */
+
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
+ {
+ COPY_CHAR_P (r, s, send);
+ s--; /* compensate for auto-increment in loop above */
+ continue;
+ }
+#endif
+
+ *r++ = c;
+ }
+
+ *r = '\0';
+ return (result);
+}
+#endif /* PROMPT_STRING_DECODE */
+
+char *
+sh_quote_reusable (s, flags)
+ char *s;
+ int flags;
+{
+ char *ret;
+
+ if (s == 0)
+ return s;
+ else if (*s == 0)
+ {
+ ret = (char *)xmalloc (3);
+ ret[0] = ret[1] = '\'';
+ ret[2] = '\0';
+ }
+ else if (ansic_shouldquote (s))
+ ret = ansic_quote (s, 0, (int *)0);
+ else if (flags)
+ ret = sh_backslash_quote (s, 0, 1);
+ else
+ ret = sh_single_quote (s);
+
+ return ret;
+}
+
+int
+sh_contains_shell_metas (string)
+ const char *string;
+{
+ const char *s;
+
+ for (s = string; s && *s; s++)
+ {
+ switch (*s)
+ {
+ case ' ': case '\t': case '\n': /* IFS white space */
+ case '\'': case '"': case '\\': /* quoting chars */
+ case '|': case '&': case ';': /* shell metacharacters */
+ case '(': case ')': case '<': case '>':
+ case '!': case '{': case '}': /* reserved words */
+ case '*': case '[': case '?': case ']': /* globbing chars */
+ case '^':
+ case '$': case '`': /* expansion chars */
+ return (1);
+ case '~': /* tilde expansion */
+ if (s == string || s[-1] == '=' || s[-1] == ':')
+ return (1);
+ break;
+ case '#':
+ if (s == string) /* comment char */
+ return (1);
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
+
+int
+sh_contains_quotes (string)
+ const char *string;
+{
+ const char *s;
+
+ for (s = string; s && *s; s++)
+ {
+ if (*s == '\'' || *s == '"' || *s == '\\')
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib/sh/shtty.c b/lib/sh/shtty.c
new file mode 100644
index 0000000..0433f5e
--- /dev/null
+++ b/lib/sh/shtty.c
@@ -0,0 +1,330 @@
+/*
+ * shtty.c -- abstract interface to the terminal, focusing on capabilities.
+ */
+
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <shtty.h>
+
+static TTYSTRUCT ttin, ttout;
+static int ttsaved = 0;
+
+int
+ttgetattr(fd, ttp)
+int fd;
+TTYSTRUCT *ttp;
+{
+#ifdef TERMIOS_TTY_DRIVER
+ return tcgetattr(fd, ttp);
+#else
+# ifdef TERMIO_TTY_DRIVER
+ return ioctl(fd, TCGETA, ttp);
+# else
+ return ioctl(fd, TIOCGETP, ttp);
+# endif
+#endif
+}
+
+int
+ttsetattr(fd, ttp)
+int fd;
+TTYSTRUCT *ttp;
+{
+#ifdef TERMIOS_TTY_DRIVER
+ return tcsetattr(fd, TCSADRAIN, ttp);
+#else
+# ifdef TERMIO_TTY_DRIVER
+ return ioctl(fd, TCSETAW, ttp);
+# else
+ return ioctl(fd, TIOCSETN, ttp);
+# endif
+#endif
+}
+
+void
+ttsave()
+{
+ if (ttsaved)
+ return;
+ ttgetattr (0, &ttin);
+ ttgetattr (1, &ttout);
+ ttsaved = 1;
+}
+
+void
+ttrestore()
+{
+ if (ttsaved == 0)
+ return;
+ ttsetattr (0, &ttin);
+ ttsetattr (1, &ttout);
+ ttsaved = 0;
+}
+
+/* Retrieve the internally-saved attributes associated with tty fd FD. */
+TTYSTRUCT *
+ttattr (fd)
+ int fd;
+{
+ if (ttsaved == 0)
+ return ((TTYSTRUCT *)0);
+ if (fd == 0)
+ return &ttin;
+ else if (fd == 1)
+ return &ttout;
+ else
+ return ((TTYSTRUCT *)0);
+}
+
+/*
+ * Change attributes in ttp so that when it is installed using
+ * ttsetattr, the terminal will be in one-char-at-a-time mode.
+ */
+int
+tt_setonechar(ttp)
+ TTYSTRUCT *ttp;
+{
+#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
+
+ /* XXX - might not want this -- it disables erase and kill processing. */
+ ttp->c_lflag &= ~ICANON;
+
+ ttp->c_lflag |= ISIG;
+# ifdef IEXTEN
+ ttp->c_lflag |= IEXTEN;
+# endif
+
+ ttp->c_iflag |= ICRNL; /* make sure we get CR->NL on input */
+ ttp->c_iflag &= ~INLCR; /* but no NL->CR */
+
+# ifdef OPOST
+ ttp->c_oflag |= OPOST;
+# endif
+# ifdef ONLCR
+ ttp->c_oflag |= ONLCR;
+# endif
+# ifdef OCRNL
+ ttp->c_oflag &= ~OCRNL;
+# endif
+# ifdef ONOCR
+ ttp->c_oflag &= ~ONOCR;
+# endif
+# ifdef ONLRET
+ ttp->c_oflag &= ~ONLRET;
+# endif
+
+ ttp->c_cc[VMIN] = 1;
+ ttp->c_cc[VTIME] = 0;
+
+#else
+
+ ttp->sg_flags |= CBREAK;
+
+#endif
+
+ return 0;
+}
+
+/* Set the tty associated with FD and TTP into one-character-at-a-time mode */
+int
+ttfd_onechar (fd, ttp)
+ int fd;
+ TTYSTRUCT *ttp;
+{
+ if (tt_setonechar(ttp) < 0)
+ return -1;
+ return (ttsetattr (fd, ttp));
+}
+
+/* Set the terminal into one-character-at-a-time mode */
+int
+ttonechar ()
+{
+ TTYSTRUCT tt;
+
+ if (ttsaved == 0)
+ return -1;
+ tt = ttin;
+ return (ttfd_onechar (0, &tt));
+}
+
+/*
+ * Change attributes in ttp so that when it is installed using
+ * ttsetattr, the terminal will be in no-echo mode.
+ */
+int
+tt_setnoecho(ttp)
+ TTYSTRUCT *ttp;
+{
+#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
+ ttp->c_lflag &= ~(ECHO|ECHOK|ECHONL);
+#else
+ ttp->sg_flags &= ~ECHO;
+#endif
+
+ return 0;
+}
+
+/* Set the tty associated with FD and TTP into no-echo mode */
+int
+ttfd_noecho (fd, ttp)
+ int fd;
+ TTYSTRUCT *ttp;
+{
+ if (tt_setnoecho (ttp) < 0)
+ return -1;
+ return (ttsetattr (fd, ttp));
+}
+
+/* Set the terminal into no-echo mode */
+int
+ttnoecho ()
+{
+ TTYSTRUCT tt;
+
+ if (ttsaved == 0)
+ return -1;
+ tt = ttin;
+ return (ttfd_noecho (0, &tt));
+}
+
+/*
+ * Change attributes in ttp so that when it is installed using
+ * ttsetattr, the terminal will be in eight-bit mode (pass8).
+ */
+int
+tt_seteightbit (ttp)
+ TTYSTRUCT *ttp;
+{
+#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
+ ttp->c_iflag &= ~ISTRIP;
+ ttp->c_cflag |= CS8;
+ ttp->c_cflag &= ~PARENB;
+#else
+ ttp->sg_flags |= ANYP;
+#endif
+
+ return 0;
+}
+
+/* Set the tty associated with FD and TTP into eight-bit mode */
+int
+ttfd_eightbit (fd, ttp)
+ int fd;
+ TTYSTRUCT *ttp;
+{
+ if (tt_seteightbit (ttp) < 0)
+ return -1;
+ return (ttsetattr (fd, ttp));
+}
+
+/* Set the terminal into eight-bit mode */
+int
+tteightbit ()
+{
+ TTYSTRUCT tt;
+
+ if (ttsaved == 0)
+ return -1;
+ tt = ttin;
+ return (ttfd_eightbit (0, &tt));
+}
+
+/*
+ * Change attributes in ttp so that when it is installed using
+ * ttsetattr, the terminal will be in non-canonical input mode.
+ */
+int
+tt_setnocanon (ttp)
+ TTYSTRUCT *ttp;
+{
+#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
+ ttp->c_lflag &= ~ICANON;
+#endif
+
+ return 0;
+}
+
+/* Set the tty associated with FD and TTP into non-canonical mode */
+int
+ttfd_nocanon (fd, ttp)
+ int fd;
+ TTYSTRUCT *ttp;
+{
+ if (tt_setnocanon (ttp) < 0)
+ return -1;
+ return (ttsetattr (fd, ttp));
+}
+
+/* Set the terminal into non-canonical mode */
+int
+ttnocanon ()
+{
+ TTYSTRUCT tt;
+
+ if (ttsaved == 0)
+ return -1;
+ tt = ttin;
+ return (ttfd_nocanon (0, &tt));
+}
+
+/*
+ * Change attributes in ttp so that when it is installed using
+ * ttsetattr, the terminal will be in cbreak, no-echo mode.
+ */
+int
+tt_setcbreak(ttp)
+ TTYSTRUCT *ttp;
+{
+ if (tt_setonechar (ttp) < 0)
+ return -1;
+ return (tt_setnoecho (ttp));
+}
+
+/* Set the tty associated with FD and TTP into cbreak (no-echo,
+ one-character-at-a-time) mode */
+int
+ttfd_cbreak (fd, ttp)
+ int fd;
+ TTYSTRUCT *ttp;
+{
+ if (tt_setcbreak (ttp) < 0)
+ return -1;
+ return (ttsetattr (fd, ttp));
+}
+
+/* Set the terminal into cbreak (no-echo, one-character-at-a-time) mode */
+int
+ttcbreak ()
+{
+ TTYSTRUCT tt;
+
+ if (ttsaved == 0)
+ return -1;
+ tt = ttin;
+ return (ttfd_cbreak (0, &tt));
+}
diff --git a/lib/sh/snprintf.c b/lib/sh/snprintf.c
new file mode 100644
index 0000000..1f0f4c7
--- /dev/null
+++ b/lib/sh/snprintf.c
@@ -0,0 +1,2221 @@
+/* snprintf - formatted output to strings, with bounds checking and allocation */
+
+/*
+ build a test version with
+ gcc -g -DDRIVER -I../.. -I../../include -o test-snprintf snprintf.c fmtu*long.o
+*/
+
+/*
+ Unix snprintf implementation.
+ derived from inetutils/libinetutils/snprintf.c Version 1.1
+
+ Copyright (C) 2001-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+
+ Original (pre-bash) Revision History:
+
+ 1.1:
+ * added changes from Miles Bader
+ * corrected a bug with %f
+ * added support for %#g
+ * added more comments :-)
+ 1.0:
+ * supporting must ANSI syntaxic_sugars
+ 0.0:
+ * support %s %c %d
+
+ THANKS(for the patches and ideas):
+ Miles Bader
+ Cyrille Rustom
+ Jacek Slabocewiz
+ Mike Parker(mouse)
+
+*/
+
+/*
+ * Currently doesn't handle (and bash/readline doesn't use):
+ * * *M$ width, precision specifications
+ * * %N$ numbered argument conversions
+ * * support for `F' is imperfect with ldfallback(), since underlying
+ * printf may not handle it -- should ideally have another autoconf test
+ */
+
+#define FLOATING_POINT
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* GCC 4.2 on Snow Leopard doesn't like the snprintf prototype */
+#if defined(DEBUG) && !defined (MACOSX)
+# undef HAVE_SNPRINTF
+# undef HAVE_ASPRINTF
+
+# define HAVE_SNPRINTF 0
+# define HAVE_ASPRINTF 0
+#endif
+
+#if defined(DRIVER) && !defined(HAVE_CONFIG_H)
+#define HAVE_LONG_LONG_INT
+#define HAVE_LONG_DOUBLE
+#ifdef __linux__
+#define HAVE_PRINTF_A_FORMAT
+#endif
+#define HAVE_ISINF_IN_LIBC
+#define HAVE_ISNAN_IN_LIBC
+#define PREFER_STDARG
+#define HAVE_STRINGIZE
+#define HAVE_LIMITS_H
+#define HAVE_STDDEF_H
+#define HAVE_LOCALE_H
+#define intmax_t long
+#endif
+
+#if !HAVE_SNPRINTF || !HAVE_ASPRINTF
+
+#include <bashtypes.h>
+
+#if defined(PREFER_STDARG)
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#include <bashansi.h>
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+#include <chartypes.h>
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifdef FLOATING_POINT
+# include <float.h> /* for manifest constants */
+# include <stdio.h> /* for sprintf */
+#endif
+
+#include <typemax.h>
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#include "stdc.h"
+#include <shmbutil.h>
+
+#ifndef DRIVER
+# include "shell.h"
+#else
+# define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */
+# define FL_ADDBASE 0x02 /* add base# prefix to converted value */
+# define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */
+# define FL_UNSIGNED 0x08 /* don't add any sign */
+extern char *fmtulong PARAMS((unsigned long int, int, char *, size_t, int));
+extern char *fmtullong PARAMS((unsigned long long int, int, char *, size_t, int));
+#endif
+
+#ifndef FREE
+# define FREE(x) if (x) free (x)
+#endif
+
+/* Bound on length of the string representing an integer value of type T.
+ Subtract one for the sign bit if T is signed;
+ 302 / 1000 is log10 (2) rounded up;
+ add one for integer division truncation;
+ add one more for a minus sign if t is signed. */
+#ifndef INT_STRLEN_BOUND
+#define INT_STRLEN_BOUND(t) \
+ ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \
+ + 1 + TYPE_SIGNED (t))
+#endif
+
+/* conversion flags */
+#define PF_ALTFORM 0x00001 /* # */
+#define PF_HEXPREFIX 0x00002 /* 0[Xx] */
+#define PF_LADJUST 0x00004 /* - */
+#define PF_ZEROPAD 0x00008 /* 0 */
+#define PF_PLUS 0x00010 /* + */
+#define PF_SPACE 0x00020 /* ' ' */
+#define PF_THOUSANDS 0x00040 /* ' */
+
+#define PF_DOT 0x00080 /* `.precision' */
+#define PF_STAR_P 0x00100 /* `*' after precision */
+#define PF_STAR_W 0x00200 /* `*' before or without precision */
+
+/* length modifiers */
+#define PF_SIGNEDCHAR 0x00400 /* hh */
+#define PF_SHORTINT 0x00800 /* h */
+#define PF_LONGINT 0x01000 /* l */
+#define PF_LONGLONG 0x02000 /* ll */
+#define PF_LONGDBL 0x04000 /* L */
+#define PF_INTMAX_T 0x08000 /* j */
+#define PF_SIZE_T 0x10000 /* z */
+#define PF_PTRDIFF_T 0x20000 /* t */
+
+#define PF_ALLOCBUF 0x40000 /* for asprintf, vasprintf */
+
+#define PFM_SN 0x01 /* snprintf, vsnprintf */
+#define PFM_AS 0x02 /* asprintf, vasprintf */
+
+#define ASBUFSIZE 128
+
+#define x_digs "0123456789abcdef"
+#define X_digs "0123456789ABCDEF"
+
+static char intbuf[INT_STRLEN_BOUND(unsigned long) + 1];
+
+static int decpoint;
+static int thoussep;
+static char *grouping;
+
+/*
+ * For the FLOATING POINT FORMAT :
+ * the challenge was finding a way to
+ * manipulate the Real numbers without having
+ * to resort to mathematical function(it
+ * would require to link with -lm) and not
+ * going down to the bit pattern(not portable)
+ *
+ * so a number, a real is:
+
+ real = integral + fraction
+
+ integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0
+ fraction = b(1)*10^-1 + b(2)*10^-2 + ...
+
+ where:
+ 0 <= a(i) => 9
+ 0 <= b(i) => 9
+
+ from then it was simple math
+ */
+
+/*
+ * size of the buffer for the integral part
+ * and the fraction part
+ */
+#define MAX_INT 99 + 1 /* 1 for the null */
+#define MAX_FRACT 307 + 1
+
+/*
+ * These functions use static buffers to store the results,
+ * and so are not reentrant
+ */
+#define itoa(n) fmtulong(n, 10, intbuf, sizeof(intbuf), 0);
+#define dtoa(n, p, f) numtoa(n, 10, p, f)
+
+#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}
+
+#define GETARG(type) (va_arg(args, type))
+
+/* Macros that do proper sign extension and handle length modifiers. Used
+ for the integer conversion specifiers. */
+#define GETSIGNED(p) \
+ (((p)->flags & PF_LONGINT) \
+ ? GETARG (long) \
+ : (((p)->flags & PF_SHORTINT) ? (long)(short)GETARG (int) \
+ : (long)GETARG (int)))
+
+#define GETUNSIGNED(p) \
+ (((p)->flags & PF_LONGINT) \
+ ? GETARG (unsigned long) \
+ : (((p)->flags & PF_SHORTINT) ? (unsigned long)(unsigned short)GETARG (int) \
+ : (unsigned long)GETARG (unsigned int)))
+
+
+#ifdef HAVE_LONG_DOUBLE
+#define GETLDOUBLE(p) GETARG (long double)
+#endif
+#define GETDOUBLE(p) GETARG (double)
+
+#define SET_SIZE_FLAGS(p, type) \
+ if (sizeof (type) > sizeof (int)) \
+ (p)->flags |= PF_LONGINT; \
+ if (sizeof (type) > sizeof (long)) \
+ (p)->flags |= PF_LONGLONG;
+
+/* this struct holds everything we need */
+struct DATA
+{
+ int length;
+ char *base; /* needed for [v]asprintf */
+ char *holder;
+ int counter;
+ const char *pf;
+
+/* FLAGS */
+ int flags;
+ int justify;
+ int width, precision;
+ char pad;
+};
+
+/* the floating point stuff */
+#ifdef FLOATING_POINT
+static double pow_10 PARAMS((int));
+static int log_10 PARAMS((double));
+static double integral PARAMS((double, double *));
+static char *numtoa PARAMS((double, int, int, char **));
+#endif
+
+static void init_data PARAMS((struct DATA *, char *, size_t, const char *, int));
+static void init_conv_flag PARAMS((struct DATA *));
+
+/* for the format */
+#ifdef FLOATING_POINT
+static void floating PARAMS((struct DATA *, double));
+static void exponent PARAMS((struct DATA *, double));
+#endif
+static void number PARAMS((struct DATA *, unsigned long, int));
+#ifdef HAVE_LONG_LONG_INT
+static void lnumber PARAMS((struct DATA *, unsigned long long, int));
+#endif
+static void pointer PARAMS((struct DATA *, unsigned long));
+static void strings PARAMS((struct DATA *, char *));
+
+#ifdef FLOATING_POINT
+# define FALLBACK_FMTSIZE 32
+# define FALLBACK_BASE 4096
+# define LFALLBACK_BASE 5120
+# ifdef HAVE_LONG_DOUBLE
+static void ldfallback PARAMS((struct DATA *, const char *, const char *, long double));
+# endif
+static void dfallback PARAMS((struct DATA *, const char *, const char *, double));
+#endif
+
+static char *groupnum PARAMS((char *));
+
+#if defined (HAVE_LONG_DOUBLE)
+# define LONGDOUBLE long double
+#else
+# define LONGDOUBLE double
+#endif
+
+#ifndef isnan
+ static inline int isnan_f (float x) { return x != x; }
+ static inline int isnan_d (double x) { return x != x; }
+ static inline int isnan_ld (LONGDOUBLE x) { return x != x; }
+ # define isnan(x) \
+ (sizeof (x) == sizeof (LONGDOUBLE) ? isnan_ld (x) \
+ : sizeof (x) == sizeof (double) ? isnan_d (x) \
+ : isnan_f (x))
+#endif
+
+#ifndef isinf
+ static inline int isinf_f (float x) { return !isnan (x) && isnan (x - x); }
+ static inline int isinf_d (double x) { return !isnan (x) && isnan (x - x); }
+ static inline int isinf_ld (LONGDOUBLE x) { return !isnan (x) && isnan (x - x); }
+ # define isinf(x) \
+ (sizeof (x) == sizeof (LONGDOUBLE) ? isinf_ld (x) \
+ : sizeof (x) == sizeof (double) ? isinf_d (x) \
+ : isinf_f (x))
+#endif
+
+#ifdef DRIVER
+static void memory_error_and_abort ();
+static void *xmalloc PARAMS((size_t));
+static void *xrealloc PARAMS((void *, size_t));
+static void xfree PARAMS((void *));
+#else
+# include <xmalloc.h>
+#endif
+
+/* those are defines specific to snprintf to hopefully
+ * make the code clearer :-)
+ */
+#define RIGHT 1
+#define LEFT 0
+#define NOT_FOUND -1
+#define FOUND 1
+#define MAX_FIELD 15
+
+/* round off to the precision */
+#define ROUND(d, p) \
+ (d < 0.) ? \
+ d - pow_10(-(p)->precision) * 0.5 : \
+ d + pow_10(-(p)->precision) * 0.5
+
+/* set default precision */
+#define DEF_PREC(p) \
+ if ((p)->precision == NOT_FOUND) \
+ (p)->precision = 6
+
+/* put a char. increment the number of chars written even if we've exceeded
+ the vsnprintf/snprintf buffer size (for the return value) */
+#define PUT_CHAR(c, p) \
+ do \
+ { \
+ if (((p)->flags & PF_ALLOCBUF) && ((p)->counter >= (p)->length - 1)) \
+ { \
+ (p)->length += ASBUFSIZE; \
+ (p)->base = (char *)xrealloc((p)->base, (p)->length); \
+ (p)->holder = (p)->base + (p)->counter; /* in case reallocated */ \
+ } \
+ if ((p)->counter < (p)->length) \
+ *(p)->holder++ = (c); \
+ (p)->counter++; \
+ } \
+ while (0)
+
+/* Output a string. P->WIDTH has already been adjusted for padding. */
+#define PUT_STRING(string, len, p) \
+ do \
+ { \
+ PAD_RIGHT (p); \
+ while ((len)-- > 0) \
+ { \
+ PUT_CHAR (*(string), (p)); \
+ (string)++; \
+ } \
+ PAD_LEFT (p); \
+ } \
+ while (0)
+
+#define PUT_PLUS(d, p, zero) \
+ if (((p)->flags & PF_PLUS) && (d) > zero) \
+ PUT_CHAR('+', p)
+
+#define PUT_SPACE(d, p, zero) \
+ if (((p)->flags & PF_SPACE) && (d) > zero) \
+ PUT_CHAR(' ', p)
+
+/* pad right */
+#define PAD_RIGHT(p) \
+ if ((p)->width > 0 && (p)->justify != LEFT) \
+ for (; (p)->width > 0; (p)->width--) \
+ PUT_CHAR((p)->pad, p)
+
+/* pad left */
+#define PAD_LEFT(p) \
+ if ((p)->width > 0 && (p)->justify == LEFT) \
+ for (; (p)->width > 0; (p)->width--) \
+ PUT_CHAR((p)->pad, p)
+
+/* pad with zeros from decimal precision */
+#define PAD_ZERO(p) \
+ if ((p)->precision > 0) \
+ for (; (p)->precision > 0; (p)->precision--) \
+ PUT_CHAR('0', p)
+
+/* if width and prec. in the args */
+#define STAR_ARGS(p) \
+ do { \
+ if ((p)->flags & PF_STAR_W) \
+ { \
+ (p)->width = GETARG (int); \
+ if ((p)->width < 0) \
+ { \
+ (p)->flags |= PF_LADJUST; \
+ (p)->justify = LEFT; \
+ (p)->width = -(p)->width; \
+ } \
+ } \
+ if ((p)->flags & PF_STAR_P) \
+ { \
+ (p)->precision = GETARG (int); \
+ if ((p)->precision < 0) \
+ { \
+ (p)->flags &= ~PF_STAR_P; \
+ (p)->precision = NOT_FOUND; \
+ } \
+ } \
+ } while (0)
+
+#if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV)
+# define GETLOCALEDATA(d, t, g) \
+ do \
+ { \
+ struct lconv *lv; \
+ if ((d) == 0) { \
+ (d) = '.'; (t) = -1; (g) = 0; /* defaults */ \
+ lv = localeconv(); \
+ if (lv) \
+ { \
+ if (lv->decimal_point && lv->decimal_point[0]) \
+ (d) = lv->decimal_point[0]; \
+ if (lv->thousands_sep && lv->thousands_sep[0]) \
+ (t) = lv->thousands_sep[0]; \
+ (g) = lv->grouping ? lv->grouping : ""; \
+ if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \
+ } \
+ } \
+ } \
+ while (0);
+#else
+# define GETLOCALEDATA(d, t, g) \
+ ( (d) = '.', (t) = ',', g = "\003" )
+#endif
+
+#ifdef FLOATING_POINT
+/*
+ * Find the nth power of 10
+ */
+static double
+pow_10(n)
+ int n;
+{
+ double P;
+
+ /* handle common cases with fast switch statement. */
+ switch (n)
+ {
+ case -3: return .001;
+ case -2: return .01;
+ case -1: return .1;
+ case 0: return 1.;
+ case 1: return 10.;
+ case 2: return 100.;
+ case 3: return 1000.;
+ }
+
+ if (n < 0)
+ {
+ P = .0001;
+ for (n += 4; n < 0; n++)
+ P /= 10.;
+ }
+ else
+ {
+ P = 10000.;
+ for (n -= 4; n > 0; n--)
+ P *= 10.;
+ }
+
+ return P;
+}
+
+/*
+ * Find the integral part of the log in base 10
+ * Note: this not a real log10()
+ I just need and approximation(integerpart) of x in:
+ 10^x ~= r
+ * log_10(200) = 2;
+ * log_10(250) = 2;
+ *
+ * NOTE: do not call this with r == 0 -- an infinite loop results.
+ */
+static int
+log_10(r)
+ double r;
+{
+ int i = 0;
+ double result = 1.;
+
+ if (r < 0.)
+ r = -r;
+
+ if (r < 1.)
+ {
+ while (result >= r)
+ {
+ result /= 10.;
+ i++;
+ }
+ return (-i);
+ }
+ else
+ {
+ while (result <= r)
+ {
+ result *= 10.;
+ i++;
+ }
+ return (i - 1);
+ }
+}
+
+/*
+ * This function return the fraction part of a double
+ * and set in ip the integral part.
+ * In many ways it resemble the modf() found on most Un*x
+ */
+static double
+integral(real, ip)
+ double real;
+ double *ip;
+{
+ int j;
+ double i, s, p;
+ double real_integral = 0.;
+
+ /* take care of the obvious */
+ /* equal to zero ? */
+ if (real == 0.)
+ {
+ *ip = 0.;
+ return (0.);
+ }
+
+ /* negative number ? */
+ if (real < 0.)
+ real = -real;
+
+ /* a fraction ? */
+ if ( real < 1.)
+ {
+ *ip = 0.;
+ return real;
+ }
+
+ /* the real work :-) */
+ for (j = log_10(real); j >= 0; j--)
+ {
+ p = pow_10(j);
+ s = (real - real_integral)/p;
+ i = 0.;
+ while (i + 1. <= s)
+ i++;
+ real_integral += i*p;
+ }
+ *ip = real_integral;
+ return (real - real_integral);
+}
+
+#define PRECISION 1.e-6
+/*
+ * return an ascii representation of the integral part of the number
+ * and set fract to be an ascii representation of the fraction part
+ * the container for the fraction and the integral part or statically
+ * declare with fix size
+ */
+static char *
+numtoa(number, base, precision, fract)
+ double number;
+ int base, precision;
+ char **fract;
+{
+ register int i, j;
+ double ip, fp; /* integer and fraction part */
+ double fraction;
+ int digits, sign;
+ static char integral_part[MAX_INT];
+ static char fraction_part[MAX_FRACT];
+ int ch;
+
+ /* taking care of the obvious case: 0.0 */
+ if (number == 0.)
+ {
+ integral_part[0] = '0';
+ integral_part[1] = '\0';
+ /* The fractional part has to take the precision into account */
+ for (ch = 0; ch < precision-1; ch++)
+ fraction_part[ch] = '0';
+ fraction_part[ch] = '0';
+ fraction_part[ch+1] = '\0';
+ if (fract)
+ *fract = fraction_part;
+ return integral_part;
+ }
+
+ /* -0 is tricky */
+ sign = (number == -0.) ? '-' : ((number < 0.) ? '-' : '+');
+ digits = MAX_INT - 1;
+
+ /* for negative numbers */
+ if (sign == '-')
+ {
+ number = -number;
+ digits--; /* sign consume one digit */
+ }
+
+ fraction = integral(number, &ip);
+ number = ip;
+
+ /* do the integral part */
+ if (ip == 0.)
+ {
+ integral_part[0] = '0';
+ i = 1;
+ }
+ else
+ {
+ for ( i = 0; i < digits && number != 0.; ++i)
+ {
+ number /= base;
+ fp = integral(number, &ip);
+ ch = (int)((fp + PRECISION)*base); /* force to round */
+ integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
+ if (! ISXDIGIT((unsigned char)integral_part[i]))
+ break; /* bail out overflow !! */
+ number = ip;
+ }
+ }
+
+ /* Oh No !! out of bound, ho well fill it up ! */
+ if (number != 0.)
+ for (i = 0; i < digits; ++i)
+ integral_part[i] = '9';
+
+ /* put the sign ? */
+ if (sign == '-')
+ integral_part[i++] = '-';
+
+ integral_part[i] = '\0';
+
+ /* reverse every thing */
+ for ( i--, j = 0; j < i; j++, i--)
+ SWAP_INT(integral_part[i], integral_part[j]);
+
+ /* the fractional part */
+ for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--)
+ {
+ fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
+ if (! DIGIT(fraction_part[i])) /* underflow ? */
+ break;
+ fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
+ }
+ fraction_part[i] = '\0';
+
+ if (fract != (char **)0)
+ *fract = fraction_part;
+
+ return integral_part;
+}
+#endif
+
+/* for %d and friends, it puts in holder
+ * the representation with the right padding
+ */
+static void
+number(p, d, base)
+ struct DATA *p;
+ unsigned long d;
+ int base;
+{
+ char *tmp, *t;
+ long sd;
+ int flags;
+
+ /* An explicit precision turns off the zero-padding flag and sets the
+ pad character back to space. */
+ if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT))
+ {
+ p->flags &= ~PF_ZEROPAD;
+ p->pad = ' ';
+ }
+
+ sd = d; /* signed for ' ' padding in base 10 */
+ flags = 0;
+ flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0;
+ if (*p->pf == 'X')
+ flags |= FL_HEXUPPER;
+
+ tmp = fmtulong (d, base, intbuf, sizeof(intbuf), flags);
+ t = 0;
+ if ((p->flags & PF_THOUSANDS))
+ {
+ GETLOCALEDATA(decpoint, thoussep, grouping);
+ if (grouping && (t = groupnum (tmp)))
+ tmp = t;
+ }
+
+ /* need to add one for any `+', but we only add one in base 10 */
+ p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS));
+ PAD_RIGHT(p);
+
+ if ((p->flags & PF_DOT) && p->precision > 0)
+ {
+ p->precision -= strlen(tmp);
+ PAD_ZERO(p);
+ }
+
+ switch (base)
+ {
+ case 10:
+ PUT_PLUS(sd, p, 0);
+ PUT_SPACE(sd, p, 0);
+ break;
+ case 8:
+ if (p->flags & PF_ALTFORM)
+ PUT_CHAR('0', p);
+ break;
+ case 16:
+ if (p->flags & PF_ALTFORM)
+ {
+ PUT_CHAR('0', p);
+ PUT_CHAR(*p->pf, p);
+ }
+ break;
+ }
+
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+
+ PAD_LEFT(p);
+ FREE (t);
+}
+
+#ifdef HAVE_LONG_LONG_INT
+/*
+ * identical to number() but works for `long long'
+ */
+static void
+lnumber(p, d, base)
+ struct DATA *p;
+ unsigned long long d;
+ int base;
+{
+ char *tmp, *t;
+ long long sd;
+ int flags;
+
+ /* An explicit precision turns off the zero-padding flag and sets the
+ pad character back to space. */
+ if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT))
+ {
+ p->flags &= ~PF_ZEROPAD;
+ p->pad = ' ';
+ }
+
+ sd = d; /* signed for ' ' padding in base 10 */
+ flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0;
+ if (*p->pf == 'X')
+ flags |= FL_HEXUPPER;
+
+ tmp = fmtullong (d, base, intbuf, sizeof(intbuf), flags);
+ t = 0;
+ if ((p->flags & PF_THOUSANDS))
+ {
+ GETLOCALEDATA(decpoint, thoussep, grouping);
+ if (grouping && (t = groupnum (tmp)))
+ tmp = t;
+ }
+
+ /* need to add one for any `+', but we only add one in base 10 */
+ p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS));
+ PAD_RIGHT(p);
+
+ if ((p->flags & PF_DOT) && p->precision > 0)
+ {
+ p->precision -= strlen(tmp);
+ PAD_ZERO(p);
+ }
+
+ switch (base)
+ {
+ case 10:
+ PUT_PLUS(sd, p, 0);
+ PUT_SPACE(sd, p, 0);
+ break;
+ case 8:
+ if (p->flags & PF_ALTFORM)
+ PUT_CHAR('0', p);
+ break;
+ case 16:
+ if (p->flags & PF_ALTFORM)
+ {
+ PUT_CHAR('0', p);
+ PUT_CHAR(*p->pf, p);
+ }
+ break;
+ }
+
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+
+ PAD_LEFT(p);
+ FREE (t);
+}
+#endif
+
+static void
+pointer(p, d)
+ struct DATA *p;
+ unsigned long d;
+{
+ char *tmp;
+
+ tmp = fmtulong(d, 16, intbuf, sizeof(intbuf), 0);
+ p->width -= strlen(tmp);
+ PAD_RIGHT(p);
+
+ /* prefix '0x' for pointers */
+ PUT_CHAR('0', p);
+ PUT_CHAR('x', p);
+
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+
+ PAD_LEFT(p);
+}
+
+/* %s strings */
+static void
+strings(p, tmp)
+ struct DATA *p;
+ char *tmp;
+{
+ size_t len;
+
+ len = strlen(tmp);
+ if (p->precision != NOT_FOUND) /* the smallest number */
+ len = (len < p->precision ? len : p->precision);
+ p->width -= len;
+
+ PUT_STRING (tmp, len, p);
+}
+
+#if HANDLE_MULTIBYTE
+/* %ls wide-character strings */
+static void
+wstrings(p, tmp)
+ struct DATA *p;
+ wchar_t *tmp;
+{
+ size_t len;
+ mbstate_t mbs;
+ char *os;
+ const wchar_t *ws;
+
+ memset (&mbs, '\0', sizeof (mbstate_t));
+ ws = (const wchar_t *)tmp;
+
+ os = (char *)NULL;
+ if (p->precision != NOT_FOUND)
+ {
+ os = (char *)xmalloc (p->precision + 1);
+ len = wcsrtombs (os, &ws, p->precision, &mbs);
+ }
+ else
+ {
+ len = wcsrtombs (NULL, &ws, 0, &mbs);
+ if (len != (size_t)-1)
+ {
+ memset (&mbs, '\0', sizeof (mbstate_t));
+ os = (char *)xmalloc (len + 1);
+ (void)wcsrtombs (os, &ws, len + 1, &mbs);
+ }
+ }
+ if (len == (size_t)-1)
+ {
+ /* invalid multibyte sequence; bail now. */
+ FREE (os);
+ return;
+ }
+
+ p->width -= len;
+ PUT_STRING (os, len, p);
+ free (os);
+}
+
+static void
+wchars (p, wc)
+ struct DATA *p;
+ wint_t wc;
+{
+ char *lbuf, *l;
+ mbstate_t mbs;
+ size_t len;
+
+ lbuf = (char *)malloc (MB_CUR_MAX+1);
+ if (lbuf == 0)
+ return;
+ memset (&mbs, '\0', sizeof (mbstate_t));
+ len = wcrtomb (lbuf, wc, &mbs);
+ if (len == (size_t)-1)
+ /* conversion failed; bail now. */
+ return;
+ p->width -= len;
+ l = lbuf;
+ PUT_STRING (l, len, p);
+ free (lbuf);
+}
+#endif /* HANDLE_MULTIBYTE */
+
+#ifdef FLOATING_POINT
+
+/* Check for [+-]infinity and NaN. If MODE == 1, we check for Infinity, else
+ (mode == 2) we check for NaN. This does the necessary printing. Returns
+ 1 if Inf or Nan, 0 if not. */
+static int
+chkinfnan(p, d, mode)
+ struct DATA *p;
+ double d;
+ int mode; /* == 1 for inf, == 2 for nan */
+{
+ int i;
+ char *tmp;
+ char *big, *small;
+
+ i = (mode == 1) ? isinf(d) : isnan(d);
+ if (i == 0)
+ return 0;
+ big = (mode == 1) ? "INF" : "NAN";
+ small = (mode == 1) ? "inf" : "nan";
+
+ tmp = (*p->pf == 'F' || *p->pf == 'G' || *p->pf == 'E') ? big : small;
+
+ if (i < 0)
+ PUT_CHAR('-', p);
+
+ while (*tmp)
+ {
+ PUT_CHAR (*tmp, p);
+ tmp++;
+ }
+
+ return 1;
+}
+
+/* %f %F %g %G floating point representation */
+static void
+floating(p, d)
+ struct DATA *p;
+ double d;
+{
+ char *tmp, *tmp2, *t;
+ int i;
+
+ if (d != 0 && (chkinfnan(p, d, 1) || chkinfnan(p, d, 2)))
+ return; /* already printed nan or inf */
+
+ GETLOCALEDATA(decpoint, thoussep, grouping);
+ DEF_PREC(p);
+ d = ROUND(d, p);
+ tmp = dtoa(d, p->precision, &tmp2);
+ t = 0;
+ if ((p->flags & PF_THOUSANDS) && grouping && (t = groupnum (tmp)))
+ tmp = t;
+
+ if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
+ {
+ /* smash the trailing zeros unless altform */
+ for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+ tmp2[i] = '\0';
+ if (tmp2[0] == '\0')
+ p->precision = 0;
+ }
+
+ /* calculate the padding. 1 for the dot */
+ p->width = p->width -
+ /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */
+#if 0
+ ((d > 0. && p->justify == RIGHT) ? 1:0) -
+#else
+ ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) -
+#endif
+ ((p->flags & PF_SPACE) ? 1:0) -
+ strlen(tmp) - p->precision -
+ ((p->precision != 0 || (p->flags & PF_ALTFORM)) ? 1 : 0); /* radix char */
+
+ if (p->pad == ' ')
+ {
+ PAD_RIGHT(p);
+ PUT_PLUS(d, p, 0.);
+ }
+ else
+ {
+ if (*tmp == '-')
+ PUT_CHAR(*tmp++, p);
+ PUT_PLUS(d, p, 0.);
+ PAD_RIGHT(p);
+ }
+ PUT_SPACE(d, p, 0.);
+
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p); /* the integral */
+ tmp++;
+ }
+ FREE (t);
+
+ if (p->precision != 0 || (p->flags & PF_ALTFORM))
+ PUT_CHAR(decpoint, p); /* put the '.' */
+
+ for (; *tmp2; tmp2++)
+ PUT_CHAR(*tmp2, p); /* the fraction */
+
+ PAD_LEFT(p);
+}
+
+/* %e %E %g %G exponent representation */
+static void
+exponent(p, d)
+ struct DATA *p;
+ double d;
+{
+ char *tmp, *tmp2;
+ int j, i;
+
+ if (d != 0 && (chkinfnan(p, d, 1) || chkinfnan(p, d, 2)))
+ return; /* already printed nan or inf */
+
+ GETLOCALEDATA(decpoint, thoussep, grouping);
+ DEF_PREC(p);
+ if (d == 0.)
+ j = 0;
+ else
+ {
+ j = log_10(d);
+ d = d / pow_10(j); /* get the Mantissa */
+ d = ROUND(d, p);
+ }
+ tmp = dtoa(d, p->precision, &tmp2);
+
+ /* 1 for unit, 1 for the '.', 1 for 'e|E',
+ * 1 for '+|-', 2 for 'exp' (but no `.' if precision == 0 */
+ /* calculate how much padding need */
+ p->width = p->width -
+ /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */
+#if 0
+ ((d > 0. && p->justify == RIGHT) ? 1:0) -
+#else
+ ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) -
+#endif
+ (p->precision != 0 || (p->flags & PF_ALTFORM)) -
+ ((p->flags & PF_SPACE) ? 1:0) - p->precision - 5;
+
+ if (p->pad == ' ')
+ {
+ PAD_RIGHT(p);
+ PUT_PLUS(d, p, 0.);
+ }
+ else
+ {
+ if (*tmp == '-')
+ PUT_CHAR(*tmp++, p);
+ PUT_PLUS(d, p, 0.);
+ PAD_RIGHT(p);
+ }
+ PUT_SPACE(d, p, 0.);
+
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+
+ if (p->precision != 0 || (p->flags & PF_ALTFORM))
+ PUT_CHAR(decpoint, p); /* the '.' */
+
+ if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
+ /* smash the trailing zeros unless altform */
+ for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+ tmp2[i] = '\0';
+
+ for (; *tmp2; tmp2++)
+ PUT_CHAR(*tmp2, p); /* the fraction */
+
+ /* the exponent put the 'e|E' */
+ if (*p->pf == 'g' || *p->pf == 'e')
+ PUT_CHAR('e', p);
+ else
+ PUT_CHAR('E', p);
+
+ /* the sign of the exp */
+ if (j >= 0)
+ PUT_CHAR('+', p);
+ else
+ {
+ PUT_CHAR('-', p);
+ j = -j;
+ }
+
+ tmp = itoa(j);
+ /* pad out to at least two spaces. pad with `0' if the exponent is a
+ single digit. */
+ if (j <= 9)
+ PUT_CHAR('0', p);
+
+ /* the exponent */
+ while (*tmp)
+ {
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+
+ PAD_LEFT(p);
+}
+#endif
+
+/* Return a new string with the digits in S grouped according to the locale's
+ grouping info and thousands separator. If no grouping should be performed,
+ this returns NULL; the caller needs to check for it. */
+static char *
+groupnum (s)
+ char *s;
+{
+ char *se, *ret, *re, *g;
+ int len, slen;
+
+ if (grouping == 0 || *grouping <= 0 || *grouping == CHAR_MAX)
+ return ((char *)NULL);
+
+ /* find min grouping to size returned string */
+ for (len = *grouping, g = grouping; *g; g++)
+ if (*g > 0 && *g < len)
+ len = *g;
+
+ slen = strlen (s);
+ len = slen / len + 1;
+ ret = (char *)xmalloc (slen + len + 1);
+ re = ret + slen + len;
+ *re = '\0';
+
+ g = grouping;
+ se = s + slen;
+ len = *g;
+
+ while (se > s)
+ {
+ *--re = *--se;
+
+ /* handle `-' inserted by numtoa() and the fmtu* family here. */
+ if (se > s && se[-1] == '-')
+ continue;
+
+ /* begin new group. */
+ if (--len == 0 && se > s)
+ {
+ *--re = thoussep;
+ len = *++g; /* was g++, but that uses first char twice (glibc bug, too) */
+ if (*g == '\0')
+ len = *--g; /* use previous grouping */
+ else if (*g == CHAR_MAX)
+ {
+ do
+ *--re = *--se;
+ while (se > s);
+ break;
+ }
+ }
+ }
+
+ if (re > ret)
+#ifdef HAVE_MEMMOVE
+ memmove (ret, re, strlen (re) + 1);
+#else
+ strcpy (ret, re);
+#endif
+
+ return ret;
+}
+
+/* initialize the conversion specifiers */
+static void
+init_conv_flag (p)
+ struct DATA *p;
+{
+ p->flags &= PF_ALLOCBUF; /* preserve PF_ALLOCBUF flag */
+ p->precision = p->width = NOT_FOUND;
+ p->justify = NOT_FOUND;
+ p->pad = ' ';
+}
+
+static void
+init_data (p, string, length, format, mode)
+ struct DATA *p;
+ char *string;
+ size_t length;
+ const char *format;
+ int mode;
+{
+ p->length = length - 1; /* leave room for '\0' */
+ p->holder = p->base = string;
+ p->pf = format;
+ p->counter = 0;
+ p->flags = (mode == PFM_AS) ? PF_ALLOCBUF : 0;
+}
+
+static int
+#if defined (__STDC__)
+vsnprintf_internal(struct DATA *data, char *string, size_t length, const char *format, va_list args)
+#else
+vsnprintf_internal(data, string, length, format, args)
+ struct DATA *data;
+ char *string;
+ size_t length;
+ const char *format;
+ va_list args;
+#endif
+{
+ double d; /* temporary holder */
+#ifdef HAVE_LONG_DOUBLE
+ long double ld; /* for later */
+#endif
+ unsigned long ul;
+#ifdef HAVE_UNSIGNED_LONG_LONG_INT
+ unsigned long long ull;
+#endif
+ int state, i, c, n;
+ char *s;
+#if HANDLE_MULTIBYTE
+ wchar_t *ws;
+ wint_t wc;
+#endif
+ const char *convstart;
+ int negprec;
+
+ /* Sanity check, the string length must be >= 0. C99 actually says that
+ LENGTH can be zero here, in the case of snprintf/vsnprintf (it's never
+ 0 in the case of asprintf/vasprintf), and the return value is the number
+ of characters that would have been written. */
+ if (length < 0)
+ return -1;
+
+ if (format == 0)
+ return 0;
+
+ /* Reset these for each call because the locale might have changed. */
+ decpoint = thoussep = 0;
+ grouping = 0;
+
+ negprec = 0;
+ for (; c = *(data->pf); data->pf++)
+ {
+ if (c != '%')
+ {
+ PUT_CHAR (c, data);
+ continue;
+ }
+
+ convstart = data->pf;
+ init_conv_flag (data); /* initialise format flags */
+
+ state = 1;
+ for (state = 1; state && *data->pf; )
+ {
+ c = *(++data->pf);
+ /* fmtend = data->pf */
+#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE)
+ if (data->flags & PF_LONGDBL)
+ {
+ switch (c)
+ {
+ case 'f': case 'F':
+ case 'e': case 'E':
+ case 'g': case 'G':
+# ifdef HAVE_PRINTF_A_FORMAT
+ case 'a': case 'A':
+# endif
+ STAR_ARGS (data);
+ ld = GETLDOUBLE (data);
+ ldfallback (data, convstart, data->pf, ld);
+ goto conv_break;
+ }
+ }
+#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */
+
+ switch (c)
+ {
+ /* Parse format flags */
+ case '\0': /* a NULL here ? ? bail out */
+ *data->holder = '\0';
+ return data->counter;
+ break;
+ case '#':
+ data->flags |= PF_ALTFORM;
+ continue;
+ case '*':
+ if (data->flags & PF_DOT)
+ data->flags |= PF_STAR_P;
+ else
+ data->flags |= PF_STAR_W;
+ continue;
+ case '-':
+ if ((data->flags & PF_DOT) == 0)
+ {
+ data->flags |= PF_LADJUST;
+ data->justify = LEFT;
+ }
+ else
+ negprec = 1;
+ continue;
+ case ' ':
+ if ((data->flags & PF_PLUS) == 0)
+ data->flags |= PF_SPACE;
+ continue;
+ case '+':
+ if ((data->flags & PF_DOT) == 0)
+ {
+ data->flags |= PF_PLUS;
+ if ((data->flags & PF_LADJUST) == 0)
+ data->justify = RIGHT;
+ }
+ continue;
+ case '\'':
+ data->flags |= PF_THOUSANDS;
+ continue;
+
+ case '0':
+ /* If we're not specifying precision (in which case we've seen
+ a `.') and we're not performing left-adjustment (in which
+ case the `0' is ignored), a `0' is taken as the zero-padding
+ flag. */
+ if ((data->flags & (PF_DOT|PF_LADJUST)) == 0)
+ {
+ data->flags |= PF_ZEROPAD;
+ data->pad = '0';
+ continue;
+ }
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ n = 0;
+ do
+ {
+ n = n * 10 + TODIGIT(c);
+ c = *(++data->pf);
+ }
+ while (DIGIT(c));
+ data->pf--; /* went too far */
+ if (n < 0)
+ n = 0;
+ if (data->flags & PF_DOT)
+ data->precision = negprec ? NOT_FOUND : n;
+ else
+ data->width = n;
+ continue;
+
+ /* optional precision */
+ case '.':
+ data->flags |= PF_DOT;
+ data->precision = 0;
+ continue;
+
+ /* length modifiers */
+ case 'h':
+ data->flags |= (data->flags & PF_SHORTINT) ? PF_SIGNEDCHAR : PF_SHORTINT;
+ continue;
+ case 'l':
+ data->flags |= (data->flags & PF_LONGINT) ? PF_LONGLONG : PF_LONGINT;
+ continue;
+ case 'L':
+ data->flags |= PF_LONGDBL;
+ continue;
+ case 'q':
+ data->flags |= PF_LONGLONG;
+ continue;
+ case 'j':
+ data->flags |= PF_INTMAX_T;
+ SET_SIZE_FLAGS(data, intmax_t);
+ continue;
+ case 'z':
+ data->flags |= PF_SIZE_T;
+ SET_SIZE_FLAGS(data, size_t);
+ continue;
+ case 't':
+ data->flags |= PF_PTRDIFF_T;
+ SET_SIZE_FLAGS(data, ptrdiff_t);
+ continue;
+
+ /* Conversion specifiers */
+#ifdef FLOATING_POINT
+ case 'f': /* float, double */
+ case 'F':
+ STAR_ARGS(data);
+ d = GETDOUBLE(data);
+ floating(data, d);
+conv_break:
+ state = 0;
+ break;
+ case 'g':
+ case 'G':
+ STAR_ARGS(data);
+ DEF_PREC(data);
+ d = GETDOUBLE(data);
+ i = (d != 0.) ? log_10(d) : -1;
+ /*
+ * for '%g|%G' ANSI: use f if exponent
+ * is in the range or [-4,p] exclusively
+ * else use %e|%E
+ */
+ if (-4 < i && i < data->precision)
+ {
+ /* reset precision */
+ data->precision -= i + 1;
+ floating(data, d);
+ }
+ else
+ {
+ /* reduce precision by 1 because of leading digit before
+ decimal point in e format, unless specified as 0. */
+ if (data->precision > 0)
+ data->precision--;
+ exponent(data, d);
+ }
+ state = 0;
+ break;
+ case 'e':
+ case 'E': /* Exponent double */
+ STAR_ARGS(data);
+ d = GETDOUBLE(data);
+ exponent(data, d);
+ state = 0;
+ break;
+# ifdef HAVE_PRINTF_A_FORMAT
+ case 'a':
+ case 'A':
+ STAR_ARGS(data);
+ d = GETDOUBLE(data);
+ dfallback(data, convstart, data->pf, d);
+ state = 0;
+ break;
+# endif /* HAVE_PRINTF_A_FORMAT */
+#endif /* FLOATING_POINT */
+ case 'U':
+ data->flags |= PF_LONGINT;
+ /* FALLTHROUGH */
+ case 'u':
+ STAR_ARGS(data);
+#ifdef HAVE_LONG_LONG_INT
+ if (data->flags & PF_LONGLONG)
+ {
+ ull = GETARG (unsigned long long);
+ lnumber(data, ull, 10);
+ }
+ else
+#endif
+ {
+ ul = GETUNSIGNED(data);
+ number(data, ul, 10);
+ }
+ state = 0;
+ break;
+ case 'D':
+ data->flags |= PF_LONGINT;
+ /* FALLTHROUGH */
+ case 'd': /* decimal */
+ case 'i':
+ STAR_ARGS(data);
+#ifdef HAVE_LONG_LONG_INT
+ if (data->flags & PF_LONGLONG)
+ {
+ ull = GETARG (long long);
+ lnumber(data, ull, 10);
+ }
+ else
+#endif
+ {
+ ul = GETSIGNED(data);
+ number(data, ul, 10);
+ }
+ state = 0;
+ break;
+ case 'o': /* octal */
+ STAR_ARGS(data);
+#ifdef HAVE_LONG_LONG_INT
+ if (data->flags & PF_LONGLONG)
+ {
+ ull = GETARG (unsigned long long);
+ lnumber(data, ull, 8);
+ }
+ else
+#endif
+ {
+ ul = GETUNSIGNED(data);
+ number(data, ul, 8);
+ }
+ state = 0;
+ break;
+ case 'x':
+ case 'X': /* hexadecimal */
+ STAR_ARGS(data);
+#ifdef HAVE_LONG_LONG_INT
+ if (data->flags & PF_LONGLONG)
+ {
+ ull = GETARG (unsigned long long);
+ lnumber(data, ull, 16);
+ }
+ else
+#endif
+ {
+ ul = GETUNSIGNED(data);
+ number(data, ul, 16);
+ }
+ state = 0;
+ break;
+ case 'p':
+ STAR_ARGS(data);
+ ul = (unsigned long)GETARG (void *);
+ pointer(data, ul);
+ state = 0;
+ break;
+#if HANDLE_MULTIBYTE
+ case 'C':
+ data->flags |= PF_LONGINT;
+ /* FALLTHROUGH */
+#endif
+ case 'c': /* character */
+ STAR_ARGS(data);
+#if HANDLE_MULTIBYTE
+ if (data->flags & PF_LONGINT)
+ {
+ wc = GETARG (wint_t);
+ wchars (data, wc);
+ }
+ else
+#endif
+ {
+ ul = GETARG (int);
+ PUT_CHAR(ul, data);
+ }
+ state = 0;
+ break;
+#if HANDLE_MULTIBYTE
+ case 'S':
+ data->flags |= PF_LONGINT;
+ /* FALLTHROUGH */
+#endif
+ case 's': /* string */
+ STAR_ARGS(data);
+#if HANDLE_MULTIBYTE
+ if (data->flags & PF_LONGINT)
+ {
+ ws = GETARG (wchar_t *);
+ wstrings (data, ws);
+ }
+ else
+#endif
+ {
+ s = GETARG (char *);
+ strings(data, s);
+ }
+ state = 0;
+ break;
+ case 'n':
+#ifdef HAVE_LONG_LONG_INT
+ if (data->flags & PF_LONGLONG)
+ *(GETARG (long long *)) = data->counter;
+ else
+#endif
+ if (data->flags & PF_LONGINT)
+ *(GETARG (long *)) = data->counter;
+ else if (data->flags & PF_SHORTINT)
+ *(GETARG (short *)) = data->counter;
+ else
+ *(GETARG (int *)) = data->counter;
+ state = 0;
+ break;
+ case '%': /* nothing just % */
+ PUT_CHAR('%', data);
+ state = 0;
+ break;
+ default:
+ /* is this an error ? maybe bail out */
+ state = 0;
+ break;
+ } /* end switch */
+ } /* end of `%' for loop */
+ } /* end of format string for loop */
+
+ if (data->length >= 0)
+ *data->holder = '\0'; /* the end ye ! */
+
+ return data->counter;
+}
+
+#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE)
+/*
+ * Printing floating point numbers accurately is an art. I'm not good
+ * at it. Fall back to sprintf for long double formats.
+ */
+static void
+ldfallback (data, fs, fe, ld)
+ struct DATA *data;
+ const char *fs, *fe;
+ long double ld;
+{
+ register char *x;
+ char fmtbuf[FALLBACK_FMTSIZE], *obuf;
+ int fl;
+
+ fl = LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2;
+ obuf = (char *)xmalloc (fl);
+ fl = fe - fs + 1;
+ strncpy (fmtbuf, fs, fl);
+ fmtbuf[fl] = '\0';
+
+ if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P))
+ sprintf (obuf, fmtbuf, data->width, data->precision, ld);
+ else if (data->flags & PF_STAR_W)
+ sprintf (obuf, fmtbuf, data->width, ld);
+ else if (data->flags & PF_STAR_P)
+ sprintf (obuf, fmtbuf, data->precision, ld);
+ else
+ sprintf (obuf, fmtbuf, ld);
+
+ for (x = obuf; *x; x++)
+ PUT_CHAR (*x, data);
+ xfree (obuf);
+}
+#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */
+
+#ifdef FLOATING_POINT
+/* Used for %a, %A if the libc printf supports them. */
+static void
+dfallback (data, fs, fe, d)
+ struct DATA *data;
+ const char *fs, *fe;
+ double d;
+{
+ register char *x;
+ char fmtbuf[FALLBACK_FMTSIZE], obuf[FALLBACK_BASE];
+ int fl;
+
+ fl = fe - fs + 1;
+ strncpy (fmtbuf, fs, fl);
+ fmtbuf[fl] = '\0';
+
+ if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P))
+ sprintf (obuf, fmtbuf, data->width, data->precision, d);
+ else if (data->flags & PF_STAR_W)
+ sprintf (obuf, fmtbuf, data->width, d);
+ else if (data->flags & PF_STAR_P)
+ sprintf (obuf, fmtbuf, data->precision, d);
+ else
+ sprintf (obuf, fmtbuf, d);
+
+ for (x = obuf; *x; x++)
+ PUT_CHAR (*x, data);
+}
+#endif /* FLOATING_POINT */
+
+#if !HAVE_SNPRINTF
+
+int
+#if defined (__STDC__)
+vsnprintf(char *string, size_t length, const char *format, va_list args)
+#else
+vsnprintf(string, length, format, args)
+ char *string;
+ size_t length;
+ const char *format;
+ va_list args;
+#endif
+{
+ struct DATA data;
+
+ if (string == 0 && length != 0)
+ return 0;
+ init_data (&data, string, length, format, PFM_SN);
+ return (vsnprintf_internal(&data, string, length, format, args));
+}
+
+int
+#if defined(PREFER_STDARG)
+snprintf(char *string, size_t length, const char * format, ...)
+#else
+snprintf(string, length, format, va_alist)
+ char *string;
+ size_t length;
+ const char *format;
+ va_dcl
+#endif
+{
+ struct DATA data;
+ int rval;
+ va_list args;
+
+ SH_VA_START(args, format);
+
+ if (string == 0 && length != 0)
+ return 0;
+ init_data (&data, string, length, format, PFM_SN);
+ rval = vsnprintf_internal (&data, string, length, format, args);
+
+ va_end(args);
+
+ return rval;
+}
+
+#endif /* HAVE_SNPRINTF */
+
+#if !HAVE_ASPRINTF
+
+int
+#if defined (__STDC__)
+vasprintf(char **stringp, const char *format, va_list args)
+#else
+vasprintf(stringp, format, args)
+ char **stringp;
+ const char *format;
+ va_list args;
+#endif
+{
+ struct DATA data;
+ char *string;
+ int r;
+
+ string = (char *)xmalloc(ASBUFSIZE);
+ init_data (&data, string, ASBUFSIZE, format, PFM_AS);
+ r = vsnprintf_internal(&data, string, ASBUFSIZE, format, args);
+ *stringp = data.base; /* not string in case reallocated */
+ return r;
+}
+
+int
+#if defined(PREFER_STDARG)
+asprintf(char **stringp, const char * format, ...)
+#else
+asprintf(stringp, format, va_alist)
+ char **stringp;
+ const char *format;
+ va_dcl
+#endif
+{
+ int rval;
+ va_list args;
+
+ SH_VA_START(args, format);
+
+ rval = vasprintf (stringp, format, args);
+
+ va_end(args);
+
+ return rval;
+}
+
+#endif /* !HAVE_ASPRINTF */
+
+#endif /* !HAVE_SNPRINTF || !HAVE_ASPRINTF */
+
+#ifdef DRIVER
+
+static void
+memory_error_and_abort ()
+{
+ write (2, "out of virtual memory\n", 22);
+ abort ();
+}
+
+static void *
+xmalloc(bytes)
+ size_t bytes;
+{
+ void *ret;
+
+ ret = malloc(bytes);
+ if (ret == 0)
+ memory_error_and_abort ();
+ return ret;
+}
+
+static void *
+xrealloc (pointer, bytes)
+ void *pointer;
+ size_t bytes;
+{
+ void *ret;
+
+ ret = pointer ? realloc(pointer, bytes) : malloc(bytes);
+ if (ret == 0)
+ memory_error_and_abort ();
+ return ret;
+}
+
+static void
+xfree(x)
+ void *x;
+{
+ if (x)
+ free (x);
+}
+
+/* set of small tests for snprintf() */
+main()
+{
+ char holder[100];
+ char *h;
+ int i, si, ai;
+
+#ifdef HAVE_LOCALE_H
+ setlocale(LC_ALL, "");
+#endif
+
+#if 1
+ si = snprintf((char *)NULL, 0, "abcde\n");
+ printf("snprintf returns %d with NULL first argument and size of 0\n", si);
+ si = snprintf(holder, 0, "abcde\n");
+ printf("snprintf returns %d with non-NULL first argument and size of 0\n", si);
+ si = snprintf((char *)NULL, 16, "abcde\n");
+ printf("snprintf returns %d with NULL first argument and non-zero size\n", si);
+
+/*
+ printf("Suite of test for snprintf:\n");
+ printf("a_format\n");
+ printf("printf() format\n");
+ printf("snprintf() format\n\n");
+*/
+/* Checking the field widths */
+
+ printf("/%%ld %%ld/, 336, 336\n");
+ snprintf(holder, sizeof holder, "/%ld %ld/\n", 336, 336);
+ asprintf(&h, "/%ld %ld/\n", 336, 336);
+ printf("/%ld %ld/\n", 336, 336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%d/, 336\n");
+ snprintf(holder, sizeof holder, "/%d/\n", 336);
+ asprintf(&h, "/%d/\n", 336);
+ printf("/%d/\n", 336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%2d/, 336\n");
+ snprintf(holder, sizeof holder, "/%2d/\n", 336);
+ asprintf(&h, "/%2d/\n", 336);
+ printf("/%2d/\n", 336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%10d/, 336\n");
+ snprintf(holder, sizeof holder, "/%10d/\n", 336);
+ asprintf(&h, "/%10d/\n", 336);
+ printf("/%10d/\n", 336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%-10d/, 336\n");
+ snprintf(holder, sizeof holder, "/%-10d/\n", 336);
+ asprintf(&h, "/%-10d/\n", 336);
+ printf("/%-10d/\n", 336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+
+/* floating points */
+
+ printf("/%%f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
+ asprintf(&h, "/%f/\n", 1234.56);
+ printf("/%f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%e/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
+ asprintf(&h, "/%e/\n", 1234.56);
+ printf("/%e/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%4.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
+ asprintf(&h, "/%4.2f/\n", 1234.56);
+ printf("/%4.2f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%3.1f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
+ asprintf(&h, "/%3.1f/\n", 1234.56);
+ printf("/%3.1f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%10.3f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
+ asprintf(&h, "/%10.3f/\n", 1234.56);
+ printf("/%10.3f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%10.3e/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
+ asprintf(&h, "/%10.3e/\n", 1234.56);
+ printf("/%10.3e/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%+4.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
+ asprintf(&h, "/%+4.2f/\n", 1234.56);
+ printf("/%+4.2f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%010.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
+ asprintf(&h, "/%010.2f/\n", 1234.56);
+ printf("/%010.2f/\n", 1234.56);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+#define BLURB "Outstanding acting !"
+/* strings precisions */
+
+ printf("/%%2s/, \"%s\"\n", BLURB);
+ snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
+ asprintf(&h, "/%2s/\n", BLURB);
+ printf("/%2s/\n", BLURB);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%22s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
+ asprintf(&h, "/%22s/\n", BLURB);
+ printf("/%22s/\n", BLURB);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%22.5s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
+ asprintf(&h, "/%22.5s/\n", BLURB);
+ printf("/%22.5s/\n", BLURB);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%-22.5s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
+ asprintf(&h, "/%-22.5s/\n", BLURB);
+ printf("/%-22.5s/\n", BLURB);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+/* see some flags */
+
+ printf("%%x %%X %%#x, 31, 31, 31\n");
+ snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
+ asprintf(&h, "%x %X %#x\n", 31, 31, 31);
+ printf("%x %X %#x\n", 31, 31, 31);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("**%%d**%% d**%% d**, 42, 42, -42\n");
+ snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
+ asprintf(&h, "**%d**% d**% d**\n", 42, 42, -42);
+ printf("**%d**% d**% d**\n", 42, 42, -42);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+/* other flags */
+
+ printf("/%%g/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%g/\n", 31.4);
+ asprintf(&h, "/%g/\n", 31.4);
+ printf("/%g/\n", 31.4);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%.6g/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
+ asprintf(&h, "/%.6g/\n", 31.4);
+ printf("/%.6g/\n", 31.4);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%.1G/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
+ asprintf(&h, "/%.1G/\n", 31.4);
+ printf("/%.1G/\n", 31.4);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%.1G/, 3100000000.4\n");
+ snprintf(holder, sizeof holder, "/%.1G/\n", 3100000000.4);
+ asprintf(&h, "/%.1G/\n", 3100000000.4);
+ printf("/%.1G/\n", 3100000000.4);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("abc%%n\n");
+ printf("abc%n", &i); printf("%d\n", i);
+ snprintf(holder, sizeof holder, "abc%n", &i);
+ printf("%s", holder); printf("%d\n\n", i);
+ asprintf(&h, "abc%n", &i);
+ printf("%s", h); printf("%d\n\n", i);
+
+ printf("%%*.*s --> 10.10\n");
+ snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
+ asprintf(&h, "%*.*s\n", 10, 10, BLURB);
+ printf("%*.*s\n", 10, 10, BLURB);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("%%%%%%%%\n");
+ snprintf(holder, sizeof holder, "%%%%\n");
+ asprintf(&h, "%%%%\n");
+ printf("%%%%\n");
+ printf("%s", holder);
+ printf("%s\n", h);
+
+#define BIG "Hello this is a too big string for the buffer"
+/* printf("A buffer to small of 10, trying to put this:\n");*/
+ printf("<%%>, %s\n", BIG);
+ i = snprintf(holder, 10, "%s\n", BIG);
+ i = asprintf(&h, "%s", BIG);
+ printf("<%s>\n", BIG);
+ printf("<%s>\n", holder);
+ printf("<%s>\n\n", h);
+
+ printf ("<%%p> vsnprintf\n");
+ i = snprintf(holder, 100, "%p", vsnprintf);
+ i = asprintf(&h, "%p", vsnprintf);
+ printf("<%p>\n", vsnprintf);
+ printf("<%s>\n", holder);
+ printf("<%s>\n\n", h);
+
+ printf ("<%%lu> LONG_MAX+1\n");
+ i = snprintf(holder, 100, "%lu", (unsigned long)(LONG_MAX)+1);
+ i = asprintf(&h, "%lu", (unsigned long)(LONG_MAX)+1);
+ printf("<%lu>\n", (unsigned long)(LONG_MAX)+1);
+ printf("<%s>\n", holder);
+ printf("<%s>\n\n", h);
+
+#ifdef HAVE_LONG_LONG_INT
+ printf ("<%%llu> LLONG_MAX+1\n");
+ i = snprintf(holder, 100, "%llu", (unsigned long long)(LLONG_MAX)+1);
+ i = asprintf(&h, "%llu", (unsigned long long)(LLONG_MAX)+1);
+ printf("<%llu>\n", (unsigned long long)(LLONG_MAX)+1);
+ printf("<%s>\n", holder);
+ printf("<%s>\n\n", h);
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+ printf ("<%%6.2LE> 42.42\n");
+ i = snprintf(holder, 100, "%6.2LE", (long double)42.42);
+ i = asprintf(&h, "%6.2LE", (long double)42.42);
+ printf ("<%6.2LE>\n", (long double)42.42);
+ printf ("<%s>\n", holder);
+ printf ("<%s>\n\n", h);
+#endif
+
+#ifdef HAVE_PRINTF_A_FORMAT
+ printf ("<%%6.2A> 42.42\n");
+ i = snprintf(holder, 100, "%6.2A", 42.42);
+ i = asprintf(&h, "%6.2A", 42.42);
+ printf ("<%6.2A>\n", 42.42);
+ printf ("<%s>\n", holder);
+ printf ("<%s>\n\n", h);
+
+ printf ("<%%6.2LA> 42.42\n");
+ i = snprintf(holder, 100, "%6.2LA", (long double)42.42);
+ i = asprintf(&h, "%6.2LA", (long double)42.42);
+ printf ("<%6.2LA>\n", (long double)42.42);
+ printf ("<%s>\n", holder);
+ printf ("<%s>\n\n", h);
+#endif
+
+ printf ("<%%.10240f> DBL_MAX\n");
+ si = snprintf(holder, 100, "%.10240f", DBL_MAX);
+ ai = asprintf(&h, "%.10240f", DBL_MAX);
+ printf ("<%.10240f>\n", DBL_MAX);
+ printf ("<%d> <%s>\n", si, holder);
+ printf ("<%d> <%s>\n\n", ai, h);
+
+ printf ("<%%.10240Lf> LDBL_MAX\n");
+ si = snprintf(holder, 100, "%.10240Lf", (long double)LDBL_MAX);
+ ai = asprintf(&h, "%.10240Lf", (long double)LDBL_MAX);
+ printf ("<%.10240Lf>\n", (long double)LDBL_MAX);
+ printf ("<%d> <%s>\n", si, holder);
+ printf ("<%d> <%s>\n\n", ai, h);
+
+ /* huh? */
+ printf("/%%g/, 421.2345\n");
+ snprintf(holder, sizeof holder, "/%g/\n", 421.2345);
+ asprintf(&h, "/%g/\n", 421.2345);
+ printf("/%g/\n", 421.2345);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%g/, 4214.2345\n");
+ snprintf(holder, sizeof holder, "/%g/\n", 4214.2345);
+ asprintf(&h, "/%g/\n", 4214.2345);
+ printf("/%g/\n", 4214.2345);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%.5g/, 4214.2345\n");
+ snprintf(holder, sizeof holder, "/%.5g/\n", 4214.2345);
+ asprintf(&h, "/%.5g/\n", 4214.2345);
+ printf("/%.5g/\n", 4214.2345);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%.4g/, 4214.2345\n");
+ snprintf(holder, sizeof holder, "/%.4g/\n", 4214.2345);
+ asprintf(&h, "/%.4g/\n", 4214.2345);
+ printf("/%.4g/\n", 4214.2345);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'ld %%'ld/, 12345, 1234567\n");
+ snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 12345, 1234567);
+ asprintf(&h, "/%'ld %'ld/\n", 12345, 1234567);
+ printf("/%'ld %'ld/\n", 12345, 1234567);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'ld %%'ld/, 336, 3336\n");
+ snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 336, 3336);
+ asprintf(&h, "/%'ld %'ld/\n", 336, 3336);
+ printf("/%'ld %'ld/\n", 336, 3336);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'ld %%'ld/, -42786, -142786\n");
+ snprintf(holder, sizeof holder, "/%'ld %'ld/\n", -42786, -142786);
+ asprintf(&h, "/%'ld %'ld/\n", -42786, -142786);
+ printf("/%'ld %'ld/\n", -42786, -142786);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'f %%'f/, 421.2345, 421234.56789\n");
+ snprintf(holder, sizeof holder, "/%'f %'f/\n", 421.2345, 421234.56789);
+ asprintf(&h, "/%'f %'f/\n", 421.2345, 421234.56789);
+ printf("/%'f %'f/\n", 421.2345, 421234.56789);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'f %%'f/, -421.2345, -421234.56789\n");
+ snprintf(holder, sizeof holder, "/%'f %'f/\n", -421.2345, -421234.56789);
+ asprintf(&h, "/%'f %'f/\n", -421.2345, -421234.56789);
+ printf("/%'f %'f/\n", -421.2345, -421234.56789);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'g %%'g/, 421.2345, 421234.56789\n");
+ snprintf(holder, sizeof holder, "/%'g %'g/\n", 421.2345, 421234.56789);
+ asprintf(&h, "/%'g %'g/\n", 421.2345, 421234.56789);
+ printf("/%'g %'g/\n", 421.2345, 421234.56789);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ printf("/%%'g %%'g/, -421.2345, -421234.56789\n");
+ snprintf(holder, sizeof holder, "/%'g %'g/\n", -421.2345, -421234.56789);
+ asprintf(&h, "/%'g %'g/\n", -421.2345, -421234.56789);
+ printf("/%'g %'g/\n", -421.2345, -421234.56789);
+ printf("%s", holder);
+ printf("%s\n", h);
+#endif
+
+ printf("/%%'g/, 4213455.8392\n");
+ snprintf(holder, sizeof holder, "/%'g/\n", 4213455.8392);
+ asprintf(&h, "/%'g/\n", 4213455.8392);
+ printf("/%'g/\n", 4213455.8392);
+ printf("%s", holder);
+ printf("%s\n", h);
+
+ exit (0);
+}
+#endif
diff --git a/lib/sh/spell.c b/lib/sh/spell.c
new file mode 100644
index 0000000..cdf465b
--- /dev/null
+++ b/lib/sh/spell.c
@@ -0,0 +1,212 @@
+/* spell.c -- spelling correction for pathnames. */
+
+/* Copyright (C) 2000-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <bashtypes.h>
+#include <posixdir.h>
+#include <posixstat.h>
+#if defined (HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+
+#include <bashansi.h>
+#include <maxpath.h>
+#include <stdc.h>
+
+static int mindist PARAMS((char *, char *, char *));
+static int spdist PARAMS((char *, char *));
+
+/*
+ * `spname' and its helpers are inspired by the code in "The UNIX
+ * Programming Environment", Kernighan & Pike, Prentice-Hall 1984,
+ * pages 209 - 213.
+ */
+
+/*
+ * `spname' -- return a correctly spelled filename
+ *
+ * int spname(char * oldname, char * newname)
+ * Returns: -1 if no reasonable match found
+ * 0 if exact match found
+ * 1 if corrected
+ * Stores corrected name in `newname'.
+ */
+int
+spname(oldname, newname)
+ char *oldname;
+ char *newname;
+{
+ char *op, *np, *p;
+ char guess[PATH_MAX + 1], best[PATH_MAX + 1];
+
+ op = oldname;
+ np = newname;
+ for (;;)
+ {
+ while (*op == '/') /* Skip slashes */
+ *np++ = *op++;
+ *np = '\0';
+
+ if (*op == '\0') /* Exact or corrected */
+ {
+ /* `.' is rarely the right thing. */
+ if (oldname[1] == '\0' && newname[1] == '\0' &&
+ oldname[0] != '.' && newname[0] == '.')
+ return -1;
+ return strcmp(oldname, newname) != 0;
+ }
+
+ /* Copy next component into guess */
+ for (p = guess; *op != '/' && *op != '\0'; op++)
+ if (p < guess + PATH_MAX)
+ *p++ = *op;
+ *p = '\0';
+
+ if (mindist(newname, guess, best) >= 3)
+ return -1; /* Hopeless */
+
+ /*
+ * Add to end of newname
+ */
+ for (p = best; *np = *p++; np++)
+ ;
+ }
+}
+
+/*
+ * Search directory for a guess
+ */
+static int
+mindist(dir, guess, best)
+ char *dir;
+ char *guess;
+ char *best;
+{
+ DIR *fd;
+ struct dirent *dp;
+ int dist, x;
+
+ dist = 3; /* Worst distance */
+ if (*dir == '\0')
+ dir = ".";
+
+ if ((fd = opendir(dir)) == NULL)
+ return dist;
+
+ while ((dp = readdir(fd)) != NULL)
+ {
+ /*
+ * Look for a better guess. If the new guess is as
+ * good as the current one, we take it. This way,
+ * any single character match will be a better match
+ * than ".".
+ */
+ x = spdist(dp->d_name, guess);
+ if (x <= dist && x != 3)
+ {
+ strcpy(best, dp->d_name);
+ dist = x;
+ if (dist == 0) /* Exact match */
+ break;
+ }
+ }
+ (void)closedir(fd);
+
+ /* Don't return `.' */
+ if (best[0] == '.' && best[1] == '\0')
+ dist = 3;
+ return dist;
+}
+
+/*
+ * `spdist' -- return the "distance" between two names.
+ *
+ * int spname(char * oldname, char * newname)
+ * Returns: 0 if strings are identical
+ * 1 if two characters are transposed
+ * 2 if one character is wrong, added or deleted
+ * 3 otherwise
+ */
+static int
+spdist(cur, new)
+ char *cur, *new;
+{
+ while (*cur == *new)
+ {
+ if (*cur == '\0')
+ return 0; /* Exact match */
+ cur++;
+ new++;
+ }
+
+ if (*cur)
+ {
+ if (*new)
+ {
+ if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
+ return 1; /* Transposition */
+
+ if (strcmp (cur + 1, new + 1) == 0)
+ return 2; /* One character mismatch */
+ }
+
+ if (strcmp(&cur[1], &new[0]) == 0)
+ return 2; /* Extra character */
+ }
+
+ if (*new && strcmp(cur, new + 1) == 0)
+ return 2; /* Missing character */
+
+ return 3;
+}
+
+char *
+dirspell (dirname)
+ char *dirname;
+{
+ int n;
+ char *guess;
+
+ n = (strlen (dirname) * 3 + 1) / 2 + 1;
+ guess = (char *)malloc (n);
+ if (guess == 0)
+ return 0;
+
+ switch (spname (dirname, guess))
+ {
+ case -1:
+ default:
+ free (guess);
+ return (char *)NULL;
+ case 0:
+ case 1:
+ return guess;
+ }
+}
diff --git a/lib/sh/strcasecmp.c b/lib/sh/strcasecmp.c
new file mode 100644
index 0000000..70d0551
--- /dev/null
+++ b/lib/sh/strcasecmp.c
@@ -0,0 +1,84 @@
+/* strcasecmp.c - functions for case-insensitive string comparison. */
+
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_STRCASECMP)
+
+#include <stdc.h>
+#include <bashansi.h>
+#include <chartypes.h>
+
+/* Compare at most COUNT characters from string1 to string2. Case
+ doesn't matter. */
+int
+strncasecmp (string1, string2, count)
+ const char *string1;
+ const char *string2;
+ size_t count;
+{
+ register const char *s1;
+ register const char *s2;
+ register int r;
+
+ if (count <= 0 || (string1 == string2))
+ return 0;
+
+ s1 = string1;
+ s2 = string2;
+ do
+ {
+ if ((r = TOLOWER ((unsigned char) *s1) - TOLOWER ((unsigned char) *s2)) != 0)
+ return r;
+ if (*s1++ == '\0')
+ break;
+ s2++;
+ }
+ while (--count != 0);
+
+ return (0);
+}
+
+/* strcmp (), but caseless. */
+int
+strcasecmp (string1, string2)
+ const char *string1;
+ const char *string2;
+{
+ register const char *s1;
+ register const char *s2;
+ register int r;
+
+ s1 = string1;
+ s2 = string2;
+
+ if (s1 == s2)
+ return (0);
+
+ while ((r = TOLOWER ((unsigned char)*s1) - TOLOWER ((unsigned char)*s2)) == 0)
+ {
+ if (*s1++ == '\0')
+ return 0;
+ s2++;
+ }
+
+ return (r);
+}
+#endif /* !HAVE_STRCASECMP */
diff --git a/lib/sh/strcasestr.c b/lib/sh/strcasestr.c
new file mode 100644
index 0000000..c819b3e
--- /dev/null
+++ b/lib/sh/strcasestr.c
@@ -0,0 +1,46 @@
+/* strcasestr.c - Find if one string appears as a substring of another string,
+ without regard to case. */
+
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashansi.h>
+#include <chartypes.h>
+
+#include <stdc.h>
+
+/* Determine if s2 occurs in s1. If so, return a pointer to the
+ match in s1. The compare is case insensitive. This is a
+ case-insensitive strstr(3). */
+char *
+strcasestr (s1, s2)
+ const char *s1;
+ const char *s2;
+{
+ register int i, l, len, c;
+
+ c = TOLOWER ((unsigned char)s2[0]);
+ len = strlen (s1);
+ l = strlen (s2);
+ for (i = 0; (len - i) >= l; i++)
+ if ((TOLOWER ((unsigned char)s1[i]) == c) && (strncasecmp (s1 + i, s2, l) == 0))
+ return ((char *)s1 + i);
+ return ((char *)0);
+}
diff --git a/lib/sh/strchrnul.c b/lib/sh/strchrnul.c
new file mode 100644
index 0000000..00cb88c
--- /dev/null
+++ b/lib/sh/strchrnul.c
@@ -0,0 +1,35 @@
+/* Searching in a string.
+ Copyright (C) 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdio.h>
+
+/* Specification. */
+#include <string.h>
+
+/* Find the first occurrence of C in S or the final NUL byte. */
+char *
+strchrnul (s, c_in)
+ const char *s;
+ int c_in;
+{
+ char c;
+ register char *s1;
+
+ for (c = c_in, s1 = (char *)s; s1 && *s1 && *s1 != c; s1++)
+ ;
+ return (s1);
+}
diff --git a/lib/sh/strdup.c b/lib/sh/strdup.c
new file mode 100644
index 0000000..3d35f7c
--- /dev/null
+++ b/lib/sh/strdup.c
@@ -0,0 +1,41 @@
+/* strdup - return a copy of a string in newly-allocated memory. */
+
+/* Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+/* Get specification. */
+#include <string.h>
+#include <stdlib.h>
+
+/* Duplicate S, returning an identical malloc'd string. */
+char *
+strdup (s)
+ const char *s;
+{
+ size_t len;
+ void *new;
+
+ len = strlen (s) + 1;
+ if ((new = malloc (len)) == NULL)
+ return NULL;
+
+ memcpy (new, s, len);
+ return ((char *)new);
+}
diff --git a/lib/sh/strerror.c b/lib/sh/strerror.c
new file mode 100644
index 0000000..bf63926
--- /dev/null
+++ b/lib/sh/strerror.c
@@ -0,0 +1,74 @@
+/* strerror.c - string corresponding to a particular value of errno. */
+
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_STRERROR)
+
+#include <bashtypes.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <shell.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* Return a string corresponding to the error number E. From
+ the ANSI C spec. */
+#if defined (strerror)
+# undef strerror
+#endif
+
+static char *errbase = "Unknown system error ";
+
+char *
+strerror (e)
+ int e;
+{
+ static char emsg[40];
+#if defined (HAVE_SYS_ERRLIST)
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+
+ if (e > 0 && e < sys_nerr)
+ return (sys_errlist[e]);
+ else
+#endif /* HAVE_SYS_ERRLIST */
+ {
+ char *z;
+
+ z = itos (e);
+ strcpy (emsg, errbase);
+ strcat (emsg, z);
+ free (z);
+ return (&emsg[0]);
+ }
+}
+#endif /* HAVE_STRERROR */
diff --git a/lib/sh/strftime.c b/lib/sh/strftime.c
new file mode 100644
index 0000000..60bee79
--- /dev/null
+++ b/lib/sh/strftime.c
@@ -0,0 +1,1012 @@
+/* strftime - formatted time and date to a string */
+/*
+ * Modified slightly by Chet Ramey for inclusion in Bash
+ */
+/*
+ * strftime.c
+ *
+ * Public-domain implementation of ISO C library routine.
+ *
+ * If you can't do prototypes, get GCC.
+ *
+ * The C99 standard now specifies just about all of the formats
+ * that were additional in the earlier versions of this file.
+ *
+ * For extensions from SunOS, add SUNOS_EXT.
+ * For extensions from HP/UX, add HPUX_EXT.
+ * For VMS dates, add VMS_EXT.
+ * For complete POSIX semantics, add POSIX_SEMANTICS.
+ *
+ * The code for %c, %x, and %X follows the C99 specification for
+ * the "C" locale.
+ *
+ * This version ignores LOCALE information.
+ * It also doesn't worry about multi-byte characters.
+ * So there.
+ *
+ * Arnold Robbins
+ * January, February, March, 1991
+ * Updated March, April 1992
+ * Updated April, 1993
+ * Updated February, 1994
+ * Updated May, 1994
+ * Updated January, 1995
+ * Updated September, 1995
+ * Updated January, 1996
+ * Updated July, 1997
+ * Updated October, 1999
+ * Updated September, 2000
+ * Updated December, 2001
+ * Updated January, 2011
+ * Updated April, 2012
+ *
+ * Fixes from ado@elsie.nci.nih.gov,
+ * February 1991, May 1992
+ * Fixes from Tor Lillqvist tml@tik.vtt.fi,
+ * May 1993
+ * Further fixes from ado@elsie.nci.nih.gov,
+ * February 1994
+ * %z code from chip@chinacat.unicom.com,
+ * Applied September 1995
+ * %V code fixed (again) and %G, %g added,
+ * January 1996
+ * %v code fixed, better configuration,
+ * July 1997
+ * Moved to C99 specification.
+ * September 2000
+ * Fixes from Tanaka Akira <akr@m17n.org>
+ * December 2001
+ */
+#include <config.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <posixtime.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/* defaults: season to taste */
+#define SUNOS_EXT 1 /* stuff in SunOS strftime routine */
+#define VMS_EXT 1 /* include %v for VMS date format */
+#define HPUX_EXT 1 /* non-conflicting stuff in HP-UX date */
+#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */
+#define POSIX_2008 1 /* flag and fw for C, F, G, Y formats */
+
+#undef strchr /* avoid AIX weirdness */
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if defined (SHELL)
+extern char *get_string_value (const char *);
+#endif
+
+extern void tzset(void);
+static int weeknumber(const struct tm *timeptr, int firstweekday);
+static int iso8601wknum(const struct tm *timeptr);
+
+#ifndef inline
+#ifdef __GNUC__
+#define inline __inline__
+#else
+#define inline /**/
+#endif
+#endif
+
+#define range(low, item, hi) max(low, min(item, hi))
+
+/* Whew! This stuff is a mess. */
+#if !defined(OS2) && !defined(MSDOS) && !defined(__CYGWIN__) && defined(HAVE_TZNAME)
+extern char *tzname[2];
+extern int daylight;
+#if defined(SOLARIS) || defined(mips) || defined (M_UNIX)
+extern long int timezone, altzone;
+#else
+# if defined (HPUX) || defined(__hpux)
+extern long int timezone;
+# else
+# if !defined(__CYGWIN__)
+extern int timezone, altzone;
+# endif
+# endif
+#endif
+#endif
+
+#undef min /* just in case */
+
+/* min --- return minimum of two numbers */
+
+static inline int
+min(int a, int b)
+{
+ return (a < b ? a : b);
+}
+
+#undef max /* also, just in case */
+
+/* max --- return maximum of two numbers */
+
+static inline int
+max(int a, int b)
+{
+ return (a > b ? a : b);
+}
+
+#ifdef POSIX_2008
+/* iso_8601_2000_year --- format a year per ISO 8601:2000 as in 1003.1 */
+
+static void
+iso_8601_2000_year(char *buf, int year, size_t fw)
+{
+ int extra;
+ char sign = '\0';
+
+ if (year >= -9999 && year <= 9999) {
+ sprintf(buf, "%0*d", (int) fw, year);
+ return;
+ }
+
+ /* now things get weird */
+ if (year > 9999) {
+ sign = '+';
+ } else {
+ sign = '-';
+ year = -year;
+ }
+
+ extra = year / 10000;
+ year %= 10000;
+ sprintf(buf, "%c_%04d_%d", sign, extra, year);
+}
+#endif /* POSIX_2008 */
+
+/* strftime --- produce formatted time */
+
+size_t
+strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
+{
+ char *endp = s + maxsize;
+ char *start = s;
+ auto char tbuf[100];
+ long off;
+ int i, w, oerrno;
+ long y;
+ static short first = 1;
+#ifdef POSIX_SEMANTICS
+ static char *savetz = NULL;
+ static int savetzlen = 0;
+ char *tz;
+#endif /* POSIX_SEMANTICS */
+#ifndef HAVE_TM_ZONE
+#ifndef HAVE_TM_NAME
+#ifndef HAVE_TZNAME
+#ifndef __CYGWIN__
+ extern char *timezone();
+ struct timeval tv;
+ struct timezone zone;
+#endif /* __CYGWIN__ */
+#endif /* HAVE_TZNAME */
+#endif /* HAVE_TM_NAME */
+#endif /* HAVE_TM_ZONE */
+#ifdef POSIX_2008
+ int pad;
+ size_t fw;
+ char flag;
+#endif /* POSIX_2008 */
+
+ /* various tables, useful in North America */
+ static const char *days_a[] = {
+ "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat",
+ };
+ static const char *days_l[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday",
+ };
+ static const char *months_a[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+ static const char *months_l[] = {
+ "January", "February", "March", "April",
+ "May", "June", "July", "August", "September",
+ "October", "November", "December",
+ };
+ static const char *ampm[] = { "AM", "PM", };
+
+ oerrno = errno;
+
+ if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
+ return 0;
+
+ /* quick check if we even need to bother */
+ if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
+ return 0;
+
+#ifndef POSIX_SEMANTICS
+ if (first) {
+ tzset();
+ first = 0;
+ }
+#else /* POSIX_SEMANTICS */
+#if defined (SHELL)
+ tz = get_string_value ("TZ");
+#else
+ tz = getenv("TZ");
+#endif
+ if (first) {
+ if (tz != NULL) {
+ int tzlen = strlen(tz);
+
+ savetz = (char *) malloc(tzlen + 1);
+ if (savetz != NULL) {
+ savetzlen = tzlen + 1;
+ strcpy(savetz, tz);
+ }
+ }
+ tzset();
+ first = 0;
+ }
+ /* if we have a saved TZ, and it is different, recapture and reset */
+ if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
+ i = strlen(tz) + 1;
+ if (i > savetzlen) {
+ savetz = (char *) realloc(savetz, i);
+ if (savetz) {
+ savetzlen = i;
+ strcpy(savetz, tz);
+ }
+ } else
+ strcpy(savetz, tz);
+ tzset();
+ }
+#endif /* POSIX_SEMANTICS */
+
+ for (; *format && s < endp - 1; format++) {
+ tbuf[0] = '\0';
+ if (*format != '%') {
+ *s++ = *format;
+ continue;
+ }
+#ifdef POSIX_2008
+ pad = '\0';
+ fw = 0;
+ flag = '\0';
+ switch (*++format) {
+ case '+':
+ flag = '+';
+ /* fall through */
+ case '0':
+ pad = '0';
+ format++;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+
+ default:
+ format--;
+ goto again;
+ }
+ for (; isdigit(*format); format++) {
+ fw = fw * 10 + (*format - '0');
+ }
+ format--;
+#endif /* POSIX_2008 */
+
+ again:
+ switch (*++format) {
+ case '\0':
+ *s++ = '%';
+ goto out;
+
+ case '%':
+ *s++ = '%';
+ continue;
+
+ case 'a': /* abbreviated weekday name */
+ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, days_a[timeptr->tm_wday]);
+ break;
+
+ case 'A': /* full weekday name */
+ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, days_l[timeptr->tm_wday]);
+ break;
+
+ case 'b': /* abbreviated month name */
+ short_month:
+ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, months_a[timeptr->tm_mon]);
+ break;
+
+ case 'B': /* full month name */
+ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+ strcpy(tbuf, "?");
+ else
+ strcpy(tbuf, months_l[timeptr->tm_mon]);
+ break;
+
+ case 'c': /* appropriate date and time representation */
+ /*
+ * This used to be:
+ *
+ * strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
+ *
+ * Now, per the ISO 1999 C standard, it this:
+ */
+ strftime(tbuf, sizeof tbuf, "%A %B %d %T %Y", timeptr);
+ break;
+
+ case 'C':
+#ifdef POSIX_2008
+ if (pad != '\0' && fw > 0) {
+ size_t min_fw = (flag ? 3 : 2);
+
+ fw = max(fw, min_fw);
+ sprintf(tbuf, flag
+ ? "%+0*ld"
+ : "%0*ld", (int) fw,
+ (timeptr->tm_year + 1900L) / 100);
+ } else
+#endif /* POSIX_2008 */
+ century:
+ sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 100);
+ break;
+
+ case 'd': /* day of the month, 01 - 31 */
+ i = range(1, timeptr->tm_mday, 31);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'D': /* date as %m/%d/%y */
+ strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
+ break;
+
+ case 'e': /* day of month, blank padded */
+ sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
+ break;
+
+ case 'E':
+ /* POSIX (now C99) locale extensions, ignored for now */
+ goto again;
+
+ case 'F': /* ISO 8601 date representation */
+ {
+#ifdef POSIX_2008
+ /*
+ * Field width for %F is for the whole thing.
+ * It must be at least 10.
+ */
+ char m_d[10];
+ strftime(m_d, sizeof m_d, "-%m-%d", timeptr);
+ size_t min_fw = 10;
+
+ if (pad != '\0' && fw > 0) {
+ fw = max(fw, min_fw);
+ } else {
+ fw = min_fw;
+ }
+
+ fw -= 6; /* -XX-XX at end are invariant */
+
+ iso_8601_2000_year(tbuf, timeptr->tm_year + 1900, fw);
+ strcat(tbuf, m_d);
+#else
+ strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
+#endif /* POSIX_2008 */
+ }
+ break;
+
+ case 'g':
+ case 'G':
+ /*
+ * Year of ISO week.
+ *
+ * If it's December but the ISO week number is one,
+ * that week is in next year.
+ * If it's January but the ISO week number is 52 or
+ * 53, that week is in last year.
+ * Otherwise, it's this year.
+ */
+ w = iso8601wknum(timeptr);
+ if (timeptr->tm_mon == 11 && w == 1)
+ y = 1900L + timeptr->tm_year + 1;
+ else if (timeptr->tm_mon == 0 && w >= 52)
+ y = 1900L + timeptr->tm_year - 1;
+ else
+ y = 1900L + timeptr->tm_year;
+
+ if (*format == 'G') {
+#ifdef POSIX_2008
+ if (pad != '\0' && fw > 0) {
+ size_t min_fw = 4;
+
+ fw = max(fw, min_fw);
+ sprintf(tbuf, flag
+ ? "%+0*ld"
+ : "%0*ld", (int) fw,
+ y);
+ } else
+#endif /* POSIX_2008 */
+ sprintf(tbuf, "%ld", y);
+ }
+ else
+ sprintf(tbuf, "%02ld", y % 100);
+ break;
+
+ case 'h': /* abbreviated month name */
+ goto short_month;
+
+ case 'H': /* hour, 24-hour clock, 00 - 23 */
+ i = range(0, timeptr->tm_hour, 23);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'I': /* hour, 12-hour clock, 01 - 12 */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i == 0)
+ i = 12;
+ else if (i > 12)
+ i -= 12;
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'j': /* day of the year, 001 - 366 */
+ sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
+ break;
+
+ case 'm': /* month, 01 - 12 */
+ i = range(0, timeptr->tm_mon, 11);
+ sprintf(tbuf, "%02d", i + 1);
+ break;
+
+ case 'M': /* minute, 00 - 59 */
+ i = range(0, timeptr->tm_min, 59);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'n': /* same as \n */
+ tbuf[0] = '\n';
+ tbuf[1] = '\0';
+ break;
+
+ case 'O':
+ /* POSIX (now C99) locale extensions, ignored for now */
+ goto again;
+
+ case 'p': /* am or pm based on 12-hour clock */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i < 12)
+ strcpy(tbuf, ampm[0]);
+ else
+ strcpy(tbuf, ampm[1]);
+ break;
+
+ case 'r': /* time as %I:%M:%S %p */
+ strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
+ break;
+
+ case 'R': /* time as %H:%M */
+ strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
+ break;
+
+#if defined(HAVE_MKTIME)
+ case 's': /* time as seconds since the Epoch */
+ {
+ struct tm non_const_timeptr;
+
+ non_const_timeptr = *timeptr;
+ sprintf(tbuf, "%ld", mktime(& non_const_timeptr));
+ break;
+ }
+#endif /* defined(HAVE_MKTIME) */
+
+ case 'S': /* second, 00 - 60 */
+ i = range(0, timeptr->tm_sec, 60);
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 't': /* same as \t */
+ tbuf[0] = '\t';
+ tbuf[1] = '\0';
+ break;
+
+ case 'T': /* time as %H:%M:%S */
+ the_time:
+ strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
+ break;
+
+ case 'u':
+ /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
+ sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
+ timeptr->tm_wday);
+ break;
+
+ case 'U': /* week of year, Sunday is first day of week */
+ sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
+ break;
+
+ case 'V': /* week of year according ISO 8601 */
+ sprintf(tbuf, "%02d", iso8601wknum(timeptr));
+ break;
+
+ case 'w': /* weekday, Sunday == 0, 0 - 6 */
+ i = range(0, timeptr->tm_wday, 6);
+ sprintf(tbuf, "%d", i);
+ break;
+
+ case 'W': /* week of year, Monday is first day of week */
+ sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
+ break;
+
+ case 'x': /* appropriate date representation */
+ strftime(tbuf, sizeof tbuf, "%A %B %d %Y", timeptr);
+ break;
+
+ case 'X': /* appropriate time representation */
+ goto the_time;
+ break;
+
+ case 'y': /* year without a century, 00 - 99 */
+ year:
+ i = timeptr->tm_year % 100;
+ sprintf(tbuf, "%02d", i);
+ break;
+
+ case 'Y': /* year with century */
+#ifdef POSIX_2008
+ if (pad != '\0' && fw > 0) {
+ size_t min_fw = 4;
+
+ fw = max(fw, min_fw);
+ sprintf(tbuf, flag
+ ? "%+0*ld"
+ : "%0*ld", (int) fw,
+ 1900L + timeptr->tm_year);
+ } else
+#endif /* POSIX_2008 */
+ sprintf(tbuf, "%ld", 1900L + timeptr->tm_year);
+ break;
+
+ /*
+ * From: Chip Rosenthal <chip@chinacat.unicom.com>
+ * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
+ *
+ * Warning: the %z [code] is implemented by inspecting the
+ * timezone name conditional compile settings, and
+ * inferring a method to get timezone offsets. I've tried
+ * this code on a couple of machines, but I don't doubt
+ * there is some system out there that won't like it.
+ * Maybe the easiest thing to do would be to bracket this
+ * with an #ifdef that can turn it off. The %z feature
+ * would be an admittedly obscure one that most folks can
+ * live without, but it would be a great help to those of
+ * us that muck around with various message processors.
+ */
+ case 'z': /* time zone offset east of GMT e.g. -0600 */
+ if (timeptr->tm_isdst < 0)
+ break;
+#ifdef HAVE_TM_NAME
+ /*
+ * Systems with tm_name probably have tm_tzadj as
+ * secs west of GMT. Convert to mins east of GMT.
+ */
+ off = -timeptr->tm_tzadj / 60;
+#else /* !HAVE_TM_NAME */
+#ifdef HAVE_TM_ZONE
+ /*
+ * Systems with tm_zone probably have tm_gmtoff as
+ * secs east of GMT. Convert to mins east of GMT.
+ */
+ off = timeptr->tm_gmtoff / 60;
+#else /* !HAVE_TM_ZONE */
+#if HAVE_TZNAME
+ /*
+ * Systems with tzname[] probably have timezone as
+ * secs west of GMT. Convert to mins east of GMT.
+ */
+# if defined(__hpux) || defined (HPUX) || defined(__CYGWIN__)
+ off = -timezone / 60;
+# else
+ /* ADR: 4 August 2001, fixed this per gazelle@interaccess.com */
+ off = -(daylight ? altzone : timezone) / 60;
+# endif
+#else /* !HAVE_TZNAME */
+ gettimeofday(& tv, & zone);
+ off = -zone.tz_minuteswest;
+#endif /* !HAVE_TZNAME */
+#endif /* !HAVE_TM_ZONE */
+#endif /* !HAVE_TM_NAME */
+ if (off < 0) {
+ tbuf[0] = '-';
+ off = -off;
+ } else {
+ tbuf[0] = '+';
+ }
+ sprintf(tbuf+1, "%02ld%02ld", off/60, off%60);
+ break;
+
+ case 'Z': /* time zone name or abbreviation */
+#ifdef HAVE_TZNAME
+ i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
+ strcpy(tbuf, tzname[i]);
+#else
+#ifdef HAVE_TM_ZONE
+ strcpy(tbuf, timeptr->tm_zone);
+#else
+#ifdef HAVE_TM_NAME
+ strcpy(tbuf, timeptr->tm_name);
+#else
+ gettimeofday(& tv, & zone);
+ strcpy(tbuf, timezone(zone.tz_minuteswest,
+ timeptr->tm_isdst > 0));
+#endif /* HAVE_TM_NAME */
+#endif /* HAVE_TM_ZONE */
+#endif /* HAVE_TZNAME */
+ break;
+
+#ifdef SUNOS_EXT
+ case 'k': /* hour, 24-hour clock, blank pad */
+ sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
+ break;
+
+ case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */
+ i = range(0, timeptr->tm_hour, 23);
+ if (i == 0)
+ i = 12;
+ else if (i > 12)
+ i -= 12;
+ sprintf(tbuf, "%2d", i);
+ break;
+#endif
+
+#ifdef HPUX_EXT
+ case 'N': /* Emperor/Era name */
+ /* this is essentially the same as the century */
+ goto century; /* %C */
+
+ case 'o': /* Emperor/Era year */
+ goto year; /* %y */
+#endif /* HPUX_EXT */
+
+
+#ifdef VMS_EXT
+ case 'v': /* date as dd-bbb-YYYY */
+ sprintf(tbuf, "%2d-%3.3s-%4ld",
+ range(1, timeptr->tm_mday, 31),
+ months_a[range(0, timeptr->tm_mon, 11)],
+ timeptr->tm_year + 1900L);
+ for (i = 3; i < 6; i++)
+ if (islower(tbuf[i]))
+ tbuf[i] = toupper(tbuf[i]);
+ break;
+#endif
+
+ default:
+ tbuf[0] = '%';
+ tbuf[1] = *format;
+ tbuf[2] = '\0';
+ break;
+ }
+ i = strlen(tbuf);
+ if (i) {
+ if (s + i < endp - 1) {
+ strcpy(s, tbuf);
+ s += i;
+ } else
+ return 0;
+ }
+ }
+out:
+ if (s < endp && *format == '\0') {
+ *s = '\0';
+ if (s == start)
+ errno = oerrno;
+ return (s - start);
+ } else
+ return 0;
+}
+
+/* isleap --- is a year a leap year? */
+
+static int
+isleap(long year)
+{
+ return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+}
+
+
+/* iso8601wknum --- compute week number according to ISO 8601 */
+
+static int
+iso8601wknum(const struct tm *timeptr)
+{
+ /*
+ * From 1003.2:
+ * If the week (Monday to Sunday) containing January 1
+ * has four or more days in the new year, then it is week 1;
+ * otherwise it is the highest numbered week of the previous
+ * year (52 or 53), and the next week is week 1.
+ *
+ * ADR: This means if Jan 1 was Monday through Thursday,
+ * it was week 1, otherwise week 52 or 53.
+ *
+ * XPG4 erroneously included POSIX.2 rationale text in the
+ * main body of the standard. Thus it requires week 53.
+ */
+
+ int weeknum, jan1day, diff;
+
+ /* get week number, Monday as first day of the week */
+ weeknum = weeknumber(timeptr, 1);
+
+ /*
+ * With thanks and tip of the hatlo to tml@tik.vtt.fi
+ *
+ * What day of the week does January 1 fall on?
+ * We know that
+ * (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
+ * (timeptr->tm_wday - jan1.tm_wday) MOD 7
+ * and that
+ * jan1.tm_yday == 0
+ * and that
+ * timeptr->tm_wday MOD 7 == timeptr->tm_wday
+ * from which it follows that. . .
+ */
+ jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
+ if (jan1day < 0)
+ jan1day += 7;
+
+ /*
+ * If Jan 1 was a Monday through Thursday, it was in
+ * week 1. Otherwise it was last year's highest week, which is
+ * this year's week 0.
+ *
+ * What does that mean?
+ * If Jan 1 was Monday, the week number is exactly right, it can
+ * never be 0.
+ * If it was Tuesday through Thursday, the weeknumber is one
+ * less than it should be, so we add one.
+ * Otherwise, Friday, Saturday or Sunday, the week number is
+ * OK, but if it is 0, it needs to be 52 or 53.
+ */
+ switch (jan1day) {
+ case 1: /* Monday */
+ break;
+ case 2: /* Tuesday */
+ case 3: /* Wednesday */
+ case 4: /* Thursday */
+ weeknum++;
+ break;
+ case 5: /* Friday */
+ case 6: /* Saturday */
+ case 0: /* Sunday */
+ if (weeknum == 0) {
+#ifdef USE_BROKEN_XPG4
+ /* XPG4 (as of March 1994) says 53 unconditionally */
+ weeknum = 53;
+#else
+ /* get week number of last week of last year */
+ struct tm dec31ly; /* 12/31 last year */
+ dec31ly = *timeptr;
+ dec31ly.tm_year--;
+ dec31ly.tm_mon = 11;
+ dec31ly.tm_mday = 31;
+ dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
+ dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L);
+ weeknum = iso8601wknum(& dec31ly);
+#endif
+ }
+ break;
+ }
+
+ if (timeptr->tm_mon == 11) {
+ /*
+ * The last week of the year
+ * can be in week 1 of next year.
+ * Sigh.
+ *
+ * This can only happen if
+ * M T W
+ * 29 30 31
+ * 30 31
+ * 31
+ */
+ int wday, mday;
+
+ wday = timeptr->tm_wday;
+ mday = timeptr->tm_mday;
+ if ( (wday == 1 && (mday >= 29 && mday <= 31))
+ || (wday == 2 && (mday == 30 || mday == 31))
+ || (wday == 3 && mday == 31))
+ weeknum = 1;
+ }
+
+ return weeknum;
+}
+
+/* weeknumber --- figure how many weeks into the year */
+
+/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
+
+static int
+weeknumber(const struct tm *timeptr, int firstweekday)
+{
+ int wday = timeptr->tm_wday;
+ int ret;
+
+ if (firstweekday == 1) {
+ if (wday == 0) /* sunday */
+ wday = 6;
+ else
+ wday--;
+ }
+ ret = ((timeptr->tm_yday + 7 - wday) / 7);
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+#if 0
+/* ADR --- I'm loathe to mess with ado's code ... */
+
+Date: Wed, 24 Apr 91 20:54:08 MDT
+From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
+To: arnold@audiofax.com
+
+Hi Arnold,
+in a process of fixing of strftime() in libraries on Atari ST I grabbed
+some pieces of code from your own strftime. When doing that it came
+to mind that your weeknumber() function compiles a little bit nicer
+in the following form:
+/*
+ * firstweekday is 0 if starting in Sunday, non-zero if in Monday
+ */
+{
+ return (timeptr->tm_yday - timeptr->tm_wday +
+ (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
+}
+How nicer it depends on a compiler, of course, but always a tiny bit.
+
+ Cheers,
+ Michal
+ ntomczak@vm.ucs.ualberta.ca
+#endif
+
+#ifdef TEST_STRFTIME
+
+/*
+ * NAME:
+ * tst
+ *
+ * SYNOPSIS:
+ * tst
+ *
+ * DESCRIPTION:
+ * "tst" is a test driver for the function "strftime".
+ *
+ * OPTIONS:
+ * None.
+ *
+ * AUTHOR:
+ * Karl Vogel
+ * Control Data Systems, Inc.
+ * vogelke@c-17igp.wpafb.af.mil
+ *
+ * BUGS:
+ * None noticed yet.
+ *
+ * COMPILE:
+ * cc -o tst -DTEST_STRFTIME strftime.c
+ */
+
+/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
+
+#ifndef NULL
+#include <stdio.h>
+#endif
+#include <sys/time.h>
+#include <string.h>
+
+#define MAXTIME 132
+
+/*
+ * Array of time formats.
+ */
+
+static char *array[] =
+{
+ "(%%A) full weekday name, var length (Sunday..Saturday) %A",
+ "(%%B) full month name, var length (January..December) %B",
+ "(%%C) Century %C",
+ "(%%D) date (%%m/%%d/%%y) %D",
+ "(%%E) Locale extensions (ignored) %E",
+ "(%%F) full month name, var length (January..December) %F",
+ "(%%H) hour (24-hour clock, 00..23) %H",
+ "(%%I) hour (12-hour clock, 01..12) %I",
+ "(%%M) minute (00..59) %M",
+ "(%%N) Emperor/Era Name %N",
+ "(%%O) Locale extensions (ignored) %O",
+ "(%%R) time, 24-hour (%%H:%%M) %R",
+ "(%%S) second (00..60) %S",
+ "(%%T) time, 24-hour (%%H:%%M:%%S) %T",
+ "(%%U) week of year, Sunday as first day of week (00..53) %U",
+ "(%%V) week of year according to ISO 8601 %V",
+ "(%%W) week of year, Monday as first day of week (00..53) %W",
+ "(%%X) appropriate locale time representation (%H:%M:%S) %X",
+ "(%%Y) year with century (1970...) %Y",
+ "(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
+ "(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
+ "(%%b) locale's abbreviated month name (Jan..Dec) %b",
+ "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
+ "(%%d) day of the month (01..31) %d",
+ "(%%e) day of the month, blank-padded ( 1..31) %e",
+ "(%%h) should be same as (%%b) %h",
+ "(%%j) day of the year (001..366) %j",
+ "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
+ "(%%l) hour, 12-hour clock, blank pad ( 0..12) %l",
+ "(%%m) month (01..12) %m",
+ "(%%o) Emperor/Era Year %o",
+ "(%%p) locale's AM or PM based on 12-hour clock %p",
+ "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
+ "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
+ "(%%v) VMS date (dd-bbb-YYYY) %v",
+ "(%%w) day of week (0..6, Sunday == 0) %w",
+ "(%%x) appropriate locale date representation %x",
+ "(%%y) last two digits of year (00..99) %y",
+ "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z",
+ (char *) NULL
+};
+
+/* main routine. */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ long time();
+
+ char *next;
+ char string[MAXTIME];
+
+ int k;
+ int length;
+
+ struct tm *tm;
+
+ long clock;
+
+ /* Call the function. */
+
+ clock = time((long *) 0);
+ tm = localtime(&clock);
+
+ for (k = 0; next = array[k]; k++) {
+ length = strftime(string, MAXTIME, next, tm);
+ printf("%s\n", string);
+ }
+
+ exit(0);
+}
+#endif /* TEST_STRFTIME */
diff --git a/lib/sh/stringlist.c b/lib/sh/stringlist.c
new file mode 100644
index 0000000..3f28b63
--- /dev/null
+++ b/lib/sh/stringlist.c
@@ -0,0 +1,297 @@
+/* stringlist.c - functions to handle a generic `list of strings' structure */
+
+/* Copyright (C) 2000-2019 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <bashansi.h>
+
+#include "shell.h"
+
+#ifdef STRDUP
+# undef STRDUP
+#endif
+#define STRDUP(x) ((x) ? savestring (x) : (char *)NULL)
+
+/* Allocate a new STRINGLIST, with room for N strings. */
+
+STRINGLIST *
+strlist_create (n)
+ int n;
+{
+ STRINGLIST *ret;
+ register int i;
+
+ ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
+ if (n)
+ {
+ ret->list = strvec_create (n+1);
+ ret->list_size = n;
+ for (i = 0; i < n; i++)
+ ret->list[i] = (char *)NULL;
+ }
+ else
+ {
+ ret->list = (char **)NULL;
+ ret->list_size = 0;
+ }
+ ret->list_len = 0;
+ return ret;
+}
+
+STRINGLIST *
+strlist_resize (sl, n)
+ STRINGLIST *sl;
+ int n;
+{
+ register int i;
+
+ if (sl == 0)
+ return (sl = strlist_create (n));
+
+ if (n > sl->list_size)
+ {
+ sl->list = strvec_resize (sl->list, n + 1);
+ for (i = sl->list_size; i <= n; i++)
+ sl->list[i] = (char *)NULL;
+ sl->list_size = n;
+ }
+ return sl;
+}
+
+void
+strlist_flush (sl)
+ STRINGLIST *sl;
+{
+ if (sl == 0 || sl->list == 0)
+ return;
+ strvec_flush (sl->list);
+ sl->list_len = 0;
+}
+
+void
+strlist_dispose (sl)
+ STRINGLIST *sl;
+{
+ if (sl == 0)
+ return;
+ if (sl->list)
+ strvec_dispose (sl->list);
+ free (sl);
+}
+
+int
+strlist_remove (sl, s)
+ STRINGLIST *sl;
+ char *s;
+{
+ int r;
+
+ if (sl == 0 || sl->list == 0 || sl->list_len == 0)
+ return 0;
+
+ r = strvec_remove (sl->list, s);
+ if (r)
+ sl->list_len--;
+ return r;
+}
+
+STRINGLIST *
+strlist_copy (sl)
+ STRINGLIST *sl;
+{
+ STRINGLIST *new;
+ register int i;
+
+ if (sl == 0)
+ return ((STRINGLIST *)0);
+ new = strlist_create (sl->list_size);
+ /* I'd like to use strvec_copy, but that doesn't copy everything. */
+ if (sl->list)
+ {
+ for (i = 0; i < sl->list_size; i++)
+ new->list[i] = STRDUP (sl->list[i]);
+ }
+ new->list_size = sl->list_size;
+ new->list_len = sl->list_len;
+ /* just being careful */
+ if (new->list)
+ new->list[new->list_len] = (char *)NULL;
+ return new;
+}
+
+/* Return a new STRINGLIST with everything from M1 and M2. */
+
+STRINGLIST *
+strlist_merge (m1, m2)
+ STRINGLIST *m1, *m2;
+{
+ STRINGLIST *sl;
+ int i, n, l1, l2;
+
+ l1 = m1 ? m1->list_len : 0;
+ l2 = m2 ? m2->list_len : 0;
+
+ sl = strlist_create (l1 + l2 + 1);
+ for (i = n = 0; i < l1; i++, n++)
+ sl->list[n] = STRDUP (m1->list[i]);
+ for (i = 0; i < l2; i++, n++)
+ sl->list[n] = STRDUP (m2->list[i]);
+ sl->list_len = n;
+ sl->list[n] = (char *)NULL;
+ return (sl);
+}
+
+/* Make STRINGLIST M1 contain everything in M1 and M2. */
+STRINGLIST *
+strlist_append (m1, m2)
+ STRINGLIST *m1, *m2;
+{
+ register int i, n, len1, len2;
+
+ if (m1 == 0)
+ return (m2 ? strlist_copy (m2) : (STRINGLIST *)0);
+
+ len1 = m1->list_len;
+ len2 = m2 ? m2->list_len : 0;
+
+ if (len2)
+ {
+ m1 = strlist_resize (m1, len1 + len2 + 1);
+ for (i = 0, n = len1; i < len2; i++, n++)
+ m1->list[n] = STRDUP (m2->list[i]);
+ m1->list[n] = (char *)NULL;
+ m1->list_len = n;
+ }
+
+ return m1;
+}
+
+STRINGLIST *
+strlist_prefix_suffix (sl, prefix, suffix)
+ STRINGLIST *sl;
+ char *prefix, *suffix;
+{
+ int plen, slen, tlen, llen, i;
+ char *t;
+
+ if (sl == 0 || sl->list == 0 || sl->list_len == 0)
+ return sl;
+
+ plen = STRLEN (prefix);
+ slen = STRLEN (suffix);
+
+ if (plen == 0 && slen == 0)
+ return (sl);
+
+ for (i = 0; i < sl->list_len; i++)
+ {
+ llen = STRLEN (sl->list[i]);
+ tlen = plen + llen + slen + 1;
+ t = (char *)xmalloc (tlen + 1);
+ if (plen)
+ strcpy (t, prefix);
+ strcpy (t + plen, sl->list[i]);
+ if (slen)
+ strcpy (t + plen + llen, suffix);
+ free (sl->list[i]);
+ sl->list[i] = t;
+ }
+
+ return (sl);
+}
+
+void
+strlist_print (sl, prefix)
+ STRINGLIST *sl;
+ char *prefix;
+{
+ register int i;
+
+ if (sl == 0)
+ return;
+ for (i = 0; i < sl->list_len; i++)
+ printf ("%s%s\n", prefix ? prefix : "", sl->list[i]);
+}
+
+void
+strlist_walk (sl, func)
+ STRINGLIST *sl;
+ sh_strlist_map_func_t *func;
+{
+ register int i;
+
+ if (sl == 0)
+ return;
+ for (i = 0; i < sl->list_len; i++)
+ if ((*func)(sl->list[i]) < 0)
+ break;
+}
+
+void
+strlist_sort (sl)
+ STRINGLIST *sl;
+{
+ if (sl == 0 || sl->list_len == 0 || sl->list == 0)
+ return;
+ strvec_sort (sl->list, 0);
+}
+
+STRINGLIST *
+strlist_from_word_list (list, alloc, starting_index, ip)
+ WORD_LIST *list;
+ int alloc, starting_index, *ip;
+{
+ STRINGLIST *ret;
+ int slen, len;
+
+ if (list == 0)
+ {
+ if (ip)
+ *ip = 0;
+ return ((STRINGLIST *)0);
+ }
+ slen = list_length (list);
+ ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
+ ret->list = strvec_from_word_list (list, alloc, starting_index, &len);
+ ret->list_size = slen + starting_index;
+ ret->list_len = len;
+ if (ip)
+ *ip = len;
+ return ret;
+}
+
+WORD_LIST *
+strlist_to_word_list (sl, alloc, starting_index)
+ STRINGLIST *sl;
+ int alloc, starting_index;
+{
+ WORD_LIST *list;
+
+ if (sl == 0 || sl->list == 0)
+ return ((WORD_LIST *)NULL);
+
+ list = strvec_to_word_list (sl->list, alloc, starting_index);
+ return list;
+}
diff --git a/lib/sh/stringvec.c b/lib/sh/stringvec.c
new file mode 100644
index 0000000..8600042
--- /dev/null
+++ b/lib/sh/stringvec.c
@@ -0,0 +1,272 @@
+/* stringvec.c - functions for managing arrays of strings. */
+
+/* Copyright (C) 2000-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include <stdio.h>
+#include <chartypes.h>
+
+#include "shell.h"
+
+/* Allocate an array of strings with room for N members. */
+char **
+strvec_create (n)
+ int n;
+{
+ return ((char **)xmalloc ((n) * sizeof (char *)));
+}
+
+/* Allocate an array of strings with room for N members. */
+char **
+strvec_mcreate (n)
+ int n;
+{
+ return ((char **)malloc ((n) * sizeof (char *)));
+}
+
+char **
+strvec_resize (array, nsize)
+ char **array;
+ int nsize;
+{
+ return ((char **)xrealloc (array, nsize * sizeof (char *)));
+}
+
+char **
+strvec_mresize (array, nsize)
+ char **array;
+ int nsize;
+{
+ return ((char **)realloc (array, nsize * sizeof (char *)));
+}
+
+/* Return the length of ARRAY, a NULL terminated array of char *. */
+int
+strvec_len (array)
+ char **array;
+{
+ register int i;
+
+ for (i = 0; array[i]; i++);
+ return (i);
+}
+
+/* Free the contents of ARRAY, a NULL terminated array of char *. */
+void
+strvec_flush (array)
+ char **array;
+{
+ register int i;
+
+ if (array == 0)
+ return;
+
+ for (i = 0; array[i]; i++)
+ free (array[i]);
+}
+
+void
+strvec_dispose (array)
+ char **array;
+{
+ if (array == 0)
+ return;
+
+ strvec_flush (array);
+ free (array);
+}
+
+int
+strvec_remove (array, name)
+ char **array, *name;
+{
+ register int i, j;
+ char *x;
+
+ if (array == 0)
+ return 0;
+
+ for (i = 0; array[i]; i++)
+ if (STREQ (name, array[i]))
+ {
+ x = array[i];
+ for (j = i; array[j]; j++)
+ array[j] = array[j + 1];
+ free (x);
+ return 1;
+ }
+ return 0;
+}
+
+/* Find NAME in ARRAY. Return the index of NAME, or -1 if not present.
+ ARRAY should be NULL terminated. */
+int
+strvec_search (array, name)
+ char **array, *name;
+{
+ int i;
+
+ for (i = 0; array[i]; i++)
+ if (STREQ (name, array[i]))
+ return (i);
+
+ return (-1);
+}
+
+/* Allocate and return a new copy of ARRAY and its contents. */
+char **
+strvec_copy (array)
+ char **array;
+{
+ register int i;
+ int len;
+ char **ret;
+
+ len = strvec_len (array);
+
+ ret = (char **)xmalloc ((len + 1) * sizeof (char *));
+ for (i = 0; array[i]; i++)
+ ret[i] = savestring (array[i]);
+ ret[i] = (char *)NULL;
+
+ return (ret);
+}
+
+/* Comparison routine for use by qsort that conforms to the new Posix
+ requirements (http://austingroupbugs.net/view.php?id=1070).
+
+ Perform a bytewise comparison if *S1 and *S2 collate equally. */
+int
+strvec_posixcmp (s1, s2)
+ register char **s1, **s2;
+{
+ int result;
+
+#if defined (HAVE_STRCOLL)
+ result = strcoll (*s1, *s2);
+ if (result != 0)
+ return result;
+#endif
+
+ if ((result = **s1 - **s2) == 0)
+ result = strcmp (*s1, *s2);
+
+ return (result);
+}
+
+/* Comparison routine for use with qsort() on arrays of strings. Uses
+ strcoll(3) if available, otherwise it uses strcmp(3). */
+int
+strvec_strcmp (s1, s2)
+ register char **s1, **s2;
+{
+#if defined (HAVE_STRCOLL)
+ return (strcoll (*s1, *s2));
+#else /* !HAVE_STRCOLL */
+ int result;
+
+ if ((result = **s1 - **s2) == 0)
+ result = strcmp (*s1, *s2);
+
+ return (result);
+#endif /* !HAVE_STRCOLL */
+}
+
+/* Sort ARRAY, a null terminated array of pointers to strings. */
+void
+strvec_sort (array, posix)
+ char **array;
+ int posix;
+{
+ if (posix)
+ qsort (array, strvec_len (array), sizeof (char *), (QSFUNC *)strvec_posixcmp);
+ else
+ qsort (array, strvec_len (array), sizeof (char *), (QSFUNC *)strvec_strcmp);
+}
+
+/* Cons up a new array of words. The words are taken from LIST,
+ which is a WORD_LIST *. If ALLOC is true, everything is malloc'ed,
+ so you should free everything in this array when you are done.
+ The array is NULL terminated. If IP is non-null, it gets the
+ number of words in the returned array. STARTING_INDEX says where
+ to start filling in the returned array; it can be used to reserve
+ space at the beginning of the array. */
+
+char **
+strvec_from_word_list (list, alloc, starting_index, ip)
+ WORD_LIST *list;
+ int alloc, starting_index, *ip;
+{
+ int count;
+ char **array;
+
+ count = list_length (list);
+ array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *));
+
+ for (count = 0; count < starting_index; count++)
+ array[count] = (char *)NULL;
+ for (count = starting_index; list; count++, list = list->next)
+ array[count] = alloc ? savestring (list->word->word) : list->word->word;
+ array[count] = (char *)NULL;
+
+ if (ip)
+ *ip = count;
+ return (array);
+}
+
+/* Convert an array of strings into the form used internally by the shell.
+ ALLOC means to allocate new storage for each WORD_DESC in the returned
+ list rather than copy the values in ARRAY. STARTING_INDEX says where
+ in ARRAY to begin. */
+
+WORD_LIST *
+strvec_to_word_list (array, alloc, starting_index)
+ char **array;
+ int alloc, starting_index;
+{
+ WORD_LIST *list;
+ WORD_DESC *w;
+ int i, count;
+
+ if (array == 0 || array[0] == 0)
+ return (WORD_LIST *)NULL;
+
+ for (count = 0; array[count]; count++)
+ ;
+
+ for (i = starting_index, list = (WORD_LIST *)NULL; i < count; i++)
+ {
+ w = make_bare_word (alloc ? array[i] : "");
+ if (alloc == 0)
+ {
+ free (w->word);
+ w->word = array[i];
+ }
+ list = make_word_list (w, list);
+ }
+ return (REVERSE_LIST (list, WORD_LIST *));
+}
diff --git a/lib/sh/strnlen.c b/lib/sh/strnlen.c
new file mode 100644
index 0000000..10414d3
--- /dev/null
+++ b/lib/sh/strnlen.c
@@ -0,0 +1,49 @@
+/* strnlen - return length of passed string, with length limit */
+
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined (HAVE_STRNLEN)
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdc.h>
+
+/* Find the length of S, but scan at most MAXLEN characters. If no '\0'
+ terminator is found within the first MAXLEN characters, return MAXLEN. */
+size_t
+strnlen (s, maxlen)
+ register const char *s;
+ size_t maxlen;
+{
+ register const char *e;
+ size_t n;
+
+ for (e = s, n = 0; *e && n < maxlen; e++, n++)
+ ;
+ return n;
+}
+#endif
diff --git a/lib/sh/strpbrk.c b/lib/sh/strpbrk.c
new file mode 100644
index 0000000..8cce830
--- /dev/null
+++ b/lib/sh/strpbrk.c
@@ -0,0 +1,49 @@
+/* strpbrk.c - locate multiple characters in a string */
+
+/* Copyright (C) 1991, 1994 Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined (HAVE_STRPBRK)
+
+#include <stdc.h>
+
+/* Find the first occurrence in S of any character in ACCEPT. */
+char *
+strpbrk (s, accept)
+ register const char *s;
+ register const char *accept;
+{
+ while (*s != '\0')
+ {
+ const char *a = accept;
+ while (*a != '\0')
+ if (*a++ == *s)
+ return (char *) s;
+ ++s;
+ }
+
+ return 0;
+}
+#endif
diff --git a/lib/sh/strstr.c b/lib/sh/strstr.c
new file mode 100644
index 0000000..c43b05e
--- /dev/null
+++ b/lib/sh/strstr.c
@@ -0,0 +1,125 @@
+/* strstr - find a substring within a string */
+
+/* Copyright (C) 1994, 1999 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * My personal strstr() implementation that beats most other algorithms.
+ * Until someone tells me otherwise, I assume that this is the
+ * fastest implementation of strstr() in C.
+ * I deliberately chose not to comment it. You should have at least
+ * as much fun trying to understand it, as I had to write it :-).
+ *
+ * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined _LIBC || defined HAVE_STRING_H
+# include <string.h>
+#endif
+#include <sys/types.h>
+
+typedef unsigned chartype;
+
+#undef strstr
+
+char *
+strstr (const char *phaystack, const char *pneedle)
+{
+ register const unsigned char *haystack, *needle;
+ register chartype b, c;
+
+ haystack = (const unsigned char *) phaystack;
+ needle = (const unsigned char *) pneedle;
+
+ b = *needle;
+ if (b != '\0')
+ {
+ haystack--; /* possible ANSI violation */
+ do
+ {
+ c = *++haystack;
+ if (c == '\0')
+ goto ret0;
+ }
+ while (c != b);
+
+ c = *++needle;
+ if (c == '\0')
+ goto foundneedle;
+ ++needle;
+ goto jin;
+
+ for (;;)
+ {
+ register chartype a;
+ register const unsigned char *rhaystack, *rneedle;
+
+ do
+ {
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+ if (a == b)
+ break;
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+shloop:; }
+ while (a != b);
+
+jin: a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+
+ if (a != c)
+ goto shloop;
+
+ rhaystack = haystack-- + 1;
+ rneedle = needle;
+ a = *rneedle;
+
+ if (*rhaystack == a)
+ do
+ {
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ if (*rhaystack != a)
+ break;
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ }
+ while (*rhaystack == a);
+
+ needle = rneedle; /* took the register-poor approach */
+
+ if (a == '\0')
+ break;
+ }
+ }
+foundneedle:
+ return (char*) haystack;
+ret0:
+ return 0;
+}
diff --git a/lib/sh/strtod.c b/lib/sh/strtod.c
new file mode 100644
index 0000000..55e1154
--- /dev/null
+++ b/lib/sh/strtod.c
@@ -0,0 +1,207 @@
+/* strtod.c - convert string to double-precision floating-point value. */
+
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef HAVE_STRTOD
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+#include <chartypes.h>
+#include <math.h>
+
+#if HAVE_FLOAT_H
+# include <float.h>
+#else
+# define DBL_MAX 1.7976931348623159e+308
+# define DBL_MIN 2.2250738585072010e-308
+#endif
+
+#include <bashansi.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+#ifndef HUGE_VAL
+# define HUGE_VAL HUGE
+#endif
+
+#ifndef locale_decpoint
+extern int locale_decpoint PARAMS((void));
+#endif
+
+/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the
+ character after the last one used in the number is put in *ENDPTR. */
+double
+strtod (nptr, endptr)
+ const char *nptr;
+ char **endptr;
+{
+ register const char *s;
+ short sign;
+
+ /* The number so far. */
+ double num;
+
+ int radixchar;
+ int got_dot; /* Found a decimal point. */
+ int got_digit; /* Seen any digits. */
+
+ /* The exponent of the number. */
+ long int exponent;
+
+ if (nptr == NULL)
+ {
+ errno = EINVAL;
+ goto noconv;
+ }
+
+ s = nptr;
+
+ /* Eat whitespace. */
+ while (ISSPACE ((unsigned char)*s))
+ ++s;
+
+ /* Get the sign. */
+ sign = *s == '-' ? -1 : 1;
+ if (*s == '-' || *s == '+')
+ ++s;
+
+ radixchar = locale_decpoint ();
+ num = 0.0;
+ got_dot = 0;
+ got_digit = 0;
+ exponent = 0;
+ for (;; ++s)
+ {
+ if (DIGIT (*s))
+ {
+ got_digit = 1;
+
+ /* Make sure that multiplication by 10 will not overflow. */
+ if (num > DBL_MAX * 0.1)
+ /* The value of the digit doesn't matter, since we have already
+ gotten as many digits as can be represented in a `double'.
+ This doesn't necessarily mean the result will overflow.
+ The exponent may reduce it to within range.
+
+ We just need to record that there was another
+ digit so that we can multiply by 10 later. */
+ ++exponent;
+ else
+ num = (num * 10.0) + (*s - '0');
+
+ /* Keep track of the number of digits after the decimal point.
+ If we just divided by 10 here, we would lose precision. */
+ if (got_dot)
+ --exponent;
+ }
+ else if (!got_dot && *s == radixchar)
+ /* Record that we have found the decimal point. */
+ got_dot = 1;
+ else
+ /* Any other character terminates the number. */
+ break;
+ }
+
+ if (!got_digit)
+ goto noconv;
+
+ if (TOLOWER ((unsigned char)*s) == 'e')
+ {
+ /* Get the exponent specified after the `e' or `E'. */
+ int save = errno;
+ char *end;
+ long int exp;
+
+ errno = 0;
+ ++s;
+ exp = strtol (s, &end, 10);
+ if (errno == ERANGE)
+ {
+ /* The exponent overflowed a `long int'. It is probably a safe
+ assumption that an exponent that cannot be represented by
+ a `long int' exceeds the limits of a `double'. */
+ if (endptr != NULL)
+ *endptr = end;
+ if (exp < 0)
+ goto underflow;
+ else
+ goto overflow;
+ }
+ else if (end == s)
+ /* There was no exponent. Reset END to point to
+ the 'e' or 'E', so *ENDPTR will be set there. */
+ end = (char *) s - 1;
+ errno = save;
+ s = end;
+ exponent += exp;
+ }
+
+ if (endptr != NULL)
+ *endptr = (char *) s;
+
+ if (num == 0.0)
+ return 0.0;
+
+ /* Multiply NUM by 10 to the EXPONENT power,
+ checking for overflow and underflow. */
+
+ if (exponent < 0)
+ {
+ if (num < DBL_MIN * pow (10.0, (double) -exponent))
+ goto underflow;
+ }
+ else if (exponent > 0)
+ {
+ if (num > DBL_MAX * pow (10.0, (double) -exponent))
+ goto overflow;
+ }
+
+ num *= pow (10.0, (double) exponent);
+
+ return num * sign;
+
+overflow:
+ /* Return an overflow error. */
+ errno = ERANGE;
+ return HUGE_VAL * sign;
+
+underflow:
+ /* Return an underflow error. */
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ errno = ERANGE;
+ return 0.0;
+
+noconv:
+ /* There was no number. */
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ return 0.0;
+}
+
+#endif /* !HAVE_STRTOD */
diff --git a/lib/sh/strtoimax.c b/lib/sh/strtoimax.c
new file mode 100644
index 0000000..584fa0b
--- /dev/null
+++ b/lib/sh/strtoimax.c
@@ -0,0 +1,113 @@
+/* strtoimax - convert string representation of a number into an intmax_t value. */
+
+/* Copyright 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Written by Paul Eggert. Modified by Chet Ramey for Bash. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdc.h>
+
+/* Verify a requirement at compile-time (unlike assert, which is runtime). */
+#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
+
+#ifndef HAVE_DECL_STRTOL
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_STRTOL
+extern long strtol PARAMS((const char *, char **, int));
+#endif
+
+#ifndef HAVE_DECL_STRTOLL
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_STRTOLL && HAVE_LONG_LONG_INT
+extern long long strtoll PARAMS((const char *, char **, int));
+#endif
+
+#ifdef strtoimax
+#undef strtoimax
+#endif
+
+intmax_t
+strtoimax (ptr, endptr, base)
+ const char *ptr;
+ char **endptr;
+ int base;
+{
+#if HAVE_LONG_LONG_INT
+ verify(size_is_that_of_long_or_long_long,
+ (sizeof (intmax_t) == sizeof (long) ||
+ sizeof (intmax_t) == sizeof (long long)));
+
+ if (sizeof (intmax_t) != sizeof (long))
+ return (strtoll (ptr, endptr, base));
+#else
+ verify (size_is_that_of_long, sizeof (intmax_t) == sizeof (long));
+#endif
+
+ return (strtol (ptr, endptr, base));
+}
+
+#ifdef TESTING
+# include <stdio.h>
+int
+main ()
+{
+ char *p, *endptr;
+ intmax_t x;
+#if HAVE_LONG_LONG_INT
+ long long y;
+#endif
+ long z;
+
+ printf ("sizeof intmax_t: %d\n", sizeof (intmax_t));
+
+#if HAVE_LONG_LONG_INT
+ printf ("sizeof long long: %d\n", sizeof (long long));
+#endif
+ printf ("sizeof long: %d\n", sizeof (long));
+
+ x = strtoimax("42", &endptr, 10);
+#if HAVE_LONG_LONG_INT
+ y = strtoll("42", &endptr, 10);
+#else
+ y = -1;
+#endif
+ z = strtol("42", &endptr, 10);
+
+ printf ("%lld %lld %ld\n", x, y, z);
+
+ exit (0);
+}
+#endif
diff --git a/lib/sh/strtol.c b/lib/sh/strtol.c
new file mode 100644
index 0000000..c839a97
--- /dev/null
+++ b/lib/sh/strtol.c
@@ -0,0 +1,259 @@
+/* strtol - convert string representation of a number into a long integer value. */
+
+/* Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !HAVE_STRTOL
+
+#include <chartypes.h>
+#include <errno.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#include <typemax.h>
+
+#include <stdc.h>
+#include <bashansi.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/* Nonzero if we are defining `strtoul' or `strtoull', operating on
+ unsigned integers. */
+#ifndef UNSIGNED
+# define UNSIGNED 0
+# define INT LONG int
+#else
+# define INT unsigned LONG int
+#endif
+
+#if UNSIGNED
+# ifdef QUAD
+# define strtol strtoull
+# else
+# define strtol strtoul
+# endif
+#else
+# ifdef QUAD
+# define strtol strtoll
+# endif
+#endif
+
+/* If QUAD is defined, we are defining `strtoll' or `strtoull',
+ operating on `long long ints. */
+
+#ifdef QUAD
+# define LONG long long
+# define STRTOL_LONG_MIN LLONG_MIN
+# define STRTOL_LONG_MAX LLONG_MAX
+# define STRTOL_ULONG_MAX ULLONG_MAX
+#else /* !QUAD */
+# define LONG long
+# define STRTOL_LONG_MIN LONG_MIN
+# define STRTOL_LONG_MAX LONG_MAX
+# define STRTOL_ULONG_MAX ULONG_MAX
+#endif
+
+/* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
+ If BASE is 0 the base is determined by the presence of a leading
+ zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
+ If BASE is < 2 or > 36, it is no longer reset to 10; EINVAL is returned.
+ If ENDPTR is not NULL, a pointer to the character after the last
+ one converted is stored in *ENDPTR. */
+
+INT
+strtol (nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ int base;
+{
+ int negative;
+ register unsigned LONG int cutoff;
+ register unsigned int cutlim;
+ register unsigned LONG int i;
+ register const char *s;
+ register unsigned char c;
+ const char *save, *end;
+ int overflow;
+
+ if (base < 0 || base == 1 || base > 36)
+ {
+ __set_errno (EINVAL);
+ return 0;
+ }
+
+ save = s = nptr;
+
+ /* Skip white space. */
+ while (ISSPACE ((unsigned char)*s))
+ ++s;
+ if (*s == '\0')
+ goto noconv;
+
+ /* Check for a sign. */
+ if (*s == '-' || *s == '+')
+ {
+ negative = (*s == '-');
+ ++s;
+ }
+ else
+ negative = 0;
+
+ /* Recognize number prefix and if BASE is zero, figure it out ourselves. */
+ if (*s == '0')
+ {
+ if ((base == 0 || base == 16) && TOUPPER ((unsigned char) s[1]) == 'X')
+ {
+ s += 2;
+ base = 16;
+ }
+ else if (base == 0)
+ base = 8;
+ }
+ else if (base == 0)
+ base = 10;
+
+ /* Save the pointer so we can check later if anything happened. */
+ save = s;
+
+ end = NULL;
+
+ cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base;
+ cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base;
+
+ overflow = 0;
+ i = 0;
+ c = *s;
+ if (sizeof (long int) != sizeof (LONG int))
+ {
+ unsigned long int j = 0;
+ unsigned long int jmax = ULONG_MAX / base;
+
+ for (;c != '\0'; c = *++s)
+ {
+ if (s == end)
+ break;
+ if (DIGIT (c))
+ c -= '0';
+ else if (ISALPHA (c))
+ c = TOUPPER (c) - 'A' + 10;
+ else
+ break;
+
+ if ((int) c >= base)
+ break;
+ /* Note that we never can have an overflow. */
+ else if (j >= jmax)
+ {
+ /* We have an overflow. Now use the long representation. */
+ i = (unsigned LONG int) j;
+ goto use_long;
+ }
+ else
+ j = j * (unsigned long int) base + c;
+ }
+
+ i = (unsigned LONG int) j;
+ }
+ else
+ for (;c != '\0'; c = *++s)
+ {
+ if (s == end)
+ break;
+ if (DIGIT (c))
+ c -= '0';
+ else if (ISALPHA (c))
+ c = TOUPPER (c) - 'A' + 10;
+ else
+ break;
+ if ((int) c >= base)
+ break;
+ /* Check for overflow. */
+ if (i > cutoff || (i == cutoff && c > cutlim))
+ overflow = 1;
+ else
+ {
+ use_long:
+ i *= (unsigned LONG int) base;
+ i += c;
+ }
+ }
+
+ /* Check if anything actually happened. */
+ if (s == save)
+ goto noconv;
+
+ /* Store in ENDPTR the address of one character
+ past the last character we converted. */
+ if (endptr != NULL)
+ *endptr = (char *) s;
+
+#if !UNSIGNED
+ /* Check for a value that is within the range of
+ `unsigned LONG int', but outside the range of `LONG int'. */
+ if (overflow == 0
+ && i > (negative
+ ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1
+ : (unsigned LONG int) STRTOL_LONG_MAX))
+ overflow = 1;
+#endif
+
+ if (overflow)
+ {
+ __set_errno (ERANGE);
+#if UNSIGNED
+ return STRTOL_ULONG_MAX;
+#else
+ return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX;
+#endif
+ }
+
+ /* Return the result of the appropriate sign. */
+ return negative ? -i : i;
+
+noconv:
+ /* We must handle a special case here: the base is 0 or 16 and the
+ first two characters are '0' and 'x', but the rest are no
+ hexadecimal digits. This is no error case. We return 0 and
+ ENDPTR points to the `x`. */
+ if (endptr != NULL)
+ {
+ if (save - nptr >= 2 && TOUPPER ((unsigned char) save[-1]) == 'X' && save[-2] == '0')
+ *endptr = (char *) &save[-1];
+ else
+ /* There was no number to convert. */
+ *endptr = (char *) nptr;
+ }
+
+ return 0L;
+}
+
+#endif /* !HAVE_STRTOL */
diff --git a/lib/sh/strtoll.c b/lib/sh/strtoll.c
new file mode 100644
index 0000000..f90300b
--- /dev/null
+++ b/lib/sh/strtoll.c
@@ -0,0 +1,30 @@
+/* strtoll - convert string representation of a number into a long long value. */
+
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_LONG_LONG_INT) && !HAVE_STRTOLL
+
+#define QUAD 1
+#undef HAVE_STRTOL
+
+#include "strtol.c"
+
+#endif /* HAVE_LONG_LONG_INT && !HAVE_STRTOLL */
diff --git a/lib/sh/strtoul.c b/lib/sh/strtoul.c
new file mode 100644
index 0000000..cbaa484
--- /dev/null
+++ b/lib/sh/strtoul.c
@@ -0,0 +1,30 @@
+/* strtoul - convert string representation of a number into an unsigned long value. */
+
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef HAVE_STRTOUL
+
+#define UNSIGNED 1
+#undef HAVE_STRTOL
+
+#include <strtol.c>
+
+#endif /* !HAVE_STRTOUL */
diff --git a/lib/sh/strtoull.c b/lib/sh/strtoull.c
new file mode 100644
index 0000000..fe6592e
--- /dev/null
+++ b/lib/sh/strtoull.c
@@ -0,0 +1,31 @@
+/* strtoull - convert string representation of a number into an unsigned long long value. */
+
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNSIGNED_LONG_LONG_INT) && !HAVE_STRTOULL
+
+#define QUAD 1
+#define UNSIGNED 1
+#undef HAVE_STRTOL
+
+#include "strtol.c"
+
+#endif /* HAVE_UNSIGNED_LONG_LONG_INT && !HAVE_STRTOULL */
diff --git a/lib/sh/strtoumax.c b/lib/sh/strtoumax.c
new file mode 100644
index 0000000..2e26efc
--- /dev/null
+++ b/lib/sh/strtoumax.c
@@ -0,0 +1,113 @@
+/* strtoumax - convert string representation of a number into an uintmax_t value. */
+
+/* Copyright 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Written by Paul Eggert. Modified by Chet Ramey for Bash. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdc.h>
+
+/* Verify a requirement at compile-time (unlike assert, which is runtime). */
+#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
+
+#ifndef HAVE_DECL_STRTOUL
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_STRTOUL
+extern unsigned long strtoul PARAMS((const char *, char **, int));
+#endif
+
+#ifndef HAVE_DECL_STRTOULL
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_STRTOULL && HAVE_UNSIGNED_LONG_LONG_INT
+extern unsigned long long strtoull PARAMS((const char *, char **, int));
+#endif
+
+#ifdef strtoumax
+#undef strtoumax
+#endif
+
+uintmax_t
+strtoumax (ptr, endptr, base)
+ const char *ptr;
+ char **endptr;
+ int base;
+{
+#if HAVE_UNSIGNED_LONG_LONG_INT
+ verify (size_is_that_of_unsigned_long_or_unsigned_long_long,
+ (sizeof (uintmax_t) == sizeof (unsigned long) ||
+ sizeof (uintmax_t) == sizeof (unsigned long long)));
+
+ if (sizeof (uintmax_t) != sizeof (unsigned long))
+ return (strtoull (ptr, endptr, base));
+#else
+ verify (size_is_that_of_unsigned_long, sizeof (uintmax_t) == sizeof (unsigned long));
+#endif
+
+ return (strtoul (ptr, endptr, base));
+}
+
+#ifdef TESTING
+# include <stdio.h>
+int
+main ()
+{
+ char *p, *endptr;
+ uintmax_t x;
+#if HAVE_UNSIGNED_LONG_LONG_INT
+ unsigned long long y;
+#endif
+ unsigned long z;
+
+ printf ("sizeof uintmax_t: %d\n", sizeof (uintmax_t));
+
+#if HAVE_UNSIGNED_LONG_LONG_INT
+ printf ("sizeof unsigned long long: %d\n", sizeof (unsigned long long));
+#endif
+ printf ("sizeof unsigned long: %d\n", sizeof (unsigned long));
+
+ x = strtoumax("42", &endptr, 10);
+#if HAVE_LONG_LONG_INT
+ y = strtoull("42", &endptr, 10);
+#else
+ y = 0;
+#endif
+ z = strtoul("42", &endptr, 10);
+
+ printf ("%llu %llu %lu\n", x, y, z);
+
+ exit (0);
+}
+#endif
diff --git a/lib/sh/strtrans.c b/lib/sh/strtrans.c
new file mode 100644
index 0000000..b2b1acc
--- /dev/null
+++ b/lib/sh/strtrans.c
@@ -0,0 +1,400 @@
+/* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
+
+/* Copyright (C) 2000-2015 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include <stdio.h>
+#include <chartypes.h>
+
+#include "shell.h"
+
+#include "shmbchar.h"
+#include "shmbutil.h"
+
+#ifdef ESC
+#undef ESC
+#endif
+#define ESC '\033' /* ASCII */
+
+/* Convert STRING by expanding the escape sequences specified by the
+ ANSI C standard. If SAWC is non-null, recognize `\c' and use that
+ as a string terminator. If we see \c, set *SAWC to 1 before
+ returning. LEN is the length of STRING. If (FLAGS&1) is non-zero,
+ that we're translating a string for `echo -e', and therefore should not
+ treat a single quote as a character that may be escaped with a backslash.
+ If (FLAGS&2) is non-zero, we're expanding for the parser and want to
+ quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
+ to remove the backslash before any unrecognized escape sequence. */
+char *
+ansicstr (string, len, flags, sawc, rlen)
+ char *string;
+ int len, flags, *sawc, *rlen;
+{
+ int c, temp;
+ char *ret, *r, *s;
+ unsigned long v;
+ size_t clen;
+ int b, mb_cur_max;
+#if defined (HANDLE_MULTIBYTE)
+ wchar_t wc;
+#endif
+
+ if (string == 0 || *string == '\0')
+ return ((char *)NULL);
+
+ mb_cur_max = MB_CUR_MAX;
+#if defined (HANDLE_MULTIBYTE)
+ temp = 4*len + 4;
+ if (temp < 12)
+ temp = 12; /* ensure enough for eventual u32cesc */
+ ret = (char *)xmalloc (temp);
+#else
+ ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
+#endif
+ for (r = ret, s = string; s && *s; )
+ {
+ c = *s++;
+ if (c != '\\' || *s == '\0')
+ {
+ clen = 1;
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 0 && is_basic (c) == 0))
+ {
+ clen = mbrtowc (&wc, s - 1, mb_cur_max, 0);
+ if (MB_INVALIDCH (clen))
+ clen = 1;
+ }
+#endif
+ *r++ = c;
+ for (--clen; clen > 0; clen--)
+ *r++ = *s++;
+ }
+ else
+ {
+ switch (c = *s++)
+ {
+#if defined (__STDC__)
+ case 'a': c = '\a'; break;
+ case 'v': c = '\v'; break;
+#else
+ case 'a': c = (int) 0x07; break;
+ case 'v': c = (int) 0x0B; break;
+#endif
+ case 'b': c = '\b'; break;
+ case 'e': case 'E': /* ESC -- non-ANSI */
+ c = ESC; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7':
+#if 1
+ if (flags & 1)
+ {
+ *r++ = '\\';
+ break;
+ }
+ /*FALLTHROUGH*/
+#endif
+ case '0':
+ /* If (FLAGS & 1), we're translating a string for echo -e (or
+ the equivalent xpg_echo option), so we obey the SUSv3/
+ POSIX-2001 requirement and accept 0-3 octal digits after
+ a leading `0'. */
+ temp = 2 + ((flags & 1) && (c == '0'));
+ for (c -= '0'; ISOCTAL (*s) && temp--; s++)
+ c = (c * 8) + OCTVALUE (*s);
+ c &= 0xFF;
+ break;
+ case 'x': /* Hex digit -- non-ANSI */
+ if ((flags & 2) && *s == '{')
+ {
+ flags |= 16; /* internal flag value */
+ s++;
+ }
+ /* Consume at least two hex characters */
+ for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
+ c = (c * 16) + HEXVALUE (*s);
+ /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
+ until a non-xdigit or `}', so potentially more than two
+ chars are consumed. */
+ if (flags & 16)
+ {
+ for ( ; ISXDIGIT ((unsigned char)*s); s++)
+ c = (c * 16) + HEXVALUE (*s);
+ flags &= ~16;
+ if (*s == '}')
+ s++;
+ }
+ /* \x followed by non-hex digits is passed through unchanged */
+ else if (temp == 2)
+ {
+ *r++ = '\\';
+ c = 'x';
+ }
+ c &= 0xFF;
+ break;
+#if defined (HANDLE_MULTIBYTE)
+ case 'u':
+ case 'U':
+ temp = (c == 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */
+ for (v = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
+ v = (v * 16) + HEXVALUE (*s);
+ if (temp == ((c == 'u') ? 4 : 8))
+ {
+ *r++ = '\\'; /* c remains unchanged */
+ break;
+ }
+ else if (v <= 0x7f) /* <= 0x7f translates directly */
+ {
+ c = v;
+ break;
+ }
+ else
+ {
+ temp = u32cconv (v, r);
+ r += temp;
+ continue;
+ }
+#endif
+ case '\\':
+ break;
+ case '\'': case '"': case '?':
+ if (flags & 1)
+ *r++ = '\\';
+ break;
+ case 'c':
+ if (sawc)
+ {
+ *sawc = 1;
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+ }
+ else if ((flags & 1) == 0 && *s == 0)
+ ; /* pass \c through */
+ else if ((flags & 1) == 0 && (c = *s))
+ {
+ s++;
+ if ((flags & 2) && c == '\\' && c == *s)
+ s++; /* Posix requires $'\c\\' do backslash escaping */
+ c = TOCTRL(c);
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ if ((flags & 4) == 0)
+ *r++ = '\\';
+ break;
+ }
+ if ((flags & 2) && (c == CTLESC || c == CTLNUL))
+ *r++ = CTLESC;
+ *r++ = c;
+ }
+ }
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+}
+
+/* Take a string STR, possibly containing non-printing characters, and turn it
+ into a $'...' ANSI-C style quoted string. Returns a new string. */
+char *
+ansic_quote (str, flags, rlen)
+ char *str;
+ int flags, *rlen;
+{
+ char *r, *ret, *s;
+ int l, rsize;
+ unsigned char c;
+ size_t clen;
+ int b;
+#if defined (HANDLE_MULTIBYTE)
+ wchar_t wc;
+#endif
+
+ if (str == 0 || *str == 0)
+ return ((char *)0);
+
+ l = strlen (str);
+ rsize = 4 * l + 4;
+ r = ret = (char *)xmalloc (rsize);
+
+ *r++ = '$';
+ *r++ = '\'';
+
+ for (s = str; c = *s; s++)
+ {
+ b = l = 1; /* 1 == add backslash; 0 == no backslash */
+ clen = 1;
+
+ switch (c)
+ {
+ case ESC: c = 'E'; break;
+#ifdef __STDC__
+ case '\a': c = 'a'; break;
+ case '\v': c = 'v'; break;
+#else
+ case 0x07: c = 'a'; break;
+ case 0x0b: c = 'v'; break;
+#endif
+
+ case '\b': c = 'b'; break;
+ case '\f': c = 'f'; break;
+ case '\n': c = 'n'; break;
+ case '\r': c = 'r'; break;
+ case '\t': c = 't'; break;
+ case '\\':
+ case '\'':
+ break;
+ default:
+#if defined (HANDLE_MULTIBYTE)
+ b = is_basic (c);
+ /* XXX - clen comparison to 0 is dicey */
+ if ((b == 0 && ((clen = mbrtowc (&wc, s, MB_CUR_MAX, 0)) < 0 || MB_INVALIDCH (clen) || iswprint (wc) == 0)) ||
+ (b == 1 && ISPRINT (c) == 0))
+#else
+ if (ISPRINT (c) == 0)
+#endif
+ {
+ *r++ = '\\';
+ *r++ = TOCHAR ((c >> 6) & 07);
+ *r++ = TOCHAR ((c >> 3) & 07);
+ *r++ = TOCHAR (c & 07);
+ continue;
+ }
+ l = 0;
+ break;
+ }
+ if (b == 0 && clen == 0)
+ break;
+
+ if (l)
+ *r++ = '\\';
+
+ if (clen == 1)
+ *r++ = c;
+ else
+ {
+ for (b = 0; b < (int)clen; b++)
+ *r++ = (unsigned char)s[b];
+ s += clen - 1; /* -1 because of the increment above */
+ }
+ }
+
+ *r++ = '\'';
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+}
+
+#if defined (HANDLE_MULTIBYTE)
+int
+ansic_wshouldquote (string)
+ const char *string;
+{
+ const wchar_t *wcs;
+ wchar_t wcc;
+ wchar_t *wcstr = NULL;
+ size_t slen;
+
+ slen = mbstowcs (wcstr, string, 0);
+
+ if (slen == (size_t)-1)
+ return 1;
+
+ wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
+ mbstowcs (wcstr, string, slen + 1);
+
+ for (wcs = wcstr; wcc = *wcs; wcs++)
+ if (iswprint(wcc) == 0)
+ {
+ free (wcstr);
+ return 1;
+ }
+
+ free (wcstr);
+ return 0;
+}
+#endif
+
+/* return 1 if we need to quote with $'...' because of non-printing chars. */
+int
+ansic_shouldquote (string)
+ const char *string;
+{
+ const char *s;
+ unsigned char c;
+
+ if (string == 0)
+ return 0;
+
+ for (s = string; c = *s; s++)
+ {
+#if defined (HANDLE_MULTIBYTE)
+ if (is_basic (c) == 0)
+ return (ansic_wshouldquote (s));
+#endif
+ if (ISPRINT (c) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* $'...' ANSI-C expand the portion of STRING between START and END and
+ return the result. The result cannot be longer than the input string. */
+char *
+ansiexpand (string, start, end, lenp)
+ char *string;
+ int start, end, *lenp;
+{
+ char *temp, *t;
+ int len, tlen;
+
+ temp = (char *)xmalloc (end - start + 1);
+ for (tlen = 0, len = start; len < end; )
+ temp[tlen++] = string[len++];
+ temp[tlen] = '\0';
+
+ if (*temp)
+ {
+ t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
+ free (temp);
+ return (t);
+ }
+ else
+ {
+ if (lenp)
+ *lenp = 0;
+ return (temp);
+ }
+}
diff --git a/lib/sh/strvis.c b/lib/sh/strvis.c
new file mode 100644
index 0000000..7a11d57
--- /dev/null
+++ b/lib/sh/strvis.c
@@ -0,0 +1,154 @@
+/* strvis.c - make unsafe graphical characters in a string visible. */
+
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This is a stripped-down version suitable for the shell's use. */
+#include <config.h>
+
+#include <unistd.h>
+
+#include "bashansi.h"
+#include <stdio.h>
+
+#include "chartypes.h"
+#include "bashintl.h"
+#include "shmbutil.h"
+
+#define SAFECHAR(c) ((c) == ' ' || (c) == '\t')
+
+#ifndef RUBOUT
+#define RUBOUT 0x7f
+#endif
+
+#ifndef CTRL_CHAR
+#define CTRL_CHAR(c) ((c) < 0x20)
+#endif
+
+#ifndef META_CHAR
+#define META_CHAR(c) ((c) > 0x7f && (c) <= UCHAR_MAX)
+#endif
+
+#ifndef UNCTRL
+#define UNCTRL(c) (TOUPPER ((c) | 0x40))
+#endif
+
+#ifndef UNMETA
+#define UNMETA(c) ((c) & 0x7f)
+#endif
+
+int
+sh_charvis (s, sindp, slen, ret, rindp)
+ const char *s;
+ size_t *sindp;
+ size_t slen;
+ char *ret;
+ size_t *rindp;
+{
+ unsigned char c;
+ size_t si, ri;
+ const char *send;
+ DECLARE_MBSTATE;
+
+ si = *sindp;
+ ri = *rindp;
+ c = s[*sindp];
+
+#if defined (HANDLE_MULTIBYTE)
+ send = (locale_mb_cur_max > 1) ? s + slen : 0;
+#else
+ send = 0;
+#endif
+
+ if (SAFECHAR (c))
+ {
+ ret[ri++] = c;
+ si++;
+ }
+ else if (c == RUBOUT)
+ {
+ ret[ri++] = '^';
+ ret[ri++] = '?';
+ si++;
+ }
+ else if (CTRL_CHAR (c))
+ {
+ ret[ri++] = '^';
+ ret[ri++] = UNCTRL (c);
+ si++;
+ }
+#if defined (HANDLE_MULTIBYTE)
+ else if (locale_utf8locale && (c & 0x80))
+ COPY_CHAR_I (ret, ri, s, send, si);
+ else if (locale_mb_cur_max > 1 && is_basic (c) == 0)
+ COPY_CHAR_I (ret, ri, s, send, si);
+#endif
+ else if (META_CHAR (c))
+ {
+ ret[ri++] = 'M';
+ ret[ri++] = '-';
+ ret[ri++] = UNMETA (c);
+ si++;
+ }
+ else
+ ret[ri++] = s[si++];
+
+ *sindp = si;
+ *rindp = ri;
+
+ return si;
+}
+
+/* Return a new string with `unsafe' non-graphical characters in S rendered
+ in a visible way. */
+char *
+sh_strvis (string)
+ const char *string;
+{
+ size_t slen, sind;
+ char *ret;
+ size_t retind, retsize;
+ unsigned char c;
+ DECLARE_MBSTATE;
+
+ if (string == 0)
+ return 0;
+ if (*string == '\0')
+ {
+ if ((ret = (char *)malloc (1)) == 0)
+ return 0;
+ ret[0] = '\0';
+ return ret;
+ }
+
+ slen = strlen (string);
+ retsize = 3 * slen + 1;
+
+ ret = (char *)malloc (retsize);
+ if (ret == 0)
+ return 0;
+
+ retind = 0;
+ sind = 0;
+
+ while (string[sind])
+ sind = sh_charvis (string, &sind, slen, ret, &retind);
+
+ ret[retind] = '\0';
+ return ret;
+}
diff --git a/lib/sh/timers.c b/lib/sh/timers.c
new file mode 100644
index 0000000..69b754c
--- /dev/null
+++ b/lib/sh/timers.c
@@ -0,0 +1,262 @@
+/* timers - functions to manage shell timers */
+
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+#include "posixtime.h"
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#if defined (HAVE_SELECT)
+# include "posixselect.h"
+# include "stat-time.h"
+#endif
+
+#include "sig.h"
+#include "bashjmp.h"
+#include "xmalloc.h"
+
+#include "timer.h"
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#ifndef FREE
+#define FREE(s) do { if (s) free (s); } while (0)
+#endif
+
+extern unsigned int falarm (unsigned int, unsigned int);
+
+static void shtimer_zero (sh_timer *);
+
+static void
+shtimer_zero (sh_timer *t)
+{
+ t->tmout.tv_sec = 0;
+ t->tmout.tv_usec = 0;
+
+ t->fd = -1;
+ t->flags = t->alrmflag = 0;
+
+ t->alrm_handler = t->old_handler = 0;
+
+ memset (t->jmpenv, '\0', sizeof (t->jmpenv));
+
+ t->tm_handler = 0;
+ t->data = 0;
+}
+
+sh_timer *
+shtimer_alloc (void)
+{
+ sh_timer *t;
+
+ t = (sh_timer *)xmalloc (sizeof (sh_timer));
+ shtimer_zero (t);
+ return t;
+}
+
+void
+shtimer_flush (sh_timer *t)
+{
+ /* The caller can manage t->data arbitrarily as long as it frees and sets
+ t->data to 0 before calling this function. Otherwise, we do what we can
+ to avoid memleaks. */
+ FREE (t->data);
+ shtimer_zero (t);
+}
+
+void
+shtimer_dispose (sh_timer *t)
+{
+ free (t);
+}
+
+/* We keep the timer as an offset into the future from the time it's set. */
+void
+shtimer_set (sh_timer *t, time_t sec, long usec)
+{
+ struct timeval now;
+
+ if (t->flags & SHTIMER_ALARM)
+ {
+ t->alrmflag = 0; /* just paranoia */
+ t->old_handler = set_signal_handler (SIGALRM, t->alrm_handler);
+ t->flags |= SHTIMER_SIGSET;
+ falarm (t->tmout.tv_sec = sec, t->tmout.tv_usec = usec);
+ t->flags |= SHTIMER_ALRMSET;
+ return;
+ }
+
+ if (gettimeofday (&now, 0) < 0)
+ timerclear (&now);
+
+ t->tmout.tv_sec = now.tv_sec + sec;
+ t->tmout.tv_usec = now.tv_usec + usec;
+ if (t->tmout.tv_usec > USEC_PER_SEC)
+ {
+ t->tmout.tv_sec++;
+ t->tmout.tv_usec -= USEC_PER_SEC;
+ }
+}
+
+void
+shtimer_unset (sh_timer *t)
+{
+ t->tmout.tv_sec = 0;
+ t->tmout.tv_usec = 0;
+
+ if (t->flags & SHTIMER_ALARM)
+ {
+ t->alrmflag = 0;
+ if (t->flags & SHTIMER_ALRMSET)
+ falarm (0, 0);
+ if (t->old_handler && (t->flags & SHTIMER_SIGSET))
+ {
+ set_signal_handler (SIGALRM, t->old_handler);
+ t->flags &= ~SHTIMER_SIGSET;
+ t->old_handler = 0;
+ }
+ }
+}
+
+void
+shtimer_cleanup (sh_timer *t)
+{
+ shtimer_unset (t);
+}
+
+void
+shtimer_clear (sh_timer *t)
+{
+ shtimer_unset (t);
+ shtimer_dispose (t);
+}
+
+int
+shtimer_chktimeout (sh_timer *t)
+{
+ struct timeval now;
+ int r;
+
+ /* Use the flag to avoid returning sigalrm_seen here */
+ if (t->flags & SHTIMER_ALARM)
+ return t->alrmflag;
+
+ /* Could check a flag for this */
+ if (t->tmout.tv_sec == 0 && t->tmout.tv_usec == 0)
+ return 0;
+
+ if (gettimeofday (&now, 0) < 0)
+ return 0;
+ r = ((now.tv_sec > t->tmout.tv_sec) ||
+ (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec));
+
+ return r;
+}
+
+#if defined (HAVE_SELECT) || defined (HAVE_PSELECT)
+int
+shtimer_select (sh_timer *t)
+{
+ int r, nfd;
+ sigset_t blocked_sigs, prevmask;
+ struct timeval now, tv;
+ fd_set readfds;
+#if defined (HAVE_PSELECT)
+ struct timespec ts;
+#endif
+
+ /* We don't want a SIGCHLD to interrupt this */
+ sigemptyset (&blocked_sigs);
+# if defined (SIGCHLD)
+ sigaddset (&blocked_sigs, SIGCHLD);
+# endif
+
+ if (gettimeofday (&now, 0) < 0)
+ {
+ if (t->flags & SHTIMER_LONGJMP)
+ sh_longjmp (t->jmpenv, 1);
+ else
+ return -1;
+ }
+
+ /* If the timer has already expired, return immediately */
+ if ((now.tv_sec > t->tmout.tv_sec) ||
+ (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec))
+ {
+ if (t->flags & SHTIMER_LONGJMP)
+ sh_longjmp (t->jmpenv, 1);
+ else if (t->tm_handler)
+ return ((*t->tm_handler) (t));
+ else
+ return 0;
+ }
+
+ /* compute timeout */
+ tv.tv_sec = t->tmout.tv_sec - now.tv_sec;
+ tv.tv_usec = t->tmout.tv_usec - now.tv_usec;
+ if (tv.tv_usec < 0)
+ {
+ tv.tv_sec--;
+ tv.tv_usec += USEC_PER_SEC;
+ }
+
+#if defined (HAVE_PSELECT)
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+#else
+ sigemptyset (&prevmask);
+#endif /* !HAVE_PSELECT */
+
+ nfd = (t->fd >= 0) ? t->fd + 1 : 0;
+ FD_ZERO (&readfds);
+ if (t->fd >= 0)
+ FD_SET (t->fd, &readfds);
+
+#if defined (HAVE_PSELECT)
+ r = pselect(nfd, &readfds, (fd_set *)0, (fd_set *)0, &ts, &blocked_sigs);
+#else
+ sigprocmask (SIG_SETMASK, &blocked_sigs, &prevmask);
+ r = select(nfd, &readfds, (fd_set *)0, (fd_set *)0, &tv);
+ sigprocmask (SIG_SETMASK, &prevmask, NULL);
+#endif
+
+ if (r < 0)
+ return r; /* caller will handle */
+ else if (r == 0 && (t->flags & SHTIMER_LONGJMP))
+ sh_longjmp (t->jmpenv, 1);
+ else if (r == 0 && t->tm_handler)
+ return ((*t->tm_handler) (t));
+ else
+ return r;
+}
+#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */
+
+int
+shtimer_alrm (sh_timer *t)
+{
+ return 0;
+}
diff --git a/lib/sh/times.c b/lib/sh/times.c
new file mode 100644
index 0000000..2423078
--- /dev/null
+++ b/lib/sh/times.c
@@ -0,0 +1,77 @@
+/* times.c - times(3) library function */
+
+/* Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_TIMES)
+
+#include <sys/types.h>
+#include <posixtime.h>
+#include <systimes.h>
+
+#if defined (HAVE_SYS_RESOURCE_H) && defined (HAVE_GETRUSAGE)
+# include <sys/resource.h>
+#endif /* HAVE_SYS_RESOURCE_H && HAVE_GETRUSAGE */
+
+extern long get_clk_tck PARAMS((void));
+
+#define CONVTCK(r) (r.tv_sec * clk_tck + r.tv_usec / (1000000 / clk_tck))
+
+clock_t
+times(tms)
+ struct tms *tms;
+{
+ clock_t rv;
+ static long clk_tck = -1;
+
+#if defined (HAVE_GETRUSAGE)
+ struct timeval tv;
+ struct rusage ru;
+
+ if (clk_tck == -1)
+ clk_tck = get_clk_tck();
+
+ if (getrusage(RUSAGE_SELF, &ru) < 0)
+ return ((clock_t)-1);
+ tms->tms_utime = CONVTCK(ru.ru_utime);
+ tms->tms_stime = CONVTCK(ru.ru_stime);
+
+ if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
+ return ((clock_t)-1);
+ tms->tms_cutime = CONVTCK(ru.ru_utime);
+ tms->tms_cstime = CONVTCK(ru.ru_stime);
+
+ if (gettimeofday(&tv, NULL) < 0)
+ return ((clock_t)-1);
+ rv = (clock_t)(CONVTCK(tv));
+#else /* !HAVE_GETRUSAGE */
+ if (clk_tck == -1)
+ clk_tck = get_clk_tck();
+
+ /* We can't do anything. */
+ tms->tms_utime = tms->tms_stime = (clock_t)0;
+ tms->tms_cutime = tms->tms_cstime = (clock_t)0;
+
+ rv = (clock_t)time((time_t *)0) * clk_tck;
+# endif /* HAVE_GETRUSAGE */
+
+ return rv;
+}
+#endif /* !HAVE_TIMES */
diff --git a/lib/sh/timeval.c b/lib/sh/timeval.c
new file mode 100644
index 0000000..f2ca762
--- /dev/null
+++ b/lib/sh/timeval.c
@@ -0,0 +1,179 @@
+/* timeval.c - functions to perform operations on struct timevals */
+
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_TIMEVAL)
+
+#include <sys/types.h>
+#include <posixtime.h>
+
+#include <bashintl.h>
+#include <stdc.h>
+
+#ifndef locale_decpoint
+extern int locale_decpoint PARAMS((void));
+#endif
+
+#include <stdio.h>
+
+struct timeval *
+difftimeval (d, t1, t2)
+ struct timeval *d, *t1, *t2;
+{
+ d->tv_sec = t2->tv_sec - t1->tv_sec;
+ d->tv_usec = t2->tv_usec - t1->tv_usec;
+ if (d->tv_usec < 0)
+ {
+ d->tv_usec += 1000000;
+ d->tv_sec -= 1;
+ if (d->tv_sec < 0) /* ??? -- BSD/OS does this */
+ {
+ d->tv_sec = 0;
+ d->tv_usec = 0;
+ }
+ }
+ return d;
+}
+
+struct timeval *
+addtimeval (d, t1, t2)
+ struct timeval *d, *t1, *t2;
+{
+ d->tv_sec = t1->tv_sec + t2->tv_sec;
+ d->tv_usec = t1->tv_usec + t2->tv_usec;
+ if (d->tv_usec >= 1000000)
+ {
+ d->tv_usec -= 1000000;
+ d->tv_sec += 1;
+ }
+ return d;
+}
+
+struct timeval *
+multimeval (d, m)
+ struct timeval *d;
+ int m;
+{
+ time_t t;
+
+ t = d->tv_usec * m;
+ d->tv_sec = d->tv_sec * m + t / 1000000;
+ d->tv_usec = t % 1000000;
+ return d;
+}
+
+struct timeval *
+divtimeval (d, m)
+ struct timeval *d;
+ int m;
+{
+ time_t t;
+
+ t = d->tv_sec;
+ d->tv_sec = t / m;
+ d->tv_usec = (d->tv_usec + 1000000 * (t % m)) / m;
+ return d;
+}
+
+/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
+ Barely-tested code from Deven T. Corzine <deven@ties.org>. */
+int
+timeval_to_cpu (rt, ut, st)
+ struct timeval *rt, *ut, *st; /* real, user, sys */
+{
+ struct timeval t1, t2;
+ register int i;
+
+ addtimeval (&t1, ut, st);
+ t2.tv_sec = rt->tv_sec;
+ t2.tv_usec = rt->tv_usec;
+
+ for (i = 0; i < 6; i++)
+ {
+ if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
+ break;
+ t1.tv_sec *= 10;
+ t1.tv_sec += t1.tv_usec / 100000;
+ t1.tv_usec *= 10;
+ t1.tv_usec %= 1000000;
+ t2.tv_sec *= 10;
+ t2.tv_sec += t2.tv_usec / 100000;
+ t2.tv_usec *= 10;
+ t2.tv_usec %= 1000000;
+ }
+ for (i = 0; i < 4; i++)
+ {
+ if (t1.tv_sec < 100000000)
+ t1.tv_sec *= 10;
+ else
+ t2.tv_sec /= 10;
+ }
+
+ return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec);
+}
+
+/* Convert a pointer to a struct timeval to seconds and thousandths of a
+ second, returning the values in *SP and *SFP, respectively. This does
+ rounding on the fractional part, not just truncation to three places. */
+void
+timeval_to_secs (tvp, sp, sfp)
+ struct timeval *tvp;
+ time_t *sp;
+ int *sfp;
+{
+ int rest;
+
+ *sp = tvp->tv_sec;
+
+ *sfp = tvp->tv_usec % 1000000; /* pretty much a no-op */
+ rest = *sfp % 1000;
+ *sfp = (*sfp * 1000) / 1000000;
+ if (rest >= 500)
+ *sfp += 1;
+
+ /* Sanity check */
+ if (*sfp >= 1000)
+ {
+ *sp += 1;
+ *sfp -= 1000;
+ }
+}
+
+/* Print the contents of a struct timeval * in a standard way to stdio
+ stream FP. */
+void
+print_timeval (fp, tvp)
+ FILE *fp;
+ struct timeval *tvp;
+{
+ time_t timestamp;
+ long minutes;
+ int seconds, seconds_fraction;
+
+ timeval_to_secs (tvp, &timestamp, &seconds_fraction);
+
+ minutes = timestamp / 60;
+ seconds = timestamp % 60;
+
+ fprintf (fp, "%ldm%d%c%03ds", minutes, seconds, locale_decpoint (), seconds_fraction);
+}
+
+#endif /* HAVE_TIMEVAL */
diff --git a/lib/sh/tmpfile.c b/lib/sh/tmpfile.c
new file mode 100644
index 0000000..ef8b067
--- /dev/null
+++ b/lib/sh/tmpfile.c
@@ -0,0 +1,311 @@
+/*
+ * tmpfile.c - functions to create and safely open temp files for the shell.
+ */
+
+/* Copyright (C) 2000-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <bashtypes.h>
+#include <posixstat.h>
+#include <posixtime.h>
+#include <filecntl.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <shell.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#define BASEOPENFLAGS (O_CREAT | O_TRUNC | O_EXCL | O_BINARY)
+
+#define DEFAULT_TMPDIR "." /* bogus default, should be changed */
+#define DEFAULT_NAMEROOT "shtmp"
+
+/* Use ANSI-C rand() interface if random(3) is not available */
+#if !HAVE_RANDOM
+#define random() rand()
+#endif
+
+extern pid_t dollar_dollar_pid;
+
+static char *get_sys_tmpdir PARAMS((void));
+static char *get_tmpdir PARAMS((int));
+
+static char *sys_tmpdir = (char *)NULL;
+static int ntmpfiles;
+static int tmpnamelen = -1;
+static unsigned long filenum = 1L;
+
+static char *
+get_sys_tmpdir ()
+{
+ if (sys_tmpdir)
+ return sys_tmpdir;
+
+#ifdef P_tmpdir
+ sys_tmpdir = P_tmpdir;
+ if (file_iswdir (sys_tmpdir))
+ return sys_tmpdir;
+#endif
+
+ sys_tmpdir = "/tmp";
+ if (file_iswdir (sys_tmpdir))
+ return sys_tmpdir;
+
+ sys_tmpdir = "/var/tmp";
+ if (file_iswdir (sys_tmpdir))
+ return sys_tmpdir;
+
+ sys_tmpdir = "/usr/tmp";
+ if (file_iswdir (sys_tmpdir))
+ return sys_tmpdir;
+
+ sys_tmpdir = DEFAULT_TMPDIR;
+
+ return sys_tmpdir;
+}
+
+static char *
+get_tmpdir (flags)
+ int flags;
+{
+ char *tdir;
+
+ tdir = (flags & MT_USETMPDIR) ? get_string_value ("TMPDIR") : (char *)NULL;
+ if (tdir && (file_iswdir (tdir) == 0 || strlen (tdir) > PATH_MAX))
+ tdir = 0;
+
+ if (tdir == 0)
+ tdir = get_sys_tmpdir ();
+
+#if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX)
+ if (tmpnamelen == -1)
+ tmpnamelen = pathconf (tdir, _PC_NAME_MAX);
+#else
+ tmpnamelen = 0;
+#endif
+
+ return tdir;
+}
+
+static void
+sh_seedrand ()
+{
+#if HAVE_RANDOM
+ int d;
+ static int seeded = 0;
+ if (seeded == 0)
+ {
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+ srandom (tv.tv_sec ^ tv.tv_usec ^ (getpid () << 16) ^ (uintptr_t)&d);
+ seeded = 1;
+ }
+#endif
+}
+
+char *
+sh_mktmpname (nameroot, flags)
+ char *nameroot;
+ int flags;
+{
+ char *filename, *tdir, *lroot;
+ struct stat sb;
+ int r, tdlen;
+ static int seeded = 0;
+
+ filename = (char *)xmalloc (PATH_MAX + 1);
+ tdir = get_tmpdir (flags);
+ tdlen = strlen (tdir);
+
+ lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
+ if (nameroot == 0)
+ flags &= ~MT_TEMPLATE;
+
+ if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
+ flags &= ~MT_TEMPLATE;
+
+#ifdef USE_MKTEMP
+ if (flags & MT_TEMPLATE)
+ strcpy (filename, nameroot);
+ else
+ sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
+ if (mktemp (filename) == 0)
+ {
+ free (filename);
+ filename = NULL;
+ }
+#else /* !USE_MKTEMP */
+ sh_seedrand ();
+ while (1)
+ {
+ filenum = (filenum << 1) ^
+ (unsigned long) time ((time_t *)0) ^
+ (unsigned long) dollar_dollar_pid ^
+ (unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++);
+ sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
+ if (tmpnamelen > 0 && tmpnamelen < 32)
+ filename[tdlen + 1 + tmpnamelen] = '\0';
+# ifdef HAVE_LSTAT
+ r = lstat (filename, &sb);
+# else
+ r = stat (filename, &sb);
+# endif
+ if (r < 0 && errno == ENOENT)
+ break;
+ }
+#endif /* !USE_MKTEMP */
+
+ return filename;
+}
+
+int
+sh_mktmpfd (nameroot, flags, namep)
+ char *nameroot;
+ int flags;
+ char **namep;
+{
+ char *filename, *tdir, *lroot;
+ int fd, tdlen;
+
+ filename = (char *)xmalloc (PATH_MAX + 1);
+ tdir = get_tmpdir (flags);
+ tdlen = strlen (tdir);
+
+ lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
+ if (nameroot == 0)
+ flags &= ~MT_TEMPLATE;
+
+ if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
+ flags &= ~MT_TEMPLATE;
+
+#ifdef USE_MKSTEMP
+ if (flags & MT_TEMPLATE)
+ strcpy (filename, nameroot);
+ else
+ sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
+ fd = mkstemp (filename);
+ if (fd < 0 || namep == 0)
+ {
+ free (filename);
+ filename = NULL;
+ }
+ if (namep)
+ *namep = filename;
+ return fd;
+#else /* !USE_MKSTEMP */
+ sh_seedrand ();
+ do
+ {
+ filenum = (filenum << 1) ^
+ (unsigned long) time ((time_t *)0) ^
+ (unsigned long) dollar_dollar_pid ^
+ (unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++);
+ sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
+ if (tmpnamelen > 0 && tmpnamelen < 32)
+ filename[tdlen + 1 + tmpnamelen] = '\0';
+ fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600);
+ }
+ while (fd < 0 && errno == EEXIST);
+
+ if (namep)
+ *namep = filename;
+ else
+ free (filename);
+
+ return fd;
+#endif /* !USE_MKSTEMP */
+}
+
+FILE *
+sh_mktmpfp (nameroot, flags, namep)
+ char *nameroot;
+ int flags;
+ char **namep;
+{
+ int fd;
+ FILE *fp;
+
+ fd = sh_mktmpfd (nameroot, flags, namep);
+ if (fd < 0)
+ return ((FILE *)NULL);
+ fp = fdopen (fd, (flags & MT_READWRITE) ? "w+" : "w");
+ if (fp == 0)
+ close (fd);
+ return fp;
+}
+
+char *
+sh_mktmpdir (nameroot, flags)
+ char *nameroot;
+ int flags;
+{
+ char *filename, *tdir, *lroot, *dirname;
+ int fd, tdlen;
+
+#ifdef USE_MKDTEMP
+ filename = (char *)xmalloc (PATH_MAX + 1);
+ tdir = get_tmpdir (flags);
+ tdlen = strlen (tdir);
+
+ lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
+ if (nameroot == 0)
+ flags &= ~MT_TEMPLATE;
+
+ if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
+ flags &= ~MT_TEMPLATE;
+
+ if (flags & MT_TEMPLATE)
+ strcpy (filename, nameroot);
+ else
+ sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
+ dirname = mkdtemp (filename);
+ if (dirname == 0)
+ {
+ free (filename);
+ filename = NULL;
+ }
+ return dirname;
+#else /* !USE_MKDTEMP */
+ filename = (char *)NULL;
+ do
+ {
+ filename = sh_mktmpname (nameroot, flags);
+ fd = mkdir (filename, 0700);
+ if (fd == 0)
+ break;
+ free (filename);
+ filename = (char *)NULL;
+ }
+ while (fd < 0 && errno == EEXIST);
+
+ return (filename);
+#endif /* !USE_MKDTEMP */
+}
diff --git a/lib/sh/uconvert.c b/lib/sh/uconvert.c
new file mode 100644
index 0000000..457552e
--- /dev/null
+++ b/lib/sh/uconvert.c
@@ -0,0 +1,124 @@
+/* uconvert - convert string representations of decimal numbers into whole
+ number/fractional value pairs. */
+
+/* Copyright (C) 2008,2009,2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#include "posixtime.h"
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+
+#include "shell.h"
+#include "builtins.h"
+
+#define DECIMAL '.' /* XXX - should use locale */
+
+#define RETURN(x) \
+do { \
+ if (ip) *ip = ipart * mult; \
+ if (up) *up = upart; \
+ if (ep) *ep = p; \
+ return (x); \
+} while (0)
+
+/*
+ * An incredibly simplistic floating point converter.
+ */
+static int multiplier[7] = { 1, 100000, 10000, 1000, 100, 10, 1 };
+
+/* Take a decimal number int-part[.[micro-part]] and convert it to the whole
+ and fractional portions. The fractional portion is returned in
+ millionths (micro); callers are responsible for multiplying appropriately.
+ EP, if non-null, gets the address of the character where conversion stops.
+ Return 1 if value converted; 0 if invalid integer for either whole or
+ fractional parts. */
+int
+uconvert(s, ip, up, ep)
+ char *s;
+ long *ip, *up;
+ char **ep;
+{
+ int n, mult;
+ long ipart, upart;
+ char *p;
+
+ ipart = upart = 0;
+ mult = 1;
+
+ if (s && (*s == '-' || *s == '+'))
+ {
+ mult = (*s == '-') ? -1 : 1;
+ p = s + 1;
+ }
+ else
+ p = s;
+
+ for ( ; p && *p; p++)
+ {
+ if (*p == DECIMAL) /* decimal point */
+ break;
+ if (DIGIT(*p) == 0)
+ RETURN(0);
+ ipart = (ipart * 10) + (*p - '0');
+ }
+
+ if (p == 0 || *p == 0) /* callers ensure p can never be 0; this is to shut up clang */
+ RETURN(1);
+
+ if (*p == DECIMAL)
+ p++;
+
+ /* Look for up to six digits past a decimal point. */
+ for (n = 0; n < 6 && p[n]; n++)
+ {
+ if (DIGIT(p[n]) == 0)
+ {
+ if (ep)
+ {
+ upart *= multiplier[n];
+ p += n; /* To set EP */
+ }
+ RETURN(0);
+ }
+ upart = (upart * 10) + (p[n] - '0');
+ }
+
+ /* Now convert to millionths */
+ upart *= multiplier[n];
+
+ if (n == 6 && p[6] >= '5' && p[6] <= '9')
+ upart++; /* round up 1 */
+
+ if (ep)
+ {
+ p += n;
+ while (DIGIT(*p))
+ p++;
+ }
+
+ RETURN(1);
+}
diff --git a/lib/sh/ufuncs.c b/lib/sh/ufuncs.c
new file mode 100644
index 0000000..4dc4853
--- /dev/null
+++ b/lib/sh/ufuncs.c
@@ -0,0 +1,140 @@
+/* ufuncs - sleep and alarm functions that understand fractional values */
+
+/* Copyright (C) 2008,2009-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#include "posixtime.h"
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if defined (HAVE_SELECT)
+# include "posixselect.h"
+# include "quit.h"
+# include "trap.h"
+# include "stat-time.h"
+#endif
+
+/* A version of `alarm' using setitimer if it's available. */
+
+#if defined (HAVE_SETITIMER)
+unsigned int
+falarm(secs, usecs)
+ unsigned int secs, usecs;
+{
+ struct itimerval it, oit;
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+
+ it.it_value.tv_sec = secs;
+ it.it_value.tv_usec = usecs;
+
+ if (setitimer(ITIMER_REAL, &it, &oit) < 0)
+ return (-1); /* XXX will be converted to unsigned */
+
+ /* Backwards compatibility with alarm(3) */
+ if (oit.it_value.tv_usec)
+ oit.it_value.tv_sec++;
+ return (oit.it_value.tv_sec);
+}
+#else
+int
+falarm (secs, usecs)
+ unsigned int secs, usecs;
+{
+ if (secs == 0 && usecs == 0)
+ return (alarm (0));
+
+ if (secs == 0 || usecs >= 500000)
+ {
+ secs++;
+ usecs = 0;
+ }
+ return (alarm (secs));
+}
+#endif /* !HAVE_SETITIMER */
+
+/* A version of sleep using fractional seconds and select. I'd like to use
+ `usleep', but it's already taken */
+
+#if defined (HAVE_TIMEVAL) && (defined (HAVE_SELECT) || defined (HAVE_PSELECT))
+int
+fsleep(sec, usec)
+ unsigned int sec, usec;
+{
+ int e, r;
+ sigset_t blocked_sigs, prevmask;
+#if defined (HAVE_PSELECT)
+ struct timespec ts;
+#else
+ struct timeval tv;
+#endif
+
+ sigemptyset (&blocked_sigs);
+# if defined (SIGCHLD)
+ sigaddset (&blocked_sigs, SIGCHLD);
+# endif
+
+#if defined (HAVE_PSELECT)
+ ts.tv_sec = sec;
+ ts.tv_nsec = usec * 1000;
+#else
+ sigemptyset (&prevmask);
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+#endif /* !HAVE_PSELECT */
+
+ do
+ {
+#if defined (HAVE_PSELECT)
+ r = pselect(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &ts, &blocked_sigs);
+#else
+ sigprocmask (SIG_SETMASK, &blocked_sigs, &prevmask);
+ r = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv);
+ sigprocmask (SIG_SETMASK, &prevmask, NULL);
+#endif
+ e = errno;
+ if (r < 0 && errno == EINTR)
+ return -1; /* caller will handle */
+ errno = e;
+ }
+ while (r < 0 && errno == EINTR);
+
+ return r;
+}
+#else /* !HAVE_TIMEVAL || !HAVE_SELECT */
+int
+fsleep(sec, usec)
+ long sec, usec;
+{
+ if (usec >= 500000) /* round */
+ sec++;
+ return (sleep(sec));
+}
+#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */
diff --git a/lib/sh/unicode.c b/lib/sh/unicode.c
new file mode 100644
index 0000000..d781353
--- /dev/null
+++ b/lib/sh/unicode.c
@@ -0,0 +1,339 @@
+/* unicode.c - functions to convert unicode characters */
+
+/* Copyright (C) 2010-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HANDLE_MULTIBYTE)
+
+#include <stdc.h>
+#include <wchar.h>
+#include <bashansi.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <limits.h>
+
+#if HAVE_ICONV
+# include <iconv.h>
+#endif
+
+#include <xmalloc.h>
+
+#ifndef USHORT_MAX
+# ifdef USHRT_MAX
+# define USHORT_MAX USHRT_MAX
+# else
+# define USHORT_MAX ((unsigned short) ~(unsigned short)0)
+# endif
+#endif
+
+#if !defined (STREQ)
+# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
+#endif /* !STREQ */
+
+#if defined (HAVE_LOCALE_CHARSET)
+extern const char *locale_charset PARAMS((void));
+#else
+extern char *get_locale_var PARAMS((char *));
+#endif
+
+extern int locale_utf8locale;
+
+static int u32init = 0;
+static int utf8locale = 0;
+#if defined (HAVE_ICONV)
+static iconv_t localconv;
+#endif
+
+#ifndef HAVE_LOCALE_CHARSET
+static char charsetbuf[40];
+
+static char *
+stub_charset ()
+{
+ char *locale, *s, *t;
+
+ locale = get_locale_var ("LC_CTYPE");
+ if (locale == 0 || *locale == 0)
+ {
+ strcpy (charsetbuf, "ASCII");
+ return charsetbuf;
+ }
+ s = strrchr (locale, '.');
+ if (s)
+ {
+ strncpy (charsetbuf, s+1, sizeof (charsetbuf) - 1);
+ charsetbuf[sizeof (charsetbuf) - 1] = '\0';
+ t = strchr (charsetbuf, '@');
+ if (t)
+ *t = 0;
+ return charsetbuf;
+ }
+ strncpy (charsetbuf, locale, sizeof (charsetbuf) - 1);
+ charsetbuf[sizeof (charsetbuf) - 1] = '\0';
+ return charsetbuf;
+}
+#endif
+
+void
+u32reset ()
+{
+#if defined (HAVE_ICONV)
+ if (u32init && localconv != (iconv_t)-1)
+ {
+ iconv_close (localconv);
+ localconv = (iconv_t)-1;
+ }
+#endif
+ u32init = 0;
+ utf8locale = 0;
+}
+
+/* u32toascii ? */
+int
+u32tochar (x, s)
+ unsigned long x;
+ char *s;
+{
+ int l;
+
+ l = (x <= UCHAR_MAX) ? 1 : ((x <= USHORT_MAX) ? 2 : 4);
+
+ if (x <= UCHAR_MAX)
+ s[0] = x & 0xFF;
+ else if (x <= USHORT_MAX) /* assume unsigned short = 16 bits */
+ {
+ s[0] = (x >> 8) & 0xFF;
+ s[1] = x & 0xFF;
+ }
+ else
+ {
+ s[0] = (x >> 24) & 0xFF;
+ s[1] = (x >> 16) & 0xFF;
+ s[2] = (x >> 8) & 0xFF;
+ s[3] = x & 0xFF;
+ }
+ s[l] = '\0';
+ return l;
+}
+
+int
+u32tocesc (wc, s)
+ u_bits32_t wc;
+ char *s;
+{
+ int l;
+
+ if (wc < 0x10000)
+ l = sprintf (s, "\\u%04X", wc);
+ else
+ l = sprintf (s, "\\U%08X", wc);
+ return l;
+}
+
+/* Convert unsigned 32-bit int to utf-8 character string */
+int
+u32toutf8 (wc, s)
+ u_bits32_t wc;
+ char *s;
+{
+ int l;
+
+ if (wc < 0x0080)
+ {
+ s[0] = (char)wc;
+ l = 1;
+ }
+ else if (wc < 0x0800)
+ {
+ s[0] = (wc >> 6) | 0xc0;
+ s[1] = (wc & 0x3f) | 0x80;
+ l = 2;
+ }
+ else if (wc < 0x10000)
+ {
+ /* Technically, we could return 0 here if 0xd800 <= wc <= 0x0dfff */
+ s[0] = (wc >> 12) | 0xe0;
+ s[1] = ((wc >> 6) & 0x3f) | 0x80;
+ s[2] = (wc & 0x3f) | 0x80;
+ l = 3;
+ }
+ else if (wc < 0x200000)
+ {
+ s[0] = (wc >> 18) | 0xf0;
+ s[1] = ((wc >> 12) & 0x3f) | 0x80;
+ s[2] = ((wc >> 6) & 0x3f) | 0x80;
+ s[3] = (wc & 0x3f) | 0x80;
+ l = 4;
+ }
+ /* Strictly speaking, UTF-8 doesn't have characters longer than 4 bytes */
+ else if (wc < 0x04000000)
+ {
+ s[0] = (wc >> 24) | 0xf8;
+ s[1] = ((wc >> 18) & 0x3f) | 0x80;
+ s[2] = ((wc >> 12) & 0x3f) | 0x80;
+ s[3] = ((wc >> 6) & 0x3f) | 0x80;
+ s[4] = (wc & 0x3f) | 0x80;
+ l = 5;
+ }
+ else if (wc < 0x080000000)
+ {
+ s[0] = (wc >> 30) | 0xfc;
+ s[1] = ((wc >> 24) & 0x3f) | 0x80;
+ s[2] = ((wc >> 18) & 0x3f) | 0x80;
+ s[3] = ((wc >> 12) & 0x3f) | 0x80;
+ s[4] = ((wc >> 6) & 0x3f) | 0x80;
+ s[5] = (wc & 0x3f) | 0x80;
+ l = 6;
+ }
+ else
+ l = 0;
+
+ s[l] = '\0';
+ return l;
+}
+
+/* Convert a 32-bit unsigned int (unicode) to a UTF-16 string. Rarely used,
+ only if sizeof(wchar_t) == 2. */
+int
+u32toutf16 (c, s)
+ u_bits32_t c;
+ wchar_t *s;
+{
+ int l;
+
+ l = 0;
+ if (c < 0x0d800 || (c >= 0x0e000 && c <= 0x0ffff))
+ {
+ s[0] = (wchar_t) (c & 0xFFFF);
+ l = 1;
+ }
+ else if (c >= 0x10000 && c <= 0x010ffff)
+ {
+ c -= 0x010000;
+ s[0] = (wchar_t)((c >> 10) + 0xd800);
+ s[1] = (wchar_t)((c & 0x3ff) + 0xdc00);
+ l = 2;
+ }
+ s[l] = 0;
+ return l;
+}
+
+/* convert a single unicode-32 character into a multibyte string and put the
+ result in S, which must be large enough (at least max(10,MB_LEN_MAX) bytes) */
+int
+u32cconv (c, s)
+ unsigned long c;
+ char *s;
+{
+ wchar_t wc;
+ wchar_t ws[3];
+ int n;
+#if HAVE_ICONV
+ const char *charset;
+ char obuf[25], *optr;
+ size_t obytesleft;
+ const char *iptr;
+ size_t sn;
+#endif
+
+#if __STDC_ISO_10646__
+ wc = c;
+ if (sizeof (wchar_t) == 4 && c <= 0x7fffffff)
+ n = wctomb (s, wc);
+ else if (sizeof (wchar_t) == 2 && c <= 0x10ffff && u32toutf16 (c, ws))
+ n = wcstombs (s, ws, MB_LEN_MAX);
+ else
+ n = -1;
+ if (n != -1)
+ return n;
+#endif
+
+#if HAVE_ICONV
+ /* this is mostly from coreutils-8.5/lib/unicodeio.c */
+ if (u32init == 0)
+ {
+ utf8locale = locale_utf8locale;
+ localconv = (iconv_t)-1;
+ if (utf8locale == 0)
+ {
+#if HAVE_LOCALE_CHARSET
+ charset = locale_charset ();
+#elif HAVE_NL_LANGINFO
+ charset = nl_langinfo (CODESET);
+#else
+ charset = stub_charset ();
+#endif
+ localconv = iconv_open (charset, "UTF-8");
+ if (localconv == (iconv_t)-1)
+ /* We assume ASCII when presented with an unknown encoding. */
+ localconv = iconv_open ("ASCII", "UTF-8");
+ }
+ u32init = 1;
+ }
+
+ /* NL_LANGINFO and locale_charset used when setting locale_utf8locale */
+
+ /* If we have a UTF-8 locale, convert to UTF-8 and return converted value. */
+ n = u32toutf8 (c, s);
+ if (utf8locale)
+ return n;
+
+ /* If the conversion is not supported, even the ASCII requested above, we
+ bail now. Currently we return the UTF-8 conversion. We could return
+ u32tocesc(). */
+ if (localconv == (iconv_t)-1)
+ return n;
+
+ optr = obuf;
+ obytesleft = sizeof (obuf);
+ iptr = s;
+ sn = n;
+
+ iconv (localconv, NULL, NULL, NULL, NULL);
+
+ if (iconv (localconv, (ICONV_CONST char **)&iptr, &sn, &optr, &obytesleft) == (size_t)-1)
+ {
+ /* You get ISO C99 escape sequences if iconv fails */
+ n = u32tocesc (c, s);
+ return n;
+ }
+
+ *optr = '\0';
+
+ /* number of chars to be copied is optr - obuf if we want to do bounds
+ checking */
+ strcpy (s, obuf);
+ return (optr - obuf);
+#endif /* HAVE_ICONV */
+
+ if (locale_utf8locale)
+ n = u32toutf8 (c, s);
+ else
+ n = u32tocesc (c, s); /* fallback is ISO C99 escape sequences */
+ return n;
+}
+#else
+void
+u32reset ()
+{
+}
+#endif /* HANDLE_MULTIBYTE */
diff --git a/lib/sh/utf8.c b/lib/sh/utf8.c
new file mode 100644
index 0000000..fed2522
--- /dev/null
+++ b/lib/sh/utf8.c
@@ -0,0 +1,196 @@
+/* utf8.c - UTF-8 character handling functions */
+
+/* Copyright (C) 2018 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "bashansi.h"
+#include "shmbutil.h"
+
+extern int locale_mb_cur_max;
+extern int locale_utf8locale;
+
+#if defined (HANDLE_MULTIBYTE)
+
+char *
+utf8_mbschr (s, c)
+ const char *s;
+ int c;
+{
+ return strchr (s, c); /* for now */
+}
+
+int
+utf8_mbscmp (s1, s2)
+ const char *s1, *s2;
+{
+ /* Use the fact that the UTF-8 encoding preserves lexicographic order. */
+ return strcmp (s1, s2);
+}
+
+char *
+utf8_mbsmbchar (str)
+ const char *str;
+{
+ register char *s;
+
+ for (s = (char *)str; *s; s++)
+ if ((*s & 0xc0) == 0x80)
+ return s;
+ return (0);
+}
+
+int
+utf8_mbsnlen(src, srclen, maxlen)
+ const char *src;
+ size_t srclen;
+ int maxlen;
+{
+ register int sind, count;
+
+ for (sind = count = 0; src[sind] && sind <= maxlen; sind++)
+ {
+ if ((src[sind] & 0xc0) != 0x80)
+ count++;
+ }
+ return (count);
+}
+
+/* Adapted from GNU gnulib. Handles UTF-8 characters up to 4 bytes long */
+int
+utf8_mblen (s, n)
+ const char *s;
+ size_t n;
+{
+ unsigned char c, c1, c2, c3;
+
+ if (s == 0)
+ return (0); /* no shift states */
+ if (n <= 0)
+ return (-1);
+
+ c = (unsigned char)*s;
+ if (c < 0x80)
+ return (c != 0);
+ if (c >= 0xc2)
+ {
+ c1 = (unsigned char)s[1];
+ if (c < 0xe0)
+ {
+ if (n == 1)
+ return -2;
+
+ /*
+ * c c1
+ *
+ * U+0080..U+07FF C2..DF 80..BF
+ */
+
+ if (n >= 2 && (c1 ^ 0x80) < 0x40) /* 0x80..0xbf */
+ return 2;
+ }
+ else if (c < 0xf0)
+ {
+ if (n == 1)
+ return -2;
+
+ /*
+ * c c1 c2
+ *
+ * U+0800..U+0FFF E0 A0..BF 80..BF
+ * U+1000..U+CFFF E1..EC 80..BF 80..BF
+ * U+D000..U+D7FF ED 80..9F 80..BF
+ * U+E000..U+FFFF EE..EF 80..BF 80..BF
+ */
+
+ if ((c1 ^ 0x80) < 0x40
+ && (c >= 0xe1 || c1 >= 0xa0)
+ && (c != 0xed || c1 < 0xa0))
+ {
+ if (n == 2)
+ return -2; /* incomplete */
+
+ c2 = (unsigned char)s[2];
+ if ((c2 ^ 0x80) < 0x40)
+ return 3;
+ }
+ }
+ else if (c <= 0xf4)
+ {
+ if (n == 1)
+ return -2;
+
+ /*
+ * c c1 c2 c3
+ *
+ * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ */
+ if (((c1 ^ 0x80) < 0x40)
+ && (c >= 0xf1 || c1 >= 0x90)
+ && (c < 0xf4 || (c == 0xf4 && c1 < 0x90)))
+ {
+ if (n == 2)
+ return -2; /* incomplete */
+
+ c2 = (unsigned char)s[2];
+ if ((c2 ^ 0x80) < 0x40)
+ {
+ if (n == 3)
+ return -2;
+
+ c3 = (unsigned char)s[3];
+ if ((c3 ^ 0x80) < 0x40)
+ return 4;
+ }
+ }
+ }
+ }
+ /* invalid or incomplete multibyte character */
+ return -1;
+}
+
+/* We can optimize this if we know the locale is UTF-8, but needs to handle
+ malformed byte sequences. */
+size_t
+utf8_mbstrlen(s)
+ const char *s;
+{
+ size_t clen, nc;
+ int mb_cur_max;
+
+ nc = 0;
+ mb_cur_max = MB_CUR_MAX;
+ while (*s && (clen = (size_t)utf8_mblen(s, mb_cur_max)) != 0)
+ {
+ if (MB_INVALIDCH(clen))
+ clen = 1; /* assume single byte */
+
+ s += clen;
+ nc++;
+ }
+ return nc;
+}
+
+#endif
diff --git a/lib/sh/vprint.c b/lib/sh/vprint.c
new file mode 100644
index 0000000..567fba3
--- /dev/null
+++ b/lib/sh/vprint.c
@@ -0,0 +1,85 @@
+/* vprint.c -- v[fs]printf() for 4.[23] BSD systems. */
+
+/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (USE_VFPRINTF_EMULATION)
+
+#include <stdio.h>
+
+#if !defined (NULL)
+# if defined (__STDC__)
+# define NULL ((void *)0)
+# else
+# define NULL 0x0
+# endif /* __STDC__ */
+#endif /* !NULL */
+
+/*
+ * Beware! Don't trust the value returned by either of these functions; it
+ * seems that pre-4.3-tahoe implementations of _doprnt () return the first
+ * argument, i.e. a char *.
+ */
+#include <varargs.h>
+
+int
+vfprintf (iop, fmt, ap)
+ FILE *iop;
+ char *fmt;
+ va_list ap;
+{
+ int len;
+ char localbuf[BUFSIZ];
+
+ if (iop->_flag & _IONBF)
+ {
+ iop->_flag &= ~_IONBF;
+ iop->_ptr = iop->_base = localbuf;
+ len = _doprnt (fmt, ap, iop);
+ (void) fflush (iop);
+ iop->_flag |= _IONBF;
+ iop->_base = NULL;
+ iop->_bufsiz = 0;
+ iop->_cnt = 0;
+ }
+ else
+ len = _doprnt (fmt, ap, iop);
+ return (ferror (iop) ? EOF : len);
+}
+
+/*
+ * Ditto for vsprintf
+ */
+int
+vsprintf (str, fmt, ap)
+ char *str, *fmt;
+ va_list ap;
+{
+ FILE f;
+ int len;
+
+ f._flag = _IOWRT|_IOSTRG;
+ f._ptr = str;
+ f._cnt = 32767;
+ len = _doprnt (fmt, ap, &f);
+ *f._ptr = 0;
+ return (len);
+}
+#endif /* USE_VFPRINTF_EMULATION */
diff --git a/lib/sh/wcsdup.c b/lib/sh/wcsdup.c
new file mode 100644
index 0000000..62a3c86
--- /dev/null
+++ b/lib/sh/wcsdup.c
@@ -0,0 +1,44 @@
+/* wcsdup.c - duplicate wide character string */
+
+/* Copyright (C) 2006 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE)
+
+#include <stdc.h>
+#include <wchar.h>
+#include <bashansi.h>
+#include <xmalloc.h>
+
+wchar_t *
+wcsdup (ws)
+ const wchar_t *ws;
+{
+ wchar_t *ret;
+ size_t len;
+
+ len = wcslen (ws);
+ ret = xmalloc ((len + 1) * sizeof (wchar_t));
+ if (ret == 0)
+ return ret;
+
+ return (wcscpy (ret, ws));
+}
+#endif /* !HAVE_WCSDUP && HANDLE_MULTIBYTE */
diff --git a/lib/sh/wcsnwidth.c b/lib/sh/wcsnwidth.c
new file mode 100644
index 0000000..9c7e7cc
--- /dev/null
+++ b/lib/sh/wcsnwidth.c
@@ -0,0 +1,56 @@
+/* wcsnwidth.c - compute display width of wide character string, up to max
+ specified width, return length. */
+
+/* Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HANDLE_MULTIBYTE)
+
+#include <stdc.h>
+#include <wchar.h>
+#include <bashansi.h>
+
+/* Return the number of wide characters that will be displayed from wide string
+ PWCS. If the display width exceeds MAX, return the number of wide chars
+ from PWCS required to display MAX characters on the screen. */
+int
+wcsnwidth(pwcs, n, max)
+ const wchar_t *pwcs;
+ size_t n, max;
+{
+ wchar_t wc, *ws;
+ int len, l;
+
+ len = 0;
+ ws = (wchar_t *)pwcs;
+ while (n-- > 0 && (wc = *ws++) != L'\0')
+ {
+ l = wcwidth (wc);
+ if (l < 0)
+ return (-1);
+ else if (l == max - len)
+ return (ws - pwcs);
+ else if (l > max - len)
+ return (--ws - pwcs);
+ len += l;
+ }
+ return (ws - pwcs);
+}
+#endif
diff --git a/lib/sh/wcswidth.c b/lib/sh/wcswidth.c
new file mode 100644
index 0000000..1a30d9f
--- /dev/null
+++ b/lib/sh/wcswidth.c
@@ -0,0 +1,46 @@
+/* wcswidth.c - compute display width of wide character string */
+
+/* Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HANDLE_MULTIBYTE) && !defined (HAVE_WCSWIDTH)
+
+#include <stdc.h>
+#include <wchar.h>
+#include <bashansi.h>
+
+int
+wcswidth(pwcs, n)
+ const wchar_t *pwcs;
+ size_t n;
+{
+ wchar_t wc;
+ int len, l;
+
+ len = 0;
+ while (n-- > 0 && (wc = *pwcs++) != L'\0')
+ {
+ if ((l = wcwidth(wc)) < 0)
+ return (-1);
+ len += l;
+ }
+ return (len);
+}
+#endif
diff --git a/lib/sh/winsize.c b/lib/sh/winsize.c
new file mode 100644
index 0000000..846fcd5
--- /dev/null
+++ b/lib/sh/winsize.c
@@ -0,0 +1,104 @@
+/* winsize.c - handle window size changes and information. */
+
+/* Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <stdc.h>
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <sys/ioctl.h>
+
+/* Try to find the definitions of `struct winsize' and TIOGCWINSZ */
+
+#if 0
+#if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ)
+# include <sys/ioctl.h>
+#endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */
+#endif
+
+#if defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
+# include <termios.h>
+#endif /* STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */
+
+/* Not in either of the standard places, look around. */
+#if !defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
+# if defined (HAVE_SYS_STREAM_H)
+# include <sys/stream.h>
+# endif /* HAVE_SYS_STREAM_H */
+# if defined (HAVE_SYS_PTEM_H) /* SVR4.2, at least, has it here */
+# include <sys/ptem.h>
+# define _IO_PTEM_H /* work around SVR4.2 1.1.4 bug */
+# endif /* HAVE_SYS_PTEM_H */
+# if defined (HAVE_SYS_PTE_H) /* ??? */
+# include <sys/pte.h>
+# endif /* HAVE_SYS_PTE_H */
+#endif /* !STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */
+
+#include <stdio.h>
+
+/* Return the fd from which we are actually getting input. */
+#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int shell_tty;
+
+#if defined (READLINE)
+/* Let's not call readline, forcing readline to initialize the termcap/terminfo
+ variables it needs, unless we have to. */
+extern int interactive_shell;
+extern int no_line_editing;
+extern int bash_readline_initialized;
+extern void rl_set_screen_size PARAMS((int, int));
+#endif
+extern void sh_set_lines_and_columns PARAMS((int, int));
+
+void
+get_new_window_size (from_sig, rp, cp)
+ int from_sig;
+ int *rp, *cp;
+{
+#if defined (TIOCGWINSZ)
+ struct winsize win;
+ int tty;
+
+ tty = input_tty ();
+ if (tty >= 0 && (ioctl (tty, TIOCGWINSZ, &win) == 0) &&
+ win.ws_row > 0 && win.ws_col > 0)
+ {
+ sh_set_lines_and_columns (win.ws_row, win.ws_col);
+#if defined (READLINE)
+ if ((interactive_shell && no_line_editing == 0) || bash_readline_initialized)
+ rl_set_screen_size (win.ws_row, win.ws_col);
+#endif
+ if (rp)
+ *rp = win.ws_row;
+ if (cp)
+ *cp = win.ws_col;
+ }
+#endif
+}
diff --git a/lib/sh/zcatfd.c b/lib/sh/zcatfd.c
new file mode 100644
index 0000000..aa8199f
--- /dev/null
+++ b/lib/sh/zcatfd.c
@@ -0,0 +1,74 @@
+/* zcatfd - copy contents of file descriptor to another */
+
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include <stdc.h>
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#ifndef ZBUFSIZ
+# define ZBUFSIZ 4096
+#endif
+
+extern ssize_t zread PARAMS((int, char *, size_t));
+extern int zwrite PARAMS((int, char *, ssize_t));
+
+/* Dump contents of file descriptor FD to OFD. FN is the filename for
+ error messages (not used right now). */
+int
+zcatfd (fd, ofd, fn)
+ int fd, ofd;
+ char *fn;
+{
+ ssize_t nr;
+ int rval;
+ char lbuf[ZBUFSIZ];
+
+ rval = 0;
+ while (1)
+ {
+ nr = zread (fd, lbuf, sizeof (lbuf));
+ if (nr == 0)
+ break;
+ else if (nr < 0)
+ {
+ rval = -1;
+ break;
+ }
+ else if (zwrite (ofd, lbuf, nr) < 0)
+ {
+ rval = -1;
+ break;
+ }
+ }
+
+ return rval;
+}
diff --git a/lib/sh/zgetline.c b/lib/sh/zgetline.c
new file mode 100644
index 0000000..5e1ef72
--- /dev/null
+++ b/lib/sh/zgetline.c
@@ -0,0 +1,126 @@
+/* zgetline - read a line of input from a specified file descriptor and return
+ a pointer to a newly-allocated buffer containing the data. */
+
+/* Copyright (C) 2008-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+#include "xmalloc.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+extern ssize_t zread PARAMS((int, char *, size_t));
+extern ssize_t zreadc PARAMS((int, char *));
+extern ssize_t zreadintr PARAMS((int, char *, size_t));
+extern ssize_t zreadcintr PARAMS((int, char *));
+
+typedef ssize_t breadfunc_t PARAMS((int, char *, size_t));
+typedef ssize_t creadfunc_t PARAMS((int, char *));
+
+/* Initial memory allocation for automatic growing buffer in zreadlinec */
+#define GET_LINE_INITIAL_ALLOCATION 16
+
+/* Derived from GNU libc's getline.
+ The behavior is almost the same as getline. See man getline.
+ The differences are
+ (1) using file descriptor instead of FILE *;
+ (2) the order of arguments: the file descriptor comes first;
+ (3) the addition of a fourth argument, DELIM; sets the delimiter to
+ be something other than newline if desired. If setting DELIM,
+ the next argument should be 1; and
+ (4) the addition of a fifth argument, UNBUFFERED_READ; this argument
+ controls whether get_line uses buffering or not to get a byte data
+ from FD. get_line uses zreadc if UNBUFFERED_READ is zero; and
+ uses zread if UNBUFFERED_READ is non-zero.
+
+ Returns number of bytes read or -1 on error. */
+
+ssize_t
+zgetline (fd, lineptr, n, delim, unbuffered_read)
+ int fd;
+ char **lineptr;
+ size_t *n;
+ int delim;
+ int unbuffered_read;
+{
+ int retval;
+ size_t nr;
+ char *line, c;
+
+ if (lineptr == 0 || n == 0 || (*lineptr == 0 && *n != 0))
+ return -1;
+
+ nr = 0;
+ line = *lineptr;
+
+ while (1)
+ {
+ retval = unbuffered_read ? zread (fd, &c, 1) : zreadc(fd, &c);
+
+ if (retval <= 0)
+ {
+ if (line && nr > 0)
+ line[nr] = '\0';
+ break;
+ }
+
+ if (nr + 2 >= *n)
+ {
+ size_t new_size;
+
+ new_size = (*n == 0) ? GET_LINE_INITIAL_ALLOCATION : *n * 2;
+ line = (*n >= new_size) ? NULL : xrealloc (*lineptr, new_size);
+
+ if (line)
+ {
+ *lineptr = line;
+ *n = new_size;
+ }
+ else
+ {
+ if (*n > 0)
+ {
+ (*lineptr)[*n - 1] = '\0';
+ nr = *n - 2;
+ }
+ break;
+ }
+ }
+
+ line[nr] = c;
+ nr++;
+
+ if (c == delim)
+ {
+ line[nr] = '\0';
+ break;
+ }
+ }
+
+ return nr - 1;
+}
diff --git a/lib/sh/zmapfd.c b/lib/sh/zmapfd.c
new file mode 100644
index 0000000..9ff50a4
--- /dev/null
+++ b/lib/sh/zmapfd.c
@@ -0,0 +1,93 @@
+/* zmapfd - read contents of file descriptor into a newly-allocated buffer */
+
+/* Copyright (C) 2006-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include "bashansi.h"
+#include "command.h"
+#include "general.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#ifndef ZBUFSIZ
+# define ZBUFSIZ 4096
+#endif
+
+extern ssize_t zread PARAMS((int, char *, size_t));
+
+/* Dump contents of file descriptor FD to *OSTR. FN is the filename for
+ error messages (not used right now). */
+int
+zmapfd (fd, ostr, fn)
+ int fd;
+ char **ostr;
+ char *fn;
+{
+ ssize_t nr;
+ int rval;
+ char lbuf[ZBUFSIZ];
+ char *result;
+ size_t rsize, rind;
+
+ rval = 0;
+ result = (char *)xmalloc (rsize = ZBUFSIZ);
+ rind = 0;
+
+ while (1)
+ {
+ nr = zread (fd, lbuf, sizeof (lbuf));
+ if (nr == 0)
+ {
+ rval = rind;
+ break;
+ }
+ else if (nr < 0)
+ {
+ free (result);
+ if (ostr)
+ *ostr = (char *)NULL;
+ return -1;
+ }
+
+ RESIZE_MALLOCED_BUFFER (result, rind, nr, rsize, ZBUFSIZ);
+ memcpy (result+rind, lbuf, nr);
+ rind += nr;
+ }
+
+ RESIZE_MALLOCED_BUFFER (result, rind, 1, rsize, 128);
+ result[rind] = '\0';
+
+ if (ostr)
+ *ostr = result;
+ else
+ free (result);
+
+ return rval;
+}
diff --git a/lib/sh/zread.c b/lib/sh/zread.c
new file mode 100644
index 0000000..dafb7f6
--- /dev/null
+++ b/lib/sh/zread.c
@@ -0,0 +1,228 @@
+/* zread - read data from file descriptor into buffer with retries */
+
+/* Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+
+#ifndef ZBUFSIZ
+# define ZBUFSIZ 4096
+#endif
+
+extern int executing_builtin;
+
+extern void check_signals_and_traps (void);
+extern void check_signals (void);
+extern int signal_is_trapped (int);
+extern int read_builtin_timeout (int);
+
+/* Read LEN bytes from FD into BUF. Retry the read on EINTR. Any other
+ error causes the loop to break. */
+ssize_t
+zread (fd, buf, len)
+ int fd;
+ char *buf;
+ size_t len;
+{
+ ssize_t r;
+
+ check_signals (); /* check for signals before a blocking read */
+ /* should generalize into a mechanism where different parts of the shell can
+ `register' timeouts and have them checked here. */
+ while (((r = read_builtin_timeout (fd)) < 0 || (r = read (fd, buf, len)) < 0) &&
+ errno == EINTR)
+ {
+ int t;
+ t = errno;
+ /* XXX - bash-5.0 */
+ /* We check executing_builtin and run traps here for backwards compatibility */
+ if (executing_builtin)
+ check_signals_and_traps (); /* XXX - should it be check_signals()? */
+ else
+ check_signals ();
+ errno = t;
+ }
+
+ return r;
+}
+
+/* Read LEN bytes from FD into BUF. Retry the read on EINTR, up to three
+ interrupts. Any other error causes the loop to break. */
+
+#ifdef NUM_INTR
+# undef NUM_INTR
+#endif
+#define NUM_INTR 3
+
+ssize_t
+zreadretry (fd, buf, len)
+ int fd;
+ char *buf;
+ size_t len;
+{
+ ssize_t r;
+ int nintr;
+
+ for (nintr = 0; ; )
+ {
+ r = read (fd, buf, len);
+ if (r >= 0)
+ return r;
+ if (r == -1 && errno == EINTR)
+ {
+ if (++nintr >= NUM_INTR)
+ return -1;
+ continue;
+ }
+ return r;
+ }
+}
+
+/* Call read(2) and allow it to be interrupted. Just a stub for now. */
+ssize_t
+zreadintr (fd, buf, len)
+ int fd;
+ char *buf;
+ size_t len;
+{
+ check_signals ();
+ return (read (fd, buf, len));
+}
+
+/* Read one character from FD and return it in CP. Return values are as
+ in read(2). This does some local buffering to avoid many one-character
+ calls to read(2), like those the `read' builtin performs. */
+
+static char lbuf[ZBUFSIZ];
+static size_t lind, lused;
+
+ssize_t
+zreadc (fd, cp)
+ int fd;
+ char *cp;
+{
+ ssize_t nr;
+
+ if (lind == lused || lused == 0)
+ {
+ nr = zread (fd, lbuf, sizeof (lbuf));
+ lind = 0;
+ if (nr <= 0)
+ {
+ lused = 0;
+ return nr;
+ }
+ lused = nr;
+ }
+ if (cp)
+ *cp = lbuf[lind++];
+ return 1;
+}
+
+/* Don't mix calls to zreadc and zreadcintr in the same function, since they
+ use the same local buffer. */
+ssize_t
+zreadcintr (fd, cp)
+ int fd;
+ char *cp;
+{
+ ssize_t nr;
+
+ if (lind == lused || lused == 0)
+ {
+ nr = zreadintr (fd, lbuf, sizeof (lbuf));
+ lind = 0;
+ if (nr <= 0)
+ {
+ lused = 0;
+ return nr;
+ }
+ lused = nr;
+ }
+ if (cp)
+ *cp = lbuf[lind++];
+ return 1;
+}
+
+/* Like zreadc, but read a specified number of characters at a time. Used
+ for `read -N'. */
+ssize_t
+zreadn (fd, cp, len)
+ int fd;
+ char *cp;
+ size_t len;
+{
+ ssize_t nr;
+
+ if (lind == lused || lused == 0)
+ {
+ if (len > sizeof (lbuf))
+ len = sizeof (lbuf);
+ nr = zread (fd, lbuf, len);
+ lind = 0;
+ if (nr <= 0)
+ {
+ lused = 0;
+ return nr;
+ }
+ lused = nr;
+ }
+ if (cp)
+ *cp = lbuf[lind++];
+ return 1;
+}
+
+void
+zreset ()
+{
+ lind = lused = 0;
+}
+
+/* Sync the seek pointer for FD so that the kernel's idea of the last char
+ read is the last char returned by zreadc. */
+void
+zsyncfd (fd)
+ int fd;
+{
+ off_t off, r;
+
+ off = lused - lind;
+ r = 0;
+ if (off > 0)
+ r = lseek (fd, -off, SEEK_CUR);
+
+ if (r != -1)
+ lused = lind = 0;
+}
diff --git a/lib/sh/zwrite.c b/lib/sh/zwrite.c
new file mode 100644
index 0000000..3240f4f
--- /dev/null
+++ b/lib/sh/zwrite.c
@@ -0,0 +1,64 @@
+/* zwrite - write contents of buffer to file descriptor, retrying on error */
+
+/* Copyright (C) 1999-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+/* Write NB bytes from BUF to file descriptor FD, retrying the write if
+ it is interrupted. We retry three times if we get a zero-length
+ write. Any other signal causes this function to return prematurely. */
+int
+zwrite (fd, buf, nb)
+ int fd;
+ char *buf;
+ size_t nb;
+{
+ int n, i, nt;
+
+ for (n = nb, nt = 0;;)
+ {
+ i = write (fd, buf, n);
+ if (i > 0)
+ {
+ n -= i;
+ if (n <= 0)
+ return nb;
+ buf += i;
+ }
+ else if (i == 0)
+ {
+ if (++nt > 3)
+ return (nb - n);
+ }
+ else if (errno != EINTR)
+ return -1;
+ }
+}