diff options
author | cvpcs <root@cvpcs.org> | 2010-06-02 11:02:31 -0500 |
---|---|---|
committer | cvpcs <root@cvpcs.org> | 2010-06-02 11:02:31 -0500 |
commit | 772f20abb0a3a0979c440114bf3a1cff5b3cef03 (patch) | |
tree | 3384b9291d73a12542c526a8557218c7435491b0 /builtins | |
download | external_bash-772f20abb0a3a0979c440114bf3a1cff5b3cef03.zip external_bash-772f20abb0a3a0979c440114bf3a1cff5b3cef03.tar.gz external_bash-772f20abb0a3a0979c440114bf3a1cff5b3cef03.tar.bz2 |
initial import of bash 4.1
Diffstat (limited to 'builtins')
56 files changed, 20543 insertions, 0 deletions
diff --git a/builtins/Makefile.in b/builtins/Makefile.in new file mode 100644 index 0000000..42f8cb8 --- /dev/null +++ b/builtins/Makefile.in @@ -0,0 +1,659 @@ +# This Makefile for building libbuiltins.a is in -*- text -*- for Emacs. +# +# Copyright (C) 1996-2009 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@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +# +SHELL = @MAKE_SHELL@ +RANLIB = @RANLIB@ +CC = @CC@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp + +EXEEXT = @EXEEXT@ + +prefix = @prefix@ + +srcdir = @srcdir@ +VPATH = .:@srcdir@ +topdir = @top_srcdir@ + +datarootdir = @datarootdir@ + +includedir = @includedir@ +datadir = @datadir@ +localedir = @localedir@ + +# Support an alternate destination root directory for package building +DESTDIR = + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +BUILD_DIR = @BUILD_DIR@ + +LIBBUILD = ${BUILD_DIR}/lib + +PROFILE_FLAGS = @PROFILE_FLAGS@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ @CROSS_COMPILE@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ ${DEBUG} +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +LIBS = @LIBS@ +LDFLAGS = @LDFLAGS@ $(LOCAL_LDFLAGS) $(CFLAGS) +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ $(LOCAL_LDFLAGS) $(CFLAGS_FOR_BUILD) +LOCAL_LDFLAGS = @LOCAL_LDFLAGS@ +#LIBS_FOR_BUILD = @LIBS_FOR_BUILD@ +LIBS_FOR_BUILD = $(LIBS) + +BASHINCDIR = ${topdir}/include + +RL_INCLUDEDIR = @RL_INCLUDEDIR@ + +INTL_LIBSRC = ${topdir}/lib/intl +INTL_BUILDDIR = ${LIBBUILD}/intl +INTL_LIBDIR = ${INTL_BUILDDIR} +INTL_LIBRARY = ${INTL_BUILDDIR}/libintl.a +INTL_INC = @INTL_INC@ +INTL_DEP = @INTL_DEP@ +LIBINTL_H = @LIBINTL_H@ + +HELPDIR = @HELPDIR@ +MKDIRS = ${topdir}/support/mkdirs + +INCLUDES = -I. -I.. @RL_INCLUDE@ -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib -I$(srcdir) ${INTL_INC} + +BASE_CCFLAGS = ${PROFILE_FLAGS} $(DEFS) $(LOCAL_DEFS) $(SYSTEM_FLAGS) \ + ${INCLUDES} $(LOCAL_CFLAGS) + +CCFLAGS = $(BASE_CCFLAGS) $(CPPFLAGS) $(CFLAGS) + +CCFLAGS_FOR_BUILD = $(BASE_CCFLAGS) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) + +GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \ + -Wcast-align -Wstrict-prototypes -Wconversion \ + -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic + +MKBUILTINS = mkbuiltins$(EXEEXT) +DIRECTDEFINE = -D $(srcdir) +HELPDIRDEFINE = @HELPDIRDEFINE@ +HELPSTRINGS = @HELPSTRINGS@ + +# xxx this is bad style +RL_LIBSRC = $(topdir)/lib/readline + +.SUFFIXES: +.SUFFIXES: .def .c .o +# How to make a .o file from a .def file. +.def.o: + $(RM) $@ + ./$(MKBUILTINS) $(DIRECTDEFINE) $< + $(CC) -c $(CCFLAGS) $*.c || ( $(RM) $*.c ; exit 1 ) + $(RM) $*.c + +# How to make a .c file from a .def file. +.def.c: + $(RM) $@ + ./$(MKBUILTINS) $(DIRECTDEFINE) $< + +# default rule for making a .o file from a .c file +.c.o: + $(RM) $@ + $(CC) -c $(CCFLAGS) $< + +DEFSRC = $(srcdir)/alias.def $(srcdir)/bind.def $(srcdir)/break.def \ + $(srcdir)/builtin.def $(srcdir)/caller.def \ + $(srcdir)/cd.def $(srcdir)/colon.def \ + $(srcdir)/command.def $(srcdir)/declare.def $(srcdir)/echo.def \ + $(srcdir)/enable.def $(srcdir)/eval.def $(srcdir)/getopts.def \ + $(srcdir)/exec.def $(srcdir)/exit.def $(srcdir)/fc.def \ + $(srcdir)/fg_bg.def $(srcdir)/hash.def $(srcdir)/help.def \ + $(srcdir)/history.def $(srcdir)/jobs.def $(srcdir)/kill.def \ + $(srcdir)/let.def $(srcdir)/read.def $(srcdir)/return.def \ + $(srcdir)/set.def $(srcdir)/setattr.def $(srcdir)/shift.def \ + $(srcdir)/source.def $(srcdir)/suspend.def $(srcdir)/test.def \ + $(srcdir)/times.def $(srcdir)/trap.def $(srcdir)/type.def \ + $(srcdir)/ulimit.def $(srcdir)/umask.def $(srcdir)/wait.def \ + $(srcdir)/reserved.def $(srcdir)/pushd.def $(srcdir)/shopt.def \ + $(srcdir)/printf.def $(srcdir)/complete.def $(srcdir)/mapfile.def + +STATIC_SOURCE = common.c evalstring.c evalfile.c getopt.c bashgetopt.c \ + getopt.h + +OFILES = builtins.o \ + alias.o bind.o break.o builtin.o caller.o cd.o colon.o command.o \ + common.o declare.o echo.o enable.o eval.o evalfile.o \ + evalstring.o exec.o exit.o fc.o fg_bg.o hash.o help.o history.o \ + jobs.o kill.o let.o mapfile.o \ + pushd.o read.o return.o set.o setattr.o shift.o source.o \ + suspend.o test.o times.o trap.o type.o ulimit.o umask.o \ + wait.o getopts.o shopt.o printf.o getopt.o bashgetopt.o complete.o + +CREATED_FILES = builtext.h builtins.c psize.aux pipesize.h + +all: $(MKBUILTINS) libbuiltins.a + +libbuiltins.a: $(MKBUILTINS) $(OFILES) builtins.o + $(RM) $@ + $(AR) $(ARFLAGS) $@ $(OFILES) + -$(RANLIB) $@ + +builtext.h builtins.c: $(MKBUILTINS) $(DEFSRC) + @-if test -f builtins.c; then mv -f builtins.c old-builtins.c; fi + @-if test -f builtext.h; then mv -f builtext.h old-builtext.h; fi + ./$(MKBUILTINS) -externfile builtext.h -structfile builtins.c \ + -noproduction $(DIRECTDEFINE) $(HELPDIRDEFINE) $(HELPSTRINGS) $(DEFSRC) + @-if cmp -s old-builtext.h builtext.h 2>/dev/null; then \ + mv old-builtext.h builtext.h; \ + else \ + $(RM) old-builtext.h; \ + fi + @-if cmp -s old-builtins.c builtins.c 2>/dev/null; then \ + mv old-builtins.c builtins.c; \ + else \ + $(RM) old-builtins.c; \ + fi + +helpdoc: $(MKBUILTINS) $(DEFSRC) + ./$(MKBUILTINS) ${HELPDIRDEFINE} -noproduction $(DIRECTDEFINE) $(DEFSRC) + +install-help: + @-if test -n "${HELPDIR}" && test -d helpfiles ; then \ + test -d $(DESTDIR)${HELPDIR} || ${SHELL} ${MKDIRS} $(DESTDIR)$(HELPDIR) ;\ + ( for f in helpfiles/*; do \ + echo installing $$f; \ + ${INSTALL_DATA} $$f $(DESTDIR)$(HELPDIR); \ + done; ) ; \ + fi + +install: @HELPINSTALL@ + +mkbuiltins.o: ../config.h +mkbuiltins.o: mkbuiltins.c + $(RM) $@ + $(CC_FOR_BUILD) -c $(CCFLAGS_FOR_BUILD) $< + +mkbuiltins$(EXEEXT): mkbuiltins.o + $(CC_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $(MKBUILTINS) mkbuiltins.o $(LIBS_FOR_BUILD) + +# rules for deficient makes, like SunOS +mkbuiltins.o: mkbuiltins.c +builtins.o: builtins.c +common.o: common.c +bashgetopt.o: bashgetopt.c +getopt.o: getopt.c +evalstring.o: evalstring.c +evalfile.o: evalfile.c + +ulimit.o: pipesize.h + +pipesize.h: psize.aux + $(SHELL) $(srcdir)/psize.sh > $@ + +psize.aux: psize.c + $(CC_FOR_BUILD) $(CCFLAGS_FOR_BUILD) -o $@ $(srcdir)/psize.c + +documentation: builtins.texi + +builtins.texi: $(MKBUILTINS) + ./$(MKBUILTINS) -documentonly $(DEFSRC) + +clean: + $(RM) $(OFILES) $(CREATED_FILES) $(MKBUILTINS) mkbuiltins.o libbuiltins.a + -test -d helpfiles && $(RM) -r helpfiles + +mostlyclean: + $(RM) $(OFILES) libbuiltins.a + +distclean maintainer-clean: clean + $(RM) Makefile + +$(OFILES): $(MKBUILTINS) ../config.h + +../version.h: ../config.h ../Makefile Makefile + -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} version.h ) + +# maintainer special - for now +po: builtins.c + xgettext -L C -o $(topdir)/po/builtins.pot --keyword='N_' builtins.c 2>/dev/null + +${LIBINTL_H}: + @echo making $@ in ${INTL_BUILDDIR} + @(cd ${INTL_BUILDDIR} && \ + $(MAKE) $(MFLAGS) libintl.h) || exit 1 + +# dependencies + +alias.o: alias.def +bind.o: bind.def +break.o: break.def +builtin.o: builtin.def +caller.o: caller.def +cd.o: cd.def +colon.o: colon.def +command.o: command.def +declare.o: declare.def +echo.o: echo.def +enable.o: enable.def +eval.o: eval.def +exec.o: exec.def +exit.o: exit.def +fc.o: fc.def +fg_bg.o: fg_bg.def +hash.o: hash.def +help.o: help.def +history.o: history.def +jobs.o: jobs.def +kill.o: kill.def +let.o: let.def +mapfile.o: mapfile.def +printf.o: printf.def +pushd.o: pushd.def +read.o: read.def +return.o: return.def +set.o: set.def +setattr.o: setattr.def +shift.o: shift.def +shopt.o: shopt.def +source.o: source.def +suspend.o: suspend.def +test.o: test.def +times.o: times.def +trap.o: trap.def +type.o: type.def +ulimit.o: ulimit.def +umask.o: umask.def +wait.o: wait.def +getopts.o: getopts.def +reserved.o: reserved.def +complete.o: complete.def + +# C files +bashgetopt.o: ../config.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h +bashgetopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h +bashgetopt.o: $(topdir)/command.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h +bashgetopt.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/quit.h $(BASHINCDIR)/maxpath.h +bashgetopt.o: $(topdir)/unwind_prot.h $(topdir)/dispose_cmd.h +bashgetopt.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/sig.h +bashgetopt.o: ../pathnames.h $(topdir)/externs.h $(srcdir)/common.h +bashgetopt.o: $(BASHINCDIR)/chartypes.h +common.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h +common.o: $(topdir)/shell.h $(topdir)/syntax.h ../config.h $(topdir)/bashjmp.h $(BASHINCDIR)/posixjmp.h +common.o: $(topdir)/sig.h $(topdir)/command.h +common.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/stdc.h $(BASHINCDIR)/memalloc.h +common.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/input.h +common.o: $(topdir)/siglist.h $(topdir)/bashhist.h $(topdir)/quit.h +common.o: $(topdir)/unwind_prot.h $(BASHINCDIR)/maxpath.h $(topdir)/jobs.h +common.o: $(topdir)/builtins.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +common.o: $(topdir)/subst.h $(topdir)/execute_cmd.h $(topdir)/error.h +common.o: $(topdir)/externs.h ../pathnames.h ./builtext.h +common.o: $(BASHINCDIR)/chartypes.h +evalfile.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h ${BASHINCDIR}/filecntl.h +evalfile.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h +evalfile.o: $(topdir)/shell.h $(topdir)/syntax.h ../config.h $(topdir)/bashjmp.h +evalfile.o: $(topdir)/command.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h +evalfile.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/quit.h $(BASHINCDIR)/maxpath.h +evalfile.o: $(topdir)/unwind_prot.h $(topdir)/dispose_cmd.h +evalfile.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/sig.h +evalfile.o: ../pathnames.h $(topdir)/externs.h +evalfile.o: $(topdir)/jobs.h $(topdir)/builtins.h $(topdir)/flags.h +evalfile.o: $(topdir)/input.h $(topdir)/execute_cmd.h +evalfile.o: $(topdir)/bashhist.h $(srcdir)/common.h +evalstring.o: ../config.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h +evalstring.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h $(BASHINCDIR)/posixjmp.h +evalstring.o: $(topdir)/sig.h $(topdir)/command.h $(topdir)/siglist.h +evalstring.o: $(BASHINCDIR)/memalloc.h $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/input.h +evalstring.o: $(topdir)/quit.h $(topdir)/unwind_prot.h +evalstring.o: $(BASHINCDIR)/maxpath.h $(topdir)/jobs.h $(topdir)/builtins.h +evalstring.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +evalstring.o: $(topdir)/externs.h $(topdir)/jobs.h $(topdir)/builtins.h +evalstring.o: $(topdir)/flags.h $(topdir)/input.h $(topdir)/execute_cmd.h +evalstring.o: $(topdir)/bashhist.h $(srcdir)/common.h +evalstring.o: $(topdir)/trap.h $(topdir)/redir.h ../pathnames.h +getopt.o: ../config.h $(BASHINCDIR)/memalloc.h +getopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h $(topdir)/command.h +getopt.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h $(topdir)/variables.h $(topdir)/conftypes.h +getopt.o: $(topdir)/quit.h $(BASHINCDIR)/maxpath.h $(topdir)/unwind_prot.h +getopt.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +getopt.o: $(topdir)/sig.h ../pathnames.h $(topdir)/externs.h +getopt.o: $(srcdir)/getopt.h +mkbuiltins.o: ../config.h $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h +mkbuiltins.o: ${BASHINCDIR}/filecntl.h +mkbuiltins.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h + +# def files +alias.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +alias.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h +alias.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +alias.o: $(topdir)/subst.h $(topdir)/externs.h $(srcdir)/common.h +alias.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +alias.o: ../pathnames.h +bind.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +bind.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +bind.o: $(topdir)/subst.h $(topdir)/externs.h $(srcdir)/bashgetopt.h +bind.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h $(topdir)/bashline.h +bind.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +bind.o: ../pathnames.h +break.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +break.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +break.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +break.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +break.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +break.o: ../pathnames.h +builtin.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +builtin.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h +builtin.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h +builtin.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +builtin.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +builtin.o: $(srcdir)/bashgetopt.h ../pathnames.h +caller.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +caller.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(topdir)/dispose_cmd.h +caller.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/externs.h +caller.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +caller.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h ./builtext.h +caller.o: ${BASHINCDIR}/chartypes.h $(topdir)/bashtypes.h ../pathnames.h +cd.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +cd.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(topdir)/dispose_cmd.h +cd.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/externs.h +cd.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +cd.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h ../pathnames.h +command.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +command.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h +command.o: $(topdir)/quit.h $(srcdir)/bashgetopt.h $(BASHINCDIR)/maxpath.h +command.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +command.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +command.o: ../pathnames.h +declare.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +declare.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +declare.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +declare.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +declare.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +declare.o: $(topdir)/arrayfunc.h $(srcdir)/bashgetopt.h +declare.o: ./builtext.h ../pathnames.h +echo.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +echo.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h +echo.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +echo.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +echo.o: $(BASHINCDIR)/maxpath.h ../pathnames.h +enable.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +enable.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +enable.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +enable.o: $(topdir)/subst.h $(topdir)/externs.h +enable.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +enable.o: $(BASHINCDIR)/maxpath.h ../pathnames.h +enable.o: $(topdir)/pcomplete.h +eval.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +eval.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +eval.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +eval.o: $(topdir)/subst.h $(topdir)/externs.h +eval.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +eval.o: $(BASHINCDIR)/maxpath.h ../pathnames.h +exec.o: $(topdir)/bashtypes.h +exec.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +exec.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +exec.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +exec.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/flags.h +exec.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +exec.o: $(srcdir)/common.h $(topdir)/execute_cmd.h $(BASHINCDIR)/maxpath.h +exec.o: $(topdir)/findcmd.h $(topdir)/jobs.h ../pathnames.h +exit.o: $(topdir)/bashtypes.h +exit.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +exit.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +exit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +exit.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/jobs.h +exit.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +exit.o: $(BASHINCDIR)/maxpath.h ./builtext.h ../pathnames.h +fc.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h +fc.o: $(topdir)/builtins.h $(topdir)/command.h $(srcdir)/bashgetopt.h +fc.o: $(topdir)/bashhist.h +fc.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +fc.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h +fc.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +fc.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/shell.h $(topdir)/syntax.h +fc.o: $(topdir)/flags.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +fc.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h $(BASHINCDIR)/chartypes.h +fc.o: ../pathnames.h +fg_bg.o: $(topdir)/bashtypes.h $(srcdir)/bashgetopt.h +fg_bg.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +fg_bg.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +fg_bg.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +fg_bg.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +fg_bg.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +fg_bg.o: $(topdir)/jobs.h ../pathnames.h +getopts.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +getopts.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +getopts.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +getopts.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +getopts.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +getopts.o: ../pathnames.h +hash.o: $(topdir)/builtins.h $(topdir)/command.h $(topdir)/quit.h +hash.o: $(topdir)/findcmd.h $(topdir)/hashlib.h +hash.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +hash.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +hash.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +hash.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h ../pathnames.h +help.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +help.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +help.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +help.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +help.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +help.o: ${srcdir}/common.h ../pathnames.h +history.o: $(topdir)/bashtypes.h +history.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +history.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +history.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +history.o: $(topdir)/subst.h $(topdir)/externs.h +history.o: ${BASHINCDIR}/filecntl.h $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h +history.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/bashhist.h $(BASHINCDIR)/maxpath.h +history.o: ../pathnames.h +inlib.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +inlib.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +inlib.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +inlib.o: $(BASHINCDIR)/maxpath.h $(topdir)/subst.h $(topdir)/externs.h +inlib.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h ../pathnames.h +jobs.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +jobs.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(srcdir)/bashgetopt.h +jobs.o: $(BASHINCDIR)/maxpath.h $(topdir)/externs.h $(topdir)/jobs.h +jobs.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +jobs.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +jobs.o: ../pathnames.h +kill.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +kill.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h +kill.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +kill.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/trap.h $(topdir)/unwind_prot.h +kill.o: $(topdir)/variables.h $(topdir)/conftypes.h $(BASHINCDIR)/maxpath.h +kill.o: $(topdir)/jobs.h ../pathnames.h +let.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +let.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +let.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +let.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +let.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +let.o: ../pathnames.h +printf.o: ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/bashjmp.h +printf.o: $(topdir)/command.h $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +printf.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +printf.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/sig.h +printf.o: ../pathnames.h $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h +printf.o: $(topdir)/variables.h $(topdir)/conftypes.h $(BASHINCDIR)/stdc.h $(srcdir)/bashgetopt.h +printf.o: $(topdir)/bashtypes.h ${srcdir}/common.h $(BASHINCDIR)/chartypes.h +printf.o: ../pathnames.h +pushd.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +pushd.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +pushd.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +pushd.o: $(topdir)/subst.h $(topdir)/externs.h +pushd.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +pushd.o: $(BASHINCDIR)/maxpath.h $(srcdir)/common.h ./builtext.h +pushd.o: ../pathnames.h +read.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +read.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +read.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +read.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +read.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +read.o: $(BASHINCDIR)/shtty.h +read.o: $(topdir)/arrayfunc.h ../pathnames.h +return.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +return.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +return.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +return.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +return.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +return.o: ../pathnames.h +set.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +set.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h +set.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +set.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +set.o: $(BASHINCDIR)/maxpath.h $(topdir)/error.h +set.o: $(topdir)/arrayfunc.h ../pathnames.h +setattr.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +setattr.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h +setattr.o: $(topdir)/quit.h $(srcdir)/common.h $(srcdir)/bashgetopt.h +setattr.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +setattr.o: $(topdir)/externs.h +setattr.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +setattr.o: $(topdir)/arrayfunc.h ../pathnames.h +shift.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +shift.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +shift.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +shift.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +shift.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +shift.o: ../pathnames.h +shopt.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +shopt.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +shopt.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +shopt.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +shopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +shopt.o: $(srcdir)/common.h $(srcdir)/bashgetopt.h ../pathnames.h +shopt.o: $(topdir)/bashhist.h +source.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +source.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/findcmd.h +source.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +source.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +source.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +source.o: $(srcdir)/bashgetopt.h $(topdir)/flags.h $(topdir)/trap.h +source.o: ../pathnames.h +suspend.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +suspend.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +suspend.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +suspend.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +suspend.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +suspend.o: $(topdir)/jobs.h ../pathnames.h +test.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +test.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +test.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +test.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +test.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +test.o: $(topdir)/test.h ../pathnames.h +times.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +times.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +times.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +times.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +times.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +times.o: $(BASHINCDIR)/posixtime.h ../pathnames.h +trap.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +trap.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h +trap.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h +trap.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +trap.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +trap.o: $(topdir)/findcmd.h ../pathnames.h +type.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +type.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +type.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h +type.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +type.o: $(topdir)/externs.h $(topdir)/hashcmd.h ../pathnames.h +type.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +ulimit.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +ulimit.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +ulimit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +ulimit.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +ulimit.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +ulimit.o: ../pathnames.h +umask.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +umask.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +umask.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +umask.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +umask.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +umask.o: $(BASHINCDIR)/chartypes.h ../pathnames.h +wait.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +wait.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +wait.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +wait.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +wait.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +wait.o: $(topdir)/jobs.h +wait.o: $(BASHINCDIR)/chartypes.h ../pathnames.h + +complete.o: ../config.h ../pathnames.h +complete.o: ${topdir}/shell.h $(topdir)/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h ${topdir}/sig.h +complete.o: ${topdir}/unwind_prot.h ${topdir}/variables.h +complete.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +complete.o: ${topdir}/builtins.h +complete.o: ${topdir}/pcomplete.h +complete.o: ${srcdir}/common.h ${srcdir}/bashgetopt.h +mapfile.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h +mapfile.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h +mapfile.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +mapfile.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h +mapfile.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/variables.h $(topdir)/conftypes.h +mapfile.o: $(topdir)/arrayfunc.h ../pathnames.h + +#bind.o: $(RL_LIBSRC)chardefs.h $(RL_LIBSRC)readline.h $(RL_LIBSRC)keymaps.h + +# libintl dependencies +bind.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +break.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +caller.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +cd.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +common.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +complete.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +declare.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +enable.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +evalfile.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +exec.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +exit.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +fc.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +fg_bg.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +getopt.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +hash.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +help.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +history.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +inlib.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +jobs.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +kill.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +let.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +mapfile.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +mkbuiltins.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +printf.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +pushd.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +read.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +return.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +set.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +setattr.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +shift.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +shopt.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +source.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +suspend.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +type.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +ulimit.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +umask.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h diff --git a/builtins/alias.def b/builtins/alias.def new file mode 100644 index 0000000..d760ceb --- /dev/null +++ b/builtins/alias.def @@ -0,0 +1,241 @@ +This file is alias.def, from which is created alias.c +It implements the builtins "alias" and "unalias" in Bash. + +Copyright (C) 1987-2009 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/>. + +$BUILTIN alias +$FUNCTION alias_builtin +$DEPENDS_ON ALIAS +$PRODUCES alias.c +$SHORT_DOC alias [-p] [name[=value] ... ] +Define or display aliases. + +Without arguments, `alias' prints the list of aliases in the reusable +form `alias NAME=VALUE' on standard output. + +Otherwise, an alias is defined for each NAME whose VALUE is given. +A trailing space in VALUE causes the next word to be checked for +alias substitution when the alias is expanded. + +Options: + -p Print all defined aliases in a reusable format + +Exit Status: +alias returns true unless a NAME is supplied for which no alias has been +defined. +$END + +#include <config.h> + +#if defined (ALIAS) + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +# include "../bashansi.h" +# include "../bashintl.h" + +# include <stdio.h> +# include "../shell.h" +# include "../alias.h" +# include "common.h" +# include "bashgetopt.h" + +/* Flags for print_alias */ +#define AL_REUSABLE 0x01 + +static void print_alias __P((alias_t *, int)); + +extern int posixly_correct; + +/* Hack the alias command in a Korn shell way. */ +int +alias_builtin (list) + WORD_LIST *list; +{ + int any_failed, offset, pflag, dflags; + alias_t **alias_list, *t; + char *name, *value; + + dflags = posixly_correct ? 0 : AL_REUSABLE; + pflag = 0; + reset_internal_getopt (); + while ((offset = internal_getopt (list, "p")) != -1) + { + switch (offset) + { + case 'p': + pflag = 1; + dflags |= AL_REUSABLE; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (list == 0 || pflag) + { + if (aliases == 0) + return (EXECUTION_SUCCESS); + + alias_list = all_aliases (); + + if (alias_list == 0) + return (EXECUTION_SUCCESS); + + for (offset = 0; alias_list[offset]; offset++) + print_alias (alias_list[offset], dflags); + + free (alias_list); /* XXX - Do not free the strings. */ + + if (list == 0) + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + any_failed = 0; + while (list) + { + name = list->word->word; + + for (offset = 0; name[offset] && name[offset] != '='; offset++) + ; + + if (offset && name[offset] == '=') + { + name[offset] = '\0'; + value = name + offset + 1; + + if (legal_alias_name (name, 0) == 0) + { + builtin_error (_("`%s': invalid alias name"), name); + any_failed++; + } + else + add_alias (name, value); + } + else + { + t = find_alias (name); + if (t) + print_alias (t, dflags); + else + { + sh_notfound (name); + any_failed++; + } + } + list = list->next; + } + + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} +#endif /* ALIAS */ + +$BUILTIN unalias +$FUNCTION unalias_builtin +$DEPENDS_ON ALIAS +$SHORT_DOC unalias [-a] name [name ...] +Remove each NAME from the list of defined aliases. + +Options: + -a remove all alias definitions. + +Return success unless a NAME is not an existing alias. +$END + +#if defined (ALIAS) +/* Remove aliases named in LIST from the aliases database. */ +int +unalias_builtin (list) + register WORD_LIST *list; +{ + register alias_t *alias; + int opt, aflag; + + aflag = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "a")) != -1) + { + switch (opt) + { + case 'a': + aflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (aflag) + { + delete_all_aliases (); + return (EXECUTION_SUCCESS); + } + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + aflag = 0; + while (list) + { + alias = find_alias (list->word->word); + + if (alias) + remove_alias (alias->name); + else + { + sh_notfound (list->word->word); + aflag++; + } + + list = list->next; + } + + return (aflag ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +/* Output ALIAS in such a way as to allow it to be read back in. */ +static void +print_alias (alias, flags) + alias_t *alias; + int flags; +{ + char *value; + + value = sh_single_quote (alias->value); + if (flags & AL_REUSABLE) + printf ("alias "); + printf ("%s=%s\n", alias->name, value); + free (value); + + fflush (stdout); +} +#endif /* ALIAS */ diff --git a/builtins/bashgetopt.c b/builtins/bashgetopt.c new file mode 100644 index 0000000..b1b7070 --- /dev/null +++ b/builtins/bashgetopt.c @@ -0,0 +1,175 @@ +/* bashgetopt.c -- `getopt' for use by the builtins. */ + +/* Copyright (C) 1992-2002 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include <chartypes.h> +#include <errno.h> + +#include "../shell.h" +#include "common.h" + +#define ISOPT(s) (((*(s) == '-') || (plus && *(s) == '+')) && (s)[1]) +#define NOTOPT(s) (((*(s) != '-') && (!plus || *(s) != '+')) || (s)[1] == '\0') + +static int sp; + +char *list_optarg; +int list_optopt; +int list_opttype; + +static WORD_LIST *lhead = (WORD_LIST *)NULL; +WORD_LIST *lcurrent = (WORD_LIST *)NULL; +WORD_LIST *loptend; /* Points to the first non-option argument in the list */ + +int +internal_getopt(list, opts) +WORD_LIST *list; +char *opts; +{ + register int c; + register char *cp; + int plus; /* nonzero means to handle +option */ + static char errstr[3] = { '-', '\0', '\0' }; + + plus = *opts == '+'; + if (plus) + opts++; + + if (list == 0) { + list_optarg = (char *)NULL; + loptend = (WORD_LIST *)NULL; /* No non-option arguments */ + return -1; + } + + if (list != lhead || lhead == 0) { + /* Hmmm.... called with a different word list. Reset. */ + sp = 1; + lcurrent = lhead = list; + loptend = (WORD_LIST *)NULL; + } + + if (sp == 1) { + if (lcurrent == 0 || NOTOPT(lcurrent->word->word)) { + lhead = (WORD_LIST *)NULL; + loptend = lcurrent; + return(-1); + } else if (lcurrent->word->word[0] == '-' && + lcurrent->word->word[1] == '-' && + lcurrent->word->word[2] == 0) { + lhead = (WORD_LIST *)NULL; + loptend = lcurrent->next; + return(-1); + } + errstr[0] = list_opttype = lcurrent->word->word[0]; + } + + list_optopt = c = lcurrent->word->word[sp]; + + if (c == ':' || (cp = strchr(opts, c)) == NULL) { + errstr[1] = c; + sh_invalidopt (errstr); + if (lcurrent->word->word[++sp] == '\0') { + lcurrent = lcurrent->next; + sp = 1; + } + list_optarg = NULL; + if (lcurrent) + loptend = lcurrent->next; + return('?'); + } + + if (*++cp == ':' || *cp == ';') { + /* `:': Option requires an argument. */ + /* `;': option argument may be missing */ + /* We allow -l2 as equivalent to -l 2 */ + if (lcurrent->word->word[sp+1]) { + list_optarg = lcurrent->word->word + sp + 1; + lcurrent = lcurrent->next; + /* If the specifier is `;', don't set optarg if the next + argument looks like another option. */ +#if 0 + } else if (lcurrent->next && (*cp == ':' || lcurrent->next->word->word[0] != '-')) { +#else + } else if (lcurrent->next && (*cp == ':' || NOTOPT(lcurrent->next->word->word))) { +#endif + lcurrent = lcurrent->next; + list_optarg = lcurrent->word->word; + lcurrent = lcurrent->next; + } else if (*cp == ';') { + list_optarg = (char *)NULL; + lcurrent = lcurrent->next; + } else { /* lcurrent->next == NULL */ + errstr[1] = c; + sh_needarg (errstr); + sp = 1; + list_optarg = (char *)NULL; + return('?'); + } + sp = 1; + } else if (*cp == '#') { + /* option requires a numeric argument */ + if (lcurrent->word->word[sp+1]) { + if (DIGIT(lcurrent->word->word[sp+1])) { + list_optarg = lcurrent->word->word + sp + 1; + lcurrent = lcurrent->next; + } else + list_optarg = (char *)NULL; + } else { + if (lcurrent->next && legal_number(lcurrent->next->word->word, (intmax_t *)0)) { + lcurrent = lcurrent->next; + list_optarg = lcurrent->word->word; + lcurrent = lcurrent->next; + } else { + errstr[1] = c; + sh_neednumarg (errstr); + sp = 1; + list_optarg = (char *)NULL; + return ('?'); + } + } + + } else { + /* No argument, just return the option. */ + if (lcurrent->word->word[++sp] == '\0') { + sp = 1; + lcurrent = lcurrent->next; + } + list_optarg = (char *)NULL; + } + + return(c); +} + +/* + * reset_internal_getopt -- force the in[ft]ernal getopt to reset + */ + +void +reset_internal_getopt () +{ + lhead = lcurrent = loptend = (WORD_LIST *)NULL; + sp = 1; +} diff --git a/builtins/bashgetopt.h b/builtins/bashgetopt.h new file mode 100644 index 0000000..f2aea26 --- /dev/null +++ b/builtins/bashgetopt.h @@ -0,0 +1,39 @@ +/* bashgetopt.h -- extern declarations for stuff defined in bashgetopt.c. */ + +/* Copyright (C) 1993 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/>. +*/ + +/* See getopt.h for the explanation of these variables. */ + +#if !defined (__BASH_GETOPT_H) +# define __BASH_GETOPT_H + +#include <stdc.h> + +extern char *list_optarg; + +extern int list_optopt; +extern int list_opttype; + +extern WORD_LIST *lcurrent; +extern WORD_LIST *loptend; + +extern int internal_getopt __P((WORD_LIST *, char *)); +extern void reset_internal_getopt __P((void)); + +#endif /* !__BASH_GETOPT_H */ diff --git a/builtins/bind.def b/builtins/bind.def new file mode 100644 index 0000000..d0c953b --- /dev/null +++ b/builtins/bind.def @@ -0,0 +1,332 @@ +This file is bind.def, from which is created bind.c. +It implements the builtin "bind" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES bind.c + +#include <config.h> + +$BUILTIN bind +$DEPENDS_ON READLINE +$FUNCTION bind_builtin +$SHORT_DOC bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command] +Set Readline key bindings and variables. + +Bind a key sequence to a Readline function or a macro, or set a +Readline variable. The non-option argument syntax is equivalent to +that found in ~/.inputrc, but must be passed as a single argument: +e.g., bind '"\C-x\C-r": re-read-init-file'. + +Options: + -m keymap Use KEYMAP as the keymap for the duration of this + command. Acceptable keymap names are emacs, + emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, + vi-command, and vi-insert. + -l List names of functions. + -P List function names and bindings. + -p List functions and bindings in a form that can be + reused as input. + -S List key sequences that invoke macros and their values + -s List key sequences that invoke macros and their values + in a form that can be reused as input. + -V List variable names and values + -v List variable names and values in a form that can + be reused as input. + -q function-name Query about which keys invoke the named function. + -u function-name Unbind all keys which are bound to the named function. + -r keyseq Remove the binding for KEYSEQ. + -f filename Read key bindings from FILENAME. + -x keyseq:shell-command Cause SHELL-COMMAND to be executed when + KEYSEQ is entered. + +Exit Status: +bind returns 0 unless an unrecognized option is given or an error occurs. +$END + +#if defined (READLINE) + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#include <readline/readline.h> +#include <readline/history.h> + +#include "../bashintl.h" + +#include "../shell.h" +#include "../bashline.h" +#include "bashgetopt.h" +#include "common.h" + +static int query_bindings __P((char *)); +static int unbind_command __P((char *)); + +extern int no_line_editing; + +#define BIND_RETURN(x) do { return_code = x; goto bind_exit; } while (0) + +#define LFLAG 0x0001 +#define PFLAG 0x0002 +#define FFLAG 0x0004 +#define VFLAG 0x0008 +#define QFLAG 0x0010 +#define MFLAG 0x0020 +#define RFLAG 0x0040 +#define PPFLAG 0x0080 +#define VVFLAG 0x0100 +#define SFLAG 0x0200 +#define SSFLAG 0x0400 +#define UFLAG 0x0800 +#define XFLAG 0x1000 + +int +bind_builtin (list) + WORD_LIST *list; +{ + int return_code; + Keymap kmap, saved_keymap; + int flags, opt; + char *initfile, *map_name, *fun_name, *unbind_name, *remove_seq, *cmd_seq; + + if (no_line_editing) + { +#if 0 + builtin_error (_("line editing not enabled")); + return (EXECUTION_FAILURE); +#else + builtin_warning (_("line editing not enabled")); +#endif + } + + kmap = saved_keymap = (Keymap) NULL; + flags = 0; + initfile = map_name = fun_name = unbind_name = remove_seq = (char *)NULL; + return_code = EXECUTION_SUCCESS; + + if (bash_readline_initialized == 0) + initialize_readline (); + + begin_unwind_frame ("bind_builtin"); + unwind_protect_var (rl_outstream); + + rl_outstream = stdout; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lvpVPsSf:q:u:m:r:x:")) != EOF) + { + switch (opt) + { + case 'l': + flags |= LFLAG; + break; + case 'v': + flags |= VFLAG; + break; + case 'p': + flags |= PFLAG; + break; + case 'f': + flags |= FFLAG; + initfile = list_optarg; + break; + case 'm': + flags |= MFLAG; + map_name = list_optarg; + break; + case 'q': + flags |= QFLAG; + fun_name = list_optarg; + break; + case 'u': + flags |= UFLAG; + unbind_name = list_optarg; + break; + case 'r': + flags |= RFLAG; + remove_seq = list_optarg; + break; + case 'V': + flags |= VVFLAG; + break; + case 'P': + flags |= PPFLAG; + break; + case 's': + flags |= SFLAG; + break; + case 'S': + flags |= SSFLAG; + break; + case 'x': + flags |= XFLAG; + cmd_seq = list_optarg; + break; + default: + builtin_usage (); + BIND_RETURN (EX_USAGE); + } + } + + list = loptend; + + /* First, see if we need to install a special keymap for this + command. Then start on the arguments. */ + + if ((flags & MFLAG) && map_name) + { + kmap = rl_get_keymap_by_name (map_name); + if (!kmap) + { + builtin_error (_("`%s': invalid keymap name"), map_name); + BIND_RETURN (EXECUTION_FAILURE); + } + } + + if (kmap) + { + saved_keymap = rl_get_keymap (); + rl_set_keymap (kmap); + } + + /* XXX - we need to add exclusive use tests here. It doesn't make sense + to use some of these options together. */ + /* Now hack the option arguments */ + if (flags & LFLAG) + rl_list_funmap_names (); + + if (flags & PFLAG) + rl_function_dumper (1); + + if (flags & PPFLAG) + rl_function_dumper (0); + + if (flags & SFLAG) + rl_macro_dumper (1); + + if (flags & SSFLAG) + rl_macro_dumper (0); + + if (flags & VFLAG) + rl_variable_dumper (1); + + if (flags & VVFLAG) + rl_variable_dumper (0); + + if ((flags & FFLAG) && initfile) + { + if (rl_read_init_file (initfile) != 0) + { + builtin_error (_("%s: cannot read: %s"), initfile, strerror (errno)); + BIND_RETURN (EXECUTION_FAILURE); + } + } + + if ((flags & QFLAG) && fun_name) + return_code = query_bindings (fun_name); + + if ((flags & UFLAG) && unbind_name) + return_code = unbind_command (unbind_name); + + if ((flags & RFLAG) && remove_seq) + { + if (rl_bind_keyseq (remove_seq, (rl_command_func_t *)NULL) != 0) + { + builtin_error (_("`%s': cannot unbind"), remove_seq); + BIND_RETURN (EXECUTION_FAILURE); + } + } + + if (flags & XFLAG) + return_code = bind_keyseq_to_unix_command (cmd_seq); + + /* Process the rest of the arguments as binding specifications. */ + while (list) + { + rl_parse_and_bind (list->word->word); + list = list->next; + } + + bind_exit: + if (saved_keymap) + rl_set_keymap (saved_keymap); + + run_unwind_frame ("bind_builtin"); + + return (sh_chkwrite (return_code)); +} + +static int +query_bindings (name) + char *name; +{ + rl_command_func_t *function; + char **keyseqs; + int j; + + function = rl_named_function (name); + if (function == 0) + { + builtin_error (_("`%s': unknown function name"), name); + return EXECUTION_FAILURE; + } + + keyseqs = rl_invoking_keyseqs (function); + + if (!keyseqs) + { + printf (_("%s is not bound to any keys.\n"), name); + return EXECUTION_FAILURE; + } + + printf (_("%s can be invoked via "), name); + for (j = 0; j < 5 && keyseqs[j]; j++) + printf ("\"%s\"%s", keyseqs[j], keyseqs[j + 1] ? ", " : ".\n"); + if (keyseqs[j]) + printf ("...\n"); + strvec_dispose (keyseqs); + return EXECUTION_SUCCESS; +} + +static int +unbind_command (name) + char *name; +{ + rl_command_func_t *function; + + function = rl_named_function (name); + if (function == 0) + { + builtin_error (_("`%s': unknown function name"), name); + return EXECUTION_FAILURE; + } + + rl_unbind_function_in_map (function, rl_get_keymap ()); + return EXECUTION_SUCCESS; +} +#endif /* READLINE */ diff --git a/builtins/break.def b/builtins/break.def new file mode 100644 index 0000000..232ac1a --- /dev/null +++ b/builtins/break.def @@ -0,0 +1,141 @@ +This file is break.def, from which is created break.c. +It implements the builtins "break" and "continue" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES break.c + +$BUILTIN break +$FUNCTION break_builtin +$SHORT_DOC break [n] +Exit for, while, or until loops. + +Exit a FOR, WHILE or UNTIL loop. If N is specified, break N enclosing +loops. + +Exit Status: +The exit status is 0 unless N is not greater than or equal to 1. +$END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" + +extern char *this_command_name; +extern int posixly_correct; + +static int check_loop_level __P((void)); + +/* The depth of while's and until's. */ +int loop_level = 0; + +/* Non-zero when a "break" instruction is encountered. */ +int breaking = 0; + +/* Non-zero when we have encountered a continue instruction. */ +int continuing = 0; + +/* Set up to break x levels, where x defaults to 1, but can be specified + as the first argument. */ +int +break_builtin (list) + WORD_LIST *list; +{ + intmax_t newbreak; + + if (check_loop_level () == 0) + return (EXECUTION_SUCCESS); + + (void)get_numeric_arg (list, 1, &newbreak); + + if (newbreak <= 0) + { + sh_erange (list->word->word, _("loop count")); + breaking = loop_level; + return (EXECUTION_FAILURE); + } + + if (newbreak > loop_level) + newbreak = loop_level; + + breaking = newbreak; + + return (EXECUTION_SUCCESS); +} + +$BUILTIN continue +$FUNCTION continue_builtin +$SHORT_DOC continue [n] +Resume for, while, or until loops. + +Resumes the next iteration of the enclosing FOR, WHILE or UNTIL loop. +If N is specified, resumes the Nth enclosing loop. + +Exit Status: +The exit status is 0 unless N is not greater than or equal to 1. +$END + +/* Set up to continue x levels, where x defaults to 1, but can be specified + as the first argument. */ +int +continue_builtin (list) + WORD_LIST *list; +{ + intmax_t newcont; + + if (check_loop_level () == 0) + return (EXECUTION_SUCCESS); + + (void)get_numeric_arg (list, 1, &newcont); + + if (newcont <= 0) + { + sh_erange (list->word->word, _("loop count")); + breaking = loop_level; + return (EXECUTION_FAILURE); + } + + if (newcont > loop_level) + newcont = loop_level; + + continuing = newcont; + + return (EXECUTION_SUCCESS); +} + +/* Return non-zero if a break or continue command would be okay. + Print an error message if break or continue is meaningless here. */ +static int +check_loop_level () +{ +#if defined (BREAK_COMPLAINS) + if (loop_level == 0 && posixly_correct == 0) + builtin_error (_("only meaningful in a `for', `while', or `until' loop")); +#endif /* BREAK_COMPLAINS */ + + return (loop_level); +} diff --git a/builtins/builtin.def b/builtins/builtin.def new file mode 100644 index 0000000..3e765e4 --- /dev/null +++ b/builtins/builtin.def @@ -0,0 +1,85 @@ +This file is builtin.def, from which is created builtin.c. +It implements the builtin "builtin" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES builtin.c + +$BUILTIN builtin +$FUNCTION builtin_builtin +$SHORT_DOC builtin [shell-builtin [arg ...]] +Execute shell builtins. + +Execute SHELL-BUILTIN with arguments ARGs without performing command +lookup. This is useful when you wish to reimplement a shell builtin +as a shell function, but need to execute the builtin within the function. + +Exit Status: +Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is +not a shell builtin.. +$END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +extern char *this_command_name; + +/* Run the command mentioned in list directly, without going through the + normal alias/function/builtin/filename lookup process. */ +int +builtin_builtin (list) + WORD_LIST *list; +{ + sh_builtin_func_t *function; + register char *command; + + if (no_options (list)) + return (EX_USAGE); + list = loptend; /* skip over possible `--' */ + + if (list == 0) + return (EXECUTION_SUCCESS); + + command = list->word->word; +#if defined (DISABLED_BUILTINS) + function = builtin_address (command); +#else /* !DISABLED_BUILTINS */ + function = find_shell_builtin (command); +#endif /* !DISABLED_BUILTINS */ + + if (!function) + { + sh_notbuiltin (command); + return (EXECUTION_FAILURE); + } + else + { + this_command_name = command; + list = list->next; + return ((*function) (list)); + } +} diff --git a/builtins/caller.def b/builtins/caller.def new file mode 100644 index 0000000..7ddbdad --- /dev/null +++ b/builtins/caller.def @@ -0,0 +1,153 @@ +This file is caller.def, from which is created caller.c. It implements the +builtin "caller" in Bash. + +Copyright (C) 2002-2008 Rocky Bernstein for 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/>. + +$PRODUCES caller.c + +$BUILTIN caller +$FUNCTION caller_builtin +$DEPENDS_ON DEBUGGER +$SHORT_DOC caller [expr] +Return the context of the current subroutine call. + +Without EXPR, returns "$line $filename". With EXPR, returns +"$line $subroutine $filename"; this extra information can be used to +provide a stack trace. + +The value of EXPR indicates how many call frames to go back before the +current one; the top frame is frame 0. + +Exit Status: +Returns 0 unless the shell is not executing a shell function or EXPR +is invalid. +$END + +#include <config.h> +#include <stdio.h> +#include "chartypes.h" +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <errno.h> + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "builtext.h" +#include "bashgetopt.h" + +#ifdef LOADABLE_BUILTIN +# include "builtins.h" +#endif + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +int +caller_builtin (list) + WORD_LIST *list; +{ +#if !defined (ARRAY_VARS) + printf ("1 NULL\n"); + return (EXECUTION_FAILURE); +#else + SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v; + ARRAY *funcname_a, *bash_source_a, *bash_lineno_a; + char *funcname_s, *source_s, *lineno_s; + intmax_t num; + + GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a); + GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a); + GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a); + + if (bash_lineno_a == 0 || array_empty (bash_lineno_a)) + return (EXECUTION_FAILURE); + + if (bash_source_a == 0 || array_empty (bash_source_a)) + return (EXECUTION_FAILURE); + + if (no_options (list)) + return (EX_USAGE); + list = loptend; /* skip over possible `--' */ + + /* If there is no argument list, then give short form: line filename. */ + if (list == 0) + { + lineno_s = array_reference (bash_lineno_a, 0); + source_s = array_reference (bash_source_a, 1); + printf("%s %s\n", lineno_s ? lineno_s : "NULL", source_s ? source_s : "NULL"); + return (EXECUTION_SUCCESS); + } + + if (funcname_a == 0 || array_empty (funcname_a)) + return (EXECUTION_FAILURE); + + if (legal_number (list->word->word, &num)) + { + lineno_s = array_reference (bash_lineno_a, num); + source_s = array_reference (bash_source_a, num+1); + funcname_s = array_reference (funcname_a, num+1); + + if (lineno_s == NULL|| source_s == NULL || funcname_s == NULL) + return (EXECUTION_FAILURE); + + printf("%s %s %s\n", lineno_s, funcname_s, source_s); + } + else + { + sh_invalidnum (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +#endif +} + +#ifdef LOADABLE_BUILTIN +static char *caller_doc[] = { +N_("Returns the context of the current subroutine call.\n\ + \n\ + Without EXPR, returns "$line $filename". With EXPR, returns\n\ + "$line $subroutine $filename"; this extra information can be used to\n\ + provide a stack trace.\n\ + \n\ + The value of EXPR indicates how many call frames to go back before the\n\ + current one; the top frame is frame 0."), + (char *)NULL +}; + +struct builtin caller_struct = { + "caller", + caller_builtin, + BUILTIN_ENABLED, + caller_doc, + "caller [EXPR]", + 0 +}; + +#endif /* LOADABLE_BUILTIN */ diff --git a/builtins/cd.def b/builtins/cd.def new file mode 100644 index 0000000..d53b258 --- /dev/null +++ b/builtins/cd.def @@ -0,0 +1,513 @@ +This file is cd.def, from which is created cd.c. It implements the +builtins "cd" and "pwd" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES cd.c +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashtypes.h" +#include "posixdir.h" +#include "posixstat.h" +#ifndef _MINIX +#include <sys/param.h> +#endif + +#include <stdio.h> + +#include "../bashansi.h" +#include "../bashintl.h" + +#include <errno.h> +#include <tilde/tilde.h> + +#include "../shell.h" +#include "../flags.h" +#include "maxpath.h" +#include "common.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int posixly_correct; +extern int array_needs_making; +extern const char * const bash_getcwd_errstr; + +static int bindpwd __P((int)); +static void setpwd __P((char *)); +static char *resetpwd __P((char *)); +static int change_to_directory __P((char *, int)); + +/* Change this to 1 to get cd spelling correction by default. */ +int cdspelling = 0; + +int cdable_vars; + +$BUILTIN cd +$FUNCTION cd_builtin +$SHORT_DOC cd [-L|-P] [dir] +Change the shell working directory. + +Change the current directory to DIR. The default DIR is the value of the +HOME shell variable. + +The variable CDPATH defines the search path for the directory containing +DIR. Alternative directory names in CDPATH are separated by a colon (:). +A null directory name is the same as the current directory. If DIR begins +with a slash (/), then CDPATH is not used. + +If the directory is not found, and the shell option `cdable_vars' is set, +the word is assumed to be a variable name. If that variable has a value, +its value is used for DIR. + +Options: + -L force symbolic links to be followed + -P use the physical directory structure without following symbolic + links + +The default is to follow symbolic links, as if `-L' were specified. + +Exit Status: +Returns 0 if the directory is changed; non-zero otherwise. +$END + +/* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */ +static void +setpwd (dirname) + char *dirname; +{ + int old_anm; + SHELL_VAR *tvar; + + old_anm = array_needs_making; + tvar = bind_variable ("PWD", dirname ? dirname : "", 0); + if (old_anm == 0 && array_needs_making && exported_p (tvar)) + { + update_export_env_inplace ("PWD=", 4, dirname ? dirname : ""); + array_needs_making = 0; + } +} + +static int +bindpwd (no_symlinks) + int no_symlinks; +{ + char *dirname, *pwdvar; + int old_anm, r; + SHELL_VAR *tvar; + + r = sh_chkwrite (EXECUTION_SUCCESS); + +#define tcwd the_current_working_directory + dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("cd"); +#undef tcwd + + old_anm = array_needs_making; + pwdvar = get_string_value ("PWD"); + + tvar = bind_variable ("OLDPWD", pwdvar, 0); + if (old_anm == 0 && array_needs_making && exported_p (tvar)) + { + update_export_env_inplace ("OLDPWD=", 7, pwdvar); + array_needs_making = 0; + } + + setpwd (dirname); + + if (dirname && dirname != the_current_working_directory) + free (dirname); + + return (r); +} + +/* Call get_working_directory to reset the value of + the_current_working_directory () */ +static char * +resetpwd (caller) + char *caller; +{ + char *tdir; + + FREE (the_current_working_directory); + the_current_working_directory = (char *)NULL; + tdir = get_working_directory (caller); + return (tdir); +} + +#define LCD_DOVARS 0x001 +#define LCD_DOSPELL 0x002 +#define LCD_PRINTPATH 0x004 +#define LCD_FREEDIRNAME 0x010 + +/* This builtin is ultimately the way that all user-visible commands should + change the current working directory. It is called by cd_to_string (), + so the programming interface is simple, and it handles errors and + restrictions properly. */ +int +cd_builtin (list) + WORD_LIST *list; +{ + char *dirname, *cdpath, *path, *temp; + int path_index, no_symlinks, opt, lflag; + +#if defined (RESTRICTED_SHELL) + if (restricted) + { + sh_restricted ((char *)NULL); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ + + no_symlinks = no_symbolic_links; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) + { + switch (opt) + { + case 'P': + no_symlinks = 1; + break; + case 'L': + no_symlinks = 0; + break; + default: + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + list = loptend; + + lflag = (cdable_vars ? LCD_DOVARS : 0) | + ((interactive && cdspelling) ? LCD_DOSPELL : 0); + + if (list == 0) + { + /* `cd' without arguments is equivalent to `cd $HOME' */ + dirname = get_string_value ("HOME"); + + if (dirname == 0) + { + builtin_error (_("HOME not set")); + return (EXECUTION_FAILURE); + } + lflag = 0; + } + else if (list->word->word[0] == '-' && list->word->word[1] == '\0') + { + /* This is `cd -', equivalent to `cd $OLDPWD' */ + dirname = get_string_value ("OLDPWD"); + + if (dirname == 0) + { + builtin_error (_("OLDPWD not set")); + return (EXECUTION_FAILURE); + } +#if 0 + lflag = interactive ? LCD_PRINTPATH : 0; +#else + lflag = LCD_PRINTPATH; /* According to SUSv3 */ +#endif + } + else if (absolute_pathname (list->word->word)) + dirname = list->word->word; + else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH"))) + { + dirname = list->word->word; + + /* Find directory in $CDPATH. */ + path_index = 0; + while (path = extract_colon_unit (cdpath, &path_index)) + { + /* OPT is 1 if the path element is non-empty */ + opt = path[0] != '\0'; + temp = sh_makepath (path, dirname, MP_DOTILDE); + free (path); + + if (change_to_directory (temp, no_symlinks)) + { + /* POSIX.2 says that if a nonempty directory from CDPATH + is used to find the directory to change to, the new + directory name is echoed to stdout, whether or not + the shell is interactive. */ + if (opt && (path = no_symlinks ? temp : the_current_working_directory)) + printf ("%s\n", path); + + free (temp); +#if 0 + /* Posix.2 says that after using CDPATH, the resultant + value of $PWD will not contain `.' or `..'. */ + return (bindpwd (posixly_correct || no_symlinks)); +#else + return (bindpwd (no_symlinks)); +#endif + } + else + free (temp); + } + + /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't + try the current directory, so we just punt now with an error + message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] + is so we don't mistakenly treat a CDPATH value of "" as not + specifying the current directory. */ + if (posixly_correct && cdpath[0]) + { + builtin_error ("%s: %s", dirname, strerror (ENOENT)); + return (EXECUTION_FAILURE); + } + } + else + dirname = list->word->word; + + /* When we get here, DIRNAME is the directory to change to. If we + chdir successfully, just return. */ + if (change_to_directory (dirname, no_symlinks)) + { + if (lflag & LCD_PRINTPATH) + printf ("%s\n", dirname); + return (bindpwd (no_symlinks)); + } + + /* If the user requests it, then perhaps this is the name of + a shell variable, whose value contains the directory to + change to. */ + if (lflag & LCD_DOVARS) + { + temp = get_string_value (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); + } + } + + /* If the user requests it, try to find a directory name similar in + spelling to the one requested, in case the user made a simple + typo. This is similar to the UNIX 8th and 9th Edition shells. */ + if (lflag & LCD_DOSPELL) + { + temp = dirspell (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); + } + else + FREE (temp); + } + + builtin_error ("%s: %s", dirname, strerror (errno)); + return (EXECUTION_FAILURE); +} + +$BUILTIN pwd +$FUNCTION pwd_builtin +$SHORT_DOC pwd [-LP] +Print the name of the current working directory. + +Options: + -L print the value of $PWD if it names the current working + directory + -P print the physical directory, without any symbolic links + +By default, `pwd' behaves as if `-L' were specified. + +Exit Status: +Returns 0 unless an invalid option is given or the current directory +cannot be read. +$END + +/* Non-zero means that pwd always prints the physical directory, without + symbolic links. */ +static int verbatim_pwd; + +/* Print the name of the current working directory. */ +int +pwd_builtin (list) + WORD_LIST *list; +{ + char *directory; + int opt, pflag; + + verbatim_pwd = no_symbolic_links; + pflag = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) + { + switch (opt) + { + case 'P': + verbatim_pwd = pflag = 1; + break; + case 'L': + verbatim_pwd = 0; + break; + default: + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + list = loptend; + +#define tcwd the_current_working_directory + + directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("pwd"); + + /* Try again using getcwd() if canonicalization fails (for instance, if + the file system has changed state underneath bash). */ + if ((tcwd && directory == 0) || + (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0)) + directory = resetpwd ("pwd"); + +#undef tcwd + + if (directory) + { + printf ("%s\n", directory); + /* This is dumb but posix-mandated. */ + if (posixly_correct && pflag) + setpwd (directory); + if (directory != the_current_working_directory) + free (directory); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + else + return (EXECUTION_FAILURE); +} + +/* Do the work of changing to the directory NEWDIR. Handle symbolic + link following, etc. This function *must* return with + the_current_working_directory either set to NULL (in which case + getcwd() will eventually be called), or set to a string corresponding + to the working directory. Return 1 on success, 0 on failure. */ + +static int +change_to_directory (newdir, nolinks) + char *newdir; + int nolinks; +{ + char *t, *tdir; + int err, canon_failed, r, ndlen, dlen; + + tdir = (char *)NULL; + + if (the_current_working_directory == 0) + { + t = get_working_directory ("chdir"); + FREE (t); + } + + t = make_absolute (newdir, the_current_working_directory); + + /* TDIR is either the canonicalized absolute pathname of NEWDIR + (nolinks == 0) or the absolute physical pathname of NEWDIR + (nolinks != 0). */ + tdir = nolinks ? sh_physpath (t, 0) + : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); + + ndlen = strlen (newdir); + dlen = strlen (t); + + /* Use the canonicalized version of NEWDIR, or, if canonicalization + failed, use the non-canonical form. */ + canon_failed = 0; + if (tdir && *tdir) + free (t); + else + { + FREE (tdir); + tdir = t; + canon_failed = 1; + } + + /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath + returns NULL (because it checks the path, it will return NULL if the + resolved path doesn't exist), fail immediately. */ + if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX)) + { +#if defined ENAMETOOLONG + if (errno != ENOENT && errno != ENAMETOOLONG) +#else + if (errno != ENOENT) +#endif + errno = ENOTDIR; + free (tdir); + return (0); + } + + /* If the chdir succeeds, update the_current_working_directory. */ + if (chdir (nolinks ? newdir : tdir) == 0) + { + /* If canonicalization failed, but the chdir succeeded, reset the + shell's idea of the_current_working_directory. */ + if (canon_failed) + { + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); + } + else + set_working_directory (tdir); + + free (tdir); + return (1); + } + + /* We failed to change to the appropriate directory name. If we tried + what the user passed (nolinks != 0), punt now. */ + if (nolinks) + { + free (tdir); + return (0); + } + + err = errno; + + /* We're not in physical mode (nolinks == 0), but we failed to change to + the canonicalized directory name (TDIR). Try what the user passed + verbatim. If we succeed, reinitialize the_current_working_directory. */ + if (chdir (newdir) == 0) + { + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); + else + free (t); + + r = 1; + } + else + { + errno = err; + r = 0; + } + + free (tdir); + return r; +} diff --git a/builtins/colon.def b/builtins/colon.def new file mode 100644 index 0000000..4b78b30 --- /dev/null +++ b/builtins/colon.def @@ -0,0 +1,67 @@ +This file is colon.def, from which is created colon.c. +It implements the builtin ":" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES colon.c + +$BUILTIN : +$DOCNAME colon +$FUNCTION colon_builtin +$SHORT_DOC : +Null command. + +No effect; the command does nothing. + +Exit Status: +Always succeeds. +$END + +$BUILTIN true +$FUNCTION colon_builtin +$SHORT_DOC true +Return a successful result. + +Exit Status: +Always succeeds. +$END + +$BUILTIN false +$FUNCTION false_builtin +$SHORT_DOC false +Return an unsuccessful result. + +Exit Status: +Always fails. +$END + +/* Return a successful result. */ +int +colon_builtin (ignore) + char *ignore; +{ + return (0); +} + +/* Return an unsuccessful result. */ +int +false_builtin (ignore) + char *ignore; +{ + return (1); +} diff --git a/builtins/command.def b/builtins/command.def new file mode 100644 index 0000000..1845279 --- /dev/null +++ b/builtins/command.def @@ -0,0 +1,219 @@ +This file is command.def, from which is created command.c. +It implements the builtin "command" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES command.c + +$BUILTIN command +$FUNCTION command_builtin +$SHORT_DOC command [-pVv] command [arg ...] +Execute a simple command or display information about commands. + +Runs COMMAND with ARGS suppressing shell function lookup, or display +information about the specified COMMANDs. Can be used to invoke commands +on disk when a function with the same name exists. + +Options: + -p use a default value for PATH that is guaranteed to find all of + the standard utilities + -v print a description of COMMAND similar to the `type' builtin + -V print a more verbose description of each COMMAND + +Exit Status: +Returns exit status of COMMAND, or failure if COMMAND is not found. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" + +#include "../shell.h" +#include "../execute_cmd.h" +#include "../flags.h" +#include "bashgetopt.h" +#include "common.h" + +#if defined (_CS_PATH) && defined (HAVE_CONFSTR) && !HAVE_DECL_CONFSTR +extern size_t confstr __P((int, char *, size_t)); +#endif + +extern int subshell_environment; + +static void restore_path __P((char *)); +static char *get_standard_path __P((void)); + +/* Run the commands mentioned in LIST without paying attention to shell + functions. */ +int +command_builtin (list) + WORD_LIST *list; +{ + int result, verbose, use_standard_path, opt; + char *old_path, *standard_path; + COMMAND *command; + + verbose = use_standard_path = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "pvV")) != -1) + { + switch (opt) + { + case 'p': + use_standard_path = 1; + break; + case 'V': + verbose = CDESC_SHORTDESC|CDESC_ABSPATH; /* look in common.h for constants */ + break; + case 'v': + verbose = CDESC_REUSABLE; /* ditto */ + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + return (EXECUTION_SUCCESS); + +#if defined (RESTRICTED_SHELL) + if (use_standard_path && restricted) + { + sh_restricted ("-p"); + return (EXECUTION_FAILURE); + } +#endif + + begin_unwind_frame ("command_builtin"); + + if (use_standard_path) + { + old_path = get_string_value ("PATH"); + /* If old_path is NULL, $PATH is unset. If so, we want to make sure + it's unset after this command completes. */ + if (old_path) + old_path = savestring (old_path); + add_unwind_protect ((Function *)restore_path, old_path); + + standard_path = get_standard_path (); + bind_variable ("PATH", standard_path ? standard_path : "", 0); + stupidly_hack_special_variables ("PATH"); + FREE (standard_path); + } + + if (verbose) + { + int found, any_found; + + for (any_found = 0; list; list = list->next) + { + found = describe_command (list->word->word, verbose); + + if (found == 0 && verbose != CDESC_REUSABLE) + sh_notfound (list->word->word); + + any_found += found; + } + + run_unwind_frame ("command_builtin"); + return (any_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + } + +#define COMMAND_BUILTIN_FLAGS (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION | CMD_COMMAND_BUILTIN) + + /* We don't want this to be reparsed (consider command echo 'foo &'), so + just make a simple_command structure and call execute_command with it. */ + command = make_bare_simple_command (); + command->value.Simple->words = (WORD_LIST *)copy_word_list (list); + command->value.Simple->redirects = (REDIRECT *)NULL; + command->flags |= COMMAND_BUILTIN_FLAGS; + command->value.Simple->flags |= COMMAND_BUILTIN_FLAGS; +#if 0 + /* This breaks for things like ( cd /tmp ; command z ababa ; echo next ) + or $(command echo a ; command echo b;) or even + { command echo a; command echo b; } & */ + /* If we're in a subshell, see if we can get away without forking + again, since we've already forked to run this builtin. */ + if (subshell_environment) + { + command->flags |= CMD_NO_FORK; + command->value.Simple->flags |= CMD_NO_FORK; + } +#endif + add_unwind_protect ((char *)dispose_command, command); + result = execute_command (command); + + run_unwind_frame ("command_builtin"); + + return (result); +} + +/* Restore the value of the $PATH variable after replacing it when + executing `command -p'. */ +static void +restore_path (var) + char *var; +{ + if (var) + { + bind_variable ("PATH", var, 0); + free (var); + } + else + unbind_variable ("PATH"); + + stupidly_hack_special_variables ("PATH"); +} + +/* Return a value for PATH that is guaranteed to find all of the standard + utilities. This uses Posix.2 configuration variables, if present. It + uses a value defined in config.h as a last resort. */ +static char * +get_standard_path () +{ +#if defined (_CS_PATH) && defined (HAVE_CONFSTR) + char *p; + size_t len; + + len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0); + if (len > 0) + { + p = (char *)xmalloc (len + 2); + *p = '\0'; + confstr (_CS_PATH, p, len); + return (p); + } + else + return (savestring (STANDARD_UTILS_PATH)); +#else /* !_CS_PATH || !HAVE_CONFSTR */ +# if defined (CS_PATH) + return (savestring (CS_PATH)); +# else + return (savestring (STANDARD_UTILS_PATH)); +# endif /* !CS_PATH */ +#endif /* !_CS_PATH || !HAVE_CONFSTR */ +} diff --git a/builtins/common.c b/builtins/common.c new file mode 100644 index 0000000..f02e99c --- /dev/null +++ b/builtins/common.c @@ -0,0 +1,890 @@ +/* common.c - utility functions for all builtins */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include <chartypes.h> +#include "../bashtypes.h" +#include "posixstat.h" +#include <signal.h> + +#include <errno.h> + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#define NEED_FPURGE_DECL + +#include "../shell.h" +#include "maxpath.h" +#include "../flags.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../input.h" +#include "../execute_cmd.h" +#include "../trap.h" +#include "bashgetopt.h" +#include "common.h" +#include "builtext.h" +#include <tilde/tilde.h> + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int indirection_level, subshell_environment; +extern int line_number; +extern int last_command_exit_value; +extern int running_trap; +extern int posixly_correct; +extern char *this_command_name, *shell_name; +extern const char * const bash_getcwd_errstr; + +/* Used by some builtins and the mainline code. */ +sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL; +sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL; + +/* **************************************************************** */ +/* */ +/* Error reporting, usage, and option processing */ +/* */ +/* **************************************************************** */ + +/* This is a lot like report_error (), but it is for shell builtins + instead of shell control structures, and it won't ever exit the + shell. */ + +static void +builtin_error_prolog () +{ + char *name; + + name = get_name_for_error (); + fprintf (stderr, "%s: ", name); + + if (interactive_shell == 0) + fprintf (stderr, _("line %d: "), executing_line_number ()); + + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); +} + +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; + + builtin_error_prolog (); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + va_end (args); + fprintf (stderr, "\n"); +} + +void +#if defined (PREFER_STDARG) +builtin_warning (const char *format, ...) +#else +builtin_warning (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + builtin_error_prolog (); + fprintf (stderr, _("warning: ")); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + va_end (args); + fprintf (stderr, "\n"); +} + +/* Print a usage summary for the currently-executing builtin command. */ +void +builtin_usage () +{ + if (this_command_name && *this_command_name) + fprintf (stderr, _("%s: usage: "), this_command_name); + fprintf (stderr, "%s\n", current_builtin->short_doc); + fflush (stderr); +} + +/* Return if LIST is NULL else barf and jump to top_level. Used by some + builtins that do not accept arguments. */ +void +no_args (list) + WORD_LIST *list; +{ + if (list) + { + builtin_error (_("too many arguments")); + top_level_cleanup (); + jump_to_top_level (DISCARD); + } +} + +/* Check that no options were given to the currently-executing builtin, + and return 0 if there were options. */ +int +no_options (list) + WORD_LIST *list; +{ + reset_internal_getopt (); + if (internal_getopt (list, "") != -1) + { + builtin_usage (); + return (1); + } + return (0); +} + +void +sh_needarg (s) + char *s; +{ + builtin_error (_("%s: option requires an argument"), s); +} + +void +sh_neednumarg (s) + char *s; +{ + builtin_error (_("%s: numeric argument required"), s); +} + +void +sh_notfound (s) + char *s; +{ + builtin_error (_("%s: not found"), s); +} + +/* Function called when one of the builtin commands detects an invalid + option. */ +void +sh_invalidopt (s) + char *s; +{ + builtin_error (_("%s: invalid option"), s); +} + +void +sh_invalidoptname (s) + char *s; +{ + builtin_error (_("%s: invalid option name"), s); +} + +void +sh_invalidid (s) + char *s; +{ + builtin_error (_("`%s': not a valid identifier"), s); +} + +void +sh_invalidnum (s) + char *s; +{ + char *msg; + + if (*s == '0' && isdigit (s[1])) + msg = _("invalid octal number"); + else if (*s == '0' && s[1] == 'x') + msg = _("invalid hex number"); + else + msg = _("invalid number"); + builtin_error ("%s: %s", s, msg); +} + +void +sh_invalidsig (s) + char *s; +{ + builtin_error (_("%s: invalid signal specification"), s); +} + +void +sh_badpid (s) + char *s; +{ + builtin_error (_("`%s': not a pid or valid job spec"), s); +} + +void +sh_readonly (s) + const char *s; +{ + builtin_error (_("%s: readonly variable"), s); +} + +void +sh_erange (s, desc) + char *s, *desc; +{ + if (s) + builtin_error (_("%s: %s out of range"), s, desc ? desc : _("argument")); + else + builtin_error (_("%s out of range"), desc ? desc : _("argument")); +} + +#if defined (JOB_CONTROL) +void +sh_badjob (s) + char *s; +{ + builtin_error (_("%s: no such job"), s); +} + +void +sh_nojobs (s) + char *s; +{ + if (s) + builtin_error (_("%s: no job control"), s); + else + builtin_error (_("no job control")); +} +#endif + +#if defined (RESTRICTED_SHELL) +void +sh_restricted (s) + char *s; +{ + if (s) + builtin_error (_("%s: restricted"), s); + else + builtin_error (_("restricted")); +} +#endif + +void +sh_notbuiltin (s) + char *s; +{ + builtin_error (_("%s: not a shell builtin"), s); +} + +void +sh_wrerror () +{ +#if defined (DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS) && defined (EPIPE) + if (errno != EPIPE) +#endif /* DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS && EPIPE */ + builtin_error (_("write error: %s"), strerror (errno)); +} + +void +sh_ttyerror (set) + int set; +{ + if (set) + builtin_error (_("error setting terminal attributes: %s"), strerror (errno)); + else + builtin_error (_("error getting terminal attributes: %s"), strerror (errno)); +} + +int +sh_chkwrite (s) + int s; +{ + fflush (stdout); + if (ferror (stdout)) + { + sh_wrerror (); + fpurge (stdout); + clearerr (stdout); + return (EXECUTION_FAILURE); + } + return (s); +} + +/* **************************************************************** */ +/* */ +/* Shell positional parameter manipulation */ +/* */ +/* **************************************************************** */ + +/* Convert a WORD_LIST into a C-style argv. Return the number of elements + in the list in *IP, if IP is non-null. A convenience function for + loadable builtins; also used by `test'. */ +char ** +make_builtin_argv (list, ip) + WORD_LIST *list; + int *ip; +{ + char **argv; + + argv = strvec_from_word_list (list, 0, 1, ip); + argv[0] = this_command_name; + return argv; +} + +/* Remember LIST in $0 ... $9, and REST_OF_ARGS. If DESTRUCTIVE is + non-zero, then discard whatever the existing arguments are, else + only discard the ones that are to be replaced. */ +void +remember_args (list, destructive) + WORD_LIST *list; + int destructive; +{ + register int i; + + for (i = 1; i < 10; i++) + { + if ((destructive || list) && dollar_vars[i]) + { + free (dollar_vars[i]); + dollar_vars[i] = (char *)NULL; + } + + if (list) + { + dollar_vars[i] = savestring (list->word->word); + list = list->next; + } + } + + /* If arguments remain, assign them to REST_OF_ARGS. + Note that copy_word_list (NULL) returns NULL, and + that dispose_words (NULL) does nothing. */ + if (destructive || list) + { + dispose_words (rest_of_args); + rest_of_args = copy_word_list (list); + } + + if (destructive) + set_dollar_vars_changed (); +} + +static int changed_dollar_vars; + +/* Have the dollar variables been reset to new values since we last + checked? */ +int +dollar_vars_changed () +{ + return (changed_dollar_vars); +} + +void +set_dollar_vars_unchanged () +{ + changed_dollar_vars = 0; +} + +void +set_dollar_vars_changed () +{ + if (variable_context) + changed_dollar_vars |= ARGS_FUNC; + else if (this_shell_builtin == set_builtin) + changed_dollar_vars |= ARGS_SETBLTIN; + else + changed_dollar_vars |= ARGS_INVOC; +} + +/* **************************************************************** */ +/* */ +/* Validating numeric input and arguments */ +/* */ +/* **************************************************************** */ + +/* Read a numeric arg for this_command_name, the name of the shell builtin + that wants it. LIST is the word list that the arg is to come from. + Accept only the numeric argument; report an error if other arguments + follow. If FATAL is 1, call throw_to_top_level, which exits the + shell; if it's 2, call jump_to_top_level (DISCARD), which aborts the + current command; if FATAL is 0, return an indication of an invalid + number by setting *NUMOK == 0 and return -1. */ +int +get_numeric_arg (list, fatal, count) + WORD_LIST *list; + int fatal; + intmax_t *count; +{ + char *arg; + + if (count) + *count = 1; + + if (list && list->word && ISOPTION (list->word->word, '-')) + list = list->next; + + if (list) + { + arg = list->word->word; + if (arg == 0 || (legal_number (arg, count) == 0)) + { + sh_neednumarg (list->word->word ? list->word->word : "`'"); + if (fatal == 0) + return 0; + else if (fatal == 1) /* fatal == 1; abort */ + throw_to_top_level (); + else /* fatal == 2; discard current command */ + { + top_level_cleanup (); + jump_to_top_level (DISCARD); + } + } + no_args (list->next); + } + + return (1); +} + +/* Get an eight-bit status value from LIST */ +int +get_exitstat (list) + WORD_LIST *list; +{ + int status; + intmax_t sval; + char *arg; + + if (list && list->word && ISOPTION (list->word->word, '-')) + list = list->next; + + if (list == 0) + return (last_command_exit_value); + + arg = list->word->word; + if (arg == 0 || legal_number (arg, &sval) == 0) + { + sh_neednumarg (list->word->word ? list->word->word : "`'"); + return 255; + } + no_args (list->next); + + status = sval & 255; + return status; +} + +/* Return the octal number parsed from STRING, or -1 to indicate + that the string contained a bad number. */ +int +read_octal (string) + char *string; +{ + int result, digits; + + result = digits = 0; + while (*string && ISOCTAL (*string)) + { + digits++; + result = (result * 8) + (*string++ - '0'); + if (result > 0777) + return -1; + } + + if (digits == 0 || *string) + result = -1; + + return (result); +} + +/* **************************************************************** */ +/* */ +/* Manipulating the current working directory */ +/* */ +/* **************************************************************** */ + +/* Return a consed string which is the current working directory. + FOR_WHOM is the name of the caller for error printing. */ +char *the_current_working_directory = (char *)NULL; + +char * +get_working_directory (for_whom) + char *for_whom; +{ + if (no_symbolic_links) + { + FREE (the_current_working_directory); + the_current_working_directory = (char *)NULL; + } + + if (the_current_working_directory == 0) + { +#if defined (GETCWD_BROKEN) + the_current_working_directory = getcwd (0, PATH_MAX); +#else + the_current_working_directory = getcwd (0, 0); +#endif + if (the_current_working_directory == 0) + { + fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"), + (for_whom && *for_whom) ? for_whom : get_name_for_error (), + _(bash_getcwd_errstr), strerror (errno)); + return (char *)NULL; + } + } + + return (savestring (the_current_working_directory)); +} + +/* Make NAME our internal idea of the current working directory. */ +void +set_working_directory (name) + char *name; +{ + FREE (the_current_working_directory); + the_current_working_directory = savestring (name); +} + +/* **************************************************************** */ +/* */ +/* Job control support functions */ +/* */ +/* **************************************************************** */ + +#if defined (JOB_CONTROL) +int +get_job_by_name (name, flags) + const char *name; + int flags; +{ + register int i, wl, cl, match, job; + register PROCESS *p; + register JOB *j; + + job = NO_JOB; + wl = strlen (name); + for (i = js.j_jobslots - 1; i >= 0; i--) + { + j = get_job_by_jid (i); + if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED)) + continue; + + p = j->pipe; + do + { + if (flags & JM_EXACT) + { + cl = strlen (p->command); + match = STREQN (p->command, name, cl); + } + else if (flags & JM_SUBSTRING) + match = strcasestr (p->command, name) != (char *)0; + else + match = STREQN (p->command, name, wl); + + if (match == 0) + { + p = p->next; + continue; + } + else if (flags & JM_FIRSTMATCH) + return i; /* return first match */ + else if (job != NO_JOB) + { + if (this_shell_builtin) + builtin_error (_("%s: ambiguous job spec"), name); + else + report_error (_("%s: ambiguous job spec"), name); + return (DUP_JOB); + } + else + job = i; + } + while (p != j->pipe); + } + + return (job); +} + +/* Return the job spec found in LIST. */ +int +get_job_spec (list) + WORD_LIST *list; +{ + register char *word; + int job, jflags; + + if (list == 0) + return (js.j_current); + + word = list->word->word; + + if (*word == '\0') + return (NO_JOB); + + if (*word == '%') + word++; + + if (DIGIT (*word) && all_digits (word)) + { + job = atoi (word); + return (job > js.j_jobslots ? NO_JOB : job - 1); + } + + jflags = 0; + switch (*word) + { + case 0: + case '%': + case '+': + return (js.j_current); + + case '-': + return (js.j_previous); + + case '?': /* Substring search requested. */ + jflags |= JM_SUBSTRING; + word++; + /* FALLTHROUGH */ + + default: + return get_job_by_name (word, jflags); + } +} +#endif /* JOB_CONTROL */ + +/* + * NOTE: `kill' calls this function with forcecols == 0 + */ +int +display_signal_list (list, forcecols) + WORD_LIST *list; + int forcecols; +{ + register int i, column; + char *name; + int result, signum, dflags; + intmax_t lsignum; + + result = EXECUTION_SUCCESS; + if (!list) + { + for (i = 1, column = 0; i < NSIG; i++) + { + name = signal_name (i); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + continue; + + if (posixly_correct && !forcecols) + { + /* This is for the kill builtin. POSIX.2 says the signal names + are displayed without the `SIG' prefix. */ + if (STREQN (name, "SIG", 3)) + name += 3; + printf ("%s%s", name, (i == NSIG - 1) ? "" : " "); + } + else + { + printf ("%2d) %s", i, name); + + if (++column < 5) + printf ("\t"); + else + { + printf ("\n"); + column = 0; + } + } + } + + if ((posixly_correct && !forcecols) || column != 0) + printf ("\n"); + return result; + } + + /* List individual signal names or numbers. */ + while (list) + { + if (legal_number (list->word->word, &lsignum)) + { + /* This is specified by Posix.2 so that exit statuses can be + mapped into signal numbers. */ + if (lsignum > 128) + lsignum -= 128; + if (lsignum < 0 || lsignum >= NSIG) + { + sh_invalidsig (list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + + signum = lsignum; + name = signal_name (signum); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + { + list = list->next; + continue; + } +#if defined (JOB_CONTROL) + /* POSIX.2 says that `kill -l signum' prints the signal name without + the `SIG' prefix. */ + printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name); +#else + printf ("%s\n", name); +#endif + } + else + { + dflags = DSIG_NOCASE; + if (posixly_correct == 0 || this_shell_builtin != kill_builtin) + dflags |= DSIG_SIGPREFIX; + signum = decode_signal (list->word->word, dflags); + if (signum == NO_SIG) + { + sh_invalidsig (list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + printf ("%d\n", signum); + } + list = list->next; + } + return (result); +} + +/* **************************************************************** */ +/* */ +/* Finding builtin commands and their functions */ +/* */ +/* **************************************************************** */ + +/* Perform a binary search and return the address of the builtin function + whose name is NAME. If the function couldn't be found, or the builtin + is disabled or has no function associated with it, return NULL. + Return the address of the builtin. + DISABLED_OKAY means find it even if the builtin is disabled. */ +struct builtin * +builtin_address_internal (name, disabled_okay) + char *name; + int disabled_okay; +{ + int hi, lo, mid, j; + + hi = num_shell_builtins - 1; + lo = 0; + + while (lo <= hi) + { + mid = (lo + hi) / 2; + + j = shell_builtins[mid].name[0] - name[0]; + + if (j == 0) + j = strcmp (shell_builtins[mid].name, name); + + if (j == 0) + { + /* It must have a function pointer. It must be enabled, or we + must have explicitly allowed disabled functions to be found, + and it must not have been deleted. */ + if (shell_builtins[mid].function && + ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) && + ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay)) + return (&shell_builtins[mid]); + else + return ((struct builtin *)NULL); + } + if (j > 0) + hi = mid - 1; + else + lo = mid + 1; + } + return ((struct builtin *)NULL); +} + +/* Return the pointer to the function implementing builtin command NAME. */ +sh_builtin_func_t * +find_shell_builtin (name) + char *name; +{ + current_builtin = builtin_address_internal (name, 0); + return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL); +} + +/* Return the address of builtin with NAME, whether it is enabled or not. */ +sh_builtin_func_t * +builtin_address (name) + char *name; +{ + current_builtin = builtin_address_internal (name, 1); + return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL); +} + +/* Return the function implementing the builtin NAME, but only if it is a + POSIX.2 special builtin. */ +sh_builtin_func_t * +find_special_builtin (name) + char *name; +{ + current_builtin = builtin_address_internal (name, 0); + return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ? + current_builtin->function : + (sh_builtin_func_t *)NULL); +} + +static int +shell_builtin_compare (sbp1, sbp2) + struct builtin *sbp1, *sbp2; +{ + int result; + + if ((result = sbp1->name[0] - sbp2->name[0]) == 0) + result = strcmp (sbp1->name, sbp2->name); + + return (result); +} + +/* Sort the table of shell builtins so that the binary search will work + in find_shell_builtin. */ +void +initialize_shell_builtins () +{ + qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin), + (QSFUNC *)shell_builtin_compare); +} diff --git a/builtins/common.h b/builtins/common.h new file mode 100644 index 0000000..efbb078 --- /dev/null +++ b/builtins/common.h @@ -0,0 +1,174 @@ +/* common.h -- extern declarations for functions defined in common.c. */ + +/* Copyright (C) 1993-2004 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (__COMMON_H) +# define __COMMON_H + +#include "stdc.h" + +#define ISOPTION(s, c) (s[0] == '-' && !s[2] && s[1] == c) + +/* Flag values for parse_and_execute () */ +#define SEVAL_NONINT 0x001 +#define SEVAL_INTERACT 0x002 +#define SEVAL_NOHIST 0x004 +#define SEVAL_NOFREE 0x008 +#define SEVAL_RESETLINE 0x010 +#define SEVAL_PARSEONLY 0x020 +#define SEVAL_NOLONGJMP 0x040 + +/* Flags for describe_command, shared between type.def and command.def */ +#define CDESC_ALL 0x001 /* type -a */ +#define CDESC_SHORTDESC 0x002 /* command -V */ +#define CDESC_REUSABLE 0x004 /* command -v */ +#define CDESC_TYPE 0x008 /* type -t */ +#define CDESC_PATH_ONLY 0x010 /* type -p */ +#define CDESC_FORCE_PATH 0x020 /* type -ap or type -P */ +#define CDESC_NOFUNCS 0x040 /* type -f */ +#define CDESC_ABSPATH 0x080 /* convert to absolute path, no ./ */ + +/* Flags for get_job_by_name */ +#define JM_PREFIX 0x01 /* prefix of job name */ +#define JM_SUBSTRING 0x02 /* substring of job name */ +#define JM_EXACT 0x04 /* match job name exactly */ +#define JM_STOPPED 0x08 /* match stopped jobs only */ +#define JM_FIRSTMATCH 0x10 /* return first matching job */ + +/* Flags for remember_args and value of changed_dollar_vars */ +#define ARGS_NONE 0x0 +#define ARGS_INVOC 0x01 +#define ARGS_FUNC 0x02 +#define ARGS_SETBLTIN 0x04 + +/* Functions from common.c */ +extern void builtin_error __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2))); +extern void builtin_warning __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2))); +extern void builtin_usage __P((void)); +extern void no_args __P((WORD_LIST *)); +extern int no_options __P((WORD_LIST *)); + +/* common error message functions */ +extern void sh_needarg __P((char *)); +extern void sh_neednumarg __P((char *)); +extern void sh_notfound __P((char *)); +extern void sh_invalidopt __P((char *)); +extern void sh_invalidoptname __P((char *)); +extern void sh_invalidid __P((char *)); +extern void sh_invalidnum __P((char *)); +extern void sh_invalidsig __P((char *)); +extern void sh_erange __P((char *, char *)); +extern void sh_badpid __P((char *)); +extern void sh_badjob __P((char *)); +extern void sh_readonly __P((const char *)); +extern void sh_nojobs __P((char *)); +extern void sh_restricted __P((char *)); +extern void sh_notbuiltin __P((char *)); +extern void sh_wrerror __P((void)); +extern void sh_ttyerror __P((int)); +extern int sh_chkwrite __P((int)); + +extern char **make_builtin_argv __P((WORD_LIST *, int *)); +extern void remember_args __P((WORD_LIST *, int)); + +extern int dollar_vars_changed __P((void)); +extern void set_dollar_vars_unchanged __P((void)); +extern void set_dollar_vars_changed __P((void)); + +extern int get_numeric_arg __P((WORD_LIST *, int, intmax_t *)); +extern int get_exitstat __P((WORD_LIST *)); +extern int read_octal __P((char *)); + +/* Keeps track of the current working directory. */ +extern char *the_current_working_directory; +extern char *get_working_directory __P((char *)); +extern void set_working_directory __P((char *)); + +#if defined (JOB_CONTROL) +extern int get_job_by_name __P((const char *, int)); +extern int get_job_spec __P((WORD_LIST *)); +#endif +extern int display_signal_list __P((WORD_LIST *, int)); + +/* It's OK to declare a function as returning a Function * without + providing a definition of what a `Function' is. */ +extern struct builtin *builtin_address_internal __P((char *, int)); +extern sh_builtin_func_t *find_shell_builtin __P((char *)); +extern sh_builtin_func_t *builtin_address __P((char *)); +extern sh_builtin_func_t *find_special_builtin __P((char *)); +extern void initialize_shell_builtins __P((void)); + +/* Functions from exit.def */ +extern void bash_logout __P((void)); + +/* Functions from getopts.def */ +extern void getopts_reset __P((int)); + +/* Functions from set.def */ +extern int minus_o_option_value __P((char *)); +extern void list_minus_o_opts __P((int, int)); +extern char **get_minus_o_opts __P((void)); +extern int set_minus_o_option __P((int, char *)); + +extern void set_shellopts __P((void)); +extern void parse_shellopts __P((char *)); +extern void initialize_shell_options __P((int)); + +extern void reset_shell_options __P((void)); + +/* Functions from shopt.def */ +extern void reset_shopt_options __P((void)); +extern char **get_shopt_options __P((void)); + +extern int shopt_setopt __P((char *, int)); +extern int shopt_listopt __P((char *, int)); + +extern int set_login_shell __P((char *, int)); + +extern void set_bashopts __P((void)); +extern void parse_bashopts __P((char *)); +extern void initialize_bashopts __P((int)); + +/* Functions from type.def */ +extern int describe_command __P((char *, int)); + +/* Functions from setattr.def */ +extern int set_or_show_attributes __P((WORD_LIST *, int, int)); +extern int show_all_var_attributes __P((int, int)); +extern int show_var_attributes __P((SHELL_VAR *, int, int)); +extern int show_name_attributes __P((char *, int)); +extern void set_var_attribute __P((char *, int, int)); + +/* Functions from pushd.def */ +extern char *get_dirstack_from_string __P((char *)); +extern char *get_dirstack_element __P((intmax_t, int)); +extern void set_dirstack_element __P((intmax_t, int, char *)); +extern WORD_LIST *get_directory_stack __P((int)); + +/* Functions from evalstring.c */ +extern int parse_and_execute __P((char *, const char *, int)); +extern void parse_and_execute_cleanup __P((void)); +extern int parse_string __P((char *, const char *, int, char **)); + +/* Functions from evalfile.c */ +extern int maybe_execute_file __P((const char *, int)); +extern int source_file __P((const char *, int)); +extern int fc_execute_file __P((const char *)); + +#endif /* !__COMMON_H */ diff --git a/builtins/complete.def b/builtins/complete.def new file mode 100644 index 0000000..2267794 --- /dev/null +++ b/builtins/complete.def @@ -0,0 +1,869 @@ +This file is complete.def, from which is created complete.c. +It implements the builtins "complete", "compgen", and "compopt" in Bash. + +Copyright (C) 1999-2009 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/>. + +$PRODUCES complete.c + +$BUILTIN complete +$DEPENDS_ON PROGRAMMABLE_COMPLETION +$FUNCTION complete_builtin +$SHORT_DOC complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...] +Specify how arguments are to be completed by Readline. + +For each NAME, specify how arguments are to be completed. If no options +are supplied, existing completion specifications are printed in a way that +allows them to be reused as input. + +Options: + -p print existing completion specifications in a reusable format + -r remove a completion specification for each NAME, or, if no + NAMEs are supplied, all completion specifications + -D apply the completions and actions as the default for commands + without any specific completion defined + -E apply the completions and actions to "empty" commands -- + completion attempted on a blank line + +When completion is attempted, the actions are applied in the order the +uppercase-letter options are listed above. The -D option takes +precedence over -E. + +Exit Status: +Returns success unless an invalid option is supplied or an error occurs. +$END + +#include <config.h> + +#include <stdio.h> + +#include "../bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../builtins.h" +#include "../pcomplete.h" +#include "../bashline.h" + +#include "common.h" +#include "bashgetopt.h" + +#include <readline/readline.h> + +#define STRDUP(x) ((x) ? savestring (x) : (char *)NULL) + +/* Structure containing all the non-action (binary) options; filled in by + build_actions(). */ +struct _optflags { + int pflag; + int rflag; + int Dflag; + int Eflag; +}; + +static int find_compact __P((char *)); +static int find_compopt __P((char *)); + +static int build_actions __P((WORD_LIST *, struct _optflags *, unsigned long *, unsigned long *)); + +static int remove_cmd_completions __P((WORD_LIST *)); + +static int print_one_completion __P((char *, COMPSPEC *)); +static int print_compitem __P((BUCKET_CONTENTS *)); +static void print_compopts __P((const char *, COMPSPEC *, int)); +static void print_all_completions __P((void)); +static int print_cmd_completions __P((WORD_LIST *)); + +static char *Garg, *Warg, *Parg, *Sarg, *Xarg, *Farg, *Carg; + +static const struct _compacts { + const char * const actname; + int actflag; + int actopt; +} compacts[] = { + { "alias", CA_ALIAS, 'a' }, + { "arrayvar", CA_ARRAYVAR, 0 }, + { "binding", CA_BINDING, 0 }, + { "builtin", CA_BUILTIN, 'b' }, + { "command", CA_COMMAND, 'c' }, + { "directory", CA_DIRECTORY, 'd' }, + { "disabled", CA_DISABLED, 0 }, + { "enabled", CA_ENABLED, 0 }, + { "export", CA_EXPORT, 'e' }, + { "file", CA_FILE, 'f' }, + { "function", CA_FUNCTION, 0 }, + { "helptopic", CA_BUILTIN, 0 }, /* for now */ + { "hostname", CA_HOSTNAME, 0 }, + { "group", CA_GROUP, 'g' }, + { "job", CA_JOB, 'j' }, + { "keyword", CA_KEYWORD, 'k' }, + { "running", CA_RUNNING, 0 }, + { "service", CA_SERVICE, 's' }, + { "setopt", CA_SETOPT, 0 }, + { "shopt", CA_SHOPT, 0 }, + { "signal", CA_SIGNAL, 0 }, + { "stopped", CA_STOPPED, 0 }, + { "user", CA_USER, 'u' }, + { "variable", CA_VARIABLE, 'v' }, + { (char *)NULL, 0, 0 }, +}; + +/* This should be a STRING_INT_ALIST */ +const static struct _compopt { + const char * const optname; + int optflag; +} compopts[] = { + { "bashdefault", COPT_BASHDEFAULT }, + { "default", COPT_DEFAULT }, + { "dirnames", COPT_DIRNAMES }, + { "filenames",COPT_FILENAMES}, + { "nospace", COPT_NOSPACE }, + { "plusdirs", COPT_PLUSDIRS }, + { (char *)NULL, 0 }, +}; + +static int +find_compact (name) + char *name; +{ + register int i; + + for (i = 0; compacts[i].actname; i++) + if (STREQ (name, compacts[i].actname)) + return i; + return -1; +} + +static int +find_compopt (name) + char *name; +{ + register int i; + + for (i = 0; compopts[i].optname; i++) + if (STREQ (name, compopts[i].optname)) + return i; + return -1; +} + +/* Build the actions and compspec options from the options specified in LIST. + ACTP is a pointer to an unsigned long in which to place the bitmap of + actions. OPTP is a pointer to an unsigned long in which to place the + btmap of compspec options (arguments to `-o'). PP, if non-null, gets 1 + if -p is supplied; RP, if non-null, gets 1 if -r is supplied. + If either is null, the corresponding option generates an error. + This also sets variables corresponding to options that take arguments as + a side effect; the caller should ensure that those variables are set to + NULL before calling build_actions. Return value: + EX_USAGE = bad option + EXECUTION_SUCCESS = some options supplied + EXECUTION_FAILURE = no options supplied +*/ + +static int +build_actions (list, flagp, actp, optp) + WORD_LIST *list; + struct _optflags *flagp; + unsigned long *actp, *optp; +{ + int opt, ind, opt_given; + unsigned long acts, copts; + + acts = copts = (unsigned long)0L; + opt_given = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "abcdefgjko:prsuvA:G:W:P:S:X:F:C:DE")) != -1) + { + opt_given = 1; + switch (opt) + { + case 'r': + if (flagp) + { + flagp->rflag = 1; + break; + } + else + { + sh_invalidopt ("-r"); + builtin_usage (); + return (EX_USAGE); + } + + case 'p': + if (flagp) + { + flagp->pflag = 1; + break; + } + else + { + sh_invalidopt ("-p"); + builtin_usage (); + return (EX_USAGE); + } + + case 'a': + acts |= CA_ALIAS; + break; + case 'b': + acts |= CA_BUILTIN; + break; + case 'c': + acts |= CA_COMMAND; + break; + case 'd': + acts |= CA_DIRECTORY; + break; + case 'e': + acts |= CA_EXPORT; + break; + case 'f': + acts |= CA_FILE; + break; + case 'g': + acts |= CA_GROUP; + break; + case 'j': + acts |= CA_JOB; + break; + case 'k': + acts |= CA_KEYWORD; + break; + case 's': + acts |= CA_SERVICE; + break; + case 'u': + acts |= CA_USER; + break; + case 'v': + acts |= CA_VARIABLE; + break; + case 'o': + ind = find_compopt (list_optarg); + if (ind < 0) + { + sh_invalidoptname (list_optarg); + return (EX_USAGE); + } + copts |= compopts[ind].optflag; + break; + case 'A': + ind = find_compact (list_optarg); + if (ind < 0) + { + builtin_error (_("%s: invalid action name"), list_optarg); + return (EX_USAGE); + } + acts |= compacts[ind].actflag; + break; + case 'C': + Carg = list_optarg; + break; + case 'D': + if (flagp) + { + flagp->Dflag = 1; + break; + } + else + { + sh_invalidopt ("-D"); + builtin_usage (); + return (EX_USAGE); + } + case 'E': + if (flagp) + { + flagp->Eflag = 1; + break; + } + else + { + sh_invalidopt ("-E"); + builtin_usage (); + return (EX_USAGE); + } + case 'F': + Farg = list_optarg; + break; + case 'G': + Garg = list_optarg; + break; + case 'P': + Parg = list_optarg; + break; + case 'S': + Sarg = list_optarg; + break; + case 'W': + Warg = list_optarg; + break; + case 'X': + Xarg = list_optarg; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + *actp = acts; + *optp = copts; + + return (opt_given ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +/* Add, remove, and display completion specifiers. */ +int +complete_builtin (list) + WORD_LIST *list; +{ + int opt_given, rval; + unsigned long acts, copts; + COMPSPEC *cs; + struct _optflags oflags; + WORD_LIST *l, *wl; + + if (list == 0) + { + print_all_completions (); + return (EXECUTION_SUCCESS); + } + + opt_given = oflags.pflag = oflags.rflag = oflags.Dflag = oflags.Eflag = 0; + + acts = copts = (unsigned long)0L; + Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL; + cs = (COMPSPEC *)NULL; + + /* Build the actions from the arguments. Also sets the [A-Z]arg variables + as a side effect if they are supplied as options. */ + rval = build_actions (list, &oflags, &acts, &copts); + if (rval == EX_USAGE) + return (rval); + opt_given = rval != EXECUTION_FAILURE; + + list = loptend; + + wl = oflags.Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL) + : (oflags.Eflag ? make_word_list (make_bare_word (EMPTYCMD), (WORD_LIST *)NULL) : 0); + + /* -p overrides everything else */ + if (oflags.pflag || (list == 0 && opt_given == 0)) + { + if (wl) + { + rval = print_cmd_completions (wl); + dispose_words (wl); + return rval; + } + else if (list == 0) + { + print_all_completions (); + return (EXECUTION_SUCCESS); + } + return (print_cmd_completions (list)); + } + + /* next, -r overrides everything else. */ + if (oflags.rflag) + { + if (wl) + { + rval = remove_cmd_completions (wl); + dispose_words (wl); + return rval; + } + else if (list == 0) + { + progcomp_flush (); + return (EXECUTION_SUCCESS); + } + return (remove_cmd_completions (list)); + } + + if (wl == 0 && list == 0 && opt_given) + { + builtin_usage (); + return (EX_USAGE); + } + + /* If we get here, we need to build a compspec and add it for each + remaining argument. */ + cs = compspec_create (); + cs->actions = acts; + cs->options = copts; + + cs->globpat = STRDUP (Garg); + cs->words = STRDUP (Warg); + cs->prefix = STRDUP (Parg); + cs->suffix = STRDUP (Sarg); + cs->funcname = STRDUP (Farg); + cs->command = STRDUP (Carg); + cs->filterpat = STRDUP (Xarg); + + for (rval = EXECUTION_SUCCESS, l = wl ? wl : list ; l; l = l->next) + { + /* Add CS as the compspec for the specified commands. */ + if (progcomp_insert (l->word->word, cs) == 0) + rval = EXECUTION_FAILURE; + } + + dispose_words (wl); + return (rval); +} + +static int +remove_cmd_completions (list) + WORD_LIST *list; +{ + WORD_LIST *l; + int ret; + + for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + if (progcomp_remove (l->word->word) == 0) + { + builtin_error (_("%s: no completion specification"), l->word->word); + ret = EXECUTION_FAILURE; + } + } + return ret; +} + +#define SQPRINTARG(a, f) \ + do { \ + if (a) \ + { \ + x = sh_single_quote (a); \ + printf ("%s %s ", f, x); \ + free (x); \ + } \ + } while (0) + +#define PRINTARG(a, f) \ + do { \ + if (a) \ + printf ("%s %s ", f, a); \ + } while (0) + +#define PRINTOPT(a, f) \ + do { \ + if (acts & a) \ + printf ("%s ", f); \ + } while (0) + +#define PRINTACT(a, f) \ + do { \ + if (acts & a) \ + printf ("-A %s ", f); \ + } while (0) + +#define PRINTCOMPOPT(a, f) \ + do { \ + if (copts & a) \ + printf ("-o %s ", f); \ + } while (0) + +#define XPRINTCOMPOPT(a, f) \ + do { \ + if (copts & a) \ + printf ("-o %s ", f); \ + else \ + printf ("+o %s ", f); \ + } while (0) + +static int +print_one_completion (cmd, cs) + char *cmd; + COMPSPEC *cs; +{ + unsigned long acts, copts; + char *x; + + printf ("complete "); + + copts = cs->options; + + /* First, print the -o options. */ + PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); + PRINTCOMPOPT (COPT_DEFAULT, "default"); + PRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); + PRINTCOMPOPT (COPT_FILENAMES, "filenames"); + PRINTCOMPOPT (COPT_NOSPACE, "nospace"); + PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); + + acts = cs->actions; + + /* simple flags next */ + PRINTOPT (CA_ALIAS, "-a"); + PRINTOPT (CA_BUILTIN, "-b"); + PRINTOPT (CA_COMMAND, "-c"); + PRINTOPT (CA_DIRECTORY, "-d"); + PRINTOPT (CA_EXPORT, "-e"); + PRINTOPT (CA_FILE, "-f"); + PRINTOPT (CA_GROUP, "-g"); + PRINTOPT (CA_JOB, "-j"); + PRINTOPT (CA_KEYWORD, "-k"); + PRINTOPT (CA_SERVICE, "-s"); + PRINTOPT (CA_USER, "-u"); + PRINTOPT (CA_VARIABLE, "-v"); + + /* now the rest of the actions */ + PRINTACT (CA_ARRAYVAR, "arrayvar"); + PRINTACT (CA_BINDING, "binding"); + PRINTACT (CA_DISABLED, "disabled"); + PRINTACT (CA_ENABLED, "enabled"); + PRINTACT (CA_FUNCTION, "function"); + PRINTACT (CA_HELPTOPIC, "helptopic"); + PRINTACT (CA_HOSTNAME, "hostname"); + PRINTACT (CA_RUNNING, "running"); + PRINTACT (CA_SETOPT, "setopt"); + PRINTACT (CA_SHOPT, "shopt"); + PRINTACT (CA_SIGNAL, "signal"); + PRINTACT (CA_STOPPED, "stopped"); + + /* now the rest of the arguments */ + + /* arguments that require quoting */ + SQPRINTARG (cs->globpat, "-G"); + SQPRINTARG (cs->words, "-W"); + SQPRINTARG (cs->prefix, "-P"); + SQPRINTARG (cs->suffix, "-S"); + SQPRINTARG (cs->filterpat, "-X"); + + SQPRINTARG (cs->command, "-C"); + + /* simple arguments that don't require quoting */ + PRINTARG (cs->funcname, "-F"); + + if (STREQ (cmd, EMPTYCMD)) + printf ("-E\n"); + else if (STREQ (cmd, DEFAULTCMD)) + printf ("-D\n"); + else + printf ("%s\n", cmd); + + return (0); +} + +static void +print_compopts (cmd, cs, full) + const char *cmd; + COMPSPEC *cs; + int full; +{ + int copts; + + printf ("compopt "); + copts = cs->options; + + if (full) + { + XPRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); + XPRINTCOMPOPT (COPT_DEFAULT, "default"); + XPRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); + XPRINTCOMPOPT (COPT_FILENAMES, "filenames"); + XPRINTCOMPOPT (COPT_NOSPACE, "nospace"); + XPRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); + } + else + { + PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); + PRINTCOMPOPT (COPT_DEFAULT, "default"); + PRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); + PRINTCOMPOPT (COPT_FILENAMES, "filenames"); + PRINTCOMPOPT (COPT_NOSPACE, "nospace"); + PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); + } + + if (STREQ (cmd, EMPTYCMD)) + printf ("-E\n"); + else if (STREQ (cmd, DEFAULTCMD)) + printf ("-D\n"); + else + printf ("%s\n", cmd); +} + +static int +print_compitem (item) + BUCKET_CONTENTS *item; +{ + COMPSPEC *cs; + char *cmd; + + cmd = item->key; + cs = (COMPSPEC *)item->data; + + return (print_one_completion (cmd, cs)); +} + +static void +print_all_completions () +{ + progcomp_walk (print_compitem); +} + +static int +print_cmd_completions (list) + WORD_LIST *list; +{ + WORD_LIST *l; + COMPSPEC *cs; + int ret; + + for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + cs = progcomp_search (l->word->word); + if (cs) + print_one_completion (l->word->word, cs); + else + { + builtin_error (_("%s: no completion specification"), l->word->word); + ret = EXECUTION_FAILURE; + } + } + + return (sh_chkwrite (ret)); +} + +$BUILTIN compgen +$DEPENDS_ON PROGRAMMABLE_COMPLETION +$FUNCTION compgen_builtin +$SHORT_DOC compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word] +Display possible completions depending on the options. + +Intended to be used from within a shell function generating possible +completions. If the optional WORD argument is supplied, matches against +WORD are generated. + +Exit Status: +Returns success unless an invalid option is supplied or an error occurs. +$END + +int +compgen_builtin (list) + WORD_LIST *list; +{ + int rval; + unsigned long acts, copts; + COMPSPEC *cs; + STRINGLIST *sl; + char *word, **matches; + + if (list == 0) + return (EXECUTION_SUCCESS); + + acts = copts = (unsigned long)0L; + Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL; + cs = (COMPSPEC *)NULL; + + /* Build the actions from the arguments. Also sets the [A-Z]arg variables + as a side effect if they are supplied as options. */ + rval = build_actions (list, (struct _optflags *)NULL, &acts, &copts); + if (rval == EX_USAGE) + return (rval); + if (rval == EXECUTION_FAILURE) + return (EXECUTION_SUCCESS); + + list = loptend; + + word = (list && list->word) ? list->word->word : ""; + + if (Farg) + builtin_error (_("warning: -F option may not work as you expect")); + if (Carg) + builtin_error (_("warning: -C option may not work as you expect")); + + /* If we get here, we need to build a compspec and evaluate it. */ + cs = compspec_create (); + cs->actions = acts; + cs->options = copts; + cs->refcount = 1; + + cs->globpat = STRDUP (Garg); + cs->words = STRDUP (Warg); + cs->prefix = STRDUP (Parg); + cs->suffix = STRDUP (Sarg); + cs->funcname = STRDUP (Farg); + cs->command = STRDUP (Carg); + cs->filterpat = STRDUP (Xarg); + + rval = EXECUTION_FAILURE; + sl = gen_compspec_completions (cs, "compgen", word, 0, 0, 0); + + /* If the compspec wants the bash default completions, temporarily + turn off programmable completion and call the bash completion code. */ + if ((sl == 0 || sl->list_len == 0) && (copts & COPT_BASHDEFAULT)) + { + matches = bash_default_completion (word, 0, 0, 0, 0); + sl = completions_to_stringlist (matches); + strvec_dispose (matches); + } + + /* This isn't perfect, but it's the best we can do, given what readline + exports from its set of completion utility functions. */ + if ((sl == 0 || sl->list_len == 0) && (copts & COPT_DEFAULT)) + { + matches = rl_completion_matches (word, rl_filename_completion_function); + sl = completions_to_stringlist (matches); + strvec_dispose (matches); + } + + if (sl) + { + if (sl->list && sl->list_len) + { + rval = EXECUTION_SUCCESS; + strlist_print (sl, (char *)NULL); + } + strlist_dispose (sl); + } + + compspec_dispose (cs); + return (rval); +} + +$BUILTIN compopt +$DEPENDS_ON PROGRAMMABLE_COMPLETION +$FUNCTION compopt_builtin +$SHORT_DOC compopt [-o|+o option] [-DE] [name ...] +Modify or display completion options. + +Modify the completion options for each NAME, or, if no NAMEs are supplied, +the completion currently begin executed. If no OPTIONs are givenm, print +the completion options for each NAME or the current completion specification. + +Options: + -o option Set completion option OPTION for each NAME + -D Change options for the "default" command completion + -E Change options for the "empty" command completion + +Using `+o' instead of `-o' turns off the specified option. + +Arguments: + +Each NAME refers to a command for which a completion specification must +have previously been defined using the `complete' builtin. If no NAMEs +are supplied, compopt must be called by a function currently generating +completions, and the options for that currently-executing completion +generator are modified. + +Exit Status: +Returns success unless an invalid option is supplied or NAME does not +have a completion specification defined. +$END + +int +compopt_builtin (list) + WORD_LIST *list; +{ + int opts_on, opts_off, *opts, opt, oind, ret, Dflag, Eflag; + WORD_LIST *l, *wl; + COMPSPEC *cs; + + opts_on = opts_off = Eflag = Dflag = 0; + ret = EXECUTION_SUCCESS; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "+o:DE")) != EOF) + { + opts = (list_opttype == '-') ? &opts_on : &opts_off; + + switch (opt) + { + case 'o': + oind = find_compopt (list_optarg); + if (oind < 0) + { + sh_invalidoptname (list_optarg); + return (EX_USAGE); + } + *opts |= compopts[oind].optflag; + break; + case 'D': + Dflag = 1; + break; + case 'E': + Eflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + wl = Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL) + : (Eflag ? make_word_list (make_bare_word (EMPTYCMD), (WORD_LIST *)NULL) : 0); + + if (list == 0 && wl == 0) + { + if (RL_ISSTATE (RL_STATE_COMPLETING) == 0 || pcomp_curcs == 0) + { + builtin_error (_("not currently executing completion function")); + return (EXECUTION_FAILURE); + } + cs = pcomp_curcs; + + if (opts_on == 0 && opts_off == 0) + { + print_compopts (pcomp_curcmd, cs, 1); + return (sh_chkwrite (ret)); + } + + /* Set the compspec options */ + pcomp_set_compspec_options (cs, opts_on, 1); + pcomp_set_compspec_options (cs, opts_off, 0); + + /* And change the readline variables the options control */ + pcomp_set_readline_variables (opts_on, 1); + pcomp_set_readline_variables (opts_off, 0); + + return (ret); + } + + for (l = wl ? wl : list; l; l = l->next) + { + cs = progcomp_search (l->word->word); + if (cs == 0) + { + builtin_error (_("%s: no completion specification"), l->word->word); + ret = EXECUTION_FAILURE; + continue; + } + if (opts_on == 0 && opts_off == 0) + { + print_compopts (l->word->word, cs, 1); + continue; /* XXX -- fill in later */ + } + + /* Set the compspec options */ + pcomp_set_compspec_options (cs, opts_on, 1); + pcomp_set_compspec_options (cs, opts_off, 0); + } + + return (ret); +} diff --git a/builtins/declare.def b/builtins/declare.def new file mode 100644 index 0000000..a0ce605 --- /dev/null +++ b/builtins/declare.def @@ -0,0 +1,562 @@ +This file is declare.def, from which is created declare.c. +It implements the builtins "declare" and "local" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES declare.c + +$BUILTIN declare +$FUNCTION declare_builtin +$SHORT_DOC declare [-aAfFilrtux] [-p] [name[=value] ...] +Set variable values and attributes. + +Declare variables and give them attributes. If no NAMEs are given, +display the attributes and values of all variables. + +Options: + -f restrict action or display to function names and definitions + -F restrict display to function names only (plus line number and + source file when debugging) + -p display the attributes and value of each NAME + +Options which set attributes: + -a to make NAMEs indexed arrays (if supported) + -A to make NAMEs associative arrays (if supported) + -i to make NAMEs have the `integer' attribute + -l to convert NAMEs to lower case on assignment + -r to make NAMEs readonly + -t to make NAMEs have the `trace' attribute + -u to convert NAMEs to upper case on assignment + -x to make NAMEs export + +Using `+' instead of `-' turns off the given attribute. + +Variables with the integer attribute have arithmetic evaluation (see +the `let' command) performed when the variable is assigned a value. + +When used in a function, `declare' makes NAMEs local, as with the `local' +command. + +Exit Status: +Returns success unless an invalid option is supplied or an error occurs. +$END + +$BUILTIN typeset +$FUNCTION declare_builtin +$SHORT_DOC typeset [-aAfFilrtux] [-p] name[=value] ... +Set variable values and attributes. + +Obsolete. See `help declare'. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "builtext.h" +#include "bashgetopt.h" + +extern int array_needs_making; +extern int posixly_correct; + +static int declare_internal __P((register WORD_LIST *, int)); + +/* Declare or change variable attributes. */ +int +declare_builtin (list) + register WORD_LIST *list; +{ + return (declare_internal (list, 0)); +} + +$BUILTIN local +$FUNCTION local_builtin +$SHORT_DOC local [option] name[=value] ... +Define local variables. + +Create a local variable called NAME, and give it VALUE. OPTION can +be any option accepted by `declare'. + +Local variables can only be used within a function; they are visible +only to the function where they are defined and its children. + +Exit Status: +Returns success unless an invalid option is supplied, an error occurs, +or the shell is not executing a function. +$END +int +local_builtin (list) + register WORD_LIST *list; +{ + if (variable_context) + return (declare_internal (list, 1)); + else + { + builtin_error (_("can only be used in a function")); + return (EXECUTION_FAILURE); + } +} + +#if defined (ARRAY_VARS) +# define DECLARE_OPTS "+acfilprtuxAF" +#else +# define DECLARE_OPTS "+cfilprtuxF" +#endif + +/* The workhorse function. */ +static int +declare_internal (list, local_var) + register WORD_LIST *list; + int local_var; +{ + int flags_on, flags_off, *flags; + int any_failed, assign_error, pflag, nodefs, opt; + char *t, *subscript_start; + SHELL_VAR *var; + FUNCTION_DEF *shell_fn; + + flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF) + { + flags = list_opttype == '+' ? &flags_off : &flags_on; + + switch (opt) + { + case 'a': +#if defined (ARRAY_VARS) + *flags |= att_array; + break; +#else + builtin_usage (); + return (EX_USAGE); +#endif + case 'A': +#if defined (ARRAY_VARS) + *flags |= att_assoc; + break; +#else + builtin_usage (); + return (EX_USAGE); +#endif + case 'p': + if (local_var == 0) + pflag++; + break; + case 'F': + nodefs++; + *flags |= att_function; + break; + case 'f': + *flags |= att_function; + break; + case 'i': + *flags |= att_integer; + break; + case 'r': + *flags |= att_readonly; + break; + case 't': + *flags |= att_trace; + break; + case 'x': + *flags |= att_exported; + array_needs_making = 1; + break; +#if defined (CASEMOD_ATTRS) +# if defined (CASEMOD_CAPCASE) + case 'c': + *flags |= att_capcase; + if (flags == &flags_on) + flags_off |= att_uppercase|att_lowercase; + break; +# endif + case 'l': + *flags |= att_lowercase; + if (flags == &flags_on) + flags_off |= att_capcase|att_uppercase; + break; + case 'u': + *flags |= att_uppercase; + if (flags == &flags_on) + flags_off |= att_capcase|att_lowercase; + break; +#endif /* CASEMOD_ATTRS */ + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + /* If there are no more arguments left, then we just want to show + some variables. */ + if (list == 0) /* declare -[aAfFirtx] */ + { + /* Show local variables defined at this context level if this is + the `local' builtin. */ + if (local_var) + { + register SHELL_VAR **vlist; + register int i; + + vlist = all_local_variables (); + + if (vlist) + { + for (i = 0; vlist[i]; i++) + print_assignment (vlist[i]); + + free (vlist); + } + } + else if (pflag && (flags_on == 0 || flags_on == att_function)) + show_all_var_attributes (flags_on == 0, nodefs); + else if (flags_on == 0) + return (set_builtin ((WORD_LIST *)NULL)); + else + set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs); + + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + if (pflag) /* declare -p [-aAfFirtx] name [name...] */ + { + for (any_failed = 0; list; list = list->next) + { + pflag = show_name_attributes (list->word->word, nodefs); + if (pflag) + { + sh_notfound (list->word->word); + any_failed++; + } + } + return (sh_chkwrite (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS)); + } + +#define NEXT_VARIABLE() free (name); list = list->next; continue + + /* There are arguments left, so we are making variables. */ + while (list) /* declare [-aAfFirx] name [name ...] */ + { + char *value, *name; + int offset, aflags; +#if defined (ARRAY_VARS) + int making_array_special, compound_array_assign, simple_array_assign; +#endif + + name = savestring (list->word->word); + offset = assignment (name, 0); + aflags = 0; + + if (offset) /* declare [-aAfFirx] name=value */ + { + name[offset] = '\0'; + value = name + offset + 1; + if (name[offset - 1] == '+') + { + aflags |= ASS_APPEND; + name[offset - 1] = '\0'; + } + } + else + value = ""; + +#if defined (ARRAY_VARS) + compound_array_assign = simple_array_assign = 0; + subscript_start = (char *)NULL; + if (t = strchr (name, '[')) /* ] */ + { + /* If offset != 0 we have already validated any array reference */ + if (offset == 0 && valid_array_reference (name) == 0) + { + sh_invalidid (name); + assign_error++; + NEXT_VARIABLE (); + } + subscript_start = t; + *t = '\0'; + making_array_special = 1; + } + else + making_array_special = 0; +#endif + + /* If we're in posix mode or not looking for a shell function (since + shell function names don't have to be valid identifiers when the + shell's not in posix mode), check whether or not the argument is a + valid, well-formed shell identifier. */ + if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0) + { + sh_invalidid (name); + assign_error++; + NEXT_VARIABLE (); + } + + /* If VARIABLE_CONTEXT has a non-zero value, then we are executing + inside of a function. This means we should make local variables, + not global ones. */ + + /* XXX - this has consequences when we're making a local copy of a + variable that was in the temporary environment. Watch out + for this. */ + if (variable_context && ((flags_on & att_function) == 0)) + { +#if defined (ARRAY_VARS) + if (flags_on & att_assoc) + var = make_local_assoc_variable (name); + else if ((flags_on & att_array) || making_array_special) + var = make_local_array_variable (name); + else +#endif + var = make_local_variable (name); + if (var == 0) + { + any_failed++; + NEXT_VARIABLE (); + } + } + else + var = (SHELL_VAR *)NULL; + + /* If we are declaring a function, then complain about it in some way. + We don't let people make functions by saying `typeset -f foo=bar'. */ + + /* There should be a way, however, to let people look at a particular + function definition by saying `typeset -f foo'. */ + + if (flags_on & att_function) + { + if (offset) /* declare -f [-rix] foo=bar */ + { + builtin_error (_("cannot use `-f' to make functions")); + free (name); + return (EXECUTION_FAILURE); + } + else /* declare -f [-rx] name [name...] */ + { + var = find_function (name); + + if (var) + { + if (readonly_p (var) && (flags_off & att_readonly)) + { + builtin_error (_("%s: readonly function"), name); + any_failed++; + NEXT_VARIABLE (); + } + + /* declare -[Ff] name [name...] */ + if (flags_on == att_function && flags_off == 0) + { +#if defined (DEBUGGER) + if (nodefs && debugging_mode) + { + shell_fn = find_function_def (var->name); + if (shell_fn) + printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file); + else + printf ("%s\n", var->name); + } + else +#endif /* DEBUGGER */ + { + t = nodefs ? var->name + : named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL); + printf ("%s\n", t); + any_failed = sh_chkwrite (any_failed); + } + } + else /* declare -[fF] -[rx] name [name...] */ + { + VSETATTR (var, flags_on); + VUNSETATTR (var, flags_off); + } + } + else + any_failed++; + NEXT_VARIABLE (); + } + } + else /* declare -[aAirx] name [name...] */ + { + /* Non-null if we just created or fetched a local variable. */ + if (var == 0) + var = find_variable (name); + + if (var == 0) + { +#if defined (ARRAY_VARS) + if (flags_on & att_assoc) + var = make_new_assoc_variable (name); + else if ((flags_on & att_array) || making_array_special) + var = make_new_array_variable (name); + else +#endif + + if (offset) + var = bind_variable (name, "", 0); + else + { + var = bind_variable (name, (char *)NULL, 0); + VSETATTR (var, att_invisible); + } + } + + /* Cannot use declare +r to turn off readonly attribute. */ + if (readonly_p (var) && (flags_off & att_readonly)) + { + sh_readonly (name); + any_failed++; + NEXT_VARIABLE (); + } + + /* Cannot use declare to assign value to readonly or noassign + variable. */ + if ((readonly_p (var) || noassign_p (var)) && offset) + { + if (readonly_p (var)) + sh_readonly (name); + assign_error++; + NEXT_VARIABLE (); + } + +#if defined (ARRAY_VARS) + if ((making_array_special || (flags_on & (att_array|att_assoc)) || array_p (var) || assoc_p (var)) && offset) + { + int vlen; + vlen = STRLEN (value); + + if (value[0] == '(' && value[vlen-1] == ')') + compound_array_assign = 1; + else + simple_array_assign = 1; + } + + /* Cannot use declare +a name or declare +A name to remove an + array variable. */ + if (((flags_off & att_array) && array_p (var)) || ((flags_off & att_assoc) && assoc_p (var))) + { + builtin_error (_("%s: cannot destroy array variables in this way"), name); + any_failed++; + NEXT_VARIABLE (); + } + + if ((flags_on & att_array) && assoc_p (var)) + { + builtin_error (_("%s: cannot convert associative to indexed array"), name); + any_failed++; + NEXT_VARIABLE (); + } + if ((flags_on & att_assoc) && array_p (var)) + { + builtin_error (_("%s: cannot convert indexed to associative array"), name); + any_failed++; + NEXT_VARIABLE (); + } + + /* declare -A name[[n]] makes name an associative array variable. */ + if (flags_on & att_assoc) + { + if (assoc_p (var) == 0) + var = convert_var_to_assoc (var); + } + /* declare -a name[[n]] or declare name[n] makes name an indexed + array variable. */ + else if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0 && assoc_p (var) == 0) + var = convert_var_to_array (var); +#endif /* ARRAY_VARS */ + + VSETATTR (var, flags_on); + VUNSETATTR (var, flags_off); + +#if defined (ARRAY_VARS) + if (offset && compound_array_assign) + assign_array_var_from_string (var, value, aflags); + else if (simple_array_assign && subscript_start) + { + /* declare [-aA] name[N]=value */ + *subscript_start = '['; /* ] */ + var = assign_array_element (name, value, 0); /* XXX - not aflags */ + *subscript_start = '\0'; + } + else if (simple_array_assign) + { + /* let bind_{array,assoc}_variable take care of this. */ + if (assoc_p (var)) + bind_assoc_variable (var, name, "0", value, aflags); + else + bind_array_variable (name, 0, value, aflags); + } + else +#endif + /* bind_variable_value duplicates the essential internals of + bind_variable() */ + if (offset) + bind_variable_value (var, value, aflags); + + /* If we found this variable in the temporary environment, as with + `var=value declare -x var', make sure it is treated identically + to `var=value export var'. Do the same for `declare -r' and + `readonly'. Preserve the attributes, except for att_tempvar. */ + /* XXX -- should this create a variable in the global scope, or + modify the local variable flags? ksh93 has it modify the + global scope. + Need to handle case like in set_var_attribute where a temporary + variable is in the same table as the function local vars. */ + if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var)) + { + SHELL_VAR *tv; + char *tvalue; + + tv = find_tempenv_variable (var->name); + if (tv) + { + tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring (""); + tv = bind_variable (var->name, tvalue, 0); + tv->attributes |= var->attributes & ~att_tempvar; + if (tv->context > 0) + VSETATTR (tv, att_propagate); + free (tvalue); + } + VSETATTR (var, att_propagate); + } + } + + stupidly_hack_special_variables (name); + + NEXT_VARIABLE (); + } + + return (assign_error ? EX_BADASSIGN + : ((any_failed == 0) ? EXECUTION_SUCCESS + : EXECUTION_FAILURE)); +} diff --git a/builtins/echo.def b/builtins/echo.def new file mode 100644 index 0000000..62c6199 --- /dev/null +++ b/builtins/echo.def @@ -0,0 +1,198 @@ +This file is echo.def, from which is created echo.c. +It implements the builtin "echo" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES echo.c +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" + +#include <stdio.h> +#include "../shell.h" + +#include "common.h" + +$BUILTIN echo +$FUNCTION echo_builtin +$DEPENDS_ON V9_ECHO +$SHORT_DOC echo [-neE] [arg ...] +Write arguments to the standard output. + +Display the ARGs on the standard output followed by a newline. + +Options: + -n do not append a newline + -e enable interpretation of the following backslash escapes + -E explicitly suppress interpretation of backslash escapes + +`echo' interprets the following backslash-escaped characters: + \a alert (bell) + \b backspace + \c suppress further output + \e escape character + \f form feed + \n new line + \r carriage return + \t horizontal tab + \v vertical tab + \\ backslash + \0nnn the character whose ASCII code is NNN (octal). NNN can be + 0 to 3 octal digits + \xHH the eight-bit character whose value is HH (hexadecimal). HH + can be one or two hex digits + +Exit Status: +Returns success unless a write error occurs. +$END + +$BUILTIN echo +$FUNCTION echo_builtin +$DEPENDS_ON !V9_ECHO +$SHORT_DOC echo [-n] [arg ...] +Write arguments to the standard output. + +Display the ARGs on the standard output followed by a newline. + +Options: + -n do not append a newline + +Exit Status: +Returns success unless a write error occurs. +$END + +#if defined (V9_ECHO) +# define VALID_ECHO_OPTIONS "neE" +#else /* !V9_ECHO */ +# define VALID_ECHO_OPTIONS "n" +#endif /* !V9_ECHO */ + +/* System V machines already have a /bin/sh with a v9 behaviour. We + give Bash the identical behaviour for these machines so that the + existing system shells won't barf. Regrettably, the SUS v2 has + standardized the Sys V echo behavior. This variable is external + so that we can have a `shopt' variable to control it at runtime. */ +#if defined (DEFAULT_ECHO_TO_XPG) || defined (STRICT_POSIX) +int xpg_echo = 1; +#else +int xpg_echo = 0; +#endif /* DEFAULT_ECHO_TO_XPG */ + +extern int posixly_correct; + +/* Print the words in LIST to standard output. If the first word is + `-n', then don't print a trailing newline. We also support the + echo syntax from Version 9 Unix systems. */ +int +echo_builtin (list) + WORD_LIST *list; +{ + int display_return, do_v9, i, len; + char *temp, *s; + + do_v9 = xpg_echo; + display_return = 1; + + if (posixly_correct && xpg_echo) + goto just_echo; + + for (; list && (temp = list->word->word) && *temp == '-'; list = list->next) + { + /* If it appears that we are handling options, then make sure that + all of the options specified are actually valid. Otherwise, the + string should just be echoed. */ + temp++; + + for (i = 0; temp[i]; i++) + { + if (strchr (VALID_ECHO_OPTIONS, temp[i]) == 0) + break; + } + + /* echo - and echo -<nonopt> both mean to just echo the arguments. */ + if (*temp == 0 || temp[i]) + break; + + /* All of the options in TEMP are valid options to ECHO. + Handle them. */ + while (i = *temp++) + { + switch (i) + { + case 'n': + display_return = 0; + break; +#if defined (V9_ECHO) + case 'e': + do_v9 = 1; + break; + case 'E': + do_v9 = 0; + break; +#endif /* V9_ECHO */ + default: + goto just_echo; /* XXX */ + } + } + } + +just_echo: + + clearerr (stdout); /* clear error before writing and testing success */ + + terminate_immediately++; + while (list) + { + i = len = 0; + temp = do_v9 ? ansicstr (list->word->word, STRLEN (list->word->word), 1, &i, &len) + : list->word->word; + if (temp) + { + if (do_v9) + { + for (s = temp; len > 0; len--) + putchar (*s++); + } + else + printf ("%s", temp); +#if defined (SunOS5) + fflush (stdout); /* Fix for bug in SunOS 5.5 printf(3) */ +#endif + } + if (do_v9 && temp) + free (temp); + list = list->next; + if (i) + { + display_return = 0; + break; + } + if (list) + putchar(' '); + } + + if (display_return) + putchar ('\n'); + + terminate_immediately--; + return (sh_chkwrite (EXECUTION_SUCCESS)); +} diff --git a/builtins/enable.def b/builtins/enable.def new file mode 100644 index 0000000..40cbb44 --- /dev/null +++ b/builtins/enable.def @@ -0,0 +1,483 @@ +This file is enable.def, from which is created enable.c. +It implements the builtin "enable" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES enable.c + +$BUILTIN enable +$FUNCTION enable_builtin +$SHORT_DOC enable [-a] [-dnps] [-f filename] [name ...] +Enable and disable shell builtins. + +Enables and disables builtin shell commands. Disabling allows you to +execute a disk command which has the same name as a shell builtin +without using a full pathname. + +Options: + -a print a list of builtins showing whether or not each is enabled + -n disable each NAME or display a list of disabled builtins + -p print the list of builtins in a reusable format + -s print only the names of Posix `special' builtins + +Options controlling dynamic loading: + -f Load builtin NAME from shared object FILENAME + -d Remove a builtin loaded with -f + +Without options, each NAME is enabled. + +To use the `test' found in $PATH instead of the shell builtin +version, type `enable -n test'. + +Exit Status: +Returns success unless NAME is not a shell builtin or an error occurs. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../builtins.h" +#include "../flags.h" +#include "common.h" +#include "bashgetopt.h" + +#if defined (PROGRAMMABLE_COMPLETION) +# include "../pcomplete.h" +#endif + +#define ENABLED 1 +#define DISABLED 2 +#define SPECIAL 4 + +#define AFLAG 0x01 +#define DFLAG 0x02 +#define FFLAG 0x04 +#define NFLAG 0x08 +#define PFLAG 0x10 +#define SFLAG 0x20 + +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) +static int dyn_load_builtin __P((WORD_LIST *, int, char *)); +#endif + +#if defined (HAVE_DLCLOSE) +static int dyn_unload_builtin __P((char *)); +static void delete_builtin __P((struct builtin *)); +static int local_dlclose __P((void *)); +#endif + +static void list_some_builtins __P((int)); +static int enable_shell_command __P((char *, int)); + +/* Enable/disable shell commands present in LIST. If list is not specified, + then print out a list of shell commands showing which are enabled and + which are disabled. */ +int +enable_builtin (list) + WORD_LIST *list; +{ + int result, flags; + int opt, filter; +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) + char *filename; +#endif + + result = EXECUTION_SUCCESS; + flags = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "adnpsf:")) != -1) + { + switch (opt) + { + case 'a': + flags |= AFLAG; + break; + case 'n': + flags |= NFLAG; + break; + case 'p': + flags |= PFLAG; + break; + case 's': + flags |= SFLAG; + break; + case 'f': +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) + flags |= FFLAG; + filename = list_optarg; + break; +#else + builtin_error (_("dynamic loading not available")); + return (EX_USAGE); +#endif +#if defined (HAVE_DLCLOSE) + case 'd': + flags |= DFLAG; + break; +#else + builtin_error (_("dynamic loading not available")); + return (EX_USAGE); +#endif /* HAVE_DLCLOSE */ + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + +#if defined (RESTRICTED_SHELL) + /* Restricted shells cannot load new builtins. */ + if (restricted && (flags & (FFLAG|DFLAG))) + { + sh_restricted ((char *)NULL); + return (EXECUTION_FAILURE); + } +#endif + + if (list == 0 || (flags & PFLAG)) + { + filter = (flags & AFLAG) ? (ENABLED | DISABLED) + : (flags & NFLAG) ? DISABLED : ENABLED; + + if (flags & SFLAG) + filter |= SPECIAL; + + list_some_builtins (filter); + } +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) + else if (flags & FFLAG) + { + filter = (flags & NFLAG) ? DISABLED : ENABLED; + if (flags & SFLAG) + filter |= SPECIAL; + + result = dyn_load_builtin (list, filter, filename); +#if defined (PROGRAMMABLE_COMPLETION) + set_itemlist_dirty (&it_builtins); +#endif + } +#endif +#if defined (HAVE_DLCLOSE) + else if (flags & DFLAG) + { + while (list) + { + opt = dyn_unload_builtin (list->word->word); + if (opt == EXECUTION_FAILURE) + result = EXECUTION_FAILURE; + list = list->next; + } +#if defined (PROGRAMMABLE_COMPLETION) + set_itemlist_dirty (&it_builtins); +#endif + } +#endif + else + { + while (list) + { + opt = enable_shell_command (list->word->word, flags & NFLAG); + + if (opt == EXECUTION_FAILURE) + { + sh_notbuiltin (list->word->word); + result = EXECUTION_FAILURE; + } + list = list->next; + } + } + return (result); +} + +/* List some builtins. + FILTER is a mask with two slots: ENABLED and DISABLED. */ +static void +list_some_builtins (filter) + int filter; +{ + register int i; + + for (i = 0; i < num_shell_builtins; i++) + { + if (shell_builtins[i].function == 0 || (shell_builtins[i].flags & BUILTIN_DELETED)) + continue; + + if ((filter & SPECIAL) && + (shell_builtins[i].flags & SPECIAL_BUILTIN) == 0) + continue; + + if ((filter & ENABLED) && (shell_builtins[i].flags & BUILTIN_ENABLED)) + printf ("enable %s\n", shell_builtins[i].name); + else if ((filter & DISABLED) && + ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0)) + printf ("enable -n %s\n", shell_builtins[i].name); + } +} + +/* Enable the shell command NAME. If DISABLE_P is non-zero, then + disable NAME instead. */ +static int +enable_shell_command (name, disable_p) + char *name; + int disable_p; +{ + struct builtin *b; + + b = builtin_address_internal (name, 1); + if (b == 0) + return (EXECUTION_FAILURE); + + if (disable_p) + b->flags &= ~BUILTIN_ENABLED; +#if defined (RESTRICTED_SHELL) + else if (restricted && ((b->flags & BUILTIN_ENABLED) == 0)) + { + sh_restricted ((char *)NULL); + return (EXECUTION_FAILURE); + } +#endif + else + b->flags |= BUILTIN_ENABLED; + +#if defined (PROGRAMMABLE_COMPLETION) + set_itemlist_dirty (&it_enabled); + set_itemlist_dirty (&it_disabled); +#endif + + return (EXECUTION_SUCCESS); +} + +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) + +#if defined (HAVE_DLFCN_H) +# include <dlfcn.h> +#endif + +static int +dyn_load_builtin (list, flags, filename) + WORD_LIST *list; + int flags; + char *filename; +{ + WORD_LIST *l; + void *handle; + + int total, size, new, replaced; + char *struct_name, *name; + struct builtin **new_builtins, *b, *new_shell_builtins, *old_builtin; + + if (list == 0) + return (EXECUTION_FAILURE); + +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif + +#if defined (_AIX) + handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL); +#else + handle = dlopen (filename, RTLD_LAZY); +#endif /* !_AIX */ + + if (handle == 0) + { + builtin_error (_("cannot open shared object %s: %s"), filename, dlerror ()); + return (EXECUTION_FAILURE); + } + + for (new = 0, l = list; l; l = l->next, new++) + ; + new_builtins = (struct builtin **)xmalloc (new * sizeof (struct builtin *)); + + /* For each new builtin in the shared object, find it and its describing + structure. If this is overwriting an existing builtin, do so, otherwise + save the loaded struct for creating the new list of builtins. */ + for (replaced = new = 0; list; list = list->next) + { + name = list->word->word; + + size = strlen (name); + struct_name = (char *)xmalloc (size + 8); + strcpy (struct_name, name); + strcpy (struct_name + size, "_struct"); + + b = (struct builtin *)dlsym (handle, struct_name); + if (b == 0) + { + builtin_error (_("cannot find %s in shared object %s: %s"), + struct_name, filename, dlerror ()); + free (struct_name); + continue; + } + + free (struct_name); + + b->flags &= ~STATIC_BUILTIN; + if (flags & SPECIAL) + b->flags |= SPECIAL_BUILTIN; + b->handle = handle; + + if (old_builtin = builtin_address_internal (name, 1)) + { + replaced++; + FASTCOPY ((char *)b, (char *)old_builtin, sizeof (struct builtin)); + } + else + new_builtins[new++] = b; + } + + if (replaced == 0 && new == 0) + { + free (new_builtins); + dlclose (handle); + return (EXECUTION_FAILURE); + } + + if (new) + { + total = num_shell_builtins + new; + size = (total + 1) * sizeof (struct builtin); + + new_shell_builtins = (struct builtin *)xmalloc (size); + FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins, + num_shell_builtins * sizeof (struct builtin)); + for (replaced = 0; replaced < new; replaced++) + FASTCOPY ((char *)new_builtins[replaced], + (char *)&new_shell_builtins[num_shell_builtins + replaced], + sizeof (struct builtin)); + + new_shell_builtins[total].name = (char *)0; + new_shell_builtins[total].function = (sh_builtin_func_t *)0; + new_shell_builtins[total].flags = 0; + + if (shell_builtins != static_shell_builtins) + free (shell_builtins); + + shell_builtins = new_shell_builtins; + num_shell_builtins = total; + initialize_shell_builtins (); + } + + free (new_builtins); + return (EXECUTION_SUCCESS); +} +#endif + +#if defined (HAVE_DLCLOSE) +static void +delete_builtin (b) + struct builtin *b; +{ + int ind, size; + struct builtin *new_shell_builtins; + + /* XXX - funky pointer arithmetic - XXX */ +#ifdef __STDC__ + ind = b - shell_builtins; +#else + ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin); +#endif + size = num_shell_builtins * sizeof (struct builtin); + new_shell_builtins = (struct builtin *)xmalloc (size); + + /* Copy shell_builtins[0]...shell_builtins[ind - 1] to new_shell_builtins */ + if (ind) + FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins, + ind * sizeof (struct builtin)); + /* Copy shell_builtins[ind+1]...shell_builtins[num_shell_builtins to + new_shell_builtins, starting at ind. */ + FASTCOPY ((char *)(&shell_builtins[ind+1]), + (char *)(&new_shell_builtins[ind]), + (num_shell_builtins - ind) * sizeof (struct builtin)); + + if (shell_builtins != static_shell_builtins) + free (shell_builtins); + + /* The result is still sorted. */ + num_shell_builtins--; + shell_builtins = new_shell_builtins; +} + +/* Tenon's MachTen has a dlclose that doesn't return a value, so we + finesse it with a local wrapper. */ +static int +local_dlclose (handle) + void *handle; +{ +#if !defined (__MACHTEN__) + return (dlclose (handle)); +#else /* __MACHTEN__ */ + dlclose (handle); + return ((dlerror () != NULL) ? -1 : 0); +#endif /* __MACHTEN__ */ +} + +static int +dyn_unload_builtin (name) + char *name; +{ + struct builtin *b; + void *handle; + int ref, i; + + b = builtin_address_internal (name, 1); + if (b == 0) + { + sh_notbuiltin (name); + return (EXECUTION_FAILURE); + } + if (b->flags & STATIC_BUILTIN) + { + builtin_error (_("%s: not dynamically loaded"), name); + return (EXECUTION_FAILURE); + } + + handle = (void *)b->handle; + for (ref = i = 0; i < num_shell_builtins; i++) + { + if (shell_builtins[i].handle == b->handle) + ref++; + } + + /* Don't remove the shared object unless the reference count of builtins + using it drops to zero. */ + if (ref == 1 && local_dlclose (handle) != 0) + { + builtin_error (_("%s: cannot delete: %s"), name, dlerror ()); + return (EXECUTION_FAILURE); + } + + /* Now remove this entry from the builtin table and reinitialize. */ + delete_builtin (b); + + return (EXECUTION_SUCCESS); +} +#endif diff --git a/builtins/eval.def b/builtins/eval.def new file mode 100644 index 0000000..ee9a23d --- /dev/null +++ b/builtins/eval.def @@ -0,0 +1,58 @@ +This file is eval.def, from which is created eval.c. +It implements the builtin "eval" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES eval.c + +$BUILTIN eval +$FUNCTION eval_builtin +$SHORT_DOC eval [arg ...] +Execute arguments as a shell command. + +Combine ARGs into a single string, use the result as input to the shell, +and execute the resulting commands. + +Exit Status: +Returns exit status of command or success if command is null. +$END + +#include <config.h> +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../shell.h" +#include "bashgetopt.h" +#include "common.h" + +/* Parse the string that these words make, and execute the command found. */ +int +eval_builtin (list) + WORD_LIST *list; +{ + if (no_options (list)) + return (EX_USAGE); + list = loptend; /* skip over possible `--' */ + + /* Note that parse_and_execute () frees the string it is passed. */ + return (list ? parse_and_execute (string_list (list), "eval", SEVAL_NOHIST) : EXECUTION_SUCCESS); +} diff --git a/builtins/evalfile.c b/builtins/evalfile.c new file mode 100644 index 0000000..972d039 --- /dev/null +++ b/builtins/evalfile.c @@ -0,0 +1,351 @@ +/* evalfile.c - read and evaluate commands from a file or file descriptor */ + +/* Copyright (C) 1996-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" + +#include <stdio.h> +#include <signal.h> +#include <errno.h> + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../flags.h" +#include "../input.h" +#include "../execute_cmd.h" +#include "../trap.h" + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#include <typemax.h> + +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +/* Flags for _evalfile() */ +#define FEVAL_ENOENTOK 0x001 +#define FEVAL_BUILTIN 0x002 +#define FEVAL_UNWINDPROT 0x004 +#define FEVAL_NONINT 0x008 +#define FEVAL_LONGJMP 0x010 +#define FEVAL_HISTORY 0x020 +#define FEVAL_CHECKBINARY 0x040 +#define FEVAL_REGFILE 0x080 +#define FEVAL_NOPUSHARGS 0x100 + +extern int posixly_correct; +extern int indirection_level, subshell_environment; +extern int return_catch_flag, return_catch_value; +extern int last_command_exit_value; + +/* How many `levels' of sourced files we have. */ +int sourcelevel = 0; + +static int +_evalfile (filename, flags) + const char *filename; + int flags; +{ + volatile int old_interactive; + procenv_t old_return_catch; + int return_val, fd, result, pflags, i, nnull; + ssize_t nr; /* return value from read(2) */ + char *string; + struct stat finfo; + size_t file_size; + sh_vmsg_func_t *errfunc; +#if defined (ARRAY_VARS) + SHELL_VAR *funcname_v, *nfv, *bash_source_v, *bash_lineno_v; + ARRAY *funcname_a, *bash_source_a, *bash_lineno_a; +# if defined (DEBUGGER) + SHELL_VAR *bash_argv_v, *bash_argc_v; + ARRAY *bash_argv_a, *bash_argc_a; +# endif + char *t, tt[2]; +#endif + + USE_VAR(pflags); + +#if defined (ARRAY_VARS) + GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a); + GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a); + GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a); +# if defined (DEBUGGER) + GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); + GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); +# endif +#endif + + fd = open (filename, O_RDONLY); + + if (fd < 0 || (fstat (fd, &finfo) == -1)) + { +file_error_and_exit: + if (((flags & FEVAL_ENOENTOK) == 0) || errno != ENOENT) + file_error (filename); + + if (flags & FEVAL_LONGJMP) + { + last_command_exit_value = 1; + jump_to_top_level (EXITPROG); + } + + return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE + : ((errno == ENOENT) ? 0 : -1)); + } + + errfunc = ((flags & FEVAL_BUILTIN) ? builtin_error : internal_error); + + if (S_ISDIR (finfo.st_mode)) + { + (*errfunc) (_("%s: is a directory"), filename); + return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1); + } + else if ((flags & FEVAL_REGFILE) && S_ISREG (finfo.st_mode) == 0) + { + (*errfunc) (_("%s: not a regular file"), filename); + return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1); + } + + file_size = (size_t)finfo.st_size; + /* Check for overflow with large files. */ + if (file_size != finfo.st_size || file_size + 1 < file_size) + { + (*errfunc) (_("%s: file is too large"), filename); + return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1); + } + +#if defined (__CYGWIN__) && defined (O_TEXT) + setmode (fd, O_TEXT); +#endif + + if (S_ISREG (finfo.st_mode) && file_size <= SSIZE_MAX) + { + string = (char *)xmalloc (1 + file_size); + nr = read (fd, string, file_size); + if (nr >= 0) + string[nr] = '\0'; + } + else + nr = zmapfd (fd, &string, 0); + + return_val = errno; + close (fd); + errno = return_val; + + if (nr < 0) /* XXX was != file_size, not < 0 */ + { + free (string); + goto file_error_and_exit; + } + + if (nr == 0) + { + free (string); + return ((flags & FEVAL_BUILTIN) ? EXECUTION_SUCCESS : 1); + } + + if ((flags & FEVAL_CHECKBINARY) && + check_binary_file (string, (nr > 80) ? 80 : nr)) + { + free (string); + (*errfunc) (_("%s: cannot execute binary file"), filename); + return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1); + } + + i = strlen (string); + if (i < nr) + { + for (nnull = i = 0; i < nr; i++) + if (string[i] == '\0') + { + memmove (string+i, string+i+1, nr - i); + nr--; + /* Even if the `check binary' flag is not set, we want to avoid + sourcing files with more than 256 null characters -- that + probably indicates a binary file. */ + if ((flags & FEVAL_BUILTIN) && ++nnull > 256) + { + free (string); + (*errfunc) (_("%s: cannot execute binary file"), filename); + return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1); + } + } + } + + if (flags & FEVAL_UNWINDPROT) + { + begin_unwind_frame ("_evalfile"); + + unwind_protect_int (return_catch_flag); + unwind_protect_jmp_buf (return_catch); + if (flags & FEVAL_NONINT) + unwind_protect_int (interactive); + unwind_protect_int (sourcelevel); + } + else + { + COPY_PROCENV (return_catch, old_return_catch); + if (flags & FEVAL_NONINT) + old_interactive = interactive; + } + + if (flags & FEVAL_NONINT) + interactive = 0; + + return_catch_flag++; + sourcelevel++; + +#if defined (ARRAY_VARS) + array_push (bash_source_a, (char *)filename); + t = itos (executing_line_number ()); + array_push (bash_lineno_a, t); + free (t); + array_push (funcname_a, "source"); /* not exactly right */ +# if defined (DEBUGGER) + /* Have to figure out a better way to do this when `source' is supplied + arguments */ + if ((flags & FEVAL_NOPUSHARGS) == 0) + { + array_push (bash_argv_a, (char *)filename); + tt[0] = '1'; tt[1] = '\0'; + array_push (bash_argc_a, tt); + } +# endif +#endif + + /* set the flags to be passed to parse_and_execute */ + pflags = SEVAL_RESETLINE; + pflags |= (flags & FEVAL_HISTORY) ? 0 : SEVAL_NOHIST; + + if (flags & FEVAL_BUILTIN) + result = EXECUTION_SUCCESS; + + return_val = setjmp (return_catch); + + /* If `return' was seen outside of a function, but in the script, then + force parse_and_execute () to clean up. */ + if (return_val) + { + parse_and_execute_cleanup (); + result = return_catch_value; + } + else + result = parse_and_execute (string, filename, pflags); + + if (flags & FEVAL_UNWINDPROT) + run_unwind_frame ("_evalfile"); + else + { + if (flags & FEVAL_NONINT) + interactive = old_interactive; + return_catch_flag--; + sourcelevel--; + COPY_PROCENV (old_return_catch, return_catch); + } + +#if defined (ARRAY_VARS) + /* These two variables cannot be unset, and cannot be affected by the + sourced file. */ + array_pop (bash_source_a); + array_pop (bash_lineno_a); + + /* FUNCNAME can be unset, and so can potentially be changed by the + sourced file. */ + GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a); + if (nfv == funcname_v) + array_pop (funcname_a); +# if defined (DEBUGGER) + if ((flags & FEVAL_NOPUSHARGS) == 0) + { + array_pop (bash_argc_a); + array_pop (bash_argv_a); + } +# endif +#endif + + return ((flags & FEVAL_BUILTIN) ? result : 1); +} + +int +maybe_execute_file (fname, force_noninteractive) + const char *fname; + int force_noninteractive; +{ + char *filename; + int result, flags; + + filename = bash_tilde_expand (fname, 0); + flags = FEVAL_ENOENTOK; + if (force_noninteractive) + flags |= FEVAL_NONINT; + result = _evalfile (filename, flags); + free (filename); + return result; +} + +#if defined (HISTORY) +int +fc_execute_file (filename) + const char *filename; +{ + int flags; + + /* We want these commands to show up in the history list if + remember_on_history is set. */ + flags = FEVAL_ENOENTOK|FEVAL_HISTORY|FEVAL_REGFILE; + return (_evalfile (filename, flags)); +} +#endif /* HISTORY */ + +int +source_file (filename, sflags) + const char *filename; + int sflags; +{ + int flags, rval; + + flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT; + if (sflags) + flags |= FEVAL_NOPUSHARGS; + /* POSIX shells exit if non-interactive and file error. */ + if (posixly_correct && !interactive_shell) + flags |= FEVAL_LONGJMP; + rval = _evalfile (filename, flags); + + run_return_trap (); + return rval; +} diff --git a/builtins/evalstring.c b/builtins/evalstring.c new file mode 100644 index 0000000..40abe00 --- /dev/null +++ b/builtins/evalstring.c @@ -0,0 +1,512 @@ +/* evalstring.c - evaluate a string as one or more shell commands. + +/* Copyright (C) 1996-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include <signal.h> + +#include <errno.h> + +#include "filecntl.h" +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../flags.h" +#include "../input.h" +#include "../execute_cmd.h" +#include "../redir.h" +#include "../trap.h" +#include "../bashintl.h" + +#include <y.tab.h> + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#include "common.h" +#include "builtext.h" + +#if !defined (errno) +extern int errno; +#endif + +#define IS_BUILTIN(s) (builtin_address_internal(s, 0) != (struct builtin *)NULL) + +extern int indirection_level, subshell_environment; +extern int line_number; +extern int current_token, shell_eof_token; +extern int last_command_exit_value; +extern int running_trap; +extern int loop_level; +extern int executing_list; +extern int comsub_ignore_return; +extern int posixly_correct; +extern sh_builtin_func_t *this_shell_builtin; + +int parse_and_execute_level = 0; + +static int cat_file __P((REDIRECT *)); + +#define PE_TAG "parse_and_execute top" +#define PS_TAG "parse_string top" + +#if defined (HISTORY) +static void +set_history_remembering () +{ + remember_on_history = enable_history_list; +} +#endif + +/* How to force parse_and_execute () to clean up after itself. */ +void +parse_and_execute_cleanup () +{ + if (running_trap) + { + run_trap_cleanup (running_trap - 1); + unfreeze_jobs_list (); + } + + if (have_unwind_protects ()) + run_unwind_frame (PE_TAG); + else + parse_and_execute_level = 0; /* XXX */ +} + +static void +parse_prologue (string, flags, tag) + char *string; + int flags; + char *tag; +{ + char *orig_string; + int x; + + orig_string = string; + /* Unwind protect this invocation of parse_and_execute (). */ + begin_unwind_frame (tag); + unwind_protect_int (parse_and_execute_level); + unwind_protect_jmp_buf (top_level); + unwind_protect_int (indirection_level); + unwind_protect_int (line_number); + unwind_protect_int (loop_level); + unwind_protect_int (executing_list); + unwind_protect_int (comsub_ignore_return); + if (flags & (SEVAL_NONINT|SEVAL_INTERACT)) + unwind_protect_int (interactive); + +#if defined (HISTORY) + if (parse_and_execute_level == 0) + add_unwind_protect (set_history_remembering, (char *)NULL); + else + unwind_protect_int (remember_on_history); /* can be used in scripts */ +# if defined (BANG_HISTORY) + if (interactive_shell) + unwind_protect_int (history_expansion_inhibited); +# endif /* BANG_HISTORY */ +#endif /* HISTORY */ + + if (interactive_shell) + { + x = get_current_prompt_level (); + add_unwind_protect (set_current_prompt_level, x); + } + + add_unwind_protect (pop_stream, (char *)NULL); + if (orig_string && ((flags & SEVAL_NOFREE) == 0)) + add_unwind_protect (xfree, orig_string); + end_unwind_frame (); + + if (flags & (SEVAL_NONINT|SEVAL_INTERACT)) + interactive = (flags & SEVAL_NONINT) ? 0 : 1; + +#if defined (HISTORY) + if (flags & SEVAL_NOHIST) + bash_history_disable (); +#endif /* HISTORY */ +} + +/* Parse and execute the commands in STRING. Returns whatever + execute_command () returns. This frees STRING. FLAGS is a + flags word; look in common.h for the possible values. Actions + are: + (flags & SEVAL_NONINT) -> interactive = 0; + (flags & SEVAL_INTERACT) -> interactive = 1; + (flags & SEVAL_NOHIST) -> call bash_history_disable () + (flags & SEVAL_NOFREE) -> don't free STRING when finished + (flags & SEVAL_RESETLINE) -> reset line_number to 1 +*/ + +int +parse_and_execute (string, from_file, flags) + char *string; + const char *from_file; + int flags; +{ + int code, lreset; + volatile int should_jump_to_top_level, last_result; + COMMAND *volatile command; + + parse_prologue (string, flags, PE_TAG); + + parse_and_execute_level++; + + lreset = flags & SEVAL_RESETLINE; + + /* Reset the line number if the caller wants us to. If we don't reset the + line number, we have to subtract one, because we will add one just + before executing the next command (resetting the line number sets it to + 0; the first line number is 1). */ + push_stream (lreset); + if (lreset == 0) + line_number--; + + indirection_level++; + + code = should_jump_to_top_level = 0; + last_result = EXECUTION_SUCCESS; + + with_input_from_string (string, from_file); + while (*(bash_input.location.string)) + { + command = (COMMAND *)NULL; + + if (interrupt_state) + { + last_result = EXECUTION_FAILURE; + break; + } + + /* Provide a location for functions which `longjmp (top_level)' to + jump to. This prevents errors in substitution from restarting + the reader loop directly, for example. */ + code = setjmp (top_level); + + if (code) + { + should_jump_to_top_level = 0; + switch (code) + { + case FORCE_EOF: + case ERREXIT: + case EXITPROG: + if (command) + run_unwind_frame ("pe_dispose"); + /* Remember to call longjmp (top_level) after the old + value for it is restored. */ + should_jump_to_top_level = 1; + goto out; + + case DISCARD: + if (command) + run_unwind_frame ("pe_dispose"); + last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */ + if (subshell_environment) + { + should_jump_to_top_level = 1; + goto out; + } + else + { +#if 0 + dispose_command (command); /* pe_dispose does this */ +#endif + continue; + } + + default: + command_error ("parse_and_execute", CMDERR_BADJUMP, code, 0); + break; + } + } + + if (parse_command () == 0) + { + if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute)) + { + last_result = EXECUTION_SUCCESS; + dispose_command (global_command); + global_command = (COMMAND *)NULL; + } + else if (command = global_command) + { + struct fd_bitmap *bitmap; + + bitmap = new_fd_bitmap (FD_BITMAP_SIZE); + begin_unwind_frame ("pe_dispose"); + add_unwind_protect (dispose_fd_bitmap, bitmap); + add_unwind_protect (dispose_command, command); /* XXX */ + + global_command = (COMMAND *)NULL; + + if ((subshell_environment & SUBSHELL_COMSUB) && comsub_ignore_return) + command->flags |= CMD_IGNORE_RETURN; + +#if defined (ONESHOT) + /* + * IF + * we were invoked as `bash -c' (startup_state == 2) AND + * parse_and_execute has not been called recursively AND + * we're not running a trap AND + * we have parsed the full command (string == '\0') AND + * we're not going to run the exit trap AND + * we have a simple command without redirections AND + * the command is not being timed AND + * the command's return status is not being inverted + * THEN + * tell the execution code that we don't need to fork + */ + if (startup_state == 2 && parse_and_execute_level == 1 && + running_trap == 0 && + *bash_input.location.string == '\0' && + command->type == cm_simple && + signal_is_trapped (EXIT_TRAP) == 0 && + command->redirects == 0 && command->value.Simple->redirects == 0 && + ((command->flags & CMD_TIME_PIPELINE) == 0) && + ((command->flags & CMD_INVERT_RETURN) == 0)) + { + command->flags |= CMD_NO_FORK; + command->value.Simple->flags |= CMD_NO_FORK; + } +#endif /* ONESHOT */ + + /* See if this is a candidate for $( <file ). */ + if (startup_state == 2 && + (subshell_environment & SUBSHELL_COMSUB) && + *bash_input.location.string == '\0' && + command->type == cm_simple && !command->redirects && + (command->flags & CMD_TIME_PIPELINE) == 0 && + command->value.Simple->words == 0 && + command->value.Simple->redirects && + command->value.Simple->redirects->next == 0 && + command->value.Simple->redirects->instruction == r_input_direction) + { + int r; + r = cat_file (command->value.Simple->redirects); + last_result = (r < 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + } + else + last_result = execute_command_internal + (command, 0, NO_PIPE, NO_PIPE, bitmap); + + dispose_command (command); + dispose_fd_bitmap (bitmap); + discard_unwind_frame ("pe_dispose"); + } + } + else + { + last_result = EXECUTION_FAILURE; + + if (interactive_shell == 0 && this_shell_builtin && + (this_shell_builtin == source_builtin || this_shell_builtin == eval_builtin) && + last_command_exit_value == EX_BADSYNTAX && posixly_correct) + { +#if 0 /* XXX - for bash-4.2 */ + should_jump_to_top_level = 1; + code = ERREXIT; + last_command_exit_value = EX_BADUSAGE; +#else + internal_warning (_("syntax errors in . or eval will cause future versions of the shell to abort as Posix requires")); +#endif + } + + /* Since we are shell compatible, syntax errors in a script + abort the execution of the script. Right? */ + break; + } + } + + out: + + run_unwind_frame (PE_TAG); + + if (interrupt_state && parse_and_execute_level == 0) + { + /* An interrupt during non-interactive execution in an + interactive shell (e.g. via $PROMPT_COMMAND) should + not cause the shell to exit. */ + interactive = interactive_shell; + throw_to_top_level (); + } + + if (should_jump_to_top_level) + jump_to_top_level (code); + + return (last_result); +} + +/* Parse a command contained in STRING according to FLAGS and return the + number of characters consumed from the string. If non-NULL, set *ENDP + to the position in the string where the parse ended. Used to validate + command substitutions during parsing to obey Posix rules about finding + the end of the command and balancing parens. */ +int +parse_string (string, from_file, flags, endp) + char *string; + const char *from_file; + int flags; + char **endp; +{ + int code, nc; + volatile int should_jump_to_top_level; + COMMAND *volatile command, *oglobal; + char *ostring; + + parse_prologue (string, flags, PS_TAG); + + /* Reset the line number if the caller wants us to. If we don't reset the + line number, we have to subtract one, because we will add one just + before executing the next command (resetting the line number sets it to + 0; the first line number is 1). */ + push_stream (0); + + code = should_jump_to_top_level = 0; + oglobal = global_command; + ostring = string; + + with_input_from_string (string, from_file); + while (*(bash_input.location.string)) + { + command = (COMMAND *)NULL; + +#if 0 + if (interrupt_state) + break; +#endif + + /* Provide a location for functions which `longjmp (top_level)' to + jump to. */ + code = setjmp (top_level); + + if (code) + { +#if defined (DEBUG) +itrace("parse_string: longjmp executed: code = %d", code); +#endif + should_jump_to_top_level = 0; + switch (code) + { + case FORCE_EOF: + case ERREXIT: + case EXITPROG: + case DISCARD: /* XXX */ + if (command) + dispose_command (command); + /* Remember to call longjmp (top_level) after the old + value for it is restored. */ + should_jump_to_top_level = 1; + goto out; + + default: + command_error ("parse_string", CMDERR_BADJUMP, code, 0); + break; + } + } + + if (parse_command () == 0) + { + dispose_command (global_command); + global_command = (COMMAND *)NULL; + } + else + { + if ((flags & SEVAL_NOLONGJMP) == 0) + { + should_jump_to_top_level = 1; + code = DISCARD; + } + else + reset_parser (); /* XXX - sets token_to_read */ + break; + } + + if (current_token == yacc_EOF || current_token == shell_eof_token) + break; + } + + out: + + global_command = oglobal; + nc = bash_input.location.string - ostring; + if (endp) + *endp = bash_input.location.string; + + run_unwind_frame (PS_TAG); + + if (should_jump_to_top_level) + jump_to_top_level (code); + + return (nc); +} + +/* Handle a $( < file ) command substitution. This expands the filename, + returning errors as appropriate, then just cats the file to the standard + output. */ +static int +cat_file (r) + REDIRECT *r; +{ + char *fn; + int fd, rval; + + if (r->instruction != r_input_direction) + return -1; + + /* Get the filename. */ + if (posixly_correct && !interactive_shell) + disallow_filename_globbing++; + fn = redirection_expand (r->redirectee.filename); + if (posixly_correct && !interactive_shell) + disallow_filename_globbing--; + + if (fn == 0) + { + redirection_error (r, AMBIGUOUS_REDIRECT); + return -1; + } + + fd = open(fn, O_RDONLY); + if (fd < 0) + { + file_error (fn); + free (fn); + return -1; + } + + rval = zcatfd (fd, 1, fn); + + free (fn); + close (fd); + + return (rval); +} diff --git a/builtins/exec.def b/builtins/exec.def new file mode 100644 index 0000000..0837a98 --- /dev/null +++ b/builtins/exec.def @@ -0,0 +1,236 @@ +This file is exec.def, from which is created exec.c. +It implements the builtin "exec" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES exec.c + +$BUILTIN exec +$FUNCTION exec_builtin +$SHORT_DOC exec [-cl] [-a name] [command [arguments ...]] [redirection ...] +Replace the shell with the given command. + +Execute COMMAND, replacing this shell with the specified program. +ARGUMENTS become the arguments to COMMAND. If COMMAND is not specified, +any redirections take effect in the current shell. + +Options: + -a name pass NAME as the zeroth argument to COMMAND + -c execute COMMAND with an empty environment + -l place a dash in the zeroth argument to COMMAND + +If the command cannot be executed, a non-interactive shell exits, unless +the shell option `execfail' is set. + +Exit Status: +Returns success unless COMMAND is not found or a redirection error occurs. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include "posixstat.h" +#include <signal.h> +#include <errno.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../execute_cmd.h" +#include "../findcmd.h" +#if defined (JOB_CONTROL) +# include "../jobs.h" +#endif +#include "../flags.h" +#include "../trap.h" +#if defined (HISTORY) +# include "../bashhist.h" +#endif +#include "common.h" +#include "bashgetopt.h" + +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int subshell_environment; +extern REDIRECT *redirection_undo_list; + +int no_exit_on_failed_exec; + +/* If the user wants this to look like a login shell, then + prepend a `-' onto NAME and return the new name. */ +static char * +mkdashname (name) + char *name; +{ + char *ret; + + ret = (char *)xmalloc (2 + strlen (name)); + ret[0] = '-'; + strcpy (ret + 1, name); + return ret; +} + +int +exec_builtin (list) + WORD_LIST *list; +{ + int exit_value = EXECUTION_FAILURE; + int cleanenv, login, opt; + char *argv0, *command, **args, **env, *newname, *com2; + + cleanenv = login = 0; + argv0 = (char *)NULL; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "cla:")) != -1) + { + switch (opt) + { + case 'c': + cleanenv = 1; + break; + case 'l': + login = 1; + break; + case 'a': + argv0 = list_optarg; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* First, let the redirections remain. */ + dispose_redirects (redirection_undo_list); + redirection_undo_list = (REDIRECT *)NULL; + + if (list == 0) + return (EXECUTION_SUCCESS); + +#if defined (RESTRICTED_SHELL) + if (restricted) + { + sh_restricted ((char *)NULL); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ + + args = strvec_from_word_list (list, 1, 0, (int *)NULL); + + /* A command with a slash anywhere in its name is not looked up in $PATH. */ + command = absolute_program (args[0]) ? args[0] : search_for_command (args[0]); + + if (command == 0) + { + sh_notfound (args[0]); + exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */ + goto failed_exec; + } + + com2 = full_pathname (command); + if (com2) + { + if (command != args[0]) + free (command); + command = com2; + } + + if (argv0) + { + free (args[0]); + args[0] = login ? mkdashname (argv0) : savestring (argv0); + } + else if (login) + { + newname = mkdashname (args[0]); + free (args[0]); + args[0] = newname; + } + + /* Decrement SHLVL by 1 so a new shell started here has the same value, + preserving the appearance. After we do that, we need to change the + exported environment to include the new value. */ + if (cleanenv == 0) + adjust_shell_level (-1); + + if (cleanenv) + env = (char **)NULL; + else + { + maybe_make_export_env (); + env = export_env; + } + +#if defined (HISTORY) + if (interactive_shell && subshell_environment == 0) + maybe_save_shell_history (); +#endif /* HISTORY */ + + restore_original_signals (); + +#if defined (JOB_CONTROL) + if (subshell_environment == 0) + end_job_control (); +#endif /* JOB_CONTROL */ + + shell_execve (command, args, env); + + /* We have to set this to NULL because shell_execve has called realloc() + to stuff more items at the front of the array, which may have caused + the memory to be freed by realloc(). We don't want to free it twice. */ + args = (char **)NULL; + if (cleanenv == 0) + adjust_shell_level (1); + + if (executable_file (command) == 0) + { + builtin_error (_("%s: cannot execute: %s"), command, strerror (errno)); + exit_value = EX_NOEXEC; /* As per Posix.2, 3.14.6 */ + } + else + file_error (command); + +failed_exec: + FREE (command); + + if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0)) + exit_shell (exit_value); + + if (args) + strvec_dispose (args); + + initialize_traps (); + initialize_signals (1); + +#if defined (JOB_CONTROL) + if (interactive_shell || job_control) + restart_job_control (); +#endif /* JOB_CONTROL */ + + return (exit_value); +} diff --git a/builtins/exit.def b/builtins/exit.def new file mode 100644 index 0000000..34728eb --- /dev/null +++ b/builtins/exit.def @@ -0,0 +1,168 @@ +This file is exit.def, from which is created exit.c. +It implements the builtins "exit", and "logout" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES exit.c + +$BUILTIN exit +$FUNCTION exit_builtin +$SHORT_DOC exit [n] +Exit the shell. + +Exits the shell with a status of N. If N is omitted, the exit status +is that of the last command executed. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "../jobs.h" + +#include "common.h" +#include "builtext.h" /* for jobs_builtin */ + +extern int check_jobs_at_exit; +extern int last_command_exit_value; +extern int running_trap, trap_saved_exit_value; +extern int subshell_environment; +extern sh_builtin_func_t *this_shell_builtin; +extern sh_builtin_func_t *last_shell_builtin; + +static int exit_or_logout __P((WORD_LIST *)); +static int sourced_logout; + +int +exit_builtin (list) + WORD_LIST *list; +{ + if (interactive) + { + fprintf (stderr, login_shell ? _("logout\n") : "exit\n"); + fflush (stderr); + } + + return (exit_or_logout (list)); +} + +$BUILTIN logout +$FUNCTION logout_builtin +$SHORT_DOC logout [n] +Exit a login shell. + +Exits a login shell with exit status N. Returns an error if not executed +in a login shell. +$END + +/* How to logout. */ +int +logout_builtin (list) + WORD_LIST *list; +{ + if (login_shell == 0 /* && interactive */) + { + builtin_error (_("not login shell: use `exit'")); + return (EXECUTION_FAILURE); + } + else + return (exit_or_logout (list)); +} + +static int +exit_or_logout (list) + WORD_LIST *list; +{ + int exit_value; + +#if defined (JOB_CONTROL) + int exit_immediate_okay, stopmsg; + + exit_immediate_okay = (interactive == 0 || + last_shell_builtin == exit_builtin || + last_shell_builtin == logout_builtin || + last_shell_builtin == jobs_builtin); + + /* Check for stopped jobs if the user wants to. */ + if (exit_immediate_okay == 0) + { + register int i; + for (i = stopmsg = 0; i < js.j_jobslots; i++) + if (jobs[i] && STOPPED (i)) + stopmsg = JSTOPPED; + else if (check_jobs_at_exit && stopmsg == 0 && jobs[i] && RUNNING (i)) + stopmsg = JRUNNING; + + if (stopmsg == JSTOPPED) + fprintf (stderr, _("There are stopped jobs.\n")); + else if (stopmsg == JRUNNING) + fprintf (stderr, _("There are running jobs.\n")); + + if (stopmsg && check_jobs_at_exit) + list_all_jobs (JLIST_STANDARD); + + if (stopmsg) + { + /* This is NOT superfluous because EOF can get here without + going through the command parser. Set both last and this + so that either `exit', `logout', or ^D will work to exit + immediately if nothing intervenes. */ + this_shell_builtin = last_shell_builtin = exit_builtin; + return (EXECUTION_FAILURE); + } + } +#endif /* JOB_CONTROL */ + + /* Get return value if present. This means that you can type + `logout 5' to a shell, and it returns 5. */ + + /* If we're running the exit trap (running_trap == 1, since running_trap + gets set to SIG+1), and we don't have a argument given to `exit' + (list == 0), use the exit status we saved before running the trap + commands (trap_saved_exit_value). */ + exit_value = (running_trap == 1 && list == 0) ? trap_saved_exit_value : get_exitstat (list); + + bash_logout (); + + last_command_exit_value = exit_value; + + /* Exit the program. */ + jump_to_top_level (EXITPROG); + /*NOTREACHED*/ +} + +void +bash_logout () +{ + /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */ + if (login_shell && sourced_logout++ == 0 && subshell_environment == 0) + { + maybe_execute_file ("~/.bash_logout", 1); +#ifdef SYS_BASH_LOGOUT + maybe_execute_file (SYS_BASH_LOGOUT, 1); +#endif + } +} diff --git a/builtins/fc.def b/builtins/fc.def new file mode 100644 index 0000000..a378d9d --- /dev/null +++ b/builtins/fc.def @@ -0,0 +1,665 @@ +This file is fc.def, from which is created fc.c. +It implements the builtin "fc" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES fc.c + +$BUILTIN fc +$FUNCTION fc_builtin +$DEPENDS_ON HISTORY +$SHORT_DOC fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command] +Display or execute commands from the history list. + +fc is used to list or edit and re-execute commands from the history list. +FIRST and LAST can be numbers specifying the range, or FIRST can be a +string, which means the most recent command beginning with that +string. + +Options: + -e ENAME select which editor to use. Default is FCEDIT, then EDITOR, + then vi + -l list lines instead of editing + -n omit line numbers when listing + -r reverse the order of the lines (newest listed first) + +With the `fc -s [pat=rep ...] [command]' format, COMMAND is +re-executed after the substitution OLD=NEW is performed. + +A useful alias to use with this is r='fc -s', so that typing `r cc' +runs the last command beginning with `cc' and typing `r' re-executes +the last command. + +Exit Status: +Returns success or status of executed command; non-zero if an error occurs. +$END + +#include <config.h> + +#if defined (HISTORY) +#ifndef _MINIX +# include <sys/param.h> +#endif +#include "../bashtypes.h" +#include "posixstat.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <chartypes.h> + +#include "../bashansi.h" +#include "../bashintl.h" +#include <errno.h> + +#include "../shell.h" +#include "../builtins.h" +#include "../flags.h" +#include "../bashhist.h" +#include "maxpath.h" +#include <readline/history.h> +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int current_command_line_count; +extern int literal_history; +extern int posixly_correct; +extern int subshell_environment, interactive_shell; + +extern int unlink __P((const char *)); + +extern FILE *sh_mktmpfp __P((char *, int, char **)); + +/* **************************************************************** */ +/* */ +/* The K*rn shell style fc command (Fix Command) */ +/* */ +/* **************************************************************** */ + +/* fc builtin command (fix command) for Bash for those who + like K*rn-style history better than csh-style. + + fc [-e ename] [-nlr] [first] [last] + + FIRST and LAST can be numbers specifying the range, or FIRST can be + a string, which means the most recent command beginning with that + string. + + -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, + then the editor which corresponds to the current readline editing + mode, then vi. + + -l means list lines instead of editing. + -n means no line numbers listed. + -r means reverse the order of the lines (making it newest listed first). + + fc -e - [pat=rep ...] [command] + fc -s [pat=rep ...] [command] + + Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's. +*/ + +/* Data structure describing a list of global replacements to perform. */ +typedef struct repl { + struct repl *next; + char *pat; + char *rep; +} REPL; + +/* Accessors for HIST_ENTRY lists that are called HLIST. */ +#define histline(i) (hlist[(i)]->line) +#define histdata(i) (hlist[(i)]->data) + +#define FREE_RLIST() \ + do { \ + for (rl = rlist; rl; ) { \ + REPL *r; \ + r = rl->next; \ + if (rl->pat) \ + free (rl->pat); \ + if (rl->rep) \ + free (rl->rep); \ + free (rl); \ + rl = r; \ + } \ + } while (0) + +static char *fc_dosubs __P((char *, REPL *)); +static char *fc_gethist __P((char *, HIST_ENTRY **)); +static int fc_gethnum __P((char *, HIST_ENTRY **)); +static int fc_number __P((WORD_LIST *)); +static void fc_replhist __P((char *)); +#ifdef INCLUDE_UNUSED +static char *fc_readline __P((FILE *)); +static void fc_addhist __P((char *)); +#endif + +/* String to execute on a file that we want to edit. */ +#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}" +#if defined (STRICT_POSIX) +# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}" +#else +# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}" +#endif + +int +fc_builtin (list) + WORD_LIST *list; +{ + register int i; + register char *sep; + int numbering, reverse, listing, execute; + int histbeg, histend, last_hist, retval, opt, rh; + FILE *stream; + REPL *rlist, *rl; + char *ename, *command, *newcom, *fcedit; + HIST_ENTRY **hlist; + char *fn; + + numbering = 1; + reverse = listing = execute = 0; + ename = (char *)NULL; + + /* Parse out the options and set which of the two forms we're in. */ + reset_internal_getopt (); + lcurrent = list; /* XXX */ + while (fc_number (loptend = lcurrent) == 0 && + (opt = internal_getopt (list, ":e:lnrs")) != -1) + { + switch (opt) + { + case 'n': + numbering = 0; + break; + + case 'l': + listing = 1; + break; + + case 'r': + reverse = 1; + break; + + case 's': + execute = 1; + break; + + case 'e': + ename = list_optarg; + break; + + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (ename && (*ename == '-') && (ename[1] == '\0')) + execute = 1; + + /* The "execute" form of the command (re-run, with possible string + substitutions). */ + if (execute) + { + rlist = (REPL *)NULL; + while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL)) + { + *sep++ = '\0'; + rl = (REPL *)xmalloc (sizeof (REPL)); + rl->next = (REPL *)NULL; + rl->pat = savestring (list->word->word); + rl->rep = savestring (sep); + + if (rlist == NULL) + rlist = rl; + else + { + rl->next = rlist; + rlist = rl; + } + list = list->next; + } + + /* If we have a list of substitutions to do, then reverse it + to get the replacements in the proper order. */ + + rlist = REVERSE_LIST (rlist, REPL *); + + hlist = history_list (); + + /* If we still have something in list, it is a command spec. + Otherwise, we use the most recent command in time. */ + command = fc_gethist (list ? list->word->word : (char *)NULL, hlist); + + if (command == NULL) + { + builtin_error (_("no command found")); + if (rlist) + FREE_RLIST (); + + return (EXECUTION_FAILURE); + } + + if (rlist) + { + newcom = fc_dosubs (command, rlist); + free (command); + FREE_RLIST (); + command = newcom; + } + + fprintf (stderr, "%s\n", command); + fc_replhist (command); /* replace `fc -s' with command */ + /* Posix says that the re-executed commands should be entered into the + history. */ + return (parse_and_execute (command, "fc", SEVAL_NOHIST)); + } + + /* This is the second form of the command (the list-or-edit-and-rerun + form). */ + hlist = history_list (); + if (hlist == 0) + return (EXECUTION_SUCCESS); + for (i = 0; hlist[i]; i++); + + /* With the Bash implementation of history, the current command line + ("fc blah..." and so on) is already part of the history list by + the time we get to this point. This just skips over that command + and makes the last command that this deals with be the last command + the user entered before the fc. We need to check whether the + line was actually added (HISTIGNORE may have caused it to not be), + so we check hist_last_line_added. */ + + /* Even though command substitution through parse_and_execute turns off + remember_on_history, command substitution in a shell when set -o history + has been enabled (interactive or not) should use it in the last_hist + calculation as if it were on. */ + rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list); + last_hist = i - rh - hist_last_line_added; + + if (list) + { + histbeg = fc_gethnum (list->word->word, hlist); + list = list->next; + + if (list) + histend = fc_gethnum (list->word->word, hlist); + else + histend = listing ? last_hist : histbeg; + } + else + { + /* The default for listing is the last 16 history items. */ + if (listing) + { + histend = last_hist; + histbeg = histend - 16 + 1; /* +1 because loop below uses >= */ + if (histbeg < 0) + histbeg = 0; + } + else + /* For editing, it is the last history command. */ + histbeg = histend = last_hist; + } + + /* "When not listing, the fc command that caused the editing shall not be + entered into the history list." */ + if (listing == 0 && hist_last_line_added) + { + bash_delete_last_history (); + /* If we're editing a single command -- the last command in the + history -- and we just removed the dummy command added by + edit_and_execute_command (), we need to check whether or not we + just removed the last command in the history and need to back + the pointer up. remember_on_history is off because we're running + in parse_and_execute(). */ + if (histbeg == histend && histend == last_hist && hlist[last_hist] == 0) + last_hist = histbeg = --histend; + } + + /* We print error messages for line specifications out of range. */ + if ((histbeg < 0) || (histend < 0)) + { + sh_erange ((char *)NULL, _("history specification")); + return (EXECUTION_FAILURE); + } + + if (histend < histbeg) + { + i = histend; + histend = histbeg; + histbeg = i; + + reverse = 1; + } + + if (listing) + stream = stdout; + else + { + numbering = 0; + stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn); + if (stream == 0) + { + builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno)); + FREE (fn); + return (EXECUTION_FAILURE); + } + } + + for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++) + { + QUIT; + if (numbering) + fprintf (stream, "%d", i + history_base); + if (listing) + { + if (posixly_correct) + fputs ("\t", stream); + else + fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); + } + fprintf (stream, "%s\n", histline (i)); + } + + if (listing) + return (sh_chkwrite (EXECUTION_SUCCESS)); + + fflush (stream); + if (ferror (stream)) + { + sh_wrerror (); + fclose (stream); + return (EXECUTION_FAILURE); + } + fclose (stream); + + /* Now edit the file of commands. */ + if (ename) + { + command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2); + sprintf (command, "%s %s", ename, fn); + } + else + { + fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND; + command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn)); + sprintf (command, "%s %s", fcedit, fn); + } + retval = parse_and_execute (command, "fc", SEVAL_NOHIST); + if (retval != EXECUTION_SUCCESS) + { + unlink (fn); + free (fn); + return (EXECUTION_FAILURE); + } + + /* Make sure parse_and_execute doesn't turn this off, even though a + call to parse_and_execute farther up the function call stack (e.g., + if this is called by vi_edit_and_execute_command) may have already + called bash_history_disable. */ + remember_on_history = 1; + + /* Turn on the `v' flag while fc_execute_file runs so the commands + will be echoed as they are read by the parser. */ + begin_unwind_frame ("fc builtin"); + add_unwind_protect ((Function *)xfree, fn); + add_unwind_protect (unlink, fn); + unwind_protect_int (echo_input_at_read); + echo_input_at_read = 1; + + retval = fc_execute_file (fn); + + run_unwind_frame ("fc builtin"); + + return (retval); +} + +/* Return 1 if LIST->word->word is a legal number for fc's use. */ +static int +fc_number (list) + WORD_LIST *list; +{ + char *s; + + if (list == 0) + return 0; + s = list->word->word; + if (*s == '-') + s++; + return (legal_number (s, (intmax_t *)NULL)); +} + +/* Return an absolute index into HLIST which corresponds to COMMAND. If + COMMAND is a number, then it was specified in relative terms. If it + is a string, then it is the start of a command line present in HLIST. */ +static int +fc_gethnum (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int sign, n, clen, rh; + register int i, j; + register char *s; + + sign = 1; + /* Count history elements. */ + for (i = 0; hlist[i]; i++); + + /* With the Bash implementation of history, the current command line + ("fc blah..." and so on) is already part of the history list by + the time we get to this point. This just skips over that command + and makes the last command that this deals with be the last command + the user entered before the fc. We need to check whether the + line was actually added (HISTIGNORE may have caused it to not be), + so we check hist_last_line_added. This needs to agree with the + calculation of last_hist in fc_builtin above. */ + /* Even though command substitution through parse_and_execute turns off + remember_on_history, command substitution in a shell when set -o history + has been enabled (interactive or not) should use it in the last_hist + calculation as if it were on. */ + rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list); + i -= rh + hist_last_line_added; + + /* No specification defaults to most recent command. */ + if (command == NULL) + return (i); + + /* Otherwise, there is a specification. It can be a number relative to + the current position, or an absolute history number. */ + s = command; + + /* Handle possible leading minus sign. */ + if (s && (*s == '-')) + { + sign = -1; + s++; + } + + if (s && DIGIT(*s)) + { + n = atoi (s); + n *= sign; + + /* If the value is negative or zero, then it is an offset from + the current history item. */ + if (n < 0) + { + n += i + 1; + return (n < 0 ? 0 : n); + } + else if (n == 0) + return (i); + else + { + n -= history_base; + return (i < n ? i : n); + } + } + + clen = strlen (command); + for (j = i; j >= 0; j--) + { + if (STREQN (command, histline (j), clen)) + return (j); + } + return (-1); +} + +/* Locate the most recent history line which begins with + COMMAND in HLIST, and return a malloc()'ed copy of it. */ +static char * +fc_gethist (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int i; + + if (hlist == 0) + return ((char *)NULL); + + i = fc_gethnum (command, hlist); + + if (i >= 0) + return (savestring (histline (i))); + else + return ((char *)NULL); +} + +#ifdef INCLUDE_UNUSED +/* Read the edited history lines from STREAM and return them + one at a time. This can read unlimited length lines. The + caller should free the storage. */ +static char * +fc_readline (stream) + FILE *stream; +{ + register int c; + int line_len = 0, lindex = 0; + char *line = (char *)NULL; + + while ((c = getc (stream)) != EOF) + { + if ((lindex + 2) >= line_len) + line = (char *)xrealloc (line, (line_len += 128)); + + if (c == '\n') + { + line[lindex++] = '\n'; + line[lindex++] = '\0'; + return (line); + } + else + line[lindex++] = c; + } + + if (!lindex) + { + if (line) + free (line); + + return ((char *)NULL); + } + + if (lindex + 2 >= line_len) + line = (char *)xrealloc (line, lindex + 3); + + line[lindex++] = '\n'; /* Finish with newline if none in file */ + line[lindex++] = '\0'; + return (line); +} +#endif + +/* Perform the SUBS on COMMAND. + SUBS is a list of substitutions, and COMMAND is a simple string. + Return a pointer to a malloc'ed string which contains the substituted + command. */ +static char * +fc_dosubs (command, subs) + char *command; + REPL *subs; +{ + register char *new, *t; + register REPL *r; + + for (new = savestring (command), r = subs; r; r = r->next) + { + t = strsub (new, r->pat, r->rep, 1); + free (new); + new = t; + } + return (new); +} + +/* Use `command' to replace the last entry in the history list, which, + by this time, is `fc blah...'. The intent is that the new command + become the history entry, and that `fc' should never appear in the + history list. This way you can do `r' to your heart's content. */ +static void +fc_replhist (command) + char *command; +{ + int n; + + if (command == 0 || *command == '\0') + return; + + n = strlen (command); + if (command[n - 1] == '\n') + command[n - 1] = '\0'; + + if (command && *command) + { + bash_delete_last_history (); + maybe_add_history (command); /* Obeys HISTCONTROL setting. */ + } +} + +#ifdef INCLUDE_UNUSED +/* Add LINE to the history, after removing a single trailing newline. */ +static void +fc_addhist (line) + char *line; +{ + register int n; + + if (line == 0 || *line == 0) + return; + + n = strlen (line); + + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + if (line && *line) + maybe_add_history (line); /* Obeys HISTCONTROL setting. */ +} +#endif + +#endif /* HISTORY */ diff --git a/builtins/fg_bg.def b/builtins/fg_bg.def new file mode 100644 index 0000000..ae7e904 --- /dev/null +++ b/builtins/fg_bg.def @@ -0,0 +1,186 @@ +This file is fg_bg.def, from which is created fg_bg.c. +It implements the builtins "bg" and "fg" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES fg_bg.c + +$BUILTIN fg +$FUNCTION fg_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC fg [job_spec] +Move job to the foreground. + +Place the job identified by JOB_SPEC in the foreground, making it the +current job. If JOB_SPEC is not present, the shell's notion of the +current job is used. + +Exit Status: +Status of command placed in foreground, or failure if an error occurs. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" + +#if defined (JOB_CONTROL) +extern char *this_command_name; + +static int fg_bg __P((WORD_LIST *, int)); + +/* How to bring a job into the foreground. */ +int +fg_builtin (list) + WORD_LIST *list; +{ + int fg_bit; + register WORD_LIST *t; + + if (job_control == 0) + { + sh_nojobs ((char *)NULL); + return (EXECUTION_FAILURE); + } + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + /* If the last arg on the line is '&', then start this job in the + background. Else, fg the job. */ + for (t = list; t && t->next; t = t->next) + ; + fg_bit = (t && t->word->word[0] == '&' && t->word->word[1] == '\0') == 0; + + return (fg_bg (list, fg_bit)); +} +#endif /* JOB_CONTROL */ + +$BUILTIN bg +$FUNCTION bg_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC bg [job_spec ...] +Move jobs to the background. + +Place the jobs identified by each JOB_SPEC in the background, as if they +had been started with `&'. If JOB_SPEC is not present, the shell's notion +of the current job is used. + +Exit Status: +Returns success unless job control is not enabled or an error occurs. +$END + +#if defined (JOB_CONTROL) +/* How to put a job into the background. */ +int +bg_builtin (list) + WORD_LIST *list; +{ + int r; + + if (job_control == 0) + { + sh_nojobs ((char *)NULL); + return (EXECUTION_FAILURE); + } + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + /* This relies on the fact that fg_bg() takes a WORD_LIST *, but only acts + on the first member (if any) of that list. */ + r = EXECUTION_SUCCESS; + do + { + if (fg_bg (list, 0) == EXECUTION_FAILURE) + r = EXECUTION_FAILURE; + if (list) + list = list->next; + } + while (list); + + return r; +} + +/* How to put a job into the foreground/background. */ +static int +fg_bg (list, foreground) + WORD_LIST *list; + int foreground; +{ + sigset_t set, oset; + int job, status, old_async_pid; + JOB *j; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (INVALID_JOB (job)) + { + if (job != DUP_JOB) + sh_badjob (list ? list->word->word : _("current")); + + goto failure; + } + + j = get_job_by_jid (job); + /* Or if j->pgrp == shell_pgrp. */ + if (IS_JOBCONTROL (job) == 0) + { + builtin_error (_("job %d started without job control"), job + 1); + goto failure; + } + + if (foreground == 0) + { + old_async_pid = last_asynchronous_pid; + last_asynchronous_pid = j->pgrp; /* As per Posix.2 5.4.2 */ + } + + status = start_job (job, foreground); + + if (status >= 0) + { + /* win: */ + UNBLOCK_CHILD (oset); + return (foreground ? status : EXECUTION_SUCCESS); + } + else + { + if (foreground == 0) + last_asynchronous_pid = old_async_pid; + + failure: + UNBLOCK_CHILD (oset); + return (EXECUTION_FAILURE); + } +} +#endif /* JOB_CONTROL */ diff --git a/builtins/getopt.c b/builtins/getopt.c new file mode 100644 index 0000000..feb18b5 --- /dev/null +++ b/builtins/getopt.c @@ -0,0 +1,309 @@ +/* getopt.c - getopt for Bash. Used by the getopt builtin. */ + +/* Copyright (C) 1993-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include "memalloc.h" +#include "../bashintl.h" +#include "../shell.h" +#include "getopt.h" + +/* For communication from `sh_getopt' to the caller. + When `sh_getopt' finds an option that takes an argument, + the argument value is returned here. */ +char *sh_optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `sh_getopt'. + + On entry to `sh_getopt', zero means this is the first call; initialize. + + When `sh_getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `sh_optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int sh_optind = 0; + +/* Index of the current argument. */ +static int sh_curopt; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; +static int sh_charindex; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int sh_opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int sh_optopt = '?'; + +/* Set to 1 when we see an invalid option; public so getopts can reset it. */ +int sh_badopt = 0; + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `sh_getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `sh_getopt' finds another option character, it returns that character, + updating `sh_optind' and `nextchar' so that the next call to `sh_getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `sh_getopt' returns `EOF'. + Then `sh_optind' is the index in ARGV of the first ARGV-element + that is not an option. + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `sh_opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `sh_optarg'. */ + +/* 1003.2 specifies the format of this message. */ +#define BADOPT(x) fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], x) +#define NEEDARG(x) fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], x) + +int +sh_getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + char c, *temp; + + sh_optarg = 0; + + if (sh_optind >= argc || sh_optind < 0) /* XXX was sh_optind > argc */ + { + sh_optind = argc; + return (EOF); + } + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (sh_optind == 0) + { + sh_optind = 1; + nextchar = (char *)NULL; + } + + if (nextchar == 0 || *nextchar == '\0') + { + /* If we have done all the ARGV-elements, stop the scan. */ + if (sh_optind >= argc) + return EOF; + + temp = argv[sh_optind]; + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, and return EOF. */ + if (temp[0] == '-' && temp[1] == '-' && temp[2] == '\0') + { + sh_optind++; + return EOF; + } + + /* If we have come to a non-option, either stop the scan or describe + it to the caller and pass it by. This makes the pseudo-option + `-' mean the end of options, but does not skip over it. */ + if (temp[0] != '-' || temp[1] == '\0') + return EOF; + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + nextchar = argv[sh_curopt = sh_optind] + 1; + sh_charindex = 1; + } + + /* Look at and handle the next option-character. */ + + c = *nextchar++; sh_charindex++; + temp = strchr (optstring, c); + + sh_optopt = c; + + /* Increment `sh_optind' when we start to process its last character. */ + if (nextchar == 0 || *nextchar == '\0') + { + sh_optind++; + nextchar = (char *)NULL; + } + + if (sh_badopt = (temp == NULL || c == ':')) + { + if (sh_opterr) + BADOPT (c); + + return '?'; + } + + if (temp[1] == ':') + { + if (nextchar && *nextchar) + { + /* This is an option that requires an argument. */ + sh_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + sh_optind++; + } + else if (sh_optind == argc) + { + if (sh_opterr) + NEEDARG (c); + + sh_optopt = c; + sh_optarg = ""; /* Needed by getopts. */ + c = (optstring[0] == ':') ? ':' : '?'; + } + else + /* We already incremented `sh_optind' once; + increment it again when taking next ARGV-elt as argument. */ + sh_optarg = argv[sh_optind++]; + nextchar = (char *)NULL; + } + return c; +} + +void +sh_getopt_restore_state (argv) + char **argv; +{ + if (nextchar) + nextchar = argv[sh_curopt] + sh_charindex; +} + +#if 0 +void +sh_getopt_debug_restore_state (argv) + char **argv; +{ + if (nextchar && nextchar != argv[sh_curopt] + sh_charindex) + { + itrace("sh_getopt_debug_restore_state: resetting nextchar"); + nextchar = argv[sh_curopt] + sh_charindex; + } +} +#endif + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `sh_getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_sh_optind = 0; + + while (1) + { + int this_option_sh_optind = sh_optind ? sh_optind : 1; + + c = sh_getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_sh_optind != 0 && digit_sh_optind != this_option_sh_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_sh_optind = this_option_sh_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", sh_optarg); + break; + + case '?': + break; + + default: + printf ("?? sh_getopt returned character code 0%o ??\n", c); + } + } + + if (sh_optind < argc) + { + printf ("non-option ARGV-elements: "); + while (sh_optind < argc) + printf ("%s ", argv[sh_optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/builtins/getopt.h b/builtins/getopt.h new file mode 100644 index 0000000..75fdec5 --- /dev/null +++ b/builtins/getopt.h @@ -0,0 +1,65 @@ +/* getopt.h - declarations for getopt. */ + +/* Copyright (C) 1989, 1990, 1991, 1992, 1993, 2008,2009 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/>. +*/ + +/* XXX THIS HAS BEEN MODIFIED FOR INCORPORATION INTO BASH XXX */ + +#ifndef _SH_GETOPT_H +#define _SH_GETOPT_H 1 + +#include "stdc.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *sh_optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `sh_optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int sh_optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int sh_opterr; + +/* Set to an option character which was unrecognized. */ + +extern int sh_optopt; + +/* Set to 1 when an unrecognized option is encountered. */ +extern int sh_badopt; + +extern int sh_getopt __P((int, char *const *, const char *)); +extern void sh_getopt_restore_state __P((char **)); + +#endif /* _SH_GETOPT_H */ diff --git a/builtins/getopts.def b/builtins/getopts.def new file mode 100644 index 0000000..c077c8e --- /dev/null +++ b/builtins/getopts.def @@ -0,0 +1,329 @@ +This file is getopts.def, from which is created getopts.c. +It implements the builtin "getopts" in Bash. + +Copyright (C) 1987-2004 Free Software Foundation, Inc. + +This file is part of GNU Bash, the Bourne Again SHell. + +Bash is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Bash is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Bash. If not, see <http://www.gnu.org/licenses/>. + +$PRODUCES getopts.c + +$BUILTIN getopts +$FUNCTION getopts_builtin +$SHORT_DOC getopts optstring name [arg] +Parse option arguments. + +Getopts is used by shell procedures to parse positional parameters +as options. + +OPTSTRING contains the option letters to be recognized; if a letter +is followed by a colon, the option is expected to have an argument, +which should be separated from it by white space. + +Each time it is invoked, getopts will place the next option in the +shell variable $name, initializing name if it does not exist, and +the index of the next argument to be processed into the shell +variable OPTIND. OPTIND is initialized to 1 each time the shell or +a shell script is invoked. When an option requires an argument, +getopts places that argument into the shell variable OPTARG. + +getopts reports errors in one of two ways. If the first character +of OPTSTRING is a colon, getopts uses silent error reporting. In +this mode, no error messages are printed. If an invalid option is +seen, getopts places the option character found into OPTARG. If a +required argument is not found, getopts places a ':' into NAME and +sets OPTARG to the option character found. If getopts is not in +silent mode, and an invalid option is seen, getopts places '?' into +NAME and unsets OPTARG. If a required argument is not found, a '?' +is placed in NAME, OPTARG is unset, and a diagnostic message is +printed. + +If the shell variable OPTERR has the value 0, getopts disables the +printing of error messages, even if the first character of +OPTSTRING is not a colon. OPTERR has the value 1 by default. + +Getopts normally parses the positional parameters ($0 - $9), but if +more arguments are given, they are parsed instead. + +Exit Status: +Returns success if an option is found; fails if the end of options is +encountered or an error occurs. +$END + +#include <config.h> + +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" +#include "getopt.h" + +#define G_EOF -1 +#define G_INVALID_OPT -2 +#define G_ARG_MISSING -3 + +extern char *this_command_name; + +static int getopts_bind_variable __P((char *, char *)); +static int dogetopts __P((int, char **)); + +/* getopts_reset is magic code for when OPTIND is reset. N is the + value that has just been assigned to OPTIND. */ +void +getopts_reset (newind) + int newind; +{ + sh_optind = newind; + sh_badopt = 0; +} + +static int +getopts_bind_variable (name, value) + char *name, *value; +{ + SHELL_VAR *v; + + if (legal_identifier (name)) + { + v = bind_variable (name, value, 0); + return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; + } + else + { + sh_invalidid (name); + return (EXECUTION_FAILURE); + } +} + +/* Error handling is now performed as specified by Posix.2, draft 11 + (identical to that of ksh-88). The special handling is enabled if + the first character of the option string is a colon; this handling + disables diagnostic messages concerning missing option arguments + and invalid option characters. The handling is as follows. + + INVALID OPTIONS: + name -> "?" + if (special_error) then + OPTARG = option character found + no error output + else + OPTARG unset + diagnostic message + fi + + MISSING OPTION ARGUMENT; + if (special_error) then + name -> ":" + OPTARG = option character found + else + name -> "?" + OPTARG unset + diagnostic message + fi + */ + +static int +dogetopts (argc, argv) + int argc; + char **argv; +{ + int ret, special_error, old_opterr, i, n; + char strval[2], numval[16]; + char *optstr; /* list of options */ + char *name; /* variable to get flag val */ + char *t; + + if (argc < 3) + { + builtin_usage (); + return (EX_USAGE); + } + + /* argv[0] is "getopts". */ + + optstr = argv[1]; + name = argv[2]; + argc -= 2; + argv += 2; + + special_error = optstr[0] == ':'; + + if (special_error) + { + old_opterr = sh_opterr; + optstr++; + sh_opterr = 0; /* suppress diagnostic messages */ + } + + if (argc > 1) + { + sh_getopt_restore_state (argv); + t = argv[0]; + argv[0] = dollar_vars[0]; + ret = sh_getopt (argc, argv, optstr); + argv[0] = t; + } + else if (rest_of_args == (WORD_LIST *)NULL) + { + for (i = 0; i < 10 && dollar_vars[i]; i++) + ; + + sh_getopt_restore_state (dollar_vars); + ret = sh_getopt (i, dollar_vars, optstr); + } + else + { + register WORD_LIST *words; + char **v; + + for (i = 0; i < 10 && dollar_vars[i]; i++) + ; + for (words = rest_of_args; words; words = words->next, i++) + ; + v = strvec_create (i + 1); + for (i = 0; i < 10 && dollar_vars[i]; i++) + v[i] = dollar_vars[i]; + for (words = rest_of_args; words; words = words->next, i++) + v[i] = words->word->word; + v[i] = (char *)NULL; + sh_getopt_restore_state (v); + ret = sh_getopt (i, v, optstr); + free (v); + } + + if (special_error) + sh_opterr = old_opterr; + + /* Set the OPTIND variable in any case, to handle "--" skipping. It's + highly unlikely that 14 digits will be too few. */ + if (sh_optind < 10) + { + numval[14] = sh_optind + '0'; + numval[15] = '\0'; + i = 14; + } + else + { + numval[i = 15] = '\0'; + n = sh_optind; + do + { + numval[--i] = (n % 10) + '0'; + } + while (n /= 10); + } + bind_variable ("OPTIND", numval + i, 0); + + /* If an error occurred, decide which one it is and set the return + code appropriately. In all cases, the option character in error + is in OPTOPT. If an invalid option was encountered, OPTARG is + NULL. If a required option argument was missing, OPTARG points + to a NULL string (that is, sh_optarg[0] == 0). */ + if (ret == '?') + { + if (sh_optarg == NULL) + ret = G_INVALID_OPT; + else if (sh_optarg[0] == '\0') + ret = G_ARG_MISSING; + } + + if (ret == G_EOF) + { + unbind_variable ("OPTARG"); + getopts_bind_variable (name, "?"); + return (EXECUTION_FAILURE); + } + + if (ret == G_INVALID_OPT) + { + /* Invalid option encountered. */ + ret = getopts_bind_variable (name, "?"); + + if (special_error) + { + strval[0] = (char)sh_optopt; + strval[1] = '\0'; + bind_variable ("OPTARG", strval, 0); + } + else + unbind_variable ("OPTARG"); + + return (ret); + } + + if (ret == G_ARG_MISSING) + { + /* Required argument missing. */ + if (special_error) + { + ret = getopts_bind_variable (name, ":"); + + strval[0] = (char)sh_optopt; + strval[1] = '\0'; + bind_variable ("OPTARG", strval, 0); + } + else + { + ret = getopts_bind_variable (name, "?"); + unbind_variable ("OPTARG"); + } + return (ret); + } + + bind_variable ("OPTARG", sh_optarg, 0); + + strval[0] = (char) ret; + strval[1] = '\0'; + return (getopts_bind_variable (name, strval)); +} + +/* The getopts builtin. Build an argv, and call dogetopts with it. */ +int +getopts_builtin (list) + WORD_LIST *list; +{ + char **av; + int ac, ret; + + if (list == 0) + { + builtin_usage (); + return EX_USAGE; + } + + reset_internal_getopt (); + if (internal_getopt (list, "") != -1) + { + builtin_usage (); + return (EX_USAGE); + } + list = loptend; + + av = make_builtin_argv (list, &ac); + ret = dogetopts (ac, av); + free ((char *)av); + + return (ret); +} diff --git a/builtins/hash.def b/builtins/hash.def new file mode 100644 index 0000000..7a8aced --- /dev/null +++ b/builtins/hash.def @@ -0,0 +1,281 @@ +This file is hash.def, from which is created hash.c. +It implements the builtin "hash" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES hash.c + +$BUILTIN hash +$FUNCTION hash_builtin +$SHORT_DOC hash [-lr] [-p pathname] [-dt] [name ...] +Remember or display program locations. + +Determine and remember the full pathname of each command NAME. If +no arguments are given, information about remembered commands is displayed. + +Options: + -d forget the remembered location of each NAME + -l display in a format that may be reused as input + -p pathname use PATHNAME is the full pathname of NAME + -r forget all remembered locations + -t print the remembered location of each NAME, preceding + each location with the corresponding NAME if multiple + NAMEs are given +Arguments: + NAME Each NAME is searched for in $PATH and added to the list + of remembered commands. + +Exit Status: +Returns success unless NAME is not found or an invalid option is given. +$END + +#include <config.h> + +#include <stdio.h> + +#include "../bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <errno.h> + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../builtins.h" +#include "../flags.h" +#include "../findcmd.h" +#include "../hashcmd.h" +#include "common.h" +#include "bashgetopt.h" + +extern int posixly_correct; +extern int dot_found_in_search; +extern char *this_command_name; + +static int add_hashed_command __P((char *, int)); +static int print_hash_info __P((BUCKET_CONTENTS *)); +static int print_portable_hash_info __P((BUCKET_CONTENTS *)); +static int print_hashed_commands __P((int)); +static int list_hashed_filename_targets __P((WORD_LIST *, int)); + +/* Print statistics on the current state of hashed commands. If LIST is + not empty, then rehash (or hash in the first place) the specified + commands. */ +int +hash_builtin (list) + WORD_LIST *list; +{ + int expunge_hash_table, list_targets, list_portably, delete, opt; + char *w, *pathname; + + if (hashing_enabled == 0) + { + builtin_error (_("hashing disabled")); + return (EXECUTION_FAILURE); + } + + expunge_hash_table = list_targets = list_portably = delete = 0; + pathname = (char *)NULL; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "dlp:rt")) != -1) + { + switch (opt) + { + case 'd': + delete = 1; + break; + case 'l': + list_portably = 1; + break; + case 'p': + pathname = list_optarg; + break; + case 'r': + expunge_hash_table = 1; + break; + case 't': + list_targets = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* hash -t requires at least one argument. */ + if (list == 0 && list_targets) + { + sh_needarg ("-t"); + return (EXECUTION_FAILURE); + } + + /* We want hash -r to be silent, but hash -- to print hashing info, so + we test expunge_hash_table. */ + if (list == 0 && expunge_hash_table == 0) + { + opt = print_hashed_commands (list_portably); + if (opt == 0 && posixly_correct == 0) + printf (_("%s: hash table empty\n"), this_command_name); + + return (EXECUTION_SUCCESS); + } + + if (expunge_hash_table) + phash_flush (); + + /* If someone runs `hash -r -t xyz' he will be disappointed. */ + if (list_targets) + return (list_hashed_filename_targets (list, list_portably)); + +#if defined (RESTRICTED_SHELL) + if (restricted && pathname && strchr (pathname, '/')) + { + sh_restricted (pathname); + return (EXECUTION_FAILURE); + } +#endif + + for (opt = EXECUTION_SUCCESS; list; list = list->next) + { + /* Add, remove or rehash the specified commands. */ + w = list->word->word; + if (pathname) + { + if (is_directory (pathname)) + { +#ifdef EISDIR + builtin_error ("%s: %s", pathname, strerror (EISDIR)); +#else + builtin_error (_("%s: is a directory"), pathname); +#endif + opt = EXECUTION_FAILURE; + } + else + phash_insert (w, pathname, 0, 0); + } + else if (absolute_program (w)) + continue; + else if (delete) + { + if (phash_remove (w)) + { + sh_notfound (w); + opt = EXECUTION_FAILURE; + } + } + else if (add_hashed_command (w, 0)) + opt = EXECUTION_FAILURE; + } + + fflush (stdout); + return (opt); +} + +static int +add_hashed_command (w, quiet) + char *w; + int quiet; +{ + int rv; + char *full_path; + + rv = 0; + if (find_function (w) == 0 && find_shell_builtin (w) == 0) + { + full_path = find_user_command (w); + if (full_path && executable_file (full_path)) + phash_insert (w, full_path, dot_found_in_search, 0); + else + { + if (quiet == 0) + sh_notfound (w); + rv++; + } + FREE (full_path); + } + return (rv); +} + +/* Print information about current hashed info. */ +static int +print_hash_info (item) + BUCKET_CONTENTS *item; +{ + printf ("%4d\t%s\n", item->times_found, pathdata(item)->path); + return 0; +} + +static int +print_portable_hash_info (item) + BUCKET_CONTENTS *item; +{ + printf ("builtin hash -p %s %s\n", pathdata(item)->path, item->key); + return 0; +} + +static int +print_hashed_commands (fmt) + int fmt; +{ + if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) + return (0); + + if (fmt == 0) + printf (_("hits\tcommand\n")); + hash_walk (hashed_filenames, fmt ? print_portable_hash_info : print_hash_info); + return (1); +} + +static int +list_hashed_filename_targets (list, fmt) + WORD_LIST *list; + int fmt; +{ + int all_found, multiple; + char *target; + WORD_LIST *l; + + all_found = 1; + multiple = list->next != 0; + + for (l = list; l; l = l->next) + { + target = phash_search (l->word->word); + if (target == 0) + { + all_found = 0; + sh_notfound (l->word->word); + continue; + } + if (fmt) + printf ("builtin hash -p %s %s\n", target, l->word->word); + else + { + if (multiple) + printf ("%s\t", l->word->word); + printf ("%s\n", target); + } + } + + return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} diff --git a/builtins/help.def b/builtins/help.def new file mode 100644 index 0000000..f857af9 --- /dev/null +++ b/builtins/help.def @@ -0,0 +1,385 @@ +This file is help.def, from which is created help.c. +It implements the builtin "help" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES help.c + +$BUILTIN help +$FUNCTION help_builtin +$DEPENDS_ON HELP_BUILTIN +$SHORT_DOC help [-dms] [pattern ...] +Display information about builtin commands. + +Displays brief summaries of builtin commands. If PATTERN is +specified, gives detailed help on all commands matching PATTERN, +otherwise the list of help topics is printed. + +Options: + -d output short description for each topic + -m display usage in pseudo-manpage format + -s output only a short usage synopsis for each topic matching + PATTERN + +Arguments: + PATTERN Pattern specifiying a help topic + +Exit Status: +Returns success unless PATTERN is not found or an invalid option is given. +$END + +#include <config.h> + +#if defined (HELP_BUILTIN) +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <errno.h> + +#include <filecntl.h> + +#include "../bashintl.h" + +#include "../shell.h" +#include "../builtins.h" +#include "../pathexp.h" +#include "common.h" +#include "bashgetopt.h" + +#include <glob/strmatch.h> +#include <glob/glob.h> + +#ifndef errno +extern int errno; +#endif + +extern const char * const bash_copyright; +extern const char * const bash_license; + +static void show_builtin_command_help __P((void)); +static int open_helpfile __P((char *)); +static void show_desc __P((char *, int)); +static void show_manpage __P((char *, int)); +static void show_longdoc __P((int)); + +/* Print out a list of the known functions in the shell, and what they do. + If LIST is supplied, print out the list which matches for each pattern + specified. */ +int +help_builtin (list) + WORD_LIST *list; +{ + register int i; + char *pattern, *name; + int plen, match_found, sflag, dflag, mflag; + + dflag = sflag = mflag = 0; + reset_internal_getopt (); + while ((i = internal_getopt (list, "dms")) != -1) + { + switch (i) + { + case 'd': + dflag = 1; + break; + case 'm': + mflag = 1; + break; + case 's': + sflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + show_shell_version (0); + show_builtin_command_help (); + return (EXECUTION_SUCCESS); + } + + /* We should consider making `help bash' do something. */ + + if (glob_pattern_p (list->word->word)) + { + printf (ngettext ("Shell commands matching keyword `", "Shell commands matching keywords `", (list->next ? 2 : 1))); + print_word_list (list, ", "); + printf ("'\n\n"); + } + + for (match_found = 0, pattern = ""; list; list = list->next) + { + pattern = list->word->word; + plen = strlen (pattern); + + for (i = 0; name = shell_builtins[i].name; i++) + { + QUIT; + if ((strncmp (pattern, name, plen) == 0) || + (strmatch (pattern, name, FNMATCH_EXTFLAG) != FNM_NOMATCH)) + { + match_found++; + if (dflag) + { + show_desc (name, i); + continue; + } + else if (mflag) + { + show_manpage (name, i); + continue; + } + + printf ("%s: %s\n", name, shell_builtins[i].short_doc); + + if (sflag == 0) + show_longdoc (i); + } + } + } + + if (match_found == 0) + { + builtin_error (_("no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'."), pattern, pattern, pattern); + return (EXECUTION_FAILURE); + } + + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +static int +open_helpfile (name) + char *name; +{ + int fd; + + fd = open (name, O_RDONLY); + if (fd == -1) + { + builtin_error (_("%s: cannot open: %s"), name, strerror (errno)); + return -1; + } + return fd; +} + +/* By convention, enforced by mkbuiltins.c, if separate help files are being + used, the long_doc array contains one string -- the full pathname of the + help file for this builtin. */ +static void +show_longdoc (i) + int i; +{ + register int j; + char * const *doc; + int fd; + + doc = shell_builtins[i].long_doc; + + if (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL) + { + fd = open_helpfile (doc[0]); + if (fd < 0) + return; + zcatfd (fd, 1, doc[0]); + close (fd); + } + else + for (j = 0; doc[j]; j++) + printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); +} + +static void +show_desc (name, i) + char *name; + int i; +{ + register int j; + char **doc, *line; + int fd, usefile; + + doc = (char **)shell_builtins[i].long_doc; + + usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); + if (usefile) + { + fd = open_helpfile (doc[0]); + if (fd < 0) + return; + zmapfd (fd, &line, doc[0]); + close (fd); + } + else + line = doc ? doc[0] : (char *)NULL; + + printf ("%s - ", name); + for (j = 0; line && line[j]; j++) + { + putchar (line[j]); + if (line[j] == '\n') + break; + } + + fflush (stdout); + + if (usefile) + free (line); +} + +/* Print builtin help in pseudo-manpage format. */ +static void +show_manpage (name, i) + char *name; + int i; +{ + register int j; + char **doc, *line; + int fd, usefile; + + doc = (char **)shell_builtins[i].long_doc; + + usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); + if (usefile) + { + fd = open_helpfile (doc[0]); + if (fd < 0) + return; + zmapfd (fd, &line, doc[0]); + close (fd); + } + else + line = doc ? _(doc[0]) : (char *)NULL; + + /* NAME */ + printf ("NAME\n"); + printf ("%*s%s - ", BASE_INDENT, " ", name); + for (j = 0; line && line[j]; j++) + { + putchar (line[j]); + if (line[j] == '\n') + break; + } + printf ("\n"); + + /* SYNOPSIS */ + printf ("SYNOPSIS\n"); + printf ("%*s%s\n\n", BASE_INDENT, " ", shell_builtins[i].short_doc); + + /* DESCRIPTION */ + printf ("DESCRIPTION\n"); + if (usefile == 0) + { + for (j = 0; doc[j]; j++) + printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); + } + else + { + for (j = 0; line && line[j]; j++) + { + putchar (line[j]); + if (line[j] == '\n') + printf ("%*s", BASE_INDENT, " "); + } + } + putchar ('\n'); + + /* SEE ALSO */ + printf ("SEE ALSO\n"); + printf ("%*sbash(1)\n\n", BASE_INDENT, " "); + + /* IMPLEMENTATION */ + printf ("IMPLEMENTATION\n"); + printf ("%*s", BASE_INDENT, " "); + show_shell_version (0); + printf ("%*s", BASE_INDENT, " "); + printf ("%s\n", _(bash_copyright)); + printf ("%*s", BASE_INDENT, " "); + printf ("%s\n", _(bash_license)); + + fflush (stdout); + if (usefile) + free (line); +} + +static void +show_builtin_command_help () +{ + int i, j; + int height, width; + char *t, blurb[128]; + + printf ( +_("These shell commands are defined internally. Type `help' to see this list.\n\ +Type `help name' to find out more about the function `name'.\n\ +Use `info bash' to find out more about the shell in general.\n\ +Use `man -k' or `info' to find out more about commands not in this list.\n\ +\n\ +A star (*) next to a name means that the command is disabled.\n\ +\n")); + + t = get_string_value ("COLUMNS"); + width = (t && *t) ? atoi (t) : 80; + if (width <= 0) + width = 80; + + width /= 2; + if (width > sizeof (blurb)) + width = sizeof (blurb); + if (width <= 3) + width = 40; + height = (num_shell_builtins + 1) / 2; /* number of rows */ + + for (i = 0; i < height; i++) + { + QUIT; + + /* first column */ + blurb[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*'; + strncpy (blurb + 1, shell_builtins[i].short_doc, width - 2); + blurb[width - 2] = '>'; /* indicate truncation */ + blurb[width - 1] = '\0'; + printf ("%s", blurb); + if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins)) + { + printf ("\n"); + break; + } + + /* two spaces */ + for (j = strlen (blurb); j < width; j++) + putc (' ', stdout); + + /* second column */ + blurb[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*'; + strncpy (blurb + 1, shell_builtins[i+height].short_doc, width - 3); + blurb[width - 3] = '>'; /* indicate truncation */ + blurb[width - 2] = '\0'; + printf ("%s\n", blurb); + } +} +#endif /* HELP_BUILTIN */ diff --git a/builtins/history.def b/builtins/history.def new file mode 100644 index 0000000..e8249e9 --- /dev/null +++ b/builtins/history.def @@ -0,0 +1,380 @@ +This file is history.def, from which is created history.c. +It implements the builtin "history" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES history.c + +$BUILTIN history +$FUNCTION history_builtin +$DEPENDS_ON HISTORY +$SHORT_DOC history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...] +Display or manipulate the history list. + +Display the history list with line numbers, prefixing each modified +entry with a `*'. An argument of N lists only the last N entries. + +Options: + -c clear the history list by deleting all of the entries + -d offset delete the history entry at offset OFFSET. + + -a append history lines from this session to the history file + -n read all history lines not already read from the history file + -r read the history file and append the contents to the history + list + -w write the current history to the history file + and append them to the history list + + -p perform history expansion on each ARG and display the result + without storing it in the history list + -s append the ARGs to the history list as a single entry + +If FILENAME is given, it is used as the history file. Otherwise, +if $HISTFILE has a value, that is used, else ~/.bash_history. + +If the $HISTTIMEFORMAT variable is set and not null, its value is used +as a format string for strftime(3) to print the time stamp associated +with each displayed history entry. No time stamps are printed otherwise. + +Exit Status: +Returns success unless an invalid option is given or an error occurs. +$END + +#include <config.h> + +#if defined (HISTORY) +#include "../bashtypes.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif +#include "posixstat.h" +#include "filecntl.h" +#include <errno.h> +#include <stdio.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../bashhist.h" +#include <readline/history.h> +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +extern int current_command_line_count; +extern int force_append_history; /* shopt -s histappend */ + +static char *histtime __P((HIST_ENTRY *, const char *)); +static int display_history __P((WORD_LIST *)); +static void push_history __P((WORD_LIST *)); +static int expand_and_print_history __P((WORD_LIST *)); + +#define AFLAG 0x01 +#define RFLAG 0x02 +#define WFLAG 0x04 +#define NFLAG 0x08 +#define SFLAG 0x10 +#define PFLAG 0x20 +#define CFLAG 0x40 +#define DFLAG 0x80 + +int +history_builtin (list) + WORD_LIST *list; +{ + int flags, opt, result, old_history_lines, obase; + char *filename, *delete_arg; + intmax_t delete_offset; + + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "acd:npsrw")) != -1) + { + switch (opt) + { + case 'a': + flags |= AFLAG; + break; + case 'c': + flags |= CFLAG; + break; + case 'n': + flags |= NFLAG; + break; + case 'r': + flags |= RFLAG; + break; + case 'w': + flags |= WFLAG; + break; + case 's': + flags |= SFLAG; + break; + case 'd': + flags |= DFLAG; + delete_arg = list_optarg; + break; + case 'p': +#if defined (BANG_HISTORY) + flags |= PFLAG; +#endif + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG); + if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG) + { + builtin_error (_("cannot use more than one of -anrw")); + return (EXECUTION_FAILURE); + } + + /* clear the history, but allow other arguments to add to it again. */ + if (flags & CFLAG) + { + bash_clear_history (); + if (list == 0) + return (EXECUTION_SUCCESS); + } + + if (flags & SFLAG) + { + if (list) + push_history (list); + return (EXECUTION_SUCCESS); + } +#if defined (BANG_HISTORY) + else if (flags & PFLAG) + { + if (list) + return (expand_and_print_history (list)); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } +#endif + else if (flags & DFLAG) + { + if ((legal_number (delete_arg, &delete_offset) == 0) + || (delete_offset < history_base) + || (delete_offset > (history_base + history_length))) + { + sh_erange (delete_arg, _("history position")); + return (EXECUTION_FAILURE); + } + opt = delete_offset; + result = bash_delete_histent (opt - history_base); + /* Since remove_history changes history_length, this can happen if + we delete the last history entry. */ + if (where_history () > history_length) + history_set_pos (history_length); + return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + } + else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0) + { + result = display_history (list); + return (sh_chkwrite (result)); + } + + filename = list ? list->word->word : get_string_value ("HISTFILE"); + result = EXECUTION_SUCCESS; + + if (flags & AFLAG) /* Append session's history to file. */ + result = maybe_append_history (filename); + else if (flags & WFLAG) /* Write entire history. */ + result = write_history (filename); + else if (flags & RFLAG) /* Read entire file. */ + result = read_history (filename); + else if (flags & NFLAG) /* Read `new' history from file. */ + { + /* Read all of the lines in the file that we haven't already read. */ + old_history_lines = history_lines_in_file; + obase = history_base; + + using_history (); + result = read_history_range (filename, history_lines_in_file, -1); + using_history (); + + history_lines_in_file = where_history (); + + /* If we're rewriting the history file at shell exit rather than just + appending the lines from this session to it, the question is whether + we reset history_lines_this_session to 0, losing any history entries + we had before we read the new entries from the history file, or + whether we count the new entries we just read from the file as + history lines added during this session. + Right now, we do the latter. This will cause these history entries + to be written to the history file along with any intermediate entries + we add when we do a `history -a', but the alternative is losing + them altogether. */ + if (force_append_history == 0) + history_lines_this_session += history_lines_in_file - old_history_lines + + history_base - obase; + } + + return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +/* Accessors for HIST_ENTRY lists that are called HLIST. */ +#define histline(i) (hlist[(i)]->line) +#define histdata(i) (hlist[(i)]->data) + +static char * +histtime (hlist, histtimefmt) + HIST_ENTRY *hlist; + const char *histtimefmt; +{ + static char timestr[128]; + time_t t; + + t = history_get_time (hlist); + if (t) + strftime (timestr, sizeof (timestr), histtimefmt, localtime (&t)); + else + strcpy (timestr, "??"); + return timestr; +} + +static int +display_history (list) + WORD_LIST *list; +{ + register int i; + intmax_t limit; + HIST_ENTRY **hlist; + char *histtimefmt, *timestr; + + if (list) + { + if (get_numeric_arg (list, 0, &limit) == 0) + return (EXECUTION_FAILURE); + + if (limit < 0) + limit = -limit; + } + else + limit = -1; + + hlist = history_list (); + + if (hlist) + { + for (i = 0; hlist[i]; i++) + ; + + if (0 <= limit && limit < i) + i -= limit; + else + i = 0; + + histtimefmt = get_string_value ("HISTTIMEFORMAT"); + + while (hlist[i]) + { + QUIT; + + timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL; + printf ("%5d%c %s%s\n", i + history_base, + histdata(i) ? '*' : ' ', + ((timestr && *timestr) ? timestr : ""), + histline(i)); + i++; + } + } + + return (EXECUTION_SUCCESS); +} + +/* Remove the last entry in the history list and add each argument in + LIST to the history. */ +static void +push_history (list) + WORD_LIST *list; +{ + char *s; + + /* Delete the last history entry if it was a single entry added to the + history list (generally the `history -s' itself), or if `history -s' + is being used in a compound command and the compound command was + added to the history as a single element (command-oriented history). + If you don't want history -s to remove the compound command from the + history, change #if 0 to #if 1 below. */ +#if 0 + if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0) +#else + if (hist_last_line_pushed == 0 && + (hist_last_line_added || + (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history)) + && bash_delete_last_history () == 0) +#endif + return; + + s = string_list (list); + /* Call check_add_history with FORCE set to 1 to skip the check against + current_command_line_count. If history -s is used in a compound + command, the above code will delete the compound command's history + entry and this call will add the line to the history as a separate + entry. Without FORCE=1, if current_command_line_count were > 1, the + line would be appended to the entry before the just-deleted entry. */ + check_add_history (s, 1); /* obeys HISTCONTROL, HISTIGNORE */ + + hist_last_line_pushed = 1; /* XXX */ + free (s); +} + +#if defined (BANG_HISTORY) +static int +expand_and_print_history (list) + WORD_LIST *list; +{ + char *s; + int r, result; + + if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0) + return EXECUTION_FAILURE; + result = EXECUTION_SUCCESS; + while (list) + { + r = history_expand (list->word->word, &s); + if (r < 0) + { + builtin_error (_("%s: history expansion failed"), list->word->word); + result = EXECUTION_FAILURE; + } + else + { + fputs (s, stdout); + putchar ('\n'); + } + FREE (s); + list = list->next; + } + fflush (stdout); + return result; +} +#endif /* BANG_HISTORY */ +#endif /* HISTORY */ diff --git a/builtins/inlib.def b/builtins/inlib.def new file mode 100644 index 0000000..c4faf0d --- /dev/null +++ b/builtins/inlib.def @@ -0,0 +1,80 @@ +This file is inlib.def, from which is created inlib.c. +It implements the Apollo-specific builtin "inlib" in Bash. + +Copyright (C) 1987-2002 Free Software Foundation, Inc. + +This file is part of GNU Bash, the Bourne Again SHell. + +Bash is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Bash is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Bash. If not, see <http://www.gnu.org/licenses/>. + +$PRODUCES inlib.c +#include <config.h> + +#include <stdio.h> +#include "../shell.h" + +$BUILTIN inlib +$FUNCTION inlib_builtin +$DEPENDS_ON apollo +$SHORT_DOC inlib pathname [pathname...] +Install user-supplied library. + +Install a user-supplied library specified by pathname in the current +shell process. The library is used to resolve external references +in programs and libraries loaded after its installation. Note +that the library is not loaded into the address space unless it is +needed to resolve an external reference. The list of inlibed +libraries is passed to all children of the current shell. + +Exit Status: +Returns success unless PATHNAME is not found or an error occurs. +$END + +#if defined (apollo) + +#include <apollo/base.h> +#include <apollo/loader.h> + +inlib_builtin (list) + WORD_LIST *list; +{ + status_$t status; + int return_value; + short len; + + if (!list) + { + builtin_usage (); + return (EX_USAGE); + } + + return_value = EXECUTION_SUCCESS; + + while (list) + { + len = (short)strlen (list->word->word); + loader_$inlib (list->word->word, len, &status); + + if (status.all != status_$ok) + { + builtin_error (_("%s: inlib failed"), list->word->word); + return_value = EXECUTION_FAILURE; + } + + list = list->next; + } + + return (return_value); +} +#endif /* apollo */ diff --git a/builtins/jobs.def b/builtins/jobs.def new file mode 100644 index 0000000..b14e91f --- /dev/null +++ b/builtins/jobs.def @@ -0,0 +1,298 @@ +This file is jobs.def, from which is created jobs.c. +It implements the builtins "jobs" and "disown" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES jobs.c + +$BUILTIN jobs +$FUNCTION jobs_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC jobs [-lnprs] [jobspec ...] or jobs -x command [args] +Display status of jobs. + +Lists the active jobs. JOBSPEC restricts output to that job. +Without options, the status of all active jobs is displayed. + +Options: + -l lists process IDs in addition to the normal information + -n list only processes that have changed status since the last + notification + -p lists process IDs only + -r restrict output to running jobs + -s restrict output to stopped jobs + +If -x is supplied, COMMAND is run after all job specifications that +appear in ARGS have been replaced with the process ID of that job's +process group leader. + +Exit Status: +Returns success unless an invalid option is given or an error occurs. +If -x is used, returns the exit status of COMMAND. +$END + +#include <config.h> + +#if defined (JOB_CONTROL) +#include "../bashtypes.h" +#include <signal.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../execute_cmd.h" +#include "bashgetopt.h" +#include "common.h" + +#define JSTATE_ANY 0x0 +#define JSTATE_RUNNING 0x1 +#define JSTATE_STOPPED 0x2 + +static int execute_list_with_replacements __P((WORD_LIST *)); + +/* The `jobs' command. Prints outs a list of active jobs. If the + argument `-l' is given, then the process id's are printed also. + If the argument `-p' is given, print the process group leader's + pid only. If `-n' is given, only processes that have changed + status since the last notification are printed. If -x is given, + replace all job specs with the pid of the appropriate process + group leader and execute the command. The -r and -s options mean + to print info about running and stopped jobs only, respectively. */ +int +jobs_builtin (list) + WORD_LIST *list; +{ + int form, execute, state, opt, any_failed, job; + sigset_t set, oset; + + execute = any_failed = 0; + form = JLIST_STANDARD; + state = JSTATE_ANY; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lpnxrs")) != -1) + { + switch (opt) + { + case 'l': + form = JLIST_LONG; + break; + case 'p': + form = JLIST_PID_ONLY; + break; + case 'n': + form = JLIST_CHANGED_ONLY; + break; + case 'x': + if (form != JLIST_STANDARD) + { + builtin_error (_("no other options allowed with `-x'")); + return (EXECUTION_FAILURE); + } + execute++; + break; + case 'r': + state = JSTATE_RUNNING; + break; + case 's': + state = JSTATE_STOPPED; + break; + + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (execute) + return (execute_list_with_replacements (list)); + + if (!list) + { + switch (state) + { + case JSTATE_ANY: + list_all_jobs (form); + break; + case JSTATE_RUNNING: + list_running_jobs (form); + break; + case JSTATE_STOPPED: + list_stopped_jobs (form); + break; + } + return (EXECUTION_SUCCESS); + } + + while (list) + { + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if ((job == NO_JOB) || jobs == 0 || get_job_by_jid (job) == 0) + { + sh_badjob (list->word->word); + any_failed++; + } + else if (job != DUP_JOB) + list_one_job ((JOB *)NULL, form, 0, job); + + UNBLOCK_CHILD (oset); + list = list->next; + } + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +static int +execute_list_with_replacements (list) + WORD_LIST *list; +{ + register WORD_LIST *l; + int job, result; + COMMAND *command; + JOB *j; + + /* First do the replacement of job specifications with pids. */ + for (l = list; l; l = l->next) + { + if (l->word->word[0] == '%') /* we have a winner */ + { + job = get_job_spec (l); + + /* A bad job spec is not really a job spec! Pass it through. */ + if (INVALID_JOB (job)) + continue; + + j = get_job_by_jid (job); + free (l->word->word); + l->word->word = itos (j->pgrp); + } + } + + /* Next make a new simple command and execute it. */ + begin_unwind_frame ("jobs_builtin"); + + command = make_bare_simple_command (); + command->value.Simple->words = copy_word_list (list); + command->value.Simple->redirects = (REDIRECT *)NULL; + command->flags |= CMD_INHIBIT_EXPANSION; + command->value.Simple->flags |= CMD_INHIBIT_EXPANSION; + + add_unwind_protect (dispose_command, command); + result = execute_command (command); + dispose_command (command); + + discard_unwind_frame ("jobs_builtin"); + return (result); +} +#endif /* JOB_CONTROL */ + +$BUILTIN disown +$FUNCTION disown_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC disown [-h] [-ar] [jobspec ...] +Remove jobs from current shell. + +Removes each JOBSPEC argument from the table of active jobs. Without +any JOBSPECs, the shell uses its notion of the current job. + +Options: + -a remove all jobs if JOBSPEC is not supplied + -h mark each JOBSPEC so that SIGHUP is not sent to the job if the + shell receives a SIGHUP + -r remove only running jobs + +Exit Status: +Returns success unless an invalid option or JOBSPEC is given. +$END + +#if defined (JOB_CONTROL) +int +disown_builtin (list) + WORD_LIST *list; +{ + int opt, job, retval, nohup_only, running_jobs, all_jobs; + sigset_t set, oset; + intmax_t pid_value; + + nohup_only = running_jobs = all_jobs = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "ahr")) != -1) + { + switch (opt) + { + case 'a': + all_jobs = 1; + break; + case 'h': + nohup_only = 1; + break; + case 'r': + running_jobs = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + retval = EXECUTION_SUCCESS; + + /* `disown -a' or `disown -r' */ + if (list == 0 && (all_jobs || running_jobs)) + { + if (nohup_only) + nohup_all_jobs (running_jobs); + else + delete_all_jobs (running_jobs); + return (EXECUTION_SUCCESS); + } + + do + { + BLOCK_CHILD (set, oset); + job = (list && legal_number (list->word->word, &pid_value) && pid_value == (pid_t) pid_value) + ? get_job_by_pid ((pid_t) pid_value, 0) + : get_job_spec (list); + + if (job == NO_JOB || jobs == 0 || INVALID_JOB (job)) + { + sh_badjob (list ? list->word->word : _("current")); + retval = EXECUTION_FAILURE; + } + else if (nohup_only) + nohup_job (job); + else + delete_job (job, 1); + UNBLOCK_CHILD (oset); + + if (list) + list = list->next; + } + while (list); + + return (retval); +} +#endif /* JOB_CONTROL */ diff --git a/builtins/kill.def b/builtins/kill.def new file mode 100644 index 0000000..18c3667 --- /dev/null +++ b/builtins/kill.def @@ -0,0 +1,265 @@ +This file is kill.def, from which is created kill.c. +It implements the builtin "kill" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES kill.c + +$BUILTIN kill +$FUNCTION kill_builtin +$SHORT_DOC kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec] +Send a signal to a job. + +Send the processes identified by PID or JOBSPEC the signal named by +SIGSPEC or SIGNUM. If neither SIGSPEC nor SIGNUM is present, then +SIGTERM is assumed. + +Options: + -s sig SIG is a signal name + -n sig SIG is a signal number + -l list the signal names; if arguments follow `-l' they are + assumed to be signal numbers for which names should be listed + +Kill is a shell builtin for two reasons: it allows job IDs to be used +instead of process IDs, and allows processes to be killed if the limit +on processes that you can create is reached. + +Exit Status: +Returns success unless an invalid option is given or an error occurs. +$END + +#include <config.h> + +#include <stdio.h> +#include <errno.h> +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../trap.h" +#include "../jobs.h" +#include "common.h" + +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int posixly_correct; + +static void kill_error __P((pid_t, int)); + +#if !defined (CONTINUE_AFTER_KILL_ERROR) +# define CONTINUE_OR_FAIL return (EXECUTION_FAILURE) +#else +# define CONTINUE_OR_FAIL goto continue_killing +#endif /* CONTINUE_AFTER_KILL_ERROR */ + +/* Here is the kill builtin. We only have it so that people can type + kill -KILL %1? No, if you fill up the process table this way you + can still kill some. */ +int +kill_builtin (list) + WORD_LIST *list; +{ + int sig, any_succeeded, listing, saw_signal, dflags; + char *sigspec, *word; + pid_t pid; + intmax_t pid_value; + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + any_succeeded = listing = saw_signal = 0; + sig = SIGTERM; + sigspec = "TERM"; + + dflags = DSIG_NOCASE | ((posixly_correct == 0) ? DSIG_SIGPREFIX : 0); + /* Process options. */ + while (list) + { + word = list->word->word; + + if (ISOPTION (word, 'l')) + { + listing++; + list = list->next; + } + else if (ISOPTION (word, 's') || ISOPTION (word, 'n')) + { + list = list->next; + if (list) + { + sigspec = list->word->word; + if (sigspec[0] == '0' && sigspec[1] == '\0') + sig = 0; + else + sig = decode_signal (sigspec, dflags); + list = list->next; +#if 0 + saw_signal++; /* XXX - for bash-4.2 */ +#endif + } + else + { + sh_needarg (word); + return (EXECUTION_FAILURE); + } + } + else if (ISOPTION (word, '-')) + { + list = list->next; + break; + } + else if (ISOPTION (word, '?')) + { + builtin_usage (); + return (EXECUTION_SUCCESS); + } + /* If this is a signal specification then process it. We only process + the first one seen; other arguments may signify process groups (e.g, + -num == process group num). */ + else if (*word == '-' && saw_signal == 0) + { + sigspec = word + 1; + sig = decode_signal (sigspec, dflags); + saw_signal++; + list = list->next; + } + else + break; + } + + if (listing) + return (display_signal_list (list, 0)); + + /* OK, we are killing processes. */ + if (sig == NO_SIG) + { + sh_invalidsig (sigspec); + return (EXECUTION_FAILURE); + } + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + while (list) + { + word = list->word->word; + + if (*word == '-') + word++; + + /* Use the entire argument in case of minus sign presence. */ + if (*word && legal_number (list->word->word, &pid_value) && (pid_value == (pid_t)pid_value)) + { + pid = (pid_t) pid_value; + + if (kill_pid (pid, sig, pid < -1) < 0) + { + if (errno == EINVAL) + sh_invalidsig (sigspec); + else + kill_error (pid, errno); + CONTINUE_OR_FAIL; + } + else + any_succeeded++; + } +#if defined (JOB_CONTROL) + else if (*list->word->word && *list->word->word != '%') + { + builtin_error (_("%s: arguments must be process or job IDs"), list->word->word); + CONTINUE_OR_FAIL; + } + else if (*word) + /* Posix.2 says you can kill without job control active (4.32.4) */ + { /* Must be a job spec. Check it out. */ + int job; + sigset_t set, oset; + JOB *j; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (INVALID_JOB (job)) + { + if (job != DUP_JOB) + sh_badjob (list->word->word); + UNBLOCK_CHILD (oset); + CONTINUE_OR_FAIL; + } + + j = get_job_by_jid (job); + /* Job spec used. Kill the process group. If the job was started + without job control, then its pgrp == shell_pgrp, so we have + to be careful. We take the pid of the first job in the pipeline + in that case. */ + pid = IS_JOBCONTROL (job) ? j->pgrp : j->pipe->pid; + + UNBLOCK_CHILD (oset); + + if (kill_pid (pid, sig, 1) < 0) + { + if (errno == EINVAL) + sh_invalidsig (sigspec); + else + kill_error (pid, errno); + CONTINUE_OR_FAIL; + } + else + any_succeeded++; + } +#endif /* !JOB_CONTROL */ + else + { + sh_badpid (list->word->word); + CONTINUE_OR_FAIL; + } + continue_killing: + list = list->next; + } + + return (any_succeeded ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +static void +kill_error (pid, e) + pid_t pid; + int e; +{ + char *x; + + x = strerror (e); + if (x == 0) + x = _("Unknown error"); + builtin_error ("(%ld) - %s", (long)pid, x); +} diff --git a/builtins/let.def b/builtins/let.def new file mode 100644 index 0000000..2601fb9 --- /dev/null +++ b/builtins/let.def @@ -0,0 +1,129 @@ +This file is let.def, from which is created let.c. +It implements the builtin "let" in Bash. + +Copyright (C) 1987-2009 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/>. + +$BUILTIN let +$FUNCTION let_builtin +$PRODUCES let.c +$SHORT_DOC let arg [arg ...] +Evaluate arithmetic expressions. + +Evaluate each ARG as an arithmetic expression. Evaluation is done in +fixed-width integers with no check for overflow, though division by 0 +is trapped and flagged as an error. The following list of operators is +grouped into levels of equal-precedence operators. The levels are listed +in order of decreasing precedence. + + id++, id-- variable post-increment, post-decrement + ++id, --id variable pre-increment, pre-decrement + -, + unary minus, plus + !, ~ logical and bitwise negation + ** exponentiation + *, /, % multiplication, division, remainder + +, - addition, subtraction + <<, >> left and right bitwise shifts + <=, >=, <, > comparison + ==, != equality, inequality + & bitwise AND + ^ bitwise XOR + | bitwise OR + && logical AND + || logical OR + expr ? expr : expr + conditional operator + =, *=, /=, %=, + +=, -=, <<=, >>=, + &=, ^=, |= assignment + +Shell variables are allowed as operands. The name of the variable +is replaced by its value (coerced to a fixed-width integer) within +an expression. The variable need not have its integer attribute +turned on to be used in an expression. + +Operators are evaluated in order of precedence. Sub-expressions in +parentheses are evaluated first and may override the precedence +rules above. + +Exit Status: +If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" + +/* Arithmetic LET function. */ +int +let_builtin (list) + WORD_LIST *list; +{ + intmax_t ret; + int expok; + + /* Skip over leading `--' argument. */ + if (list && list->word && ISOPTION (list->word->word, '-')) + list = list->next; + + if (list == 0) + { + builtin_error (_("expression expected")); + return (EXECUTION_FAILURE); + } + + for (; list; list = list->next) + { + ret = evalexp (list->word->word, &expok); + if (expok == 0) + return (EXECUTION_FAILURE); + } + + return ((ret == 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +#ifdef INCLUDE_UNUSED +int +exp_builtin (list) + WORD_LIST *list; +{ + char *exp; + intmax_t ret; + int expok; + + if (list == 0) + { + builtin_error (_("expression expected")); + return (EXECUTION_FAILURE); + } + + exp = string_list (list); + ret = evalexp (exp, &expok); + (void)free (exp); + return (((ret == 0) || (expok == 0)) ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} +#endif diff --git a/builtins/mapfile.def b/builtins/mapfile.def new file mode 100644 index 0000000..0946de3 --- /dev/null +++ b/builtins/mapfile.def @@ -0,0 +1,351 @@ +This file is mapfile.def, from which is created mapfile.c. +It implements the builtin "mapfile" in Bash. + +Copyright (C) 2005-2006 Rocky Bernstein for Free Software Foundation, Inc. +Copyright (C) 2008,2009 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/>. + +$PRODUCES mapfile.c + +$BUILTIN mapfile +$FUNCTION mapfile_builtin +$SHORT_DOC mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] +Read lines from the standard input into an indexed array variable. + +Read lines from the standard input into the indexed array variable ARRAY, or +from file descriptor FD if the -u option is supplied. The variable MAPFILE +is the default ARRAY. + +Options: + -n count Copy at most COUNT lines. If COUNT is 0, all lines are copied. + -O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0. + -s count Discard the first COUNT lines read. + -t Remove a trailing newline from each line read. + -u fd Read lines from file descriptor FD instead of the standard input. + -C callback Evaluate CALLBACK each time QUANTUM lines are read. + -c quantum Specify the number of lines read between each call to CALLBACK. + +Arguments: + ARRAY Array variable name to use for file data. + +If -C is supplied without -c, the default quantum is 5000. When +CALLBACK is evaluated, it is supplied the index of the next array +element to be assigned as an additional argument. + +If not supplied with an explicit origin, mapfile will clear ARRAY before +assigning to it. + +Exit Status: +Returns success unless an invalid option is given or ARRAY is readonly or +not an indexed array. +$END + +$BUILTIN readarray +$FUNCTION mapfile_builtin +$SHORT_DOC readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] +Read lines from a file into an array variable. + +A synonym for `mapfile'. +$END + +#include <config.h> + +#include "builtins.h" +#include "posixstat.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "bashintl.h" + +#include <stdio.h> +#include <errno.h> + +#include "../bashintl.h" +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +#if defined (ARRAY_VARS) + +#define DEFAULT_ARRAY_NAME "MAPFILE" + +/* The value specifying how frequently `mapfile' calls the callback. */ +#define DEFAULT_QUANTUM 5000 + +/* Values for FLAGS */ +#define MAPF_CLEARARRAY 0x01 +#define MAPF_CHOP 0x02 + +static int +run_callback(callback, current_index) + const char *callback; + unsigned int current_index; +{ + unsigned int execlen; + char *execstr; + int flags; + + execlen = strlen (callback) + 10; + /* 1 for space between %s and %d, + another 1 for the last nul char for C string. */ + execlen += 2; + execstr = xmalloc (execlen); + + flags = SEVAL_NOHIST; +#if 0 + if (interactive) + flags |= SEVAL_INTERACT; +#endif + snprintf (execstr, execlen, "%s %d", callback, current_index); + return parse_and_execute(execstr, NULL, flags); +} + +static void +do_chop(line) + char * line; +{ + int length; + + length = strlen (line); + if (length && line[length-1] == '\n') + line[length-1] = '\0'; +} + +static int +mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_name, flags) + int fd; + long line_count_goal, origin, nskip, callback_quantum; + char *callback, *array_name; + int flags; +{ + char *line; + size_t line_length; + unsigned int array_index, line_count; + SHELL_VAR *entry; + int unbuffered_read; + + line = NULL; + line_length = 0; + unbuffered_read = 0; + + /* The following check should be done before reading any lines. Doing it + here allows us to call bind_array_element instead of bind_array_variable + and skip the variable lookup on every call. */ + entry = find_or_make_array_variable (array_name, 1); + if (entry == 0 || readonly_p (entry) || noassign_p (entry)) + { + if (entry && readonly_p (entry)) + err_readonly (array_name); + + return (EXECUTION_FAILURE); + } + else if (array_p (entry) == 0) + { + builtin_error (_("%s: not an indexed array"), array_name); + return (EXECUTION_FAILURE); + } + + if (flags & MAPF_CLEARARRAY) + array_flush (array_cell (entry)); + +#ifndef __CYGWIN__ + unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); +#else + unbuffered_read = 1; +#endif + + zreset (); + + /* Skip any lines at beginning of file? */ + for (line_count = 0; line_count < nskip; line_count++) + if (zgetline (fd, &line, &line_length, unbuffered_read) < 0) + break; + + line = 0; + line_length = 0; + + /* Reset the buffer for bash own stream */ + interrupt_immediately++; + for (array_index = origin, line_count = 1; + zgetline (fd, &line, &line_length, unbuffered_read) != -1; + array_index++, line_count++) + { + /* Have we exceeded # of lines to store? */ + if (line_count_goal != 0 && line_count > line_count_goal) + break; + + /* Remove trailing newlines? */ + if (flags & MAPF_CHOP) + do_chop (line); + + /* Has a callback been registered and if so is it time to call it? */ + if (callback && line_count && (line_count % callback_quantum) == 0) + { + run_callback (callback, array_index); + + /* Reset the buffer for bash own stream. */ + if (unbuffered_read == 0) + zsyncfd (fd); + } + + bind_array_element (entry, array_index, line, 0); + } + + xfree (line); + + if (unbuffered_read == 0) + zsyncfd (fd); + + interrupt_immediately--; + return EXECUTION_SUCCESS; +} + +int +mapfile_builtin (list) + WORD_LIST *list; +{ + int opt, code, fd, clear_array, flags; + intmax_t intval; + long lines, origin, nskip, callback_quantum; + char *array_name, *callback; + + clear_array = 1; + fd = 0; + lines = origin = nskip = 0; + flags = MAPF_CLEARARRAY; + callback_quantum = DEFAULT_QUANTUM; + callback = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "u:n:O:tC:c:s:")) != -1) + { + switch (opt) + { + case 'u': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (int)intval) + { + builtin_error (_("%s: invalid file descriptor specification"), list_optarg); + return (EXECUTION_FAILURE); + } + else + fd = intval; + + if (sh_validfd (fd) == 0) + { + builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno)); + return (EXECUTION_FAILURE); + } + break; + + case 'n': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (unsigned)intval) + { + builtin_error (_("%s: invalid line count"), list_optarg); + return (EXECUTION_FAILURE); + } + else + lines = intval; + break; + + case 'O': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (unsigned)intval) + { + builtin_error (_("%s: invalid array origin"), list_optarg); + return (EXECUTION_FAILURE); + } + else + origin = intval; + flags &= ~MAPF_CLEARARRAY; + break; + case 't': + flags |= MAPF_CHOP; + break; + case 'C': + callback = list_optarg; + break; + case 'c': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval <= 0 || intval != (unsigned)intval) + { + builtin_error (_("%s: invalid callback quantum"), list_optarg); + return (EXECUTION_FAILURE); + } + else + callback_quantum = intval; + break; + case 's': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (unsigned)intval) + { + builtin_error (_("%s: invalid line count"), list_optarg); + return (EXECUTION_FAILURE); + } + else + nskip = intval; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + array_name = DEFAULT_ARRAY_NAME; + else if (list->word == 0 || list->word->word == 0) + { + builtin_error ("internal error: getting variable name"); + return (EXECUTION_FAILURE); + } + else if (list->word->word[0] == '\0') + { + builtin_error (_("empty array variable name")); + return (EX_USAGE); + } + else + array_name = list->word->word; + + if (legal_identifier (array_name) == 0 && valid_array_reference (array_name) == 0) + { + sh_invalidid (array_name); + return (EXECUTION_FAILURE); + } + + return mapfile (fd, lines, origin, nskip, callback_quantum, callback, array_name, flags); +} + +#else + +int +mapfile_builtin (list) + WORD_LIST *list; +{ + builtin_error (_("array variable support required")); + return (EXECUTION_FAILURE); +} + +#endif /* ARRAY_VARS */ diff --git a/builtins/mkbuiltins.c b/builtins/mkbuiltins.c new file mode 100644 index 0000000..cdd45e4 --- /dev/null +++ b/builtins/mkbuiltins.c @@ -0,0 +1,1595 @@ +/* mkbuiltins.c - Create builtins.c, builtext.h, and builtdoc.c from + a single source file called builtins.def. */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (CROSS_COMPILING) +# include <config.h> +#else /* CROSS_COMPILING */ +/* A conservative set of defines based on POSIX/SUS3/XPG6 */ +# define HAVE_UNISTD_H +# define HAVE_STRING_H +# define HAVE_STDLIB_H + +# define HAVE_RENAME +#endif /* CROSS_COMPILING */ + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#ifndef _MINIX +# include "../bashtypes.h" +# if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +# endif +#endif + +#include "posixstat.h" +#include "filecntl.h" + +#include "../bashansi.h" +#include <stdio.h> +#include <errno.h> + +#include "stdc.h" + +#define DOCFILE "builtins.texi" + +#ifndef errno +extern int errno; +#endif + +static char *xmalloc (), *xrealloc (); + +#if !defined (__STDC__) && !defined (strcpy) +extern char *strcpy (); +#endif /* !__STDC__ && !strcpy */ + +#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) + +/* Flag values that builtins can have. */ +#define BUILTIN_FLAG_SPECIAL 0x01 +#define BUILTIN_FLAG_ASSIGNMENT 0x02 +#define BUILTIN_FLAG_POSIX_BUILTIN 0x04 + +#define BASE_INDENT 4 + +/* If this stream descriptor is non-zero, then write + texinfo documentation to it. */ +FILE *documentation_file = (FILE *)NULL; + +/* Non-zero means to only produce documentation. */ +int only_documentation = 0; + +/* Non-zero means to not do any productions. */ +int inhibit_production = 0; + +/* Non-zero means to produce separate help files for each builtin, named by + the builtin name, in `./helpfiles'. */ +int separate_helpfiles = 0; + +/* Non-zero means to create single C strings for each `longdoc', with + embedded newlines, for ease of translation. */ +int single_longdoc_strings = 1; + +/* The name of a directory into which the separate external help files will + eventually be installed. */ +char *helpfile_directory; + +/* The name of a directory to precede the filename when reporting + errors. */ +char *error_directory = (char *)NULL; + +/* The name of the structure file. */ +char *struct_filename = (char *)NULL; + +/* The name of the external declaration file. */ +char *extern_filename = (char *)NULL; + +/* Here is a structure for manipulating arrays of data. */ +typedef struct { + int size; /* Number of slots allocated to array. */ + int sindex; /* Current location in array. */ + int width; /* Size of each element. */ + int growth_rate; /* How fast to grow. */ + char **array; /* The array itself. */ +} ARRAY; + +/* Here is a structure defining a single BUILTIN. */ +typedef struct { + char *name; /* The name of this builtin. */ + char *function; /* The name of the function to call. */ + char *shortdoc; /* The short documentation for this builtin. */ + char *docname; /* Possible name for documentation string. */ + ARRAY *longdoc; /* The long documentation for this builtin. */ + ARRAY *dependencies; /* Null terminated array of #define names. */ + int flags; /* Flags for this builtin. */ +} BUILTIN_DESC; + +/* Here is a structure which defines a DEF file. */ +typedef struct { + char *filename; /* The name of the input def file. */ + ARRAY *lines; /* The contents of the file. */ + int line_number; /* The current line number. */ + char *production; /* The name of the production file. */ + FILE *output; /* Open file stream for PRODUCTION. */ + ARRAY *builtins; /* Null terminated array of BUILTIN_DESC *. */ +} DEF_FILE; + +/* The array of all builtins encountered during execution of this code. */ +ARRAY *saved_builtins = (ARRAY *)NULL; + +/* The Posix.2 so-called `special' builtins. */ +char *special_builtins[] = +{ + ":", ".", "source", "break", "continue", "eval", "exec", "exit", + "export", "readonly", "return", "set", "shift", "times", "trap", "unset", + (char *)NULL +}; + +/* The builtin commands that take assignment statements as arguments. */ +char *assignment_builtins[] = +{ + "alias", "declare", "export", "local", "readonly", "typeset", + (char *)NULL +}; + +/* The builtin commands that are special to the POSIX search order. */ +char *posix_builtins[] = +{ + "alias", "bg", "cd", "command", "false", "fc", "fg", "getopts", "jobs", + "kill", "newgrp", "pwd", "read", "true", "umask", "unalias", "wait", + (char *)NULL +}; + +/* Forward declarations. */ +static int is_special_builtin (); +static int is_assignment_builtin (); +static int is_posix_builtin (); + +#if !defined (HAVE_RENAME) +static int rename (); +#endif + +void extract_info (); + +void file_error (); +void line_error (); + +void write_file_headers (); +void write_file_footers (); +void write_ifdefs (); +void write_endifs (); +void write_documentation (); +void write_longdocs (); +void write_builtins (); + +int write_helpfiles (); + +void free_defs (); +void add_documentation (); + +void must_be_building (); +void remove_trailing_whitespace (); + +#define document_name(b) ((b)->docname ? (b)->docname : (b)->name) + + +/* For each file mentioned on the command line, process it and + write the information to STRUCTFILE and EXTERNFILE, while + creating the production file if neccessary. */ +int +main (argc, argv) + int argc; + char **argv; +{ + int arg_index = 1; + FILE *structfile, *externfile; + char *documentation_filename, *temp_struct_filename; + + structfile = externfile = (FILE *)NULL; + documentation_filename = DOCFILE; + temp_struct_filename = (char *)NULL; + + while (arg_index < argc && argv[arg_index][0] == '-') + { + char *arg = argv[arg_index++]; + + if (strcmp (arg, "-externfile") == 0) + extern_filename = argv[arg_index++]; + else if (strcmp (arg, "-structfile") == 0) + struct_filename = argv[arg_index++]; + else if (strcmp (arg, "-noproduction") == 0) + inhibit_production = 1; + else if (strcmp (arg, "-document") == 0) + documentation_file = fopen (documentation_filename, "w"); + else if (strcmp (arg, "-D") == 0) + { + int len; + + if (error_directory) + free (error_directory); + + error_directory = xmalloc (2 + strlen (argv[arg_index])); + strcpy (error_directory, argv[arg_index]); + len = strlen (error_directory); + + if (len && error_directory[len - 1] != '/') + strcat (error_directory, "/"); + + arg_index++; + } + else if (strcmp (arg, "-documentonly") == 0) + { + only_documentation = 1; + documentation_file = fopen (documentation_filename, "w"); + } + else if (strcmp (arg, "-H") == 0) + { + separate_helpfiles = 1; + helpfile_directory = argv[arg_index++]; + } + else if (strcmp (arg, "-S") == 0) + single_longdoc_strings = 0; + else + { + fprintf (stderr, "%s: Unknown flag %s.\n", argv[0], arg); + exit (2); + } + } + + /* If there are no files to process, just quit now. */ + if (arg_index == argc) + exit (0); + + if (!only_documentation) + { + /* Open the files. */ + if (struct_filename) + { + temp_struct_filename = xmalloc (15); + sprintf (temp_struct_filename, "mk-%ld", (long) getpid ()); + structfile = fopen (temp_struct_filename, "w"); + + if (!structfile) + file_error (temp_struct_filename); + } + + if (extern_filename) + { + externfile = fopen (extern_filename, "w"); + + if (!externfile) + file_error (extern_filename); + } + + /* Write out the headers. */ + write_file_headers (structfile, externfile); + } + + if (documentation_file) + { + fprintf (documentation_file, "@c Table of builtins created with %s.\n", + argv[0]); + fprintf (documentation_file, "@ftable @asis\n"); + } + + /* Process the .def files. */ + while (arg_index < argc) + { + register char *arg; + + arg = argv[arg_index++]; + + extract_info (arg, structfile, externfile); + } + + /* Close the files. */ + if (!only_documentation) + { + /* Write the footers. */ + write_file_footers (structfile, externfile); + + if (structfile) + { + write_longdocs (structfile, saved_builtins); + fclose (structfile); + rename (temp_struct_filename, struct_filename); + } + + if (externfile) + fclose (externfile); + } + + if (separate_helpfiles) + { + write_helpfiles (saved_builtins); + } + + if (documentation_file) + { + fprintf (documentation_file, "@end ftable\n"); + fclose (documentation_file); + } + + exit (0); +} + +/* **************************************************************** */ +/* */ +/* Array Functions and Manipulators */ +/* */ +/* **************************************************************** */ + +/* Make a new array, and return a pointer to it. The array will + contain elements of size WIDTH, and is initialized to no elements. */ +ARRAY * +array_create (width) + int width; +{ + ARRAY *array; + + array = (ARRAY *)xmalloc (sizeof (ARRAY)); + array->size = 0; + array->sindex = 0; + array->width = width; + + /* Default to increasing size in units of 20. */ + array->growth_rate = 20; + + array->array = (char **)NULL; + + return (array); +} + +/* Copy the array of strings in ARRAY. */ +ARRAY * +copy_string_array (array) + ARRAY *array; +{ + register int i; + ARRAY *copy; + + if (!array) + return (ARRAY *)NULL; + + copy = array_create (sizeof (char *)); + + copy->size = array->size; + copy->sindex = array->sindex; + copy->width = array->width; + + copy->array = (char **)xmalloc ((1 + array->sindex) * sizeof (char *)); + + for (i = 0; i < array->sindex; i++) + copy->array[i] = savestring (array->array[i]); + + copy->array[i] = (char *)NULL; + + return (copy); +} + +/* Add ELEMENT to ARRAY, growing the array if neccessary. */ +void +array_add (element, array) + char *element; + ARRAY *array; +{ + if (array->sindex + 2 > array->size) + array->array = (char **)xrealloc + (array->array, (array->size += array->growth_rate) * array->width); + + array->array[array->sindex++] = element; + array->array[array->sindex] = (char *)NULL; +} + +/* Free an allocated array and data pointer. */ +void +array_free (array) + ARRAY *array; +{ + if (array->array) + free (array->array); + + free (array); +} + +/* **************************************************************** */ +/* */ +/* Processing a DEF File */ +/* */ +/* **************************************************************** */ + +/* The definition of a function. */ +typedef int Function (); +typedef int mk_handler_func_t __P((char *, DEF_FILE *, char *)); + +/* Structure handles processor directives. */ +typedef struct { + char *directive; + mk_handler_func_t *function; +} HANDLER_ENTRY; + +extern int builtin_handler __P((char *, DEF_FILE *, char *)); +extern int function_handler __P((char *, DEF_FILE *, char *)); +extern int short_doc_handler __P((char *, DEF_FILE *, char *)); +extern int comment_handler __P((char *, DEF_FILE *, char *)); +extern int depends_on_handler __P((char *, DEF_FILE *, char *)); +extern int produces_handler __P((char *, DEF_FILE *, char *)); +extern int end_handler __P((char *, DEF_FILE *, char *)); +extern int docname_handler __P((char *, DEF_FILE *, char *)); + +HANDLER_ENTRY handlers[] = { + { "BUILTIN", builtin_handler }, + { "DOCNAME", docname_handler }, + { "FUNCTION", function_handler }, + { "SHORT_DOC", short_doc_handler }, + { "$", comment_handler }, + { "COMMENT", comment_handler }, + { "DEPENDS_ON", depends_on_handler }, + { "PRODUCES", produces_handler }, + { "END", end_handler }, + { (char *)NULL, (mk_handler_func_t *)NULL } +}; + +/* Return the entry in the table of handlers for NAME. */ +HANDLER_ENTRY * +find_directive (directive) + char *directive; +{ + register int i; + + for (i = 0; handlers[i].directive; i++) + if (strcmp (handlers[i].directive, directive) == 0) + return (&handlers[i]); + + return ((HANDLER_ENTRY *)NULL); +} + +/* Non-zero indicates that a $BUILTIN has been seen, but not + the corresponding $END. */ +static int building_builtin = 0; + +/* Non-zero means to output cpp line and file information before + printing the current line to the production file. */ +int output_cpp_line_info = 0; + +/* The main function of this program. Read FILENAME and act on what is + found. Lines not starting with a dollar sign are copied to the + $PRODUCES target, if one is present. Lines starting with a dollar sign + are directives to this program, specifying the name of the builtin, the + function to call, the short documentation and the long documentation + strings. FILENAME can contain multiple $BUILTINs, but only one $PRODUCES + target. After the file has been processed, write out the names of + builtins found in each $BUILTIN. Plain text found before the $PRODUCES + is ignored, as is "$$ comment text". */ +void +extract_info (filename, structfile, externfile) + char *filename; + FILE *structfile, *externfile; +{ + register int i; + DEF_FILE *defs; + struct stat finfo; + size_t file_size; + char *buffer, *line; + int fd, nr; + + if (stat (filename, &finfo) == -1) + file_error (filename); + + fd = open (filename, O_RDONLY, 0666); + + if (fd == -1) + file_error (filename); + + file_size = (size_t)finfo.st_size; + buffer = xmalloc (1 + file_size); + + if ((nr = read (fd, buffer, file_size)) < 0) + file_error (filename); + + /* This is needed on WIN32, and does not hurt on Unix. */ + if (nr < file_size) + file_size = nr; + + close (fd); + + if (nr == 0) + { + fprintf (stderr, "mkbuiltins: %s: skipping zero-length file\n", filename); + return; + } + + /* Create and fill in the initial structure describing this file. */ + defs = (DEF_FILE *)xmalloc (sizeof (DEF_FILE)); + defs->filename = filename; + defs->lines = array_create (sizeof (char *)); + defs->line_number = 0; + defs->production = (char *)NULL; + defs->output = (FILE *)NULL; + defs->builtins = (ARRAY *)NULL; + + /* Build the array of lines. */ + i = 0; + while (i < file_size) + { + array_add (&buffer[i], defs->lines); + + while (buffer[i] != '\n' && i < file_size) + i++; + buffer[i++] = '\0'; + } + + /* Begin processing the input file. We don't write any output + until we have a file to write output to. */ + output_cpp_line_info = 1; + + /* Process each line in the array. */ + for (i = 0; line = defs->lines->array[i]; i++) + { + defs->line_number = i; + + if (*line == '$') + { + register int j; + char *directive; + HANDLER_ENTRY *handler; + + /* Isolate the directive. */ + for (j = 0; line[j] && !whitespace (line[j]); j++); + + directive = xmalloc (j); + strncpy (directive, line + 1, j - 1); + directive[j -1] = '\0'; + + /* Get the function handler and call it. */ + handler = find_directive (directive); + + if (!handler) + { + line_error (defs, "Unknown directive `%s'", directive); + free (directive); + continue; + } + else + { + /* Advance to the first non-whitespace character. */ + while (whitespace (line[j])) + j++; + + /* Call the directive handler with the FILE, and ARGS. */ + (*(handler->function)) (directive, defs, line + j); + } + free (directive); + } + else + { + if (building_builtin) + add_documentation (defs, line); + else if (defs->output) + { + if (output_cpp_line_info) + { + /* If we're handed an absolute pathname, don't prepend + the directory name. */ + if (defs->filename[0] == '/') + fprintf (defs->output, "#line %d \"%s\"\n", + defs->line_number + 1, defs->filename); + else + fprintf (defs->output, "#line %d \"%s%s\"\n", + defs->line_number + 1, + error_directory ? error_directory : "./", + defs->filename); + output_cpp_line_info = 0; + } + + fprintf (defs->output, "%s\n", line); + } + } + } + + /* Close the production file. */ + if (defs->output) + fclose (defs->output); + + /* The file has been processed. Write the accumulated builtins to + the builtins.c file, and write the extern definitions to the + builtext.h file. */ + write_builtins (defs, structfile, externfile); + + free (buffer); + free_defs (defs); +} + +#define free_safely(x) if (x) free (x) + +static void +free_builtin (builtin) + BUILTIN_DESC *builtin; +{ + register int i; + + free_safely (builtin->name); + free_safely (builtin->function); + free_safely (builtin->shortdoc); + free_safely (builtin->docname); + + if (builtin->longdoc) + array_free (builtin->longdoc); + + if (builtin->dependencies) + { + for (i = 0; builtin->dependencies->array[i]; i++) + free (builtin->dependencies->array[i]); + array_free (builtin->dependencies); + } +} + +/* Free all of the memory allocated to a DEF_FILE. */ +void +free_defs (defs) + DEF_FILE *defs; +{ + register int i; + register BUILTIN_DESC *builtin; + + if (defs->production) + free (defs->production); + + if (defs->lines) + array_free (defs->lines); + + if (defs->builtins) + { + for (i = 0; builtin = (BUILTIN_DESC *)defs->builtins->array[i]; i++) + { + free_builtin (builtin); + free (builtin); + } + array_free (defs->builtins); + } + free (defs); +} + +/* **************************************************************** */ +/* */ +/* The Handler Functions Themselves */ +/* */ +/* **************************************************************** */ + +/* Strip surrounding whitespace from STRING, and + return a pointer to the start of it. */ +char * +strip_whitespace (string) + char *string; +{ + while (whitespace (*string)) + string++; + + remove_trailing_whitespace (string); + return (string); +} + +/* Remove only the trailing whitespace from STRING. */ +void +remove_trailing_whitespace (string) + char *string; +{ + register int i; + + i = strlen (string) - 1; + + while (i > 0 && whitespace (string[i])) + i--; + + string[++i] = '\0'; +} + +/* Ensure that there is a argument in STRING and return it. + FOR_WHOM is the name of the directive which needs the argument. + DEFS is the DEF_FILE in which the directive is found. + If there is no argument, produce an error. */ +char * +get_arg (for_whom, defs, string) + char *for_whom, *string; + DEF_FILE *defs; +{ + char *new; + + new = strip_whitespace (string); + + if (!*new) + line_error (defs, "%s requires an argument", for_whom); + + return (savestring (new)); +} + +/* Error if not building a builtin. */ +void +must_be_building (directive, defs) + char *directive; + DEF_FILE *defs; +{ + if (!building_builtin) + line_error (defs, "%s must be inside of a $BUILTIN block", directive); +} + +/* Return the current builtin. */ +BUILTIN_DESC * +current_builtin (directive, defs) + char *directive; + DEF_FILE *defs; +{ + must_be_building (directive, defs); + if (defs->builtins) + return ((BUILTIN_DESC *)defs->builtins->array[defs->builtins->sindex - 1]); + else + return ((BUILTIN_DESC *)NULL); +} + +/* Add LINE to the long documentation for the current builtin. + Ignore blank lines until the first non-blank line has been seen. */ +void +add_documentation (defs, line) + DEF_FILE *defs; + char *line; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin ("(implied LONGDOC)", defs); + + remove_trailing_whitespace (line); + + if (!*line && !builtin->longdoc) + return; + + if (!builtin->longdoc) + builtin->longdoc = array_create (sizeof (char *)); + + array_add (line, builtin->longdoc); +} + +/* How to handle the $BUILTIN directive. */ +int +builtin_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + BUILTIN_DESC *new; + char *name; + + /* If we are already building a builtin, we cannot start a new one. */ + if (building_builtin) + { + line_error (defs, "%s found before $END", self); + return (-1); + } + + output_cpp_line_info++; + + /* Get the name of this builtin, and stick it in the array. */ + name = get_arg (self, defs, arg); + + /* If this is the first builtin, create the array to hold them. */ + if (!defs->builtins) + defs->builtins = array_create (sizeof (BUILTIN_DESC *)); + + new = (BUILTIN_DESC *)xmalloc (sizeof (BUILTIN_DESC)); + new->name = name; + new->function = (char *)NULL; + new->shortdoc = (char *)NULL; + new->docname = (char *)NULL; + new->longdoc = (ARRAY *)NULL; + new->dependencies = (ARRAY *)NULL; + new->flags = 0; + + if (is_special_builtin (name)) + new->flags |= BUILTIN_FLAG_SPECIAL; + if (is_assignment_builtin (name)) + new->flags |= BUILTIN_FLAG_ASSIGNMENT; + if (is_posix_builtin (name)) + new->flags |= BUILTIN_FLAG_POSIX_BUILTIN; + + array_add ((char *)new, defs->builtins); + building_builtin = 1; + + return (0); +} + +/* How to handle the $FUNCTION directive. */ +int +function_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin == 0) + { + line_error (defs, "syntax error: no current builtin for $FUNCTION directive"); + exit (1); + } + if (builtin->function) + line_error (defs, "%s already has a function (%s)", + builtin->name, builtin->function); + else + builtin->function = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $DOCNAME directive. */ +int +docname_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin->docname) + line_error (defs, "%s already had a docname (%s)", + builtin->name, builtin->docname); + else + builtin->docname = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $SHORT_DOC directive. */ +int +short_doc_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin->shortdoc) + line_error (defs, "%s already has short documentation (%s)", + builtin->name, builtin->shortdoc); + else + builtin->shortdoc = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $COMMENT directive. */ +int +comment_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + return (0); +} + +/* How to handle the $DEPENDS_ON directive. */ +int +depends_on_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + register BUILTIN_DESC *builtin; + char *dependent; + + builtin = current_builtin (self, defs); + dependent = get_arg (self, defs, arg); + + if (!builtin->dependencies) + builtin->dependencies = array_create (sizeof (char *)); + + array_add (dependent, builtin->dependencies); + + return (0); +} + +/* How to handle the $PRODUCES directive. */ +int +produces_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + /* If just hacking documentation, don't change any of the production + files. */ + if (only_documentation) + return (0); + + output_cpp_line_info++; + + if (defs->production) + line_error (defs, "%s already has a %s definition", defs->filename, self); + else + { + defs->production = get_arg (self, defs, arg); + + if (inhibit_production) + return (0); + + defs->output = fopen (defs->production, "w"); + + if (!defs->output) + file_error (defs->production); + + fprintf (defs->output, "/* %s, created from %s. */\n", + defs->production, defs->filename); + } + return (0); +} + +/* How to handle the $END directive. */ +int +end_handler (self, defs, arg) + char *self; + DEF_FILE *defs; + char *arg; +{ + must_be_building (self, defs); + building_builtin = 0; + return (0); +} + +/* **************************************************************** */ +/* */ +/* Error Handling Functions */ +/* */ +/* **************************************************************** */ + +/* Produce an error for DEFS with FORMAT and ARGS. */ +void +line_error (defs, format, arg1, arg2) + DEF_FILE *defs; + char *format, *arg1, *arg2; +{ + if (defs->filename[0] != '/') + fprintf (stderr, "%s", error_directory ? error_directory : "./"); + fprintf (stderr, "%s:%d:", defs->filename, defs->line_number + 1); + fprintf (stderr, format, arg1, arg2); + fprintf (stderr, "\n"); + fflush (stderr); +} + +/* Print error message for FILENAME. */ +void +file_error (filename) + char *filename; +{ + perror (filename); + exit (2); +} + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "mkbuiltins: out of virtual memory\n"); + abort (); +} + +/* **************************************************************** */ +/* */ +/* Creating the Struct and Extern Files */ +/* */ +/* **************************************************************** */ + +/* Return a pointer to a newly allocated builtin which is + an exact copy of BUILTIN. */ +BUILTIN_DESC * +copy_builtin (builtin) + BUILTIN_DESC *builtin; +{ + BUILTIN_DESC *new; + + new = (BUILTIN_DESC *)xmalloc (sizeof (BUILTIN_DESC)); + + new->name = savestring (builtin->name); + new->shortdoc = savestring (builtin->shortdoc); + new->longdoc = copy_string_array (builtin->longdoc); + new->dependencies = copy_string_array (builtin->dependencies); + + new->function = + builtin->function ? savestring (builtin->function) : (char *)NULL; + new->docname = + builtin->docname ? savestring (builtin->docname) : (char *)NULL; + + return (new); +} + +/* How to save away a builtin. */ +void +save_builtin (builtin) + BUILTIN_DESC *builtin; +{ + BUILTIN_DESC *newbuiltin; + + newbuiltin = copy_builtin (builtin); + + /* If this is the first builtin to be saved, create the array + to hold it. */ + if (!saved_builtins) + saved_builtins = array_create (sizeof (BUILTIN_DESC *)); + + array_add ((char *)newbuiltin, saved_builtins); +} + +/* Flags that mean something to write_documentation (). */ +#define STRING_ARRAY 0x01 +#define TEXINFO 0x02 +#define PLAINTEXT 0x04 +#define HELPFILE 0x08 + +char *structfile_header[] = { + "/* builtins.c -- the built in shell commands. */", + "", + "/* This file is manufactured by ./mkbuiltins, and should not be", + " edited by hand. See the source to mkbuiltins for details. */", + "", + "/* Copyright (C) 1987-2009 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/>.", + "*/", + "", + "/* The list of shell builtins. Each element is name, function, flags,", + " long-doc, short-doc. The long-doc field contains a pointer to an array", + " of help lines. The function takes a WORD_LIST *; the first word in the", + " list is the first arg to the command. The list has already had word", + " expansion performed.", + "", + " Functions which need to look at only the simple commands (e.g.", + " the enable_builtin ()), should ignore entries where", + " (array[i].function == (sh_builtin_func_t *)NULL). Such entries are for", + " the list of shell reserved control structures, like `if' and `while'.", + " The end of the list is denoted with a NULL name field. */", + "", + "#include \"../builtins.h\"", + (char *)NULL + }; + +char *structfile_footer[] = { + " { (char *)0x0, (sh_builtin_func_t *)0x0, 0, (char **)0x0, (char *)0x0 }", + "};", + "", + "struct builtin *shell_builtins = static_shell_builtins;", + "struct builtin *current_builtin;", + "", + "int num_shell_builtins =", + "\tsizeof (static_shell_builtins) / sizeof (struct builtin) - 1;", + (char *)NULL +}; + +/* Write out any neccessary opening information for + STRUCTFILE and EXTERNFILE. */ +void +write_file_headers (structfile, externfile) + FILE *structfile, *externfile; +{ + register int i; + + if (structfile) + { + for (i = 0; structfile_header[i]; i++) + fprintf (structfile, "%s\n", structfile_header[i]); + + fprintf (structfile, "#include \"%s\"\n", + extern_filename ? extern_filename : "builtext.h"); + + fprintf (structfile, "#include \"bashintl.h\"\n"); + + fprintf (structfile, "\nstruct builtin static_shell_builtins[] = {\n"); + } + + if (externfile) + fprintf (externfile, + "/* %s - The list of builtins found in libbuiltins.a. */\n", + extern_filename ? extern_filename : "builtext.h"); +} + +/* Write out any necessary closing information for + STRUCTFILE and EXTERNFILE. */ +void +write_file_footers (structfile, externfile) + FILE *structfile, *externfile; +{ + register int i; + + /* Write out the footers. */ + if (structfile) + { + for (i = 0; structfile_footer[i]; i++) + fprintf (structfile, "%s\n", structfile_footer[i]); + } +} + +/* Write out the information accumulated in DEFS to + STRUCTFILE and EXTERNFILE. */ +void +write_builtins (defs, structfile, externfile) + DEF_FILE *defs; + FILE *structfile, *externfile; +{ + register int i; + + /* Write out the information. */ + if (defs->builtins) + { + register BUILTIN_DESC *builtin; + + for (i = 0; i < defs->builtins->sindex; i++) + { + builtin = (BUILTIN_DESC *)defs->builtins->array[i]; + + /* Write out any #ifdefs that may be there. */ + if (!only_documentation) + { + if (builtin->dependencies) + { + write_ifdefs (externfile, builtin->dependencies->array); + write_ifdefs (structfile, builtin->dependencies->array); + } + + /* Write the extern definition. */ + if (externfile) + { + if (builtin->function) + fprintf (externfile, "extern int %s __P((WORD_LIST *));\n", + builtin->function); + + fprintf (externfile, "extern char * const %s_doc[];\n", + document_name (builtin)); + } + + /* Write the structure definition. */ + if (structfile) + { + fprintf (structfile, " { \"%s\", ", builtin->name); + + if (builtin->function) + fprintf (structfile, "%s, ", builtin->function); + else + fprintf (structfile, "(sh_builtin_func_t *)0x0, "); + + fprintf (structfile, "%s%s%s%s, %s_doc,\n", + "BUILTIN_ENABLED | STATIC_BUILTIN", + (builtin->flags & BUILTIN_FLAG_SPECIAL) ? " | SPECIAL_BUILTIN" : "", + (builtin->flags & BUILTIN_FLAG_ASSIGNMENT) ? " | ASSIGNMENT_BUILTIN" : "", + (builtin->flags & BUILTIN_FLAG_POSIX_BUILTIN) ? " | POSIX_BUILTIN" : "", + document_name (builtin)); + + fprintf + (structfile, " N_(\"%s\"), (char *)NULL },\n", + builtin->shortdoc ? builtin->shortdoc : builtin->name); + + } + + if (structfile || separate_helpfiles) + /* Save away this builtin for later writing of the + long documentation strings. */ + save_builtin (builtin); + + /* Write out the matching #endif, if neccessary. */ + if (builtin->dependencies) + { + if (externfile) + write_endifs (externfile, builtin->dependencies->array); + + if (structfile) + write_endifs (structfile, builtin->dependencies->array); + } + } + + if (documentation_file) + { + fprintf (documentation_file, "@item %s\n", builtin->name); + write_documentation + (documentation_file, builtin->longdoc->array, 0, TEXINFO); + } + } + } +} + +/* Write out the long documentation strings in BUILTINS to STREAM. */ +void +write_longdocs (stream, builtins) + FILE *stream; + ARRAY *builtins; +{ + register int i; + register BUILTIN_DESC *builtin; + char *dname; + char *sarray[2]; + + for (i = 0; i < builtins->sindex; i++) + { + builtin = (BUILTIN_DESC *)builtins->array[i]; + + if (builtin->dependencies) + write_ifdefs (stream, builtin->dependencies->array); + + /* Write the long documentation strings. */ + dname = document_name (builtin); + fprintf (stream, "char * const %s_doc[] =", dname); + + if (separate_helpfiles) + { + int l = strlen (helpfile_directory) + strlen (dname) + 1; + sarray[0] = (char *)xmalloc (l + 1); + sprintf (sarray[0], "%s/%s", helpfile_directory, dname); + sarray[1] = (char *)NULL; + write_documentation (stream, sarray, 0, STRING_ARRAY|HELPFILE); + free (sarray[0]); + } + else + write_documentation (stream, builtin->longdoc->array, 0, STRING_ARRAY); + + if (builtin->dependencies) + write_endifs (stream, builtin->dependencies->array); + + } +} + +/* Write an #ifdef string saying what needs to be defined (or not defined) + in order to allow compilation of the code that will follow. + STREAM is the stream to write the information to, + DEFINES is a null terminated array of define names. + If a define is preceded by an `!', then the sense of the test is + reversed. */ +void +write_ifdefs (stream, defines) + FILE *stream; + char **defines; +{ + register int i; + + if (!stream) + return; + + fprintf (stream, "#if "); + + for (i = 0; defines[i]; i++) + { + char *def = defines[i]; + + if (*def == '!') + fprintf (stream, "!defined (%s)", def + 1); + else + fprintf (stream, "defined (%s)", def); + + if (defines[i + 1]) + fprintf (stream, " && "); + } + fprintf (stream, "\n"); +} + +/* Write an #endif string saying what defines controlled the compilation + of the immediately preceding code. + STREAM is the stream to write the information to. + DEFINES is a null terminated array of define names. */ +void +write_endifs (stream, defines) + FILE *stream; + char **defines; +{ + register int i; + + if (!stream) + return; + + fprintf (stream, "#endif /* "); + + for (i = 0; defines[i]; i++) + { + fprintf (stream, "%s", defines[i]); + + if (defines[i + 1]) + fprintf (stream, " && "); + } + + fprintf (stream, " */\n"); +} + +/* Write DOCUMENTATION to STREAM, perhaps surrounding it with double-quotes + and quoting special characters in the string. Handle special things for + internationalization (gettext) and the single-string vs. multiple-strings + issues. */ +void +write_documentation (stream, documentation, indentation, flags) + FILE *stream; + char **documentation; + int indentation, flags; +{ + register int i, j; + register char *line; + int string_array, texinfo, base_indent, last_cpp, filename_p; + + if (stream == 0) + return; + + string_array = flags & STRING_ARRAY; + filename_p = flags & HELPFILE; + + if (string_array) + { + fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n"); /* } */ + if (single_longdoc_strings) + { + if (filename_p == 0) + { + if (documentation && documentation[0] && documentation[0][0]) + fprintf (stream, "N_(\""); + else + fprintf (stream, "N_(\" "); /* the empty string translates specially. */ + } + else + fprintf (stream, "\""); + } + } + + base_indent = (string_array && single_longdoc_strings && filename_p == 0) ? BASE_INDENT : 0; + + for (i = last_cpp = 0, texinfo = (flags & TEXINFO); line = documentation[i]; i++) + { + /* Allow #ifdef's to be written out verbatim, but don't put them into + separate help files. */ + if (*line == '#') + { + if (string_array && filename_p == 0 && single_longdoc_strings == 0) + fprintf (stream, "%s\n", line); + last_cpp = 1; + continue; + } + else + last_cpp = 0; + + /* prefix with N_( for gettext */ + if (string_array && single_longdoc_strings == 0) + { + if (filename_p == 0) + { + if (line[0]) + fprintf (stream, " N_(\""); + else + fprintf (stream, " N_(\" "); /* the empty string translates specially. */ + } + else + fprintf (stream, " \""); + } + + if (indentation) + for (j = 0; j < indentation; j++) + fprintf (stream, " "); + + /* Don't indent the first line, because of how the help builtin works. */ + if (i == 0) + indentation += base_indent; + + if (string_array) + { + for (j = 0; line[j]; j++) + { + switch (line[j]) + { + case '\\': + case '"': + fprintf (stream, "\\%c", line[j]); + break; + + default: + fprintf (stream, "%c", line[j]); + } + } + + /* closing right paren for gettext */ + if (single_longdoc_strings == 0) + { + if (filename_p == 0) + fprintf (stream, "\"),\n"); + else + fprintf (stream, "\",\n"); + } + else if (documentation[i+1]) + /* don't add extra newline after last line */ + fprintf (stream, "\\n\\\n"); + } + else if (texinfo) + { + for (j = 0; line[j]; j++) + { + switch (line[j]) + { + case '@': + case '{': + case '}': + fprintf (stream, "@%c", line[j]); + break; + + default: + fprintf (stream, "%c", line[j]); + } + } + fprintf (stream, "\n"); + } + else + fprintf (stream, "%s\n", line); + } + + /* closing right paren for gettext */ + if (string_array && single_longdoc_strings) + { + if (filename_p == 0) + fprintf (stream, "\"),\n"); + else + fprintf (stream, "\",\n"); + } + + if (string_array) + fprintf (stream, "#endif /* HELP_BUILTIN */\n (char *)NULL\n};\n"); +} + +int +write_helpfiles (builtins) + ARRAY *builtins; +{ + char *helpfile, *bname; + FILE *helpfp; + int i, hdlen; + BUILTIN_DESC *builtin; + + i = mkdir ("helpfiles", 0777); + if (i < 0 && errno != EEXIST) + { + fprintf (stderr, "write_helpfiles: helpfiles: cannot create directory\n"); + return -1; + } + + hdlen = strlen ("helpfiles/"); + for (i = 0; i < builtins->sindex; i++) + { + builtin = (BUILTIN_DESC *)builtins->array[i]; + + bname = document_name (builtin); + helpfile = (char *)xmalloc (hdlen + strlen (bname) + 1); + sprintf (helpfile, "helpfiles/%s", bname); + + helpfp = fopen (helpfile, "w"); + if (helpfp == 0) + { + fprintf (stderr, "write_helpfiles: cannot open %s\n", helpfile); + free (helpfile); + continue; + } + + write_documentation (helpfp, builtin->longdoc->array, 4, PLAINTEXT); + + fflush (helpfp); + fclose (helpfp); + free (helpfile); + } + return 0; +} + +static int +_find_in_table (name, name_table) + char *name, *name_table[]; +{ + register int i; + + for (i = 0; name_table[i]; i++) + if (strcmp (name, name_table[i]) == 0) + return 1; + return 0; +} + +static int +is_special_builtin (name) + char *name; +{ + return (_find_in_table (name, special_builtins)); +} + +static int +is_assignment_builtin (name) + char *name; +{ + return (_find_in_table (name, assignment_builtins)); +} + +static int +is_posix_builtin (name) + char *name; +{ + return (_find_in_table (name, posix_builtins)); +} + +#if !defined (HAVE_RENAME) +static int +rename (from, to) + char *from, *to; +{ + unlink (to); + if (link (from, to) < 0) + return (-1); + unlink (from); + return (0); +} +#endif /* !HAVE_RENAME */ diff --git a/builtins/printf.def b/builtins/printf.def new file mode 100644 index 0000000..277566f --- /dev/null +++ b/builtins/printf.def @@ -0,0 +1,1118 @@ +This file is printf.def, from which is created printf.c. +It implements the builtin "printf" in Bash. + +Copyright (C) 1997-2009 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/>. + +$PRODUCES printf.c + +$BUILTIN printf +$FUNCTION printf_builtin +$SHORT_DOC printf [-v var] format [arguments] +Formats and prints ARGUMENTS under control of the FORMAT. + +Options: + -v var assign the output to shell variable VAR rather than + display it on the standard output + +FORMAT is a character string which contains three types of objects: plain +characters, which are simply copied to standard output; character escape +sequences, which are converted and copied to the standard output; and +format specifications, each of which causes printing of the next successive +argument. + +In addition to the standard format specifications described in printf(1) +and printf(3), printf interprets: + + %b expand backslash escape sequences in the corresponding argument + %q quote the argument in a way that can be reused as shell input + +Exit Status: +Returns success unless an invalid option is given or a write or assignment +error occurs. +$END + +#include <config.h> + +#include "../bashtypes.h" + +#include <errno.h> +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#else + /* Assume 32-bit ints. */ +# define INT_MAX 2147483647 +# define INT_MIN (-2147483647-1) +#endif + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include <stdio.h> +#include <chartypes.h> + +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "shmbutil.h" +#include "stdc.h" +#include "bashgetopt.h" +#include "common.h" + +#if defined (PRI_MACROS_BROKEN) +# undef PRIdMAX +#endif + +#if !defined (PRIdMAX) +# if HAVE_LONG_LONG +# define PRIdMAX "lld" +# else +# define PRIdMAX "ld" +# endif +#endif + +#if !defined (errno) +extern int errno; +#endif + +#define PC(c) \ + do { \ + char b[2]; \ + tw++; \ + b[0] = c; b[1] = '\0'; \ + if (vflag) \ + vbadd (b, 1); \ + else \ + putchar (c); \ + } while (0) + +#define PF(f, func) \ + do { \ + int nw; \ + clearerr (stdout); \ + if (have_fieldwidth && have_precision) \ + nw = vflag ? vbprintf (f, fieldwidth, precision, func) : printf (f, fieldwidth, precision, func); \ + else if (have_fieldwidth) \ + nw = vflag ? vbprintf (f, fieldwidth, func) : printf (f, fieldwidth, func); \ + else if (have_precision) \ + nw = vflag ? vbprintf (f, precision, func) : printf (f, precision, func); \ + else \ + nw = vflag ? vbprintf (f, func) : printf (f, func); \ + tw += nw; \ + if (ferror (stdout)) \ + { \ + sh_wrerror (); \ + clearerr (stdout); \ + return (EXECUTION_FAILURE); \ + } \ + } while (0) + +/* We free the buffer used by mklong() if it's `too big'. */ +#define PRETURN(value) \ + do \ + { \ + if (vflag) \ + { \ + bind_printf_variable (vname, vbuf, 0); \ + stupidly_hack_special_variables (vname); \ + } \ + if (conv_bufsize > 4096 ) \ + { \ + free (conv_buf); \ + conv_bufsize = 0; \ + conv_buf = 0; \ + } \ + if (vbsize > 4096) \ + { \ + free (vbuf); \ + vbsize = 0; \ + vbuf = 0; \ + } \ + else if (vbuf) \ + vbuf[0] = 0; \ + terminate_immediately--; \ + fflush (stdout); \ + if (ferror (stdout)) \ + { \ + sh_wrerror (); \ + clearerr (stdout); \ + return (EXECUTION_FAILURE); \ + } \ + return (value); \ + } \ + while (0) + +#define SKIP1 "#'-+ 0" +#define LENMODS "hjlLtz" + +#if !HAVE_ASPRINTF +extern int asprintf __P((char **, const char *, ...)) __attribute__((__format__ (printf, 2, 3))); +#endif + +#if !HAVE_VSNPRINTF +extern int vsnprintf __P((char *, size_t, const char *, va_list)) __attribute__((__format__ (printf, 3, 0))); +#endif + +static void printf_erange __P((char *)); +static int printstr __P((char *, char *, int, int, int)); +static int tescape __P((char *, char *, int *)); +static char *bexpand __P((char *, int, int *, int *)); +static char *vbadd __P((char *, int)); +static int vbprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2))); +static char *mklong __P((char *, char *, size_t)); +static int getchr __P((void)); +static char *getstr __P((void)); +static int getint __P((void)); +static intmax_t getintmax __P((void)); +static uintmax_t getuintmax __P((void)); +static SHELL_VAR *bind_printf_variable __P((char *, char *, int)); + +#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN) +typedef long double floatmax_t; +# define FLOATMAX_CONV "L" +# define strtofltmax strtold +#else +typedef double floatmax_t; +# define FLOATMAX_CONV "" +# define strtofltmax strtod +#endif +static floatmax_t getfloatmax __P((void)); + +static intmax_t asciicode __P((void)); + +static WORD_LIST *garglist; +static int retval; +static int conversion_error; + +/* printf -v var support */ +static int vflag = 0; +static char *vbuf, *vname; +static size_t vbsize; +static int vblen; + +static intmax_t tw; + +static char *conv_buf; +static size_t conv_bufsize; + +int +printf_builtin (list) + WORD_LIST *list; +{ + int ch, fieldwidth, precision; + int have_fieldwidth, have_precision; + char convch, thisch, nextch, *format, *modstart, *fmt, *start; + + conversion_error = 0; + retval = EXECUTION_SUCCESS; + + vflag = 0; + + reset_internal_getopt (); + while ((ch = internal_getopt (list, "v:")) != -1) + { + switch (ch) + { + case 'v': + vname = list_optarg; +#if defined (ARRAY_VARS) + if (legal_identifier (vname) || valid_array_reference (vname)) +#else + if (legal_identifier (vname)) +#endif + { + vflag = 1; + vblen = 0; + if (vbuf) + vbuf[0] = 0; + } + else + { + sh_invalidid (vname); + return (EX_USAGE); + } + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; /* skip over possible `--' */ + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (list->word->word == 0 || list->word->word[0] == '\0') + return (EXECUTION_SUCCESS); + + format = list->word->word; + tw = 0; + + garglist = list->next; + + /* If the format string is empty after preprocessing, return immediately. */ + if (format == 0 || *format == 0) + return (EXECUTION_SUCCESS); + + terminate_immediately++; + + /* Basic algorithm is to scan the format string for conversion + specifications -- once one is found, find out if the field + width or precision is a '*'; if it is, gather up value. Note, + format strings are reused as necessary to use up the provided + arguments, arguments of zero/null string are provided to use + up the format string. */ + do + { + tw = 0; + /* find next format specification */ + for (fmt = format; *fmt; fmt++) + { + precision = fieldwidth = 0; + have_fieldwidth = have_precision = 0; + + if (*fmt == '\\') + { + fmt++; + /* A NULL third argument to tescape means to bypass the + special processing for arguments to %b. */ + fmt += tescape (fmt, &nextch, (int *)NULL); + PC (nextch); + fmt--; /* for loop will increment it for us again */ + continue; + } + + if (*fmt != '%') + { + PC (*fmt); + continue; + } + + /* ASSERT(*fmt == '%') */ + start = fmt++; + + if (*fmt == '%') /* %% prints a % */ + { + PC ('%'); + continue; + } + + /* found format specification, skip to field width */ + for (; *fmt && strchr(SKIP1, *fmt); ++fmt) + ; + + /* Skip optional field width. */ + if (*fmt == '*') + { + fmt++; + have_fieldwidth = 1; + fieldwidth = getint (); + } + else + while (DIGIT (*fmt)) + fmt++; + + /* Skip optional '.' and precision */ + if (*fmt == '.') + { + ++fmt; + if (*fmt == '*') + { + fmt++; + have_precision = 1; + precision = getint (); + } + else + { + /* Negative precisions are allowed but treated as if the + precision were missing; I would like to allow a leading + `+' in the precision number as an extension, but lots + of asprintf/fprintf implementations get this wrong. */ +#if 0 + if (*fmt == '-' || *fmt == '+') +#else + if (*fmt == '-') +#endif + fmt++; + while (DIGIT (*fmt)) + fmt++; + } + } + + /* skip possible format modifiers */ + modstart = fmt; + while (*fmt && strchr (LENMODS, *fmt)) + fmt++; + + if (*fmt == 0) + { + builtin_error (_("`%s': missing format character"), start); + PRETURN (EXECUTION_FAILURE); + } + + convch = *fmt; + thisch = modstart[0]; + nextch = modstart[1]; + modstart[0] = convch; + modstart[1] = '\0'; + + switch(convch) + { + case 'c': + { + char p; + + p = getchr (); + PF(start, p); + break; + } + + case 's': + { + char *p; + + p = getstr (); + PF(start, p); + break; + } + + case 'n': + { + char *var; + + var = getstr (); + if (var && *var) + { + if (legal_identifier (var)) + bind_var_to_int (var, tw); + else + { + sh_invalidid (var); + PRETURN (EXECUTION_FAILURE); + } + } + break; + } + + case 'b': /* expand escapes in argument */ + { + char *p, *xp; + int rlen, r; + + p = getstr (); + ch = rlen = r = 0; + xp = bexpand (p, strlen (p), &ch, &rlen); + + if (xp) + { + /* Have to use printstr because of possible NUL bytes + in XP -- printf does not handle that well. */ + r = printstr (start, xp, rlen, fieldwidth, precision); + if (r < 0) + { + sh_wrerror (); + clearerr (stdout); + retval = EXECUTION_FAILURE; + } + free (xp); + } + + if (ch || r < 0) + PRETURN (retval); + break; + } + + case 'q': /* print with shell quoting */ + { + char *p, *xp; + int r; + + r = 0; + p = getstr (); + if (p && *p == 0) /* XXX - getstr never returns null */ + xp = savestring ("''"); + else if (ansic_shouldquote (p)) + xp = ansic_quote (p, 0, (int *)0); + else + xp = sh_backslash_quote (p); + if (xp) + { + /* Use printstr to get fieldwidth and precision right. */ + r = printstr (start, xp, strlen (xp), fieldwidth, precision); + if (r < 0) + { + sh_wrerror (); + clearerr (stdout); + } + free (xp); + } + + if (r < 0) + PRETURN (EXECUTION_FAILURE); + break; + } + + case 'd': + case 'i': + { + char *f; + long p; + intmax_t pp; + + p = pp = getintmax (); + if (p != pp) + { + f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); + PF (f, pp); + } + else + { + /* Optimize the common case where the integer fits + in "long". This also works around some long + long and/or intmax_t library bugs in the common + case, e.g. glibc 2.2 x86. */ + f = mklong (start, "l", 1); + PF (f, p); + } + break; + } + + case 'o': + case 'u': + case 'x': + case 'X': + { + char *f; + unsigned long p; + uintmax_t pp; + + p = pp = getuintmax (); + if (p != pp) + { + f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); + PF (f, pp); + } + else + { + f = mklong (start, "l", 1); + PF (f, p); + } + break; + } + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': +#if defined (HAVE_PRINTF_A_FORMAT) + case 'a': + case 'A': +#endif + { + char *f; + floatmax_t p; + + p = getfloatmax (); + f = mklong (start, FLOATMAX_CONV, sizeof(FLOATMAX_CONV) - 1); + PF (f, p); + break; + } + + /* We don't output unrecognized format characters; we print an + error message and return a failure exit status. */ + default: + builtin_error (_("`%c': invalid format character"), convch); + PRETURN (EXECUTION_FAILURE); + } + + modstart[0] = thisch; + modstart[1] = nextch; + } + + if (ferror (stdout)) + { + sh_wrerror (); + clearerr (stdout); + PRETURN (EXECUTION_FAILURE); + } + } + while (garglist && garglist != list->next); + + if (conversion_error) + retval = EXECUTION_FAILURE; + + PRETURN (retval); +} + +static void +printf_erange (s) + char *s; +{ + builtin_error (_("warning: %s: %s"), s, strerror(ERANGE)); +} + +/* We duplicate a lot of what printf(3) does here. */ +static int +printstr (fmt, string, len, fieldwidth, precision) + char *fmt; /* format */ + char *string; /* expanded string argument */ + int len; /* length of expanded string */ + int fieldwidth; /* argument for width of `*' */ + int precision; /* argument for precision of `*' */ +{ +#if 0 + char *s; +#endif + int padlen, nc, ljust, i; + int fw, pr; /* fieldwidth and precision */ + +#if 0 + if (string == 0 || *string == '\0') +#else + if (string == 0 || len == 0) +#endif + return 0; + +#if 0 + s = fmt; +#endif + if (*fmt == '%') + fmt++; + + ljust = fw = 0; + pr = -1; + + /* skip flags */ + while (strchr (SKIP1, *fmt)) + { + if (*fmt == '-') + ljust = 1; + fmt++; + } + + /* get fieldwidth, if present */ + if (*fmt == '*') + { + fmt++; + fw = fieldwidth; + if (fw < 0) + { + fw = -fw; + ljust = 1; + } + } + else if (DIGIT (*fmt)) + { + fw = *fmt++ - '0'; + while (DIGIT (*fmt)) + fw = (fw * 10) + (*fmt++ - '0'); + } + + /* get precision, if present */ + if (*fmt == '.') + { + fmt++; + if (*fmt == '*') + { + fmt++; + pr = precision; + } + else if (DIGIT (*fmt)) + { + pr = *fmt++ - '0'; + while (DIGIT (*fmt)) + pr = (pr * 10) + (*fmt++ - '0'); + } + } + +#if 0 + /* If we remove this, get rid of `s'. */ + if (*fmt != 'b' && *fmt != 'q') + { + internal_error ("format parsing problem: %s", s); + fw = pr = 0; + } +#endif + + /* chars from string to print */ + nc = (pr >= 0 && pr <= len) ? pr : len; + + padlen = fw - nc; + if (padlen < 0) + padlen = 0; + if (ljust) + padlen = -padlen; + + /* leading pad characters */ + for (; padlen > 0; padlen--) + PC (' '); + + /* output NC characters from STRING */ + for (i = 0; i < nc; i++) + PC (string[i]); + + /* output any necessary trailing padding */ + for (; padlen < 0; padlen++) + PC (' '); + + return (ferror (stdout) ? -1 : 0); +} + +/* Convert STRING by expanding the escape sequences specified by the + POSIX standard for printf's `%b' format string. If SAWC is non-null, + perform the processing appropriate for %b arguments. In particular, + recognize `\c' and use that as a string terminator. If we see \c, set + *SAWC to 1 before returning. LEN is the length of STRING. */ + +/* Translate a single backslash-escape sequence starting at ESTART (the + character after the backslash) and return the number of characters + consumed by the sequence. CP is the place to return the translated + value. *SAWC is set to 1 if the escape sequence was \c, since that means + to short-circuit the rest of the processing. If SAWC is null, we don't + do the \c short-circuiting, and \c is treated as an unrecognized escape + sequence; we also bypass the other processing specific to %b arguments. */ +static int +tescape (estart, cp, sawc) + char *estart; + char *cp; + int *sawc; +{ + register char *p; + int temp, c, evalue; + + p = estart; + + switch (c = *p++) + { +#if defined (__STDC__) + case 'a': *cp = '\a'; break; +#else + case 'a': *cp = '\007'; break; +#endif + + case 'b': *cp = '\b'; break; + + case 'e': + case 'E': *cp = '\033'; break; /* ESC -- non-ANSI */ + + case 'f': *cp = '\f'; break; + + case 'n': *cp = '\n'; break; + + case 'r': *cp = '\r'; break; + + case 't': *cp = '\t'; break; + + case 'v': *cp = '\v'; break; + + /* The octal escape sequences are `\0' followed by up to three octal + digits (if SAWC), or `\' followed by up to three octal digits (if + !SAWC). As an extension, we allow the latter form even if SAWC. */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + evalue = OCTVALUE (c); + for (temp = 2 + (!evalue && !!sawc); ISOCTAL (*p) && temp--; p++) + evalue = (evalue * 8) + OCTVALUE (*p); + *cp = evalue & 0xFF; + break; + + /* And, as another extension, we allow \xNNN, where each N is a + hex digit. */ + case 'x': +#if 0 + for (evalue = 0; ISXDIGIT ((unsigned char)*p); p++) +#else + for (temp = 2, evalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++) +#endif + evalue = (evalue * 16) + HEXVALUE (*p); + if (p == estart + 1) + { + builtin_error (_("missing hex digit for \\x")); + *cp = '\\'; + return 0; + } + *cp = evalue & 0xFF; + break; + + case '\\': /* \\ -> \ */ + *cp = c; + break; + + /* SAWC == 0 means that \', \", and \? are recognized as escape + sequences, though the only processing performed is backslash + removal. */ + case '\'': case '"': case '?': + if (!sawc) + *cp = c; + else + { + *cp = '\\'; + return 0; + } + break; + + case 'c': + if (sawc) + { + *sawc = 1; + break; + } + /* other backslash escapes are passed through unaltered */ + default: + *cp = '\\'; + return 0; + } + return (p - estart); +} + +static char * +bexpand (string, len, sawc, lenp) + char *string; + int len, *sawc, *lenp; +{ + int temp; + char *ret, *r, *s, c; + +#if 0 + if (string == 0 || *string == '\0') +#else + if (string == 0 || len == 0) +#endif + { + if (sawc) + *sawc = 0; + if (lenp) + *lenp = 0; + return ((char *)NULL); + } + + ret = (char *)xmalloc (len + 1); + for (r = ret, s = string; s && *s; ) + { + c = *s++; + if (c != '\\' || *s == '\0') + { + *r++ = c; + continue; + } + temp = 0; + s += tescape (s, &c, &temp); + if (temp) + { + if (sawc) + *sawc = 1; + break; + } + + *r++ = c; + } + + *r = '\0'; + if (lenp) + *lenp = r - ret; + return ret; +} + +static char * +vbadd (buf, blen) + char *buf; + int blen; +{ + size_t nlen; + + nlen = vblen + blen + 1; + if (nlen >= vbsize) + { + vbsize = ((nlen + 63) >> 6) << 6; + vbuf = (char *)xrealloc (vbuf, vbsize); + } + + if (blen == 1) + vbuf[vblen++] = buf[0]; + else if (blen > 1) + { + FASTCOPY (buf, vbuf + vblen, blen); + vblen += blen; + } + vbuf[vblen] = '\0'; + +#ifdef DEBUG + if (strlen (vbuf) != vblen) + internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, (int)strlen (vbuf)); +#endif + + return vbuf; +} + +static int +#if defined (PREFER_STDARG) +vbprintf (const char *format, ...) +#else +vbprintf (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + size_t nlen; + int blen; + + SH_VA_START (args, format); + blen = vsnprintf (vbuf + vblen, vbsize - vblen, format, args); + va_end (args); + + nlen = vblen + blen + 1; + if (nlen >= vbsize) + { + vbsize = ((nlen + 63) >> 6) << 6; + vbuf = (char *)xrealloc (vbuf, vbsize); + SH_VA_START (args, format); + blen = vsnprintf (vbuf + vblen, vbsize - vblen, format, args); + va_end (args); + } + + vblen += blen; + vbuf[vblen] = '\0'; + +#ifdef DEBUG + if (strlen (vbuf) != vblen) + internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, (int)strlen (vbuf)); +#endif + + return (blen); +} + +static char * +mklong (str, modifiers, mlen) + char *str; + char *modifiers; + size_t mlen; +{ + size_t len, slen; + + slen = strlen (str); + len = slen + mlen + 1; + + if (len > conv_bufsize) + { + conv_bufsize = (((len + 1023) >> 10) << 10); + conv_buf = (char *)xrealloc (conv_buf, conv_bufsize); + } + + FASTCOPY (str, conv_buf, slen - 1); + FASTCOPY (modifiers, conv_buf + slen - 1, mlen); + + conv_buf[len - 2] = str[slen - 1]; + conv_buf[len - 1] = '\0'; + return (conv_buf); +} + +static int +getchr () +{ + int ret; + + if (garglist == 0) + return ('\0'); + + ret = (int)garglist->word->word[0]; + garglist = garglist->next; + return ret; +} + +static char * +getstr () +{ + char *ret; + + if (garglist == 0) + return (""); + + ret = garglist->word->word; + garglist = garglist->next; + return ret; +} + +static int +getint () +{ + intmax_t ret; + + ret = getintmax (); + + if (ret > INT_MAX) + { + printf_erange (garglist->word->word); + ret = INT_MAX; + } + else if (ret < INT_MIN) + { + printf_erange (garglist->word->word); + ret = INT_MIN; + } + + return ((int)ret); +} + +static intmax_t +getintmax () +{ + intmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtoimax (garglist->word->word, &ep, 0); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* POSIX.2 says ``...a diagnostic message shall be written to standard + error, and the utility shall not exit with a zero exit status, but + shall continue processing any remaining operands and shall write the + value accumulated at the time the error was detected to standard + output.'' Yecch. */ +#if 0 + ret = 0; /* return partially-converted value from strtoimax */ +#endif + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +static uintmax_t +getuintmax () +{ + uintmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtoumax (garglist->word->word, &ep, 0); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* Same POSIX.2 conversion error requirements as getintmax(). */ + ret = 0; + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +static floatmax_t +getfloatmax () +{ + floatmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtofltmax (garglist->word->word, &ep); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* Same thing about POSIX.2 conversion error requirements. */ + ret = 0; + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +/* NO check is needed for garglist here. */ +static intmax_t +asciicode () +{ + register intmax_t ch; +#if defined (HANDLE_MULTIBYTE) + wchar_t wc; + size_t mblength, slen; +#endif + DECLARE_MBSTATE; + +#if defined (HANDLE_MULTIBYTE) + slen = strlen (garglist->word->word+1); + mblength = MBLEN (garglist->word->word+1, slen); + if (mblength > 1) + { + mblength = mbtowc (&wc, garglist->word->word+1, slen); + ch = wc; /* XXX */ + } + else +#endif + ch = (unsigned char)garglist->word->word[1]; + + garglist = garglist->next; + return (ch); +} + +static SHELL_VAR * +bind_printf_variable (name, value, flags) + char *name; + char *value; + int flags; +{ +#if defined (ARRAY_VARS) + if (valid_array_reference (name) == 0) + return (bind_variable (name, value, flags)); + else + return (assign_array_element (name, value, flags)); +#else /* !ARRAY_VARS */ + return bind_variable (name, value, flags); +#endif /* !ARRAY_VARS */ +} diff --git a/builtins/psize.c b/builtins/psize.c new file mode 100644 index 0000000..30881fb --- /dev/null +++ b/builtins/psize.c @@ -0,0 +1,79 @@ +/* psize.c - Find pipe size. */ + +/* Copyright (C) 1987, 1991 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Write output in 128-byte chunks until we get a sigpipe or write gets an + EPIPE. Then report how many bytes we wrote. We assume that this is the + pipe size. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#ifndef _MINIX +#include "../bashtypes.h" +#endif +#include <signal.h> +#include <errno.h> + +#include "../command.h" +#include "../general.h" +#include "../sig.h" + +#ifndef errno +extern int errno; +#endif + +int nw; + +sighandler +sigpipe (sig) + int sig; +{ + fprintf (stderr, "%d\n", nw); + exit (0); +} + +int +main (argc, argv) + int argc; + char **argv; +{ + char buf[128]; + register int i; + + for (i = 0; i < 128; i++) + buf[i] = ' '; + + signal (SIGPIPE, sigpipe); + + nw = 0; + for (;;) + { + int n; + n = write (1, buf, 128); + nw += n; + } + return (0); +} diff --git a/builtins/psize.sh b/builtins/psize.sh new file mode 100644 index 0000000..c4d73e1 --- /dev/null +++ b/builtins/psize.sh @@ -0,0 +1,45 @@ +#! /bin/sh +# +# psize.sh -- determine this system's pipe size, and write a define to +# pipesize.h so ulimit.c can use it. + +: ${TMPDIR:=/tmp} +# try to use mktemp(1) if the system supports it +{ TMPFILE="`mktemp $TMPDIR/pipsize.XXXXXX 2>/dev/null`"; } 2>/dev/null +used_mktemp=true + +if [ -z "$TMPFILE" ]; then + TMPNAME=pipsize.$$ + TMPFILE=$TMPDIR/$TMPNAME + used_mktemp=false +fi + +trap 'rm -f "$TMPFILE" ; exit 1' 1 2 3 6 15 +trap 'rm -f "$TMPFILE"' 0 + +echo "/*" +echo " * pipesize.h" +echo " *" +echo " * This file is automatically generated by psize.sh" +echo " * Do not edit!" +echo " */" +echo "" + +# +# Try to avoid tempfile races. We can't really check for the file's +# existance before we run psize.aux, because `test -e' is not portable, +# `test -h' (test for symlinks) is not portable, and `test -f' only +# checks for regular files. If we used mktemp(1), we're ahead of the +# game. +# +$used_mktemp || rm -f "$TMPFILE" + +./psize.aux 2>"$TMPFILE" | sleep 3 + +if [ -s "$TMPFILE" ]; then + echo "#define PIPESIZE `cat "$TMPFILE"`" +else + echo "#define PIPESIZE 512" +fi + +exit 0 diff --git a/builtins/pushd.def b/builtins/pushd.def new file mode 100644 index 0000000..05b7529 --- /dev/null +++ b/builtins/pushd.def @@ -0,0 +1,778 @@ +This file is pushd.def, from which is created pushd.c. It implements the +builtins "pushd", "popd", and "dirs" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES pushd.c + +$BUILTIN pushd +$FUNCTION pushd_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC pushd [-n] [+N | -N | dir] +Add directories to stack. + +Adds a directory to the top of the directory stack, or rotates +the stack, making the new top of the stack the current working +directory. With no arguments, exchanges the top two directories. + +Options: + -n Suppresses the normal change of directory when adding + directories to the stack, so only the stack is manipulated. + +Arguments: + +N Rotates the stack so that the Nth directory (counting + from the left of the list shown by `dirs', starting with + zero) is at the top. + + -N Rotates the stack so that the Nth directory (counting + from the right of the list shown by `dirs', starting with + zero) is at the top. + + dir Adds DIR to the directory stack at the top, making it the + new current working directory. + +The `dirs' builtin displays the directory stack. + +Exit Status: +Returns success unless an invalid argument is supplied or the directory +change fails. +$END + +$BUILTIN popd +$FUNCTION popd_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC popd [-n] [+N | -N] +Remove directories from stack. + +Removes entries from the directory stack. With no arguments, removes +the top directory from the stack, and changes to the new top directory. + +Options: + -n Suppresses the normal change of directory when removing + directories from the stack, so only the stack is manipulated. + +Arguments: + +N Removes the Nth entry counting from the left of the list + shown by `dirs', starting with zero. For example: `popd +0' + removes the first directory, `popd +1' the second. + + -N Removes the Nth entry counting from the right of the list + shown by `dirs', starting with zero. For example: `popd -0' + removes the last directory, `popd -1' the next to last. + +The `dirs' builtin displays the directory stack. + +Exit Status: +Returns success unless an invalid argument is supplied or the directory +change fails. +$END + +$BUILTIN dirs +$FUNCTION dirs_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC dirs [-clpv] [+N] [-N] +Display directory stack. + +Display the list of currently remembered directories. Directories +find their way onto the list with the `pushd' command; you can get +back up through the list with the `popd' command. + +Options: + -c clear the directory stack by deleting all of the elements + -l do not print tilde-prefixed versions of directories relative + to your home directory + -p print the directory stack with one entry per line + -v print the directory stack with one entry per line prefixed + with its position in the stack + +Arguments: + +N Displays the Nth entry counting from the left of the list shown by + dirs when invoked without options, starting with zero. + + -N Displays the Nth entry counting from the right of the list shown by + dirs when invoked without options, starting with zero. + +Exit Status: +Returns success unless an invalid option is supplied or an error occurs. +$END + +#include <config.h> + +#if defined (PUSHD_AND_POPD) +#include <stdio.h> +#ifndef _MINIX +# include <sys/param.h> +#endif + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include <errno.h> + +#include <tilde/tilde.h> + +#include "../shell.h" +#include "maxpath.h" +#include "common.h" +#include "builtext.h" + +#ifdef LOADABLE_BUILTIN +# include "builtins.h" +#endif + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* The list of remembered directories. */ +static char **pushd_directory_list = (char **)NULL; + +/* Number of existing slots in this list. */ +static int directory_list_size; + +/* Offset to the end of the list. */ +static int directory_list_offset; + +static void pushd_error __P((int, char *)); +static void clear_directory_stack __P((void)); +static int cd_to_string __P((char *)); +static int change_to_temp __P((char *)); +static void add_dirstack_element __P((char *)); +static int get_dirstack_index __P((intmax_t, int, int *)); + +#define NOCD 0x01 +#define ROTATE 0x02 +#define LONGFORM 0x04 +#define CLEARSTAK 0x08 + +int +pushd_builtin (list) + WORD_LIST *list; +{ + WORD_LIST *orig_list; + char *temp, *current_directory, *top; + int j, flags, skipopt; + intmax_t num; + char direction; + + orig_list = list; + if (list && list->word && ISOPTION (list->word->word, '-')) + { + list = list->next; + skipopt = 1; + } + else + skipopt = 0; + + /* If there is no argument list then switch current and + top of list. */ + if (list == 0) + { + if (directory_list_offset == 0) + { + builtin_error (_("no other directory")); + return (EXECUTION_FAILURE); + } + + current_directory = get_working_directory ("pushd"); + if (current_directory == 0) + return (EXECUTION_FAILURE); + + j = directory_list_offset - 1; + temp = pushd_directory_list[j]; + pushd_directory_list[j] = current_directory; + j = change_to_temp (temp); + free (temp); + return j; + } + + for (flags = 0; skipopt == 0 && list; list = list->next) + { + if (ISOPTION (list->word->word, 'n')) + { + flags |= NOCD; + } + else if (ISOPTION (list->word->word, '-')) + { + list = list->next; + break; + } + else if (list->word->word[0] == '-' && list->word->word[1] == '\0') + /* Let `pushd -' work like it used to. */ + break; + else if (((direction = list->word->word[0]) == '+') || direction == '-') + { + if (legal_number (list->word->word + 1, &num) == 0) + { + sh_invalidnum (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + + if (direction == '-') + num = directory_list_offset - num; + + if (num > directory_list_offset || num < 0) + { + pushd_error (directory_list_offset, list->word->word); + return (EXECUTION_FAILURE); + } + flags |= ROTATE; + } + else if (*list->word->word == '-') + { + sh_invalidopt (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + else + break; + } + + if (flags & ROTATE) + { + /* Rotate the stack num times. Remember, the current + directory acts like it is part of the stack. */ + temp = get_working_directory ("pushd"); + + if (num == 0) + { + j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS; + free (temp); + return j; + } + + do + { + top = pushd_directory_list[directory_list_offset - 1]; + + for (j = directory_list_offset - 2; j > -1; j--) + pushd_directory_list[j + 1] = pushd_directory_list[j]; + + pushd_directory_list[j + 1] = temp; + + temp = top; + num--; + } + while (num); + + j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS; + free (temp); + return j; + } + + if (list == 0) + return (EXECUTION_SUCCESS); + + /* Change to the directory in list->word->word. Save the current + directory on the top of the stack. */ + current_directory = get_working_directory ("pushd"); + if (current_directory == 0) + return (EXECUTION_FAILURE); + + j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS; + if (j == EXECUTION_SUCCESS) + { + add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory); + dirs_builtin ((WORD_LIST *)NULL); + if (flags & NOCD) + free (current_directory); + return (EXECUTION_SUCCESS); + } + else + { + free (current_directory); + return (EXECUTION_FAILURE); + } +} + +/* Pop the directory stack, and then change to the new top of the stack. + If LIST is non-null it should consist of a word +N or -N, which says + what element to delete from the stack. The default is the top one. */ +int +popd_builtin (list) + WORD_LIST *list; +{ + register int i; + intmax_t which; + int flags; + char direction; + char *which_word; + + which_word = (char *)NULL; + for (flags = 0, which = 0, direction = '+'; list; list = list->next) + { + if (ISOPTION (list->word->word, 'n')) + { + flags |= NOCD; + } + else if (ISOPTION (list->word->word, '-')) + { + list = list->next; + break; + } + else if (((direction = list->word->word[0]) == '+') || direction == '-') + { + if (legal_number (list->word->word + 1, &which) == 0) + { + sh_invalidnum (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + which_word = list->word->word; + } + else if (*list->word->word == '-') + { + sh_invalidopt (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + else + break; + } + + if (which > directory_list_offset || (directory_list_offset == 0 && which == 0)) + { + pushd_error (directory_list_offset, which_word ? which_word : ""); + return (EXECUTION_FAILURE); + } + + /* Handle case of no specification, or top of stack specification. */ + if ((direction == '+' && which == 0) || + (direction == '-' && which == directory_list_offset)) + { + i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1]) + : EXECUTION_SUCCESS; + if (i != EXECUTION_SUCCESS) + return (i); + free (pushd_directory_list[--directory_list_offset]); + } + else + { + /* Since an offset other than the top directory was specified, + remove that directory from the list and shift the remainder + of the list into place. */ + i = (direction == '+') ? directory_list_offset - which : which; + free (pushd_directory_list[i]); + directory_list_offset--; + + /* Shift the remainder of the list into place. */ + for (; i < directory_list_offset; i++) + pushd_directory_list[i] = pushd_directory_list[i + 1]; + } + + dirs_builtin ((WORD_LIST *)NULL); + return (EXECUTION_SUCCESS); +} + +/* Print the current list of directories on the directory stack. */ +int +dirs_builtin (list) + WORD_LIST *list; +{ + int flags, desired_index, index_flag, vflag; + intmax_t i; + char *temp, *w; + + for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next) + { + if (ISOPTION (list->word->word, 'l')) + { + flags |= LONGFORM; + } + else if (ISOPTION (list->word->word, 'c')) + { + flags |= CLEARSTAK; + } + else if (ISOPTION (list->word->word, 'v')) + { + vflag |= 2; + } + else if (ISOPTION (list->word->word, 'p')) + { + vflag |= 1; + } + else if (ISOPTION (list->word->word, '-')) + { + list = list->next; + break; + } + else if (*list->word->word == '+' || *list->word->word == '-') + { + int sign; + if (legal_number (w = list->word->word + 1, &i) == 0) + { + sh_invalidnum (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + sign = (*list->word->word == '+') ? 1 : -1; + desired_index = get_dirstack_index (i, sign, &index_flag); + } + else + { + sh_invalidopt (list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + + if (flags & CLEARSTAK) + { + clear_directory_stack (); + return (EXECUTION_SUCCESS); + } + + if (index_flag && (desired_index < 0 || desired_index > directory_list_offset)) + { + pushd_error (directory_list_offset, w); + return (EXECUTION_FAILURE); + } + +#define DIRSTACK_FORMAT(temp) \ + (flags & LONGFORM) ? temp : polite_directory_format (temp) + + /* The first directory printed is always the current working directory. */ + if (index_flag == 0 || (index_flag == 1 && desired_index == 0)) + { + temp = get_working_directory ("dirs"); + if (temp == 0) + temp = savestring (_("<no current directory>")); + if (vflag & 2) + printf ("%2d %s", 0, DIRSTACK_FORMAT (temp)); + else + printf ("%s", DIRSTACK_FORMAT (temp)); + free (temp); + if (index_flag) + { + putchar ('\n'); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + } + +#define DIRSTACK_ENTRY(i) \ + (flags & LONGFORM) ? pushd_directory_list[i] \ + : polite_directory_format (pushd_directory_list[i]) + + /* Now print the requested directory stack entries. */ + if (index_flag) + { + if (vflag & 2) + printf ("%2d %s", directory_list_offset - desired_index, + DIRSTACK_ENTRY (desired_index)); + else + printf ("%s", DIRSTACK_ENTRY (desired_index)); + } + else + for (i = directory_list_offset - 1; i >= 0; i--) + if (vflag >= 2) + printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i)); + else + printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i)); + + putchar ('\n'); + + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +static void +pushd_error (offset, arg) + int offset; + char *arg; +{ + if (offset == 0) + builtin_error (_("directory stack empty")); + else + sh_erange (arg, _("directory stack index")); +} + +static void +clear_directory_stack () +{ + register int i; + + for (i = 0; i < directory_list_offset; i++) + free (pushd_directory_list[i]); + directory_list_offset = 0; +} + +/* Switch to the directory in NAME. This uses the cd_builtin to do the work, + so if the result is EXECUTION_FAILURE then an error message has already + been printed. */ +static int +cd_to_string (name) + char *name; +{ + WORD_LIST *tlist; + WORD_LIST *dir; + int result; + + dir = make_word_list (make_word (name), NULL); + tlist = make_word_list (make_word ("--"), dir); + result = cd_builtin (tlist); + dispose_words (tlist); + return (result); +} + +static int +change_to_temp (temp) + char *temp; +{ + int tt; + + tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE; + + if (tt == EXECUTION_SUCCESS) + dirs_builtin ((WORD_LIST *)NULL); + + return (tt); +} + +static void +add_dirstack_element (dir) + char *dir; +{ + if (directory_list_offset == directory_list_size) + pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10); + pushd_directory_list[directory_list_offset++] = dir; +} + +static int +get_dirstack_index (ind, sign, indexp) + intmax_t ind; + int sign, *indexp; +{ + if (indexp) + *indexp = sign > 0 ? 1 : 2; + + /* dirs +0 prints the current working directory. */ + /* dirs -0 prints last element in directory stack */ + if (ind == 0 && sign > 0) + return 0; + else if (ind == directory_list_offset) + { + if (indexp) + *indexp = sign > 0 ? 2 : 1; + return 0; + } + else if (ind >= 0 && ind <= directory_list_offset) + return (sign > 0 ? directory_list_offset - ind : ind); + else + return -1; +} + +/* Used by the tilde expansion code. */ +char * +get_dirstack_from_string (string) + char *string; +{ + int ind, sign, index_flag; + intmax_t i; + + sign = 1; + if (*string == '-' || *string == '+') + { + sign = (*string == '-') ? -1 : 1; + string++; + } + if (legal_number (string, &i) == 0) + return ((char *)NULL); + + index_flag = 0; + ind = get_dirstack_index (i, sign, &index_flag); + if (index_flag && (ind < 0 || ind > directory_list_offset)) + return ((char *)NULL); + if (index_flag == 0 || (index_flag == 1 && ind == 0)) + return (get_string_value ("PWD")); + else + return (pushd_directory_list[ind]); +} + +#ifdef INCLUDE_UNUSED +char * +get_dirstack_element (ind, sign) + intmax_t ind; + int sign; +{ + int i; + + i = get_dirstack_index (ind, sign, (int *)NULL); + return (i < 0 || i > directory_list_offset) ? (char *)NULL + : pushd_directory_list[i]; +} +#endif + +void +set_dirstack_element (ind, sign, value) + intmax_t ind; + int sign; + char *value; +{ + int i; + + i = get_dirstack_index (ind, sign, (int *)NULL); + if (ind == 0 || i < 0 || i > directory_list_offset) + return; + free (pushd_directory_list[i]); + pushd_directory_list[i] = savestring (value); +} + +WORD_LIST * +get_directory_stack (flags) + int flags; +{ + register int i; + WORD_LIST *ret; + char *d, *t; + + for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++) + { + d = (flags&1) ? polite_directory_format (pushd_directory_list[i]) + : pushd_directory_list[i]; + ret = make_word_list (make_word (d), ret); + } + /* Now the current directory. */ + d = get_working_directory ("dirstack"); + i = 0; /* sentinel to decide whether or not to free d */ + if (d == 0) + d = "."; + else + { + t = polite_directory_format (d); + /* polite_directory_format sometimes returns its argument unchanged. + If it does not, we can free d right away. If it does, we need to + mark d to be deleted later. */ + if (t != d) + { + free (d); + d = t; + } + else /* t == d, so d is what we want */ + i = 1; + } + ret = make_word_list (make_word (d), ret); + if (i) + free (d); + return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */ +} + +#ifdef LOADABLE_BUILTIN +char * const dirs_doc[] = { +N_("Display the list of currently remembered directories. Directories\n\ + find their way onto the list with the `pushd' command; you can get\n\ + back up through the list with the `popd' command.\n\ + \n\ + Options:\n\ + -c clear the directory stack by deleting all of the elements\n\ + -l do not print tilde-prefixed versions of directories relative\n\ + to your home directory\n\ + -p print the directory stack with one entry per line\n\ + -v print the directory stack with one entry per line prefixed\n\ + with its position in the stack\n\ + \n\ + Arguments:\n\ + +N Displays the Nth entry counting from the left of the list shown by\n\ + dirs when invoked without options, starting with zero.\n\ + \n\ + -N Displays the Nth entry counting from the right of the list shown by\n\ + dirs when invoked without options, starting with zero."), + (char *)NULL +}; + +char * const pushd_doc[] = { +N_("Adds a directory to the top of the directory stack, or rotates\n\ + the stack, making the new top of the stack the current working\n\ + directory. With no arguments, exchanges the top two directories.\n\ + \n\ + Options:\n\ + -n Suppresses the normal change of directory when adding\n\ + directories to the stack, so only the stack is manipulated.\n\ + \n\ + Arguments:\n\ + +N Rotates the stack so that the Nth directory (counting\n\ + from the left of the list shown by `dirs', starting with\n\ + zero) is at the top.\n\ + \n\ + -N Rotates the stack so that the Nth directory (counting\n\ + from the right of the list shown by `dirs', starting with\n\ + zero) is at the top.\n\ + \n\ + dir Adds DIR to the directory stack at the top, making it the\n\ + new current working directory.\n\ + \n\ + The `dirs' builtin displays the directory stack."), + (char *)NULL +}; + +char * const popd_doc[] = { +N_("Removes entries from the directory stack. With no arguments, removes\n\ + the top directory from the stack, and changes to the new top directory.\n\ + \n\ + Options:\n\ + -n Suppresses the normal change of directory when removing\n\ + directories from the stack, so only the stack is manipulated.\n\ + \n\ + Arguments:\n\ + +N Removes the Nth entry counting from the left of the list\n\ + shown by `dirs', starting with zero. For example: `popd +0'\n\ + removes the first directory, `popd +1' the second.\n\ + \n\ + -N Removes the Nth entry counting from the right of the list\n\ + shown by `dirs', starting with zero. For example: `popd -0'\n\ + removes the last directory, `popd -1' the next to last.\n\ + \n\ + The `dirs' builtin displays the directory stack."), + (char *)NULL +}; + +struct builtin pushd_struct = { + "pushd", + pushd_builtin, + BUILTIN_ENABLED, + pushd_doc, + "pushd [+N | -N] [-n] [dir]", + 0 +}; + +struct builtin popd_struct = { + "popd", + popd_builtin, + BUILTIN_ENABLED, + popd_doc, + "popd [+N | -N] [-n]", + 0 +}; + +struct builtin dirs_struct = { + "dirs", + dirs_builtin, + BUILTIN_ENABLED, + dirs_doc, + "dirs [-clpv] [+N] [-N]", + 0 +}; +#endif /* LOADABLE_BUILTIN */ + +#endif /* PUSHD_AND_POPD */ diff --git a/builtins/read.def b/builtins/read.def new file mode 100644 index 0000000..20860be --- /dev/null +++ b/builtins/read.def @@ -0,0 +1,985 @@ +This file is read.def, from which is created read.c. +It implements the builtin "read" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES read.c + +$BUILTIN read +$FUNCTION read_builtin +$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...] +Read a line from the standard input and split it into fields. + +Reads a single line from the standard input, or from file descriptor FD +if the -u option is supplied. The line is split into fields as with word +splitting, and the first word is assigned to the first NAME, the second +word to the second NAME, and so on, with any leftover words assigned to +the last NAME. Only the characters found in $IFS are recognized as word +delimiters. + +If no NAMEs are supplied, the line read is stored in the REPLY variable. + +Options: + -a array assign the words read to sequential indices of the array + variable ARRAY, starting at zero + -d delim continue until the first character of DELIM is read, rather + than newline + -e use Readline to obtain the line in an interactive shell + -i text Use TEXT as the initial text for Readline + -n nchars return after reading NCHARS characters rather than waiting + for a newline, but honor a delimiter if fewer than NCHARS + characters are read before the delimiter + -N nchars return only after reading exactly NCHARS characters, unless + EOF is encountered or read times out, ignoring any delimiter + -p prompt output the string PROMPT without a trailing newline before + attempting to read + -r do not allow backslashes to escape any characters + -s do not echo input coming from a terminal + -t timeout time out and return failure if a complete line of input is + not read withint TIMEOUT seconds. The value of the TMOUT + variable is the default timeout. TIMEOUT may be a + fractional number. If TIMEOUT is 0, read returns success only + if input is available on the specified file descriptor. The + exit status is greater than 128 if the timeout is exceeded + -u fd read from file descriptor FD instead of the standard input + +Exit Status: +The return code is zero, unless end-of-file is encountered, read times out, +or an invalid file descriptor is supplied as the argument to -u. +$END + +#include <config.h> + +#include "bashtypes.h" +#include "posixstat.h" + +#include <stdio.h> + +#include "bashansi.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <signal.h> +#include <errno.h> + +#ifdef __CYGWIN__ +# include <fcntl.h> +# include <io.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +#include <shtty.h> + +#if defined (READLINE) +#include "../bashline.h" +#include <readline/readline.h> +#endif + +#if defined (BUFFERED_INPUT) +# include "input.h" +#endif + +#if !defined(errno) +extern int errno; +#endif + +struct ttsave +{ + int fd; + TTYSTRUCT *attrs; +}; + +#if defined (READLINE) +static void reset_attempted_completion_function __P((char *)); +static int set_itext __P((void)); +static char *edit_line __P((char *, char *)); +static void set_eol_delim __P((int)); +static void reset_eol_delim __P((char *)); +#endif +static SHELL_VAR *bind_read_variable __P((char *, char *)); +#if defined (HANDLE_MULTIBYTE) +static int read_mbchar __P((int, char *, int, int, int)); +#endif +static void ttyrestore __P((struct ttsave *)); + +static sighandler sigalrm __P((int)); +static void reset_alarm __P((void)); + +static procenv_t alrmbuf; +static SigHandler *old_alrm; +static unsigned char delim; + +static sighandler +sigalrm (s) + int s; +{ + longjmp (alrmbuf, 1); +} + +static void +reset_alarm () +{ + set_signal_handler (SIGALRM, old_alrm); + falarm (0, 0); +} + +/* Read the value of the shell variables whose names follow. + The reading is done from the current input stream, whatever + that may be. Successive words of the input line are assigned + to the variables mentioned in LIST. The last variable in LIST + gets the remainder of the words on the line. If no variables + are mentioned in LIST, then the default variable is $REPLY. */ +int +read_builtin (list) + WORD_LIST *list; +{ + register char *varname; + int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2; + int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul; + int raw, edit, nchars, silent, have_timeout, ignore_delim, fd; + unsigned int tmsec, tmusec; + long ival, uval; + intmax_t intval; + char c; + char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname; + char *e, *t, *t1, *ps2, *tofree; + struct stat tsb; + SHELL_VAR *var; + TTYSTRUCT ttattrs, ttset; + struct ttsave termsave; +#if defined (ARRAY_VARS) + WORD_LIST *alist; +#endif +#if defined (READLINE) + char *rlbuf, *itext; + int rlind; +#endif + + USE_VAR(size); + USE_VAR(i); + USE_VAR(pass_next); + USE_VAR(print_ps2); + USE_VAR(saw_escape); + USE_VAR(input_is_pipe); +/* USE_VAR(raw); */ + USE_VAR(edit); + USE_VAR(tmsec); + USE_VAR(tmusec); + USE_VAR(nchars); + USE_VAR(silent); + USE_VAR(ifs_chars); + USE_VAR(prompt); + USE_VAR(arrayname); +#if defined (READLINE) + USE_VAR(rlbuf); + USE_VAR(rlind); + USE_VAR(itext); +#endif + USE_VAR(list); + USE_VAR(ps2); + + i = 0; /* Index into the string that we are reading. */ + raw = edit = 0; /* Not reading raw input by default. */ + silent = 0; + arrayname = prompt = (char *)NULL; + fd = 0; /* file descriptor to read from */ + +#if defined (READLINE) + rlbuf = itext = (char *)0; + rlind = 0; +#endif + + tmsec = tmusec = 0; /* no timeout */ + nr = nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0; + delim = '\n'; /* read until newline */ + ignore_delim = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:N:")) != -1) + { + switch (opt) + { + case 'r': + raw = 1; + break; + case 'p': + prompt = list_optarg; + break; + case 's': + silent = 1; + break; + case 'e': +#if defined (READLINE) + edit = 1; +#endif + break; + case 'i': +#if defined (READLINE) + itext = list_optarg; +#endif + break; +#if defined (ARRAY_VARS) + case 'a': + arrayname = list_optarg; + break; +#endif + case 't': + code = uconvert (list_optarg, &ival, &uval); + if (code == 0 || ival < 0 || uval < 0) + { + builtin_error (_("%s: invalid timeout specification"), list_optarg); + return (EXECUTION_FAILURE); + } + else + { + have_timeout = 1; + tmsec = ival; + tmusec = uval; + } + break; + case 'N': + ignore_delim = 1; + delim = -1; + case 'n': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (int)intval) + { + sh_invalidnum (list_optarg); + return (EXECUTION_FAILURE); + } + else + nchars = intval; + break; + case 'u': + code = legal_number (list_optarg, &intval); + if (code == 0 || intval < 0 || intval != (int)intval) + { + builtin_error (_("%s: invalid file descriptor specification"), list_optarg); + return (EXECUTION_FAILURE); + } + else + fd = intval; + if (sh_validfd (fd) == 0) + { + builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno)); + return (EXECUTION_FAILURE); + } + break; + case 'd': + delim = *list_optarg; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* `read -t 0 var' tests whether input is available with select/FIONREAD, + and fails if those are unavailable */ + if (have_timeout && tmsec == 0 && tmusec == 0) +#if 0 + return (EXECUTION_FAILURE); +#else + return (input_avail (fd) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +#endif + + /* If we're asked to ignore the delimiter, make sure we do. */ + if (ignore_delim) + delim = -1; + + /* IF IFS is unset, we use the default of " \t\n". */ + ifs_chars = getifs (); + if (ifs_chars == 0) /* XXX - shouldn't happen */ + ifs_chars = ""; + /* If we want to read exactly NCHARS chars, don't split on IFS */ + if (ignore_delim) + ifs_chars = ""; + for (skip_ctlesc = skip_ctlnul = 0, e = ifs_chars; *e; e++) + skip_ctlesc |= *e == CTLESC, skip_ctlnul |= *e == CTLNUL; + + input_string = (char *)xmalloc (size = 112); /* XXX was 128 */ + input_string[0] = '\0'; + + /* $TMOUT, if set, is the default timeout for read. */ + if (have_timeout == 0 && (e = get_string_value ("TMOUT"))) + { + code = uconvert (e, &ival, &uval); + if (code == 0 || ival < 0 || uval < 0) + tmsec = tmusec = 0; + else + { + tmsec = ival; + tmusec = uval; + } + } + + begin_unwind_frame ("read_builtin"); + +#if defined (BUFFERED_INPUT) + if (interactive == 0 && default_buffered_input >= 0 && fd_is_bash_input (fd)) + sync_buffered_stream (default_buffered_input); +#endif + + input_is_tty = isatty (fd); + if (input_is_tty == 0) +#ifndef __CYGWIN__ + input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); +#else + input_is_pipe = 1; +#endif + + /* If the -p, -e or -s flags were given, but input is not coming from the + terminal, turn them off. */ + if ((prompt || edit || silent) && input_is_tty == 0) + { + prompt = (char *)NULL; +#if defined (READLINE) + itext = (char *)NULL; +#endif + edit = silent = 0; + } + +#if defined (READLINE) + if (edit) + add_unwind_protect (xfree, rlbuf); +#endif + + pass_next = 0; /* Non-zero signifies last char was backslash. */ + saw_escape = 0; /* Non-zero signifies that we saw an escape char */ + + if (tmsec > 0 || tmusec > 0) + { + /* Turn off the timeout if stdin is a regular file (e.g. from + input redirection). */ + if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode)) + tmsec = tmusec = 0; + } + + if (tmsec > 0 || tmusec > 0) + { + code = setjmp (alrmbuf); + if (code) + { + /* Tricky. The top of the unwind-protect stack is the free of + input_string. We want to run all the rest and use input_string, + so we have to remove it from the stack. */ + remove_unwind_protect (); + run_unwind_frame ("read_builtin"); + input_string[i] = '\0'; /* make sure it's terminated */ + retval = 128+SIGALRM; + goto assign_vars; + } + old_alrm = set_signal_handler (SIGALRM, sigalrm); + add_unwind_protect (reset_alarm, (char *)NULL); +#if defined (READLINE) + if (edit) + add_unwind_protect (reset_attempted_completion_function, (char *)NULL); +#endif + falarm (tmsec, tmusec); + } + + /* If we've been asked to read only NCHARS chars, or we're using some + character other than newline to terminate the line, do the right + thing to readline or the tty. */ + if (nchars > 0 || delim != '\n') + { +#if defined (READLINE) + if (edit) + { + if (nchars > 0) + { + unwind_protect_int (rl_num_chars_to_read); + rl_num_chars_to_read = nchars; + } + if (delim != '\n') + { + set_eol_delim (delim); + add_unwind_protect (reset_eol_delim, (char *)NULL); + } + } + else +#endif + if (input_is_tty) + { + /* ttsave() */ + termsave.fd = fd; + ttgetattr (fd, &ttattrs); + termsave.attrs = &ttattrs; + + ttset = ttattrs; + i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset); + if (i < 0) + sh_ttyerror (1); + add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); + } + } + else if (silent) /* turn off echo but leave term in canonical mode */ + { + /* ttsave (); */ + termsave.fd = fd; + ttgetattr (fd, &ttattrs); + termsave.attrs = &ttattrs; + + ttset = ttattrs; + i = ttfd_noecho (fd, &ttset); /* ttnoecho (); */ + if (i < 0) + sh_ttyerror (1); + + add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); + } + + /* This *must* be the top unwind-protect on the stack, so the manipulation + of the unwind-protect stack after the realloc() works right. */ + add_unwind_protect (xfree, input_string); + interrupt_immediately++; + terminate_immediately++; + + unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe; + + if (prompt && edit == 0) + { + fprintf (stderr, "%s", prompt); + fflush (stderr); + } + +#if defined (__CYGWIN__) && defined (O_TEXT) + setmode (0, O_TEXT); +#endif + + ps2 = 0; + for (print_ps2 = eof = retval = 0;;) + { +#if defined (READLINE) + if (edit) + { + if (rlbuf && rlbuf[rlind] == '\0') + { + xfree (rlbuf); + rlbuf = (char *)0; + } + if (rlbuf == 0) + { + rlbuf = edit_line (prompt ? prompt : "", itext); + rlind = 0; + } + if (rlbuf == 0) + { + eof = 1; + break; + } + c = rlbuf[rlind++]; + } + else + { +#endif + + if (print_ps2) + { + if (ps2 == 0) + ps2 = get_string_value ("PS2"); + fprintf (stderr, "%s", ps2 ? ps2 : ""); + fflush (stderr); + print_ps2 = 0; + } + + if (unbuffered_read) + retval = zread (fd, &c, 1); + else + retval = zreadc (fd, &c); + + if (retval <= 0) + { + eof = 1; + break; + } + +#if defined (READLINE) + } +#endif + + if (i + 4 >= size) /* XXX was i + 2; use i + 4 for multibyte/read_mbchar */ + { + input_string = (char *)xrealloc (input_string, size += 128); + remove_unwind_protect (); + add_unwind_protect (xfree, input_string); + } + + /* If the next character is to be accepted verbatim, a backslash + newline pair still disappears from the input. */ + if (pass_next) + { + pass_next = 0; + if (c == '\n') + { + i--; /* back up over the CTLESC */ + if (interactive && input_is_tty && raw == 0) + print_ps2 = 1; + } + else + goto add_char; + continue; + } + + /* This may cause problems if IFS contains CTLESC */ + if (c == '\\' && raw == 0) + { + pass_next++; + if (skip_ctlesc == 0) + { + saw_escape++; + input_string[i++] = CTLESC; + } + continue; + } + + if ((unsigned char)c == delim) + break; + + if ((skip_ctlesc == 0 && c == CTLESC) || (skip_ctlnul == 0 && c == CTLNUL)) + { + saw_escape++; + input_string[i++] = CTLESC; + } + +add_char: + input_string[i++] = c; + +#if defined (HANDLE_MULTIBYTE) + if (nchars > 0 && MB_CUR_MAX > 1) + { + input_string[i] = '\0'; /* for simplicity and debugging */ + i += read_mbchar (fd, input_string, i, c, unbuffered_read); + } +#endif + + nr++; + + if (nchars > 0 && nr >= nchars) + break; + } + input_string[i] = '\0'; + +#if 1 + if (retval < 0) + { + builtin_error (_("read error: %d: %s"), fd, strerror (errno)); + run_unwind_frame ("read_builtin"); + return (EXECUTION_FAILURE); + } +#endif + + if (tmsec > 0 || tmusec > 0) + reset_alarm (); + + if (nchars > 0 || delim != '\n') + { +#if defined (READLINE) + if (edit) + { + if (nchars > 0) + rl_num_chars_to_read = 0; + if (delim != '\n') + reset_eol_delim ((char *)NULL); + } + else +#endif + if (input_is_tty) + ttyrestore (&termsave); + } + else if (silent) + ttyrestore (&termsave); + + if (unbuffered_read == 0) + zsyncfd (fd); + + discard_unwind_frame ("read_builtin"); + + retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + +assign_vars: + + interrupt_immediately--; + terminate_immediately--; + +#if defined (ARRAY_VARS) + /* If -a was given, take the string read, break it into a list of words, + an assign them to `arrayname' in turn. */ + if (arrayname) + { + if (legal_identifier (arrayname) == 0) + { + sh_invalidid (arrayname); + xfree (input_string); + return (EXECUTION_FAILURE); + } + + var = find_or_make_array_variable (arrayname, 1); + if (var == 0) + { + xfree (input_string); + return EXECUTION_FAILURE; /* readonly or noassign */ + } + array_flush (array_cell (var)); + + alist = list_string (input_string, ifs_chars, 0); + if (alist) + { + if (saw_escape) + dequote_list (alist); + else + word_list_remove_quoted_nulls (alist); + assign_array_var_from_word_list (var, alist, 0); + dispose_words (alist); + } + xfree (input_string); + return (retval); + } +#endif /* ARRAY_VARS */ + + /* If there are no variables, save the text of the line read to the + variable $REPLY. ksh93 strips leading and trailing IFS whitespace, + so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the + same way, but I believe that the difference in behaviors is useful + enough to not do it. Without the bash behavior, there is no way + to read a line completely without interpretation or modification + unless you mess with $IFS (e.g., setting it to the empty string). + If you disagree, change the occurrences of `#if 0' to `#if 1' below. */ + if (list == 0) + { +#if 0 + orig_input_string = input_string; + for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++) + ; + input_string = t; + input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape); +#endif + + if (saw_escape) + { + t = dequote_string (input_string); + var = bind_variable ("REPLY", t, 0); + free (t); + } + else + var = bind_variable ("REPLY", input_string, 0); + VUNSETATTR (var, att_invisible); + + free (input_string); + return (retval); + } + + /* This code implements the Posix.2 spec for splitting the words + read and assigning them to variables. */ + orig_input_string = input_string; + + /* Remove IFS white space at the beginning of the input string. If + $IFS is null, no field splitting is performed. */ + for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++) + ; + input_string = t; + for (; list->next; list = list->next) + { + varname = list->word->word; +#if defined (ARRAY_VARS) + if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0) +#else + if (legal_identifier (varname) == 0) +#endif + { + sh_invalidid (varname); + xfree (orig_input_string); + return (EXECUTION_FAILURE); + } + + /* If there are more variables than words read from the input, + the remaining variables are set to the empty string. */ + if (*input_string) + { + /* This call updates INPUT_STRING. */ + t = get_word_from_string (&input_string, ifs_chars, &e); + if (t) + *e = '\0'; + /* Don't bother to remove the CTLESC unless we added one + somewhere while reading the string. */ + if (t && saw_escape) + { + t1 = dequote_string (t); + var = bind_read_variable (varname, t1); + xfree (t1); + } + else + var = bind_read_variable (varname, t); + } + else + { + t = (char *)0; + var = bind_read_variable (varname, ""); + } + + FREE (t); + if (var == 0) + { + xfree (orig_input_string); + return (EXECUTION_FAILURE); + } + + stupidly_hack_special_variables (varname); + VUNSETATTR (var, att_invisible); + } + + /* Now assign the rest of the line to the last variable argument. */ +#if defined (ARRAY_VARS) + if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0) +#else + if (legal_identifier (list->word->word) == 0) +#endif + { + sh_invalidid (list->word->word); + xfree (orig_input_string); + return (EXECUTION_FAILURE); + } + +#if 0 + /* This has to be done this way rather than using string_list + and list_string because Posix.2 says that the last variable gets the + remaining words and their intervening separators. */ + input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape); +#else + /* Check whether or not the number of fields is exactly the same as the + number of variables. */ + tofree = NULL; + if (*input_string) + { + t1 = input_string; + t = get_word_from_string (&input_string, ifs_chars, &e); + if (*input_string == 0) + tofree = input_string = t; + else + { + input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape); + tofree = t; + } + } +#endif + + if (saw_escape) + { + t = dequote_string (input_string); + var = bind_read_variable (list->word->word, t); + xfree (t); + } + else + var = bind_read_variable (list->word->word, input_string); + stupidly_hack_special_variables (list->word->word); + FREE (tofree); + + if (var) + VUNSETATTR (var, att_invisible); + xfree (orig_input_string); + + return (retval); +} + +static SHELL_VAR * +bind_read_variable (name, value) + char *name, *value; +{ +#if defined (ARRAY_VARS) + if (valid_array_reference (name) == 0) + return (bind_variable (name, value, 0)); + else + return (assign_array_element (name, value, 0)); +#else /* !ARRAY_VARS */ + return bind_variable (name, value, 0); +#endif /* !ARRAY_VARS */ +} + +#if defined (HANDLE_MULTIBYTE) +static int +read_mbchar (fd, string, ind, ch, unbuffered) + int fd; + char *string; + int ind, ch, unbuffered; +{ + char mbchar[MB_LEN_MAX + 1]; + int i, n, r; + char c; + size_t ret; + mbstate_t ps, ps_back; + wchar_t wc; + + memset (&ps, '\0', sizeof (mbstate_t)); + memset (&ps_back, '\0', sizeof (mbstate_t)); + + mbchar[0] = ch; + i = 1; + for (n = 0; n <= MB_LEN_MAX; n++) + { + ps_back = ps; + ret = mbrtowc (&wc, mbchar, i, &ps); + if (ret == (size_t)-2) + { + ps = ps_back; + if (unbuffered) + r = zread (fd, &c, 1); + else + r = zreadc (fd, &c); + if (r < 0) + goto mbchar_return; + mbchar[i++] = c; + continue; + } + else if (ret == (size_t)-1 || ret == (size_t)0 || ret > (size_t)0) + break; + } + +mbchar_return: + if (i > 1) /* read a multibyte char */ + /* mbchar[0] is already string[ind-1] */ + for (r = 1; r < i; r++) + string[ind+r-1] = mbchar[r]; + return i - 1; +} +#endif + + +static void +ttyrestore (ttp) + struct ttsave *ttp; +{ + ttsetattr (ttp->fd, ttp->attrs); +} + +#if defined (READLINE) +static rl_completion_func_t *old_attempted_completion_function = 0; +static rl_hook_func_t *old_startup_hook; +static char *deftext; + +static void +reset_attempted_completion_function (cp) + char *cp; +{ + if (rl_attempted_completion_function == 0 && old_attempted_completion_function) + rl_attempted_completion_function = old_attempted_completion_function; +} + +static int +set_itext () +{ + int r1, r2; + + r1 = r2 = 0; + if (old_startup_hook) + r1 = (*old_startup_hook) (); + if (deftext) + { + r2 = rl_insert_text (deftext); + deftext = (char *)NULL; + rl_startup_hook = old_startup_hook; + old_startup_hook = (rl_hook_func_t *)NULL; + } + return (r1 || r2); +} + +static char * +edit_line (p, itext) + char *p; + char *itext; +{ + char *ret; + int len; + + if (bash_readline_initialized == 0) + initialize_readline (); + + old_attempted_completion_function = rl_attempted_completion_function; + rl_attempted_completion_function = (rl_completion_func_t *)NULL; + if (itext) + { + old_startup_hook = rl_startup_hook; + rl_startup_hook = set_itext; + deftext = itext; + } + ret = readline (p); + rl_attempted_completion_function = old_attempted_completion_function; + old_attempted_completion_function = (rl_completion_func_t *)NULL; + + if (ret == 0) + return ret; + len = strlen (ret); + ret = (char *)xrealloc (ret, len + 2); + ret[len++] = delim; + ret[len] = '\0'; + return ret; +} + +static int old_delim_ctype; +static rl_command_func_t *old_delim_func; +static int old_newline_ctype; +static rl_command_func_t *old_newline_func; + +static unsigned char delim_char; + +static void +set_eol_delim (c) + int c; +{ + Keymap cmap; + + if (bash_readline_initialized == 0) + initialize_readline (); + cmap = rl_get_keymap (); + + /* Change newline to self-insert */ + old_newline_ctype = cmap[RETURN].type; + old_newline_func = cmap[RETURN].function; + cmap[RETURN].type = ISFUNC; + cmap[RETURN].function = rl_insert; + + /* Bind the delimiter character to accept-line. */ + old_delim_ctype = cmap[c].type; + old_delim_func = cmap[c].function; + cmap[c].type = ISFUNC; + cmap[c].function = rl_newline; + + delim_char = c; +} + +static void +reset_eol_delim (cp) + char *cp; +{ + Keymap cmap; + + cmap = rl_get_keymap (); + + cmap[RETURN].type = old_newline_ctype; + cmap[RETURN].function = old_newline_func; + + cmap[delim_char].type = old_delim_ctype; + cmap[delim_char].function = old_delim_func; +} +#endif diff --git a/builtins/reserved.def b/builtins/reserved.def new file mode 100644 index 0000000..2478f16 --- /dev/null +++ b/builtins/reserved.def @@ -0,0 +1,288 @@ +This file is reserved.def, in which the shell reserved words are defined. +It has no direct C file production, but defines builtins for the Bash +builtin help command. + +Copyright (C) 1987-2009 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/>. + +$BUILTIN for +$SHORT_DOC for NAME [in WORDS ... ] ; do COMMANDS; done +Execute commands for each member in a list. + +The `for' loop executes a sequence of commands for each member in a +list of items. If `in WORDS ...;' is not present, then `in "$@"' is +assumed. For each element in WORDS, NAME is set to that element, and +the COMMANDS are executed. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN for (( +$DOCNAME arith_for +$SHORT_DOC for (( exp1; exp2; exp3 )); do COMMANDS; done +Arithmetic for loop. + +Equivalent to + (( EXP1 )) + while (( EXP2 )); do + COMMANDS + (( EXP3 )) + done +EXP1, EXP2, and EXP3 are arithmetic expressions. If any expression is +omitted, it behaves as if it evaluates to 1. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN select +$SHORT_DOC select NAME [in WORDS ... ;] do COMMANDS; done +Select words from a list and execute commands. + +The WORDS are expanded, generating a list of words. The +set of expanded words is printed on the standard error, each +preceded by a number. If `in WORDS' is not present, `in "$@"' +is assumed. The PS3 prompt is then displayed and a line read +from the standard input. If the line consists of the number +corresponding to one of the displayed words, then NAME is set +to that word. If the line is empty, WORDS and the prompt are +redisplayed. If EOF is read, the command completes. Any other +value read causes NAME to be set to null. The line read is saved +in the variable REPLY. COMMANDS are executed after each selection +until a break command is executed. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN time +$SHORT_DOC time [-p] pipeline +Report time consumed by pipeline's execution. + +Execute PIPELINE and print a summary of the real time, user CPU time, +and system CPU time spent executing PIPELINE when it terminates. + +Options: + -p print the timing summary in the portable Posix format + +The value of the TIMEFORMAT variable is used as the output format. + +Exit Status: +The return status is the return status of PIPELINE. +$END + +$BUILTIN case +$SHORT_DOC case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac +Execute commands based on pattern matching. + +Selectively execute COMMANDS based upon WORD matching PATTERN. The +`|' is used to separate multiple patterns. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN if +$SHORT_DOC if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi +Execute commands based on conditional. + +The `if COMMANDS' list is executed. If its exit status is zero, then the +`then COMMANDS' list is executed. Otherwise, each `elif COMMANDS' list is +executed in turn, and if its exit status is zero, the corresponding +`then COMMANDS' list is executed and the if command completes. Otherwise, +the `else COMMANDS' list is executed, if present. The exit status of the +entire construct is the exit status of the last command executed, or zero +if no condition tested true. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN while +$SHORT_DOC while COMMANDS; do COMMANDS; done +Execute commands as long as a test succeeds. + +Expand and execute COMMANDS as long as the final command in the +`while' COMMANDS has an exit status of zero. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN until +$SHORT_DOC until COMMANDS; do COMMANDS; done +Execute commands as long as a test does not succeed. + +Expand and execute COMMANDS as long as the final command in the +`until' COMMANDS has an exit status which is not zero. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN coproc +$SHORT_DOC coproc [NAME] command [redirections] +Create a coprocess named NAME. + +Execute COMMAND asynchronously, with the standard output and standard +input of the command connected via a pipe to file descriptors assigned +to indices 0 and 1 of an array variable NAME in the executing shell. +The default NAME is "COPROC". + +Exit Status: +Returns the exit status of COMMAND. +$END + +$BUILTIN function +$SHORT_DOC function name { COMMANDS ; } or name () { COMMANDS ; } +Define shell function. + +Create a shell function named NAME. When invoked as a simple command, +NAME runs COMMANDs in the calling shell's context. When NAME is invoked, +the arguments are passed to the function as $1...$n, and the function's +name is in $FUNCNAME. + +Exit Status: +Returns success unless NAME is readonly. +$END + +$BUILTIN { ... } +$DOCNAME grouping_braces +$SHORT_DOC { COMMANDS ; } +Group commands as a unit. + +Run a set of commands in a group. This is one way to redirect an +entire set of commands. + +Exit Status: +Returns the status of the last command executed. +$END + +$BUILTIN % +$DOCNAME fg_percent +$SHORT_DOC job_spec [&] +Resume job in foreground. + +Equivalent to the JOB_SPEC argument to the `fg' command. Resume a +stopped or background job. JOB_SPEC can specify either a job name +or a job number. Following JOB_SPEC with a `&' places the job in +the background, as if the job specification had been supplied as an +argument to `bg'. + +Exit Status: +Returns the status of the resumed job. +$END + +$BUILTIN (( ... )) +$DOCNAME arith +$SHORT_DOC (( expression )) +Evaluate arithmetic expression. + +The EXPRESSION is evaluated according to the rules for arithmetic +evaluation. Equivalent to "let EXPRESSION". + +Exit Status: +Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise. +$END + +$BUILTIN [[ ... ]] +$DOCNAME conditional +$SHORT_DOC [[ expression ]] +Execute conditional command. + +Returns a status of 0 or 1 depending on the evaluation of the conditional +expression EXPRESSION. Expressions are composed of the same primaries used +by the `test' builtin, and may be combined using the following operators: + + ( EXPRESSION ) Returns the value of EXPRESSION + ! EXPRESSION True if EXPRESSION is false; else false + EXPR1 && EXPR2 True if both EXPR1 and EXPR2 are true; else false + EXPR1 || EXPR2 True if either EXPR1 or EXPR2 is true; else false + +When the `==' and `!=' operators are used, the string to the right of +the operator is used as a pattern and pattern matching is performed. +When the `=~' operator is used, the string to the right of the operator +is matched as a regular expression. + +The && and || operators do not evaluate EXPR2 if EXPR1 is sufficient to +determine the expression's value. + +Exit Status: +0 or 1 depending on value of EXPRESSION. +$END + +$BUILTIN variables +$DOCNAME variable_help +$SHORT_DOC variables - Names and meanings of some shell variables +Common shell variable names and usage. + +BASH_VERSION Version information for this Bash. +CDPATH A colon-separated list of directories to search + for directories given as arguments to `cd'. +GLOBIGNORE A colon-separated list of patterns describing filenames to + be ignored by pathname expansion. +#if defined (HISTORY) +HISTFILE The name of the file where your command history is stored. +HISTFILESIZE The maximum number of lines this file can contain. +HISTSIZE The maximum number of history lines that a running + shell can access. +#endif /* HISTORY */ +HOME The complete pathname to your login directory. +HOSTNAME The name of the current host. +HOSTTYPE The type of CPU this version of Bash is running under. +IGNOREEOF Controls the action of the shell on receipt of an EOF + character as the sole input. If set, then the value + of it is the number of EOF characters that can be seen + in a row on an empty line before the shell will exit + (default 10). When unset, EOF signifies the end of input. +MACHTYPE A string describing the current system Bash is running on. +MAILCHECK How often, in seconds, Bash checks for new mail. +MAILPATH A colon-separated list of filenames which Bash checks + for new mail. +OSTYPE The version of Unix this version of Bash is running on. +PATH A colon-separated list of directories to search when + looking for commands. +PROMPT_COMMAND A command to be executed before the printing of each + primary prompt. +PS1 The primary prompt string. +PS2 The secondary prompt string. +PWD The full pathname of the current directory. +SHELLOPTS A colon-separated list of enabled shell options. +TERM The name of the current terminal type. +TIMEFORMAT The output format for timing statistics displayed by the + `time' reserved word. +auto_resume Non-null means a command word appearing on a line by + itself is first looked for in the list of currently + stopped jobs. If found there, that job is foregrounded. + A value of `exact' means that the command word must + exactly match a command in the list of stopped jobs. A + value of `substring' means that the command word must + match a substring of the job. Any other value means that + the command must be a prefix of a stopped job. +#if defined (HISTORY) +# if defined (BANG_HISTORY) +histchars Characters controlling history expansion and quick + substitution. The first character is the history + substitution character, usually `!'. The second is + the `quick substitution' character, usually `^'. The + third is the `history comment' character, usually `#'. +# endif /* BANG_HISTORY */ +HISTIGNORE A colon-separated list of patterns used to decide which + commands should be saved on the history list. +#endif /* HISTORY */ +$END diff --git a/builtins/return.def b/builtins/return.def new file mode 100644 index 0000000..641ee16 --- /dev/null +++ b/builtins/return.def @@ -0,0 +1,76 @@ +This file is return.def, from which is created return.c. +It implements the builtin "return" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES return.c + +$BUILTIN return + +$FUNCTION return_builtin +$SHORT_DOC return [n] +Return from a shell function. + +Causes a function or sourced script to exit with the return value +specified by N. If N is omitted, the return status is that of the +last command executed within the function or script. + +Exit Status: +Returns N, or failure if the shell is not executing a function or script. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +extern int last_command_exit_value; +extern int subshell_environment; +extern int return_catch_flag, return_catch_value; + +/* If we are executing a user-defined function then exit with the value + specified as an argument. if no argument is given, then the last + exit status is used. */ +int +return_builtin (list) + WORD_LIST *list; +{ + if (no_options (list)) + return (EX_USAGE); + list = loptend; /* skip over possible `--' */ + + return_catch_value = get_exitstat (list); + + if (return_catch_flag) + longjmp (return_catch, 1); + else + { + builtin_error (_("can only `return' from a function or sourced script")); + return (EXECUTION_FAILURE); + } +} diff --git a/builtins/set.def b/builtins/set.def new file mode 100644 index 0000000..5e550cb --- /dev/null +++ b/builtins/set.def @@ -0,0 +1,854 @@ +This file is set.def, from which is created set.c. +It implements the "set" and "unset" builtins in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES set.c + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../flags.h" +#include "common.h" +#include "bashgetopt.h" + +#if defined (READLINE) +# include "../input.h" +# include "../bashline.h" +# include <readline/readline.h> +#endif + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +extern int posixly_correct, ignoreeof, eof_encountered_limit; +#if defined (HISTORY) +extern int dont_save_function_defs; +#endif +#if defined (READLINE) +extern int no_line_editing; +#endif /* READLINE */ + +$BUILTIN set +$FUNCTION set_builtin +$SHORT_DOC set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...] +Set or unset values of shell options and positional parameters. + +Change the value of shell attributes and positional parameters, or +display the names and values of shell variables. + +Options: + -a Mark variables which are modified or created for export. + -b Notify of job termination immediately. + -e Exit immediately if a command exits with a non-zero status. + -f Disable file name generation (globbing). + -h Remember the location of commands as they are looked up. + -k All assignment arguments are placed in the environment for a + command, not just those that precede the command name. + -m Job control is enabled. + -n Read commands but do not execute them. + -o option-name + Set the variable corresponding to option-name: + allexport same as -a + braceexpand same as -B +#if defined (READLINE) + emacs use an emacs-style line editing interface +#endif /* READLINE */ + errexit same as -e + errtrace same as -E + functrace same as -T + hashall same as -h +#if defined (BANG_HISTORY) + histexpand same as -H +#endif /* BANG_HISTORY */ +#if defined (HISTORY) + history enable command history +#endif + ignoreeof the shell will not exit upon reading EOF + interactive-comments + allow comments to appear in interactive commands + keyword same as -k + monitor same as -m + noclobber same as -C + noexec same as -n + noglob same as -f + nolog currently accepted but ignored + notify same as -b + nounset same as -u + onecmd same as -t + physical same as -P + pipefail the return value of a pipeline is the status of + the last command to exit with a non-zero status, + or zero if no command exited with a non-zero status + posix change the behavior of bash where the default + operation differs from the Posix standard to + match the standard + privileged same as -p + verbose same as -v +#if defined (READLINE) + vi use a vi-style line editing interface +#endif /* READLINE */ + xtrace same as -x + -p Turned on whenever the real and effective user ids do not match. + Disables processing of the $ENV file and importing of shell + functions. Turning this option off causes the effective uid and + gid to be set to the real uid and gid. + -t Exit after reading and executing one command. + -u Treat unset variables as an error when substituting. + -v Print shell input lines as they are read. + -x Print commands and their arguments as they are executed. +#if defined (BRACE_EXPANSION) + -B the shell will perform brace expansion +#endif /* BRACE_EXPANSION */ + -C If set, disallow existing regular files to be overwritten + by redirection of output. + -E If set, the ERR trap is inherited by shell functions. +#if defined (BANG_HISTORY) + -H Enable ! style history substitution. This flag is on + by default when the shell is interactive. +#endif /* BANG_HISTORY */ + -P If set, do not follow symbolic links when executing commands + such as cd which change the current directory. + -T If set, the DEBUG trap is inherited by shell functions. + - Assign any remaining arguments to the positional parameters. + The -x and -v options are turned off. + +Using + rather than - causes these flags to be turned off. The +flags can also be used upon invocation of the shell. The current +set of flags may be found in $-. The remaining n ARGs are positional +parameters and are assigned, in order, to $1, $2, .. $n. If no +ARGs are given, all shell variables are printed. + +Exit Status: +Returns success unless an invalid option is given. +$END + +typedef int setopt_set_func_t __P((int, char *)); +typedef int setopt_get_func_t __P((char *)); + +static void print_minus_o_option __P((char *, int, int)); +static void print_all_shell_variables __P((void)); + +static int set_ignoreeof __P((int, char *)); +static int set_posix_mode __P((int, char *)); + +#if defined (READLINE) +static int set_edit_mode __P((int, char *)); +static int get_edit_mode __P((char *)); +#endif + +#if defined (HISTORY) +static int bash_set_history __P((int, char *)); +#endif + +static const char * const on = "on"; +static const char * const off = "off"; + +/* A struct used to match long options for set -o to the corresponding + option letter or internal variable. The functions can be called to + dynamically generate values. */ +const struct { + char *name; + int letter; + int *variable; + setopt_set_func_t *set_func; + setopt_get_func_t *get_func; +} o_options[] = { + { "allexport", 'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#if defined (BRACE_EXPANSION) + { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#endif +#if defined (READLINE) + { "emacs", '\0', (int *)NULL, set_edit_mode, get_edit_mode }, +#endif + { "errexit", 'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "errtrace", 'E', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "functrace", 'T', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "hashall", 'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#if defined (BANG_HISTORY) + { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#endif /* BANG_HISTORY */ +#if defined (HISTORY) + { "history", '\0', &enable_history_list, bash_set_history, (setopt_get_func_t *)NULL }, +#endif + { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL }, + { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "keyword", 'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "monitor", 'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "noclobber", 'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "noexec", 'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "noglob", 'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#if defined (HISTORY) + { "nolog", '\0', &dont_save_function_defs, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#endif +#if defined (JOB_CONTROL) + { "notify", 'b', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#endif /* JOB_CONTROL */ + { "nounset", 'u', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "onecmd", 't', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "physical", 'P', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "pipefail", '\0', &pipefail_opt, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "posix", '\0', &posixly_correct, set_posix_mode, (setopt_get_func_t *)NULL }, + { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + { "verbose", 'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#if defined (READLINE) + { "vi", '\0', (int *)NULL, set_edit_mode, get_edit_mode }, +#endif + { "xtrace", 'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, + {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +}; + +#define N_O_OPTIONS (sizeof (o_options) / sizeof (o_options[0])) + +#define GET_BINARY_O_OPTION_VALUE(i, name) \ + ((o_options[i].get_func) ? (*o_options[i].get_func) (name) \ + : (*o_options[i].variable)) + +#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \ + ((o_options[i].set_func) ? (*o_options[i].set_func) (onoff, name) \ + : (*o_options[i].variable = (onoff == FLAG_ON))) + +int +minus_o_option_value (name) + char *name; +{ + register int i; + int *on_or_off; + + for (i = 0; o_options[i].name; i++) + { + if (STREQ (name, o_options[i].name)) + { + if (o_options[i].letter) + { + on_or_off = find_flag (o_options[i].letter); + return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off); + } + else + return (GET_BINARY_O_OPTION_VALUE (i, name)); + } + } + + return (-1); +} + +#define MINUS_O_FORMAT "%-15s\t%s\n" + +static void +print_minus_o_option (name, value, pflag) + char *name; + int value, pflag; +{ + if (pflag == 0) + printf (MINUS_O_FORMAT, name, value ? on : off); + else + printf ("set %co %s\n", value ? '-' : '+', name); +} + +void +list_minus_o_opts (mode, reusable) + int mode, reusable; +{ + register int i; + int *on_or_off, value; + + for (i = 0; o_options[i].name; i++) + { + if (o_options[i].letter) + { + value = 0; + on_or_off = find_flag (o_options[i].letter); + if (on_or_off == FLAG_UNKNOWN) + on_or_off = &value; + if (mode == -1 || mode == *on_or_off) + print_minus_o_option (o_options[i].name, *on_or_off, reusable); + } + else + { + value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name); + if (mode == -1 || mode == value) + print_minus_o_option (o_options[i].name, value, reusable); + } + } +} + +char ** +get_minus_o_opts () +{ + char **ret; + int i; + + ret = strvec_create (N_O_OPTIONS + 1); + for (i = 0; o_options[i].name; i++) + ret[i] = o_options[i].name; + ret[i] = (char *)NULL; + return ret; +} + +static int +set_ignoreeof (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + ignoreeof = on_or_off == FLAG_ON; + unbind_variable ("ignoreeof"); + if (ignoreeof) + bind_variable ("IGNOREEOF", "10", 0); + else + unbind_variable ("IGNOREEOF"); + sv_ignoreeof ("IGNOREEOF"); + return 0; +} + +static int +set_posix_mode (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + posixly_correct = on_or_off == FLAG_ON; + if (posixly_correct == 0) + unbind_variable ("POSIXLY_CORRECT"); + else + bind_variable ("POSIXLY_CORRECT", "y", 0); + sv_strict_posix ("POSIXLY_CORRECT"); + return (0); +} + +#if defined (READLINE) +/* Magic. This code `knows' how readline handles rl_editing_mode. */ +static int +set_edit_mode (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + int isemacs; + + if (on_or_off == FLAG_ON) + { + rl_variable_bind ("editing-mode", option_name); + + if (interactive) + with_input_from_stdin (); + no_line_editing = 0; + } + else + { + isemacs = rl_editing_mode == 1; + if ((isemacs && *option_name == 'e') || (!isemacs && *option_name == 'v')) + { + if (interactive) + with_input_from_stream (stdin, "stdin"); + no_line_editing = 1; + } + } + return 1-no_line_editing; +} + +static int +get_edit_mode (name) + char *name; +{ + return (*name == 'e' ? no_line_editing == 0 && rl_editing_mode == 1 + : no_line_editing == 0 && rl_editing_mode == 0); +} +#endif /* READLINE */ + +#if defined (HISTORY) +static int +bash_set_history (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + if (on_or_off == FLAG_ON) + { + enable_history_list = 1; + bash_history_enable (); + if (history_lines_this_session == 0) + load_history (); + } + else + { + enable_history_list = 0; + bash_history_disable (); + } + return (1 - enable_history_list); +} +#endif + +int +set_minus_o_option (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + register int i; + + for (i = 0; o_options[i].name; i++) + { + if (STREQ (option_name, o_options[i].name)) + { + if (o_options[i].letter == 0) + { + SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name); + return (EXECUTION_SUCCESS); + } + else + { + if (change_flag (o_options[i].letter, on_or_off) == FLAG_ERROR) + { + sh_invalidoptname (option_name); + return (EXECUTION_FAILURE); + } + else + return (EXECUTION_SUCCESS); + } + + } + } + + sh_invalidoptname (option_name); + return (EX_USAGE); +} + +static void +print_all_shell_variables () +{ + SHELL_VAR **vars; + + vars = all_shell_variables (); + if (vars) + { + print_var_list (vars); + free (vars); + } + + /* POSIX.2 does not allow function names and definitions to be output when + `set' is invoked without options (PASC Interp #202). */ + if (posixly_correct == 0) + { + vars = all_shell_functions (); + if (vars) + { + print_func_list (vars); + free (vars); + } + } +} + +void +set_shellopts () +{ + char *value; + char tflag[N_O_OPTIONS]; + int vsize, i, vptr, *ip, exported; + SHELL_VAR *v; + + for (vsize = i = 0; o_options[i].name; i++) + { + tflag[i] = 0; + if (o_options[i].letter) + { + ip = find_flag (o_options[i].letter); + if (ip && *ip) + { + vsize += strlen (o_options[i].name) + 1; + tflag[i] = 1; + } + } + else if (GET_BINARY_O_OPTION_VALUE (i, o_options[i].name)) + { + vsize += strlen (o_options[i].name) + 1; + tflag[i] = 1; + } + } + + value = (char *)xmalloc (vsize + 1); + + for (i = vptr = 0; o_options[i].name; i++) + { + if (tflag[i]) + { + strcpy (value + vptr, o_options[i].name); + vptr += strlen (o_options[i].name); + value[vptr++] = ':'; + } + } + + if (vptr) + vptr--; /* cut off trailing colon */ + value[vptr] = '\0'; + + v = find_variable ("SHELLOPTS"); + + /* Turn off the read-only attribute so we can bind the new value, and + note whether or not the variable was exported. */ + if (v) + { + VUNSETATTR (v, att_readonly); + exported = exported_p (v); + } + else + exported = 0; + + v = bind_variable ("SHELLOPTS", value, 0); + + /* Turn the read-only attribute back on, and turn off the export attribute + if it was set implicitly by mark_modified_vars and SHELLOPTS was not + exported before we bound the new value. */ + VSETATTR (v, att_readonly); + if (mark_modified_vars && exported == 0 && exported_p (v)) + VUNSETATTR (v, att_exported); + + free (value); +} + +void +parse_shellopts (value) + char *value; +{ + char *vname; + int vptr; + + vptr = 0; + while (vname = extract_colon_unit (value, &vptr)) + { + set_minus_o_option (FLAG_ON, vname); + free (vname); + } +} + +void +initialize_shell_options (no_shellopts) + int no_shellopts; +{ + char *temp; + SHELL_VAR *var; + + if (no_shellopts == 0) + { + var = find_variable ("SHELLOPTS"); + /* set up any shell options we may have inherited. */ + if (var && imported_p (var)) + { + temp = (array_p (var) || assoc_p (var)) ? (char *)NULL : savestring (value_cell (var)); + if (temp) + { + parse_shellopts (temp); + free (temp); + } + } + } + + /* Set up the $SHELLOPTS variable. */ + set_shellopts (); +} + +/* Reset the values of the -o options that are not also shell flags. This is + called from execute_cmd.c:initialize_subshell() when setting up a subshell + to run an executable shell script without a leading `#!'. */ +void +reset_shell_options () +{ +#if defined (HISTORY) + remember_on_history = enable_history_list = 1; +#endif + ignoreeof = 0; +} + +/* Set some flags from the word values in the input list. If LIST is empty, + then print out the values of the variables instead. If LIST contains + non-flags, then set $1 - $9 to the successive words of LIST. */ +int +set_builtin (list) + WORD_LIST *list; +{ + int on_or_off, flag_name, force_assignment, opts_changed, rv, r; + register char *arg; + char s[3]; + + if (list == 0) + { + print_all_shell_variables (); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + /* Check validity of flag arguments. */ + rv = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((flag_name = internal_getopt (list, optflags)) != -1) + { + switch (flag_name) + { + case '?': + builtin_usage (); + return (list_optopt == '?' ? EXECUTION_SUCCESS : EX_USAGE); + default: + break; + } + } + + /* Do the set command. While the list consists of words starting with + '-' or '+' treat them as flags, otherwise, start assigning them to + $1 ... $n. */ + for (force_assignment = opts_changed = 0; list; ) + { + arg = list->word->word; + + /* If the argument is `--' or `-' then signal the end of the list + and remember the remaining arguments. */ + if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2]))) + { + list = list->next; + + /* `set --' unsets the positional parameters. */ + if (arg[1] == '-') + force_assignment = 1; + + /* Until told differently, the old shell behaviour of + `set - [arg ...]' being equivalent to `set +xv [arg ...]' + stands. Posix.2 says the behaviour is marked as obsolescent. */ + else + { + change_flag ('x', '+'); + change_flag ('v', '+'); + opts_changed = 1; + } + + break; + } + + if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+')) + { + while (flag_name = *++arg) + { + if (flag_name == '?') + { + builtin_usage (); + return (EXECUTION_SUCCESS); + } + else if (flag_name == 'o') /* -+o option-name */ + { + char *option_name; + WORD_LIST *opt; + + opt = list->next; + + if (opt == 0) + { + list_minus_o_opts (-1, (on_or_off == '+')); + rv = sh_chkwrite (rv); + continue; + } + + option_name = opt->word->word; + + if (option_name == 0 || *option_name == '\0' || + *option_name == '-' || *option_name == '+') + { + list_minus_o_opts (-1, (on_or_off == '+')); + continue; + } + list = list->next; /* Skip over option name. */ + + opts_changed = 1; + if ((r = set_minus_o_option (on_or_off, option_name)) != EXECUTION_SUCCESS) + { + set_shellopts (); + return (r); + } + } + else if (change_flag (flag_name, on_or_off) == FLAG_ERROR) + { + s[0] = on_or_off; + s[1] = flag_name; + s[2] = '\0'; + sh_invalidopt (s); + builtin_usage (); + set_shellopts (); + return (EXECUTION_FAILURE); + } + opts_changed = 1; + } + } + else + { + break; + } + list = list->next; + } + + /* Assigning $1 ... $n */ + if (list || force_assignment) + remember_args (list, 1); + /* Set up new value of $SHELLOPTS */ + if (opts_changed) + set_shellopts (); + return (rv); +} + +$BUILTIN unset +$FUNCTION unset_builtin +$SHORT_DOC unset [-f] [-v] [name ...] +Unset values and attributes of shell variables and functions. + +For each NAME, remove the corresponding variable or function. + +Options: + -f treat each NAME as a shell function + -v treat each NAME as a shell variable + +Without options, unset first tries to unset a variable, and if that fails, +tries to unset a function. + +Some variables cannot be unset; also see `readonly'. + +Exit Status: +Returns success unless an invalid option is given or a NAME is read-only. +$END + +#define NEXT_VARIABLE() any_failed++; list = list->next; continue; + +int +unset_builtin (list) + WORD_LIST *list; +{ + int unset_function, unset_variable, unset_array, opt, any_failed; + char *name; + + unset_function = unset_variable = unset_array = any_failed = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fv")) != -1) + { + switch (opt) + { + case 'f': + unset_function = 1; + break; + case 'v': + unset_variable = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (unset_function && unset_variable) + { + builtin_error (_("cannot simultaneously unset a function and a variable")); + return (EXECUTION_FAILURE); + } + + while (list) + { + SHELL_VAR *var; + int tem; +#if defined (ARRAY_VARS) + char *t; +#endif + + name = list->word->word; + +#if defined (ARRAY_VARS) + unset_array = 0; + if (!unset_function && valid_array_reference (name)) + { + t = strchr (name, '['); + *t++ = '\0'; + unset_array++; + } +#endif + + /* Bash allows functions with names which are not valid identifiers + to be created when not in posix mode, so check only when in posix + mode when unsetting a function. */ + if (((unset_function && posixly_correct) || !unset_function) && legal_identifier (name) == 0) + { + sh_invalidid (name); + NEXT_VARIABLE (); + } + + var = unset_function ? find_function (name) : find_variable (name); + + if (var && !unset_function && non_unsettable_p (var)) + { + builtin_error (_("%s: cannot unset"), name); + NEXT_VARIABLE (); + } + + /* Posix.2 says that unsetting readonly variables is an error. */ + if (var && readonly_p (var)) + { + builtin_error (_("%s: cannot unset: readonly %s"), + name, unset_function ? "function" : "variable"); + NEXT_VARIABLE (); + } + + /* Unless the -f option is supplied, the name refers to a variable. */ +#if defined (ARRAY_VARS) + if (var && unset_array) + { + if (array_p (var) == 0 && assoc_p (var) == 0) + { + builtin_error (_("%s: not an array variable"), name); + NEXT_VARIABLE (); + } + else + { + tem = unbind_array_element (var, t); + if (tem == -1) + any_failed++; + } + } + else +#endif /* ARRAY_VARS */ + tem = unset_function ? unbind_func (name) : unbind_variable (name); + + /* This is what Posix.2 draft 11+ says. ``If neither -f nor -v + is specified, the name refers to a variable; if a variable by + that name does not exist, a function by that name, if any, + shall be unset.'' */ + if (tem == -1 && !unset_function && !unset_variable) + tem = unbind_func (name); + + /* SUSv3, POSIX.1-2001 say: ``Unsetting a variable or function that + was not previously set shall not be considered an error.'' */ + + if (unset_function == 0) + stupidly_hack_special_variables (name); + + list = list->next; + } + + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} diff --git a/builtins/setattr.def b/builtins/setattr.def new file mode 100644 index 0000000..8b4cdf7 --- /dev/null +++ b/builtins/setattr.def @@ -0,0 +1,514 @@ +This file is setattr.def, from which is created setattr.c. +It implements the builtins "export" and "readonly", in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES setattr.c + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +extern int posixly_correct; +extern int array_needs_making; +extern char *this_command_name; +extern sh_builtin_func_t *this_shell_builtin; + +#ifdef ARRAY_VARS +extern int declare_builtin __P((WORD_LIST *)); +#endif + +#define READONLY_OR_EXPORT \ + (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin) + +$BUILTIN export +$FUNCTION export_builtin +$SHORT_DOC export [-fn] [name[=value] ...] or export -p +Set export attribute for shell variables. + +Marks each NAME for automatic export to the environment of subsequently +executed commands. If VALUE is supplied, assign VALUE before exporting. + +Options: + -f refer to shell functions + -n remove the export property from each NAME + -p display a list of all exported variables and functions + +An argument of `--' disables further option processing. + +Exit Status: +Returns success unless an invalid option is given or NAME is invalid. +$END + +/* For each variable name in LIST, make that variable appear in the + environment passed to simple commands. If there is no LIST, then + print all such variables. An argument of `-n' says to remove the + exported attribute from variables named in LIST. An argument of + -f indicates that the names present in LIST refer to functions. */ +int +export_builtin (list) + register WORD_LIST *list; +{ + return (set_or_show_attributes (list, att_exported, 0)); +} + +$BUILTIN readonly +$FUNCTION readonly_builtin +$SHORT_DOC readonly [-af] [name[=value] ...] or readonly -p +Mark shell variables as unchangeable. + +Mark each NAME as read-only; the values of these NAMEs may not be +changed by subsequent assignment. If VALUE is supplied, assign VALUE +before marking as read-only. + +Options: + -a refer to indexed array variables + -A refer to associative array variables + -f refer to shell functions + -p display a list of all readonly variables and functions + +An argument of `--' disables further option processing. + +Exit Status: +Returns success unless an invalid option is given or NAME is invalid. +$END + +/* For each variable name in LIST, make that variable readonly. Given an + empty LIST, print out all existing readonly variables. */ +int +readonly_builtin (list) + register WORD_LIST *list; +{ + return (set_or_show_attributes (list, att_readonly, 0)); +} + +#if defined (ARRAY_VARS) +# define ATTROPTS "aAfnp" +#else +# define ATTROPTS "fnp" +#endif + +/* For each variable name in LIST, make that variable have the specified + ATTRIBUTE. An arg of `-n' says to remove the attribute from the the + remaining names in LIST (doesn't work for readonly). */ +int +set_or_show_attributes (list, attribute, nodefs) + register WORD_LIST *list; + int attribute, nodefs; +{ + register SHELL_VAR *var; + int assign, undo, any_failed, assign_error, opt; + int functions_only, arrays_only, assoc_only; + int aflags; + char *name; +#if defined (ARRAY_VARS) + WORD_LIST *nlist, *tlist; + WORD_DESC *w; +#endif + + functions_only = arrays_only = assoc_only = 0; + undo = any_failed = assign_error = 0; + /* Read arguments from the front of the list. */ + reset_internal_getopt (); + while ((opt = internal_getopt (list, ATTROPTS)) != -1) + { + switch (opt) + { + case 'n': + undo = 1; + break; + case 'f': + functions_only = 1; + break; +#if defined (ARRAY_VARS) + case 'a': + arrays_only = 1; + break; + case 'A': + assoc_only = 1; + break; +#endif + case 'p': + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list) + { + if (attribute & att_exported) + array_needs_making = 1; + + /* Cannot undo readonly status, silently disallowed. */ + if (undo && (attribute & att_readonly)) + attribute &= ~att_readonly; + + while (list) + { + name = list->word->word; + + if (functions_only) /* xxx -f name */ + { + var = find_function (name); + if (var == 0) + { + builtin_error (_("%s: not a function"), name); + any_failed++; + } + else + SETVARATTR (var, attribute, undo); + + list = list->next; + continue; + } + + /* xxx [-np] name[=value] */ + assign = assignment (name, 0); + + aflags = 0; + if (assign) + { + name[assign] = '\0'; + if (name[assign - 1] == '+') + { + aflags |= ASS_APPEND; + name[assign - 1] = '\0'; + } + } + + if (legal_identifier (name) == 0) + { + sh_invalidid (name); + if (assign) + assign_error++; + else + any_failed++; + list = list->next; + continue; + } + + if (assign) /* xxx [-np] name=value */ + { + name[assign] = '='; + if (aflags & ASS_APPEND) + name[assign - 1] = '+'; +#if defined (ARRAY_VARS) + /* Let's try something here. Turn readonly -a xxx=yyy into + declare -ra xxx=yyy and see what that gets us. */ + if (arrays_only || assoc_only) + { + tlist = list->next; + list->next = (WORD_LIST *)NULL; + w = arrays_only ? make_word ("-ra") : make_word ("-rA"); + nlist = make_word_list (w, list); + opt = declare_builtin (nlist); + if (opt != EXECUTION_SUCCESS) + assign_error++; + list->next = tlist; + dispose_word (w); + free (nlist); + } + else +#endif + /* This word has already been expanded once with command + and parameter expansion. Call do_assignment_no_expand (), + which does not do command or parameter substitution. If + the assignment is not performed correctly, flag an error. */ + if (do_assignment_no_expand (name) == 0) + assign_error++; + name[assign] = '\0'; + if (aflags & ASS_APPEND) + name[assign - 1] = '\0'; + } + + set_var_attribute (name, attribute, undo); + list = list->next; + } + } + else + { + SHELL_VAR **variable_list; + register int i; + + if ((attribute & att_function) || functions_only) + { + variable_list = all_shell_functions (); + if (attribute != att_function) + attribute &= ~att_function; /* so declare -xf works, for example */ + } + else + variable_list = all_shell_variables (); + +#if defined (ARRAY_VARS) + if (attribute & att_array) + { + arrays_only++; + if (attribute != att_array) + attribute &= ~att_array; + } + else if (attribute & att_assoc) + { + assoc_only++; + if (attribute != att_assoc) + attribute &= ~att_assoc; + } +#endif + + if (variable_list) + { + for (i = 0; var = variable_list[i]; i++) + { +#if defined (ARRAY_VARS) + if (arrays_only && array_p (var) == 0) + continue; + else if (assoc_only && assoc_p (var) == 0) + continue; +#endif + if ((var->attributes & attribute)) + { + show_var_attributes (var, READONLY_OR_EXPORT, nodefs); + if (any_failed = sh_chkwrite (any_failed)) + break; + } + } + free (variable_list); + } + } + + return (assign_error ? EX_BADASSIGN + : ((any_failed == 0) ? EXECUTION_SUCCESS + : EXECUTION_FAILURE)); +} + +/* Show all variable variables (v == 1) or functions (v == 0) with + attributes. */ +int +show_all_var_attributes (v, nodefs) + int v, nodefs; +{ + SHELL_VAR **variable_list, *var; + int any_failed; + register int i; + + variable_list = v ? all_shell_variables () : all_shell_functions (); + if (variable_list == 0) + return (EXECUTION_SUCCESS); + + for (i = any_failed = 0; var = variable_list[i]; i++) + { + show_var_attributes (var, READONLY_OR_EXPORT, nodefs); + if (any_failed = sh_chkwrite (any_failed)) + break; + } + free (variable_list); + return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +/* Show the attributes for shell variable VAR. If NODEFS is non-zero, + don't show function definitions along with the name. If PATTR is + non-zero, it indicates we're being called from `export' or `readonly'. + In POSIX mode, this prints the name of the calling builtin (`export' + or `readonly') instead of `declare', and doesn't print function defs + when called by `export' or `readonly'. */ +int +show_var_attributes (var, pattr, nodefs) + SHELL_VAR *var; + int pattr, nodefs; +{ + char flags[16], *x; + int i; + + i = 0; + + /* pattr == 0 means we are called from `declare'. */ + if (pattr == 0 || posixly_correct == 0) + { +#if defined (ARRAY_VARS) + if (array_p (var)) + flags[i++] = 'a'; + + if (assoc_p (var)) + flags[i++] = 'A'; +#endif + + if (function_p (var)) + flags[i++] = 'f'; + + if (integer_p (var)) + flags[i++] = 'i'; + + if (readonly_p (var)) + flags[i++] = 'r'; + + if (trace_p (var)) + flags[i++] = 't'; + + if (exported_p (var)) + flags[i++] = 'x'; + + if (capcase_p (var)) + flags[i++] = 'c'; + + if (lowercase_p (var)) + flags[i++] = 'l'; + + if (uppercase_p (var)) + flags[i++] = 'u'; + } + else + { +#if defined (ARRAY_VARS) + if (array_p (var)) + flags[i++] = 'a'; + + if (assoc_p (var)) + flags[i++] = 'A'; +#endif + + if (function_p (var)) + flags[i++] = 'f'; + } + + flags[i] = '\0'; + + /* If we're printing functions with definitions, print the function def + first, then the attributes, instead of printing output that can't be + reused as input to recreate the current state. */ + if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0)) + { + printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL)); + nodefs++; + if (pattr == 0 && i == 1 && flags[0] == 'f') + return 0; /* don't print `declare -f name' */ + } + + if (pattr == 0 || posixly_correct == 0) + printf ("declare -%s ", i ? flags : "-"); + else if (i) + printf ("%s -%s ", this_command_name, flags); + else + printf ("%s ", this_command_name); + +#if defined (ARRAY_VARS) + if (array_p (var)) + print_array_assignment (var, 1); + else if (assoc_p (var)) + print_assoc_assignment (var, 1); + else +#endif + /* force `readonly' and `export' to not print out function definitions + when in POSIX mode. */ + if (nodefs || (function_p (var) && pattr != 0 && posixly_correct)) + printf ("%s\n", var->name); + else if (function_p (var)) + printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL)); + else if (invisible_p (var)) + printf ("%s\n", var->name); + else + { + x = sh_double_quote (var_isset (var) ? value_cell (var) : ""); + printf ("%s=%s\n", var->name, x); + free (x); + } + return (0); +} + +int +show_name_attributes (name, nodefs) + char *name; + int nodefs; +{ + SHELL_VAR *var; + + var = find_variable_internal (name, 1); + + if (var && invisible_p (var) == 0) + { + show_var_attributes (var, READONLY_OR_EXPORT, nodefs); + return (0); + } + else + return (1); +} + +void +set_var_attribute (name, attribute, undo) + char *name; + int attribute, undo; +{ + SHELL_VAR *var, *tv; + char *tvalue; + + if (undo) + var = find_variable (name); + else + { + tv = find_tempenv_variable (name); + /* XXX -- need to handle case where tv is a temp variable in a + function-scope context, since function_env has been merged into + the local variables table. */ + if (tv && tempvar_p (tv)) + { + tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring (""); + + var = bind_variable (tv->name, tvalue, 0); + var->attributes |= tv->attributes & ~att_tempvar; + VSETATTR (tv, att_propagate); + if (var->context != 0) + VSETATTR (var, att_propagate); + SETVARATTR (tv, attribute, undo); /* XXX */ + + stupidly_hack_special_variables (tv->name); + + free (tvalue); + } + else + { + var = find_variable_internal (name, 0); + if (var == 0) + { + var = bind_variable (name, (char *)NULL, 0); + VSETATTR (var, att_invisible); + } + else if (var->context != 0) + VSETATTR (var, att_propagate); + } + } + + if (var) + SETVARATTR (var, attribute, undo); + + if (var && (exported_p (var) || (attribute & att_exported))) + array_needs_making++; /* XXX */ +} diff --git a/builtins/shift.def b/builtins/shift.def new file mode 100644 index 0000000..3213086 --- /dev/null +++ b/builtins/shift.def @@ -0,0 +1,101 @@ +This file is shift.def, from which is created shift.c. +It implements the builtin "shift" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES shift.c + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" + +$BUILTIN shift +$FUNCTION shift_builtin +$SHORT_DOC shift [n] +Shift positional parameters. + +Rename the positional parameters $N+1,$N+2 ... to $1,$2 ... If N is +not given, it is assumed to be 1. + +Exit Status: +Returns success unless N is negative or greater than $#. +$END + +int print_shift_error; + +/* Shift the arguments ``left''. Shift DOLLAR_VARS down then take one + off of REST_OF_ARGS and place it into DOLLAR_VARS[9]. If LIST has + anything in it, it is a number which says where to start the + shifting. Return > 0 if `times' > $#, otherwise 0. */ +int +shift_builtin (list) + WORD_LIST *list; +{ + intmax_t times; + register int count; + WORD_LIST *temp; + + if (get_numeric_arg (list, 0, ×) == 0) + return (EXECUTION_FAILURE); + + if (times == 0) + return (EXECUTION_SUCCESS); + else if (times < 0) + { + sh_erange (list ? list->word->word : NULL, _("shift count")); + return (EXECUTION_FAILURE); + } + else if (times > number_of_args ()) + { + if (print_shift_error) + sh_erange (list ? list->word->word : NULL, _("shift count")); + return (EXECUTION_FAILURE); + } + + while (times-- > 0) + { + if (dollar_vars[1]) + free (dollar_vars[1]); + + for (count = 1; count < 9; count++) + dollar_vars[count] = dollar_vars[count + 1]; + + if (rest_of_args) + { + temp = rest_of_args; + dollar_vars[9] = savestring (temp->word->word); + rest_of_args = rest_of_args->next; + temp->next = (WORD_LIST *)NULL; + dispose_words (temp); + } + else + dollar_vars[9] = (char *)NULL; + } + return (EXECUTION_SUCCESS); +} diff --git a/builtins/shopt.def b/builtins/shopt.def new file mode 100644 index 0000000..68c7245 --- /dev/null +++ b/builtins/shopt.def @@ -0,0 +1,715 @@ +This file is shopt.def, from which is created shopt.c. +It implements the Bash `shopt' builtin. + +Copyright (C) 1994-2009 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/>. + +$PRODUCES shopt.c + +$BUILTIN shopt +$FUNCTION shopt_builtin +$SHORT_DOC shopt [-pqsu] [-o] [optname ...] +Set and unset shell options. + +Change the setting of each shell option OPTNAME. Without any option +arguments, list all shell options with an indication of whether or not each +is set. + +Options: + -o restrict OPTNAMEs to those defined for use with `set -o' + -p print each shell option with an indication of its status + -q suppress output + -s enable (set) each OPTNAME + -u disable (unset) each OPTNAME + +Exit Status: +Returns success if OPTNAME is enabled; fails if an invalid option is +given or OPTNAME is disabled. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "version.h" + +#include "../bashintl.h" + +#include "../shell.h" +#include "../flags.h" +#include "common.h" +#include "bashgetopt.h" + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#define UNSETOPT 0 +#define SETOPT 1 + +#define OPTFMT "%-15s\t%s\n" + +extern int allow_null_glob_expansion, fail_glob_expansion, glob_dot_filenames; +extern int cdable_vars, mail_warning, source_uses_path; +extern int no_exit_on_failed_exec, print_shift_error; +extern int check_hashed_filenames, promptvars; +extern int cdspelling, expand_aliases; +extern int extended_quote; +extern int check_window_size; +extern int glob_ignore_case, match_ignore_case; +extern int hup_on_exit; +extern int xpg_echo; +extern int gnu_error_format; +extern int check_jobs_at_exit; +extern int autocd; +extern int glob_star; + +#if defined (EXTENDED_GLOB) +extern int extended_glob; +#endif + +#if defined (READLINE) +extern int hist_verify, history_reediting, perform_hostname_completion; +extern int no_empty_command_completion; +extern int force_fignore; +extern int dircomplete_spelling; + +extern int enable_hostname_completion __P((int)); +#endif + +#if defined (PROGRAMMABLE_COMPLETION) +extern int prog_completion_enabled; +#endif + +#if defined (RESTRICTED_SHELL) +extern char *shell_name; +#endif + +#if defined (DEBUGGER) +extern int debugging_mode; +#endif + +static void shopt_error __P((char *)); + +static int set_shellopts_after_change __P((char *, int)); +static int shopt_enable_hostname_completion __P((char *, int)); +static int set_compatibility_level __P((char *, int)); + +#if defined (RESTRICTED_SHELL) +static int set_restricted_shell __P((char *, int)); +#endif + +static int shopt_login_shell; +static int shopt_compat31; +static int shopt_compat32; +static int shopt_compat40; + +typedef int shopt_set_func_t __P((char *, int)); + +static struct { + char *name; + int *value; + shopt_set_func_t *set_func; +} shopt_vars[] = { + { "autocd", &autocd, (shopt_set_func_t *)NULL }, + { "cdable_vars", &cdable_vars, (shopt_set_func_t *)NULL }, + { "cdspell", &cdspelling, (shopt_set_func_t *)NULL }, + { "checkhash", &check_hashed_filenames, (shopt_set_func_t *)NULL }, +#if defined (JOB_CONTROL) + { "checkjobs", &check_jobs_at_exit, (shopt_set_func_t *)NULL }, +#endif + { "checkwinsize", &check_window_size, (shopt_set_func_t *)NULL }, +#if defined (HISTORY) + { "cmdhist", &command_oriented_history, (shopt_set_func_t *)NULL }, +#endif + { "compat31", &shopt_compat31, set_compatibility_level }, + { "compat32", &shopt_compat32, set_compatibility_level }, + { "compat40", &shopt_compat40, set_compatibility_level }, +#if defined (READLINE) + { "dirspell", &dircomplete_spelling, (shopt_set_func_t *)NULL }, +#endif + { "dotglob", &glob_dot_filenames, (shopt_set_func_t *)NULL }, + { "execfail", &no_exit_on_failed_exec, (shopt_set_func_t *)NULL }, + { "expand_aliases", &expand_aliases, (shopt_set_func_t *)NULL }, +#if defined (DEBUGGER) + { "extdebug", &debugging_mode, (shopt_set_func_t *)NULL }, +#endif +#if defined (EXTENDED_GLOB) + { "extglob", &extended_glob, (shopt_set_func_t *)NULL }, +#endif + { "extquote", &extended_quote, (shopt_set_func_t *)NULL }, + { "failglob", &fail_glob_expansion, (shopt_set_func_t *)NULL }, +#if defined (READLINE) + { "force_fignore", &force_fignore, (shopt_set_func_t *)NULL }, +#endif + { "globstar", &glob_star, (shopt_set_func_t *)NULL }, + { "gnu_errfmt", &gnu_error_format, (shopt_set_func_t *)NULL }, +#if defined (HISTORY) + { "histappend", &force_append_history, (shopt_set_func_t *)NULL }, +#endif +#if defined (READLINE) + { "histreedit", &history_reediting, (shopt_set_func_t *)NULL }, + { "histverify", &hist_verify, (shopt_set_func_t *)NULL }, + { "hostcomplete", &perform_hostname_completion, shopt_enable_hostname_completion }, +#endif + { "huponexit", &hup_on_exit, (shopt_set_func_t *)NULL }, + { "interactive_comments", &interactive_comments, set_shellopts_after_change }, +#if defined (HISTORY) + { "lithist", &literal_history, (shopt_set_func_t *)NULL }, +#endif + { "login_shell", &shopt_login_shell, set_login_shell }, + { "mailwarn", &mail_warning, (shopt_set_func_t *)NULL }, +#if defined (READLINE) + { "no_empty_cmd_completion", &no_empty_command_completion, (shopt_set_func_t *)NULL }, +#endif + { "nocaseglob", &glob_ignore_case, (shopt_set_func_t *)NULL }, + { "nocasematch", &match_ignore_case, (shopt_set_func_t *)NULL }, + { "nullglob", &allow_null_glob_expansion, (shopt_set_func_t *)NULL }, +#if defined (PROGRAMMABLE_COMPLETION) + { "progcomp", &prog_completion_enabled, (shopt_set_func_t *)NULL }, +#endif + { "promptvars", &promptvars, (shopt_set_func_t *)NULL }, +#if defined (RESTRICTED_SHELL) + { "restricted_shell", &restricted_shell, set_restricted_shell }, +#endif + { "shift_verbose", &print_shift_error, (shopt_set_func_t *)NULL }, + { "sourcepath", &source_uses_path, (shopt_set_func_t *)NULL }, + { "xpg_echo", &xpg_echo, (shopt_set_func_t *)NULL }, + { (char *)0, (int *)0, (shopt_set_func_t *)NULL } +}; + +#define N_SHOPT_OPTIONS (sizeof (shopt_vars) / sizeof (shopt_vars[0])) + +#define GET_SHOPT_OPTION_VALUE(i) (*shopt_vars[i].value) + +static const char * const on = "on"; +static const char * const off = "off"; + +static int find_shopt __P((char *)); +static int toggle_shopts __P((int, WORD_LIST *, int)); +static void print_shopt __P((char *, int, int)); +static int list_shopts __P((WORD_LIST *, int)); +static int list_some_shopts __P((int, int)); +static int list_shopt_o_options __P((WORD_LIST *, int)); +static int list_some_o_options __P((int, int)); +static int set_shopt_o_options __P((int, WORD_LIST *, int)); + +#define SFLAG 0x01 +#define UFLAG 0x02 +#define QFLAG 0x04 +#define OFLAG 0x08 +#define PFLAG 0x10 + +int +shopt_builtin (list) + WORD_LIST *list; +{ + int opt, flags, rval; + + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "psuoq")) != -1) + { + switch (opt) + { + case 's': + flags |= SFLAG; + break; + case 'u': + flags |= UFLAG; + break; + case 'q': + flags |= QFLAG; + break; + case 'o': + flags |= OFLAG; + break; + case 'p': + flags |= PFLAG; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if ((flags & (SFLAG|UFLAG)) == (SFLAG|UFLAG)) + { + builtin_error (_("cannot set and unset shell options simultaneously")); + return (EXECUTION_FAILURE); + } + + rval = EXECUTION_SUCCESS; + if ((flags & OFLAG) && ((flags & (SFLAG|UFLAG)) == 0)) /* shopt -o */ + rval = list_shopt_o_options (list, flags); + else if (list && (flags & OFLAG)) /* shopt -so args */ + rval = set_shopt_o_options ((flags & SFLAG) ? FLAG_ON : FLAG_OFF, list, flags & QFLAG); + else if (flags & OFLAG) /* shopt -so */ + rval = list_some_o_options ((flags & SFLAG) ? 1 : 0, flags); + else if (list && (flags & (SFLAG|UFLAG))) /* shopt -su args */ + rval = toggle_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, list, flags & QFLAG); + else if ((flags & (SFLAG|UFLAG)) == 0) /* shopt [args] */ + rval = list_shopts (list, flags); + else /* shopt -su */ + rval = list_some_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, flags); + return (rval); +} + +/* Reset the options managed by `shopt' to the values they would have at + shell startup. */ +void +reset_shopt_options () +{ + allow_null_glob_expansion = glob_dot_filenames = 0; + cdable_vars = mail_warning = 0; + no_exit_on_failed_exec = print_shift_error = 0; + check_hashed_filenames = cdspelling = expand_aliases = check_window_size = 0; + + source_uses_path = promptvars = 1; + +#if defined (EXTENDED_GLOB) + extended_glob = 0; +#endif + +#if defined (HISTORY) + literal_history = force_append_history = 0; + command_oriented_history = 1; +#endif + +#if defined (READLINE) + hist_verify = history_reediting = 0; + perform_hostname_completion = 1; +#endif + + shopt_login_shell = login_shell; +} + +static int +find_shopt (name) + char *name; +{ + int i; + + for (i = 0; shopt_vars[i].name; i++) + if (STREQ (name, shopt_vars[i].name)) + return i; + return -1; +} + +static void +shopt_error (s) + char *s; +{ + builtin_error (_("%s: invalid shell option name"), s); +} + +static int +toggle_shopts (mode, list, quiet) + int mode; + WORD_LIST *list; + int quiet; +{ + WORD_LIST *l; + int ind, rval; + + for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next) + { + ind = find_shopt (l->word->word); + if (ind < 0) + { + shopt_error (l->word->word); + rval = EXECUTION_FAILURE; + } + else + { + *shopt_vars[ind].value = mode; /* 1 for set, 0 for unset */ + if (shopt_vars[ind].set_func) + (*shopt_vars[ind].set_func) (shopt_vars[ind].name, mode); + } + } + + set_bashopts (); + return (rval); +} + +static void +print_shopt (name, val, flags) + char *name; + int val, flags; +{ + if (flags & PFLAG) + printf ("shopt %s %s\n", val ? "-s" : "-u", name); + else + printf (OPTFMT, name, val ? on : off); +} + +/* List the values of all or any of the `shopt' options. Returns 0 if + all were listed or all variables queried were on; 1 otherwise. */ +static int +list_shopts (list, flags) + WORD_LIST *list; + int flags; +{ + WORD_LIST *l; + int i, val, rval; + + if (list == 0) + { + for (i = 0; shopt_vars[i].name; i++) + { + val = *shopt_vars[i].value; + if ((flags & QFLAG) == 0) + print_shopt (shopt_vars[i].name, val, flags); + } + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next) + { + i = find_shopt (l->word->word); + if (i < 0) + { + shopt_error (l->word->word); + rval = EXECUTION_FAILURE; + continue; + } + val = *shopt_vars[i].value; + if (val == 0) + rval = EXECUTION_FAILURE; + if ((flags & QFLAG) == 0) + print_shopt (l->word->word, val, flags); + } + + return (sh_chkwrite (rval)); +} + +static int +list_some_shopts (mode, flags) + int mode, flags; +{ + int val, i; + + for (i = 0; shopt_vars[i].name; i++) + { + val = *shopt_vars[i].value; + if (((flags & QFLAG) == 0) && mode == val) + print_shopt (shopt_vars[i].name, val, flags); + } + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +static int +list_shopt_o_options (list, flags) + WORD_LIST *list; + int flags; +{ + WORD_LIST *l; + int val, rval; + + if (list == 0) + { + if ((flags & QFLAG) == 0) + list_minus_o_opts (-1, (flags & PFLAG)); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next) + { + val = minus_o_option_value (l->word->word); + if (val == -1) + { + sh_invalidoptname (l->word->word); + rval = EXECUTION_FAILURE; + continue; + } + if (val == 0) + rval = EXECUTION_FAILURE; + if ((flags & QFLAG) == 0) + { + if (flags & PFLAG) + printf ("set %co %s\n", val ? '-' : '+', l->word->word); + else + printf (OPTFMT, l->word->word, val ? on : off); + } + } + return (sh_chkwrite (rval)); +} + +static int +list_some_o_options (mode, flags) + int mode, flags; +{ + if ((flags & QFLAG) == 0) + list_minus_o_opts (mode, (flags & PFLAG)); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +static int +set_shopt_o_options (mode, list, quiet) + int mode; + WORD_LIST *list; + int quiet; +{ + WORD_LIST *l; + int rval; + + for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next) + { + if (set_minus_o_option (mode, l->word->word) == EXECUTION_FAILURE) + rval = EXECUTION_FAILURE; + } + set_shellopts (); + return rval; +} + +/* If we set or unset interactive_comments with shopt, make sure the + change is reflected in $SHELLOPTS. */ +static int +set_shellopts_after_change (option_name, mode) + char *option_name; + int mode; +{ + set_shellopts (); + return (0); +} + +static int +shopt_enable_hostname_completion (option_name, mode) + char *option_name; + int mode; +{ + return (enable_hostname_completion (mode)); +} + +static int +set_compatibility_level (option_name, mode) + char *option_name; + int mode; +{ + /* Need to change logic here as we add more compatibility levels */ + + /* First, check option_name so we can turn off other compat options when + one is set. */ + if (mode && option_name[6] == '3' && option_name[7] == '1') + shopt_compat32 = shopt_compat40 = 0; + else if (mode && option_name[6] == '3' && option_name[7] == '2') + shopt_compat31 = shopt_compat40 = 0; + else if (mode && option_name[6] == '4' && option_name[7] == '0') + shopt_compat31 = shopt_compat32 = 0; + + /* Then set shell_compatibility_level based on what remains */ + if (shopt_compat31) + shell_compatibility_level = 31; + else if (shopt_compat32) + shell_compatibility_level = 32; + else if (shopt_compat40) + shell_compatibility_level = 40; + else + shell_compatibility_level = DEFAULT_COMPAT_LEVEL; + return 0; +} + +#if defined (RESTRICTED_SHELL) +/* Don't allow the value of restricted_shell to be modified. */ + +static int +set_restricted_shell (option_name, mode) + char *option_name; + int mode; +{ + static int save_restricted = -1; + + if (save_restricted == -1) + save_restricted = shell_is_restricted (shell_name); + + restricted_shell = save_restricted; + return (0); +} +#endif /* RESTRICTED_SHELL */ + +/* Not static so shell.c can call it to initialize shopt_login_shell */ +int +set_login_shell (option_name, mode) + char *option_name; + int mode; +{ + shopt_login_shell = login_shell != 0; + return (0); +} + +char ** +get_shopt_options () +{ + char **ret; + int n, i; + + n = sizeof (shopt_vars) / sizeof (shopt_vars[0]); + ret = strvec_create (n + 1); + for (i = 0; shopt_vars[i].name; i++) + ret[i] = savestring (shopt_vars[i].name); + ret[i] = (char *)NULL; + return ret; +} + +/* + * External interface for other parts of the shell. NAME is a string option; + * MODE is 0 if we want to unset an option; 1 if we want to set an option. + * REUSABLE is 1 if we want to print output in a form that may be reused. + */ +int +shopt_setopt (name, mode) + char *name; + int mode; +{ + WORD_LIST *wl; + int r; + + wl = add_string_to_list (name, (WORD_LIST *)NULL); + r = toggle_shopts (mode, wl, 0); + dispose_words (wl); + return r; +} + +int +shopt_listopt (name, reusable) + char *name; + int reusable; +{ + int i; + + if (name == 0) + return (list_shopts ((WORD_LIST *)NULL, reusable ? PFLAG : 0)); + + i = find_shopt (name); + if (i < 0) + { + shopt_error (name); + return (EXECUTION_FAILURE); + } + + print_shopt (name, *shopt_vars[i].value, reusable ? PFLAG : 0); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +void +set_bashopts () +{ + char *value; + char tflag[N_SHOPT_OPTIONS]; + int vsize, i, vptr, *ip, exported; + SHELL_VAR *v; + + for (vsize = i = 0; shopt_vars[i].name; i++) + { + tflag[i] = 0; + if (GET_SHOPT_OPTION_VALUE (i)) + { + vsize += strlen (shopt_vars[i].name) + 1; + tflag[i] = 1; + } + } + + value = (char *)xmalloc (vsize + 1); + + for (i = vptr = 0; shopt_vars[i].name; i++) + { + if (tflag[i]) + { + strcpy (value + vptr, shopt_vars[i].name); + vptr += strlen (shopt_vars[i].name); + value[vptr++] = ':'; + } + } + + if (vptr) + vptr--; /* cut off trailing colon */ + value[vptr] = '\0'; + + v = find_variable ("BASHOPTS"); + + /* Turn off the read-only attribute so we can bind the new value, and + note whether or not the variable was exported. */ + if (v) + { + VUNSETATTR (v, att_readonly); + exported = exported_p (v); + } + else + exported = 0; + + v = bind_variable ("BASHOPTS", value, 0); + + /* Turn the read-only attribute back on, and turn off the export attribute + if it was set implicitly by mark_modified_vars and SHELLOPTS was not + exported before we bound the new value. */ + VSETATTR (v, att_readonly); + if (mark_modified_vars && exported == 0 && exported_p (v)) + VUNSETATTR (v, att_exported); + + free (value); +} + +void +parse_bashopts (value) + char *value; +{ + char *vname; + int vptr, ind; + + vptr = 0; + while (vname = extract_colon_unit (value, &vptr)) + { + ind = find_shopt (vname); + if (ind >= 0) + *shopt_vars[ind].value = 1; + free (vname); + } +} + +void +initialize_bashopts (no_bashopts) + int no_bashopts; +{ + char *temp; + SHELL_VAR *var; + + if (no_bashopts == 0) + { + var = find_variable ("BASHOPTS"); + /* set up any shell options we may have inherited. */ + if (var && imported_p (var)) + { + temp = (array_p (var) || assoc_p (var)) ? (char *)NULL : savestring (value_cell (var)); + if (temp) + { + parse_bashopts (temp); + free (temp); + } + } + } + + /* Set up the $BASHOPTS variable. */ + set_bashopts (); +} diff --git a/builtins/source.def b/builtins/source.def new file mode 100644 index 0000000..72627db --- /dev/null +++ b/builtins/source.def @@ -0,0 +1,190 @@ +This file is source.def, from which is created source.c. +It implements the builtins "." and "source" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES source.c + +$BUILTIN source +$FUNCTION source_builtin +$SHORT_DOC source filename [arguments] +Execute commands from a file in the current shell. + +Read and execute commands from FILENAME in the current shell. The +entries in $PATH are used to find the directory containing FILENAME. +If any ARGUMENTS are supplied, they become the positional parameters +when FILENAME is executed. + +Exit Status: +Returns the status of the last command executed in FILENAME; fails if +FILENAME cannot be read. +$END + +$BUILTIN . +$DOCNAME dot +$FUNCTION source_builtin +$SHORT_DOC . filename [arguments] +Execute commands from a file in the current shell. + +Read and execute commands from FILENAME in the current shell. The +entries in $PATH are used to find the directory containing FILENAME. +If any ARGUMENTS are supplied, they become the positional parameters +when FILENAME is executed. + +Exit Status: +Returns the status of the last command executed in FILENAME; fails if +FILENAME cannot be read. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif +#include <errno.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../flags.h" +#include "../findcmd.h" +#include "common.h" +#include "bashgetopt.h" +#include "../trap.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int posixly_correct; + +static void maybe_pop_dollar_vars __P((void)); + +/* If non-zero, `.' uses $PATH to look up the script to be sourced. */ +int source_uses_path = 1; + +/* If non-zero, `.' looks in the current directory if the filename argument + is not found in the $PATH. */ +int source_searches_cwd = 1; + +/* If this . script is supplied arguments, we save the dollar vars and + replace them with the script arguments for the duration of the script's + execution. If the script does not change the dollar vars, we restore + what we saved. If the dollar vars are changed in the script, and we are + not executing a shell function, we leave the new values alone and free + the saved values. */ +static void +maybe_pop_dollar_vars () +{ + if (variable_context == 0 && (dollar_vars_changed () & ARGS_SETBLTIN)) + dispose_saved_dollar_vars (); + else + pop_dollar_vars (); + if (debugging_mode) + pop_args (); /* restore BASH_ARGC and BASH_ARGV */ + set_dollar_vars_unchanged (); +} + +/* Read and execute commands from the file passed as argument. Guess what. + This cannot be done in a subshell, since things like variable assignments + take place in there. So, I open the file, place it into a large string, + close the file, and then execute the string. */ +int +source_builtin (list) + WORD_LIST *list; +{ + int result; + char *filename, *debug_trap; + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + if (list == 0) + { + builtin_error (_("filename argument required")); + builtin_usage (); + return (EX_USAGE); + } + +#if defined (RESTRICTED_SHELL) + if (restricted && strchr (list->word->word, '/')) + { + sh_restricted (list->word->word); + return (EXECUTION_FAILURE); + } +#endif + + filename = (char *)NULL; + /* XXX -- should this be absolute_pathname? */ + if (posixly_correct && strchr (list->word->word, '/')) + filename = savestring (list->word->word); + else if (absolute_pathname (list->word->word)) + filename = savestring (list->word->word); + else if (source_uses_path) + filename = find_path_file (list->word->word); + if (filename == 0) + { + if (source_searches_cwd == 0) + { + builtin_error (_("%s: file not found"), list->word->word); + return (EXECUTION_FAILURE); + } + else + filename = savestring (list->word->word); + } + + begin_unwind_frame ("source"); + add_unwind_protect ((Function *)xfree, filename); + + if (list->next) + { + push_dollar_vars (); + add_unwind_protect ((Function *)maybe_pop_dollar_vars, (char *)NULL); + remember_args (list->next, 1); + if (debugging_mode) + push_args (list->next); /* Update BASH_ARGV and BASH_ARGC */ + } + set_dollar_vars_unchanged (); + + /* Don't inherit the DEBUG trap unless function_trace_mode (overloaded) + is set. XXX - should sourced files inherit the RETURN trap? Functions + don't. */ + debug_trap = TRAP_STRING (DEBUG_TRAP); + if (debug_trap && function_trace_mode == 0) + { + debug_trap = savestring (debug_trap); + add_unwind_protect (xfree, debug_trap); + add_unwind_protect (set_debug_trap, debug_trap); + restore_default_signal (DEBUG_TRAP); + } + + result = source_file (filename, (list && list->next)); + + run_unwind_frame ("source"); + + return (result); +} diff --git a/builtins/suspend.def b/builtins/suspend.def new file mode 100644 index 0000000..8711e68 --- /dev/null +++ b/builtins/suspend.def @@ -0,0 +1,127 @@ +This file is suspend.def, from which is created suspend.c. +It implements the builtin "suspend" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES suspend.c + +$BUILTIN suspend +$DEPENDS_ON JOB_CONTROL +$FUNCTION suspend_builtin +$SHORT_DOC suspend [-f] +Suspend shell execution. + +Suspend the execution of this shell until it receives a SIGCONT signal. +Unless forced, login shells cannot be suspended. + +Options: + -f force the suspend, even if the shell is a login shell + +Exit Status: +Returns success unless job control is not enabled or an error occurs. +$END + +#include <config.h> + +#if defined (JOB_CONTROL) +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashtypes.h" +#include <signal.h> +#include "../bashintl.h" +#include "../shell.h" +#include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" + +static sighandler suspend_continue __P((int)); + +static SigHandler *old_cont; +#if 0 +static SigHandler *old_stop; +#endif + +/* Continue handler. */ +static sighandler +suspend_continue (sig) + int sig; +{ + set_signal_handler (SIGCONT, old_cont); +#if 0 + set_signal_handler (SIGSTOP, old_stop); +#endif + SIGRETURN (0); +} + +/* Suspending the shell. If -f is the arg, then do the suspend + no matter what. Otherwise, complain if a login shell. */ +int +suspend_builtin (list) + WORD_LIST *list; +{ + int opt, force; + + reset_internal_getopt (); + force = 0; + while ((opt = internal_getopt (list, "f")) != -1) + switch (opt) + { + case 'f': + force++; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + + list = loptend; + + if (job_control == 0) + { + sh_nojobs (_("cannot suspend")); + return (EXECUTION_FAILURE); + } + + if (force == 0) + { + no_args (list); + + if (login_shell) + { + builtin_error (_("cannot suspend a login shell")); + return (EXECUTION_FAILURE); + } + } + + /* XXX - should we put ourselves back into the original pgrp now? If so, + call end_job_control() here and do the right thing in suspend_continue + (that is, call restart_job_control()). */ + old_cont = (SigHandler *)set_signal_handler (SIGCONT, suspend_continue); +#if 0 + old_stop = (SigHandler *)set_signal_handler (SIGSTOP, SIG_DFL); +#endif + killpg (shell_pgrp, SIGSTOP); + return (EXECUTION_SUCCESS); +} + +#endif /* JOB_CONTROL */ diff --git a/builtins/test.def b/builtins/test.def new file mode 100644 index 0000000..4adff93 --- /dev/null +++ b/builtins/test.def @@ -0,0 +1,154 @@ +This file is test.def, from which is created test.c. +It implements the builtin "test" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES test.c + +$BUILTIN test +$FUNCTION test_builtin +$SHORT_DOC test [expr] +Evaluate conditional expression. + +Exits with a status of 0 (true) or 1 (false) depending on +the evaluation of EXPR. Expressions may be unary or binary. Unary +expressions are often used to examine the status of a file. There +are string operators as well, and numeric comparison operators. + +File operators: + + -a FILE True if file exists. + -b FILE True if file is block special. + -c FILE True if file is character special. + -d FILE True if file is a directory. + -e FILE True if file exists. + -f FILE True if file exists and is a regular file. + -g FILE True if file is set-group-id. + -h FILE True if file is a symbolic link. + -L FILE True if file is a symbolic link. + -k FILE True if file has its `sticky' bit set. + -p FILE True if file is a named pipe. + -r FILE True if file is readable by you. + -s FILE True if file exists and is not empty. + -S FILE True if file is a socket. + -t FD True if FD is opened on a terminal. + -u FILE True if the file is set-user-id. + -w FILE True if the file is writable by you. + -x FILE True if the file is executable by you. + -O FILE True if the file is effectively owned by you. + -G FILE True if the file is effectively owned by your group. + -N FILE True if the file has been modified since it was last read. + + FILE1 -nt FILE2 True if file1 is newer than file2 (according to + modification date). + + FILE1 -ot FILE2 True if file1 is older than file2. + + FILE1 -ef FILE2 True if file1 is a hard link to file2. + +String operators: + + -z STRING True if string is empty. + + -n STRING + STRING True if string is not empty. + + STRING1 = STRING2 + True if the strings are equal. + STRING1 != STRING2 + True if the strings are not equal. + STRING1 < STRING2 + True if STRING1 sorts before STRING2 lexicographically. + STRING1 > STRING2 + True if STRING1 sorts after STRING2 lexicographically. + +Other operators: + + -o OPTION True if the shell option OPTION is enabled. + ! EXPR True if expr is false. + EXPR1 -a EXPR2 True if both expr1 AND expr2 are true. + EXPR1 -o EXPR2 True if either expr1 OR expr2 is true. + + arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne, + -lt, -le, -gt, or -ge. + +Arithmetic binary operators return true if ARG1 is equal, not-equal, +less-than, less-than-or-equal, greater-than, or greater-than-or-equal +than ARG2. + +Exit Status: +Returns success if EXPR evaluates to true; fails if EXPR evaluates to +false or an invalid argument is given. +$END + +$BUILTIN [ +$DOCNAME test_bracket +$FUNCTION test_builtin +$SHORT_DOC [ arg... ] +Evaluate conditional expression. + +This is a synonym for the "test" builtin, but the last argument must +be a literal `]', to match the opening `['. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../test.h" +#include "common.h" + +extern char *this_command_name; + +/* TEST/[ builtin. */ +int +test_builtin (list) + WORD_LIST *list; +{ + char **argv; + int argc, result; + + /* We let Matthew Bradburn and Kevin Braunsdorf's code do the + actual test command. So turn the list of args into an array + of strings, since that is what their code wants. */ + if (list == 0) + { + if (this_command_name[0] == '[' && !this_command_name[1]) + { + builtin_error (_("missing `]'")); + return (EX_BADUSAGE); + } + + return (EXECUTION_FAILURE); + } + + argv = make_builtin_argv (list, &argc); + result = test_command (argc, argv); + free ((char *)argv); + + return (result); +} diff --git a/builtins/times.def b/builtins/times.def new file mode 100644 index 0000000..f31f433 --- /dev/null +++ b/builtins/times.def @@ -0,0 +1,119 @@ +This file is times.def, from which is created times.c. +It implements the builtin "times" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES times.c + +$BUILTIN times +$FUNCTION times_builtin +$SHORT_DOC times +Display process times. + +Prints the accumulated user and system times for the shell and all of its +child processes. + +Exit Status: +Always succeeds. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashtypes.h" +#include "../shell.h" + +#include <posixtime.h> + +#if defined (HAVE_SYS_TIMES_H) +# include <sys/times.h> +#endif /* HAVE_SYS_TIMES_H */ + +#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE) +# include <sys/resource.h> +#endif + +#include "common.h" + +/* Print the totals for system and user time used. */ +int +times_builtin (list) + WORD_LIST *list; +{ +#if defined (HAVE_GETRUSAGE) && defined (HAVE_TIMEVAL) && defined (RUSAGE_SELF) + struct rusage self, kids; + + USE_VAR(list); + + if (no_options (list)) + return (EX_USAGE); + + getrusage (RUSAGE_SELF, &self); + getrusage (RUSAGE_CHILDREN, &kids); /* terminated child processes */ + + print_timeval (stdout, &self.ru_utime); + putchar (' '); + print_timeval (stdout, &self.ru_stime); + putchar ('\n'); + print_timeval (stdout, &kids.ru_utime); + putchar (' '); + print_timeval (stdout, &kids.ru_stime); + putchar ('\n'); + +#else +# if defined (HAVE_TIMES) + /* This uses the POSIX.1/XPG5 times(2) interface, which fills in a + `struct tms' with values of type clock_t. */ + struct tms t; + + USE_VAR(list); + + if (no_options (list)) + return (EX_USAGE); + + times (&t); + + print_clock_t (stdout, t.tms_utime); + putchar (' '); + print_clock_t (stdout, t.tms_stime); + putchar ('\n'); + print_clock_t (stdout, t.tms_cutime); + putchar (' '); + print_clock_t (stdout, t.tms_cstime); + putchar ('\n'); + +# else /* !HAVE_TIMES */ + + USE_VAR(list); + + if (no_options (list)) + return (EX_USAGE); + printf ("0.00 0.00\n0.00 0.00\n"); + +# endif /* HAVE_TIMES */ +#endif /* !HAVE_TIMES */ + + return (sh_chkwrite (EXECUTION_SUCCESS)); +} diff --git a/builtins/trap.def b/builtins/trap.def new file mode 100644 index 0000000..a8da71d --- /dev/null +++ b/builtins/trap.def @@ -0,0 +1,280 @@ +This file is trap.def, from which is created trap.c. +It implements the builtin "trap" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES trap.c + +$BUILTIN trap +$FUNCTION trap_builtin +$SHORT_DOC trap [-lp] [[arg] signal_spec ...] +Trap signals and other events. + +Defines and activates handlers to be run when the shell receives signals +or other conditions. + +ARG is a command to be read and executed when the shell receives the +signal(s) SIGNAL_SPEC. If ARG is absent (and a single SIGNAL_SPEC +is supplied) or `-', each specified signal is reset to its original +value. If ARG is the null string each SIGNAL_SPEC is ignored by the +shell and by the commands it invokes. + +If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell. If +a SIGNAL_SPEC is DEBUG, ARG is executed before every simple command. + +If no arguments are supplied, trap prints the list of commands associated +with each signal. + +Options: + -l print a list of signal names and their corresponding numbers + -p display the trap commands associated with each SIGNAL_SPEC + +Each SIGNAL_SPEC is either a signal name in <signal.h> or a signal number. +Signal names are case insensitive and the SIG prefix is optional. A +signal may be sent to the shell with "kill -signal $$". + +Exit Status: +Returns success unless a SIGSPEC is invalid or an invalid option is given. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "../bashtypes.h" +#include <signal.h> +#include <stdio.h> +#include "../bashansi.h" + +#include "../shell.h" +#include "../trap.h" +#include "common.h" +#include "bashgetopt.h" + +static void showtrap __P((int)); +static int display_traps __P((WORD_LIST *)); + +/* The trap command: + + trap <arg> <signal ...> + trap <signal ...> + trap -l + trap -p [sigspec ...] + trap [--] + + Set things up so that ARG is executed when SIGNAL(s) N is recieved. + If ARG is the empty string, then ignore the SIGNAL(s). If there is + no ARG, then set the trap for SIGNAL(s) to its original value. Just + plain "trap" means to print out the list of commands associated with + each signal number. Single arg of "-l" means list the signal names. */ + +/* Possible operations to perform on the list of signals.*/ +#define SET 0 /* Set this signal to first_arg. */ +#define REVERT 1 /* Revert to this signals original value. */ +#define IGNORE 2 /* Ignore this signal. */ + +extern int posixly_correct; + +int +trap_builtin (list) + WORD_LIST *list; +{ + int list_signal_names, display, result, opt; + + list_signal_names = display = 0; + result = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lp")) != -1) + { + switch (opt) + { + case 'l': + list_signal_names++; + break; + case 'p': + display++; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + opt = DSIG_NOCASE|DSIG_SIGPREFIX; /* flags for decode_signal */ + + if (list_signal_names) + return (sh_chkwrite (display_signal_list ((WORD_LIST *)NULL, 1))); + else if (display || list == 0) + return (sh_chkwrite (display_traps (list))); + else + { + char *first_arg; + int operation, sig, first_signal; + + operation = SET; + first_arg = list->word->word; + first_signal = first_arg && *first_arg && all_digits (first_arg) && signal_object_p (first_arg, opt); + + /* Backwards compatibility. XXX - question about whether or not we + should throw an error if an all-digit argument doesn't correspond + to a valid signal number (e.g., if it's `50' on a system with only + 32 signals). */ + if (first_signal) + operation = REVERT; + /* When in posix mode, the historical behavior of looking for a + missing first argument is disabled. To revert to the original + signal handling disposition, use `-' as the first argument. */ + else if (posixly_correct == 0 && first_arg && *first_arg && + (*first_arg != '-' || first_arg[1]) && + signal_object_p (first_arg, opt) && list->next == 0) + operation = REVERT; + else + { + list = list->next; + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + else if (*first_arg == '\0') + operation = IGNORE; + else if (first_arg[0] == '-' && !first_arg[1]) + operation = REVERT; + } + + while (list) + { + sig = decode_signal (list->word->word, opt); + + if (sig == NO_SIG) + { + sh_invalidsig (list->word->word); + result = EXECUTION_FAILURE; + } + else + { + switch (operation) + { + case SET: + set_signal (sig, first_arg); + break; + + case REVERT: + restore_default_signal (sig); + + /* Signals that the shell treats specially need special + handling. */ + switch (sig) + { + case SIGINT: + if (interactive) + set_signal_handler (SIGINT, sigint_sighandler); + else + set_signal_handler (SIGINT, termsig_sighandler); + break; + + case SIGQUIT: + /* Always ignore SIGQUIT. */ + set_signal_handler (SIGQUIT, SIG_IGN); + break; + case SIGTERM: +#if defined (JOB_CONTROL) + case SIGTTIN: + case SIGTTOU: + case SIGTSTP: +#endif /* JOB_CONTROL */ + if (interactive) + set_signal_handler (sig, SIG_IGN); + break; + } + break; + + case IGNORE: + ignore_signal (sig); + break; + } + } + list = list->next; + } + } + + return (result); +} + +static void +showtrap (i) + int i; +{ + char *t, *p, *sn; + + p = trap_list[i]; + if (p == (char *)DEFAULT_SIG) + return; + + t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p); + sn = signal_name (i); + /* Make sure that signals whose names are unknown (for whatever reason) + are printed as signal numbers. */ + if (STREQN (sn, "SIGJUNK", 7) || STREQN (sn, "unknown", 7)) + printf ("trap -- %s %d\n", t ? t : "''", i); + else if (posixly_correct) + { + if (STREQN (sn, "SIG", 3)) + printf ("trap -- %s %s\n", t ? t : "''", sn+3); + else + printf ("trap -- %s %s\n", t ? t : "''", sn); + } + else + printf ("trap -- %s %s\n", t ? t : "''", sn); + + FREE (t); +} + +static int +display_traps (list) + WORD_LIST *list; +{ + int result, i; + + if (list == 0) + { + for (i = 0; i < BASH_NSIG; i++) + showtrap (i); + return (EXECUTION_SUCCESS); + } + + for (result = EXECUTION_SUCCESS; list; list = list->next) + { + i = decode_signal (list->word->word, DSIG_NOCASE|DSIG_SIGPREFIX); + if (i == NO_SIG) + { + sh_invalidsig (list->word->word); + result = EXECUTION_FAILURE; + } + else + showtrap (i); + } + + return (result); +} diff --git a/builtins/type.def b/builtins/type.def new file mode 100644 index 0000000..ee341bb --- /dev/null +++ b/builtins/type.def @@ -0,0 +1,403 @@ +This file is type.def, from which is created type.c. +It implements the builtin "type" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES type.c + +$BUILTIN type +$FUNCTION type_builtin +$SHORT_DOC type [-afptP] name [name ...] +Display information about command type. + +For each NAME, indicate how it would be interpreted if used as a +command name. + +Options: + -a display all locations containing an executable named NAME; + includes aliases, builtins, and functions, if and only if + the `-p' option is not also used + -f suppress shell function lookup + -P force a PATH search for each NAME, even if it is an alias, + builtin, or function, and returns the name of the disk file + that would be executed + -p returns either the name of the disk file that would be executed, + or nothing if `type -t NAME' would not return `file'. + -t output a single word which is one of `alias', `keyword', + `function', `builtin', `file' or `', if NAME is an alias, shell + reserved word, shell function, shell builtin, disk file, or not + found, respectively + +Arguments: + NAME Command name to be interpreted. + +Exit Status: +Returns success if all of the NAMEs are found; fails if any are not found. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include "posixstat.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../findcmd.h" +#include "../hashcmd.h" + +#if defined (ALIAS) +#include "../alias.h" +#endif /* ALIAS */ + +#include "common.h" +#include "bashgetopt.h" + +extern int find_reserved_word __P((char *)); + +extern char *this_command_name; +extern int expand_aliases, posixly_correct; + +/* For each word in LIST, find out what the shell is going to do with + it as a simple command. i.e., which file would this shell use to + execve, or if it is a builtin command, or an alias. Possible flag + arguments: + -t Returns the "type" of the object, one of + `alias', `keyword', `function', `builtin', + or `file'. + + -p Returns the pathname of the file if -type is + a file. + + -a Returns all occurrences of words, whether they + be a filename in the path, alias, function, + or builtin. + + -f Suppress shell function lookup, like `command'. + + -P Force a path search even in the presence of other + definitions. + + Order of evaluation: + alias + keyword + function + builtin + file + */ + +int +type_builtin (list) + WORD_LIST *list; +{ + int dflags, any_failed, opt; + WORD_LIST *this; + + if (list == 0) + return (EXECUTION_SUCCESS); + + dflags = CDESC_SHORTDESC; /* default */ + any_failed = 0; + + /* Handle the obsolescent `-type', `-path', and `-all' by prescanning + the arguments and converting those options to the form that + internal_getopt recognizes. Converts `--type', `--path', and `--all' + also. THIS SHOULD REALLY GO AWAY. */ + for (this = list; this && this->word->word[0] == '-'; this = this->next) + { + char *flag = &(this->word->word[1]); + + if (STREQ (flag, "type") || STREQ (flag, "-type")) + { + this->word->word[1] = 't'; + this->word->word[2] = '\0'; + } + else if (STREQ (flag, "path") || STREQ (flag, "-path")) + { + this->word->word[1] = 'p'; + this->word->word[2] = '\0'; + } + else if (STREQ (flag, "all") || STREQ (flag, "-all")) + { + this->word->word[1] = 'a'; + this->word->word[2] = '\0'; + } + } + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "afptP")) != -1) + { + switch (opt) + { + case 'a': + dflags |= CDESC_ALL; + break; + case 'f': + dflags |= CDESC_NOFUNCS; + break; + case 'p': + dflags |= CDESC_PATH_ONLY; + dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC); + break; + case 't': + dflags |= CDESC_TYPE; + dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC); + break; + case 'P': /* shorthand for type -ap */ + dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH); + dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC); + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + while (list) + { + int found; + + found = describe_command (list->word->word, dflags); + + if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0) + sh_notfound (list->word->word); + + any_failed += found == 0; + list = list->next; + } + + opt = (any_failed == 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; + return (sh_chkwrite (opt)); +} + +/* + * Describe COMMAND as required by the type and command builtins. + * + * Behavior is controlled by DFLAGS. Flag values are + * CDESC_ALL print all descriptions of a command + * CDESC_SHORTDESC print the description for type and command -V + * CDESC_REUSABLE print in a format that may be reused as input + * CDESC_TYPE print the type for type -t + * CDESC_PATH_ONLY print the path for type -p + * CDESC_FORCE_PATH force a path search for type -P + * CDESC_NOFUNCS skip function lookup for type -f + * CDESC_ABSPATH convert to absolute path, no ./ prefix + * + * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or + * return after finding it once. + */ +int +describe_command (command, dflags) + char *command; + int dflags; +{ + int found, i, found_file, f, all; + char *full_path, *x; + SHELL_VAR *func; +#if defined (ALIAS) + alias_t *alias; +#endif + + all = (dflags & CDESC_ALL) != 0; + found = found_file = 0; + full_path = (char *)NULL; + +#if defined (ALIAS) + /* Command is an alias? */ + if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command))) + { + if (dflags & CDESC_TYPE) + puts ("alias"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is aliased to `%s'\n"), command, alias->value); + else if (dflags & CDESC_REUSABLE) + { + x = sh_single_quote (alias->value); + printf ("alias %s=%s\n", command, x); + free (x); + } + + found = 1; + + if (all == 0) + return (1); + } +#endif /* ALIAS */ + + /* Command is a shell reserved word? */ + if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0) + { + if (dflags & CDESC_TYPE) + puts ("keyword"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is a shell keyword\n"), command); + else if (dflags & CDESC_REUSABLE) + printf ("%s\n", command); + + found = 1; + + if (all == 0) + return (1); + } + + /* Command is a function? */ + if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command))) + { + if (dflags & CDESC_TYPE) + puts ("function"); + else if (dflags & CDESC_SHORTDESC) + { + char *result; + + printf (_("%s is a function\n"), command); + + /* We're blowing away THE_PRINTED_COMMAND here... */ + + result = named_function_string (command, function_cell (func), FUNC_MULTILINE|FUNC_EXTERNAL); + printf ("%s\n", result); + } + else if (dflags & CDESC_REUSABLE) + printf ("%s\n", command); + + found = 1; + + if (all == 0) + return (1); + } + + /* Command is a builtin? */ + if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command)) + { + if (dflags & CDESC_TYPE) + puts ("builtin"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is a shell builtin\n"), command); + else if (dflags & CDESC_REUSABLE) + printf ("%s\n", command); + + found = 1; + + if (all == 0) + return (1); + } + + /* Command is a disk file? */ + /* If the command name given is already an absolute command, just + check to see if it is executable. */ + if (absolute_program (command)) + { + f = file_status (command); + if (f & FS_EXECABLE) + { + if (dflags & CDESC_TYPE) + puts ("file"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is %s\n"), command, command); + else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) + printf ("%s\n", command); + + /* There's no use looking in the hash table or in $PATH, + because they're not consulted when an absolute program + name is supplied. */ + return (1); + } + } + + /* If the user isn't doing "-a", then we might care about + whether the file is present in our hash table. */ + if (all == 0 || (dflags & CDESC_FORCE_PATH)) + { + if (full_path = phash_search (command)) + { + if (dflags & CDESC_TYPE) + puts ("file"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is hashed (%s)\n"), command, full_path); + else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) + printf ("%s\n", full_path); + + free (full_path); + return (1); + } + } + + /* Now search through $PATH. */ + while (1) + { + if (all == 0) + full_path = find_user_command (command); + else + full_path = + user_command_matches (command, FS_EXEC_ONLY, found_file); + /* XXX - should that be FS_EXEC_PREFERRED? */ + + if (!full_path) + break; + + /* If we found the command as itself by looking through $PATH, it + probably doesn't exist. Check whether or not the command is an + executable file. If it's not, don't report a match. This is + the default posix mode behavior */ + if (STREQ (full_path, command) || posixly_correct) + { + f = file_status (full_path); + if ((f & FS_EXECABLE) == 0) + { + free (full_path); + full_path = (char *)NULL; + if (all == 0) + break; + } + else if (ABSPATH (full_path)) + ; /* placeholder; don't need to do anything yet */ + else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC)) + { + f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0); + full_path = sh_makepath ((char *)NULL, full_path, f); + } + } + /* If we require a full path and don't have one, make one */ + else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0) + full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT); + + found_file++; + found = 1; + + if (dflags & CDESC_TYPE) + puts ("file"); + else if (dflags & CDESC_SHORTDESC) + printf (_("%s is %s\n"), command, full_path); + else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) + printf ("%s\n", full_path); + + free (full_path); + full_path = (char *)NULL; + + if (all == 0) + break; + } + + return (found); +} diff --git a/builtins/ulimit.def b/builtins/ulimit.def new file mode 100644 index 0000000..7c1e256 --- /dev/null +++ b/builtins/ulimit.def @@ -0,0 +1,772 @@ +This file is ulimit.def, from which is created ulimit.c. +It implements the builtin "ulimit" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES ulimit.c + +$BUILTIN ulimit +$FUNCTION ulimit_builtin +$DEPENDS_ON !_MINIX +$SHORT_DOC ulimit [-SHacdefilmnpqrstuvx] [limit] +Modify shell resource limits. + +Provides control over the resources available to the shell and processes +it creates, on systems that allow such control. + +Options: + -S use the `soft' resource limit + -H use the `hard' resource limit + -a all current limits are reported + -b the socket buffer size + -c the maximum size of core files created + -d the maximum size of a process's data segment + -e the maximum scheduling priority (`nice') + -f the maximum size of files written by the shell and its children + -i the maximum number of pending signals + -l the maximum size a process may lock into memory + -m the maximum resident set size + -n the maximum number of open file descriptors + -p the pipe buffer size + -q the maximum number of bytes in POSIX message queues + -r the maximum real-time scheduling priority + -s the maximum stack size + -t the maximum amount of cpu time in seconds + -u the maximum number of user processes + -v the size of virtual memory + -x the maximum number of file locks + +If LIMIT is given, it is the new value of the specified resource; the +special LIMIT values `soft', `hard', and `unlimited' stand for the +current soft limit, the current hard limit, and no limit, respectively. +Otherwise, the current value of the specified resource is printed. If +no option is given, then -f is assumed. + +Values are in 1024-byte increments, except for -t, which is in seconds, +-p, which is in increments of 512 bytes, and -u, which is an unscaled +number of processes. + +Exit Status: +Returns success unless an invalid option is supplied or an error occurs. +$END + +#if !defined (_MINIX) + +#include <config.h> + +#include "../bashtypes.h" +#ifndef _MINIX +# include <sys/param.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "../bashintl.h" + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" +#include "pipesize.h" + +#if !defined (errno) +extern int errno; +#endif + +/* For some reason, HPUX chose to make these definitions visible only if + _KERNEL is defined, so we define _KERNEL before including <sys/resource.h> + and #undef it afterward. */ +#if defined (HAVE_RESOURCE) +# include <sys/time.h> +# if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL) +# define _KERNEL +# endif +# include <sys/resource.h> +# if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL) +# undef _KERNEL +# endif +#elif defined (HAVE_SYS_TIMES_H) +# include <sys/times.h> +#endif + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +/* Check for the most basic symbols. If they aren't present, this + system's <sys/resource.h> isn't very useful to us. */ +#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT) +# undef HAVE_RESOURCE +#endif + +#if !defined (RLIMTYPE) +# define RLIMTYPE long +# define string_to_rlimtype(s) strtol(s, (char **)NULL, 10) +# define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "") +#endif + +/* Some systems use RLIMIT_NOFILE, others use RLIMIT_OFILE */ +#if defined (HAVE_RESOURCE) && defined (RLIMIT_OFILE) && !defined (RLIMIT_NOFILE) +# define RLIMIT_NOFILE RLIMIT_OFILE +#endif /* HAVE_RESOURCE && RLIMIT_OFILE && !RLIMIT_NOFILE */ + +/* Some systems have these, some do not. */ +#ifdef RLIMIT_FSIZE +# define RLIMIT_FILESIZE RLIMIT_FSIZE +#else +# define RLIMIT_FILESIZE 256 +#endif + +#define RLIMIT_PIPESIZE 257 + +#ifdef RLIMIT_NOFILE +# define RLIMIT_OPENFILES RLIMIT_NOFILE +#else +# define RLIMIT_OPENFILES 258 +#endif + +#ifdef RLIMIT_VMEM +# define RLIMIT_VIRTMEM RLIMIT_VMEM +# define RLIMIT_VMBLKSZ 1024 +#else +# ifdef RLIMIT_AS +# define RLIMIT_VIRTMEM RLIMIT_AS +# define RLIMIT_VMBLKSZ 1024 +# else +# define RLIMIT_VIRTMEM 259 +# define RLIMIT_VMBLKSZ 1 +# endif +#endif + +#ifdef RLIMIT_NPROC +# define RLIMIT_MAXUPROC RLIMIT_NPROC +#else +# define RLIMIT_MAXUPROC 260 +#endif + +#if !defined (RLIM_INFINITY) +# define RLIM_INFINITY 0x7fffffff +#endif + +#if !defined (RLIM_SAVED_CUR) +# define RLIM_SAVED_CUR RLIM_INFINITY +#endif + +#if !defined (RLIM_SAVED_MAX) +# define RLIM_SAVED_MAX RLIM_INFINITY +#endif + +#define LIMIT_HARD 0x01 +#define LIMIT_SOFT 0x02 + +/* "Blocks" are defined as 512 bytes when in Posix mode and 1024 bytes + otherwise. */ +#define POSIXBLK -2 + +#define BLOCKSIZE(x) (((x) == POSIXBLK) ? (posixly_correct ? 512 : 1024) : (x)) + +extern int posixly_correct; + +static int _findlim __P((int)); + +static int ulimit_internal __P((int, char *, int, int)); + +static int get_limit __P((int, RLIMTYPE *, RLIMTYPE *)); +static int set_limit __P((int, RLIMTYPE, int)); + +static void printone __P((int, RLIMTYPE, int)); +static void print_all_limits __P((int)); + +static int set_all_limits __P((int, RLIMTYPE)); + +static int filesize __P((RLIMTYPE *)); +static int pipesize __P((RLIMTYPE *)); +static int getmaxuprc __P((RLIMTYPE *)); +static int getmaxvm __P((RLIMTYPE *, RLIMTYPE *)); + +typedef struct { + int option; /* The ulimit option for this limit. */ + int parameter; /* Parameter to pass to get_limit (). */ + int block_factor; /* Blocking factor for specific limit. */ + const char * const description; /* Descriptive string to output. */ + const char * const units; /* scale */ +} RESOURCE_LIMITS; + +static RESOURCE_LIMITS limits[] = { +#ifdef RLIMIT_PTHREAD + { 'T', RLIMIT_PTHREAD, 1, "number of threads", (char *)NULL }, +#endif +#ifdef RLIMIT_SBSIZE + { 'b', RLIMIT_SBSIZE, 1, "socket buffer size", "bytes" }, +#endif +#ifdef RLIMIT_CORE + { 'c', RLIMIT_CORE, POSIXBLK, "core file size", "blocks" }, +#endif +#ifdef RLIMIT_DATA + { 'd', RLIMIT_DATA, 1024, "data seg size", "kbytes" }, +#endif +#ifdef RLIMIT_NICE + { 'e', RLIMIT_NICE, 1, "scheduling priority", (char *)NULL }, +#endif + { 'f', RLIMIT_FILESIZE, POSIXBLK, "file size", "blocks" }, +#ifdef RLIMIT_SIGPENDING + { 'i', RLIMIT_SIGPENDING, 1, "pending signals", (char *)NULL }, +#endif +#ifdef RLIMIT_MEMLOCK + { 'l', RLIMIT_MEMLOCK, 1024, "max locked memory", "kbytes" }, +#endif +#ifdef RLIMIT_RSS + { 'm', RLIMIT_RSS, 1024, "max memory size", "kbytes" }, +#endif /* RLIMIT_RSS */ + { 'n', RLIMIT_OPENFILES, 1, "open files", (char *)NULL}, + { 'p', RLIMIT_PIPESIZE, 512, "pipe size", "512 bytes" }, +#ifdef RLIMIT_MSGQUEUE + { 'q', RLIMIT_MSGQUEUE, 1, "POSIX message queues", "bytes" }, +#endif +#ifdef RLIMIT_RTPRIO + { 'r', RLIMIT_RTPRIO, 1, "real-time priority", (char *)NULL }, +#endif +#ifdef RLIMIT_STACK + { 's', RLIMIT_STACK, 1024, "stack size", "kbytes" }, +#endif +#ifdef RLIMIT_CPU + { 't', RLIMIT_CPU, 1, "cpu time", "seconds" }, +#endif /* RLIMIT_CPU */ + { 'u', RLIMIT_MAXUPROC, 1, "max user processes", (char *)NULL }, +#if defined (HAVE_RESOURCE) + { 'v', RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory", "kbytes" }, +#endif +#ifdef RLIMIT_SWAP + { 'w', RLIMIT_SWAP, 1024, "swap size", "kbytes" }, +#endif +#ifdef RLIMIT_LOCKS + { 'x', RLIMIT_LOCKS, 1, "file locks", (char *)NULL }, +#endif + { -1, -1, -1, (char *)NULL, (char *)NULL } +}; +#define NCMDS (sizeof(limits) / sizeof(limits[0])) + +typedef struct _cmd { + int cmd; + char *arg; +} ULCMD; + +static ULCMD *cmdlist; +static int ncmd; +static int cmdlistsz; + +#if !defined (HAVE_RESOURCE) && !defined (HAVE_ULIMIT) +long +ulimit (cmd, newlim) + int cmd; + long newlim; +{ + errno = EINVAL; + return -1; +} +#endif /* !HAVE_RESOURCE && !HAVE_ULIMIT */ + +static int +_findlim (opt) + int opt; +{ + register int i; + + for (i = 0; limits[i].option > 0; i++) + if (limits[i].option == opt) + return i; + return -1; +} + +static char optstring[4 + 2 * NCMDS]; + +/* Report or set limits associated with certain per-process resources. + See the help documentation in builtins.c for a full description. */ +int +ulimit_builtin (list) + register WORD_LIST *list; +{ + register char *s; + int c, limind, mode, opt, all_limits; + + mode = 0; + + all_limits = 0; + + /* Idea stolen from pdksh -- build option string the first time called. */ + if (optstring[0] == 0) + { + s = optstring; + *s++ = 'a'; *s++ = 'S'; *s++ = 'H'; + for (c = 0; limits[c].option > 0; c++) + { + *s++ = limits[c].option; + *s++ = ';'; + } + *s = '\0'; + } + + /* Initialize the command list. */ + if (cmdlistsz == 0) + cmdlist = (ULCMD *)xmalloc ((cmdlistsz = 16) * sizeof (ULCMD)); + ncmd = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, optstring)) != -1) + { + switch (opt) + { + case 'a': + all_limits++; + break; + + /* -S and -H are modifiers, not real options. */ + case 'S': + mode |= LIMIT_SOFT; + break; + + case 'H': + mode |= LIMIT_HARD; + break; + + case '?': + builtin_usage (); + return (EX_USAGE); + + default: + if (ncmd >= cmdlistsz) + cmdlist = (ULCMD *)xrealloc (cmdlist, (cmdlistsz *= 2) * sizeof (ULCMD)); + cmdlist[ncmd].cmd = opt; + cmdlist[ncmd++].arg = list_optarg; + break; + } + } + list = loptend; + + if (all_limits) + { +#ifdef NOTYET + if (list) /* setting */ + { + if (STREQ (list->word->word, "unlimited") == 0) + { + builtin_error (_("%s: invalid limit argument"), list->word->word); + return (EXECUTION_FAILURE); + } + return (set_all_limits (mode == 0 ? LIMIT_SOFT|LIMIT_HARD : mode, RLIM_INFINITY)); + } +#endif + print_all_limits (mode == 0 ? LIMIT_SOFT : mode); + return (sh_chkwrite (EXECUTION_SUCCESS)); + } + + /* default is `ulimit -f' */ + if (ncmd == 0) + { + cmdlist[ncmd].cmd = 'f'; + /* `ulimit something' is same as `ulimit -f something' */ + cmdlist[ncmd++].arg = list ? list->word->word : (char *)NULL; + if (list) + list = list->next; + } + + /* verify each command in the list. */ + for (c = 0; c < ncmd; c++) + { + limind = _findlim (cmdlist[c].cmd); + if (limind == -1) + { + builtin_error (_("`%c': bad command"), cmdlist[c].cmd); + return (EX_USAGE); + } + } + + for (c = 0; c < ncmd; c++) + if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE) + return (EXECUTION_FAILURE); + + return (EXECUTION_SUCCESS); +} + +static int +ulimit_internal (cmd, cmdarg, mode, multiple) + int cmd; + char *cmdarg; + int mode, multiple; +{ + int opt, limind, setting; + int block_factor; + RLIMTYPE soft_limit, hard_limit, real_limit, limit; + + setting = cmdarg != 0; + limind = _findlim (cmd); + if (mode == 0) + mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT; + opt = get_limit (limind, &soft_limit, &hard_limit); + if (opt < 0) + { + builtin_error (_("%s: cannot get limit: %s"), limits[limind].description, + strerror (errno)); + return (EXECUTION_FAILURE); + } + + if (setting == 0) /* print the value of the specified limit */ + { + printone (limind, (mode & LIMIT_SOFT) ? soft_limit : hard_limit, multiple); + return (EXECUTION_SUCCESS); + } + + /* Setting the limit. */ + if (STREQ (cmdarg, "hard")) + real_limit = hard_limit; + else if (STREQ (cmdarg, "soft")) + real_limit = soft_limit; + else if (STREQ (cmdarg, "unlimited")) + real_limit = RLIM_INFINITY; + else if (all_digits (cmdarg)) + { + limit = string_to_rlimtype (cmdarg); + block_factor = BLOCKSIZE(limits[limind].block_factor); + real_limit = limit * block_factor; + + if ((real_limit / block_factor) != limit) + { + sh_erange (cmdarg, _("limit")); + return (EXECUTION_FAILURE); + } + } + else + { + sh_invalidnum (cmdarg); + return (EXECUTION_FAILURE); + } + + if (set_limit (limind, real_limit, mode) < 0) + { + builtin_error (_("%s: cannot modify limit: %s"), limits[limind].description, + strerror (errno)); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +static int +get_limit (ind, softlim, hardlim) + int ind; + RLIMTYPE *softlim, *hardlim; +{ + RLIMTYPE value; +#if defined (HAVE_RESOURCE) + struct rlimit limit; +#endif + + if (limits[ind].parameter >= 256) + { + switch (limits[ind].parameter) + { + case RLIMIT_FILESIZE: + if (filesize (&value) < 0) + return -1; + break; + case RLIMIT_PIPESIZE: + if (pipesize (&value) < 0) + return -1; + break; + case RLIMIT_OPENFILES: + value = (RLIMTYPE)getdtablesize (); + break; + case RLIMIT_VIRTMEM: + return (getmaxvm (softlim, hardlim)); + case RLIMIT_MAXUPROC: + if (getmaxuprc (&value) < 0) + return -1; + break; + default: + errno = EINVAL; + return -1; + } + *softlim = *hardlim = value; + return (0); + } + else + { +#if defined (HAVE_RESOURCE) + if (getrlimit (limits[ind].parameter, &limit) < 0) + return -1; + *softlim = limit.rlim_cur; + *hardlim = limit.rlim_max; +# if defined (HPUX9) + if (limits[ind].parameter == RLIMIT_FILESIZE) + { + *softlim *= 512; + *hardlim *= 512; /* Ugh. */ + } + else +# endif /* HPUX9 */ + return 0; +#else + errno = EINVAL; + return -1; +#endif + } +} + +static int +set_limit (ind, newlim, mode) + int ind; + RLIMTYPE newlim; + int mode; +{ +#if defined (HAVE_RESOURCE) + struct rlimit limit; + RLIMTYPE val; +#endif + + if (limits[ind].parameter >= 256) + switch (limits[ind].parameter) + { + case RLIMIT_FILESIZE: +#if !defined (HAVE_RESOURCE) + return (ulimit (2, newlim / 512L)); +#else + errno = EINVAL; + return -1; +#endif + + case RLIMIT_OPENFILES: +#if defined (HAVE_SETDTABLESIZE) +# if defined (__CYGWIN__) + /* Grrr... Cygwin declares setdtablesize as void. */ + setdtablesize (newlim); + return 0; +# else + return (setdtablesize (newlim)); +# endif +#endif + case RLIMIT_PIPESIZE: + case RLIMIT_VIRTMEM: + case RLIMIT_MAXUPROC: + default: + errno = EINVAL; + return -1; + } + else + { +#if defined (HAVE_RESOURCE) + if (getrlimit (limits[ind].parameter, &limit) < 0) + return -1; +# if defined (HPUX9) + if (limits[ind].parameter == RLIMIT_FILESIZE) + newlim /= 512; /* Ugh. */ +# endif /* HPUX9 */ + val = (current_user.euid != 0 && newlim == RLIM_INFINITY && + (mode & LIMIT_HARD) == 0 && /* XXX -- test */ + (limit.rlim_cur <= limit.rlim_max)) + ? limit.rlim_max : newlim; + if (mode & LIMIT_SOFT) + limit.rlim_cur = val; + if (mode & LIMIT_HARD) + limit.rlim_max = val; + + return (setrlimit (limits[ind].parameter, &limit)); +#else + errno = EINVAL; + return -1; +#endif + } +} + +static int +getmaxvm (softlim, hardlim) + RLIMTYPE *softlim, *hardlim; +{ +#if defined (HAVE_RESOURCE) + struct rlimit datalim, stacklim; + + if (getrlimit (RLIMIT_DATA, &datalim) < 0) + return -1; + + if (getrlimit (RLIMIT_STACK, &stacklim) < 0) + return -1; + + /* Protect against overflow. */ + *softlim = (datalim.rlim_cur / 1024L) + (stacklim.rlim_cur / 1024L); + *hardlim = (datalim.rlim_max / 1024L) + (stacklim.rlim_max / 1024L); + return 0; +#else + errno = EINVAL; + return -1; +#endif /* HAVE_RESOURCE */ +} + +static int +filesize(valuep) + RLIMTYPE *valuep; +{ +#if !defined (HAVE_RESOURCE) + long result; + if ((result = ulimit (1, 0L)) < 0) + return -1; + else + *valuep = (RLIMTYPE) result * 512; + return 0; +#else + errno = EINVAL; + return -1; +#endif +} + +static int +pipesize (valuep) + RLIMTYPE *valuep; +{ +#if defined (PIPE_BUF) + /* This is defined on Posix systems. */ + *valuep = (RLIMTYPE) PIPE_BUF; + return 0; +#else +# if defined (_POSIX_PIPE_BUF) + *valuep = (RLIMTYPE) _POSIX_PIPE_BUF; + return 0; +# else +# if defined (PIPESIZE) + /* This is defined by running a program from the Makefile. */ + *valuep = (RLIMTYPE) PIPESIZE; + return 0; +# else + errno = EINVAL; + return -1; +# endif /* PIPESIZE */ +# endif /* _POSIX_PIPE_BUF */ +#endif /* PIPE_BUF */ +} + +static int +getmaxuprc (valuep) + RLIMTYPE *valuep; +{ + long maxchild; + + maxchild = getmaxchild (); + if (maxchild < 0) + { + errno = EINVAL; + return -1; + } + else + { + *valuep = (RLIMTYPE) maxchild; + return 0; + } +} + +static void +print_all_limits (mode) + int mode; +{ + register int i; + RLIMTYPE softlim, hardlim; + + if (mode == 0) + mode |= LIMIT_SOFT; + + for (i = 0; limits[i].option > 0; i++) + { + if (get_limit (i, &softlim, &hardlim) == 0) + printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1); + else if (errno != EINVAL) + builtin_error ("%s: cannot get limit: %s", limits[i].description, + strerror (errno)); + } +} + +static void +printone (limind, curlim, pdesc) + int limind; + RLIMTYPE curlim; + int pdesc; +{ + char unitstr[64]; + int factor; + + factor = BLOCKSIZE(limits[limind].block_factor); + if (pdesc) + { + if (limits[limind].units) + sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option); + else + sprintf (unitstr, "(-%c) ", limits[limind].option); + + printf ("%-20s %16s", limits[limind].description, unitstr); + } + if (curlim == RLIM_INFINITY) + puts ("unlimited"); + else if (curlim == RLIM_SAVED_MAX) + puts ("hard"); + else if (curlim == RLIM_SAVED_CUR) + puts ("soft"); + else + print_rlimtype ((curlim / factor), 1); +} + +/* Set all limits to NEWLIM. NEWLIM currently must be RLIM_INFINITY, which + causes all limits to be set as high as possible depending on mode (like + csh `unlimit'). Returns -1 if NEWLIM is invalid, 0 if all limits + were set successfully, and 1 if at least one limit could not be set. + + To raise all soft limits to their corresponding hard limits, use + ulimit -S -a unlimited + To attempt to raise all hard limits to infinity (superuser-only), use + ulimit -H -a unlimited + To attempt to raise all soft and hard limits to infinity, use + ulimit -a unlimited +*/ + +static int +set_all_limits (mode, newlim) + int mode; + RLIMTYPE newlim; +{ + register int i; + int retval = 0; + + if (newlim != RLIM_INFINITY) + { + errno = EINVAL; + return -1; + } + + if (mode == 0) + mode = LIMIT_SOFT|LIMIT_HARD; + + for (retval = i = 0; limits[i].option > 0; i++) + if (set_limit (i, newlim, mode) < 0) + { + builtin_error (_("%s: cannot modify limit: %s"), limits[i].description, + strerror (errno)); + retval = 1; + } + return retval; +} + +#endif /* !_MINIX */ diff --git a/builtins/umask.def b/builtins/umask.def new file mode 100644 index 0000000..289a0f5 --- /dev/null +++ b/builtins/umask.def @@ -0,0 +1,316 @@ +This file is umask.def, from which is created umask.c. +It implements the builtin "umask" in Bash. + +Copyright (C) 1987-2009 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/>. + +$PRODUCES umask.c + +$BUILTIN umask +$FUNCTION umask_builtin +$SHORT_DOC umask [-p] [-S] [mode] +Display or set file mode mask. + +Sets the user file-creation mask to MODE. If MODE is omitted, prints +the current value of the mask. + +If MODE begins with a digit, it is interpreted as an octal number; +otherwise it is a symbolic mode string like that accepted by chmod(1). + +Options: + -p if MODE is omitted, output in a form that may be reused as input + -S makes the output symbolic; otherwise an octal number is output + +Exit Status: +Returns success unless MODE is invalid or an invalid option is given. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include "filecntl.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <stdio.h> +#include <chartypes.h> + +#include "../bashintl.h" + +#include "../shell.h" +#include "posixstat.h" +#include "common.h" +#include "bashgetopt.h" + +#ifdef __LCC__ +#define mode_t int +#endif + +/* **************************************************************** */ +/* */ +/* UMASK Builtin and Helpers */ +/* */ +/* **************************************************************** */ + +static void print_symbolic_umask __P((mode_t)); +static int symbolic_umask __P((WORD_LIST *)); + +/* Set or display the mask used by the system when creating files. Flag + of -S means display the umask in a symbolic mode. */ +int +umask_builtin (list) + WORD_LIST *list; +{ + int print_symbolically, opt, umask_value, pflag; + mode_t umask_arg; + + print_symbolically = pflag = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Sp")) != -1) + { + switch (opt) + { + case 'S': + print_symbolically++; + break; + case 'p': + pflag++; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (list) + { + if (DIGIT (*list->word->word)) + { + umask_value = read_octal (list->word->word); + + /* Note that other shells just let you set the umask to zero + by specifying a number out of range. This is a problem + with those shells. We don't change the umask if the input + is lousy. */ + if (umask_value == -1) + { + sh_erange (list->word->word, _("octal number")); + return (EXECUTION_FAILURE); + } + } + else + { + umask_value = symbolic_umask (list); + if (umask_value == -1) + return (EXECUTION_FAILURE); + } + umask_arg = (mode_t)umask_value; + umask (umask_arg); + if (print_symbolically) + print_symbolic_umask (umask_arg); + } + else /* Display the UMASK for this user. */ + { + umask_arg = umask (022); + umask (umask_arg); + + if (pflag) + printf ("umask%s ", (print_symbolically ? " -S" : "")); + if (print_symbolically) + print_symbolic_umask (umask_arg); + else + printf ("%04lo\n", (unsigned long)umask_arg); + } + + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +/* Print the umask in a symbolic form. In the output, a letter is + printed if the corresponding bit is clear in the umask. */ +static void +print_symbolic_umask (um) + mode_t um; +{ + char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ + int i; + + i = 0; + if ((um & S_IRUSR) == 0) + ubits[i++] = 'r'; + if ((um & S_IWUSR) == 0) + ubits[i++] = 'w'; + if ((um & S_IXUSR) == 0) + ubits[i++] = 'x'; + ubits[i] = '\0'; + + i = 0; + if ((um & S_IRGRP) == 0) + gbits[i++] = 'r'; + if ((um & S_IWGRP) == 0) + gbits[i++] = 'w'; + if ((um & S_IXGRP) == 0) + gbits[i++] = 'x'; + gbits[i] = '\0'; + + i = 0; + if ((um & S_IROTH) == 0) + obits[i++] = 'r'; + if ((um & S_IWOTH) == 0) + obits[i++] = 'w'; + if ((um & S_IXOTH) == 0) + obits[i++] = 'x'; + obits[i] = '\0'; + + printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits); +} + +int +parse_symbolic_mode (mode, initial_bits) + char *mode; + int initial_bits; +{ + int who, op, perm, bits, c; + char *s; + + for (s = mode, bits = initial_bits;;) + { + who = op = perm = 0; + + /* Parse the `who' portion of the symbolic mode clause. */ + while (member (*s, "agou")) + { + switch (c = *s++) + { + case 'u': + who |= S_IRWXU; + continue; + case 'g': + who |= S_IRWXG; + continue; + case 'o': + who |= S_IRWXO; + continue; + case 'a': + who |= S_IRWXU | S_IRWXG | S_IRWXO; + continue; + default: + break; + } + } + + /* The operation is now sitting in *s. */ + op = *s++; + switch (op) + { + case '+': + case '-': + case '=': + break; + default: + builtin_error (_("`%c': invalid symbolic mode operator"), op); + return (-1); + } + + /* Parse out the `perm' section of the symbolic mode clause. */ + while (member (*s, "rwx")) + { + c = *s++; + + switch (c) + { + case 'r': + perm |= S_IRUGO; + break; + case 'w': + perm |= S_IWUGO; + break; + case 'x': + perm |= S_IXUGO; + break; + } + } + + /* Now perform the operation or return an error for a + bad permission string. */ + if (!*s || *s == ',') + { + if (who) + perm &= who; + + switch (op) + { + case '+': + bits |= perm; + break; + case '-': + bits &= ~perm; + break; + case '=': + if (who == 0) + who = S_IRWXU | S_IRWXG | S_IRWXO; + bits &= ~who; + bits |= perm; + break; + + /* No other values are possible. */ + } + + if (*s == '\0') + break; + else + s++; /* skip past ',' */ + } + else + { + builtin_error (_("`%c': invalid symbolic mode character"), *s); + return (-1); + } + } + + return (bits); +} + +/* Set the umask from a symbolic mode string similar to that accepted + by chmod. If the -S argument is given, then print the umask in a + symbolic form. */ +static int +symbolic_umask (list) + WORD_LIST *list; +{ + int um, bits; + + /* Get the initial umask. Don't change it yet. */ + um = umask (022); + umask (um); + + /* All work is done with the complement of the umask -- it's + more intuitive and easier to deal with. It is complemented + again before being returned. */ + bits = parse_symbolic_mode (list->word->word, ~um & 0777); + if (bits == -1) + return (-1); + + um = ~bits & 0777; + return (um); +} diff --git a/builtins/wait.def b/builtins/wait.def new file mode 100644 index 0000000..0206926 --- /dev/null +++ b/builtins/wait.def @@ -0,0 +1,186 @@ +This file is wait.def, from which is created wait.c. +It implements the builtin "wait" in Bash. + +Copyright (C) 1987-2009 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/>. + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON JOB_CONTROL +$PRODUCES wait.c +$SHORT_DOC wait [id] +Wait for job completion and return exit status. + +Waits for the process identified by ID, which may be a process ID or a +job specification, and reports its termination status. If ID is not +given, waits for all currently active child processes, and the return +status is zero. If ID is a a job specification, waits for all processes +in the job's pipeline. + +Exit Status: +Returns the status of ID; fails if ID is invalid or an invalid option is +given. +$END + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON !JOB_CONTROL +$SHORT_DOC wait [pid] +Wait for process completion and return exit status. + +Waits for the specified process and reports its termination status. If +PID is not given, all currently active child processes are waited for, +and the return code is zero. PID must be a process ID. + +Exit Status: +Returns the status of ID; fails if ID is invalid or an invalid option is +given. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <chartypes.h> + +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" + +extern int wait_signal_received; + +procenv_t wait_intr_buf; + +/* Wait for the pid in LIST to stop or die. If no arguments are given, then + wait for all of the active background processes of the shell and return + 0. If a list of pids or job specs are given, return the exit status of + the last one waited for. */ + +#define WAIT_RETURN(s) \ + do \ + { \ + interrupt_immediately = old_interrupt_immediately;\ + return (s);\ + } \ + while (0) + +int +wait_builtin (list) + WORD_LIST *list; +{ + int status, code; + volatile int old_interrupt_immediately; + + USE_VAR(list); + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + old_interrupt_immediately = interrupt_immediately; + interrupt_immediately++; + + /* POSIX.2 says: When the shell is waiting (by means of the wait utility) + for asynchronous commands to complete, the reception of a signal for + which a trap has been set shall cause the wait utility to return + immediately with an exit status greater than 128, after which the trap + associated with the signal shall be taken. + + We handle SIGINT here; it's the only one that needs to be treated + specially (I think), since it's handled specially in {no,}jobs.c. */ + code = setjmp (wait_intr_buf); + if (code) + { + status = 128 + wait_signal_received; + WAIT_RETURN (status); + } + + /* We support jobs or pids. + wait <pid-or-job> [pid-or-job ...] */ + + /* But wait without any arguments means to wait for all of the shell's + currently active background processes. */ + if (list == 0) + { + wait_for_background_pids (); + WAIT_RETURN (EXECUTION_SUCCESS); + } + + status = EXECUTION_SUCCESS; + while (list) + { + pid_t pid; + char *w; + intmax_t pid_value; + + w = list->word->word; + if (DIGIT (*w)) + { + if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value) + { + pid = (pid_t)pid_value; + status = wait_for_single_pid (pid); + } + else + { + sh_badpid (w); + WAIT_RETURN (EXECUTION_FAILURE); + } + } +#if defined (JOB_CONTROL) + else if (*w && *w == '%') + /* Must be a job spec. Check it out. */ + { + int job; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (INVALID_JOB (job)) + { + if (job != DUP_JOB) + sh_badjob (list->word->word); + UNBLOCK_CHILD (oset); + status = 127; /* As per Posix.2, section 4.70.2 */ + list = list->next; + continue; + } + + /* Job spec used. Wait for the last pid in the pipeline. */ + UNBLOCK_CHILD (oset); + status = wait_for_job (job); + } +#endif /* JOB_CONTROL */ + else + { + sh_badpid (w); + status = EXECUTION_FAILURE; + } + list = list->next; + } + + WAIT_RETURN (status); +} |