diff options
| author | Jari Aalto <jari.aalto@cante.net> | 1996-08-26 18:22:31 +0000 |
|---|---|---|
| committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:46:49 +0000 |
| commit | 726f63884db0132f01745f1fb4465e6621088ccf (patch) | |
| tree | 6c2f7765a890a97e0e513cb539df43283a8f7c4d /builtins | |
| download | android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.gz android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.bz2 android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.zip | |
Imported from ../bash-1.14.7.tar.gz.
Diffstat (limited to 'builtins')
50 files changed, 11360 insertions, 0 deletions
diff --git a/builtins/ChangeLog b/builtins/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/builtins/ChangeLog diff --git a/builtins/Makefile b/builtins/Makefile new file mode 100644 index 0000000..6472f7d --- /dev/null +++ b/builtins/Makefile @@ -0,0 +1,267 @@ +# 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/alias.def b/builtins/alias.def new file mode 100644 index 0000000..a878c70 --- /dev/null +++ b/builtins/alias.def @@ -0,0 +1,180 @@ +This file is alias.def, from which is created alias.c +It implements the builtins "alias" and "unalias" 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. + +$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. +$END + +#include "../config.h" + +#if defined (ALIAS) +# include <stdio.h> +# include "../shell.h" +# include "../alias.h" +# include "common.h" + +extern int interactive; +static void print_alias (); + +/* Hack the alias command in a Korn shell way. */ +alias_builtin (list) + WORD_LIST *list; +{ + int any_failed = 0; + + if (!list) + { + register int i; + ASSOC **alias_list; + + if (!aliases) + return (EXECUTION_FAILURE); + + alias_list = all_aliases (); + + if (!alias_list) + return (EXECUTION_FAILURE); + + for (i = 0; alias_list[i]; i++) + print_alias (alias_list[i]); + + free (alias_list); /* XXX - Do not free the strings. */ + } + else + { + while (list) + { + register char *value, *name = list->word->word; + register int offset; + + for (offset = 0; name[offset] && name[offset] != '='; offset++) + ; + + if (offset && name[offset] == '=') + { + name[offset] = '\0'; + value = name + offset + 1; + + add_alias (name, value); + } + else + { + ASSOC *t = find_alias (name); + if (t) + print_alias (t); + else + { + if (interactive) + builtin_error ("`%s' not found", name); + any_failed++; + } + } + list = list->next; + } + } + if (any_failed) + return (EXECUTION_FAILURE); + else + return (EXECUTION_SUCCESS); +} +#endif /* ALIAS */ + +$BUILTIN unalias +$FUNCTION unalias_builtin +$DEPENDS_ON ALIAS +$SHORT_DOC unalias [-a] [name ...] +Remove NAMEs from the list of defined aliases. If the -a option is given, +then remove all alias definitions. +$END + +#if defined (ALIAS) +/* Remove aliases named in LIST from the aliases database. */ +unalias_builtin (list) + register WORD_LIST *list; +{ + register ASSOC *alias; + int any_failed = 0; + + while (list && *list->word->word == '-') + { + register char *word = list->word->word; + + if (ISOPTION (word, 'a')) + { + delete_all_aliases (); + list = list->next; + } + else if (ISOPTION (word, '-')) + { + list = list->next; + break; + } + else + { + bad_option (word); + return (EXECUTION_FAILURE); + } + } + + while (list) + { + alias = find_alias (list->word->word); + + if (alias) + remove_alias (alias->name); + else + { + if (interactive) + builtin_error ("`%s' not an alias", list->word->word); + + any_failed++; + } + + list = list->next; + } + + if (any_failed) + return (EXECUTION_FAILURE); + else + return (EXECUTION_SUCCESS); +} + +/* Output ALIAS in such a way as to allow it to be read back in. */ +static void +print_alias (alias) + ASSOC *alias; +{ + char *value = single_quote (alias->value); + + printf ("alias %s=%s\n", alias->name, value); + free (value); + + fflush (stdout); +} +#endif /* ALIAS */ diff --git a/builtins/bashgetopt.c b/builtins/bashgetopt.c new file mode 100644 index 0000000..95dcaac --- /dev/null +++ b/builtins/bashgetopt.c @@ -0,0 +1,136 @@ +/* bashgetopt.c -- `getopt' for use by the builtins. */ + +/* Copyright (C) 1992 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 <errno.h> +#include "shell.h" + +#include "bashansi.h" + +#define ERR(S, C) builtin_error("%s%c", (S), (C)) + +static int sp; + +char *list_optarg; +int list_optopt; + +static WORD_LIST *lhead = (WORD_LIST *)NULL; +WORD_LIST *lcurrent = (WORD_LIST *)NULL; +WORD_LIST *loptend; /* Points to the first non-option argument in the list */ + +int +internal_getopt(list, opts) +WORD_LIST *list; +char *opts; +{ + register int c; + register char *cp; + + if (!list) { + list_optarg = (char *)NULL; + loptend = (WORD_LIST *)NULL; /* No non-option arguments */ + return -1; + } + + if (list != lhead || !lhead) { + /* Hmmm.... called with a different word list. Reset. */ + sp = 1; + lcurrent = lhead = list; + loptend = (WORD_LIST *)NULL; + } + + if (sp == 1) { + if (!lcurrent || + (lcurrent->word->word[0] != '-' || lcurrent->word->word[1] == '\0')) { + lhead = (WORD_LIST *)NULL; + loptend = lcurrent; + return(-1); + } else if (lcurrent->word->word[0] == '-' && + lcurrent->word->word[1] == '-' && + lcurrent->word->word[2] == 0) { + lhead = (WORD_LIST *)NULL; + loptend = lcurrent->next; + return(-1); + } + } + + list_optopt = c = lcurrent->word->word[sp]; + + if (c == ':' || (cp = strchr(opts, c)) == NULL) { + ERR("illegal option: -", c); + if (lcurrent->word->word[++sp] == '\0') { + lcurrent = lcurrent->next; + sp = 1; + } + list_optarg = NULL; + if (lcurrent) + loptend = lcurrent->next; + return('?'); + } + + if (*++cp == ':') { + /* Option requires an argument. */ + /* We allow -l2 as equivalent to -l 2 */ + if (lcurrent->word->word[sp+1] != '\0') { + list_optarg = &(lcurrent->word->word[sp+1]); + lcurrent = lcurrent->next; + } else if (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 { + /* No argument, just return the option. */ + if (lcurrent->word->word[++sp] == '\0') { + sp = 1; + lcurrent = lcurrent->next; + } + list_optarg = (char *)NULL; + } + + return(c); +} + +/* + * reset_internal_getopt -- force the in[ft]ernal getopt to reset + */ + +void +reset_internal_getopt () +{ + lhead = lcurrent = loptend = (WORD_LIST *)NULL; + sp = 1; +} + +void +report_bad_option () +{ + char s[3]; + + s[0] = '-'; + s[1] = list_optopt; + s[2] = '\0'; + bad_option (s); +} diff --git a/builtins/bashgetopt.h b/builtins/bashgetopt.h new file mode 100644 index 0000000..0360dbb --- /dev/null +++ b/builtins/bashgetopt.h @@ -0,0 +1,37 @@ +/* bashgetopt.h -- extern declarations for stuff defined in bashgetopt.c. */ + +/* Copyright (C) 1993 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, 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. */ + +/* See getopt.h for the explanation of these variables. */ + +#if !defined (__BASH_GETOPT_H) +# define __BASH_GETOPT_H + +extern char *list_optarg; + +extern int list_optopt; + +extern WORD_LIST *lcurrent; +extern WORD_LIST *loptend; + +extern int internal_getopt (); +extern void reset_internal_getopt (); +extern void report_bad_option (); + +#endif /* !__BASH_GETOPT_H */ diff --git a/builtins/bind.def b/builtins/bind.def new file mode 100644 index 0000000..cdcd6a3 --- /dev/null +++ b/builtins/bind.def @@ -0,0 +1,219 @@ +This file is bind.def, from which is created bind.c. +It implements the builtin "bind" 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 bind.c + +$BUILTIN bind +$DEPENDS_ON READLINE +$FUNCTION bind_builtin +$SHORT_DOC bind [-lvd] [-m keymap] [-f filename] [-q name] [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'. +Arguments we accept: + -m keymap Use `keymap' as the keymap for the duration of this + command. Acceptable keymap names are emacs, + emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, + vi-command, and vi-insert. + -l List names of functions. + -v List function names and bindings. + -d Dump functions and bindings such that they + can be read back in. + -f filename Read key bindings from FILENAME. + -q function-name Query about which keys invoke the named function. +$END + +#include <stdio.h> +#include "../shell.h" +#if defined (READLINE) +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ +#include <readline/readline.h> +#include <readline/history.h> +#include "bashgetopt.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]" + +int +bind_builtin (list) + WORD_LIST *list; +{ + int return_code = EXECUTION_SUCCESS; + FILE *old_rl_outstream; + Keymap kmap, saved_keymap; + int lflag, dflag, fflag, vflag, qflag, mflag, opt; + char *initfile, *map_name, *fun_name; + + 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; + + if (!bash_readline_initialized) + initialize_readline (); + + /* Cannot use unwind_protect_pointer () on "FILE *", it is only + guaranteed to work for strings. */ + /* XXX -- see if we can use unwind_protect here */ + old_rl_outstream = rl_outstream; + rl_outstream = stdout; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lvdf:q:m:")) != EOF) + { + switch (opt) + { + case 'l': + lflag++; + break; + + case 'v': + vflag++; + break; + + case 'd': + dflag++; + break; + + case 'f': + fflag++; + initfile = list_optarg; + break; + + case 'm': + mflag++; + map_name = list_optarg; + break; + + case 'q': + qflag++; + fun_name = list_optarg; + break; + + default: + builtin_error (USAGE); + BIND_RETURN (EX_USAGE); + } + } + + list = loptend; + + /* First, see if we need to install a special keymap for this + command. Then start on the arguments. */ + + if (mflag && map_name) + { + kmap = rl_get_keymap_by_name (map_name); + if (!kmap) + { + builtin_error ("`%s': illegal keymap name", map_name); + BIND_RETURN (EXECUTION_FAILURE); + } + } + + if (kmap) + { + saved_keymap = rl_get_keymap (); + rl_set_keymap (kmap); + } + + /* XXX - we need to add exclusive use tests here. It doesn't make sense + to use some of these options together. */ + /* Now hack the option arguments */ + if (lflag) + rl_list_funmap_names (0); + + if (vflag) + rl_function_dumper (0); + + if (dflag) + rl_function_dumper (1); + + if (fflag && initfile) + { + if (rl_read_init_file (initfile) != 0) + { + builtin_error ("cannot read %s: %s", initfile, strerror (errno)); + BIND_RETURN (EXECUTION_FAILURE); + } + } + + if (qflag && fun_name) + return_code = query_bindings (fun_name); + + /* Process the rest of the arguments as binding specifications. */ + while (list) + { + rl_parse_and_bind (list->word->word); + list = list->next; + } + + bind_exit: + if (saved_keymap) + rl_set_keymap (saved_keymap); + + rl_outstream = old_rl_outstream; + return (return_code); +} + +static int +query_bindings (name) + char *name; +{ + Function *function; + char **keyseqs; + int j; + + function = rl_named_function (name); + if (!function) + { + builtin_error ("unknown function name `%s'", name); + return EXECUTION_FAILURE; + } + + keyseqs = rl_invoking_keyseqs (function); + + if (!keyseqs) + { + printf ("%s is not bound to any keys.\n", name); + return EXECUTION_FAILURE; + } + + printf ("%s can be invoked via ", name); + for (j = 0; j < 5 && keyseqs[j]; j++) + printf ("\"%s\"%s", keyseqs[j], keyseqs[j + 1] ? ", " : ".\n"); + if (keyseqs[j]) + printf ("...\n"); + free_array (keyseqs); + return EXECUTION_SUCCESS; +} +#endif /* READLINE */ diff --git a/builtins/break.def b/builtins/break.def new file mode 100644 index 0000000..d72f9e3 --- /dev/null +++ b/builtins/break.def @@ -0,0 +1,110 @@ +This file is break.def, from which is created break.c. +It implements the builtins "break" and "continue" 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 break.c + +$BUILTIN break +$FUNCTION break_builtin +$SHORT_DOC break [n] +Exit from within a FOR, WHILE or UNTIL loop. If N is specified, +break N levels. +$END + +#include "../shell.h" + +extern char *this_command_name; + +static int check_loop_level (); + +/* The depth of while's and until's. */ +int loop_level = 0; + +/* Non-zero when a "break" instruction is encountered. */ +int breaking = 0; + +/* Non-zero when we have encountered a continue instruction. */ +int continuing = 0; + +/* Set up to break x levels, where x defaults to 1, but can be specified + as the first argument. */ +break_builtin (list) + WORD_LIST *list; +{ + int newbreak; + + if (!check_loop_level ()) + return (EXECUTION_FAILURE); + + newbreak = get_numeric_arg (list); + + if (newbreak <= 0) + return (EXECUTION_FAILURE); + + if (newbreak > loop_level) + newbreak = loop_level; + + breaking = newbreak; + + return (EXECUTION_SUCCESS); +} + +$BUILTIN continue +$FUNCTION continue_builtin +$SHORT_DOC continue [n] +Resume the next iteration of the enclosing FOR, WHILE or UNTIL loop. +If N is specified, resume at the N-th enclosing loop. +$END + +/* Set up to continue x levels, where x defaults to 1, but can be specified + as the first argument. */ +continue_builtin (list) + WORD_LIST *list; +{ + int newcont; + + if (!check_loop_level ()) + return (EXECUTION_FAILURE); + + newcont = get_numeric_arg (list); + + if (newcont <= 0) + return (EXECUTION_FAILURE); + + if (newcont > loop_level) + newcont = loop_level; + + continuing = newcont; + + return (EXECUTION_SUCCESS); +} + +/* Return non-zero if a break or continue command would be okay. + Print an error message if break or continue is meaningless here. */ +static int +check_loop_level () +{ +#if defined (BREAK_COMPLAINS) + if (!loop_level) + builtin_error ("Only meaningful in a `for', `while', or `until' loop"); +#endif /* BREAK_COMPLAINS */ + + return (loop_level); +} diff --git a/builtins/builtin.def b/builtins/builtin.def new file mode 100644 index 0000000..824b30d --- /dev/null +++ b/builtins/builtin.def @@ -0,0 +1,67 @@ +This file is builtin.def, from which is created builtin.c. +It implements the builtin "builtin" 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 builtin.c + +$BUILTIN builtin +$FUNCTION builtin_builtin +$SHORT_DOC builtin [shell-builtin [arg ...]] +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 "../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. */ +builtin_builtin (list) + WORD_LIST *list; +{ + Function *function; + register char *command; + + if (!list) + return (EXECUTION_SUCCESS); + + command = (list->word->word); +#if defined (DISABLED_BUILTINS) + function = builtin_address (command); +#else /* !DISABLED_BUILTINS */ + function = find_shell_builtin (command); +#endif /* !DISABLED_BUILTINS */ + + if (!function) + { + builtin_error ("%s: not a shell builtin", command); + return (EXECUTION_FAILURE); + } + else + { + this_command_name = command; + list = list->next; + return ((*function) (list)); + } +} diff --git a/builtins/cd.def b/builtins/cd.def new file mode 100644 index 0000000..338f694 --- /dev/null +++ b/builtins/cd.def @@ -0,0 +1,689 @@ +This file is cd.def, from which is created cd.c. It implements the +builtins "cd", "pwd", "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 cd.c + +#include <stdio.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 <errno.h> +#include <tilde/tilde.h> + +#include "../shell.h" +#include "../flags.h" +#include "../maxpath.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +static int change_to_directory (), cd_to_string (); + +$BUILTIN cd +$FUNCTION cd_builtin +$SHORT_DOC cd [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 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 +name. If that variable has a value, then cd to the value of that +variable. +$END + +/* This builtin is ultimately the way that all user-visible commands should + change the current working directory. It is called by cd_to_string (), + so the programming interface is simple, and it handles errors and + restrictions properly. */ +int +cd_builtin (list) + WORD_LIST *list; +{ + char *dirname; + +#if defined (RESTRICTED_SHELL) + if (restricted) + { + builtin_error ("restricted"); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ + + if (list) + { + 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)) + { + 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); + return (EXECUTION_FAILURE); + } + goto bind_and_exit; + } + else + { + dirname = get_string_value ("HOME"); + + if (!dirname) + return (EXECUTION_FAILURE); + + if (!change_to_directory (dirname)) + { + file_error (dirname); + 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) + { + builtin_error ("%s", buffer); + free (buffer); + } + } + else + directory = get_working_directory ("pwd"); + + if (directory) + { + 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. + +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) + { + builtin_error ("No other directory"); + 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 == '-') + { + int num; + if (1 == sscanf (&(list->word->word)[1], "%d", &num)) + { + if (direction == '-') + num = directory_list_offset - num; + + if (num > directory_list_offset || num < 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); + } + 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); + + return (tt); + } + } + } + } + + /* 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 (cd_builtin (list) == EXECUTION_SUCCESS) + { + if (directory_list_offset == directory_list_size) + { + pushd_directory_list = (char **) + xrealloc (pushd_directory_list, + (directory_list_size += 10) * sizeof (char *)); + } + 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)) + { + 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; + } + 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); + 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); +} +#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. +$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) + WORD_LIST *list; +{ + register int i; + int which = 0; + char direction = '+'; + + if (list) + { + direction = *(list->word->word); + + if ((direction != '+' && direction != '-') || + (1 != sscanf (&((list->word->word)[1]), "%d", &which))) + { + builtin_error ("bad arg `%s'", list->word->word); + return (EXECUTION_FAILURE); + } + } + + if (which > directory_list_offset || (!directory_list_offset && !which)) + { + if (!directory_list_offset) + builtin_error ("Directory stack empty"); + else + builtin_error ("Stack contains only %d directories", + directory_list_offset + 1); + return (EXECUTION_FAILURE); + } + + /* 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]); + } + 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--; + + /* 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); +} +#endif /* PUSHD_AND_POPD */ + +/* Do the work of changing to the directory NEWDIR. Handle symbolic + link following, etc. */ + +static int +change_to_directory (newdir) + char *newdir; +{ + char *t; + + if (!no_symbolic_links) + { + int chdir_return = 0; + char *tdir = (char *)NULL; + + if (!the_current_working_directory) + { + t = get_working_directory ("cd_links"); + FREE (t); + } + + if (the_current_working_directory) + t = make_absolute (newdir, the_current_working_directory); + else + t = savestring (newdir); + + /* TDIR is the canonicalized absolute pathname of the NEWDIR. */ + tdir = canonicalize_pathname (t); + + /* Use the canonicalized version of NEWDIR, or, if canonicalization + failed, use the non-canonical form. */ + if (tdir && *tdir) + free (t); + else + { + FREE (tdir); + + tdir = t; + } + + if (chdir (tdir) < 0) + { + int err; + + chdir_return = 0; + free (tdir); + + err = errno; + + /* We failed changing to the canonicalized directory name. Try + what the user passed verbatim. If we succeed, reinitialize + the_current_working_directory. */ + if (chdir (newdir) == 0) + { + chdir_return = 1; + if (the_current_working_directory) + { + free (the_current_working_directory); + the_current_working_directory = (char *)NULL; + } + + tdir = get_working_directory ("cd"); + FREE (tdir); + } + else + errno = err; + } + else + { + chdir_return = 1; + + FREE (the_current_working_directory); + the_current_working_directory = tdir; + } + + return (chdir_return); + } + else + { + if (chdir (newdir) < 0) + return (0); + else + return (1); + } +} + +/* 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 = make_word_list (make_word (name), NULL); + int result = (cd_builtin (tlist)); + dispose_words (tlist); + return (result); +} diff --git a/builtins/colon.def b/builtins/colon.def new file mode 100644 index 0000000..4ae5b65 --- /dev/null +++ b/builtins/colon.def @@ -0,0 +1,37 @@ +This file is colon.def, from which is created colon.c. +It implements the builtin ":" 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 colon.c + +$BUILTIN : +$DOCNAME colon_builtin +$FUNCTION colon_builtin +$SHORT_DOC : +No effect; the command does nothing. A zero exit code is returned. +$END + +/* Do nothing. This command is a no-op. */ +int +colon_builtin (ignore) + char *ignore; +{ + return (0); +} diff --git a/builtins/command.def b/builtins/command.def new file mode 100644 index 0000000..b84613e --- /dev/null +++ b/builtins/command.def @@ -0,0 +1,177 @@ +This file is command.def, from which is created command.c. +It implements the builtin "command" 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 command.c + +$BUILTIN command +$FUNCTION command_builtin +$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 +for PATH that is guaranteed to find all of the standard utilities. If +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 "../shell.h" +#include "bashgetopt.h" + +extern int subshell_environment; + +static void restore_path (); +static char *get_standard_path (); + +/* Run the commands mentioned in LIST without paying attention to shell + functions. */ +int +command_builtin (list) + WORD_LIST *list; +{ + int result, verbose = 0, use_standard_path = 0, opt; + char *old_path; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "pvV")) != -1) + { + switch (opt) + { + case 'p': + use_standard_path = 1; + break; + case 'V': + verbose = 2; + break; + case 'v': + verbose = 4; + break; + + default: + report_bad_option (); + builtin_error ("usage: command [-pvV] [command [arg...]]"); + return (EX_USAGE); + } + } + list = loptend; + + if (!list) + return (EXECUTION_SUCCESS); + + if (verbose) + { + int found, any_found = 0; + + while (list) + { + + found = describe_command (list->word->word, verbose, 0); + + if (!found) + builtin_error ("%s: not found", list->word->word); + + any_found += found; + list = list->next; + } + return (any_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + } + + 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); + } + + run_unwind_frame ("command_builtin"); + + return (result); +} + +/* Restore the value of the $PATH variable after replacing it when + executing `command -p'. */ +static void +restore_path (var) + char *var; +{ + bind_variable ("PATH", var); + free (var); +} + +/* Return a value for PATH that is guaranteed to find all of the standard + utilities. This uses Posix.2 configuration variables, if present. It + uses a value defined in config.h as a last resort. */ +static char * +get_standard_path () +{ +#if defined (_CS_PATH) && !defined (hpux_7) && !defined (NetBSD) + char *p; + size_t len; + + len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0); + p = xmalloc ((int)len + 2); + *p = '\0'; + confstr (_CS_PATH, p, len); + return (p); +#else /* !_CSPATH || hpux_7 || NetBSD */ +# if defined (CS_PATH) + return (savestring (CS_PATH)); +# else + return (savestring (STANDARD_UTILS_PATH)); +# endif /* !CS_PATH */ +#endif /* !_CS_PATH || hpux_7 */ +} diff --git a/builtins/common.c b/builtins/common.c new file mode 100644 index 0000000..ff940b5 --- /dev/null +++ b/builtins/common.c @@ -0,0 +1,829 @@ +/* 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. */ + +#include <stdio.h> +#include <sys/types.h> +#include "../posixstat.h" +#if defined (HAVE_VFPRINTF) +#include <varargs.h> +#endif /* VFPRINTF */ + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include "../shell.h" +#include "../unwind_prot.h" +#include "../maxpath.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../input.h" +#include "../execute_cmd.h" +#include "hashcom.h" +#include "common.h" +#include <tilde/tilde.h> + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +extern int no_symbolic_links, interactive, interactive_shell; +extern int indirection_level, startup_state; +extern int last_command_exit_value; +extern int hashing_disabled; +extern int variable_context; +extern char *this_command_name, *shell_name; +extern COMMAND *global_command; +extern HASH_TABLE *hashed_filenames; + +/* 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); +} + +/* 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) +void +builtin_error (va_alist) + va_dcl +{ + char *format; + va_list args; + + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + + va_start (args); + format = va_arg (args, char *); + vfprintf (stderr, format, args); + va_end (args); + fprintf (stderr, "\n"); +} +#else /* !HAVE_VFPRINTF */ +void +builtin_error (format, arg1, arg2, arg3, arg4, arg5) + char *format, *arg1, *arg2, *arg3, *arg4, *arg5; +{ + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + + fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif /* !HAVE_VFPRINTF */ + +/* Remember LIST in $0 ... $9, and REST_OF_ARGS. If DESTRUCTIVE is + non-zero, then discard whatever the existing arguments are, else + only discard the ones that are to be replaced. */ +void +remember_args (list, destructive) + WORD_LIST *list; + int destructive; +{ + register int i; + + for (i = 1; i < 10; i++) + { + if (destructive && dollar_vars[i]) + { + free (dollar_vars[i]); + dollar_vars[i] = (char *)NULL; + } + + if (list) + { + if (!destructive && dollar_vars[i]) + free (dollar_vars[i]); + + dollar_vars[i] = savestring (list->word->word); + list = list->next; + } + } + + /* If arguments remain, assign them to REST_OF_ARGS. + Note that copy_word_list (NULL) returns NULL, and + that dispose_words (NULL) does nothing. */ + if (destructive || list) + { + dispose_words (rest_of_args); + rest_of_args = copy_word_list (list); + } + + if (destructive) + set_dollar_vars_changed (); +} + +/* 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 */ +/* */ +/* **************************************************************** */ + +static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; +static int dollar_arg_stack_slots = 0; +static int dollar_arg_stack_index = 0; + +void +push_context () +{ + push_dollar_vars (); + variable_context++; +} + +void +pop_context () +{ + pop_dollar_vars (); + kill_all_local_variables (); + variable_context--; +} + +/* Save the existing positional parameters on a stack. */ +void +push_dollar_vars () +{ + if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) + { + dollar_arg_stack = (WORD_LIST **) + 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; +} + +/* Restore the positional parameters from our stack. */ +void +pop_dollar_vars () +{ + if (!dollar_arg_stack || !dollar_arg_stack_index) + return; + + remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); + dispose_words (dollar_arg_stack[dollar_arg_stack_index]); + dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; +} + +void +dispose_saved_dollar_vars () +{ + if (!dollar_arg_stack || !dollar_arg_stack_index) + 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; + +/* Have the dollar variables been reset to new values since we last + checked? */ +dollar_vars_changed () +{ + return (changed_dollar_vars); +} + +void +set_dollar_vars_unchanged () +{ + changed_dollar_vars = 0; +} + +void +set_dollar_vars_changed () +{ + changed_dollar_vars = 1; +} + +/* Function called when one of the builtin commands detects a bad + option. */ +void +bad_option (s) + char *s; +{ + builtin_error ("unknown option: %s", s); +} + +/* Return a consed string which is the current working directory. + FOR_WHOM is the name of the caller for error printing. */ +char *the_current_working_directory = (char *)NULL; + +char * +get_working_directory (for_whom) + char *for_whom; +{ + if (no_symbolic_links) + { + if (the_current_working_directory) + free (the_current_working_directory); + + the_current_working_directory = (char *)NULL; + } + + if (!the_current_working_directory) + { + char *directory; + + the_current_working_directory = xmalloc (MAXPATHLEN); + directory = getwd (the_current_working_directory); + if (!directory) + { + 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); + + free (the_current_working_directory); + the_current_working_directory = (char *)NULL; + return (char *)NULL; + } + } + + return (savestring (the_current_working_directory)); +} + +/* Make NAME our internal idea of the current working directory. */ +void +set_working_directory (name) + char *name; +{ + if (the_current_working_directory) + free (the_current_working_directory); + + the_current_working_directory = savestring (name); +} + +#if defined (JOB_CONTROL) +/* Return the job spec found in LIST. */ +get_job_spec (list) + WORD_LIST *list; +{ + register char *word; + int job = NO_JOB; + int substring = 0; + + if (!list) + return (current_job); + + word = list->word->word; + + if (!*word) + return (current_job); + + if (*word == '%') + word++; + + if (digit (*word) && (sscanf (word, "%d", &job) == 1)) + return (job - 1); + + switch (*word) + { + case 0: + case '%': + case '+': + return (current_job); + + case '-': + return (previous_job); + + case '?': /* Substring search requested. */ + substring++; + word++; + goto find_string; + + default: + find_string: + { + register int i, wl = strlen (word); + for (i = 0; i < job_slots; i++) + { + if (jobs[i]) + { + register PROCESS *p = jobs[i]->pipe; + do + { + if ((substring && strindex (p->command, word)) || + (strncmp (p->command, word, wl) == 0)) + if (job != NO_JOB) + { + builtin_error ("ambigious job spec: %s", word); + return (DUP_JOB); + } + else + job = i; + + p = p->next; + } + while (p != jobs[i]->pipe); + } + } + return (job); + } + } +} +#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; +{ + 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); + +#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 (); + 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; + } + } + } + + 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 (jump_to_top_level) + longjmp (top_level, code); + + return (last_result); +} + +/* Return the address of the builtin named NAME. + DISABLED_OKAY means find it even if the builtin is disabled. */ +static Function * +builtin_address_internal (name, disabled_okay) + char *name; + int disabled_okay; +{ + int hi, lo, mid, j; + + hi = num_shell_builtins - 1; + lo = 0; + + while (lo <= hi) + { + mid = (lo + hi) / 2; + + j = shell_builtins[mid].name[0] - name[0]; + + if (j == 0) + j = strcmp (shell_builtins[mid].name, name); + + if (j == 0) + { + /* It must have a function pointer. It must be enabled, or we + must have explicitly allowed disabled functions to be found. */ + if (shell_builtins[mid].function && + ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay)) + return (shell_builtins[mid].function); + else + return ((Function *)NULL); + } + if (j > 0) + hi = mid - 1; + else + lo = mid + 1; + } + return ((Function *)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. */ +Function * +find_shell_builtin (name) + char *name; +{ + return (builtin_address_internal (name, 0)); +} + +/* Return the address of builtin with NAME, irregardless of its state of + enableness. */ +Function * +builtin_address (name) + char *name; +{ + return (builtin_address_internal (name, 1)); +} + +static int +shell_builtin_compare (sbp1, sbp2) + struct builtin *sbp1, *sbp2; +{ + int result; + + if ((result = sbp1->name[0] - sbp2->name[0]) == 0) + result = strcmp (sbp1->name, sbp2->name); + + return (result); +} + +/* Sort the table of shell builtins so that the binary search will work + in find_shell_builtin. */ +void +initialize_shell_builtins () +{ + qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin), + shell_builtin_compare); +} + +/* Return a new string which is the quoted version of STRING. This is used + by alias and trap. */ +char * +single_quote (string) + char *string; +{ + register int i, j, c; + char *result; + + result = (char *)xmalloc (3 + (3 * strlen (string))); + + result[0] = '\''; + + for (i = 0, j = 1; string && (c = string[i]); i++) + { + result[j++] = c; + + if (c == '\'') + { + result[j++] = '\\'; /* insert escaped single quote */ + result[j++] = '\''; + result[j++] = '\''; /* start new quoted string */ + } + } + + result[j++] = '\''; + result[j] = '\0'; + + return (result); +} + +char * +double_quote (string) + char *string; +{ + register int i, j, c; + char *result; + + result = (char *)xmalloc (3 + (3 * strlen (string))); + + result[0] = '"'; + + for (i = 0, j = 1; string && (c = string[i]); i++) + { + switch (c) + { + case '"': + case '$': + case '`': + case '\\': + result[j++] = '\\'; + default: + result[j++] = c; + break; + } + } + + result[j++] = '"'; + result[j] = '\0'; + + return (result); +} diff --git a/builtins/common.h b/builtins/common.h new file mode 100644 index 0000000..c7c99e7 --- /dev/null +++ b/builtins/common.h @@ -0,0 +1,69 @@ +/* common.h -- extern declarations for functions defined in common.c. */ + +/* Copyright (C) 1993 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, 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. */ + +#if !defined (__COMMON_H) +# define __COMMON_H + +#define ISOPTION(s, c) (s[0] == '-' && !s[2] && s[1] == c) + +extern void builtin_error (); +extern void bad_option (); + +extern int get_numeric_arg (); + +extern void remember_args (); + +extern void no_args (); + +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 (); +extern int dollar_vars_changed (); +extern void set_dollar_vars_unchanged (), set_dollar_vars_changed (); + +/* Keeps track of the current working directory. */ +extern char *the_current_working_directory; +extern char *get_working_directory (); +extern void set_working_directory (); + +#if defined (JOB_CONTROL) +extern int get_job_spec (); +#endif + +extern int parse_and_execute (); +extern void parse_and_execute_cleanup (); + +extern void initialize_shell_builtins (); + +/* It's OK to declare a function as returning a Function * without + providing a definition of what a `Function' is. */ +extern Function *find_shell_builtin (); +extern Function *builtin_address (); + +extern char *single_quote (); +extern char *double_quote (); + +#endif /* !__COMMON_H */ diff --git a/builtins/declare.def b/builtins/declare.def new file mode 100644 index 0000000..17b7ea2 --- /dev/null +++ b/builtins/declare.def @@ -0,0 +1,290 @@ +This file is declare.def, from which is created declare.c. +It implements the builtins "declare" and "local" 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 declare.c + +$BUILTIN declare +$FUNCTION declare_builtin +$SHORT_DOC declare [-[frxi]] name[=value] ... +Declare variables and/or give them attributes. If no NAMEs are +given, then display the values of variables instead. + +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. + +Variables with the integer attribute have arithmetic evaluation (see +`let') done when the variable is assigned to. + +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] ... +Obsolete. See `declare'. +$END + +#include <stdio.h> + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include "../shell.h" + +extern int variable_context, array_needs_making; + +static int declare_internal (); + +/* Declare or change variable attributes. */ +int +declare_builtin (list) + register WORD_LIST *list; +{ + return (declare_internal (list, 0)); +} + +$BUILTIN local +$FUNCTION local_builtin +$SHORT_DOC local name[=value] ... +Create a local variable called NAME, and give it VALUE. LOCAL +can only be used within a function; it makes the variable NAME +have a visible scope restricted to that function and its children. +$END +int +local_builtin (list) + register WORD_LIST *list; +{ + if (variable_context) + return (declare_internal (list, 1)); + else + { + builtin_error ("Can only be used in a function"); + return (EXECUTION_FAILURE); + } +} + +/* The workhorse function. */ +static int +declare_internal (list, local_var) + register WORD_LIST *list; + int local_var; +{ + int flags_on = 0, flags_off = 0; + int any_failed = 0; + + while (list) + { + register char *t = list->word->word; + int *flags; + + if (t[0] == '-' && t[1] == '-' && t[2] == '\0') + { + list = list->next; + break; + } + + if (*t != '+' && *t != '-') + break; + + if (*t == '+') + flags = &flags_off; + else + flags = &flags_on; + + t++; + + while (*t) + { + if (*t == 'f') + *flags |= att_function, t++; + else if (*t == 'x') + *flags |= att_exported, t++, array_needs_making = 1; + else if (*t == 'r') + *flags |= att_readonly, t++; + else if (*t == 'i') + *flags |= att_integer, t++; + else + { + builtin_error ("unknown option: `-%c'", *t); + return (EX_USAGE); + } + } + + list = list->next; + } + + /* If there are no more arguments left, then we just want to show + some variables. */ + if (!list) + { + /* Show local variables defined at this context level if this is + the `local' builtin. */ + if (local_var) + { + register SHELL_VAR **vlist; + register int i; + + vlist = map_over (variable_in_context, shell_variables); + + if (vlist) + { + for (i = 0; vlist[i]; i++) + print_assignment (vlist[i]); + + free (vlist); + } + } + else + { + if (!flags_on) + set_builtin ((WORD_LIST *)NULL); + else + set_or_show_attributes ((WORD_LIST *)NULL, flags_on); + } + + fflush (stdout); + return (EXECUTION_SUCCESS); + } + +#define NEXT_VARIABLE() free (name); list = list->next; continue + + /* There are arguments left, so we are making variables. */ + while (list) + { + char *value, *name = savestring (list->word->word); + int offset = assignment (name); + + if (offset) + { + name[offset] = '\0'; + value = name + offset + 1; + } + else + value = ""; + + if (legal_identifier (name) == 0) + { + builtin_error ("%s: not a legal variable name", name); + any_failed++; + NEXT_VARIABLE (); + } + + /* If VARIABLE_CONTEXT has a non-zero value, then we are executing + inside of a function. This means we should make local variables, + not global ones. */ + + if (variable_context) + 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'. */ + + /* There should be a way, however, to let people look at a particular + function definition by saying `typeset -f foo'. */ + + if (flags_on & att_function) + { + if (offset) + { + builtin_error ("Can't use `-f' to make functions"); + return (EXECUTION_FAILURE); + } + else + { + SHELL_VAR *find_function (), *funvar; + + funvar = find_function (name); + + if (funvar) + { + if (readonly_p (funvar) && (flags_off & att_readonly)) + { + builtin_error ("%s: readonly function", name); + any_failed++; + NEXT_VARIABLE (); + } + + if (flags_on == att_function && flags_off == 0) + { + char *result = named_function_string + (name, (COMMAND *)function_cell (funvar), 1); + printf ("%s\n", result); + } + else + { + funvar->attributes |= flags_on; + funvar->attributes &= ~flags_off; + } + } + else + any_failed++; + NEXT_VARIABLE (); + } + } + else + { + SHELL_VAR *var; + + var = find_variable (name); + + if (!var) + var = bind_variable (name, ""); + + if (readonly_p (var) && (flags_off & att_readonly)) + { + builtin_error ("%s: readonly variable", name); + any_failed++; + NEXT_VARIABLE (); + } + + var->attributes |= flags_on; + var->attributes &= ~flags_off; + + 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); + } + } + + stupidly_hack_special_variables (name); + + NEXT_VARIABLE (); + } + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} diff --git a/builtins/echo.def b/builtins/echo.def new file mode 100644 index 0000000..7539103 --- /dev/null +++ b/builtins/echo.def @@ -0,0 +1,168 @@ +This file is echo.def, from which is created echo.c. +It implements the builtin "echo" 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 echo.c +#include <stdio.h> +#include "../shell.h" + +$BUILTIN echo +$FUNCTION echo_builtin +$DEPENDS_ON V9_ECHO +$SHORT_DOC echo [-neE] [arg ...] +Output the ARGs. If -n is specified, the trailing newline is +suppressed. If the -e option is given, interpretation of the +following backslash-escaped characters is turned on: + \a alert (bell) + \b backspace + \c suppress trailing newline + \f form feed + \n new line + \r carriage return + \t horizontal tab + \v vertical tab + \\ backslash + \num the character whose ASCII code is NUM (octal). + +You can explicitly turn off the interpretation of the above characters +with the -E option. +$END + +$BUILTIN echo +$FUNCTION echo_builtin +$DEPENDS_ON !V9_ECHO +$SHORT_DOC echo [-n] [arg ...] +Output the ARGs. If -n is specified, the trailing newline is suppressed. +$END + +#if defined (V9_ECHO) +# define VALID_ECHO_OPTIONS "neE" +#else /* !V9_ECHO */ +# define VALID_ECHO_OPTIONS "n" +#endif /* !V9_ECHO */ + +/* 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_builtin (list) + WORD_LIST *list; +{ + int display_return = 1, do_v9 = 0; + +#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; +#endif /* DEFAULT_ECHO_TO_USG */ + + while (list && list->word->word[0] == '-') + { + register char *temp; + register int i; + + /* 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]); + + for (i = 0; temp[i]; i++) + { + if (strchr (VALID_ECHO_OPTIONS, temp[i]) == 0) + goto just_echo; + } + + if (!*temp) + goto just_echo; + + /* All of the options in TEMP are valid options to ECHO. + Handle them. */ + while (*temp) + { + if (*temp == 'n') + display_return = 0; +#if defined (V9_ECHO) + else if (*temp == 'e') + do_v9 = 1; + else if (*temp == 'E') + do_v9 = 0; +#endif /* V9_ECHO */ + else + goto just_echo; + + temp++; + } + list = list->next; + } + +just_echo: + + if (list) + { +#if defined (V9_ECHO) + if (do_v9) + { + 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(' '); + } + } + else +#endif /* V9_ECHO */ + print_word_list (list, " "); + } + if (display_return) + printf ("\n"); + fflush (stdout); + return (EXECUTION_SUCCESS); +} diff --git a/builtins/enable.def b/builtins/enable.def new file mode 100644 index 0000000..2aeae39 --- /dev/null +++ b/builtins/enable.def @@ -0,0 +1,156 @@ +This file is enable.def, from which is created enable.c. +It implements the builtin "enable" 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 enable.c + +$BUILTIN enable +$FUNCTION enable_builtin +$SHORT_DOC enable [-n] [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 +NAMEs are enabled. For example, to use the `test' found on your +path instead of the shell builtin version, you type `enable -n test'. +$END + +#include "../shell.h" +#include "../builtins.h" +#include "common.h" + +#define ENABLED 1 +#define DISABLED 2 + +static int enable_shell_command (); +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. */ +enable_builtin (list) + WORD_LIST *list; +{ + int result = 0, any_failed = 0; + int disable_p, all_p; + + disable_p = all_p = 0; + + while (list && list->word->word && list->word->word[0] == '-') + { + 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 + { + bad_option (arg); + return (EXECUTION_FAILURE); + } + } + + if (!list) + { + int filter; + + if (all_p) + filter = ENABLED | DISABLED; + else if (disable_p) + filter = DISABLED; + else + filter = ENABLED; + + list_some_builtins (filter); + } + else + { + while (list) + { + result = enable_shell_command (list->word->word, disable_p); + + if (!result) + { + builtin_error ("%s: not a shell builtin", list->word->word); + any_failed++; + } + list = list->next; + } + } + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +/* List some builtins. + FILTER is a mask with two slots: ENABLED and DISABLED. */ +static void +list_some_builtins (filter) + int filter; +{ + register int i; + + for (i = 0; i < num_shell_builtins; i++) + { + if (!shell_builtins[i].function) + continue; + + if ((filter & ENABLED) && + (shell_builtins[i].flags & BUILTIN_ENABLED)) + { + printf ("enable %s\n", shell_builtins[i].name); + } + else if ((filter & DISABLED) && + ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0)) + { + printf ("enable -n %s\n", shell_builtins[i].name); + } + } +} + +/* Enable the shell command NAME. If DISABLE_P is non-zero, then + disable NAME instead. */ +static int +enable_shell_command (name, disable_p) + char *name; + int disable_p; +{ + register int i; + int found = 0; + + for (i = 0; i < num_shell_builtins; i++) + { + if (!shell_builtins[i].function) + continue; + + if (STREQ (name, shell_builtins[i].name)) + { + found++; + + if (disable_p) + shell_builtins[i].flags &= ~BUILTIN_ENABLED; + else + shell_builtins[i].flags |= BUILTIN_ENABLED; + } + } + return (found); +} diff --git a/builtins/eval.def b/builtins/eval.def new file mode 100644 index 0000000..5c3eda8 --- /dev/null +++ b/builtins/eval.def @@ -0,0 +1,45 @@ +This file is eval.def, from which is created eval.c. +It implements the builtin "eval" 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 eval.c + +$BUILTIN eval +$FUNCTION eval_builtin +$SHORT_DOC eval [arg ...] +Read ARGs as input to the shell and execute the resulting command(s). +$END + +#include "../shell.h" + +/* Parse the string that these words make, and execute the command found. */ +int +eval_builtin (list) + WORD_LIST *list; +{ + int result; + + /* 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); +} diff --git a/builtins/exec.def b/builtins/exec.def new file mode 100644 index 0000000..f950afc --- /dev/null +++ b/builtins/exec.def @@ -0,0 +1,163 @@ +This file is exec.def, from which is created exec.c. +It implements the builtin "exec" 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 exec.c + +$BUILTIN exec +$FUNCTION exec_builtin +$SHORT_DOC exec [ [-] 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. +$END + +#include "../shell.h" +#include <sys/types.h> +#include "../posixstat.h" +#include <signal.h> +#include <errno.h> + +#include "../execute_cmd.h" +#include "common.h" +#include "../flags.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 +exec_builtin (list) + WORD_LIST *list; +{ + int exit_value = EXECUTION_FAILURE; + + maybe_make_export_env (); + + /* First, let the redirections remain. */ + dispose_redirects (redirection_undo_list); + redirection_undo_list = (REDIRECT *)NULL; + + if (!list) + return (EXECUTION_SUCCESS); + else + { + /* Otherwise, execve the new command with args. */ + char *command, **args; + int dash_name = 0; + + 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++; + } + + if (!list) + return (EXECUTION_SUCCESS); + +#if defined (RESTRICTED_SHELL) + if (restricted) + { + builtin_error ("restricted"); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ + + args = make_word_array (list); + + /* 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; + } + + 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. */ + adjust_shell_level (-1); + maybe_make_export_env (); + +#if defined (HISTORY) + maybe_save_shell_history (); +#endif /* HISTORY */ + restore_original_signals (); + +#if defined (JOB_CONTROL) + if (subshell_environment == 0) + end_job_control (); +#endif /* JOB_CONTROL */ + + shell_execve (command, args, export_env); + + 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); + + failed_exec: + if (command) + free (command); + + if (subshell_environment || + (!interactive && !find_variable ("no_exit_on_failed_exec"))) + exit (exit_value); + + initialize_traps (); + reinitialize_signals (); + +#if defined (JOB_CONTROL) + restart_job_control (); +#endif /* JOB_CONTROL */ + + return (exit_value); + } +} diff --git a/builtins/exit.def b/builtins/exit.def new file mode 100644 index 0000000..25a3b63 --- /dev/null +++ b/builtins/exit.def @@ -0,0 +1,129 @@ +This file is exit.def, from which is created exit.c. +It implements the builtins "exit" and "logout" 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 exit.c + +$BUILTIN exit +$FUNCTION exit_builtin +$SHORT_DOC exit [n] +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 <sys/types.h> +#include "../shell.h" +#include "../jobs.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; + +int +exit_builtin (list) + WORD_LIST *list; +{ + if (interactive) + { + fprintf (stderr, login_shell ? "logout\n" : "exit\n"); + fflush (stderr); + } + + return (exit_or_logout (list)); +} + +$BUILTIN logout +$FUNCTION logout_builtin +$SHORT_DOC logout +Logout of a login shell. +$END + +/* How to logout. */ +int +logout_builtin (list) + WORD_LIST *list; +{ + if (!login_shell && interactive) + { + builtin_error ("Not login shell: use `exit'"); + return (EXECUTION_FAILURE); + } + else + return (exit_or_logout (list)); +} + +/* Clean up work for exiting or logging out. */ +Function *last_shell_builtin = (Function *)NULL; +Function *this_shell_builtin = (Function *)NULL; + +static int +exit_or_logout (list) + WORD_LIST *list; +{ + int exit_value; + +#if defined (JOB_CONTROL) + int exit_immediate_okay; + + exit_immediate_okay = (!interactive || + last_shell_builtin == exit_builtin || + last_shell_builtin == logout_builtin || + last_shell_builtin == jobs_builtin); + + /* Check for stopped jobs if the user wants to. */ + if (!exit_immediate_okay) + { + register int i; + for (i = 0; i < job_slots; i++) + if (jobs[i] && (jobs[i]->state == JSTOPPED)) + { + fprintf (stderr, "There are stopped jobs.\n"); + + /* This is NOT superfluous because EOF can get here without + going through the command parser. Set both last and this + so that either `exit', `logout', or ^D will work to exit + immediately if nothing intervenes. */ + this_shell_builtin = last_shell_builtin = exit_builtin; + return (EXECUTION_FAILURE); + } + } +#endif /* JOB_CONTROL */ + + /* Get return value if present. This means that you can type + `logout 5' to a shell, and it returns 5. */ + if (list) + exit_value = get_numeric_arg (list); + else + exit_value = 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); + + last_command_exit_value = exit_value; + + /* Exit the program. */ + longjmp (top_level, EXITPROG); +} diff --git a/builtins/fc.def b/builtins/fc.def new file mode 100644 index 0000000..1f6db68 --- /dev/null +++ b/builtins/fc.def @@ -0,0 +1,691 @@ +This file is fc.def, from which is created fc.c. +It implements the builtin "fc" 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 fc.c + +$BUILTIN fc +$FUNCTION fc_builtin +$DEPENDS_ON HISTORY +$SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd] + +FIRST and LAST can be numbers specifying the range, or FIRST can be a +string, which means the most recent command beginning with that +string. + + -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, + then the editor which corresponds to the current readline editing + mode, then vi. + + -l means list lines instead of editing. + -n means no line numbers listed. + -r means reverse the order of the lines (making it newest listed first). + +With the `fc -s [pat=rep ...] [command]' format, the command is +re-executed after the substitution OLD=NEW is performed. + +A useful alias to use with this is r='fc -s', so that typing `r cc' +runs the last command beginning with `cc' and typing `r' re-executes +the last command. +$END + +#include <stdio.h> +#include "../bashansi.h" +#include "../shell.h" +#if defined (HISTORY) +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <errno.h> +#include "../builtins.h" +#include "../flags.h" +#include "../maxpath.h" +#include "../bashhist.h" +#include <readline/history.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 echo_input_at_read; + +extern int unlink (); + +/* **************************************************************** */ +/* */ +/* The K*rn shell style fc command (Fix Command) */ +/* */ +/* **************************************************************** */ + +/* fc builtin command (fix command) for Bash for those who + like K*rn-style history better than csh-style. + + fc [-e ename] [-nlr] [first] [last] + + FIRST and LAST can be numbers specifying the range, or FIRST can be + a string, which means the most recent command beginning with that + string. + + -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, + then the editor which corresponds to the current readline editing + mode, then vi. + + -l means list lines instead of editing. + -n means no line numbers listed. + -r means reverse the order of the lines (making it newest listed first). + + fc -e - [pat=rep ...] [command] + fc -s [pat=rep ...] [command] + + Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's. +*/ + +static char *fc_dosubs (), *fc_replace (), *fc_gethist (), *fc_readline (); +static int fc_gethnum (); +static void fc_replhist (), fc_addhist (); + +/* Data structure describing a list of global replacements to perform. */ +typedef struct repl { + struct repl *next; + char *pat; + 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) + +#define FREE_RLIST() \ + do { \ + for (rl = rlist; rl; ) { \ + REPL *r; \ + r = rl->next; \ + if (rl->pat) \ + free (rl->pat); \ + if (rl->rep) \ + free (rl->rep); \ + free (rl); \ + rl = r; \ + } \ + } while (0) + +/* String to execute on a file that we want to edit. */ +#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}" + +int +fc_builtin (list) + WORD_LIST *list; +{ + register int i; + register char *sep; + 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; + HIST_ENTRY **hlist; + char fn[MAXPATHLEN]; + + numbering = 1; + reverse = listing = execute = 0; + + /* Parse out the options and set which of the two forms we're in. */ + + while (list && *list->word->word == '-') + { + register char *s = &((list->word->word)[1]); + + if (!isletter (*s)) + 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); + } + } + list = list->next; + } + + if (ename && (*ename == '-') && (ename[1] == '\0')) + execute = 1; + + /* The "execute" form of the command (re-run, with possible string + substitutions). */ + if (execute) + { + while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL)) + { + *sep++ = '\0'; + rl = (REPL *)xmalloc (sizeof (REPL)); + rl->next = (REPL *)NULL; + rl->pat = savestring (list->word->word); + rl->rep = savestring (sep); + + if (rlist == NULL) + rlist = rl; + else + { + rl->next = rlist; + rlist = rl; + } + list = list->next; + } + + /* If we have a list of substitutions to do, then reverse it + to get the replacements in the proper order. */ + + if (rlist && rlist->next) + 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); + + if (command == NULL) + { + builtin_error ("no command found"); + if (rlist) + FREE_RLIST (); + + return (EXECUTION_FAILURE); + } + + if (rlist) + { + newcom = fc_dosubs (command, rlist); + free (command); + FREE_RLIST (); + command = newcom; + } + + printf ("%s\n", command); + fc_replhist (command); /* replace `fc -e -' with command */ + return (parse_and_execute (command, "fc", -1)); + } + + /* This is the second form of the command (the list-or-edit-and-rerun + form). */ + hlist = history_list (); + if (hlist == 0) + return (EXECUTION_SUCCESS); + for (i = 0; hlist[i]; i++); + + /* With the Bash implementation of history, the current command line + ("fc blah..." and so on) is already part of the history list by + the time we get to this point. This just skips over that command + and makes the last command that this deals with be the last command + the user entered before the fc. */ + + last_hist = i - 2; + + if (list) + { + histbeg = fc_gethnum (list->word->word, hlist); + list = list->next; + + if (list) + histend = fc_gethnum (list->word->word, hlist); + else + { + if (listing) + histend = last_hist; + else + histend = histbeg; + } + } + else + { + /* The default for listing is the last 16 history items. */ + if (listing) + { + histend = last_hist; + histbeg = histend - 16; + if (histbeg < 0) + histbeg = 0; + } + else + { + /* For editing, it is the last history command. */ + histbeg = histend = last_hist; + } + } + + /* We print error messages for line specifications out of range. */ + if ((histbeg < 0) || (histend < 0) || + (histbeg > last_hist) || (histend > last_hist)) + { + builtin_error ("history specification out of range"); + return (EXECUTION_FAILURE); + } + + if (histend < histbeg) + { + int t = histend; + + histend = histbeg; + histbeg = t; + reverse = 1; + } + + if (listing) + stream = stdout; + else + { + numbering = 0; + sprintf (fn, "/tmp/bash%d", (int)time ((long *) 0) + (int)getpid ()); + + stream = fopen (fn, "w"); + + if (!stream) + { + builtin_error ("cannot open temp file %s", fn); + return (EXECUTION_FAILURE); + } + } + + if (!reverse) + { + 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)); + } + } + + if (listing) + return (EXECUTION_SUCCESS); + + fclose (stream); + + /* Now edit the file of commands. */ + if (ename) + { + command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2); + sprintf (command, "%s %s", ename, fn); + } + else + { + command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn)); + sprintf (command, "%s %s", FC_EDIT_COMMAND, fn); + } + parse_and_execute (command, "fc", -1); + + /* Now reopen the file and execute the edited commands. */ + + stream = fopen (fn, "r"); + + if (stream == NULL) + { + builtin_error ("cannot reopen temp file %s", fn); + unlink (fn); + return (EXECUTION_FAILURE); + } + + retval = EXECUTION_SUCCESS; + first = 1; + + /* First, write the commands to the history file. This will not happen + when we call parse_and_execute, since parse_and_execute disables + the command line history while it executes. */ + + while ((line = fc_readline (stream)) != NULL) + { + if (line[0] == '\n') + { + free (line); + continue; /* Skip blank lines. */ + } + + if (first) + { + first = 0; + fc_replhist (line); + } + else + fc_addhist (line); + + free (line); + } + fclose (stream); + + /* Turn on the `v' flag while maybe_execute_file runs so the commands + will be echoed as they are read by the parser. */ + begin_unwind_frame ("fc builtin"); + add_unwind_protect (unlink, fn); + unwind_protect_int (echo_input_at_read); + echo_input_at_read = 1; + + retval = maybe_execute_file (fn, 0); + + run_unwind_frame ("fc builtin"); + + return (retval); +} + +/* Return an absolute index into HLIST which corresponds to COMMAND. If + COMMAND is a number, then it was specified in relative terms. If it + is a string, then it is the start of a command line present in HLIST. */ +static int +fc_gethnum (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int sign = 1, n, clen; + register int i, j; + register char *s; + + /* Count history elements. */ + for (i = 0; hlist[i]; i++); + + /* With the Bash implementation of history, the current command line + ("fc blah..." and so on) is already part of the history list by + the time we get to this point. This just skips over that command + and makes the last command that this deals with be the last command + the user entered before the fc. */ + i -= 2; + + /* No specification defaults to most recent command. */ + if (command == NULL) + return (i); + + /* Otherwise, there is a specification. It can be a number relative to + the current position, or an absolute history number. */ + s = command; + + /* Handle possible leading minus sign. */ + if (s && (*s == '-')) + { + sign = -1; + s++; + } + + if (s && digit(*s)) + { + n = atoi (s); + n *= sign; + + /* Anything specified greater than the last history element that we + deal with is an error. */ + if (n > i + history_base) + return (-1); + + /* If the value is negative or zero, then it is an offset from + the current history item. */ + if (n < 0) + return (i + n + 1); + else if (n == 0) + return (i); + else + return (n - history_base); + } + + clen = strlen (command); + for (j = i; j >= 0; j--) + { + if (STREQN (command, histline (j), clen)) + return (j); + } + return (-1); +} + +/* Locate the most recent history line which begins with + COMMAND in HLIST, and return a malloc()'ed copy of it. */ +static char * +fc_gethist (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int i; + + if (!hlist) + return ((char *)NULL); + + i = fc_gethnum (command, hlist); + + if (i >= 0) + return (savestring (histline (i))); + else + return ((char *)NULL); +} + +/* Read the edited history lines from STREAM and return them + one at a time. This can read unlimited length lines. The + caller should free the storage. */ +static char * +fc_readline (stream) + FILE *stream; +{ + register int c; + int line_len = 0, lindex = 0; + char *line = (char *)NULL; + + while ((c = getc (stream)) != EOF) + { + if ((lindex + 2) >= line_len) + line = (char *) xrealloc (line, (line_len += 128)); + + if (c == '\n') + { + line[lindex++] = '\n'; + line[lindex++] = '\0'; + return (line); + } + else + line[lindex++] = c; + } + + if (!lindex) + { + if (line) + free (line); + + return ((char *)NULL); + } + + if (lindex + 2 >= line_len) + line = (char *)xrealloc (line, lindex + 3); + + line[lindex++] = '\n'; /* Finish with newline if none in file */ + line[lindex++] = '\0'; + return (line); +} + +/* Perform the SUBS on COMMAND. + SUBS is a list of substitutions, and COMMAND is a simple string. + Return a pointer to a malloc'ed string which contains the substituted + command. */ +static char * +fc_dosubs (command, subs) + char *command; + REPL *subs; +{ + register char *new = savestring (command); + register REPL *r; + + for (r = subs; r; r = r->next) + { + register char *t; + + t = fc_replace (r->pat, r->rep, new); + 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 + history list. This way you can do `r' to your heart's content. */ +static void +fc_replhist (command) + char *command; +{ + register int i; + HIST_ENTRY **hlist, *histent, *discard; + char *data; + int n; + + if (!command || !*command) + return; + + hlist = history_list (); + + if (hlist == NULL) + return; + + for (i = 0; hlist[i]; i++); + i--; + + /* History_get () takes a parameter that should be + offset by history_base. */ + + histent = history_get (history_base + i); /* Don't free this */ + if (histent == NULL) + return; + + n = strlen (command); + + if (command[n - 1] == '\n') + command[n - 1] = '\0'; + + if (command && *command) + { + discard = remove_history (i); + if (discard) + { + if (discard->line) + free (discard->line); + free ((char *) discard); + } + maybe_add_history (command); /* Obeys HISTCONTROL setting. */ + } +} + +/* Add LINE to the history, after removing a single trailing newline. */ +static void +fc_addhist (line) + char *line; +{ + register int n; + + n = strlen (line); + + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + if (line && *line) + maybe_add_history (line); +} +#endif /* HISTORY */ diff --git a/builtins/fg_bg.def b/builtins/fg_bg.def new file mode 100644 index 0000000..e48af38 --- /dev/null +++ b/builtins/fg_bg.def @@ -0,0 +1,145 @@ +This file is fg_bg.def, from which is created fg_bg.c. +It implements the builtins "bg" and "fg" in Bash. + +Copyright (C) 1987, 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 fg_bg.c + +$BUILTIN fg +$FUNCTION fg_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC fg [job_spec] +Place JOB_SPEC in the foreground, and make it the current job. If +JOB_SPEC is not present, the shell's notion of the current job is +used. +$END + +#include <sys/types.h> +#include <signal.h> +#include "../shell.h" +#include "../jobs.h" + +#if defined (JOB_CONTROL) +extern char *this_command_name; + +static int fg_bg (); + +/* How to bring a job into the foreground. */ +int +fg_builtin (list) + WORD_LIST *list; +{ + int fg_bit = 1; + register WORD_LIST *t = list; + + if (!job_control) + { + builtin_error ("no job control"); + return (EXECUTION_FAILURE); + } + + /* 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; + + return (fg_bg (list, fg_bit)); +} +#endif /* JOB_CONTROL */ + +$BUILTIN bg +$FUNCTION bg_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC bg [job_spec] +Place JOB_SPEC in the background, as if it had been started with +`&'. If JOB_SPEC is not present, the shell's notion of the current +job is used. +$END + +#if defined (JOB_CONTROL) +/* How to put a job into the background. */ +int +bg_builtin (list) + WORD_LIST *list; +{ + if (!job_control) + { + builtin_error ("no job control"); + return (EXECUTION_FAILURE); + } + + return (fg_bg (list, 0)); +} + +/* How to put a job into the foreground/background. */ +static int +fg_bg (list, foreground) + WORD_LIST *list; + int foreground; +{ + sigset_t set, oset; + int job, status = EXECUTION_SUCCESS, old_async_pid; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (job < 0 || job >= job_slots || !jobs[job]) + { + if (job != DUP_JOB) + builtin_error ("No such job %s", list ? list->word->word : ""); + + goto failure; + } + + /* Or if jobs[job]->pgrp == shell_pgrp. */ + if (!(jobs[job]->flags & J_JOBCONTROL)) + { + builtin_error ("job %%%d started without job control", job + 1); + goto failure; + } + + if (!foreground) + { + old_async_pid = last_asynchronous_pid; + last_asynchronous_pid = jobs[job]->pgrp; /* As per Posix.2 5.4.2 */ + } + + status = start_job (job, foreground); + + if (status >= 0) + { + /* win: */ + UNBLOCK_CHILD (oset); + return (status); + } + else + { + if (!foreground) + last_asynchronous_pid = old_async_pid; + + failure: + UNBLOCK_CHILD (oset); + return (EXECUTION_FAILURE); + } +} +#endif /* JOB_CONTROL */ diff --git a/builtins/getopt.c b/builtins/getopt.c new file mode 100644 index 0000000..7603430 --- /dev/null +++ b/builtins/getopt.c @@ -0,0 +1,283 @@ +/* getopt for BASH. + + Copyright (C) 1993, 1994 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "../memalloc.h" +#include "../shell.h" +#include "getopt.h" + +/* For communication from `sh_getopt' to the caller. + When `sh_getopt' finds an option that takes an argument, + the argument value is returned here. */ +char *sh_optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `sh_getopt'. + + On entry to `sh_getopt', zero means this is the first call; initialize. + + When `sh_getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `sh_optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int sh_optind = 0; + +/* Index of the current argument. */ +static int sh_curopt; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; +static int sh_charindex; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int sh_opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int sh_optopt = '?'; + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `sh_getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `sh_getopt' finds another option character, it returns that character, + updating `sh_optind' and `nextchar' so that the next call to `sh_getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `sh_getopt' returns `EOF'. + Then `sh_optind' is the index in ARGV of the first ARGV-element + that is not an option. + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `sh_opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `sh_optarg'. */ + +/* 1003.2 specifies the format of this message. */ +#define BADOPT(x) fprintf (stderr, "%s: illegal option -- %c\n", argv[0], x) +#define NEEDARG(x) fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], x) + +int +sh_getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + int option_index; + char c, *temp; + + sh_optarg = 0; + + if (sh_optind > argc || sh_optind < 0) + { + sh_optind = argc; + return (EOF); + } + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (sh_optind == 0) + { + sh_optind = 1; + nextchar = (char *)NULL; + } + + if (nextchar == 0 || *nextchar == '\0') + { + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + if (sh_optind == argc) + return EOF; + + temp = argv[sh_optind]; + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, and return EOF. */ + if (temp[0] == '-' && temp[1] == '-' && temp[2] == '\0') + { + sh_optind++; + return EOF; + } + + /* If we have come to a non-option, either stop the scan or describe + it to the caller and pass it by. This makes the pseudo-option + `-' mean the end of options, but does not skip over it. */ + if (temp[0] != '-' || temp[1] == '\0') + return EOF; + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + nextchar = argv[sh_curopt = sh_optind] + 1; + sh_charindex = 1; + } + + /* Look at and handle the next option-character. */ + + c = *nextchar++; sh_charindex++; + temp = strchr (optstring, c); + + /* 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 (sh_opterr) + BADOPT (c); + + return '?'; + } + + if (temp[1] == ':') + { + if (nextchar && *nextchar) + { + /* This is an option that requires an argument. */ + sh_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + sh_optind++; + } + else if (sh_optind == argc) + { + if (sh_opterr) + NEEDARG (c); + + sh_optopt = c; + c = (optstring[0] == ':') ? ':' : '?'; + } + else + /* We already incremented `sh_optind' once; + increment it again when taking next ARGV-elt as argument. */ + sh_optarg = argv[sh_optind++]; + nextchar = (char *)NULL; + } + return c; +} + +void +sh_getopt_restore_state (argv) + char **argv; +{ + if (nextchar) + nextchar = argv[sh_curopt] + sh_charindex; +} + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `sh_getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_sh_optind = 0; + + while (1) + { + int this_option_sh_optind = sh_optind ? sh_optind : 1; + + c = sh_getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_sh_optind != 0 && digit_sh_optind != this_option_sh_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_sh_optind = this_option_sh_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", sh_optarg); + break; + + case '?': + break; + + default: + printf ("?? sh_getopt returned character code 0%o ??\n", c); + } + } + + if (sh_optind < argc) + { + printf ("non-option ARGV-elements: "); + while (sh_optind < argc) + printf ("%s ", argv[sh_optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/builtins/getopt.h b/builtins/getopt.h new file mode 100644 index 0000000..fa9878c --- /dev/null +++ b/builtins/getopt.h @@ -0,0 +1,57 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* XXX THIS HAS BEEN MODIFIED FOR INCORPORATION INTO BASH XXX */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *sh_optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `sh_optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int sh_optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int sh_opterr; + +/* Set to an option character which was unrecognized. */ + +extern int sh_optopt; + +extern int sh_getopt (); +extern void sh_getopt_restore_state (); + +#endif /* _GETOPT_H */ diff --git a/builtins/getopts.def b/builtins/getopts.def new file mode 100644 index 0000000..0f2b82f --- /dev/null +++ b/builtins/getopts.def @@ -0,0 +1,300 @@ +This file is getopts.def, from which is created getopts.c. +It implements the builtin "getopts" 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 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. + +OPTSTRING contains the option letters to be recognized; if a letter +is followed by a colon, the option is expected to have an argument, +which should be separated from it by white space. + +Each time it is invoked, getopts will place the next option in the +shell variable $name, initializing name if it does not exist, and +the index of the next argument to be processed into the shell +variable OPTIND. OPTIND is initialized to 1 each time the shell or +a shell script is invoked. When an option requires an argument, +getopts places that argument into the shell variable OPTARG. + +getopts reports errors in one of two ways. If the first character +of OPTSTRING is a colon, getopts uses silent error reporting. In +this mode, no error messages are printed. If an illegal option is +seen, getopts places the option character found into OPTARG. If a +required argument is not found, getopts places a ':' into NAME and +sets OPTARG to the option character found. If getopts is not in +silent mode, and an illegal option is seen, getopts places '?' into +NAME and unsets OPTARG. If a required option is not found, a '?' +is placed in NAME, OPTARG is unset, and a diagnostic message is +printed. + +If the shell variable OPTERR has the value 0, getopts disables the +printing of error messages, even if the first character of +OPTSTRING is not a colon. OPTERR has the value 1 by default. + +Getopts normally parses the positional parameters ($0 - $9), but if +more arguments are given, they are parsed instead. +$END + +#include <stdio.h> + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include "../shell.h" + +#if defined (GETOPTS_BUILTIN) +#include "getopt.h" + +#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; + +/* getopts_reset is magic code for when OPTIND is reset. N is the + value that has just been assigned to OPTIND. */ +void +getopts_reset (newind) + int newind; +{ + sh_optind = newind; +} + +/* Error handling is now performed as specified by Posix.2, draft 11 + (identical to that of ksh-88). The special handling is enabled if + the first character of the option string is a colon; this handling + disables diagnostic messages concerning missing option arguments + and illegal option characters. The handling is as follows. + + ILLEGAL OPTIONS: + name -> "?" + if (special_error) then + OPTARG = option character found + no error output + else + OPTARG unset + diagnostic message + fi + + MISSING OPTION ARGUMENT; + if (special_error) then + name -> ":" + OPTARG = option character found + else + name -> "?" + OPTARG unset + diagnostic message + fi + */ + +static int +dogetopts (argc, argv) + int argc; + char **argv; +{ + int ret, special_error, old_opterr = 0, i, n; + char strval[2], numval[16]; + char *optstr; /* list of options */ + char *name; /* variable to get flag val */ + char *t; + + if (argc < 3) + { + builtin_error("usage: getopts optstring name [arg]"); + return (EX_USAGE); + } + + /* argv[0] is "getopts". */ + + optstr = argv[1]; + name = argv[2]; + argc -= 2; + argv += 2; + + special_error = optstr[0] == ':'; + + if (special_error) + { + old_opterr = sh_opterr; + optstr++; + sh_opterr = 0; /* suppress diagnostic messages */ + } + + if (argc > 1) + { + sh_getopt_restore_state (argv); + t = argv[0]; + argv[0] = dollar_vars[0]; + ret = sh_getopt (argc, argv, optstr); + argv[0] = t; + } + else if (rest_of_args == (WORD_LIST *)NULL) + { + register int 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++); + v = (char **)xmalloc ((i + 1) * sizeof (char *)); + for (i = 0; i < 10 && dollar_vars[i]; i++) + v[i] = dollar_vars[i]; + for (words = rest_of_args; words; words = words->next, i++) + v[i] = words->word->word; + v[i] = (char *)NULL; + ret = sh_getopt (i, v, optstr); + free (v); + } + + if (special_error) + sh_opterr = old_opterr; + + /* Set the OPTIND variable in any case, to handle "--" skipping. */ + if (sh_optind < 10) + { + numval[14] = sh_optind + '0'; + numval[15] = '\0'; + i = 14; + } + else + { + numval[i = 15] = '\0'; + n = sh_optind; + do + { + numval[--i] = (n % 10) + '0'; + } + while (n /= 10); + } + bind_variable ("OPTIND", numval + i); + + /* 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 + NULL. If a required option argument was missing, OPTARG points + to a NULL string (that is, optarg[0] == 0). */ + if (ret == '?') + { + if (sh_optarg == NULL) + ret = G_ILLEGAL_OPT; + else if (sh_optarg[0] == '\0') + ret = G_ARG_MISSING; + } + + if (ret == G_EOF) + { + bind_variable (name, "?"); + return (EXECUTION_FAILURE); + } + + if (ret == G_ILLEGAL_OPT) + { + /* Illegal option encountered. */ + strval[0] = '?'; + strval[1] = '\0'; + bind_variable (name, strval); + + if (special_error) + { + strval[0] = (char) sh_optopt; + strval[1] = '\0'; + bind_variable ("OPTARG", strval); + } + else + makunbound ("OPTARG", shell_variables); + return (EXECUTION_SUCCESS); + } + + if (ret == G_ARG_MISSING) + { + /* Required argument missing. */ + if (special_error) + { + strval[0] = ':'; + strval[1] = '\0'; + bind_variable (name, strval); + + strval[0] = (char) sh_optopt; + strval[1] = '\0'; + bind_variable ("OPTARG", strval); + } + else + { + strval[0] = '?'; + strval[1] = '\0'; + bind_variable (name, strval); + makunbound ("OPTARG", shell_variables); + } + return (EXECUTION_SUCCESS); + } + + bind_variable ("OPTARG", sh_optarg); + + strval[0] = (char) ret; + strval[1] = '\0'; + bind_variable (name, strval); + + return (EXECUTION_SUCCESS); +} + +/* The getopts builtin. Build an argv, and call dogetopts with it. */ +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; + + ret = dogetopts (ac, av); + free ((char *)av); + return (ret); +} +#endif /* GETOPTS_BUILTIN */ diff --git a/builtins/hash.def b/builtins/hash.def new file mode 100644 index 0000000..f4d319b --- /dev/null +++ b/builtins/hash.def @@ -0,0 +1,222 @@ +This file is hash.def, from which is created hash.c. +It implements the builtin "hash" 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 hash.c + +$BUILTIN hash +$FUNCTION hash_builtin +$SHORT_DOC hash [-r] [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. +$END + +#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 */ + +#include "../shell.h" +#include "../builtins.h" +#include "../flags.h" +#include "hashcom.h" +#include "common.h" +#include "../execute_cmd.h" + +extern int dot_found_in_search; + +void +initialize_filename_hashing () +{ + hashed_filenames = make_hash_table (FILENAME_HASH_BUCKETS); +} + +/* 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. */ +hash_builtin (list) + WORD_LIST *list; +{ + int expunge_hash_table = 0; + int any_failed = 0; + + if (hashing_disabled) + { + builtin_error ("hashing disabled"); + return (EXECUTION_FAILURE); + } + + while (list) + { + char *arg = list->word->word; + + if (ISOPTION (arg, '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 ...]"); + return (EX_USAGE); + } + else + break; + } + + /* 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) + { + /* 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"); + + return (EXECUTION_SUCCESS); + } + + if (expunge_hash_table) + { + int bucket = 0; + register BUCKET_CONTENTS *item_list, *prev; + + 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) + { + /* 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 (full_path && executable_file (full_path)) + remember_filename (word, full_path, dot_found_in_search, 0); + else + { + builtin_error ("%s: not found", word); + any_failed++; + } + } + if (full_path) + free (full_path); + + list = list->next; + } + + fflush (stdout); + + if (any_failed) + return (EXECUTION_FAILURE); + else + return (EXECUTION_SUCCESS); +} + +/* 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. */ +void +remember_filename (filename, full_pathname, check_dot, found) + char *filename, *full_pathname; + int check_dot, found; +{ + register BUCKET_CONTENTS *item; + + if (hashing_disabled) + 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)); + } + pathdata(item)->path = savestring (full_pathname); + pathdata(item)->check_dot = check_dot; + item->times_found = found; +} diff --git a/builtins/hashcom.h b/builtins/hashcom.h new file mode 100644 index 0000000..defe2fc --- /dev/null +++ b/builtins/hashcom.h @@ -0,0 +1,32 @@ +/* hashcom.h - Common defines for hashing filenames. */ + +/* Copyright (C) 1993 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, 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 "../hash.h" + +#define FILENAME_HASH_BUCKETS 631 + +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. */ +} PATH_DATA; + +#define pathdata(x) ((PATH_DATA *)(x)->data) diff --git a/builtins/help.def b/builtins/help.def new file mode 100644 index 0000000..c9f1db8 --- /dev/null +++ b/builtins/help.def @@ -0,0 +1,134 @@ +This file is help.def, from which is created help.c. +It implements the builtin "help" 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 help.c + +$BUILTIN help +$FUNCTION 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 <stdio.h> +#include "../shell.h" +#include "../builtins.h" + +#if defined (USE_GLOB_LIBRARY) +# include <glob/glob.h> +#else +# define FNM_NOMATCH 1 +#endif /* USE_GLOB_LIBRARY */ + +/* 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. */ +help_builtin (list) + WORD_LIST *list; +{ + if (!list) + { + register int i, j; + char blurb[256]; + + 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"); + + for (i = 0; i < num_shell_builtins; 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); + + } + if (i % 2) + printf ("\n"); + } + else + { + int match_found = 0; + char *pattern = ""; + + if (glob_pattern_p (list->word->word)) + { + printf ("Shell commands matching keyword%s `", + list->next ? "s" : ""); + print_word_list (list, ", "); + printf ("'\n\n"); + } + + while (list) + { + register int i = 0, plen; + char *name; + + pattern = list->word->word; + plen = strlen (pattern); + + while (name = shell_builtins[i].name) + { + int doc_index; + + QUIT; + if ((strncmp (pattern, name, plen) == 0) || + (fnmatch (pattern, name, 0) != FNM_NOMATCH)) + { + printf ("%s: %s\n", name, shell_builtins[i].short_doc); + + 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++; + } + list = list->next; + } + + if (!match_found) + { + fprintf (stderr, "No help topics match `%s'. Try `help help'.\n", + pattern); + fflush (stderr); + return (EXECUTION_FAILURE); + } + } + fflush (stdout); + return (EXECUTION_SUCCESS); +} diff --git a/builtins/history.def b/builtins/history.def new file mode 100644 index 0000000..814e705 --- /dev/null +++ b/builtins/history.def @@ -0,0 +1,179 @@ +This file is history.def, from which is created history.c. +It implements the builtin "history" 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 history.c + +$BUILTIN history +$FUNCTION history_builtin +$DEPENDS_ON HISTORY +$SHORT_DOC history [n] [ [-awrn] [filename]] +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 +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. +$END + +#include "../shell.h" +#if defined (HISTORY) +#include <sys/types.h> +#include <sys/file.h> +#include "../filecntl.h" +#include "../posixstat.h" +#include "../bashhist.h" +#include <readline/history.h> + +/* History. Arg of -w FILENAME means write file, arg of -r FILENAME + means read file. Arg of N means only display that many items. */ + +history_builtin (list) + WORD_LIST *list; +{ + register int i; + int limited = 0, limit = 0; + HIST_ENTRY **hlist; + + while (list) + { + 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) + { + list = list->next; + break; + } + else if (*list->word->word == '-') + { + bad_option (list->word->word); + builtin_error ("usage: history [n] [-rwan [filename]]"); + return (EX_USAGE); + } + else + break; + } + + if (list) + { + limited = 1; + limit = get_numeric_arg (list); + } + + hlist = history_list (); + + if (hlist) + { + for (i = 0; hlist[i]; i++); + + if (limit < 0) + limit = -limit; + + if (!limited) + 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); + i++; + } + } + return (EXECUTION_SUCCESS); +} +#endif /* HISTORY */ diff --git a/builtins/inlib.def b/builtins/inlib.def new file mode 100644 index 0000000..023945b --- /dev/null +++ b/builtins/inlib.def @@ -0,0 +1,74 @@ +This file is inlib.def, from which is created inlib.c. +It implements the Apollo-specific builtin "inlib" 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 inlib.c +#include <stdio.h> +#include "../shell.h" + +$BUILTIN inlib +$FUNCTION inlib_builtin +$DEPENDS_ON apollo +$SHORT_DOC inlib pathname [pathname...] +Install a user-supplied library specified by pathname in the current +shell process. The library is used to resolve external references +in programs and libraries loaded after its installation. Note +that the library is not loaded into the address space unless it is +needed to resolve an external reference. The list of inlibed +libraries is passed to all children of the current shell. +$END + +#if defined (apollo) + +#include <apollo/base.h> +#include <apollo/loader.h> + +inlib_builtin (list) + WORD_LIST *list; +{ + status_$t status; + int return_value; + short len; + + if (!list) + { + builtin_error ("usage: inlib pathname [pathname...]"); + return (EX_USAGE); + } + + return_value = EXECUTION_SUCCESS; + + while (list) + { + len = (short)strlen (list->word->word); + loader_$inlib (list->word->word, len, &status); + + if (status.all != status_$ok) + { + builtin_error ("inlib failed for %s", list->word->word); + return_value = EXECUTION_FAILURE; + } + + list = list->next; + } + + return (return_value); +} +#endif /* apollo */ diff --git a/builtins/jobs.def b/builtins/jobs.def new file mode 100644 index 0000000..8a293da --- /dev/null +++ b/builtins/jobs.def @@ -0,0 +1,171 @@ +This file is jobs.def, from which is created jobs.c. +It implements the builtin "jobs" in Bash. + +Copyright (C) 1987, 1989, 1991, 1992 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 jobs.c + +$BUILTIN jobs +$FUNCTION jobs_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC jobs [-lnp] [jobspec ...] | 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. +$END + +#include "../shell.h" + +#if defined (JOB_CONTROL) +#include <sys/types.h> +#include <signal.h> +#include "../jobs.h" + +#include "bashgetopt.h" + +extern int job_control, interactive_shell; +static int execute_list_with_replacements (); + +/* The `jobs' command. Prints outs a list of active jobs. If the + argument `-l' is given, then the process id's are printed also. + If the argument `-p' is given, print the process group leader's + pid only. If `-n' is given, only processes that have changed + status since the last notification are printed. If -x is given, + replace all job specs with the pid of the appropriate process + group leader and execute the command. */ +int +jobs_builtin (list) + WORD_LIST *list; +{ + int form = JLIST_STANDARD, execute = 0; + int opt; + int any_failed = 0; + + if (!job_control && !interactive_shell) + return (EXECUTION_SUCCESS); + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "lpnx")) != -1) + { + switch (opt) + { + case 'l': + form = JLIST_LONG; + break; + case 'p': + form = JLIST_PID_ONLY; + break; + case 'n': + form = JLIST_CHANGED_ONLY; + break; + case 'x': + if (form != JLIST_STANDARD) + { + builtin_error ("Other options not allowed with `-x'"); + return (EXECUTION_FAILURE); + } + execute++; + break; + + default: + builtin_error ("usage: jobs [-lpn [jobspec]] [-x command [args]]"); + return (EX_USAGE); + } + } + + list = loptend; + + if (execute) + return (execute_list_with_replacements (list)); + + if (!list) + { + list_jobs (form); + 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); + any_failed++; + } + else if (job != DUP_JOB) + list_one_job ((JOB *)NULL, form, 0, job); + + UNBLOCK_CHILD (oset); + list = list->next; + } + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +static int +execute_list_with_replacements (list) + WORD_LIST *list; +{ + register WORD_LIST *l; + int job, result; + + /* First do the replacement of job specifications with pids. */ + for (l = list; l; l = l->next) + { + if (l->word->word[0] == '%') /* we have a winner */ + { + job = get_job_spec (l); + + /* A bad job spec is not really a job spec! Pass it through. */ + if (job < 0 || job >= job_slots || !jobs[job]) + continue; + + free (l->word->word); + l->word->word = itos (jobs[job]->pgrp); + } + } + + /* Next make a new simple command and execute it. */ + begin_unwind_frame ("jobs_builtin"); + { + COMMAND *command = (COMMAND *)NULL; + + add_unwind_protect (dispose_command, command); + + command = make_bare_simple_command (); + command->value.Simple->words = copy_word_list (list); + command->value.Simple->redirects = (REDIRECT *)NULL; + command->flags |= CMD_INHIBIT_EXPANSION; + command->value.Simple->flags |= CMD_INHIBIT_EXPANSION; + + result = execute_command (command); + } + + run_unwind_frame ("jobs_builtin"); + return (result); +} +#endif /* JOB_CONTROL */ diff --git a/builtins/kill.def b/builtins/kill.def new file mode 100644 index 0000000..53d5c8f --- /dev/null +++ b/builtins/kill.def @@ -0,0 +1,281 @@ +This file is kill.def, from which is created kill.c. +It implements the builtin "kill" 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 kill.c + +$BUILTIN kill +$FUNCTION kill_builtin +$DEPENDS_ON JOB_CONTROL +$SHORT_DOC kill [-s sigspec | -sigspec] [pid | job]... | -l [signum] +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 +be signal numbers for which names should be listed. Kill is a shell +builtin for two reasons: it allows job IDs to be used instead of +process IDs, and, 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 "../bashtypes.h" +#include "../shell.h" +#include "../trap.h" +#include "../jobs.h" +#include "common.h" +#include <errno.h> + +#if defined (JOB_CONTROL) +extern int interactive; +extern int posixly_correct; + +#if !defined (CONTINUE_AFTER_KILL_ERROR) +# define CONTINUE_OR_FAIL return (EXECUTION_FAILURE) +#else +# define CONTINUE_OR_FAIL goto continue_killing +#endif /* CONTINUE_AFTER_KILL_ERROR */ + +/* Here is the kill builtin. We only have it so that people can type + kill -KILL %1? No, if you fill up the process table this way you + can still kill some. */ +int +kill_builtin (list) + WORD_LIST *list; +{ + int signal = SIGTERM; + int any_succeeded = 0, listing = 0, saw_signal = 0; + char *sigspec = "TERM", *word; + pid_t pid; + + if (!list) + return (EXECUTION_SUCCESS); + + /* Process options. */ + while (list) + { + word = list->word->word; + + if (ISOPTION (word, 'l')) + { + listing++; + list = list->next; + } + else if (ISOPTION (word, 's')) + { + list = list->next; + if (list) + { + sigspec = list->word->word; + if (sigspec[0] == '0' && !sigspec[1]) + signal = 0; + else + signal = decode_signal (sigspec); + list = list->next; + } + else + { + builtin_error ("-s requires an argument"); + return (EXECUTION_FAILURE); + } + } + else if (ISOPTION (word, '-')) + { + list = list->next; + break; + } + /* If this is a signal specification then process it. We only process + the first one seen; other arguments may signify process groups (e.g, + -num == process group num). */ + else if ((*word == '-') && !saw_signal) + { + sigspec = word + 1; + signal = decode_signal (sigspec); + saw_signal++; + list = list->next; + } + else + break; + } + + 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); + } + + /* OK, we are killing processes. */ + if (signal == NO_SIG) + { + builtin_error ("bad signal spec `%s'", sigspec); + return (EXECUTION_FAILURE); + } + + while (list) + { + word = list->word->word; + + if (*word == '-') + word++; + + if (all_digits (word)) + { + /* Use the entire argument in case of minus sign presence. */ + pid = (pid_t) atoi (list->word->word); + + if (kill_pid (pid, signal, 0) < 0) + goto signal_error; + else + any_succeeded++; + } + else if (*list->word->word != '%') + { + builtin_error ("No such pid %s", 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; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (job < 0 || job >= job_slots || !jobs[job]) + { + if (job != DUP_JOB) + builtin_error ("No such job %s", list->word->word); + UNBLOCK_CHILD (oset); + CONTINUE_OR_FAIL; + } + + /* Job spec used. Kill the process group. If the job was started + without job control, then its pgrp == shell_pgrp, so we have + to be careful. We take the pid of the first job in the pipeline + in that case. */ + if (jobs[job]->flags & J_JOBCONTROL) + pid = jobs[job]->pgrp; + else + pid = jobs[job]->pipe->pid; + + UNBLOCK_CHILD (oset); + + if (kill_pid (pid, signal, 1) < 0) + { + signal_error: + if (errno == EPERM) + builtin_error ("(%d) - Not owner", (int)pid); + else if (errno == ESRCH) + builtin_error ("(%d) - No such pid", (int)pid); + else + builtin_error ("Invalid signal %d", signal); + CONTINUE_OR_FAIL; + } + else + any_succeeded++; + } + else + { + builtin_error ("bad process specification `%s'", list->word->word); + CONTINUE_OR_FAIL; + } + continue_killing: + list = list->next; + } + + if (any_succeeded) + return (EXECUTION_SUCCESS); + else + return (EXECUTION_FAILURE); +} +#endif /* JOB_CONTROL */ diff --git a/builtins/let.def b/builtins/let.def new file mode 100644 index 0000000..fdb3a6f --- /dev/null +++ b/builtins/let.def @@ -0,0 +1,77 @@ +This file is let.def, from which is created let.c. +It implements the builtin "let" 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. + +$BUILTIN let +$FUNCTION let_builtin +$PRODUCES let.c +$SHORT_DOC let arg [arg ...] +Each ARG is an arithmetic expression to be evaluated. Evaluation +is done in long integers with no check for overflow, though division +by 0 is trapped and flagged as an error. The following list of +operators is grouped into levels of equal-precedence operators. +The levels are listed in order of decreasing precedence. + + - unary minus + ! logical NOT + * / % multiplication, division, remainder + + - addition, subtraction + <= >= < > comparison + == != equality inequality + = assignment + +Shell variables are allowed as operands. The name of the variable +is replaced by its value (coerced to a long integer) within +an expression. The variable need not have its integer attribute +turned on to be used in an expression. + +Operators are evaluated in order of precedence. Sub-expressions in +parentheses are evaluated first and may override the precedence +rules above. + +If the last ARG evaluates to 0, let returns 1; 0 is returned +otherwise. +$END + +#include "../shell.h" + +/* Arithmetic LET function. */ +let_builtin (list) + WORD_LIST *list; +{ + long ret = 0L; + + if (!list) + { + builtin_error ("argument (expression) expected"); + return (EXECUTION_FAILURE); + } + + while (list) + { + ret = evalexp (list->word->word); + list = list->next; + } + + if (ret == 0L) + return (EXECUTION_FAILURE); + else + return (EXECUTION_SUCCESS); +} diff --git a/builtins/mkbuiltins.c b/builtins/mkbuiltins.c new file mode 100644 index 0000000..572d01e --- /dev/null +++ b/builtins/mkbuiltins.c @@ -0,0 +1,1311 @@ +/* mkbuiltins.c - Create builtins.c, builtext.h, and builtdoc.c from + a single source file called builtins.def. */ + +/* 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. */ + +#include "../bashansi.h" +#include "../config.h" +#include <stdio.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include "../filecntl.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#define DOCFILE "builtins.texi" + +static char *xmalloc (), *xrealloc (); + +#if !defined (__STDC__) && !defined (strcpy) +extern char *strcpy (); +#endif /* !__STDC__ && !strcpy */ + +#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) + +/* Flag values that builtins can have. */ +#define BUILTIN_FLAG_SPECIAL 0x01 + +/* If this stream descriptor is non-zero, then write + texinfo documentation to it. */ +FILE *documentation_file = (FILE *)NULL; + +/* Non-zero means to only produce documentation. */ +int only_documentation = 0; + +/* Non-zero means to not do any productions. */ +int inhibit_production = 0; + +#if !defined (OLDCODE) +int no_long_document = 0; +#endif /* !OLDCODE */ + +/* The name of a directory to precede the filename when reporting + errors. */ +char *error_directory = (char *)NULL; + +/* The name of the structure file. */ +char *struct_filename = (char *)NULL; + +/* The name of the external declaration file. */ +char *extern_filename = (char *)NULL; + +/* Here is a structure for manipulating arrays of data. */ +typedef struct { + int size; /* Number of slots allocated to array. */ + int sindex; /* Current location in array. */ + int width; /* Size of each element. */ + int growth_rate; /* How fast to grow. */ + char **array; /* The array itself. */ +} ARRAY; + +/* Here is a structure defining a single BUILTIN. */ +typedef struct { + char *name; /* The name of this builtin. */ + char *function; /* The name of the function to call. */ + char *shortdoc; /* The short documentation for this builtin. */ + char *docname; /* Possible name for documentation string. */ + ARRAY *longdoc; /* The long documentation for this builtin. */ + ARRAY *dependencies; /* Null terminated array of #define names. */ + int flags; /* Flags for this builtin. */ +} BUILTIN_DESC; + +/* Here is a structure which defines a DEF file. */ +typedef struct { + char *filename; /* The name of the input def file. */ + ARRAY *lines; /* The contents of the file. */ + int line_number; /* The current line number. */ + char *production; /* The name of the production file. */ + FILE *output; /* Open file stream for PRODUCTION. */ + ARRAY *builtins; /* Null terminated array of BUILTIN_DESC *. */ +} DEF_FILE; + +/* The array of all builtins encountered during execution of this code. */ +ARRAY *saved_builtins = (ARRAY *)NULL; + +/* The Posix.2 so-called `special' builtins. */ +char *special_builtins[] = +{ + ":", ".", "source", "break", "continue", "eval", "exec", "exit", + "export", "readonly", "return", "set", "shift", "trap", "unset", + (char *)NULL +}; +static int is_special_builtin (); + + +/* 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. */ +main (argc, argv) + int argc; + char **argv; +{ + int arg_index = 1; + FILE *structfile, *externfile; + char *documentation_filename, *temp_struct_filename; + + structfile = externfile = (FILE *)NULL; + documentation_filename = DOCFILE; + temp_struct_filename = (char *)NULL; + + while (arg_index < argc && argv[arg_index][0] == '-') + { + char *arg = argv[arg_index++]; + + if (strcmp (arg, "-externfile") == 0) + extern_filename = argv[arg_index++]; + else if (strcmp (arg, "-structfile") == 0) + struct_filename = argv[arg_index++]; + else if (strcmp (arg, "-noproduction") == 0) + inhibit_production = 1; + else if (strcmp (arg, "-document") == 0) + documentation_file = fopen (documentation_filename, "w"); + else if (strcmp (arg, "-D") == 0) + { + int len; + + if (error_directory) + free (error_directory); + + error_directory = xmalloc (2 + strlen (argv[arg_index])); + strcpy (error_directory, argv[arg_index]); + len = strlen (error_directory); + + if (len && error_directory[len - 1] != '/') + strcat (error_directory, "/"); + + arg_index++; + } + else if (strcmp (arg, "-documentonly") == 0) + { + only_documentation = 1; + documentation_file = fopen (documentation_filename, "w"); + } +#if !defined (OLDCODE) + else if (strcmp (arg, "-nodocument") == 0) + no_long_document = 1; +#endif /* !OLDCODE */ + else + { + fprintf (stderr, "%s: Unknown flag %s.\n", argv[0], arg); + exit (2); + } + } + + /* If there are no files to process, just quit now. */ + if (arg_index == argc) + exit (0); + + if (!only_documentation) + { + /* Open the files. */ + if (struct_filename) + { + temp_struct_filename = xmalloc (15); + sprintf (temp_struct_filename, "mk-%d", (int) getpid ()); + structfile = fopen (temp_struct_filename, "w"); + + if (!structfile) + file_error (temp_struct_filename); + } + + if (extern_filename) + { + externfile = fopen (extern_filename, "w"); + + if (!externfile) + file_error (extern_filename); + } + + /* Write out the headers. */ + write_file_headers (structfile, externfile); + } + + if (documentation_file) + { + fprintf (documentation_file, "@c Table of builtins created with %s.\n", + argv[0]); + fprintf (documentation_file, "@ftable @asis\n"); + } + + /* Process the .def files. */ + while (arg_index < argc) + { + register char *arg; + + arg = argv[arg_index++]; + + extract_info (arg, structfile, externfile); + } + + /* Close the files. */ + if (!only_documentation) + { + /* Write the footers. */ + write_file_footers (structfile, externfile); + + if (structfile) + { + write_longdocs (structfile, saved_builtins); + fclose (structfile); + link (temp_struct_filename, struct_filename); + unlink (temp_struct_filename); + } + + if (externfile) + fclose (externfile); + } + + if (documentation_file) + { + fprintf (documentation_file, "@end ftable\n"); + fclose (documentation_file); + } + + exit (0); +} + +/* **************************************************************** */ +/* */ +/* Array Functions and Manipulators */ +/* */ +/* **************************************************************** */ + +/* Make a new array, and return a pointer to it. The array will + contain elements of size WIDTH, and is initialized to no elements. */ +ARRAY * +array_create (width) + int width; +{ + ARRAY *array; + + array = (ARRAY *)xmalloc (sizeof (ARRAY)); + array->size = 0; + array->sindex = 0; + array->width = width; + + /* Default to increasing size in units of 20. */ + array->growth_rate = 20; + + array->array = (char **)NULL; + + return (array); +} + +/* Copy the array of strings in ARRAY. */ +ARRAY * +copy_string_array (array) + ARRAY *array; +{ + register int i; + ARRAY *copy; + + if (!array) + return (ARRAY *)NULL; + + copy = array_create (sizeof (char *)); + + copy->size = array->size; + copy->sindex = array->sindex; + copy->width = array->width; + + copy->array = (char **)xmalloc ((1 + array->sindex) * sizeof (char *)); + + for (i = 0; i < array->sindex; i++) + copy->array[i] = savestring (array->array[i]); + + copy->array[i] = (char *)NULL; + + return (copy); +} + +/* Add ELEMENT to ARRAY, growing the array if neccessary. */ +array_add (element, array) + char *element; + ARRAY *array; +{ + if (array->sindex + 2 > array->size) + array->array = (char **)xrealloc + (array->array, (array->size += array->growth_rate) * array->width); + +#if defined (HAVE_BCOPY) + bcopy (&element, &(array->array[array->sindex]), array->width); + array->sindex++; + bzero (&(array->array[array->sindex]), array->width); +#else + array->array[array->sindex++] = element; + array->array[array->sindex] = (char *)NULL; +#endif /* !HAVE_BCOPY */ +} + +/* Free an allocated array and data pointer. */ +array_free (array) + ARRAY *array; +{ + if (array->array) + free (array->array); + + free (array); +} + +/* **************************************************************** */ +/* */ +/* Processing a DEF File */ +/* */ +/* **************************************************************** */ + +/* The definition of a function. */ +typedef int Function (); + +/* Structure handles processor directives. */ +typedef struct { + char *directive; + Function *function; +} HANDLER_ENTRY; + +extern int + builtin_handler (), function_handler (), short_doc_handler (), + comment_handler (), depends_on_handler (), produces_handler (), + end_handler (), docname_handler (); + +HANDLER_ENTRY handlers[] = { + { "BUILTIN", builtin_handler }, + { "DOCNAME", docname_handler }, + { "FUNCTION", function_handler }, + { "SHORT_DOC", short_doc_handler }, + { "$", comment_handler }, + { "COMMENT", comment_handler }, + { "DEPENDS_ON", depends_on_handler }, + { "PRODUCES", produces_handler }, + { "END", end_handler }, + { (char *)NULL, (Function *)NULL } +}; + +/* Return the entry in the table of handlers for NAME. */ +HANDLER_ENTRY * +find_directive (directive) + char *directive; +{ + register int i; + + for (i = 0; handlers[i].directive; i++) + if (strcmp (handlers[i].directive, directive) == 0) + return (&handlers[i]); + + return ((HANDLER_ENTRY *)NULL); +} + +/* Non-zero indicates that a $BUILTIN has been seen, but not + the corresponding $END. */ +static int building_builtin = 0; + +/* Non-zero means to output cpp line and file information before + printing the current line to the production file. */ +int output_cpp_line_info = 0; + +/* The main function of this program. Read FILENAME and act on what is + found. Lines not starting with a dollar sign are copied to the + $PRODUCES target, if one is present. Lines starting with a dollar sign + are directives to this program, specifying the name of the builtin, the + function to call, the short documentation and the long documentation + strings. FILENAME can contain multiple $BUILTINs, but only one $PRODUCES + target. After the file has been processed, write out the names of + builtins found in each $BUILTIN. Plain text found before the $PRODUCES + is ignored, as is "$$ comment text". */ +extract_info (filename, structfile, externfile) + char *filename; + FILE *structfile, *externfile; +{ + register int i; + DEF_FILE *defs; + struct stat finfo; + char *buffer, *line; + int fd; + + if (stat (filename, &finfo) == -1) + file_error (filename); + + fd = open (filename, O_RDONLY, 0666); + + if (fd == -1) + file_error (filename); + + buffer = xmalloc (1 + (int)finfo.st_size); + + if (read (fd, buffer, finfo.st_size) != finfo.st_size) + file_error (filename); + + close (fd); + + /* Create and fill in the initial structure describing this file. */ + defs = (DEF_FILE *)xmalloc (sizeof (DEF_FILE)); + defs->filename = filename; + defs->lines = array_create (sizeof (char *)); + defs->line_number = 0; + defs->production = (char *)NULL; + defs->output = (FILE *)NULL; + defs->builtins = (ARRAY *)NULL; + + /* Build the array of lines. */ + i = 0; + while (i < finfo.st_size) + { + array_add (&buffer[i], defs->lines); + + while (buffer[i] != '\n' && i < finfo.st_size) + i++; + buffer[i++] = '\0'; + } + + /* Begin processing the input file. We don't write any output + until we have a file to write output to. */ + output_cpp_line_info = 1; + + /* Process each line in the array. */ + for (i = 0; line = defs->lines->array[i]; i++) + { + defs->line_number = i; + + if (*line == '$') + { + register int j; + char *directive; + HANDLER_ENTRY *handler; + + /* Isolate the directive. */ + for (j = 0; line[j] && !whitespace (line[j]); j++); + + directive = xmalloc (j); + strncpy (directive, line + 1, j - 1); + directive[j -1] = '\0'; + + /* Get the function handler and call it. */ + handler = find_directive (directive); + + if (!handler) + { + line_error (defs, "Unknown directive `%s'", directive); + free (directive); + continue; + } + else + { + /* Advance to the first non-whitespace character. */ + while (whitespace (line[j])) + j++; + + /* Call the directive handler with the FILE, and ARGS. */ + (*(handler->function)) (directive, defs, line + j); + } + free (directive); + } + else + { + if (building_builtin) + add_documentation (defs, line); + else if (defs->output) + { + if (output_cpp_line_info) + { + /* If we're handed an absolute pathname, don't prepend + the directory name. */ + if (defs->filename[0] == '/') + fprintf (defs->output, "#line %d \"%s\"\n", + defs->line_number + 1, defs->filename); + else + fprintf (defs->output, "#line %d \"%s%s\"\n", + defs->line_number + 1, + error_directory ? error_directory : "./", + defs->filename); + output_cpp_line_info = 0; + } + + fprintf (defs->output, "%s\n", line); + } + } + } + + /* Close the production file. */ + if (defs->output) + fclose (defs->output); + + /* The file has been processed. Write the accumulated builtins to + the builtins.c file, and write the extern definitions to the + builtext.h file. */ + write_builtins (defs, structfile, externfile); + + free (buffer); + free_defs (defs); +} + +#define free_safely(x) if (x) free (x) + +static void +free_builtin (builtin) + BUILTIN_DESC *builtin; +{ + register int i; + + free_safely (builtin->name); + free_safely (builtin->function); + free_safely (builtin->shortdoc); + free_safely (builtin->docname); + + if (builtin->longdoc) + array_free (builtin->longdoc); + + if (builtin->dependencies) + { + for (i = 0; builtin->dependencies->array[i]; i++) + free (builtin->dependencies->array[i]); + array_free (builtin->dependencies); + } +} + +/* Free all of the memory allocated to a DEF_FILE. */ +free_defs (defs) + DEF_FILE *defs; +{ + register int i; + register BUILTIN_DESC *builtin; + + if (defs->production) + free (defs->production); + + if (defs->lines) + array_free (defs->lines); + + if (defs->builtins) + { + for (i = 0; builtin = (BUILTIN_DESC *)defs->builtins->array[i]; i++) + { + free_builtin (builtin); + free (builtin); + } + array_free (defs->builtins); + } + free (defs); +} + +/* **************************************************************** */ +/* */ +/* The Handler Functions Themselves */ +/* */ +/* **************************************************************** */ + +/* Strip surrounding whitespace from STRING, and + return a pointer to the start of it. */ +char * +strip_whitespace (string) + char *string; +{ + while (whitespace (*string)) + string++; + + remove_trailing_whitespace (string); + return (string); +} + +/* Remove only the trailing whitespace from STRING. */ +remove_trailing_whitespace (string) + char *string; +{ + register int i; + + i = strlen (string) - 1; + + while (i > 0 && whitespace (string[i])) + i--; + + string[++i] = '\0'; +} + +/* Ensure that there is a argument in STRING and return it. + FOR_WHOM is the name of the directive which needs the argument. + DEFS is the DEF_FILE in which the directive is found. + If there is no argument, produce an error. */ +char * +get_arg (for_whom, defs, string) + char *for_whom, *string; + DEF_FILE *defs; +{ + char *new; + + new = strip_whitespace (string); + + if (!*new) + line_error (defs, "%s requires an argument", for_whom); + + return (savestring (new)); +} + +/* Error if not building a builtin. */ +must_be_building (directive, defs) + char *directive; + DEF_FILE *defs; +{ + if (!building_builtin) + line_error (defs, "%s must be inside of a $BUILTIN block", directive); +} + +/* Return the current builtin. */ +BUILTIN_DESC * +current_builtin (directive, defs) + char *directive; + DEF_FILE *defs; +{ + must_be_building (directive, defs); + return ((BUILTIN_DESC *)defs->builtins->array[defs->builtins->sindex - 1]); +} + +/* Add LINE to the long documentation for the current builtin. + Ignore blank lines until the first non-blank line has been seen. */ +add_documentation (defs, line) + DEF_FILE *defs; + char *line; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin ("(implied LONGDOC)", defs); + + remove_trailing_whitespace (line); + + if (!*line && !builtin->longdoc) + return; + + if (!builtin->longdoc) + builtin->longdoc = array_create (sizeof (char *)); + + array_add (line, builtin->longdoc); +} + +/* How to handle the $BUILTIN directive. */ +int +builtin_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + /* 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)); + + 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; + } + return (0); +} + +/* How to handle the $FUNCTION directive. */ +int +function_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin->function) + line_error (defs, "%s already has a function (%s)", + builtin->name, builtin->function); + else + builtin->function = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $DOCNAME directive. */ +int +docname_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin->docname) + line_error (defs, "%s already had a docname (%s)", + builtin->name, builtin->docname); + else + builtin->docname = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $SHORT_DOC directive. */ +short_doc_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + register BUILTIN_DESC *builtin; + + builtin = current_builtin (self, defs); + + if (builtin->shortdoc) + line_error (defs, "%s already has short documentation (%s)", + builtin->name, builtin->shortdoc); + else + builtin->shortdoc = get_arg (self, defs, arg); + + return (0); +} + +/* How to handle the $COMMENT directive. */ +comment_handler (self, defs) + char *self; + DEF_FILE *defs; +{ +} + +/* How to handle the $DEPENDS_ON directive. */ +depends_on_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + register BUILTIN_DESC *builtin; + char *dependent; + + builtin = current_builtin (self, defs); + dependent = get_arg (self, defs, arg); + + if (!builtin->dependencies) + builtin->dependencies = array_create (sizeof (char *)); + + array_add (dependent, builtin->dependencies); + + return (0); +} + +/* How to handle the $PRODUCES directive. */ +produces_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + /* If just hacking documentation, don't change any of the production + files. */ + if (only_documentation) + return (0); + + output_cpp_line_info++; + + if (defs->production) + line_error (defs, "%s already has a %s definition", defs->filename, self); + else + { + defs->production = get_arg (self, defs, arg); + + if (inhibit_production) + return (0); + + defs->output = fopen (defs->production, "w"); + + if (!defs->output) + file_error (defs->production); + + fprintf (defs->output, "/* %s, created from %s. */\n", + defs->production, defs->filename); + } + return (0); +} + +/* How to handle the $END directive. */ +end_handler (self, defs, arg) + char *self, *arg; + DEF_FILE *defs; +{ + must_be_building (self, defs); + building_builtin = 0; +} + +/* **************************************************************** */ +/* */ +/* Error Handling Functions */ +/* */ +/* **************************************************************** */ + +/* Produce an error for DEFS with FORMAT and ARGS. */ +line_error (defs, format, arg1, arg2) + DEF_FILE *defs; + char *format, *arg1, *arg2; +{ + if (defs->filename[0] != '/') + fprintf (stderr, "%s", error_directory ? error_directory : "./"); + fprintf (stderr, "%s:%d:", defs->filename, defs->line_number + 1); + fprintf (stderr, format, arg1, arg2); + fprintf (stderr, "\n"); + fflush (stderr); +} + +/* Print error message for FILENAME. */ +file_error (filename) + char *filename; +{ + perror (filename); + exit (2); +} + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "mkbuiltins: Out of virtual memory!\n"); + abort (); +} + +/* **************************************************************** */ +/* */ +/* Creating the Struct and Extern Files */ +/* */ +/* **************************************************************** */ + +/* Return a pointer to a newly allocated builtin which is + an exact copy of BUILTIN. */ +BUILTIN_DESC * +copy_builtin (builtin) + BUILTIN_DESC *builtin; +{ + BUILTIN_DESC *new; + + new = (BUILTIN_DESC *)xmalloc (sizeof (BUILTIN_DESC)); + + new->name = savestring (builtin->name); + new->shortdoc = savestring (builtin->shortdoc); + new->longdoc = copy_string_array (builtin->longdoc); + new->dependencies = copy_string_array (builtin->dependencies); + + new->function = + builtin->function ? savestring (builtin->function) : (char *)NULL; + new->docname = + builtin->docname ? savestring (builtin->docname) : (char *)NULL; + + return (new); +} + +/* How to save away a builtin. */ +save_builtin (builtin) + BUILTIN_DESC *builtin; +{ + BUILTIN_DESC *newbuiltin; + + newbuiltin = copy_builtin (builtin); + + /* If this is the first builtin to be saved, create the array + to hold it. */ + if (!saved_builtins) + saved_builtins = array_create (sizeof (BUILTIN_DESC *)); + + array_add ((char *)newbuiltin, saved_builtins); +} + +/* Flags that mean something to write_documentation (). */ +#define STRING_ARRAY 1 +#define TEXINFO 2 + +char *structfile_header[] = { + "/* builtins.c -- the built in shell commands. */", + "", + "/* This file is manufactured by ./mkbuiltins, and should not be", + " edited by hand. See the source to mkbuiltins for details. */", + "", + "/* Copyright (C) 1987, 1991, 1992 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. */", + "", + "/* The list of shell builtins. Each element is name, function, enabled-p,", + " long-doc, short-doc. The long-doc field contains a pointer to an array", + " of help lines. The function takes a WORD_LIST *; the first word in the", + " list is the first arg to the command. The list has already had word", + " expansion performed.", + "", + " Functions which need to look at only the simple commands (e.g.", + " the enable_builtin ()), should ignore entries where", + " (array[i].function == (Function *)NULL). Such entries are for", + " the list of shell reserved control structures, like `if' and `while'.", + " The end of the list is denoted with a NULL name field. */", + "", + "#include \"../builtins.h\"", + (char *)NULL + }; + +char *structfile_footer[] = { + " { (char *)0x0, (Function *)0x0, 0, (char **)0x0, (char *)0x0 }", + "};", + "", + "int num_shell_builtins =", + "\tsizeof (shell_builtins) / sizeof (struct builtin) - 1;", + (char *)NULL +}; + +/* Write out any neccessary opening information for + STRUCTFILE and EXTERNFILE. */ +write_file_headers (structfile, externfile) + FILE *structfile, *externfile; +{ + register int i; + + if (structfile) + { + for (i = 0; structfile_header[i]; i++) + fprintf (structfile, "%s\n", structfile_header[i]); + + fprintf (structfile, "#include \"%s\"\n", + extern_filename ? extern_filename : "builtext.h"); + fprintf (structfile, "\nstruct builtin shell_builtins[] = {\n"); + } + + if (externfile) + fprintf (externfile, + "/* %s - The list of builtins found in libbuiltins.a. */\n", + extern_filename ? extern_filename : "builtext.h"); +} + +/* Write out any necessary closing information for + STRUCTFILE and EXTERNFILE. */ +write_file_footers (structfile, externfile) + FILE *structfile, *externfile; +{ + register int i; + + /* Write out the footers. */ + if (structfile) + { + for (i = 0; structfile_footer[i]; i++) + fprintf (structfile, "%s\n", structfile_footer[i]); + } +} + +/* Write out the information accumulated in DEFS to + STRUCTFILE and EXTERNFILE. */ +write_builtins (defs, structfile, externfile) + DEF_FILE *defs; + FILE *structfile, *externfile; +{ + register int i; + + /* Write out the information. */ + if (defs->builtins) + { + register BUILTIN_DESC *builtin; + + for (i = 0; i < defs->builtins->sindex; i++) + { + builtin = (BUILTIN_DESC *)defs->builtins->array[i]; + + /* Write out any #ifdefs that may be there. */ + if (!only_documentation) + { + if (builtin->dependencies) + { + if (builtin->function) + write_ifdefs (externfile, builtin->dependencies->array); + write_ifdefs (structfile, builtin->dependencies->array); + } + + /* Write the extern definition. */ + if (externfile) + { + if (builtin->function) + fprintf (externfile, "extern int %s ();\n", + builtin->function); + + fprintf (externfile, "extern char *%s_doc[];\n", + builtin->docname ?builtin->docname : builtin->name); + } + + /* Write the structure definition. */ + if (structfile) + { + fprintf (structfile, " { \"%s\", ", builtin->name); + + if (builtin->function) + fprintf (structfile, "%s, ", builtin->function); + 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, + builtin->docname ? builtin->docname : builtin->name); + +#undef SPECIAL_FLAG_STRING +#undef NORMAL_FLAG_STRING + + fprintf + (structfile, " \"%s\" },\n", + builtin->shortdoc ? builtin->shortdoc : builtin->name); + + /* Save away this builtin for later writing of the + long documentation strings. */ + save_builtin (builtin); + } + + /* Write out the matching #endif, if neccessary. */ + if (builtin->dependencies) + { + if (externfile) + write_endifs (externfile, builtin->dependencies->array); + + if (structfile) + write_endifs (structfile, builtin->dependencies->array); + } + } + + if (documentation_file) + { + fprintf (documentation_file, "@item %s\n", builtin->name); + write_documentation + (documentation_file, builtin->longdoc->array, 0, TEXINFO); + } + } + } +} + +/* Write out the long documentation strings in BUILTINS to STREAM. */ +write_longdocs (stream, builtins) + FILE *stream; + ARRAY *builtins; +{ + register int i; + register BUILTIN_DESC *builtin; + + for (i = 0; i < builtins->sindex; i++) + { + builtin = (BUILTIN_DESC *)builtins->array[i]; + + if (builtin->dependencies) + write_ifdefs (stream, builtin->dependencies->array); + + /* Write the long documentation strings. */ + fprintf (stream, "char *%s_doc[] =", + builtin->docname ? builtin->docname : builtin->name); + write_documentation (stream, builtin->longdoc->array, 0, STRING_ARRAY); + + if (builtin->dependencies) + write_endifs (stream, builtin->dependencies->array); + + } +} + +/* Write an #ifdef string saying what needs to be defined (or not defined) + in order to allow compilation of the code that will follow. + STREAM is the stream to write the information to, + DEFINES is a null terminated array of define names. + If a define is preceded by an `!', then the sense of the test is + reversed. */ +write_ifdefs (stream, defines) + FILE *stream; + char **defines; +{ + register int i; + + if (!stream) + return; + + fprintf (stream, "#if "); + + for (i = 0; defines[i]; i++) + { + char *def = defines[i]; + + if (*def == '!') + fprintf (stream, "!defined (%s)", def + 1); + else + fprintf (stream, "defined (%s)", def); + + if (defines[i + 1]) + fprintf (stream, " && "); + } + fprintf (stream, "\n"); +} + +/* Write an #endif string saying what defines controlled the compilation + of the immediately preceding code. + STREAM is the stream to write the information to. + DEFINES is a null terminated array of define names. */ +write_endifs (stream, defines) + FILE *stream; + char **defines; +{ + register int i; + + if (!stream) + return; + + fprintf (stream, "#endif /* "); + + for (i = 0; defines[i]; i++) + { + fprintf (stream, "%s", defines[i]); + + if (defines[i + 1]) + fprintf (stream, " && "); + } + + fprintf (stream, " */\n"); +} + +/* Write DOCUMENTAION to STREAM, perhaps surrounding it with double-quotes + and quoting special characters in the string. */ +write_documentation (stream, documentation, indentation, flags) + FILE *stream; + char **documentation; + int indentation, flags; +{ + register int i, j; + register char *line; + int string_array = (flags & STRING_ARRAY); /* Mutually exclusive. */ + int texinfo = (flags & TEXINFO); + + if (!stream) + return; + + if (string_array) + fprintf (stream, " {\n"); + +#if !defined (OLDCODE) + /* XXX -- clean me up; for experiment only */ + if (no_long_document) + goto end_of_document; +#endif /* !OLDCODE */ + + for (i = 0; line = documentation[i]; i++) + { + /* Allow #ifdef's to be written out verbatim. */ + if (*line == '#') + { + if (string_array) + fprintf (stream, "%s\n", line); + continue; + } + + if (string_array) + fprintf (stream, " \""); + + if (indentation) + for (j = 0; j < indentation; j++) + fprintf (stream, " "); + + if (string_array) + { + for (j = 0; line[j]; j++) + { + switch (line[j]) + { + case '\\': + case '"': + fprintf (stream, "\\%c", line[j]); + break; + + default: + fprintf (stream, "%c", line[j]); + } + } + + fprintf (stream, "\",\n"); + } + else if (texinfo) + { + for (j = 0; line[j]; j++) + { + switch (line[j]) + { + case '@': + case '{': + case '}': + fprintf (stream, "@%c", line[j]); + break; + + default: + fprintf (stream, "%c", line[j]); + } + } + fprintf (stream, "\n"); + } + else + fprintf (stream, "%s\n", line); + } + +#if !defined (OLDCODE) +end_of_document: +#endif /* !OLDCODE */ + + if (string_array) + fprintf (stream, " (char *)NULL\n};\n"); +} + +static int +is_special_builtin (name) + char *name; +{ + register int i; + + for (i = 0; special_builtins[i]; i++) + if (strcmp (name, special_builtins[i]) == 0) + return 1; + return 0; +} diff --git a/builtins/psize.c b/builtins/psize.c new file mode 100644 index 0000000..03a4f6e --- /dev/null +++ b/builtins/psize.c @@ -0,0 +1,63 @@ +/* psize.c - Find pipe size. */ + +/* Copyright (C) 1987, 1991 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. */ + +/* 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 <stdio.h> +#include <sys/types.h> +#include <signal.h> +#include <errno.h> + +#include "../command.h" +#include "../general.h" +extern int errno; + +int nw; + +sighandler +sigpipe (sig) + int sig; +{ + fprintf (stderr, "%d\n", nw); + exit (0); +} + +main (argc, argv) + int argc; + char **argv; +{ + char buf[128]; + register int i; + + for (i = 0; i < 128; i++) + buf[i] = ' '; + + signal (SIGPIPE, sigpipe); + + nw = 0; + for (;;) + { + int n; + n = write (1, buf, 128); + nw += n; + } +} diff --git a/builtins/psize.sh b/builtins/psize.sh new file mode 100755 index 0000000..961becd --- /dev/null +++ b/builtins/psize.sh @@ -0,0 +1,24 @@ +#! /bin/sh +# +# psize.sh -- determine this system's pipe size, and write a define to +# pipesize.h so ulimit.c can use it. + +echo "/*" +echo " * pipesize.h" +echo " *" +echo " * This file is automatically generated by psize.sh" +echo " * Do not edit!" +echo " */" +echo "" + +./psize.aux 2>/tmp/pipesize | sleep 3 + +if [ -s /tmp/pipesize ]; then + echo "#define PIPESIZE `cat /tmp/pipesize`" +else + echo "#define PIPESIZE 512" +fi + +rm -f /tmp/pipesize + +exit 0 diff --git a/builtins/read.def b/builtins/read.def new file mode 100644 index 0000000..7b6dfc9 --- /dev/null +++ b/builtins/read.def @@ -0,0 +1,276 @@ +This file is read.def, from which is created read.c. +It implements the builtin "read" 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 read.c + +$BUILTIN read +$FUNCTION read_builtin +$SHORT_DOC read [-r] [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 +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. +$END + +#include <stdio.h> +#include "../shell.h" +#include "common.h" + +#define issep(c) (strchr (ifs_chars, (c)) != (char *)0) + +static int stream_close (); + +extern int interrupt_immediately; + +/* 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. */ +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; + SHELL_VAR *var; + + i = 0; /* Index into the string that we are reading. */ + raw_mode = 0; /* Not reading raw input be default. */ + + while (list) + { + if (ISOPTION (list->word->word, 'r')) + { + raw_mode = 1; + list = list->next; + } + else if (ISOPTION (list->word->word, '-')) + { + list = list->next; + break; + } + else if (*list->word->word == '-') + { + bad_option (list->word->word); + builtin_error ("usage: read [-r] [name ...]"); + 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); + } + + var = find_variable ("IFS"); + ifs_chars = var ? value_cell (var) : " \t\n"; + + input_string = xmalloc (size = 128); + + setbuf (input_stream, (char *)NULL); + + begin_unwind_frame ("read_builtin"); + add_unwind_protect (xfree, input_string); + add_unwind_protect (stream_close, input_stream); + interrupt_immediately++; + + 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) + { + if (i + 2 >= size) + input_string = xrealloc (input_string, size += 128); + + /* If the next character is to be accepted verbatim, a backslash + newline pair still disappears from the input. */ + if (pass_next) + { + if (c == '\n') + i--; /* back up over the CTLESC */ + else + input_string[i++] = c; + pass_next = 0; + continue; + } + + if (c == '\\' && !raw_mode) + { + pass_next++; + saw_escape++; + input_string[i++] = CTLESC; + continue; + } + + if (c == '\n') + break; + + if (c == CTLESC || c == CTLNUL) + { + saw_escape++; + input_string[i++] = CTLESC; + } + + input_string[i++] = c; + } + input_string[i] = '\0'; + + interrupt_immediately--; + discard_unwind_frame ("read_builtin"); + + fclose (input_stream); + + if (c == EOF) + { + retval = EXECUTION_FAILURE; + /* input_string[0] = '\0'; */ + } + else + retval = EXECUTION_SUCCESS; + + if (!list) + { + if (saw_escape) + { + t = dequote_string (input_string); + var = bind_variable ("REPLY", t); + free (t); + } + else + var = bind_variable ("REPLY", input_string); + var->attributes &= ~att_invisible; + free (input_string); + } + else + { + /* 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) + { + 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); + } + + /* If there are more variables than words read from the input, + the remaining variables are set to the empty string. */ + if (*input_string) + { + /* This call updates INPUT_STRING. */ + t = get_word_from_string (&input_string, ifs_chars, &e); + if (t) + *e = '\0'; + /* Don't bother to remove the CTLESC unless we added one + somewhere while reading the string. */ + if (t && saw_escape) + { + t1 = dequote_string (t); + var = bind_variable (varname, t1); + free (t1); + } + else + var = bind_variable (varname, t); + } + else + { + t = (char *)0; + var = bind_variable (varname, ""); + } + + stupidly_hack_special_variables (varname); + var->attributes &= ~att_invisible; + + if (t) + free (t); + } + + if (legal_identifier (list->word->word) == 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); + 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; +{ + return (fclose (file)); +} diff --git a/builtins/reserved.def b/builtins/reserved.def new file mode 100644 index 0000000..4074ae0 --- /dev/null +++ b/builtins/reserved.def @@ -0,0 +1,154 @@ +This file is reserved.def, in which the shell reserved words are defined. +It has no direct C file production, but defines builtins for the Bash +builtin help command. + +Copyright (C) 1987, 1989, 1991, 1992 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. + +$BUILTIN for +$SHORT_DOC for NAME [in WORDS ... ;] do COMMANDS; done +The `for' loop executes a sequence of commands for each member in a +list of items. If `in WORDS ...;' is not present, then `in "$@"' is +assumed. For each element in WORDS, NAME is set to that element, and +the COMMANDS are executed. +$END + +$BUILTIN select +$SHORT_DOC select NAME [in WORDS ... ;] do COMMANDS; done +The WORDS are expanded, generating a list of words. The +set of expanded words is printed on the standard error, each +preceded by a number. If `in WORDS' is not present, `in "$@"' +is assumed. The PS3 prompt is then displayed and a line read +from the standard input. If the line consists of the number +corresponding to one of the displayed words, then NAME is set +to that word. If the line is empty, WORDS and the prompt are +redisplayed. If EOF is read, the command completes. Any other +value read causes NAME to be set to null. The line read is saved +in the variable REPLY. COMMANDS are executed after each selection +until a break or return command is executed. +$END + +$BUILTIN case +$SHORT_DOC case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac +Selectively execute COMMANDS based upon WORD matching PATTERN. The +`|' is used to separate multiple patterns. +$END + +$BUILTIN if +$SHORT_DOC if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi +The if COMMANDS are executed. If the exit status is zero, then the then +COMMANDS are executed. Otherwise, each of the elif COMMANDS are executed +in turn, and if the exit status is zero, the corresponding then COMMANDS +are executed and the if command completes. Otherwise, the else COMMANDS +are executed, if present. The exit status is the exit status of the last +command executed, or zero if no condition tested true. +$END + +$BUILTIN while +$SHORT_DOC while COMMANDS; do COMMANDS; done +Expand and execute COMMANDS as long as the final command in the +`while' COMMANDS has an exit status of zero. +$END + +$BUILTIN until +$SHORT_DOC until COMMANDS; do COMMANDS; done +Expand and execute COMMANDS as long as the final command in the +`until' COMMANDS has an exit status which is not zero. +$END + +$BUILTIN function +$SHORT_DOC function NAME { COMMANDS ; } or NAME () { COMMANDS ; } +Create a simple command invoked by NAME which runs COMMANDS. +Arguments on the command line along with NAME are passed to the +function as $0 .. $n. +$END + +$BUILTIN { ... } +$DOCNAME grouping_braces +$SHORT_DOC { COMMANDS } +Run a set of commands in a group. This is one way to redirect an +entire set of commands. +$END + +$BUILTIN % +$DOCNAME fg_percent +$SHORT_DOC %[DIGITS | WORD] [&] +This is similar to the `fg' command. Resume a stopped or background +job. If you specifiy DIGITS, then that job is used. If you specify +WORD, then the job whose name begins with WORD is used. Following the +job specification with a `&' places the job in the background. +$END + +$BUILTIN variables +$DOCNAME variable_help +$SHORT_DOC variables - Some variable names and meanings +BASH_VERSION The version numbers of this Bash. +CDPATH A colon separated list of directories to search + when the argument to `cd' is not found in the current + directory. +#if defined (HISTORY) +HISTFILE The name of the file where your command history is stored. +HISTFILESIZE The maximum number of lines this file can contain. +HISTSIZE The maximum number of history lines that a running + shell can access. +#endif /* HISTORY */ +HOME The complete pathname to your login directory. +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. +MAILCHECK How often, in seconds, Bash checks for new mail. +MAILPATH A colon-separated list of filenames which Bash checks + for new mail. +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. +TERM The name of the current terminal type. +auto_resume Non-null means a command word appearing on a line by + itself is first looked for in the list of currently + stopped jobs. If found there, that job is foregrounded. + A value of `exact' means that the command word must + exactly match a command in the list of stopped jobs. A + value of `substring' means that the command word must + match a substring of the job. Any other value means that + the command must be a prefix of a stopped job. +#if defined (HISTORY) +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 + substitution character, usually `!'. The second is + 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. +#endif /* HISTORY */ +$END diff --git a/builtins/return.def b/builtins/return.def new file mode 100644 index 0000000..8340a44 --- /dev/null +++ b/builtins/return.def @@ -0,0 +1,57 @@ +This file is return.def, from which is created return.c. +It implements the builtin "return" 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 return.c + +$BUILTIN return + +$FUNCTION return_builtin +$SHORT_DOC return [n] +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 "../shell.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 + exit status is used. */ +int +return_builtin (list) + WORD_LIST *list; +{ + return_catch_value = get_numeric_arg (list); + + if (!list) + return_catch_value = last_command_exit_value; + + if (return_catch_flag) + longjmp (return_catch, 1); + else + { + builtin_error ("Can only `return' from a function"); + return (EXECUTION_FAILURE); + } +} diff --git a/builtins/set.def b/builtins/set.def new file mode 100644 index 0000000..a97168c --- /dev/null +++ b/builtins/set.def @@ -0,0 +1,528 @@ +This file is set.def, from which is created set.c. +It implements the "set" and "unset" builtins 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 set.c + +#include <stdio.h> +#include "../shell.h" +#include "../flags.h" + +#include "bashgetopt.h" + +extern int interactive; +extern int noclobber, no_brace_expansion, posixly_correct; +#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 ...] + -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. + -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 + 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 +#if defined (READLINE) + emacs use an emacs-style line editing interface +#endif /* READLINE */ + errexit same as -e +#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 + monitor same as -m + noclobber disallow redirection to existing files + 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 + verbose same as -v +#if defined (READLINE) + vi use a vi-style line editing interface +#endif /* READLINE */ + xtrace same as -x + -p Turned on whenever the real and effective user ids do not match. + Disables processing of the $ENV file and importing of shell + functions. Turning this option off causes the effective uid and + gid to be set to the real uid and gid. + -t Exit after reading and executing one command. + -u Treat unset variables as an error when substituting. + -v Print shell input lines as they are read. + -x Print commands and their arguments as they are executed. + -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 (BANG_HISTORY) + -H Enable ! style history substitution. This flag is on + by default. +#endif /* BANG_HISTORY */ + -C If set, disallow existing regular files to be overwritten + by redirection of output. + -P If set, do not follow symbolic links when executing commands + such as cd which change the current directory. + +Using + rather than - causes these flags to be turned off. The +flags can also be used upon invocation of the shell. The current +set of flags may be found in $-. The remaining n ARGs are positional +parameters and are assigned, in order, to $1, $2, .. $n. If no +ARGs are given, all shell variables are printed. +$END + +/* An a-list used to match long options for set -o to the corresponding + option letter. */ +struct { + char *name; + int letter; +} o_options[] = { + { "allexport", 'a' }, + { "errexit", 'e' }, +#if defined (BANG_HISTORY) + { "histexpand", 'H' }, +#endif /* BANG_HISTORY */ + { "monitor", 'm' }, + { "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}, +}; + +#define MINUS_O_FORMAT "%-15s\t%s\n" + +void +list_minus_o_opts () +{ + register int i; + char *on = "on", *off = "off"; + + printf (MINUS_O_FORMAT, "braceexpand", (no_brace_expansion == 0) ? on : off); + printf (MINUS_O_FORMAT, "noclobber", (noclobber == 1) ? on : off); + + if (find_variable ("ignoreeof") || find_variable ("IGNOREEOF")) + printf (MINUS_O_FORMAT, "ignoreeof", on); + else + printf (MINUS_O_FORMAT, "ignoreeof", off); + + printf (MINUS_O_FORMAT, "interactive-comments", + interactive_comments ? on : off); + + printf (MINUS_O_FORMAT, "posix", posixly_correct ? on : off); + +#if defined (READLINE) + if (no_line_editing) + { + printf (MINUS_O_FORMAT, "emacs", off); + printf (MINUS_O_FORMAT, "vi", off); + } + 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); + } +#endif /* READLINE */ + + for (i = 0; o_options[i].name; i++) + { + 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); + } +} + +set_minus_o_option (on_or_off, option_name) + int on_or_off; + char *option_name; +{ + int option_char = -1; + + if (STREQ (option_name, "braceexpand")) + { + if (on_or_off == FLAG_ON) + no_brace_expansion = 0; + else + no_brace_expansion = 1; + } + else if (STREQ (option_name, "noclobber")) + { + if (on_or_off == FLAG_ON) + bind_variable ("noclobber", ""); + else + unbind_variable ("noclobber"); + stupidly_hack_special_variables ("noclobber"); + } + else if (STREQ (option_name, "ignoreeof")) + { + unbind_variable ("ignoreeof"); + unbind_variable ("IGNOREEOF"); + if (on_or_off == FLAG_ON) + bind_variable ("IGNOREEOF", "10"); + stupidly_hack_special_variables ("IGNOREEOF"); + } + +#if defined (READLINE) + else if ((STREQ (option_name, "emacs")) || (STREQ (option_name, "vi"))) + { + if (on_or_off == FLAG_ON) + { + rl_variable_bind ("editing-mode", option_name); + + 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); + } + } +#endif /* READLINE */ + else if (STREQ (option_name, "interactive-comments")) + interactive_comments = (on_or_off == FLAG_ON); + else if (STREQ (option_name, "posix")) + { + 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"); + } + } + else + { + 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) + { + bad_option (option_name); + return (EXECUTION_FAILURE); + } + } + return (EXECUTION_SUCCESS); +} + +/* 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. */ +set_builtin (list) + WORD_LIST *list; +{ + int on_or_off, flag_name, force_assignment = 0; + + if (!list) + { + 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); + } + + 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)) + { + char c; + + if (arg[0] != '-' && arg[0] != '+') + break; + + /* `-' or `--' signifies end of flag arguments. */ + if (arg[0] == '-' && + (!arg[1] || (arg[1] == '-' && !arg[2]))) + break; + + while (c = *++arg) + { + if (find_flag (c) == FLAG_UNKNOWN && c != 'o') + { + char s[2]; + s[0] = c; s[1] = '\0'; + bad_option (s); + if (c == '?') + printf ("usage: %s\n", USAGE_STRING); + 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) + { + char *string = 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]))) + { + list = list->next; + + /* `set --' unsets the positional parameters. */ + if (string[1] == '-') + force_assignment = 1; + + /* Until told differently, the old shell behaviour of + `set - [arg ...]' being equivalent to `set +xv [arg ...]' + stands. Posix.2 says the behaviour is marked as obsolescent. */ + else + { + change_flag ('x', '+'); + change_flag ('v', '+'); + } + + break; + } + + if ((on_or_off = *string) && + (on_or_off == '-' || on_or_off == '+')) + { + int i = 1; + while (flag_name = string[i++]) + { + if (flag_name == '?') + { + printf ("usage: %s\n", USAGE_STRING); + return (EXECUTION_SUCCESS); + } + else if (flag_name == 'o') /* -+o option-name */ + { + char *option_name; + WORD_LIST *opt; + + opt = list->next; + + if (!opt) + { + list_minus_o_opts (); + continue; + } + + option_name = opt->word->word; + + if (!option_name || !*option_name || (*option_name == '-')) + { + list_minus_o_opts (); + continue; + } + list = list->next; /* Skip over option name. */ + + 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); + return (EXECUTION_FAILURE); + } + } + } + } + else + { + break; + } + list = list->next; + } + + /* Assigning $1 ... $n */ + if (list || force_assignment) + remember_args (list, 1); + return (EXECUTION_SUCCESS); +} + +$BUILTIN unset +$FUNCTION unset_builtin +$SHORT_DOC unset [-f] [-v] [name ...] +For each NAME, remove the corresponding variable or function. Given +the `-v', unset will only act on variables. Given the `-f' flag, +unset will only act on functions. With neither flag, unset first +tries to unset a variable, and if that fails, then tries to unset a +function. Some variables (such as PATH and IFS) cannot be unset; also +see readonly. +$END + +unset_builtin (list) + WORD_LIST *list; +{ + int unset_function = 0, unset_variable = 0, opt; + int any_failed = 0; + char *name; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fv")) != -1) + { + switch (opt) + { + case 'f': + unset_function = 1; + break; + case 'v': + unset_variable = 1; + break; + default: + return (EXECUTION_FAILURE); + } + } + + list = loptend; + + if (unset_function && unset_variable) + { + builtin_error ("cannot simultaneously unset a function and a variable"); + return (EXECUTION_FAILURE); + } + + while (list) + { + name = list->word->word; + + if (!unset_function && + find_name_in_list (name, non_unsettable_vars) > -1) + { + builtin_error ("%s: cannot unset", name); + any_failed++; + } + else + { + SHELL_VAR *var; + int tem; + + 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"); + any_failed++; + list = list->next; + continue; + } + + /* 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); + } + list = list->next; + } + + if (any_failed) + return (EXECUTION_FAILURE); + else + return (EXECUTION_SUCCESS); +} diff --git a/builtins/setattr.def b/builtins/setattr.def new file mode 100644 index 0000000..2340e1a --- /dev/null +++ b/builtins/setattr.def @@ -0,0 +1,253 @@ +This file is setattr.def, from which is created setattr.c. +It implements the builtins "export" and "readonly", 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 setattr.c + +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" + +extern int array_needs_making; +extern char *this_command_name; + +$BUILTIN export +$FUNCTION export_builtin +$SHORT_DOC export [-n] [-f] [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' +is given, a list of all names that are exported in this shell is +printed. An argument of `-n' says to remove the export property +from subsequent NAMEs. An argument of `--' disables further option +processing. +$END + +/* For each variable name in LIST, make that variable appear in the + environment passed to simple commands. If there is no LIST, then + print all such variables. An argument of `-n' says to remove the + exported attribute from variables named in LIST. An argument of + -f indicates that the names present in LIST refer to functions. */ +export_builtin (list) + register WORD_LIST *list; +{ + return (set_or_show_attributes (list, att_exported)); +} + +$BUILTIN readonly +$FUNCTION readonly_builtin +$SHORT_DOC readonly [-n] [-f] [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 +processing. +$END + +/* For each variable name in LIST, make that variable readonly. Given an + empty LIST, print out all existing readonly variables. */ +readonly_builtin (list) + register WORD_LIST *list; +{ + return (set_or_show_attributes (list, att_readonly)); +} + +/* 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) + register WORD_LIST *list; + int attribute; +{ + register SHELL_VAR *var; + int assign, undo = 0, functions_only = 0, any_failed = 0, opt; + + /* Read arguments from the front of the list. */ + reset_internal_getopt (); + while ((opt = internal_getopt (list, "nfp")) != -1) + { + switch (opt) + { + case 'n': + undo = 1; + break; + case 'f': + functions_only = 1; + break; + case 'p': + break; + default: + builtin_error ("usage: %s [-nfp] [varname]", this_command_name); + return (EX_USAGE); + } + } + list = loptend; + + if (list) + { + if (attribute & att_exported) + array_needs_making = 1; + + /* Cannot undo readonly status. */ + if (undo && (attribute & att_readonly)) + attribute &= ~att_readonly; + + while (list) + { + register char *name = list->word->word; + + if (functions_only) + { + var = find_function (name); + if (!var) + { + builtin_error ("%s: not a function", name); + any_failed++; + } + else + { + if (undo) + var->attributes &= ~attribute; + else + var->attributes |= attribute; + } + list = list->next; + if (attribute == att_exported) + array_needs_making++; + continue; + } + + assign = assignment (name); + + if (assign) + name[assign] = '\0'; + if (legal_identifier (name) == 0) + { + builtin_error ("%s: not a legal variable name", name); + any_failed++; + list = list->next; + continue; + } + + if (assign) + { + 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); + 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 */ + list = list->next; + } + } + else + { + SHELL_VAR **variable_list; + register int i; + + if ((attribute & att_function) || functions_only) + { + variable_list = all_shell_functions (); + if (attribute != att_function) + attribute &= ~att_function; /* so declare -xf works, for example */ + } + else + variable_list = all_shell_variables (); + + if (variable_list) + { + for (i = 0; var = variable_list[i]; i++) + { + if ((var->attributes & attribute) && !invisible_p (var)) + { + char flags[6]; + + flags[0] = '\0'; + + if (exported_p (var)) + strcat (flags, "x"); + + if (readonly_p (var)) + strcat (flags, "r"); + + if (function_p (var)) + strcat (flags, "f"); + + if (integer_p (var)) + strcat (flags, "i"); + + if (flags[0]) + { + printf ("declare -%s ", flags); + + 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 (); + + printf ("%s\n", named_function_string + (var->name, function_cell (var), 1)); + } + } + } + } + free (variable_list); + } + } + return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} diff --git a/builtins/shift.def b/builtins/shift.def new file mode 100644 index 0000000..4d8fed0 --- /dev/null +++ b/builtins/shift.def @@ -0,0 +1,95 @@ +This file is shift.def, from which is created shift.c. +It implements the builtin "shift" 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 shift.c + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include "../shell.h" + +$BUILTIN shift +$FUNCTION shift_builtin +$SHORT_DOC shift [n] +The positional parameters from $N+1 ... are renamed to $1 ... If N is +not given, it is assumed to be 1. +$END + +/* Shift the arguments ``left''. Shift DOLLAR_VARS down then take one + off of REST_OF_ARGS and place it into DOLLAR_VARS[9]. If LIST has + anything in it, it is a number which says where to start the + shifting. Return > 0 if `times' > $#, otherwise 0. */ +int +shift_builtin (list) + WORD_LIST *list; +{ + int times, number; + WORD_LIST *args; + + times = get_numeric_arg (list); + + if (!times) + return (EXECUTION_SUCCESS); + + 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) + { + builtin_error ("shift count must be <= $#"); + return (EXECUTION_FAILURE); + } + + while (times-- > 0) + { + register int count; + + if (dollar_vars[1]) + free (dollar_vars[1]); + + for (count = 1; count < 9; count++) + dollar_vars[count] = dollar_vars[count + 1]; + + if (rest_of_args) + { + WORD_LIST *temp = rest_of_args; + + dollar_vars[9] = savestring (temp->word->word); + rest_of_args = rest_of_args->next; + temp->next = (WORD_LIST *)NULL; + dispose_words (temp); + } + else + dollar_vars[9] = (char *)NULL; + } + + return (EXECUTION_SUCCESS); +} diff --git a/builtins/source.def b/builtins/source.def new file mode 100644 index 0000000..895e98b --- /dev/null +++ b/builtins/source.def @@ -0,0 +1,186 @@ +This file is source.def, from which is created source.c. +It implements the builtins "." and "source" 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 source.c + +$BUILTIN source +$FUNCTION source_builtin +$SHORT_DOC source filename +Read and execute commands from FILENAME and return. The pathnames +in $PATH are used to find the directory containing FILENAME. +$END +$BUILTIN . +$DOCNAME dot +$FUNCTION source_builtin +$SHORT_DOC . filename +Read and execute commands from FILENAME and return. The pathnames +in $PATH are used to find the directory containing FILENAME. +$END +/* source.c - Implements the `.' and `source' builtins. */ + +#include <sys/types.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 */ + +#include "../shell.h" +#include "../posixstat.h" +#include "../filecntl.h" +#include "../execute_cmd.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; + +/* How many `levels' of sourced files we have. */ +int sourcelevel = 0; + +/* If this . script is supplied arguments, we save the dollar vars and + replace them with the script arguments for the duration of the script's + execution. If the script does not change the dollar vars, we restore + what we saved. If the dollar vars are changed in the script, we leave + the new values alone and free the saved values. */ +static void +maybe_pop_dollar_vars () +{ + if (dollar_vars_changed ()) + { + dispose_saved_dollar_vars (); + set_dollar_vars_unchanged (); + } + else + pop_dollar_vars (); +} + +/* Read and execute commands from the file passed as argument. Guess what. + This cannot be done in a subshell, since things like variable assignments + take place in there. So, I open the file, place it into a large string, + close the file, and then execute the string. */ +source_builtin (list) + WORD_LIST *list; +{ + int result, return_val; + + /* Assume the best. */ + result = EXECUTION_SUCCESS; + + if (list) + { + 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++; + + set_dollar_vars_unchanged (); + + return_catch_flag++; + return_val = setjmp (return_catch); + + if (return_val) + parse_and_execute_cleanup (); + else + result = parse_and_execute (string, filename, -1); + + run_unwind_frame ("File Sourcing"); + + /* 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 + { + builtin_error ("filename argument required"); + result = EXECUTION_FAILURE; + } + return (result); +} diff --git a/builtins/suspend.def b/builtins/suspend.def new file mode 100644 index 0000000..48edc20 --- /dev/null +++ b/builtins/suspend.def @@ -0,0 +1,86 @@ +This file is suspend.def, from which is created suspend.c. +It implements the builtin "suspend" 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 suspend.c + +$BUILTIN suspend +$DEPENDS_ON JOB_CONTROL +$FUNCTION suspend_builtin +$SHORT_DOC suspend [-f] +Suspend the execution of this shell until it receives a SIGCONT +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 <signal.h> +#include "../shell.h" +#include "../jobs.h" + +#if defined (JOB_CONTROL) +extern int job_control; + +static SigHandler *old_cont, *old_tstp; + +/* Continue handler. */ +sighandler +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 */ +} + +/* Suspending the shell. If -f is the arg, then do the suspend + no matter what. Otherwise, complain if a login shell. */ +int +suspend_builtin (list) + WORD_LIST *list; +{ + if (!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) + { + builtin_error ("Can't 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); + return (EXECUTION_SUCCESS); +} + +#endif /* JOB_CONTROL */ diff --git a/builtins/test.def b/builtins/test.def new file mode 100644 index 0000000..2b1457b --- /dev/null +++ b/builtins/test.def @@ -0,0 +1,144 @@ +This file is test.def, from which is created test.c. +It implements the builtin "test" 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 test.c + +$BUILTIN test +$FUNCTION test_builtin +$SHORT_DOC test [expr] +Exits with a status of 0 (trueness) or 1 (falseness) depending on +the evaluation of EXPR. Expressions may be unary or binary. Unary +expressions are often used to examine the status of a file. There +are string operators as well, and numeric comparison operators. + +File operators: + + -b FILE True if file is block special. + -c FILE True if file is character special. + -d FILE True if file is a directory. + -e FILE True if file exists. + -f FILE True if file exists and is a regular file. + -g FILE True if file is set-group-id. + -h FILE True if file is a symbolic link. Use "-L". + -L FILE True if file is a symbolic link. + -k FILE True if file has its "sticky" bit set. + -p FILE True if file is a named pipe. + -r FILE True if file is readable by you. + -s FILE True if file is not empty. + -S FILE True if file is a socket. + -t FD True if FD is opened on a terminal. + -u FILE True if the file is set-user-id. + -w FILE True if the file is writable by you. + -x FILE True if the file is executable by you. + -O FILE True if the file is effectively owned by you. + -G FILE True if the file is effectively owned by your group. + + FILE1 -nt FILE2 True if file1 is newer than (according to + modification date) file2. + + FILE1 -ot FILE2 True if file1 is older than file2. + + FILE1 -ef FILE2 True if file1 is a hard link to file2. + +String operators: + + -z STRING True if string is empty. + + -n STRING + or STRING True if string is not empty. + + STRING1 = STRING2 + True if the strings are equal. + STRING1 != STRING2 + True if the strings are not equal. + +Other operators: + + ! EXPR True if expr is false. + EXPR1 -a EXPR2 True if both expr1 AND expr2 are true. + EXPR1 -o EXPR2 True if either expr1 OR expr2 is true. + + arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne, + -lt, -le, -gt, or -ge. + +Arithmetic binary operators return true if ARG1 is equal, not-equal, +less-than, less-than-or-equal, greater-than, or greater-than-or-equal +than ARG2. +$END + +$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. +$END + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include "../shell.h" +extern char *this_command_name; + +/* TEST/[ builtin. */ +int +test_builtin (list) + WORD_LIST *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) + { + if (this_command_name[0] == '[' && !this_command_name[1]) + builtin_error ("missing `]'"); + + 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); + + result = test_command (argc, argv); + free_array (argv); + return (result); +} diff --git a/builtins/times.def b/builtins/times.def new file mode 100644 index 0000000..9c42768 --- /dev/null +++ b/builtins/times.def @@ -0,0 +1,89 @@ +This file is times.def, from which is created times.c. +It implements the builtin "times" 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 times.c + +$BUILTIN times +$FUNCTION times_builtin +$SHORT_DOC times +Print the accumulated user and system times for processes run from +the shell. +$END + +#include "../shell.h" +#include <sys/types.h> + +#if defined (hpux) || defined (USGr4) || defined (XD88) || defined (USGr3) +# undef HAVE_RESOURCE +#endif /* hpux || USGr4 || XD88 || USGr3 */ + +#if defined (_POSIX_VERSION) || !defined (HAVE_RESOURCE) +# include <sys/times.h> +#else /* !_POSIX_VERSION && HAVE_RESOURCE */ +# include <sys/time.h> +# include <sys/resource.h> +#endif /* !_POSIX_VERSION && HAVE_RESOURCE */ + +/* Print the totals for system and user time used. The + information comes from variables in jobs.c used to keep + track of this stuff. */ +times_builtin (list) + WORD_LIST *list; +{ +#if !defined (_POSIX_VERSION) && defined (HAVE_RESOURCE) && defined (RUSAGE_SELF) + struct rusage self, kids; + + getrusage (RUSAGE_SELF, &self); + getrusage (RUSAGE_CHILDREN, &kids); /* terminated child processes */ + + print_timeval (&self.ru_utime); + putchar (' '); + print_timeval (&self.ru_stime); + putchar ('\n'); + print_timeval (&kids.ru_utime); + putchar (' '); + print_timeval (&kids.ru_stime); + putchar ('\n'); + +#else /* _POSIX_VERSION || !HAVE_RESOURCE || !RUSAGE_SELF */ +# if !defined (BrainDeath) + struct tms t; + + times (&t); + + /* 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. */ + + print_time_in_hz (t.tms_utime); + putchar (' '); + print_time_in_hz (t.tms_stime); + putchar ('\n'); + print_time_in_hz (t.tms_cutime); + putchar (' '); + print_time_in_hz (t.tms_cstime); + putchar ('\n'); +# endif /* BrainDeath */ +#endif /* _POSIX_VERSION || !HAVE_RESOURCE || !RUSAGE_SELF */ + + return (EXECUTION_SUCCESS); +} diff --git a/builtins/trap.def b/builtins/trap.def new file mode 100644 index 0000000..b81651d --- /dev/null +++ b/builtins/trap.def @@ -0,0 +1,204 @@ +This file is trap.def, from which is created trap.c. +It implements the builtin "trap" 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 trap.c + +$BUILTIN trap +$FUNCTION trap_builtin +$SHORT_DOC trap [arg] [signal_spec] +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 $$". +$END + +#include <sys/types.h> +#include <signal.h> +#include "../shell.h" +#include "../trap.h" +#include "common.h" + +/* The trap command: + + trap <arg> <signal ...> + trap <signal ...> + trap -l + trap [--] + + Set things up so that ARG is executed when SIGNAL(s) N is recieved. + If ARG is the empty string, then ignore the SIGNAL(s). If there is + no ARG, then set the trap for SIGNAL(s) to its original value. Just + plain "trap" means to print out the list of commands associated with + each signal number. Single arg of "-l" means list the signal names. */ + +/* Possible operations to perform on the list of signals.*/ +#define SET 0 /* Set this signal to first_arg. */ +#define REVERT 1 /* Revert to this signals original value. */ +#define IGNORE 2 /* Ignore this signal. */ + +extern int interactive; + +trap_builtin (list) + WORD_LIST *list; +{ + register int i; + int list_signal_names = 0; + + while (list) + { + if (ISOPTION (list->word->word, '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]"); + return (EX_USAGE); + } + else + break; + } + + if (list_signal_names) + { + 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); + } + + if (list) + { + char *first_arg = list->word->word; + int operation = SET, any_failed = 0; + + if (signal_object_p (first_arg)) + operation = REVERT; + else + { + list = list->next; + if (*first_arg == '\0') + operation = IGNORE; + else if (first_arg[0] == '-' && !first_arg[1]) + operation = REVERT; + } + + 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++; + } + else + { + switch (operation) + { + case SET: + set_signal (sig, first_arg); + break; + + case REVERT: + restore_default_signal (sig); + + /* Signals that the shell treats specially need special + handling. */ + switch (sig) + { + case SIGINT: + if (interactive) + set_signal_handler (SIGINT, sigint_sighandler); + else + set_signal_handler (SIGINT, termination_unwind_protect); + break; + + case SIGQUIT: + /* Always ignore SIGQUIT. */ + set_signal_handler (SIGQUIT, SIG_IGN); + break; + case SIGTERM: +#if defined (JOB_CONTROL) + case SIGTTIN: + case SIGTTOU: + case SIGTSTP: +#endif /* JOB_CONTROL */ + if (interactive) + set_signal_handler (sig, SIG_IGN); + break; + } + break; + + case IGNORE: + ignore_signal (sig); + break; + } + } + list = list->next; + } + return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + } + + for (i = 0; i < NSIG; i++) + { + char *t, *p; + + p = trap_list[i]; + + if (p == (char *)DEFAULT_SIG) + continue; + + t = (p == (char *)IGNORE_SIG) ? (char *)NULL : single_quote (p); + printf ("trap -- %s %s\n", t ? t : "''", signal_name (i)); + if (t) + free (t); + } + return (EXECUTION_SUCCESS); +} diff --git a/builtins/type.def b/builtins/type.def new file mode 100644 index 0000000..aecc303 --- /dev/null +++ b/builtins/type.def @@ -0,0 +1,325 @@ +This file is type.def, from which is created type.c. +It implements the builtin "type" in Bash. + +Copyright (C) 1987, 1989, 1991, 1992 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 type.c + +$BUILTIN type +$FUNCTION type_builtin +$SHORT_DOC type [-all] [-type | -path] [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 +`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 -all 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. +$END + +#include <stdio.h> +#include <sys/types.h> +#include "../posixstat.h" +#include "../shell.h" +#include "../execute_cmd.h" + +#if defined (ALIAS) +#include "../alias.h" +#endif /* ALIAS */ + +#include "common.h" + +extern STRING_INT_ALIST word_token_alist[]; + +/* For each word in LIST, find out what the shell is going to do with + it as a simple command. i.e., which file would this shell use to + execve, or if it is a builtin command, or an alias. Possible flag + arguments: + -type Returns the "type" of the object, one of + `alias', `keyword', `function', `builtin', + or `file'. + + -path Returns the pathname of the file if -type is + a file. + + -all Returns all occurrences of words, whether they + be a filename in the path, alias, function, + or builtin. + Order of evaluation: + alias + keyword + function + builtin + file + */ +type_builtin (list) + WORD_LIST *list; +{ + int path_only, type_only, all, verbose; + int successful_finds; + + 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]); + + if (flag[0] == 't' && (!flag[1] || strcmp (flag + 1, "ype") == 0)) + { + type_only = 1; + path_only = 0; + } + else if (flag[0] == 'p' && (!flag[1] || strcmp (flag + 1, "ath") == 0)) + { + path_only = 1; + type_only = 0; + } + else if (flag[0] == 'a' && (!flag[1] || strcmp (flag + 1, "ll") == 0)) + { + all = 1; + } + else + { + bad_option (flag); + builtin_error ("usage: type [-all | -path | -type ] name [name ...]"); + return (EX_USAGE); + } + list = list->next; + } + + if (type_only) + verbose = 1; + else if (!path_only) + verbose = 2; + else if (path_only) + verbose = 3; + else + verbose = 0; + + while (list) + { + int found; + + found = describe_command (list->word->word, verbose, all); + + if (!found && !path_only && !type_only) + builtin_error ("%s: not found", list->word->word); + + successful_finds += found; + list = list->next; + } + + fflush (stdout); + + if (successful_finds != 0) + return (EXECUTION_SUCCESS); + else + return (EXECUTION_FAILURE); +} + +/* + * Describe COMMAND as required by the type builtin. + * + * If VERBOSE == 0, don't print anything + * If VERBOSE == 1, print short description as for `type -t' + * If VERBOSE == 2, print long description as for `type' and `command -V' + * If VERBOSE == 3, print path name only for disk files + * If VERBOSE == 4, print string used to invoke COMMAND, for `command -v' + * + * ALL says whether or not to look for all occurrences of COMMAND, or + * return after finding it once. + */ +describe_command (command, verbose, all) + char *command; + int verbose, all; +{ + int found = 0, i, found_file = 0; + char *full_path = (char *)NULL; + SHELL_VAR *func; + +#if defined (ALIAS) + /* Command is an alias? */ + ASSOC *alias = find_alias (command); + + if (alias) + { + if (verbose == 1) + printf ("alias\n"); + else if (verbose == 2) + printf ("%s is aliased to `%s'\n", command, alias->value); + else if (verbose == 4) + { + char *x = single_quote (alias->value); + printf ("alias %s=%s\n", command, x); + free (x); + } + + found = 1; + + if (!all) + return (1); + } +#endif /* ALIAS */ + + /* Command is a shell reserved word? */ + i = find_reserved_word (command); + if (i >= 0) + { + if (verbose == 1) + printf ("keyword\n"); + else if (verbose == 2) + printf ("%s is a shell keyword\n", command); + else if (verbose == 4) + printf ("%s\n", command); + + found = 1; + + if (!all) + return (1); + } + + /* Command is a function? */ + func = find_function (command); + + if (func) + { + if (verbose == 1) + printf ("function\n"); + else if (verbose == 2) + { +#define PRETTY_PRINT_FUNC 1 + char *result; + + printf ("%s is a function\n", command); + + /* We're blowing away THE_PRINTED_COMMAND here... */ + + result = named_function_string (command, + (COMMAND *) function_cell (func), + PRETTY_PRINT_FUNC); + printf ("%s\n", result); +#undef PRETTY_PRINT_FUNC + } + else if (verbose == 4) + printf ("%s\n", command); + + found = 1; + + if (!all) + return (1); + } + + /* Command is a builtin? */ + if (find_shell_builtin (command)) + { + if (verbose == 1) + printf ("builtin\n"); + else if (verbose == 2) + printf ("%s is a shell builtin\n", command); + else if (verbose == 4) + printf ("%s\n", command); + + found = 1; + + if (!all) + return (1); + } + + /* Command is a disk file? */ + /* If the command name given is already an absolute command, just + check to see if it is executable. */ + if (absolute_program (command)) + { + int f = file_status (command); + if (f & FS_EXECABLE) + { + if (verbose == 1) + printf ("file\n"); + else if (verbose == 2) + printf ("%s is %s\n", command, command); + else if (verbose == 3 || verbose == 4) + printf ("%s\n", command); + + /* There's no use looking in the hash table or in $PATH, + because they're not consulted when an absolute program + name is supplied. */ + return (1); + } + } + + /* If the user isn't doing "-all", then we might care about + whether the file is present in our hash table. */ + if (!all) + { + if ((full_path = find_hashed_filename (command)) != (char *)NULL) + { + if (verbose == 1) + printf ("file\n"); + else if (verbose == 2) + printf ("%s is hashed (%s)\n", command, full_path); + else if (verbose == 3 || verbose == 4) + printf ("%s\n", full_path); + + return (1); + } + } + + /* Now search through $PATH. */ + while (1) + { + if (!all) + full_path = find_user_command (command); + else + full_path = + user_command_matches (command, FS_EXEC_ONLY, found_file); + /* XXX - should that be FS_EXEC_PREFERRED? */ + + if (!full_path) + break; + + found_file++; + found = 1; + + if (verbose == 1) + printf ("file\n"); + else if (verbose == 2) + printf ("%s is %s\n", command, full_path); + else if (verbose == 3 || verbose == 4) + printf ("%s\n", full_path); + + free (full_path); + full_path = (char *)NULL; + + if (!all) + break; + } + + return (found); +} diff --git a/builtins/ulimit.def b/builtins/ulimit.def new file mode 100644 index 0000000..1947c36 --- /dev/null +++ b/builtins/ulimit.def @@ -0,0 +1,731 @@ +This file is ulimit.def, from which is created ulimit.c. +It implements the builtin "ulimit" 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 ulimit.c + +$BUILTIN ulimit +$FUNCTION ulimit_builtin +$DEPENDS_ON !MINIX +$SHORT_DOC ulimit [-SHacdfmstpnuv [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: + + -S use the `soft' resource limit + -H use the `hard' resource limit + -a all current limits are reported + -c the maximum size of core files created + -d the maximum size of a process's data segment + -m the maximum resident set 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 +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 <sys/param.h> +#include <errno.h> +#include "../shell.h" +#include "pipesize.h" + +#if !defined (errno) +extern int errno; +#endif + +#if defined (HAVE_RESOURCE) +# include <sys/time.h> +# include <sys/resource.h> +#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) +# undef HAVE_RESOURCE +#endif + +#if !defined (RLIMTYPE) +# define RLIMTYPE long +# define string_to_rlimtype string_to_long +# define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "") +#endif + +static void print_long (); + +/* **************************************************************** */ +/* */ +/* Ulimit builtin and Hacks. */ +/* */ +/* **************************************************************** */ + +/* Block size for ulimit operations. */ +#define ULIMIT_BLOCK_SIZE ((long)1024) + +#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 + +#define u_ALL_LIMITS 0x7ff + +#if !defined (RLIM_INFINITY) +# 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 */ + +#define LIMIT_HARD 0x01 +#define LIMIT_SOFT 0x02 + +static RLIMTYPE shell_ulimit (); +static RLIMTYPE pipesize (); +static RLIMTYPE open_files (); + +#if defined (HAVE_RESOURCE) +static RLIMTYPE getmaxvm (); +#endif /* HAVE_RESOURCE */ + +static void print_specific_limits (); +static void print_all_limits (); + +static char t[2]; + +/* 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; +{ +# if defined (HAVE_SETDTABLESIZE) + if (cmd == u_NUM_OPEN_FILES) + return (1); +# endif /* HAVE_SETDTABLSIZE */ + + return ((current > new) || (current_user.uid == 0)); +} +#endif /* !HAVE_RESOURCE */ + +/* Report or set limits associated with certain per-process resources. + See the help documentation in builtins.c for a full description. + + Rewritten by Chet Ramey 6/30/91. */ +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; + + c = mode = verbose_print = opt_eof = 0; + limit = (RLIMTYPE)-1; + + do + { + cmd = setting = all_limits = specific_limits = 0; + block_factor = ULIMIT_BLOCK_SIZE; + + /* read_options: */ + if (list && !opt_eof && *list->word->word == '-') + { + 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); + } + } + } + + 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) + { + print_specific_limits (cmd, mode); + continue; + } + + current_limit = shell_ulimit (cmd, real_limit, 0, mode); + + 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 + { + 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"); + } + } + while (list); + + return (EXECUTION_SUCCESS); +} + +/* The ulimit that we call from within Bash. + + 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. + + Systems without BSD resource limits can specify only u_FILE_SIZE. + This includes most USG systems. + + Chet Ramey supplied the BSD resource limit code. */ +static RLIMTYPE +shell_ulimit (which, newlim, setting, mode) + int which, setting, mode; + RLIMTYPE newlim; +{ +#if defined (HAVE_RESOURCE) + struct rlimit limit; + int cmd; + + if (mode == 0) + mode |= LIMIT_SOFT; +#endif + + switch (which) + { +#if !defined (HAVE_RESOURCE) + + case u_FILE_SIZE: + if (!setting) + { + /* 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 */ + } + 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; +#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 */ + + /* 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 ()); + + 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)); + + case u_MAX_VIRTUAL_MEM: + if (setting) + { + errno = EINVAL; + return ((RLIMTYPE)-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: + errno = EINVAL; + return ((RLIMTYPE)-1); + } +} + +#if defined (HAVE_RESOURCE) +static RLIMTYPE +getmaxvm (mode) + int mode; +{ + 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); + else + maxdata = (mode & LIMIT_SOFT) ? rl.rlim_cur : rl.rlim_max; + + if (getrlimit (RLIMIT_STACK, &rl) < 0) + return ((RLIMTYPE)-1); + else + maxstack = (mode & LIMIT_SOFT) ? rl.rlim_cur : rl.rlim_max; + + /* Protect against overflow. */ + return ((maxdata / 1024L) + (maxstack / 1024L)); +#endif /* !RLIMIT_VMEM */ +} +#endif /* HAVE_RESOURCE */ + +static RLIMTYPE +open_files (mode) + int mode; +{ +#if !defined (RLIMIT_NOFILE) + return ((RLIMTYPE)getdtablesize ()); +#else + struct rlimit rl; + + getrlimit (RLIMIT_NOFILE, &rl); + if (mode & LIMIT_SOFT) + return (rl.rlim_cur); + else + return (rl.rlim_max); +#endif +} + +static RLIMTYPE +pipesize () +{ +#if defined (PIPE_BUF) + /* This is defined on Posix systems. */ + return ((RLIMTYPE) PIPE_BUF); +#else +# if defined (PIPESIZE) + /* This is defined by running a program from the Makefile. */ + return ((RLIMTYPE) PIPESIZE); +# else + errno = EINVAL; + return ((RLIMTYPE)-1); +# 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; +{ + 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); +} + +static void +print_specific_bsd_limits (cmd, mode) + int cmd, mode; +{ + register int i; + + 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); + } + +#if defined (HAVE_RESOURCE) + if (cmd & u_MAX_VIRTUAL_MEM) + { + printf ("%-25s", "virtual memory (kbytes)"); + print_rlimtype (getmaxvm (mode), 1); + } +#endif /* HAVE_RESOURCE */ +} + +static void +print_all_limits (mode) + int mode; +{ + if (mode == 0) + mode |= LIMIT_SOFT; + + print_specific_limits (u_ALL_LIMITS, mode); +} diff --git a/builtins/umask.def b/builtins/umask.def new file mode 100644 index 0000000..1d84aa9 --- /dev/null +++ b/builtins/umask.def @@ -0,0 +1,288 @@ +This file is umask.def, from which is created umask.c. +It implements the builtin "umask" 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 umask.c + +$BUILTIN umask +$FUNCTION umask_builtin +$SHORT_DOC umask [-S] [mode] +The user file-creation mask is set to MODE. If MODE is omitted, or if +`-S' is supplied, the current value of the mask is printed. The `-S' +option makes the output symbolic; otherwise an octal number is output. +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 <sys/file.h> +#include "../shell.h" +#include "../posixstat.h" +#include "common.h" + +/* **************************************************************** */ +/* */ +/* UMASK Builtin and Helpers */ +/* */ +/* **************************************************************** */ + +static void print_symbolic_umask (); +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. */ +umask_builtin (list) + WORD_LIST *list; +{ + int print_symbolically = 0; + + while (list) + { + if (ISOPTION (list->word->word, 'S')) + { + list = list->next; + 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]"); + return (EX_USAGE); + } + else + break; + } + + if (list) + { + int new_umask; + + if (digit (*list->word->word)) + { + new_umask = 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) + { + builtin_error ("`%s' is not an octal number from 000 to 777", + list->word->word); + return (EXECUTION_FAILURE); + } + } + else + { + new_umask = symbolic_umask (list); + if (new_umask == -1) + return (EXECUTION_FAILURE); + } + umask (new_umask); + if (print_symbolically) + print_symbolic_umask (new_umask); + } + else /* Display the UMASK for this user. */ + { + int old_umask; + + old_umask = umask (022); + umask (old_umask); + + if (print_symbolically) + print_symbolic_umask (old_umask); + else + printf ("%03o\n", old_umask); + } + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +/* Print the umask in a symbolic form. In the output, a letter is + printed if the corresponding bit is clear in the umask. */ +static void +print_symbolic_umask (um) + int um; +{ + char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ + int i; + + i = 0; + if ((um & S_IRUSR) == 0) + ubits[i++] = 'r'; + if ((um & S_IWUSR) == 0) + ubits[i++] = 'w'; + if ((um & S_IXUSR) == 0) + ubits[i++] = 'x'; + ubits[i] = '\0'; + + i = 0; + if ((um & S_IRGRP) == 0) + gbits[i++] = 'r'; + if ((um & S_IWGRP) == 0) + gbits[i++] = 'w'; + if ((um & S_IXGRP) == 0) + gbits[i++] = 'x'; + gbits[i] = '\0'; + + i = 0; + if ((um & S_IROTH) == 0) + obits[i++] = 'r'; + if ((um & S_IWOTH) == 0) + obits[i++] = 'w'; + if ((um & S_IXOTH) == 0) + obits[i++] = 'x'; + obits[i] = '\0'; + + printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits); +} + +/* Set the umask from a symbolic mode string similar to that accepted + by chmod. If the -S argument is given, then print the umask in a + symbolic form. */ +static int +symbolic_umask (list) + WORD_LIST *list; +{ + int um, umc, c; + int who, op, perm, mask; + char *s; + + /* Get the initial umask. Don't change it yet. */ + um = umask (022); + umask (um); + + /* All work below is done with the complement of the umask -- its + more intuitive and easier to deal with. It is complemented + again before being returned. */ + umc = ~um; + + s = list->word->word; + + for (;;) + { + who = op = perm = mask = 0; + + /* Parse the `who' portion of the symbolic mode clause. */ + while (member (*s, "agou")) + { + switch (c = *s++) + { + case 'u': + who |= S_IRWXU; + continue; + case 'g': + who |= S_IRWXG; + continue; + case 'o': + who |= S_IRWXO; + continue; + case 'a': + who |= S_IRWXU | S_IRWXG | S_IRWXO; + continue; + default: + break; + } + } + + /* The operation is now sitting in *s. */ + op = *s++; + switch (op) + { + case '+': + case '-': + case '=': + break; + default: + builtin_error ("bad symbolic mode operator: %c", op); + return (-1); + } + + /* Parse out the `perm' section of the symbolic mode clause. */ + while (member (*s, "rwx")) + { + c = *s++; + + switch (c) + { + case 'r': + perm |= S_IRUGO; + break; + + case 'w': + perm |= S_IWUGO; + break; + + case 'x': + perm |= S_IXUGO; + break; + } + } + + /* Now perform the operation or return an error for a + bad permission string. */ + if (!*s || *s == ',') + { + if (who) + perm &= who; + + switch (op) + { + case '+': + umc |= perm; + break; + + case '-': + umc &= ~perm; + break; + + case '=': + umc &= ~who; + umc |= perm; + break; + + default: + builtin_error ("bad operation character: %c", op); + return (-1); + } + + if (!*s) + { + um = ~umc & 0777; + break; + } + else + s++; /* skip past ',' */ + } + else + { + builtin_error ("bad character in symbolic mode: %c", *s); + return (-1); + } + } + return (um); +} diff --git a/builtins/wait.def b/builtins/wait.def new file mode 100644 index 0000000..f613179 --- /dev/null +++ b/builtins/wait.def @@ -0,0 +1,132 @@ +This file is wait.def, from which is created wait.c. +It implements the builtin "wait" 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. + + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON JOB_CONTROL +$PRODUCES wait.c +$SHORT_DOC wait [n] +Wait for the specified process and report its termination status. If +N is not given, all currently active child processes are waited for, +and the return code is zero. N may be a process ID or a job +specification; if a job spec is given, all processes in the job's +pipeline are waited for. +$END + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON !JOB_CONTROL +$SHORT_DOC wait [n] +Wait for the specified process and report its termination status. If +N is not given, all currently active child processes are waited for, +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 <signal.h> +#include "../shell.h" +#include "../jobs.h" + +extern int interrupt_immediately; + +/* Wait for the pid in LIST to stop or die. If no arguments are given, then + wait for all of the active background processes of the shell and return + 0. If a list of pids or job specs are given, return the exit status of + the last one waited for. */ +wait_builtin (list) + WORD_LIST *list; +{ + int status = EXECUTION_SUCCESS; + + begin_unwind_frame ("wait_builtin"); + unwind_protect_int (interrupt_immediately); + interrupt_immediately++; + + /* We support jobs or pids. + wait <pid-or-job> [pid-or-job ...] */ + + /* But wait without any arguments means to wait for all of the shell's + currently active background processes. */ + if (!list) + { + wait_for_background_pids (); + status = EXECUTION_SUCCESS; + goto return_status; + } + + while (list) + { + pid_t pid; + char *w; + + w = list->word->word; + if (digit (*w)) + { + if (all_digits (w + 1)) + { + pid = (pid_t)atoi (w); + status = wait_for_single_pid (pid); + } + else + { + builtin_error ("`%s' is not a pid or legal job spec", w); + status = EXECUTION_FAILURE; + goto return_status; + } + } +#if defined (JOB_CONTROL) + else if (job_control && *w) + /* Must be a job spec. Check it out. */ + { + int job; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (job < 0 || job >= job_slots || !jobs[job]) + { + if (job != DUP_JOB) + builtin_error ("No such job %s", list->word->word); + UNBLOCK_CHILD (oset); + status = 127; /* As per Posix.2, section 4.70.2 */ + list = list->next; + continue; + } + + /* Job spec used. Wait for the last pid in the pipeline. */ + UNBLOCK_CHILD (oset); + status = wait_for_job (job); + } +#endif /* JOB_CONTROL */ + else + { + builtin_error ("`%s' is not a pid or legal job spec", w); + status = EXECUTION_FAILURE; + } + list = list->next; + } + return_status: + run_unwind_frame ("wait_builtin"); + return (status); +} |
