1
0
Fork 0

Adding upstream version 5.2.37.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-21 06:49:21 +02:00
parent cf91100bce
commit fa1b3d3922
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
1435 changed files with 757174 additions and 0 deletions

View file

@ -0,0 +1,335 @@
#
# Simple makefile for the sample loadable builtins
#
# Copyright (C) 1996-2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PACKAGE = @PACKAGE_NAME@
VERSION = @PACKAGE_VERSION@
# 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@
STYLE_CFLAGS = @STYLE_CFLAGS@
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) $(STYLE_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 dsv cut stat getconf
OTHERPROG = necho hello cat pushd 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)
getconf: getconf.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ getconf.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)
dsv: dsv.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dsv.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) -Wno-format-security -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 Makefile.sample 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) Makefile.sample $(DESTDIR)$(loadablesdir)/Makefile.sample
@$(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)/Makefile.sample
-$(RM) $(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
getconf.o: getconf.c getconf.h
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
dsv.o: dsv.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

View file

@ -0,0 +1,101 @@
#
# Sample makefile for bash loadable builtin development
#
# Copyright (C) 2015-2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PACKAGE = @PACKAGE_NAME@
VERSION = @PACKAGE_VERSION@
PACKAGE_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 $@ $<

View file

@ -0,0 +1,44 @@
#
# Sample makefile for bash loadable builtin development
#
# Copyright (C) 2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# these should match the ones in Makefile.in (for the make install target)
prefix = @prefix@
exec_prefix = @exec_prefix@
libdir = @libdir@
# ${loadablesdir} is where the example loadable builtins and data files
# are installed (make install target in Makefile.in)
loadablesdir = @loadablesdir@
DESTDIR =
# include Makefile.inc for all boilerplate definitions
include $(DESTDIR)$(loadablesdir)/Makefile.inc
# here, `example' is the name of the shared object
# replace `example' with the appropriate filename
all: example
example: example.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ example.o $(SHOBJ_LIBS)
example.o: example.c

82
examples/loadables/README Normal file
View file

@ -0,0 +1,82 @@
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. Some
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, csv, which allows you to manipulate data from comma-separated
values files, fdflags, which lets you change the flags associated
with one of the shell's file descriptors, 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.
csv.c Process a line of csv data and store it in an indexed array.
cut.c Cut out selected portions of each line of a file.
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.
mkfifo.c Create named pipes.
mktemp.c Make unique temporary file name.
mypid.c Add $MYPID variable, demonstrate use of unload hook function.
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.

245
examples/loadables/accept.c Normal file
View file

@ -0,0 +1,245 @@
/* 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 <limits.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;
{
SHELL_VAR *v;
intmax_t iport;
int opt;
char *tmoutarg, *fdvar, *rhostvar, *rhost, *bindaddr;
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 = bindaddr = (char *)NULL;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "b:r:t:v:")) != -1)
{
switch (opt)
{
case 'b':
bindaddr = list_optarg;
break;
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 = bindaddr ? inet_addr (bindaddr) : htonl(INADDR_ANY);
if (server.sin_addr.s_addr == INADDR_NONE)
{
builtin_error ("invalid address: %s", strerror (errno));
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 (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0)
{
builtin_error ("socket bind failure: %s", strerror (errno));
close (servsock);
return (EXECUTION_FAILURE);
}
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); /* XXX */
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:",
" -b address use ADDRESS as the IP address to listen on; the",
" default is INADDR_ANY",
" -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 [-b address] [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

279
examples/loadables/asort.c Normal file
View file

@ -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
};

View file

@ -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 */
};

138
examples/loadables/cat.c Normal file
View file

@ -0,0 +1,138 @@
/*
* cat replacement
*
* no options - the way cat was intended
*/
/*
Copyright (C) 1999-2009,2022 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, fn)
int fd;
char *fn;
{
char buf[4096], *s;
int n, w, e;
while (n = read(fd, buf, sizeof (buf))) {
if (n < 0) {
e = errno;
write(2, "cat: read error: ", 18);
write(2, fn, strlen(fn));
write(2, ": ", 2);
s = strerror(e);
write(2, s, strlen(s));
write(2, "\n", 1);
return 1;
}
QUIT;
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;
}
QUIT;
}
return 0;
}
int
cat_main (argc, argv)
int argc;
char **argv;
{
int i, fd, r;
char *s;
if (argc == 1)
return (fcopy(0, "standard input"));
for (i = r = 1; i < argc; i++) {
QUIT;
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, argv[i]);
if (fd != 0)
close(fd);
}
QUIT;
return (r);
}
int
cat_builtin(list)
WORD_LIST *list;
{
char **v;
int c, r;
v = make_builtin_argv(list, &c);
QUIT;
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
};

206
examples/loadables/csv.c Normal file
View file

@ -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, dstring)
SHELL_VAR *csv;
char *line, *dstring;
{
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 == *dstring)
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, dstring);
}
delim = *field;
*field = '\0';
bind_array_element (csv, ind, buf, 0);
ind++;
*field = delim;
if (delim == *dstring)
prev = field + 1;
}
while (delim == *dstring);
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 */
};

631
examples/loadables/cut.c Normal file
View file

@ -0,0 +1,631 @@
/* 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)
{
QUIT;
if (line[n] == '\n')
line[n] = '\0'; /* cutline expects no newline terminator */
cutline (v, line, ops); /* can modify line */
}
if (fd > 0)
close (fd);
QUIT;
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 */
};

View file

@ -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 */
};

300
examples/loadables/dsv.c Normal file
View file

@ -0,0 +1,300 @@
/* dsv - process a line of delimiter-separated data and populate an indexed
array with the fields */
/*
Copyright (C) 2022 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 DSV_ARRAY_DEFAULT "DSV"
#define NQUOTE 0
#define DQUOTE 1
#define SQUOTE 2
#define F_SHELLQUOTE 0x01
#define F_GREEDY 0x02
#define F_PRESERVE 0x04
/* Split LINE into delimiter-separated fields, storing each field into a
separate element of array variable DSV, starting at index 0. The format
of LINE is delimiter-separated values. By default, this splits lines of
CSV data as described in RFC 4180. If *DSTRING is any other value than
',', this uses that character as a field delimiter. Pass F_SHELLQUOTE in
FLAGS to understand shell-like double-quoting and backslash-escaping in
double quotes instead of the "" CSV behavior, and shell-like single quotes.
Pass F_GREEDY in FLAGS to consume multiple leading and trailing instances
of *DSTRING and consecutive instances of *DSTRING in LINE without creating
null fields. If you want to preserve the quote characters in the generated
fields, pass F_PRESERVE; by default, this removes them. */
static int
dsvsplit (dsv, line, dstring, flags)
SHELL_VAR *dsv;
char *line, *dstring;
int flags;
{
arrayind_t ind;
char *field, *prev, *buf, *xbuf;
int delim, qstate;
int b, rval;
xbuf = 0;
ind = 0;
field = prev = line;
/* If we want a greedy split, consume leading instances of *DSTRING */
if (flags & F_GREEDY)
{
while (*prev == *dstring)
prev++;
field = prev;
}
do
{
if (*prev == '"')
{
if (xbuf == 0)
xbuf = xmalloc (strlen (prev) + 1);
buf = xbuf;
b = 0;
if (flags & F_PRESERVE)
buf[b++] = *prev;
qstate = DQUOTE;
for (field = ++prev; *field; field++)
{
if (qstate == DQUOTE && *field == '"' && field[1] == '"' && (flags & F_SHELLQUOTE) == 0)
buf[b++] = *field++; /* skip double quote */
else if (qstate == DQUOTE && (flags & F_SHELLQUOTE) && *field == '\\' && strchr (slashify_in_quotes, field[1]) != 0)
buf[b++] = *++field; /* backslash quoted double quote */
else if (qstate == DQUOTE && *field == '"')
{
qstate = NQUOTE;
if (flags & F_PRESERVE)
buf[b++] = *field;
}
else if (qstate == NQUOTE && *field == *dstring)
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 if ((flags & F_SHELLQUOTE) && *prev == '\'')
{
if (xbuf == 0)
xbuf = xmalloc (strlen (prev) + 1);
buf = xbuf;
b = 0;
if (flags & F_PRESERVE)
buf[b++] = *prev;
qstate = SQUOTE;
for (field = ++prev; *field; field++)
{
if (qstate == SQUOTE && *field == '\'')
{
qstate = NQUOTE;
if (flags & F_PRESERVE)
buf[b++] = *field;
}
else if (qstate == NQUOTE && *field == *dstring)
break;
else
/* This copies any text between a closing single quote and the
delimiter. If you want to change that, make sure to do the
copy only if qstate == SQUOTE. */
buf[b++] = *field;
}
buf[b] = '\0';
}
else
{
buf = prev;
field = prev + strcspn (prev, dstring);
}
delim = *field;
*field = '\0';
if ((flags & F_GREEDY) == 0 || buf[0])
{
bind_array_element (dsv, ind, buf, 0);
ind++;
}
*field = delim;
if (delim == *dstring)
prev = field + 1;
}
while (delim == *dstring);
if (xbuf)
free (xbuf);
return (rval = ind); /* number of fields */
}
int
dsv_builtin (list)
WORD_LIST *list;
{
int opt, rval, flags;
char *array_name, *dsvstring, *delims;
SHELL_VAR *v;
array_name = 0;
rval = EXECUTION_SUCCESS;
delims = ",";
flags = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "a:d:Sgp")) != -1)
{
switch (opt)
{
case 'a':
array_name = list_optarg;
break;
case 'd':
delims = list_optarg;
break;
case 'S':
flags |= F_SHELLQUOTE;
break;
case 'g':
flags |= F_GREEDY;
break;
case 'p':
flags |= F_PRESERVE;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (array_name == 0)
array_name = DSV_ARRAY_DEFAULT;
if (legal_identifier (array_name) == 0)
{
sh_invalidid (array_name);
return (EXECUTION_FAILURE);
}
if (list == 0)
{
builtin_error ("dsv 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));
dsvstring = list->word->word;
if (dsvstring == 0 || *dsvstring == 0)
return (EXECUTION_SUCCESS);
opt = dsvsplit (v, dsvstring, delims, flags);
/* 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
dsv_builtin_load (name)
char *name;
{
return (1);
}
/* Called when builtin is disabled. */
void
dsv_builtin_unload (name)
char *name;
{
}
char *dsv_doc[] = {
"Read delimiter-separated fields from STRING.",
"",
"Parse STRING, a line of delimiter-separated values, into individual",
"fields, and store them into the indexed array ARRAYNAME starting at",
"index 0. The parsing understands and skips over double-quoted strings. ",
"If ARRAYNAME is not supplied, \"DSV\" is the default array name.",
"If the delimiter is a comma, the default, this parses comma-",
"separated values as specified in RFC 4180.",
"",
"The -d option specifies the delimiter. The delimiter is the first",
"character of the DELIMS argument. Specifying a DELIMS argument that",
"contains more than one character is not supported and will produce",
"unexpected results. The -S option enables shell-like quoting: double-",
"quoted strings can contain backslashes preceding special characters,",
"and the backslash will be removed; and single-quoted strings are",
"processed as the shell would process them. The -g option enables a",
"greedy split: sequences of the delimiter are skipped at the beginning",
"and end of STRING, and consecutive instances of the delimiter in STRING",
"do not generate empty fields. If the -p option is supplied, dsv leaves",
"quote characters as part of the generated field; otherwise they are",
"removed.",
"",
"The return value is 0 unless an invalid option is supplied or the ARRAYNAME",
"argument is invalid or readonly.",
(char *)NULL
};
struct builtin dsv_struct = {
"dsv", /* builtin name */
dsv_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
dsv_doc, /* array of long documentation strings. */
"dsv [-a ARRAYNAME] [-d DELIMS] [-Sgp] string", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,374 @@
/* Loadable builtin to get and set file descriptor flags. */
/* See Makefile for compilation details. */
/*
Copyright (C) 2017-2022 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 unused 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 */
};

629
examples/loadables/finfo.c Normal file
View file

@ -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

1163
examples/loadables/getconf.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
/*
Copyright (C) 2021 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/>.
*/
/* getconf.h -- replacement definitions for ones the system doesn't provide
and don't appear in <typemax.h> */
#ifndef _GETCONF_H
#define _GETCONF_H
/* Some systems do not define these; use POSIX.2 minimum recommended values. */
#ifndef _POSIX2_COLL_WEIGHTS_MAX
# define _POSIX2_COLL_WEIGHTS_MAX 2
#endif
/* If we're on a posix system, but the system doesn't define the necessary
constants, use posix.1 minimum values. */
#if defined (_POSIX_VERSION)
#ifndef _POSIX_ARG_MAX
# define _POSIX_ARG_MAX 4096
#endif
#ifndef _POSIX_CHILD_MAX
# define _POSIX_CHILD_MAX 6
#endif
#ifndef _POSIX_LINK_MAX
# define _POSIX_LINK_MAX 8
#endif
#ifndef _POSIX_MAX_CANON
# define _POSIX_MAX_CANON 255
#endif
#ifndef _POSIX_MAX_INPUT
# define _POSIX_MAX_INPUT 255
#endif
#ifndef _POSIX_NAME_MAX
# define _POSIX_NAME_MAX 14
#endif
#ifndef _POSIX_NGROUPS_MAX
# define _POSIX_NGROUPS_MAX 0
#endif
#ifndef _POSIX_OPEN_MAX
# define _POSIX_OPEN_MAX 16
#endif
#ifndef _POSIX_PATH_MAX
# define _POSIX_PATH_MAX 255
#endif
#ifndef _POSIX_PIPE_BUF
# define _POSIX_PIPE_BUF 512
#endif
#ifndef _POSIX_SSIZE_MAX
# define _POSIX_SSIZE_MAX 32767
#endif
#ifndef _POSIX_STREAM_MAX
# define _POSIX_STREAM_MAX 8
#endif
#ifndef _POSIX_TZNAME_MAX
# define _POSIX_TZNAME_MAX 3
#endif
#ifndef _POSIX2_BC_BASE_MAX
# define _POSIX2_BC_BASE_MAX 99
#endif
#ifndef _POSIX2_BC_DIM_MAX
# define _POSIX2_BC_DIM_MAX 2048
#endif
#ifndef _POSIX2_BC_SCALE_MAX
# define _POSIX2_BC_SCALE_MAX 99
#endif
#ifndef _POSIX2_BC_STRING_MAX
# define _POSIX2_BC_STRING_MAX 1000
#endif
#ifndef _POSIX2_EQUIV_CLASS_MAX
# define _POSIX2_EQUIV_CLASS_MAX 2
#endif
#ifndef _POSIX2_EXPR_NEST_MAX
# define _POSIX2_EXPR_NEST_MAX 32
#endif
#ifndef _POSIX2_LINE_MAX
# define _POSIX2_LINE_MAX 2048
#endif
#ifndef _POSIX2_RE_DUP_MAX
# define _POSIX2_RE_DUP_MAX 255
#endif
#endif /* _POSIX_VERSION */
/* ANSI/ISO C, POSIX.1-200x, XPG 4.2, and C language type limits.
Defined only if the system include files and <typemax.h> don't. */
#ifndef CHAR_MAX
# define CHAR_MAX 127
#endif
#ifndef CHAR_MIN
# define CHAR_MIN -128
#endif
#ifndef SCHAR_MAX
# define SCHAR_MAX 127
#endif
#ifndef SCHAR_MIN
# define SCHAR_MIN -128
#endif
#ifndef INT_BIT
# define INT_BIT (sizeof (int) * CHAR_BIT)
#endif
#ifndef LONG_BIT
# define LONG_BIT (sizeof (long int) * CHAR_BIT)
#endif
#ifndef WORD_BIT
# define WORD_BIT (sizeof (int) * CHAR_BIT)
#endif
#if !defined (PRIdMAX)
# if HAVE_LONG_LONG
# define PRIdMAX "lld"
# else
# define PRIdMAX "ld"
# endif
#endif
#endif /* _GETCONF_H */

170
examples/loadables/head.c Normal file
View file

@ -0,0 +1,170 @@
/* 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)
{
QUIT;
if (putchar (ch) == EOF)
{
builtin_error ("write error: %s", strerror (errno));
return EXECUTION_FAILURE;
}
QUIT;
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;
}
QUIT;
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 */
};

View file

@ -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 */
};

329
examples/loadables/id.c Normal file
View file

@ -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
};

236
examples/loadables/ln.c Normal file
View file

@ -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 */
};

View file

@ -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

View file

@ -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
};

245
examples/loadables/mkdir.c Normal file
View file

@ -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 permission 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
};

146
examples/loadables/mkfifo.c Normal file
View file

@ -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
};

212
examples/loadables/mktemp.c Normal file
View file

@ -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 */
};

View file

@ -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
};

View file

@ -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
};

View file

@ -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;
}

View file

@ -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

View file

@ -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.

View file

@ -0,0 +1,53 @@
/*
* perl builtin
*/
#include <config.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#ifndef errno
extern int errno;
#endif
extern char **make_builtin_argv (WORD_LIST *, int *);
extern char **export_env;
extern void perl_close(void);
extern int perl_main(int, char **, char **);
int
bperl_builtin(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;
}
void
bperl_builtin_unload (char *s)
{
perl_close();
}
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
};

View file

@ -0,0 +1,38 @@
#include <EXTERN.h> /* from the Perl distribution */
#include <perl.h> /* from the Perl distribution */
#define iperl my_perl /* I guess the name `my_perl' is required */
extern void xs_init (pTHX);
static PerlInterpreter *iperl; /*** The Perl interpreter ***/
static int first = 1;
void
perl_close (void)
{
PERL_SYS_TERM();
}
int
perl_main(int argc, char **argv, char **env)
{
int r;
if (first) {
first = 0;
PERL_SYS_INIT3(&argc, &argv, &env);
}
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);
}

193
examples/loadables/print.c Normal file
View file

@ -0,0 +1,193 @@
/*
* 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);
if (ostr)
fprintf (ofp, "%s", ostr);
free (ostr);
if (sawc)
return (0);
if (l->next)
fprintf (ofp, " ");
}
return (1);
}

View file

@ -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
};

117
examples/loadables/push.c Normal file
View file

@ -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
};

View file

@ -0,0 +1,207 @@
/*
* realpath -- canonicalize pathnames, resolving symlinks
*
* usage: realpath [-cqsv] [-a name] pathname [pathname...]
*
* options: -a name assign each canonicalized pathname to indexed array
* variable NAME
* -c check whether or not each resolved path exists
* -q no output, exit status determines whether path is valid
* -s strip . and .. from the pathname only, no symlink resolution
* -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,2021,2022 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(WORD_LIST *list)
{
int opt, cflag, vflag, qflag, sflag, aflag, es;
char *r, realbuf[PATH_MAX], *p, *newpath;
struct stat sb;
#if defined (ARRAY_VARS)
arrayind_t ind;
char *aname;
SHELL_VAR *v;
#endif
if (list == 0) {
builtin_usage();
return (EX_USAGE);
}
vflag = cflag = qflag = aflag = sflag = 0;
#if defined (ARRAY_VARS)
aname = NULL;
v = NULL;
ind = 0;
#endif
reset_internal_getopt();
while ((opt = internal_getopt (list, "a:cqsv")) != -1) {
switch (opt) {
#if defined (ARRAY_VARS)
case 'a':
aflag = 1;
aname = list_optarg;
break;
#endif
case 'c':
cflag = 1;
break;
case 'q':
qflag = 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);
}
#if defined (ARRAY_VARS)
if (aflag && legal_identifier (aname) == 0) {
sh_invalidid(aname);
return (EXECUTION_FAILURE);
}
if (aname && builtin_unbind_variable (aname) == -2)
return (EXECUTION_FAILURE);
if (aname) {
v = find_or_make_array_variable (aname, 1);
if (v == 0 || readonly_p (v) || noassign_p (v)) {
if (v && readonly_p (v))
err_readonly (aname);
return (EXECUTION_FAILURE);
} else if (array_p (v) == 0) {
builtin_error ("%s: not an indexed array", aname);
return (EXECUTION_FAILURE);
}
if (invisible_p (v))
VUNSETATTR (v, att_invisible);
array_flush (array_cell (v));
}
#endif
for (es = EXECUTION_SUCCESS; list; list = list->next) {
p = list->word->word;
if (sflag) {
/* sh_canonpath doesn't convert to absolute pathnames */
newpath = make_absolute(p, get_string_value("PWD"));
r = sh_canonpath(newpath, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
free(newpath);
} else
r = sh_realpath(p, realbuf);
if (r == 0) {
es = EXECUTION_FAILURE;
if (qflag == 0)
builtin_error("%s: cannot resolve: %s", p, strerror(errno));
continue;
}
if (cflag && (stat(r, &sb) < 0)) {
es = EXECUTION_FAILURE;
if (qflag == 0)
builtin_error("%s: %s", p, strerror(errno));
continue;
}
if (aflag) {
bind_array_element (v, ind, r, 0);
ind++;
}
if (qflag == 0) {
if (vflag)
printf ("%s -> ", p);
printf("%s\n", r);
}
if (sflag)
free (r);
}
return es;
}
char *realpath_doc[] = {
"Display pathname in canonical form.",
"",
"Display the canonicalized version of each PATHNAME argument, resolving",
"symbolic links.",
"The -a option stores each canonicalized PATHNAME argument into the indexed",
"array VARNAME.",
"The -c option checks whether or not each resolved name exists.",
"The -q option produces no output; the exit status determines the",
"validity of each PATHNAME, but any array assignment is still performed.",
"If the -s option is supplied, canonicalize . and .. pathname components",
"without resolving symbolic links.",
"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 [-a varname] [-cqsv] pathname [pathname...]", /* usage synopsis */
0 /* reserved for internal use */
};

185
examples/loadables/rm.c Normal file
View file

@ -0,0 +1,185 @@
/* 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
QUIT;
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
QUIT;
}
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;
QUIT;
/* 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)
{
QUIT;
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 */
};

View file

@ -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 */
};

502
examples/loadables/seq.c Normal file
View file

@ -0,0 +1,502 @@
/* 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 (PRI_MACROS_BROKEN)
# undef PRIdMAX
#endif
#if !defined (PRIdMAX)
# if HAVE_LONG_LONG
# define PRIdMAX "lld"
# else
# define PRIdMAX "ld"
# endif
#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
};

View file

@ -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
};

179
examples/loadables/sleep.c Normal file
View file

@ -0,0 +1,179 @@
/*
* sleep -- sleep for fractions of a second
*
* usage: sleep seconds[.fraction]
*
* as an extension, we support the GNU time interval format (2m20s)
*/
/*
Copyright (C) 1999-2021 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 "loadables.h"
#define S_SEC 1
#define S_MIN (60*S_SEC)
#define S_HOUR (60*S_MIN)
#define S_DAY (24*S_HOUR)
static int
parse_gnutimefmt (char *string, long *sp, long *up)
{
int c, r;
char *s, *ep;
long tsec, tusec, accumsec, accumusec, t;
int mult;
tsec = tusec = 0;
accumsec = accumusec = 0;
mult = 1;
for (s = string; s && *s; s++) {
r = uconvert(s, &accumsec, &accumusec, &ep);
if (r == 0 && *ep == 0)
return r;
c = *ep;
mult = 1;
switch (c) {
case '\0':
case 's':
mult = S_SEC;
break;
case 'm':
mult = S_MIN;
break;
case 'h':
mult = S_HOUR;
break;
case 'd':
mult = S_DAY;
break;
default:
return 0;
}
/* multiply the accumulated value by the multiplier */
t = accumusec * mult;
accumsec = accumsec * mult + (t / 1000000);
accumusec = t % 1000000;
/* add to running total */
tsec += accumsec;
tusec += accumusec;
if (tusec >= 1000000) {
tsec++;
tusec -= 1000000;
}
/* reset and continue */
accumsec = accumusec = 0;
mult = 1;
if (c == 0)
break;
s = ep;
}
if (sp)
*sp = tsec;
if (up)
*up = tusec;
return 1;
}
int
sleep_builtin (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
*
* A heuristic: if the conversion failed, but the argument appears to
* contain a GNU-like interval specifier (e.g. "1m30s"), try to parse
* it. If we can't, return the right exit code to tell
* execute_builtin to try and execute a disk command instead.
*/
if (r == 0 && (strchr ("dhms", *ep) || strpbrk (list->word->word, "dhms")))
r = parse_gnutimefmt (list->word->word, &sec, &usec);
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.",
"As an extension, sleep accepts GNU-style time intervals (e.g., 2m30s).",
(char *)NULL
};
struct builtin sleep_struct = {
"sleep",
sleep_builtin,
BUILTIN_ENABLED,
sleep_doc,
"sleep seconds[.fraction]",
0
};

464
examples/loadables/stat.c Normal file
View file

@ -0,0 +1,464 @@
/* stat - load up an associative array with stat information about a file */
/* See Makefile for compilation details. */
/*
Copyright (C) 2016,2022 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
};
#define DEFTIMEFMT "%a %b %e %k:%M:%S %Z %Y"
#ifndef TIMELEN_MAX
# define TIMELEN_MAX 128
#endif
static char *stattime (time_t, const char *);
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, timefmt)
time_t t;
const char *timefmt;
{
char *tbuf, *ret;
const char *fmt;
size_t tlen;
struct tm *tm;
fmt = timefmt ? timefmt : DEFTIMEFMT;
tm = localtime (&t);
ret = xmalloc (TIMELEN_MAX);
tlen = strftime (ret, TIMELEN_MAX, fmt, tm);
if (tlen == 0)
tlen = strftime (ret, TIMELEN_MAX, DEFTIMEFMT, tm);
return ret;
}
static char *
statval (which, fname, flags, fmt, sp)
int which;
char *fname;
int flags;
char *fmt;
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, fmt) : itos (sp->st_atime));
case ST_MTIME:
return ((flags & 2) ? stattime (sp->st_mtime, fmt) : itos (sp->st_mtime));
case ST_CTIME:
return ((flags & 2) ? stattime (sp->st_ctime, fmt) : 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, fmt, sp)
char *vname;
SHELL_VAR *var;
char *fname;
int flags;
char *fmt;
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, fmt, 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, *timefmt;
struct stat st;
SHELL_VAR *v;
aname = "STAT";
flags = 0;
timefmt = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "A:F: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 'F':
timefmt = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
if (legal_identifier (aname) == 0)
{
sh_invalidid (aname);
return (EXECUTION_FAILURE);
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
#if 0
unbind_variable (aname);
#endif
fname = list->word->word;
if (getstat (fname, flags, &st) < 0)
{
builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
return (EXECUTION_FAILURE);
}
v = find_or_make_array_variable (aname, 3);
if (v == 0)
{
builtin_error ("%s: cannot create variable", aname);
return (EXECUTION_FAILURE);
}
if (loadstat (aname, v, fname, flags, timefmt, &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. When -l is used,",
"the -F option supplies a format string passed to strftime(3) to",
"display the file time information.",
"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 */
};

View file

@ -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 */
};

53
examples/loadables/sync.c Normal file
View file

@ -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 */
};

183
examples/loadables/tee.c Normal file
View file

@ -0,0 +1,183 @@
/* tee - duplicate standard input */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2021 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;
}
QUIT;
}
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;
QUIT;
}
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);
}
QUIT;
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 */
};

View file

@ -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 */
};

View file

@ -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
};

83
examples/loadables/tty.c Normal file
View file

@ -0,0 +1,83 @@
/* tty - return terminal name */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2021 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);
QUIT;
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 */
};

162
examples/loadables/uname.c Normal file
View file

@ -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
};

View file

@ -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 */
};

View file

@ -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
};