summaryrefslogtreecommitdiffstats
path: root/examples/loadables
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:17:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:17:24 +0000
commit9d8085074991d5c0a42d6fc96a2d1a3ee918aad1 (patch)
treec85bca1e6c11eb872edfc64c524d20f2b7e3307b /examples/loadables
parentInitial commit. (diff)
downloadbash-70baf9b08adef67074da9e233fa847220a474b78.tar.xz
bash-70baf9b08adef67074da9e233fa847220a474b78.zip
Adding upstream version 5.1.upstream/5.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--examples/loadables/Makefile.in324
-rw-r--r--examples/loadables/Makefile.inc.in108
-rw-r--r--examples/loadables/README76
-rw-r--r--examples/loadables/accept.c234
-rw-r--r--examples/loadables/asort.c279
-rw-r--r--examples/loadables/basename.c131
-rw-r--r--examples/loadables/cat.c122
-rw-r--r--examples/loadables/csv.c206
-rw-r--r--examples/loadables/cut.c625
-rw-r--r--examples/loadables/dirname.c119
-rw-r--r--examples/loadables/fdflags.c374
-rw-r--r--examples/loadables/finfo.c629
-rw-r--r--examples/loadables/head.c167
-rw-r--r--examples/loadables/hello.c96
-rw-r--r--examples/loadables/id.c329
-rw-r--r--examples/loadables/ln.c236
-rw-r--r--examples/loadables/loadables.h34
-rw-r--r--examples/loadables/logname.c74
-rw-r--r--examples/loadables/mkdir.c245
-rw-r--r--examples/loadables/mkfifo.c146
-rw-r--r--examples/loadables/mktemp.c212
-rw-r--r--examples/loadables/mypid.c89
-rw-r--r--examples/loadables/necho.c54
-rw-r--r--examples/loadables/pathchk.c381
-rw-r--r--examples/loadables/perl/Makefile.in99
-rw-r--r--examples/loadables/perl/README6
-rw-r--r--examples/loadables/perl/bperl.c46
-rw-r--r--examples/loadables/perl/iperl.c24
-rw-r--r--examples/loadables/print.c192
-rw-r--r--examples/loadables/printenv.c94
-rw-r--r--examples/loadables/push.c117
-rw-r--r--examples/loadables/realpath.c145
-rw-r--r--examples/loadables/rm.c181
-rw-r--r--examples/loadables/rmdir.c72
-rw-r--r--examples/loadables/seq.c490
-rw-r--r--examples/loadables/setpgid.c121
-rw-r--r--examples/loadables/sleep.c101
-rw-r--r--examples/loadables/stat.c430
-rw-r--r--examples/loadables/strftime.c128
-rw-r--r--examples/loadables/sync.c53
-rw-r--r--examples/loadables/tee.c180
-rw-r--r--examples/loadables/template.c75
-rw-r--r--examples/loadables/truefalse.c72
-rw-r--r--examples/loadables/tty.c82
-rw-r--r--examples/loadables/uname.c162
-rw-r--r--examples/loadables/unlink.c74
-rw-r--r--examples/loadables/whoami.c75
47 files changed, 8309 insertions, 0 deletions
diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in
new file mode 100644
index 0000000..be46121
--- /dev/null
+++ b/examples/loadables/Makefile.in
@@ -0,0 +1,324 @@
+#
+# Simple makefile for the sample loadable builtins
+#
+# Copyright (C) 1996-2019 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute 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@
+
+# Include some boilerplate Gnu makefile definitions.
+prefix = @prefix@
+
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+infodir = @infodir@
+includedir = @includedir@
+
+datarootdir = @datarootdir@
+
+loadablesdir = @loadablesdir@
+headersdir = @headersdir@
+
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Support an alternate destination root directory for package building
+DESTDIR =
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALLMODE= -m 0755
+
+@SET_MAKE@
+CC = @CC@
+RM = rm -f
+
+SHELL = @MAKE_SHELL@
+
+host_os = @host_os@
+host_cpu = @host_cpu@
+host_vendor = @host_vendor@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+CPPFLAGS = @CPPFLAGS@
+
+BASHINCDIR = ${topdir}/include
+
+SUPPORT_SRC = $(topdir)/support/
+
+LIBBUILD = ${BUILD_DIR}/lib
+
+INTL_LIBSRC = ${topdir}/lib/intl
+INTL_BUILDDIR = ${LIBBUILD}/intl
+INTL_INC = @INTL_INC@
+LIBINTL_H = @LIBINTL_H@
+
+CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(CFLAGS)
+
+#
+# These values are generated for configure by ${topdir}/support/shobj-conf.
+# If your system is not supported by that script, but includes facilities for
+# dynamic loading of shared objects, please update the script and send the
+# changes to bash-maintainers@gnu.org.
+#
+SHOBJ_CC = @SHOBJ_CC@
+SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
+SHOBJ_LD = @SHOBJ_LD@
+SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ @LDFLAGS@
+SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
+SHOBJ_LIBS = @SHOBJ_LIBS@
+SHOBJ_STATUS = @SHOBJ_STATUS@
+
+INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
+ -I$(BASHINCDIR) -I$(BUILD_DIR) -I$(LIBBUILD) \
+ -I$(BUILD_DIR)/builtins $(INTL_INC)
+
+.c.o:
+ $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<
+
+
+ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
+ tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \
+ uname sync push ln unlink realpath strftime mypid setpgid seq rm \
+ accept csv cut
+OTHERPROG = necho hello cat pushd stat asort
+
+all: $(SHOBJ_STATUS)
+
+supported: $(ALLPROG)
+others: $(OTHERPROG)
+
+unsupported:
+ @echo "Your system (${host_os}) is not supported by the"
+ @echo "${topdir}/support/shobj-conf script."
+ @echo "If your operating system provides facilities for dynamic"
+ @echo "loading of shared objects using the dlopen(3) interface,"
+ @echo "please update the script and re-run configure."
+ @echo "Please send the changes you made to bash-maintainers@gnu.org"
+ @echo "for inclusion in future bash releases."
+
+everything: supported others
+
+print: print.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ print.o $(SHOBJ_LIBS)
+
+necho: necho.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ necho.o $(SHOBJ_LIBS)
+
+hello: hello.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ hello.o $(SHOBJ_LIBS)
+
+truefalse: truefalse.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS)
+
+accept: accept.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ accept.o $(SHOBJ_LIBS)
+
+sleep: sleep.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS)
+
+finfo: finfo.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ finfo.o $(SHOBJ_LIBS)
+
+cat: cat.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS)
+
+rm: rm.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rm.o $(SHOBJ_LIBS)
+
+fdflags: fdflags.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ fdflags.o $(SHOBJ_LIBS)
+
+seq: seq.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ seq.o $(SHOBJ_LIBS)
+
+logname: logname.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS)
+
+basename: basename.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ basename.o $(SHOBJ_LIBS)
+
+dirname: dirname.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dirname.o $(SHOBJ_LIBS)
+
+tty: tty.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tty.o $(SHOBJ_LIBS)
+
+pathchk: pathchk.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pathchk.o $(SHOBJ_LIBS)
+
+tee: tee.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tee.o $(SHOBJ_LIBS)
+
+mkdir: mkdir.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkdir.o $(SHOBJ_LIBS)
+
+rmdir: rmdir.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS)
+
+mkfifo: mkfifo.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkfifo.o $(SHOBJ_LIBS)
+
+mktemp: mktemp.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mktemp.o $(SHOBJ_LIBS)
+
+head: head.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS)
+
+printenv: printenv.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS)
+
+id: id.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS)
+
+whoami: whoami.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS)
+
+uname: uname.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS)
+
+sync: sync.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS)
+
+push: push.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS)
+
+ln: ln.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS)
+
+unlink: unlink.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS)
+
+realpath: realpath.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
+
+csv: csv.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ csv.o $(SHOBJ_LIBS)
+
+cut: cut.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
+
+strftime: strftime.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS)
+
+mypid: mypid.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mypid.o $(SHOBJ_LIBS)
+
+setpgid: setpgid.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ setpgid.o $(SHOBJ_LIBS)
+
+stat: stat.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS)
+
+asort: asort.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ asort.o $(SHOBJ_LIBS)
+
+# pushd is a special case. We use the same source that the builtin version
+# uses, with special compilation options.
+#
+pushd.c: ${topdir}/builtins/pushd.def
+ $(RM) $@
+ ${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def
+
+pushd.o: pushd.c
+ $(RM) $@
+ $(SHOBJ_CC) -DHAVE_CONFIG_H -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(INC) -c -o $@ $<
+
+pushd: pushd.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS)
+
+clean:
+ $(RM) $(ALLPROG) $(OTHERPROG) *.o
+ -( cd perl && ${MAKE} ${MFLAGS} $@ )
+
+mostlyclean: clean
+ -( cd perl && ${MAKE} ${MFLAGS} $@ )
+
+distclean maintainer-clean: clean
+ $(RM) Makefile Makefile.inc pushd.c
+ -( cd perl && ${MAKE} ${MFLAGS} $@ )
+
+installdirs:
+ @${SHELL} $(SUPPORT_SRC)mkinstalldirs $(DESTDIR)$(loadablesdir)
+
+install-dev: installdirs
+ @$(INSTALL_DATA) Makefile.inc $(DESTDIR)$(loadablesdir)/Makefile.inc
+ @$(INSTALL_DATA) $(srcdir)/loadables.h $(DESTDIR)$(loadablesdir)/loadables.h
+ @( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" install-headers)
+
+install-supported: all installdirs install-dev
+ @echo installing example loadable builtins in $(DESTDIR)${loadablesdir}
+ @for prog in ${ALLPROG}; do \
+ echo $$prog ; \
+ $(INSTALL_PROGRAM) $(INSTALLMODE) $$prog $(DESTDIR)$(loadablesdir)/$$prog ;\
+ done
+
+uninstall-dev:
+ -$(RM) $(DESTDIR)$(loadablesdir)/Makefile.inc $(DESTDIR)$(loadablesdir)/loadables.h
+ -( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" uninstall-headers)
+
+uninstall-supported: uninstall-dev
+ -( cd $(DESTDIR)${loadablesdir} && $(RM) ${ALLPROG} )
+
+install-unsupported:
+uninstall-unsupported:
+
+install: install-$(SHOBJ_STATUS)
+uninstall: uninstall-$(SHOBJ_STATUS)
+
+print.o: print.c
+truefalse.o: truefalse.c
+accept.o: accept.c
+sleep.o: sleep.c
+finfo.o: finfo.c
+logname.o: logname.c
+basename.o: basename.c
+dirname.o: dirname.c
+tty.o: tty.c
+pathchk.o: pathchk.c
+tee.o: tee.c
+head.o: head.c
+rmdir.o: rmdir.c
+necho.o: necho.c
+hello.o: hello.c
+cat.o: cat.c
+csv.o: csv.c
+cut.o: cut.c
+printenv.o: printenv.c
+id.o: id.c
+whoami.o: whoami.c
+uname.o: uname.c
+sync.o: sync.c
+push.o: push.c
+mkdir.o: mkdir.c
+mktemp.o: mktemp.c
+realpath.o: realpath.c
+strftime.o: strftime.c
+setpgid.o: setpgid.c
+stat.o: stat.c
+fdflags.o: fdflags.c
+seq.o: seq.c
+asort.o: asort.c
diff --git a/examples/loadables/Makefile.inc.in b/examples/loadables/Makefile.inc.in
new file mode 100644
index 0000000..8b419a7
--- /dev/null
+++ b/examples/loadables/Makefile.inc.in
@@ -0,0 +1,108 @@
+#
+# Sample makefile for bash loadable builtin development
+#
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+
+# Include some boilerplate Gnu makefile definitions.
+prefix = @prefix@
+
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+infodir = @infodir@
+includedir = @includedir@
+
+datarootdir = @datarootdir@
+
+loadablesdir = @loadablesdir@
+headersdir = @headersdir@
+
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Support an alternate destination root directory for package building
+DESTDIR =
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALLMODE= -m 0755
+
+@SET_MAKE@
+CC = @CC@
+RM = rm -f
+
+SHELL = @MAKE_SHELL@
+
+host_os = @host_os@
+host_cpu = @host_cpu@
+host_vendor = @host_vendor@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+CPPFLAGS = @CPPFLAGS@
+
+BASHINCDIR = ${topdir}/include
+
+SUPPORT_SRC = $(topdir)/support/
+
+LIBBUILD = ${BUILD_DIR}/lib
+
+INTL_LIBSRC = ${topdir}/lib/intl
+INTL_BUILDDIR = ${LIBBUILD}/intl
+INTL_INC = @INTL_INC@
+LIBINTL_H = @LIBINTL_H@
+
+CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS)
+
+#
+# These values are generated for configure by ${topdir}/support/shobj-conf.
+# If your system is not supported by that script, but includes facilities for
+# dynamic loading of shared objects, please update the script and send the
+# changes to bash-maintainers@gnu.org.
+#
+SHOBJ_CC = @SHOBJ_CC@
+SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
+SHOBJ_LD = @SHOBJ_LD@
+SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ @LDFLAGS@
+SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
+SHOBJ_LIBS = @SHOBJ_LIBS@
+SHOBJ_STATUS = @SHOBJ_STATUS@
+
+INC = -I$(headersdir) -I$(headersdir)/include -I$(headersdir)/builtins
+
+.c.o:
+ $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<
+
+all: example
+
+example: example.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ example.o $(SHOBJ_LIBS)
+
+example.o: example.c
diff --git a/examples/loadables/README b/examples/loadables/README
new file mode 100644
index 0000000..6820c96
--- /dev/null
+++ b/examples/loadables/README
@@ -0,0 +1,76 @@
+Some examples of ready-to-dynamic-load builtins. Most of the
+examples given are reimplementations of standard commands whose
+execution time is dominated by process startup time. The
+exceptions are sleep, which allows you to sleep for fractions
+of a second, finfo, which provides access to the rest of the
+elements of the `stat' structure that `test' doesn't let you
+see, and pushd/popd/dirs, which allows you to compile them out
+of the shell.
+
+All of the new builtins in ksh93 that bash didn't already have
+are included here, as is the ksh `print' builtin.
+
+The configure script in the top-level source directory uses the
+support/shobj-conf script to set the right values in the Makefile,
+so you should not need to change the Makefile. If your system
+is not supported by support/shobj-conf, and it has the necessary
+facilities for building shared objects and support for the
+dlopen/dlsyn/dlclose/dlerror family of functions, please make
+the necessary changes to support/shobj-conf and send the changes
+to bash-maintainers@gnu.org.
+
+Loadable builtins are loaded into a running shell with
+
+ enable -f filename builtin-name
+
+enable uses a simple reference-counting scheme to avoid unloading a
+shared object that implements more than one loadable builtin before
+all loadable builtins implemented in the object are removed.
+
+Many of the details needed by builtin writers are found in hello.c,
+the canonical example. There is no real `builtin writers' programming
+guide'. The file template.c provides a template to use for creating
+new loadable builtins.
+
+The file "Makefile.inc" is created using the same values that configure
+writes into Makefile.in, and is installed in the same directory as the
+rest of the example builtins. It's intended to be a start at something
+that can be modified or included to help you build your own loadables
+without having to search for the right CFLAGS and LDFLAGS.
+
+basename.c Return non-directory portion of pathname.
+cat.c cat(1) replacement with no options - the way cat was intended.
+dirname.c Return directory portion of pathname.
+fdflags.c Change the flag associated with one of bash's open file descriptors.
+finfo.c Print file info.
+head.c Copy first part of files.
+hello.c Obligatory "Hello World" / sample loadable.
+id.c POSIX.2 user identity.
+ln.c Make links.
+loadables.h File loadable builtins can include for shell definitions.
+logname.c Print login name of current user.
+Makefile.in Simple makefile for the sample loadable builtins.
+Makefile.inc.in Sample makefile to use for loadable builtin development.
+mkdir.c Make directories.
+mypid.c Add $MYPID variable, demonstrate use of unload hook functio.n
+necho.c echo without options or argument interpretation.
+pathchk.c Check pathnames for validity and portability.
+print.c Loadable ksh-93 style print builtin.
+printenv.c Minimal builtin clone of BSD printenv(1).
+push.c Anyone remember TOPS-20?
+realpath.c Canonicalize pathnames, resolving symlinks.
+rm.c Remove files and directories.
+rmdir.c Remove directory.
+seq.c Print a sequence of decimal or floating point numbers.
+setpgid.c Set a process's pgrp; example of how to wrap a system call.
+sleep.c sleep for fractions of a second.
+stat.c populate an associative array with information about a file
+strftime.c Loadable builtin interface to strftime(3).
+sync.c Sync the disks by forcing pending filesystem writes to complete.
+tee.c Duplicate standard input.
+template.c Example template for loadable builtin.
+truefalse.c True and false builtins.
+tty.c Return terminal name.
+uname.c Print system information.
+unlink.c Remove a directory entry.
+whoami.c Print out username of current user.
diff --git a/examples/loadables/accept.c b/examples/loadables/accept.c
new file mode 100644
index 0000000..54cf38c
--- /dev/null
+++ b/examples/loadables/accept.c
@@ -0,0 +1,234 @@
+/* accept - listen for and accept a remote network connection on a given port */
+
+/*
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <stdlib.h>
+#include <unistd.h>
+#include "bashtypes.h"
+#include <errno.h>
+#include <time.h>
+#include "typemax.h"
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "loadables.h"
+
+static int accept_bind_variable (char *, int);
+
+int
+accept_builtin (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *l;
+ SHELL_VAR *v;
+ intmax_t iport;
+ int opt;
+ char *tmoutarg, *fdvar, *rhostvar, *rhost;
+ unsigned short uport;
+ int servsock, clisock;
+ struct sockaddr_in server, client;
+ socklen_t clientlen;
+ struct timeval timeval;
+ struct linger linger = { 0, 0 };
+
+ rhostvar = tmoutarg = fdvar = rhost = (char *)NULL;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "r:t:v:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'r':
+ rhostvar = list_optarg;
+ break;
+ case 't':
+ tmoutarg = list_optarg;
+ break;
+ case 'v':
+ fdvar = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+ list = loptend;
+
+ /* Validate input and variables */
+ if (tmoutarg)
+ {
+ long ival, uval;
+ opt = uconvert (tmoutarg, &ival, &uval, (char **)0);
+ if (opt == 0 || ival < 0 || uval < 0)
+ {
+ builtin_error ("%s: invalid timeout specification", tmoutarg);
+ return (EXECUTION_FAILURE);
+ }
+ timeval.tv_sec = ival;
+ timeval.tv_usec = uval;
+ /* XXX - should we warn if ival == uval == 0 ? */
+ }
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (legal_number (list->word->word, &iport) == 0 || iport < 0 || iport > TYPE_MAXIMUM (unsigned short))
+ {
+ builtin_error ("%s: invalid port number", list->word->word);
+ return (EXECUTION_FAILURE);
+ }
+ uport = (unsigned short)iport;
+
+ if (fdvar == 0)
+ fdvar = "ACCEPT_FD";
+
+ unbind_variable (fdvar);
+ if (rhostvar)
+ unbind_variable (rhostvar);
+
+ if ((servsock = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
+ {
+ builtin_error ("cannot create socket: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+
+ memset ((char *)&server, 0, sizeof (server));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(uport);
+ server.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0)
+ {
+ builtin_error ("socket bind failure: %s", strerror (errno));
+ close (servsock);
+ return (EXECUTION_FAILURE);
+ }
+
+ opt = 1;
+ setsockopt (servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt));
+ setsockopt (servsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof (linger));
+
+ if (listen (servsock, 1) < 0)
+ {
+ builtin_error ("listen failure: %s", strerror (errno));
+ close (servsock);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (tmoutarg)
+ {
+ fd_set iofds;
+
+ FD_ZERO(&iofds);
+ FD_SET(servsock, &iofds);
+
+ opt = select (servsock+1, &iofds, 0, 0, &timeval);
+ if (opt < 0)
+ builtin_error ("select failure: %s", strerror (errno));
+ if (opt <= 0)
+ {
+ close (servsock);
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ clientlen = sizeof (client);
+ if ((clisock = accept (servsock, (struct sockaddr *)&client, &clientlen)) < 0)
+ {
+ builtin_error ("client accept failure: %s", strerror (errno));
+ close (servsock);
+ return (EXECUTION_FAILURE);
+ }
+
+ close (servsock);
+
+ accept_bind_variable (fdvar, clisock);
+ if (rhostvar)
+ {
+ rhost = inet_ntoa (client.sin_addr);
+ v = builtin_bind_variable (rhostvar, rhost, 0);
+ if (v == 0 || readonly_p (v) || noassign_p (v))
+ builtin_error ("%s: cannot set variable", rhostvar);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+static int
+accept_bind_variable (varname, intval)
+ char *varname;
+ int intval;
+{
+ SHELL_VAR *v;
+ char ibuf[INT_STRLEN_BOUND (int) + 1], *p;
+
+ p = fmtulong (intval, 10, ibuf, sizeof (ibuf), 0);
+ v = builtin_bind_variable (varname, p, 0);
+ if (v == 0 || readonly_p (v) || noassign_p (v))
+ builtin_error ("%s: cannot set variable", varname);
+ return (v != 0);
+}
+
+char *accept_doc[] = {
+ "Accept a network connection on a specified port.",
+ ""
+ "This builtin allows a bash script to act as a TCP/IP server.",
+ "",
+ "Options, if supplied, have the following meanings:",
+ " -t timeout wait TIMEOUT seconds for a connection. TIMEOUT may",
+ " be a decimal number including a fractional portion",
+ " -v varname store the numeric file descriptor of the connected",
+ " socket into VARNAME. The default VARNAME is ACCEPT_FD",
+ " -r rhost store the IP address of the remote host into the shell",
+ " variable RHOST, in dotted-decimal notation",
+ "",
+ "If successful, the shell variable ACCEPT_FD, or the variable named by the",
+ "-v option, will be set to the fd of the connected socket, suitable for",
+ "use as 'read -u$ACCEPT_FD'. RHOST, if supplied, will hold the IP address",
+ "of the remote client. The return status is 0.",
+ "",
+ "On failure, the return status is 1 and ACCEPT_FD (or VARNAME) and RHOST,",
+ "if supplied, will be unset.",
+ "",
+ "The server socket fd will be closed before accept returns.",
+ (char *) NULL
+};
+
+struct builtin accept_struct = {
+ "accept", /* builtin name */
+ accept_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ accept_doc, /* array of long documentation strings. */
+ "accept [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/asort.c b/examples/loadables/asort.c
new file mode 100644
index 0000000..e847ef8
--- /dev/null
+++ b/examples/loadables/asort.c
@@ -0,0 +1,279 @@
+/*
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+ 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 <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "bashtypes.h"
+#include "shell.h"
+#include "builtins.h"
+#include "common.h"
+#include "xmalloc.h"
+#include "bashgetopt.h"
+
+typedef struct sort_element {
+ ARRAY_ELEMENT *v; // used when sorting array in-place
+ char *key; // used when sorting assoc array
+ char *value; // points to value of array element or assoc entry
+ double num; // used for numeric sort
+} sort_element;
+
+static int reverse_flag;
+static int numeric_flag;
+
+static int
+compare(const void *p1, const void *p2) {
+ const sort_element e1 = *(sort_element *) p1;
+ const sort_element e2 = *(sort_element *) p2;
+
+ if (numeric_flag) {
+ if (reverse_flag)
+ return (e2.num > e1.num) ? 1 : (e2.num < e1.num) ? -1 : 0;
+ else
+ return (e1.num > e2.num) ? 1 : (e1.num < e2.num) ? -1 : 0;
+ }
+ else {
+ if (reverse_flag)
+ return strcoll(e2.value, e1.value);
+ else
+ return strcoll(e1.value, e2.value);
+ }
+}
+
+static int
+sort_index(SHELL_VAR *dest, SHELL_VAR *source) {
+ HASH_TABLE *hash;
+ BUCKET_CONTENTS *bucket;
+ sort_element *sa;
+ ARRAY *array, *dest_array;
+ ARRAY_ELEMENT *ae;
+ size_t i, j, n;
+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1]; // used by fmtulong
+ char *key;
+
+ dest_array = array_cell(dest);
+
+ if (assoc_p(source)) {
+ hash = assoc_cell(source);
+ n = hash->nentries;
+ sa = xmalloc(n * sizeof(sort_element));
+ i = 0;
+ for ( j = 0; j < hash->nbuckets; ++j ) {
+ bucket = hash->bucket_array[j];
+ while ( bucket ) {
+ sa[i].v = NULL;
+ sa[i].key = bucket->key;
+ if ( numeric_flag )
+ sa[i].num = strtod(bucket->data, NULL);
+ else
+ sa[i].value = bucket->data;
+ i++;
+ bucket = bucket->next;
+ }
+ }
+ }
+ else {
+ array = array_cell(source);
+ n = array_num_elements(array);
+ sa = xmalloc(n * sizeof(sort_element));
+ i = 0;
+
+ for (ae = element_forw(array->head); ae != array->head; ae = element_forw(ae)) {
+ sa[i].v = ae;
+ if (numeric_flag)
+ sa[i].num = strtod(element_value(ae), NULL);
+ else
+ sa[i].value = element_value(ae);
+ i++;
+ }
+ }
+
+ // sanity check
+ if ( i != n ) {
+ builtin_error("%s: corrupt array", source->name);
+ return EXECUTION_FAILURE;
+ }
+
+ qsort(sa, n, sizeof(sort_element), compare);
+
+ array_flush(dest_array);
+
+ for ( i = 0; i < n; ++i ) {
+ if ( assoc_p(source) )
+ key = sa[i].key;
+ else
+ key = fmtulong((long unsigned)sa[i].v->ind, 10, ibuf, sizeof(ibuf), 0);
+
+ array_insert(dest_array, i, key);
+ }
+
+ return EXECUTION_SUCCESS;
+}
+
+static int
+sort_inplace(SHELL_VAR *var) {
+ size_t i, n;
+ ARRAY *a;
+ ARRAY_ELEMENT *ae;
+ sort_element *sa = 0;
+
+ a = array_cell(var);
+ n = array_num_elements(a);
+
+ if ( n == 0 )
+ return EXECUTION_SUCCESS;
+
+ sa = xmalloc(n * sizeof(sort_element));
+
+ i = 0;
+ for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
+ sa[i].v = ae;
+ if (numeric_flag)
+ sa[i].num = strtod(element_value(ae), NULL);
+ else
+ sa[i].value = element_value(ae);
+ i++;
+ }
+
+ // sanity check
+ if ( i != n ) {
+ builtin_error("%s: corrupt array", var->name);
+ return EXECUTION_FAILURE;
+ }
+
+ qsort(sa, n, sizeof(sort_element), compare);
+
+ // for in-place sort, simply "rewire" the array elements
+ sa[0].v->prev = sa[n-1].v->next = a->head;
+ a->head->next = sa[0].v;
+ a->head->prev = sa[n-1].v;
+ a->max_index = n - 1;
+ for (i = 0; i < n; i++) {
+ sa[i].v->ind = i;
+ if (i > 0)
+ sa[i].v->prev = sa[i-1].v;
+ if (i < n - 1)
+ sa[i].v->next = sa[i+1].v;
+ }
+ xfree(sa);
+ return EXECUTION_SUCCESS;
+}
+
+int
+asort_builtin(WORD_LIST *list) {
+ SHELL_VAR *var, *var2;
+ char *word;
+ int opt, ret;
+ int index_flag = 0;
+
+ numeric_flag = 0;
+ reverse_flag = 0;
+
+ reset_internal_getopt();
+ while ((opt = internal_getopt(list, "inr")) != -1) {
+ switch (opt) {
+ case 'i': index_flag = 1; break;
+ case 'n': numeric_flag = 1; break;
+ case 'r': reverse_flag = 1; break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0) {
+ builtin_usage();
+ return EX_USAGE;
+ }
+
+ if (legal_identifier (list->word->word) == 0) {
+ sh_invalidid (list->word->word);
+ return EXECUTION_FAILURE;
+ }
+
+ if ( index_flag ) {
+ if ( list->next == 0 || list->next->next ) {
+ builtin_usage();
+ return EX_USAGE;
+ }
+ if (legal_identifier (list->next->word->word) == 0) {
+ sh_invalidid (list->next->word->word);
+ return EXECUTION_FAILURE;
+ }
+ var = find_or_make_array_variable(list->word->word, 1);
+ if (var == 0)
+ return EXECUTION_FAILURE;
+ var2 = find_variable(list->next->word->word);
+ if ( !var2 || ( !array_p(var2) && !assoc_p(var2) ) ) {
+ builtin_error("%s: Not an array", list->next->word->word);
+ return EXECUTION_FAILURE;
+ }
+ return sort_index(var, var2);
+ }
+
+ while (list) {
+ word = list->word->word;
+ var = find_variable(word);
+ list = list->next;
+
+ if (var == 0 || array_p(var) == 0) {
+ builtin_error("%s: Not an array", word);
+ continue;
+ }
+ if (readonly_p(var) || noassign_p(var)) {
+ if (readonly_p(var))
+ err_readonly(word);
+ continue;
+ }
+
+ if ( (ret = sort_inplace(var)) != EXECUTION_SUCCESS )
+ return ret;
+ }
+ return EXECUTION_SUCCESS;
+
+}
+
+char *asort_doc[] = {
+ "Sort arrays in-place.",
+ "",
+ "Options:",
+ " -n compare according to string numerical value",
+ " -r reverse the result of comparisons",
+ " -i sort using indices/keys",
+ "",
+ "If -i is supplied, SOURCE is not sorted in-place, but the indices (or keys",
+ "if associative) of SOURCE, after sorting it by its values, are placed as",
+ "values in the indexed array DEST",
+ "",
+ "Associative arrays may not be sorted in-place.",
+ "",
+ "Exit status:",
+ "Return value is zero unless an error happened (like invalid variable name",
+ "or readonly array).",
+ (char *)NULL
+};
+
+struct builtin asort_struct = {
+ "asort",
+ asort_builtin,
+ BUILTIN_ENABLED,
+ asort_doc,
+ "asort [-nr] array ... or asort [-nr] -i dest source",
+ 0
+};
diff --git a/examples/loadables/basename.c b/examples/loadables/basename.c
new file mode 100644
index 0000000..29dd1a6
--- /dev/null
+++ b/examples/loadables/basename.c
@@ -0,0 +1,131 @@
+/* basename - return nondirectory portion of pathname */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+int
+basename_builtin (list)
+ WORD_LIST *list;
+{
+ int slen, sufflen, off;
+ char *string, *suffix, *fn;
+
+ if (no_options (list))
+ return (EX_USAGE);
+ list = loptend;
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ string = list->word->word;
+ suffix = (char *)NULL;
+ if (list->next)
+ {
+ list = list->next;
+ suffix = list->word->word;
+ }
+
+ if (list->next)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ slen = strlen (string);
+
+ /* Strip trailing slashes */
+ while (slen > 0 && string[slen - 1] == '/')
+ slen--;
+
+ /* (2) If string consists entirely of slash characters, string shall be
+ set to a single slash character. In this case, skip steps (3)
+ through (5). */
+ if (slen == 0)
+ {
+ fputs ("/\n", stdout);
+ return (EXECUTION_SUCCESS);
+ }
+
+ /* (3) If there are any trailing slash characters in string, they
+ shall be removed. */
+ string[slen] = '\0';
+
+ /* (4) If there are any slash characters remaining in string, the prefix
+ of string up to an including the last slash character in string
+ shall be removed. */
+ while (--slen >= 0)
+ if (string[slen] == '/')
+ break;
+
+ fn = string + slen + 1;
+
+ /* (5) If the suffix operand is present, is not identical to the
+ characters remaining in string, and is identical to a suffix
+ of the characters remaining in string, the suffix suffix
+ shall be removed from string. Otherwise, string shall not be
+ modified by this step. */
+ if (suffix)
+ {
+ sufflen = strlen (suffix);
+ slen = strlen (fn);
+ if (sufflen < slen)
+ {
+ off = slen - sufflen;
+ if (strcmp (fn + off, suffix) == 0)
+ fn[off] = '\0';
+ }
+ }
+ printf ("%s\n", fn);
+ return (EXECUTION_SUCCESS);
+}
+
+char *basename_doc[] = {
+ "Return non-directory portion of pathname.",
+ "",
+ "The STRING is converted to a filename corresponding to the last",
+ "pathname component in STRING. If the suffix string SUFFIX is",
+ "supplied, it is removed.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin basename_struct = {
+ "basename", /* builtin name */
+ basename_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ basename_doc, /* array of long documentation strings. */
+ "basename string [suffix]", /* usage synopsis */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c
new file mode 100644
index 0000000..be99c4c
--- /dev/null
+++ b/examples/loadables/cat.c
@@ -0,0 +1,122 @@
+/*
+ * cat replacement
+ *
+ * no options - the way cat was intended
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <fcntl.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char *strerror ();
+extern char **make_builtin_argv ();
+
+static int
+fcopy(fd)
+int fd;
+{
+ char buf[1024], *s;
+ int n, w, e;
+
+ while (n = read(fd, buf, sizeof (buf))) {
+ w = write(1, buf, n);
+ if (w != n) {
+ e = errno;
+ write(2, "cat: write error: ", 18);
+ s = strerror(e);
+ write(2, s, strlen(s));
+ write(2, "\n", 1);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+cat_main (argc, argv)
+int argc;
+char **argv;
+{
+ int i, fd, r;
+ char *s;
+
+ if (argc == 1)
+ return (fcopy(0));
+
+ for (i = r = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1] == '\0')
+ fd = 0;
+ else {
+ fd = open(argv[i], O_RDONLY, 0666);
+ if (fd < 0) {
+ s = strerror(errno);
+ write(2, "cat: cannot open ", 17);
+ write(2, argv[i], strlen(argv[i]));
+ write(2, ": ", 2);
+ write(2, s, strlen(s));
+ write(2, "\n", 1);
+ continue;
+ }
+ }
+ r = fcopy(fd);
+ if (fd != 0)
+ close(fd);
+ }
+ return (r);
+}
+
+int
+cat_builtin(list)
+WORD_LIST *list;
+{
+ char **v;
+ int c, r;
+
+ v = make_builtin_argv(list, &c);
+ r = cat_main(c, v);
+ free(v);
+
+ return r;
+}
+
+char *cat_doc[] = {
+ "Display files.",
+ "",
+ "Read each FILE and display it on the standard output. If any",
+ "FILE is `-' or if no FILE argument is given, the standard input",
+ "is read.",
+ (char *)0
+};
+
+struct builtin cat_struct = {
+ "cat",
+ cat_builtin,
+ BUILTIN_ENABLED,
+ cat_doc,
+ "cat [-] [file ...]",
+ 0
+};
diff --git a/examples/loadables/csv.c b/examples/loadables/csv.c
new file mode 100644
index 0000000..11228f1
--- /dev/null
+++ b/examples/loadables/csv.c
@@ -0,0 +1,206 @@
+/* csv - process a line of csv data and populate an indexed array with the
+ fields */
+
+/*
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+ 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/>.
+*/
+
+/* See Makefile for compilation details. */
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <stdio.h>
+
+#include "loadables.h"
+
+#define CSV_ARRAY_DEFAULT "CSV"
+
+#define NQUOTE 0
+#define DQUOTE 1
+
+/* Split LINE into comma-separated fields, storing each field into a separate
+ element of array variable CSV, starting at index 0. The format of LINE is
+ as described in RFC 4180. */
+static int
+csvsplit (csv, line)
+ SHELL_VAR *csv;
+ char *line;
+{
+ arrayind_t ind;
+ char *field, *prev, *buf, *xbuf;
+ int delim, qstate;
+ int b, rval;
+
+ xbuf = 0;
+ ind = 0;
+ field = prev = line;
+
+ do
+ {
+ if (*prev == '"')
+ {
+ if (xbuf == 0)
+ xbuf = xmalloc (strlen (prev) + 1);
+ buf = xbuf;
+ b = 0;
+ qstate = DQUOTE;
+ for (field = ++prev; *field; field++)
+ {
+ if (qstate == DQUOTE && *field == '"' && field[1] == '"')
+ buf[b++] = *field++; /* skip double quote */
+ else if (qstate == DQUOTE && *field == '"')
+ qstate = NQUOTE;
+ else if (qstate == NQUOTE && *field == ',')
+ break;
+ else
+ /* This copies any text between a closing double quote and the
+ delimiter. If you want to change that, make sure to do the
+ copy only if qstate == DQUOTE. */
+ buf[b++] = *field;
+ }
+ buf[b] = '\0';
+ }
+ else
+ {
+ buf = prev;
+ field = prev + strcspn (prev, ",");
+ }
+
+ delim = *field;
+ *field = '\0';
+
+ bind_array_element (csv, ind, buf, 0);
+ ind++;
+
+ *field = delim;
+
+ if (delim == ',')
+ prev = field + 1;
+ }
+ while (delim == ',');
+
+ if (xbuf)
+ free (xbuf);
+
+ return (rval = ind); /* number of fields */
+}
+
+int
+csv_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, rval;
+ char *array_name, *csvstring;
+ SHELL_VAR *v;
+
+ array_name = 0;
+ rval = EXECUTION_SUCCESS;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "a:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ array_name = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (array_name == 0)
+ array_name = CSV_ARRAY_DEFAULT;
+
+ if (legal_identifier (array_name) == 0)
+ {
+ sh_invalidid (array_name);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (list == 0)
+ {
+ builtin_error ("csv string argument required");
+ return (EX_USAGE);
+ }
+
+ v = find_or_make_array_variable (array_name, 1);
+ if (v == 0 || readonly_p (v) || noassign_p (v))
+ {
+ if (v && readonly_p (v))
+ err_readonly (array_name);
+ return (EXECUTION_FAILURE);
+ }
+ else if (array_p (v) == 0)
+ {
+ builtin_error ("%s: not an indexed array", array_name);
+ return (EXECUTION_FAILURE);
+ }
+ if (invisible_p (v))
+ VUNSETATTR (v, att_invisible);
+ array_flush (array_cell (v));
+
+ csvstring = list->word->word;
+
+ if (csvstring == 0 || *csvstring == 0)
+ return (EXECUTION_SUCCESS);
+
+ opt = csvsplit (v, csvstring);
+ /* Maybe do something with OPT here, it's the number of fields */
+
+ return (rval);
+}
+
+/* Called when builtin is enabled and loaded from the shared object. If this
+ function returns 0, the load fails. */
+int
+csv_builtin_load (name)
+ char *name;
+{
+ return (1);
+}
+
+/* Called when builtin is disabled. */
+void
+csv_builtin_unload (name)
+ char *name;
+{
+}
+
+char *csv_doc[] = {
+ "Read comma-separated fields from a string.",
+ "",
+ "Parse STRING, a line of comma-separated values, into individual fields,",
+ "and store them into the indexed array ARRAYNAME starting at index 0.",
+ "If ARRAYNAME is not supplied, \"CSV\" is the default array name.",
+ (char *)NULL
+};
+
+struct builtin csv_struct = {
+ "csv", /* builtin name */
+ csv_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ csv_doc, /* array of long documentation strings. */
+ "csv [-a ARRAY] string", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c
new file mode 100644
index 0000000..ad9a833
--- /dev/null
+++ b/examples/loadables/cut.c
@@ -0,0 +1,625 @@
+/* cut,lcut - extract specified fields from a line and assign them to an array
+ or print them to the standard output */
+
+/*
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+ 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/>.
+*/
+
+/* See Makefile for compilation details. */
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "loadables.h"
+#include "shmbutil.h"
+
+#define CUT_ARRAY_DEFAULT "CUTFIELDS"
+
+#define NOPOS -2 /* sentinel for unset startpos/endpos */
+
+#define BOL 0
+#define EOL INT_MAX
+#define NORANGE -1 /* just a position, no range */
+
+#define BFLAG (1 << 0)
+#define CFLAG (1 << 1)
+#define DFLAG (1 << 2)
+#define FFLAG (1 << 3)
+#define SFLAG (1 << 4)
+
+struct cutpos
+{
+ int startpos, endpos; /* zero-based, correction done in getlist() */
+};
+
+struct cutop
+{
+ int flags;
+ int delim;
+ int npos;
+ struct cutpos *poslist;
+};
+
+static int
+poscmp (a, b)
+ void *a, *b;
+{
+ struct cutpos *p1, *p2;
+
+ p1 = (struct cutpos *)a;
+ p2 = (struct cutpos *)b;
+ return (p1->startpos - p2->startpos);
+}
+
+static int
+getlist (arg, opp)
+ char *arg;
+ struct cutpos **opp;
+{
+ char *ntok, *ltok, *larg;
+ int s, e;
+ intmax_t num;
+ struct cutpos *poslist;
+ int npos, nsize;
+
+ poslist = 0;
+ nsize = npos = 0;
+ s = e = 0;
+ larg = arg;
+ while (ltok = strsep (&larg, ","))
+ {
+ if (*ltok == 0)
+ continue;
+
+ ntok = strsep (&ltok, "-");
+ if (*ntok == 0)
+ s = BOL;
+ else
+ {
+ if (legal_number (ntok, &num) == 0 || (int)num != num || num <= 0)
+ {
+ builtin_error ("%s: invalid list value", ntok);
+ *opp = poslist;
+ return -1;
+ }
+ s = num;
+ s--; /* fields are 1-based */
+ }
+ if (ltok == 0)
+ e = NORANGE;
+ else if (*ltok == 0)
+ e = EOL;
+ else
+ {
+ if (legal_number (ltok, &num) == 0 || (int)num != num || num <= 0)
+ {
+ builtin_error ("%s: invalid list value", ltok);
+ *opp = poslist;
+ return -1;
+ }
+ e = num;
+ e--;
+ if (e == s)
+ e = NORANGE;
+ }
+
+ if (npos == nsize)
+ {
+ nsize += 4;
+ poslist = (struct cutpos *)xrealloc (poslist, nsize * sizeof (struct cutpos));
+ }
+ poslist[npos].startpos = s;
+ poslist[npos].endpos = e;
+ npos++;
+ }
+ if (npos == 0)
+ {
+ builtin_error ("missing list of positions");
+ *opp = poslist;
+ return -1;
+ }
+
+ qsort (poslist, npos, sizeof(poslist[0]), poscmp);
+ *opp = poslist;
+
+ return npos;
+}
+
+static int
+cutbytes (v, line, ops)
+ SHELL_VAR *v;
+ char *line;
+ struct cutop *ops;
+{
+ arrayind_t ind;
+ char *buf, *bmap;
+ size_t llen;
+ int i, b, n, s, e;
+
+ llen = strlen (line);
+ buf = xmalloc (llen + 1);
+ bmap = xmalloc (llen + 1);
+ memset (bmap, 0, llen);
+
+ for (n = 0; n < ops->npos; n++)
+ {
+ s = ops->poslist[n].startpos; /* no translation needed yet */
+ e = ops->poslist[n].endpos;
+ if (e == NORANGE)
+ e = s;
+ else if (e == EOL || e >= llen)
+ e = llen - 1;
+ /* even if a column is specified multiple times, it will only be printed
+ once */
+ for (i = s; i <= e; i++)
+ bmap[i] = 1;
+ }
+
+ b = 0;
+ for (i = 0; i < llen; i++)
+ if (bmap[i])
+ buf[b++] = line[i];
+ buf[b] = 0;
+
+ if (v)
+ {
+ ind = 0;
+ bind_array_element (v, ind, buf, 0);
+ ind++;
+ }
+ else
+ printf ("%s\n", buf);
+
+ free (buf);
+ free (bmap);
+
+ return ind;
+}
+
+static int
+cutchars (v, line, ops)
+ SHELL_VAR *v;
+ char *line;
+ struct cutop *ops;
+{
+ arrayind_t ind;
+ char *buf, *bmap;
+ wchar_t *wbuf, *wb2;
+ size_t llen, wlen;
+ int i, b, n, s, e;
+
+ if (MB_CUR_MAX == 1)
+ return (cutbytes (v, line, ops));
+ if (locale_utf8locale && utf8_mbsmbchar (line) == 0)
+ return (cutbytes (v, line, ops));
+
+ llen = strlen (line);
+ wbuf = (wchar_t *)xmalloc ((llen + 1) * sizeof (wchar_t));
+
+ wlen = mbstowcs (wbuf, line, llen);
+ if (MB_INVALIDCH (wlen))
+ {
+ free (wbuf);
+ return (cutbytes (v, line, ops));
+ }
+
+ bmap = xmalloc (llen + 1);
+ memset (bmap, 0, llen);
+
+ for (n = 0; n < ops->npos; n++)
+ {
+ s = ops->poslist[n].startpos; /* no translation needed yet */
+ e = ops->poslist[n].endpos;
+ if (e == NORANGE)
+ e = s;
+ else if (e == EOL || e >= wlen)
+ e = wlen - 1;
+ /* even if a column is specified multiple times, it will only be printed
+ once */
+ for (i = s; i <= e; i++)
+ bmap[i] = 1;
+ }
+
+ wb2 = (wchar_t *)xmalloc ((wlen + 1) * sizeof (wchar_t));
+ b = 0;
+ for (i = 0; i < wlen; i++)
+ if (bmap[i])
+ wb2[b++] = wbuf[i];
+ wb2[b] = 0;
+
+ free (wbuf);
+
+ buf = bmap;
+ n = wcstombs (buf, wb2, llen);
+
+ if (v)
+ {
+ ind = 0;
+ bind_array_element (v, ind, buf, 0);
+ ind++;
+ }
+ else
+ printf ("%s\n", buf);
+
+ free (buf);
+ free (wb2);
+
+ return ind;
+}
+
+/* The basic strategy is to cut the line into fields using strsep, populate
+ an array of fields from 0..nf, then select those fields using the same
+ bitmap approach as cut{bytes,chars} and assign them to the array variable
+ V or print them on stdout. This function obeys SFLAG. */
+static int
+cutfields (v, line, ops)
+ SHELL_VAR *v;
+ char *line;
+ struct cutop *ops;
+{
+ arrayind_t ind;
+ char *buf, *bmap, *field, **fields, delim[2];
+ size_t llen, fsize;
+ int i, b, n, s, e, nf;
+
+ ind = 0;
+
+ delim[0] = ops->delim;
+ delim[1] = '\0';
+
+ fields = 0;
+ nf = 0;
+ fsize = 0;
+
+ field = buf = line;
+ do
+ {
+ field = strsep (&buf, delim); /* destructive */
+ if (nf == fsize)
+ {
+ fsize += 8;
+ fields = xrealloc (fields, fsize * sizeof (char *));
+ }
+ fields[nf] = field;
+ if (field)
+ nf++;
+ }
+ while (field);
+
+ if (nf == 1)
+ {
+ free (fields);
+ if (ops->flags & SFLAG)
+ return ind;
+ if (v)
+ {
+ bind_array_element (v, ind, line, 0);
+ ind++;
+ }
+ else
+ printf ("%s\n", line);
+ return ind;
+ }
+
+ bmap = xmalloc (nf + 1);
+ memset (bmap, 0, nf);
+
+ for (n = 0; n < ops->npos; n++)
+ {
+ s = ops->poslist[n].startpos; /* no translation needed yet */
+ e = ops->poslist[n].endpos;
+ if (e == NORANGE)
+ e = s;
+ else if (e == EOL || e >= nf)
+ e = nf - 1;
+ /* even if a column is specified multiple times, it will only be printed
+ once */
+ for (i = s; i <= e; i++)
+ bmap[i] = 1;
+ }
+
+ for (i = 1, b = 0; b < nf; b++)
+ {
+ if (bmap[b] == 0)
+ continue;
+ if (v)
+ {
+ bind_array_element (v, ind, fields[b], 0);
+ ind++;
+ }
+ else
+ {
+ if (i == 0)
+ putchar (ops->delim);
+ printf ("%s", fields[b]);
+ }
+ i = 0;
+ }
+ if (v == 0)
+ putchar ('\n');
+
+ return nf;
+}
+
+static int
+cutline (v, line, ops)
+ SHELL_VAR *v;
+ char *line;
+ struct cutop *ops;
+{
+ int rval;
+
+ if (ops->flags & BFLAG)
+ rval = cutbytes (v, line, ops);
+ else if (ops->flags & CFLAG)
+ rval = cutchars (v, line, ops);
+ else
+ rval = cutfields (v, line, ops);
+
+ return (rval >= 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+}
+
+static int
+cutfile (v, list, ops)
+ SHELL_VAR *v;
+ WORD_LIST *list;
+ struct cutop *ops;
+{
+ int fd, unbuffered_read;
+ char *line, *b;
+ size_t llen;
+ WORD_LIST *l;
+ ssize_t n;
+
+ line = 0;
+ llen = 0;
+
+ l = list;
+ do
+ {
+ /* for each file */
+ if (l == 0 || (l->word->word[0] == '-' && l->word->word[1] == '\0'))
+ fd = 0;
+ else
+ fd = open (l->word->word, O_RDONLY);
+ if (fd < 0)
+ {
+ file_error (l->word->word);
+ return (EXECUTION_FAILURE);
+ }
+
+#ifndef __CYGWIN__
+ unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+#else
+ unbuffered_read = 1;
+#endif
+
+ while ((n = zgetline (fd, &line, &llen, '\n', unbuffered_read)) != -1)
+ cutline (v, line, ops); /* can modify line */
+ if (fd > 0)
+ close (fd);
+
+ if (l)
+ l = l->next;
+ }
+ while (l);
+
+ free (line);
+ return EXECUTION_SUCCESS;
+}
+
+#define OPTSET(x) ((cutflags & (x)) ? 1 : 0)
+
+static int
+cut_internal (which, list)
+ int which; /* not used yet */
+ WORD_LIST *list;
+{
+ int opt, rval, cutflags, delim, npos;
+ char *array_name, *cutstring, *list_arg;
+ SHELL_VAR *v;
+ struct cutop op;
+ struct cutpos *poslist;
+
+ v = 0;
+ rval = EXECUTION_SUCCESS;
+
+ cutflags = 0;
+ array_name = 0;
+ list_arg = 0;
+ delim = '\t';
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "a:b:c:d:f:sn")) != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ array_name = list_optarg;
+ break;
+ case 'b':
+ cutflags |= BFLAG;
+ list_arg = list_optarg;
+ break;
+ case 'c':
+ cutflags |= CFLAG;
+ list_arg = list_optarg;
+ break;
+ case 'd':
+ cutflags |= DFLAG;
+ delim = list_optarg[0];
+ if (delim == 0 || list_optarg[1])
+ {
+ builtin_error ("delimiter must be a single non-null character");
+ return (EX_USAGE);
+ }
+ break;
+ case 'f':
+ cutflags |= FFLAG;
+ list_arg = list_optarg;
+ break;
+ case 'n':
+ break;
+ case 's':
+ cutflags |= SFLAG;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (array_name && (legal_identifier (array_name) == 0))
+ {
+ sh_invalidid (array_name);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (list == 0 && which == 0)
+ {
+ builtin_error ("string argument required");
+ return (EX_USAGE);
+ }
+
+ /* options are mutually exclusive and one is required */
+ if ((OPTSET (BFLAG) + OPTSET (CFLAG) + OPTSET (FFLAG)) != 1)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if ((npos = getlist (list_arg, &poslist)) < 0)
+ {
+ free (poslist);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (array_name)
+ {
+ v = find_or_make_array_variable (array_name, 1);
+ if (v == 0 || readonly_p (v) || noassign_p (v))
+ {
+ if (v && readonly_p (v))
+ err_readonly (array_name);
+ return (EXECUTION_FAILURE);
+ }
+ else if (array_p (v) == 0)
+ {
+ builtin_error ("%s: not an indexed array", array_name);
+ return (EXECUTION_FAILURE);
+ }
+ if (invisible_p (v))
+ VUNSETATTR (v, att_invisible);
+ array_flush (array_cell (v));
+ }
+
+ op.flags = cutflags;
+ op.delim = delim;
+ op.npos = npos;
+ op.poslist = poslist;
+
+ /* we implement cut as a builtin with a cutfile() function that opens each
+ filename in LIST as a filename (or `-' for stdin) and runs cutline on
+ every line in the file. */
+ if (which == 0)
+ {
+ cutstring = list->word->word;
+ if (cutstring == 0 || *cutstring == 0)
+ {
+ free (poslist);
+ return (EXECUTION_SUCCESS);
+ }
+ rval = cutline (v, cutstring, &op);
+ }
+ else
+ rval = cutfile (v, list, &op);
+
+ return (rval);
+}
+
+int
+lcut_builtin (list)
+ WORD_LIST *list;
+{
+ return (cut_internal (0, list));
+}
+
+int
+cut_builtin (list)
+ WORD_LIST *list;
+{
+ return (cut_internal (1, list));
+}
+
+char *lcut_doc[] = {
+ "Extract selected fields from a string.",
+ "",
+ "Select portions of LINE (as specified by LIST) and assign them to",
+ "elements of the indexed array ARRAY starting at index 0, or write",
+ "them to the standard output if -a is not specified.",
+ "",
+ "Items specified by LIST are either column positions or fields delimited",
+ "by a special character, and are described more completely in cut(1).",
+ "",
+ "Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
+ "field delimiter is specified by -d (default TAB). Column numbering",
+ "starts at 1.",
+ (char *)NULL
+};
+
+struct builtin lcut_struct = {
+ "lcut", /* builtin name */
+ lcut_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ lcut_doc, /* array of long documentation strings. */
+ "lcut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] line", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
+
+char *cut_doc[] = {
+ "Extract selected fields from each line of a file.",
+ "",
+ "Select portions of each line (as specified by LIST) from each FILE",
+ "and write them to the standard output. cut reads from the standard",
+ "input if no FILE arguments are specified or if a FILE argument is a",
+ "single hyphen.",
+ "",
+ "Items specified by LIST are either column positions or fields delimited",
+ "by a special character, and are described more completely in cut(1).",
+ "",
+ "Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
+ "field delimiter is specified by -d (default TAB). Column numbering",
+ "starts at 1.",
+ (char *)NULL
+};
+
+struct builtin cut_struct = {
+ "cut", /* builtin name */
+ cut_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ cut_doc, /* array of long documentation strings. */
+ "cut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] [file ...]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/dirname.c b/examples/loadables/dirname.c
new file mode 100644
index 0000000..d802ca7
--- /dev/null
+++ b/examples/loadables/dirname.c
@@ -0,0 +1,119 @@
+/* dirname - return directory portion of pathname */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+int
+dirname_builtin (list)
+ WORD_LIST *list;
+{
+ int slen;
+ char *string;
+
+ if (no_options (list))
+ return (EX_USAGE);
+ list = loptend;
+
+ if (list == 0 || list->next)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ string = list->word->word;
+ slen = strlen (string);
+
+ /* Strip trailing slashes */
+ while (slen > 0 && string[slen - 1] == '/')
+ slen--;
+
+ /* (2) If string consists entirely of slash characters, string shall be
+ set to a single slash character. In this case, skip steps (3)
+ through (8). */
+ if (slen == 0)
+ {
+ fputs ("/\n", stdout);
+ return (EXECUTION_SUCCESS);
+ }
+
+ /* (3) If there are any trailing slash characters in string, they
+ shall be removed. */
+ string[slen] = '\0';
+
+ /* (4) If there are no slash characters remaining in string, string
+ shall be set to a single period character. In this case, skip
+ steps (5) through (8).
+
+ (5) If there are any trailing nonslash characters in string,
+ they shall be removed. */
+
+ while (--slen >= 0)
+ if (string[slen] == '/')
+ break;
+
+ if (slen < 0)
+ {
+ fputs (".\n", stdout);
+ return (EXECUTION_SUCCESS);
+ }
+
+ /* (7) If there are any trailing slash characters in string, they
+ shall be removed. */
+ while (--slen >= 0)
+ if (string[slen] != '/')
+ break;
+ string[++slen] = '\0';
+
+ /* (8) If the remaining string is empty, string shall be set to a single
+ slash character. */
+ printf ("%s\n", (slen == 0) ? "/" : string);
+ return (EXECUTION_SUCCESS);
+}
+
+char *dirname_doc[] = {
+ "Display directory portion of pathname.",
+ "",
+ "The STRING is converted to the name of the directory containing",
+ "the filename corresponding to the last pathname component in STRING.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin dirname_struct = {
+ "dirname", /* builtin name */
+ dirname_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ dirname_doc, /* array of long documentation strings. */
+ "dirname string", /* usage synopsis */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/fdflags.c b/examples/loadables/fdflags.c
new file mode 100644
index 0000000..fbe5230
--- /dev/null
+++ b/examples/loadables/fdflags.c
@@ -0,0 +1,374 @@
+/* Loadable builtin to get and set file descriptor flags. */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 2017,2018,2019 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <fcntl.h>
+#include <errno.h>
+#include "bashansi.h"
+#include <stdio.h>
+
+#include "loadables.h"
+
+#ifndef FD_CLOEXEC
+# define FD_CLOEXEC 1
+#endif
+
+static const struct
+{
+ const char *name;
+ int value;
+} file_flags[] =
+{
+#ifdef O_APPEND
+ { "append", O_APPEND },
+#else
+# define O_APPEND 0
+#endif
+#ifdef O_ASYNC
+ { "async", O_ASYNC },
+#else
+# define O_ASYNC 0
+#endif
+#ifdef O_SYNC
+ { "sync", O_SYNC },
+#else
+# define O_SYNC 0
+#endif
+#ifdef O_NONBLOCK
+ { "nonblock", O_NONBLOCK },
+#else
+# define O_NONBLOCK 0
+#endif
+#ifdef O_FSYNC
+ { "fsync", O_FSYNC },
+#else
+# define O_FSYNC 0
+#endif
+#ifdef O_DSYNC
+ { "dsync", O_DSYNC },
+#else
+# define O_DSYNC 0
+#endif
+#ifdef O_RSYNC
+ { "rsync", O_RSYNC },
+#else
+# define O_RSYNC 0
+#endif
+#ifdef O_ALT_IO
+ { "altio", O_ALT_IO },
+#else
+# define O_ALT_IO 0
+#endif
+#ifdef O_DIRECT
+ { "direct", O_DIRECT },
+#else
+# define O_DIRECT 0
+#endif
+#ifdef O_NOATIME
+ { "noatime", O_NOATIME },
+#else
+# define O_NOATIME 0
+#endif
+#ifdef O_NOSIGPIPE
+ { "nosigpipe", O_NOSIGPIPE },
+#else
+# define O_NOSIGPIPE 0
+#endif
+
+#ifndef O_CLOEXEC
+# define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_FSYNC|O_DSYNC|\
+ O_RSYNC|O_ALT_IO|O_DIRECT|O_NOATIME|O_NOSIGPIPE)
+
+/* An unsed bit in the file status flags word we can use to pass around the
+ state of close-on-exec. */
+# define O_CLOEXEC ((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
+#endif
+
+#ifdef O_CLOEXEC
+ { "cloexec", O_CLOEXEC },
+#endif
+};
+
+#define N_FLAGS (sizeof (file_flags) / sizeof (file_flags[0]))
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* FIX THIS */
+static int
+getallflags ()
+{
+ int i, allflags;
+
+ for (i = allflags = 0; i < N_FLAGS; i++)
+ allflags |= file_flags[i].value;
+ return allflags;
+}
+
+static int
+getflags(int fd, int p)
+{
+ int c, f;
+ int allflags;
+
+ if ((c = fcntl(fd, F_GETFD)) == -1)
+ {
+ if (p)
+ builtin_error("can't get status for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
+
+ if ((f = fcntl(fd, F_GETFL)) == -1)
+ {
+ if (p)
+ builtin_error("Can't get flags for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
+
+ if (c)
+ f |= O_CLOEXEC;
+
+ return f & getallflags();
+}
+
+static void
+printone(int fd, int p, int verbose)
+{
+ int f;
+ size_t i;
+
+ if ((f = getflags(fd, p)) == -1)
+ return;
+
+ printf ("%d:", fd);
+
+ for (i = 0; i < N_FLAGS; i++)
+ {
+ if (f & file_flags[i].value)
+ {
+ printf ("%s%s", verbose ? "+" : "", file_flags[i].name);
+ f &= ~file_flags[i].value;
+ }
+ else if (verbose)
+ printf ( "-%s", file_flags[i].name);
+ else
+ continue;
+
+ if (f || (verbose && i != N_FLAGS - 1))
+ putchar (',');
+ }
+ printf ("\n");
+}
+
+static int
+parseflags(char *s, int *p, int *n)
+{
+ int f, *v;
+ size_t i;
+
+ f = 0;
+ *p = *n = 0;
+
+ for (s = strtok(s, ","); s; s = strtok(NULL, ","))
+ {
+ switch (*s)
+ {
+ case '+':
+ v = p;
+ s++;
+ break;
+ case '-':
+ v = n;
+ s++;
+ break;
+ default:
+ v = &f;
+ break;
+ }
+
+ for (i = 0; i < N_FLAGS; i++)
+ if (strcmp(s, file_flags[i].name) == 0)
+ {
+ *v |= file_flags[i].value;
+ break;
+ }
+ if (i == N_FLAGS)
+ builtin_error("invalid flag `%s'", s);
+ }
+
+ return f;
+}
+
+static void
+setone(int fd, char *v, int verbose)
+{
+ int f, n, pos, neg, cloexec;
+
+ f = getflags(fd, 1);
+ if (f == -1)
+ return;
+
+ parseflags(v, &pos, &neg);
+
+ cloexec = -1;
+
+ if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0)
+ cloexec = FD_CLOEXEC;
+ if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
+ cloexec = 0;
+
+ if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
+ builtin_error("can't set status for fd %d: %s", fd, strerror(errno));
+
+ pos &= ~O_CLOEXEC;
+ neg &= ~O_CLOEXEC;
+ f &= ~O_CLOEXEC;
+
+ n = f;
+ n |= pos;
+ n &= ~neg;
+
+ if (n != f && fcntl(fd, F_SETFL, n) == -1)
+ builtin_error("can't set flags for fd %d: %s", fd, strerror(errno));
+}
+
+static int
+getmaxfd ()
+{
+ int maxfd, ignore;
+
+#ifdef F_MAXFD
+ maxfd = fcntl (0, F_MAXFD);
+ if (maxfd > 0)
+ return maxfd;
+#endif
+
+ maxfd = getdtablesize ();
+ if (maxfd <= 0)
+ maxfd = HIGH_FD_MAX;
+ for (maxfd--; maxfd > 0; maxfd--)
+ if (fcntl (maxfd, F_GETFD, &ignore) != -1)
+ break;
+
+ return maxfd;
+}
+
+int
+fdflags_builtin (WORD_LIST *list)
+{
+ int opt, maxfd, i, num, verbose, setflag;
+ char *setspec;
+ WORD_LIST *l;
+ intmax_t inum;
+
+ setflag = verbose = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "s:v")) != -1)
+ {
+ switch (opt)
+ {
+ case 's':
+ setflag = 1;
+ setspec = list_optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ }
+ list = loptend;
+
+ /* Maybe we could provide some default here, but we don't yet. */
+ if (list == 0 && setflag)
+ return (EXECUTION_SUCCESS);
+
+ if (list == 0)
+ {
+ maxfd = getmaxfd ();
+ if (maxfd < 0)
+ {
+ builtin_error ("can't get max fd: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ for (i = 0; i < maxfd; i++)
+ printone (i, 0, verbose);
+ return (EXECUTION_SUCCESS);
+ }
+
+ opt = EXECUTION_SUCCESS;
+ for (l = list; l; l = l->next)
+ {
+ if (legal_number (l->word->word, &inum) == 0 || inum < 0)
+ {
+ builtin_error ("%s: invalid file descriptor", l->word->word);
+ opt = EXECUTION_FAILURE;
+ continue;
+ }
+ num = inum; /* truncate to int */
+ if (setflag)
+ setone (num, setspec, verbose);
+ else
+ printone (num, 1, verbose);
+ }
+
+ return (opt);
+}
+
+char *fdflags_doc[] =
+{
+ "Display and modify file descriptor flags.",
+ "",
+ "Display or, if the -s option is supplied, set flags for each file",
+ "descriptor supplied as an argument. If the -v option is supplied,",
+ "the display is verbose, including each settable option name in the",
+ "form of a string such as that accepted by the -s option.",
+ "",
+ "The -s option accepts a string with a list of flag names, each preceded",
+ "by a `+' (set) or `-' (unset). Those changes are applied to each file",
+ "descriptor supplied as an argument.",
+ "",
+ "If no file descriptor arguments are supplied, the displayed information",
+ "consists of the status of flags for each of the shell's open files.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. The flags must include BUILTIN_ENABLED so the
+ builtin can be used. */
+struct builtin fdflags_struct = {
+ "fdflags", /* builtin name */
+ fdflags_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ fdflags_doc, /* array of long documentation strings. */
+ "fdflags [-v] [-s flags_string] [fd ...]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c
new file mode 100644
index 0000000..8c278c3
--- /dev/null
+++ b/examples/loadables/finfo.c
@@ -0,0 +1,629 @@
+/*
+ * finfo - print file info
+ *
+ * Chet Ramey
+ * chet@po.cwru.edu
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <sys/types.h>
+#ifdef MAJOR_IN_MKDEV
+# include <sys/mkdev.h>
+#endif
+#ifdef MAJOR_IN_SYSMACROS
+# include <sys/sysmacros.h>
+#endif
+#include "posixstat.h"
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include "posixtime.h"
+
+#include "bashansi.h"
+#include "shell.h"
+#include "builtins.h"
+#include "common.h"
+#include "getopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char **make_builtin_argv ();
+
+static void perms();
+static int printst();
+static int printsome();
+static void printmode();
+static int printfinfo();
+static int finfo_main();
+
+extern int sh_optind;
+extern char *sh_optarg;
+extern char *this_command_name;
+
+static char *prog;
+static int pmask;
+
+#define OPT_UID 0x00001
+#define OPT_GID 0x00002
+#define OPT_DEV 0x00004
+#define OPT_INO 0x00008
+#define OPT_PERM 0x00010
+#define OPT_LNKNAM 0x00020
+#define OPT_FID 0x00040
+#define OPT_NLINK 0x00080
+#define OPT_RDEV 0x00100
+#define OPT_SIZE 0x00200
+#define OPT_ATIME 0x00400
+#define OPT_MTIME 0x00800
+#define OPT_CTIME 0x01000
+#define OPT_BLKSIZE 0x02000
+#define OPT_BLKS 0x04000
+#define OPT_FTYPE 0x08000
+#define OPT_PMASK 0x10000
+#define OPT_OPERM 0x20000
+
+#define OPT_ASCII 0x1000000
+
+#define OPTIONS "acdgiflmnopsuACGMP:U"
+
+static int
+octal(s)
+char *s;
+{
+ int r;
+
+ r = *s - '0';
+ while (*++s >= '0' && *s <= '7')
+ r = (r * 8) + (*s - '0');
+ return r;
+}
+
+static int
+finfo_main(argc, argv)
+int argc;
+char **argv;
+{
+ register int i;
+ int mode, flags, opt;
+
+ sh_optind = 0; /* XXX */
+ prog = base_pathname(argv[0]);
+ if (argc == 1) {
+ builtin_usage();
+ return(1);
+ }
+ flags = 0;
+ while ((opt = sh_getopt(argc, argv, OPTIONS)) != EOF) {
+ switch(opt) {
+ case 'a': flags |= OPT_ATIME; break;
+ case 'A': flags |= OPT_ATIME|OPT_ASCII; break;
+ case 'c': flags |= OPT_CTIME; break;
+ case 'C': flags |= OPT_CTIME|OPT_ASCII; break;
+ case 'd': flags |= OPT_DEV; break;
+ case 'i': flags |= OPT_INO; break;
+ case 'f': flags |= OPT_FID; break;
+ case 'g': flags |= OPT_GID; break;
+ case 'G': flags |= OPT_GID|OPT_ASCII; break;
+ case 'l': flags |= OPT_LNKNAM; break;
+ case 'm': flags |= OPT_MTIME; break;
+ case 'M': flags |= OPT_MTIME|OPT_ASCII; break;
+ case 'n': flags |= OPT_NLINK; break;
+ case 'o': flags |= OPT_OPERM; break;
+ case 'p': flags |= OPT_PERM; break;
+ case 'P': flags |= OPT_PMASK; pmask = octal(sh_optarg); break;
+ case 's': flags |= OPT_SIZE; break;
+ case 'u': flags |= OPT_UID; break;
+ case 'U': flags |= OPT_UID|OPT_ASCII; break;
+ default: builtin_usage (); return(1);
+ }
+ }
+
+ argc -= sh_optind;
+ argv += sh_optind;
+
+ if (argc == 0) {
+ builtin_usage();
+ return(1);
+ }
+
+ for (i = 0; i < argc; i++)
+ opt = flags ? printsome (argv[i], flags) : printfinfo(argv[i]);
+
+ return(opt);
+}
+
+static struct stat *
+getstat(f)
+char *f;
+{
+ static struct stat st;
+ int fd, r;
+ intmax_t lfd;
+
+ if (strncmp(f, "/dev/fd/", 8) == 0) {
+ if ((legal_number(f + 8, &lfd) == 0) || (int)lfd != lfd) {
+ builtin_error("%s: invalid fd", f + 8);
+ return ((struct stat *)0);
+ }
+ fd = lfd;
+ r = fstat(fd, &st);
+ } else
+#ifdef HAVE_LSTAT
+ r = lstat(f, &st);
+#else
+ r = stat(f, &st);
+#endif
+ if (r < 0) {
+ builtin_error("%s: cannot stat: %s", f, strerror(errno));
+ return ((struct stat *)0);
+ }
+ return (&st);
+}
+
+static int
+printfinfo(f)
+char *f;
+{
+ struct stat *st;
+
+ st = getstat(f);
+ return (st ? printst(st) : 1);
+}
+
+static int
+getperm(m)
+int m;
+{
+ return (m & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID));
+}
+
+static void
+perms(m)
+int m;
+{
+ char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
+ int i;
+
+ i = 0;
+ if (m & S_IRUSR)
+ ubits[i++] = 'r';
+ if (m & S_IWUSR)
+ ubits[i++] = 'w';
+ if (m & S_IXUSR)
+ ubits[i++] = 'x';
+ ubits[i] = '\0';
+
+ i = 0;
+ if (m & S_IRGRP)
+ gbits[i++] = 'r';
+ if (m & S_IWGRP)
+ gbits[i++] = 'w';
+ if (m & S_IXGRP)
+ gbits[i++] = 'x';
+ gbits[i] = '\0';
+
+ i = 0;
+ if (m & S_IROTH)
+ obits[i++] = 'r';
+ if (m & S_IWOTH)
+ obits[i++] = 'w';
+ if (m & S_IXOTH)
+ obits[i++] = 'x';
+ obits[i] = '\0';
+
+ if (m & S_ISUID)
+ ubits[2] = (m & S_IXUSR) ? 's' : 'S';
+ if (m & S_ISGID)
+ gbits[2] = (m & S_IXGRP) ? 's' : 'S';
+ if (m & S_ISVTX)
+ obits[2] = (m & S_IXOTH) ? 't' : 'T';
+
+ printf ("u=%s,g=%s,o=%s", ubits, gbits, obits);
+}
+
+static void
+printmode(mode)
+int mode;
+{
+ if (S_ISBLK(mode))
+ printf("S_IFBLK ");
+ if (S_ISCHR(mode))
+ printf("S_IFCHR ");
+ if (S_ISDIR(mode))
+ printf("S_IFDIR ");
+ if (S_ISREG(mode))
+ printf("S_IFREG ");
+ if (S_ISFIFO(mode))
+ printf("S_IFIFO ");
+ if (S_ISLNK(mode))
+ printf("S_IFLNK ");
+ if (S_ISSOCK(mode))
+ printf("S_IFSOCK ");
+#ifdef S_ISWHT
+ if (S_ISWHT(mode))
+ printf("S_ISWHT ");
+#endif
+ perms(getperm(mode));
+ printf("\n");
+}
+
+static int
+printst(st)
+struct stat *st;
+{
+ struct passwd *pw;
+ struct group *gr;
+ char *owner;
+ int ma, mi, d;
+
+ ma = major (st->st_rdev);
+ mi = minor (st->st_rdev);
+#if defined (makedev)
+ d = makedev (ma, mi);
+#else
+ d = st->st_rdev & 0xFF;
+#endif
+ printf("Device (major/minor): %d (%d/%d)\n", d, ma, mi);
+
+ printf("Inode: %d\n", (int) st->st_ino);
+ printf("Mode: (%o) ", (int) st->st_mode);
+ printmode((int) st->st_mode);
+ printf("Link count: %d\n", (int) st->st_nlink);
+ pw = getpwuid(st->st_uid);
+ owner = pw ? pw->pw_name : "unknown";
+ printf("Uid of owner: %d (%s)\n", (int) st->st_uid, owner);
+ gr = getgrgid(st->st_gid);
+ owner = gr ? gr->gr_name : "unknown";
+ printf("Gid of owner: %d (%s)\n", (int) st->st_gid, owner);
+ printf("Device type: %d\n", (int) st->st_rdev);
+ printf("File size: %ld\n", (long) st->st_size);
+ printf("File last access time: %s", ctime (&st->st_atime));
+ printf("File last modify time: %s", ctime (&st->st_mtime));
+ printf("File last status change time: %s", ctime (&st->st_ctime));
+ fflush(stdout);
+ return(0);
+}
+
+static int
+printsome(f, flags)
+char *f;
+int flags;
+{
+ struct stat *st;
+ struct passwd *pw;
+ struct group *gr;
+ int p;
+ char *b;
+
+ st = getstat(f);
+ if (st == NULL)
+ return (1);
+
+ /* Print requested info */
+ if (flags & OPT_ATIME) {
+ if (flags & OPT_ASCII)
+ printf("%s", ctime(&st->st_atime));
+ else
+ printf("%ld\n", st->st_atime);
+ } else if (flags & OPT_MTIME) {
+ if (flags & OPT_ASCII)
+ printf("%s", ctime(&st->st_mtime));
+ else
+ printf("%ld\n", st->st_mtime);
+ } else if (flags & OPT_CTIME) {
+ if (flags & OPT_ASCII)
+ printf("%s", ctime(&st->st_ctime));
+ else
+ printf("%ld\n", st->st_ctime);
+ } else if (flags & OPT_DEV)
+ printf("%lu\n", (unsigned long)st->st_dev);
+ else if (flags & OPT_INO)
+ printf("%lu\n", (unsigned long)st->st_ino);
+ else if (flags & OPT_FID)
+ printf("%lu:%lu\n", (unsigned long)st->st_dev, (unsigned long)st->st_ino);
+ else if (flags & OPT_NLINK)
+ printf("%lu\n", (unsigned long)st->st_nlink);
+ else if (flags & OPT_LNKNAM) {
+#ifdef S_ISLNK
+ b = xmalloc(4096);
+ p = readlink(f, b, 4096);
+ if (p >= 0 && p < 4096)
+ b[p] = '\0';
+ else {
+ p = errno;
+ strcpy(b, prog);
+ strcat(b, ": ");
+ strcat(b, strerror(p));
+ }
+ printf("%s\n", b);
+ free(b);
+#else
+ printf("%s\n", f);
+#endif
+ } else if (flags & OPT_PERM) {
+ perms(st->st_mode);
+ printf("\n");
+ } else if (flags & OPT_OPERM)
+ printf("%o\n", getperm(st->st_mode));
+ else if (flags & OPT_PMASK)
+ printf("%o\n", getperm(st->st_mode) & pmask);
+ else if (flags & OPT_UID) {
+ pw = getpwuid(st->st_uid);
+ if (flags & OPT_ASCII)
+ printf("%s\n", pw ? pw->pw_name : "unknown");
+ else
+ printf("%d\n", st->st_uid);
+ } else if (flags & OPT_GID) {
+ gr = getgrgid(st->st_gid);
+ if (flags & OPT_ASCII)
+ printf("%s\n", gr ? gr->gr_name : "unknown");
+ else
+ printf("%d\n", st->st_gid);
+ } else if (flags & OPT_SIZE)
+ printf("%ld\n", (long) st->st_size);
+
+ return (0);
+}
+
+#ifndef NOBUILTIN
+int
+finfo_builtin(list)
+ WORD_LIST *list;
+{
+ int c, r;
+ char **v;
+ WORD_LIST *l;
+
+ v = make_builtin_argv (list, &c);
+ r = finfo_main (c, v);
+ free (v);
+
+ return r;
+}
+
+static char *finfo_doc[] = {
+ "Display information about file attributes.",
+ "",
+ "Display information about each FILE. Only single operators should",
+ "be supplied. If no options are supplied, a summary of the info",
+ "available about each FILE is printed. If FILE is of the form",
+ "/dev/fd/XX, file descriptor XX is described. Operators, if supplied,",
+ "have the following meanings:",
+ "",
+ " -a last file access time",
+ " -A last file access time in ctime format",
+ " -c last file status change time",
+ " -C last file status change time in ctime format",
+ " -m last file modification time",
+ " -M last file modification time in ctime format",
+ " -d device",
+ " -i inode",
+ " -f composite file identifier (device:inode)",
+ " -g gid of owner",
+ " -G group name of owner",
+ " -l name of file pointed to by symlink",
+ " -n link count",
+ " -o permissions in octal",
+ " -p permissions in ascii",
+ " -P mask permissions ANDed with MASK (like with umask)",
+ " -s file size in bytes",
+ " -u uid of owner",
+ " -U user name of owner",
+ (char *)0
+};
+
+struct builtin finfo_struct = {
+ "finfo",
+ finfo_builtin,
+ BUILTIN_ENABLED,
+ finfo_doc,
+ "finfo [-acdgiflmnopsuACGMPU] file [file...]",
+ 0
+};
+#endif
+
+#ifdef NOBUILTIN
+#if defined (PREFER_STDARG)
+# include <stdarg.h>
+#else
+# if defined (PREFER_VARARGS)
+# include <varargs.h>
+# endif
+#endif
+
+char *this_command_name;
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ this_command_name = argv[0];
+ exit(finfo_main(argc, argv));
+}
+
+void
+builtin_usage()
+{
+ fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, prog, OPTIONS);
+}
+
+#ifndef HAVE_STRERROR
+char *
+strerror(e)
+int e;
+{
+ static char ebuf[40];
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+
+ if (e < 0 || e > sys_nerr) {
+ sprintf(ebuf,"Unknown error code %d", e);
+ return (&ebuf[0]);
+ }
+ return (sys_errlist[e]);
+}
+#endif
+
+char *
+xmalloc(s)
+size_t s;
+{
+ char *ret;
+ extern char *malloc();
+
+ ret = malloc(s);
+ if (ret)
+ return (ret);
+ fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s);
+ exit(1);
+}
+
+char *
+base_pathname(p)
+char *p;
+{
+ char *t;
+
+ if (t = strrchr(p, '/'))
+ return(++t);
+ return(p);
+}
+
+int
+legal_number (string, result)
+ char *string;
+ long *result;
+{
+ int sign;
+ long value;
+
+ sign = 1;
+ value = 0;
+
+ if (result)
+ *result = 0;
+
+ /* Skip leading whitespace characters. */
+ while (whitespace (*string))
+ string++;
+
+ if (!*string)
+ return (0);
+
+ /* We allow leading `-' or `+'. */
+ if (*string == '-' || *string == '+')
+ {
+ if (!digit (string[1]))
+ return (0);
+
+ if (*string == '-')
+ sign = -1;
+
+ string++;
+ }
+
+ while (digit (*string))
+ {
+ if (result)
+ value = (value * 10) + digit_value (*string);
+ string++;
+ }
+
+ /* Skip trailing whitespace, if any. */
+ while (whitespace (*string))
+ string++;
+
+ /* Error if not at end of string. */
+ if (*string)
+ return (0);
+
+ if (result)
+ *result = value * sign;
+
+ return (1);
+}
+
+int sh_optind;
+char *sh_optarg;
+int sh_opterr;
+
+extern int optind;
+extern char *optarg;
+
+int
+sh_getopt(c, v, o)
+int c;
+char **v, *o;
+{
+ int r;
+
+ r = getopt(c, v, o);
+ sh_optind = optind;
+ sh_optarg = optarg;
+ return r;
+}
+
+#if defined (USE_VARARGS)
+void
+#if defined (PREFER_STDARG)
+builtin_error (const char *format, ...)
+#else
+builtin_error (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+ va_list args;
+
+ if (this_command_name && *this_command_name)
+ fprintf (stderr, "%s: ", this_command_name);
+
+#if defined (PREFER_STDARG)
+ va_start (args, format);
+#else
+ va_start (args);
+#endif
+
+ vfprintf (stderr, format, args);
+ va_end (args);
+ fprintf (stderr, "\n");
+}
+#else
+void
+builtin_error (format, arg1, arg2, arg3, arg4, arg5)
+ char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
+{
+ if (this_command_name && *this_command_name)
+ fprintf (stderr, "%s: ", this_command_name);
+
+ fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+}
+#endif /* !USE_VARARGS */
+
+#endif
diff --git a/examples/loadables/head.c b/examples/loadables/head.c
new file mode 100644
index 0000000..1edca6c
--- /dev/null
+++ b/examples/loadables/head.c
@@ -0,0 +1,167 @@
+/* head - copy first part of files. */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "filecntl.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include "chartypes.h"
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+static void
+munge_list (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *l, *nl;
+ WORD_DESC *wd;
+ char *arg;
+
+ for (l = list; l; l = l->next)
+ {
+ arg = l->word->word;
+ if (arg[0] != '-' || arg[1] == '-' || (DIGIT(arg[1]) == 0))
+ return;
+ /* We have -[0-9]* */
+ wd = make_bare_word (arg+1);
+ nl = make_word_list (wd, l->next);
+ l->word->word[1] = 'n';
+ l->word->word[2] = '\0';
+ l->next = nl;
+ l = nl; /* skip over new argument */
+ }
+}
+
+static int
+file_head (fp, cnt)
+ FILE *fp;
+ int cnt;
+{
+ int ch;
+
+ while (cnt--)
+ {
+ while ((ch = getc (fp)) != EOF)
+ {
+ if (putchar (ch) == EOF)
+ {
+ builtin_error ("write error: %s", strerror (errno));
+ return EXECUTION_FAILURE;
+ }
+ if (ch == '\n')
+ break;
+ }
+ }
+ return (EXECUTION_SUCCESS);
+}
+
+int
+head_builtin (list)
+ WORD_LIST *list;
+{
+ int nline, opt, rval;
+ WORD_LIST *l;
+ FILE *fp;
+
+ char *t;
+
+ munge_list (list); /* change -num into -n num */
+
+ reset_internal_getopt ();
+ nline = 10;
+ while ((opt = internal_getopt (list, "n:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'n':
+ nline = atoi (list_optarg);
+ if (nline <= 0)
+ {
+ builtin_error ("bad line count: %s", list_optarg);
+ return (EX_USAGE);
+ }
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ return (file_head (stdin, nline));
+
+ for (rval = EXECUTION_SUCCESS, opt = 1, l = list; l; l = l->next)
+ {
+ fp = fopen (l->word->word, "r");
+ if (fp == NULL)
+ {
+ builtin_error ("%s: %s", l->word->word, strerror (errno));
+ continue;
+ }
+ if (list->next) /* more than one file */
+ {
+ printf ("%s==> %s <==\n", opt ? "" : "\n", l->word->word);
+ opt = 0;
+ }
+ rval = file_head (fp, nline);
+ fclose (fp);
+ }
+
+ return (rval);
+}
+
+char *head_doc[] = {
+ "Display lines from beginning of file.",
+ "",
+ "Copy the first N lines from the input files to the standard output.",
+ "N is supplied as an argument to the `-n' option. If N is not given,",
+ "the first ten lines are copied.",
+ (char *)NULL
+};
+
+struct builtin head_struct = {
+ "head", /* builtin name */
+ head_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ head_doc, /* array of long documentation strings. */
+ "head [-n num] [file ...]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/hello.c b/examples/loadables/hello.c
new file mode 100644
index 0000000..b09362b
--- /dev/null
+++ b/examples/loadables/hello.c
@@ -0,0 +1,96 @@
+/* Sample builtin to be dynamically loaded with enable -f and create a new
+ builtin. */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "loadables.h"
+
+/* A builtin `xxx' is normally implemented with an `xxx_builtin' function.
+ If you're converting a command that uses the normal Unix argc/argv
+ calling convention, use argv = make_builtin_argv (list, &argc) and call
+ the original `main' something like `xxx_main'. Look at cat.c for an
+ example.
+
+ Builtins should use internal_getopt to parse options. It is the same as
+ getopt(3), but it takes a WORD_LIST *. Look at print.c for an example
+ of its use.
+
+ If the builtin takes no options, call no_options(list) before doing
+ anything else. If it returns a non-zero value, your builtin should
+ immediately return EX_USAGE. Look at logname.c for an example.
+
+ A builtin command returns EXECUTION_SUCCESS for success and
+ EXECUTION_FAILURE to indicate failure. */
+int
+hello_builtin (list)
+ WORD_LIST *list;
+{
+ printf("hello world\n");
+ fflush (stdout);
+ return (EXECUTION_SUCCESS);
+}
+
+int
+hello_builtin_load (s)
+ char *s;
+{
+ printf ("hello builtin loaded\n");
+ fflush (stdout);
+ return (1);
+}
+
+void
+hello_builtin_unload (s)
+ char *s;
+{
+ printf ("hello builtin unloaded\n");
+ fflush (stdout);
+}
+
+/* An array of strings forming the `long' documentation for a builtin xxx,
+ which is printed by `help xxx'. It must end with a NULL. By convention,
+ the first line is a short description. */
+char *hello_doc[] = {
+ "Sample builtin.",
+ "",
+ "this is the long doc for the sample hello builtin",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. The flags must include BUILTIN_ENABLED so the
+ builtin can be used. */
+struct builtin hello_struct = {
+ "hello", /* builtin name */
+ hello_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ hello_doc, /* array of long documentation strings. */
+ "hello", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/id.c b/examples/loadables/id.c
new file mode 100644
index 0000000..f857b54
--- /dev/null
+++ b/examples/loadables/id.c
@@ -0,0 +1,329 @@
+/*
+ * id - POSIX.2 user identity
+ *
+ * (INCOMPLETE -- supplementary groups for other users not yet done)
+ *
+ * usage: id [-Ggu] [-nr] [user]
+ *
+ * The default output format looks something like:
+ * uid=xxx(chet) gid=xx groups=aa(aname), bb(bname), cc(cname)
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "bashtypes.h"
+#include <pwd.h>
+#include <grp.h>
+#include "bashansi.h"
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#else
+# include <sys/param.h>
+#endif
+
+#if !defined (HAVE_GETPW_DECLS)
+extern struct passwd *getpwuid ();
+#endif
+extern struct group *getgrgid ();
+
+#include "shell.h"
+#include "builtins.h"
+#include "stdc.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#define ID_ALLGROUPS 0x001 /* -G */
+#define ID_GIDONLY 0x002 /* -g */
+#define ID_USENAME 0x004 /* -n */
+#define ID_USEREAL 0x008 /* -r */
+#define ID_USERONLY 0x010 /* -u */
+
+#define ID_FLAGSET(s) ((id_flags & (s)) != 0)
+
+static int id_flags;
+
+static uid_t ruid, euid;
+static gid_t rgid, egid;
+
+static char *id_user;
+
+static int inituser ();
+
+static int id_pruser ();
+static int id_prgrp ();
+static int id_prgroups ();
+static int id_prall ();
+
+int
+id_builtin (list)
+ WORD_LIST *list;
+{
+ int opt;
+ char *user;
+
+ id_flags = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "Ggnru")) != -1)
+ {
+ switch (opt)
+ {
+ case 'G': id_flags |= ID_ALLGROUPS; break;
+ case 'g': id_flags |= ID_GIDONLY; break;
+ case 'n': id_flags |= ID_USENAME; break;
+ case 'r': id_flags |= ID_USEREAL; break;
+ case 'u': id_flags |= ID_USERONLY; break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ user = list ? list->word->word : (char *)NULL;
+
+ /* Check for some invalid option combinations */
+ opt = ID_FLAGSET (ID_ALLGROUPS) + ID_FLAGSET (ID_GIDONLY) + ID_FLAGSET (ID_USERONLY);
+ if (opt > 1 || (opt == 0 && ((id_flags & (ID_USEREAL|ID_USENAME)) != 0)))
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (list && list->next)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (inituser (user) < 0)
+ return (EXECUTION_FAILURE);
+
+ opt = 0;
+ if (id_flags & ID_USERONLY)
+ opt += id_pruser ((id_flags & ID_USEREAL) ? ruid : euid);
+ else if (id_flags & ID_GIDONLY)
+ opt += id_prgrp ((id_flags & ID_USEREAL) ? rgid : egid);
+ else if (id_flags & ID_ALLGROUPS)
+ opt += id_prgroups (user);
+ else
+ opt += id_prall (user);
+ putchar ('\n');
+ fflush (stdout);
+
+ return (opt == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+}
+
+static int
+inituser (uname)
+ char *uname;
+{
+ struct passwd *pwd;
+
+ if (uname)
+ {
+ pwd = getpwnam (uname);
+ if (pwd == 0)
+ {
+ builtin_error ("%s: no such user", uname);
+ return -1;
+ }
+ ruid = euid = pwd->pw_uid;
+ rgid = egid = pwd->pw_gid;
+ }
+ else
+ {
+ ruid = current_user.uid;
+ euid = current_user.euid;
+ rgid = current_user.gid;
+ egid = current_user.egid;
+ }
+ return 0;
+}
+
+/* Print the name or value of user ID UID. */
+static int
+id_pruser (uid)
+ int uid;
+{
+ struct passwd *pwd = NULL;
+ int r;
+
+ r = 0;
+ if (id_flags & ID_USENAME)
+ {
+ pwd = getpwuid (uid);
+ if (pwd == NULL)
+ r = 1;
+ }
+ if (pwd)
+ printf ("%s", pwd->pw_name);
+ else
+ printf ("%u", (unsigned) uid);
+
+ return r;
+}
+
+/* Print the name or value of group ID GID. */
+
+static int
+id_prgrp (gid)
+ int gid;
+{
+ struct group *grp = NULL;
+ int r;
+
+ r = 0;
+ if (id_flags & ID_USENAME)
+ {
+ grp = getgrgid (gid);
+ if (grp == NULL)
+ r = 1;
+ }
+
+ if (grp)
+ printf ("%s", grp->gr_name);
+ else
+ printf ("%u", (unsigned) gid);
+
+ return r;
+}
+
+static int
+id_prgroups (uname)
+ char *uname;
+{
+ int *glist, ng, i, r;
+
+ r = 0;
+ id_prgrp (rgid);
+ if (egid != rgid)
+ {
+ putchar (' ');
+ id_prgrp (egid);
+ }
+
+ if (uname)
+ {
+ builtin_error ("supplementary groups for other users not yet implemented");
+ glist = (int *)NULL;
+ ng = 0;
+ r = 1;
+ }
+ else
+ glist = get_group_array (&ng);
+
+ for (i = 0; i < ng; i++)
+ if (glist[i] != rgid && glist[i] != egid)
+ {
+ putchar (' ');
+ id_prgrp (glist[i]);
+ }
+
+ return r;
+}
+
+static int
+id_prall (uname)
+ char *uname;
+{
+ int r, i, ng, *glist;
+ struct passwd *pwd;
+ struct group *grp;
+
+ r = 0;
+ printf ("uid=%u", (unsigned) ruid);
+ pwd = getpwuid (ruid);
+ if (pwd == NULL)
+ r = 1;
+ else
+ printf ("(%s)", pwd->pw_name);
+
+ printf (" gid=%u", (unsigned) rgid);
+ grp = getgrgid (rgid);
+ if (grp == NULL)
+ r = 1;
+ else
+ printf ("(%s)", grp->gr_name);
+
+ if (euid != ruid)
+ {
+ printf (" euid=%u", (unsigned) euid);
+ pwd = getpwuid (euid);
+ if (pwd == NULL)
+ r = 1;
+ else
+ printf ("(%s)", pwd->pw_name);
+ }
+
+ if (egid != rgid)
+ {
+ printf (" egid=%u", (unsigned) egid);
+ grp = getgrgid (egid);
+ if (grp == NULL)
+ r = 1;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+
+ if (uname)
+ {
+ builtin_error ("supplementary groups for other users not yet implemented");
+ glist = (int *)NULL;
+ ng = 0;
+ r = 1;
+ }
+ else
+ glist = get_group_array (&ng);
+
+ if (ng > 0)
+ printf (" groups=");
+ for (i = 0; i < ng; i++)
+ {
+ if (i > 0)
+ printf (", ");
+ printf ("%u", (unsigned) glist[i]);
+ grp = getgrgid (glist[i]);
+ if (grp == NULL)
+ r = 1;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+
+ return r;
+}
+
+char *id_doc[] = {
+ "Display information about user."
+ "",
+ "Return information about user identity",
+ (char *)NULL
+};
+
+struct builtin id_struct = {
+ "id",
+ id_builtin,
+ BUILTIN_ENABLED,
+ id_doc,
+ "id [user]\n\tid -G [-n] [user]\n\tid -g [-nr] [user]\n\tid -u [-nr] [user]",
+ 0
+};
diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c
new file mode 100644
index 0000000..874e9db
--- /dev/null
+++ b/examples/loadables/ln.c
@@ -0,0 +1,236 @@
+/* ln - make links */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "posixstat.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+typedef int unix_link_syscall_t PARAMS((const char *, const char *));
+
+#define LN_SYMLINK 0x01
+#define LN_UNLINK 0x02
+#define LN_NOFOLLOW 0x04
+
+static unix_link_syscall_t *linkfn;
+static int dolink ();
+
+int
+ln_builtin (list)
+ WORD_LIST *list;
+{
+ int rval, opt, flags;
+ WORD_LIST *l;
+ char *sdir;
+ struct stat sb;
+
+ flags = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "fs")) != -1)
+ {
+ switch (opt)
+ {
+ case 'f':
+ flags |= LN_UNLINK;
+ break;
+ case 's':
+ flags |= LN_SYMLINK;
+ break;
+ case 'h':
+ case 'n':
+ flags |= LN_NOFOLLOW;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ linkfn = (flags & LN_SYMLINK) ? symlink : link;
+
+ if (list->next == 0) /* ln target, equivalent to ln target . */
+ return (dolink (list->word->word, ".", flags));
+
+ if (list->next->next == 0) /* ln target source */
+ return (dolink (list->word->word, list->next->word->word, flags));
+
+ /* ln target1 target2 ... directory */
+
+ /* find last argument: target directory, and make sure it's an existing
+ directory. */
+ for (l = list; l->next; l = l->next)
+ ;
+ sdir = l->word->word;
+
+ if (stat(sdir, &sb) < 0)
+ {
+ builtin_error ("%s", sdir);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (S_ISDIR (sb.st_mode) == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
+ rval += dolink (list->word->word, sdir, flags);
+
+ return rval;
+}
+
+static char *
+mkdirpath (dir, file)
+ char *dir, *file;
+{
+ int dlen, flen;
+ char *ret;
+
+ dlen = strlen (dir);
+ flen = strlen (file);
+
+ ret = xmalloc (2 + dlen + flen);
+
+ strcpy (ret, dir);
+ if (ret[dlen - 1] != '/')
+ ret[dlen++] = '/';
+ strcpy (ret + dlen, file);
+ return ret;
+}
+
+#if defined (HAVE_LSTAT)
+# define LSTAT lstat
+# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b)))
+#else
+# define LSTAT stat
+# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b)))
+#endif
+
+static int
+dolink (src, dst, flags)
+ char *src, *dst;
+ int flags;
+{
+ struct stat ssb, dsb;
+ int exists;
+ char *dst_path, *p;
+
+ /* If we're not doing symlinks, the source must exist and not be a
+ directory. */
+ if ((flags & LN_SYMLINK) == 0)
+ {
+ if (stat (src, &ssb) != 0)
+ {
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ if (S_ISDIR (ssb.st_mode))
+ {
+ errno = EISDIR;
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ /* If the destination is a directory, create the final filename by appending
+ the basename of the source to the destination. */
+ dst_path = 0;
+ if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
+ {
+ if ((p = strrchr (src, '/')) == 0)
+ p = src;
+ else
+ p++;
+
+ dst_path = mkdirpath (dst, p);
+ dst = dst_path;
+ }
+
+ exists = LSTAT (dst, &dsb) == 0;
+
+ /* If -f was specified, and the destination exists, unlink it. */
+ if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
+ {
+ builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
+ FREE (dst_path);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* Perform the link. */
+ if ((*linkfn) (src, dst) != 0)
+ {
+ builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
+ FREE (dst_path);
+ return (EXECUTION_FAILURE);
+ }
+
+ FREE (dst_path);
+ return (EXECUTION_SUCCESS);
+}
+
+char *ln_doc[] = {
+ "Link files.",
+ "",
+ "Create a new directory entry with the same modes as the original",
+ "file. The -f option means to unlink any existing file, permitting",
+ "the link to occur. The -s option means to create a symbolic link.",
+ "By default, ln makes hard links. Specifying -n or its synonym -h",
+ "causes ln to not resolve symlinks in the target file or directory.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin ln_struct = {
+ "ln", /* builtin name */
+ ln_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ ln_doc, /* array of long documentation strings. */
+ "ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/loadables.h b/examples/loadables/loadables.h
new file mode 100644
index 0000000..c730357
--- /dev/null
+++ b/examples/loadables/loadables.h
@@ -0,0 +1,34 @@
+/* loadables.h -- Include files needed by all loadable builtins */
+
+/* Copyright (C) 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/>.
+*/
+
+
+#ifndef __LOADABLES_H_
+#define __LOADABLES_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#endif
diff --git a/examples/loadables/logname.c b/examples/loadables/logname.c
new file mode 100644
index 0000000..27e6591
--- /dev/null
+++ b/examples/loadables/logname.c
@@ -0,0 +1,74 @@
+/* logname - print login name of current user */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+int
+logname_builtin (list)
+ WORD_LIST *list;
+{
+ char *np;
+
+ if (no_options (list))
+ return (EX_USAGE);
+
+ np = getlogin ();
+ if (np == 0)
+ {
+ builtin_error ("cannot find username: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ printf ("%s\n", np);
+ return (EXECUTION_SUCCESS);
+}
+
+char *logname_doc[] = {
+ "Display user login name.",
+ "",
+ "Write the current user's login name to the standard output",
+ "and exit. logname ignores the LOGNAME and USER variables.",
+ "logname ignores any non-option arguments.",
+ (char *)NULL
+};
+
+struct builtin logname_struct = {
+ "logname",
+ logname_builtin,
+ BUILTIN_ENABLED,
+ logname_doc,
+ "logname",
+ 0
+};
+
diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c
new file mode 100644
index 0000000..d5d3955
--- /dev/null
+++ b/examples/loadables/mkdir.c
@@ -0,0 +1,245 @@
+/* mkdir - make directories */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <errno.h>
+#include <stdio.h>
+#include "bashansi.h"
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
+
+extern int parse_symbolic_mode ();
+
+static int make_path ();
+
+static int original_umask;
+
+int
+mkdir_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, pflag, mflag, omode, rval, nmode, parent_mode;
+ char *mode;
+ WORD_LIST *l;
+
+ reset_internal_getopt ();
+ pflag = mflag = 0;
+ mode = (char *)NULL;
+ while ((opt = internal_getopt(list, "m:p")) != -1)
+ switch (opt)
+ {
+ case 'p':
+ pflag = 1;
+ break;
+ case 'm':
+ mflag = 1;
+ mode = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (mode == NULL)
+ omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */
+ else if (ISOCTAL (*mode)) /* octal number */
+ {
+ omode = read_octal (mode);
+ if (omode < 0)
+ {
+ builtin_error ("invalid file mode: %s", mode);
+ return (EXECUTION_FAILURE);
+ }
+ }
+ else /* symbolic mode */
+ {
+ /* initial bits are a=rwx; the mode argument modifies them */
+ omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (omode < 0)
+ {
+ builtin_error ("invalid file mode: %s", mode);
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ /* Make the new mode */
+ original_umask = umask (0);
+ umask (original_umask);
+
+ nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask;
+ parent_mode = nmode | (S_IWUSR|S_IXUSR); /* u+wx */
+
+ /* Adjust new mode based on mode argument */
+ nmode &= omode;
+
+ for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
+ {
+ if (pflag && make_path (l->word->word, mflag, nmode, parent_mode))
+ {
+ rval = EXECUTION_FAILURE;
+ continue;
+ }
+ else if (pflag == 0 && mkdir (l->word->word, nmode) < 0)
+ {
+ builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ }
+ }
+ return rval;
+}
+
+/* Make all the directories leading up to PATH, then create PATH. Note that
+ this changes the process's umask; make sure that all paths leading to a
+ return reset it to ORIGINAL_UMASK */
+static int
+make_path (path, user_mode, nmode, parent_mode)
+ char *path;
+ int user_mode;
+ int nmode, parent_mode;
+{
+ int oumask;
+ struct stat sb;
+ char *p, *npath;
+
+ if (stat (path, &sb) == 0)
+ {
+ if (S_ISDIR (sb.st_mode) == 0)
+ {
+ builtin_error ("`%s': file exists but is not a directory", path);
+ return 1;
+ }
+
+ if (user_mode && chmod (path, nmode))
+ {
+ builtin_error ("%s: %s", path, strerror (errno));
+ return 1;
+ }
+
+ return 0;
+ }
+
+ oumask = umask (0);
+ npath = savestring (path); /* So we can write to it. */
+
+ /* Check whether or not we need to do anything with intermediate dirs. */
+
+ /* Skip leading slashes. */
+ p = npath;
+ while (*p == '/')
+ p++;
+
+ while (p = strchr (p, '/'))
+ {
+ *p = '\0';
+ if (stat (npath, &sb) != 0)
+ {
+ if (mkdir (npath, 0))
+ {
+ builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
+ umask (original_umask);
+ free (npath);
+ return 1;
+ }
+ if (chmod (npath, parent_mode) != 0)
+ {
+ builtin_error ("cannot chmod directory `%s': %s", npath, strerror (errno));
+ umask (original_umask);
+ free (npath);
+ return 1;
+ }
+ }
+ else if (S_ISDIR (sb.st_mode) == 0)
+ {
+ builtin_error ("`%s': file exists but is not a directory", npath);
+ umask (original_umask);
+ free (npath);
+ return 1;
+ }
+
+ *p++ = '/'; /* restore slash */
+ while (*p == '/')
+ p++;
+ }
+
+ /* Create the final directory component. */
+ if (stat (npath, &sb) && mkdir (npath, nmode))
+ {
+ builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
+ umask (original_umask);
+ free (npath);
+ return 1;
+ }
+
+ umask (original_umask);
+ free (npath);
+ return 0;
+}
+
+char *mkdir_doc[] = {
+ "Create directories.",
+ "",
+ "Make directories. Create the directories named as arguments, in",
+ "the order specified, using mode rwxrwxrwx as modified by the current",
+ "umask (see `help umask'). The -m option causes the file permission",
+ "bits of the final directory to be MODE. The MODE argument may be",
+ "an octal number or a symbolic mode like that used by chmod(1). If",
+ "a symbolic mode is used, the operations are interpreted relative to",
+ "an initial mode of \"a=rwx\". The -p option causes any required",
+ "intermediate directories in PATH to be created. The directories",
+ "are created with permssion bits of rwxrwxrwx as modified by the current",
+ "umask, plus write and search permissions for the owner. mkdir",
+ "returns 0 if the directories are created successfully, and non-zero",
+ "if an error occurs.",
+ (char *)NULL
+};
+
+struct builtin mkdir_struct = {
+ "mkdir",
+ mkdir_builtin,
+ BUILTIN_ENABLED,
+ mkdir_doc,
+ "mkdir [-p] [-m mode] directory [directory ...]",
+ 0
+};
diff --git a/examples/loadables/mkfifo.c b/examples/loadables/mkfifo.c
new file mode 100644
index 0000000..2bc4e98
--- /dev/null
+++ b/examples/loadables/mkfifo.c
@@ -0,0 +1,146 @@
+/* mkfifo - make FIFOs */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <errno.h>
+#include <stdio.h>
+#include "bashansi.h"
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
+
+extern int parse_symbolic_mode ();
+
+static int original_umask;
+
+int
+mkfifo_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, mflag, omode, rval, nmode, basemode;
+ char *mode;
+ WORD_LIST *l;
+
+ mflag = 0;
+ mode = (char *)NULL;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt(list, "m:")) != -1)
+ switch (opt)
+ {
+ case 'm':
+ mflag = 1;
+ mode = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ basemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ if (mode == NULL)
+ omode = basemode;
+ else if (ISOCTAL (*mode)) /* octal number */
+ {
+ omode = read_octal (mode);
+ if (omode < 0)
+ {
+ builtin_error ("invalid file mode: %s", mode);
+ return (EXECUTION_FAILURE);
+ }
+ }
+ else /* symbolic mode */
+ {
+ /* initial bits are a=rwx; the mode argument modifies them */
+ omode = parse_symbolic_mode (mode, basemode);
+ if (omode < 0)
+ {
+ builtin_error ("invalid file mode: %s", mode);
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ /* Make the new mode */
+ original_umask = umask (0);
+ umask (original_umask);
+
+ nmode = basemode & ~original_umask;
+ /* Adjust new mode based on mode argument */
+ nmode &= omode;
+
+ for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
+ {
+ if (mkfifo (l->word->word, nmode) < 0)
+ {
+ builtin_error ("cannot create FIFO `%s': %s", l->word->word, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ }
+ }
+ return rval;
+}
+
+
+char *mkfifo_doc[] = {
+ "Create FIFOs (named pipes).",
+ "",
+ "Make FIFOs. Create the FIFOs named as arguments, in",
+ "the order specified, using mode a=rw as modified by the current",
+ "umask (see `help umask'). The -m option causes the file permission",
+ "bits of the final FIFO to be MODE. The MODE argument may be",
+ "an octal number or a symbolic mode like that used by chmod(1). If",
+ "a symbolic mode is used, the operations are interpreted relative to",
+ "an initial mode of \"a=rw\". mkfifo returns 0 if the FIFOs are",
+ "umask, plus write and search permissions for the owner. mkdir",
+ "created successfully, and non-zero if an error occurs.",
+ (char *)NULL
+};
+
+struct builtin mkfifo_struct = {
+ "mkfifo",
+ mkfifo_builtin,
+ BUILTIN_ENABLED,
+ mkfifo_doc,
+ "mkfifo [-m mode] fifo_name [fifo_name ...]",
+ 0
+};
diff --git a/examples/loadables/mktemp.c b/examples/loadables/mktemp.c
new file mode 100644
index 0000000..1f7c9ad
--- /dev/null
+++ b/examples/loadables/mktemp.c
@@ -0,0 +1,212 @@
+/* mktemp - create temporary file or directory */
+
+/*
+ Copyright (C) 2019 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "loadables.h"
+
+#define DEFAULT_PREFIX "shtmp"
+
+int
+mktemp_builtin (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *l;
+ int rval, opt, fd, mflags, base_mflags;
+ int dflag, qflag, tflag, uflag, onetime;
+ char *prefix, *varname, *filename, *template;
+ SHELL_VAR *v;
+
+ dflag = qflag = uflag = tflag = onetime = 0;
+ prefix = varname = 0;
+ rval = EXECUTION_SUCCESS;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "dqut:v:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ prefix = list_optarg;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'v':
+ varname = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (varname) /* check for validity, not readonly */
+ {
+ if (legal_identifier (varname) == 0)
+ {
+ if (qflag == 0)
+ sh_invalidid (varname);
+ return (EXECUTION_FAILURE);
+ }
+ v = find_variable (varname);
+ if (v && readonly_p (v))
+ {
+ if (qflag == 0)
+ sh_readonly (varname);
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ onetime = (list == 0); /* once through the loop, $TMPDIR/prefix.XXXXXX */
+
+ if (prefix == 0)
+ prefix = DEFAULT_PREFIX;
+ base_mflags = MT_USETMPDIR|MT_USERANDOM; /* USERANDOM not strictly needed */
+
+ while (list || onetime)
+ {
+ mflags = base_mflags;
+ onetime = 0;
+#if defined (USE_MKTEMP) && defined (USE_MKSTEMP)
+ if (list)
+ {
+ template = list->word->word;
+ mflags |= MT_TEMPLATE;
+ }
+#else
+ /* This is sub-optimal. */
+ if (list)
+ {
+ /* Treat the basename as a prefix */
+ template = strrchr (list->word->word, '/');
+ if (template)
+ template++;
+ else
+ template = list->word->word;
+ }
+#endif
+ else
+ template = prefix;
+
+ if (dflag)
+ {
+ filename = sh_mktmpdir (template, mflags);
+ if (filename == 0)
+ {
+ if (qflag == 0)
+ builtin_error ("%s: cannot create directory", template);
+ rval = EXECUTION_FAILURE;
+ }
+ else
+ {
+ if (uflag)
+ rmdir (filename);
+ printf ("%s\n", filename);
+ }
+ }
+ else /* filename */
+ {
+ fd = sh_mktmpfd (template, mflags, &filename);
+ if (fd < 0)
+ {
+ if (qflag == 0)
+ builtin_error ("%s: cannot create file", template);
+ rval = EXECUTION_FAILURE;
+ }
+ else
+ {
+ close (fd);
+ if (uflag)
+ unlink (filename);
+ printf ("%s\n", filename);
+ }
+ }
+
+ /* Assign variable if requested */
+ if (filename && varname)
+ {
+ v = builtin_bind_variable (varname, filename, 0);
+ if (v == 0 || readonly_p (v) || noassign_p (v))
+ {
+ builtin_error ("%s: cannot set variable", varname);
+ rval = EXECUTION_FAILURE;
+ }
+ }
+
+ FREE (filename);
+
+ if (list)
+ list = list->next;
+ }
+
+ return (rval);
+}
+
+char *mktemp_doc[] = {
+ "Make unique temporary file name",
+ "",
+ "Take each supplied filename template and overwrite a portion of it",
+ "to create a filename, which is unique and may be used by the calling",
+ "script. TEMPLATE is a string ending in some number of 'X's. If",
+ "TEMPLATE is not supplied, shtmp.XXXXXX is used and $TMPDIR is used as",
+ "the name of the containing directory. Files are created u+rw; directories",
+ "are created u+rwx.",
+ "",
+ "Options, if supplied, have the following meanings:",
+ "",
+ " -d Create a directory instead of a file",
+ " -q Do not print error messages about file creation failure",
+ " -t PREFIX Use PREFIX as the directory in which to create files",
+ " -u Do not create anything; simply print a name",
+ " -v VAR Store the generated name into shell variable VAR",
+ "",
+ "Any PREFIX supplied with -t is ignored if TEMPLATE is supplied.",
+ "",
+ "The return status is true if the file or directory was created successfully;",
+ "false if an error occurs or VAR is invalid or readonly.",
+
+ (char *)NULL
+};
+
+struct builtin mktemp_struct = {
+ "mktemp", /* builtin name */
+ mktemp_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ mktemp_doc, /* array of long documentation strings. */
+ "mktemp [-d] [-q] [-t prefix] [-u] [-v varname] [template] ...",
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/mypid.c b/examples/loadables/mypid.c
new file mode 100644
index 0000000..fc1b267
--- /dev/null
+++ b/examples/loadables/mypid.c
@@ -0,0 +1,89 @@
+/* This module should be dynamically loaded with enable -f
+ * which would create a new builtin named mypid. You'll need
+ * the source code for GNU bash to recompile this module.
+ *
+ * Then, from within bash, enable -f ./mypid enable_mypid, where ./mypid
+ * is the binary obtained from running make. Hereafter, `${MYPID}'
+ * is a shell builtin variable.
+ *
+ * This defines an unload hook function that is called when the builtin is
+ * deleted with enable -d that will unbind the MYPID variable so future
+ * references to it do not attempt to access memory that is no longer part
+ * of this process's address space.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "builtins.h"
+#include "shell.h"
+
+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
+ do \
+ { SHELL_VAR *v = bind_variable (var, (val), 0); \
+ if (v) \
+ { \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ } \
+ while (0)
+
+static SHELL_VAR *
+assign_mypid (
+ SHELL_VAR *self,
+ char *value,
+ arrayind_t unused,
+ char *key )
+{
+ return (self);
+}
+
+static SHELL_VAR *
+get_mypid (SHELL_VAR *var)
+{
+ int rv;
+ char *p;
+
+ rv = getpid();
+ p = itos (rv);
+
+ FREE (value_cell (var));
+
+ VSETATTR (var, att_integer);
+ var_setvalue (var, p);
+ return (var);
+}
+
+int
+enable_mypid_builtin(WORD_LIST *list)
+{
+ INIT_DYNAMIC_VAR ("MYPID", (char *)NULL, get_mypid, assign_mypid);
+
+ return 0;
+}
+
+void
+enable_mypid_builtin_unload (char *s)
+{
+ unbind_variable ("MYPID");
+}
+
+char const *enable_mypid_doc[] = {
+ "Enable $MYPID.",
+ "",
+ "Enables use of the ${MYPID} dynamic variable. ",
+ "It will yield the current pid of a subshell.",
+ (char *)0
+};
+
+struct builtin enable_mypid_struct = {
+ "enable_mypid",
+ enable_mypid_builtin,
+ BUILTIN_ENABLED,
+ (char**)(void*)enable_mypid_doc,
+ "enable_mypid N",
+ 0
+};
diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c
new file mode 100644
index 0000000..dd2092b
--- /dev/null
+++ b/examples/loadables/necho.c
@@ -0,0 +1,54 @@
+/* necho - echo without options or argument interpretation */
+
+/* Sample builtin to be dynamically loaded with enable -f and replace an
+ existing builtin. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <stdio.h>
+#include "builtins.h"
+#include "shell.h"
+
+int
+necho_builtin (list)
+WORD_LIST *list;
+{
+ print_word_list (list, " ");
+ printf("\n");
+ fflush (stdout);
+ return (EXECUTION_SUCCESS);
+}
+
+char *necho_doc[] = {
+ "Display arguments.",
+ "",
+ "Print the arguments to the standard output separated",
+ "by space characters and terminated with a newline.",
+ (char *)NULL
+};
+
+struct builtin necho_struct = {
+ "echo",
+ necho_builtin,
+ BUILTIN_ENABLED,
+ necho_doc,
+ "echo [args]",
+ 0
+};
+
diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c
new file mode 100644
index 0000000..c1151db
--- /dev/null
+++ b/examples/loadables/pathchk.c
@@ -0,0 +1,381 @@
+/* pathchk - check pathnames for validity and portability */
+
+/* Usage: pathchk [-p] path ...
+
+ For each PATH, print a message if any of these conditions are false:
+ * all existing leading directories in PATH have search (execute) permission
+ * strlen (PATH) <= PATH_MAX
+ * strlen (each_directory_in_PATH) <= NAME_MAX
+
+ Exit status:
+ 0 All PATH names passed all of the tests.
+ 1 An error occurred.
+
+ Options:
+ -p Instead of performing length checks on the
+ underlying filesystem, test the length of the
+ pathname and its components against the POSIX.1
+ minimum limits for portability, _POSIX_NAME_MAX
+ and _POSIX_PATH_MAX in 2.9.2. Also check that
+ the pathname contains no character not in the
+ portable filename character set. */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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>
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#include "bashansi.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "stdc.h"
+#include "bashgetopt.h"
+#include "maxpath.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if !defined (_POSIX_PATH_MAX)
+# define _POSIX_PATH_MAX 255
+#endif
+#if !defined (_POSIX_NAME_MAX)
+# define _POSIX_NAME_MAX 14
+#endif
+
+/* How do we get PATH_MAX? */
+#if defined (_POSIX_VERSION) && !defined (PATH_MAX)
+# define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
+#endif
+
+/* How do we get NAME_MAX? */
+#if defined (_POSIX_VERSION) && !defined (NAME_MAX)
+# define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX)
+#endif
+
+#if !defined (PATH_MAX_FOR)
+# define PATH_MAX_FOR(p) PATH_MAX
+#endif
+
+#if !defined (NAME_MAX_FOR)
+# define NAME_MAX_FOR(p) NAME_MAX
+#endif
+
+extern char *strerror ();
+
+static int validate_path ();
+
+int
+pathchk_builtin (list)
+ WORD_LIST *list;
+{
+ int retval, pflag, opt;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "p")) != -1)
+ {
+ switch (opt)
+ {
+ case 'p':
+ pflag = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ for (retval = 0; list; list = list->next)
+ retval |= validate_path (list->word->word, pflag);
+
+ return (retval ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
+
+char *pathchk_doc[] = {
+ "Check pathnames for validity.",
+ "",
+ "Check each pathname argument for validity (i.e., it may be used to",
+ "create or access a file without causing syntax errors) and portability",
+ "(i.e., no filename truncation will result). If the `-p' option is",
+ "supplied, more extensive portability checks are performed.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin pathchk_struct = {
+ "pathchk", /* builtin name */
+ pathchk_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ pathchk_doc, /* array of long documentation strings. */
+ "pathchk [-p] pathname ...", /* usage synopsis */
+ 0 /* reserved for internal use */
+};
+
+/* The remainder of this file is stolen shamelessly from `pathchk.c' in
+ the sh-utils-1.12 distribution, by
+
+ David MacKenzie <djm@gnu.ai.mit.edu>
+ and Jim Meyering <meyering@cs.utexas.edu> */
+
+/* Each element is nonzero if the corresponding ASCII character is
+ in the POSIX portable character set, and zero if it is not.
+ In addition, the entry for `/' is nonzero to simplify checking. */
+static char const portable_chars[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* If PATH contains only portable characters, return 1, else 0. */
+
+static int
+portable_chars_only (path)
+ const char *path;
+{
+ const char *p;
+
+ for (p = path; *p; ++p)
+ if (portable_chars[(const unsigned char) *p] == 0)
+ {
+ builtin_error ("path `%s' contains nonportable character `%c'", path, *p);
+ return 0;
+ }
+ return 1;
+}
+
+/* On some systems, stat can return EINTR. */
+
+#ifndef EINTR
+# define SAFE_STAT(name, buf) stat (name, buf)
+#else
+# define SAFE_STAT(name, buf) safe_stat (name, buf)
+static inline int
+safe_stat (name, buf)
+ const char *name;
+ struct stat *buf;
+{
+ int ret;
+
+ do
+ ret = stat (name, buf);
+ while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+#endif
+
+/* Return 1 if PATH is a usable leading directory, 0 if not,
+ 2 if it doesn't exist. */
+
+static int
+dir_ok (path)
+ const char *path;
+{
+ struct stat stats;
+
+ if (SAFE_STAT (path, &stats))
+ return 2;
+
+ if (!S_ISDIR (stats.st_mode))
+ {
+ builtin_error ("`%s' is not a directory", path);
+ return 0;
+ }
+
+ /* Use access to test for search permission because
+ testing permission bits of st_mode can lose with new
+ access control mechanisms. Of course, access loses if you're
+ running setuid. */
+ if (access (path, X_OK) != 0)
+ {
+ if (errno == EACCES)
+ builtin_error ("directory `%s' is not searchable", path);
+ else
+ builtin_error ("%s: %s", path, strerror (errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+static char *
+xstrdup (s)
+ char *s;
+{
+ return (savestring (s));
+}
+
+/* Make sure that
+ strlen (PATH) <= PATH_MAX
+ && strlen (each-existing-directory-in-PATH) <= NAME_MAX
+
+ If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
+ _POSIX_NAME_MAX instead, and make sure that PATH contains no
+ characters not in the POSIX portable filename character set, which
+ consists of A-Z, a-z, 0-9, ., _, -.
+
+ Make sure that all leading directories along PATH that exist have
+ `x' permission.
+
+ Return 0 if all of these tests are successful, 1 if any fail. */
+
+static int
+validate_path (path, portability)
+ char *path;
+ int portability;
+{
+ int path_max;
+ int last_elem; /* Nonzero if checking last element of path. */
+ int exists; /* 2 if the path element exists. */
+ char *slash;
+ char *parent; /* Last existing leading directory so far. */
+
+ if (portability && !portable_chars_only (path))
+ return 1;
+
+ if (*path == '\0')
+ return 0;
+
+#ifdef lint
+ /* Suppress `used before initialized' warning. */
+ exists = 0;
+#endif
+
+ /* Figure out the parent of the first element in PATH. */
+ parent = xstrdup (*path == '/' ? "/" : ".");
+
+ slash = path;
+ last_elem = 0;
+ while (1)
+ {
+ int name_max;
+ int length; /* Length of partial path being checked. */
+ char *start; /* Start of path element being checked. */
+
+ /* Find the end of this element of the path.
+ Then chop off the rest of the path after this element. */
+ while (*slash == '/')
+ slash++;
+ start = slash;
+ slash = strchr (slash, '/');
+ if (slash != NULL)
+ *slash = '\0';
+ else
+ {
+ last_elem = 1;
+ slash = strchr (start, '\0');
+ }
+
+ if (!last_elem)
+ {
+ exists = dir_ok (path);
+ if (exists == 0)
+ {
+ free (parent);
+ return 1;
+ }
+ }
+
+ length = slash - start;
+ /* Since we know that `parent' is a directory, it's ok to call
+ pathconf with it as the argument. (If `parent' isn't a directory
+ or doesn't exist, the behavior of pathconf is undefined.)
+ But if `parent' is a directory and is on a remote file system,
+ it's likely that pathconf can't give us a reasonable value
+ and will return -1. (NFS and tempfs are not POSIX . . .)
+ In that case, we have no choice but to assume the pessimal
+ POSIX minimums. */
+ name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
+ if (name_max < 0)
+ name_max = _POSIX_NAME_MAX;
+ if (length > name_max)
+ {
+ builtin_error ("name `%s' has length %d; exceeds limit of %d",
+ start, length, name_max);
+ free (parent);
+ return 1;
+ }
+
+ if (last_elem)
+ break;
+
+ if (exists == 1)
+ {
+ free (parent);
+ parent = xstrdup (path);
+ }
+
+ *slash++ = '/';
+ }
+
+ /* `parent' is now the last existing leading directory in the whole path,
+ so it's ok to call pathconf with it as the argument. */
+ path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
+ if (path_max < 0)
+ path_max = _POSIX_PATH_MAX;
+ free (parent);
+ if (strlen (path) > path_max)
+ {
+ builtin_error ("path `%s' has length %lu; exceeds limit of %d",
+ path, (unsigned long)strlen (path), path_max);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/examples/loadables/perl/Makefile.in b/examples/loadables/perl/Makefile.in
new file mode 100644
index 0000000..59f39b6
--- /dev/null
+++ b/examples/loadables/perl/Makefile.in
@@ -0,0 +1,99 @@
+#
+# Makefile for builtin perl interpreter
+#
+#
+# Copyright (C) 1998 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute 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 some boilerplate Gnu makefile definitions.
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+libdir = @libdir@
+infodir = @infodir@
+includedir = @includedir@
+
+datarootdir = @datarootdir@
+
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+@SET_MAKE@
+CC = @CC@
+RM = rm -f
+
+SHELL = @MAKE_SHELL@
+
+PERL5 = perl5
+
+CFLAGS = @CFLAGS@
+
+#
+# These values are generated for configure by ${topdir}/support/shobj-conf.
+# If your system is not supported by that script, but includes facilities for
+# dynamic loading of shared objects, please update the script and send the
+# changes to bash-maintainers@gnu.org.
+#
+SHOBJ_CC = @SHOBJ_CC@
+SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
+SHOBJ_LD = @SHOBJ_LD@
+SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@
+SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
+SHOBJ_LIBS = @SHOBJ_LIBS@
+SHOBJ_STATUS = @SHOBJ_STATUS@
+
+# Values used for compiling the perl files
+PERL_LDOPTS = `${PERL5} -MExtUtils::Embed -e ldopts`
+PERL_CFLAGS = ${CCFLAGS} `${PERL5} -MExtUtils::Embed -e ccopts`
+
+SRC = bperl.c iperl.c perlxsi.c
+OBJ = bperl.o iperl.o perlxsi.o
+
+BUILTIN = bperl5
+
+INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \
+ -I$(topdir)/include -I$(BUILD_DIR) -I$(BUILD_DIR)/lib \
+ -I$(BUILD_DIR)/builtins
+
+
+${BUILTIN}: ${OBJ}
+ ${RM} $@
+ ${SHOBJ_LD} ${SHOBJ_LDFLAGS} ${SHOBJ_XLDFLAGS} -o $@ ${OBJ} ${PERL_LDOPTS} ${SHOBJ_LIBS}
+
+bperl.o: bperl.c
+ ${RM} $@
+ $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ ${srcdir}/bperl.c
+
+iperl.o: iperl.c
+ ${RM} $@
+ $(SHOBJ_CC) ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ ${srcdir}/iperl.c
+
+perlxsi.c:
+ ${PERL5} -MExtUtils::Embed -e xsinit -- -o $@
+
+perlxsi.o: perlxsi.c
+ ${RM} $@
+ ${SHOBJ_CC} ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ perlxsi.c
+
+clean mostlyclean:
+ ${RM} ${OBJ}
+ ${RM} ${BUILTIN}
+
+distclean maintainer-clean: clean
+ ${RM} perlxsi.c
diff --git a/examples/loadables/perl/README b/examples/loadables/perl/README
new file mode 100644
index 0000000..a70a99b
--- /dev/null
+++ b/examples/loadables/perl/README
@@ -0,0 +1,6 @@
+This illustrates how to build a perl interpreter into bash. It's not
+especially useful; more a proof of concept (it provides none of the
+bash internals to the perl interpreter, for example).
+
+This *may* require adding "-rpath /path/to/perl/CORE" and -lperl options
+when compiling bash itself.
diff --git a/examples/loadables/perl/bperl.c b/examples/loadables/perl/bperl.c
new file mode 100644
index 0000000..77e3f7c
--- /dev/null
+++ b/examples/loadables/perl/bperl.c
@@ -0,0 +1,46 @@
+/*
+ * perl builtin
+ */
+#include <config.h>
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char **make_builtin_argv ();
+extern char **export_env;
+
+extern int perl_main();
+
+bperl_builtin(list)
+WORD_LIST *list;
+{
+ char **v;
+ int c, r;
+
+ v = make_builtin_argv(list, &c);
+ r = perl_main(c, v, export_env);
+ free(v);
+
+ return r;
+}
+
+char *bperl_doc[] = {
+ "An interface to a perl5 interpreter.",
+ (char *)0
+};
+
+struct builtin bperl_struct = {
+ "bperl",
+ bperl_builtin,
+ BUILTIN_ENABLED,
+ bperl_doc,
+ "bperl [perl options] [file ...]",
+ 0
+};
diff --git a/examples/loadables/perl/iperl.c b/examples/loadables/perl/iperl.c
new file mode 100644
index 0000000..92a6038
--- /dev/null
+++ b/examples/loadables/perl/iperl.c
@@ -0,0 +1,24 @@
+#include <EXTERN.h> /* from the Perl distribution */
+#include <perl.h> /* from the Perl distribution */
+
+extern void xs_init _((void));
+
+static PerlInterpreter *iperl; /*** The Perl interpreter ***/
+
+int
+perl_main(int argc, char **argv, char **env)
+{
+ int r;
+
+ iperl = perl_alloc();
+ perl_construct(iperl);
+ perl_parse(iperl, xs_init, argc, argv, (char **)NULL);
+ r = perl_run(iperl);
+
+PerlIO_flush(PerlIO_stdout());
+PerlIO_flush(PerlIO_stderr());
+
+ perl_destruct(iperl);
+ perl_free(iperl);
+ return (r);
+}
diff --git a/examples/loadables/print.c b/examples/loadables/print.c
new file mode 100644
index 0000000..0120dbf
--- /dev/null
+++ b/examples/loadables/print.c
@@ -0,0 +1,192 @@
+/*
+ * print -- loadable ksh-93 style print builtin
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "bashtypes.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "bashansi.h"
+#include "shell.h"
+#include "builtins.h"
+#include "stdc.h"
+#include "bashgetopt.h"
+#include "builtext.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+int print_builtin ();
+static int printargs ();
+
+static FILE *ofp;
+
+extern char *this_command_name;
+
+static char *print_doc[] = {
+ "Display arguments.",
+ "",
+ "Output the arguments. The -f option means to use the argument as a",
+ "format string as would be supplied to printf(1). The rest of the",
+ "options are as in ksh.",
+ (char *)NULL
+};
+
+struct builtin print_struct = {
+ "print",
+ print_builtin,
+ BUILTIN_ENABLED,
+ print_doc,
+ "print [-Rnprs] [-u unit] [-f format] [arguments]",
+ (char *)0
+};
+
+#ifndef ISOPTION
+#define ISOPTION(s, c) (s[0] == '-' && s[2] == '\0' && s[1] == c)
+#endif
+
+int
+print_builtin (list)
+ WORD_LIST *list;
+{
+ int c, r, nflag, raw, ofd, sflag;
+ intmax_t lfd;
+ char **v, *pfmt, *arg;
+ WORD_LIST *l;
+
+ nflag = raw = sflag = 0;
+ ofd = 1;
+ pfmt = 0;
+
+ reset_internal_getopt ();
+ while ((c = internal_getopt (list, "Rnprsu:f:")) != -1)
+ {
+ switch (c)
+ {
+ case 'R':
+ raw = 2;
+ loptend = lcurrent;
+ if (loptend && ISOPTION (loptend->word->word, 'n'))
+ {
+ loptend = loptend->next;
+ nflag = 1;
+ }
+ goto opt_end;
+ case 'r':
+ raw = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'p':
+ break; /* NOP */
+ case 'u':
+ if (all_digits (list_optarg) && legal_number (list_optarg, &lfd) && lfd == (int)lfd)
+ ofd = lfd;
+ else
+ {
+ for (l = list; l->next && l->next != lcurrent; l = l->next);
+ lcurrent = loptend = l;
+ goto opt_end;
+ }
+ break;
+ case 'f':
+ pfmt = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+opt_end:
+ list = loptend;
+
+ ofp = (ofd == 1) ? stdout : fdopen (dup (ofd), "w");
+
+ if (pfmt)
+ {
+ WORD_DESC *w;
+ WORD_LIST *nlist;
+
+ w = make_word (pfmt);
+ nlist = make_word_list (w, list);
+ r = printf_builtin (nlist);
+ nlist->next = (WORD_LIST *)NULL;
+ dispose_words (nlist);
+ return (r);
+ }
+
+ if (raw)
+ {
+ for (l = list; l; l = l->next)
+ {
+ fprintf (ofp, "%s", l->word->word);
+ if (l->next)
+ fprintf (ofp, " ");
+ }
+ if (nflag == 0)
+ fprintf (ofp, "\n");
+ fflush (ofp);
+ return (0);
+ }
+
+ r = printargs (list, ofp);
+ if (r && nflag == 0)
+ fprintf (ofp, "\n");
+ if (ofd != 1)
+ fclose (ofp);
+ return 0;
+}
+
+static int
+printargs (list, ofp)
+ WORD_LIST *list;
+ FILE *ofp;
+{
+ WORD_LIST *l;
+ char *ostr;
+ int sawc;
+
+ for (sawc = 0, l = list; l; l = l->next)
+ {
+ ostr = ansicstr (l->word->word, strlen (l->word->word), 0, &sawc, (int *)0);
+ fprintf (ofp, "%s", ostr);
+ free (ostr);
+ if (sawc)
+ return (0);
+ if (l->next)
+ fprintf (ofp, " ");
+ }
+ return (1);
+}
diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c
new file mode 100644
index 0000000..8c7f720
--- /dev/null
+++ b/examples/loadables/printenv.c
@@ -0,0 +1,94 @@
+/*
+ * printenv -- minimal builtin clone of BSD printenv(1).
+ *
+ * usage: printenv [varname]
+ *
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+extern char **export_env;
+
+int
+printenv_builtin (list)
+ WORD_LIST *list;
+{
+ register char **envp;
+ int opt;
+ SHELL_VAR *var;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ /* printenv */
+ if (list == 0)
+ {
+ maybe_make_export_env (); /* this allows minimal code */
+ for (envp = export_env; *envp; envp++)
+ printf ("%s\n", *envp);
+ return (EXECUTION_SUCCESS);
+ }
+
+ /* printenv varname */
+ var = find_variable (list->word->word);
+ if (var == 0 || (exported_p (var) == 0))
+ return (EXECUTION_FAILURE);
+
+ if (function_p (var))
+ print_var_function (var);
+ else
+ print_var_value (var, 0);
+
+ printf("\n");
+ return (EXECUTION_SUCCESS);
+}
+
+char *printenv_doc[] = {
+ "Display environment.",
+ "",
+ "Print names and values of environment variables",
+ (char *)NULL
+};
+
+struct builtin printenv_struct = {
+ "printenv",
+ printenv_builtin,
+ BUILTIN_ENABLED,
+ printenv_doc,
+ "printenv [varname]",
+ 0
+};
diff --git a/examples/loadables/push.c b/examples/loadables/push.c
new file mode 100644
index 0000000..b27455b
--- /dev/null
+++ b/examples/loadables/push.c
@@ -0,0 +1,117 @@
+/*
+ * push - anyone remember TOPS-20?
+ *
+ */
+
+/*
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "jobs.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern pid_t dollar_dollar_pid;
+extern int last_command_exit_value;
+
+int
+push_builtin (list)
+ WORD_LIST *list;
+{
+ pid_t pid;
+ int xstatus, opt;
+
+ xstatus = EXECUTION_SUCCESS;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ pid = make_child (savestring ("push"), 0);
+ if (pid == -1)
+ {
+ builtin_error ("cannot fork: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ else if (pid == 0)
+ {
+ /* Shell variable adjustments: $SHLVL, $$, $PPID, $! */
+ adjust_shell_level (1);
+ dollar_dollar_pid = getpid ();
+ set_ppid ();
+
+ /* Clean up job control stuff. */
+ stop_making_children ();
+ cleanup_the_pipeline ();
+ delete_all_jobs (0);
+
+ last_asynchronous_pid = NO_PID;
+
+ /* Make sure the job control code has the right values for
+ the shell's process group and tty process group, and that
+ the signals are set correctly for job control. */
+ initialize_job_control (0);
+ initialize_job_signals ();
+
+ /* And read commands until exit. */
+ reader_loop ();
+ exit_shell (last_command_exit_value);
+ }
+ else
+ {
+ stop_pipeline (0, (COMMAND *)NULL);
+ xstatus = wait_for (pid, 0);
+ return (xstatus);
+ }
+}
+
+char *push_doc[] = {
+ "Create child shell.",
+ "",
+ "Create a child that is an exact duplicate of the running shell",
+ "and wait for it to exit. The $SHLVL, $!, $$, and $PPID variables",
+ "are adjusted in the child. The return value is the exit status",
+ "of the child.",
+ (char *)NULL
+};
+
+struct builtin push_struct = {
+ "push",
+ push_builtin,
+ BUILTIN_ENABLED,
+ push_doc,
+ "push",
+ 0
+};
diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c
new file mode 100644
index 0000000..0974ac4
--- /dev/null
+++ b/examples/loadables/realpath.c
@@ -0,0 +1,145 @@
+/*
+ * realpath -- canonicalize pathnames, resolving symlinks
+ *
+ * usage: realpath [-csv] pathname [pathname...]
+ *
+ * options: -c check whether or not each resolved path exists
+ * -s no output, exit status determines whether path is valid
+ * -v produce verbose output
+ *
+ *
+ * exit status: 0 if all pathnames resolved
+ * 1 if any of the pathname arguments could not be resolved
+ *
+ *
+ * Bash loadable builtin version
+ *
+ * Chet Ramey
+ * chet@po.cwru.edu
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <maxpath.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char *sh_realpath();
+
+int
+realpath_builtin(list)
+WORD_LIST *list;
+{
+ int opt, cflag, vflag, sflag, es;
+ char *r, realbuf[PATH_MAX], *p;
+ struct stat sb;
+
+ if (list == 0) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ vflag = cflag = sflag = 0;
+ reset_internal_getopt();
+ while ((opt = internal_getopt (list, "csv")) != -1) {
+ switch (opt) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ }
+
+ list = loptend;
+
+ if (list == 0) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ for (es = EXECUTION_SUCCESS; list; list = list->next) {
+ p = list->word->word;
+ r = sh_realpath(p, realbuf);
+ if (r == 0) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: cannot resolve: %s", p, strerror(errno));
+ continue;
+ }
+ if (cflag && (stat(realbuf, &sb) < 0)) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: %s", p, strerror(errno));
+ continue;
+ }
+ if (sflag == 0) {
+ if (vflag)
+ printf ("%s -> ", p);
+ printf("%s\n", realbuf);
+ }
+ }
+ return es;
+}
+
+char *realpath_doc[] = {
+ "Display pathname in canonical form.",
+ "",
+ "Display the canonicalized version of each PATHNAME argument, resolving",
+ "symbolic links. The -c option checks whether or not each resolved name",
+ "exists. The -s option produces no output; the exit status determines the",
+ "validity of each PATHNAME. The -v option produces verbose output. The",
+ "exit status is 0 if each PATHNAME was resolved; non-zero otherwise.",
+ (char *)NULL
+};
+
+struct builtin realpath_struct = {
+ "realpath", /* builtin name */
+ realpath_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ realpath_doc, /* array of long documentation strings */
+ "realpath [-csv] pathname [pathname...]", /* usage synopsis */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/rm.c b/examples/loadables/rm.c
new file mode 100644
index 0000000..0af5493
--- /dev/null
+++ b/examples/loadables/rm.c
@@ -0,0 +1,181 @@
+/* rm - remove files and directories with -r */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <dirent.h>
+#include "builtins.h"
+#include "shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+static int rm_file(const char *fname);
+
+static int force, recursive;
+
+static int
+_remove_directory(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *dp;
+ size_t dirlen;
+ int err;
+
+ dirlen = strlen (dirname);
+ err = 0;
+
+ if ((dir = opendir(dirname)))
+ {
+ while ((dp = readdir(dir)))
+ {
+#ifdef __GNUC__
+ char fname[dirlen + 1 + strlen (dp->d_name) + 1];
+#else
+ char *fname;
+ int fnsize;
+#endif
+
+ if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
+ continue;
+
+#ifdef __GNUC__
+ snprintf(fname, sizeof (fname), "%s/%s", dirname, dp->d_name);
+#else
+ fnsize = dirlen + 1 + strlen (dp->d_name) + 1;
+ fname = xmalloc (fnsize);
+ snprintf(fname, fnsize, "%s/%s", dirname, dp->d_name);
+#endif
+
+ if (rm_file (fname) && force == 0)
+ err = 1;
+#ifndef __GNUC__
+ free (fname);
+#endif
+ }
+
+ closedir(dir);
+
+ if (err == 0 && rmdir (dirname) && force == 0)
+ err = 1;
+ }
+ else if (force == 0)
+ err = 1;
+
+ if (err)
+ builtin_error ("%s: %s", dirname, strerror (errno));
+
+ return err;
+}
+
+static int
+rm_file(const char *fname)
+{
+ if (unlink (fname) == 0)
+ return 0;
+
+ /* If FNAME is a directory glibc returns EISDIR but correct POSIX value
+ would be EPERM. If we get that error and FNAME is a directory and -r
+ was supplied, recursively remove the directory and its contents */
+ if ((errno == EISDIR || errno == EPERM) && recursive && file_isdir (fname))
+ return _remove_directory(fname);
+ else if (force)
+ return 0;
+
+ builtin_error ("%s: %s", fname, strerror (errno));
+ return 1;
+}
+
+int
+rm_builtin (list)
+ WORD_LIST *list;
+{
+ const char *name;
+ WORD_LIST *l;
+ int rval, opt;
+
+ recursive = force = 0;
+ rval = EXECUTION_SUCCESS;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "Rrfi")) != -1)
+ {
+ switch (opt)
+ {
+ case 'R':
+ case 'r':
+ recursive = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'i':
+ return (EX_DISKFALLBACK);
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ if (force == 0)
+ {
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+ return (EXECUTION_SUCCESS);
+ }
+
+ for (l = list; l; l = l->next)
+ {
+ if (rm_file(l->word->word) && force == 0)
+ rval = EXECUTION_FAILURE;
+ }
+
+ return rval;
+}
+
+char *rm_doc[] = {
+ "Remove files.",
+ "",
+ "rm removes the files specified as arguments.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin rm_struct = {
+ "rm", /* builtin name */
+ rm_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ rm_doc, /* array of long documentation strings. */
+ "rm [-rf] file ...", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/rmdir.c b/examples/loadables/rmdir.c
new file mode 100644
index 0000000..001c2bd
--- /dev/null
+++ b/examples/loadables/rmdir.c
@@ -0,0 +1,72 @@
+/* rmdir - remove directory */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+int
+rmdir_builtin (list)
+ WORD_LIST *list;
+{
+ int rval;
+ WORD_LIST *l;
+
+ if (no_options (list))
+ return (EX_USAGE);
+
+ for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
+ if (rmdir (l->word->word) < 0)
+ {
+ builtin_error ("%s: %s", l->word->word, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ }
+
+ return rval;
+}
+
+char *rmdir_doc[] = {
+ "Remove directory.",
+ "",
+ "rmdir removes the directory entry specified by each argument,",
+ "provided the directory is empty.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin rmdir_struct = {
+ "rmdir", /* builtin name */
+ rmdir_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ rmdir_doc, /* array of long documentation strings. */
+ "rmdir directory ...", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/seq.c b/examples/loadables/seq.c
new file mode 100644
index 0000000..e562407
--- /dev/null
+++ b/examples/loadables/seq.c
@@ -0,0 +1,490 @@
+/* seq - print sequence of numbers to standard output.
+ Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "bashansi.h"
+#include "loadables.h"
+#include "bashintl.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN)
+typedef long double floatmax_t;
+# define FLOATMAX_CONV "L"
+# define strtofltmax strtold
+# define FLOATMAX_FMT "%Lg"
+# define FLOATMAX_WFMT "%0.Lf"
+# define USE_LONG_DOUBLE
+#else
+typedef double floatmax_t;
+# define FLOATMAX_CONV ""
+# define strtofltmax strtod
+# define FLOATMAX_FMT "%g"
+# define FLOATMAX_WFMT "%0.f"
+#endif
+static floatmax_t getfloatmax PARAMS((const char *));
+static char *genformat PARAMS((floatmax_t, floatmax_t, floatmax_t));
+
+#define MAX(a, b) (((a) < (b))? (b) : (a))
+
+static int conversion_error = 0;
+
+/* If true print all number with equal width. */
+static int equal_width;
+
+/* The string used to separate two numbers. */
+static char const *separator;
+
+/* The string output after all numbers have been output. */
+static char const terminator[] = "\n";
+
+static char decimal_point;
+
+/* Pretty much the same as the version in builtins/printf.def */
+static floatmax_t
+getfloatmax (arg)
+ const char *arg;
+{
+ floatmax_t ret;
+ char *ep;
+
+ errno = 0;
+ ret = strtofltmax (arg, &ep);
+
+ if (*ep)
+ {
+ sh_invalidnum ((char *)arg);
+ conversion_error = 1;
+ }
+ else if (errno == ERANGE)
+ {
+ builtin_error ("warning: %s: %s", arg, strerror(ERANGE));
+ conversion_error = 1;
+ }
+
+ if (ret == -0.0)
+ ret = 0.0;
+
+ return (ret);
+}
+
+/* If FORMAT is a valid printf format for a double argument, return
+ its long double equivalent, allocated from dynamic storage. This
+ was written by Ulrich Drepper, taken from coreutils:seq.c */
+static char *
+long_double_format (char const *fmt)
+{
+ size_t i;
+ size_t length_modifier_offset;
+ int has_L;
+
+ for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
+ {
+ if (!fmt[i])
+ {
+ builtin_error ("format %s has no %% directive", fmt);
+ return 0;
+ }
+ }
+
+ i++;
+ i += strspn (fmt + i, "-+#0 '"); /* zero or more flags */
+ i += strspn (fmt + i, "0123456789"); /* optional minimum field width */
+ if (fmt[i] == '.') /* optional precision */
+ {
+ i++;
+ i += strspn (fmt + i, "0123456789");
+ }
+
+ length_modifier_offset = i; /* optional length modifier */
+ /* we could ignore an 'l' length modifier here */
+ has_L = (fmt[i] == 'L');
+ i += has_L;
+ switch (fmt[i])
+ {
+ case '\0':
+ builtin_error ("format %s ends in %%", fmt);
+ return 0;
+ case 'A':
+ case 'a':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ break;
+ default:
+ builtin_error ("format %s has unknown `%%%c' directive", fmt, fmt[i]);
+ return 0;
+ }
+ for (i++; ; i += (fmt[i] == '%') + 1)
+ if (fmt[i] == '%' && fmt[i + 1] != '%')
+ {
+ builtin_error ("format %s has too many %% directives", fmt);
+ return 0;
+ }
+ else if (fmt[i] == 0)
+ {
+ size_t format_size = i + 1;
+ char *ldfmt = xmalloc (format_size + 1);
+ memcpy (ldfmt, fmt, length_modifier_offset);
+#ifdef USE_LONG_DOUBLE
+ ldfmt[length_modifier_offset] = 'L';
+ strcpy (ldfmt + length_modifier_offset + 1,
+ fmt + length_modifier_offset + has_L);
+#else
+ strcpy (ldfmt + length_modifier_offset, fmt + length_modifier_offset);
+#endif
+ return ldfmt;
+ }
+}
+
+/* Return the number of digits following the decimal point in NUMBUF */
+static int
+getprec (numbuf)
+ const char *numbuf;
+{
+ int p;
+ char *dp;
+
+ if (dp = strchr (numbuf, decimal_point))
+ dp++; /* skip over decimal point */
+ for (p = 0; dp && *dp && ISDIGIT (*dp); dp++)
+ p++;
+ return p;
+}
+
+/* Return the default format given FIRST, INCR, and LAST. */
+static char *
+genformat (first, incr, last)
+ floatmax_t first, incr, last;
+{
+ static char buf[6 + 2 * INT_STRLEN_BOUND (int)];
+ int wfirst, wlast, width;
+ int iprec, fprec, lprec, prec;
+
+ if (equal_width == 0)
+ return (FLOATMAX_FMT);
+
+ /* OK, we have to figure out the largest number of decimal places. This is
+ a little more expensive than using the original strings. */
+ snprintf (buf, sizeof (buf), FLOATMAX_FMT, incr);
+ iprec = getprec (buf);
+
+ wfirst = snprintf (buf, sizeof (buf), FLOATMAX_FMT, first);
+ fprec = getprec (buf);
+
+ prec = MAX (fprec, iprec);
+
+ wlast = snprintf (buf, sizeof (buf), FLOATMAX_FMT, last);
+ lprec = getprec (buf);
+
+ /* increase first width by any increased precision in increment */
+ wfirst += (prec - fprec);
+
+ /* adjust last width to use precision from first/incr */
+ wlast += (prec - lprec);
+
+ if (lprec && prec == 0)
+ wlast--; /* no decimal point */
+ if (lprec == 0 && prec)
+ wlast++; /* include decimal point */
+ if (fprec == 0 && prec)
+ wfirst++; /* include decimal point */
+
+ width = MAX (wfirst, wlast);
+ if (width)
+ sprintf (buf, "%%0%d.%d%sf", width, prec, FLOATMAX_CONV);
+ else
+ sprintf (buf, "%%.%d%sf", prec, FLOATMAX_CONV);
+
+ return buf;
+}
+
+int
+print_fltseq (fmt, first, last, incr)
+ const char *fmt;
+ floatmax_t first, last, incr;
+{
+ int n;
+ floatmax_t next;
+ const char *s;
+
+ n = 0; /* iteration counter */
+ s = "";
+ for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr)
+ {
+ QUIT;
+ if (*s && fputs (s, stdout) == EOF)
+ return (sh_chkwrite (EXECUTION_FAILURE));
+ if (printf (fmt, next) < 0)
+ return (sh_chkwrite (EXECUTION_FAILURE));
+ s = separator;
+ n++;
+ }
+
+ if (n > 0 && fputs (terminator, stdout) == EOF)
+ return (sh_chkwrite (EXECUTION_FAILURE));
+ return (sh_chkwrite (EXECUTION_SUCCESS));
+}
+
+/* must be <= INT_STRLEN_BOUND(intmax_t) */
+int
+width_needed (num)
+ intmax_t num;
+{
+ int ret;
+
+ ret = num < 0; /* sign */
+ if (ret)
+ num = -num;
+ do
+ ret++;
+ while (num /= 10);
+ return ret;
+}
+
+int
+print_intseq (ifirst, ilast, iincr)
+ intmax_t ifirst, ilast, iincr;
+{
+ char intwfmt[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX)];
+ const char *s;
+ intmax_t i, next;
+
+ /* compute integer format string */
+ if (equal_width) /* -w supplied */
+ {
+ int wfirst, wlast, width;
+
+ wfirst = width_needed (ifirst);
+ wlast = width_needed (ilast);
+ width = MAX(wfirst, wlast);
+
+ /* The leading %s is for the separator */
+ snprintf (intwfmt, sizeof (intwfmt), "%%s%%0%u" PRIdMAX, width);
+ }
+
+ /* We could use braces.c:mkseq here but that allocates lots of memory */
+ s = "";
+ for (i = ifirst; (ifirst <= ilast) ? (i <= ilast) : (i >= ilast); i = next)
+ {
+ QUIT;
+ /* The leading %s is for the separator */
+ if (printf (equal_width ? intwfmt : "%s%" PRIdMAX, s, i) < 0)
+ return (sh_chkwrite (EXECUTION_FAILURE));
+ s = separator;
+ next = i + iincr;
+ }
+
+ if (fputs (terminator, stdout) == EOF)
+ return (sh_chkwrite (EXECUTION_FAILURE));
+ return (sh_chkwrite (EXECUTION_SUCCESS));
+}
+
+int
+seq_builtin (list)
+ WORD_LIST *list;
+{
+ floatmax_t first, last, incr;
+ intmax_t ifirst, ilast, iincr;
+ WORD_LIST *l;
+ int opt, nargs, intseq, freefmt;
+ char *first_str, *incr_str, *last_str;
+ char const *fmtstr; /* The printf(3) format used for output. */
+
+ equal_width = 0;
+ separator = "\n";
+ fmtstr = NULL;
+
+ first = 1.0;
+ last = 0.0;
+ incr = 0.0; /* set later */
+ ifirst = ilast = iincr = 0;
+ first_str = incr_str = last_str = 0;
+
+ intseq = freefmt = 0;
+ opt = 0;
+
+ reset_internal_getopt ();
+ while (opt != -1)
+ {
+ l = lcurrent ? lcurrent : list;
+ if (l && l->word && l->word->word && l->word->word[0] == '-' &&
+ (l->word->word[1] == '.' || DIGIT (l->word->word[1])))
+ {
+ loptend = l;
+ break; /* negative number */
+ }
+ if ((opt = internal_getopt (list, "f:s:w")) == -1)
+ break;
+
+ switch (opt)
+ {
+ case 'f':
+ fmtstr = list_optarg;
+ break;
+ case 's':
+ separator = list_optarg;
+ break;
+ case 'w':
+ equal_width = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+
+ for (nargs = 1, l = list; l->next; l = l->next)
+ nargs++;
+ if (nargs > 3)
+ {
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+
+ /* LAST */
+ conversion_error = 0;
+ last = getfloatmax (last_str = l->word->word);
+ if (conversion_error)
+ return (EXECUTION_FAILURE);
+
+ /* FIRST LAST */
+ if (nargs > 1)
+ {
+ conversion_error = 0;
+ first = getfloatmax (first_str = list->word->word);
+ if (conversion_error)
+ return (EXECUTION_FAILURE);
+ }
+
+ /* FIRST INCR LAST */
+ if (nargs > 2)
+ {
+ conversion_error = 0;
+ incr = getfloatmax (incr_str = list->next->word->word);
+ if (conversion_error)
+ return (EXECUTION_FAILURE);
+ if (incr == 0.0)
+ {
+ builtin_error ("zero %screment", (first < last) ? "in" : "de");
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ /* Sanitize arguments */
+ if (incr == 0.0)
+ incr = (first <= last) ? 1.0 : -1.0;
+ if ((incr < 0.0 && first < last) || (incr > 0 && first > last))
+ {
+ builtin_error ("incorrect %screment", (first < last) ? "in" : "de");
+ return (EXECUTION_FAILURE);
+ }
+
+ /* validate format here */
+ if (fmtstr)
+ {
+ fmtstr = long_double_format (fmtstr);
+ freefmt = 1;
+ if (fmtstr == 0)
+ return (EXECUTION_FAILURE);
+ }
+
+ if (fmtstr != NULL && equal_width)
+ {
+ builtin_warning ("-w ignored when the format string is specified");
+ equal_width = 0;
+ }
+
+ /* Placeholder for later additional conditions */
+ if (last_str && all_digits (last_str) &&
+ (first_str == 0 || all_digits (first_str)) &&
+ (incr_str == 0 || all_digits (incr_str)) &&
+ fmtstr == NULL)
+ intseq = 1;
+
+ if (intseq)
+ {
+ ifirst = (intmax_t)first; /* truncation */
+ ilast = (intmax_t)last;
+ iincr = (intmax_t)incr;
+
+ return (print_intseq (ifirst, ilast, iincr));
+ }
+
+ decimal_point = locale_decpoint ();
+ if (fmtstr == NULL)
+ fmtstr = genformat (first, incr, last);
+
+ print_fltseq (fmtstr, first, last, incr);
+
+ if (freefmt)
+ free ((void *)fmtstr);
+ return sh_chkwrite (EXECUTION_SUCCESS);
+}
+
+/* Taken largely from GNU seq. */
+char *seq_doc[] = {
+ "Print numbers from FIRST to LAST, in steps of INCREMENT.",
+ "",
+ "-f FORMAT use printf style floating-point FORMAT",
+ "-s STRING use STRING to separate numbers (default: \\n)",
+ "-w equalize width by padding with leading zeroes",
+ "",
+ "If FIRST or INCREMENT is omitted, it defaults to 1. However, an",
+ "omitted INCREMENT defaults to -1 when LAST is smaller than FIRST.",
+ "The sequence of numbers ends when the sum of the current number and",
+ "INCREMENT would become greater than LAST.",
+ "FIRST, INCREMENT, and LAST are interpreted as floating point values.",
+ "",
+ "FORMAT must be suitable for printing one argument of type 'double';",
+ "it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point",
+ "decimal numbers with maximum precision PREC, and to %g otherwise.",
+ (char *)NULL
+};
+
+struct builtin seq_struct = {
+ "seq",
+ seq_builtin,
+ BUILTIN_ENABLED,
+ seq_doc,
+ "seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST",
+ 0
+};
diff --git a/examples/loadables/setpgid.c b/examples/loadables/setpgid.c
new file mode 100644
index 0000000..e56fd51
--- /dev/null
+++ b/examples/loadables/setpgid.c
@@ -0,0 +1,121 @@
+/* setpgid.c: bash loadable wrapper for setpgid system call
+
+ An example of how to wrap a system call with a loadable builtin.
+
+ Originally contributed by Jason Vas Dias <jason.vas.dias@gmail.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/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+#include "bashtypes.h"
+#include "posixtime.h"
+
+#include <stdio.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "common.h"
+
+#include "bashgetopt.h"
+
+#if !defined (_POSIX_VERSION)
+# define setpgid(pid, pgrp) setpgrp (pid, pgrp)
+#endif
+
+int
+setpgid_builtin (list)
+ WORD_LIST *list;
+{
+ register WORD_LIST *wl;
+ intmax_t pid_arg, pgid_arg;
+ pid_t pid, pgid;
+ char *pidstr, *pgidstr;
+
+ wl = list;
+ pid = pgid = 0;
+
+ if (wl == 0 || wl->next == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ pidstr = wl->word ? wl->word->word : 0;
+ pgidstr = wl->next->word ? wl->next->word->word : 0;
+
+ if (pidstr == 0 || pgidstr == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (legal_number (pidstr, &pid_arg) == 0)
+ {
+ builtin_error ("%s: pid argument must be numeric", pidstr);
+ return (EXECUTION_FAILURE);
+ }
+ if (pid_arg < 0)
+ {
+ builtin_error("%s: negative pid values not allowed", pidstr);
+ return (EXECUTION_FAILURE);
+ }
+ pid = pid_arg;
+
+ if (legal_number (pgidstr, &pgid_arg) == 0)
+ {
+ builtin_error ("%s: pgrp argument must be numeric", pgidstr);
+ return (EXECUTION_FAILURE);
+ }
+ if (pgid_arg < 0)
+ {
+ builtin_error ("%s: negative pgrp values not allowed", pgidstr);
+ return (EXECUTION_FAILURE);
+ }
+ pgid = pgid_arg;
+
+ errno = 0;
+ if (setpgid(pid, pgid) < 0)
+ {
+ builtin_error("setpgid failed: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ return (EXECUTION_SUCCESS);
+}
+
+const char *setpgid_doc[] = {
+ "invoke the setpgid(2) system call",
+ "",
+ "Arguments:",
+ " pid : numeric process identifier, >= 0",
+ " pgrpid: numeric process group identifier, >=0",
+ "See the setpgid(2) manual page.",
+ (const char *)NULL
+};
+
+struct builtin setpgid_struct = {
+ "setpgid",
+ setpgid_builtin,
+ BUILTIN_ENABLED,
+ (char **)setpgid_doc,
+ "setpgid pid pgrpid",
+ 0
+};
diff --git a/examples/loadables/sleep.c b/examples/loadables/sleep.c
new file mode 100644
index 0000000..fc2203d
--- /dev/null
+++ b/examples/loadables/sleep.c
@@ -0,0 +1,101 @@
+/*
+ * sleep -- sleep for fractions of a second
+ *
+ * usage: sleep seconds[.fraction]
+ */
+
+/*
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 (TIME_WITH_SYS_TIME)
+# include <sys/time.h>
+# include <time.h>
+#else
+# if defined (HAVE_SYS_TIME_H)
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+
+#include "shell.h"
+#include "builtins.h"
+#include "common.h"
+
+int
+sleep_builtin (list)
+WORD_LIST *list;
+{
+ long sec, usec;
+ char *ep;
+ int r, mul;
+ time_t t;
+
+ if (list == 0) {
+ builtin_usage();
+ return(EX_USAGE);
+ }
+
+ /* Skip over `--' */
+ if (list->word && ISOPTION (list->word->word, '-'))
+ list = list->next;
+
+ if (*list->word->word == '-' || list->next) {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ r = uconvert(list->word->word, &sec, &usec, &ep);
+ /* Maybe postprocess conversion failures here based on EP */
+
+ if (r) {
+ fsleep(sec, usec);
+ QUIT;
+ return(EXECUTION_SUCCESS);
+ }
+
+ builtin_error("%s: bad sleep interval", list->word->word);
+ return (EXECUTION_FAILURE);
+}
+
+static char *sleep_doc[] = {
+ "Suspend execution for specified period.",
+ ""
+ "sleep suspends execution for a minimum of SECONDS[.FRACTION] seconds.",
+ (char *)NULL
+};
+
+struct builtin sleep_struct = {
+ "sleep",
+ sleep_builtin,
+ BUILTIN_ENABLED,
+ sleep_doc,
+ "sleep seconds[.fraction]",
+ 0
+};
diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c
new file mode 100644
index 0000000..52b9580
--- /dev/null
+++ b/examples/loadables/stat.c
@@ -0,0 +1,430 @@
+/* stat - load up an associative array with stat information about a file */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 <sys/types.h>
+#include "posixstat.h"
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include "posixtime.h"
+
+#include "bashansi.h"
+#include "shell.h"
+#include "builtins.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+#define ST_NAME 0
+#define ST_DEV 1
+#define ST_INO 2
+#define ST_MODE 3
+#define ST_NLINK 4
+#define ST_UID 5
+#define ST_GID 6
+#define ST_RDEV 7
+#define ST_SIZE 8
+#define ST_ATIME 9
+#define ST_MTIME 10
+#define ST_CTIME 11
+#define ST_BLKSIZE 12
+#define ST_BLOCKS 13
+#define ST_CHASELINK 14
+#define ST_PERMS 15
+
+#define ST_END 16
+
+static char *arraysubs[] =
+ {
+ "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
+ "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
+ 0
+ };
+
+static int
+getstat (fname, flags, sp)
+ const char *fname;
+ int flags;
+ struct stat *sp;
+{
+ intmax_t lfd;
+ int fd, r;
+
+ if (strncmp (fname, "/dev/fd/", 8) == 0)
+ {
+ if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ fd = lfd;
+ r = fstat(fd, sp);
+ }
+#ifdef HAVE_LSTAT
+ else if (flags & 1)
+ r = lstat(fname, sp);
+#endif
+ else
+ r = stat(fname, sp);
+
+ return r;
+}
+
+static char *
+statlink (fname, sp)
+ char *fname;
+ struct stat *sp;
+{
+#if defined (HAVE_READLINK)
+ char linkbuf[PATH_MAX];
+ int n;
+
+ if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
+ {
+ linkbuf[n] = '\0';
+ return (savestring (linkbuf));
+ }
+ else
+#endif
+ return (savestring (fname));
+}
+
+static char *
+octalperms (m)
+ int m;
+{
+ int operms;
+ char *ret;
+
+ operms = 0;
+
+ if (m & S_IRUSR)
+ operms |= 0400;
+ if (m & S_IWUSR)
+ operms |= 0200;
+ if (m & S_IXUSR)
+ operms |= 0100;
+
+ if (m & S_IRGRP)
+ operms |= 0040;
+ if (m & S_IWGRP)
+ operms |= 0020;
+ if (m & S_IXGRP)
+ operms |= 0010;
+
+ if (m & S_IROTH)
+ operms |= 0004;
+ if (m & S_IWOTH)
+ operms |= 0002;
+ if (m & S_IXOTH)
+ operms |= 0001;
+
+ if (m & S_ISUID)
+ operms |= 04000;
+ if (m & S_ISGID)
+ operms |= 02000;
+ if (m & S_ISVTX)
+ operms |= 01000;
+
+ ret = (char *)xmalloc (16);
+ snprintf (ret, 16, "%04o", operms);
+ return ret;
+}
+
+static char *
+statperms (m)
+ int m;
+{
+ char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
+ int i;
+ char *ret;
+
+ i = 0;
+ if (m & S_IRUSR)
+ ubits[i++] = 'r';
+ if (m & S_IWUSR)
+ ubits[i++] = 'w';
+ if (m & S_IXUSR)
+ ubits[i++] = 'x';
+ ubits[i] = '\0';
+
+ i = 0;
+ if (m & S_IRGRP)
+ gbits[i++] = 'r';
+ if (m & S_IWGRP)
+ gbits[i++] = 'w';
+ if (m & S_IXGRP)
+ gbits[i++] = 'x';
+ gbits[i] = '\0';
+
+ i = 0;
+ if (m & S_IROTH)
+ obits[i++] = 'r';
+ if (m & S_IWOTH)
+ obits[i++] = 'w';
+ if (m & S_IXOTH)
+ obits[i++] = 'x';
+ obits[i] = '\0';
+
+ if (m & S_ISUID)
+ ubits[2] = (m & S_IXUSR) ? 's' : 'S';
+ if (m & S_ISGID)
+ gbits[2] = (m & S_IXGRP) ? 's' : 'S';
+ if (m & S_ISVTX)
+ obits[2] = (m & S_IXOTH) ? 't' : 'T';
+
+ ret = (char *)xmalloc (32);
+ snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
+ return ret;
+}
+
+static char *
+statmode(mode)
+ int mode;
+{
+ char *modestr, *m;
+
+ modestr = m = (char *)xmalloc (8);
+ if (S_ISBLK (mode))
+ *m++ = 'b';
+ if (S_ISCHR (mode))
+ *m++ = 'c';
+ if (S_ISDIR (mode))
+ *m++ = 'd';
+ if (S_ISREG(mode))
+ *m++ = '-';
+ if (S_ISFIFO(mode))
+ *m++ = 'p';
+ if (S_ISLNK(mode))
+ *m++ = 'l';
+ if (S_ISSOCK(mode))
+ *m++ = 's';
+
+#ifdef S_ISDOOR
+ if (S_ISDOOR (mode))
+ *m++ = 'D';
+#endif
+#ifdef S_ISWHT
+ if (S_ISWHT(mode))
+ *m++ = 'W';
+#endif
+#ifdef S_ISNWK
+ if (S_ISNWK(mode))
+ *m++ = 'n';
+#endif
+#ifdef S_ISMPC
+ if (S_ISMPC (mode))
+ *m++ = 'm';
+#endif
+
+ *m = '\0';
+ return (modestr);
+}
+
+static char *
+stattime (t)
+ time_t t;
+{
+ char *tbuf, *ret;
+ size_t tlen;
+
+ tbuf = ctime (&t);
+ tlen = strlen (tbuf);
+ ret = savestring (tbuf);
+ ret[tlen-1] = '\0';
+ return ret;
+}
+
+static char *
+statval (which, fname, flags, sp)
+ int which;
+ char *fname;
+ int flags;
+ struct stat *sp;
+{
+ int temp;
+
+ switch (which)
+ {
+ case ST_NAME:
+ return savestring (fname);
+ case ST_DEV:
+ return itos (sp->st_dev);
+ case ST_INO:
+ return itos (sp->st_ino);
+ case ST_MODE:
+ return (statmode (sp->st_mode));
+ case ST_NLINK:
+ return itos (sp->st_nlink);
+ case ST_UID:
+ return itos (sp->st_uid);
+ case ST_GID:
+ return itos (sp->st_gid);
+ case ST_RDEV:
+ return itos (sp->st_rdev);
+ case ST_SIZE:
+ return itos (sp->st_size);
+ case ST_ATIME:
+ return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime));
+ case ST_MTIME:
+ return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime));
+ case ST_CTIME:
+ return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime));
+ case ST_BLKSIZE:
+ return itos (sp->st_blksize);
+ case ST_BLOCKS:
+ return itos (sp->st_blocks);
+ case ST_CHASELINK:
+ return (statlink (fname, sp));
+ case ST_PERMS:
+ temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
+ return (flags & 2) ? statperms (temp) : octalperms (temp);
+ default:
+ return savestring ("42");
+ }
+}
+
+static int
+loadstat (vname, var, fname, flags, sp)
+ char *vname;
+ SHELL_VAR *var;
+ char *fname;
+ int flags;
+ struct stat *sp;
+{
+ int i;
+ char *key, *value;
+ SHELL_VAR *v;
+
+ for (i = 0; arraysubs[i]; i++)
+ {
+ key = savestring (arraysubs[i]);
+ value = statval (i, fname, flags, sp);
+ v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
+ }
+ return 0;
+}
+
+int
+stat_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, flags;
+ char *aname, *fname;
+ struct stat st;
+ SHELL_VAR *v;
+
+ aname = "STAT";
+ flags = 0;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "A:Ll")) != -1)
+ {
+ switch (opt)
+ {
+ case 'A':
+ aname = list_optarg;
+ break;
+ case 'L':
+ flags |= 1; /* operate on links rather than resolving them */
+ break;
+ case 'l':
+ flags |= 2;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+ list = loptend;
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ fname = list->word->word;
+
+ if (getstat (fname, flags, &st) < 0)
+ {
+ builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+
+ unbind_variable (aname);
+ v = make_new_assoc_variable (aname);
+ if (v == 0)
+ {
+ builtin_error ("%s: cannot create variable", aname);
+ return (EXECUTION_FAILURE);
+ }
+ if (loadstat (aname, v, fname, flags, &st) < 0)
+ {
+ builtin_error ("%s: cannot assign file status information", aname);
+ unbind_variable (aname);
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+/* An array of strings forming the `long' documentation for a builtin xxx,
+ which is printed by `help xxx'. It must end with a NULL. By convention,
+ the first line is a short description. */
+char *stat_doc[] = {
+ "Load an associative array with file status information.",
+ "",
+ "Take a filename and load the status information returned by a",
+ "stat(2) call on that file into the associative array specified",
+ "by the -A option. The default array name is STAT. If the -L",
+ "option is supplied, stat does not resolve symbolic links and",
+ "reports information about the link itself. The -l option results",
+ "in longer-form listings for some of the fields. The exit status is 0",
+ "unless the stat fails or assigning the array is unsuccessful.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. The flags must include BUILTIN_ENABLED so the
+ builtin can be used. */
+struct builtin stat_struct = {
+ "stat", /* builtin name */
+ stat_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ stat_doc, /* array of long documentation strings. */
+ "stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/strftime.c b/examples/loadables/strftime.c
new file mode 100644
index 0000000..f4e194e
--- /dev/null
+++ b/examples/loadables/strftime.c
@@ -0,0 +1,128 @@
+/* strftime - loadable builtin interface to strftime(3) */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "bashtypes.h"
+#include "posixtime.h"
+
+#include <stdio.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+int
+strftime_builtin (list)
+ WORD_LIST *list;
+{
+ char *format, *tbuf;
+ size_t tbsize, tsize;
+ time_t secs;
+ struct tm *t;
+ int n;
+ intmax_t i;
+
+ if (no_options (list))
+ return (EX_USAGE);
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ format = list->word->word;
+ if (format == 0 || *format == 0)
+ {
+ printf ("\n");
+ return (EXECUTION_SUCCESS);
+ }
+
+ list = list->next;
+
+ if (list && list->word->word)
+ {
+ n = legal_number (list->word->word, &i);
+ if (n == 0 || i < 0 || i != (time_t)i)
+ {
+ sh_invalidnum (list->word->word);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ secs = i;
+ }
+ else
+ secs = NOW;
+
+ t = localtime (&secs);
+
+ tbsize = strlen (format) * 4;
+ tbuf = 0;
+
+ /* Now try to figure out how big the buffer should really be. strftime(3)
+ will return the number of bytes placed in the buffer unless it's greater
+ than MAXSIZE, in which case it returns 0. */
+ for (n = 1; n <= 8; n++)
+ {
+ tbuf = xrealloc (tbuf, tbsize * n);
+ tsize = strftime (tbuf, tbsize * n, format, t);
+ if (tsize)
+ break;
+ }
+
+ if (tsize)
+ printf ("%s\n", tbuf);
+ free (tbuf);
+
+ return (EXECUTION_SUCCESS);
+}
+
+/* An array of strings forming the `long' documentation for a builtin xxx,
+ which is printed by `help xxx'. It must end with a NULL. */
+char *strftime_doc[] = {
+ "Display formatted time.",
+ "",
+ "Converts date and time format to a string and displays it on the",
+ "standard output. If the optional second argument is supplied, it",
+ "is used as the number of seconds since the epoch to use in the",
+ "conversion, otherwise the current time is used.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. The flags must include BUILTIN_ENABLED so the
+ builtin can be used. */
+struct builtin strftime_struct = {
+ "strftime", /* builtin name */
+ strftime_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ strftime_doc, /* array of long documentation strings. */
+ "strftime format [seconds]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/sync.c b/examples/loadables/sync.c
new file mode 100644
index 0000000..4fbeee1
--- /dev/null
+++ b/examples/loadables/sync.c
@@ -0,0 +1,53 @@
+/* sync - sync the disks by forcing pending filesystem writes to complete */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+
+int
+sync_builtin (list)
+ WORD_LIST *list;
+{
+ sync();
+ return (EXECUTION_SUCCESS);
+}
+
+char *sync_doc[] = {
+ "Sync disks.",
+ ""
+ "Force completion of pending disk writes",
+ (char *)NULL
+};
+
+struct builtin sync_struct = {
+ "sync", /* builtin name */
+ sync_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ sync_doc, /* array of long documentation strings. */
+ "sync", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/tee.c b/examples/loadables/tee.c
new file mode 100644
index 0000000..283f23f
--- /dev/null
+++ b/examples/loadables/tee.c
@@ -0,0 +1,180 @@
+/* tee - duplicate standard input */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "filecntl.h"
+
+#include <signal.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+typedef struct flist {
+ struct flist *next;
+ int fd;
+ char *fname;
+} FLIST;
+
+static FLIST *tee_flist;
+
+#define TEE_BUFSIZE 8192
+
+extern int interrupt_immediately;
+
+extern char *strerror ();
+
+int
+tee_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, append, nointr, rval, fd, fflags;
+ int n, nr, nw;
+ FLIST *fl;
+ char *buf, *bp;
+
+ char *t;
+
+ reset_internal_getopt ();
+ append = nointr = 0;
+ tee_flist = (FLIST *)NULL;
+ while ((opt = internal_getopt (list, "ai")) != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ append = 1;
+ break;
+ case 'i':
+ nointr = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (nointr == 0)
+ interrupt_immediately++;
+
+ buf = xmalloc (TEE_BUFSIZE);
+
+ /* Initialize output file list. */
+ fl = tee_flist = (FLIST *)xmalloc (sizeof(FLIST));
+ tee_flist->fd = 1;
+ tee_flist->fname = "stdout";
+ tee_flist->next = (FLIST *)NULL;
+
+ /* Add file arguments to list of output files. */
+ fflags = append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC;
+ for (rval = EXECUTION_SUCCESS; list; list = list->next)
+ {
+ fd = open (list->word->word, fflags, 0666);
+ if (fd < 0)
+ {
+ builtin_error ("%s: cannot open: %s", list->word->word, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ }
+ else
+ {
+ fl->next = (FLIST *)xmalloc (sizeof(FLIST));
+ fl->next->fd = fd;
+ fl->next->fname = list->word->word;
+ fl = fl->next;
+ fl->next = (FLIST *)NULL;
+ }
+ }
+
+ while ((nr = read(0, buf, TEE_BUFSIZE)) > 0)
+ for (fl = tee_flist; fl; fl = fl->next)
+ {
+ n = nr;
+ bp = buf;
+ do
+ {
+ if ((nw = write (fl->fd, bp, n)) == -1)
+ {
+ builtin_error ("%s: write error: %s", fl->fname, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ break;
+ }
+ bp += nw;
+ }
+ while (n -= nw);
+ }
+ if (nr < 0)
+ builtin_error ("read error: %s", strerror (errno));
+
+ /* Deallocate resources -- this is a builtin command. */
+ tee_flist = tee_flist->next; /* skip bogus close of stdout */
+ while (tee_flist)
+ {
+ fl = tee_flist;
+ if (close (fl->fd) < 0)
+ {
+ builtin_error ("%s: close_error: %s", fl->fname, strerror (errno));
+ rval = EXECUTION_FAILURE;
+ }
+ tee_flist = tee_flist->next;
+ free (fl);
+ }
+
+ return (rval);
+}
+
+char *tee_doc[] = {
+ "Duplicate standard output.",
+ "",
+ "Copy standard input to standard output, making a copy in each",
+ "filename argument. If the `-a' option is given, the specified",
+ "files are appended to, otherwise they are overwritten. If the",
+ "`-i' option is supplied, tee ignores interrupts.",
+ (char *)NULL
+};
+
+struct builtin tee_struct = {
+ "tee", /* builtin name */
+ tee_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ tee_doc, /* array of long documentation strings. */
+ "tee [-ai] [file ...]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/template.c b/examples/loadables/template.c
new file mode 100644
index 0000000..094b80c
--- /dev/null
+++ b/examples/loadables/template.c
@@ -0,0 +1,75 @@
+/* template - example template for loadable builtin */
+
+/* See Makefile for compilation details. */
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <stdio.h>
+#include <errno.h>
+
+#include "loadables.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+extern char *strerror ();
+
+int
+template_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, rval;
+
+ rval = EXECUTION_SUCCESS;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ return (rval);
+}
+
+/* Called when `template' is enabled and loaded from the shared object. If this
+ function returns 0, the load fails. */
+int
+template_builtin_load (name)
+ char *name;
+{
+ return (1);
+}
+
+/* Called when `template' is disabled. */
+void
+template_builtin_unload (name)
+ char *name;
+{
+}
+
+char *template_doc[] = {
+ "Short description.",
+ ""
+ "Longer description of builtin and usage.",
+ (char *)NULL
+};
+
+struct builtin template_struct = {
+ "template", /* builtin name */
+ template_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ template_doc, /* array of long documentation strings. */
+ "template", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/truefalse.c b/examples/loadables/truefalse.c
new file mode 100644
index 0000000..e011fa1
--- /dev/null
+++ b/examples/loadables/truefalse.c
@@ -0,0 +1,72 @@
+/* true and false builtins */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "shell.h"
+#include "builtins.h"
+#include "common.h"
+
+int
+true_builtin (list)
+ WORD_LIST *list;
+{
+ return EXECUTION_SUCCESS;
+}
+
+int
+false_builtin (list)
+ WORD_LIST *list;
+{
+ return EXECUTION_FAILURE;
+}
+
+static char *true_doc[] = {
+ "Exit successfully.",
+ "",
+ "Return a successful result.",
+ (char *)NULL
+};
+
+static char *false_doc[] = {
+ "Exit unsuccessfully.",
+ "",
+ "Return an unsuccessful result.",
+ (char *)NULL
+};
+
+struct builtin true_struct = {
+ "true",
+ true_builtin,
+ BUILTIN_ENABLED,
+ true_doc,
+ "true",
+ 0
+};
+
+struct builtin false_struct = {
+ "false",
+ false_builtin,
+ BUILTIN_ENABLED,
+ false_doc,
+ "false",
+ 0
+};
diff --git a/examples/loadables/tty.c b/examples/loadables/tty.c
new file mode 100644
index 0000000..febf518
--- /dev/null
+++ b/examples/loadables/tty.c
@@ -0,0 +1,82 @@
+/* tty - return terminal name */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+extern char *ttyname ();
+
+int
+tty_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, sflag;
+ char *t;
+
+ reset_internal_getopt ();
+ sflag = 0;
+ while ((opt = internal_getopt (list, "s")) != -1)
+ {
+ switch (opt)
+ {
+ case 's':
+ sflag = 1;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ t = ttyname (0);
+ if (sflag == 0)
+ puts (t ? t : "not a tty");
+ return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+}
+
+char *tty_doc[] = {
+ "Display terminal name.",
+ "",
+ "tty writes the name of the terminal that is opened for standard",
+ "input to standard output. If the `-s' option is supplied, nothing",
+ "is written; the exit status determines whether or not the standard",
+ "input is connected to a tty.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin tty_struct = {
+ "tty", /* builtin name */
+ tty_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ tty_doc, /* array of long documentation strings. */
+ "tty [-s]", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c
new file mode 100644
index 0000000..106a1c8
--- /dev/null
+++ b/examples/loadables/uname.c
@@ -0,0 +1,162 @@
+/*
+ * uname - print system information
+ *
+ * usage: uname [-amnrsv]
+ *
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "bashtypes.h"
+
+#if defined (HAVE_UNAME)
+# include <sys/utsname.h>
+#else
+struct utsname {
+ char sysname[32];
+ char nodename[32];
+ char release[32];
+ char version[32];
+ char machine[32];
+};
+#endif
+
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#define FLAG_SYSNAME 0x01 /* -s */
+#define FLAG_NODENAME 0x02 /* -n */
+#define FLAG_RELEASE 0x04 /* -r */
+#define FLAG_VERSION 0x08 /* -v */
+#define FLAG_MACHINE 0x10 /* -m, -p */
+
+#define FLAG_ALL 0x1f
+
+#ifndef errno
+extern int errno;
+#endif
+
+static void uprint();
+
+static int uname_flags;
+
+int
+uname_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, r;
+ struct utsname uninfo;
+
+ uname_flags = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "amnprsv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ uname_flags |= FLAG_ALL;
+ break;
+ case 'm':
+ case 'p':
+ uname_flags |= FLAG_MACHINE;
+ break;
+ case 'n':
+ uname_flags |= FLAG_NODENAME;
+ break;
+ case 'r':
+ uname_flags |= FLAG_RELEASE;
+ break;
+ case 's':
+ uname_flags |= FLAG_SYSNAME;
+ break;
+ case 'v':
+ uname_flags |= FLAG_VERSION;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (uname_flags == 0)
+ uname_flags = FLAG_SYSNAME;
+
+ /* Only ancient systems will not have uname(2). */
+#ifdef HAVE_UNAME
+ if (uname (&uninfo) < 0)
+ {
+ builtin_error ("cannot get system name: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+#else
+ builtin_error ("cannot get system information: uname(2) not available");
+ return (EXECUTION_FAILURE);
+#endif
+
+ uprint (FLAG_SYSNAME, uninfo.sysname);
+ uprint (FLAG_NODENAME, uninfo.nodename);
+ uprint (FLAG_RELEASE, uninfo.release);
+ uprint (FLAG_VERSION, uninfo.version);
+ uprint (FLAG_MACHINE, uninfo.machine);
+
+ return (EXECUTION_SUCCESS);
+}
+
+static void
+uprint (flag, info)
+ int flag;
+ char *info;
+{
+ if (uname_flags & flag)
+ {
+ uname_flags &= ~flag;
+ printf ("%s%c", info, uname_flags ? ' ' : '\n');
+ }
+}
+
+char *uname_doc[] = {
+ "Display system information.",
+ "",
+ "Display information about the system hardware and OS.",
+ (char *)NULL
+};
+
+struct builtin uname_struct = {
+ "uname",
+ uname_builtin,
+ BUILTIN_ENABLED,
+ uname_doc,
+ "uname [-amnrsv]",
+ 0
+};
diff --git a/examples/loadables/unlink.c b/examples/loadables/unlink.c
new file mode 100644
index 0000000..ff2a78a
--- /dev/null
+++ b/examples/loadables/unlink.c
@@ -0,0 +1,74 @@
+/* unlink - remove a directory entry */
+
+/* Should only be used to remove directories by a superuser prepared to let
+ fsck clean up the file system. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "common.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+int
+unlink_builtin (list)
+ WORD_LIST *list;
+{
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (unlink (list->word->word) != 0)
+ {
+ builtin_error ("%s: cannot unlink: %s", list->word->word, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+char *unlink_doc[] = {
+ "Remove a directory entry.",
+ "",
+ "Forcibly remove a directory entry, even if it's a directory.",
+ (char *)NULL
+};
+
+struct builtin unlink_struct = {
+ "unlink", /* builtin name */
+ unlink_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ unlink_doc, /* array of long documentation strings. */
+ "unlink name", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c
new file mode 100644
index 0000000..3e7e36e
--- /dev/null
+++ b/examples/loadables/whoami.c
@@ -0,0 +1,75 @@
+/*
+ * whoami - print out username of current user
+ */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ 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 "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+int
+whoami_builtin (list)
+ WORD_LIST *list;
+{
+ int opt;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+ if (list)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ if (current_user.user_name == 0)
+ get_current_user_info ();
+ printf ("%s\n", current_user.user_name);
+ return (EXECUTION_SUCCESS);
+}
+
+char *whoami_doc[] = {
+ "Print user name",
+ "",
+ "Display name of current user.",
+ (char *)NULL
+};
+
+struct builtin whoami_struct = {
+ "whoami",
+ whoami_builtin,
+ BUILTIN_ENABLED,
+ whoami_doc,
+ "whoami",
+ 0
+};