diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:33:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:33:23 +0000 |
commit | 1d5cace9db9aef76f26b2d7ba54bbb76443b00b2 (patch) | |
tree | 314a15dd1aa103da13bdc83ba1d2105a290bc5ba /examples/loadables | |
parent | Initial commit. (diff) | |
download | bash-upstream/5.0.tar.xz bash-upstream/5.0.zip |
Adding upstream version 5.0.upstream/5.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
41 files changed, 6526 insertions, 0 deletions
diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in new file mode 100644 index 0000000..cc3d3a7 --- /dev/null +++ b/examples/loadables/Makefile.in @@ -0,0 +1,300 @@ +# +# Simple makefile for the sample loadable builtins +# +# Copyright (C) 1996-2015 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ + +# 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. -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 printenv id whoami \ + uname sync push ln unlink realpath strftime mypid setpgid seq +OTHERPROG = necho hello cat pushd stat rm + +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) + +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) + +head: head.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS) + +printenv: printenv.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS) + +id: id.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS) + +whoami: whoami.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS) + +uname: uname.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS) + +sync: sync.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS) + +push: push.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS) + +ln: ln.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS) + +unlink: unlink.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS) + +realpath: realpath.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS) + +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) + +# pushd is a special case. We use the same source that the builtin version +# uses, with special compilation options. +# +pushd.c: ${topdir}/builtins/pushd.def + $(RM) $@ + ${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def + +pushd.o: pushd.c + $(RM) $@ + $(SHOBJ_CC) -DHAVE_CONFIG_H -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(INC) -c -o $@ $< + +pushd: pushd.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS) + +clean: + $(RM) $(ALLPROG) $(OTHERPROG) *.o + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +mostlyclean: clean + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +distclean maintainer-clean: clean + $(RM) Makefile Makefile.inc pushd.c + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +installdirs: + @${SHELL} $(SUPPORT_SRC)mkinstalldirs $(DESTDIR)$(loadablesdir) + +install-dev: installdirs + @$(INSTALL_DATA) Makefile.inc $(DESTDIR)$(loadablesdir)/Makefile.inc + @$(INSTALL_DATA) $(srcdir)/loadables.h $(DESTDIR)$(loadablesdir)/loadables.h + @( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" install-headers) + +install-supported: all installdirs install-dev + @echo installing example loadable builtins in $(DESTDIR)${loadablesdir} + @for prog in ${ALLPROG}; do \ + echo $$prog ; \ + $(INSTALL_PROGRAM) $(INSTALLMODE) $$prog $(DESTDIR)$(loadablesdir)/$$prog ;\ + done + +uninstall-dev: + -$(RM) $(DESTDIR)$(loadablesdir)/Makefile.inc $(DESTDIR)$(loadablesdir)/loadables.h + -( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" uninstall-headers) + +uninstall-supported: uninstall-dev + -( cd $(DESTDIR)${loadablesdir} && $(RM) ${ALLPROG} ) + +install-unsupported: +uninstall-unsupported: + +install: install-$(SHOBJ_STATUS) +uninstall: uninstall-$(SHOBJ_STATUS) + +print.o: print.c +truefalse.o: truefalse.c +sleep.o: sleep.c +finfo.o: finfo.c +logname.o: logname.c +basename.o: basename.c +dirname.o: dirname.c +tty.o: tty.c +pathchk.o: pathchk.c +tee.o: tee.c +head.o: head.c +rmdir.o: rmdir.c +necho.o: necho.c +hello.o: hello.c +cat.o: cat.c +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 +realpath.o: realpath.c +strftime.o: strftime.c +setpgid.o: setpgid.c +stat.o: stat.c +fdflags.o: fdflags.c +seq.o: seq.c diff --git a/examples/loadables/Makefile.inc.in b/examples/loadables/Makefile.inc.in new file mode 100644 index 0000000..8b419a7 --- /dev/null +++ b/examples/loadables/Makefile.inc.in @@ -0,0 +1,108 @@ +# +# Sample makefile for bash loadable builtin development +# +# Copyright (C) 2015 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +# Include some boilerplate Gnu makefile definitions. +prefix = @prefix@ + +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +infodir = @infodir@ +includedir = @includedir@ + +datarootdir = @datarootdir@ + +loadablesdir = @loadablesdir@ +headersdir = @headersdir@ + +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +# Support an alternate destination root directory for package building +DESTDIR = + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALLMODE= -m 0755 + +@SET_MAKE@ +CC = @CC@ +RM = rm -f + +SHELL = @MAKE_SHELL@ + +host_os = @host_os@ +host_cpu = @host_cpu@ +host_vendor = @host_vendor@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +CPPFLAGS = @CPPFLAGS@ + +BASHINCDIR = ${topdir}/include + +SUPPORT_SRC = $(topdir)/support/ + +LIBBUILD = ${BUILD_DIR}/lib + +INTL_LIBSRC = ${topdir}/lib/intl +INTL_BUILDDIR = ${LIBBUILD}/intl +INTL_INC = @INTL_INC@ +LIBINTL_H = @LIBINTL_H@ + +CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS) + +# +# These values are generated for configure by ${topdir}/support/shobj-conf. +# If your system is not supported by that script, but includes facilities for +# dynamic loading of shared objects, please update the script and send the +# changes to bash-maintainers@gnu.org. +# +SHOBJ_CC = @SHOBJ_CC@ +SHOBJ_CFLAGS = @SHOBJ_CFLAGS@ +SHOBJ_LD = @SHOBJ_LD@ +SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ @LDFLAGS@ +SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@ +SHOBJ_LIBS = @SHOBJ_LIBS@ +SHOBJ_STATUS = @SHOBJ_STATUS@ + +INC = -I$(headersdir) -I$(headersdir)/include -I$(headersdir)/builtins + +.c.o: + $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $< + +all: example + +example: example.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ example.o $(SHOBJ_LIBS) + +example.o: example.c diff --git a/examples/loadables/README b/examples/loadables/README new file mode 100644 index 0000000..f9bcfac --- /dev/null +++ b/examples/loadables/README @@ -0,0 +1,76 @@ +Some examples of ready-to-dynamic-load builtins. Most of the +examples given are reimplementations of standard commands whose +execution time is dominated by process startup time. The +exceptions are sleep, which allows you to sleep for fractions +of a second, finfo, which provides access to the rest of the +elements of the `stat' structure that `test' doesn't let you +see, and pushd/popd/dirs, which allows you to compile them out +of the shell. + +All of the new builtins in ksh93 that bash didn't already have +are included here, as is the ksh `print' builtin. + +The configure script in the top-level source directory uses the +support/shobj-conf script to set the right values in the Makefile, +so you should not need to change the Makefile. If your system +is not supported by support/shobj-conf, and it has the necessary +facilities for building shared objects and support for the +dlopen/dlsyn/dlclose/dlerror family of functions, please make +the necessary changes to support/shobj-conf and send the changes +to bash-maintainers@gnu.org. + +Loadable builtins are loaded into a running shell with + + enable -f filename builtin-name + +enable uses a simple reference-counting scheme to avoid unloading a +shared object that implements more than one loadable builtin before +all loadable builtins implemented in the object are removed. + +Many of the details needed by builtin writers are found in hello.c, +the canonical example. There is no real `builtin writers' programming +guide'. The file template.c provides a template to use for creating +new loadable builtins. + +The file "Makefile.inc" is created using the same values that configure +writes into Makefile.in, and is installed in the same directory as the +rest of the example builtins. It's intended to be a start at something +that can be modified or included to help you build your own loadables +without having to search for the right CFLAGS and LDFLAGS. + +basename.c Return non-directory portion of pathname. +cat.c cat(1) replacement with no options - the way cat was intended. +dirname.c Return directory portion of pathname. +fdflags.c Change the flag associated with one of bash's open file desriptors. +finfo.c Print file info. +head.c Copy first part of files. +hello.c Obligatory "Hello World" / sample loadable. +id.c POSIX.2 user identity. +ln.c Make links. +loadables.h File loadable builtins can include for shell definitions. +logname.c Print login name of current user. +Makefile.in Simple makefile for the sample loadable builtins. +Makefile.inc.in Sample makefile to use for loadable builtin development. +mkdir.c Make directories. +mypid.c Add $MYPID variable, demonstrate use of unload hook functio.n +necho.c echo without options or argument interpretation. +pathchk.c Check pathnames for validity and portability. +print.c Loadable ksh-93 style print builtin. +printenv.c Minimal builtin clone of BSD printenv(1). +push.c Anyone remember TOPS-20? +realpath.c Canonicalize pathnames, resolving symlinks. +rm.c Remove files and directories. +rmdir.c Remove directory. +seq.c Print a sequence of decimal or floating point numbers. +setpgid.c Set a process's pgrp; example of how to wrap a system call. +sleep.c sleep for fractions of a second. +stat.c populate an associative array with information about a file +strftime.c Loadable builtin interface to strftime(3). +sync.c Sync the disks by forcing pending filesystem writes to complete. +tee.c Duplicate standard input. +template.c Example template for loadable builtin. +truefalse.c True and false builtins. +tty.c Return terminal name. +uname.c Print system information. +unlink.c Remove a directory entry. +whoami.c Print out username of current user. diff --git a/examples/loadables/basename.c b/examples/loadables/basename.c new file mode 100644 index 0000000..0734322 --- /dev/null +++ b/examples/loadables/basename.c @@ -0,0 +1,132 @@ +/* basename - return nondirectory 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 +basename_builtin (list) + WORD_LIST *list; +{ + int slen, sufflen, off; + char *string, *suffix, *fn; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + string = list->word->word; + suffix = (char *)NULL; + if (list->next) + { + list = list->next; + suffix = list->word->word; + } + + if (list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + slen = strlen (string); + + /* Strip trailing slashes */ + while (slen > 0 && string[slen - 1] == '/') + slen--; + + /* (2) If string consists entirely of slash characters, string shall be + set to a single slash character. In this case, skip steps (3) + through (5). */ + if (slen == 0) + { + fputs ("/\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (3) If there are any trailing slash characters in string, they + shall be removed. */ + string[slen] = '\0'; + + /* (4) If there are any slash characters remaining in string, the prefix + of string up to an including the last slash character in string + shall be removed. */ + while (--slen >= 0) + if (string[slen] == '/') + break; + + fn = string + slen + 1; + + /* (5) If the suffix operand is present, is not identical to the + characters remaining in string, and is identical to a suffix + of the characters remaining in string, the suffix suffix + shall be removed from string. Otherwise, string shall not be + modified by this step. */ + if (suffix) + { + sufflen = strlen (suffix); + slen = strlen (fn); + if (sufflen < slen) + { + off = slen - sufflen; + if (strcmp (fn + off, suffix) == 0) + fn[off] = '\0'; + } + } + printf ("%s\n", fn); + return (EXECUTION_SUCCESS); +} + +char *basename_doc[] = { + "Return non-directory portion of pathname.", + "", + "The STRING is converted to a filename corresponding to the last", + "pathname component in STRING. If the suffix string SUFFIX is", + "supplied, it is removed.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin basename_struct = { + "basename", /* builtin name */ + basename_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + basename_doc, /* array of long documentation strings. */ + "basename string [suffix]", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c new file mode 100644 index 0000000..be99c4c --- /dev/null +++ b/examples/loadables/cat.c @@ -0,0 +1,122 @@ +/* + * cat replacement + * + * no options - the way cat was intended + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <fcntl.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +extern char *strerror (); +extern char **make_builtin_argv (); + +static int +fcopy(fd) +int fd; +{ + char buf[1024], *s; + int n, w, e; + + while (n = read(fd, buf, sizeof (buf))) { + w = write(1, buf, n); + if (w != n) { + e = errno; + write(2, "cat: write error: ", 18); + s = strerror(e); + write(2, s, strlen(s)); + write(2, "\n", 1); + return 1; + } + } + return 0; +} + +int +cat_main (argc, argv) +int argc; +char **argv; +{ + int i, fd, r; + char *s; + + if (argc == 1) + return (fcopy(0)); + + for (i = r = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '\0') + fd = 0; + else { + fd = open(argv[i], O_RDONLY, 0666); + if (fd < 0) { + s = strerror(errno); + write(2, "cat: cannot open ", 17); + write(2, argv[i], strlen(argv[i])); + write(2, ": ", 2); + write(2, s, strlen(s)); + write(2, "\n", 1); + continue; + } + } + r = fcopy(fd); + if (fd != 0) + close(fd); + } + return (r); +} + +int +cat_builtin(list) +WORD_LIST *list; +{ + char **v; + int c, r; + + v = make_builtin_argv(list, &c); + r = cat_main(c, v); + free(v); + + return r; +} + +char *cat_doc[] = { + "Display files.", + "", + "Read each FILE and display it on the standard output. If any", + "FILE is `-' or if no FILE argument is given, the standard input", + "is read.", + (char *)0 +}; + +struct builtin cat_struct = { + "cat", + cat_builtin, + BUILTIN_ENABLED, + cat_doc, + "cat [-] [file ...]", + 0 +}; diff --git a/examples/loadables/dirname.c b/examples/loadables/dirname.c new file mode 100644 index 0000000..d802ca7 --- /dev/null +++ b/examples/loadables/dirname.c @@ -0,0 +1,119 @@ +/* dirname - return directory portion of pathname */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" +#include "common.h" +#include "bashgetopt.h" + +int +dirname_builtin (list) + WORD_LIST *list; +{ + int slen; + char *string; + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + if (list == 0 || list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + string = list->word->word; + slen = strlen (string); + + /* Strip trailing slashes */ + while (slen > 0 && string[slen - 1] == '/') + slen--; + + /* (2) If string consists entirely of slash characters, string shall be + set to a single slash character. In this case, skip steps (3) + through (8). */ + if (slen == 0) + { + fputs ("/\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (3) If there are any trailing slash characters in string, they + shall be removed. */ + string[slen] = '\0'; + + /* (4) If there are no slash characters remaining in string, string + shall be set to a single period character. In this case, skip + steps (5) through (8). + + (5) If there are any trailing nonslash characters in string, + they shall be removed. */ + + while (--slen >= 0) + if (string[slen] == '/') + break; + + if (slen < 0) + { + fputs (".\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (7) If there are any trailing slash characters in string, they + shall be removed. */ + while (--slen >= 0) + if (string[slen] != '/') + break; + string[++slen] = '\0'; + + /* (8) If the remaining string is empty, string shall be set to a single + slash character. */ + printf ("%s\n", (slen == 0) ? "/" : string); + return (EXECUTION_SUCCESS); +} + +char *dirname_doc[] = { + "Display directory portion of pathname.", + "", + "The STRING is converted to the name of the directory containing", + "the filename corresponding to the last pathname component in STRING.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin dirname_struct = { + "dirname", /* builtin name */ + dirname_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + dirname_doc, /* array of long documentation strings. */ + "dirname string", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/fdflags.c b/examples/loadables/fdflags.c new file mode 100644 index 0000000..f309466 --- /dev/null +++ b/examples/loadables/fdflags.c @@ -0,0 +1,336 @@ +/* Loadable builtin to get and set file descriptor flags. */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2017 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" + +static const struct +{ + const char *name; + int value; +} file_flags[] = +{ +#ifdef O_APPEND + { "append", O_APPEND }, +#endif +#ifdef O_ASYNC + { "async", O_ASYNC }, +#endif +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NONBLOCK + { "nonblock", O_NONBLOCK }, +#endif +#ifdef O_FSYNC + { "fsync", O_FSYNC }, +#endif +#ifdef O_DSYNC + { "dsync", O_DSYNC }, +#endif +#ifdef O_RSYNC + { "rsync", O_RSYNC }, +#endif +#ifdef O_ALT_IO + { "altio", O_ALT_IO }, +#endif +#ifdef O_DIRECT + { "direct", O_DIRECT }, +#endif +#ifdef O_NOATIME + { "noatime", O_NOATIME }, +#endif +#ifdef O_NOSIGPIPE + { "nosigpipe", O_NOSIGPIPE }, +#endif +#ifdef O_CLOEXEC + { "cloexec", O_CLOEXEC }, +#endif +}; + +#define N_FLAGS (sizeof (file_flags) / sizeof (file_flags[0])) + +#ifndef errno +extern int errno; +#endif + +/* FIX THIS */ +static int +getallflags () +{ + int i, allflags; + + for (i = allflags = 0; i < N_FLAGS; i++) + allflags |= file_flags[i].value; + return allflags; +} + +static int +getflags(int fd, int p) +{ + int c, f; + int allflags; + + if ((c = fcntl(fd, F_GETFD)) == -1) + { + if (p) + builtin_error("can't get status for fd %d: %s", fd, strerror(errno)); + return -1; + } + + if ((f = fcntl(fd, F_GETFL)) == -1) + { + if (p) + builtin_error("Can't get flags for fd %d: %s", fd, strerror(errno)); + return -1; + } + + if (c) + f |= O_CLOEXEC; + + return f & getallflags(); +} + +static void +printone(int fd, int p, int verbose) +{ + int f; + size_t i; + + if ((f = getflags(fd, p)) == -1) + return; + + printf ("%d:", fd); + + for (i = 0; i < N_FLAGS; i++) + { + if (f & file_flags[i].value) + { + printf ("%s%s", verbose ? "+" : "", file_flags[i].name); + f &= ~file_flags[i].value; + } + else if (verbose) + printf ( "-%s", file_flags[i].name); + else + continue; + + if (f || (verbose && i != N_FLAGS - 1)) + putchar (','); + } + printf ("\n"); +} + +static int +parseflags(char *s, int *p, int *n) +{ + int f, *v; + size_t i; + + f = 0; + *p = *n = 0; + + for (s = strtok(s, ","); s; s = strtok(NULL, ",")) + { + switch (*s) + { + case '+': + v = p; + s++; + break; + case '-': + v = n; + s++; + break; + default: + v = &f; + break; + } + + for (i = 0; i < N_FLAGS; i++) + if (strcmp(s, file_flags[i].name) == 0) + { + *v |= file_flags[i].value; + break; + } + if (i == N_FLAGS) + builtin_error("invalid flag `%s'", s); + } + + return f; +} + +static void +setone(int fd, char *v, int verbose) +{ + int f, n, pos, neg, cloexec; + + f = getflags(fd, 1); + if (f == -1) + return; + + parseflags(v, &pos, &neg); + + cloexec = -1; + if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0) + cloexec = FD_CLOEXEC; + if ((neg & O_CLOEXEC) && (f & O_CLOEXEC)) + cloexec = 0; + if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1) + builtin_error("can't set status for fd %d: %s", fd, strerror(errno)); + + pos &= ~O_CLOEXEC; + neg &= ~O_CLOEXEC; + f &= ~O_CLOEXEC; + + n = f; + n |= pos; + n &= ~neg; + + if (n != f && fcntl(fd, F_SETFL, n) == -1) + builtin_error("can't set flags for fd %d: %s", fd, strerror(errno)); +} + +static int +getmaxfd () +{ + int maxfd, ignore; + +#ifdef F_MAXFD + maxfd = fcntl (0, F_MAXFD); + if (maxfd > 0) + return maxfd; +#endif + + maxfd = getdtablesize (); + if (maxfd <= 0) + maxfd = HIGH_FD_MAX; + for (maxfd--; maxfd > 0; maxfd--) + if (fcntl (maxfd, F_GETFD, &ignore) != -1) + break; + + return maxfd; +} + +int +fdflags_builtin (WORD_LIST *list) +{ + int opt, maxfd, i, num, verbose, setflag; + char *setspec; + WORD_LIST *l; + intmax_t inum; + + setflag = verbose = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "s:v")) != -1) + { + switch (opt) + { + case 's': + setflag = 1; + setspec = list_optarg; + break; + case 'v': + verbose = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + + } + list = loptend; + + /* Maybe we could provide some default here, but we don't yet. */ + if (list == 0 && setflag) + return (EXECUTION_SUCCESS); + + if (list == 0) + { + maxfd = getmaxfd (); + if (maxfd < 0) + { + builtin_error ("can't get max fd: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + for (i = 0; i < maxfd; i++) + printone (i, 0, verbose); + return (EXECUTION_SUCCESS); + } + + opt = EXECUTION_SUCCESS; + for (l = list; l; l = l->next) + { + if (legal_number (l->word->word, &inum) == 0 || inum < 0) + { + builtin_error ("%s: invalid file descriptor", l->word->word); + opt = EXECUTION_FAILURE; + continue; + } + num = inum; /* truncate to int */ + if (setflag) + setone (num, setspec, verbose); + else + printone (num, 1, verbose); + } + + return (opt); +} + +char *fdflags_doc[] = +{ + "Display and modify file descriptor flags.", + "", + "Display or, if the -s option is supplied, set flags for each file", + "descriptor supplied as an argument. If the -v option is supplied,", + "the display is verbose, including each settable option name in the", + "form of a string such as that accepted by the -s option.", + "", + "The -s option accepts a string with a list of flag names, each preceded", + "by a `+' (set) or `-' (unset). Those changes are applied to each file", + "descriptor supplied as an argument.", + "", + "If no file descriptor arguments are supplied, the displayed information", + "consists of the status of flags for each of the shell's open files.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin fdflags_struct = { + "fdflags", /* builtin name */ + fdflags_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + fdflags_doc, /* array of long documentation strings. */ + "fdflags [-v] [-s flags_string] [fd ...]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c new file mode 100644 index 0000000..4273aa5 --- /dev/null +++ b/examples/loadables/finfo.c @@ -0,0 +1,623 @@ +/* + * 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> +#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("%d\n", st->st_dev); + else if (flags & OPT_INO) + printf("%lu\n", (unsigned long)st->st_ino); + else if (flags & OPT_FID) + printf("%d:%lu\n", st->st_dev, (unsigned long)st->st_ino); + else if (flags & OPT_NLINK) + printf("%d\n", st->st_nlink); + else if (flags & OPT_LNKNAM) { +#ifdef S_ISLNK + b = xmalloc(4096); + p = readlink(f, b, 4096); + if (p >= 0 && p < 4096) + b[p] = '\0'; + else { + p = errno; + strcpy(b, prog); + strcat(b, ": "); + strcat(b, strerror(p)); + } + printf("%s\n", b); + free(b); +#else + printf("%s\n", f); +#endif + } else if (flags & OPT_PERM) { + perms(st->st_mode); + printf("\n"); + } else if (flags & OPT_OPERM) + printf("%o\n", getperm(st->st_mode)); + else if (flags & OPT_PMASK) + printf("%o\n", getperm(st->st_mode) & pmask); + else if (flags & OPT_UID) { + pw = getpwuid(st->st_uid); + if (flags & OPT_ASCII) + printf("%s\n", pw ? pw->pw_name : "unknown"); + else + printf("%d\n", st->st_uid); + } else if (flags & OPT_GID) { + gr = getgrgid(st->st_gid); + if (flags & OPT_ASCII) + printf("%s\n", gr ? gr->gr_name : "unknown"); + else + printf("%d\n", st->st_gid); + } else if (flags & OPT_SIZE) + printf("%ld\n", (long) st->st_size); + + return (0); +} + +#ifndef NOBUILTIN +int +finfo_builtin(list) + WORD_LIST *list; +{ + int c, r; + char **v; + WORD_LIST *l; + + v = make_builtin_argv (list, &c); + r = finfo_main (c, v); + free (v); + + return r; +} + +static char *finfo_doc[] = { + "Display information about file attributes.", + "", + "Display information about each FILE. Only single operators should", + "be supplied. If no options are supplied, a summary of the info", + "available about each FILE is printed. If FILE is of the form", + "/dev/fd/XX, file descriptor XX is described. Operators, if supplied,", + "have the following meanings:", + "", + " -a last file access time", + " -A last file access time in ctime format", + " -c last file status change time", + " -C last file status change time in ctime format", + " -m last file modification time", + " -M last file modification time in ctime format", + " -d device", + " -i inode", + " -f composite file identifier (device:inode)", + " -g gid of owner", + " -G group name of owner", + " -l name of file pointed to by symlink", + " -n link count", + " -o permissions in octal", + " -p permissions in ascii", + " -P mask permissions ANDed with MASK (like with umask)", + " -s file size in bytes", + " -u uid of owner", + " -U user name of owner", + (char *)0 +}; + +struct builtin finfo_struct = { + "finfo", + finfo_builtin, + BUILTIN_ENABLED, + finfo_doc, + "finfo [-acdgiflmnopsuACGMPU] file [file...]", + 0 +}; +#endif + +#ifdef NOBUILTIN +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# if defined (PREFER_VARARGS) +# include <varargs.h> +# endif +#endif + +char *this_command_name; + +main(argc, argv) +int argc; +char **argv; +{ + this_command_name = argv[0]; + exit(finfo_main(argc, argv)); +} + +void +builtin_usage() +{ + fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, prog, OPTIONS); +} + +#ifndef HAVE_STRERROR +char * +strerror(e) +int e; +{ + static char ebuf[40]; + extern int sys_nerr; + extern char *sys_errlist[]; + + if (e < 0 || e > sys_nerr) { + sprintf(ebuf,"Unknown error code %d", e); + return (&ebuf[0]); + } + return (sys_errlist[e]); +} +#endif + +char * +xmalloc(s) +size_t s; +{ + char *ret; + extern char *malloc(); + + ret = malloc(s); + if (ret) + return (ret); + fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s); + exit(1); +} + +char * +base_pathname(p) +char *p; +{ + char *t; + + if (t = strrchr(p, '/')) + return(++t); + return(p); +} + +int +legal_number (string, result) + char *string; + long *result; +{ + int sign; + long value; + + sign = 1; + value = 0; + + if (result) + *result = 0; + + /* Skip leading whitespace characters. */ + while (whitespace (*string)) + string++; + + if (!*string) + return (0); + + /* We allow leading `-' or `+'. */ + if (*string == '-' || *string == '+') + { + if (!digit (string[1])) + return (0); + + if (*string == '-') + sign = -1; + + string++; + } + + while (digit (*string)) + { + if (result) + value = (value * 10) + digit_value (*string); + string++; + } + + /* Skip trailing whitespace, if any. */ + while (whitespace (*string)) + string++; + + /* Error if not at end of string. */ + if (*string) + return (0); + + if (result) + *result = value * sign; + + return (1); +} + +int sh_optind; +char *sh_optarg; +int sh_opterr; + +extern int optind; +extern char *optarg; + +int +sh_getopt(c, v, o) +int c; +char **v, *o; +{ + int r; + + r = getopt(c, v, o); + sh_optind = optind; + sh_optarg = optarg; + return r; +} + +#if defined (USE_VARARGS) +void +#if defined (PREFER_STDARG) +builtin_error (const char *format, ...) +#else +builtin_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); +#endif + + vfprintf (stderr, format, args); + va_end (args); + fprintf (stderr, "\n"); +} +#else +void +builtin_error (format, arg1, arg2, arg3, arg4, arg5) + char *format, *arg1, *arg2, *arg3, *arg4, *arg5; +{ + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + + fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif /* !USE_VARARGS */ + +#endif diff --git a/examples/loadables/head.c b/examples/loadables/head.c new file mode 100644 index 0000000..1edca6c --- /dev/null +++ b/examples/loadables/head.c @@ -0,0 +1,167 @@ +/* head - copy first part of files. */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> +#include "chartypes.h" + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +static void +munge_list (list) + WORD_LIST *list; +{ + WORD_LIST *l, *nl; + WORD_DESC *wd; + char *arg; + + for (l = list; l; l = l->next) + { + arg = l->word->word; + if (arg[0] != '-' || arg[1] == '-' || (DIGIT(arg[1]) == 0)) + return; + /* We have -[0-9]* */ + wd = make_bare_word (arg+1); + nl = make_word_list (wd, l->next); + l->word->word[1] = 'n'; + l->word->word[2] = '\0'; + l->next = nl; + l = nl; /* skip over new argument */ + } +} + +static int +file_head (fp, cnt) + FILE *fp; + int cnt; +{ + int ch; + + while (cnt--) + { + while ((ch = getc (fp)) != EOF) + { + if (putchar (ch) == EOF) + { + builtin_error ("write error: %s", strerror (errno)); + return EXECUTION_FAILURE; + } + if (ch == '\n') + break; + } + } + return (EXECUTION_SUCCESS); +} + +int +head_builtin (list) + WORD_LIST *list; +{ + int nline, opt, rval; + WORD_LIST *l; + FILE *fp; + + char *t; + + munge_list (list); /* change -num into -n num */ + + reset_internal_getopt (); + nline = 10; + while ((opt = internal_getopt (list, "n:")) != -1) + { + switch (opt) + { + case 'n': + nline = atoi (list_optarg); + if (nline <= 0) + { + builtin_error ("bad line count: %s", list_optarg); + return (EX_USAGE); + } + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + return (file_head (stdin, nline)); + + for (rval = EXECUTION_SUCCESS, opt = 1, l = list; l; l = l->next) + { + fp = fopen (l->word->word, "r"); + if (fp == NULL) + { + builtin_error ("%s: %s", l->word->word, strerror (errno)); + continue; + } + if (list->next) /* more than one file */ + { + printf ("%s==> %s <==\n", opt ? "" : "\n", l->word->word); + opt = 0; + } + rval = file_head (fp, nline); + fclose (fp); + } + + return (rval); +} + +char *head_doc[] = { + "Display lines from beginning of file.", + "", + "Copy the first N lines from the input files to the standard output.", + "N is supplied as an argument to the `-n' option. If N is not given,", + "the first ten lines are copied.", + (char *)NULL +}; + +struct builtin head_struct = { + "head", /* builtin name */ + head_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + head_doc, /* array of long documentation strings. */ + "head [-n num] [file ...]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/hello.c b/examples/loadables/hello.c new file mode 100644 index 0000000..b09362b --- /dev/null +++ b/examples/loadables/hello.c @@ -0,0 +1,96 @@ +/* Sample builtin to be dynamically loaded with enable -f and create a new + builtin. */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "loadables.h" + +/* A builtin `xxx' is normally implemented with an `xxx_builtin' function. + If you're converting a command that uses the normal Unix argc/argv + calling convention, use argv = make_builtin_argv (list, &argc) and call + the original `main' something like `xxx_main'. Look at cat.c for an + example. + + Builtins should use internal_getopt to parse options. It is the same as + getopt(3), but it takes a WORD_LIST *. Look at print.c for an example + of its use. + + If the builtin takes no options, call no_options(list) before doing + anything else. If it returns a non-zero value, your builtin should + immediately return EX_USAGE. Look at logname.c for an example. + + A builtin command returns EXECUTION_SUCCESS for success and + EXECUTION_FAILURE to indicate failure. */ +int +hello_builtin (list) + WORD_LIST *list; +{ + printf("hello world\n"); + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +int +hello_builtin_load (s) + char *s; +{ + printf ("hello builtin loaded\n"); + fflush (stdout); + return (1); +} + +void +hello_builtin_unload (s) + char *s; +{ + printf ("hello builtin unloaded\n"); + fflush (stdout); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. By convention, + the first line is a short description. */ +char *hello_doc[] = { + "Sample builtin.", + "", + "this is the long doc for the sample hello builtin", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin hello_struct = { + "hello", /* builtin name */ + hello_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + hello_doc, /* array of long documentation strings. */ + "hello", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/id.c b/examples/loadables/id.c new file mode 100644 index 0000000..f857b54 --- /dev/null +++ b/examples/loadables/id.c @@ -0,0 +1,329 @@ +/* + * id - POSIX.2 user identity + * + * (INCOMPLETE -- supplementary groups for other users not yet done) + * + * usage: id [-Ggu] [-nr] [user] + * + * The default output format looks something like: + * uid=xxx(chet) gid=xx groups=aa(aname), bb(bname), cc(cname) + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <stdio.h> +#include "bashtypes.h" +#include <pwd.h> +#include <grp.h> +#include "bashansi.h" + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#else +# include <sys/param.h> +#endif + +#if !defined (HAVE_GETPW_DECLS) +extern struct passwd *getpwuid (); +#endif +extern struct group *getgrgid (); + +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "common.h" +#include "bashgetopt.h" + +#define ID_ALLGROUPS 0x001 /* -G */ +#define ID_GIDONLY 0x002 /* -g */ +#define ID_USENAME 0x004 /* -n */ +#define ID_USEREAL 0x008 /* -r */ +#define ID_USERONLY 0x010 /* -u */ + +#define ID_FLAGSET(s) ((id_flags & (s)) != 0) + +static int id_flags; + +static uid_t ruid, euid; +static gid_t rgid, egid; + +static char *id_user; + +static int inituser (); + +static int id_pruser (); +static int id_prgrp (); +static int id_prgroups (); +static int id_prall (); + +int +id_builtin (list) + WORD_LIST *list; +{ + int opt; + char *user; + + id_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Ggnru")) != -1) + { + switch (opt) + { + case 'G': id_flags |= ID_ALLGROUPS; break; + case 'g': id_flags |= ID_GIDONLY; break; + case 'n': id_flags |= ID_USENAME; break; + case 'r': id_flags |= ID_USEREAL; break; + case 'u': id_flags |= ID_USERONLY; break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + user = list ? list->word->word : (char *)NULL; + + /* Check for some invalid option combinations */ + opt = ID_FLAGSET (ID_ALLGROUPS) + ID_FLAGSET (ID_GIDONLY) + ID_FLAGSET (ID_USERONLY); + if (opt > 1 || (opt == 0 && ((id_flags & (ID_USEREAL|ID_USENAME)) != 0))) + { + builtin_usage (); + return (EX_USAGE); + } + + if (list && list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + if (inituser (user) < 0) + return (EXECUTION_FAILURE); + + opt = 0; + if (id_flags & ID_USERONLY) + opt += id_pruser ((id_flags & ID_USEREAL) ? ruid : euid); + else if (id_flags & ID_GIDONLY) + opt += id_prgrp ((id_flags & ID_USEREAL) ? rgid : egid); + else if (id_flags & ID_ALLGROUPS) + opt += id_prgroups (user); + else + opt += id_prall (user); + putchar ('\n'); + fflush (stdout); + + return (opt == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +static int +inituser (uname) + char *uname; +{ + struct passwd *pwd; + + if (uname) + { + pwd = getpwnam (uname); + if (pwd == 0) + { + builtin_error ("%s: no such user", uname); + return -1; + } + ruid = euid = pwd->pw_uid; + rgid = egid = pwd->pw_gid; + } + else + { + ruid = current_user.uid; + euid = current_user.euid; + rgid = current_user.gid; + egid = current_user.egid; + } + return 0; +} + +/* Print the name or value of user ID UID. */ +static int +id_pruser (uid) + int uid; +{ + struct passwd *pwd = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + pwd = getpwuid (uid); + if (pwd == NULL) + r = 1; + } + if (pwd) + printf ("%s", pwd->pw_name); + else + printf ("%u", (unsigned) uid); + + return r; +} + +/* Print the name or value of group ID GID. */ + +static int +id_prgrp (gid) + int gid; +{ + struct group *grp = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + grp = getgrgid (gid); + if (grp == NULL) + r = 1; + } + + if (grp) + printf ("%s", grp->gr_name); + else + printf ("%u", (unsigned) gid); + + return r; +} + +static int +id_prgroups (uname) + char *uname; +{ + int *glist, ng, i, r; + + r = 0; + id_prgrp (rgid); + if (egid != rgid) + { + putchar (' '); + id_prgrp (egid); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + for (i = 0; i < ng; i++) + if (glist[i] != rgid && glist[i] != egid) + { + putchar (' '); + id_prgrp (glist[i]); + } + + return r; +} + +static int +id_prall (uname) + char *uname; +{ + int r, i, ng, *glist; + struct passwd *pwd; + struct group *grp; + + r = 0; + printf ("uid=%u", (unsigned) ruid); + pwd = getpwuid (ruid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + + printf (" gid=%u", (unsigned) rgid); + grp = getgrgid (rgid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + + if (euid != ruid) + { + printf (" euid=%u", (unsigned) euid); + pwd = getpwuid (euid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + } + + if (egid != rgid) + { + printf (" egid=%u", (unsigned) egid); + grp = getgrgid (egid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + if (ng > 0) + printf (" groups="); + for (i = 0; i < ng; i++) + { + if (i > 0) + printf (", "); + printf ("%u", (unsigned) glist[i]); + grp = getgrgid (glist[i]); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + return r; +} + +char *id_doc[] = { + "Display information about user." + "", + "Return information about user identity", + (char *)NULL +}; + +struct builtin id_struct = { + "id", + id_builtin, + BUILTIN_ENABLED, + id_doc, + "id [user]\n\tid -G [-n] [user]\n\tid -g [-nr] [user]\n\tid -u [-nr] [user]", + 0 +}; diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c new file mode 100644 index 0000000..93764a3 --- /dev/null +++ b/examples/loadables/ln.c @@ -0,0 +1,236 @@ +/* ln - make links */ + +/* 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" + +#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 __P((const char *, const char *)); + +#define LN_SYMLINK 0x01 +#define LN_UNLINK 0x02 +#define LN_NOFOLLOW 0x04 + +static unix_link_syscall_t *linkfn; +static int dolink (); + +int +ln_builtin (list) + WORD_LIST *list; +{ + int rval, opt, flags; + WORD_LIST *l; + char *sdir; + struct stat sb; + + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fs")) != -1) + { + switch (opt) + { + case 'f': + flags |= LN_UNLINK; + break; + case 's': + flags |= LN_SYMLINK; + break; + case 'h': + case 'n': + flags |= LN_NOFOLLOW; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + linkfn = (flags & LN_SYMLINK) ? symlink : link; + + if (list->next == 0) /* ln target, equivalent to ln target . */ + return (dolink (list->word->word, ".", flags)); + + if (list->next->next == 0) /* ln target source */ + return (dolink (list->word->word, list->next->word->word, flags)); + + /* ln target1 target2 ... directory */ + + /* find last argument: target directory, and make sure it's an existing + directory. */ + for (l = list; l->next; l = l->next) + ; + sdir = l->word->word; + + if (stat(sdir, &sb) < 0) + { + builtin_error ("%s", sdir); + return (EXECUTION_FAILURE); + } + + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + for (rval = EXECUTION_SUCCESS; list != l; list = list->next) + rval += dolink (list->word->word, sdir, flags); + + return rval; +} + +static char * +mkdirpath (dir, file) + char *dir, *file; +{ + int dlen, flen; + char *ret; + + dlen = strlen (dir); + flen = strlen (file); + + ret = xmalloc (2 + dlen + flen); + + strcpy (ret, dir); + if (ret[dlen - 1] != '/') + ret[dlen++] = '/'; + strcpy (ret + dlen, file); + return ret; +} + +#if defined (HAVE_LSTAT) +# define LSTAT lstat +# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b))) +#else +# define LSTAT stat +# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b))) +#endif + +static int +dolink (src, dst, flags) + char *src, *dst; + int flags; +{ + struct stat ssb, dsb; + int exists; + char *dst_path, *p; + + /* If we're not doing symlinks, the source must exist and not be a + directory. */ + if ((flags & LN_SYMLINK) == 0) + { + if (stat (src, &ssb) != 0) + { + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + if (S_ISDIR (ssb.st_mode)) + { + errno = EISDIR; + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + } + + /* If the destination is a directory, create the final filename by appending + the basename of the source to the destination. */ + dst_path = 0; + if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode)) + { + if ((p = strrchr (src, '/')) == 0) + p = src; + else + p++; + + dst_path = mkdirpath (dst, p); + dst = dst_path; + } + + exists = LSTAT (dst, &dsb) == 0; + + /* If -f was specified, and the destination exists, unlink it. */ + if ((flags & LN_UNLINK) && exists && unlink (dst) != 0) + { + builtin_error ("%s: cannot unlink: %s", dst, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + /* Perform the link. */ + if ((*linkfn) (src, dst) != 0) + { + builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + FREE (dst_path); + return (EXECUTION_SUCCESS); +} + +char *ln_doc[] = { + "Link files.", + "", + "Create a new directory entry with the same modes as the original", + "file. The -f option means to unlink any existing file, permitting", + "the link to occur. The -s option means to create a symbolic link.", + "By default, ln makes hard links. Specifying -n or its synonym -h", + "causes ln to not resolve symlinks in the target file or directory.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin ln_struct = { + "ln", /* builtin name */ + ln_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + ln_doc, /* array of long documentation strings. */ + "ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/loadables.h b/examples/loadables/loadables.h new file mode 100644 index 0000000..c730357 --- /dev/null +++ b/examples/loadables/loadables.h @@ -0,0 +1,34 @@ +/* loadables.h -- Include files needed by all loadable builtins */ + +/* Copyright (C) 2015 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef __LOADABLES_H_ +#define __LOADABLES_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#endif diff --git a/examples/loadables/logname.c b/examples/loadables/logname.c new file mode 100644 index 0000000..27e6591 --- /dev/null +++ b/examples/loadables/logname.c @@ -0,0 +1,74 @@ +/* logname - print login name of current user */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +int +logname_builtin (list) + WORD_LIST *list; +{ + char *np; + + if (no_options (list)) + return (EX_USAGE); + + np = getlogin (); + if (np == 0) + { + builtin_error ("cannot find username: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + printf ("%s\n", np); + return (EXECUTION_SUCCESS); +} + +char *logname_doc[] = { + "Display user login name.", + "", + "Write the current user's login name to the standard output", + "and exit. logname ignores the LOGNAME and USER variables.", + "logname ignores any non-option arguments.", + (char *)NULL +}; + +struct builtin logname_struct = { + "logname", + logname_builtin, + BUILTIN_ENABLED, + logname_doc, + "logname", + 0 +}; + diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c new file mode 100644 index 0000000..b381119 --- /dev/null +++ b/examples/loadables/mkdir.c @@ -0,0 +1,245 @@ +/* mkdir - make directories */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "bashtypes.h" +#include "posixstat.h" +#include <errno.h> +#include <stdio.h> +#include "bashansi.h" +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +#define ISOCTAL(c) ((c) >= '0' && (c) <= '7') + +extern int parse_symbolic_mode (); + +static int make_path (); + +static int original_umask; + +int +mkdir_builtin (list) + WORD_LIST *list; +{ + int opt, pflag, mflag, omode, rval, nmode, parent_mode; + char *mode; + WORD_LIST *l; + + reset_internal_getopt (); + pflag = mflag = 0; + mode = (char *)NULL; + while ((opt = internal_getopt(list, "m:p")) != -1) + switch (opt) + { + case 'p': + pflag = 1; + break; + case 'm': + mflag = 1; + mode = list_optarg; + break; + CASE_HELPOPT; + default: + builtin_usage(); + return (EX_USAGE); + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (mode == NULL) + omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */ + else if (ISOCTAL (*mode)) /* octal number */ + { + omode = read_octal (mode); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + } + else if (mode) + { + /* initial bits are a=rwx; the mode argument modifies them */ + omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + } + + /* Make the new mode */ + original_umask = umask (0); + umask (original_umask); + + nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask; + parent_mode = nmode | (S_IWUSR|S_IXUSR); /* u+wx */ + + /* Adjust new mode based on mode argument */ + nmode &= omode; + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + if (pflag && make_path (l->word->word, mflag, nmode, parent_mode)) + { + rval = EXECUTION_FAILURE; + continue; + } + else if (pflag == 0 && mkdir (l->word->word, nmode) < 0) + { + builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + } + return rval; +} + +/* Make all the directories leading up to PATH, then create PATH. Note that + this changes the process's umask; make sure that all paths leading to a + return reset it to ORIGINAL_UMASK */ +static int +make_path (path, user_mode, nmode, parent_mode) + char *path; + int user_mode; + int nmode, parent_mode; +{ + int oumask; + struct stat sb; + char *p, *npath; + + if (stat (path, &sb) == 0) + { + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", path); + return 1; + } + + if (user_mode && chmod (path, nmode)) + { + builtin_error ("%s: %s", path, strerror (errno)); + return 1; + } + + return 0; + } + + oumask = umask (0); + npath = savestring (path); /* So we can write to it. */ + + /* Check whether or not we need to do anything with intermediate dirs. */ + + /* Skip leading slashes. */ + p = npath; + while (*p == '/') + p++; + + while (p = strchr (p, '/')) + { + *p = '\0'; + if (stat (npath, &sb) != 0) + { + if (mkdir (npath, 0)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + if (chmod (npath, parent_mode) != 0) + { + builtin_error ("cannot chmod directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + } + else if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", npath); + umask (original_umask); + free (npath); + return 1; + } + + *p++ = '/'; /* restore slash */ + while (*p == '/') + p++; + } + + /* Create the final directory component. */ + if (stat (npath, &sb) && mkdir (npath, nmode)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + + umask (original_umask); + free (npath); + return 0; +} + +char *mkdir_doc[] = { + "Create directories.", + "", + "Make directories. Create the directories named as arguments, in", + "the order specified, using mode rwxrwxrwx as modified by the current", + "umask (see `help umask'). The -m option causes the file permission", + "bits of the final directory to be MODE. The MODE argument may be", + "an octal number or a symbolic mode like that used by chmod(1). If", + "a symbolic mode is used, the operations are interpreted relative to", + "an initial mode of \"a=rwx\". The -p option causes any required", + "intermediate directories in PATH to be created. The directories", + "are created with permssion bits of rwxrwxrwx as modified by the current", + "umask, plus write and search permissions for the owner. mkdir", + "returns 0 if the directories are created successfully, and non-zero", + "if an error occurs.", + (char *)NULL +}; + +struct builtin mkdir_struct = { + "mkdir", + mkdir_builtin, + BUILTIN_ENABLED, + mkdir_doc, + "mkdir [-p] [-m mode] directory [directory ...]", + 0 +}; diff --git a/examples/loadables/mypid.c b/examples/loadables/mypid.c new file mode 100644 index 0000000..fc1b267 --- /dev/null +++ b/examples/loadables/mypid.c @@ -0,0 +1,89 @@ +/* This module should be dynamically loaded with enable -f + * which would create a new builtin named mypid. You'll need + * the source code for GNU bash to recompile this module. + * + * Then, from within bash, enable -f ./mypid enable_mypid, where ./mypid + * is the binary obtained from running make. Hereafter, `${MYPID}' + * is a shell builtin variable. + * + * This defines an unload hook function that is called when the builtin is + * deleted with enable -d that will unbind the MYPID variable so future + * references to it do not attempt to access memory that is no longer part + * of this process's address space. + */ +#include <config.h> + +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "builtins.h" +#include "shell.h" + +#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \ + do \ + { SHELL_VAR *v = bind_variable (var, (val), 0); \ + if (v) \ + { \ + v->dynamic_value = gfunc; \ + v->assign_func = afunc; \ + } \ + } \ + while (0) + +static SHELL_VAR * +assign_mypid ( + SHELL_VAR *self, + char *value, + arrayind_t unused, + char *key ) +{ + return (self); +} + +static SHELL_VAR * +get_mypid (SHELL_VAR *var) +{ + int rv; + char *p; + + rv = getpid(); + p = itos (rv); + + FREE (value_cell (var)); + + VSETATTR (var, att_integer); + var_setvalue (var, p); + return (var); +} + +int +enable_mypid_builtin(WORD_LIST *list) +{ + INIT_DYNAMIC_VAR ("MYPID", (char *)NULL, get_mypid, assign_mypid); + + return 0; +} + +void +enable_mypid_builtin_unload (char *s) +{ + unbind_variable ("MYPID"); +} + +char const *enable_mypid_doc[] = { + "Enable $MYPID.", + "", + "Enables use of the ${MYPID} dynamic variable. ", + "It will yield the current pid of a subshell.", + (char *)0 +}; + +struct builtin enable_mypid_struct = { + "enable_mypid", + enable_mypid_builtin, + BUILTIN_ENABLED, + (char**)(void*)enable_mypid_doc, + "enable_mypid N", + 0 +}; diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c new file mode 100644 index 0000000..dd2092b --- /dev/null +++ b/examples/loadables/necho.c @@ -0,0 +1,54 @@ +/* necho - echo without options or argument interpretation */ + +/* Sample builtin to be dynamically loaded with enable -f and replace an + existing builtin. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" + +int +necho_builtin (list) +WORD_LIST *list; +{ + print_word_list (list, " "); + printf("\n"); + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +char *necho_doc[] = { + "Display arguments.", + "", + "Print the arguments to the standard output separated", + "by space characters and terminated with a newline.", + (char *)NULL +}; + +struct builtin necho_struct = { + "echo", + necho_builtin, + BUILTIN_ENABLED, + necho_doc, + "echo [args]", + 0 +}; + diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c new file mode 100644 index 0000000..c1151db --- /dev/null +++ b/examples/loadables/pathchk.c @@ -0,0 +1,381 @@ +/* pathchk - check pathnames for validity and portability */ + +/* Usage: pathchk [-p] path ... + + For each PATH, print a message if any of these conditions are false: + * all existing leading directories in PATH have search (execute) permission + * strlen (PATH) <= PATH_MAX + * strlen (each_directory_in_PATH) <= NAME_MAX + + Exit status: + 0 All PATH names passed all of the tests. + 1 An error occurred. + + Options: + -p Instead of performing length checks on the + underlying filesystem, test the length of the + pathname and its components against the POSIX.1 + minimum limits for portability, _POSIX_NAME_MAX + and _POSIX_PATH_MAX in 2.9.2. Also check that + the pathname contains no character not in the + portable filename character set. */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> +#include "posixstat.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "stdc.h" +#include "bashgetopt.h" +#include "maxpath.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +#if !defined (_POSIX_PATH_MAX) +# define _POSIX_PATH_MAX 255 +#endif +#if !defined (_POSIX_NAME_MAX) +# define _POSIX_NAME_MAX 14 +#endif + +/* How do we get PATH_MAX? */ +#if defined (_POSIX_VERSION) && !defined (PATH_MAX) +# define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX) +#endif + +/* How do we get NAME_MAX? */ +#if defined (_POSIX_VERSION) && !defined (NAME_MAX) +# define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX) +#endif + +#if !defined (PATH_MAX_FOR) +# define PATH_MAX_FOR(p) PATH_MAX +#endif + +#if !defined (NAME_MAX_FOR) +# define NAME_MAX_FOR(p) NAME_MAX +#endif + +extern char *strerror (); + +static int validate_path (); + +int +pathchk_builtin (list) + WORD_LIST *list; +{ + int retval, pflag, opt; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "p")) != -1) + { + switch (opt) + { + case 'p': + pflag = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + for (retval = 0; list; list = list->next) + retval |= validate_path (list->word->word, pflag); + + return (retval ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +char *pathchk_doc[] = { + "Check pathnames for validity.", + "", + "Check each pathname argument for validity (i.e., it may be used to", + "create or access a file without causing syntax errors) and portability", + "(i.e., no filename truncation will result). If the `-p' option is", + "supplied, more extensive portability checks are performed.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin pathchk_struct = { + "pathchk", /* builtin name */ + pathchk_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + pathchk_doc, /* array of long documentation strings. */ + "pathchk [-p] pathname ...", /* usage synopsis */ + 0 /* reserved for internal use */ +}; + +/* The remainder of this file is stolen shamelessly from `pathchk.c' in + the sh-utils-1.12 distribution, by + + David MacKenzie <djm@gnu.ai.mit.edu> + and Jim Meyering <meyering@cs.utexas.edu> */ + +/* Each element is nonzero if the corresponding ASCII character is + in the POSIX portable character set, and zero if it is not. + In addition, the entry for `/' is nonzero to simplify checking. */ +static char const portable_chars[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* If PATH contains only portable characters, return 1, else 0. */ + +static int +portable_chars_only (path) + const char *path; +{ + const char *p; + + for (p = path; *p; ++p) + if (portable_chars[(const unsigned char) *p] == 0) + { + builtin_error ("path `%s' contains nonportable character `%c'", path, *p); + return 0; + } + return 1; +} + +/* On some systems, stat can return EINTR. */ + +#ifndef EINTR +# define SAFE_STAT(name, buf) stat (name, buf) +#else +# define SAFE_STAT(name, buf) safe_stat (name, buf) +static inline int +safe_stat (name, buf) + const char *name; + struct stat *buf; +{ + int ret; + + do + ret = stat (name, buf); + while (ret < 0 && errno == EINTR); + + return ret; +} +#endif + +/* Return 1 if PATH is a usable leading directory, 0 if not, + 2 if it doesn't exist. */ + +static int +dir_ok (path) + const char *path; +{ + struct stat stats; + + if (SAFE_STAT (path, &stats)) + return 2; + + if (!S_ISDIR (stats.st_mode)) + { + builtin_error ("`%s' is not a directory", path); + return 0; + } + + /* Use access to test for search permission because + testing permission bits of st_mode can lose with new + access control mechanisms. Of course, access loses if you're + running setuid. */ + if (access (path, X_OK) != 0) + { + if (errno == EACCES) + builtin_error ("directory `%s' is not searchable", path); + else + builtin_error ("%s: %s", path, strerror (errno)); + return 0; + } + + return 1; +} + +static char * +xstrdup (s) + char *s; +{ + return (savestring (s)); +} + +/* Make sure that + strlen (PATH) <= PATH_MAX + && strlen (each-existing-directory-in-PATH) <= NAME_MAX + + If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and + _POSIX_NAME_MAX instead, and make sure that PATH contains no + characters not in the POSIX portable filename character set, which + consists of A-Z, a-z, 0-9, ., _, -. + + Make sure that all leading directories along PATH that exist have + `x' permission. + + Return 0 if all of these tests are successful, 1 if any fail. */ + +static int +validate_path (path, portability) + char *path; + int portability; +{ + int path_max; + int last_elem; /* Nonzero if checking last element of path. */ + int exists; /* 2 if the path element exists. */ + char *slash; + char *parent; /* Last existing leading directory so far. */ + + if (portability && !portable_chars_only (path)) + return 1; + + if (*path == '\0') + return 0; + +#ifdef lint + /* Suppress `used before initialized' warning. */ + exists = 0; +#endif + + /* Figure out the parent of the first element in PATH. */ + parent = xstrdup (*path == '/' ? "/" : "."); + + slash = path; + last_elem = 0; + while (1) + { + int name_max; + int length; /* Length of partial path being checked. */ + char *start; /* Start of path element being checked. */ + + /* Find the end of this element of the path. + Then chop off the rest of the path after this element. */ + while (*slash == '/') + slash++; + start = slash; + slash = strchr (slash, '/'); + if (slash != NULL) + *slash = '\0'; + else + { + last_elem = 1; + slash = strchr (start, '\0'); + } + + if (!last_elem) + { + exists = dir_ok (path); + if (exists == 0) + { + free (parent); + return 1; + } + } + + length = slash - start; + /* Since we know that `parent' is a directory, it's ok to call + pathconf with it as the argument. (If `parent' isn't a directory + or doesn't exist, the behavior of pathconf is undefined.) + But if `parent' is a directory and is on a remote file system, + it's likely that pathconf can't give us a reasonable value + and will return -1. (NFS and tempfs are not POSIX . . .) + In that case, we have no choice but to assume the pessimal + POSIX minimums. */ + name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent); + if (name_max < 0) + name_max = _POSIX_NAME_MAX; + if (length > name_max) + { + builtin_error ("name `%s' has length %d; exceeds limit of %d", + start, length, name_max); + free (parent); + return 1; + } + + if (last_elem) + break; + + if (exists == 1) + { + free (parent); + parent = xstrdup (path); + } + + *slash++ = '/'; + } + + /* `parent' is now the last existing leading directory in the whole path, + so it's ok to call pathconf with it as the argument. */ + path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent); + if (path_max < 0) + path_max = _POSIX_PATH_MAX; + free (parent); + if (strlen (path) > path_max) + { + builtin_error ("path `%s' has length %lu; exceeds limit of %d", + path, (unsigned long)strlen (path), path_max); + return 1; + } + + return 0; +} diff --git a/examples/loadables/perl/Makefile.in b/examples/loadables/perl/Makefile.in new file mode 100644 index 0000000..59f39b6 --- /dev/null +++ b/examples/loadables/perl/Makefile.in @@ -0,0 +1,99 @@ +# +# Makefile for builtin perl interpreter +# +# +# Copyright (C) 1998 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# Include some boilerplate Gnu makefile definitions. +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +libdir = @libdir@ +infodir = @infodir@ +includedir = @includedir@ + +datarootdir = @datarootdir@ + +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +@SET_MAKE@ +CC = @CC@ +RM = rm -f + +SHELL = @MAKE_SHELL@ + +PERL5 = perl5 + +CFLAGS = @CFLAGS@ + +# +# These values are generated for configure by ${topdir}/support/shobj-conf. +# If your system is not supported by that script, but includes facilities for +# dynamic loading of shared objects, please update the script and send the +# changes to bash-maintainers@gnu.org. +# +SHOBJ_CC = @SHOBJ_CC@ +SHOBJ_CFLAGS = @SHOBJ_CFLAGS@ +SHOBJ_LD = @SHOBJ_LD@ +SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ +SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@ +SHOBJ_LIBS = @SHOBJ_LIBS@ +SHOBJ_STATUS = @SHOBJ_STATUS@ + +# Values used for compiling the perl files +PERL_LDOPTS = `${PERL5} -MExtUtils::Embed -e ldopts` +PERL_CFLAGS = ${CCFLAGS} `${PERL5} -MExtUtils::Embed -e ccopts` + +SRC = bperl.c iperl.c perlxsi.c +OBJ = bperl.o iperl.o perlxsi.o + +BUILTIN = bperl5 + +INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \ + -I$(topdir)/include -I$(BUILD_DIR) -I$(BUILD_DIR)/lib \ + -I$(BUILD_DIR)/builtins + + +${BUILTIN}: ${OBJ} + ${RM} $@ + ${SHOBJ_LD} ${SHOBJ_LDFLAGS} ${SHOBJ_XLDFLAGS} -o $@ ${OBJ} ${PERL_LDOPTS} ${SHOBJ_LIBS} + +bperl.o: bperl.c + ${RM} $@ + $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ ${srcdir}/bperl.c + +iperl.o: iperl.c + ${RM} $@ + $(SHOBJ_CC) ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ ${srcdir}/iperl.c + +perlxsi.c: + ${PERL5} -MExtUtils::Embed -e xsinit -- -o $@ + +perlxsi.o: perlxsi.c + ${RM} $@ + ${SHOBJ_CC} ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ perlxsi.c + +clean mostlyclean: + ${RM} ${OBJ} + ${RM} ${BUILTIN} + +distclean maintainer-clean: clean + ${RM} perlxsi.c diff --git a/examples/loadables/perl/README b/examples/loadables/perl/README new file mode 100644 index 0000000..a70a99b --- /dev/null +++ b/examples/loadables/perl/README @@ -0,0 +1,6 @@ +This illustrates how to build a perl interpreter into bash. It's not +especially useful; more a proof of concept (it provides none of the +bash internals to the perl interpreter, for example). + +This *may* require adding "-rpath /path/to/perl/CORE" and -lperl options +when compiling bash itself. diff --git a/examples/loadables/perl/bperl.c b/examples/loadables/perl/bperl.c new file mode 100644 index 0000000..77e3f7c --- /dev/null +++ b/examples/loadables/perl/bperl.c @@ -0,0 +1,46 @@ +/* + * perl builtin + */ +#include <config.h> + +#include <fcntl.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +extern char **make_builtin_argv (); +extern char **export_env; + +extern int perl_main(); + +bperl_builtin(list) +WORD_LIST *list; +{ + char **v; + int c, r; + + v = make_builtin_argv(list, &c); + r = perl_main(c, v, export_env); + free(v); + + return r; +} + +char *bperl_doc[] = { + "An interface to a perl5 interpreter.", + (char *)0 +}; + +struct builtin bperl_struct = { + "bperl", + bperl_builtin, + BUILTIN_ENABLED, + bperl_doc, + "bperl [perl options] [file ...]", + 0 +}; diff --git a/examples/loadables/perl/iperl.c b/examples/loadables/perl/iperl.c new file mode 100644 index 0000000..92a6038 --- /dev/null +++ b/examples/loadables/perl/iperl.c @@ -0,0 +1,24 @@ +#include <EXTERN.h> /* from the Perl distribution */ +#include <perl.h> /* from the Perl distribution */ + +extern void xs_init _((void)); + +static PerlInterpreter *iperl; /*** The Perl interpreter ***/ + +int +perl_main(int argc, char **argv, char **env) +{ + int r; + + iperl = perl_alloc(); + perl_construct(iperl); + perl_parse(iperl, xs_init, argc, argv, (char **)NULL); + r = perl_run(iperl); + +PerlIO_flush(PerlIO_stdout()); +PerlIO_flush(PerlIO_stderr()); + + perl_destruct(iperl); + perl_free(iperl); + return (r); +} diff --git a/examples/loadables/print.c b/examples/loadables/print.c new file mode 100644 index 0000000..0120dbf --- /dev/null +++ b/examples/loadables/print.c @@ -0,0 +1,192 @@ +/* + * print -- loadable ksh-93 style print builtin + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "bashtypes.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "bashgetopt.h" +#include "builtext.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +int print_builtin (); +static int printargs (); + +static FILE *ofp; + +extern char *this_command_name; + +static char *print_doc[] = { + "Display arguments.", + "", + "Output the arguments. The -f option means to use the argument as a", + "format string as would be supplied to printf(1). The rest of the", + "options are as in ksh.", + (char *)NULL +}; + +struct builtin print_struct = { + "print", + print_builtin, + BUILTIN_ENABLED, + print_doc, + "print [-Rnprs] [-u unit] [-f format] [arguments]", + (char *)0 +}; + +#ifndef ISOPTION +#define ISOPTION(s, c) (s[0] == '-' && s[2] == '\0' && s[1] == c) +#endif + +int +print_builtin (list) + WORD_LIST *list; +{ + int c, r, nflag, raw, ofd, sflag; + intmax_t lfd; + char **v, *pfmt, *arg; + WORD_LIST *l; + + nflag = raw = sflag = 0; + ofd = 1; + pfmt = 0; + + reset_internal_getopt (); + while ((c = internal_getopt (list, "Rnprsu:f:")) != -1) + { + switch (c) + { + case 'R': + raw = 2; + loptend = lcurrent; + if (loptend && ISOPTION (loptend->word->word, 'n')) + { + loptend = loptend->next; + nflag = 1; + } + goto opt_end; + case 'r': + raw = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 'p': + break; /* NOP */ + case 'u': + if (all_digits (list_optarg) && legal_number (list_optarg, &lfd) && lfd == (int)lfd) + ofd = lfd; + else + { + for (l = list; l->next && l->next != lcurrent; l = l->next); + lcurrent = loptend = l; + goto opt_end; + } + break; + case 'f': + pfmt = list_optarg; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + +opt_end: + list = loptend; + + ofp = (ofd == 1) ? stdout : fdopen (dup (ofd), "w"); + + if (pfmt) + { + WORD_DESC *w; + WORD_LIST *nlist; + + w = make_word (pfmt); + nlist = make_word_list (w, list); + r = printf_builtin (nlist); + nlist->next = (WORD_LIST *)NULL; + dispose_words (nlist); + return (r); + } + + if (raw) + { + for (l = list; l; l = l->next) + { + fprintf (ofp, "%s", l->word->word); + if (l->next) + fprintf (ofp, " "); + } + if (nflag == 0) + fprintf (ofp, "\n"); + fflush (ofp); + return (0); + } + + r = printargs (list, ofp); + if (r && nflag == 0) + fprintf (ofp, "\n"); + if (ofd != 1) + fclose (ofp); + return 0; +} + +static int +printargs (list, ofp) + WORD_LIST *list; + FILE *ofp; +{ + WORD_LIST *l; + char *ostr; + int sawc; + + for (sawc = 0, l = list; l; l = l->next) + { + ostr = ansicstr (l->word->word, strlen (l->word->word), 0, &sawc, (int *)0); + fprintf (ofp, "%s", ostr); + free (ostr); + if (sawc) + return (0); + if (l->next) + fprintf (ofp, " "); + } + return (1); +} diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c new file mode 100644 index 0000000..8c7f720 --- /dev/null +++ b/examples/loadables/printenv.c @@ -0,0 +1,94 @@ +/* + * printenv -- minimal builtin clone of BSD printenv(1). + * + * usage: printenv [varname] + * + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +extern char **export_env; + +int +printenv_builtin (list) + WORD_LIST *list; +{ + register char **envp; + int opt; + SHELL_VAR *var; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* printenv */ + if (list == 0) + { + maybe_make_export_env (); /* this allows minimal code */ + for (envp = export_env; *envp; envp++) + printf ("%s\n", *envp); + return (EXECUTION_SUCCESS); + } + + /* printenv varname */ + var = find_variable (list->word->word); + if (var == 0 || (exported_p (var) == 0)) + return (EXECUTION_FAILURE); + + if (function_p (var)) + print_var_function (var); + else + print_var_value (var, 0); + + printf("\n"); + return (EXECUTION_SUCCESS); +} + +char *printenv_doc[] = { + "Display environment.", + "", + "Print names and values of environment variables", + (char *)NULL +}; + +struct builtin printenv_struct = { + "printenv", + printenv_builtin, + BUILTIN_ENABLED, + printenv_doc, + "printenv [varname]", + 0 +}; diff --git a/examples/loadables/push.c b/examples/loadables/push.c new file mode 100644 index 0000000..9bcd5c3 --- /dev/null +++ b/examples/loadables/push.c @@ -0,0 +1,117 @@ +/* + * push - anyone remember TOPS-20? + * + */ + +/* + 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 "jobs.h" +#include "bashgetopt.h" +#include "common.h" + +#ifndef errno +extern int errno; +#endif + +extern int 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); + return (xstatus); + } +} + +char *push_doc[] = { + "Create child shell.", + "", + "Create a child that is an exact duplicate of the running shell", + "and wait for it to exit. The $SHLVL, $!, $$, and $PPID variables", + "are adjusted in the child. The return value is the exit status", + "of the child.", + (char *)NULL +}; + +struct builtin push_struct = { + "push", + push_builtin, + BUILTIN_ENABLED, + push_doc, + "push", + 0 +}; diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c new file mode 100644 index 0000000..9892ddb --- /dev/null +++ b/examples/loadables/realpath.c @@ -0,0 +1,145 @@ +/* + * realpath -- canonicalize pathnames, resolving symlinks + * + * usage: realpath [-csv] pathname [pathname...] + * + * options: -c check whether or not each resolved path exists + * -s no output, exit status determines whether path is valid + * -v produce verbose output + * + * + * exit status: 0 if all pathnames resolved + * 1 if any of the pathname arguments could not be resolved + * + * + * Bash loadable builtin version + * + * Chet Ramey + * chet@po.cwru.edu + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include "bashansi.h" +#include <maxpath.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#ifndef errno +extern int errno; +#endif + +extern char *sh_realpath(); + +int +realpath_builtin(list) +WORD_LIST *list; +{ + int opt, cflag, vflag, sflag, es; + char *r, realbuf[PATH_MAX], *p; + struct stat sb; + + if (list == 0) { + builtin_usage(); + return (EX_USAGE); + } + + vflag = cflag = sflag = 0; + reset_internal_getopt(); + while ((opt = internal_getopt (list, "csv")) != -1) { + switch (opt) { + case 'c': + cflag = 1; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + CASE_HELPOPT; + default: + builtin_usage(); + return (EX_USAGE); + } + } + + list = loptend; + + if (list == 0) { + builtin_usage(); + return (EX_USAGE); + } + + for (es = EXECUTION_SUCCESS; list; list = list->next) { + p = list->word->word; + r = sh_realpath(p, realbuf); + if (r == 0) { + es = EXECUTION_FAILURE; + if (sflag == 0) + builtin_error("%s: cannot resolve: %s", p, strerror(errno)); + continue; + } + if (cflag && (stat(realbuf, &sb) < 0)) { + es = EXECUTION_FAILURE; + if (sflag == 0) + builtin_error("%s: %s", p, strerror(errno)); + continue; + } + if (sflag == 0) { + if (vflag) + printf ("%s -> ", p); + printf("%s\n", realbuf); + } + } + return es; +} + +char *realpath_doc[] = { + "Display pathname in canonical form.", + "", + "Display the canonicalized version of each PATHNAME argument, resolving", + "symbolic links. The -c option checks whether or not each resolved name", + "exists. The -s option produces no output; the exit status determines the", + "valididty of each PATHNAME. The -v option produces verbose output. The", + "exit status is 0 if each PATHNAME was resolved; non-zero otherwise.", + (char *)NULL +}; + +struct builtin realpath_struct = { + "realpath", /* builtin name */ + realpath_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + realpath_doc, /* array of long documentation strings */ + "realpath [-csv] pathname [pathname...]", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/rm.c b/examples/loadables/rm.c new file mode 100644 index 0000000..adfbffd --- /dev/null +++ b/examples/loadables/rm.c @@ -0,0 +1,177 @@ +/* rm - remove files and directories with -r */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <stdio.h> +#include <errno.h> +#include <dirent.h> +#include "builtins.h" +#include "shell.h" +#include "common.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +static int rm_file(const char *fname); + +static int force, recursive; + +static int +_remove_directory(const char *dirname) +{ + DIR *dir; + struct dirent *dp; + size_t dirlen; + int err; + + dirlen = strlen (dirname); + err = 0; + + if ((dir = opendir(dirname))) + { + while ((dp = readdir(dir))) + { +#ifdef __GNUC__ + char fname[dirlen + 1 + strlen (dp->d_name) + 1]; +#else + char *fname; + int fnsize; +#endif + + if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0))) + continue; + +#ifdef __GNUC__ + snprintf(fname, sizeof (fname), "%s/%s", dirname, dp->d_name); +#else + fnsize = dirlen + 1 + strlen (dp->d_name) + 1; + fname = xmalloc (fnsize); + snprintf(fname, fnsize, "%s/%s", dirname, dp->d_name); +#endif + + if (rm_file (fname) && force == 0) + err = 1; +#ifndef __GNUC__ + free (fname); +#endif + } + + closedir(dir); + + if (err == 0 && rmdir (dirname) && force == 0) + err = 1; + } + else if (force == 0) + err = 1; + + if (err) + builtin_error ("%s: %s", dirname, strerror (errno)); + + return err; +} + +static int +rm_file(const char *fname) +{ + if (unlink (fname) == 0) + return 0; + + /* If FNAME is a directory glibc returns EISDIR but correct POSIX value + would be EPERM. If we get that error and FNAME is a directory and -r + was supplied, recursively remove the directory and its contents */ + if ((errno == EISDIR || errno == EPERM) && recursive && file_isdir (fname)) + return _remove_directory(fname); + else if (force) + return 0; + + builtin_error ("%s: %s", fname, strerror (errno)); + return 1; +} + +int +rm_builtin (list) + WORD_LIST *list; +{ + const char *name; + WORD_LIST *l; + int rval, opt; + + recursive = force = 0; + rval = EXECUTION_SUCCESS; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Rrfi")) != -1) + { + switch (opt) + { + case 'R': + case 'r': + recursive = 1; + break; + case 'f': + force = 1; + break; + case 'i': + return (EX_DISKFALLBACK); + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + for (l = list; l; l = l->next) + { + if (rm_file(l->word->word) && force == 0) + rval = EXECUTION_FAILURE; + } + + return rval; +} + +char *rm_doc[] = { + "Remove files.", + "", + "rm removes the files specified as arguments.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin rm_struct = { + "rm", /* builtin name */ + rm_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + rm_doc, /* array of long documentation strings. */ + "rm [-rf] file ...", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/rmdir.c b/examples/loadables/rmdir.c new file mode 100644 index 0000000..001c2bd --- /dev/null +++ b/examples/loadables/rmdir.c @@ -0,0 +1,72 @@ +/* rmdir - remove directory */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <stdio.h> +#include <errno.h> +#include "builtins.h" +#include "shell.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +int +rmdir_builtin (list) + WORD_LIST *list; +{ + int rval; + WORD_LIST *l; + + if (no_options (list)) + return (EX_USAGE); + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + if (rmdir (l->word->word) < 0) + { + builtin_error ("%s: %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + + return rval; +} + +char *rmdir_doc[] = { + "Remove directory.", + "", + "rmdir removes the directory entry specified by each argument,", + "provided the directory is empty.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin rmdir_struct = { + "rmdir", /* builtin name */ + rmdir_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + rmdir_doc, /* array of long documentation strings. */ + "rmdir directory ...", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/seq.c b/examples/loadables/seq.c new file mode 100644 index 0000000..d8f3e0a --- /dev/null +++ b/examples/loadables/seq.c @@ -0,0 +1,490 @@ +/* seq - print sequence of numbers to standard output. + Copyright (C) 2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */ + +#include <config.h> + +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "bashansi.h" +#include "loadables.h" +#include "bashintl.h" + +#ifndef errno +extern int errno; +#endif + +#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN) +typedef long double floatmax_t; +# define FLOATMAX_CONV "L" +# define strtofltmax strtold +# define FLOATMAX_FMT "%Lg" +# define FLOATMAX_WFMT "%0.Lf" +# define USE_LONG_DOUBLE +#else +typedef double floatmax_t; +# define FLOATMAX_CONV "" +# define strtofltmax strtod +# define FLOATMAX_FMT "%g" +# define FLOATMAX_WFMT "%0.f" +#endif +static floatmax_t getfloatmax __P((const char *)); +static char *genformat __P((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; /* interation counter */ + s = ""; + for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr) + { + QUIT; + if (*s && fputs (s, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + if (printf (fmt, next) < 0) + return (sh_chkwrite (EXECUTION_FAILURE)); + s = separator; + n++; + } + + if (n > 0 && fputs (terminator, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +/* must be <= INT_STRLEN_BOUND(intmax_t) */ +int +width_needed (num) + intmax_t num; +{ + int ret; + + ret = num < 0; /* sign */ + if (ret) + num = -num; + do + ret++; + while (num /= 10); + return ret; +} + +int +print_intseq (ifirst, ilast, iincr) + intmax_t ifirst, ilast, iincr; +{ + char intwfmt[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX)]; + const char *s; + intmax_t i, next; + + /* compute integer format string */ + if (equal_width) /* -w supplied */ + { + int wfirst, wlast, width; + + wfirst = width_needed (ifirst); + wlast = width_needed (ilast); + width = MAX(wfirst, wlast); + + /* The leading %s is for the separator */ + snprintf (intwfmt, sizeof (intwfmt), "%%s%%0%u" PRIdMAX, width); + } + + /* We could use braces.c:mkseq here but that allocates lots of memory */ + s = ""; + for (i = ifirst; (ifirst <= ilast) ? (i <= ilast) : (i >= ilast); i = next) + { + QUIT; + /* The leading %s is for the separator */ + if (printf (equal_width ? intwfmt : "%s%" PRIdMAX, s, i) < 0) + return (sh_chkwrite (EXECUTION_FAILURE)); + s = separator; + next = i + iincr; + } + + if (fputs (terminator, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +int +seq_builtin (list) + WORD_LIST *list; +{ + floatmax_t first, last, incr; + intmax_t ifirst, ilast, iincr; + WORD_LIST *l; + int opt, nargs, intseq, freefmt; + char *first_str, *incr_str, *last_str; + char const *fmtstr; /* The printf(3) format used for output. */ + + equal_width = 0; + separator = "\n"; + fmtstr = NULL; + + first = 1.0; + last = 0.0; + incr = 0.0; /* set later */ + ifirst = ilast = iincr = 0; + first_str = incr_str = last_str = 0; + + intseq = freefmt = 0; + opt = 0; + + reset_internal_getopt (); + while (opt != -1) + { + l = lcurrent ? lcurrent : list; + if (l && l->word && l->word->word && l->word->word[0] == '-' && + (l->word->word[1] == '.' || DIGIT (l->word->word[1]))) + { + loptend = l; + break; /* negative number */ + } + if ((opt = internal_getopt (list, "f:s:w")) == -1) + break; + + switch (opt) + { + case 'f': + fmtstr = list_optarg; + break; + case 's': + separator = list_optarg; + break; + case 'w': + equal_width = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + for (nargs = 1, l = list; l->next; l = l->next) + nargs++; + if (nargs > 3) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + /* LAST */ + conversion_error = 0; + last = getfloatmax (last_str = l->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + + /* FIRST LAST */ + if (nargs > 1) + { + conversion_error = 0; + first = getfloatmax (first_str = list->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + } + + /* FIRST INCR LAST */ + if (nargs > 2) + { + conversion_error = 0; + incr = getfloatmax (incr_str = list->next->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + if (incr == 0.0) + { + builtin_error ("zero %screment", (first < last) ? "in" : "de"); + return (EXECUTION_FAILURE); + } + } + + /* Sanitize arguments */ + if (incr == 0.0) + incr = (first <= last) ? 1.0 : -1.0; + if ((incr < 0.0 && first < last) || (incr > 0 && first > last)) + { + builtin_error ("incorrect %screment", (first < last) ? "in" : "de"); + return (EXECUTION_FAILURE); + } + + /* validate format here */ + if (fmtstr) + { + fmtstr = long_double_format (fmtstr); + freefmt = 1; + if (fmtstr == 0) + return (EXECUTION_FAILURE); + } + + if (fmtstr != NULL && equal_width) + { + builtin_warning ("-w ignored when the format string is specified"); + equal_width = 0; + } + + /* Placeholder for later additional conditions */ + if (last_str && all_digits (last_str) && + (first_str == 0 || all_digits (first_str)) && + (incr_str == 0 || all_digits (incr_str)) && + fmtstr == NULL) + intseq = 1; + + if (intseq) + { + ifirst = (intmax_t)first; /* truncation */ + ilast = (intmax_t)last; + iincr = (intmax_t)incr; + + return (print_intseq (ifirst, ilast, iincr)); + } + + decimal_point = locale_decpoint (); + if (fmtstr == NULL) + fmtstr = genformat (first, incr, last); + + print_fltseq (fmtstr, first, last, incr); + + if (freefmt) + free ((void *)fmtstr); + return sh_chkwrite (EXECUTION_SUCCESS); +} + +/* Taken largely from GNU seq. */ +char *seq_doc[] = { + "Print numbers from FIRST to LAST, in steps of INCREMENT.", + "", + "-f FORMAT use printf style floating-point FORMAT", + "-s STRING use STRING to separate numbers (default: \\n)", + "-w equalize width by padding with leading zeroes", + "", + "If FIRST or INCREMENT is omitted, it defaults to 1. However, an", + "omitted INCREMENT defaults to -1 when LAST is smaller than FIRST.", + "The sequence of numbers ends when the sum of the current number and", + "INCREMENT would become greater than LAST.", + "FIRST, INCREMENT, and LAST are interpreted as floating point values.", + "", + "FORMAT must be suitable for printing one argument of type 'double';", + "it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point", + "decimal numbers with maximum precision PREC, and to %g otherwise.", + (char *)NULL +}; + +struct builtin seq_struct = { + "seq", + seq_builtin, + BUILTIN_ENABLED, + seq_doc, + "seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST", + 0 +}; diff --git a/examples/loadables/setpgid.c b/examples/loadables/setpgid.c new file mode 100644 index 0000000..7da58f1 --- /dev/null +++ b/examples/loadables/setpgid.c @@ -0,0 +1,121 @@ +/* setpgid.c: bash loadable wrapper for setpgid system call + + An example of how to wrap a system call with a loadable builtin. + + Originally contributed by Jason Vas Dias <jason.vas.dias@gmail.com> + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include <errno.h> +#include <string.h> + +#include "bashtypes.h" +#include "posixtime.h" + +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "common.h" + +#include "bashgetopt.h" + +#if !defined (_POSIX_VERSION) +# define setpgid(pid, pgrp) setpgrp (pid, pgrp) +#endif + +int +setpgid_builtin (list) + WORD_LIST *list; +{ + register WORD_LIST *wl; + intmax_t pid_arg, pgid_arg; + pid_t pid, pgid; + char *pidstr, *pgidstr; + + wl = list; + pid = pgid = 0; + + if (wl == 0 || wl->next == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + pidstr = wl->word ? wl->word->word : 0; + pgidstr = wl->next->word ? wl->next->word->word : 0; + + if (pidstr == 0 || pgidstr == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (legal_number (pidstr, &pid_arg) == 0) + { + builtin_error ("%s: pid argument must be numeric", pidstr); + return (EXECUTION_FAILURE); + } + if (pid_arg < 0) + { + builtin_error("%s: negative pid values not allowed", pidstr); + return (EXECUTION_FAILURE); + } + pid = pid_arg; + + if (legal_number (pgidstr, &pgid_arg) == 0) + { + builtin_error ("%s: pgrp argument must be numeric", pgidstr); + return (EXECUTION_FAILURE); + } + if (pgid_arg < 0) + { + builtin_error ("%s: negative pgrp values not allowed", pgidstr); + return (EXECUTION_FAILURE); + } + pgid = pgid_arg; + + errno = 0; + if (setpgid(pid, pgid) < 0) + { + builtin_error("setpgid failed: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + return (EXECUTION_SUCCESS); +} + +const char *setpgid_doc[] = { + "invoke the setpgid(2) system call", + "", + "Arguments:", + " pid : numeric process identifer, >= 0", + " pgrpid: numeric process group identifier, >=0", + "See the setpgid(2) manual page.", + (const char *)NULL +}; + +struct builtin setpgid_struct = { + "setpgid", + setpgid_builtin, + BUILTIN_ENABLED, + (char **)setpgid_doc, + "setpgid pid pgrpid", + 0 +}; diff --git a/examples/loadables/sleep.c b/examples/loadables/sleep.c new file mode 100644 index 0000000..92b1a8f --- /dev/null +++ b/examples/loadables/sleep.c @@ -0,0 +1,94 @@ +/* + * sleep -- sleep for fractions of a second + * + * usage: sleep seconds[.fraction] + */ + +/* + 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" + +#if defined (TIME_WITH_SYS_TIME) +# include <sys/time.h> +# include <time.h> +#else +# if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <stdio.h> +#include "chartypes.h" + +#include "shell.h" +#include "builtins.h" +#include "common.h" + +int +sleep_builtin (list) +WORD_LIST *list; +{ + long sec, usec; + + 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); + } + + if (uconvert(list->word->word, &sec, &usec)) { + fsleep(sec, usec); + return(EXECUTION_SUCCESS); + } + + builtin_error("%s: bad sleep interval", list->word->word); + return (EXECUTION_FAILURE); +} + +static char *sleep_doc[] = { + "Suspend execution for specified period.", + "" + "sleep suspends execution for a minimum of SECONDS[.FRACTION] seconds.", + (char *)NULL +}; + +struct builtin sleep_struct = { + "sleep", + sleep_builtin, + BUILTIN_ENABLED, + sleep_doc, + "sleep seconds[.fraction]", + 0 +}; diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c new file mode 100644 index 0000000..52b9580 --- /dev/null +++ b/examples/loadables/stat.c @@ -0,0 +1,430 @@ +/* stat - load up an associative array with stat information about a file */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +#include <sys/types.h> +#include "posixstat.h" +#include <stdio.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include "posixtime.h" + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "common.h" +#include "bashgetopt.h" + +#ifndef errno +extern int errno; +#endif + +#define ST_NAME 0 +#define ST_DEV 1 +#define ST_INO 2 +#define ST_MODE 3 +#define ST_NLINK 4 +#define ST_UID 5 +#define ST_GID 6 +#define ST_RDEV 7 +#define ST_SIZE 8 +#define ST_ATIME 9 +#define ST_MTIME 10 +#define ST_CTIME 11 +#define ST_BLKSIZE 12 +#define ST_BLOCKS 13 +#define ST_CHASELINK 14 +#define ST_PERMS 15 + +#define ST_END 16 + +static char *arraysubs[] = + { + "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev", + "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms", + 0 + }; + +static int +getstat (fname, flags, sp) + const char *fname; + int flags; + struct stat *sp; +{ + intmax_t lfd; + int fd, r; + + if (strncmp (fname, "/dev/fd/", 8) == 0) + { + if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd) + { + errno = EINVAL; + return -1; + } + fd = lfd; + r = fstat(fd, sp); + } +#ifdef HAVE_LSTAT + else if (flags & 1) + r = lstat(fname, sp); +#endif + else + r = stat(fname, sp); + + return r; +} + +static char * +statlink (fname, sp) + char *fname; + struct stat *sp; +{ +#if defined (HAVE_READLINK) + char linkbuf[PATH_MAX]; + int n; + + if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0) + { + linkbuf[n] = '\0'; + return (savestring (linkbuf)); + } + else +#endif + return (savestring (fname)); +} + +static char * +octalperms (m) + int m; +{ + int operms; + char *ret; + + operms = 0; + + if (m & S_IRUSR) + operms |= 0400; + if (m & S_IWUSR) + operms |= 0200; + if (m & S_IXUSR) + operms |= 0100; + + if (m & S_IRGRP) + operms |= 0040; + if (m & S_IWGRP) + operms |= 0020; + if (m & S_IXGRP) + operms |= 0010; + + if (m & S_IROTH) + operms |= 0004; + if (m & S_IWOTH) + operms |= 0002; + if (m & S_IXOTH) + operms |= 0001; + + if (m & S_ISUID) + operms |= 04000; + if (m & S_ISGID) + operms |= 02000; + if (m & S_ISVTX) + operms |= 01000; + + ret = (char *)xmalloc (16); + snprintf (ret, 16, "%04o", operms); + return ret; +} + +static char * +statperms (m) + int m; +{ + char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ + int i; + char *ret; + + i = 0; + if (m & S_IRUSR) + ubits[i++] = 'r'; + if (m & S_IWUSR) + ubits[i++] = 'w'; + if (m & S_IXUSR) + ubits[i++] = 'x'; + ubits[i] = '\0'; + + i = 0; + if (m & S_IRGRP) + gbits[i++] = 'r'; + if (m & S_IWGRP) + gbits[i++] = 'w'; + if (m & S_IXGRP) + gbits[i++] = 'x'; + gbits[i] = '\0'; + + i = 0; + if (m & S_IROTH) + obits[i++] = 'r'; + if (m & S_IWOTH) + obits[i++] = 'w'; + if (m & S_IXOTH) + obits[i++] = 'x'; + obits[i] = '\0'; + + if (m & S_ISUID) + ubits[2] = (m & S_IXUSR) ? 's' : 'S'; + if (m & S_ISGID) + gbits[2] = (m & S_IXGRP) ? 's' : 'S'; + if (m & S_ISVTX) + obits[2] = (m & S_IXOTH) ? 't' : 'T'; + + ret = (char *)xmalloc (32); + snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits); + return ret; +} + +static char * +statmode(mode) + int mode; +{ + char *modestr, *m; + + modestr = m = (char *)xmalloc (8); + if (S_ISBLK (mode)) + *m++ = 'b'; + if (S_ISCHR (mode)) + *m++ = 'c'; + if (S_ISDIR (mode)) + *m++ = 'd'; + if (S_ISREG(mode)) + *m++ = '-'; + if (S_ISFIFO(mode)) + *m++ = 'p'; + if (S_ISLNK(mode)) + *m++ = 'l'; + if (S_ISSOCK(mode)) + *m++ = 's'; + +#ifdef S_ISDOOR + if (S_ISDOOR (mode)) + *m++ = 'D'; +#endif +#ifdef S_ISWHT + if (S_ISWHT(mode)) + *m++ = 'W'; +#endif +#ifdef S_ISNWK + if (S_ISNWK(mode)) + *m++ = 'n'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (mode)) + *m++ = 'm'; +#endif + + *m = '\0'; + return (modestr); +} + +static char * +stattime (t) + time_t t; +{ + char *tbuf, *ret; + size_t tlen; + + tbuf = ctime (&t); + tlen = strlen (tbuf); + ret = savestring (tbuf); + ret[tlen-1] = '\0'; + return ret; +} + +static char * +statval (which, fname, flags, sp) + int which; + char *fname; + int flags; + struct stat *sp; +{ + int temp; + + switch (which) + { + case ST_NAME: + return savestring (fname); + case ST_DEV: + return itos (sp->st_dev); + case ST_INO: + return itos (sp->st_ino); + case ST_MODE: + return (statmode (sp->st_mode)); + case ST_NLINK: + return itos (sp->st_nlink); + case ST_UID: + return itos (sp->st_uid); + case ST_GID: + return itos (sp->st_gid); + case ST_RDEV: + return itos (sp->st_rdev); + case ST_SIZE: + return itos (sp->st_size); + case ST_ATIME: + return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime)); + case ST_MTIME: + return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime)); + case ST_CTIME: + return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime)); + case ST_BLKSIZE: + return itos (sp->st_blksize); + case ST_BLOCKS: + return itos (sp->st_blocks); + case ST_CHASELINK: + return (statlink (fname, sp)); + case ST_PERMS: + temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID); + return (flags & 2) ? statperms (temp) : octalperms (temp); + default: + return savestring ("42"); + } +} + +static int +loadstat (vname, var, fname, flags, sp) + char *vname; + SHELL_VAR *var; + char *fname; + int flags; + struct stat *sp; +{ + int i; + char *key, *value; + SHELL_VAR *v; + + for (i = 0; arraysubs[i]; i++) + { + key = savestring (arraysubs[i]); + value = statval (i, fname, flags, sp); + v = bind_assoc_variable (var, vname, key, value, ASS_FORCE); + } + return 0; +} + +int +stat_builtin (list) + WORD_LIST *list; +{ + int opt, flags; + char *aname, *fname; + struct stat st; + SHELL_VAR *v; + + aname = "STAT"; + flags = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "A:Ll")) != -1) + { + switch (opt) + { + case 'A': + aname = list_optarg; + break; + case 'L': + flags |= 1; /* operate on links rather than resolving them */ + break; + case 'l': + flags |= 2; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + fname = list->word->word; + + if (getstat (fname, flags, &st) < 0) + { + builtin_error ("%s: cannot stat: %s", fname, strerror (errno)); + return (EXECUTION_FAILURE); + } + + unbind_variable (aname); + v = make_new_assoc_variable (aname); + if (v == 0) + { + builtin_error ("%s: cannot create variable", aname); + return (EXECUTION_FAILURE); + } + if (loadstat (aname, v, fname, flags, &st) < 0) + { + builtin_error ("%s: cannot assign file status information", aname); + unbind_variable (aname); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. By convention, + the first line is a short description. */ +char *stat_doc[] = { + "Load an associative array with file status information.", + "", + "Take a filename and load the status information returned by a", + "stat(2) call on that file into the associative array specified", + "by the -A option. The default array name is STAT. If the -L", + "option is supplied, stat does not resolve symbolic links and", + "reports information about the link itself. The -l option results", + "in longer-form listings for some of the fields. The exit status is 0", + "unless the stat fails or assigning the array is unsuccessful.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin stat_struct = { + "stat", /* builtin name */ + stat_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + stat_doc, /* array of long documentation strings. */ + "stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/strftime.c b/examples/loadables/strftime.c new file mode 100644 index 0000000..2de09e3 --- /dev/null +++ b/examples/loadables/strftime.c @@ -0,0 +1,125 @@ +/* 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" + +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 (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (no_options (list)) + 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 < 4; n++) + { + tbuf = xrealloc (tbuf, tbsize * n); + tsize = strftime (tbuf, tbsize * n, format, t); + if (tsize) + break; + } + + printf ("%s\n", tbuf); + free (tbuf); + + return (EXECUTION_SUCCESS); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. */ +char *strftime_doc[] = { + "Display formatted time.", + "", + "Converts date and time format to a string and displays it on the", + "standard output. If the optional second argument is supplied, it", + "is used as the number of seconds since the epoch to use in the", + "conversion, otherwise the current time is used.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin strftime_struct = { + "strftime", /* builtin name */ + strftime_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + strftime_doc, /* array of long documentation strings. */ + "strftime format [seconds]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/sync.c b/examples/loadables/sync.c new file mode 100644 index 0000000..4fbeee1 --- /dev/null +++ b/examples/loadables/sync.c @@ -0,0 +1,53 @@ +/* sync - sync the disks by forcing pending filesystem writes to complete */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +int +sync_builtin (list) + WORD_LIST *list; +{ + sync(); + return (EXECUTION_SUCCESS); +} + +char *sync_doc[] = { + "Sync disks.", + "" + "Force completion of pending disk writes", + (char *)NULL +}; + +struct builtin sync_struct = { + "sync", /* builtin name */ + sync_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + sync_doc, /* array of long documentation strings. */ + "sync", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/tee.c b/examples/loadables/tee.c new file mode 100644 index 0000000..819d83a --- /dev/null +++ b/examples/loadables/tee.c @@ -0,0 +1,180 @@ +/* tee - duplicate standard input */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" + +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +typedef struct flist { + struct flist *next; + int fd; + char *fname; +} FLIST; + +static FLIST *tee_flist; + +#define TEE_BUFSIZE 8192 + +extern int interrupt_immediately; + +extern char *strerror (); + +int +tee_builtin (list) + WORD_LIST *list; +{ + int opt, append, nointr, rval, fd, fflags; + int n, nr, nw; + FLIST *fl; + char *buf, *bp; + + char *t; + + reset_internal_getopt (); + append = nointr = 0; + tee_flist = (FLIST *)NULL; + while ((opt = internal_getopt (list, "ai")) != -1) + { + switch (opt) + { + case 'a': + append = 1; + break; + case 'i': + nointr = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (nointr == 0) + interrupt_immediately++; + + buf = xmalloc (TEE_BUFSIZE); + + /* Initialize output file list. */ + fl = tee_flist = (FLIST *)xmalloc (sizeof(FLIST)); + tee_flist->fd = 1; + tee_flist->fname = "stdout"; + tee_flist->next = (FLIST *)NULL; + + /* Add file arguments to list of output files. */ + fflags = append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC; + for (rval = EXECUTION_SUCCESS; list; list = list->next) + { + fd = open (list->word->word, fflags, 0666); + if (fd < 0) + { + builtin_error ("%s: cannot open: %s", list->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + else + { + fl->next = (FLIST *)xmalloc (sizeof(FLIST)); + fl->next->fd = fd; + fl->next->fname = list->word->word; + fl = fl->next; + fl->next = (FLIST *)NULL; + } + } + + while ((nr = read(0, buf, TEE_BUFSIZE)) > 0) + for (fl = tee_flist; fl; fl = fl->next) + { + n = nr; + bp = buf; + do + { + if ((nw = write (fl->fd, bp, n)) == -1) + { + builtin_error ("%s: write error: %s", fl->fname, strerror (errno)); + rval = EXECUTION_FAILURE; + break; + } + bp += nw; + } + while (n -= nw); + } + if (nr < 0) + builtin_error ("read error: %s", strerror (errno)); + + /* Deallocate resources -- this is a builtin command. */ + tee_flist = tee_flist->next; /* skip bogus close of stdout */ + while (tee_flist) + { + fl = tee_flist; + if (close (fl->fd) < 0) + { + builtin_error ("%s: close_error: %s", fl->fname, strerror (errno)); + rval = EXECUTION_FAILURE; + } + tee_flist = tee_flist->next; + free (fl); + } + + return (rval); +} + +char *tee_doc[] = { + "Duplicate standard output.", + "", + "Copy standard input to standard output, making a copy in each", + "filename argument. If the `-a' option is gived, the specified", + "files are appended to, otherwise they are overwritten. If the", + "`-i' option is supplied, tee ignores interrupts.", + (char *)NULL +}; + +struct builtin tee_struct = { + "tee", /* builtin name */ + tee_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + tee_doc, /* array of long documentation strings. */ + "tee [-ai] [file ...]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/template.c b/examples/loadables/template.c new file mode 100644 index 0000000..094b80c --- /dev/null +++ b/examples/loadables/template.c @@ -0,0 +1,75 @@ +/* template - example template for loadable builtin */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "bashansi.h" +#include <stdio.h> +#include <errno.h> + +#include "loadables.h" + +#if !defined (errno) +extern int errno; +#endif + +extern char *strerror (); + +int +template_builtin (list) + WORD_LIST *list; +{ + int opt, rval; + + rval = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + return (rval); +} + +/* Called when `template' is enabled and loaded from the shared object. If this + function returns 0, the load fails. */ +int +template_builtin_load (name) + char *name; +{ + return (1); +} + +/* Called when `template' is disabled. */ +void +template_builtin_unload (name) + char *name; +{ +} + +char *template_doc[] = { + "Short description.", + "" + "Longer description of builtin and usage.", + (char *)NULL +}; + +struct builtin template_struct = { + "template", /* builtin name */ + template_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + template_doc, /* array of long documentation strings. */ + "template", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/truefalse.c b/examples/loadables/truefalse.c new file mode 100644 index 0000000..e011fa1 --- /dev/null +++ b/examples/loadables/truefalse.c @@ -0,0 +1,72 @@ +/* true and false builtins */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "bashtypes.h" +#include "shell.h" +#include "builtins.h" +#include "common.h" + +int +true_builtin (list) + WORD_LIST *list; +{ + return EXECUTION_SUCCESS; +} + +int +false_builtin (list) + WORD_LIST *list; +{ + return EXECUTION_FAILURE; +} + +static char *true_doc[] = { + "Exit successfully.", + "", + "Return a successful result.", + (char *)NULL +}; + +static char *false_doc[] = { + "Exit unsuccessfully.", + "", + "Return an unsuccessful result.", + (char *)NULL +}; + +struct builtin true_struct = { + "true", + true_builtin, + BUILTIN_ENABLED, + true_doc, + "true", + 0 +}; + +struct builtin false_struct = { + "false", + false_builtin, + BUILTIN_ENABLED, + false_doc, + "false", + 0 +}; diff --git a/examples/loadables/tty.c b/examples/loadables/tty.c new file mode 100644 index 0000000..febf518 --- /dev/null +++ b/examples/loadables/tty.c @@ -0,0 +1,82 @@ +/* tty - return terminal name */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +extern char *ttyname (); + +int +tty_builtin (list) + WORD_LIST *list; +{ + int opt, sflag; + char *t; + + reset_internal_getopt (); + sflag = 0; + while ((opt = internal_getopt (list, "s")) != -1) + { + switch (opt) + { + case 's': + sflag = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + t = ttyname (0); + if (sflag == 0) + puts (t ? t : "not a tty"); + return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +char *tty_doc[] = { + "Display terminal name.", + "", + "tty writes the name of the terminal that is opened for standard", + "input to standard output. If the `-s' option is supplied, nothing", + "is written; the exit status determines whether or not the standard", + "input is connected to a tty.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin tty_struct = { + "tty", /* builtin name */ + tty_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + tty_doc, /* array of long documentation strings. */ + "tty [-s]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c new file mode 100644 index 0000000..106a1c8 --- /dev/null +++ b/examples/loadables/uname.c @@ -0,0 +1,162 @@ +/* + * uname - print system information + * + * usage: uname [-amnrsv] + * + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <stdio.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNAME) +# include <sys/utsname.h> +#else +struct utsname { + char sysname[32]; + char nodename[32]; + char release[32]; + char version[32]; + char machine[32]; +}; +#endif + +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#define FLAG_SYSNAME 0x01 /* -s */ +#define FLAG_NODENAME 0x02 /* -n */ +#define FLAG_RELEASE 0x04 /* -r */ +#define FLAG_VERSION 0x08 /* -v */ +#define FLAG_MACHINE 0x10 /* -m, -p */ + +#define FLAG_ALL 0x1f + +#ifndef errno +extern int errno; +#endif + +static void uprint(); + +static int uname_flags; + +int +uname_builtin (list) + WORD_LIST *list; +{ + int opt, r; + struct utsname uninfo; + + uname_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "amnprsv")) != -1) + { + switch (opt) + { + case 'a': + uname_flags |= FLAG_ALL; + break; + case 'm': + case 'p': + uname_flags |= FLAG_MACHINE; + break; + case 'n': + uname_flags |= FLAG_NODENAME; + break; + case 'r': + uname_flags |= FLAG_RELEASE; + break; + case 's': + uname_flags |= FLAG_SYSNAME; + break; + case 'v': + uname_flags |= FLAG_VERSION; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (uname_flags == 0) + uname_flags = FLAG_SYSNAME; + + /* Only ancient systems will not have uname(2). */ +#ifdef HAVE_UNAME + if (uname (&uninfo) < 0) + { + builtin_error ("cannot get system name: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } +#else + builtin_error ("cannot get system information: uname(2) not available"); + return (EXECUTION_FAILURE); +#endif + + uprint (FLAG_SYSNAME, uninfo.sysname); + uprint (FLAG_NODENAME, uninfo.nodename); + uprint (FLAG_RELEASE, uninfo.release); + uprint (FLAG_VERSION, uninfo.version); + uprint (FLAG_MACHINE, uninfo.machine); + + return (EXECUTION_SUCCESS); +} + +static void +uprint (flag, info) + int flag; + char *info; +{ + if (uname_flags & flag) + { + uname_flags &= ~flag; + printf ("%s%c", info, uname_flags ? ' ' : '\n'); + } +} + +char *uname_doc[] = { + "Display system information.", + "", + "Display information about the system hardware and OS.", + (char *)NULL +}; + +struct builtin uname_struct = { + "uname", + uname_builtin, + BUILTIN_ENABLED, + uname_doc, + "uname [-amnrsv]", + 0 +}; diff --git a/examples/loadables/unlink.c b/examples/loadables/unlink.c new file mode 100644 index 0000000..ff2a78a --- /dev/null +++ b/examples/loadables/unlink.c @@ -0,0 +1,74 @@ +/* unlink - remove a directory entry */ + +/* Should only be used to remove directories by a superuser prepared to let + fsck clean up the file system. */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "common.h" + +#ifndef errno +extern int errno; +#endif + +int +unlink_builtin (list) + WORD_LIST *list; +{ + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (unlink (list->word->word) != 0) + { + builtin_error ("%s: cannot unlink: %s", list->word->word, strerror (errno)); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +char *unlink_doc[] = { + "Remove a directory entry.", + "", + "Forcibly remove a directory entry, even if it's a directory.", + (char *)NULL +}; + +struct builtin unlink_struct = { + "unlink", /* builtin name */ + unlink_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + unlink_doc, /* array of long documentation strings. */ + "unlink name", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c new file mode 100644 index 0000000..3e7e36e --- /dev/null +++ b/examples/loadables/whoami.c @@ -0,0 +1,75 @@ +/* + * whoami - print out username of current user + */ + +/* + Copyright (C) 1999-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +int +whoami_builtin (list) + WORD_LIST *list; +{ + int opt; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (current_user.user_name == 0) + get_current_user_info (); + printf ("%s\n", current_user.user_name); + return (EXECUTION_SUCCESS); +} + +char *whoami_doc[] = { + "Print user name", + "", + "Display name of current user.", + (char *)NULL +}; + +struct builtin whoami_struct = { + "whoami", + whoami_builtin, + BUILTIN_ENABLED, + whoami_doc, + "whoami", + 0 +}; |