diff options
| author | Jari Aalto <jari.aalto@cante.net> | 1996-12-23 17:02:34 +0000 |
|---|---|---|
| committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:46:49 +0000 |
| commit | ccc6cda312fea9f0468ee65b8f368e9653e1380b (patch) | |
| tree | b059878adcfd876c4acb8030deda1eeb918c7e75 /builtins | |
| parent | 726f63884db0132f01745f1fb4465e6621088ccf (diff) | |
| download | android_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.tar.gz android_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.tar.bz2 android_external_bash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.zip | |
Imported from ../bash-2.0.tar.gz.
Diffstat (limited to 'builtins')
52 files changed, 6409 insertions, 3670 deletions
diff --git a/builtins/Makefile b/builtins/Makefile deleted file mode 100644 index 6472f7d..0000000 --- a/builtins/Makefile +++ /dev/null @@ -1,267 +0,0 @@ -# This Makefile for building libbuiltins.a is in -*- text -*- for Emacs. -# -MKBUILTINS = mkbuiltins -RANLIB = /usr/bin/ranlib -CFLAGS = -g -I.. -I. -SHELL = /bin/sh -# CC = cc -AR = ar -RM = rm -f -CP = cp - -srcdir = . -VPATH = .:$(srcdir) - -.SUFFIXES: -.SUFFIXES: .def .c .o -# How to make a .o file from a .def file. -.def.o: - $(RM) $@ - ./$(MKBUILTINS) $(DIRECTDEFINE) $< - $(CC) -c $(CFLAGS) $(CPPFLAGS) $*.c || ( $(RM) $*.c ; exit 1 ) - $(RM) $*.c - -# How to make a .c file from a .def file. -.def.c: - $(RM) $@ - ./$(MKBUILTINS) $(DIRECTDEFINE) $< - -# Here is a rule for making .o files from .c files that does not -# force the type of the machine (like -M_MACHINE) into the flags. -.c.o: - $(RM) $@ - $(CC) -c $(CFLAGS) $(CPPFLAGS) $< - -DEFS = $(srcdir)/alias.def $(srcdir)/bind.def $(srcdir)/break.def \ - $(srcdir)/builtin.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 - -STATIC_SOURCE = common.c getopt.c bashgetopt.c getopt.h - -OFILES = builtins.o \ - alias.o bind.o break.o builtin.o cd.o colon.o command.o \ - common.o declare.o echo.o enable.o eval.o exec.o exit.o \ - fc.o fg_bg.o hash.o help.o history.o jobs.o kill.o \ - let.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 getopt.o bashgetopt.o - -THINGS_TO_TAR = $(DEFS) $(STATIC_SOURCE) Makefile ChangeLog - -CREATED_FILES = builtext.h builtins.c psize.aux pipesize.h - -all: $(MKBUILTINS) libbuiltins.a - -libbuiltins.a: $(MKBUILTINS) $(OFILES) - $(RM) $@ - $(AR) cq $@ $(OFILES) - -$(RANLIB) $@ - -builtext.h builtins.c: $(MKBUILTINS) $(DEFS) - $(RM) builtext.h builtins.c - ./$(MKBUILTINS) -externfile builtext.h -structfile builtins.c \ - -noproduction $(DIRECTDEFINE) $(DEFS) - -mkbuiltins: $(srcdir)/mkbuiltins.c ../config.h - $(CC) $(CFLAGS) -o $(MKBUILTINS) $(srcdir)/mkbuiltins.c - -ulimit.o: ulimit.def pipesize.h - -pipesize.h: psize.aux - $(SHELL) $(srcdir)/psize.sh > pipesize.h - -psize.aux: psize.c - $(CC) $(CFLAGS) -o $@ $(srcdir)/psize.c - -documentation: builtins.texi - -$(OFILES): $(MKBUILTINS) ../config.h - -builtins.texi: $(MKBUILTINS) - ./$(MKBUILTINS) -documentonly $(DEFS) - -clean: - $(RM) $(OFILES) $(CREATED_FILES) $(MKBUILTINS) - -mostlyclean: - $(RM) $(OFILES) libbuiltins.a - -distclean realclean maintainer-clean: clean - $(RM) libbuiltins.a - -alias.o: alias.def -bind.o: bind.def -break.o: break.def -builtin.o: builtin.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 -read.o: read.def -return.o: return.def -set.o: set.def -setattr.o: setattr.def -shift.o: shift.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 -umask.o: umask.def -wait.o: wait.def -getopts.o: getopts.def -reserved.o: reserved.def - -common.o: ../shell.h ../command.h ../config.h ../memalloc.h ../general.h -common.o: ../variables.h ../input.h hashcom.h ../bashhist.h -common.o: ../quit.h ../unwind_prot.h ../maxpath.h ../jobs.h ../builtins.h -common.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -common.o: ../execute_cmd.h ../error.h -alias.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -alias.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -alias.o: ../shell.h ../unwind_prot.h ../variables.h common.h ../maxpath.h -bind.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -bind.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -bind.o: ../maxpath.h -bind.o: ../shell.h ../unwind_prot.h ../variables.h bashgetopt.h -break.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -break.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -break.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -builtin.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -builtin.o: ../quit.h common.h ../maxpath.h -builtin.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -builtin.o: ../shell.h ../unwind_prot.h ../variables.h -cd.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -cd.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -cd.o: ../shell.h ../unwind_prot.h ../variables.h common.h ../maxpath.h -command.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -command.o: ../quit.h bashgetopt.h ../maxpath.h -command.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -command.o: ../shell.h ../unwind_prot.h ../variables.h -declare.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -declare.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -declare.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -echo.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -echo.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -echo.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -enable.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -enable.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -enable.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -eval.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -eval.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -eval.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -exec.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -exec.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -exec.o: ../shell.h ../unwind_prot.h ../variables.h common.h ../execute_cmd.h -exec.o: ../maxpath.h ../flags.h -exit.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -exit.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -exit.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -fc.o: ../builtins.h ../command.h bashgetopt.h ../bashhist.h -fc.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -fc.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -fc.o: ../flags.h ../unwind_prot.h ../variables.h ../shell.h ../maxpath.h -fg_bg.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -fg_bg.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -fg_bg.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -getopts.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -getopts.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -getopts.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -hash.o: ../builtins.h ../command.h ../quit.h ../execute_cmd.h -hash.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -hash.o: ../shell.h ../unwind_prot.h ../variables.h common.h ../maxpath.h -help.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -help.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -help.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -history.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -history.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -history.o: ../filecntl.h ../shell.h ../unwind_prot.h ../variables.h -history.o: ../bashhist.h ../maxpath.h -inlib.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -inlib.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -inlib.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -jobs.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -jobs.o: ../quit.h bashgetopt.h ../maxpath.h -jobs.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -jobs.o: ../shell.h ../unwind_prot.h ../variables.h -kill.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -kill.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -kill.o: ../shell.h ../trap.h ../unwind_prot.h ../variables.h ../maxpath.h -let.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -let.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -let.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -read.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -read.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -read.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -return.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -return.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -return.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -set.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -set.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -set.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -setattr.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -setattr.o: ../quit.h common.h bashgetopt.h ../maxpath.h -setattr.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -setattr.o: ../shell.h ../unwind_prot.h ../variables.h -shift.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -shift.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -shift.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -source.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -source.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -source.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -suspend.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -suspend.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -suspend.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -test.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -test.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -test.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -times.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -times.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -times.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -trap.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -trap.o: ../quit.h common.h ../maxpath.h -trap.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -trap.o: ../shell.h ../unwind_prot.h ../variables.h ../execute_cmd.h -type.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -type.o: ../quit.h common.h ../maxpath.h -type.o: ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -type.o: ../shell.h ../unwind_prot.h ../variables.h -ulimit.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -ulimit.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -ulimit.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -umask.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -umask.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -umask.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h -wait.o: ../command.h ../config.h ../memalloc.h ../error.h ../general.h -wait.o: ../quit.h ../dispose_cmd.h ../make_cmd.h ../subst.h ../externs.h -wait.o: ../shell.h ../unwind_prot.h ../variables.h ../maxpath.h - -bashgetopt.o: ../bashansi.h ../ansi_stdlib.h -mkbuiltins.o: ../bashansi.h ../ansi_stdlib.h -fc.o: ../bashansi.h ../ansi_stdlib.h - -#bind.o: $(RL_LIBSRC)chardefs.h $(RL_LIBSRC)readline.h $(RL_LIBSRC)keymaps.h diff --git a/builtins/Makefile.in b/builtins/Makefile.in new file mode 100644 index 0000000..f38917b --- /dev/null +++ b/builtins/Makefile.in @@ -0,0 +1,375 @@ +# This Makefile for building libbuiltins.a is in -*- text -*- for Emacs. +# +MKBUILTINS = mkbuiltins +RANLIB = @RANLIB@ +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +SHELL = /bin/sh +CC = @CC@ +AR = @AR@ +RM = rm -f +CP = cp + +LIBS = @LIBS@ + +srcdir = @srcdir@ +VPATH = .:@srcdir@ +topdir = @top_srcdir@ +includedir = @includedir@ + +DEFS = @DEFS@ + +INCLUDES = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(srcdir) + +CCFLAGS = $(DEFS) $(SYSTEM_FLAGS) $(CPPFLAGS) ${INCLUDES} $(LOCAL_CFLAGS) $(CFLAGS) + +DIRECTDEFINE = -D $(srcdir) + +# 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)/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 + +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 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 \ + 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 getopt.o bashgetopt.o + +CREATED_FILES = builtext.h builtins.c psize.aux pipesize.h + +all: $(MKBUILTINS) libbuiltins.a + +libbuiltins.a: $(MKBUILTINS) $(OFILES) + $(RM) $@ + $(AR) cr $@ $(OFILES) + -$(RANLIB) $@ + +builtext.h builtins.c: $(MKBUILTINS) $(DEFSRC) + $(RM) builtext.h builtins.c + ./$(MKBUILTINS) -externfile builtext.h -structfile builtins.c \ + -noproduction $(DIRECTDEFINE) $(DEFSRC) + +mkbuiltins: $(srcdir)/mkbuiltins.c ../config.h + $(CC) $(CCFLAGS) -o $(MKBUILTINS) $(srcdir)/mkbuiltins.c $(LIBS) + +# rules for deficient makes, like SunOS +common.o: common.c +bashgetopt.o: bashgetopt.c +getopt.o: getopt.c + +ulimit.o: ulimit.def pipesize.h + +pipesize.h: psize.aux + $(SHELL) $(srcdir)/psize.sh > pipesize.h + +psize.aux: psize.c + $(CC) $(CCFLAGS) -o $@ $(srcdir)/psize.c + +documentation: builtins.texi + +$(OFILES): $(MKBUILTINS) ../config.h + +builtins.texi: $(MKBUILTINS) + ./$(MKBUILTINS) -documentonly $(DEFSRC) + +clean: + $(RM) $(OFILES) $(CREATED_FILES) $(MKBUILTINS) libbuiltins.a + +mostlyclean: + $(RM) $(OFILES) libbuiltins.a + +distclean maintainer-clean: clean + $(RM) Makefile + +alias.o: alias.def +bind.o: bind.def +break.o: break.def +builtin.o: builtin.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 +pushd.o: pushd.def +read.o: read.def +return.o: return.def +set.o: set.def +setattr.o: setattr.def +shift.o: shift.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 +umask.o: umask.def +wait.o: wait.def +getopts.o: getopts.def +reserved.o: reserved.def + +common.o: $(topdir)/shell.h $(topdir)/command.h ../config.h +common.o: $(topdir)/memalloc.h $(topdir)/general.h +common.o: $(topdir)/variables.h $(topdir)/input.h $(srcdir)/hashcom.h +common.o: $(topdir)/bashhist.h $(topdir)/quit.h $(topdir)/unwind_prot.h +common.o: $(topdir)/maxpath.h $(topdir)/jobs.h $(topdir)/builtins.h +common.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +common.o: $(topdir)/execute_cmd.h $(topdir)/error.h $(topdir)/externs.h +alias.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +alias.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/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)/unwind_prot.h $(topdir)/variables.h +bind.o: $(topdir)/command.h ../config.h $(topdir)/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)/maxpath.h $(topdir)/bashline.h +bind.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +break.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +break.o: $(topdir)/error.h $(topdir)/general.h +break.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +break.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +break.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +builtin.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +builtin.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/externs.h +builtin.o: $(topdir)/quit.h $(srcdir)/common.h $(topdir)/maxpath.h +builtin.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +builtin.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +cd.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h $(topdir)/error.h +cd.o: $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +cd.o: $(srcdir)/common.h $(topdir)/maxpath.h +command.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +command.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/externs.h +command.o: $(topdir)/quit.h $(srcdir)/bashgetopt.h $(topdir)/maxpath.h +command.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +command.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +declare.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +declare.o: $(topdir)/error.h $(topdir)/general.h +declare.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +declare.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +declare.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +echo.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h $(topdir)/error.h +echo.o: $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +echo.o: $(topdir)/maxpath.h +enable.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +enable.o: $(topdir)/error.h $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +enable.o: $(topdir)/maxpath.h +eval.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +eval.o: $(topdir)/error.h $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +eval.o: $(topdir)/maxpath.h +exec.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +exec.o: $(topdir)/error.h $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +exec.o: $(srcdir)/common.h $(topdir)/execute_cmd.h $(topdir)/maxpath.h +exit.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +exit.o: $(topdir)/error.h $(topdir)/general.h +exit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +exit.o: $(topdir)/subst.h $(topdir)/externs.h +exit.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +exit.o: $(topdir)/maxpath.h +fc.o: $(topdir)/builtins.h $(topdir)/command.h $(srcdir)/bashgetopt.h +fc.o: $(topdir)/bashhist.h +fc.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h $(topdir)/error.h +fc.o: $(topdir)/general.h $(topdir)/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 +fc.o: $(topdir)/flags.h $(topdir)/unwind_prot.h $(topdir)/variables.h +fg_bg.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +fg_bg.o: $(topdir)/error.h $(topdir)/general.h +fg_bg.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +fg_bg.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +fg_bg.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +getopts.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +getopts.o: $(topdir)/error.h $(topdir)/general.h +getopts.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +getopts.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +getopts.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +hash.o: $(topdir)/builtins.h $(topdir)/command.h $(topdir)/quit.h +hash.o: $(topdir)/execute_cmd.h $(topdir)/hashlib.h +hash.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +hash.o: $(topdir)/error.h $(topdir)/general.h +hash.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +hash.o: $(srcdir)/common.h $(topdir)/maxpath.h +help.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +help.o: $(topdir)/error.h $(topdir)/general.h +help.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +help.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +help.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +history.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +history.o: $(topdir)/error.h $(topdir)/general.h +history.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +history.o: $(topdir)/subst.h $(topdir)/externs.h +history.o: $(topdir)/filecntl.h $(topdir)/shell.h $(topdir)/unwind_prot.h +history.o: $(topdir)/variables.h $(topdir)/bashhist.h $(topdir)/maxpath.h +inlib.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +inlib.o: $(topdir)/error.h $(topdir)/general.h +inlib.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +inlib.o: $(topdir)/maxpath.h $(topdir)/subst.h $(topdir)/externs.h +inlib.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +jobs.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h $(topdir)/error.h +jobs.o: $(topdir)/general.h $(topdir)/quit.h $(srcdir)/bashgetopt.h +jobs.o: $(topdir)/maxpath.h $(topdir)/externs.h +jobs.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +jobs.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +kill.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h $(topdir)/error.h +kill.o: $(topdir)/general.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)/trap.h $(topdir)/unwind_prot.h +kill.o: $(topdir)/variables.h $(topdir)/maxpath.h +let.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +let.o: $(topdir)/error.h $(topdir)/general.h +let.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +let.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +let.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +pushd.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +pushd.o: $(topdir)/error.h $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +pushd.o: $(topdir)/maxpath.h $(srcdir)/common.h +read.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +read.o: $(topdir)/error.h $(topdir)/general.h +read.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +read.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +read.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +return.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +return.o: $(topdir)/error.h $(topdir)/general.h +return.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +return.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +return.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +set.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +set.o: $(topdir)/general.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)/unwind_prot.h $(topdir)/variables.h +set.o: $(topdir)/maxpath.h $(topdir)/error.h +setattr.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +setattr.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/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)/unwind_prot.h $(topdir)/variables.h +shift.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +shift.o: $(topdir)/error.h $(topdir)/general.h +shift.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +shift.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +shift.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +source.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +source.o: $(topdir)/error.h $(topdir)/general.h +source.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +source.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +source.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +suspend.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +suspend.o: $(topdir)/error.h $(topdir)/general.h +suspend.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +suspend.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +suspend.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +test.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +test.o: $(topdir)/error.h $(topdir)/general.h +test.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +test.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +test.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +times.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +times.o: $(topdir)/error.h $(topdir)/general.h +times.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +times.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +times.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +trap.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +trap.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/externs.h +trap.o: $(topdir)/quit.h $(srcdir)/common.h $(topdir)/maxpath.h +trap.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +trap.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +trap.o: $(topdir)/execute_cmd.h +type.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +type.o: $(topdir)/error.h $(topdir)/general.h +type.o: $(topdir)/quit.h $(srcdir)/common.h $(topdir)/maxpath.h +type.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h +type.o: $(topdir)/externs.h +type.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +ulimit.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +ulimit.o: $(topdir)/error.h $(topdir)/general.h +ulimit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +ulimit.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +ulimit.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +umask.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +umask.o: $(topdir)/error.h $(topdir)/general.h +umask.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +umask.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +umask.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +wait.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +wait.o: $(topdir)/error.h $(topdir)/general.h +wait.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +wait.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +wait.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +shopt.o: $(topdir)/command.h ../config.h $(topdir)/memalloc.h +shopt.o: $(topdir)/error.h $(topdir)/general.h +shopt.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +shopt.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/maxpath.h +shopt.o: $(topdir)/shell.h $(topdir)/unwind_prot.h $(topdir)/variables.h +shopt.o: $(srcdir)/common.h $(srcdir)/bashgetopt.h +bashgetopt.o: $(topdir)/bashansi.h $(topdir)/ansi_stdlib.h +mkbuiltins.o: $(topdir)/bashansi.h $(topdir)/ansi_stdlib.h +fc.o: $(topdir)/bashansi.h $(topdir)/ansi_stdlib.h + +#bind.o: $(RL_LIBSRC)chardefs.h $(RL_LIBSRC)readline.h $(RL_LIBSRC)keymaps.h diff --git a/builtins/alias.def b/builtins/alias.def index a878c70..42955b7 100644 --- a/builtins/alias.def +++ b/builtins/alias.def @@ -23,85 +23,108 @@ $BUILTIN alias $FUNCTION alias_builtin $DEPENDS_ON ALIAS $PRODUCES alias.c -$SHORT_DOC alias [ name[=value] ... ] -`alias' with no arguments prints the list of aliases in the form -NAME=VALUE on standard output. 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. Alias returns true -unless a NAME is given for which no alias has been defined. +$SHORT_DOC alias [-p] [name[=value] ... ] +`alias' with no arguments or with the -p option prints the list +of aliases in the 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. Alias returns +true unless a NAME is given for which no alias has been defined. $END -#include "../config.h" +#include <config.h> #if defined (ALIAS) + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + # include <stdio.h> # include "../shell.h" # include "../alias.h" # include "common.h" +# include "bashgetopt.h" extern int interactive; static void print_alias (); /* Hack the alias command in a Korn shell way. */ +int alias_builtin (list) WORD_LIST *list; { - int any_failed = 0; + int any_failed, offset, pflag; + alias_t **alias_list, *t; + char *name, *value; - if (!list) + pflag = 0; + reset_internal_getopt (); + while ((offset = internal_getopt (list, "p")) != -1) { - register int i; - ASSOC **alias_list; + switch (offset) + { + case 'p': + pflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } - if (!aliases) + list = loptend; + + if (list == 0 || pflag) + { + if (aliases == 0) return (EXECUTION_FAILURE); alias_list = all_aliases (); - if (!alias_list) + if (alias_list == 0) return (EXECUTION_FAILURE); - for (i = 0; alias_list[i]; i++) - print_alias (alias_list[i]); + for (offset = 0; alias_list[offset]; offset++) + print_alias (alias_list[offset]); free (alias_list); /* XXX - Do not free the strings. */ + + if (list == 0) + return (EXECUTION_SUCCESS); } - else + + any_failed = 0; + while (list) { - while (list) - { - register char *value, *name = list->word->word; - register int offset; + name = list->word->word; - for (offset = 0; name[offset] && name[offset] != '='; offset++) - ; + for (offset = 0; name[offset] && name[offset] != '='; offset++) + ; - if (offset && name[offset] == '=') - { - name[offset] = '\0'; - value = name + offset + 1; + if (offset && name[offset] == '=') + { + name[offset] = '\0'; + value = name + offset + 1; - add_alias (name, value); - } + add_alias (name, value); + } + else + { + t = find_alias (name); + if (t) + print_alias (t); else { - ASSOC *t = find_alias (name); - if (t) - print_alias (t); - else - { - if (interactive) - builtin_error ("`%s' not found", name); - any_failed++; - } + if (interactive) + builtin_error ("`%s' not found", name); + any_failed++; } - list = list->next; } + list = list->next; } - if (any_failed) - return (EXECUTION_FAILURE); - else - return (EXECUTION_SUCCESS); + + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); } #endif /* ALIAS */ @@ -115,33 +138,37 @@ $END #if defined (ALIAS) /* Remove aliases named in LIST from the aliases database. */ +int unalias_builtin (list) register WORD_LIST *list; { - register ASSOC *alias; - int any_failed = 0; + register alias_t *alias; + int opt, aflag; - while (list && *list->word->word == '-') + aflag = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "a")) != -1) { - register char *word = list->word->word; - - if (ISOPTION (word, 'a')) - { - delete_all_aliases (); - list = list->next; - } - else if (ISOPTION (word, '-')) + switch (opt) { - list = list->next; + case 'a': + aflag = 1; break; + default: + builtin_usage (); + return (EX_USAGE); } - else - { - bad_option (word); - return (EXECUTION_FAILURE); - } } + list = loptend; + + if (aflag) + { + delete_all_aliases (); + return (EXECUTION_SUCCESS); + } + + aflag = 0; while (list) { alias = find_alias (list->word->word); @@ -151,27 +178,25 @@ unalias_builtin (list) else { if (interactive) - builtin_error ("`%s' not an alias", list->word->word); + builtin_error ("`%s': not an alias", list->word->word); - any_failed++; + aflag++; } list = list->next; } - if (any_failed) - return (EXECUTION_FAILURE); - else - return (EXECUTION_SUCCESS); + 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) - ASSOC *alias; + alias_t *alias; { - char *value = single_quote (alias->value); + char *value; + value = single_quote (alias->value); printf ("alias %s=%s\n", alias->name, value); free (value); diff --git a/builtins/bashgetopt.c b/builtins/bashgetopt.c index 95dcaac..72ac900 100644 --- a/builtins/bashgetopt.c +++ b/builtins/bashgetopt.c @@ -18,10 +18,17 @@ You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include <errno.h> -#include "shell.h" -#include "bashansi.h" +#include "../shell.h" +#include "common.h" #define ERR(S, C) builtin_error("%s%c", (S), (C)) @@ -41,14 +48,21 @@ char *opts; { register int c; register char *cp; + int plus; /* nonzero means to handle +option */ - if (!list) { + if (*opts == '+') { + plus = 1; + opts++; + } else + plus = 0; + + if (list == 0) { list_optarg = (char *)NULL; loptend = (WORD_LIST *)NULL; /* No non-option arguments */ return -1; } - if (list != lhead || !lhead) { + if (list != lhead || lhead == 0) { /* Hmmm.... called with a different word list. Reset. */ sp = 1; lcurrent = lhead = list; @@ -56,7 +70,7 @@ char *opts; } if (sp == 1) { - if (!lcurrent || + if (lcurrent == 0 || (lcurrent->word->word[0] != '-' || lcurrent->word->word[1] == '\0')) { lhead = (WORD_LIST *)NULL; loptend = lcurrent; @@ -84,23 +98,46 @@ char *opts; return('?'); } - if (*++cp == ':') { - /* Option requires an argument. */ + 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] != '\0') { - list_optarg = &(lcurrent->word->word[sp+1]); + 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. */ + } else if (lcurrent->next && (*cp == ':' || lcurrent->next->word->word[0] != '-')) { + lcurrent = lcurrent->next; + list_optarg = lcurrent->word->word; + lcurrent = lcurrent->next; + } else if (*cp == ';') { + list_optarg = (char *)NULL; lcurrent = lcurrent->next; - } else if (lcurrent->next == NULL) { + } else { /* lcurrent->next == NULL */ ERR("option requires an argument: -", c); sp = 1; list_optarg = (char *)NULL; return('?'); - } else { - lcurrent = lcurrent->next; - list_optarg = lcurrent->word->word; - lcurrent = lcurrent->next; } sp = 1; + } else if (*cp == '#') { + /* optional 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, (long *)0)) { + lcurrent = lcurrent->next; + list_optarg = lcurrent->word->word; + lcurrent = lcurrent->next; + } else + list_optarg = (char *)NULL; + } + } else { /* No argument, just return the option. */ if (lcurrent->word->word[++sp] == '\0') { diff --git a/builtins/bind.def b/builtins/bind.def index cdcd6a3..908803a 100644 --- a/builtins/bind.def +++ b/builtins/bind.def @@ -21,10 +21,12 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES bind.c +#include <config.h> + $BUILTIN bind $DEPENDS_ON READLINE $FUNCTION bind_builtin -$SHORT_DOC bind [-lvd] [-m keymap] [-f filename] [-q name] [keyseq:readline-function] +$SHORT_DOC bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-r keyseq] [keyseq:readline-function] Bind a key sequence to a Readline function, or to a macro. The syntax is equivalent to that found in ~/.inputrc, but must be passed as a single argument: bind '"\C-x\C-r": re-read-init-file'. @@ -34,49 +36,75 @@ Arguments we accept: emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, vi-command, and vi-insert. -l List names of functions. - -v List function names and bindings. - -d Dump functions and bindings such that they - can be read back in. + -P List function names and bindings. + -p List functions and bindings in a form that can be + reused as input. + -r keyseq Remove the binding for KEYSEQ. -f filename Read key bindings from FILENAME. -q function-name Query about which keys invoke the named function. + -V List variable names and values + -v List variable names and values 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. $END -#include <stdio.h> -#include "../shell.h" #if defined (READLINE) + +#if defined (HAVE_UNISTD_H) +# 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 "../shell.h" +#include "../bashline.h" #include "bashgetopt.h" +#include "common.h" static int query_bindings (); -extern int bash_readline_initialized; extern int no_line_editing; #define BIND_RETURN(x) do { return_code = x; goto bind_exit; } while (0) -#define USAGE "usage: bind [-lvd] [-m keymap] [-f filename] [-q name] [keyseq:readline_func]" +#define LFLAG 0x01 +#define PFLAG 0x02 +#define FFLAG 0x04 +#define VFLAG 0x08 +#define QFLAG 0x10 +#define MFLAG 0x20 +#define RFLAG 0x40 +#define PPFLAG 0x80 +#define VVFLAG 0x100 +#define SFLAG 0x200 +#define SSFLAG 0x400 int bind_builtin (list) WORD_LIST *list; { - int return_code = EXECUTION_SUCCESS; + int return_code; FILE *old_rl_outstream; Keymap kmap, saved_keymap; - int lflag, dflag, fflag, vflag, qflag, mflag, opt; - char *initfile, *map_name, *fun_name; + int flags, opt; + char *initfile, *map_name, *fun_name, *remove_seq; if (no_line_editing) return (EXECUTION_FAILURE); kmap = saved_keymap = (Keymap) NULL; - lflag = dflag = vflag = fflag = qflag = mflag = 0; - initfile = map_name = fun_name = (char *)NULL; + flags = 0; + initfile = map_name = fun_name = remove_seq = (char *)NULL; + return_code = EXECUTION_SUCCESS; if (!bash_readline_initialized) initialize_readline (); @@ -88,39 +116,49 @@ bind_builtin (list) rl_outstream = stdout; reset_internal_getopt (); - while ((opt = internal_getopt (list, "lvdf:q:m:")) != EOF) + while ((opt = internal_getopt (list, "lvpVPsSf:q:m:r:")) != EOF) { switch (opt) { case 'l': - lflag++; + flags |= LFLAG; break; - case 'v': - vflag++; + flags |= VFLAG; break; - - case 'd': - dflag++; + case 'p': + flags |= PFLAG; break; - case 'f': - fflag++; + flags |= FFLAG; initfile = list_optarg; break; - case 'm': - mflag++; + flags |= MFLAG; map_name = list_optarg; break; - case 'q': - qflag++; + flags |= QFLAG; fun_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; default: - builtin_error (USAGE); + builtin_usage (); BIND_RETURN (EX_USAGE); } } @@ -130,12 +168,12 @@ bind_builtin (list) /* First, see if we need to install a special keymap for this command. Then start on the arguments. */ - if (mflag && map_name) + if ((flags & MFLAG) && map_name) { kmap = rl_get_keymap_by_name (map_name); if (!kmap) { - builtin_error ("`%s': illegal keymap name", map_name); + builtin_error ("`%s': invalid keymap name", map_name); BIND_RETURN (EXECUTION_FAILURE); } } @@ -149,16 +187,28 @@ bind_builtin (list) /* 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 (lflag) - rl_list_funmap_names (0); + if (flags & LFLAG) + rl_list_funmap_names (); - if (vflag) + if (flags & PFLAG) + rl_function_dumper (1); + + if (flags & PPFLAG) rl_function_dumper (0); - if (dflag) - rl_function_dumper (1); + if (flags & SFLAG) + rl_macro_dumper (1); + + if (flags & SSFLAG) + rl_macro_dumper (0); - if (fflag && initfile) + 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) { @@ -167,9 +217,18 @@ bind_builtin (list) } } - if (qflag && fun_name) + if ((flags & QFLAG) && fun_name) return_code = query_bindings (fun_name); + if ((flags & RFLAG) && remove_seq) + { + if (rl_set_key (remove_seq, (Function *)NULL, rl_get_keymap ()) != 0) + { + builtin_error ("cannot unbind %s", remove_seq); + BIND_RETURN (EXECUTION_FAILURE); + } + } + /* Process the rest of the arguments as binding specifications. */ while (list) { diff --git a/builtins/break.def b/builtins/break.def index d72f9e3..d6f8598 100644 --- a/builtins/break.def +++ b/builtins/break.def @@ -27,8 +27,14 @@ $SHORT_DOC break [n] Exit from within a FOR, WHILE or UNTIL loop. If N is specified, break N levels. $END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif #include "../shell.h" +#include "common.h" extern char *this_command_name; @@ -45,6 +51,7 @@ 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; { @@ -75,6 +82,7 @@ $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; { @@ -103,7 +111,7 @@ check_loop_level () { #if defined (BREAK_COMPLAINS) if (!loop_level) - builtin_error ("Only meaningful in a `for', `while', or `until' loop"); + 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 index 824b30d..75d25a0 100644 --- a/builtins/builtin.def +++ b/builtins/builtin.def @@ -28,15 +28,20 @@ Run a shell builtin. This is useful when you wish to rename a shell builtin to be a function, but need the functionality of the builtin within the function itself. $END +#include <config.h> -#include "../shell.h" +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "../shell.h" #include "common.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; { diff --git a/builtins/cd.def b/builtins/cd.def index 338f694..e6611d0 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -1,5 +1,5 @@ This file is cd.def, from which is created cd.c. It implements the -builtins "cd", "pwd", "pushd", "popd", and "dirs" in Bash. +builtins "cd" and "pwd" in Bash. Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. @@ -20,15 +20,20 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES cd.c +#include <config.h> -#include <stdio.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashtypes.h" +#include "../posixdir.h" +#include "../posixstat.h" #include <sys/param.h> -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include <stdio.h> + +#include "../bashansi.h" #include <errno.h> #include <tilde/tilde.h> @@ -37,27 +42,102 @@ $PRODUCES cd.c #include "../flags.h" #include "../maxpath.h" #include "common.h" +#include "bashgetopt.h" #if !defined (errno) extern int errno; #endif /* !errno */ -static int change_to_directory (), cd_to_string (); +extern int posixly_correct, interactive; +extern char *bash_getcwd_errstr; + +static int change_to_directory (); + +static char *cdspell (); +static int spname (), mindist (), spdist (); +int cdspelling = 1; + +int cdable_vars; $BUILTIN cd $FUNCTION cd_builtin -$SHORT_DOC cd [dir] +$SHORT_DOC cd [-PL] [dir] Change the current directory to DIR. The variable $HOME is the default DIR. The variable $CDPATH defines the search path for -the directory containing DIR. Alternative directory names are -separated by a colon (:). A null directory name is the same as +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, i.e. `.'. If DIR begins with a slash (/), then $CDPATH is not used. If the directory is not found, and the -shell variable `cdable_vars' exists, then try the word as a variable +shell option `cdable_vars' is set, then try the word as a variable name. If that variable has a value, then cd to the value of that -variable. +variable. The -P option says to use the physical directory structure +instead of following symbolic links; the -L option forces symbolic links +to be followed. $END +/* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste + them together into PATH/DIR. Tilde expansion is performed on PATH if + DOTILDE is non-zero. If PATH is the empty string, it is converted to + `./', since a null element in $CDPATH means the current directory. */ +static char * +mkpath (path, dir, dotilde) + char *path, *dir; + int dotilde; +{ + int dirlen, pathlen; + char *ret, *xpath; + + if (*path == '\0') + { + xpath = xmalloc (2); + xpath[0] = '.'; + xpath[1] = '\0'; + pathlen = 1; + } + else + { + xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path; + pathlen = strlen (xpath); + } + + dirlen = strlen (dir); + ret = xmalloc (2 + dirlen + pathlen); + strcpy (ret, xpath); + if (xpath[pathlen - 1] != '/') + { + ret[pathlen++] = '/'; + ret[pathlen] = '\0'; + } + strcpy (ret + pathlen, dir); + if (xpath != path) + free (xpath); + return (ret); +} + +static int +bindpwd (no_symlinks) + int no_symlinks; +{ + char *dirname; + int old_symlinks; + + if (no_symlinks) + { + old_symlinks = no_symbolic_links; + no_symbolic_links = 1; + dirname = get_working_directory ("cd"); + no_symbolic_links = old_symlinks; + } + else + dirname = get_working_directory ("cd"); + + bind_variable ("OLDPWD", get_string_value ("PWD")); + bind_variable ("PWD", dirname); + + FREE (dirname); + return (EXECUTION_SUCCESS); +} + /* 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 @@ -66,7 +146,9 @@ int cd_builtin (list) WORD_LIST *list; { - char *dirname; + char *dirname, *cdpath, *path, *temp; + int path_index, no_symlinks, opt; + struct stat sb; #if defined (RESTRICTED_SHELL) if (restricted) @@ -76,535 +158,210 @@ cd_builtin (list) } #endif /* RESTRICTED_SHELL */ - if (list) + no_symlinks = no_symbolic_links; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) { - char *extract_colon_unit (); - char *path_string = get_string_value ("CDPATH"); - char *path; - int path_index = 0, dirlen, pathlen; - - dirname = list->word->word; - - if (path_string && !absolute_pathname (dirname)) + switch (opt) { - while ((path = extract_colon_unit (path_string, &path_index))) - { - char *dir; - - if (*path == '~') - { - char *te_string = tilde_expand (path); - - free (path); - path = te_string; - } - - if (!*path) - { - free (path); - path = xmalloc (2); - path[0] = '.'; /* by definition. */ - path[1] = '\0'; - } - - dirlen = strlen (dirname); - pathlen = strlen (path); - dir = xmalloc (2 + dirlen + pathlen); - strcpy (dir, path); - if (path[pathlen - 1] != '/') - { - dir[pathlen++] = '/'; - dir[pathlen] = '\0'; - } - strcpy (dir + pathlen, dirname); - free (path); - - if (change_to_directory (dir)) - { - /* replaces (strncmp (dir, "./", 2) != 0) */ - if (dir[0] != '.' || dir[1] != '/') - printf ("%s\n", dir); - - free (dir); - goto bind_and_exit; - } - else - free (dir); - } - } - - if (!change_to_directory (dirname)) - { - /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */ - if (dirname[0] == '-' && dirname[1] == '\0') - { - char *t = get_string_value ("OLDPWD"); - - if (t && change_to_directory (t)) - goto bind_and_exit; - } - - /* If the user requests it, then perhaps this is the name of - a shell variable, whose value contains the directory to - change to. If that is the case, then change to that - directory. */ - if (find_variable ("cdable_vars")) - { - char *t = get_string_value (dirname); - - if (t && change_to_directory (t)) - { - printf ("%s\n", t); - goto bind_and_exit; - } - } - - file_error (dirname); + case 'P': + no_symlinks = 1; + break; + case 'L': + no_symlinks = 0; + break; + default: + builtin_usage (); return (EXECUTION_FAILURE); } - goto bind_and_exit; } - else + list = loptend; + + if (list == 0) { + /* `cd' without arguments is equivalent to `cd $HOME' */ dirname = get_string_value ("HOME"); - if (!dirname) - return (EXECUTION_FAILURE); - - if (!change_to_directory (dirname)) + if (dirname == 0) { - file_error (dirname); + builtin_error ("HOME not set"); return (EXECUTION_FAILURE); } - bind_and_exit: - { - char *directory; - - directory = get_working_directory ("cd"); - - bind_variable ("OLDPWD", get_string_value ("PWD")); - bind_variable ("PWD", directory); - - FREE (directory); - } - return (EXECUTION_SUCCESS); - } -} - -$BUILTIN pwd -$FUNCTION pwd_builtin -$SHORT_DOC pwd -Print the current working directory. -$END - -/* Non-zero means that pwd always give verbatim directory, regardless of - symbolic link following. */ -static int verbatim_pwd; - -/* Print the name of the current working directory. */ -pwd_builtin (list) - WORD_LIST *list; -{ - char *directory, *s; - -#if 0 - no_args (list); -#else - verbatim_pwd = no_symbolic_links; - if (list && (s = list->word->word) && s[0] == '-' && s[1] == 'P' && !s[2]) - verbatim_pwd = 1; -#endif - - if (verbatim_pwd) - { - char *buffer = xmalloc (MAXPATHLEN); - directory = getwd (buffer); - - if (!directory) + if (change_to_directory (dirname, no_symlinks) == 0) { - builtin_error ("%s", buffer); - free (buffer); + builtin_error ("%s: %s", dirname, strerror (errno)); + return (EXECUTION_FAILURE); } } - else - directory = get_working_directory ("pwd"); - - if (directory) + else if (list->word->word[0] == '-' && list->word->word[1] == '\0') { - printf ("%s\n", directory); - fflush (stdout); - free (directory); - return (EXECUTION_SUCCESS); - } - else - return (EXECUTION_FAILURE); -} - -$BUILTIN pushd -$FUNCTION pushd_builtin -$DEPENDS_ON PUSHD_AND_POPD -$SHORT_DOC pushd [dir | +n | -n] -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. - -+n Rotates the stack so that the Nth directory (counting - from the left of the list shown by `dirs') is at the top. - --n Rotates the stack so that the Nth directory (counting - from the right) is at the top. - -dir adds DIR to the directory stack at the top, making it the - new current working directory. + /* This is `cd -', equivalent to `cd $OLDPWD' */ + dirname = get_string_value ("OLDPWD"); -You can see the directory stack with the `dirs' command. -$END - -#if defined (PUSHD_AND_POPD) -/* Some useful commands whose behaviour has been observed in Csh. */ - -/* The list of remembered directories. */ -static char **pushd_directory_list = (char **)NULL; - -/* Number of existing slots in this list. */ -static int directory_list_size = 0; - -/* Offset to the end of the list. */ -static int directory_list_offset = 0; - -pushd_builtin (list) - WORD_LIST *list; -{ - char *temp, *current_directory; - int j = directory_list_offset - 1; - char direction = '+'; - - /* If there is no argument list then switch current and - top of list. */ - if (!list) - { - if (!directory_list_offset) + if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0) { - builtin_error ("No other directory"); + if (dirname == 0) + builtin_error ("OLDPWD not set"); + else + builtin_error ("%s: %s", dirname, strerror (errno)); return (EXECUTION_FAILURE); } - - current_directory = get_working_directory ("pushd"); - if (!current_directory) - return (EXECUTION_FAILURE); - - temp = pushd_directory_list[j]; - pushd_directory_list[j] = current_directory; - goto change_to_temp; } else { - direction = *(list->word->word); - if (direction == '+' || direction == '-') + dirname = list->word->word; + + if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH"))) { - int num; - if (1 == sscanf (&(list->word->word)[1], "%d", &num)) + /* Find directory in $CDPATH. */ + path_index = 0; + while ((path = extract_colon_unit (cdpath, &path_index))) { - if (direction == '-') - num = directory_list_offset - num; + temp = mkpath (path, dirname, 1); + free (path); - if (num > directory_list_offset || num < 0) + if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0) { - if (!directory_list_offset) - builtin_error ("Directory stack empty"); - else - builtin_error ("Stack contains only %d directories", - directory_list_offset + 1); - return (EXECUTION_FAILURE); + free (temp); + continue; } - else - { - /* Rotate the stack num times. Remember, the - current directory acts like it is part of the - stack. */ - temp = get_working_directory ("pushd"); - - if (!num) - goto change_to_temp; - - do - { - char *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); - temp = savestring (temp); - change_to_temp: - { - int tt = EXECUTION_FAILURE; - - if (temp) - { - tt = cd_to_string (temp); - free (temp); - } - - if ((tt == EXECUTION_SUCCESS)) - dirs_builtin ((WORD_LIST *)NULL); + if (change_to_directory (temp, no_symlinks)) + { + if (temp[0] != '.' || temp[1] != '/') + printf ("%s\n", temp); - return (tt); - } + free (temp); + /* Posix.2 says that after using CDPATH, the resultant + value of $PWD will not contain symlinks. */ + return (bindpwd (posixly_correct)); } + else + free (temp); } } - /* 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) - return (EXECUTION_FAILURE); + if (change_to_directory (dirname, no_symlinks)) + return (bindpwd (no_symlinks)); - if (cd_builtin (list) == EXECUTION_SUCCESS) + /* If the user requests it, then perhaps this is the name of + a shell variable, whose value contains the directory to + change to. If that is the case, then change to that + directory. */ + if (cdable_vars) { - if (directory_list_offset == directory_list_size) + temp = get_string_value (dirname); + if (temp && change_to_directory (temp, no_symlinks)) { - pushd_directory_list = (char **) - xrealloc (pushd_directory_list, - (directory_list_size += 10) * sizeof (char *)); + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); } - pushd_directory_list[directory_list_offset++] = current_directory; - - dirs_builtin ((WORD_LIST *)NULL); - - return (EXECUTION_SUCCESS); - } - else - { - free (current_directory); - return (EXECUTION_FAILURE); } - } -} -#endif /* PUSHD_AND_POPD */ - -$BUILTIN dirs -$FUNCTION dirs_builtin -$DEPENDS_ON PUSHD_AND_POPD -$SHORT_DOC dirs [-l] -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. - -The -l flag specifies that `dirs' should not print shorthand versions -of directories which are relative to your home directory. This means -that `~/bin' might be displayed as `/homes/bfox/bin'. -$END - -#if defined (PUSHD_AND_POPD) -/* Print the current list of directories on the directory stack. */ -dirs_builtin (list) - WORD_LIST *list; -{ - int i, format, desired_index, index_flag; - char *temp, *w; - format = index_flag = 0; - desired_index = -1; - /* Maybe do long form or print specific dir stack entry? */ - while (list) - { - if (strcmp (list->word->word, "-l") == 0) - { - format++; - list = list->next; - } - else if (*list->word->word == '+' && all_digits (list->word->word + 1)) + /* 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 (interactive && cdspelling) { - w = list->word->word + 1; - index_flag = 1; - i = atoi (w); - /* dirs +0 prints the current working directory. */ - if (i == 0) - desired_index = i; - else if (i == directory_list_offset) - { - desired_index = 0; - index_flag = 2; - } - else - desired_index = directory_list_offset - i; - list = list->next; + temp = cdspell (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + free (temp); + return (bindpwd (no_symlinks)); + } + else + FREE (temp); } - else if (*list->word->word == '-' && all_digits (list->word->word + 1)) - { - w = list->word->word + 1; - i = atoi (w); - index_flag = 2; - /* dirs -X where X is directory_list_offset prints the current - working directory. */ - if (i == directory_list_offset) - { - index_flag = 1; - desired_index = 0; - } - else - desired_index = i; - list = list->next; - } - else - { - bad_option (list->word->word); - return (EXECUTION_FAILURE); - } - } - if (index_flag && (desired_index < 0 || desired_index > directory_list_offset)) - { - if (directory_list_offset == 0) - builtin_error ("directory stack empty"); - else - builtin_error ("%s: bad directory stack index", w); + builtin_error ("%s: %s", dirname, strerror (errno)); return (EXECUTION_FAILURE); } - /* The first directory printed is always the current working directory. */ - if (!index_flag || (index_flag == 1 && desired_index == 0)) - { - temp = get_working_directory ("dirs"); - if (!temp) - temp = savestring ("<no directory>"); - printf ("%s", format ? temp : polite_directory_format (temp)); - free (temp); - if (index_flag) - { - putchar ('\n'); - return EXECUTION_SUCCESS; - } - } - -#define DIRSTACK_ENTRY(i) \ - format ? pushd_directory_list[i] \ - : polite_directory_format (pushd_directory_list[i]) - - /* Now print the requested directory stack entries. */ - if (index_flag) - printf ("%s", DIRSTACK_ENTRY (desired_index)); - else - for (i = (directory_list_offset - 1); i > -1; i--) - printf (" %s", DIRSTACK_ENTRY (i)); - - putchar ('\n'); - fflush (stdout); - return (EXECUTION_SUCCESS); + return (bindpwd (no_symlinks)); } -#endif /* PUSHD_AND_POPD */ - -$BUILTIN popd -$FUNCTION popd_builtin -$DEPENDS_ON PUSHD_AND_POPD -$SHORT_DOC popd [+n | -n] -Removes entries from the directory stack. With no arguments, -removes the top directory from the stack, and cd's to the new -top directory. -+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. - -You can see the directory stack with the `dirs' command. +$BUILTIN pwd +$FUNCTION pwd_builtin +$SHORT_DOC pwd [-PL] +Print the current working directory. With the -P option, pwd prints +the physical directory, without any symbolic links; the -L option +makes pwd follow symbolic links. $END -#if defined (PUSHD_AND_POPD) -/* 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. */ -popd_builtin (list) +/* 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; { - register int i; - int which = 0; - char direction = '+'; + char *directory, *buffer; + int opt; - if (list) + verbatim_pwd = no_symbolic_links; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) { - direction = *(list->word->word); - - if ((direction != '+' && direction != '-') || - (1 != sscanf (&((list->word->word)[1]), "%d", &which))) + switch (opt) { - builtin_error ("bad arg `%s'", list->word->word); + case 'P': + verbatim_pwd = 1; + break; + case 'L': + verbatim_pwd = 0; + break; + default: + builtin_usage (); return (EXECUTION_FAILURE); } } + list = loptend; - if (which > directory_list_offset || (!directory_list_offset && !which)) + if (verbatim_pwd) { - if (!directory_list_offset) - builtin_error ("Directory stack empty"); - else - builtin_error ("Stack contains only %d directories", - directory_list_offset + 1); - return (EXECUTION_FAILURE); - } + buffer = xmalloc (PATH_MAX); + directory = getcwd (buffer, PATH_MAX); - /* Handle case of no specification, or top of stack specification. */ - if ((direction == '+' && which == 0) || - (direction == '-' && which == directory_list_offset)) - { - i = cd_to_string (pushd_directory_list[directory_list_offset - 1]); - if (i != EXECUTION_SUCCESS) - return (i); - free (pushd_directory_list[--directory_list_offset]); + if (directory == 0) + { + builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno)); + free (buffer); + } } 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. */ - - if (direction == '+') - i = directory_list_offset - which; - else - i = which; - - free (pushd_directory_list[i]); - directory_list_offset--; + directory = get_working_directory ("pwd"); - /* Shift the remainder of the list into place. */ - for (; i < directory_list_offset; i++) - pushd_directory_list[i] = pushd_directory_list[i + 1]; + if (directory) + { + printf ("%s\n", directory); + fflush (stdout); + free (directory); + return (EXECUTION_SUCCESS); } - - dirs_builtin ((WORD_LIST *)NULL); - - return (EXECUTION_SUCCESS); + else + return (EXECUTION_FAILURE); } -#endif /* PUSHD_AND_POPD */ /* Do the work of changing to the directory NEWDIR. Handle symbolic link following, etc. */ static int -change_to_directory (newdir) +change_to_directory (newdir, nolinks) char *newdir; + int nolinks; { char *t; - if (!no_symbolic_links) + if (nolinks == 0) { int chdir_return = 0; char *tdir = (char *)NULL; - if (!the_current_working_directory) + if (the_current_working_directory == 0) { t = get_working_directory ("cd_links"); FREE (t); @@ -625,7 +382,6 @@ change_to_directory (newdir) else { FREE (tdir); - tdir = t; } @@ -667,23 +423,171 @@ change_to_directory (newdir) return (chdir_return); } else + return (chdir (newdir) == 0); +} + +/* Code for cd spelling correction. Original patch submitted by + Neil Russel (caret@c-side.com). */ + +static char * +cdspell (dirname) + char *dirname; +{ + int n; + char *guess; + + n = (strlen (dirname) * 3 + 1) / 2 + 1; + guess = xmalloc (n); + + switch (spname (dirname, guess)) { - if (chdir (newdir) < 0) - return (0); - else - return (1); + case -1: + default: + free (guess); + return (char *)NULL; + case 0: + case 1: + return guess; + } +} + +/* + * `spname' and its helpers are inspired by the code in "The UNIX + * Programming Environment, Kernighan & Pike, Prentice-Hall 1984", + * pages 209 - 213. + */ + +/* + * `spname' -- return a correctly spelled filename + * + * int spname(char * oldname, char * newname) + * Returns: -1 if no reasonable match found + * 0 if exact match found + * 1 if corrected + * Stores corrected name in `newname'. + */ +static int +spname(oldname, newname) + char *oldname; + char *newname; +{ + char *op, *np, *p; + char guess[PATH_MAX + 1], best[PATH_MAX + 1]; + + op = oldname; + np = newname; + for (;;) + { + while (*op == '/') /* Skip slashes */ + *np++ = *op++; + *np = '\0'; + + if (*op == '\0') /* Exact or corrected */ + { + /* `.' is rarely the right thing. */ + if (oldname[1] == '\0' && newname[1] == '\0' && + oldname[0] != '.' && newname[0] == '.') + return -1; + return strcmp(oldname, newname) != 0; + } + + /* Copy next component into guess */ + for (p = guess; *op != '/' && *op != '\0'; op++) + if (p < guess + PATH_MAX) + *p++ = *op; + *p = '\0'; + + if (mindist(newname, guess, best) >= 3) + return -1; /* Hopeless */ + + /* + * Add to end of newname + */ + for (p = best; *np = *p++; np++) + ; } } -/* 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. */ +/* + * Search directory for a guess + */ static int -cd_to_string (name) - char *name; +mindist(dir, guess, best) + char *dir; + char *guess; + char *best; { - WORD_LIST *tlist = make_word_list (make_word (name), NULL); - int result = (cd_builtin (tlist)); - dispose_words (tlist); - return (result); + DIR *fd; + struct dirent *dp; + int dist, x; + + dist = 3; /* Worst distance */ + if (*dir == '\0') + dir = "."; + + if ((fd = opendir(dir)) == NULL) + return dist; + + while ((dp = readdir(fd)) != NULL) + { + /* + * Look for a better guess. If the new guess is as + * good as the current one, we take it. This way, + * any single character match will be a better match + * than ".". + */ + x = spdist(dp->d_name, guess); + if (x <= dist && x != 3) + { + strcpy(best, dp->d_name); + dist = x; + if (dist == 0) /* Exact match */ + break; + } + } + (void)closedir(fd); + + return dist; +} + +/* + * `spdist' -- return the "distance" between two names. + * + * int spname(char * oldname, char * newname) + * Returns: 0 if strings are identical + * 1 if two characters are transposed + * 2 if one character is wrong, added or deleted + * 3 otherwise + */ +static int +spdist(cur, new) + char *cur, *new; +{ + while (*cur == *new) + { + if (*cur == '\0') + return 0; /* Exact match */ + cur++; + new++; + } + + if (*cur) + { + if (*new) + { + if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0) + return 1; /* Transposition */ + + if (strcmp (cur + 1, new + 1) == 0) + return 2; /* One character mismatch */ + } + + if (strcmp(&cur[1], &new[0]) == 0) + return 2; /* Extra character */ + } + + if (*new && strcmp(cur, new + 1) == 0) + return 2; /* Missing character */ + + return 3; } diff --git a/builtins/colon.def b/builtins/colon.def index 4ae5b65..7fd9656 100644 --- a/builtins/colon.def +++ b/builtins/colon.def @@ -28,10 +28,32 @@ $SHORT_DOC : No effect; the command does nothing. A zero exit code is returned. $END -/* Do nothing. This command is a no-op. */ +$BUILTIN true +$DOCNAME true_builtin +$FUNCTION colon_builtin +$SHORT_DOC true +Return a successful result. +$END + +$BUILTIN false +$DOCNAME false_builtin +$FUNCTION false_builtin +$SHORT_DOC false +Return an unsuccessful result. +$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 index b84613e..3a14dc6 100644 --- a/builtins/command.def +++ b/builtins/command.def @@ -23,7 +23,7 @@ $PRODUCES command.c $BUILTIN command $FUNCTION command_builtin -$SHORT_DOC command [-pVv] [command [arg ...]] +$SHORT_DOC command [-pVv] command [arg ...] Runs COMMAND with ARGS ignoring shell functions. If you have a shell function called `ls', and you wish to call the command `ls', you can say "command ls". If the -p option is given, a default value is used @@ -32,14 +32,19 @@ the -V or -v option is given, a string is printed describing COMMAND. The -V option produces a more verbose description. $END -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include "../shell.h" +#include "../execute_cmd.h" +#include "../flags.h" #include "bashgetopt.h" +#include "common.h" extern int subshell_environment; @@ -52,9 +57,11 @@ int command_builtin (list) WORD_LIST *list; { - int result, verbose = 0, use_standard_path = 0, opt; - char *old_path; - + 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) { @@ -69,73 +76,80 @@ command_builtin (list) case 'v': verbose = 4; break; - default: - report_bad_option (); - builtin_error ("usage: command [-pvV] [command [arg...]]"); + builtin_usage (); return (EX_USAGE); } } list = loptend; - if (!list) + if (list == 0) return (EXECUTION_SUCCESS); if (verbose) { - int found, any_found = 0; + int found, any_found; - while (list) + for (any_found = 0; list; list = list->next) { - found = describe_command (list->word->word, verbose, 0); - if (!found) + if (found == 0) builtin_error ("%s: not found", list->word->word); any_found += found; - list = list->next; } return (any_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } +#if defined (RESTRICTED_SHELL) + if (use_standard_path && restricted) + { + builtin_error ("restricted: cannot use -p"); + return (EXECUTION_FAILURE); + } +#endif + begin_unwind_frame ("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 *command; - - if (use_standard_path) - { - char *standard_path; - - old_path = get_string_value ("PATH"); - if (old_path) - old_path = savestring (old_path); - else - old_path = savestring (""); - add_unwind_protect ((Function *)restore_path, old_path); - - standard_path = get_standard_path (); - bind_variable ("PATH", standard_path); - free (standard_path); - } - command = make_bare_simple_command (); - command->value.Simple->words = (WORD_LIST *)copy_word_list (list); - command->value.Simple->redirects = (REDIRECT *)NULL; - command->flags |= (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION); - command->value.Simple->flags |= (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION); - /* 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; - } - add_unwind_protect ((char *)dispose_command, command); - result = execute_command (command); - } + if (use_standard_path) + { + old_path = get_string_value ("PATH"); + if (old_path) + old_path = savestring (old_path); + else + { + old_path = xmalloc (1); + old_path[0] = '\0'; + } + add_unwind_protect ((Function *)restore_path, old_path); + + standard_path = get_standard_path (); + bind_variable ("PATH", standard_path ? standard_path : ""); + FREE (standard_path); + } + + command = make_bare_simple_command (); + command->value.Simple->words = (WORD_LIST *)copy_word_list (list); + command->value.Simple->redirects = (REDIRECT *)NULL; + command->flags |= (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION); + command->value.Simple->flags |= (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION); +#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"); @@ -158,7 +172,7 @@ restore_path (var) static char * get_standard_path () { -#if defined (_CS_PATH) && !defined (hpux_7) && !defined (NetBSD) +#if defined (_CS_PATH) && defined (HAVE_CONFSTR) char *p; size_t len; @@ -167,11 +181,11 @@ get_standard_path () *p = '\0'; confstr (_CS_PATH, p, len); return (p); -#else /* !_CSPATH || hpux_7 || NetBSD */ +#else /* !_CSPATH || !HAVE_CONFSTR */ # if defined (CS_PATH) return (savestring (CS_PATH)); # else return (savestring (STANDARD_UTILS_PATH)); # endif /* !CS_PATH */ -#endif /* !_CS_PATH || hpux_7 */ +#endif /* !_CS_PATH || !HAVE_CONFSTR */ } diff --git a/builtins/common.c b/builtins/common.c index ff940b5..2936b88 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -16,27 +16,37 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> #include <sys/types.h> #include "../posixstat.h" -#if defined (HAVE_VFPRINTF) -#include <varargs.h> -#endif /* VFPRINTF */ +#include <signal.h> + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# if defined (PREFER_VARARGS) +# include <varargs.h> +# endif +#endif -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include "../bashansi.h" #include "../shell.h" -#include "../unwind_prot.h" #include "../maxpath.h" +#include "../flags.h" #include "../jobs.h" #include "../builtins.h" #include "../input.h" #include "../execute_cmd.h" +#include "../trap.h" #include "hashcom.h" +#include "bashgetopt.h" #include "common.h" #include <tilde/tilde.h> @@ -45,91 +55,57 @@ #endif extern int no_symbolic_links, interactive, interactive_shell; -extern int indirection_level, startup_state; +extern int indirection_level, startup_state, subshell_environment; +extern int line_number; extern int last_command_exit_value; -extern int hashing_disabled; +extern int running_trap; +extern int hashing_enabled; extern int variable_context; +extern int posixly_correct; extern char *this_command_name, *shell_name; extern COMMAND *global_command; extern HASH_TABLE *hashed_filenames; +extern char *bash_getcwd_errstr; -/* 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. */ -int -get_numeric_arg (list) - WORD_LIST *list; -{ - int count = 1; - - if (list) - { - register char *arg; - int sign = 1; - - arg = list->word->word; - if (!arg) - goto bad_number; - - /* Skip optional leading white space. */ - while (whitespace (*arg)) - arg++; - - if (!*arg) - goto bad_number; - - /* We allow leading `-' or `+'. */ - if (*arg == '-' || *arg == '+') - { - if (!digit (arg[1])) - goto bad_number; - - if (*arg == '-') - sign = -1; - - arg++; - } - - for (count = 0; digit (*arg); arg++) - count = (count * 10) + digit_value (*arg); - - /* Skip trailing whitespace, if any. */ - while (whitespace (*arg)) - arg++; - - if (!*arg) - count = count * sign; - else - { - bad_number: - builtin_error ("bad non-numeric arg `%s'", list->word->word); - throw_to_top_level (); - } - no_args (list->next); - } - return (count); -} +/* **************************************************************** */ +/* */ +/* 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. */ -#if defined (HAVE_VFPRINTF) +#if defined (USE_VARARGS) void -builtin_error (va_alist) +#if defined (PREFER_STDARG) +builtin_error (const char *format, ...) +#else +builtin_error (format, va_alist) + const char *format; va_dcl +#endif { - char *format; va_list args; + char *name; + + name = get_name_for_error (); + fprintf (stderr, "%s: ", name); if (this_command_name && *this_command_name) fprintf (stderr, "%s: ", this_command_name); +#if defined (PREFER_STDARG) + va_start (args, format); +#else va_start (args); - format = va_arg (args, char *); +#endif + vfprintf (stderr, format, args); va_end (args); fprintf (stderr, "\n"); } -#else /* !HAVE_VFPRINTF */ +#else /* !USE_VARARGS */ void builtin_error (format, arg1, arg2, arg3, arg4, arg5) char *format, *arg1, *arg2, *arg3, *arg4, *arg5; @@ -141,7 +117,75 @@ builtin_error (format, arg1, arg2, arg3, arg4, arg5) fprintf (stderr, "\n"); fflush (stderr); } -#endif /* !HAVE_VFPRINTF */ +#endif /* !USE_VARARGS */ + +/* 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"); + jump_to_top_level (DISCARD); + } +} + +/* Function called when one of the builtin commands detects a bad + option. */ +void +bad_option (s) + char *s; +{ + builtin_error ("unknown option: %s", s); +} + +/* 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); +} + +/* **************************************************************** */ +/* */ +/* 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 = word_list_to_argv (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 @@ -155,7 +199,7 @@ remember_args (list, destructive) for (i = 1; i < 10; i++) { - if (destructive && dollar_vars[i]) + if ((destructive || list) && dollar_vars[i]) { free (dollar_vars[i]); dollar_vars[i] = (char *)NULL; @@ -163,9 +207,6 @@ remember_args (list, destructive) if (list) { - if (!destructive && dollar_vars[i]) - free (dollar_vars[i]); - dollar_vars[i] = savestring (list->word->word); list = list->next; } @@ -184,142 +225,15 @@ remember_args (list, destructive) set_dollar_vars_changed (); } -/* Return if LIST is NULL else barf and jump to top_level. */ -void -no_args (list) - WORD_LIST *list; -{ - if (list) - { - builtin_error ("extra arguments"); - longjmp (top_level, DISCARD); - } -} - -/* 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 = 0; - int digits = 0; - - while (*string && *string >= '0' && *string < '8') - { - digits++; - result = (result * 8) + *string++ - '0'; - } - - if (!digits || result > 0777 || *string) - result = -1; - - return (result); -} - -/* Temporary static. */ -static char *dotted_filename = (char *)NULL; - -/* Return the full pathname that FILENAME hashes to. If FILENAME - is hashed, but data->check_dot is non-zero, check ./FILENAME - and return that if it is executable. */ -char * -find_hashed_filename (filename) - char *filename; -{ - register BUCKET_CONTENTS *item; - - if (hashing_disabled) - return ((char *)NULL); - - item = find_hash_item (filename, hashed_filenames); - - if (item) - { - /* If this filename is hashed, but `.' comes before it in the path, - then see if `./filename' is an executable. */ - if (pathdata(item)->check_dot) - { - if (dotted_filename) - free (dotted_filename); - - dotted_filename = (char *)xmalloc (3 + strlen (filename)); - strcpy (dotted_filename, "./"); - strcat (dotted_filename, filename); - - if (executable_file (dotted_filename)) - return (dotted_filename); - - /* Watch out. If this file was hashed to "./filename", and - "./filename" is not executable, then return NULL. */ - - /* Since we already know "./filename" is not executable, what - we're really interested in is whether or not the `path' - portion of the hashed filename is equivalent to the current - directory, but only if it starts with a `.'. (This catches - ./. and so on.) same_file () is in execute_cmd.c; it tests - general Unix file equivalence -- same device and inode. */ - { - char *path = pathdata (item)->path; - - if (*path == '.') - { - int same = 0; - char *tail; - - tail = (char *) strrchr (path, '/'); - - if (tail) - { - *tail = '\0'; - same = same_file - (".", path, (struct stat *)NULL, (struct stat *)NULL); - *tail = '/'; - } - if (same) - return ((char *)NULL); - } - } - } - return (pathdata (item)->path); - } - else - return ((char *)NULL); -} - -/* Remove FILENAME from the table of hashed commands. */ -void -remove_hashed_filename (filename) - char *filename; -{ - register BUCKET_CONTENTS *item; - - if (hashing_disabled) - return; - - item = remove_hash_item (filename, hashed_filenames); - if (item) - { - if (item->data) - { - free (pathdata(item)->path); - free (item->data); - } - if (item->key) - free (item->key); - free (item); - } -} - /* **************************************************************** */ /* */ -/* Pushing and Popping a Context */ +/* Pushing and Popping variable contexts */ /* */ /* **************************************************************** */ static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; -static int dollar_arg_stack_slots = 0; -static int dollar_arg_stack_index = 0; +static int dollar_arg_stack_slots; +static int dollar_arg_stack_index; void push_context () @@ -346,15 +260,15 @@ push_dollar_vars () xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) * sizeof (WORD_LIST **)); } - dollar_arg_stack[dollar_arg_stack_index] = list_rest_of_args (); - dollar_arg_stack[++dollar_arg_stack_index] = (WORD_LIST *)NULL; + dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); + dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; } /* Restore the positional parameters from our stack. */ void pop_dollar_vars () { - if (!dollar_arg_stack || !dollar_arg_stack_index) + if (!dollar_arg_stack || dollar_arg_stack_index == 0) return; remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); @@ -365,17 +279,18 @@ pop_dollar_vars () void dispose_saved_dollar_vars () { - if (!dollar_arg_stack || !dollar_arg_stack_index) + if (!dollar_arg_stack || dollar_arg_stack_index == 0) return; dispose_words (dollar_arg_stack[dollar_arg_stack_index]); dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; } -static int changed_dollar_vars = 0; +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); @@ -390,18 +305,141 @@ set_dollar_vars_unchanged () void set_dollar_vars_changed () { - changed_dollar_vars = 1; + changed_dollar_vars = 1; } -/* Function called when one of the builtin commands detects a bad - option. */ -void -bad_option (s) - char *s; +/* **************************************************************** */ +/* */ +/* 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. */ +int +get_numeric_arg (list) + WORD_LIST *list; { - builtin_error ("unknown option: %s", s); + long count = 1; + + if (list) + { + register char *arg; + + arg = list->word->word; + if (!arg || (legal_number (arg, &count) == 0)) + { + builtin_error ("bad non-numeric arg `%s'", list->word->word); + throw_to_top_level (); + } + no_args (list->next); + } + return (count); +} + +/* 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 && *string >= '0' && *string < '8') + { + digits++; + result = (result * 8) + *string++ - '0'; + } + + if (!digits || result > 0777 || *string) + result = -1; + + return (result); +} + +/* **************************************************************** */ +/* */ +/* Command name hashing */ +/* */ +/* **************************************************************** */ + +/* Return the full pathname that FILENAME hashes to. If FILENAME + is hashed, but (data->flags & HASH_CHKDOT) is non-zero, check + ./FILENAME and return that if it is executable. */ +char * +find_hashed_filename (filename) + char *filename; +{ + register BUCKET_CONTENTS *item; + char *path, *dotted_filename, *tail; + int same; + + if (hashing_enabled == 0) + return ((char *)NULL); + + item = find_hash_item (filename, hashed_filenames); + + if (item == NULL) + return ((char *)NULL); + + /* If this filename is hashed, but `.' comes before it in the path, + see if ./filename is executable. If the hashed value is not an + absolute pathname, see if ./`hashed-value' exists. */ + path = pathdata(item)->path; + if (pathdata(item)->flags & (HASH_CHKDOT|HASH_RELPATH)) + { + tail = (pathdata(item)->flags & HASH_RELPATH) ? path : filename; + dotted_filename = xmalloc (3 + strlen (tail)); + dotted_filename[0] = '.'; dotted_filename[1] = '/'; + strcpy (dotted_filename + 2, tail); + + if (executable_file (dotted_filename)) + return (dotted_filename); + + free (dotted_filename); + +#if 0 + if (pathdata(item)->flags & HASH_RELPATH) + return ((char *)NULL); +#endif + + /* Watch out. If this file was hashed to "./filename", and + "./filename" is not executable, then return NULL. */ + + /* Since we already know "./filename" is not executable, what + we're really interested in is whether or not the `path' + portion of the hashed filename is equivalent to the current + directory, but only if it starts with a `.'. (This catches + ./. and so on.) same_file () tests general Unix file + equivalence -- same device and inode. */ + if (*path == '.') + { + same = 0; + tail = (char *)strrchr (path, '/'); + + if (tail) + { + *tail = '\0'; + same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL); + *tail = '/'; + } + + return same ? (char *)NULL : path; + } + } + + return (path); } +/* **************************************************************** */ +/* */ +/* 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; @@ -410,6 +448,8 @@ char * get_working_directory (for_whom) char *for_whom; { + char *directory; + if (no_symbolic_links) { if (the_current_working_directory) @@ -418,21 +458,18 @@ get_working_directory (for_whom) the_current_working_directory = (char *)NULL; } - if (!the_current_working_directory) + if (the_current_working_directory == 0) { - char *directory; - - the_current_working_directory = xmalloc (MAXPATHLEN); - directory = getwd (the_current_working_directory); - if (!directory) + the_current_working_directory = xmalloc (PATH_MAX); + the_current_working_directory[0] = '\0'; + directory = getcwd (the_current_working_directory, PATH_MAX); + if (directory == 0) { - if (for_whom && *for_whom) - fprintf (stderr, "%s: ", for_whom); - else - fprintf (stderr, "%s: ", get_name_for_error ()); - - fprintf (stderr, "could not get current directory: %s\n", - the_current_working_directory); + fprintf (stderr, "%s: could not get current directory: %s\n", + (for_whom && *for_whom) ? for_whom : get_name_for_error (), + the_current_working_directory[0] + ? the_current_working_directory + : bash_getcwd_errstr); free (the_current_working_directory); the_current_working_directory = (char *)NULL; @@ -448,35 +485,43 @@ void set_working_directory (name) char *name; { - if (the_current_working_directory) - free (the_current_working_directory); - + FREE (the_current_working_directory); the_current_working_directory = savestring (name); } +/* **************************************************************** */ +/* */ +/* Job control support functions */ +/* */ +/* **************************************************************** */ + #if defined (JOB_CONTROL) /* Return the job spec found in LIST. */ +int get_job_spec (list) WORD_LIST *list; { register char *word; - int job = NO_JOB; - int substring = 0; + int job, substring; - if (!list) + if (list == 0) return (current_job); word = list->word->word; - if (!*word) + if (*word == '\0') return (current_job); if (*word == '%') word++; - if (digit (*word) && (sscanf (word, "%d", &job) == 1)) - return (job - 1); + if (digit (*word) && all_digits (word)) + { + job = atoi (word); + return (job - 1); + } + substring = 0; switch (*word) { case 0: @@ -490,21 +535,24 @@ get_job_spec (list) case '?': /* Substring search requested. */ substring++; word++; - goto find_string; + /* FALLTHROUGH */ default: - find_string: { - register int i, wl = strlen (word); + register int i, wl; + + job = NO_JOB; + wl = strlen (word); for (i = 0; i < job_slots; i++) { if (jobs[i]) { - register PROCESS *p = jobs[i]->pipe; + register PROCESS *p; + p = jobs[i]->pipe; do { if ((substring && strindex (p->command, word)) || - (strncmp (p->command, word, wl) == 0)) + (STREQN (p->command, word, wl))) if (job != NO_JOB) { builtin_error ("ambigious job spec: %s", word); @@ -524,171 +572,100 @@ get_job_spec (list) } #endif /* JOB_CONTROL */ -int parse_and_execute_level = 0; - -/* How to force parse_and_execute () to clean up after itself. */ -void -parse_and_execute_cleanup () -{ - run_unwind_frame ("parse_and_execute_top"); -} - -/* Parse and execute the commands in STRING. Returns whatever - execute_command () returns. This frees STRING. INTERACT is - the new value for `interactive' while the commands are being - executed. A value of -1 means don't change it. */ int -parse_and_execute (string, from_file, interact) - char *string; - char *from_file; - int interact; +display_signal_list (list, forcecols) + WORD_LIST *list; + int forcecols; { - int last_result = EXECUTION_SUCCESS; - int code = 0, jump_to_top_level = 0; - char *orig_string = string; - - /* Unwind protect this invocation of parse_and_execute (). */ - begin_unwind_frame ("parse_and_execute_top"); - unwind_protect_int (parse_and_execute_level); - unwind_protect_jmp_buf (top_level); - unwind_protect_int (indirection_level); - if (interact != -1 && interactive != interact) - unwind_protect_int (interactive); + register int i, column; + char *name; + int result; + long signum; -#if defined (HISTORY) - if (interactive_shell) + result = EXECUTION_SUCCESS; + if (!list) { - unwind_protect_int (remember_on_history); -# if defined (BANG_HISTORY) - unwind_protect_int (history_expansion_inhibited); -# endif /* BANG_HISTORY */ - } -#endif /* HISTORY */ - - add_unwind_protect (pop_stream, (char *)NULL); - if (orig_string) - add_unwind_protect (xfree, orig_string); - end_unwind_frame (); - - parse_and_execute_level++; - push_stream (); - indirection_level++; - if (interact != -1) - interactive = interact; - -#if defined (HISTORY) - /* We don't remember text read by the shell this way on - the history list, and we don't use !$ in shell scripts. */ - remember_on_history = 0; -# if defined (BANG_HISTORY) - history_expansion_inhibited = 1; -# endif /* BANG_HISTORY */ -#endif /* HISTORY */ - - with_input_from_string (string, from_file); - { - COMMAND *command; - - while (*(bash_input.location.string)) - { - 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) - { - jump_to_top_level = 0; - switch (code) - { - case FORCE_EOF: - case EXITPROG: - run_unwind_frame ("pe_dispose"); - /* Remember to call longjmp (top_level) after the old - value for it is restored. */ - jump_to_top_level = 1; - goto out; - - case DISCARD: - dispose_command (command); - run_unwind_frame ("pe_dispose"); - last_command_exit_value = 1; - continue; - - default: - programming_error ("bad jump to top_level: %d", code); - break; - } - } - - if (parse_command () == 0) - { - if ((command = global_command) != (COMMAND *)NULL) - { - struct fd_bitmap *bitmap; - - bitmap = new_fd_bitmap (FD_BITMAP_SIZE); - begin_unwind_frame ("pe_dispose"); - add_unwind_protect (dispose_fd_bitmap, bitmap); - - global_command = (COMMAND *)NULL; - -#if defined (ONESHOT) - if (startup_state == 2 && *bash_input.location.string == '\0' && - command->type == cm_simple && !command->redirects && - !command->value.Simple->redirects) - { - command->flags |= CMD_NO_FORK; - command->value.Simple->flags |= CMD_NO_FORK; - } -#endif /* ONESHOT */ - - last_result = execute_command_internal - (command, 0, NO_PIPE, NO_PIPE, bitmap); - - dispose_command (command); - run_unwind_frame ("pe_dispose"); - } - } - else - { - last_result = EXECUTION_FAILURE; - - /* Since we are shell compatible, syntax errors in a script - abort the execution of the script. Right? */ - break; - } - } - } + for (i = 1, column = 0; i < NSIG; i++) + { + name = signal_name (i); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + continue; - out: + if (posixly_correct && !forcecols) + printf ("%s%s", name, (i == NSIG - 1) ? "" : " "); + else + { + printf ("%2d) %s", i, name); + + if (++column < 4) + printf ("\t"); + else + { + printf ("\n"); + column = 0; + } + } + } - run_unwind_frame ("parse_and_execute_top"); + if ((posixly_correct && !forcecols) || column != 0) + printf ("\n"); + return result; + } - if (interrupt_state && parse_and_execute_level == 0) + /* List individual signal names or numbers. */ + while (list) { - /* 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 (legal_number (list->word->word, &signum)) + { + /* This is specified by Posix.2 so that exit statuses can be + mapped into signal numbers. */ + if (signum > 128) + signum -= 128; + if (signum < 0 || signum >= NSIG) + { + builtin_error ("bad signal number: %s", list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + + name = signal_name (signum); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + { + list = list->next; + continue; + } + printf ("%s\n", name); + } + else + { + signum = decode_signal (list->word->word); + if (signum == NO_SIG) + { + builtin_error ("%s: not a signal specification", list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + printf ("%ld\n", signum); + } + list = list->next; } - - if (jump_to_top_level) - longjmp (top_level, code); - - return (last_result); + return (result); } -/* Return the address of the builtin named NAME. +/* **************************************************************** */ +/* */ +/* 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. */ -static Function * +struct builtin * builtin_address_internal (name, disabled_okay) char *name; int disabled_okay; @@ -710,40 +687,53 @@ builtin_address_internal (name, disabled_okay) if (j == 0) { /* It must have a function pointer. It must be enabled, or we - must have explicitly allowed disabled functions to be found. */ + 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].function); + return (&shell_builtins[mid]); else - return ((Function *)NULL); + return ((struct builtin *)NULL); } if (j > 0) hi = mid - 1; else lo = mid + 1; } - return ((Function *)NULL); + return ((struct builtin *)NULL); } -/* 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 pointer to the function implementing builtin command NAME. */ Function * find_shell_builtin (name) - char *name; + char *name; { - return (builtin_address_internal (name, 0)); + current_builtin = builtin_address_internal (name, 0); + return (current_builtin ? current_builtin->function : (Function *)NULL); } -/* Return the address of builtin with NAME, irregardless of its state of - enableness. */ +/* Return the address of builtin with NAME, whether it is enabled or not. */ Function * builtin_address (name) char *name; { - return (builtin_address_internal (name, 1)); + current_builtin = builtin_address_internal (name, 1); + return (current_builtin ? current_builtin->function : (Function *)NULL); } +/* Return the function implementing the builtin NAME, but only if it is a + POSIX.2 special builtin. */ +Function * +find_special_builtin (name) + char *name; +{ + current_builtin = builtin_address_internal (name, 0); + return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ? + current_builtin->function : + (Function *)NULL); +} + static int shell_builtin_compare (sbp1, sbp2) struct builtin *sbp1, *sbp2; @@ -765,49 +755,56 @@ initialize_shell_builtins () shell_builtin_compare); } -/* Return a new string which is the quoted version of STRING. This is used - by alias and trap. */ +/* **************************************************************** */ +/* */ +/* Functions for quoting strings to be re-read as input */ +/* */ +/* **************************************************************** */ + +/* Return a new string which is the single-quoted version of STRING. + Used by alias and trap, among others. */ char * single_quote (string) char *string; { - register int i, j, c; - char *result; - - result = (char *)xmalloc (3 + (3 * strlen (string))); + register int c; + char *result, *r, *s; - result[0] = '\''; + result = (char *)xmalloc (3 + (4 * strlen (string))); + r = result; + *r++ = '\''; - for (i = 0, j = 1; string && (c = string[i]); i++) + for (s = string; s && (c = *s); s++) { - result[j++] = c; + *r++ = c; if (c == '\'') { - result[j++] = '\\'; /* insert escaped single quote */ - result[j++] = '\''; - result[j++] = '\''; /* start new quoted string */ + *r++ = '\\'; /* insert escaped single quote */ + *r++ = '\''; + *r++ = '\''; /* start new quoted string */ } } - result[j++] = '\''; - result[j] = '\0'; + *r++ = '\''; + *r = '\0'; return (result); } +/* Quote STRING using double quotes. Return a new string. */ char * double_quote (string) char *string; { - register int i, j, c; - char *result; - - result = (char *)xmalloc (3 + (3 * strlen (string))); + register int c; + char *result, *r, *s; - result[0] = '"'; + result = (char *)xmalloc (3 + (2 * strlen (string))); + r = result; + *r++ = '"'; - for (i = 0, j = 1; string && (c = string[i]); i++) + for (s = string; s && (c = *s); s++) { switch (c) { @@ -815,15 +812,86 @@ double_quote (string) case '$': case '`': case '\\': - result[j++] = '\\'; + *r++ = '\\'; default: - result[j++] = c; + *r++ = c; break; } } - result[j++] = '"'; - result[j] = '\0'; + *r++ = '"'; + *r = '\0'; return (result); } + +/* Quote special characters in STRING using backslashes. Return a new + string. */ +char * +backslash_quote (string) + char *string; +{ + int c; + char *result, *r, *s; + + result = xmalloc (2 * strlen (string) + 1); + + for (r = result, s = string; s && (c = *s); s++) + { + switch (c) + { + case ' ': case '\t': case '\n': /* IFS white space */ + case '\'': case '"': case '\\': /* quoting chars */ + case '|': case '&': case ';': /* shell metacharacters */ + case '(': case ')': case '<': case '>': + case '!': case '{': case '}': /* reserved words */ + case '*': case '[': case '?': case ']': /* globbing chars */ + case '^': + case '$': case '`': /* expansion chars */ + *r++ = '\\'; + *r++ = c; + break; + case '#': /* comment char */ + if (s == string) + *r++ = '\\'; + /* FALLTHROUGH */ + default: + *r++ = c; + break; + } + } + + *r = '\0'; + return (result); +} + +int +contains_shell_metas (string) + char *string; +{ + char *s; + + for (s = string; s && *s; s++) + { + switch (*s) + { + case ' ': case '\t': case '\n': /* IFS white space */ + case '\'': case '"': case '\\': /* quoting chars */ + case '|': case '&': case ';': /* shell metacharacters */ + case '(': case ')': case '<': case '>': + case '!': case '{': case '}': /* reserved words */ + case '*': case '[': case '?': case ']': /* globbing chars */ + case '^': + case '$': case '`': /* expansion chars */ + return (1); + case '#': + if (s == string) /* comment char */ + return (1); + /* FALLTHROUGH */ + default: + break; + } + } + + return (0); +} diff --git a/builtins/common.h b/builtins/common.h index c7c99e7..ce831f7 100644 --- a/builtins/common.h +++ b/builtins/common.h @@ -21,23 +21,23 @@ #if !defined (__COMMON_H) # define __COMMON_H +#include "../stdc.h" + #define ISOPTION(s, c) (s[0] == '-' && !s[2] && s[1] == c) -extern void builtin_error (); +extern void builtin_error __P((const char *, ...)); +extern void builtin_usage (); extern void bad_option (); -extern int get_numeric_arg (); +extern char **make_builtin_argv (); +extern int get_numeric_arg (); extern void remember_args (); - extern void no_args (); +extern int no_options (); extern int read_octal (); -extern char *find_hashed_filename (); -extern void remove_hashed_filename (); -extern void remember_filename (); - extern void push_context (), pop_context (); extern void push_dollar_vars (), pop_dollar_vars (); extern void dispose_saved_dollar_vars (); @@ -53,17 +53,55 @@ extern void set_working_directory (); extern int get_job_spec (); #endif -extern int parse_and_execute (); -extern void parse_and_execute_cleanup (); - -extern void initialize_shell_builtins (); +extern int display_signal_list (); /* 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 (); extern Function *find_shell_builtin (); extern Function *builtin_address (); +extern Function *find_special_builtin (); + +extern void initialize_shell_builtins (); extern char *single_quote (); extern char *double_quote (); +extern char *backslash_quote (); +extern int contains_shell_metas (); + +/* Functions from hash.def */ +extern void initialize_filename_hashing (); +extern void flush_hashed_filenames (); +extern char *find_hashed_filename (); +extern void remove_hashed_filename (); +extern void remember_filename (); + +/* Functions from set.def */ +extern void initialize_shell_options (); +extern void list_minus_o_opts (); +extern int set_minus_o_option (); +extern int minus_o_option_value (); + +/* Functions from type.def */ +extern int describe_command (); + +/* Functions from setattr.def */ +extern int set_or_show_attributes (); +extern int show_var_attributes (); +extern int show_name_attributes (); +extern void set_var_attribute (); + +/* Functions from pushd.def */ +extern char *get_dirstack_element (); +extern void set_dirstack_element (); +extern WORD_LIST *get_directory_stack (); + +/* Functions from evalstring.c */ +extern int parse_and_execute (); +extern void parse_and_execute_cleanup (); + +/* Functions from evalfile.c */ +extern int maybe_execute_file __P((char *, int)); +extern int source_file __P((char *)); #endif /* !__COMMON_H */ diff --git a/builtins/declare.def b/builtins/declare.def index 17b7ea2..68514d3 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -23,39 +23,50 @@ $PRODUCES declare.c $BUILTIN declare $FUNCTION declare_builtin -$SHORT_DOC declare [-[frxi]] name[=value] ... +$SHORT_DOC declare [-afFrxi] [-p] name[=value] ... Declare variables and/or give them attributes. If no NAMEs are -given, then display the values of variables instead. +given, then display the values of variables instead. The -p option +will display the attributes and values of each NAME. The flags are: - -f to select from among function names only, - -r to make NAMEs readonly, - -x to make NAMEs export, - -i to make NAMEs have the `integer' attribute set. + -a to make NAMEs arrays (if supported) + -f to select from among function names only + -F to display function names without definitions + -r to make NAMEs readonly + -x to make NAMEs export + -i to make NAMEs have the `integer' attribute set Variables with the integer attribute have arithmetic evaluation (see `let') done when the variable is assigned to. +When displaying values of variables, -f displays a function's name +and definition. The -F option restricts the display to function +name only. + Using `+' instead of `-' turns off the given attribute instead. When used in a function, makes NAMEs local, as with the `local' command. $END $BUILTIN typeset $FUNCTION declare_builtin -$SHORT_DOC typeset [-[frxi]] name[=value] ... +$SHORT_DOC typeset [-afFrxi] [-p] name[=value] ... Obsolete. See `declare'. $END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include "../bashansi.h" #include "../shell.h" +#include "common.h" +#include "builtext.h" extern int variable_context, array_needs_making; @@ -84,7 +95,7 @@ local_builtin (list) return (declare_internal (list, 1)); else { - builtin_error ("Can only be used in a function"); + builtin_error ("can only be used in a function"); return (EXECUTION_FAILURE); } } @@ -95,14 +106,14 @@ declare_internal (list, local_var) register WORD_LIST *list; int local_var; { - int flags_on = 0, flags_off = 0; - int any_failed = 0; + int flags_on, flags_off, *flags, any_failed, assign_error, pflag, nodefs; + char *t; + SHELL_VAR *var; + flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0; while (list) { - register char *t = list->word->word; - int *flags; - + t = list->word->word; if (t[0] == '-' && t[1] == '-' && t[2] == '\0') { list = list->next; @@ -112,16 +123,18 @@ declare_internal (list, local_var) if (*t != '+' && *t != '-') break; - if (*t == '+') - flags = &flags_off; - else - flags = &flags_on; - - t++; + flags = (*t++ == '+') ? &flags_off : &flags_on; while (*t) { - if (*t == 'f') + if (*t == 'p' && local_var == 0) + pflag++, t++; + else if (*t == 'F') + { + nodefs++; + *flags |= att_function; t++; + } + else if (*t == 'f') *flags |= att_function, t++; else if (*t == 'x') *flags |= att_exported, t++, array_needs_making = 1; @@ -129,9 +142,14 @@ declare_internal (list, local_var) *flags |= att_readonly, t++; else if (*t == 'i') *flags |= att_integer, t++; +#if defined (ARRAY_VARS) + else if (*t == 'a') + *flags |= att_array, t++; +#endif else { builtin_error ("unknown option: `-%c'", *t); + builtin_usage (); return (EX_USAGE); } } @@ -141,7 +159,7 @@ declare_internal (list, local_var) /* If there are no more arguments left, then we just want to show some variables. */ - if (!list) + if (list == 0) /* declare -[afFirx] */ { /* Show local variables defined at this context level if this is the `local' builtin. */ @@ -162,25 +180,45 @@ declare_internal (list, local_var) } else { - if (!flags_on) + if (flags_on == 0) set_builtin ((WORD_LIST *)NULL); else - set_or_show_attributes ((WORD_LIST *)NULL, flags_on); + set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs); } fflush (stdout); return (EXECUTION_SUCCESS); } + if (pflag) /* declare -p [-afFirx] name [name...] */ + { + for (any_failed = 0; list; list = list->next) + { + pflag = show_name_attributes (list->word->word, nodefs); + if (pflag) + { + builtin_error ("%s: not found", list->word->word); + any_failed++; + } + } + return (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) + while (list) /* declare [-afFirx] name [name ...] */ { - char *value, *name = savestring (list->word->word); - int offset = assignment (name); + char *value, *name; + int offset; +#if defined (ARRAY_VARS) + int making_array_special, assigning_array_special; +#endif + + name = savestring (list->word->word); + offset = assignment (name); - if (offset) + if (offset) /* declare [-afFirx] name=value */ { name[offset] = '\0'; value = name + offset + 1; @@ -188,10 +226,21 @@ declare_internal (list, local_var) else value = ""; +#if defined (ARRAY_VARS) + assigning_array_special = 0; + if (t = strchr (name, '[')) + { + *t = '\0'; + making_array_special = 1; + } + else + making_array_special = 0; +#endif + if (legal_identifier (name) == 0) { - builtin_error ("%s: not a legal variable name", name); - any_failed++; + builtin_error ("`%s': not a valid identifier", name); + assign_error++; NEXT_VARIABLE (); } @@ -200,7 +249,14 @@ declare_internal (list, local_var) not global ones. */ if (variable_context) - make_local_variable (name); + { +#if defined (ARRAY_VARS) + if ((flags_on & att_array) || making_array_special) + make_local_array_variable (name); + else +#endif + make_local_variable (name); + } /* 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'. */ @@ -210,36 +266,35 @@ declare_internal (list, local_var) if (flags_on & att_function) { - if (offset) + if (offset) /* declare -f [-rix] foo=bar */ { builtin_error ("Can't use `-f' to make functions"); return (EXECUTION_FAILURE); } - else + else /* declare -f [-rx] name [name...] */ { - SHELL_VAR *find_function (), *funvar; - - funvar = find_function (name); + var = find_function (name); - if (funvar) + if (var) { - if (readonly_p (funvar) && (flags_off & att_readonly)) + 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) { - char *result = named_function_string - (name, (COMMAND *)function_cell (funvar), 1); - printf ("%s\n", result); + t = nodefs ? var->name + : named_function_string (name, function_cell (var), 1); + printf ("%s\n", t); } - else + else /* declare -[fF] -[rx] name [name...] */ { - funvar->attributes |= flags_on; - funvar->attributes &= ~flags_off; + var->attributes |= flags_on; + var->attributes &= ~flags_off; } } else @@ -247,15 +302,21 @@ declare_internal (list, local_var) NEXT_VARIABLE (); } } - else + else /* declare -[airx] name [name...] */ { - SHELL_VAR *var; - var = find_variable (name); - if (!var) - var = bind_variable (name, ""); + if (var == 0) + { +#if defined (ARRAY_VARS) + if ((flags_on & att_array) || making_array_special) + var = make_new_array_variable (name); + else +#endif + var = bind_variable (name, ""); + } + /* Cannot use declare +r to turn off readonly attribute. */ if (readonly_p (var) && (flags_off & att_readonly)) { builtin_error ("%s: readonly variable", name); @@ -263,22 +324,55 @@ declare_internal (list, local_var) NEXT_VARIABLE (); } + /* Cannot use declare to assign value to readonly variable. */ + if (readonly_p (var) && offset) + { + builtin_error ("%s: readonly variable", name); + assign_error++; + NEXT_VARIABLE (); + } + +#if defined (ARRAY_VARS) + /* declare -a name=value does not work; declare name=value when + name is already an array does not work. */ + if ((making_array_special || (flags_on & att_array) || array_p (var)) && offset) + { + if (value[0] == '(' && strchr (value, ')')) + assigning_array_special = 1; + else + { + builtin_error ("%s: cannot assign to array variables in this way", name); + assign_error++; + NEXT_VARIABLE (); + } + } + + /* Cannot use declare +a name to remove an array variable. */ + if ((flags_off & att_array) && array_p (var)) + { + builtin_error ("%s: cannot destroy array variables in this way", name); + any_failed++; + NEXT_VARIABLE (); + } + + /* declare -a name makes name an array variable. */ + if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0) + var = convert_var_to_array (var); +#endif /* ARRAY_VARS */ + var->attributes |= flags_on; var->attributes &= ~flags_off; +#if defined (ARRAY_VARS) + if (offset && assigning_array_special) + assign_array_var_from_string (var, value); + else +#endif if (offset) { - free (var->value); - if (integer_p (var)) - { - long val, evalexp (); - char *itos (); - - val = evalexp (value); - var->value = itos ((int)val); - } - else - var->value = savestring (value); + t = make_variable_value (var, value); + FREE (var->value); + var->value = t; } } @@ -286,5 +380,8 @@ declare_internal (list, local_var) NEXT_VARIABLE (); } - return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); + + return (assign_error ? EX_BADASSIGN + : ((any_failed == 0) ? EXECUTION_SUCCESS + : EXECUTION_FAILURE)); } diff --git a/builtins/echo.def b/builtins/echo.def index 7539103..74ca0f4 100644 --- a/builtins/echo.def +++ b/builtins/echo.def @@ -20,6 +20,12 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES echo.c +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> #include "../shell.h" @@ -33,6 +39,7 @@ following backslash-escaped characters is turned on: \a alert (bell) \b backspace \c suppress trailing newline + \E escape character \f form feed \n new line \r carriage return @@ -60,109 +67,91 @@ $END /* 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. */ + echo syntax from Version 9 Unix systems. */ +int echo_builtin (list) WORD_LIST *list; { - int display_return = 1, do_v9 = 0; + int display_return, do_v9, i; + char *temp; #if defined (DEFAULT_ECHO_TO_USG) /* 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. */ do_v9 = 1; +#else + do_v9 = 0; #endif /* DEFAULT_ECHO_TO_USG */ - while (list && list->word->word[0] == '-') - { - register char *temp; - register int i; + display_return = 1; + 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 = &(list->word->word[1]); + temp++; for (i = 0; temp[i]; i++) { if (strchr (VALID_ECHO_OPTIONS, temp[i]) == 0) - goto just_echo; + break; } - if (!*temp) - goto just_echo; + /* 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 (*temp) + while (i = *temp++) { - if (*temp == 'n') - display_return = 0; + switch (i) + { + case 'n': + display_return = 0; + break; #if defined (V9_ECHO) - else if (*temp == 'e') - do_v9 = 1; - else if (*temp == 'E') - do_v9 = 0; + case 'e': + do_v9 = 1; + break; + case 'E': + do_v9 = 0; + break; #endif /* V9_ECHO */ - else - goto just_echo; - - temp++; + default: + goto just_echo; /* XXX */ + } } - list = list->next; } just_echo: - if (list) + while (list) { -#if defined (V9_ECHO) - if (do_v9) + i = 0; + temp = do_v9 ? ansicstr (list->word->word, STRLEN (list->word->word), &i) + : list->word->word; + if (temp) { - while (list) - { - register char *s = list->word->word; - register int c; - - while (c = *s++) - { - if (c == '\\' && *s) - { - switch (c = *s++) - { - case 'a': c = '\007'; break; - case 'b': c = '\b'; break; - case 'c': display_return = 0; continue; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = (int) 0x0B; break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c -= '0'; - if (*s >= '0' && *s <= '7') - c = c * 8 + (*s++ - '0'); - if (*s >= '0' && *s <= '7') - c = c * 8 + (*s++ - '0'); - break; - case '\\': break; - default: putchar ('\\'); break; - } - } - putchar(c); - } - list = list->next; - if (list) - putchar(' '); - } + printf ("%s", temp); + fflush (stdout); /* Fix for bug in SunOS 5.5 printf(3) */ } - else -#endif /* V9_ECHO */ - print_word_list (list, " "); + if (do_v9 && temp) + free (temp); + list = list->next; + if (i) + { + display_return = 0; + break; + } + if (list) + putchar(' '); } + if (display_return) - printf ("\n"); + putchar ('\n'); fflush (stdout); return (EXECUTION_SUCCESS); } diff --git a/builtins/enable.def b/builtins/enable.def index 2aeae39..37c4f2b 100644 --- a/builtins/enable.def +++ b/builtins/enable.def @@ -23,20 +23,54 @@ $PRODUCES enable.c $BUILTIN enable $FUNCTION enable_builtin -$SHORT_DOC enable [-n] [name ...] +$SHORT_DOC enable [-pnds] [-a] [-f filename] [name ...] Enable and disable builtin shell commands. This allows you to use a disk command which has the same name as a shell -builtin. If -n is used, the NAMEs become disabled. Otherwise +builtin. If -n is used, the NAMEs become disabled; otherwise NAMEs are enabled. For example, to use the `test' found on your -path instead of the shell builtin version, you type `enable -n test'. +path instead of the shell builtin version, type `enable -n test'. +On systems supporting dynamic loading, the -f option may be used +to load new builtins from the shared object FILENAME. The -d +option will delete a builtin previously loaded with -f. If no +non-option names are given, or the -p option is supplied, a list +of builtins is printed. The -a option means to print every builtin +with an indication of whether or not it is enabled. The -s option +restricts the output to the Posix.2 `special' builtins. The -n +option displays a list of all disabled builtins. $END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashansi.h" #include "../shell.h" #include "../builtins.h" +#include "../flags.h" #include "common.h" +#include "bashgetopt.h" + +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) +static int dyn_load_builtin (); +#endif + +#if defined (HAVE_DLCLOSE) +static int dyn_unload_builtin (); +#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 static int enable_shell_command (); static void list_some_builtins (); @@ -44,61 +78,117 @@ static void list_some_builtins (); /* 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 = 0, any_failed = 0; - int disable_p, all_p; + int result, flags; + int opt, filter; +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) + char *filename; +#endif - disable_p = all_p = 0; + result = EXECUTION_SUCCESS; + flags = 0; - while (list && list->word->word && list->word->word[0] == '-') + reset_internal_getopt (); + while ((opt = internal_getopt (list, "adnpsf:")) != -1) { - char *arg = list->word->word; - - list = list->next; - - if (ISOPTION (arg, 'n')) - disable_p = 1; - else if (arg[1] == 'a' && (arg[2] == 0 || strcmp (arg + 2, "ll") == 0)) - all_p = 1; - else if (ISOPTION (arg, '-')) - break; - else + switch (opt) { - bad_option (arg); - return (EXECUTION_FAILURE); + 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); } } - if (!list) + list = loptend; + +#if defined (RESTRICTED_SHELL) + /* Restricted shells cannot load new builtins. */ + if (restricted && (flags & (FFLAG|DFLAG))) { - int filter; + builtin_error ("restricted"); + return (EXECUTION_FAILURE); + } +#endif - if (all_p) - filter = ENABLED | DISABLED; - else if (disable_p) - filter = DISABLED; - else - filter = ENABLED; + 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); + } +#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; + } + } +#endif else { while (list) { - result = enable_shell_command (list->word->word, disable_p); + opt = enable_shell_command (list->word->word, flags & NFLAG); - if (!result) + if (opt == EXECUTION_FAILURE) { builtin_error ("%s: not a shell builtin", list->word->word); - any_failed++; + result = EXECUTION_FAILURE; } list = list->next; } } - return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); + return (result); } /* List some builtins. @@ -111,19 +201,18 @@ list_some_builtins (filter) for (i = 0; i < num_shell_builtins; i++) { - if (!shell_builtins[i].function) + if (shell_builtins[i].function == 0 || (shell_builtins[i].flags & BUILTIN_DELETED)) continue; - if ((filter & ENABLED) && - (shell_builtins[i].flags & BUILTIN_ENABLED)) - { - printf ("enable %s\n", shell_builtins[i].name); - } + 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); - } + printf ("enable -n %s\n", shell_builtins[i].name); } } @@ -134,23 +223,202 @@ enable_shell_command (name, disable_p) char *name; int disable_p; { - register int i; - int found = 0; + struct builtin *b; - for (i = 0; i < num_shell_builtins; i++) + b = builtin_address_internal (name, 1); + if (b == 0) + return (EXECUTION_FAILURE); + + if (disable_p) + b->flags &= ~BUILTIN_ENABLED; + else + b->flags |= BUILTIN_ENABLED; + + return (EXECUTION_SUCCESS); +} + +#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) +#include <dlfcn.h> + +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) { - if (!shell_builtins[i].function) - continue; + builtin_error ("cannot open shared object %s: %s", filename, dlerror ()); + return (EXECUTION_FAILURE); + } - if (STREQ (name, shell_builtins[i].name)) - { - found++; + for (new = 0, l = list; l; l = l->next, new++) + ; + new_builtins = (struct builtin **)xmalloc (new * sizeof (struct builtin *)); - if (disable_p) - shell_builtins[i].flags &= ~BUILTIN_ENABLED; - else - shell_builtins[i].flags |= BUILTIN_ENABLED; + /* 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 = 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); } - return (found); + + 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 = (Function *)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 */ + ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin); + 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; +} + +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) + { + builtin_error ("%s: not a shell builtin", 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 && dlclose (handle) != 0) + { + builtin_error ("cannot delete %s: %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 index 5c3eda8..a394896 100644 --- a/builtins/eval.def +++ b/builtins/eval.def @@ -27,19 +27,24 @@ $SHORT_DOC eval [arg ...] Read ARGs as input to the shell and execute the resulting command(s). $END +#include <config.h> +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + #include "../shell.h" +#include "bashgetopt.h" + +extern int parse_and_execute (); /* Parse the string that these words make, and execute the command found. */ int eval_builtin (list) WORD_LIST *list; { - int result; + if (no_options (list)) + return (EX_USAGE); /* Note that parse_and_execute () frees the string it is passed. */ - if (list) - result = parse_and_execute (string_list (list), "eval", -1); - else - result = EXECUTION_SUCCESS; - return (result); + return (list ? parse_and_execute (string_list (list), "eval", -1) : EXECUTION_SUCCESS); } diff --git a/builtins/evalfile.c b/builtins/evalfile.c new file mode 100644 index 0000000..14fbc4d --- /dev/null +++ b/builtins/evalfile.c @@ -0,0 +1,211 @@ +/* Copyright (C) 1996 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 1, 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; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <sys/types.h> +#include "../posixstat.h" +#include "../filecntl.h" + +#include <stdio.h> +#include <signal.h> +#include <errno.h> + +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../flags.h" +#include "../input.h" +#include "../execute_cmd.h" + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#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 + +extern int interactive, interactive_shell, posixly_correct; +extern int indirection_level, startup_state, 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) + char *filename; + int flags; +{ + volatile int old_interactive; + procenv_t old_return_catch; + int return_val, fd, result; + char *string; + struct stat finfo; + VFunction *errfunc; + + 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 = (VFunction *)((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 (S_ISREG (finfo.st_mode) == 0) + { + (*errfunc) ("%s: not a regular file", filename); + return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1); + } + + string = xmalloc (1 + (int)finfo.st_size); + result = read (fd, string, finfo.st_size); + string[result] = '\0'; + + return_val = errno; + close (fd); + errno = return_val; + + if (result != (int)finfo.st_size) + { + free (string); + goto file_error_and_exit; + } + + if (check_binary_file ((unsigned char *)string, (result > 80) ? 80 : result)) + { + 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 (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, -1); + + 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); + } + + return ((flags & FEVAL_BUILTIN) ? result : 1); +} + +int +maybe_execute_file (fname, force_noninteractive) + char *fname; + int force_noninteractive; +{ + char *filename; + int result, flags; + + filename = bash_tilde_expand (fname); + flags = FEVAL_ENOENTOK; + if (force_noninteractive) + flags |= FEVAL_NONINT; + result = _evalfile (filename, flags); + free (filename); + return result; +} + +int +source_file (filename) + char *filename; +{ + int flags; + + flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT; + /* POSIX shells exit if non-interactive and file error. */ + if (posixly_correct && !interactive_shell) + flags |= FEVAL_LONGJMP; + return (_evalfile (filename, flags)); +} diff --git a/builtins/evalstring.c b/builtins/evalstring.c new file mode 100644 index 0000000..2e84068 --- /dev/null +++ b/builtins/evalstring.c @@ -0,0 +1,228 @@ +/* Copyright (C) 1996 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 1, 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; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <signal.h> + +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../flags.h" +#include "../input.h" +#include "../execute_cmd.h" + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#include "common.h" + +extern int interactive, interactive_shell; +extern int indirection_level, startup_state, subshell_environment; +extern int line_number; +extern int last_command_exit_value; +extern int running_trap; +extern COMMAND *global_command; + +int parse_and_execute_level = 0; + +/* 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 (); + } + run_unwind_frame ("parse_and_execute_top"); +} + +/* Parse and execute the commands in STRING. Returns whatever + execute_command () returns. This frees STRING. INTERACT is + the new value for `interactive' while the commands are being + executed. A value of -1 means don't change it. */ +int +parse_and_execute (string, from_file, interact) + char *string; + char *from_file; + int interact; +{ + int code; + volatile int should_jump_to_top_level, last_result; + char *orig_string; + COMMAND *volatile command; + + orig_string = string; + /* Unwind protect this invocation of parse_and_execute (). */ + begin_unwind_frame ("parse_and_execute_top"); + unwind_protect_int (parse_and_execute_level); + unwind_protect_jmp_buf (top_level); + unwind_protect_int (indirection_level); + unwind_protect_int (line_number); + if (interact != -1 && interactive != interact) + unwind_protect_int (interactive); + +#if defined (HISTORY) + if (interactive_shell) + { + unwind_protect_int (remember_on_history); +# if defined (BANG_HISTORY) + unwind_protect_int (history_expansion_inhibited); +# endif /* BANG_HISTORY */ + } +#endif /* HISTORY */ + + add_unwind_protect (pop_stream, (char *)NULL); + if (orig_string) + add_unwind_protect (xfree, orig_string); + end_unwind_frame (); + + parse_and_execute_level++; + push_stream (1); /* reset the line number */ + indirection_level++; + if (interact != -1) + interactive = interact; + +#if defined (HISTORY) + bash_history_disable (); +#endif /* HISTORY */ + + code = should_jump_to_top_level = 0; + last_result = EXECUTION_SUCCESS; + command = (COMMAND *)NULL; + + with_input_from_string (string, from_file); + while (*(bash_input.location.string)) + { + 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 EXITPROG: + 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: + run_unwind_frame ("pe_dispose"); + last_command_exit_value = 1; /* XXX */ + if (subshell_environment) + { + should_jump_to_top_level = 1; + goto out; + } + else + { + dispose_command (command); /* XXX */ + continue; + } + + default: + programming_error ("parse_and_execute: bad jump: code %d", code); + break; + } + } + + if (parse_command () == 0) + { + if (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); + + global_command = (COMMAND *)NULL; + +#if defined (ONESHOT) + if (startup_state == 2 && *bash_input.location.string == '\0' && + command->type == cm_simple && !command->redirects && + !command->value.Simple->redirects) + { + command->flags |= CMD_NO_FORK; + command->value.Simple->flags |= CMD_NO_FORK; + } +#endif /* ONESHOT */ + + 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; + + /* Since we are shell compatible, syntax errors in a script + abort the execution of the script. Right? */ + break; + } + } + + out: + + run_unwind_frame ("parse_and_execute_top"); + + 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); +} diff --git a/builtins/exec.def b/builtins/exec.def index f950afc..c14af75 100644 --- a/builtins/exec.def +++ b/builtins/exec.def @@ -23,141 +23,190 @@ $PRODUCES exec.c $BUILTIN exec $FUNCTION exec_builtin -$SHORT_DOC exec [ [-] file [redirection ...]] +$SHORT_DOC exec [-cl] [-a name] file [redirection ...] Exec FILE, replacing this shell with the specified program. If FILE is not specified, the redirections take effect in this -shell. If the first argument is `-', then place a dash in the -zeroth arg passed to FILE. If the file cannot be exec'ed and -the shell is not interactive, then the shell exits, unless the -shell variable "no_exit_on_failed_exec" exists. +shell. If the first argument is `-l', then place a dash in the +zeroth arg passed to FILE, as login does. If the `-c' option +is supplied, FILE is executed with a null environment. The `-a' +option means to make set argv[0] of the executed process to NAME. +If the file cannot be executed and the shell is not interactive, +then the shell exits, unless the shell option `execfail' is set. $END -#include "../shell.h" +#include <config.h> + #include <sys/types.h> #include "../posixstat.h" #include <signal.h> #include <errno.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" + +#include "../shell.h" #include "../execute_cmd.h" -#include "common.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 interactive, 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 = 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; - maybe_make_export_env (); + 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) + if (list == 0) return (EXECUTION_SUCCESS); - else + +#if defined (RESTRICTED_SHELL) + if (restricted) { - /* Otherwise, execve the new command with args. */ - char *command, **args; - int dash_name = 0; + builtin_error ("restricted"); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ - if (list->word->word[0] == '-' && !list->word->word[1]) - { - /* The user would like to exec this command as if it was a - login command. Do so. */ - list = list->next; - dash_name++; - } + args = word_list_to_argv (list, 1, 0, (int *)NULL); - if (!list) - return (EXECUTION_SUCCESS); + /* 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 defined (RESTRICTED_SHELL) - if (restricted) - { - builtin_error ("restricted"); - return (EXECUTION_FAILURE); - } -#endif /* RESTRICTED_SHELL */ + if (command == 0) + { + builtin_error ("%s: not found", args[0]); + exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */ + goto failed_exec; + } - args = make_word_array (list); + command = full_pathname (command); - /* A command with a slash anywhere in its name is not looked up in - the search path. */ - if (absolute_program (args[0])) - command = args[0]; - else - command = find_user_command (args[0]); - if (!command) - { - builtin_error ("%s: not found", args[0]); - exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */ - goto failed_exec; - } + 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; + } - command = full_pathname (command); - /* If the user wants this to look like a login shell, then - prepend a `-' onto the first argument (argv[0]). */ - if (dash_name) - { - char *new_name = xmalloc (2 + strlen (args[0])); - new_name[0] = '-'; - strcpy (new_name + 1, args[0]); - free (args[0]); - args[0] = new_name; - } + /* 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); - /* 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. */ - adjust_shell_level (-1); + if (cleanenv) + env = (char **)NULL; + else + { maybe_make_export_env (); + env = export_env; + } #if defined (HISTORY) - maybe_save_shell_history (); + maybe_save_shell_history (); #endif /* HISTORY */ - restore_original_signals (); + + restore_original_signals (); #if defined (JOB_CONTROL) - if (subshell_environment == 0) - end_job_control (); + if (subshell_environment == 0) + end_job_control (); #endif /* JOB_CONTROL */ - shell_execve (command, args, export_env); - - adjust_shell_level (1); + shell_execve (command, args, env); + if (cleanenv == 0) + adjust_shell_level (1); - if (!executable_file (command)) - { - builtin_error ("%s: cannot execute: %s", command, strerror (errno)); - exit_value = EX_NOEXEC; /* As per Posix.2, 3.14.6 */ - } - else - file_error (command); + 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: - if (command) - free (command); +failed_exec: + if (command) + free (command); - if (subshell_environment || - (!interactive && !find_variable ("no_exit_on_failed_exec"))) - exit (exit_value); + if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0)) + exit_shell (exit_value); - initialize_traps (); - reinitialize_signals (); + initialize_traps (); + reinitialize_signals (); #if defined (JOB_CONTROL) - restart_job_control (); + restart_job_control (); #endif /* JOB_CONTROL */ - return (exit_value); - } + return (exit_value); } diff --git a/builtins/exit.def b/builtins/exit.def index 25a3b63..23a89ca 100644 --- a/builtins/exit.def +++ b/builtins/exit.def @@ -1,5 +1,5 @@ This file is exit.def, from which is created exit.c. -It implements the builtins "exit" and "logout" in Bash. +It implements the builtins "exit", and "logout" in Bash. Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. @@ -28,18 +28,26 @@ Exit the shell with a status of N. If N is omitted, the exit status is that of the last command executed. $END -#include <stdio.h> +#include <config.h> + #include <sys/types.h> +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" #include "../jobs.h" +#include "common.h" #include "builtext.h" /* for jobs_builtin */ extern int interactive, login_shell; extern int last_command_exit_value; static int exit_or_logout (); -static int sourced_logout = 0; +static int sourced_logout; int exit_builtin (list) @@ -65,9 +73,9 @@ int logout_builtin (list) WORD_LIST *list; { - if (!login_shell && interactive) + if (login_shell == 0 && interactive) { - builtin_error ("Not login shell: use `exit'"); + builtin_error ("not login shell: use `exit'"); return (EXECUTION_FAILURE); } else @@ -87,7 +95,7 @@ exit_or_logout (list) #if defined (JOB_CONTROL) int exit_immediate_okay; - exit_immediate_okay = (!interactive || + exit_immediate_okay = (interactive == 0 || last_shell_builtin == exit_builtin || last_shell_builtin == logout_builtin || last_shell_builtin == jobs_builtin); @@ -97,7 +105,7 @@ exit_or_logout (list) { register int i; for (i = 0; i < job_slots; i++) - if (jobs[i] && (jobs[i]->state == JSTOPPED)) + if (jobs[i] && STOPPED (i)) { fprintf (stderr, "There are stopped jobs.\n"); @@ -113,17 +121,20 @@ exit_or_logout (list) /* Get return value if present. This means that you can type `logout 5' to a shell, and it returns 5. */ - if (list) - exit_value = get_numeric_arg (list); - else - exit_value = last_command_exit_value; + exit_value = list ? get_numeric_arg (list) : last_command_exit_value; /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */ if (login_shell && sourced_logout++ == 0) - maybe_execute_file ("~/.bash_logout", 1); + { + maybe_execute_file ("~/.bash_logout", 1); +#ifdef SYS_BASH_LOGOUT + maybe_execute_file (SYS_BASH_LOGOUT, 1); +#endif + } last_command_exit_value = exit_value; /* Exit the program. */ - longjmp (top_level, EXITPROG); + jump_to_top_level (EXITPROG); + /*NOTREACHED*/ } diff --git a/builtins/fc.def b/builtins/fc.def index 1f6db68..16eb8d2 100644 --- a/builtins/fc.def +++ b/builtins/fc.def @@ -46,23 +46,32 @@ runs the last command beginning with `cc' and typing `r' re-executes the last command. $END -#include <stdio.h> -#include "../bashansi.h" -#include "../shell.h" +#include <config.h> + #if defined (HISTORY) #include <sys/param.h> -#include <sys/types.h> -#include <sys/stat.h> +#include "bashtypes.h" +#include "posixstat.h" #include <sys/file.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "../bashansi.h" #include <errno.h> + +#include "../shell.h" #include "../builtins.h" #include "../flags.h" #include "../maxpath.h" #include "../bashhist.h" #include <readline/history.h> #include "bashgetopt.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 */ @@ -100,8 +109,8 @@ extern int unlink (); Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's. */ -static char *fc_dosubs (), *fc_replace (), *fc_gethist (), *fc_readline (); -static int fc_gethnum (); +static char *fc_dosubs (), *fc_gethist (), *fc_readline (); +static int fc_gethnum (), fc_number (); static void fc_replhist (), fc_addhist (); /* Data structure describing a list of global replacements to perform. */ @@ -111,8 +120,6 @@ typedef struct repl { char *rep; } REPL; -#define USAGE "usage: fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [command]" - /* Accessors for HIST_ENTRY lists that are called HLIST. */ #define histline(i) (hlist[(i)]->line) #define histdata(i) (hlist[(i)]->data) @@ -143,61 +150,51 @@ fc_builtin (list) int numbering, reverse, listing, execute; int histbeg, histend, last_hist, retval, first, opt; FILE *stream; - REPL *rlist = (REPL *) NULL, *rl; - char *ename = NULL, *command, *newcom, *line; + REPL *rlist, *rl; + char *ename, *command, *newcom, *line; HIST_ENTRY **hlist; - char fn[MAXPATHLEN]; + char fn[64]; numbering = 1; reverse = listing = execute = 0; + ename = (char *)NULL; /* Parse out the options and set which of the two forms we're in. */ - - while (list && *list->word->word == '-') + reset_internal_getopt (); + lcurrent = list; /* XXX */ + while (fc_number (loptend = lcurrent) == 0 && + (opt = internal_getopt (list, ":e:lnrs")) != -1) { - register char *s = &((list->word->word)[1]); + switch (opt) + { + case 'n': + numbering = 0; + break; - if (!isletter (*s)) - break; + case 'l': + listing = 1; + break; - while (opt = *s++) - { - switch (opt) - { - case 'n': - numbering = 0; - break; - - case 'l': - listing = 1; - break; - - case 'r': - reverse = 1; - break; - - case 's': - execute = 1; - break; - - case 'e': - list = list->next; - if (list == NULL) - { - builtin_error (USAGE); - return (EX_USAGE); - } - ename = list->word->word; - break; - - default: - builtin_error (USAGE); - return (EX_USAGE); - } + case 'r': + reverse = 1; + break; + + case 's': + execute = 1; + break; + + case 'e': + ename = list_optarg; + break; + + default: + builtin_usage (); + return (EX_USAGE); } - list = list->next; } + list = loptend; + if (ename && (*ename == '-') && (ename[1] == '\0')) execute = 1; @@ -205,6 +202,7 @@ fc_builtin (list) substitutions). */ if (execute) { + rlist = (REPL *)NULL; while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL)) { *sep++ = '\0'; @@ -227,16 +225,13 @@ fc_builtin (list) to get the replacements in the proper order. */ if (rlist && rlist->next) - rlist = (REPL *) reverse_list ((GENERIC_LIST *) rlist); + rlist = (REPL *)reverse_list ((GENERIC_LIST *) rlist); hlist = history_list (); /* If we still have something in list, it is a command spec. Otherwise, we use the most recent command in time. */ - if (list) - command = fc_gethist (list->word->word, hlist); - else - command = fc_gethist ((char *) NULL, hlist); + command = fc_gethist (list ? list->word->word : (char *)NULL, hlist); if (command == NULL) { @@ -255,8 +250,8 @@ fc_builtin (list) command = newcom; } - printf ("%s\n", command); - fc_replhist (command); /* replace `fc -e -' with command */ + fprintf (stderr, "%s\n", command); + fc_replhist (command); /* replace `fc -s' with command */ return (parse_and_execute (command, "fc", -1)); } @@ -283,12 +278,7 @@ fc_builtin (list) if (list) histend = fc_gethnum (list->word->word, hlist); else - { - if (listing) - histend = last_hist; - else - histend = histbeg; - } + histend = listing ? last_hist : histbeg; } else { @@ -301,10 +291,8 @@ fc_builtin (list) histbeg = 0; } else - { - /* For editing, it is the last history command. */ - histbeg = histend = last_hist; - } + /* For editing, it is the last history command. */ + histbeg = histend = last_hist; } /* We print error messages for line specifications out of range. */ @@ -317,10 +305,10 @@ fc_builtin (list) if (histend < histbeg) { - int t = histend; - + i = histend; histend = histbeg; - histbeg = t; + histbeg = i; + reverse = 1; } @@ -329,40 +317,25 @@ fc_builtin (list) else { numbering = 0; - sprintf (fn, "/tmp/bash%d", (int)time ((long *) 0) + (int)getpid ()); + sprintf (fn, "/tmp/bash%d", (int)time ((time_t *) 0) + (int)getpid ()); stream = fopen (fn, "w"); - if (!stream) + if (stream == 0) { builtin_error ("cannot open temp file %s", fn); return (EXECUTION_FAILURE); } } - if (!reverse) + for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++) { - for (i = histbeg; i <= histend; i++) - { - QUIT; - if (numbering) - fprintf (stream, "%d", i + history_base); - if (listing) - fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); - fprintf (stream, "%s\n", histline (i)); - } - } - else - { - for (i = histend; i >= histbeg; i--) - { - QUIT; - if (numbering) - fprintf (stream, "%d", i + history_base); - if (listing) - fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); - fprintf (stream, "%s\n", histline (i)); - } + QUIT; + if (numbering) + fprintf (stream, "%d", i + history_base); + if (listing) + fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); + fprintf (stream, "%s\n", histline (i)); } if (listing) @@ -381,7 +354,12 @@ fc_builtin (list) command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn)); sprintf (command, "%s %s", FC_EDIT_COMMAND, fn); } - parse_and_execute (command, "fc", -1); + retval = parse_and_execute (command, "fc", -1); + if (retval != EXECUTION_SUCCESS) + { + unlink (fn); + return (EXECUTION_FAILURE); + } /* Now reopen the file and execute the edited commands. */ @@ -435,6 +413,21 @@ fc_builtin (list) 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, (long *)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. */ @@ -572,58 +565,18 @@ fc_dosubs (command, subs) char *command; REPL *subs; { - register char *new = savestring (command); + register char *new, *t; register REPL *r; - for (r = subs; r; r = r->next) + for (new = savestring (command), r = subs; r; r = r->next) { - register char *t; - - t = fc_replace (r->pat, r->rep, new); + t = strsub (new, r->pat, r->rep, 1); free (new); new = t; } return (new); } -/* Replace the occurrences of PAT with REP in COMMAND. - This returns a new string; the caller should free it. */ -static char * -fc_replace (pat, rep, command) - char *pat, *rep, *command; -{ - register int i; - int patlen, replen, templen; - char *new, *temp; - - patlen = strlen (pat); - replen = strlen (rep); - - temp = savestring (command); - templen = strlen (temp); - i = 0; - - for (; (i + patlen) <= templen; i++) - { - if (STREQN (temp + i, pat, patlen)) - { - new = (char *) xmalloc (1 + (replen - patlen) + templen); - - strncpy (new, temp, i); - strncpy (new + i, rep, replen); - strncpy (new + i + replen, - temp + i + patlen, templen - (i + patlen)); - new[templen + (replen - patlen)] = '\0'; /* just in case */ - - free (temp); - temp = new; - i += replen; - templen = strlen (temp); - } - } - return (temp); -} - /* 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 @@ -634,10 +587,9 @@ fc_replhist (command) { register int i; HIST_ENTRY **hlist, *histent, *discard; - char *data; int n; - if (!command || !*command) + if (command == 0 || *command == '\0') return; hlist = history_list (); @@ -665,8 +617,7 @@ fc_replhist (command) discard = remove_history (i); if (discard) { - if (discard->line) - free (discard->line); + FREE (discard->line); free ((char *) discard); } maybe_add_history (command); /* Obeys HISTCONTROL setting. */ diff --git a/builtins/fg_bg.def b/builtins/fg_bg.def index e48af38..49d0200 100644 --- a/builtins/fg_bg.def +++ b/builtins/fg_bg.def @@ -30,10 +30,18 @@ JOB_SPEC is not present, the shell's notion of the current job is used. $END +#include <config.h> + #include <sys/types.h> #include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" #include "../jobs.h" +#include "common.h" #if defined (JOB_CONTROL) extern char *this_command_name; @@ -45,23 +53,23 @@ int fg_builtin (list) WORD_LIST *list; { - int fg_bit = 1; - register WORD_LIST *t = list; + int fg_bit; + register WORD_LIST *t; - if (!job_control) + if (job_control == 0) { builtin_error ("no job control"); return (EXECUTION_FAILURE); } + if (no_options (list)) + return (EX_USAGE); + /* If the last arg on the line is '&', then start this job in the background. Else, fg the job. */ - - while (t && t->next) - t = t->next; - - if (t && t->word->word[0] == '&' && !t->word->word[1]) - fg_bit = 0; + 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)); } @@ -82,12 +90,15 @@ int bg_builtin (list) WORD_LIST *list; { - if (!job_control) + if (job_control == 0) { builtin_error ("no job control"); return (EXECUTION_FAILURE); } + if (no_options (list)) + return (EX_USAGE); + return (fg_bg (list, 0)); } @@ -98,27 +109,27 @@ fg_bg (list, foreground) int foreground; { sigset_t set, oset; - int job, status = EXECUTION_SUCCESS, old_async_pid; + int job, status, old_async_pid; BLOCK_CHILD (set, oset); job = get_job_spec (list); - if (job < 0 || job >= job_slots || !jobs[job]) + if (job < 0 || job >= job_slots || jobs[job] == 0) { if (job != DUP_JOB) - builtin_error ("No such job %s", list ? list->word->word : ""); + builtin_error ("%s: no such job", list ? list->word->word : "current"); goto failure; } /* Or if jobs[job]->pgrp == shell_pgrp. */ - if (!(jobs[job]->flags & J_JOBCONTROL)) + if (IS_JOBCONTROL (job) == 0) { builtin_error ("job %%%d started without job control", job + 1); goto failure; } - if (!foreground) + if (foreground == 0) { old_async_pid = last_asynchronous_pid; last_asynchronous_pid = jobs[job]->pgrp; /* As per Posix.2 5.4.2 */ @@ -134,7 +145,7 @@ fg_bg (list, foreground) } else { - if (!foreground) + if (foreground == 0) last_asynchronous_pid = old_async_pid; failure: diff --git a/builtins/getopt.c b/builtins/getopt.c index 7603430..bc69489 100644 --- a/builtins/getopt.c +++ b/builtins/getopt.c @@ -17,6 +17,12 @@ along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> #include "../memalloc.h" #include "../shell.h" @@ -66,6 +72,9 @@ int sh_opterr = 1; int sh_optopt = '?'; +/* Set to 1 when we see an illegal 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. @@ -102,7 +111,6 @@ sh_getopt (argc, argv, optstring) char *const *argv; const char *optstring; { - int option_index; char c, *temp; sh_optarg = 0; @@ -124,10 +132,18 @@ sh_getopt (argc, argv, optstring) nextchar = (char *)NULL; } + /* Do the increment of `sh_optind' we deferred because the last option + was illegal. */ + if (sh_badopt && (nextchar == 0 || *nextchar == '\0')) + { + sh_badopt = 0; + sh_optind++; + nextchar = (char *)NULL; + } + if (nextchar == 0 || *nextchar == '\0') { - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ + /* If we have done all the ARGV-elements, stop the scan. */ if (sh_optind == argc) return EOF; @@ -158,16 +174,11 @@ sh_getopt (argc, argv, optstring) c = *nextchar++; sh_charindex++; temp = strchr (optstring, c); - /* Increment `sh_optind' when we start to process its last character. */ - if (nextchar == 0 || *nextchar == '\0') - { - sh_optind++; - nextchar = (char *)NULL; - } - sh_optopt = c; - if (temp == NULL || c == ':') + /* If the option is illegal, return an error, but defer updating sh_optind + until the next call so $OPTIND is correct. */ + if (sh_badopt = (temp == NULL || c == ':')) { if (sh_opterr) BADOPT (c); @@ -175,6 +186,13 @@ sh_getopt (argc, argv, optstring) return '?'; } + /* Increment `sh_optind' when we start to process its last character. */ + if (nextchar == 0 || *nextchar == '\0') + { + sh_optind++; + nextchar = (char *)NULL; + } + if (temp[1] == ':') { if (nextchar && *nextchar) @@ -191,6 +209,7 @@ sh_getopt (argc, argv, optstring) NEEDARG (c); sh_optopt = c; + sh_optarg = ""; /* Needed by getopts. */ c = (optstring[0] == ':') ? ':' : '?'; } else diff --git a/builtins/getopt.h b/builtins/getopt.h index fa9878c..7086bb7 100644 --- a/builtins/getopt.h +++ b/builtins/getopt.h @@ -51,6 +51,9 @@ extern int sh_opterr; extern int sh_optopt; +/* Set to 1 when an unrecognized option is encountered. */ +extern int sh_badopt; + extern int sh_getopt (); extern void sh_getopt_restore_state (); diff --git a/builtins/getopts.def b/builtins/getopts.def index 0f2b82f..f3d9aee 100644 --- a/builtins/getopts.def +++ b/builtins/getopts.def @@ -22,7 +22,6 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES getopts.c $BUILTIN getopts -$DEPENDS_ON GETOPTS_BUILTIN $FUNCTION getopts_builtin $SHORT_DOC getopts optstring name [arg] Getopts is used by shell procedures to parse positional parameters. @@ -57,22 +56,24 @@ Getopts normally parses the positional parameters ($0 - $9), but if more arguments are given, they are parsed instead. $END +#include <config.h> + #include <stdio.h> -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif -#include "../shell.h" +#include "../bashansi.h" -#if defined (GETOPTS_BUILTIN) +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" #include "getopt.h" -#define G_EOF (-1) -#define G_ILLEGAL_OPT (-2) -#define G_ARG_MISSING (-3) +#define G_EOF -1 +#define G_ILLEGAL_OPT -2 +#define G_ARG_MISSING -3 extern char *this_command_name; extern WORD_LIST *rest_of_args; @@ -84,6 +85,25 @@ 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); + return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; + } + else + { + builtin_error ("`%s': not a valid identifier", name); + return (EXECUTION_FAILURE); + } } /* Error handling is now performed as specified by Posix.2, draft 11 @@ -118,7 +138,7 @@ dogetopts (argc, argv) int argc; char **argv; { - int ret, special_error, old_opterr = 0, i, n; + 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 */ @@ -126,7 +146,7 @@ dogetopts (argc, argv) if (argc < 3) { - builtin_error("usage: getopts optstring name [arg]"); + builtin_usage (); return (EX_USAGE); } @@ -156,19 +176,19 @@ dogetopts (argc, argv) } else if (rest_of_args == (WORD_LIST *)NULL) { - register int i; - - for (i = 0; i < 10 && dollar_vars[i]; i++); + for (i = 0; i < 10 && dollar_vars[i]; i++) + ; ret = sh_getopt (i, dollar_vars, optstr); } else { - register int i; 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++); + for (i = 0; i < 10 && dollar_vars[i]; i++) + ; + for (words = rest_of_args; words; words = words->next, i++) + ; v = (char **)xmalloc ((i + 1) * sizeof (char *)); for (i = 0; i < 10 && dollar_vars[i]; i++) v[i] = dollar_vars[i]; @@ -203,9 +223,9 @@ dogetopts (argc, argv) /* 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 SH_OPTOPT. If an illegal option was encountered, OPTARG is + is in OPTOPT. If an illegal option was encountered, OPTARG is NULL. If a required option argument was missing, OPTARG points - to a NULL string (that is, optarg[0] == 0). */ + to a NULL string (that is, sh_optarg[0] == 0). */ if (ret == '?') { if (sh_optarg == NULL) @@ -216,26 +236,25 @@ dogetopts (argc, argv) if (ret == G_EOF) { - bind_variable (name, "?"); + getopts_bind_variable (name, "?"); return (EXECUTION_FAILURE); } if (ret == G_ILLEGAL_OPT) { /* Illegal option encountered. */ - strval[0] = '?'; - strval[1] = '\0'; - bind_variable (name, strval); + ret = getopts_bind_variable (name, "?"); if (special_error) { - strval[0] = (char) sh_optopt; + strval[0] = (char)sh_optopt; strval[1] = '\0'; bind_variable ("OPTARG", strval); } else makunbound ("OPTARG", shell_variables); - return (EXECUTION_SUCCESS); + + return (ret); } if (ret == G_ARG_MISSING) @@ -243,31 +262,25 @@ dogetopts (argc, argv) /* Required argument missing. */ if (special_error) { - strval[0] = ':'; - strval[1] = '\0'; - bind_variable (name, strval); + ret = getopts_bind_variable (name, ":"); - strval[0] = (char) sh_optopt; + strval[0] = (char)sh_optopt; strval[1] = '\0'; bind_variable ("OPTARG", strval); } else { - strval[0] = '?'; - strval[1] = '\0'; - bind_variable (name, strval); + ret = getopts_bind_variable (name, "?"); makunbound ("OPTARG", shell_variables); } - return (EXECUTION_SUCCESS); + return (ret); } bind_variable ("OPTARG", sh_optarg); strval[0] = (char) ret; strval[1] = '\0'; - bind_variable (name, strval); - - return (EXECUTION_SUCCESS); + return (getopts_bind_variable (name, strval)); } /* The getopts builtin. Build an argv, and call dogetopts with it. */ @@ -275,26 +288,27 @@ int getopts_builtin (list) WORD_LIST *list; { - register int i; char **av; int ac, ret; - WORD_LIST *t; if (list == 0) return EXECUTION_FAILURE; - for (t = list, ac = 0; t; t = t->next, ac++); - - ac++; - av = (char **)xmalloc ((1 + ac) * sizeof (char *)); - av[ac] = (char *) NULL; - av[0] = this_command_name; - - for (t = list, i = 1; t; t = t->next, i++) - av[i] = t->word->word; + reset_internal_getopt (); + while ((ret = internal_getopt (list, "")) != -1) + { + switch (ret) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + av = make_builtin_argv (list, &ac); ret = dogetopts (ac, av); free ((char *)av); + return (ret); } -#endif /* GETOPTS_BUILTIN */ diff --git a/builtins/hash.def b/builtins/hash.def index f4d319b..2f69f65 100644 --- a/builtins/hash.def +++ b/builtins/hash.def @@ -23,182 +23,167 @@ $PRODUCES hash.c $BUILTIN hash $FUNCTION hash_builtin -$SHORT_DOC hash [-r] [name ...] +$SHORT_DOC hash [-r] [-p pathname] [name ...] For each NAME, the full pathname of the command is determined and -remembered. The -r option causes the shell to forget all remembered -locations. If no arguments are given, information about remembered -commands is presented. +remembered. If the -p option is supplied, PATHNAME is used as the +full pathname of NAME, and no path search is performed. The -r +option causes the shell to forget all remembered locations. If no +arguments are given, information about remembered commands is displayed. $END +#include <config.h> + #include <sys/types.h> #include "../posixstat.h" #include <stdio.h> -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include "../shell.h" #include "../builtins.h" #include "../flags.h" +#include "../execute_cmd.h" #include "hashcom.h" #include "common.h" -#include "../execute_cmd.h" +#include "bashgetopt.h" extern int dot_found_in_search; +extern char *this_command_name; + +static int add_hashed_command (); +static int print_hashed_commands (); + +static int hashing_initialized = 0; + +HASH_TABLE *hashed_filenames; void initialize_filename_hashing () { - hashed_filenames = make_hash_table (FILENAME_HASH_BUCKETS); + if (hashing_initialized == 0) + { + hashed_filenames = make_hash_table (FILENAME_HASH_BUCKETS); + hashing_initialized = 1; + } +} + +static void +free_filename_data (data) + char *data; +{ + free (((PATH_DATA *)data)->path); + free (data); +} + +void +flush_hashed_filenames () +{ + flush_hash_table (hashed_filenames, free_filename_data); +} + +/* Remove FILENAME from the table of hashed commands. */ +void +remove_hashed_filename (filename) + char *filename; +{ + register BUCKET_CONTENTS *item; + + if (hashing_enabled == 0) + return; + + item = remove_hash_item (filename, hashed_filenames); + if (item) + { + if (item->data) + free_filename_data (item->data); + free (item->key); + free (item); + } } /* 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 = 0; - int any_failed = 0; + int expunge_hash_table, opt; + char *word, *pathname; - if (hashing_disabled) + if (hashing_enabled == 0) { builtin_error ("hashing disabled"); return (EXECUTION_FAILURE); } - while (list) + expunge_hash_table = 0; + pathname = (char *)NULL; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "rp:")) != -1) { - char *arg = list->word->word; - - if (ISOPTION (arg, 'r')) + switch (opt) { + case 'r': expunge_hash_table = 1; - list = list->next; - } - else if (ISOPTION (arg, '-')) - { - list = list->next; break; - } - else if (*arg == '-') - { - bad_option (list->word->word); - builtin_error ("usage: hash [-r] [command ...]"); + case 'p': + pathname = list_optarg; + break; + default: + builtin_usage (); return (EX_USAGE); } - else - break; } + list = loptend; /* We want hash -r to be silent, but hash -- to print hashing info. That - is the reason for the !expunge_hash_table. */ - if (!list && !expunge_hash_table) + is the reason for the test of expunge_hash_table. */ + if (list == 0 && expunge_hash_table == 0) { - /* Print information about current hashed info. */ - int any_printed = 0; - int bucket = 0; - register BUCKET_CONTENTS *item_list; - - while (bucket < hashed_filenames->nbuckets) - { - item_list = get_hash_bucket (bucket, hashed_filenames); - if (item_list) - { - if (!any_printed) - { - printf ("hits\tcommand\n"); - any_printed++; - } - while (item_list) - { - printf ("%4d\t%s\n", - item_list->times_found, pathdata(item_list)->path); - item_list = item_list->next; - } - } - bucket++; - } - - if (!any_printed) - printf ("No commands in hash table.\n"); + if (print_hashed_commands () == 0) + printf ("%s: hash table empty\n", this_command_name); return (EXECUTION_SUCCESS); } if (expunge_hash_table) - { - int bucket = 0; - register BUCKET_CONTENTS *item_list, *prev; + flush_hashed_filenames (); - while (bucket < hashed_filenames->nbuckets) - { - item_list = get_hash_bucket (bucket, hashed_filenames); - if (item_list) - { - while (item_list) - { - prev = item_list; - free (item_list->key); - free (pathdata(item_list)->path); - free (item_list->data); - item_list = item_list->next; - free (prev); - } - hashed_filenames->bucket_array[bucket] = (BUCKET_CONTENTS *)NULL; - } - bucket++; - } - } - - while (list) + for (opt = EXECUTION_SUCCESS; list; list = list->next) { /* Add or rehash the specified commands. */ - char *word; - char *full_path; - SHELL_VAR *var; - word = list->word->word; - if (absolute_program (word)) - { - list = list->next; - continue; - } - full_path = find_user_command (word); - var = find_function (word); - - if (!find_shell_builtin (word) && (!var)) + if (pathname) + remember_filename (word, pathname, 0, 0); + else { - if (full_path && executable_file (full_path)) - remember_filename (word, full_path, dot_found_in_search, 0); - else + if (absolute_program (word)) { - builtin_error ("%s: not found", word); - any_failed++; + list = list->next; + continue; } - } - if (full_path) - free (full_path); - list = list->next; + if (add_hashed_command (word)) + opt = EXECUTION_FAILURE; + } } fflush (stdout); - if (any_failed) - return (EXECUTION_FAILURE); - else - return (EXECUTION_SUCCESS); + return (opt); } /* Place FILENAME (key) and FULL_PATHNAME (data->path) into the hash table. CHECK_DOT if non-null is for future calls to - find_hashed_filename (). FOUND is the initial value for - times_found. */ + find_hashed_filename (); it means that this file was found + in a directory in $PATH that is not an absolute pathname. + FOUND is the initial value for times_found. */ void remember_filename (filename, full_pathname, check_dot, found) char *filename, *full_pathname; @@ -206,17 +191,74 @@ remember_filename (filename, full_pathname, check_dot, found) { register BUCKET_CONTENTS *item; - if (hashing_disabled) + if (hashing_enabled == 0) return; + item = add_hash_item (filename, hashed_filenames); if (item->data) free (pathdata(item)->path); else { item->key = savestring (filename); - item->data = (char *)xmalloc (sizeof (PATH_DATA)); + item->data = xmalloc (sizeof (PATH_DATA)); } pathdata(item)->path = savestring (full_pathname); - pathdata(item)->check_dot = check_dot; + pathdata(item)->flags = 0; + if (check_dot) + pathdata(item)->flags |= HASH_CHKDOT; + if (*full_pathname != '/') + pathdata(item)->flags |= HASH_RELPATH; item->times_found = found; } + +static int +add_hashed_command (word, quiet) + char *word; + int quiet; +{ + int rv; + char *full_path; + + rv = 0; + if (find_function (word) == 0 && find_shell_builtin (word) == 0) + { + full_path = find_user_command (word); + if (full_path && executable_file (full_path)) + remember_filename (word, full_path, dot_found_in_search, 0); + else + { + if (quiet == 0) + builtin_error ("%s: not found", word); + rv++; + } + if (full_path) + free (full_path); + } + return (rv); +} + +/* Print information about current hashed info. */ +static int +print_hashed_commands () +{ + BUCKET_CONTENTS *item_list; + int bucket, any_printed; + + for (bucket = any_printed = 0; bucket < hashed_filenames->nbuckets; bucket++) + { + item_list = get_hash_bucket (bucket, hashed_filenames); + if (item_list == 0) + continue; + + if (any_printed == 0) + { + printf ("hits\tcommand\n"); + any_printed++; + } + + for ( ; item_list; item_list = item_list->next) + printf ("%4d\t%s\n", item_list->times_found, pathdata(item_list)->path); + + } + return (any_printed); +} diff --git a/builtins/hashcom.h b/builtins/hashcom.h index defe2fc..5943b5b 100644 --- a/builtins/hashcom.h +++ b/builtins/hashcom.h @@ -18,7 +18,7 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "../hash.h" +#include "../hashlib.h" #define FILENAME_HASH_BUCKETS 631 @@ -26,7 +26,10 @@ extern HASH_TABLE *hashed_filenames; typedef struct { char *path; /* The full pathname of the file. */ - int check_dot; /* Whether `.' appeared before this one in $PATH. */ + int flags; } PATH_DATA; +#define HASH_RELPATH 0x01 /* this filename is a relative pathname. */ +#define HASH_CHKDOT 0x02 /* check `.' since it was earlier in $PATH */ + #define pathdata(x) ((PATH_DATA *)(x)->data) diff --git a/builtins/help.def b/builtins/help.def index c9f1db8..e5e82d5 100644 --- a/builtins/help.def +++ b/builtins/help.def @@ -23,112 +23,131 @@ $PRODUCES help.c $BUILTIN help $FUNCTION help_builtin +$DEPENDS_ON HELP_BUILTIN $SHORT_DOC help [pattern ...] Display helpful information about builtin commands. If PATTERN is specified, gives detailed help on all commands matching PATTERN, otherwise a list of the builtins is printed. $END +#include <config.h> + +#if defined (HELP_BUILTIN) #include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" #include "../builtins.h" +#include "bashgetopt.h" + +#include <glob/fnmatch.h> +#include <glob/glob.h> -#if defined (USE_GLOB_LIBRARY) -# include <glob/glob.h> -#else -# define FNM_NOMATCH 1 -#endif /* USE_GLOB_LIBRARY */ +static void show_builtin_command_help (); /* 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; { - if (!list) - { - register int i, j; - char blurb[256]; + register int i, j; + char *pattern, *name; + int plen, match_found; - show_shell_version (); - printf ( -"Shell commands that 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\ -\n\ -A star (*) next to a name means that the command is disabled.\n\ -\n"); + if (list == 0) + { + show_shell_version (0); + show_builtin_command_help (); + return (EXECUTION_SUCCESS); + } - for (i = 0; i < num_shell_builtins; i++) + /* Placeholder for future options. */ + reset_internal_getopt (); + while ((i = internal_getopt (list, "")) != -1) + { + switch (i) { - QUIT; - sprintf (blurb, "%c%s", - (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*', - shell_builtins[i].short_doc); - - blurb[35] = '\0'; - printf ("%s", blurb); - - if (i % 2) - printf ("\n"); - else - for (j = strlen (blurb); j < 35; j++) - putc (' ', stdout); - + default: + builtin_usage (); + return (EX_USAGE); } - if (i % 2) - printf ("\n"); } - else - { - int match_found = 0; - char *pattern = ""; + list = loptend; - if (glob_pattern_p (list->word->word)) - { - printf ("Shell commands matching keyword%s `", - list->next ? "s" : ""); - print_word_list (list, ", "); - printf ("'\n\n"); - } + /* We should consider making `help bash' do something. */ - while (list) - { - register int i = 0, plen; - char *name; + if (glob_pattern_p (list->word->word)) + { + printf ("Shell commands matching keyword%s `", list->next ? "s" : ""); + print_word_list (list, ", "); + printf ("'\n\n"); + } - pattern = list->word->word; - plen = strlen (pattern); + for (match_found = 0, pattern = ""; list; list = list->next) + { + pattern = list->word->word; + plen = strlen (pattern); - while (name = shell_builtins[i].name) + for (i = 0; name = shell_builtins[i].name; i++) + { + QUIT; + if ((strncmp (pattern, name, plen) == 0) || + (fnmatch (pattern, name, 0) != FNM_NOMATCH)) { - int doc_index; + printf ("%s: %s\n", name, shell_builtins[i].short_doc); - QUIT; - if ((strncmp (pattern, name, plen) == 0) || - (fnmatch (pattern, name, 0) != FNM_NOMATCH)) - { - printf ("%s: %s\n", name, shell_builtins[i].short_doc); + for (j = 0; shell_builtins[i].long_doc[j]; j++) + printf (" %s\n", shell_builtins[i].long_doc[j]); - for (doc_index = 0; - shell_builtins[i].long_doc[doc_index]; doc_index++) - printf (" %s\n", shell_builtins[i].long_doc[doc_index]); - - match_found++; - } - i++; + match_found++; } - list = list->next; } + } - if (!match_found) - { - fprintf (stderr, "No help topics match `%s'. Try `help help'.\n", - pattern); - fflush (stderr); - return (EXECUTION_FAILURE); - } + if (match_found == 0) + { + builtin_error ("no help topics match `%s'. Try `help help'.", pattern); + return (EXECUTION_FAILURE); } + fflush (stdout); return (EXECUTION_SUCCESS); } + +static void +show_builtin_command_help () +{ + int i, j; + char blurb[36]; + + 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\ +\n\ +A star (*) next to a name means that the command is disabled.\n\ +\n"); + + for (i = 0; i < num_shell_builtins; i++) + { + QUIT; + blurb[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*'; + strncpy (blurb + 1, shell_builtins[i].short_doc, 34); + blurb[35] = '\0'; + printf ("%s", blurb); + + if (i % 2) + printf ("\n"); + else + for (j = strlen (blurb); j < 35; j++) + putc (' ', stdout); + } + if (i % 2) + printf ("\n"); +} +#endif /* HELP_BUILTIN */ diff --git a/builtins/history.def b/builtins/history.def index 814e705..939eb25 100644 --- a/builtins/history.def +++ b/builtins/history.def @@ -24,156 +24,277 @@ $PRODUCES history.c $BUILTIN history $FUNCTION history_builtin $DEPENDS_ON HISTORY -$SHORT_DOC history [n] [ [-awrn] [filename]] +$SHORT_DOC history [-c] [n] or history -awrn [filename] or history -ps arg [arg...] Display the history list with line numbers. Lines listed with with a `*' have been modified. Argument of N says to list only -the last N lines. Argument `-w' means to write out the current -history file; `-r' means to read it instead. Argument `-a' means +the last N lines. The -c option causes the history list to be +cleared by deleting all of the entries. The `-w' option writes out the +current history to the history file; `-r' means to read the file and +append the contents to the history list instead. `-a' means to append history lines from this session to the history file. Argument `-n' means to read all history lines not already read -from the history file. If FILENAME is given, then use that file, -else if $HISTFILE has a value, use that, else use ~/.bash_history. +from the history file and append them to the history list. If +FILENAME is given, then that is used as the history file else +if $HISTFILE has a value, that is used, else ~/.bash_history. +If the -s option is supplied, the non-option ARGs are appended to +the history list as a single entry. The -p option means to perform +history expansion on each ARG and display the result, without storing +anything in the history list. $END -#include "../shell.h" +#include <config.h> + #if defined (HISTORY) #include <sys/types.h> #include <sys/file.h> -#include "../filecntl.h" #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 "../shell.h" #include "../bashhist.h" #include <readline/history.h> +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif -/* History. Arg of -w FILENAME means write file, arg of -r FILENAME - means read file. Arg of N means only display that many items. */ +static void display_history (); +static void push_history (); +static int expand_and_print_history (); +#define AFLAG 0x01 +#define RFLAG 0x02 +#define WFLAG 0x04 +#define NFLAG 0x08 +#define SFLAG 0x10 +#define PFLAG 0x20 +#define CFLAG 0x40 + +int history_builtin (list) WORD_LIST *list; { - register int i; - int limited = 0, limit = 0; - HIST_ENTRY **hlist; + int flags, opt, result; + char *filename; - while (list) + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "acnpsrw")) != -1) { - char *arg = list->word->word; - - if ((arg[0] == '-') && - (strlen (arg) == 2) && - (member (arg[1], "rwan"))) - { - char *file; - int result = EXECUTION_SUCCESS; - - if (list->next) - file = list->next->word->word; - else - file = get_string_value ("HISTFILE"); - - switch (arg[1]) - { - case 'a': /* Append `new' lines to file. */ - { - if (history_lines_this_session) - { - void using_history (); - - if (history_lines_this_session < where_history ()) - { - /* If the filename was supplied, then create it - if it doesn't already exist. */ - if (file) - { - struct stat buf; - - if (stat (file, &buf) == -1) - { - int tem; - - tem = open (file, O_CREAT, 0666); - close (tem); - } - } - - result = - append_history (history_lines_this_session, file); - history_lines_in_file += history_lines_this_session; - history_lines_this_session = 0; - } - } - break; - } - - case 'w': /* Write entire history. */ - { - result = write_history (file); - break; - } - - case 'r': /* Read entire file. */ - { - result = read_history (file); - break; - } - - case 'n': /* Read `new' history from file. */ - { - /* Read all of the lines in the file that we haven't - already read. */ - using_history (); - result = read_history_range (file, history_lines_in_file, -1); - using_history (); - history_lines_in_file = where_history (); - - break; - } - } - return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS); - } - else if (strcmp (list->word->word, "--") == 0) + switch (opt) { - list = list->next; + case 'a': + flags |= AFLAG; break; - } - else if (*list->word->word == '-') - { - bad_option (list->word->word); - builtin_error ("usage: history [n] [-rwan [filename]]"); + 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 'p': +#if defined (BANG_HISTORY) + flags |= PFLAG; +#endif + break; + default: + builtin_usage (); return (EX_USAGE); } - else - break; + } + 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) + { + 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 (EXECUTION_SUCCESS); + } +#endif + else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0) + { + display_history (list); + return (EXECUTION_SUCCESS); + } + + 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. */ + using_history (); + result = read_history_range (filename, history_lines_in_file, -1); + using_history (); + history_lines_in_file = where_history (); + } + + 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 void +display_history (list) + WORD_LIST *list; +{ + register int i; + int limited, limit; + HIST_ENTRY **hlist; + if (list) { limited = 1; limit = get_numeric_arg (list); } + else + limited = limit = 0; hlist = history_list (); if (hlist) { - for (i = 0; hlist[i]; i++); + for (i = 0; hlist[i]; i++) + ; if (limit < 0) limit = -limit; - if (!limited) + if ((limited == 0) || ((i -= limit) < 0)) i = 0; - else - if ((i -= limit) < 0) - i = 0; while (hlist[i]) { QUIT; printf ("%5d%c %s\n", i + history_base, - hlist[i]->data ? '*' : ' ', hlist[i]->line); + histdata(i) ? '*' : ' ', + histline(i)); i++; } } - return (EXECUTION_SUCCESS); } + +static int +delete_last_history () +{ + register int i; + HIST_ENTRY **hlist, *histent, *discard; + + hlist = history_list (); + if (hlist == NULL) + return 0; + + for (i = 0; hlist[i]; i++) + ; + i--; + + /* History_get () takes a parameter that must be offset by history_base. */ + histent = history_get (history_base + i); /* Don't free this */ + if (histent == NULL) + return 0; + + discard = remove_history (i); + if (discard) + { + if (discard->line) + free (discard->line); + free ((char *) discard); + } + return (1); +} + +/* 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; + + if (delete_last_history () == 0) + return; + s = string_list (list); + maybe_add_history (s); /* Obeys HISTCONTROL setting. */ + free (s); +} + +#if defined (BANG_HISTORY) +static int +expand_and_print_history (list) + WORD_LIST *list; +{ + char *s; + int r, result; + + if (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 index 023945b..291eea1 100644 --- a/builtins/inlib.def +++ b/builtins/inlib.def @@ -20,6 +20,8 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES inlib.c +#include <config.h> + #include <stdio.h> #include "../shell.h" diff --git a/builtins/jobs.def b/builtins/jobs.def index 8a293da..2896a76 100644 --- a/builtins/jobs.def +++ b/builtins/jobs.def @@ -1,5 +1,5 @@ This file is jobs.def, from which is created jobs.c. -It implements the builtin "jobs" in Bash. +It implements the builtins "jobs" and "disown" in Bash. Copyright (C) 1987, 1989, 1991, 1992 Free Software Foundation, Inc. @@ -24,24 +24,38 @@ $PRODUCES jobs.c $BUILTIN jobs $FUNCTION jobs_builtin $DEPENDS_ON JOB_CONTROL -$SHORT_DOC jobs [-lnp] [jobspec ...] | jobs -x command [args] +$SHORT_DOC jobs [-lnprs] [jobspec ...] or jobs -x command [args] Lists the active jobs. The -l option lists process id's in addition to the normal information; the -p option lists process id's only. If -n is given, only processes that have changed status since the last -notification are printed. JOBSPEC restricts output to that job. -If -x is given, 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. +notification are printed. JOBSPEC restricts output to that job. The +-r and -s options restrict output to running and stopped jobs only, +respectively. Without options, the status of all active jobs is +printed. If -x is given, 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. $END -#include "../shell.h" +#include <config.h> #if defined (JOB_CONTROL) -#include <sys/types.h> +#include "../bashtypes.h" #include <signal.h> -#include "../jobs.h" +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.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 extern int job_control, interactive_shell; static int execute_list_with_replacements (); @@ -52,20 +66,24 @@ static int execute_list_with_replacements (); 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. */ + 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 = JLIST_STANDARD, execute = 0; - int opt; - int any_failed = 0; + int form, execute, state, opt, any_failed, job; + sigset_t set, oset; - if (!job_control && !interactive_shell) + if (job_control == 0 && interactive_shell == 0) return (EXECUTION_SUCCESS); + execute = any_failed = 0; + form = JLIST_STANDARD; + state = JSTATE_ANY; + reset_internal_getopt (); - while ((opt = internal_getopt (list, "lpnx")) != -1) + while ((opt = internal_getopt (list, "lpnxrs")) != -1) { switch (opt) { @@ -86,9 +104,15 @@ jobs_builtin (list) } execute++; break; + case 'r': + state = JSTATE_RUNNING; + break; + case 's': + state = JSTATE_STOPPED; + break; default: - builtin_error ("usage: jobs [-lpn [jobspec]] [-x command [args]]"); + builtin_usage (); return (EX_USAGE); } } @@ -100,21 +124,29 @@ jobs_builtin (list) if (!list) { - list_jobs (form); + 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) { - int job; - sigset_t set, oset; - BLOCK_CHILD (set, oset); job = get_job_spec (list); if ((job == NO_JOB) || !jobs || !jobs[job]) { - builtin_error ("No such job %s", list->word->word); + builtin_error ("no such job %s", list->word->word); any_failed++; } else if (job != DUP_JOB) @@ -169,3 +201,62 @@ execute_list_with_replacements (list) return (result); } #endif /* JOB_CONTROL */ + +$BUILTIN disown +$FUNCTION disown_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC disown [-h] [jobspec ...] +By default, removes each JOBSPEC argument from the table of active jobs. +If the -h option is given, the job is not removed from the table, but is +marked so that SIGHUP is not sent to the job if the shell receives a +SIGHUP. +$END + +#if defined (JOB_CONTROL) +int +disown_builtin (list) + WORD_LIST *list; +{ + int opt, job, retval, nohup_only; + sigset_t set, oset; + + nohup_only = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "h")) != -1) + { + switch (opt) + { + case 'h': + nohup_only = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + retval = EXECUTION_SUCCESS; + + do + { + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (job == NO_JOB || jobs == 0 || jobs[job] == 0) + { + builtin_error ("no such job %s", list->word->word); + retval = EXECUTION_FAILURE; + } + else if (nohup_only) + nohup_job (job); + else + delete_job (job); + 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 index 53d5c8f..ba779bf 100644 --- a/builtins/kill.def +++ b/builtins/kill.def @@ -24,7 +24,7 @@ $PRODUCES kill.c $BUILTIN kill $FUNCTION kill_builtin $DEPENDS_ON JOB_CONTROL -$SHORT_DOC kill [-s sigspec | -sigspec] [pid | job]... | -l [signum] +$SHORT_DOC kill [-s sigspec | -n signum | -sigspec] [pid | job]... or kill -l [sigspec] Send the processes named by PID (or JOB) the signal SIGSPEC. If SIGSPEC is not present, then SIGTERM is assumed. An argument of `-l' lists the signal names; if arguments follow `-l' they are assumed to @@ -34,17 +34,25 @@ process IDs, and, if you have reached the limit on processes that you can create, you don't have to start a process to kill another one. $END -/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ -#if !defined (errno) -extern int errno; -#endif /* !errno */ +#include <config.h> + +#include <stdio.h> +#include <errno.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" -#include "../bashtypes.h" #include "../shell.h" #include "../trap.h" #include "../jobs.h" #include "common.h" -#include <errno.h> + +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) +extern int errno; +#endif /* !errno */ #if defined (JOB_CONTROL) extern int interactive; @@ -63,14 +71,17 @@ int kill_builtin (list) WORD_LIST *list; { - int signal = SIGTERM; - int any_succeeded = 0, listing = 0, saw_signal = 0; - char *sigspec = "TERM", *word; + int signal, any_succeeded, listing, saw_signal; + char *sigspec, *word; pid_t pid; - if (!list) + if (list == 0) return (EXECUTION_SUCCESS); + any_succeeded = listing = saw_signal = 0; + signal = SIGTERM; + sigspec = "TERM"; + /* Process options. */ while (list) { @@ -81,13 +92,13 @@ kill_builtin (list) listing++; list = list->next; } - else if (ISOPTION (word, 's')) + else if (ISOPTION (word, 's') || ISOPTION (word, 'n')) { list = list->next; if (list) { sigspec = list->word->word; - if (sigspec[0] == '0' && !sigspec[1]) + if (sigspec[0] == '0' && sigspec[1] == '\0') signal = 0; else signal = decode_signal (sigspec); @@ -95,7 +106,7 @@ kill_builtin (list) } else { - builtin_error ("-s requires an argument"); + builtin_error ("%s requires an argument", word); return (EXECUTION_FAILURE); } } @@ -104,6 +115,11 @@ kill_builtin (list) 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). */ @@ -119,75 +135,7 @@ kill_builtin (list) } if (listing) - { - if (!list) - { - register int i; - register int column = 0; - char *name; - - for (i = 1; i < NSIG; i++) - { - name = signal_name (i); - if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) - continue; - - if (posixly_correct) - printf ("%s%s", name, (i == NSIG - 1) ? "" : " "); - else - { - printf ("%2d) %s", i, name); - - if (++column < 4) - printf ("\t"); - else - { - printf ("\n"); - column = 0; - } - } - } - - if (posixly_correct || column != 0) - printf ("\n"); - } - else - { - /* List individual signal names. */ - while (list) - { - int signum; - char *name; - - if ((sscanf (list->word->word, "%d", &signum) != 1) || - (signum <= 0)) - { - list_error: - builtin_error ("bad signal number: %s", list->word->word); - list = list->next; - continue; - } - - /* This is specified by Posix.2 so that exit statuses can be - mapped into signal numbers. */ - if (signum > 128) - signum -= 128; - - if (signum >= NSIG) - goto list_error; - - name = signal_name (signum); - if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) - { - list = list->next; - continue; - } - printf ("%s\n", name); - list = list->next; - } - } - return (EXECUTION_SUCCESS); - } + return (display_signal_list (list, 0)); /* OK, we are killing processes. */ if (signal == NO_SIG) @@ -215,15 +163,11 @@ kill_builtin (list) } else if (*list->word->word != '%') { - builtin_error ("No such pid %s", list->word->word); + builtin_error ("%s: no such pid", list->word->word); CONTINUE_OR_FAIL; } -#if 1 else if (interactive) /* Posix.2 says you can kill without job control active (4.32.4) */ -#else - else if (job_control) /* can't kill jobs if not using job control */ -#endif { /* Must be a job spec. Check it out. */ int job; sigset_t set, oset; @@ -234,7 +178,7 @@ kill_builtin (list) if (job < 0 || job >= job_slots || !jobs[job]) { if (job != DUP_JOB) - builtin_error ("No such job %s", list->word->word); + builtin_error ("%s: no such job", list->word->word); UNBLOCK_CHILD (oset); CONTINUE_OR_FAIL; } @@ -243,10 +187,7 @@ kill_builtin (list) 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. */ - if (jobs[job]->flags & J_JOBCONTROL) - pid = jobs[job]->pgrp; - else - pid = jobs[job]->pipe->pid; + pid = IS_JOBCONTROL (job) ? jobs[job]->pgrp : jobs[job]->pipe->pid; UNBLOCK_CHILD (oset); @@ -266,16 +207,13 @@ kill_builtin (list) } else { - builtin_error ("bad process specification `%s'", list->word->word); + builtin_error ("`%s' is not a pid or valid job spec", list->word->word); CONTINUE_OR_FAIL; } continue_killing: list = list->next; } - if (any_succeeded) - return (EXECUTION_SUCCESS); - else - return (EXECUTION_FAILURE); + return (any_succeeded ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } #endif /* JOB_CONTROL */ diff --git a/builtins/let.def b/builtins/let.def index fdb3a6f..60b6675 100644 --- a/builtins/let.def +++ b/builtins/let.def @@ -29,13 +29,23 @@ 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. - - unary minus - ! logical NOT - * / % multiplication, division, remainder - + - addition, subtraction - <= >= < > comparison - == != equality inequality - = assignment + -, + unary minus, plus + !, ~ logical and bitwise negation + *, /, % 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 expression + =, *=, /=, %=, + +=, -=, <<=, >>=, + &=, ^=, |= assignment Shell variables are allowed as operands. The name of the variable is replaced by its value (coerced to a long integer) within @@ -50,28 +60,49 @@ If the last ARG evaluates to 0, let returns 1; 0 is returned otherwise. $END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" +#include "common.h" /* Arithmetic LET function. */ +int let_builtin (list) WORD_LIST *list; { - long ret = 0L; + long ret; - if (!list) + if (list == 0) { - builtin_error ("argument (expression) expected"); + builtin_error ("expression expected"); return (EXECUTION_FAILURE); } - while (list) + for (; list; list = list->next) + ret = evalexp (list->word->word); + + return ((ret == 0L) ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +int +exp_builtin (list) + WORD_LIST *list; +{ + char *exp; + int ret; + + if (list == 0) { - ret = evalexp (list->word->word); - list = list->next; + builtin_error ("expression expected"); + return (EXECUTION_FAILURE); } - if (ret == 0L) - return (EXECUTION_FAILURE); - else - return (EXECUTION_SUCCESS); + exp = string_list (list); + ret = evalexp (exp); + free (exp); + return ((ret == 0L) ? EXECUTION_FAILURE : EXECUTION_SUCCESS); } diff --git a/builtins/mkbuiltins.c b/builtins/mkbuiltins.c index 572d01e..ae93bd4 100644 --- a/builtins/mkbuiltins.c +++ b/builtins/mkbuiltins.c @@ -19,6 +19,12 @@ You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../bashansi.h" #include "../config.h" #include <stdio.h> @@ -50,6 +56,7 @@ extern char *strcpy (); /* Flag values that builtins can have. */ #define BUILTIN_FLAG_SPECIAL 0x01 +#define BUILTIN_FLAG_ASSIGNMENT 0x02 /* If this stream descriptor is non-zero, then write texinfo documentation to it. */ @@ -115,12 +122,41 @@ char *special_builtins[] = "export", "readonly", "return", "set", "shift", "trap", "unset", (char *)NULL }; + +/* The builtin commands that take assignment statements as arguments. */ +char *assignment_builtins[] = +{ + "alias", "declare", "export", "local", "readonly", "typeset", + (char *)NULL +}; + +/* Forward declarations. */ static int is_special_builtin (); +static int is_assignment_builtin (); + +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 (); +void free_defs (); +void add_documentation (); + +void must_be_building (); +void remove_trailing_whitespace (); /* 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; @@ -305,6 +341,7 @@ copy_string_array (array) } /* Add ELEMENT to ARRAY, growing the array if neccessary. */ +void array_add (element, array) char *element; ARRAY *array; @@ -324,6 +361,7 @@ array_add (element, array) } /* Free an allocated array and data pointer. */ +void array_free (array) ARRAY *array; { @@ -397,6 +435,7 @@ int output_cpp_line_info = 0; 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; @@ -548,6 +587,7 @@ free_builtin (builtin) } /* Free all of the memory allocated to a DEF_FILE. */ +void free_defs (defs) DEF_FILE *defs; { @@ -592,6 +632,7 @@ strip_whitespace (string) } /* Remove only the trailing whitespace from STRING. */ +void remove_trailing_whitespace (string) char *string; { @@ -625,6 +666,7 @@ get_arg (for_whom, defs, string) } /* Error if not building a builtin. */ +void must_be_building (directive, defs) char *directive; DEF_FILE *defs; @@ -645,6 +687,7 @@ current_builtin (directive, defs) /* 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; @@ -670,38 +713,42 @@ builtin_handler (self, defs, arg) char *self, *arg; DEF_FILE *defs; { + BUILTIN_DESC *new; + char *name; + /* If we are already building a builtin, we cannot start a new one. */ if (building_builtin) - return (line_error (defs, "%s found before $END", self)); + { + 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. */ - { - BUILTIN_DESC *new; - char *name; - - 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; - - array_add ((char *)new, defs->builtins); - building_builtin = 1; - } + 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; + + array_add ((char *)new, defs->builtins); + building_builtin = 1; + return (0); } @@ -744,6 +791,7 @@ docname_handler (self, defs, arg) } /* How to handle the $SHORT_DOC directive. */ +int short_doc_handler (self, defs, arg) char *self, *arg; DEF_FILE *defs; @@ -762,13 +810,16 @@ short_doc_handler (self, defs, arg) } /* How to handle the $COMMENT directive. */ +int comment_handler (self, defs) char *self; DEF_FILE *defs; { + return (0); } /* How to handle the $DEPENDS_ON directive. */ +int depends_on_handler (self, defs, arg) char *self, *arg; DEF_FILE *defs; @@ -788,6 +839,7 @@ depends_on_handler (self, defs, arg) } /* How to handle the $PRODUCES directive. */ +int produces_handler (self, defs, arg) char *self, *arg; DEF_FILE *defs; @@ -820,12 +872,14 @@ produces_handler (self, defs, arg) } /* How to handle the $END directive. */ +int end_handler (self, defs, arg) char *self, *arg; DEF_FILE *defs; { must_be_building (self, defs); building_builtin = 0; + return (0); } /* **************************************************************** */ @@ -835,6 +889,7 @@ end_handler (self, defs, arg) /* **************************************************************** */ /* Produce an error for DEFS with FORMAT and ARGS. */ +void line_error (defs, format, arg1, arg2) DEF_FILE *defs; char *format, *arg1, *arg2; @@ -848,6 +903,7 @@ line_error (defs, format, arg1, arg2) } /* Print error message for FILENAME. */ +void file_error (filename) char *filename; { @@ -895,7 +951,7 @@ xrealloc (pointer, bytes) static void memory_error_and_abort () { - fprintf (stderr, "mkbuiltins: Out of virtual memory!\n"); + fprintf (stderr, "mkbuiltins: out of virtual memory\n"); abort (); } @@ -929,6 +985,7 @@ copy_builtin (builtin) } /* How to save away a builtin. */ +void save_builtin (builtin) BUILTIN_DESC *builtin; { @@ -972,7 +1029,7 @@ char *structfile_header[] = { " along with Bash; see the file COPYING. If not, write to the Free", " Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */", "", - "/* The list of shell builtins. Each element is name, function, enabled-p,", + "/* 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", @@ -992,13 +1049,17 @@ char *structfile_footer[] = { " { (char *)0x0, (Function *)0x0, 0, (char **)0x0, (char *)0x0 }", "};", "", + "struct builtin *shell_builtins = static_shell_builtins;", + "struct builtin *current_builtin;", + "", "int num_shell_builtins =", - "\tsizeof (shell_builtins) / sizeof (struct builtin) - 1;", + "\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; { @@ -1011,7 +1072,7 @@ write_file_headers (structfile, externfile) fprintf (structfile, "#include \"%s\"\n", extern_filename ? extern_filename : "builtext.h"); - fprintf (structfile, "\nstruct builtin shell_builtins[] = {\n"); + fprintf (structfile, "\nstruct builtin static_shell_builtins[] = {\n"); } if (externfile) @@ -1022,6 +1083,7 @@ write_file_headers (structfile, externfile) /* Write out any necessary closing information for STRUCTFILE and EXTERNFILE. */ +void write_file_footers (structfile, externfile) FILE *structfile, *externfile; { @@ -1037,6 +1099,7 @@ write_file_footers (structfile, externfile) /* Write out the information accumulated in DEFS to STRUCTFILE and EXTERNFILE. */ +void write_builtins (defs, structfile, externfile) DEF_FILE *defs; FILE *structfile, *externfile; @@ -1057,8 +1120,7 @@ write_builtins (defs, structfile, externfile) { if (builtin->dependencies) { - if (builtin->function) - write_ifdefs (externfile, builtin->dependencies->array); + write_ifdefs (externfile, builtin->dependencies->array); write_ifdefs (structfile, builtin->dependencies->array); } @@ -1083,20 +1145,14 @@ write_builtins (defs, structfile, externfile) else fprintf (structfile, "(Function *)0x0, "); -#define SPECIAL_FLAG_STRING "BUILTIN_ENABLED | STATIC_BUILTIN | SPECIAL_BUILTIN" -#define NORMAL_FLAG_STRING "BUILTIN_ENABLED | STATIC_BUILTIN" - - fprintf (structfile, "%s, %s_doc,\n", - (builtin->flags & BUILTIN_FLAG_SPECIAL) ? - SPECIAL_FLAG_STRING : - NORMAL_FLAG_STRING, + fprintf (structfile, "%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->docname ? builtin->docname : builtin->name); -#undef SPECIAL_FLAG_STRING -#undef NORMAL_FLAG_STRING - fprintf - (structfile, " \"%s\" },\n", + (structfile, " \"%s\", (char *)NULL },\n", builtin->shortdoc ? builtin->shortdoc : builtin->name); /* Save away this builtin for later writing of the @@ -1126,6 +1182,7 @@ write_builtins (defs, structfile, externfile) } /* Write out the long documentation strings in BUILTINS to STREAM. */ +void write_longdocs (stream, builtins) FILE *stream; ARRAY *builtins; @@ -1157,6 +1214,7 @@ write_longdocs (stream, builtins) 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; @@ -1187,6 +1245,7 @@ write_ifdefs (stream, defines) 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; @@ -1211,6 +1270,7 @@ write_endifs (stream, defines) /* Write DOCUMENTAION to STREAM, perhaps surrounding it with double-quotes and quoting special characters in the string. */ +void write_documentation (stream, documentation, indentation, flags) FILE *stream; char **documentation; @@ -1218,14 +1278,14 @@ write_documentation (stream, documentation, indentation, flags) { register int i, j; register char *line; - int string_array = (flags & STRING_ARRAY); /* Mutually exclusive. */ - int texinfo = (flags & TEXINFO); + int string_array, texinfo; if (!stream) return; + string_array = flags & STRING_ARRAY; if (string_array) - fprintf (stream, " {\n"); + fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n"); #if !defined (OLDCODE) /* XXX -- clean me up; for experiment only */ @@ -1233,7 +1293,7 @@ write_documentation (stream, documentation, indentation, flags) goto end_of_document; #endif /* !OLDCODE */ - for (i = 0; line = documentation[i]; i++) + for (i = 0, texinfo = (flags & TEXINFO); line = documentation[i]; i++) { /* Allow #ifdef's to be written out verbatim. */ if (*line == '#') @@ -1295,17 +1355,31 @@ end_of_document: #endif /* !OLDCODE */ if (string_array) - fprintf (stream, " (char *)NULL\n};\n"); + fprintf (stream, "#endif /* HELP_BUILTIN */\n (char *)NULL\n};\n"); } static int -is_special_builtin (name) - char *name; +_find_in_table (name, name_table) + char *name, *name_table[]; { register int i; - for (i = 0; special_builtins[i]; i++) - if (strcmp (name, special_builtins[i]) == 0) + 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)); +} diff --git a/builtins/psize.c b/builtins/psize.c index 03a4f6e..0ba1c51 100644 --- a/builtins/psize.c +++ b/builtins/psize.c @@ -21,6 +21,11 @@ /* 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) +# include <unistd.h> +#endif #include <stdio.h> #include <sys/types.h> @@ -29,6 +34,7 @@ #include "../command.h" #include "../general.h" +#include "../sig.h" extern int errno; int nw; diff --git a/builtins/pushd.def b/builtins/pushd.def new file mode 100644 index 0000000..3eadae0 --- /dev/null +++ b/builtins/pushd.def @@ -0,0 +1,604 @@ +This file is pushd.def, from which is created pushd.c. It implements the +builtins "pushd", "popd", and "dirs" in Bash. + +Copyright (C) 1987, 1989, 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 1, 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; see the file COPYING. If not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +$PRODUCES pushd.c + +$BUILTIN pushd +$FUNCTION pushd_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC pushd [dir | +N | -N] [-n] +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. + ++N Rotates the stack so that the Nth directory (counting + from the left of the list shown by `dirs') is at the top. + +-N Rotates the stack so that the Nth directory (counting + from the right) is at the top. + +-n suppress the normal change of directory when adding directories + to the stack, so only the stack is manipulated. + +dir adds DIR to the directory stack at the top, making it the + new current working directory. + +You can see the directory stack with the `dirs' command. +$END + +$BUILTIN popd +$FUNCTION popd_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC popd [+N | -N] [-n] +Removes entries from the directory stack. With no arguments, +removes the top directory from the stack, and cd's to the new +top directory. + ++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. + +-n suppress the normal change of directory when removing directories + from the stack, so only the stack is manipulated. + +You can see the directory stack with the `dirs' command. +$END + +$BUILTIN dirs +$FUNCTION dirs_builtin +$DEPENDS_ON PUSHD_AND_POPD +$SHORT_DOC dirs [-clpv] [+N] [-N] +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. + +The -l flag specifies that `dirs' should not print shorthand versions +of directories which are relative to your home directory. This means +that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag +causes `dirs' to print the directory stack with one entry per line, +prepending the directory name with its position in the stack. The -p +flag does the same thing, but the stack position is not prepended. +The -c flag clears the directory stack by deleting all of the elements. + ++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. +$END + +#include <config.h> + +#if defined (PUSHD_AND_POPD) +#include <stdio.h> +#include <sys/param.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" + +#include <errno.h> + +#include <tilde/tilde.h> + +#include "../shell.h" +#include "../maxpath.h" +#include "common.h" +#include "builtext.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +static char *m_badarg = "%s: bad argument"; + +/* 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 (); +static void clear_directory_stack (); +static int cd_to_string (); +static int change_to_temp (); +static int get_dirstack_index (); +static void add_dirstack_element (); + +#define NOCD 0x01 +#define ROTATE 0x02 +#define LONGFORM 0x04 +#define CLEARSTAK 0x08 + +int +pushd_builtin (list) + WORD_LIST *list; +{ + char *temp, *current_directory, *top; + int j, flags; + long num; + char direction; + + /* 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; 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) + { + builtin_error (m_badarg, 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 == '-') + { + bad_option (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 (list) : EXECUTION_SUCCESS; + if (j == EXECUTION_SUCCESS) + { + add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory); + dirs_builtin ((WORD_LIST *)NULL); + 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; + long which; + int flags; + char direction; + + for (flags = 0, which = 0L, 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) + { + builtin_error (m_badarg, list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + else if (*list->word->word == '-') + { + bad_option (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, list ? list->word->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; + long 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) + { + builtin_error (m_badarg, list->word->word); + builtin_usage (); + return (EXECUTION_FAILURE); + } + sign = (*list->word->word == '+') ? 1 : -1; + desired_index = get_dirstack_index (i, sign, &index_flag); + } + else + { + bad_option (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 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'); + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +static void +pushd_error (offset, arg) + int offset; + char *arg; +{ + if (offset == 0) + builtin_error ("directory stack empty"); + else if (arg) + builtin_error ("%s: bad directory stack index", arg); + else + builtin_error ("bad 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; + int result; + + tlist = make_word_list (make_word (name), NULL); + 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; +{ + int j; + + if (directory_list_offset == directory_list_size) + { + j = (directory_list_size += 10) * sizeof (char *); + pushd_directory_list = (char **)xrealloc (pushd_directory_list, j); + } + pushd_directory_list[directory_list_offset++] = dir; +} + +static int +get_dirstack_index (ind, sign, indexp) + int ind, 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 + return (sign > 0 ? directory_list_offset - ind : ind); +} + +char * +get_dirstack_element (ind, sign) + int ind, sign; +{ + int i; + + i = get_dirstack_index (ind, sign, (int *)NULL); + return (i < 0 || i > directory_list_offset) ? (char *)NULL + : pushd_directory_list[i]; +} + +void +set_dirstack_element (ind, sign, value) + int ind, 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 () +{ + register int i; + WORD_LIST *ret; + char *d, *t; + + for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++) + { + d = polite_directory_format (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 *)); */ +} +#endif /* PUSHD_AND_POPD */ diff --git a/builtins/read.def b/builtins/read.def index 7b6dfc9..eb04a30 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -23,101 +23,170 @@ $PRODUCES read.c $BUILTIN read $FUNCTION read_builtin -$SHORT_DOC read [-r] [name ...] +$SHORT_DOC read [-r] [-p prompt] [-a array] [-e] [name ...] One line is read from the standard input, and the first word is -assigned to the first NAME, the second word to the second NAME, etc. -with leftover words assigned to the last NAME. Only the characters +assigned to the first NAME, the second word to the second NAME, and so +on, with leftover words assigned to the last NAME. Only the characters found in $IFS are recognized as word delimiters. The return code is -zero, unless end-of-file is encountered. If the -r option is given, -this signifies `raw' input, and backslash processing is disabled. +zero, unless end-of-file is encountered. If no NAMEs are supplied, the +line read is stored in the REPLY variable. If the -r option is given, +this signifies `raw' input, and backslash escaping is disabled. If +the `-p' option is supplied, the string supplied as an argument is +output without a trailing newline before attempting to read. If -a is +supplied, the words read are assigned to sequential indices of ARRAY, +starting at zero. If -e is supplied and the shell is interactive, +readline is used to obtain the line. $END +#include <config.h> + #include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" #include "common.h" +#include "bashgetopt.h" -#define issep(c) (strchr (ifs_chars, (c)) != (char *)0) +#if defined (READLINE) +#include "../bashline.h" +#include <readline/readline.h> +#endif -static int stream_close (); +#define issep(c) (strchr (ifs_chars, (c))) extern int interrupt_immediately; +#if defined (READLINE) +static char *edit_line (); +#endif +static SHELL_VAR *bind_read_variable (); + /* 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. - - S. R. Bourne's shell complains if you don't name a variable - to receive the stuff that is read. GNU's shell doesn't. This - allows you to let the user type random things. */ + are mentioned in LIST, then the default variable is $REPLY. */ +int read_builtin (list) WORD_LIST *list; { register char *varname; - int size, c, i, fildes, raw_mode, pass_next, saw_escape, retval; - char *input_string, *orig_input_string, *ifs_chars, *t; - FILE *input_stream; + int size, i, raw, pass_next, saw_escape, eof, opt, retval, edit; + char c; + char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname; + char *e, *t, *t1; SHELL_VAR *var; +#if defined (ARRAY_VARS) + WORD_LIST *alist; +#endif +#if defined (READLINE) + char *rlbuf; + int rlind; +#endif i = 0; /* Index into the string that we are reading. */ - raw_mode = 0; /* Not reading raw input be default. */ + raw = edit = 0; /* Not reading raw input by default. */ + arrayname = prompt = (char *)NULL; + +#if defined (READLINE) + rlbuf = (char *)0; + rlind = 0; +#endif - while (list) + reset_internal_getopt (); + while ((opt = internal_getopt (list, "erp:a:")) != -1) { - if (ISOPTION (list->word->word, 'r')) - { - raw_mode = 1; - list = list->next; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; + switch (opt) + { + case 'r': + raw = 1; break; - } - else if (*list->word->word == '-') - { - bad_option (list->word->word); - builtin_error ("usage: read [-r] [name ...]"); + case 'p': + prompt = list_optarg; + break; + case 'e': +#if defined (READLINE) + edit = 1; +#endif + break; +#if defined (ARRAY_VARS) + case 'a': + arrayname = list_optarg; + break; +#endif + default: + builtin_usage (); return (EX_USAGE); } - else - break; - } - - /* We need unbuffered input from stdin. So we make a new stream with - the same file descriptor as stdin, then unbuffer it. */ - fildes = dup (fileno (stdin)); - - if (fildes == -1) - return (EXECUTION_FAILURE); - - input_stream = fdopen (fildes, "r"); - - if (!input_stream) - { - close (fildes); - return (EXECUTION_FAILURE); } + list = loptend; + /* IF IFS is unset, we use the default of " \t\n". */ var = find_variable ("IFS"); ifs_chars = var ? value_cell (var) : " \t\n"; - - input_string = xmalloc (size = 128); + if (ifs_chars == 0) /* XXX */ + ifs_chars = ""; /* XXX */ - setbuf (input_stream, (char *)NULL); + input_string = xmalloc (size = 128); begin_unwind_frame ("read_builtin"); add_unwind_protect (xfree, input_string); - add_unwind_protect (stream_close, input_stream); +#if defined (READLINE) + add_unwind_protect (xfree, rlbuf); +#endif interrupt_immediately++; + /* If the -p or -e flags were given, but input is not coming from the + terminal, turn them off. */ + if ((prompt || edit) && (isatty (0) == 0)) + { + prompt = (char *)NULL; + edit = 0; + } + + if (prompt && edit == 0) + { + fprintf (stderr, "%s", prompt); + fflush (stderr); + } + pass_next = 0; /* Non-zero signifies last char was backslash. */ saw_escape = 0; /* Non-zero signifies that we saw an escape char */ - while ((c = getc (input_stream)) != EOF) + for (eof = 0;;) { +#if defined (READLINE) + if (edit) + { + if (rlbuf && rlbuf[rlind] == '\0') + { + free (rlbuf); + rlbuf = (char *)0; + } + if (rlbuf == 0) + { + rlbuf = edit_line (prompt ? prompt : ""); + rlind = 0; + } + if (rlbuf == 0) + { + eof = 1; + break; + } + c = rlbuf[rlind++]; + } + else +#endif + if (read (0, &c, 1) != 1) + { + eof = 1; + break; + } + if (i + 2 >= size) input_string = xrealloc (input_string, size += 128); @@ -133,7 +202,7 @@ read_builtin (list) continue; } - if (c == '\\' && !raw_mode) + if (c == '\\' && raw == 0) { pass_next++; saw_escape++; @@ -157,15 +226,31 @@ read_builtin (list) interrupt_immediately--; discard_unwind_frame ("read_builtin"); - fclose (input_stream); + retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS; - if (c == EOF) +#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) { - retval = EXECUTION_FAILURE; - /* input_string[0] = '\0'; */ + var = find_variable (arrayname); + if (var == 0) + var = make_new_array_variable (arrayname); + else if (array_p (var) == 0) + var = convert_var_to_array (var); + + empty_array (array_cell (var)); + + alist = list_string (input_string, ifs_chars, 0); + if (alist) + { + assign_array_var_from_word_list (var, alist); + dispose_words (alist); + } + free (input_string); + return (retval); } - else - retval = EXECUTION_SUCCESS; +#endif /* ARRAY_VARS */ if (!list) { @@ -179,98 +264,140 @@ read_builtin (list) var = bind_variable ("REPLY", input_string); var->attributes &= ~att_invisible; free (input_string); + return (retval); } - else + + /* 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) && issep(*t); t++) + ; + input_string = t; + + for (; list->next; list = list->next) { - /* This code implements the Posix.2 spec for splitting the words - read and assigning them to variables. If $IFS is unset, we - use the default value of " \t\n". */ - 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 && spctabnl (*t) && issep (*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 { - char *e, *t1; - - varname = list->word->word; - if (legal_identifier (varname) == 0) - { - builtin_error ("%s: not a legal variable name", varname); - free (orig_input_string); - return (EXECUTION_FAILURE); - } + builtin_error ("`%s': not a valid identifier", varname); + free (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) + /* 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) { - /* 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_variable (varname, t1); - free (t1); - } - else - var = bind_variable (varname, t); + t1 = dequote_string (t); + var = bind_read_variable (varname, t1); + free (t1); } else - { - t = (char *)0; - var = bind_variable (varname, ""); - } - - stupidly_hack_special_variables (varname); - var->attributes &= ~att_invisible; - - if (t) - free (t); + var = bind_read_variable (varname, t); + } + else + { + t = (char *)0; + var = bind_read_variable (varname, ""); } - if (legal_identifier (list->word->word) == 0) + FREE (t); + if (var == 0) { - builtin_error ("%s: not a legal variable name", list->word->word); free (orig_input_string); return (EXECUTION_FAILURE); } - /* 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); - - if (saw_escape) - { - t = dequote_string (input_string); - var = bind_variable (list->word->word, t); - free (t); - } - else - var = bind_variable (list->word->word, input_string); - stupidly_hack_special_variables (list->word->word); + stupidly_hack_special_variables (varname); var->attributes &= ~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 + { + builtin_error ("`%s': not a valid identifier", list->word->word); free (orig_input_string); + return (EXECUTION_FAILURE); } + /* 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); + + if (saw_escape) + { + t = dequote_string (input_string); + var = bind_read_variable (list->word->word, t); + free (t); + } + else + var = bind_read_variable (list->word->word, input_string); + stupidly_hack_special_variables (list->word->word); + if (var) + var->attributes &= ~att_invisible; + free (orig_input_string); + return (retval); } -/* This way I don't have to know whether fclose () is a - function or a macro. */ -static int -stream_close (file) - FILE *file; +static SHELL_VAR * +bind_read_variable (name, value) + char *name, *value; +{ +#if defined (ARRAY_VARS) + if (valid_array_reference (name) == 0) + { + if (legal_identifier (name) == 0) + { + builtin_error ("`%s': not a valid identifier", name); + return ((SHELL_VAR *)NULL); + } + return (bind_variable (name, value)); + } + else + return (do_array_element_assignment (name, value)); +#else + return bind_variable (name, value); +#endif +} + +#if defined (READLINE) +static char * +edit_line (p) + char *p; { - return (fclose (file)); + char *ret; + int len; + + if (!bash_readline_initialized) + initialize_readline (); + ret = readline (p); + if (ret == 0) + return ret; + len = strlen (ret); + ret = xrealloc (ret, len + 2); + ret[len++] = '\n'; + ret[len] = '\0'; + return ret; } +#endif diff --git a/builtins/reserved.def b/builtins/reserved.def index 4074ae0..da59050 100644 --- a/builtins/reserved.def +++ b/builtins/reserved.def @@ -43,6 +43,15 @@ in the variable REPLY. COMMANDS are executed after each selection until a break or return command is executed. $END +$BUILTIN time +$SHORT_DOC time [-p] PIPELINE +Execute PIPELINE and print a summary of the real time, user CPU time, +and system CPU time spent executing PIPELINE when it terminates. +The return status is the return status of PIPELINE. The `-p' option +prints the timing summary in a slightly different format. This uses +the value of the TIMEFORMAT variable as the output format. +$END + $BUILTIN case $SHORT_DOC case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac Selectively execute COMMANDS based upon WORD matching PATTERN. The @@ -97,10 +106,12 @@ $END $BUILTIN variables $DOCNAME variable_help $SHORT_DOC variables - Some variable names and meanings -BASH_VERSION The version numbers of this Bash. +BASH_VERSION Version information for this Bash. CDPATH A colon separated list of directories to search when the argument to `cd' is not found in the current directory. +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. @@ -108,22 +119,29 @@ 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. @@ -133,9 +151,6 @@ auto_resume Non-null means a command word appearing on a line by match a substring of the job. Any other value means that the command must be a prefix of a stopped job. #if defined (HISTORY) -command_oriented_history - Non-null means to save multiple-line commands together on - a single history line. # if defined (BANG_HISTORY) histchars Characters controlling history expansion and quick substitution. The first character is the history @@ -143,12 +158,7 @@ histchars Characters controlling history expansion and quick the `quick substitution' character, usually `^'. The third is the `history comment' character, usually `#'. # endif /* BANG_HISTORY */ -HISTCONTROL Set to a value of `ignorespace', it means don't enter - lines which begin with a space or tab on the history - list. Set to a value of `ignoredups', it means don't - enter lines which match the last entered line. Set to - `ignoreboth' means to combine the two options. Unset, - or set to any other value than those above means to save - all lines on the history list. +HISTIGNORE A colon-separated list of patterns used to decide which + command should be saved on the history list. #endif /* HISTORY */ $END diff --git a/builtins/return.def b/builtins/return.def index 8340a44..2ab01ef 100644 --- a/builtins/return.def +++ b/builtins/return.def @@ -29,11 +29,17 @@ Causes a function to exit with the return value specified by N. If N is omitted, the return status is that of the last command. $END +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" +#include "common.h" extern int last_command_exit_value; extern int return_catch_flag, return_catch_value; -extern jmp_buf return_catch; /* 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 @@ -42,16 +48,13 @@ int return_builtin (list) WORD_LIST *list; { - return_catch_value = get_numeric_arg (list); - - if (!list) - return_catch_value = last_command_exit_value; + return_catch_value = list ? get_numeric_arg (list) : last_command_exit_value; if (return_catch_flag) longjmp (return_catch, 1); else { - builtin_error ("Can only `return' from a function"); + builtin_error ("can only `return' from a function or sourced script"); return (EXECUTION_FAILURE); } } diff --git a/builtins/set.def b/builtins/set.def index a97168c..d31e77e 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -21,62 +21,79 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES set.c +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> + +#include "../bashansi.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 interactive; -extern int noclobber, no_brace_expansion, posixly_correct; +extern int noclobber, posixly_correct, ignoreeof, eof_encountered_limit; #if defined (READLINE) extern int rl_editing_mode, no_line_editing; #endif /* READLINE */ -#define USAGE_STRING "set [--abefhknotuvxldHCP] [-o option] [arg ...]" - $BUILTIN set $FUNCTION set_builtin -$SHORT_DOC set [--abefhknotuvxldHCP] [-o option] [arg ...] +$SHORT_DOC set [--abefhkmnptuvxBCHP] [-o option] [arg ...] -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 Locate and remember function commands as functions are - defined. Function commands are normally looked up when - the function is executed. + -h Remember the location of commands as they are looked up. -i Force the shell to be an "interactive" one. Interactive shells always read `~/.bashrc' on startup. - -k All keyword arguments are placed in the environment for a + -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 the shell will perform brace expansion + braceexpand same as -B #if defined (READLINE) emacs use an emacs-style line editing interface #endif /* READLINE */ errexit same as -e + hashall same as -h #if defined (BANG_HISTORY) histexpand same as -H #endif /* BANG_HISTORY */ 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 disallow redirection to existing files + noclobber same as -C noexec same as -n noglob same as -f - nohash same as -d notify save as -b nounset same as -u - physical same as -P - posix change the behavior of bash where the default - operation differs from the 1003.2 standard to - match the standard - privileged same as -p + onecmd same as -t + physical same as -P + posix change the behavior of bash where the default + operation differs from the 1003.2 standard to + match the standard + privileged same as -p verbose same as -v #if defined (READLINE) vi use a vi-style line editing interface @@ -85,15 +102,14 @@ $SHORT_DOC set [--abefhknotuvxldHCP] [-o option] [arg ...] -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. + 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. - -l Save and restore the binding of the NAME in a FOR command. - -d Disable the hashing of commands that are looked up for execution. - Normally, commands are remembered in a hash table, and once - found, do not have to be looked up again. +#if defined (BRACE_EXPANSION) + -B the shell will perform brace expansion +#endif /* BRACE_EXPANSION */ #if defined (BANG_HISTORY) -H Enable ! style history substitution. This flag is on by default. @@ -110,6 +126,20 @@ parameters and are assigned, in order, to $1, $2, .. $n. If no ARGs are given, all shell variables are printed. $END +static int set_ignoreeof (); + +#if defined (READLINE) +static int set_edit_mode (); +static int get_edit_mode (); +#endif + +#if defined (HISTORY) +static int bash_set_history (); +#endif + +static char *on = "on"; +static char *off = "off"; + /* An a-list used to match long options for set -o to the corresponding option letter. */ struct { @@ -117,201 +147,354 @@ struct { int letter; } o_options[] = { { "allexport", 'a' }, +#if defined (BRACE_EXPANSION) + { "braceexpand",'B' }, +#endif { "errexit", 'e' }, + { "hashall", 'h' }, #if defined (BANG_HISTORY) { "histexpand", 'H' }, #endif /* BANG_HISTORY */ + { "keyword", 'k' }, { "monitor", 'm' }, + { "noclobber", 'C' }, { "noexec", 'n' }, { "noglob", 'f' }, - { "nohash", 'd' }, #if defined (JOB_CONTROL) { "notify", 'b' }, #endif /* JOB_CONTROL */ - {"nounset", 'u' }, - {"physical", 'P' }, - {"privileged", 'p' }, - {"verbose", 'v' }, - {"xtrace", 'x' }, - {(char *)NULL, 0}, + { "nounset", 'u' }, + { "onecmd", 't' }, + { "physical", 'P' }, + { "privileged", 'p' }, + { "verbose", 'v' }, + { "xtrace", 'x' }, + {(char *)NULL, 0 }, +}; + +struct { + char *name; + int *variable; + Function *set_func; + Function *get_func; +} binary_o_options[] = { +#if defined (HISTORY) + { "history", &remember_on_history, bash_set_history, (Function *)NULL }, +#endif + { "ignoreeof", &ignoreeof, set_ignoreeof, (Function *)NULL }, + { "interactive-comments", &interactive_comments, (Function *)NULL, (Function *)NULL }, + { "posix", &posixly_correct, (Function *)NULL, (Function *)NULL }, +#if defined (READLINE) + { "emacs", (int *)NULL, set_edit_mode, get_edit_mode }, + { "vi", (int *)NULL, set_edit_mode, get_edit_mode }, +#endif + { (char *)NULL, (int *)NULL } }; +#define GET_BINARY_O_OPTION_VALUE(i, name) \ + ((binary_o_options[i].get_func) ? (*binary_o_options[i].get_func) (name) \ + : (*binary_o_options[i].variable)) + +#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \ + ((binary_o_options[i].set_func) ? (*binary_o_options[i].set_func) (onoff, name) \ + : (*binary_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)) + { + on_or_off = find_flag (o_options[i].letter); + return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off); + } + } + for (i = 0; binary_o_options[i].name; i++) + { + if (STREQ (name, binary_o_options[i].name)) + return (GET_BINARY_O_OPTION_VALUE (i, name)); + } + + return (-1); +} + #define MINUS_O_FORMAT "%-15s\t%s\n" void -list_minus_o_opts () +list_minus_o_opts (mode) + int mode; { register int i; - char *on = "on", *off = "off"; + int *on_or_off, value; - printf (MINUS_O_FORMAT, "braceexpand", (no_brace_expansion == 0) ? on : off); - printf (MINUS_O_FORMAT, "noclobber", (noclobber == 1) ? on : off); + for (value = i = 0; o_options[i].name; i++) + { + 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) + printf (MINUS_O_FORMAT, o_options[i].name, *on_or_off ? on : off); + } + for (i = 0; binary_o_options[i].name; i++) + { + value = GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name); + if (mode == -1 || mode == value) + printf (MINUS_O_FORMAT, binary_o_options[i].name, value ? on : off); + } +} - if (find_variable ("ignoreeof") || find_variable ("IGNOREEOF")) - printf (MINUS_O_FORMAT, "ignoreeof", on); - else - printf (MINUS_O_FORMAT, "ignoreeof", off); +static void +minus_o_option_commands () +{ + register int i; + int *on_or_off, value; - printf (MINUS_O_FORMAT, "interactive-comments", - interactive_comments ? on : off); + for (value = i = 0; o_options[i].name; i++) + { + on_or_off = find_flag (o_options[i].letter); + if (on_or_off == FLAG_UNKNOWN) + on_or_off = &value; + printf ("set %co %s\n", *on_or_off ? '-' : '+', o_options[i].name); + } + for (i = 0; binary_o_options[i].name; i++) + { + value = GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name); + printf ("set %co %s\n", value ? '-' : '+', binary_o_options[i].name); + } +} - printf (MINUS_O_FORMAT, "posix", posixly_correct ? on : off); +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"); + else + unbind_variable ("IGNOREEOF"); + sv_ignoreeof ("IGNOREEOF"); + return 0; +} #if defined (READLINE) - if (no_line_editing) +/* 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) { - printf (MINUS_O_FORMAT, "emacs", off); - printf (MINUS_O_FORMAT, "vi", off); + rl_variable_bind ("editing-mode", option_name); + + if (interactive) + with_input_from_stdin (); + no_line_editing = 0; } else { - /* Magic. This code `knows' how readline handles rl_editing_mode. */ - printf (MINUS_O_FORMAT, "emacs", (rl_editing_mode == 1) ? on : off); - printf (MINUS_O_FORMAT, "vi", (rl_editing_mode == 0) ? on : off); + 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 */ - for (i = 0; o_options[i].name; i++) +#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) { - int *on_or_off, zero = 0; - - on_or_off = find_flag (o_options[i].letter); - if (on_or_off == FLAG_UNKNOWN) - on_or_off = &zero; - printf (MINUS_O_FORMAT, o_options[i].name, (*on_or_off == 1) ? on : off); + bash_history_enable (); + if (history_lines_this_session == 0) + load_history (); } + else + bash_history_disable (); + return (1 - remember_on_history); } +#endif +int set_minus_o_option (on_or_off, option_name) int on_or_off; char *option_name; { - int option_char = -1; + int option_char; + VFunction *set_func; + register int i; - if (STREQ (option_name, "braceexpand")) + for (i = 0; binary_o_options[i].name; i++) { - if (on_or_off == FLAG_ON) - no_brace_expansion = 0; - else - no_brace_expansion = 1; + if (STREQ (option_name, binary_o_options[i].name)) + { + SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name); + return (EXECUTION_SUCCESS); + } } - else if (STREQ (option_name, "noclobber")) + + for (i = 0, option_char = -1, set_func = 0; o_options[i].name; i++) { - if (on_or_off == FLAG_ON) - bind_variable ("noclobber", ""); - else - unbind_variable ("noclobber"); - stupidly_hack_special_variables ("noclobber"); + if (STREQ (option_name, o_options[i].name)) + { + option_char = o_options[i].letter; + break; + } } - else if (STREQ (option_name, "ignoreeof")) + if (option_char == -1) { - unbind_variable ("ignoreeof"); - unbind_variable ("IGNOREEOF"); - if (on_or_off == FLAG_ON) - bind_variable ("IGNOREEOF", "10"); - stupidly_hack_special_variables ("IGNOREEOF"); + builtin_error ("%s: unknown option name", option_name); + return (EXECUTION_FAILURE); } - -#if defined (READLINE) - else if ((STREQ (option_name, "emacs")) || (STREQ (option_name, "vi"))) + if (change_flag (option_char, on_or_off) == FLAG_ERROR) { - if (on_or_off == FLAG_ON) - { - rl_variable_bind ("editing-mode", option_name); + bad_option (option_name); + return (EXECUTION_FAILURE); + } + return (EXECUTION_SUCCESS); +} - if (interactive) - with_input_from_stdin (); - no_line_editing = 0; - } - else - { - int isemacs = (rl_editing_mode == 1); - if ((isemacs && STREQ (option_name, "emacs")) || - (!isemacs && STREQ (option_name, "vi"))) - { - if (interactive) - with_input_from_stream (stdin, "stdin"); - no_line_editing = 1; - } - else - builtin_error ("not in %s editing mode", option_name); - } +static void +print_all_shell_variables () +{ + SHELL_VAR **vars; + + vars = all_shell_variables (); + if (vars) + { + print_var_list (vars); + free (vars); } -#endif /* READLINE */ - else if (STREQ (option_name, "interactive-comments")) - interactive_comments = (on_or_off == FLAG_ON); - else if (STREQ (option_name, "posix")) + + vars = all_shell_functions (); + if (vars) { - posixly_correct = (on_or_off == FLAG_ON); - unbind_variable ("POSIXLY_CORRECT"); - unbind_variable ("POSIX_PEDANTIC"); - if (on_or_off == FLAG_ON) - { - bind_variable ("POSIXLY_CORRECT", ""); - stupidly_hack_special_variables ("POSIXLY_CORRECT"); - } + print_var_list (vars); + free (vars); } - else +} + +void +set_shellopts () +{ + char *value; + int vsize, i, vptr, *ip; + SHELL_VAR *v; + + for (vsize = i = 0; o_options[i].name; i++) { - register int i; - for (i = 0; o_options[i].name; i++) - { - if (STREQ (option_name, o_options[i].name)) - { - option_char = o_options[i].letter; - break; - } - } - if (option_char == -1) - { - builtin_error ("%s: unknown option name", option_name); - return (EXECUTION_FAILURE); - } - if (change_flag (option_char, on_or_off) == FLAG_ERROR) + ip = find_flag (o_options[i].letter); + if (ip && *ip) + vsize += strlen (o_options[i].name) + 1; + } + for (i = 0; binary_o_options[i].name; i++) + if (GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name)) + vsize += strlen (binary_o_options[i].name) + 1; + + value = xmalloc (vsize + 1); + + for (i = vptr = 0; o_options[i].name; i++) + { + ip = find_flag (o_options[i].letter); + if (ip && *ip) { - bad_option (option_name); - return (EXECUTION_FAILURE); + strcpy (value + vptr, o_options[i].name); + vptr += strlen (o_options[i].name); + value[vptr++] = ':'; } } - return (EXECUTION_SUCCESS); + for (i = 0; binary_o_options[i].name; i++) + if (GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name)) + { + strcpy (value + vptr, binary_o_options[i].name); + vptr += strlen (binary_o_options[i].name); + value[vptr++] = ':'; + } + value[--vptr] = '\0'; /* cut off trailing colon */ + + v = find_variable ("SHELLOPTS"); + if (v) + v->attributes &= ~att_readonly; + v = bind_variable ("SHELLOPTS", value); + v->attributes |= att_readonly; + + 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 () +{ + char *temp; + + /* set up any shell options we may have inherited. */ + if (temp = get_string_value ("SHELLOPTS")) + parse_shellopts (temp); + + /* Set up the $SHELLOPTS variable. */ + set_shellopts (); } /* 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 = 0; + int on_or_off, flag_name, force_assignment, opts_changed; + WORD_LIST *l; + register char *arg; - if (!list) + if (list == 0) { - SHELL_VAR **vars; - - vars = all_shell_variables (); - if (vars) - { - print_var_list (vars); - free (vars); - } - - vars = all_shell_functions (); - if (vars) - { - print_var_list (vars); - free (vars); - } - + print_all_shell_variables (); return (EXECUTION_SUCCESS); } /* Check validity of flag arguments. */ if (*list->word->word == '-' || *list->word->word == '+') { - register char *arg; - WORD_LIST *save_list = list; - - while (list && (arg = list->word->word)) + for (l = list; l && (arg = l->word->word); l = l->next) { char c; @@ -319,8 +502,7 @@ set_builtin (list) break; /* `-' or `--' signifies end of flag arguments. */ - if (arg[0] == '-' && - (!arg[1] || (arg[1] == '-' && !arg[2]))) + if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2]))) break; while (c = *++arg) @@ -331,30 +513,28 @@ set_builtin (list) s[0] = c; s[1] = '\0'; bad_option (s); if (c == '?') - printf ("usage: %s\n", USAGE_STRING); + builtin_usage (); return (c == '?' ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } } - list = list->next; } - list = save_list; } /* Do the set command. While the list consists of words starting with '-' or '+' treat them as flags, otherwise, start assigning them to $1 ... $n. */ - while (list) + for (force_assignment = opts_changed = 0; list; ) { - char *string = list->word->word; + arg = list->word->word; /* If the argument is `--' or `-' then signal the end of the list and remember the remaining arguments. */ - if (string[0] == '-' && (!string[1] || (string[1] == '-' && !string[2]))) + if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2]))) { list = list->next; /* `set --' unsets the positional parameters. */ - if (string[1] == '-') + if (arg[1] == '-') force_assignment = 1; /* Until told differently, the old shell behaviour of @@ -364,20 +544,19 @@ set_builtin (list) { change_flag ('x', '+'); change_flag ('v', '+'); + opts_changed = 1; } break; } - if ((on_or_off = *string) && - (on_or_off == '-' || on_or_off == '+')) + if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+')) { - int i = 1; - while (flag_name = string[i++]) + while (flag_name = *++arg) { if (flag_name == '?') { - printf ("usage: %s\n", USAGE_STRING); + builtin_usage (); return (EXECUTION_SUCCESS); } else if (flag_name == 'o') /* -+o option-name */ @@ -387,36 +566,47 @@ set_builtin (list) opt = list->next; - if (!opt) + if (opt == 0) { - list_minus_o_opts (); + if (on_or_off == '-') + list_minus_o_opts (-1); + else + minus_o_option_commands (); continue; } option_name = opt->word->word; - if (!option_name || !*option_name || (*option_name == '-')) + if (option_name == 0 || *option_name == '\0' || + *option_name == '-' || *option_name == '+') { - list_minus_o_opts (); + if (on_or_off == '-') + list_minus_o_opts (-1); + else + minus_o_option_commands (); continue; } list = list->next; /* Skip over option name. */ + opts_changed = 1; if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS) - return (EXECUTION_FAILURE); - } - else - { - if (change_flag (flag_name, on_or_off) == FLAG_ERROR) { - char opt[3]; - opt[0] = on_or_off; - opt[1] = flag_name; - opt[2] = '\0'; - bad_option (opt); + set_shellopts (); return (EXECUTION_FAILURE); } } + else if (change_flag (flag_name, on_or_off) == FLAG_ERROR) + { + char opt[3]; + opt[0] = on_or_off; + opt[1] = flag_name; + opt[2] = '\0'; + bad_option (opt); + builtin_usage (); + set_shellopts (); + return (EXECUTION_FAILURE); + } + opts_changed = 1; } } else @@ -429,6 +619,9 @@ set_builtin (list) /* Assigning $1 ... $n */ if (list || force_assignment) remember_args (list, 1); + /* Set up new value of $SHELLOPTS */ + if (opts_changed) + set_shellopts (); return (EXECUTION_SUCCESS); } @@ -443,13 +636,17 @@ function. Some variables (such as PATH and IFS) cannot be unset; also see readonly. $END +#define NEXT_VARIABLE() any_failed++; list = list->next; continue; + +int unset_builtin (list) WORD_LIST *list; { - int unset_function = 0, unset_variable = 0, opt; - int any_failed = 0; + 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) { @@ -462,7 +659,8 @@ unset_builtin (list) unset_variable = 1; break; default: - return (EXECUTION_FAILURE); + builtin_usage (); + return (EX_USAGE); } } @@ -476,53 +674,69 @@ unset_builtin (list) while (list) { + SHELL_VAR *var; + int tem; +#if defined (ARRAY_VARS) + char *t; +#endif + name = list->word->word; - if (!unset_function && - find_name_in_list (name, non_unsettable_vars) > -1) +#if defined (ARRAY_VARS) + if (!unset_function && valid_array_reference (name)) { - builtin_error ("%s: cannot unset", name); - any_failed++; + t = strchr (name, '['); + *t++ = '\0'; + unset_array++; } - else +#endif + + var = unset_function ? find_function (name) : find_variable (name); + + if (var && !unset_function && non_unsettable_p (var)) { - SHELL_VAR *var; - int tem; + builtin_error ("%s: cannot unset", name); + NEXT_VARIABLE (); + } - var = unset_function ? find_function (name) : find_variable (name); + /* 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 (); + } - /* Posix.2 says that unsetting readonly variables is an error. */ - if (var && readonly_p (var)) + /* 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) { - builtin_error ("%s: cannot unset: readonly %s", - name, unset_function ? "function" : "variable"); - any_failed++; - list = list->next; - continue; + builtin_error ("%s: not an array variable", name); + NEXT_VARIABLE (); } - - /* Unless the -f option is supplied, the name refers to a - variable. */ - tem = makunbound - (name, unset_function ? shell_functions : shell_variables); - - /* 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 = makunbound (name, shell_functions); - - if (tem == -1) - any_failed++; - else if (!unset_function) - stupidly_hack_special_variables (name); + else + tem = unbind_array_element (var, t); } + else +#endif /* ARRAY_VARS */ + tem = makunbound (name, unset_function ? shell_functions : shell_variables); + + /* 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 = makunbound (name, shell_functions); + + if (tem == -1) + any_failed++; + else if (!unset_function) + stupidly_hack_special_variables (name); + list = list->next; } - if (any_failed) - return (EXECUTION_FAILURE); - else - return (EXECUTION_SUCCESS); + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); } diff --git a/builtins/setattr.def b/builtins/setattr.def index 2340e1a..a83f348 100644 --- a/builtins/setattr.def +++ b/builtins/setattr.def @@ -21,6 +21,15 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES setattr.c +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "../bashansi.h" + #include "../shell.h" #include "common.h" #include "bashgetopt.h" @@ -30,7 +39,7 @@ extern char *this_command_name; $BUILTIN export $FUNCTION export_builtin -$SHORT_DOC export [-n] [-f] [name ...] or export -p +$SHORT_DOC export [-nf] [name ...] or export -p NAMEs are marked for automatic export to the environment of subsequently executed commands. If the -f option is given, the NAMEs refer to functions. If no NAMEs are given, or if `-p' @@ -45,46 +54,51 @@ $END 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)); + return (set_or_show_attributes (list, att_exported, 0)); } $BUILTIN readonly $FUNCTION readonly_builtin -$SHORT_DOC readonly [-n] [-f] [name ...] or readonly -p +$SHORT_DOC readonly [-anf] [name ...] or readonly -p The given NAMEs are marked readonly and the values of these NAMEs may not be changed by subsequent assignment. If the -f option is given, then functions corresponding to the NAMEs are so marked. If no arguments are given, or if `-p' is given, a list of all readonly names is printed. An argument of `-n' says to remove the readonly property -from subsequent NAMEs. An argument of `--' disables further option +from subsequent NAMEs. The `-a' option means to treat each NAME as +an array variable. An argument of `--' disables further option processing. $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)); + return (set_or_show_attributes (list, att_readonly, 0)); } /* 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. */ int -set_or_show_attributes (list, attribute) +set_or_show_attributes (list, attribute, nodefs) register WORD_LIST *list; - int attribute; + int attribute, nodefs; { register SHELL_VAR *var; - int assign, undo = 0, functions_only = 0, any_failed = 0, opt; + int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt; + char *name; + undo = functions_only = arrays_only = any_failed = assign_error = 0; /* Read arguments from the front of the list. */ reset_internal_getopt (); - while ((opt = internal_getopt (list, "nfp")) != -1) + while ((opt = internal_getopt (list, "anfp")) != -1) { switch (opt) { @@ -94,10 +108,15 @@ set_or_show_attributes (list, attribute) case 'f': functions_only = 1; break; +#if defined (ARRAY_VARS) + case 'a': + arrays_only = 1; + break; +#endif case 'p': break; default: - builtin_error ("usage: %s [-nfp] [varname]", this_command_name); + builtin_usage (); return (EX_USAGE); } } @@ -108,85 +127,56 @@ set_or_show_attributes (list, attribute) if (attribute & att_exported) array_needs_making = 1; - /* Cannot undo readonly status. */ + /* Cannot undo readonly status, silently disallowed. */ if (undo && (attribute & att_readonly)) attribute &= ~att_readonly; while (list) { - register char *name = list->word->word; + name = list->word->word; - if (functions_only) + if (functions_only) /* xxx -f name */ { var = find_function (name); - if (!var) + if (var == 0) { builtin_error ("%s: not a function", name); any_failed++; } else - { - if (undo) - var->attributes &= ~attribute; - else - var->attributes |= attribute; - } + SETVARATTR (var, attribute, undo); + list = list->next; - if (attribute == att_exported) - array_needs_making++; continue; } + /* xxx [-np] name[=value] */ assign = assignment (name); if (assign) name[assign] = '\0'; + if (legal_identifier (name) == 0) { - builtin_error ("%s: not a legal variable name", name); - any_failed++; + builtin_error ("`%s': not a valid identifier", name); + assign_error++; list = list->next; continue; } - if (assign) + if (assign) /* xxx [-np] name=value */ { name[assign] = '='; /* 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. */ - do_assignment_no_expand (name); + 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 (undo) - { - var = find_variable (name); - if (var) - var->attributes &= ~attribute; - } - else - { - SHELL_VAR *find_tempenv_variable (), *tv; - - if (tv = find_tempenv_variable (name)) - { - var = bind_variable (tv->name, tv->value); - dispose_variable (tv); - } - else - var = find_variable (name); - - if (!var) - { - var = bind_variable (name, (char *)NULL); - var->attributes |= att_invisible; - } - - var->attributes |= attribute; - } - - array_needs_making++; /* XXX */ + set_var_attribute (name, attribute, undo); list = list->next; } } @@ -204,50 +194,133 @@ set_or_show_attributes (list, attribute) else variable_list = all_shell_variables (); +#if defined (ARRAY_VARS) + if (attribute & att_array) + { + arrays_only++; + if (attribute != att_array) + attribute &= ~att_array; + } +#endif + if (variable_list) { for (i = 0; var = variable_list[i]; i++) { - if ((var->attributes & attribute) && !invisible_p (var)) - { - char flags[6]; +#if defined (ARRAY_VARS) + if (arrays_only && array_p (var) == 0) + continue; +#endif + if ((var->attributes & attribute) && invisible_p (var) == 0) + show_var_attributes (var, nodefs); + } + free (variable_list); + } + } + + return (assign_error ? EX_BADASSIGN + : ((any_failed == 0) ? EXECUTION_SUCCESS + : EXECUTION_FAILURE)); +} + +int +show_var_attributes (var, nodefs) + SHELL_VAR *var; + int nodefs; +{ + char flags[6], *x; + int i; - flags[0] = '\0'; + i = 0; - if (exported_p (var)) - strcat (flags, "x"); +#if defined (ARRAY_VARS) + if (array_p (var)) + flags[i++] = 'a'; +#endif - if (readonly_p (var)) - strcat (flags, "r"); + if (function_p (var)) + flags[i++] = 'f'; - if (function_p (var)) - strcat (flags, "f"); + if (integer_p (var)) + flags[i++] = 'i'; - if (integer_p (var)) - strcat (flags, "i"); + if (readonly_p (var)) + flags[i++] = 'r'; - if (flags[0]) - { - printf ("declare -%s ", flags); + if (exported_p (var)) + flags[i++] = 'x'; - if (!function_p (var)) - { - char *x = double_quote (value_cell (var)); - printf ("%s=%s\n", var->name, x); - free (x); - } - else - { - char *named_function_string (); + flags[i] = '\0'; - printf ("%s\n", named_function_string - (var->name, function_cell (var), 1)); - } - } - } - } - free (variable_list); + printf ("declare -%s ", i ? flags : "-"); + +#if defined (ARRAY_VARS) + if (array_p (var)) + print_array_assignment (var, 1); + else +#endif + if (nodefs) + printf ("%s\n", var->name); + else if (function_p (var)) + printf ("%s\n", named_function_string (var->name, function_cell (var), 1)); + else + { + x = double_quote (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_tempenv_variable (name); + if (var == 0) + var = find_variable (name); + + if (var && invisible_p (var) == 0) + { + show_var_attributes (var, nodefs); + return (0); + } + else + return (1); +} + +void +set_var_attribute (name, attribute, undo) + char *name; + int attribute, undo; +{ + SHELL_VAR *var, *tv; + + if (undo) + var = find_variable (name); + else + { + if (tv = find_tempenv_variable (name)) + { + var = bind_variable (tv->name, tv->value); + dispose_variable (tv); + } + else + var = find_variable (name); + + if (var == 0) + { + var = bind_variable (name, (char *)NULL); + var->attributes |= att_invisible; } } - return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + + if (var) + SETVARATTR (var, attribute, undo); + + if (var && ((var->attributes & att_exported) || (attribute & att_exported))) + array_needs_making++; /* XXX */ } diff --git a/builtins/shift.def b/builtins/shift.def index 4d8fed0..8af4d5d 100644 --- a/builtins/shift.def +++ b/builtins/shift.def @@ -21,13 +21,16 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. $PRODUCES shift.c -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include "../shell.h" +#include "common.h" $BUILTIN shift $FUNCTION shift_builtin @@ -36,6 +39,8 @@ The positional parameters from $N+1 ... are renamed to $1 ... If N is not given, it is assumed to be 1. $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 @@ -44,34 +49,28 @@ int shift_builtin (list) WORD_LIST *list; { - int times, number; - WORD_LIST *args; + int times; + register int count; + WORD_LIST *temp; times = get_numeric_arg (list); - if (!times) + if (times == 0) return (EXECUTION_SUCCESS); - - if (times < 0) + else if (times < 0) { builtin_error ("shift count must be >= 0"); return (EXECUTION_FAILURE); } - - args = list_rest_of_args (); - number = list_length (args); - dispose_words (args); - - if (times > number) + else if (times > number_of_args ()) { - builtin_error ("shift count must be <= $#"); + if (print_shift_error) + builtin_error ("shift count must be <= $#"); return (EXECUTION_FAILURE); } while (times-- > 0) { - register int count; - if (dollar_vars[1]) free (dollar_vars[1]); @@ -80,8 +79,7 @@ shift_builtin (list) if (rest_of_args) { - WORD_LIST *temp = 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; @@ -90,6 +88,5 @@ shift_builtin (list) 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..5372bbb --- /dev/null +++ b/builtins/shopt.def @@ -0,0 +1,343 @@ +This file is shopt.def, from which is created shopt.c. +It implements the Bash `shopt' builtin. + +Copyright (C) 1994 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 1, 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; see the file COPYING. If not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +$PRODUCES shopt.c + +$BUILTIN shopt +$DOCNAME shopt_builtin +$FUNCTION shopt_builtin +$SHORT_DOC shopt [-pqsu] [-o long-option] optname [optname...] +Toggle the values of variables controlling optional behavior. +The -s flag means to enable (set) each OPTNAME; the -u flag +unsets each OPTNAME. The -q flag suppresses output; the exit +status indicates whether each OPTNAME is set or unset. The -o +option restricts the OPTNAMEs to those defined for use with +`set -o'. With no options, or with the -p option, a list of all +settable options is displayed, with an indication of whether or +not each is set. +$END + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "../shell.h" +#include "../flags.h" +#include "common.h" +#include "bashgetopt.h" + +#define UNSETOPT 0 +#define SETOPT 1 + +#define OPTFMT "%-15s\t%s\n" + +extern int allow_null_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, interactive_comments; +extern int cdspelling, expand_aliases; +extern int check_window_size; + +#if defined (HISTORY) +extern int hist_verify, literal_history, command_oriented_history; +extern int force_append_history; +#endif + +#if defined (READLINE) +extern int history_reediting, perform_hostname_completion; +extern void enable_hostname_completion (); +#endif + +static int set_interactive_comments (); + +static struct { + char *name; + int *value; + Function *set_func; +} shopt_vars[] = { + { "cdable_vars", &cdable_vars, (Function *)NULL }, + { "cdspell", &cdspelling, (Function *)NULL }, + { "checkhash", &check_hashed_filenames, (Function *)NULL }, + { "checkwinsize", &check_window_size, (Function *)NULL }, +#if defined (HISTORY) + { "cmdhist", &command_oriented_history, (Function *)NULL }, +#endif + { "dotglob", &glob_dot_filenames, (Function *)NULL }, + { "execfail", &no_exit_on_failed_exec, (Function *)NULL }, + { "expand_aliases", &expand_aliases, (Function *)NULL }, +#if defined (READLINE) + { "histreedit", &history_reediting, (Function *)NULL }, +#endif +#if defined (HISTORY) + { "histappend", &force_append_history, (Function *)NULL }, + { "histverify", &hist_verify, (Function *)NULL }, +#endif +#if defined (READLINE) + { "hostcomplete", &perform_hostname_completion, (Function *)enable_hostname_completion }, +#endif + { "interactive_comments", &interactive_comments, set_interactive_comments }, +#if defined (HISTORY) + { "lithist", &literal_history, (Function *)NULL }, +#endif + { "mailwarn", &mail_warning, (Function *)NULL }, + { "nullglob", &allow_null_glob_expansion, (Function *)NULL }, + { "promptvars", &promptvars, (Function *)NULL }, + { "shift_verbose", &print_shift_error, (Function *)NULL }, + { "sourcepath", &source_uses_path, (Function *)NULL }, + { (char *)0, (int *)0, (Function *)NULL } +}; + +static char *on = "on"; +static char *off = "off"; + +static int list_shopt_o_options (); +static int list_some_o_options (), list_some_shopts (); +static int toggle_shopts (), list_shopts (), set_shopt_o_options (); + +#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 & QFLAG); + 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) ? FLAG_ON : FLAG_OFF, flags & QFLAG); + 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 & QFLAG); + else /* shopt -su */ + rval = list_some_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, flags & QFLAG); + return (rval); +} + +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; +} + +#define SHOPT_ERROR(str) builtin_error ("%s: unknown shell option name", str) + +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) (mode); + } + } + return (rval); +} + +/* 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, quiet) + WORD_LIST *list; + int quiet; +{ + WORD_LIST *l; + int i, val, rval; + + if (list == 0) + { + for (i = 0; shopt_vars[i].name; i++) + { + val = *shopt_vars[i].value; + if (quiet == 0) + printf (OPTFMT, shopt_vars[i].name, val ? on : off); + } + return (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 (quiet == 0) + printf (OPTFMT, l->word->word, val ? on : off); + } + return (rval); +} + +static int +list_some_shopts (mode, quiet) + int mode, quiet; +{ + int val, i; + + for (i = 0; shopt_vars[i].name; i++) + { + val = *shopt_vars[i].value; + if (quiet == 0 && mode == val) + printf (OPTFMT, shopt_vars[i].name, val ? on : off); + } + return (EXECUTION_SUCCESS); +} + +static int +list_shopt_o_options (list, quiet) + WORD_LIST *list; + int quiet; +{ + WORD_LIST *l; + int val, rval; + + if (list == 0) + { + if (quiet == 0) + list_minus_o_opts (-1); + return (EXECUTION_SUCCESS); + } + + for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next) + { + val = minus_o_option_value (l->word->word); + if (val == -1) + { + builtin_error ("%s: unknown option name", l->word->word); + rval = EXECUTION_FAILURE; + continue; + } + if (val == 0) + rval = EXECUTION_FAILURE; + if (quiet == 0) + printf (OPTFMT, l->word->word, val ? "on" : "off"); + } + return (rval); +} + +static int +list_some_o_options (mode, quiet) + int mode, quiet; +{ + if (quiet == 0) + list_minus_o_opts (mode); + return (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_interactive_comments (mode) + int mode; +{ + set_shellopts (); + return (0); +} diff --git a/builtins/source.def b/builtins/source.def index 895e98b..1bb3e73 100644 --- a/builtins/source.def +++ b/builtins/source.def @@ -36,34 +36,34 @@ in $PATH are used to find the directory containing FILENAME. $END /* source.c - Implements the `.' and `source' builtins. */ -#include <sys/types.h> +#include <config.h> + +#include "../bashtypes.h" +#include "../posixstat.h" +#include "../filecntl.h" #include <sys/file.h> #include <errno.h> -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include "../shell.h" -#include "../posixstat.h" -#include "../filecntl.h" #include "../execute_cmd.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 */ -/* Variables used here but defined in other files. */ -extern int return_catch_flag, return_catch_value; -extern jmp_buf return_catch; -extern int posixly_correct; -extern int interactive, interactive_shell, last_command_exit_value; +#if defined (RESTRICTED_SHELL) +extern int restricted; +#endif -/* How many `levels' of sourced files we have. */ -int sourcelevel = 0; +/* If non-zero, `.' uses $PATH to look up the script to be sourced. */ +int source_uses_path = 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 @@ -86,101 +86,51 @@ maybe_pop_dollar_vars () 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, return_val; - - /* Assume the best. */ - result = EXECUTION_SUCCESS; + int result; + char *filename; - if (list) + if (list == 0) { - char *string, *filename; - struct stat finfo; - int fd, tt; - - filename = find_path_file (list->word->word); - if (!filename) - filename = savestring (list->word->word); - - if (((fd = open (filename, O_RDONLY)) < 0) || (fstat (fd, &finfo) < 0)) - goto file_error_exit; - - string = (char *)xmalloc (1 + (int)finfo.st_size); - tt = read (fd, string, finfo.st_size); - string[finfo.st_size] = '\0'; - - /* Close the open file, preserving the state of errno. */ - { int temp = errno; close (fd); errno = temp; } - - if (tt != finfo.st_size) - { - free (string); - - file_error_exit: - file_error (filename); - free (filename); - - /* POSIX shells exit if non-interactive and file error. */ - if (posixly_correct && !interactive_shell) - { - last_command_exit_value = 1; - longjmp (top_level, EXITPROG); - } - - return (EXECUTION_FAILURE); - } - - if (tt > 80) - tt = 80; - - if (check_binary_file ((unsigned char *)string, tt)) - { - free (string); - builtin_error ("%s: cannot execute binary file", filename); - free (filename); - return (EX_BINARY_FILE); - } - - begin_unwind_frame ("File Sourcing"); - - if (list->next) - { - push_dollar_vars (); - add_unwind_protect ((Function *)maybe_pop_dollar_vars, (char *)NULL); - remember_args (list->next, 1); - } - - unwind_protect_int (return_catch_flag); - unwind_protect_jmp_buf (return_catch); - unwind_protect_int (interactive); - unwind_protect_int (sourcelevel); - add_unwind_protect ((Function *)xfree, filename); - interactive = 0; - sourcelevel++; + builtin_error ("filename argument required"); + builtin_usage (); + return (EX_USAGE); + } - set_dollar_vars_unchanged (); + if (no_options (list)) + return (EX_USAGE); - return_catch_flag++; - return_val = setjmp (return_catch); +#if defined (RESTRICTED_SHELL) + if (restricted && strchr (list->word->word, '/')) + { + builtin_error ("%s: restricted", list->word->word); + return (EXECUTION_FAILURE); + } +#endif - if (return_val) - parse_and_execute_cleanup (); - else - result = parse_and_execute (string, filename, -1); + filename = (char *)NULL; + if (source_uses_path) + filename = find_path_file (list->word->word); + if (filename == 0) + filename = savestring (list->word->word); - run_unwind_frame ("File Sourcing"); + begin_unwind_frame ("source"); + add_unwind_protect ((Function *)xfree, filename); - /* If RETURN_VAL is non-zero, then we return the value given - to return_builtin (), since that is how we got here. */ - if (return_val) - result = return_catch_value; - } - else + if (list->next) { - builtin_error ("filename argument required"); - result = EXECUTION_FAILURE; + push_dollar_vars (); + add_unwind_protect ((Function *)maybe_pop_dollar_vars, (char *)NULL); + remember_args (list->next, 1); } + set_dollar_vars_unchanged (); + + result = source_file (filename); + + run_unwind_frame ("source"); + return (result); } diff --git a/builtins/suspend.def b/builtins/suspend.def index 48edc20..151c34d 100644 --- a/builtins/suspend.def +++ b/builtins/suspend.def @@ -30,15 +30,23 @@ signal. The `-f' if specified says not to complain about this being a login shell if it is; just suspend anyway. $END -#include <sys/types.h> +#include <config.h> + +#if defined (JOB_CONTROL) +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashtypes.h" #include <signal.h> #include "../shell.h" #include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" -#if defined (JOB_CONTROL) extern int job_control; -static SigHandler *old_cont, *old_tstp; +static SigHandler *old_cont, *old_stop; /* Continue handler. */ sighandler @@ -46,10 +54,10 @@ suspend_continue (sig) int sig; { set_signal_handler (SIGCONT, old_cont); - set_signal_handler (SIGTSTP, old_tstp); -#if !defined (VOID_SIGHANDLER) - return (0); -#endif /* !VOID_SIGHANDLER */ +#if 0 + set_signal_handler (SIGSTOP, old_stop); +#endif + SIGRETURN (0); } /* Suspending the shell. If -f is the arg, then do the suspend @@ -58,28 +66,45 @@ int suspend_builtin (list) WORD_LIST *list; { - if (!job_control) + 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) { - builtin_error ("Cannot suspend a shell without job control"); + builtin_error ("cannot suspend a shell without job control"); return (EXECUTION_FAILURE); } - if (list) - if (strcmp (list->word->word, "-f") == 0) - goto do_suspend; - - no_args (list); - - if (login_shell) + if (force == 0) { - builtin_error ("Can't suspend a login shell"); - return (EXECUTION_FAILURE); + no_args (list); + + if (login_shell) + { + builtin_error ("cannot suspend a login shell"); + return (EXECUTION_FAILURE); + } } -do_suspend: old_cont = (SigHandler *)set_signal_handler (SIGCONT, suspend_continue); - old_tstp = (SigHandler *)set_signal_handler (SIGTSTP, SIG_DFL); - killpg (shell_pgrp, SIGTSTP); +#if 0 + old_stop = (SigHandler *)set_signal_handler (SIGSTOP, SIG_DFL); +#endif + killpg (shell_pgrp, SIGSTOP); return (EXECUTION_SUCCESS); } diff --git a/builtins/test.def b/builtins/test.def index 2b1457b..03de47d 100644 --- a/builtins/test.def +++ b/builtins/test.def @@ -37,12 +37,12 @@ File operators: -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. Use "-L". + -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. + -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 is not empty. + -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. @@ -63,12 +63,16 @@ String operators: -z STRING True if string is empty. -n STRING - or STRING True if string is not empty. + 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: @@ -88,18 +92,21 @@ $BUILTIN [ $DOCNAME test_bracket $FUNCTION test_builtin $SHORT_DOC [ arg... ] -This is a synonym for the "test" shell builtin, excepting that the -last argument must be literally `]', to match the `[' which invoked -the test. +This is a synonym for the "test" builtin, but the last +argument must be a literal `]', to match the opening `['. $END -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "../bashansi.h" #include "../shell.h" +#include "common.h" + extern char *this_command_name; /* TEST/[ builtin. */ @@ -109,12 +116,11 @@ test_builtin (list) { char **argv; int argc, result; - WORD_LIST *t = list; /* 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 his code wants. */ - if (!list) + of strings, since that is what their code wants. */ + if (list == 0) { if (this_command_name[0] == '[' && !this_command_name[1]) builtin_error ("missing `]'"); @@ -122,23 +128,9 @@ test_builtin (list) return (EXECUTION_FAILURE); } - /* Get the length of the argument list. */ - for (argc = 0; t; t = t->next, argc++); - - /* Account for argv[0] being a command name. This makes our life easier. */ - argc++; - argv = (char **)xmalloc ((1 + argc) * sizeof (char *)); - argv[argc] = (char *)NULL; - - /* this_command_name is the name of the command that invoked this - function. So you can't call test_builtin () directly from - within this code, there are too many things to worry about. */ - argv[0] = savestring (this_command_name); - - for (t = list, argc = 1; t; t = t->next, argc++) - argv[argc] = savestring (t->word->word); - + argv = make_builtin_argv (list, &argc); result = test_command (argc, argv); - free_array (argv); + free ((char *)argv); + return (result); } diff --git a/builtins/times.def b/builtins/times.def index 9c42768..216abcb 100644 --- a/builtins/times.def +++ b/builtins/times.def @@ -28,62 +28,79 @@ Print the accumulated user and system times for processes run from the shell. $END -#include "../shell.h" -#include <sys/types.h> +#include <config.h> -#if defined (hpux) || defined (USGr4) || defined (XD88) || defined (USGr3) -# undef HAVE_RESOURCE -#endif /* hpux || USGr4 || XD88 || USGr3 */ +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif -#if defined (_POSIX_VERSION) || !defined (HAVE_RESOURCE) -# include <sys/times.h> -#else /* !_POSIX_VERSION && HAVE_RESOURCE */ +#include <stdio.h> +#include "../bashtypes.h" +#include "../shell.h" + +#if TIME_WITH_SYS_TIME # include <sys/time.h> +# include <time.h> +#else +# if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if defined (HAVE_SYS_TIMES_H) +# include <sys/times.h> +#endif /* HAVE_SYS_TIMES_H */ + +#if defined (HAVE_SYS_RESOURCE_H) # include <sys/resource.h> -#endif /* !_POSIX_VERSION && HAVE_RESOURCE */ +#endif + +#include "common.h" -/* Print the totals for system and user time used. The - information comes from variables in jobs.c used to keep - track of this stuff. */ +/* Print the totals for system and user time used. */ +int times_builtin (list) WORD_LIST *list; { -#if !defined (_POSIX_VERSION) && defined (HAVE_RESOURCE) && defined (RUSAGE_SELF) +#if defined (HAVE_GETRUSAGE) && defined (HAVE_TIMEVAL) && defined (RUSAGE_SELF) struct rusage self, kids; getrusage (RUSAGE_SELF, &self); getrusage (RUSAGE_CHILDREN, &kids); /* terminated child processes */ - print_timeval (&self.ru_utime); + print_timeval (stdout, &self.ru_utime); putchar (' '); - print_timeval (&self.ru_stime); + print_timeval (stdout, &self.ru_stime); putchar ('\n'); - print_timeval (&kids.ru_utime); + print_timeval (stdout, &kids.ru_utime); putchar (' '); - print_timeval (&kids.ru_stime); + print_timeval (stdout, &kids.ru_stime); putchar ('\n'); -#else /* _POSIX_VERSION || !HAVE_RESOURCE || !RUSAGE_SELF */ -# if !defined (BrainDeath) - struct tms t; - - times (&t); - +#else +# if defined (HAVE_TIMES) /* As of System V.3, HP-UX 6.5, and other ATT-like systems, this stuff is returned in terms of clock ticks (HZ from sys/param.h). C'mon, guys. This kind of stupid clock-dependent stuff is exactly the reason 4.2BSD introduced the `timeval' struct. */ + struct tms t; + + times (&t); - print_time_in_hz (t.tms_utime); + print_time_in_hz (stdout, t.tms_utime); putchar (' '); - print_time_in_hz (t.tms_stime); + print_time_in_hz (stdout, t.tms_stime); putchar ('\n'); - print_time_in_hz (t.tms_cutime); + print_time_in_hz (stdout, t.tms_cutime); putchar (' '); - print_time_in_hz (t.tms_cstime); + print_time_in_hz (stdout, t.tms_cstime); putchar ('\n'); -# endif /* BrainDeath */ -#endif /* _POSIX_VERSION || !HAVE_RESOURCE || !RUSAGE_SELF */ +# else /* !HAVE_TIMES */ + printf ("0.00 0.00\n0.00 0.00\n"); +# endif /* HAVE_TIMES */ +#endif /* !HAVE_TIMES */ return (EXECUTION_SUCCESS); } diff --git a/builtins/trap.def b/builtins/trap.def index b81651d..f6a8ee4 100644 --- a/builtins/trap.def +++ b/builtins/trap.def @@ -23,30 +23,46 @@ $PRODUCES trap.c $BUILTIN trap $FUNCTION trap_builtin -$SHORT_DOC trap [arg] [signal_spec] +$SHORT_DOC trap [arg] [signal_spec] or trap -l The command ARG is to be read and executed when the shell receives signal(s) SIGNAL_SPEC. If ARG is absent all specified signals are -reset to their original values. If ARG is the null string this -signal is ignored by the shell and by the commands it invokes. If -SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from -the shell. The trap command with no arguments prints the list of -commands associated with each signal number. SIGNAL_SPEC is either -a signal name in <signal.h>, or a signal number. The syntax `trap -l' -prints a list of signal names and their corresponding numbers. -Note that a signal can be sent to the shell with "kill -signal $$". +reset to their original values. If ARG is the null string each +SIGNAL_SPEC is ignored by the shell and by the commands it invokes. +If SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from +the shell. If SIGNAL_SPEC is DEBUG, ARG is executed after every +command. If ARG is `-p' then the trap commands associated with +each SIGNAL_SPEC are displayed. If no arguments are supplied or if +only `-p' is given, trap prints the list of commands associated with +each signal number. SIGNAL_SPEC is either a signal name in <signal.h> +or a signal number. `trap -l' prints a list of signal names and their +corresponding numbers. Note that a signal can be sent to the shell +with "kill -signal $$". $END -#include <sys/types.h> +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# 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 int display_traps (); /* 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. @@ -62,60 +78,44 @@ $END extern int interactive; +int trap_builtin (list) WORD_LIST *list; { - register int i; - int list_signal_names = 0; + int list_signal_names, display, result, opt; - while (list) + list_signal_names = display = 0; + result = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lp")) != -1) { - if (ISOPTION (list->word->word, 'l')) + switch (opt) { + case 'l': list_signal_names++; - list = list->next; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; break; - } - else if ((*list->word->word == '-') && list->word->word[1]) - { - bad_option (list->word->word); - builtin_error ("usage: trap [-l] [arg] [sigspec]"); + case 'p': + display++; + break; + default: + builtin_usage (); return (EX_USAGE); } - else - break; } + list = loptend; if (list_signal_names) + return (display_signal_list ((WORD_LIST *)NULL, 1)); + else if (display || list == 0) + return (display_traps (list)); + else { - int column = 0; - - for (i = 0; i < NSIG; i++) - { - printf ("%2d) %s", i, signal_name (i)); - if (++column < 4) - printf ("\t"); - else - { - printf ("\n"); - column = 0; - } - } - if (column != 0) - printf ("\n"); - return (EXECUTION_SUCCESS); - } + char *first_arg; + int operation, sig; - if (list) - { - char *first_arg = list->word->word; - int operation = SET, any_failed = 0; - - if (signal_object_p (first_arg)) + operation = SET; + first_arg = list->word->word; + if (first_arg && *first_arg && signal_object_p (first_arg)) operation = REVERT; else { @@ -128,15 +128,13 @@ trap_builtin (list) while (list) { - int sig; - sig = decode_signal (list->word->word); if (sig == NO_SIG) { builtin_error ("%s: not a signal specification", list->word->word); - any_failed++; + result = EXECUTION_FAILURE; } else { @@ -183,22 +181,52 @@ trap_builtin (list) } list = list->next; } - return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } - for (i = 0; i < NSIG; i++) - { - char *t, *p; + return (result); +} + +static void +showtrap (i) + int i; +{ + char *t, *p; + + p = trap_list[i]; + + if (p == (char *)DEFAULT_SIG) + return; + + t = (p == (char *)IGNORE_SIG) ? (char *)NULL : single_quote (p); + printf ("trap -- %s %s\n", t ? t : "''", signal_name (i)); + if (t) + free (t); +} - p = trap_list[i]; +static int +display_traps (list) + WORD_LIST *list; +{ + int result, i; - if (p == (char *)DEFAULT_SIG) - continue; + if (list == 0) + { + for (i = 0; i <= NSIG; i++) + showtrap (i); + return (EXECUTION_SUCCESS); + } - t = (p == (char *)IGNORE_SIG) ? (char *)NULL : single_quote (p); - printf ("trap -- %s %s\n", t ? t : "''", signal_name (i)); - if (t) - free (t); + for (result = EXECUTION_SUCCESS; list; list = list->next) + { + i = decode_signal (list->word->word); + if (i == NO_SIG) + { + result = EXECUTION_FAILURE; + builtin_error ("%s: not a signal specification", list->word->word); + } + else + showtrap (i); } - return (EXECUTION_SUCCESS); + + return (result); } diff --git a/builtins/type.def b/builtins/type.def index aecc303..5e828c6 100644 --- a/builtins/type.def +++ b/builtins/type.def @@ -23,26 +23,38 @@ $PRODUCES type.c $BUILTIN type $FUNCTION type_builtin -$SHORT_DOC type [-all] [-type | -path] [name ...] +$SHORT_DOC type [-apt] name [name ...] For each NAME, indicate how it would be interpreted if used as a command name. -If the -type flag is used, returns a single word which is one of +If the -t option is used, returns 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 unfound, respectively. -If the -path flag is used, either returns the name of the disk file -that would be exec'ed, or nothing if -type wouldn't return `file'. +If the -p flag is used, either returns the name of the disk file +that would be executed, or nothing if -t would not return `file'. -If the -all flag is used, displays all of the places that contain an +If the -a flag is used, displays all of the places that contain an executable named `file'. This includes aliases and functions, if and -only if the -path flag is not also used. +only if the -p flag is not also used. + +Type accepts -all, -path, and -type in place of -a, -p, and -t, +respectively. $END -#include <stdio.h> -#include <sys/types.h> +#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 "../shell.h" #include "../execute_cmd.h" @@ -52,7 +64,7 @@ $END #include "common.h" -extern STRING_INT_ALIST word_token_alist[]; +extern int find_reserved_word (); /* 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 @@ -75,18 +87,19 @@ extern STRING_INT_ALIST word_token_alist[]; builtin file */ +int type_builtin (list) WORD_LIST *list; { int path_only, type_only, all, verbose; int successful_finds; + if (list == 0) + return (EXECUTION_SUCCESS); + path_only = type_only = all = 0; successful_finds = 0; - if (!list) - return (EXECUTION_SUCCESS); - while (list && *(list->word->word) == '-') { char *flag = &(list->word->word[1]); @@ -108,7 +121,7 @@ type_builtin (list) else { bad_option (flag); - builtin_error ("usage: type [-all | -path | -type ] name [name ...]"); + builtin_usage (); return (EX_USAGE); } list = list->next; @@ -116,7 +129,7 @@ type_builtin (list) if (type_only) verbose = 1; - else if (!path_only) + else if (path_only == 0) verbose = 2; else if (path_only) verbose = 3; @@ -156,22 +169,29 @@ type_builtin (list) * ALL says whether or not to look for all occurrences of COMMAND, or * return after finding it once. */ +int describe_command (command, verbose, all) char *command; int verbose, all; { - int found = 0, i, found_file = 0; - char *full_path = (char *)NULL; + int found, i, found_file; + char *full_path; SHELL_VAR *func; +#if defined (ALIAS) + alias_t *alias; +#endif + + found = found_file = 0; + full_path = (char *)NULL; #if defined (ALIAS) /* Command is an alias? */ - ASSOC *alias = find_alias (command); + alias = find_alias (command); if (alias) { if (verbose == 1) - printf ("alias\n"); + puts ("alias"); else if (verbose == 2) printf ("%s is aliased to `%s'\n", command, alias->value); else if (verbose == 4) @@ -193,7 +213,7 @@ describe_command (command, verbose, all) if (i >= 0) { if (verbose == 1) - printf ("keyword\n"); + puts ("keyword"); else if (verbose == 2) printf ("%s is a shell keyword\n", command); else if (verbose == 4) @@ -211,7 +231,7 @@ describe_command (command, verbose, all) if (func) { if (verbose == 1) - printf ("function\n"); + puts ("function"); else if (verbose == 2) { #define PRETTY_PRINT_FUNC 1 @@ -240,7 +260,7 @@ describe_command (command, verbose, all) if (find_shell_builtin (command)) { if (verbose == 1) - printf ("builtin\n"); + puts ("builtin"); else if (verbose == 2) printf ("%s is a shell builtin\n", command); else if (verbose == 4) @@ -261,7 +281,7 @@ describe_command (command, verbose, all) if (f & FS_EXECABLE) { if (verbose == 1) - printf ("file\n"); + puts ("file"); else if (verbose == 2) printf ("%s is %s\n", command, command); else if (verbose == 3 || verbose == 4) @@ -281,7 +301,7 @@ describe_command (command, verbose, all) if ((full_path = find_hashed_filename (command)) != (char *)NULL) { if (verbose == 1) - printf ("file\n"); + puts ("file"); else if (verbose == 2) printf ("%s is hashed (%s)\n", command, full_path); else if (verbose == 3 || verbose == 4) @@ -308,7 +328,7 @@ describe_command (command, verbose, all) found = 1; if (verbose == 1) - printf ("file\n"); + puts ("file"); else if (verbose == 2) printf ("%s is %s\n", command, full_path); else if (verbose == 3 || verbose == 4) diff --git a/builtins/ulimit.def b/builtins/ulimit.def index 1947c36..546dfd4 100644 --- a/builtins/ulimit.def +++ b/builtins/ulimit.def @@ -24,7 +24,7 @@ $PRODUCES ulimit.c $BUILTIN ulimit $FUNCTION ulimit_builtin $DEPENDS_ON !MINIX -$SHORT_DOC ulimit [-SHacdfmstpnuv [limit]] +$SHORT_DOC ulimit [-SHacdflmnpstuv] [limit] Ulimit provides control over the resources available to processes started by the shell, on systems that allow such control. If an option is given, it is interpreted as follows: @@ -34,52 +34,68 @@ option is given, it is interpreted as follows: -a all current limits are reported -c the maximum size of core files created -d the maximum size of a process's data segment + -f the maximum size of files created by the shell + -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 -s the maximum stack size -t the maximum amount of cpu time in seconds - -f the maximum size of files created by the shell - -p the pipe buffer size - -n the maximum number of open file descriptors -u the maximum number of user processes -v the size of virtual memory If LIMIT is given, it is the new value of the specified resource. Otherwise, the current value of the specified resource is printed. -If no option is given, then -f is assumed. Values are in 1k +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. $END -#include <stdio.h> -#include <sys/types.h> +#include <config.h> + +#include "../bashtypes.h" #include <sys/param.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> #include <errno.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 #else # include <sys/times.h> #endif -#if defined (HAVE_UNISTD_H) -# include <unistd.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 (GETRLIMIT_MISSING) +#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT) # undef HAVE_RESOURCE #endif @@ -89,509 +105,412 @@ extern int errno; # define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "") #endif -static void print_long (); +#define DESCFMT "%-28s" -/* **************************************************************** */ -/* */ -/* Ulimit builtin and Hacks. */ -/* */ -/* **************************************************************** */ +/* 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 */ -/* Block size for ulimit operations. */ -#define ULIMIT_BLOCK_SIZE ((long)1024) +/* 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 -#define u_FILE_SIZE 0x001 -#define u_MAX_BREAK_VAL 0x002 -#define u_PIPE_SIZE 0x004 -#define u_CORE_FILE_SIZE 0x008 -#define u_DATA_SEG_SIZE 0x010 -#define u_PHYS_MEM_SIZE 0x020 -#define u_CPU_TIME_LIMIT 0x040 -#define u_STACK_SIZE 0x080 -#define u_NUM_OPEN_FILES 0x100 -#define u_MAX_VIRTUAL_MEM 0x200 -#define u_MAX_USER_PROCS 0x400 +#ifdef RLIMIT_NOFILE +# define RLIMIT_OPENFILES RLIMIT_NOFILE +#else +# define RLIMIT_OPENFILES 258 +#endif -#define u_ALL_LIMITS 0x7ff +#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 +# define RLIM_INFINITY 0x7fffffff #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 */ +#if !defined (RLIM_INVALID) +# define RLIM_INVALID (RLIMTYPE)-1 +#endif #define LIMIT_HARD 0x01 #define LIMIT_SOFT 0x02 -static RLIMTYPE shell_ulimit (); +static int ulimit_internal (); +static void printone (); +static void print_all_limits (); + +static int get_limit (); +static int set_limit (); + +static RLIMTYPE filesize (); static RLIMTYPE pipesize (); -static RLIMTYPE open_files (); +static RLIMTYPE getmaxuprc (); +static RLIMTYPE getmaxvm (); + +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. */ + char *description; /* Descriptive string to output. */ +} RESOURCE_LIMITS; +static RESOURCE_LIMITS limits[] = { +#ifdef RLIMIT_CORE + { 'c', RLIMIT_CORE, 1024, "core file size (blocks)" }, +#endif +#ifdef RLIMIT_DATA + { 'd', RLIMIT_DATA, 1024, "data seg size (kbytes)" }, +#endif + { 'f', RLIMIT_FILESIZE, 1024, "file size (blocks)" }, +#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" }, + { 'p', RLIMIT_PIPESIZE, 512, "pipe size (512 bytes)" }, +#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" }, #if defined (HAVE_RESOURCE) -static RLIMTYPE getmaxvm (); -#endif /* HAVE_RESOURCE */ + { 'v', RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory (kbytes)" }, +#endif + { -1, -1, -1, (char *)NULL } +}; +#define NCMDS (sizeof(limits) / sizeof(limits[0])) -static void print_specific_limits (); -static void print_all_limits (); +typedef struct _cmd { + int cmd; + char *arg; +} ULCMD; -static char t[2]; +static ULCMD *cmdlist; +static int ncmd; +static int cmdlistsz; -/* Return 1 if the limit associated with CMD can be raised from CURRENT - to NEW. This is for USG systems without HAVE_RESOURCE, most of which - do not allow any user other than root to raise limits. There are, - however, exceptions. */ -#if !defined (HAVE_RESOURCE) static int -canraise (cmd, current, new) - int cmd; - RLIMTYPE current, new; +_findlim (opt) + int opt; { -# if defined (HAVE_SETDTABLESIZE) - if (cmd == u_NUM_OPEN_FILES) - return (1); -# endif /* HAVE_SETDTABLSIZE */ + register int i; - return ((current > new) || (current_user.uid == 0)); + for (i = 0; limits[i].option > 0; i++) + if (limits[i].option == opt) + return i; + return -1; } -#endif /* !HAVE_RESOURCE */ -/* Report or set limits associated with certain per-process resources. - See the help documentation in builtins.c for a full description. +static char optstring[4 + 2 * NCMDS]; - Rewritten by Chet Ramey 6/30/91. */ +/* 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, setting, cmd, mode, verbose_print, opt_eof; - int all_limits, specific_limits; - long block_factor; - RLIMTYPE current_limit, real_limit, limit; + int c, limind, mode, opt, all_limits; - c = mode = verbose_print = opt_eof = 0; - limit = (RLIMTYPE)-1; + mode = 0; - do - { - cmd = setting = all_limits = specific_limits = 0; - block_factor = ULIMIT_BLOCK_SIZE; + all_limits = 0; - /* read_options: */ - if (list && !opt_eof && *list->word->word == '-') + /* 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 = &(list->word->word[1]); - list = list->next; - - while (*s && (c = *s++)) - { - switch (c) - { -#define ADD_CMD(x) { if (cmd) specific_limits++; cmd |= (x); } - - case '-': /* ulimit -- */ - opt_eof++; - break; - - case 'a': - all_limits++; - break; - - case 'f': - ADD_CMD (u_FILE_SIZE); - break; - -#if defined (HAVE_RESOURCE) - /* -S and -H are modifiers, not real options. */ - case 'S': - mode |= LIMIT_SOFT; - break; - - case 'H': - mode |= LIMIT_HARD; - break; - - case 'c': - ADD_CMD (u_CORE_FILE_SIZE); - break; - - case 'd': - ADD_CMD (u_DATA_SEG_SIZE); - break; - -#if !defined (USGr4) - case 'm': - ADD_CMD (u_PHYS_MEM_SIZE); - break; -#endif /* USGr4 */ - - case 't': - ADD_CMD (u_CPU_TIME_LIMIT); - block_factor = 1; /* seconds */ - break; - - case 's': - ADD_CMD (u_STACK_SIZE); - break; - - case 'v': - ADD_CMD (u_MAX_VIRTUAL_MEM); - block_factor = 1; - break; - - case 'u': - ADD_CMD (u_MAX_USER_PROCS); - block_factor = 1; - break; - -#endif /* HAVE_RESOURCE */ - - case 'p': - ADD_CMD (u_PIPE_SIZE); - block_factor = 512; - break; - - case 'n': - ADD_CMD (u_NUM_OPEN_FILES); - block_factor = 1; - break; - - default: /* error_case: */ - t[0] = c; - t[1] = '\0'; - bad_option (t); -#if !defined (HAVE_RESOURCE) - builtin_error("usage: ulimit [-afnp] [new limit]"); -#else - builtin_error("usage: ulimit [-SHacmdstfnpuv] [new limit]"); -#endif - return (EX_USAGE); - } - } + *s++ = limits[c].option; + *s++ = ';'; } + *s = '\0'; + } - if (all_limits) - { - print_all_limits (mode); - return (EXECUTION_SUCCESS); - } - - if (specific_limits) - { - print_specific_limits (cmd, mode); - if (list) - verbose_print++; - continue; - } - - if (cmd == 0) - cmd = u_FILE_SIZE; - - /* If an argument was supplied for the command, then we want to - set the limit. Note that `ulimit something' means a command - of -f with argument `something'. */ - if (list) - { - if (opt_eof || (*list->word->word != '-')) - { - s = list->word->word; - list = list->next; - - if (STREQ (s, "unlimited")) - limit = RLIM_INFINITY; - else if (all_digits (s)) - limit = string_to_rlimtype (s); - else - { - builtin_error ("bad non-numeric arg `%s'", s); - return (EXECUTION_FAILURE); - } - setting++; - } - else if (!opt_eof) - verbose_print++; - } - - if (limit == RLIM_INFINITY) - block_factor = 1; - - real_limit = limit * block_factor; - - /* If more than one option is given, list each in a verbose format, - the same that is used for -a. */ - if (!setting && verbose_print) + /* 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) { - print_specific_limits (cmd, mode); - continue; + 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; - current_limit = shell_ulimit (cmd, real_limit, 0, mode); + if (all_limits) + { + print_all_limits (mode == 0 ? LIMIT_SOFT : mode); + return (EXECUTION_SUCCESS); + } - if (setting) - { -#if !defined (HAVE_RESOURCE) - /* Most USG systems do not most allow limits to be raised by any - user other than root. There are, however, exceptions. */ - if (canraise (cmd, current_limit, real_limit) == 0) - { - builtin_error ("cannot raise limit: %s", strerror (EPERM)); - return (EXECUTION_FAILURE); - } -#endif /* !HAVE_RESOURCE */ - - if (shell_ulimit (cmd, real_limit, 1, mode) == (RLIMTYPE)-1) - { - builtin_error ("cannot raise limit: %s", strerror (errno)); - return (EXECUTION_FAILURE); - } - - continue; - } - else + /* 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) { - if (current_limit < 0) - builtin_error ("cannot get limit: %s", strerror (errno)); - else if (current_limit != RLIM_INFINITY) - print_rlimtype ((current_limit / block_factor), 1); - else - printf ("unlimited\n"); + builtin_error ("bad command: `%c'", cmdlist[c].cmd); + return (EX_USAGE); } } - while (list); + + 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); } -/* The ulimit that we call from within Bash. +static int +ulimit_internal (cmd, cmdarg, mode, multiple) + int cmd; + char *cmdarg; + int mode, multiple; +{ + int opt, limind, setting; + long block_factor; + RLIMTYPE current_limit, real_limit, limit; - WHICH says which limit to twiddle; SETTING is non-zero if NEWLIM - contains the desired new limit. Otherwise, the existing limit is - returned. If mode & LIMIT_HARD, the hard limit is used; if - mode & LIMIT_SOFT, the soft limit. Both may be set by specifying - -H and -S; if both are specified, or if neither is specified, the - soft limit will be returned. + limit = RLIM_INVALID; + setting = cmdarg != 0; + limind = _findlim (cmd); + if (mode == 0) + mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT; + opt = get_limit (limind, mode, ¤t_limit); + if (opt < 0) + { + builtin_error ("cannot get limit: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } - Systems without BSD resource limits can specify only u_FILE_SIZE. - This includes most USG systems. + if (setting == 0) /* print the value of the specified limit */ + { + printone (limind, current_limit, multiple); + return (EXECUTION_SUCCESS); + } + + /* Setting the limit. */ + if (STREQ (cmdarg, "unlimited")) + limit = RLIM_INFINITY; + else if (all_digits (cmdarg)) + limit = string_to_rlimtype (cmdarg); + else + { + builtin_error ("bad non-numeric arg `%s'", cmdarg); + return (EXECUTION_FAILURE); + } - Chet Ramey supplied the BSD resource limit code. */ -static RLIMTYPE -shell_ulimit (which, newlim, setting, mode) - int which, setting, mode; - RLIMTYPE newlim; + block_factor = (limit == RLIM_INFINITY) ? 1 : limits[limind].block_factor; + real_limit = limit * block_factor; + + if (set_limit (limind, real_limit, mode) < 0) + { + builtin_error ("cannot modify limit: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + return (EXECUTION_SUCCESS); +} + +static int +get_limit (ind, mode, limptr) + int ind, mode; + RLIMTYPE *limptr; { + RLIMTYPE value; #if defined (HAVE_RESOURCE) struct rlimit limit; - int cmd; - - if (mode == 0) - mode |= LIMIT_SOFT; #endif - switch (which) + if (limits[ind].parameter >= 256) { -#if !defined (HAVE_RESOURCE) - - case u_FILE_SIZE: - if (!setting) + switch (limits[ind].parameter) { - /* ulimit () returns a number that is in 512 byte blocks, thus we - must multiply it by 512 to get back to bytes. This is false - only under HP/UX 6.x. */ - RLIMTYPE result; - - result = ulimit (1, 0L); - -# if defined (hpux) && !defined (_POSIX_VERSION) - return (result); -# else - return (result * 512); -# endif /* hpux 6.x */ + case RLIMIT_FILESIZE: + value = filesize (); + break; + case RLIMIT_PIPESIZE: + value = pipesize (); + break; + case RLIMIT_OPENFILES: + value = (RLIMTYPE)getdtablesize (); + break; + case RLIMIT_VIRTMEM: + value = getmaxvm (mode); + break; + case RLIMIT_MAXUPROC: + value = getmaxuprc (mode); + break; + default: + errno = EINVAL; + return -1; } - else - return (ulimit (2, newlim / 512L)); - - break; - -#else /* defined (HAVE_RESOURCE) */ - - case u_FILE_SIZE: - cmd = RLIMIT_FSIZE; - goto do_ulimit; - - case u_CORE_FILE_SIZE: - cmd = RLIMIT_CORE; - goto do_ulimit; - - case u_DATA_SEG_SIZE: - cmd = RLIMIT_DATA; - goto do_ulimit; - -#if !defined (USGr4) - case u_PHYS_MEM_SIZE: -# if defined (RLIMIT_RSS) - cmd = RLIMIT_RSS; -# else /* !RLIMIT_RSS */ - errno = EINVAL; - return ((RLIMTYPE)-1); -# endif /* !RLIMIT_RSS */ - - goto do_ulimit; -#endif /* USGr4 */ - - case u_CPU_TIME_LIMIT: -#if defined (RLIMIT_CPU) - cmd = RLIMIT_CPU; - goto do_ulimit; + *limptr = value; + return ((value == RLIM_INVALID) ? -1 : 0); + } + else + { +#if defined (HAVE_RESOURCE) + if (getrlimit (limits[ind].parameter, &limit) < 0) + return -1; + value = (mode & LIMIT_SOFT) ? limit.rlim_cur : limit.rlim_max; + *limptr = value; + return 0; #else errno = EINVAL; - return ((RLIMTYPE)-1); -# endif /* !RLIMIT_CPU */ - - - case u_STACK_SIZE: - cmd = RLIMIT_STACK; - - do_ulimit: - - if (getrlimit (cmd, &limit) != 0) - return ((RLIMTYPE)-1); - - if (!setting) - { - if (mode & LIMIT_SOFT) - return (limit.rlim_cur); - else - return (limit.rlim_max); - } - else - { - if (mode & LIMIT_SOFT) - { - /* Non-root users are only allowed to raise a limit up to the - hard limit, not to infinity. */ - if (current_user.euid != 0 && newlim == RLIM_INFINITY) - limit.rlim_cur = limit.rlim_max; - else - limit.rlim_cur = newlim; - } - if (mode & LIMIT_HARD) - limit.rlim_max = newlim; - - return (setrlimit (cmd, &limit)); - } - - break; - -#endif /* HAVE_RESOURCE */ + return -1; +#endif + } +} - /* You can't get or set the pipe size with getrlimit, so we have to - cheat. */ - case u_PIPE_SIZE: - if (setting) - { - errno = EINVAL; - return ((RLIMTYPE)-1); - } - return (pipesize ()); +static int +set_limit (ind, newlim, mode) + int ind; + RLIMTYPE newlim; + int mode; +{ +#if defined (HAVE_RESOURCE) + struct rlimit limit; + RLIMTYPE val; +#endif - case u_NUM_OPEN_FILES: - if (setting) - { -#if defined (HAVE_RESOURCE) && defined (RLIMIT_NOFILE) - cmd = RLIMIT_NOFILE; - goto do_ulimit; -#else -# if defined (HAVE_SETDTABLESIZE) - return (setdtablesize (newlim)); -# else - errno = EINVAL; - return ((RLIMTYPE)-1); -# endif /* HAVE_SETDTABLESIZE */ -#endif /* !HAVE_RESOURCE || !RLIMIT_NOFILE */ - } - else - return (open_files (mode)); + if (limits[ind].parameter >= 256) + switch (limits[ind].parameter) + { + case RLIMIT_FILESIZE: +#if !defined (HAVE_RESOURCE) + return (ulimit (2, newlim / 512L)); +#endif - case u_MAX_VIRTUAL_MEM: - if (setting) - { - errno = EINVAL; - return ((RLIMTYPE)-1); - } - else - { + case RLIMIT_OPENFILES: +#if defined (HAVE_SETDTABLESIZE) + return (setdtablesize (newlim)); +#endif + case RLIMIT_PIPESIZE: + case RLIMIT_VIRTMEM: + case RLIMIT_MAXUPROC: + default: + errno = EINVAL; + return -1; + } + else + { #if defined (HAVE_RESOURCE) - return (getmaxvm (mode)); -#else /* !HAVE_RESOURCE */ - errno = EINVAL; - return ((RLIMTYPE)-1); -#endif /* !HAVE_RESOURCE */ - } - - case u_MAX_USER_PROCS: -#if defined (HAVE_RESOURCE) && defined (RLIMIT_NPROC) - cmd = RLIMIT_NPROC; - goto do_ulimit; -#else /* !HAVE_RESOURCE || !RLIMIT_NPROC */ - errno = EINVAL; - return ((RLIMTYPE)-1); -#endif /* !HAVE_RESOURCE || !RLIMIT_NPROC */ - - default: + if (getrlimit (limits[ind].parameter, &limit) < 0) + return -1; + val = (current_user.euid != 0 && newlim == RLIM_INFINITY) + ? 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 ((RLIMTYPE)-1); + return -1; +#endif } } -#if defined (HAVE_RESOURCE) static RLIMTYPE getmaxvm (mode) int mode; { +#if defined (HAVE_RESOURCE) struct rlimit rl; - -#if defined (RLIMIT_VMEM) - if (getrlimit (RLIMIT_VMEM, &rl) < 0) - return ((RLIMTYPE)-1); - else - return (((mode & LIMIT_SOFT) ? rl.rlim_cur : rl.rlim_max) / 1024L); -#else /* !RLIMIT_VMEM */ RLIMTYPE maxdata, maxstack; if (getrlimit (RLIMIT_DATA, &rl) < 0) - return ((RLIMTYPE)-1); + return (RLIM_INVALID); else maxdata = (mode & LIMIT_SOFT) ? rl.rlim_cur : rl.rlim_max; if (getrlimit (RLIMIT_STACK, &rl) < 0) - return ((RLIMTYPE)-1); + return (RLIM_INVALID); else maxstack = (mode & LIMIT_SOFT) ? rl.rlim_cur : rl.rlim_max; /* Protect against overflow. */ return ((maxdata / 1024L) + (maxstack / 1024L)); -#endif /* !RLIMIT_VMEM */ -} +#else + errno = EINVAL; + return RLIM_INVALID; #endif /* HAVE_RESOURCE */ +} static RLIMTYPE -open_files (mode) - int mode; +filesize() { -#if !defined (RLIMIT_NOFILE) - return ((RLIMTYPE)getdtablesize ()); +#if !defined (HAVE_RESOURCE) + return ((RLIMTYPE)ulimit (1, 0L)); #else - struct rlimit rl; - - getrlimit (RLIMIT_NOFILE, &rl); - if (mode & LIMIT_SOFT) - return (rl.rlim_cur); - else - return (rl.rlim_max); + errno = EINVAL; + return RLIM_INVALID; #endif } @@ -607,125 +526,57 @@ pipesize () return ((RLIMTYPE) PIPESIZE); # else errno = EINVAL; - return ((RLIMTYPE)-1); + return RLIM_INVALID; # endif /* PIPESIZE */ #endif /* PIPE_BUF */ } -/* ulimit(2) returns information about file size limits in terms of 512-byte - blocks. This is the factor by which to divide to turn it into information - in terms of 1024-byte blocks. Except for hpux 6.x, which returns it in - terms of bytes. */ -#if !defined (hpux) || defined (_POSIX_VERSION) -# define ULIMIT_DIVISOR 2 -#else -# define ULIMIT_DIVISOR 1024 -#endif - -#if defined (HAVE_RESOURCE) - -typedef struct { - int option_cmd; /* The ulimit command for this limit. */ - int parameter; /* Parameter to pass to getrlimit (). */ - int block_factor; /* Blocking factor for specific limit. */ - char *description; /* Descriptive string to output. */ -} BSD_RESOURCE_LIMITS; - -static BSD_RESOURCE_LIMITS limits[] = { - { u_CORE_FILE_SIZE, RLIMIT_CORE, 1024, "core file size (blocks)" }, - { u_DATA_SEG_SIZE, RLIMIT_DATA, 1024, "data seg size (kbytes)" }, - { u_FILE_SIZE, RLIMIT_FSIZE, 1024, "file size (blocks)" }, -#if !defined (USGr4) && defined (RLIMIT_RSS) - { u_PHYS_MEM_SIZE, RLIMIT_RSS, 1024, "max memory size (kbytes)" }, -#endif /* USGr4 && RLIMIT_RSS */ - { u_STACK_SIZE, RLIMIT_STACK, 1024, "stack size (kbytes)" }, -#if defined (RLIMIT_CPU) - { u_CPU_TIME_LIMIT, RLIMIT_CPU, 1, "cpu time (seconds)" }, -#endif /* RLIMIT_CPU */ -#if defined (RLIMIT_NPROC) - { u_MAX_USER_PROCS, RLIMIT_NPROC, 1, "max user processes" }, -#endif /* RLIMIT_NPROC */ - { 0, 0, 0, (char *)NULL } -}; - -static void -print_bsd_limit (i, mode) - int i, mode; +static RLIMTYPE +getmaxuprc (mode) + int mode; { - struct rlimit rl; - RLIMTYPE limit; - - getrlimit (limits[i].parameter, &rl); - if (mode & LIMIT_HARD) - limit = rl.rlim_max; - else - limit = rl.rlim_cur; - printf ("%-25s", limits[i].description); - if (limit == RLIM_INFINITY) - printf ("unlimited\n"); - else - print_rlimtype ((limit / limits[i].block_factor), 1); +# if defined (HAVE_SYSCONF) && defined (_SC_CHILD_MAX) + return ((RLIMTYPE)sysconf (_SC_CHILD_MAX)); +# else /* !HAVE_SYSCONF || !_SC_CHILD_MAX */ +# if defined (MAXUPRC) + return ((RLIMTYPE)MAXUPRC); +# else /* MAXUPRC */ + errno = EINVAL; + return RLIM_INVALID; +# endif /* !MAXUPRC */ +# endif /* !HAVE_SYSCONF || !_SC_CHILD_MAX */ } static void -print_specific_bsd_limits (cmd, mode) - int cmd, mode; +print_all_limits (mode) + int mode; { register int i; + RLIMTYPE value; - for (i = 0; limits[i].option_cmd; i++) - if (cmd & limits[i].option_cmd) - print_bsd_limit (i, mode); -} -#endif /* HAVE_RESOURCE */ - -/* Print the limits corresponding to a specific set of resources. This is - called when an option string contains more than one character (e.g. -at), - because limits may not be specified with that kind of argument. */ -static void -print_specific_limits (cmd, mode) - int cmd, mode; -{ if (mode == 0) - mode = LIMIT_SOFT; - -#if defined (HAVE_RESOURCE) - print_specific_bsd_limits (cmd, mode); -#else /* !HAVE_RESOURCE */ - if (cmd & u_FILE_SIZE) - { - printf ("%-25s", "file size (blocks)"); - print_rlimtype ((ulimit (1, 0L) / ULIMIT_DIVISOR), 1); - } -#endif /* !HAVE_RESOURCE */ - - if (cmd & u_PIPE_SIZE) - { - printf ("%-25s", "pipe size (512 bytes)"); - print_rlimtype ((pipesize () / 512), 1); - } - - if (cmd & u_NUM_OPEN_FILES) - { - printf ("%-25s", "open files"); - print_rlimtype (open_files (mode), 1); - } + mode |= LIMIT_SOFT; -#if defined (HAVE_RESOURCE) - if (cmd & u_MAX_VIRTUAL_MEM) + for (i = 0; limits[i].option > 0; i++) { - printf ("%-25s", "virtual memory (kbytes)"); - print_rlimtype (getmaxvm (mode), 1); + if (get_limit (i, mode, &value) < 0) + value = RLIM_INVALID; + printone (i, value, 1); } -#endif /* HAVE_RESOURCE */ } static void -print_all_limits (mode) - int mode; +printone (limind, curlim, pdesc) + int limind; + RLIMTYPE curlim; + int pdesc; { - if (mode == 0) - mode |= LIMIT_SOFT; - - print_specific_limits (u_ALL_LIMITS, mode); + if (pdesc) + printf (DESCFMT, limits[limind].description); + if (curlim == RLIM_INFINITY) + puts ("unlimited"); + else if (curlim == RLIM_INVALID) + printf ("cannot get limit: %s\n", strerror (errno)); + else + print_rlimtype ((curlim / limits[limind].block_factor), 1); } diff --git a/builtins/umask.def b/builtins/umask.def index 1d84aa9..5ef8f3b 100644 --- a/builtins/umask.def +++ b/builtins/umask.def @@ -31,12 +31,22 @@ 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). $END -#include <stdio.h> -#include <sys/types.h> +#include <config.h> + +#include "../bashtypes.h" +#include "../filecntl.h" #include <sys/file.h> + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <stdio.h> + #include "../shell.h" #include "../posixstat.h" #include "common.h" +#include "bashgetopt.h" /* **************************************************************** */ /* */ @@ -49,47 +59,40 @@ static int symbolic_umask (); /* 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 = 0; + int print_symbolically, opt, umask_value; - while (list) + print_symbolically = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "S")) != -1) { - if (ISOPTION (list->word->word, 'S')) + switch (opt) { - list = list->next; + case 'S': print_symbolically++; - continue; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; break; - } - else if (*(list->word->word) == '-') - { - bad_option (list->word->word); - builtin_error ("usage: umask [-S] [mode]"); + default: + builtin_usage (); return (EX_USAGE); } - else - break; } + list = loptend; + if (list) { - int new_umask; - if (digit (*list->word->word)) { - new_umask = read_octal (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 (new_umask == -1) + if (umask_value == -1) { builtin_error ("`%s' is not an octal number from 000 to 777", list->word->word); @@ -98,26 +101,25 @@ umask_builtin (list) } else { - new_umask = symbolic_umask (list); - if (new_umask == -1) + umask_value = symbolic_umask (list); + if (umask_value == -1) return (EXECUTION_FAILURE); } - umask (new_umask); + umask (umask_value); if (print_symbolically) - print_symbolic_umask (new_umask); + print_symbolic_umask (umask_value); } else /* Display the UMASK for this user. */ { - int old_umask; - - old_umask = umask (022); - umask (old_umask); + umask_value = umask (022); + umask (umask_value); if (print_symbolically) - print_symbolic_umask (old_umask); + print_symbolic_umask (umask_value); else - printf ("%03o\n", old_umask); + printf ("%03o\n", umask_value); } + fflush (stdout); return (EXECUTION_SUCCESS); } @@ -176,7 +178,7 @@ symbolic_umask (list) um = umask (022); umask (um); - /* All work below is done with the complement of the umask -- its + /* All work below is done with the complement of the umask -- it's more intuitive and easier to deal with. It is complemented again before being returned. */ umc = ~um; @@ -266,7 +268,7 @@ symbolic_umask (list) break; default: - builtin_error ("bad operation character: %c", op); + builtin_error ("bad symbolic mode operator: %c", op); return (-1); } diff --git a/builtins/wait.def b/builtins/wait.def index f613179..b9290d9 100644 --- a/builtins/wait.def +++ b/builtins/wait.def @@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - $BUILTIN wait $FUNCTION wait_builtin $DEPENDS_ON JOB_CONTROL @@ -42,10 +41,18 @@ and the return code is zero. N is a process ID; if it is not given, all child processes of the shell are waited for. $END -#include <sys/types.h> +#include <config.h> + +#include "../bashtypes.h" #include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include "../shell.h" #include "../jobs.h" +#include "common.h" extern int interrupt_immediately; @@ -53,10 +60,17 @@ extern int interrupt_immediately; 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 { run_unwind_frame ("wait_builtin"); return (s); } while (0) + +int wait_builtin (list) WORD_LIST *list; { - int status = EXECUTION_SUCCESS; + int status; + + if (no_options (list)) + return (EX_USAGE); begin_unwind_frame ("wait_builtin"); unwind_protect_int (interrupt_immediately); @@ -67,13 +81,13 @@ wait_builtin (list) /* But wait without any arguments means to wait for all of the shell's currently active background processes. */ - if (!list) + if (list == 0) { wait_for_background_pids (); - status = EXECUTION_SUCCESS; - goto return_status; + WAIT_RETURN (EXECUTION_SUCCESS); } + status = EXECUTION_SUCCESS; while (list) { pid_t pid; @@ -89,9 +103,8 @@ wait_builtin (list) } else { - builtin_error ("`%s' is not a pid or legal job spec", w); - status = EXECUTION_FAILURE; - goto return_status; + builtin_error ("`%s' is not a pid or valid job spec", w); + WAIT_RETURN (EXECUTION_FAILURE); } } #if defined (JOB_CONTROL) @@ -107,7 +120,7 @@ wait_builtin (list) if (job < 0 || job >= job_slots || !jobs[job]) { if (job != DUP_JOB) - builtin_error ("No such job %s", list->word->word); + builtin_error ("%s: no such job", list->word->word); UNBLOCK_CHILD (oset); status = 127; /* As per Posix.2, section 4.70.2 */ list = list->next; @@ -121,12 +134,11 @@ wait_builtin (list) #endif /* JOB_CONTROL */ else { - builtin_error ("`%s' is not a pid or legal job spec", w); + builtin_error ("`%s' is not a pid or valid job spec", w); status = EXECUTION_FAILURE; } list = list->next; } - return_status: - run_unwind_frame ("wait_builtin"); - return (status); + + WAIT_RETURN (status); } |
