From 5cef52a76316d98edb20e26bf793694592066f1e Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Sat, 3 Dec 2011 22:51:04 -0500 Subject: [PATCH] commit bash-20060727 snapshot --- CWRU/CWRU.chlog | 16 +- CWRU/CWRU.chlog~ | 15 +- Makefile.in | 7 +- Makefile.in~ | 11 +- arrayfunc.c | 1 - bashline.c | 7 +- bashline.c~ | 5 +- builtins/caller.def | 1 - builtins/cd.def | 1 + builtins/cd.def~ | 525 ++++++++++++ builtins/common.c | 3 - builtins/echo.def | 2 + builtins/echo.def~ | 183 +++++ builtins/evalfile.c | 2 +- builtins/evalfile.c~ | 1 - builtins/evalstring.c | 5 +- builtins/evalstring.c~ | 352 ++++++++ builtins/fc.def | 1 - builtins/fc.def~ | 631 ++++++++++++++ builtins/printf.def | 3 +- builtins/printf.def~ | 1008 +++++++++++++++++++++++ builtins/set.def | 1 - builtins/source.def | 4 +- builtins/source.def~ | 174 ++++ builtins/suspend.def | 4 +- builtins/suspend.def~ | 119 +++ builtins/trap.def | 2 +- builtins/wait.def | 1 - builtins/wait.def~ | 177 ++++ error.c | 2 - error.c~ | 454 ++++++++++ externs.h | 3 +- externs.h~ | 4 + general.c | 1 - general.c~ | 7 +- input.c | 2 + input.c~ | 625 ++++++++++++++ jobs.c | 12 +- jobs.c~ | 9 +- lib/glob/glob.c | 2 +- lib/glob/smatch.c | 1 - lib/readline/bind.c | 1 - lib/readline/complete.c | 4 +- lib/readline/complete.c~ | 23 +- lib/readline/display.c | 4 +- lib/readline/display.c~ | 82 +- lib/readline/histexpand.c | 12 +- lib/readline/histexpand.c~ | 1593 ++++++++++++++++++++++++++++++++++++ lib/readline/input.c | 10 +- lib/readline/input.c~ | 15 +- lib/readline/text.c | 16 +- lib/readline/text.c~ | 10 +- lib/readline/vi_mode.c | 14 +- lib/readline/vi_mode.c~ | 8 +- lib/sh/netopen.c | 10 + lib/sh/netopen.c~ | 340 ++++++++ lib/sh/shmatch.c | 3 +- lib/sh/shmatch.c~ | 121 +++ lib/sh/strtrans.c | 2 +- lib/sh/tmpfile.c | 5 +- lib/sh/tmpfile.c~ | 214 +++++ lib/sh/winsize.c | 1 + lib/sh/winsize.c~ | 82 ++ locale.c | 1 - make_cmd.c | 1 - parse.y | 19 +- parse.y~ | 17 +- pcomplete.c | 2 +- subst.c | 26 +- subst.c~ | 26 +- trap.c | 1 - trap.c~ | 17 +- unwind_prot.c | 2 - unwind_prot.c~ | 320 ++++++++ variables.c | 14 - variables.h | 1 - 76 files changed, 7162 insertions(+), 239 deletions(-) create mode 100644 builtins/cd.def~ create mode 100644 builtins/echo.def~ create mode 100644 builtins/evalstring.c~ create mode 100644 builtins/fc.def~ create mode 100644 builtins/printf.def~ create mode 100644 builtins/source.def~ create mode 100644 builtins/suspend.def~ create mode 100644 builtins/wait.def~ create mode 100644 error.c~ create mode 100644 input.c~ create mode 100644 lib/readline/histexpand.c~ create mode 100644 lib/sh/netopen.c~ create mode 100644 lib/sh/shmatch.c~ create mode 100644 lib/sh/tmpfile.c~ create mode 100644 lib/sh/winsize.c~ create mode 100644 unwind_prot.c~ diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index c0eeaa115..1f9dc27a4 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13516,7 +13516,7 @@ lib/readline/display.c 7/5 --- builtins/printf.def - - add echo's write error handling to printf. Suggested by + - add more of echo's write error handling to printf. Suggested by martin.wilck@fujitsu-siemens.com 7/7 @@ -13566,3 +13566,17 @@ lib/malloc/{stats,table}.h lib/termcap/{termcap,tparam}.c - include and provide macro replacement for bcopy if necessary + + 7/27 + ---- +lib/readline/histexpand.c + - add support for `<<<' here-string redirection operator to + history_tokenize_word. Bug reported by agriffis@gentoo.org + +externs.h + - don't add prototype for strerror() if HAVE_STRERROR defined + + 7/29 + ---- +subst.c + - in list_string, use `string' instead of `s' -- s is not initialized diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index 56fb9dce9..154072d41 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -13516,7 +13516,7 @@ lib/readline/display.c 7/5 --- builtins/printf.def - - add echo's write error handling to printf. Suggested by + - add more of echo's write error handling to printf. Suggested by martin.wilck@fujitsu-siemens.com 7/7 @@ -13562,3 +13562,16 @@ lib/readline/display.c ---- lib/malloc/{stats,table}.h - include for prototypes for memset, strlen + +lib/termcap/{termcap,tparam}.c + - include and provide macro replacement for bcopy if + necessary + + 7/27 + ---- +lib/readline/histexpand.c + - add support for `<<<' here-string redirection operator to + history_tokenize_word. Bug reported by agriffis@gentoo.org + +externs.h + - don't add prototype for strerror() if HAVE_STRERROR defined diff --git a/Makefile.in b/Makefile.in index 8220f3b8e..f75363100 100644 --- a/Makefile.in +++ b/Makefile.in @@ -147,8 +147,11 @@ LDFLAGS_FOR_BUILD = $(LDFLAGS) INCLUDES = -I. @RL_INCLUDE@ -I$(srcdir) -I$(BASHINCDIR) -I$(LIBSRC) $(INTL_INC) -GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \ - -Wcast-align -Wstrict-prototypes -Wconversion \ +# Maybe add: -Wextra +GCC_LINT_FLAGS = -O -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wno-parentheses \ + -Wcast-align -Wstrict-prototypes -Wconversion -Wformat \ + -Wformat-nonliteral -Wmissing-braces -Wuninitialized \ + -Wmissing-declarations -Winline \ -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic GCC_LINT_CFLAGS = $(BASE_CCFLAGS) $(CPPFLAGS) $(GCC_LINT_FLAGS) diff --git a/Makefile.in~ b/Makefile.in~ index 02e8e2c52..35cd448e6 100644 --- a/Makefile.in~ +++ b/Makefile.in~ @@ -147,8 +147,11 @@ LDFLAGS_FOR_BUILD = $(LDFLAGS) INCLUDES = -I. @RL_INCLUDE@ -I$(srcdir) -I$(BASHINCDIR) -I$(LIBSRC) $(INTL_INC) -GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \ - -Wcast-align -Wstrict-prototypes -Wconversion \ +# Maybe add: -Wextra +GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wno-parentheses \ + -Wcast-align -Wstrict-prototypes -Wconversion -Wformat \ + -Wformat-nonliteral -Wmissing-braces -Wuninitialized \ + -Wmissing-declarations -Winline \ -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic GCC_LINT_CFLAGS = $(BASE_CCFLAGS) $(CPPFLAGS) $(GCC_LINT_FLAGS) @@ -201,7 +204,7 @@ SHLIB_SOURCE = ${SH_LIBSRC}/clktck.c ${SH_LIBSRC}/getcwd.c \ ${SH_LIBSRC}/memset.c ${SH_LIBSRC}/xstrchr.c \ ${SH_LIBSRC}/zcatfd.c ${SH_LIBSRC}/shmatch.c \ ${SH_LIBSRC}/strnlen.c ${SH_LIBSRC}/winsize.c \ - ${SH_LIBSRC}/shaccess.c + ${SH_LIBSRC}/eaccess.c ${SH_LIBSRC}/wcsdup.c SHLIB_LIB = -lsh SHLIB_LIBNAME = libsh.a @@ -499,7 +502,7 @@ CREATED_SUPPORT = signames.h recho$(EXEEXT) zecho$(EXEEXT) printenv$(EXEEXT) \ tests/recho$(EXEEXT) tests/zecho$(EXEEXT) \ tests/printenv$(EXEEXT) mksignames$(EXEEXT) lsignames.h \ mksyntax${EXEEXT} syntax.c $(VERSPROG) $(VERSOBJ) \ - buildversion.o mksignames.o signames.o + buildversion.o mksignames.o signames.o buildsignames.o CREATED_CONFIGURE = config.h config.cache config.status config.log \ stamp-h po/POTFILES CREATED_MAKEFILES = Makefile builtins/Makefile doc/Makefile \ diff --git a/arrayfunc.c b/arrayfunc.c index 58f1c1b7c..48a099dde 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -273,7 +273,6 @@ expand_compound_array_assignment (value, flags) WORD_LIST *list, *nlist; char *val; int ni; - arrayind_t ind, last_ind; /* I don't believe this condition is ever true any more. */ if (*value == '(') /*)*/ diff --git a/bashline.c b/bashline.c index 249f43b7a..809fe32b6 100644 --- a/bashline.c +++ b/bashline.c @@ -1461,7 +1461,9 @@ command_word_completion_function (hint_text, state) else { int match, freetemp; - char *temp; +#if 0 + char *temp; /* shadows previous declaration */ +#endif if (absolute_program (hint)) { @@ -1760,8 +1762,9 @@ bash_servicename_completion_function (text, state) if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen))) break; /* Not primary, check aliases */ - for (alist = srvent->s_aliases; aentry = *alist; alist++) + for (alist = srvent->s_aliases; *alist; alist++) { + aentry = *alist; if (STREQN (sname, aentry, snamelen)) { afound = 1; diff --git a/bashline.c~ b/bashline.c~ index a515f63b2..4b11de926 100644 --- a/bashline.c~ +++ b/bashline.c~ @@ -1254,7 +1254,6 @@ command_word_completion_function (hint_text, state) mapping_over = searching_path = 0; hint_is_dir = CMD_IS_DIR (hint_text); - val = (char *)NULL; temp = rl_variable_value ("completion-ignore-case"); @@ -1462,7 +1461,9 @@ command_word_completion_function (hint_text, state) else { int match, freetemp; - char *temp; +#if 0 + char *temp; /* shadows previous declaration */ +#endif if (absolute_program (hint)) { diff --git a/builtins/caller.def b/builtins/caller.def index 5142cab9a..b6e823882 100644 --- a/builtins/caller.def +++ b/builtins/caller.def @@ -76,7 +76,6 @@ caller_builtin (list) SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v; ARRAY *funcname_a, *bash_source_a, *bash_lineno_a; char *funcname_s, *source_s, *lineno_s; - ARRAY_ELEMENT *ae; intmax_t num; GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a); diff --git a/builtins/cd.def b/builtins/cd.def index 025e4f5e1..54e328e4f 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -60,6 +60,7 @@ extern char *bash_getcwd_errstr; static int bindpwd __P((int)); static void setpwd __P((char *)); +static char *resetpwd __P((char *)); static int change_to_directory __P((char *, int)); static char *cdspell __P((char *)); diff --git a/builtins/cd.def~ b/builtins/cd.def~ new file mode 100644 index 000000000..025e4f5e1 --- /dev/null +++ b/builtins/cd.def~ @@ -0,0 +1,525 @@ +This file is cd.def, from which is created cd.c. It implements the +builtins "cd" and "pwd" in Bash. + +Copyright (C) 1987-2005 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +$PRODUCES cd.c +#include + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include "../bashtypes.h" +#include "posixdir.h" +#include "posixstat.h" +#ifndef _MINIX +#include +#endif + +#include + +#include "../bashansi.h" +#include "../bashintl.h" + +#include +#include + +#include "../shell.h" +#include "../flags.h" +#include "maxpath.h" +#include "common.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int posixly_correct; +extern int array_needs_making; +extern char *bash_getcwd_errstr; + +static int bindpwd __P((int)); +static void setpwd __P((char *)); +static int change_to_directory __P((char *, int)); + +static char *cdspell __P((char *)); + +/* Change this to 1 to get cd spelling correction by default. */ +int cdspelling = 0; + +int cdable_vars; + +$BUILTIN cd +$FUNCTION cd_builtin +$SHORT_DOC cd [-L|-P] [dir] +Change the 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 in CDPATH +are separated by a colon (:). A null directory name is the same as +the current directory, i.e. `.'. If DIR begins with a slash (/), +then CDPATH is not used. If the directory is not found, and the +shell option `cdable_vars' is set, then try the word as a variable +name. If that variable has a value, then cd to the value of that +variable. The -P option says to use the physical directory structure +instead of following symbolic links; the -L option forces symbolic links +to be followed. +$END + +/* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */ +static void +setpwd (dirname) + char *dirname; +{ + int old_anm; + SHELL_VAR *tvar; + + old_anm = array_needs_making; + tvar = bind_variable ("PWD", dirname ? dirname : "", 0); + if (old_anm == 0 && array_needs_making && exported_p (tvar)) + { + update_export_env_inplace ("PWD=", 4, dirname ? dirname : ""); + array_needs_making = 0; + } +} + +static int +bindpwd (no_symlinks) + int no_symlinks; +{ + char *dirname, *pwdvar; + int old_anm; + SHELL_VAR *tvar; + +#define tcwd the_current_working_directory + dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("cd"); +#undef tcwd + + old_anm = array_needs_making; + pwdvar = get_string_value ("PWD"); + + tvar = bind_variable ("OLDPWD", pwdvar, 0); + if (old_anm == 0 && array_needs_making && exported_p (tvar)) + { + update_export_env_inplace ("OLDPWD=", 7, pwdvar); + array_needs_making = 0; + } + + setpwd (dirname); + + if (dirname && dirname != the_current_working_directory) + free (dirname); + + return (EXECUTION_SUCCESS); +} + +/* Call get_working_directory to reset the value of + the_current_working_directory () */ +static char * +resetpwd (caller) + char *caller; +{ + char *tdir; + + FREE (the_current_working_directory); + the_current_working_directory = (char *)NULL; + tdir = get_working_directory (caller); + return (tdir); +} + +#define LCD_DOVARS 0x001 +#define LCD_DOSPELL 0x002 +#define LCD_PRINTPATH 0x004 +#define LCD_FREEDIRNAME 0x010 + +/* This builtin is ultimately the way that all user-visible commands should + change the current working directory. It is called by cd_to_string (), + so the programming interface is simple, and it handles errors and + restrictions properly. */ +int +cd_builtin (list) + WORD_LIST *list; +{ + char *dirname, *cdpath, *path, *temp; + int path_index, no_symlinks, opt, lflag; + +#if defined (RESTRICTED_SHELL) + if (restricted) + { + sh_restricted ((char *)NULL); + return (EXECUTION_FAILURE); + } +#endif /* RESTRICTED_SHELL */ + + no_symlinks = no_symbolic_links; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) + { + switch (opt) + { + case 'P': + no_symlinks = 1; + break; + case 'L': + no_symlinks = 0; + break; + default: + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + list = loptend; + + lflag = (cdable_vars ? LCD_DOVARS : 0) | + ((interactive && cdspelling) ? LCD_DOSPELL : 0); + + if (list == 0) + { + /* `cd' without arguments is equivalent to `cd $HOME' */ + dirname = get_string_value ("HOME"); + + if (dirname == 0) + { + builtin_error (_("HOME not set")); + return (EXECUTION_FAILURE); + } + lflag = 0; + } + else if (list->word->word[0] == '-' && list->word->word[1] == '\0') + { + /* This is `cd -', equivalent to `cd $OLDPWD' */ + dirname = get_string_value ("OLDPWD"); + + if (dirname == 0) + { + builtin_error (_("OLDPWD not set")); + return (EXECUTION_FAILURE); + } +#if 0 + lflag = interactive ? LCD_PRINTPATH : 0; +#else + lflag = LCD_PRINTPATH; /* According to SUSv3 */ +#endif + } + else if (absolute_pathname (list->word->word)) + dirname = list->word->word; + else if (cdpath = get_string_value ("CDPATH")) + { + dirname = list->word->word; + + /* Find directory in $CDPATH. */ + path_index = 0; + while (path = extract_colon_unit (cdpath, &path_index)) + { + /* OPT is 1 if the path element is non-empty */ + opt = path[0] != '\0'; + temp = sh_makepath (path, dirname, MP_DOTILDE); + free (path); + + if (change_to_directory (temp, no_symlinks)) + { + /* POSIX.2 says that if a nonempty directory from CDPATH + is used to find the directory to change to, the new + directory name is echoed to stdout, whether or not + the shell is interactive. */ + if (opt && (path = no_symlinks ? temp : the_current_working_directory)) + printf ("%s\n", path); + + free (temp); +#if 0 + /* Posix.2 says that after using CDPATH, the resultant + value of $PWD will not contain `.' or `..'. */ + return (bindpwd (posixly_correct || no_symlinks)); +#else + return (bindpwd (no_symlinks)); +#endif + } + else + free (temp); + } + + /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't + try the current directory, so we just punt now with an error + message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] + is so we don't mistakenly treat a CDPATH value of "" as not + specifying the current directory. */ + if (posixly_correct && cdpath[0]) + { + builtin_error ("%s: %s", dirname, strerror (ENOENT)); + return (EXECUTION_FAILURE); + } + } + else + dirname = list->word->word; + + /* When we get here, DIRNAME is the directory to change to. If we + chdir successfully, just return. */ + if (change_to_directory (dirname, no_symlinks)) + { + if (lflag & LCD_PRINTPATH) + printf ("%s\n", dirname); + return (bindpwd (no_symlinks)); + } + + /* If the user requests it, then perhaps this is the name of + a shell variable, whose value contains the directory to + change to. */ + if (lflag & LCD_DOVARS) + { + temp = get_string_value (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); + } + } + + /* If the user requests it, try to find a directory name similar in + spelling to the one requested, in case the user made a simple + typo. This is similar to the UNIX 8th and 9th Edition shells. */ + if (lflag & LCD_DOSPELL) + { + temp = cdspell (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); + } + else + FREE (temp); + } + + builtin_error ("%s: %s", dirname, strerror (errno)); + return (EXECUTION_FAILURE); +} + +$BUILTIN pwd +$FUNCTION pwd_builtin +$SHORT_DOC pwd [-LP] +Print the current working directory. With the -P option, pwd prints +the physical directory, without any symbolic links; the -L option +makes pwd follow symbolic links. +$END + +/* Non-zero means that pwd always prints the physical directory, without + symbolic links. */ +static int verbatim_pwd; + +/* Print the name of the current working directory. */ +int +pwd_builtin (list) + WORD_LIST *list; +{ + char *directory; + int opt, pflag; + + verbatim_pwd = no_symbolic_links; + pflag = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "LP")) != -1) + { + switch (opt) + { + case 'P': + verbatim_pwd = pflag = 1; + break; + case 'L': + verbatim_pwd = 0; + break; + default: + builtin_usage (); + return (EXECUTION_FAILURE); + } + } + list = loptend; + +#define tcwd the_current_working_directory + + directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("pwd"); + + /* Try again using getcwd() if canonicalization fails (for instance, if + the file system has changed state underneath bash). */ + if ((tcwd && directory == 0) || + (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0)) + directory = resetpwd ("pwd"); + +#undef tcwd + + if (directory) + { + printf ("%s\n", directory); + /* This is dumb but posix-mandated. */ + if (posixly_correct && pflag) + setpwd (directory); + if (directory != the_current_working_directory) + free (directory); + fflush (stdout); + if (ferror (stdout)) + { + sh_wrerror (); + clearerr (stdout); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); + } + else + return (EXECUTION_FAILURE); +} + +/* Do the work of changing to the directory NEWDIR. Handle symbolic + link following, etc. This function *must* return with + the_current_working_directory either set to NULL (in which case + getcwd() will eventually be called), or set to a string corresponding + to the working directory. Return 1 on success, 0 on failure. */ + +static int +change_to_directory (newdir, nolinks) + char *newdir; + int nolinks; +{ + char *t, *tdir; + int err, canon_failed, r, ndlen, dlen; + + tdir = (char *)NULL; + + if (the_current_working_directory == 0) + { + t = get_working_directory ("chdir"); + FREE (t); + } + + t = make_absolute (newdir, the_current_working_directory); + + /* TDIR is either the canonicalized absolute pathname of NEWDIR + (nolinks == 0) or the absolute physical pathname of NEWDIR + (nolinks != 0). */ + tdir = nolinks ? sh_physpath (t, 0) + : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); + + ndlen = strlen (newdir); + dlen = strlen (t); + + /* Use the canonicalized version of NEWDIR, or, if canonicalization + failed, use the non-canonical form. */ + canon_failed = 0; + if (tdir && *tdir) + free (t); + else + { + FREE (tdir); + tdir = t; + canon_failed = 1; + } + + /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath + returns NULL (because it checks the path, it will return NULL if the + resolved path doesn't exist), fail immediately. */ + if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX)) + { +#if defined ENAMETOOLONG + if (errno != ENOENT && errno != ENAMETOOLONG) +#else + if (errno != ENOENT) +#endif + errno = ENOTDIR; + free (tdir); + return (0); + } + + /* If the chdir succeeds, update the_current_working_directory. */ + if (chdir (nolinks ? newdir : tdir) == 0) + { + /* If canonicalization failed, but the chdir succeeded, reset the + shell's idea of the_current_working_directory. */ + if (canon_failed) + { + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); + } + else + set_working_directory (tdir); + + free (tdir); + return (1); + } + + /* We failed to change to the appropriate directory name. If we tried + what the user passed (nolinks != 0), punt now. */ + if (nolinks) + { + free (tdir); + return (0); + } + + err = errno; + + /* We're not in physical mode (nolinks == 0), but we failed to change to + the canonicalized directory name (TDIR). Try what the user passed + verbatim. If we succeed, reinitialize the_current_working_directory. */ + if (chdir (newdir) == 0) + { + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); + else + free (t); + + r = 1; + } + else + { + errno = err; + r = 0; + } + + free (tdir); + return r; +} + +/* Code for cd spelling correction. Original patch submitted by + Neil Russel (caret@c-side.com). */ + +static char * +cdspell (dirname) + char *dirname; +{ + int n; + char *guess; + + n = (strlen (dirname) * 3 + 1) / 2 + 1; + guess = (char *)xmalloc (n); + + switch (spname (dirname, guess)) + { + case -1: + default: + free (guess); + return (char *)NULL; + case 0: + case 1: + return guess; + } +} diff --git a/builtins/common.c b/builtins/common.c index 9d8f09ab4..7b9613e1f 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -467,9 +467,6 @@ char * get_working_directory (for_whom) char *for_whom; { - char *directory; - size_t dsize; - if (no_symbolic_links) { FREE (the_current_working_directory); diff --git a/builtins/echo.def b/builtins/echo.def index 6792659a7..0effc8945 100644 --- a/builtins/echo.def +++ b/builtins/echo.def @@ -31,6 +31,8 @@ $PRODUCES echo.c #include #include "../shell.h" +#include "common.h" + $BUILTIN echo $FUNCTION echo_builtin $DEPENDS_ON V9_ECHO diff --git a/builtins/echo.def~ b/builtins/echo.def~ new file mode 100644 index 000000000..6792659a7 --- /dev/null +++ b/builtins/echo.def~ @@ -0,0 +1,183 @@ +This file is echo.def, from which is created echo.c. +It implements the builtin "echo" in Bash. + +Copyright (C) 1987-2002 Free Software Foundation, Inc. + +This file is part of GNU Bash, the Bourne Again SHell. + +Bash is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +$PRODUCES echo.c +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include "../bashansi.h" + +#include +#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 + \E escape character + \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 */ + +/* System V machines already have a /bin/sh with a v9 behaviour. We + give Bash the identical behaviour for these machines so that the + existing system shells won't barf. Regrettably, the SUS v2 has + standardized the Sys V echo behavior. This variable is external + so that we can have a `shopt' variable to control it at runtime. */ +#if defined (DEFAULT_ECHO_TO_XPG) || defined (STRICT_POSIX) +int xpg_echo = 1; +#else +int xpg_echo = 0; +#endif /* DEFAULT_ECHO_TO_XPG */ + +extern int posixly_correct; + +/* Print the words in LIST to standard output. If the first word is + `-n', then don't print a trailing newline. We also support the + echo syntax from Version 9 Unix systems. */ +int +echo_builtin (list) + WORD_LIST *list; +{ + int display_return, do_v9, i, len; + char *temp, *s; + + do_v9 = xpg_echo; + display_return = 1; + + if (posixly_correct && xpg_echo) + goto just_echo; + + for (; list && (temp = list->word->word) && *temp == '-'; list = list->next) + { + /* If it appears that we are handling options, then make sure that + all of the options specified are actually valid. Otherwise, the + string should just be echoed. */ + temp++; + + for (i = 0; temp[i]; i++) + { + if (strchr (VALID_ECHO_OPTIONS, temp[i]) == 0) + break; + } + + /* echo - and echo - both mean to just echo the arguments. */ + if (*temp == 0 || temp[i]) + break; + + /* All of the options in TEMP are valid options to ECHO. + Handle them. */ + while (i = *temp++) + { + switch (i) + { + case 'n': + display_return = 0; + break; +#if defined (V9_ECHO) + case 'e': + do_v9 = 1; + break; + case 'E': + do_v9 = 0; + break; +#endif /* V9_ECHO */ + default: + goto just_echo; /* XXX */ + } + } + } + +just_echo: + + clearerr (stdout); /* clear error before writing and testing success */ + + while (list) + { + i = len = 0; + temp = do_v9 ? ansicstr (list->word->word, STRLEN (list->word->word), 1, &i, &len) + : list->word->word; + if (temp) + { + if (do_v9) + { + for (s = temp; len > 0; len--) + putchar (*s++); + } + else + printf ("%s", temp); +#if defined (SunOS5) + fflush (stdout); /* Fix for bug in SunOS 5.5 printf(3) */ +#endif + } + if (do_v9 && temp) + free (temp); + list = list->next; + if (i) + { + display_return = 0; + break; + } + if (list) + putchar(' '); + } + + if (display_return) + putchar ('\n'); + fflush (stdout); + if (ferror (stdout)) + { + sh_wrerror (); + clearerr (stdout); + return (EXECUTION_FAILURE); + } + return (EXECUTION_SUCCESS); +} diff --git a/builtins/evalfile.c b/builtins/evalfile.c index 81be017bd..d05bc7bb3 100644 --- a/builtins/evalfile.c +++ b/builtins/evalfile.c @@ -63,7 +63,7 @@ extern int errno; #define FEVAL_NOPUSHARGS 0x100 extern int posixly_correct; -extern int indirection_level, startup_state, subshell_environment; +extern int indirection_level, subshell_environment; extern int return_catch_flag, return_catch_value; extern int last_command_exit_value; diff --git a/builtins/evalfile.c~ b/builtins/evalfile.c~ index 75314e2bf..81be017bd 100644 --- a/builtins/evalfile.c~ +++ b/builtins/evalfile.c~ @@ -201,7 +201,6 @@ file_error_and_exit: #if defined (ARRAY_VARS) array_push (bash_source_a, (char *)filename); t = itos (executing_line_number ()); -itrace("evalfile: pushing %s to bash_lineno array"); array_push (bash_lineno_a, t); free (t); array_push (funcname_a, "source"); /* not exactly right */ diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 04afac3d7..36f0ad398 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -56,7 +56,7 @@ extern int errno; #define IS_BUILTIN(s) (builtin_address_internal(s, 0) != (struct builtin *)NULL) -extern int indirection_level, startup_state, subshell_environment; +extern int indirection_level, subshell_environment; extern int line_number; extern int last_command_exit_value; extern int running_trap; @@ -316,9 +316,8 @@ static int cat_file (r) REDIRECT *r; { - char lbuf[128], *fn; + char *fn; int fd, rval; - ssize_t nr; if (r->instruction != r_input_direction) return -1; diff --git a/builtins/evalstring.c~ b/builtins/evalstring.c~ new file mode 100644 index 000000000..610fa9caf --- /dev/null +++ b/builtins/evalstring.c~ @@ -0,0 +1,352 @@ +/* Evaluate a string as one or more shell commands. + + Copyright (C) 1996-2005 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include +#include + +#include + +#include "filecntl.h" +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "../builtins.h" +#include "../flags.h" +#include "../input.h" +#include "../execute_cmd.h" +#include "../redir.h" +#include "../trap.h" + +#if defined (HISTORY) +# include "../bashhist.h" +#endif + +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +#define IS_BUILTIN(s) (builtin_address_internal(s, 0) != (struct builtin *)NULL) + +extern int indirection_level, startup_state, subshell_environment; +extern int line_number; +extern int last_command_exit_value; +extern int running_trap; +extern int loop_level; +extern int posixly_correct; + +int parse_and_execute_level = 0; + +static int cat_file __P((REDIRECT *)); + +/* How to force parse_and_execute () to clean up after itself. */ +void +parse_and_execute_cleanup () +{ + if (running_trap) + { + run_trap_cleanup (running_trap - 1); + unfreeze_jobs_list (); + } + run_unwind_frame ("parse_and_execute_top"); +} + +/* Parse and execute the commands in STRING. Returns whatever + execute_command () returns. This frees STRING. FLAGS is a + flags word; look in common.h for the possible values. Actions + are: + (flags & SEVAL_NONINT) -> interactive = 0; + (flags & SEVAL_INTERACT) -> interactive = 1; + (flags & SEVAL_NOHIST) -> call bash_history_disable () + (flags & SEVAL_NOFREE) -> don't free STRING when finished + (flags & SEVAL_RESETLINE) -> reset line_number to 1 +*/ + +int +parse_and_execute (string, from_file, flags) + char *string; + const char *from_file; + int flags; +{ + int code, x, lreset; + volatile int should_jump_to_top_level, last_result; + char *orig_string; + COMMAND *volatile command; + + orig_string = string; + /* Unwind protect this invocation of parse_and_execute (). */ + begin_unwind_frame ("parse_and_execute_top"); + unwind_protect_int (parse_and_execute_level); + unwind_protect_jmp_buf (top_level); + unwind_protect_int (indirection_level); + unwind_protect_int (line_number); + unwind_protect_int (loop_level); + if (flags & (SEVAL_NONINT|SEVAL_INTERACT)) + unwind_protect_int (interactive); + + lreset = flags & SEVAL_RESETLINE; + +#if defined (HISTORY) + unwind_protect_int (remember_on_history); /* can be used in scripts */ +# if defined (BANG_HISTORY) + if (interactive_shell) + { + unwind_protect_int (history_expansion_inhibited); + } +# endif /* BANG_HISTORY */ +#endif /* HISTORY */ + + if (interactive_shell) + { + x = get_current_prompt_level (); + add_unwind_protect (set_current_prompt_level, x); + } + + add_unwind_protect (pop_stream, (char *)NULL); + if (orig_string && ((flags & SEVAL_NOFREE) == 0)) + add_unwind_protect (xfree, orig_string); + end_unwind_frame (); + + parse_and_execute_level++; + + /* Reset the line number if the caller wants us to. If we don't reset the + line number, we have to subtract one, because we will add one just + before executing the next command (resetting the line number sets it to + 0; the first line number is 1). */ + push_stream (lreset); + if (lreset == 0) + line_number--; + + indirection_level++; + if (flags & (SEVAL_NONINT|SEVAL_INTERACT)) + interactive = (flags & SEVAL_NONINT) ? 0 : 1; + +#if defined (HISTORY) + if (flags & SEVAL_NOHIST) + bash_history_disable (); +#endif /* HISTORY */ + + code = should_jump_to_top_level = 0; + last_result = EXECUTION_SUCCESS; + + with_input_from_string (string, from_file); + while (*(bash_input.location.string)) + { + command = (COMMAND *)NULL; + + if (interrupt_state) + { + last_result = EXECUTION_FAILURE; + break; + } + + /* Provide a location for functions which `longjmp (top_level)' to + jump to. This prevents errors in substitution from restarting + the reader loop directly, for example. */ + code = setjmp (top_level); + + if (code) + { + should_jump_to_top_level = 0; + switch (code) + { + case FORCE_EOF: + case ERREXIT: + case EXITPROG: + if (command) + run_unwind_frame ("pe_dispose"); + /* Remember to call longjmp (top_level) after the old + value for it is restored. */ + should_jump_to_top_level = 1; + goto out; + + case DISCARD: + if (command) + run_unwind_frame ("pe_dispose"); + last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */ + if (subshell_environment) + { + should_jump_to_top_level = 1; + goto out; + } + else + { +#if 0 + dispose_command (command); /* pe_dispose does this */ +#endif + continue; + } + + default: + command_error ("parse_and_execute", CMDERR_BADJUMP, code, 0); + break; + } + } + + if (parse_command () == 0) + { + if (interactive_shell == 0 && read_but_dont_execute) + { + last_result = EXECUTION_SUCCESS; + dispose_command (global_command); + global_command = (COMMAND *)NULL; + } + else if (command = global_command) + { + struct fd_bitmap *bitmap; + + bitmap = new_fd_bitmap (FD_BITMAP_SIZE); + begin_unwind_frame ("pe_dispose"); + add_unwind_protect (dispose_fd_bitmap, bitmap); + add_unwind_protect (dispose_command, command); /* XXX */ + + global_command = (COMMAND *)NULL; + +#if defined (ONESHOT) + /* + * IF + * we were invoked as `bash -c' (startup_state == 2) AND + * parse_and_execute has not been called recursively AND + * we're not running a trap AND + * we have parsed the full command (string == '\0') AND + * we have a simple command without redirections AND + * the command is not being timed AND + * the command's return status is not being inverted + * THEN + * tell the execution code that we don't need to fork + */ + if (startup_state == 2 && parse_and_execute_level == 1 && + running_trap == 0 && + *bash_input.location.string == '\0' && + command->type == cm_simple && + !command->redirects && !command->value.Simple->redirects && + ((command->flags & CMD_TIME_PIPELINE) == 0) && + ((command->flags & CMD_INVERT_RETURN) == 0)) + { + command->flags |= CMD_NO_FORK; + command->value.Simple->flags |= CMD_NO_FORK; + } +#endif /* ONESHOT */ + + /* See if this is a candidate for $( type == cm_simple && !command->redirects && + (command->flags & CMD_TIME_PIPELINE) == 0 && + command->value.Simple->words == 0 && + command->value.Simple->redirects && + command->value.Simple->redirects->next == 0 && + command->value.Simple->redirects->instruction == r_input_direction) + { + int r; + r = cat_file (command->value.Simple->redirects); + last_result = (r < 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + } + else + last_result = execute_command_internal + (command, 0, NO_PIPE, NO_PIPE, bitmap); + + dispose_command (command); + dispose_fd_bitmap (bitmap); + discard_unwind_frame ("pe_dispose"); + } + } + else + { + last_result = EXECUTION_FAILURE; + + /* Since we are shell compatible, syntax errors in a script + abort the execution of the script. Right? */ + break; + } + } + + out: + + run_unwind_frame ("parse_and_execute_top"); + + if (interrupt_state && parse_and_execute_level == 0) + { + /* An interrupt during non-interactive execution in an + interactive shell (e.g. via $PROMPT_COMMAND) should + not cause the shell to exit. */ + interactive = interactive_shell; + throw_to_top_level (); + } + + if (should_jump_to_top_level) + jump_to_top_level (code); + + return (last_result); +} + +/* Handle a $( < file ) command substitution. This expands the filename, + returning errors as appropriate, then just cats the file to the standard + output. */ +static int +cat_file (r) + REDIRECT *r; +{ + char *fn; + int fd, rval; + + if (r->instruction != r_input_direction) + return -1; + + /* Get the filename. */ + if (posixly_correct && !interactive_shell) + disallow_filename_globbing++; + fn = redirection_expand (r->redirectee.filename); + if (posixly_correct && !interactive_shell) + disallow_filename_globbing--; + + if (fn == 0) + { + redirection_error (r, AMBIGUOUS_REDIRECT); + return -1; + } + + fd = open(fn, O_RDONLY); + if (fd < 0) + { + file_error (fn); + free (fn); + return -1; + } + + rval = zcatfd (fd, 1, fn); + + free (fn); + close (fd); + + return (rval); +} diff --git a/builtins/fc.def b/builtins/fc.def index ebe368326..101eb008c 100644 --- a/builtins/fc.def +++ b/builtins/fc.def @@ -81,7 +81,6 @@ $END extern int errno; #endif /* !errno */ -extern int echo_input_at_read; extern int current_command_line_count; extern int literal_history; extern int posixly_correct; diff --git a/builtins/fc.def~ b/builtins/fc.def~ new file mode 100644 index 000000000..ebe368326 --- /dev/null +++ b/builtins/fc.def~ @@ -0,0 +1,631 @@ +This file is fc.def, from which is created fc.c. +It implements the builtin "fc" in Bash. + +Copyright (C) 1987-2005 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, 59 Temple Place, Suite 330, Boston, MA 02111 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] +fc is used to list or edit and re-execute commands from the history list. +FIRST and LAST can be numbers specifying the range, or FIRST can be a +string, which means the most recent command beginning with that +string. + + -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, + 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 + +#if defined (HISTORY) +#ifndef _MINIX +# include +#endif +#include "../bashtypes.h" +#include "posixstat.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include +#endif + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include +#include + +#include "../bashansi.h" +#include "../bashintl.h" +#include + +#include "../shell.h" +#include "../builtins.h" +#include "../flags.h" +#include "../bashhist.h" +#include "maxpath.h" +#include +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int echo_input_at_read; +extern int current_command_line_count; +extern int literal_history; +extern int posixly_correct; + +extern int unlink __P((const char *)); + +extern FILE *sh_mktmpfp __P((char *, int, char **)); +extern int delete_last_history __P((void)); + +/* **************************************************************** */ +/* */ +/* The K*rn shell style fc command (Fix Command) */ +/* */ +/* **************************************************************** */ + +/* fc builtin command (fix command) for Bash for those who + like K*rn-style history better than csh-style. + + fc [-e ename] [-nlr] [first] [last] + + FIRST and LAST can be numbers specifying the range, or FIRST can be + a string, which means the most recent command beginning with that + string. + + -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, + then the editor which corresponds to the current readline editing + mode, then vi. + + -l means list lines instead of editing. + -n means no line numbers listed. + -r means reverse the order of the lines (making it newest listed first). + + fc -e - [pat=rep ...] [command] + fc -s [pat=rep ...] [command] + + Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's. +*/ + +/* Data structure describing a list of global replacements to perform. */ +typedef struct repl { + struct repl *next; + char *pat; + char *rep; +} REPL; + +/* Accessors for HIST_ENTRY lists that are called HLIST. */ +#define histline(i) (hlist[(i)]->line) +#define histdata(i) (hlist[(i)]->data) + +#define FREE_RLIST() \ + do { \ + for (rl = rlist; rl; ) { \ + REPL *r; \ + r = rl->next; \ + if (rl->pat) \ + free (rl->pat); \ + if (rl->rep) \ + free (rl->rep); \ + free (rl); \ + rl = r; \ + } \ + } while (0) + +static char *fc_dosubs __P((char *, REPL *)); +static char *fc_gethist __P((char *, HIST_ENTRY **)); +static int fc_gethnum __P((char *, HIST_ENTRY **)); +static int fc_number __P((WORD_LIST *)); +static void fc_replhist __P((char *)); +#ifdef INCLUDE_UNUSED +static char *fc_readline __P((FILE *)); +static void fc_addhist __P((char *)); +#endif + +/* String to execute on a file that we want to edit. */ +#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}" +#if defined (STRICT_POSIX) +# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}" +#else +# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}" +#endif + +int +fc_builtin (list) + WORD_LIST *list; +{ + register int i; + register char *sep; + int numbering, reverse, listing, execute; + int histbeg, histend, last_hist, retval, opt; + FILE *stream; + REPL *rlist, *rl; + char *ename, *command, *newcom, *fcedit; + HIST_ENTRY **hlist; + char *fn; + + numbering = 1; + reverse = listing = execute = 0; + ename = (char *)NULL; + + /* Parse out the options and set which of the two forms we're in. */ + reset_internal_getopt (); + lcurrent = list; /* XXX */ + while (fc_number (loptend = lcurrent) == 0 && + (opt = internal_getopt (list, ":e:lnrs")) != -1) + { + switch (opt) + { + case 'n': + numbering = 0; + break; + + case 'l': + listing = 1; + break; + + case 'r': + reverse = 1; + break; + + case 's': + execute = 1; + break; + + case 'e': + ename = list_optarg; + break; + + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + if (ename && (*ename == '-') && (ename[1] == '\0')) + execute = 1; + + /* The "execute" form of the command (re-run, with possible string + substitutions). */ + if (execute) + { + rlist = (REPL *)NULL; + while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL)) + { + *sep++ = '\0'; + rl = (REPL *)xmalloc (sizeof (REPL)); + rl->next = (REPL *)NULL; + rl->pat = savestring (list->word->word); + rl->rep = savestring (sep); + + if (rlist == NULL) + rlist = rl; + else + { + rl->next = rlist; + rlist = rl; + } + list = list->next; + } + + /* If we have a list of substitutions to do, then reverse it + to get the replacements in the proper order. */ + + rlist = REVERSE_LIST (rlist, REPL *); + + hlist = history_list (); + + /* If we still have something in list, it is a command spec. + Otherwise, we use the most recent command in time. */ + command = fc_gethist (list ? list->word->word : (char *)NULL, hlist); + + if (command == NULL) + { + builtin_error (_("no command found")); + if (rlist) + FREE_RLIST (); + + return (EXECUTION_FAILURE); + } + + if (rlist) + { + newcom = fc_dosubs (command, rlist); + free (command); + FREE_RLIST (); + command = newcom; + } + + fprintf (stderr, "%s\n", command); + fc_replhist (command); /* replace `fc -s' with command */ + return (parse_and_execute (command, "fc", SEVAL_NOHIST)); + } + + /* This is the second form of the command (the list-or-edit-and-rerun + form). */ + hlist = history_list (); + if (hlist == 0) + return (EXECUTION_SUCCESS); + for (i = 0; hlist[i]; i++); + + /* With the Bash implementation of history, the current command line + ("fc blah..." and so on) is already part of the history list by + the time we get to this point. This just skips over that command + and makes the last command that this deals with be the last command + the user entered before the fc. We need to check whether the + line was actually added (HISTIGNORE may have caused it to not be), + so we check hist_last_line_added. */ + + /* "When not listing, he fc command that caused the editing shall not be + entered into the history list." */ + if (listing == 0 && hist_last_line_added) + delete_last_history (); + + last_hist = i - 1 - hist_last_line_added; + + if (list) + { + histbeg = fc_gethnum (list->word->word, hlist); + list = list->next; + + if (list) + histend = fc_gethnum (list->word->word, hlist); + else + histend = listing ? last_hist : histbeg; + } + else + { + /* The default for listing is the last 16 history items. */ + if (listing) + { + histend = last_hist; + histbeg = histend - 16 + 1; /* +1 because loop below uses >= */ + if (histbeg < 0) + histbeg = 0; + } + else + /* For editing, it is the last history command. */ + histbeg = histend = last_hist; + } + + /* We print error messages for line specifications out of range. */ + if ((histbeg < 0) || (histend < 0)) + { + sh_erange ((char *)NULL, _("history specification")); + return (EXECUTION_FAILURE); + } + + if (histend < histbeg) + { + i = histend; + histend = histbeg; + histbeg = i; + + reverse = 1; + } + + if (listing) + stream = stdout; + else + { + numbering = 0; + stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn); + if (stream == 0) + { + builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno)); + FREE (fn); + return (EXECUTION_FAILURE); + } + } + + for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++) + { + QUIT; + if (numbering) + fprintf (stream, "%d", i + history_base); + if (listing) + { + if (posixly_correct) + fputs ("\t", stream); + else + fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); + } + fprintf (stream, "%s\n", histline (i)); + } + + if (listing) + return (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 + { + fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND; + command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn)); + sprintf (command, "%s %s", fcedit, fn); + } + retval = parse_and_execute (command, "fc", SEVAL_NOHIST); + if (retval != EXECUTION_SUCCESS) + { + unlink (fn); + free (fn); + return (EXECUTION_FAILURE); + } + + /* Make sure parse_and_execute doesn't turn this off, even though a + call to parse_and_execute farther up the function call stack (e.g., + if this is called by vi_edit_and_execute_command) may have already + called bash_history_disable. */ + remember_on_history = 1; + + /* Turn on the `v' flag while fc_execute_file runs so the commands + will be echoed as they are read by the parser. */ + begin_unwind_frame ("fc builtin"); + add_unwind_protect ((Function *)xfree, fn); + add_unwind_protect (unlink, fn); + unwind_protect_int (echo_input_at_read); + echo_input_at_read = 1; + + retval = fc_execute_file (fn); + + run_unwind_frame ("fc builtin"); + + return (retval); +} + +/* Return 1 if LIST->word->word is a legal number for fc's use. */ +static int +fc_number (list) + WORD_LIST *list; +{ + char *s; + + if (list == 0) + return 0; + s = list->word->word; + if (*s == '-') + s++; + return (legal_number (s, (intmax_t *)NULL)); +} + +/* Return an absolute index into HLIST which corresponds to COMMAND. If + COMMAND is a number, then it was specified in relative terms. If it + is a string, then it is the start of a command line present in HLIST. */ +static int +fc_gethnum (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int sign = 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. We need to check whether the + line was actually added (HISTIGNORE may have caused it to not be), + so we check hist_last_line_added. */ + i -= 1 + hist_last_line_added; + + /* No specification defaults to most recent command. */ + if (command == NULL) + return (i); + + /* Otherwise, there is a specification. It can be a number relative to + the current position, or an absolute history number. */ + s = command; + + /* Handle possible leading minus sign. */ + if (s && (*s == '-')) + { + sign = -1; + s++; + } + + if (s && DIGIT(*s)) + { + n = atoi (s); + n *= sign; + + /* If the value is negative or zero, then it is an offset from + the current history item. */ + if (n < 0) + { + n += i + 1; + return (n < 0 ? 0 : n); + } + else if (n == 0) + return (i); + else + { + n -= history_base; + return (i < n ? i : n); + } + } + + clen = strlen (command); + for (j = i; j >= 0; j--) + { + if (STREQN (command, histline (j), clen)) + return (j); + } + return (-1); +} + +/* Locate the most recent history line which begins with + COMMAND in HLIST, and return a malloc()'ed copy of it. */ +static char * +fc_gethist (command, hlist) + char *command; + HIST_ENTRY **hlist; +{ + int i; + + if (hlist == 0) + return ((char *)NULL); + + i = fc_gethnum (command, hlist); + + if (i >= 0) + return (savestring (histline (i))); + else + return ((char *)NULL); +} + +#ifdef INCLUDE_UNUSED +/* Read the edited history lines from STREAM and return them + one at a time. This can read unlimited length lines. The + caller should free the storage. */ +static char * +fc_readline (stream) + FILE *stream; +{ + register int c; + int line_len = 0, lindex = 0; + char *line = (char *)NULL; + + while ((c = getc (stream)) != EOF) + { + if ((lindex + 2) >= line_len) + line = (char *)xrealloc (line, (line_len += 128)); + + if (c == '\n') + { + line[lindex++] = '\n'; + line[lindex++] = '\0'; + return (line); + } + else + line[lindex++] = c; + } + + if (!lindex) + { + if (line) + free (line); + + return ((char *)NULL); + } + + if (lindex + 2 >= line_len) + line = (char *)xrealloc (line, lindex + 3); + + line[lindex++] = '\n'; /* Finish with newline if none in file */ + line[lindex++] = '\0'; + return (line); +} +#endif + +/* Perform the SUBS on COMMAND. + SUBS is a list of substitutions, and COMMAND is a simple string. + Return a pointer to a malloc'ed string which contains the substituted + command. */ +static char * +fc_dosubs (command, subs) + char *command; + REPL *subs; +{ + register char *new, *t; + register REPL *r; + + for (new = savestring (command), r = subs; r; r = r->next) + { + t = strsub (new, r->pat, r->rep, 1); + free (new); + new = t; + } + return (new); +} + +/* Use `command' to replace the last entry in the history list, which, + by this time, is `fc blah...'. The intent is that the new command + become the history entry, and that `fc' should never appear in the + history list. This way you can do `r' to your heart's content. */ +static void +fc_replhist (command) + char *command; +{ + int n; + + if (command == 0 || *command == '\0') + return; + + n = strlen (command); + if (command[n - 1] == '\n') + command[n - 1] = '\0'; + + if (command && *command) + { + delete_last_history (); + maybe_add_history (command); /* Obeys HISTCONTROL setting. */ + } +} + +#ifdef INCLUDE_UNUSED +/* Add LINE to the history, after removing a single trailing newline. */ +static void +fc_addhist (line) + char *line; +{ + register int n; + + if (line == 0 || *line == 0) + return; + + n = strlen (line); + + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + if (line && *line) + maybe_add_history (line); /* Obeys HISTCONTROL setting. */ +} +#endif + +#endif /* HISTORY */ diff --git a/builtins/printf.def b/builtins/printf.def index c2fa5c4ef..3658b48ae 100644 --- a/builtins/printf.def +++ b/builtins/printf.def @@ -108,6 +108,7 @@ extern int errno; (void)fputs (b, stdout); \ if (ferror (stdout)) \ { \ + sh_wrerror (); \ clearerr (stdout); \ return (EXECUTION_FAILURE); \ } \ @@ -826,7 +827,7 @@ vbadd (buf, blen) #ifdef DEBUG if (strlen (vbuf) != vblen) - internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, strlen (vbuf)); + internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, (int)strlen (vbuf)); #endif return vbuf; diff --git a/builtins/printf.def~ b/builtins/printf.def~ new file mode 100644 index 000000000..67e978ebd --- /dev/null +++ b/builtins/printf.def~ @@ -0,0 +1,1008 @@ +This file is printf.def, from which is created printf.c. +It implements the builtin "printf" in Bash. + +Copyright (C) 1997-2005 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + +$PRODUCES printf.c + +$BUILTIN printf +$FUNCTION printf_builtin +$SHORT_DOC printf [-v var] format [arguments] +printf formats and prints ARGUMENTS under control of the FORMAT. FORMAT +is a character string which contains three types of objects: plain +characters, which are simply copied to standard output, character escape +sequences which are converted and copied to the standard output, and +format specifications, each of which causes printing of the next successive +argument. In addition to the standard printf(1) formats, %b means to +expand backslash escape sequences in the corresponding argument, and %q +means to quote the argument in a way that can be reused as shell input. +If the -v option is supplied, the output is placed into the value of the +shell variable VAR rather than being sent to the standard output. +$END + +#include + +#include "../bashtypes.h" + +#include +#if defined (HAVE_LIMITS_H) +# include +#else + /* Assume 32-bit ints. */ +# define INT_MAX 2147483647 +# define INT_MIN (-2147483647-1) +#endif + +#include +#include + +#ifdef HAVE_INTTYPES_H +# include +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "stdc.h" +#include "bashgetopt.h" +#include "common.h" + +#if !defined (PRIdMAX) +# if HAVE_LONG_LONG +# define PRIdMAX "lld" +# else +# define PRIdMAX "ld" +# endif +#endif + +#if !defined (errno) +extern int errno; +#endif + +#define PC(c) \ + do { \ + char b[2]; \ + tw++; \ + b[0] = c; b[1] = '\0'; \ + if (vflag) \ + vbadd (b, 1); \ + else \ + putchar (c); \ + } while (0) + +#define PF(f, func) \ + do { \ + char *b = 0; \ + int nw; \ + if (have_fieldwidth && have_precision) \ + nw = asprintf(&b, f, fieldwidth, precision, func); \ + else if (have_fieldwidth) \ + nw = asprintf(&b, f, fieldwidth, func); \ + else if (have_precision) \ + nw = asprintf(&b, f, precision, func); \ + else \ + nw = asprintf(&b, f, func); \ + tw += nw; \ + if (b) \ + { \ + if (vflag) \ + (void)vbadd (b, nw); \ + else \ + (void)fputs (b, stdout); \ + if (ferror (stdout)) \ + { \ + sh_wrerror (); \ + clearerr (stdout); \ + return (EXECUTION_FAILURE); \ + } \ + free (b); \ + } \ + } while (0) + +/* We free the buffer used by mklong() if it's `too big'. */ +#define PRETURN(value) \ + do \ + { \ + if (vflag) \ + { \ + bind_variable (vname, vbuf, 0); \ + stupidly_hack_special_variables (vname); \ + } \ + if (conv_bufsize > 4096 ) \ + { \ + free (conv_buf); \ + conv_bufsize = 0; \ + conv_buf = 0; \ + } \ + if (vbsize > 4096) \ + { \ + free (vbuf); \ + vbsize = 0; \ + vbuf = 0; \ + } \ + fflush (stdout); \ + if (ferror (stdout)) \ + { \ + clearerr (stdout); \ + return (EXECUTION_FAILURE); \ + } \ + return (value); \ + } \ + while (0) + +#define SKIP1 "#'-+ 0" +#define LENMODS "hjlLtz" + +static void printf_erange __P((char *)); +static int printstr __P((char *, char *, int, int, int)); +static int tescape __P((char *, char *, int *)); +static char *bexpand __P((char *, int, int *, int *)); +static char *vbadd __P((char *, int)); +static char *mklong __P((char *, char *, size_t)); +static int getchr __P((void)); +static char *getstr __P((void)); +static int getint __P((void)); +static intmax_t getintmax __P((void)); +static uintmax_t getuintmax __P((void)); + +#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN) +typedef long double floatmax_t; +# define FLOATMAX_CONV "L" +# define strtofltmax strtold +#else +typedef double floatmax_t; +# define FLOATMAX_CONV "" +# define strtofltmax strtod +#endif +static floatmax_t getfloatmax __P((void)); + +static int asciicode __P((void)); + +static WORD_LIST *garglist; +static int retval; +static int conversion_error; + +/* printf -v var support */ +static int vflag = 0; +static char *vbuf, *vname; +static size_t vbsize; +static int vblen; + +static intmax_t tw; + +static char *conv_buf; +static size_t conv_bufsize; + +int +printf_builtin (list) + WORD_LIST *list; +{ + int ch, fieldwidth, precision; + int have_fieldwidth, have_precision; + char convch, thisch, nextch, *format, *modstart, *fmt, *start; + + conversion_error = 0; + retval = EXECUTION_SUCCESS; + + vflag = 0; + + reset_internal_getopt (); + while ((ch = internal_getopt (list, "v:")) != -1) + { + switch (ch) + { + case 'v': + if (legal_identifier (vname = list_optarg)) + { + vflag = 1; + vblen = 0; + } + else + { + sh_invalidid (vname); + return (EX_USAGE); + } + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; /* skip over possible `--' */ + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (list->word->word == 0 || list->word->word[0] == '\0') + return (EXECUTION_SUCCESS); + + format = list->word->word; + tw = 0; + + garglist = list->next; + + /* If the format string is empty after preprocessing, return immediately. */ + if (format == 0 || *format == 0) + return (EXECUTION_SUCCESS); + + /* Basic algorithm is to scan the format string for conversion + specifications -- once one is found, find out if the field + width or precision is a '*'; if it is, gather up value. Note, + format strings are reused as necessary to use up the provided + arguments, arguments of zero/null string are provided to use + up the format string. */ + do + { + tw = 0; + /* find next format specification */ + for (fmt = format; *fmt; fmt++) + { + precision = fieldwidth = 0; + have_fieldwidth = have_precision = 0; + + if (*fmt == '\\') + { + fmt++; + /* A NULL third argument to tescape means to bypass the + special processing for arguments to %b. */ + fmt += tescape (fmt, &nextch, (int *)NULL); + PC (nextch); + fmt--; /* for loop will increment it for us again */ + continue; + } + + if (*fmt != '%') + { + PC (*fmt); + continue; + } + + /* ASSERT(*fmt == '%') */ + start = fmt++; + + if (*fmt == '%') /* %% prints a % */ + { + PC ('%'); + continue; + } + + /* found format specification, skip to field width */ + for (; *fmt && strchr(SKIP1, *fmt); ++fmt) + ; + + /* Skip optional field width. */ + if (*fmt == '*') + { + fmt++; + have_fieldwidth = 1; + fieldwidth = getint (); + } + else + while (DIGIT (*fmt)) + fmt++; + + /* Skip optional '.' and precision */ + if (*fmt == '.') + { + ++fmt; + if (*fmt == '*') + { + fmt++; + have_precision = 1; + precision = getint (); + } + else + { + /* Negative precisions are allowed but treated as if the + precision were missing; I would like to allow a leading + `+' in the precision number as an extension, but lots + of asprintf/fprintf implementations get this wrong. */ +#if 0 + if (*fmt == '-' || *fmt == '+') +#else + if (*fmt == '-') +#endif + fmt++; + while (DIGIT (*fmt)) + fmt++; + } + } + + /* skip possible format modifiers */ + modstart = fmt; + while (*fmt && strchr (LENMODS, *fmt)) + fmt++; + + if (*fmt == 0) + { + builtin_error (_("`%s': missing format character"), start); + PRETURN (EXECUTION_FAILURE); + } + + convch = *fmt; + thisch = modstart[0]; + nextch = modstart[1]; + modstart[0] = convch; + modstart[1] = '\0'; + + switch(convch) + { + case 'c': + { + char p; + + p = getchr (); + PF(start, p); + break; + } + + case 's': + { + char *p; + + p = getstr (); + PF(start, p); + break; + } + + case 'n': + { + char *var; + + var = getstr (); + if (var && *var) + { + if (legal_identifier (var)) + bind_var_to_int (var, tw); + else + { + sh_invalidid (var); + PRETURN (EXECUTION_FAILURE); + } + } + break; + } + + case 'b': /* expand escapes in argument */ + { + char *p, *xp; + int rlen, r; + + p = getstr (); + ch = rlen = r = 0; + xp = bexpand (p, strlen (p), &ch, &rlen); + + if (xp) + { + /* Have to use printstr because of possible NUL bytes + in XP -- printf does not handle that well. */ + r = printstr (start, xp, rlen, fieldwidth, precision); + if (r < 0) + { + sh_wrerror (); + clearerr (stdout); + retval = EXECUTION_FAILURE; + } + free (xp); + } + + if (ch || r < 0) + PRETURN (retval); + break; + } + + case 'q': /* print with shell quoting */ + { + char *p, *xp; + int r; + + r = 0; + p = getstr (); + if (ansic_shouldquote (p)) + xp = ansic_quote (p, 0, (int *)0); + else + xp = sh_backslash_quote (p); + if (xp) + { + /* Use printstr to get fieldwidth and precision right. */ + r = printstr (start, xp, strlen (xp), fieldwidth, precision); + if (r < 0) + { + sh_wrerror (); + clearerr (stdout); + } + free (xp); + } + + if (r < 0) + PRETURN (EXECUTION_FAILURE); + break; + } + + case 'd': + case 'i': + { + char *f; + long p; + intmax_t pp; + + p = pp = getintmax (); + if (p != pp) + { + f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); + PF (f, pp); + } + else + { + /* Optimize the common case where the integer fits + in "long". This also works around some long + long and/or intmax_t library bugs in the common + case, e.g. glibc 2.2 x86. */ + f = mklong (start, "l", 1); + PF (f, p); + } + break; + } + + case 'o': + case 'u': + case 'x': + case 'X': + { + char *f; + unsigned long p; + uintmax_t pp; + + p = pp = getuintmax (); + if (p != pp) + { + f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); + PF (f, pp); + } + else + { + f = mklong (start, "l", 1); + PF (f, p); + } + break; + } + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': +#if defined (HAVE_PRINTF_A_FORMAT) + case 'a': + case 'A': +#endif + { + char *f; + floatmax_t p; + + p = getfloatmax (); + f = mklong (start, FLOATMAX_CONV, sizeof(FLOATMAX_CONV) - 1); + PF (f, p); + break; + } + + /* We don't output unrecognized format characters; we print an + error message and return a failure exit status. */ + default: + builtin_error (_("`%c': invalid format character"), convch); + PRETURN (EXECUTION_FAILURE); + } + + modstart[0] = thisch; + modstart[1] = nextch; + } + + if (ferror (stdout)) + { + sh_wrerror (); + clearerr (stdout); + PRETURN (EXECUTION_FAILURE); + } + } + while (garglist && garglist != list->next); + + if (conversion_error) + retval = EXECUTION_FAILURE; + + PRETURN (retval); +} + +static void +printf_erange (s) + char *s; +{ + builtin_error ("warning: %s: %s", s, strerror(ERANGE)); +} + +/* We duplicate a lot of what printf(3) does here. */ +static int +printstr (fmt, string, len, fieldwidth, precision) + char *fmt; /* format */ + char *string; /* expanded string argument */ + int len; /* length of expanded string */ + int fieldwidth; /* argument for width of `*' */ + int precision; /* argument for precision of `*' */ +{ +#if 0 + char *s; +#endif + int padlen, nc, ljust, i; + int fw, pr; /* fieldwidth and precision */ + +#if 0 + if (string == 0 || *string == '\0') +#else + if (string == 0 || len == 0) +#endif + return; + +#if 0 + s = fmt; +#endif + if (*fmt == '%') + fmt++; + + ljust = fw = 0; + pr = -1; + + /* skip flags */ + while (strchr (SKIP1, *fmt)) + { + if (*fmt == '-') + ljust = 1; + fmt++; + } + + /* get fieldwidth, if present */ + if (*fmt == '*') + { + fmt++; + fw = fieldwidth; + if (fw < 0) + { + fw = -fw; + ljust = 1; + } + } + else if (DIGIT (*fmt)) + { + fw = *fmt++ - '0'; + while (DIGIT (*fmt)) + fw = (fw * 10) + (*fmt++ - '0'); + } + + /* get precision, if present */ + if (*fmt == '.') + { + fmt++; + if (*fmt == '*') + { + fmt++; + pr = precision; + } + else if (DIGIT (*fmt)) + { + pr = *fmt++ - '0'; + while (DIGIT (*fmt)) + pr = (pr * 10) + (*fmt++ - '0'); + } + } + +#if 0 + /* If we remove this, get rid of `s'. */ + if (*fmt != 'b' && *fmt != 'q') + { + internal_error ("format parsing problem: %s", s); + fw = pr = 0; + } +#endif + + /* chars from string to print */ + nc = (pr >= 0 && pr <= len) ? pr : len; + + padlen = fw - nc; + if (padlen < 0) + padlen = 0; + if (ljust) + padlen = -padlen; + + /* leading pad characters */ + for (; padlen > 0; padlen--) + PC (' '); + + /* output NC characters from STRING */ + for (i = 0; i < nc; i++) + PC (string[i]); + + /* output any necessary trailing padding */ + for (; padlen < 0; padlen++) + PC (' '); + + return (ferror (stdout) ? -1 : 0); +} + +/* Convert STRING by expanding the escape sequences specified by the + POSIX standard for printf's `%b' format string. If SAWC is non-null, + perform the processing appropriate for %b arguments. In particular, + recognize `\c' and use that as a string terminator. If we see \c, set + *SAWC to 1 before returning. LEN is the length of STRING. */ + +/* Translate a single backslash-escape sequence starting at ESTART (the + character after the backslash) and return the number of characters + consumed by the sequence. CP is the place to return the translated + value. *SAWC is set to 1 if the escape sequence was \c, since that means + to short-circuit the rest of the processing. If SAWC is null, we don't + do the \c short-circuiting, and \c is treated as an unrecognized escape + sequence; we also bypass the other processing specific to %b arguments. */ +static int +tescape (estart, cp, sawc) + char *estart; + char *cp; + int *sawc; +{ + register char *p; + int temp, c, evalue; + + p = estart; + + switch (c = *p++) + { +#if defined (__STDC__) + case 'a': *cp = '\a'; break; +#else + case 'a': *cp = '\007'; break; +#endif + + case 'b': *cp = '\b'; break; + + case 'e': + case 'E': *cp = '\033'; break; /* ESC -- non-ANSI */ + + case 'f': *cp = '\f'; break; + + case 'n': *cp = '\n'; break; + + case 'r': *cp = '\r'; break; + + case 't': *cp = '\t'; break; + + case 'v': *cp = '\v'; break; + + /* The octal escape sequences are `\0' followed by up to three octal + digits (if SAWC), or `\' followed by up to three octal digits (if + !SAWC). As an extension, we allow the latter form even if SAWC. */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + evalue = OCTVALUE (c); + for (temp = 2 + (!evalue && !!sawc); ISOCTAL (*p) && temp--; p++) + evalue = (evalue * 8) + OCTVALUE (*p); + *cp = evalue & 0xFF; + break; + + /* And, as another extension, we allow \xNNN, where each N is a + hex digit. */ + case 'x': +#if 0 + for (evalue = 0; ISXDIGIT ((unsigned char)*p); p++) +#else + for (temp = 2, evalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++) +#endif + evalue = (evalue * 16) + HEXVALUE (*p); + if (p == estart + 1) + { + builtin_error (_("missing hex digit for \\x")); + *cp = '\\'; + return 0; + } + *cp = evalue & 0xFF; + break; + + case '\\': /* \\ -> \ */ + *cp = c; + break; + + /* SAWC == 0 means that \', \", and \? are recognized as escape + sequences, though the only processing performed is backslash + removal. */ + case '\'': case '"': case '?': + if (!sawc) + *cp = c; + else + { + *cp = '\\'; + return 0; + } + break; + + case 'c': + if (sawc) + { + *sawc = 1; + break; + } + /* other backslash escapes are passed through unaltered */ + default: + *cp = '\\'; + return 0; + } + return (p - estart); +} + +static char * +bexpand (string, len, sawc, lenp) + char *string; + int len, *sawc, *lenp; +{ + int temp; + char *ret, *r, *s, c; + +#if 0 + if (string == 0 || *string == '\0') +#else + if (string == 0 || len == 0) +#endif + { + if (sawc) + *sawc = 0; + if (lenp) + *lenp = 0; + return ((char *)NULL); + } + + ret = (char *)xmalloc (len + 1); + for (r = ret, s = string; s && *s; ) + { + c = *s++; + if (c != '\\' || *s == '\0') + { + *r++ = c; + continue; + } + temp = 0; + s += tescape (s, &c, &temp); + if (temp) + { + if (sawc) + *sawc = 1; + break; + } + + *r++ = c; + } + + *r = '\0'; + if (lenp) + *lenp = r - ret; + return ret; +} + +static char * +vbadd (buf, blen) + char *buf; + int blen; +{ + size_t nlen; + + nlen = vblen + blen + 1; + if (nlen >= vbsize) + { + vbsize = ((nlen + 63) >> 6) << 6; + vbuf = (char *)xrealloc (vbuf, vbsize); + } + + if (blen == 1) + vbuf[vblen++] = buf[0]; + else + { + FASTCOPY (buf, vbuf + vblen, blen); + vblen += blen; + } + vbuf[vblen] = '\0'; + +#ifdef DEBUG + if (strlen (vbuf) != vblen) + internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, strlen (vbuf)); +#endif + + return vbuf; +} + +static char * +mklong (str, modifiers, mlen) + char *str; + char *modifiers; + size_t mlen; +{ + size_t len, slen; + + slen = strlen (str); + len = slen + mlen + 1; + + if (len > conv_bufsize) + { + conv_bufsize = (((len + 1023) >> 10) << 10); + conv_buf = (char *)xrealloc (conv_buf, conv_bufsize); + } + + FASTCOPY (str, conv_buf, slen - 1); + FASTCOPY (modifiers, conv_buf + slen - 1, mlen); + + conv_buf[len - 2] = str[slen - 1]; + conv_buf[len - 1] = '\0'; + return (conv_buf); +} + +static int +getchr () +{ + int ret; + + if (garglist == 0) + return ('\0'); + + ret = (int)garglist->word->word[0]; + garglist = garglist->next; + return ret; +} + +static char * +getstr () +{ + char *ret; + + if (garglist == 0) + return (""); + + ret = garglist->word->word; + garglist = garglist->next; + return ret; +} + +static int +getint () +{ + intmax_t ret; + + ret = getintmax (); + + if (ret > INT_MAX) + { + printf_erange (garglist->word->word); + ret = INT_MAX; + } + else if (ret < INT_MIN) + { + printf_erange (garglist->word->word); + ret = INT_MIN; + } + + return ((int)ret); +} + +static intmax_t +getintmax () +{ + intmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtoimax (garglist->word->word, &ep, 0); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* POSIX.2 says ``...a diagnostic message shall be written to standard + error, and the utility shall not exit with a zero exit status, but + shall continue processing any remaining operands and shall write the + value accumulated at the time the error was detected to standard + output.'' Yecch. */ + ret = 0; + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +static uintmax_t +getuintmax () +{ + uintmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtoumax (garglist->word->word, &ep, 0); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* Same POSIX.2 conversion error requirements as getintmax(). */ + ret = 0; + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +static floatmax_t +getfloatmax () +{ + floatmax_t ret; + char *ep; + + if (garglist == 0) + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtofltmax (garglist->word->word, &ep); + + if (*ep) + { + sh_invalidnum (garglist->word->word); + /* Same thing about POSIX.2 conversion error requirements. */ + ret = 0; + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + + garglist = garglist->next; + return (ret); +} + +/* NO check is needed for garglist here. */ +static int +asciicode () +{ + register int ch; + + ch = garglist->word->word[1]; + garglist = garglist->next; + return (ch); +} diff --git a/builtins/set.def b/builtins/set.def index 3bb327040..d1086695a 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -578,7 +578,6 @@ set_builtin (list) WORD_LIST *list; { int on_or_off, flag_name, force_assignment, opts_changed; - WORD_LIST *l; register char *arg; char s[3]; diff --git a/builtins/source.def b/builtins/source.def index f9f812f89..9576f09e1 100644 --- a/builtins/source.def +++ b/builtins/source.def @@ -68,9 +68,7 @@ $END extern int errno; #endif /* !errno */ -#if defined (RESTRICTED_SHELL) -extern int restricted; -#endif +static void maybe_pop_dollar_vars __P((void)); /* If non-zero, `.' uses $PATH to look up the script to be sourced. */ int source_uses_path = 1; diff --git a/builtins/source.def~ b/builtins/source.def~ new file mode 100644 index 000000000..f9f812f89 --- /dev/null +++ b/builtins/source.def~ @@ -0,0 +1,174 @@ +This file is source.def, from which is created source.c. +It implements the builtins "." and "source" in Bash. + +Copyright (C) 1987-2003 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +$PRODUCES source.c + +$BUILTIN source +$FUNCTION source_builtin +$SHORT_DOC source filename [arguments] +Read and execute commands from FILENAME and return. The pathnames +in $PATH are used to find the directory containing FILENAME. If any +ARGUMENTS are supplied, they become the positional parameters when +FILENAME is executed. +$END +$BUILTIN . +$DOCNAME dot +$FUNCTION source_builtin +$SHORT_DOC . filename [arguments] +Read and execute commands from FILENAME and return. The pathnames +in $PATH are used to find the directory containing FILENAME. If any +ARGUMENTS are supplied, they become the positional parameters when +FILENAME is executed. +$END +/* source.c - Implements the `.' and `source' builtins. */ + +#include + +#include "../bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# include +#endif +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include "../bashansi.h" +#include "../bashintl.h" + +#include "../shell.h" +#include "../flags.h" +#include "../findcmd.h" +#include "common.h" +#include "bashgetopt.h" +#include "../trap.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if defined (RESTRICTED_SHELL) +extern int restricted; +#endif + +/* If non-zero, `.' uses $PATH to look up the script to be sourced. */ +int source_uses_path = 1; + +/* If non-zero, `.' looks in the current directory if the filename argument + is not found in the $PATH. */ +int source_searches_cwd = 1; + +/* If this . script is supplied arguments, we save the dollar vars and + replace them with the script arguments for the duration of the script's + execution. If the script does not change the dollar vars, we restore + what we saved. If the dollar vars are changed in the script, and we are + not executing a shell function, we leave the new values alone and free + the saved values. */ +static void +maybe_pop_dollar_vars () +{ + if (variable_context == 0 && (dollar_vars_changed () & ARGS_SETBLTIN)) + dispose_saved_dollar_vars (); + else + pop_dollar_vars (); + if (debugging_mode) + pop_args (); /* restore BASH_ARGC and BASH_ARGV */ + set_dollar_vars_unchanged (); +} + +/* Read and execute commands from the file passed as argument. Guess what. + This cannot be done in a subshell, since things like variable assignments + take place in there. So, I open the file, place it into a large string, + close the file, and then execute the string. */ +int +source_builtin (list) + WORD_LIST *list; +{ + int result; + char *filename, *debug_trap; + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + if (list == 0) + { + builtin_error (_("filename argument required")); + builtin_usage (); + return (EX_USAGE); + } + +#if defined (RESTRICTED_SHELL) + if (restricted && strchr (list->word->word, '/')) + { + sh_restricted (list->word->word); + return (EXECUTION_FAILURE); + } +#endif + + filename = (char *)NULL; + if (source_uses_path) + filename = find_path_file (list->word->word); + if (filename == 0) + { + if (source_searches_cwd == 0) + { + builtin_error (_("%s: file not found"), list->word->word); + return (EXECUTION_FAILURE); + } + else + filename = savestring (list->word->word); + } + + begin_unwind_frame ("source"); + add_unwind_protect ((Function *)xfree, filename); + + if (list->next) + { + push_dollar_vars (); + add_unwind_protect ((Function *)maybe_pop_dollar_vars, (char *)NULL); + remember_args (list->next, 1); + if (debugging_mode) + push_args (list->next); /* Update BASH_ARGV and BASH_ARGC */ + } + set_dollar_vars_unchanged (); + + /* Don't inherit the DEBUG trap unless function_trace_mode (overloaded) + is set. XXX - should sourced files inherit the RETURN trap? Functions + don't. */ + debug_trap = TRAP_STRING (DEBUG_TRAP); + if (debug_trap && function_trace_mode == 0) + { + debug_trap = savestring (debug_trap); + add_unwind_protect (xfree, debug_trap); + add_unwind_protect (set_debug_trap, debug_trap); + restore_default_signal (DEBUG_TRAP); + } + + result = source_file (filename, (list && list->next)); + + run_unwind_frame ("source"); + + return (result); +} diff --git a/builtins/suspend.def b/builtins/suspend.def index d616d775b..ea86ae22c 100644 --- a/builtins/suspend.def +++ b/builtins/suspend.def @@ -48,13 +48,15 @@ $END #include "common.h" #include "bashgetopt.h" +static sighandler suspend_continue __P((int)); + static SigHandler *old_cont; #if 0 static SigHandler *old_stop; #endif /* Continue handler. */ -sighandler +static sighandler suspend_continue (sig) int sig; { diff --git a/builtins/suspend.def~ b/builtins/suspend.def~ new file mode 100644 index 000000000..d616d775b --- /dev/null +++ b/builtins/suspend.def~ @@ -0,0 +1,119 @@ +This file is suspend.def, from which is created suspend.c. +It implements the builtin "suspend" in Bash. + +Copyright (C) 1987-2003 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, 59 Temple Place, Suite 330, Boston, MA 02111 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 + +#if defined (JOB_CONTROL) +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include "../bashtypes.h" +#include +#include "../bashintl.h" +#include "../shell.h" +#include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" + +static SigHandler *old_cont; +#if 0 +static SigHandler *old_stop; +#endif + +/* Continue handler. */ +sighandler +suspend_continue (sig) + int sig; +{ + set_signal_handler (SIGCONT, old_cont); +#if 0 + set_signal_handler (SIGSTOP, old_stop); +#endif + SIGRETURN (0); +} + +/* Suspending the shell. If -f is the arg, then do the suspend + no matter what. Otherwise, complain if a login shell. */ +int +suspend_builtin (list) + WORD_LIST *list; +{ + int opt, force; + + reset_internal_getopt (); + force = 0; + while ((opt = internal_getopt (list, "f")) != -1) + switch (opt) + { + case 'f': + force++; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + + list = loptend; + + if (job_control == 0) + { + sh_nojobs (_("cannot suspend")); + return (EXECUTION_FAILURE); + } + + if (force == 0) + { + no_args (list); + + if (login_shell) + { + builtin_error (_("cannot suspend a login shell")); + return (EXECUTION_FAILURE); + } + } + + /* XXX - should we put ourselves back into the original pgrp now? If so, + call end_job_control() here and do the right thing in suspend_continue + (that is, call restart_job_control()). */ + old_cont = (SigHandler *)set_signal_handler (SIGCONT, suspend_continue); +#if 0 + old_stop = (SigHandler *)set_signal_handler (SIGSTOP, SIG_DFL); +#endif + killpg (shell_pgrp, SIGSTOP); + return (EXECUTION_SUCCESS); +} + +#endif /* JOB_CONTROL */ diff --git a/builtins/trap.def b/builtins/trap.def index 426833a15..2735791e4 100644 --- a/builtins/trap.def +++ b/builtins/trap.def @@ -87,7 +87,7 @@ int trap_builtin (list) WORD_LIST *list; { - int list_signal_names, display, result, opt, first_signal; + int list_signal_names, display, result, opt; list_signal_names = display = 0; result = EXECUTION_SUCCESS; diff --git a/builtins/wait.def b/builtins/wait.def index 22a92bea0..a30959528 100644 --- a/builtins/wait.def +++ b/builtins/wait.def @@ -59,7 +59,6 @@ $END #include "common.h" #include "bashgetopt.h" -extern int interrupt_immediately; extern int wait_signal_received; procenv_t wait_intr_buf; diff --git a/builtins/wait.def~ b/builtins/wait.def~ new file mode 100644 index 000000000..22a92bea0 --- /dev/null +++ b/builtins/wait.def~ @@ -0,0 +1,177 @@ +This file is wait.def, from which is created wait.c. +It implements the builtin "wait" in Bash. + +Copyright (C) 1987-2005 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, 59 Temple Place, Suite 330, Boston, MA 02111 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 + +#include "../bashtypes.h" +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include + +#include "../bashansi.h" + +#include "../shell.h" +#include "../jobs.h" +#include "common.h" +#include "bashgetopt.h" + +extern int interrupt_immediately; +extern int wait_signal_received; + +procenv_t wait_intr_buf; + +/* Wait for the pid in LIST to stop or die. If no arguments are given, then + wait for all of the active background processes of the shell and return + 0. If a list of pids or job specs are given, return the exit status of + the last one waited for. */ + +#define WAIT_RETURN(s) \ + do \ + { \ + interrupt_immediately = old_interrupt_immediately;\ + return (s);\ + } \ + while (0) + +int +wait_builtin (list) + WORD_LIST *list; +{ + int status, code; + volatile int old_interrupt_immediately; + + USE_VAR(list); + + if (no_options (list)) + return (EX_USAGE); + list = loptend; + + old_interrupt_immediately = interrupt_immediately; + interrupt_immediately++; + + /* POSIX.2 says: When the shell is waiting (by means of the wait utility) + for asynchronous commands to complete, the reception of a signal for + which a trap has been set shall cause the wait utility to return + immediately with an exit status greater than 128, after which the trap + associated with the signal shall be taken. + + We handle SIGINT here; it's the only one that needs to be treated + specially (I think), since it's handled specially in {no,}jobs.c. */ + code = setjmp (wait_intr_buf); + if (code) + { + status = 128 + wait_signal_received; + WAIT_RETURN (status); + } + + /* We support jobs or pids. + wait [pid-or-job ...] */ + + /* But wait without any arguments means to wait for all of the shell's + currently active background processes. */ + if (list == 0) + { + wait_for_background_pids (); + WAIT_RETURN (EXECUTION_SUCCESS); + } + + status = EXECUTION_SUCCESS; + while (list) + { + pid_t pid; + char *w; + intmax_t pid_value; + + w = list->word->word; + if (DIGIT (*w)) + { + if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value) + { + pid = (pid_t)pid_value; + status = wait_for_single_pid (pid); + } + else + { + sh_badpid (w); + WAIT_RETURN (EXECUTION_FAILURE); + } + } +#if defined (JOB_CONTROL) + else if (*w && *w == '%') + /* Must be a job spec. Check it out. */ + { + int job; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (INVALID_JOB (job)) + { + if (job != DUP_JOB) + sh_badjob (list->word->word); + UNBLOCK_CHILD (oset); + status = 127; /* As per Posix.2, section 4.70.2 */ + list = list->next; + continue; + } + + /* Job spec used. Wait for the last pid in the pipeline. */ + UNBLOCK_CHILD (oset); + status = wait_for_job (job); + } +#endif /* JOB_CONTROL */ + else + { + sh_badpid (w); + status = EXECUTION_FAILURE; + } + list = list->next; + } + + WAIT_RETURN (status); +} diff --git a/error.c b/error.c index edb0e2e9a..83f6a7a9f 100644 --- a/error.c +++ b/error.c @@ -52,8 +52,6 @@ extern int errno; extern int executing_line_number __P((void)); -extern int interactive_shell, interactive, startup_state; -extern char *dollar_vars[]; extern char *shell_name; #if defined (JOB_CONTROL) extern pid_t shell_pgrp; diff --git a/error.c~ b/error.c~ new file mode 100644 index 000000000..edb0e2e9a --- /dev/null +++ b/error.c~ @@ -0,0 +1,454 @@ +/* error.c -- Functions for handling errors. */ +/* Copyright (C) 1993-2003 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include "config.h" + +#include "bashtypes.h" +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#if defined (PREFER_STDARG) +# include +#else +# include +#endif + +#include + +#include +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#include "bashansi.h" +#include "bashintl.h" + +#include "shell.h" +#include "flags.h" +#include "input.h" + +#if defined (HISTORY) +# include "bashhist.h" +#endif + +extern int executing_line_number __P((void)); + +extern int interactive_shell, interactive, startup_state; +extern char *dollar_vars[]; +extern char *shell_name; +#if defined (JOB_CONTROL) +extern pid_t shell_pgrp; +extern int give_terminal_to __P((pid_t, int)); +#endif /* JOB_CONTROL */ + +#if defined (ARRAY_VARS) +extern char *bash_badsub_errmsg; +#endif + +static void error_prolog __P((int)); + +/* The current maintainer of the shell. You change this in the + Makefile. */ +#if !defined (MAINTAINER) +#define MAINTAINER "bash-maintainers@gnu.org" +#endif + +char *the_current_maintainer = MAINTAINER; + +int gnu_error_format = 0; + +static void +error_prolog (print_lineno) + int print_lineno; +{ + char *ename; + int line; + + ename = get_name_for_error (); + line = (print_lineno && interactive_shell == 0) ? executing_line_number () : -1; + + if (line > 0) + fprintf (stderr, "%s:%s%d: ", ename, gnu_error_format ? "" : " line ", line); + else + fprintf (stderr, "%s: ", ename); +} + +/* Return the name of the shell or the shell script for error reporting. */ +char * +get_name_for_error () +{ + char *name; +#if defined (ARRAY_VARS) + SHELL_VAR *bash_source_v; + ARRAY *bash_source_a; +#endif + + name = (char *)NULL; + if (interactive_shell == 0) + { +#if defined (ARRAY_VARS) + bash_source_v = find_variable ("BASH_SOURCE"); + if (bash_source_v && array_p (bash_source_v) && + (bash_source_a = array_cell (bash_source_v))) + name = array_reference (bash_source_a, 0); + if (name == 0) +#endif + name = dollar_vars[0]; + } + if (name == 0 && shell_name && *shell_name) + name = base_pathname (shell_name); + if (name == 0) +#if defined (PROGRAM) + name = PROGRAM; +#else + name = "bash"; +#endif + + return (name); +} + +/* Report an error having to do with FILENAME. This does not use + sys_error so the filename is not interpreted as a printf-style + format string. */ +void +file_error (filename) + const char *filename; +{ + report_error ("%s: %s", filename, strerror (errno)); +} + +void +#if defined (PREFER_STDARG) +programming_error (const char *format, ...) +#else +programming_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + char *h; + +#if defined (JOB_CONTROL) + give_terminal_to (shell_pgrp, 0); +#endif /* JOB_CONTROL */ + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + va_end (args); + +#if defined (HISTORY) + if (remember_on_history) + { + h = last_history_line (); + fprintf (stderr, _("last command: %s\n"), h ? h : "(null)"); + } +#endif + +#if 0 + fprintf (stderr, "Report this to %s\n", the_current_maintainer); +#endif + + fprintf (stderr, _("Aborting...")); + fflush (stderr); + + abort (); +} + +/* Print an error message and, if `set -e' has been executed, exit the + shell. Used in this file by file_error and programming_error. Used + outside this file mostly to report substitution and expansion errors, + and for bad invocation options. */ +void +#if defined (PREFER_STDARG) +report_error (const char *format, ...) +#else +report_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + error_prolog (1); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); + if (exit_immediately_on_error) + exit_shell (1); +} + +void +#if defined (PREFER_STDARG) +fatal_error (const char *format, ...) +#else +fatal_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + error_prolog (0); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); + sh_exit (2); +} + +void +#if defined (PREFER_STDARG) +internal_error (const char *format, ...) +#else +internal_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + error_prolog (1); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); +} + +void +#if defined (PREFER_STDARG) +internal_warning (const char *format, ...) +#else +internal_warning (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + fprintf (stderr, _("%s: warning: "), get_name_for_error ()); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); +} + +void +#if defined (PREFER_STDARG) +sys_error (const char *format, ...) +#else +sys_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + int e; + va_list args; + + e = errno; + error_prolog (0); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, ": %s\n", strerror (e)); + + va_end (args); +} + +/* An error from the parser takes the general form + + shell_name: input file name: line number: message + + The input file name and line number are omitted if the shell is + currently interactive. If the shell is not currently interactive, + the input file name is inserted only if it is different from the + shell name. */ +void +#if defined (PREFER_STDARG) +parser_error (int lineno, const char *format, ...) +#else +parser_error (lineno, format, va_alist) + int lineno; + const char *format; + va_dcl +#endif +{ + va_list args; + char *ename, *iname; + + ename = get_name_for_error (); + iname = yy_input_name (); + + if (interactive) + fprintf (stderr, "%s: ", ename); + else if (interactive_shell) + fprintf (stderr, "%s: %s:%s%d: ", ename, iname, gnu_error_format ? "" : " line ", lineno); + else if (STREQ (ename, iname)) + fprintf (stderr, "%s:%s%d: ", ename, gnu_error_format ? "" : " line ", lineno); + else + fprintf (stderr, "%s: %s:%s%d: ", ename, iname, gnu_error_format ? "" : " line ", lineno); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); + + if (exit_immediately_on_error) + exit_shell (2); +} + +#ifdef DEBUG +void +#if defined (PREFER_STDARG) +itrace (const char *format, ...) +#else +itrace (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + fprintf(stderr, "TRACE: pid %ld: ", (long)getpid()); + + SH_VA_START (args, format); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + + va_end (args); + + fflush(stderr); +} + +/* A trace function for silent debugging -- doesn't require a control + terminal. */ +void +#if defined (PREFER_STDARG) +trace (const char *format, ...) +#else +trace (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + static FILE *tracefp = (FILE *)NULL; + + if (tracefp == NULL) + tracefp = fopen("/tmp/bash-trace.log", "a+"); + + if (tracefp == NULL) + tracefp = stderr; + else + fcntl (fileno (tracefp), F_SETFD, 1); /* close-on-exec */ + + fprintf(tracefp, "TRACE: pid %ld: ", (long)getpid()); + + SH_VA_START (args, format); + + vfprintf (tracefp, format, args); + fprintf (tracefp, "\n"); + + va_end (args); + + fflush(tracefp); +} + +#endif /* DEBUG */ + +/* **************************************************************** */ +/* */ +/* Common error reporting */ +/* */ +/* **************************************************************** */ + + +static char *cmd_error_table[] = { + N_("unknown command error"), /* CMDERR_DEFAULT */ + N_("bad command type"), /* CMDERR_BADTYPE */ + N_("bad connector"), /* CMDERR_BADCONN */ + N_("bad jump"), /* CMDERR_BADJUMP */ + 0 +}; + +void +command_error (func, code, e, flags) + const char *func; + int code, e, flags; /* flags currently unused */ +{ + if (code > CMDERR_LAST) + code = CMDERR_DEFAULT; + + programming_error ("%s: %s: %d", func, _(cmd_error_table[code]), e); +} + +char * +command_errstr (code) + int code; +{ + if (code > CMDERR_LAST) + code = CMDERR_DEFAULT; + + return (_(cmd_error_table[code])); +} + +#ifdef ARRAY_VARS +void +err_badarraysub (s) + const char *s; +{ + report_error ("%s: %s", s, _(bash_badsub_errmsg)); +} +#endif + +void +err_unboundvar (s) + const char *s; +{ + report_error (_("%s: unbound variable"), s); +} + +void +err_readonly (s) + const char *s; +{ + report_error (_("%s: readonly variable"), s); +} diff --git a/externs.h b/externs.h index 6c05ba306..bc28eda2f 100644 --- a/externs.h +++ b/externs.h @@ -112,6 +112,7 @@ extern void set_default_locale __P((void)); extern void set_default_locale_vars __P((void)); extern int set_locale_var __P((char *, char *)); extern int set_lang __P((char *, char *)); +extern void set_default_lang __P((void)); extern char *get_locale_var __P((char *)); extern char *localetrans __P((char *, int, int *)); extern char *mk_msgstr __P((char *, int *)); @@ -254,7 +255,7 @@ extern int strcasecmp __P((const char *, const char *)); #endif /* HAVE_STRCASECMP */ /* declarations for functions defined in lib/sh/strerror.c */ -#if !defined (strerror) +#if !defined (HAVE_STRERROR) && !defined (strerror) extern char *strerror __P((int)); #endif diff --git a/externs.h~ b/externs.h~ index 094d92455..d2cd6fb83 100644 --- a/externs.h~ +++ b/externs.h~ @@ -112,6 +112,7 @@ extern void set_default_locale __P((void)); extern void set_default_locale_vars __P((void)); extern int set_locale_var __P((char *, char *)); extern int set_lang __P((char *, char *)); +extern void set_default_lang __P((void)); extern char *get_locale_var __P((char *)); extern char *localetrans __P((char *, int, int *)); extern char *mk_msgstr __P((char *, int *)); @@ -225,6 +226,9 @@ extern char *sh_realpath __P((const char *, char *)); extern int sh_setlinebuf __P((FILE *)); #endif +/* declarations for functions defined in lib/sh/shaccess.c */ +extern int sh_eaccess __P((char *, int)); + /* declarations for functions defined in lib/sh/shmatch.c */ extern int sh_regmatch __P((const char *, const char *, int)); diff --git a/general.c b/general.c index 20dbd4443..4ac4de041 100644 --- a/general.c +++ b/general.c @@ -48,7 +48,6 @@ extern int errno; #endif /* !errno */ extern int expand_aliases; -extern int interrupt_immediately; extern int interactive_comments; extern int check_hashed_filenames; extern int source_uses_path; diff --git a/general.c~ b/general.c~ index 3384e84b9..20dbd4443 100644 --- a/general.c~ +++ b/general.c~ @@ -477,8 +477,13 @@ check_binary_file (sample, sample_len) if (c == '\n') return (0); +#if 0 if (ISSPACE (c) == 0 && ISPRINT (c) == 0) +#else + if (c == '\0') +#endif return (1); + } return (0); @@ -503,7 +508,7 @@ int file_iswdir (fn) char *fn; { - return (file_isdir (fn) && test_eaccess (fn, W_OK) == 0); + return (file_isdir (fn) && sh_eaccess (fn, W_OK) == 0); } /* Return 1 if STRING contains an absolute pathname, else 0. Used by `cd' diff --git a/input.c b/input.c index f6af14c27..7933da2f2 100644 --- a/input.c +++ b/input.c @@ -47,6 +47,8 @@ extern int errno; #endif /* !errno */ +extern void termsig_handler __P((int)); + /* Functions to handle reading input on systems that don't restart read(2) if a signal is received. */ diff --git a/input.c~ b/input.c~ new file mode 100644 index 000000000..f6af14c27 --- /dev/null +++ b/input.c~ @@ -0,0 +1,625 @@ +/* input.c -- functions to perform buffered input with synchronization. */ + +/* 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 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include "config.h" + +#include "bashtypes.h" +#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H) +# include +#endif +#include "filecntl.h" +#include "posixstat.h" +#include +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include "bashansi.h" +#include "bashintl.h" + +#include "command.h" +#include "general.h" +#include "input.h" +#include "error.h" +#include "externs.h" +#include "quit.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* Functions to handle reading input on systems that don't restart read(2) + if a signal is received. */ + +static char localbuf[128]; +static int local_index = 0, local_bufused = 0; + +/* Posix and USG systems do not guarantee to restart read () if it is + interrupted by a signal. We do the read ourselves, and restart it + if it returns EINTR. */ +int +getc_with_restart (stream) + FILE *stream; +{ + unsigned char uc; + + CHECK_TERMSIG; + + /* Try local buffering to reduce the number of read(2) calls. */ + if (local_index == local_bufused || local_bufused == 0) + { + while (1) + { + CHECK_TERMSIG; + local_bufused = read (fileno (stream), localbuf, sizeof(localbuf)); + if (local_bufused > 0) + break; + else if (local_bufused == 0 || errno != EINTR) + { + local_index = 0; + return EOF; + } + } + local_index = 0; + } + uc = localbuf[local_index++]; + return uc; +} + +int +ungetc_with_restart (c, stream) + int c; + FILE *stream; +{ + if (local_index == 0 || c == EOF) + return EOF; + localbuf[--local_index] = c; + return c; +} + +#if defined (BUFFERED_INPUT) + +/* A facility similar to stdio, but input-only. */ + +#if defined (USING_BASH_MALLOC) +# define MAX_INPUT_BUFFER_SIZE 8176 +#else +# define MAX_INPUT_BUFFER_SIZE 8192 +#endif + +#if !defined (SEEK_CUR) +# define SEEK_CUR 1 +#endif /* !SEEK_CUR */ + +#ifdef max +# undef max +#endif +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#ifdef min +# undef min +#endif +#define min(a, b) ((a) > (b) ? (b) : (a)) + +extern int interactive_shell; + +int bash_input_fd_changed; + +/* This provides a way to map from a file descriptor to the buffer + associated with that file descriptor, rather than just the other + way around. This is needed so that buffers are managed properly + in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the + correspondence is maintained. */ +static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL; +static int nbuffers; + +#define ALLOCATE_BUFFERS(n) \ + do { if ((n) >= nbuffers) allocate_buffers (n); } while (0) + +/* Make sure `buffers' has at least N elements. */ +static void +allocate_buffers (n) + int n; +{ + register int i, orig_nbuffers; + + orig_nbuffers = nbuffers; + nbuffers = n + 20; + buffers = (BUFFERED_STREAM **)xrealloc + (buffers, nbuffers * sizeof (BUFFERED_STREAM *)); + + /* Zero out the new buffers. */ + for (i = orig_nbuffers; i < nbuffers; i++) + buffers[i] = (BUFFERED_STREAM *)NULL; +} + +/* Construct and return a BUFFERED_STREAM corresponding to file descriptor + FD, using BUFFER. */ +static BUFFERED_STREAM * +make_buffered_stream (fd, buffer, bufsize) + int fd; + char *buffer; + size_t bufsize; +{ + BUFFERED_STREAM *bp; + + bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM)); + ALLOCATE_BUFFERS (fd); + buffers[fd] = bp; + bp->b_fd = fd; + bp->b_buffer = buffer; + bp->b_size = bufsize; + bp->b_used = bp->b_inputp = bp->b_flag = 0; + if (bufsize == 1) + bp->b_flag |= B_UNBUFF; + return (bp); +} + +/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */ +static BUFFERED_STREAM * +copy_buffered_stream (bp) + BUFFERED_STREAM *bp; +{ + BUFFERED_STREAM *nbp; + + if (!bp) + return ((BUFFERED_STREAM *)NULL); + + nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM)); + xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM)); + return (nbp); +} + +int +set_bash_input_fd (fd) + int fd; +{ + if (bash_input.type == st_bstream) + bash_input.location.buffered_fd = fd; + else if (interactive_shell == 0) + default_buffered_input = fd; + return 0; +} + +int +fd_is_bash_input (fd) + int fd; +{ + if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd) + return 1; + else if (interactive_shell == 0 && default_buffered_input == fd) + return 1; + return 0; +} + +/* Save the buffered stream corresponding to file descriptor FD (which bash + is using to read input) to a buffered stream associated with NEW_FD. If + NEW_FD is -1, a new file descriptor is allocated with fcntl. The new + file descriptor is returned on success, -1 on error. */ +int +save_bash_input (fd, new_fd) + int fd, new_fd; +{ + int nfd; + + /* Sync the stream so we can re-read from the new file descriptor. We + might be able to avoid this by copying the buffered stream verbatim + to the new file descriptor. */ + if (buffers[fd]) + sync_buffered_stream (fd); + + /* Now take care of duplicating the file descriptor that bash is + using for input, so we can reinitialize it later. */ + nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd; + if (nfd == -1) + { + if (fcntl (fd, F_GETFD, 0) == 0) + sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd); + return -1; + } + + if (buffers[nfd]) + { + /* What's this? A stray buffer without an associated open file + descriptor? Free up the buffer and report the error. */ + internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd); + free_buffered_stream (buffers[nfd]); + } + + /* Reinitialize bash_input.location. */ + if (bash_input.type == st_bstream) + { + bash_input.location.buffered_fd = nfd; + fd_to_buffered_stream (nfd); + close_buffered_fd (fd); /* XXX */ + } + else + /* If the current input type is not a buffered stream, but the shell + is not interactive and therefore using a buffered stream to read + input (e.g. with an `eval exec 3>output' inside a script), note + that the input fd has been changed. pop_stream() looks at this + value and adjusts the input fd to the new value of + default_buffered_input accordingly. */ + bash_input_fd_changed++; + + if (default_buffered_input == fd) + default_buffered_input = nfd; + + SET_CLOSE_ON_EXEC (nfd); + return nfd; +} + +/* Check that file descriptor FD is not the one that bash is currently + using to read input from a script. FD is about to be duplicated onto, + which means that the kernel will close it for us. If FD is the bash + input file descriptor, we need to seek backwards in the script (if + possible and necessary -- scripts read from stdin are still unbuffered), + allocate a new file descriptor to use for bash input, and re-initialize + the buffered stream. Make sure the file descriptor used to save bash + input is set close-on-exec. Returns 0 on success, -1 on failure. This + works only if fd is > 0 -- if fd == 0 and bash is reading input from + fd 0, save_bash_input is used instead, to cooperate with input + redirection (look at redir.c:add_undo_redirect()). */ +int +check_bash_input (fd) + int fd; +{ + if (fd_is_bash_input (fd)) + { + if (fd > 0) + return ((save_bash_input (fd, -1) == -1) ? -1 : 0); + else if (fd == 0) + return ((sync_buffered_stream (fd) == -1) ? -1 : 0); + } + return 0; +} + +/* This is the buffered stream analogue of dup2(fd1, fd2). The + BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists. + BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the + redirect code for constructs like 4<&0 and 3b_buffer && buffers[fd1]->b_buffer == buffers[fd2]->b_buffer) + buffers[fd2] = (BUFFERED_STREAM *)NULL; + else + free_buffered_stream (buffers[fd2]); + } + buffers[fd2] = copy_buffered_stream (buffers[fd1]); + if (buffers[fd2]) + buffers[fd2]->b_fd = fd2; + + if (is_bash_input) + { + if (!buffers[fd2]) + fd_to_buffered_stream (fd2); + buffers[fd2]->b_flag |= B_WASBASHINPUT; + } + + return (fd2); +} + +/* Return 1 if a seek on FD will succeed. */ +#ifndef __CYGWIN__ +# define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0) +#else +# define fd_is_seekable(fd) 0 +#endif /* __CYGWIN__ */ + +/* Take FD, a file descriptor, and create and return a buffered stream + corresponding to it. If something is wrong and the file descriptor + is invalid, return a NULL stream. */ +BUFFERED_STREAM * +fd_to_buffered_stream (fd) + int fd; +{ + char *buffer; + size_t size; + struct stat sb; + + if (fstat (fd, &sb) < 0) + { + close (fd); + return ((BUFFERED_STREAM *)NULL); + } + + size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1; + if (size == 0) + size = 1; + buffer = (char *)xmalloc (size); + + return (make_buffered_stream (fd, buffer, size)); +} + +/* Return a buffered stream corresponding to FILE, a file name. */ +BUFFERED_STREAM * +open_buffered_stream (file) + char *file; +{ + int fd; + + fd = open (file, O_RDONLY); + return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL); +} + +/* Deallocate a buffered stream and free up its resources. Make sure we + zero out the slot in BUFFERS that points to BP. */ +void +free_buffered_stream (bp) + BUFFERED_STREAM *bp; +{ + int n; + + if (!bp) + return; + + n = bp->b_fd; + if (bp->b_buffer) + free (bp->b_buffer); + free (bp); + buffers[n] = (BUFFERED_STREAM *)NULL; +} + +/* Close the file descriptor associated with BP, a buffered stream, and free + up the stream. Return the status of closing BP's file descriptor. */ +int +close_buffered_stream (bp) + BUFFERED_STREAM *bp; +{ + int fd; + + if (!bp) + return (0); + fd = bp->b_fd; + free_buffered_stream (bp); + return (close (fd)); +} + +/* Deallocate the buffered stream associated with file descriptor FD, and + close FD. Return the status of the close on FD. */ +int +close_buffered_fd (fd) + int fd; +{ + if (fd < 0) + { + errno = EBADF; + return -1; + } + if (fd >= nbuffers || !buffers || !buffers[fd]) + return (close (fd)); + return (close_buffered_stream (buffers[fd])); +} + +/* Make the BUFFERED_STREAM associcated with buffers[FD] be BP, and return + the old BUFFERED_STREAM. */ +BUFFERED_STREAM * +set_buffered_stream (fd, bp) + int fd; + BUFFERED_STREAM *bp; +{ + BUFFERED_STREAM *ret; + + ret = buffers[fd]; + buffers[fd] = bp; + return ret; +} + +/* Read a buffer full of characters from BP, a buffered stream. */ +static int +b_fill_buffer (bp) + BUFFERED_STREAM *bp; +{ + ssize_t nr; + + CHECK_TERMSIG; + nr = zread (bp->b_fd, bp->b_buffer, bp->b_size); + if (nr <= 0) + { + bp->b_used = 0; + bp->b_buffer[0] = 0; + if (nr == 0) + bp->b_flag |= B_EOF; + else + bp->b_flag |= B_ERROR; + return (EOF); + } + +#if defined (__CYGWIN__) + /* If on cygwin, translate \r\n to \n. */ + if (nr >= 2 && bp->b_buffer[nr - 2] == '\r' && bp->b_buffer[nr - 1] == '\n') + { + bp->b_buffer[nr - 2] = '\n'; + nr--; + } +#endif + + bp->b_used = nr; + bp->b_inputp = 0; + return (bp->b_buffer[bp->b_inputp++] & 0xFF); +} + +/* Get a character from buffered stream BP. */ +#define bufstream_getc(bp) \ + (bp->b_inputp == bp->b_used || !bp->b_used) \ + ? b_fill_buffer (bp) \ + : bp->b_buffer[bp->b_inputp++] & 0xFF + +/* Push C back onto buffered stream BP. */ +static int +bufstream_ungetc(c, bp) + int c; + BUFFERED_STREAM *bp; +{ + if (c == EOF || bp->b_inputp == 0) + return (EOF); + + bp->b_buffer[--bp->b_inputp] = c; + return (c); +} + +/* Seek backwards on file BFD to synchronize what we've read so far + with the underlying file pointer. */ +int +sync_buffered_stream (bfd) + int bfd; +{ + BUFFERED_STREAM *bp; + off_t chars_left; + + if (buffers == 0 || (bp = buffers[bfd]) == 0) + return (-1); + + chars_left = bp->b_used - bp->b_inputp; + if (chars_left) + lseek (bp->b_fd, -chars_left, SEEK_CUR); + bp->b_used = bp->b_inputp = 0; + return (0); +} + +int +buffered_getchar () +{ + CHECK_TERMSIG; + +#if !defined (DJGPP) + return (bufstream_getc (buffers[bash_input.location.buffered_fd])); +#else + /* On DJGPP, ignore \r. */ + int ch; + while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r') + ; + return ch; +#endif +} + +int +buffered_ungetchar (c) + int c; +{ + return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd])); +} + +/* Make input come from file descriptor BFD through a buffered stream. */ +void +with_input_from_buffered_stream (bfd, name) + int bfd; + char *name; +{ + INPUT_STREAM location; + BUFFERED_STREAM *bp; + + location.buffered_fd = bfd; + /* Make sure the buffered stream exists. */ + bp = fd_to_buffered_stream (bfd); + init_yy_io (bp == 0 ? return_EOF : buffered_getchar, + buffered_ungetchar, st_bstream, name, location); +} + +#if defined (TEST) +void * +xmalloc(s) +int s; +{ + return (malloc (s)); +} + +void * +xrealloc(s, size) +char *s; +int size; +{ + if (!s) + return(malloc (size)); + else + return(realloc (s, size)); +} + +void +init_yy_io () +{ +} + +process(bp) +BUFFERED_STREAM *bp; +{ + int c; + + while ((c = bufstream_getc(bp)) != EOF) + putchar(c); +} + +BASH_INPUT bash_input; + +struct stat dsb; /* can be used from gdb */ + +/* imitate /bin/cat */ +main(argc, argv) +int argc; +char **argv; +{ + register int i; + BUFFERED_STREAM *bp; + + if (argc == 1) { + bp = fd_to_buffered_stream (0); + process(bp); + exit(0); + } + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '\0') { + bp = fd_to_buffered_stream (0); + if (!bp) + continue; + process(bp); + free_buffered_stream (bp); + } else { + bp = open_buffered_stream (argv[i]); + if (!bp) + continue; + process(bp); + close_buffered_stream (bp); + } + } + exit(0); +} +#endif /* TEST */ +#endif /* BUFFERED_INPUT */ diff --git a/jobs.c b/jobs.c index 32df5a0a3..5e4b1f675 100644 --- a/jobs.c +++ b/jobs.c @@ -142,7 +142,6 @@ typedef int sh_job_map_func_t __P((JOB *, int, int, int)); /* Variables used here but defined in other files. */ extern int subshell_environment, line_number; extern int posixly_correct, shell_level; -extern int interrupt_immediately; extern int last_command_exit_value, last_command_exit_signal; extern int loop_level, breaking; extern int sourcelevel; @@ -761,7 +760,7 @@ bgp_search (pid) static void bgp_prune () { - struct pidstat *ps, *p; + struct pidstat *ps; while (bgpids.npid > js.c_childmax) { @@ -975,8 +974,7 @@ delete_job (job_index, dflags) { register JOB *temp; PROCESS *proc; - int ndel, status; - pid_t pid; + int ndel; if (js.j_jobslots == 0 || jobs_list_frozen) return; @@ -2827,14 +2825,14 @@ start_job (job, foreground) if (foreground) { pid_t pid; - int s; + int st; pid = find_last_pid (job, 0); UNBLOCK_CHILD (oset); - s = wait_for (pid); + st = wait_for (pid); shell_tty_info = save_stty; set_tty_state (); - return (s); + return (st); } else { diff --git a/jobs.c~ b/jobs.c~ index bdd0e0ab0..9a970cd90 100644 --- a/jobs.c~ +++ b/jobs.c~ @@ -142,7 +142,6 @@ typedef int sh_job_map_func_t __P((JOB *, int, int, int)); /* Variables used here but defined in other files. */ extern int subshell_environment, line_number; extern int posixly_correct, shell_level; -extern int interrupt_immediately; extern int last_command_exit_value, last_command_exit_signal; extern int loop_level, breaking; extern int sourcelevel; @@ -761,7 +760,7 @@ bgp_search (pid) static void bgp_prune () { - struct pidstat *ps, *p; + struct pidstat *ps; while (bgpids.npid > js.c_childmax) { @@ -975,8 +974,7 @@ delete_job (job_index, dflags) { register JOB *temp; PROCESS *proc; - int ndel, status; - pid_t pid; + int ndel; if (js.j_jobslots == 0 || jobs_list_frozen) return; @@ -1079,7 +1077,6 @@ add_process (name, pid) { PROCESS *t, *p; -itrace("add_process: name = %s running_trap = %d", name, running_trap); #if defined (RECYCLES_PIDS) int j; p = find_process (pid, 0, &j); @@ -1670,7 +1667,6 @@ make_child (command, async_p) sigemptyset (&oset); sigprocmask (SIG_BLOCK, &set, &oset); -itrace("make_child: command = %s", command ? command : ""); making_children (); #if defined (BUFFERED_INPUT) @@ -3436,7 +3432,6 @@ notify_of_job_status () fprintf (stderr, "\n"); if (dir == 0) dir = current_working_directory (); -itrace("calling pretty_print_job for job %d", job); pretty_print_job (job, JLIST_STANDARD, stderr); if (dir && (strcmp (dir, jobs[job]->wd) != 0)) fprintf (stderr, diff --git a/lib/glob/glob.c b/lib/glob/glob.c index 5c564f660..08a7da853 100644 --- a/lib/glob/glob.c +++ b/lib/glob/glob.c @@ -184,7 +184,7 @@ mbskipname (pat, dname) { int ret; wchar_t *pat_wc, *dn_wc; - size_t pat_n, dn_n, n; + size_t pat_n, dn_n; pat_n = xdupmbstowcs (&pat_wc, NULL, pat); dn_n = xdupmbstowcs (&dn_wc, NULL, dname); diff --git a/lib/glob/smatch.c b/lib/glob/smatch.c index 12fde3d0f..be4f927c7 100644 --- a/lib/glob/smatch.c +++ b/lib/glob/smatch.c @@ -247,7 +247,6 @@ rangecmp_wc (c1, c2) { static wchar_t s1[2] = { L' ', L'\0' }; static wchar_t s2[2] = { L' ', L'\0' }; - int ret; if (c1 == c2) return 0; diff --git a/lib/readline/bind.c b/lib/readline/bind.c index c40e4ef76..08c906bfc 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -1537,7 +1537,6 @@ rl_variable_value (name) const char *name; { register int i; - int v; /* Check for simple variables first. */ i = find_boolean_var (name); diff --git a/lib/readline/complete.c b/lib/readline/complete.c index e7f6af108..73f834a68 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -1104,7 +1104,8 @@ compute_lcd_of_matches (match_list, matches, text) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - mbstate_t ps_back = ps1; + mbstate_t ps_back; + ps_back = ps1; if (!_rl_compare_chars (match_list[i], si, &ps1, match_list[i+1], si, &ps2)) break; else if ((v = _rl_get_char_len (&match_list[i][si], &ps_back)) > 1) @@ -1896,7 +1897,6 @@ rl_filename_completion_function (text, state) static char *filename = (char *)NULL; static char *dirname = (char *)NULL; static char *users_dirname = (char *)NULL; - static char *orig_filename = (char *)NULL; static int filename_len; char *temp; int dirlen; diff --git a/lib/readline/complete.c~ b/lib/readline/complete.c~ index d188a262c..ad253b166 100644 --- a/lib/readline/complete.c~ +++ b/lib/readline/complete.c~ @@ -950,7 +950,7 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char) rl_compentry_func_t *our_func; int found_quote, quote_char; { - char **matches, *temp; + char **matches; rl_completion_found_quote = found_quote; rl_completion_quote_character = quote_char; @@ -969,28 +969,9 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char) } } - /* Beware -- we're stripping the quotes here. Do this only if we know - we are doing filename completion and the application has defined a - filename dequoting function. */ - /* XXX -- can move this into rl_filename_completion_function and use - rl_completion_found_quote and rl_completion_quote_char. Should be - after the directory rewriting hook and maybe the directory completion - hook. Also need to change bash_directory_expansion in the same way - as rl_filename_completion_function. */ - temp = (char *)NULL; - -#if 0 - if (found_quote && our_func == rl_filename_completion_function && - rl_filename_dequoting_function) - { - /* delete single and double quotes */ - temp = (*rl_filename_dequoting_function) (text, quote_char); - text = temp; /* not freeing text is not a memory leak */ - } -#endif + /* XXX -- filename dequoting moved into rl_filename_completion_function */ matches = rl_completion_matches (text, our_func); - FREE (temp); return matches; } diff --git a/lib/readline/display.c b/lib/readline/display.c index 253cf65f8..288b37bb4 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -2365,12 +2365,14 @@ _rl_col_width (str, start, end) int start, end; { wchar_t wc; - mbstate_t ps = {0}; + mbstate_t ps; int tmp, point, width, max; if (end <= start) return 0; + memset (&ps, 0, sizeof (mbstate_t)); + point = 0; max = end; diff --git a/lib/readline/display.c~ b/lib/readline/display.c~ index 5b48aab19..253cf65f8 100644 --- a/lib/readline/display.c~ +++ b/lib/readline/display.c~ @@ -1,6 +1,6 @@ /* display.c -- readline redisplay facility. */ -/* Copyright (C) 1987-2005 Free Software Foundation, Inc. +/* Copyright (C) 1987-2006 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -59,10 +59,6 @@ extern char *strchr (), *strrchr (); #endif /* !strchr && !__STDC__ */ -#if defined (HACK_TERMCAP_MOTION) -extern char *_rl_term_forward_char; -#endif - static void update_line PARAMS((char *, char *, int, int, int, int)); static void space_to_eol PARAMS((int)); static void delete_chars PARAMS((int)); @@ -80,7 +76,8 @@ static int *inv_lbreaks, *vis_lbreaks; static int inv_lbsize, vis_lbsize; /* Heuristic used to decide whether it is faster to move from CUR to NEW - by backing up or outputting a carriage return and moving forward. */ + by backing up or outputting a carriage return and moving forward. CUR + and NEW are either both buffer positions or absolute screen positions. */ #define CR_FASTER(new, cur) (((new) + 1) < ((cur) - (new))) /* _rl_last_c_pos is an absolute cursor position in multibyte locales and a @@ -143,6 +140,7 @@ int _rl_last_c_pos = 0; int _rl_last_v_pos = 0; static int cpos_adjusted; +static int cpos_buffer_position; /* Number of lines currently on screen minus 1. */ int _rl_vis_botlin = 0; @@ -460,7 +458,7 @@ rl_redisplay () { register int in, out, c, linenum, cursor_linenum; register char *line; - int c_pos, inv_botlin, lb_botlin, lb_linenum, o_cpos; + int inv_botlin, lb_botlin, lb_linenum, o_cpos; int newlines, lpos, temp, modmark, n0, num; char *prompt_this_line; #if defined (HANDLE_MULTIBYTE) @@ -484,7 +482,7 @@ rl_redisplay () } /* Draw the line into the buffer. */ - c_pos = -1; + cpos_buffer_position = -1; line = invisible_line; out = inv_botlin = 0; @@ -666,7 +664,7 @@ rl_redisplay () prompt_last_screen_line = newlines; /* Draw the rest of the line (after the prompt) into invisible_line, keeping - track of where the cursor is (c_pos), the number of the line containing + track of where the cursor is (cpos_buffer_position), the number of the line containing the cursor (lb_linenum), the last line number (lb_botlin and inv_botlin). It maintains an array of line breaks for display (inv_lbreaks). This handles expanding tabs for display and displaying meta characters. */ @@ -719,7 +717,7 @@ rl_redisplay () if (in == rl_point) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } @@ -813,7 +811,7 @@ rl_redisplay () } if (in == rl_point) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } for (i = in; i < in+wc_bytes; i++) @@ -844,9 +842,9 @@ rl_redisplay () } line[out] = '\0'; - if (c_pos < 0) + if (cpos_buffer_position < 0) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } @@ -855,7 +853,7 @@ rl_redisplay () inv_lbreaks[newlines+1] = out; cursor_linenum = lb_linenum; - /* C_POS == position in buffer where cursor should be placed. + /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed. CURSOR_LINENUM == line number where the cursor should be placed. */ /* PWP: now is when things get a bit hairy. The visible and invisible @@ -1011,7 +1009,7 @@ rl_redisplay () pos = inv_lbreaks[cursor_linenum]; /* nleft == number of characters in the line buffer between the start of the line and the desired cursor position. */ - nleft = c_pos - pos; + nleft = cpos_buffer_position - pos; /* NLEFT is now a number of characters in a buffer. When in a multibyte locale, however, _rl_last_c_pos is an absolute cursor @@ -1057,11 +1055,11 @@ rl_redisplay () will be LMARGIN. */ /* The number of characters that will be displayed before the cursor. */ - ndisp = c_pos - wrap_offset; + ndisp = cpos_buffer_position - wrap_offset; nleft = prompt_visible_length + wrap_offset; /* Where the new cursor position will be on the screen. This can be longer than SCREENWIDTH; if it is, lmargin will be adjusted. */ - phys_c_pos = c_pos - (last_lmargin ? last_lmargin : wrap_offset); + phys_c_pos = cpos_buffer_position - (last_lmargin ? last_lmargin : wrap_offset); t = _rl_screenwidth / 3; /* If the number of characters had already exceeded the screenwidth, @@ -1072,7 +1070,7 @@ rl_redisplay () two-thirds of the way across the screen. */ if (phys_c_pos > _rl_screenwidth - 2) { - lmargin = c_pos - (2 * t); + lmargin = cpos_buffer_position - (2 * t); if (lmargin < 0) lmargin = 0; /* If the left margin would be in the middle of a prompt with @@ -1086,7 +1084,7 @@ rl_redisplay () { /* If we are moving back towards the beginning of the line and the last margin is no longer correct, compute a new one. */ - lmargin = ((c_pos - 1) / t) * t; /* XXX */ + lmargin = ((cpos_buffer_position - 1) / t) * t; /* XXX */ if (wrap_offset && lmargin > 0 && lmargin < nleft) lmargin = nleft; } @@ -1131,7 +1129,7 @@ rl_redisplay () if (visible_first_line_len > _rl_screenwidth) visible_first_line_len = _rl_screenwidth; - _rl_move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); + _rl_move_cursor_relative (cpos_buffer_position - lmargin, &invisible_line[lmargin]); last_lmargin = lmargin; } } @@ -1724,13 +1722,12 @@ _rl_move_cursor_relative (new, data) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { dpos = _rl_col_width (data, 0, new); -#if 0 - if (dpos > woff) -#else - if (dpos > prompt_last_invisible) -#endif + if (dpos > prompt_last_invisible) /* XXX - don't use woff here */ { dpos -= woff; + /* Since this will be assigned to _rl_last_c_pos at the end (more + precisely, _rl_last_c_pos == dpos when this function returns), + let the caller know. */ cpos_adjusted = 1; } } @@ -1751,7 +1748,7 @@ _rl_move_cursor_relative (new, data) else #endif i = _rl_last_c_pos - woff; - if (new == 0 || CR_FASTER (new, _rl_last_c_pos) || + if (dpos == 0 || CR_FASTER (dpos, _rl_last_c_pos) || (_rl_term_autowrap && i == _rl_screenwidth)) { #if defined (__MSDOS__) @@ -1773,19 +1770,27 @@ _rl_move_cursor_relative (new, data) sequence telling the terminal to move forward one character. That kind of control is for people who don't know what the data is underneath the cursor. */ -#if defined (HACK_TERMCAP_MOTION) - if (_rl_term_forward_char) - { - for (i = cpos; i < dpos; i++) - tputs (_rl_term_forward_char, 1, _rl_output_character_function); - } - else -#endif /* HACK_TERMCAP_MOTION */ + + /* However, we need a handle on where the current display position is + in the buffer for the immediately preceding comment to be true. + In multibyte locales, we don't currently have that info available. + Without it, we don't know where the data we have to display begins + in the buffer and we have to go back to the beginning of the screen + line. In this case, we can use the terminal sequence to move forward + if it's available. */ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - tputs (_rl_term_cr, 1, _rl_output_character_function); - for (i = 0; i < new; i++) - putc (data[i], rl_outstream); + if (_rl_term_forward_char) + { + for (i = cpos; i < dpos; i++) + tputs (_rl_term_forward_char, 1, _rl_output_character_function); + } + else + { + tputs (_rl_term_cr, 1, _rl_output_character_function); + for (i = 0; i < new; i++) + putc (data[i], rl_outstream); + } } else for (i = cpos; i < new; i++) @@ -2213,7 +2218,8 @@ _rl_update_final () char *last_line; last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; - _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); + cpos_buffer_position = -1; /* don't know where we are in buffer */ + _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); /* XXX */ _rl_clear_to_eol (0); putc (last_line[_rl_screenwidth - 1], rl_outstream); } diff --git a/lib/readline/histexpand.c b/lib/readline/histexpand.c index 684701469..f46c0b2a4 100644 --- a/lib/readline/histexpand.c +++ b/lib/readline/histexpand.c @@ -56,8 +56,6 @@ typedef int _hist_search_func_t PARAMS((const char *, int)); -extern int rl_byte_oriented; /* declared in mbutil.c */ - static char error_pointer; static char *subst_lhs; @@ -564,12 +562,12 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - int c, l; + int ch, l; l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY); - c = string[l]; + ch = string[l]; /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ - if (i && (c == '\'' || c == '"')) - quoted_search_delimiter = c; + if (i && (ch == '\'' || ch == '"')) + quoted_search_delimiter = ch; } else #endif /* HANDLE_MULTIBYTE */ @@ -1430,6 +1428,8 @@ history_tokenize_word (string, ind) { if (peek == '<' && string[i + 2] == '-') i++; + else if (peek == '<' && string[i + 2] == '<') + i++; i += 2; return i; } diff --git a/lib/readline/histexpand.c~ b/lib/readline/histexpand.c~ new file mode 100644 index 000000000..dd9872628 --- /dev/null +++ b/lib/readline/histexpand.c~ @@ -0,0 +1,1593 @@ +/* histexpand.c -- history expansion. */ + +/* Copyright (C) 1989-2004 Free Software Foundation, Inc. + + This file contains the GNU History Library (the Library), a set of + routines for managing the text of previously typed lines. + + The Library 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. + + The Library 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. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include +#endif + +#include + +#if defined (HAVE_STDLIB_H) +# include +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# ifndef _MINIX +# include +# endif +# include +#endif + +#include "rlmbutil.h" + +#include "history.h" +#include "histlib.h" + +#include "rlshell.h" +#include "xmalloc.h" + +#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" +#define HISTORY_QUOTE_CHARACTERS "\"'`" + +#define slashify_in_quotes "\\`\"$" + +typedef int _hist_search_func_t PARAMS((const char *, int)); + +static char error_pointer; + +static char *subst_lhs; +static char *subst_rhs; +static int subst_lhs_len; +static int subst_rhs_len; + +static char *get_history_word_specifier PARAMS((char *, char *, int *)); +static char *history_find_word PARAMS((char *, int)); +static int history_tokenize_word PARAMS((const char *, int)); +static char *history_substring PARAMS((const char *, int, int)); + +static char *quote_breaks PARAMS((char *)); + +/* Variables exported by this file. */ +/* The character that represents the start of a history expansion + request. This is usually `!'. */ +char history_expansion_char = '!'; + +/* The character that invokes word substitution if found at the start of + a line. This is usually `^'. */ +char history_subst_char = '^'; + +/* During tokenization, if this character is seen as the first character + of a word, then it, and all subsequent characters upto a newline are + ignored. For a Bourne shell, this should be '#'. Bash special cases + the interactive comment character to not be a comment delimiter. */ +char history_comment_char = '\0'; + +/* The list of characters which inhibit the expansion of text if found + immediately following history_expansion_char. */ +char *history_no_expand_chars = " \t\n\r="; + +/* If set to a non-zero value, single quotes inhibit history expansion. + The default is 0. */ +int history_quotes_inhibit_expansion = 0; + +/* Used to split words by history_tokenize_internal. */ +char *history_word_delimiters = HISTORY_WORD_DELIMITERS; + +/* If set, this points to a function that is called to verify that a + particular history expansion should be performed. */ +rl_linebuf_func_t *history_inhibit_expansion_function; + +/* **************************************************************** */ +/* */ +/* History Expansion */ +/* */ +/* **************************************************************** */ + +/* Hairy history expansion on text, not tokens. This is of general + use, and thus belongs in this library. */ + +/* The last string searched for by a !?string? search. */ +static char *search_string; + +/* The last string matched by a !?string? search. */ +static char *search_match; + +/* Return the event specified at TEXT + OFFSET modifying OFFSET to + point to after the event specifier. Just a pointer to the history + line is returned; NULL is returned in the event of a bad specifier. + You pass STRING with *INDEX equal to the history_expansion_char that + begins this specification. + DELIMITING_QUOTE is a character that is allowed to end the string + specification for what to search for in addition to the normal + characters `:', ` ', `\t', `\n', and sometimes `?'. + So you might call this function like: + line = get_history_event ("!echo:p", &index, 0); */ +char * +get_history_event (string, caller_index, delimiting_quote) + const char *string; + int *caller_index; + int delimiting_quote; +{ + register int i; + register char c; + HIST_ENTRY *entry; + int which, sign, local_index, substring_okay; + _hist_search_func_t *search_func; + char *temp; + + /* The event can be specified in a number of ways. + + !! the previous command + !n command line N + !-n current command-line minus N + !str the most recent command starting with STR + !?str[?] + the most recent command containing STR + + All values N are determined via HISTORY_BASE. */ + + i = *caller_index; + + if (string[i] != history_expansion_char) + return ((char *)NULL); + + /* Move on to the specification. */ + i++; + + sign = 1; + substring_okay = 0; + +#define RETURN_ENTRY(e, w) \ + return ((e = history_get (w)) ? e->line : (char *)NULL) + + /* Handle !! case. */ + if (string[i] == history_expansion_char) + { + i++; + which = history_base + (history_length - 1); + *caller_index = i; + RETURN_ENTRY (entry, which); + } + + /* Hack case of numeric line specification. */ + if (string[i] == '-') + { + sign = -1; + i++; + } + + if (_rl_digit_p (string[i])) + { + /* Get the extent of the digits and compute the value. */ + for (which = 0; _rl_digit_p (string[i]); i++) + which = (which * 10) + _rl_digit_value (string[i]); + + *caller_index = i; + + if (sign < 0) + which = (history_length + history_base) - which; + + RETURN_ENTRY (entry, which); + } + + /* This must be something to search for. If the spec begins with + a '?', then the string may be anywhere on the line. Otherwise, + the string must be found at the start of a line. */ + if (string[i] == '?') + { + substring_okay++; + i++; + } + + /* Only a closing `?' or a newline delimit a substring search string. */ + for (local_index = i; c = string[i]; i++) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int v; + mbstate_t ps; + + memset (&ps, 0, sizeof (mbstate_t)); + /* These produce warnings because we're passing a const string to a + function that takes a non-const string. */ + _rl_adjust_point ((char *)string, i, &ps); + if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1) + { + i += v - 1; + continue; + } + } + +#endif /* HANDLE_MULTIBYTE */ + if ((!substring_okay && (whitespace (c) || c == ':' || + (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || + string[i] == delimiting_quote)) || + string[i] == '\n' || + (substring_okay && string[i] == '?')) + break; + } + + which = i - local_index; + temp = (char *)xmalloc (1 + which); + if (which) + strncpy (temp, string + local_index, which); + temp[which] = '\0'; + + if (substring_okay && string[i] == '?') + i++; + + *caller_index = i; + +#define FAIL_SEARCH() \ + do { \ + history_offset = history_length; free (temp) ; return (char *)NULL; \ + } while (0) + + /* If there is no search string, try to use the previous search string, + if one exists. If not, fail immediately. */ + if (*temp == '\0' && substring_okay) + { + if (search_string) + { + free (temp); + temp = savestring (search_string); + } + else + FAIL_SEARCH (); + } + + search_func = substring_okay ? history_search : history_search_prefix; + while (1) + { + local_index = (*search_func) (temp, -1); + + if (local_index < 0) + FAIL_SEARCH (); + + if (local_index == 0 || substring_okay) + { + entry = current_history (); + history_offset = history_length; + + /* If this was a substring search, then remember the + string that we matched for word substitution. */ + if (substring_okay) + { + FREE (search_string); + search_string = temp; + + FREE (search_match); + search_match = history_find_word (entry->line, local_index); + } + else + free (temp); + + return (entry->line); + } + + if (history_offset) + history_offset--; + else + FAIL_SEARCH (); + } +#undef FAIL_SEARCH +#undef RETURN_ENTRY +} + +/* Function for extracting single-quoted strings. Used for inhibiting + history expansion within single quotes. */ + +/* Extract the contents of STRING as if it is enclosed in single quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening single quote; on exit, SINDEX is left pointing + to the closing single quote. */ +static void +hist_string_extract_single_quoted (string, sindex) + char *string; + int *sindex; +{ + register int i; + + for (i = *sindex; string[i] && string[i] != '\''; i++) + ; + + *sindex = i; +} + +static char * +quote_breaks (s) + char *s; +{ + register char *p, *r; + char *ret; + int len = 3; + + for (p = s; p && *p; p++, len++) + { + if (*p == '\'') + len += 3; + else if (whitespace (*p) || *p == '\n') + len += 2; + } + + r = ret = (char *)xmalloc (len); + *r++ = '\''; + for (p = s; p && *p; ) + { + if (*p == '\'') + { + *r++ = '\''; + *r++ = '\\'; + *r++ = '\''; + *r++ = '\''; + p++; + } + else if (whitespace (*p) || *p == '\n') + { + *r++ = '\''; + *r++ = *p++; + *r++ = '\''; + } + else + *r++ = *p++; + } + *r++ = '\''; + *r = '\0'; + return ret; +} + +static char * +hist_error(s, start, current, errtype) + char *s; + int start, current, errtype; +{ + char *temp; + const char *emsg; + int ll, elen; + + ll = current - start; + + switch (errtype) + { + case EVENT_NOT_FOUND: + emsg = "event not found"; + elen = 15; + break; + case BAD_WORD_SPEC: + emsg = "bad word specifier"; + elen = 18; + break; + case SUBST_FAILED: + emsg = "substitution failed"; + elen = 19; + break; + case BAD_MODIFIER: + emsg = "unrecognized history modifier"; + elen = 29; + break; + case NO_PREV_SUBST: + emsg = "no previous substitution"; + elen = 24; + break; + default: + emsg = "unknown expansion error"; + elen = 23; + break; + } + + temp = (char *)xmalloc (ll + elen + 3); + strncpy (temp, s + start, ll); + temp[ll] = ':'; + temp[ll + 1] = ' '; + strcpy (temp + ll + 2, emsg); + return (temp); +} + +/* Get a history substitution string from STR starting at *IPTR + and return it. The length is returned in LENPTR. + + A backslash can quote the delimiter. If the string is the + empty string, the previous pattern is used. If there is + no previous pattern for the lhs, the last history search + string is used. + + If IS_RHS is 1, we ignore empty strings and set the pattern + to "" anyway. subst_lhs is not changed if the lhs is empty; + subst_rhs is allowed to be set to the empty string. */ + +static char * +get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) + char *str; + int *iptr, delimiter, is_rhs, *lenptr; +{ + register int si, i, j, k; + char *s; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; +#endif + + s = (char *)NULL; + i = *iptr; + +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); + _rl_adjust_point (str, i, &ps); +#endif + + for (si = i; str[si] && str[si] != delimiter; si++) +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int v; + if ((v = _rl_get_char_len (str + si, &ps)) > 1) + si += v - 1; + else if (str[si] == '\\' && str[si + 1] == delimiter) + si++; + } + else +#endif /* HANDLE_MULTIBYTE */ + if (str[si] == '\\' && str[si + 1] == delimiter) + si++; + + if (si > i || is_rhs) + { + s = (char *)xmalloc (si - i + 1); + for (j = 0, k = i; k < si; j++, k++) + { + /* Remove a backslash quoting the search string delimiter. */ + if (str[k] == '\\' && str[k + 1] == delimiter) + k++; + s[j] = str[k]; + } + s[j] = '\0'; + if (lenptr) + *lenptr = j; + } + + i = si; + if (str[i]) + i++; + *iptr = i; + + return s; +} + +static void +postproc_subst_rhs () +{ + char *new; + int i, j, new_size; + + new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); + for (i = j = 0; i < subst_rhs_len; i++) + { + if (subst_rhs[i] == '&') + { + if (j + subst_lhs_len >= new_size) + new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); + strcpy (new + j, subst_lhs); + j += subst_lhs_len; + } + else + { + /* a single backslash protects the `&' from lhs interpolation */ + if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') + i++; + if (j >= new_size) + new = (char *)xrealloc (new, new_size *= 2); + new[j++] = subst_rhs[i]; + } + } + new[j] = '\0'; + free (subst_rhs); + subst_rhs = new; + subst_rhs_len = j; +} + +/* Expand the bulk of a history specifier starting at STRING[START]. + Returns 0 if everything is OK, -1 if an error occurred, and 1 + if the `p' modifier was supplied and the caller should just print + the returned string. Returns the new index into string in + *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ +static int +history_expand_internal (string, start, end_index_ptr, ret_string, current_line) + char *string; + int start, *end_index_ptr; + char **ret_string; + char *current_line; /* for !# */ +{ + int i, n, starting_index; + int substitute_globally, subst_bywords, want_quotes, print_only; + char *event, *temp, *result, *tstr, *t, c, *word_spec; + int result_len; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + result = (char *)xmalloc (result_len = 128); + + i = start; + + /* If it is followed by something that starts a word specifier, + then !! is implied as the event specifier. */ + + if (member (string[i + 1], ":$*%^")) + { + char fake_s[3]; + int fake_i = 0; + i++; + fake_s[0] = fake_s[1] = history_expansion_char; + fake_s[2] = '\0'; + event = get_history_event (fake_s, &fake_i, 0); + } + else if (string[i + 1] == '#') + { + i += 2; + event = current_line; + } + else + { + int quoted_search_delimiter = 0; + + /* If the character before this `!' is a double or single + quote, then this expansion takes place inside of the + quoted string. If we have to search for some text ("!foo"), + allow the delimiter to end the search string. */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int c, l; + l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY); + c = string[l]; + /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ + if (i && (c == '\'' || c == '"')) + quoted_search_delimiter = c; + } + else +#endif /* HANDLE_MULTIBYTE */ + if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) + quoted_search_delimiter = string[i - 1]; + + event = get_history_event (string, &i, quoted_search_delimiter); + } + + if (event == 0) + { + *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); + free (result); + return (-1); + } + + /* If a word specifier is found, then do what that requires. */ + starting_index = i; + word_spec = get_history_word_specifier (string, event, &i); + + /* There is no such thing as a `malformed word specifier'. However, + it is possible for a specifier that has no match. In that case, + we complain. */ + if (word_spec == (char *)&error_pointer) + { + *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); + free (result); + return (-1); + } + + /* If no word specifier, than the thing of interest was the event. */ + temp = word_spec ? savestring (word_spec) : savestring (event); + FREE (word_spec); + + /* Perhaps there are other modifiers involved. Do what they say. */ + want_quotes = substitute_globally = subst_bywords = print_only = 0; + starting_index = i; + + while (string[i] == ':') + { + c = string[i + 1]; + + if (c == 'g' || c == 'a') + { + substitute_globally = 1; + i++; + c = string[i + 1]; + } + else if (c == 'G') + { + subst_bywords = 1; + i++; + c = string[i + 1]; + } + + switch (c) + { + default: + *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); + free (result); + free (temp); + return -1; + + case 'q': + want_quotes = 'q'; + break; + + case 'x': + want_quotes = 'x'; + break; + + /* :p means make this the last executed line. So we + return an error state after adding this line to the + history. */ + case 'p': + print_only++; + break; + + /* :t discards all but the last part of the pathname. */ + case 't': + tstr = strrchr (temp, '/'); + if (tstr) + { + tstr++; + t = savestring (tstr); + free (temp); + temp = t; + } + break; + + /* :h discards the last part of a pathname. */ + case 'h': + tstr = strrchr (temp, '/'); + if (tstr) + *tstr = '\0'; + break; + + /* :r discards the suffix. */ + case 'r': + tstr = strrchr (temp, '.'); + if (tstr) + *tstr = '\0'; + break; + + /* :e discards everything but the suffix. */ + case 'e': + tstr = strrchr (temp, '.'); + if (tstr) + { + t = savestring (tstr); + free (temp); + temp = t; + } + break; + + /* :s/this/that substitutes `that' for the first + occurrence of `this'. :gs/this/that substitutes `that' + for each occurrence of `this'. :& repeats the last + substitution. :g& repeats the last substitution + globally. */ + + case '&': + case 's': + { + char *new_event; + int delimiter, failed, si, l_temp, ws, we; + + if (c == 's') + { + if (i + 2 < (int)strlen (string)) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + _rl_adjust_point (string, i + 2, &ps); + if (_rl_get_char_len (string + i + 2, &ps) > 1) + delimiter = 0; + else + delimiter = string[i + 2]; + } + else +#endif /* HANDLE_MULTIBYTE */ + delimiter = string[i + 2]; + } + else + break; /* no search delimiter */ + + i += 3; + + t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len); + /* An empty substitution lhs with no previous substitution + uses the last search string as the lhs. */ + if (t) + { + FREE (subst_lhs); + subst_lhs = t; + } + else if (!subst_lhs) + { + if (search_string && *search_string) + { + subst_lhs = savestring (search_string); + subst_lhs_len = strlen (subst_lhs); + } + else + { + subst_lhs = (char *) NULL; + subst_lhs_len = 0; + } + } + + FREE (subst_rhs); + subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); + + /* If `&' appears in the rhs, it's supposed to be replaced + with the lhs. */ + if (member ('&', subst_rhs)) + postproc_subst_rhs (); + } + else + i += 2; + + /* If there is no lhs, the substitution can't succeed. */ + if (subst_lhs_len == 0) + { + *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); + free (result); + free (temp); + return -1; + } + + l_temp = strlen (temp); + /* Ignore impossible cases. */ + if (subst_lhs_len > l_temp) + { + *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); + free (result); + free (temp); + return (-1); + } + + /* Find the first occurrence of THIS in TEMP. */ + /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three + cases to consider: + + 1. substitute_globally == subst_bywords == 0 + 2. substitute_globally == 1 && subst_bywords == 0 + 3. substitute_globally == 0 && subst_bywords == 1 + + In the first case, we substitute for the first occurrence only. + In the second case, we substitute for every occurrence. + In the third case, we tokenize into words and substitute the + first occurrence of each word. */ + + si = we = 0; + for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) + { + /* First skip whitespace and find word boundaries if + we're past the end of the word boundary we found + the last time. */ + if (subst_bywords && si > we) + { + for (; temp[si] && whitespace (temp[si]); si++) + ; + ws = si; + we = history_tokenize_word (temp, si); + } + + if (STREQN (temp+si, subst_lhs, subst_lhs_len)) + { + int len = subst_rhs_len - subst_lhs_len + l_temp; + new_event = (char *)xmalloc (1 + len); + strncpy (new_event, temp, si); + strncpy (new_event + si, subst_rhs, subst_rhs_len); + strncpy (new_event + si + subst_rhs_len, + temp + si + subst_lhs_len, + l_temp - (si + subst_lhs_len)); + new_event[len] = '\0'; + free (temp); + temp = new_event; + + failed = 0; + + if (substitute_globally) + { + /* Reported to fix a bug that causes it to skip every + other match when matching a single character. Was + si += subst_rhs_len previously. */ + si += subst_rhs_len - 1; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + else if (subst_bywords) + { + si = we; + l_temp = strlen (temp); + continue; + } + else + break; + } + } + + if (substitute_globally > 1) + { + substitute_globally = 0; + continue; /* don't want to increment i */ + } + + if (failed == 0) + continue; /* don't want to increment i */ + + *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); + free (result); + free (temp); + return (-1); + } + } + i += 2; + } + /* Done with modfiers. */ + /* Believe it or not, we have to back the pointer up by one. */ + --i; + + if (want_quotes) + { + char *x; + + if (want_quotes == 'q') + x = sh_single_quote (temp); + else if (want_quotes == 'x') + x = quote_breaks (temp); + else + x = savestring (temp); + + free (temp); + temp = x; + } + + n = strlen (temp); + if (n >= result_len) + result = (char *)xrealloc (result, n + 2); + strcpy (result, temp); + free (temp); + + *end_index_ptr = i; + *ret_string = result; + return (print_only); +} + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + -1) If there was an error in expansion. + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + 2) If the `p' modifier was given and the caller should print the result + + If an error ocurred in expansion, then OUTPUT contains a descriptive + error message. */ + +#define ADD_STRING(s) \ + do \ + { \ + int sl = strlen (s); \ + j += sl; \ + if (j >= result_len) \ + { \ + while (j >= result_len) \ + result_len += 128; \ + result = (char *)xrealloc (result, result_len); \ + } \ + strcpy (result + j - sl, s); \ + } \ + while (0) + +#define ADD_CHAR(c) \ + do \ + { \ + if (j >= result_len - 1) \ + result = (char *)xrealloc (result, result_len += 64); \ + result[j++] = c; \ + result[j] = '\0'; \ + } \ + while (0) + +int +history_expand (hstring, output) + char *hstring; + char **output; +{ + register int j; + int i, r, l, passc, cc, modified, eindex, only_printing, dquote; + char *string; + + /* The output string, and its length. */ + int result_len; + char *result; + +#if defined (HANDLE_MULTIBYTE) + char mb[MB_LEN_MAX]; + mbstate_t ps; +#endif + + /* Used when adding the string. */ + char *temp; + + if (output == 0) + return 0; + + /* Setting the history expansion character to 0 inhibits all + history expansion. */ + if (history_expansion_char == 0) + { + *output = savestring (hstring); + return (0); + } + + /* Prepare the buffer for printing error messages. */ + result = (char *)xmalloc (result_len = 256); + result[0] = '\0'; + + only_printing = modified = 0; + l = strlen (hstring); + + /* Grovel the string. Only backslash and single quotes can quote the + history escape character. We also handle arg specifiers. */ + + /* Before we grovel forever, see if the history_expansion_char appears + anywhere within the text. */ + + /* The quick substitution character is a history expansion all right. That + is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, + that is the substitution that we do. */ + if (hstring[0] == history_subst_char) + { + string = (char *)xmalloc (l + 5); + + string[0] = string[1] = history_expansion_char; + string[2] = ':'; + string[3] = 's'; + strcpy (string + 4, hstring); + l += 4; + } + else + { +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + string = hstring; + /* If not quick substitution, still maybe have to do expansion. */ + + /* `!' followed by one of the characters in history_no_expand_chars + is NOT an expansion. */ + for (i = dquote = 0; string[i]; i++) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int v; + v = _rl_get_char_len (string + i, &ps); + if (v > 1) + { + i += v - 1; + continue; + } + } +#endif /* HANDLE_MULTIBYTE */ + + cc = string[i + 1]; + /* The history_comment_char, if set, appearing at the beginning + of a word signifies that the rest of the line should not have + history expansion performed on it. + Skip the rest of the line and break out of the loop. */ + if (history_comment_char && string[i] == history_comment_char && + (i == 0 || member (string[i - 1], history_word_delimiters))) + { + while (string[i]) + i++; + break; + } + else if (string[i] == history_expansion_char) + { + if (!cc || member (cc, history_no_expand_chars)) + continue; + /* If the calling application has set + history_inhibit_expansion_function to a function that checks + for special cases that should not be history expanded, + call the function and skip the expansion if it returns a + non-zero value. */ + else if (history_inhibit_expansion_function && + (*history_inhibit_expansion_function) (string, i)) + continue; + else + break; + } + /* Shell-like quoting: allow backslashes to quote double quotes + inside a double-quoted string. */ + else if (dquote && string[i] == '\\' && cc == '"') + i++; + /* More shell-like quoting: if we're paying attention to single + quotes and letting them quote the history expansion character, + then we need to pay attention to double quotes, because single + quotes are not special inside double-quoted strings. */ + else if (history_quotes_inhibit_expansion && string[i] == '"') + { + dquote = 1 - dquote; + } + else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'') + { + /* If this is bash, single quotes inhibit history expansion. */ + i++; + hist_string_extract_single_quoted (string, &i); + } + else if (history_quotes_inhibit_expansion && string[i] == '\\') + { + /* If this is bash, allow backslashes to quote single + quotes and the history expansion character. */ + if (cc == '\'' || cc == history_expansion_char) + i++; + } + + } + + if (string[i] != history_expansion_char) + { + free (result); + *output = savestring (string); + return (0); + } + } + + /* Extract and perform the substitution. */ + for (passc = dquote = i = j = 0; i < l; i++) + { + int tchar = string[i]; + + if (passc) + { + passc = 0; + ADD_CHAR (tchar); + continue; + } + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int k, c; + + c = tchar; + memset (mb, 0, sizeof (mb)); + for (k = 0; k < MB_LEN_MAX; k++) + { + mb[k] = (char)c; + memset (&ps, 0, sizeof (mbstate_t)); + if (_rl_get_char_len (mb, &ps) == -2) + c = string[++i]; + else + break; + } + if (strlen (mb) > 1) + { + ADD_STRING (mb); + break; + } + } +#endif /* HANDLE_MULTIBYTE */ + + if (tchar == history_expansion_char) + tchar = -3; + else if (tchar == history_comment_char) + tchar = -2; + + switch (tchar) + { + default: + ADD_CHAR (string[i]); + break; + + case '\\': + passc++; + ADD_CHAR (tchar); + break; + + case '"': + dquote = 1 - dquote; + ADD_CHAR (tchar); + break; + + case '\'': + { + /* If history_quotes_inhibit_expansion is set, single quotes + inhibit history expansion. */ + if (dquote == 0 && history_quotes_inhibit_expansion) + { + int quote, slen; + + quote = i++; + hist_string_extract_single_quoted (string, &i); + + slen = i - quote + 2; + temp = (char *)xmalloc (slen); + strncpy (temp, string + quote, slen); + temp[slen - 1] = '\0'; + ADD_STRING (temp); + free (temp); + } + else + ADD_CHAR (string[i]); + break; + } + + case -2: /* history_comment_char */ + if (i == 0 || member (string[i - 1], history_word_delimiters)) + { + temp = (char *)xmalloc (l - i + 1); + strcpy (temp, string + i); + ADD_STRING (temp); + free (temp); + i = l; + } + else + ADD_CHAR (string[i]); + break; + + case -3: /* history_expansion_char */ + cc = string[i + 1]; + + /* If the history_expansion_char is followed by one of the + characters in history_no_expand_chars, then it is not a + candidate for expansion of any kind. */ + if (member (cc, history_no_expand_chars)) + { + ADD_CHAR (string[i]); + break; + } + +#if defined (NO_BANG_HASH_MODIFIERS) + /* There is something that is listed as a `word specifier' in csh + documentation which means `the expanded text to this point'. + That is not a word specifier, it is an event specifier. If we + don't want to allow modifiers with `!#', just stick the current + output line in again. */ + if (cc == '#') + { + if (result) + { + temp = (char *)xmalloc (1 + strlen (result)); + strcpy (temp, result); + ADD_STRING (temp); + free (temp); + } + i++; + break; + } +#endif + + r = history_expand_internal (string, i, &eindex, &temp, result); + if (r < 0) + { + *output = temp; + free (result); + if (string != hstring) + free (string); + return -1; + } + else + { + if (temp) + { + modified++; + if (*temp) + ADD_STRING (temp); + free (temp); + } + only_printing = r == 1; + i = eindex; + } + break; + } + } + + *output = result; + if (string != hstring) + free (string); + + if (only_printing) + { +#if 0 + add_history (result); +#endif + return (2); + } + + return (modified != 0); +} + +/* Return a consed string which is the word specified in SPEC, and found + in FROM. NULL is returned if there is no spec. The address of + ERROR_POINTER is returned if the word specified cannot be found. + CALLER_INDEX is the offset in SPEC to start looking; it is updated + to point to just after the last character parsed. */ +static char * +get_history_word_specifier (spec, from, caller_index) + char *spec, *from; + int *caller_index; +{ + register int i = *caller_index; + int first, last; + int expecting_word_spec = 0; + char *result; + + /* The range of words to return doesn't exist yet. */ + first = last = 0; + result = (char *)NULL; + + /* If we found a colon, then this *must* be a word specification. If + it isn't, then it is an error. */ + if (spec[i] == ':') + { + i++; + expecting_word_spec++; + } + + /* Handle special cases first. */ + + /* `%' is the word last searched for. */ + if (spec[i] == '%') + { + *caller_index = i + 1; + return (search_match ? savestring (search_match) : savestring ("")); + } + + /* `*' matches all of the arguments, but not the command. */ + if (spec[i] == '*') + { + *caller_index = i + 1; + result = history_arg_extract (1, '$', from); + return (result ? result : savestring ("")); + } + + /* `$' is last arg. */ + if (spec[i] == '$') + { + *caller_index = i + 1; + return (history_arg_extract ('$', '$', from)); + } + + /* Try to get FIRST and LAST figured out. */ + + if (spec[i] == '-') + first = 0; + else if (spec[i] == '^') + { + first = 1; + i++; + } + else if (_rl_digit_p (spec[i]) && expecting_word_spec) + { + for (first = 0; _rl_digit_p (spec[i]); i++) + first = (first * 10) + _rl_digit_value (spec[i]); + } + else + return ((char *)NULL); /* no valid `first' for word specifier */ + + if (spec[i] == '^' || spec[i] == '*') + { + last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ + i++; + } + else if (spec[i] != '-') + last = first; + else + { + i++; + + if (_rl_digit_p (spec[i])) + { + for (last = 0; _rl_digit_p (spec[i]); i++) + last = (last * 10) + _rl_digit_value (spec[i]); + } + else if (spec[i] == '$') + { + i++; + last = '$'; + } +#if 0 + else if (!spec[i] || spec[i] == ':') + /* check against `:' because there could be a modifier separator */ +#else + else + /* csh seems to allow anything to terminate the word spec here, + leaving it as an abbreviation. */ +#endif + last = -1; /* x- abbreviates x-$ omitting word `$' */ + } + + *caller_index = i; + + if (last >= first || last == '$' || last < 0) + result = history_arg_extract (first, last, from); + + return (result ? result : (char *)&error_pointer); +} + +/* Extract the args specified, starting at FIRST, and ending at LAST. + The args are taken from STRING. If either FIRST or LAST is < 0, + then make that arg count from the right (subtract from the number of + tokens, so that FIRST = -1 means the next to last token on the line). + If LAST is `$' the last arg from STRING is used. */ +char * +history_arg_extract (first, last, string) + int first, last; + const char *string; +{ + register int i, len; + char *result; + int size, offset; + char **list; + + /* XXX - think about making history_tokenize return a struct array, + each struct in array being a string and a length to avoid the + calls to strlen below. */ + if ((list = history_tokenize (string)) == NULL) + return ((char *)NULL); + + for (len = 0; list[len]; len++) + ; + + if (last < 0) + last = len + last - 1; + + if (first < 0) + first = len + first - 1; + + if (last == '$') + last = len - 1; + + if (first == '$') + first = len - 1; + + last++; + + if (first >= len || last > len || first < 0 || last < 0 || first > last) + result = ((char *)NULL); + else + { + for (size = 0, i = first; i < last; i++) + size += strlen (list[i]) + 1; + result = (char *)xmalloc (size + 1); + result[0] = '\0'; + + for (i = first, offset = 0; i < last; i++) + { + strcpy (result + offset, list[i]); + offset += strlen (list[i]); + if (i + 1 < last) + { + result[offset++] = ' '; + result[offset] = 0; + } + } + } + + for (i = 0; i < len; i++) + free (list[i]); + free (list); + + return (result); +} + +static int +history_tokenize_word (string, ind) + const char *string; + int ind; +{ + register int i; + int delimiter; + + i = ind; + delimiter = 0; + + if (member (string[i], "()\n")) + { + i++; + return i; + } + + if (member (string[i], "<>;&|$")) + { + int peek = string[i + 1]; + + if (peek == string[i] && peek != '$') + { + if (peek == '<' && string[i + 2] == '-') + i++; + else if (peek == '<' && string[i + 2] == '<') + i++; + i += 2; + return i; + } + else + { + if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || + (peek == '>' && string[i] == '&') || + (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ + (peek == '(' && string[i] == '$')) /* ) */ + { + i += 2; + return i; + } + } + + if (string[i] != '$') + { + i++; + return i; + } + } + + /* Get word from string + i; */ + + if (member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i++]; + + for (; string[i]; i++) + { + if (string[i] == '\\' && string[i + 1] == '\n') + { + i++; + continue; + } + + if (string[i] == '\\' && delimiter != '\'' && + (delimiter != '"' || member (string[i], slashify_in_quotes))) + { + i++; + continue; + } + + if (delimiter && string[i] == delimiter) + { + delimiter = 0; + continue; + } + + if (!delimiter && (member (string[i], history_word_delimiters))) + break; + + if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i]; + } + + return i; +} + +static char * +history_substring (string, start, end) + const char *string; + int start, end; +{ + register int len; + register char *result; + + len = end - start; + result = (char *)xmalloc (len + 1); + strncpy (result, string + start, len); + result[len] = '\0'; + return result; +} + +/* Parse STRING into tokens and return an array of strings. If WIND is + not -1 and INDP is not null, we also want the word surrounding index + WIND. The position in the returned array of strings is returned in + *INDP. */ +static char ** +history_tokenize_internal (string, wind, indp) + const char *string; + int wind, *indp; +{ + char **result; + register int i, start, result_index, size; + + /* If we're searching for a string that's not part of a word (e.g., " "), + make sure we set *INDP to a reasonable value. */ + if (indp && wind != -1) + *indp = -1; + + /* Get a token, and stuff it into RESULT. The tokens are split + exactly where the shell would split them. */ + for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) + { + /* Skip leading whitespace. */ + for (; string[i] && whitespace (string[i]); i++) + ; + if (string[i] == 0 || string[i] == history_comment_char) + return (result); + + start = i; + + i = history_tokenize_word (string, start); + + /* If we have a non-whitespace delimiter character (which would not be + skipped by the loop above), use it and any adjacent delimiters to + make a separate field. Any adjacent white space will be skipped the + next time through the loop. */ + if (i == start && history_word_delimiters) + { + i++; + while (string[i] && member (string[i], history_word_delimiters)) + i++; + } + + /* If we are looking for the word in which the character at a + particular index falls, remember it. */ + if (indp && wind != -1 && wind >= start && wind < i) + *indp = result_index; + + if (result_index + 2 >= size) + result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); + + result[result_index++] = history_substring (string, start, i); + result[result_index] = (char *)NULL; + } + + return (result); +} + +/* Return an array of tokens, much as the shell might. The tokens are + parsed out of STRING. */ +char ** +history_tokenize (string) + const char *string; +{ + return (history_tokenize_internal (string, -1, (int *)NULL)); +} + +/* Find and return the word which contains the character at index IND + in the history line LINE. Used to save the word matched by the + last history !?string? search. */ +static char * +history_find_word (line, ind) + char *line; + int ind; +{ + char **words, *s; + int i, wind; + + words = history_tokenize_internal (line, ind, &wind); + if (wind == -1 || words == 0) + return ((char *)NULL); + s = words[wind]; + for (i = 0; i < wind; i++) + free (words[i]); + for (i = wind + 1; words[i]; i++) + free (words[i]); + free (words); + return s; +} diff --git a/lib/readline/input.c b/lib/readline/input.c index dcc86e146..f4de57b04 100644 --- a/lib/readline/input.c +++ b/lib/readline/input.c @@ -550,21 +550,21 @@ _rl_read_mbchar (mbchar, size) } /* Read a multibyte-character string whose first character is FIRST into - the buffer MB of length MBLEN. Returns the last character read, which + the buffer MB of length MLEN. Returns the last character read, which may be FIRST. Used by the search functions, among others. Very similar to _rl_read_mbchar. */ int -_rl_read_mbstring (first, mb, mblen) +_rl_read_mbstring (first, mb, mlen) int first; char *mb; - int mblen; + int mlen; { int i, c; mbstate_t ps; c = first; - memset (mb, 0, mblen); - for (i = 0; i < mblen; i++) + memset (mb, 0, mlen); + for (i = 0; i < mlen; i++) { mb[i] = (char)c; memset (&ps, 0, sizeof (mbstate_t)); diff --git a/lib/readline/input.c~ b/lib/readline/input.c~ index 0ec507ec2..dcc86e146 100644 --- a/lib/readline/input.c~ +++ b/lib/readline/input.c~ @@ -179,6 +179,7 @@ rl_gather_tyi () struct timeval timeout; #endif + chars_avail = 0; tty = fileno (rl_instream); #if defined (HAVE_SELECT) @@ -220,6 +221,13 @@ rl_gather_tyi () } #endif /* O_NDELAY */ +#if defined (__MINGW32__) + /* Use getch/_kbhit to check for available console input, in the same way + that we read it normally. */ + chars_avail = isatty (tty) ? _kbhit () : 0; + result = 0; +#endif + /* If there's nothing available, don't waste time trying to read something. */ if (chars_avail <= 0) @@ -263,7 +271,7 @@ rl_set_keyboard_input_timeout (u) int o; o = _keyboard_input_timeout; - if (u > 0) + if (u >= 0) _keyboard_input_timeout = u; return (o); } @@ -303,6 +311,11 @@ _rl_input_available () return (chars_avail); #endif +#endif + +#if defined (__MINGW32__) + if (isatty (tty)) + return (_kbhit ()); #endif return 0; diff --git a/lib/readline/text.c b/lib/readline/text.c index 6c969a881..399a48c5f 100644 --- a/lib/readline/text.c +++ b/lib/readline/text.c @@ -1237,8 +1237,8 @@ rl_change_case (count, op) #if defined (HANDLE_MULTIBYTE) wchar_t wc, nwc; char mb[MB_LEN_MAX+1]; - int mblen; - mbstate_t ps; + int mlen; + mbstate_t mps; #endif start = rl_point; @@ -1255,7 +1255,7 @@ rl_change_case (count, op) SWAP (start, end); #if defined (HANDLE_MULTIBYTE) - memset (&ps, 0, sizeof (mbstate_t)); + memset (&mps, 0, sizeof (mbstate_t)); #endif /* We are going to modify some text, so let's prepare to undo it. */ @@ -1290,15 +1290,15 @@ rl_change_case (count, op) #if defined (HANDLE_MULTIBYTE) else { - mbrtowc (&wc, rl_line_buffer + start, end - start, &ps); + mbrtowc (&wc, rl_line_buffer + start, end - start, &mps); nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc); if (nwc != wc) /* just skip unchanged characters */ { - mblen = wcrtomb (mb, nwc, &ps); - if (mblen > 0) - mb[mblen] = '\0'; + mlen = wcrtomb (mb, nwc, &mps); + if (mlen > 0) + mb[mlen] = '\0'; /* Assume the same width */ - strncpy (rl_line_buffer + start, mb, mblen); + strncpy (rl_line_buffer + start, mb, mlen); } } #endif diff --git a/lib/readline/text.c~ b/lib/readline/text.c~ index f8d272cc9..d771de0de 100644 --- a/lib/readline/text.c~ +++ b/lib/readline/text.c~ @@ -1237,8 +1237,8 @@ rl_change_case (count, op) #if defined (HANDLE_MULTIBYTE) wchar_t wc, nwc; char mb[MB_LEN_MAX+1]; - int mblen, p; - mbstate_t ps; + int mblen; + mbstate_t mps; #endif start = rl_point; @@ -1255,7 +1255,7 @@ rl_change_case (count, op) SWAP (start, end); #if defined (HANDLE_MULTIBYTE) - memset (&ps, 0, sizeof (mbstate_t)); + memset (&mps, 0, sizeof (mbstate_t)); #endif /* We are going to modify some text, so let's prepare to undo it. */ @@ -1290,11 +1290,11 @@ rl_change_case (count, op) #if defined (HANDLE_MULTIBYTE) else { - mbrtowc (&wc, rl_line_buffer + start, end - start, &ps); + mbrtowc (&wc, rl_line_buffer + start, end - start, &mps); nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc); if (nwc != wc) /* just skip unchanged characters */ { - mblen = wcrtomb (mb, nwc, &ps); + mblen = wcrtomb (mb, nwc, &mps); if (mblen > 0) mb[mblen] = '\0'; /* Assume the same width */ diff --git a/lib/readline/vi_mode.c b/lib/readline/vi_mode.c index dd0fa029a..d0b7e330a 100644 --- a/lib/readline/vi_mode.c +++ b/lib/readline/vi_mode.c @@ -738,7 +738,7 @@ _rl_vi_change_mbchar_case (count) { wchar_t wc; char mb[MB_LEN_MAX+1]; - int mblen, p; + int mlen, p; mbstate_t ps; memset (&ps, 0, sizeof (mbstate_t)); @@ -762,9 +762,9 @@ _rl_vi_change_mbchar_case (count) if (wc) { p = rl_point; - mblen = wcrtomb (mb, wc, &ps); - if (mblen >= 0) - mb[mblen] = '\0'; + mlen = wcrtomb (mb, wc, &ps); + if (mlen >= 0) + mb[mlen] = '\0'; rl_begin_undo_group (); rl_vi_delete (1, 0); if (rl_point < p) /* Did we retreat at EOL? */ @@ -1457,9 +1457,9 @@ _rl_vi_change_char (count, c, mb) } static int -_rl_vi_callback_getchar (mb, mblen) +_rl_vi_callback_getchar (mb, mlen) char *mb; - int mblen; + int mlen; { int c; @@ -1469,7 +1469,7 @@ _rl_vi_callback_getchar (mb, mblen) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - c = _rl_read_mbstring (c, mb, mblen); + c = _rl_read_mbstring (c, mb, mlen); #endif return c; diff --git a/lib/readline/vi_mode.c~ b/lib/readline/vi_mode.c~ index fce05f1d1..7cb838b0d 100644 --- a/lib/readline/vi_mode.c~ +++ b/lib/readline/vi_mode.c~ @@ -653,7 +653,7 @@ _rl_vi_save_insert (up) { int len, start, end; - if (up == 0) + if (up == 0 || up->what != UNDO_INSERT) { if (vi_insert_buffer_size >= 1) vi_insert_buffer[0] = '\0'; @@ -1457,9 +1457,9 @@ _rl_vi_change_char (count, c, mb) } static int -_rl_vi_callback_getchar (mb, mblen) +_rl_vi_callback_getchar (mb, mlen) char *mb; - int mblen; + int mlen; { int c; @@ -1469,7 +1469,7 @@ _rl_vi_callback_getchar (mb, mblen) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - c = _rl_read_mbstring (c, mb, mblen); + c = _rl_read_mbstring (c, mb, mlen); #endif return c; diff --git a/lib/sh/netopen.c b/lib/sh/netopen.c index aaf0c4755..c30f47750 100644 --- a/lib/sh/netopen.c +++ b/lib/sh/netopen.c @@ -66,6 +66,16 @@ extern int errno; extern int inet_aton __P((const char *, struct in_addr *)); #endif +#ifndef HAVE_GETADDRINFO +static int _getaddr __P((char *, struct in_addr)); +static int _getserv __P((char *, int, unsigned short)); +static int _netopen4 __P((char *, char *, int)); +#else /* HAVE_GETADDRINFO */ +static int _netopen6 __P((char *, char *, int)); +#endif + +static int _netopen __P((char *, char *, int)); + #ifndef HAVE_GETADDRINFO /* Stuff the internet address corresponding to HOST into AP, in network byte order. Return 1 on success, 0 on failure. */ diff --git a/lib/sh/netopen.c~ b/lib/sh/netopen.c~ new file mode 100644 index 000000000..aaf0c4755 --- /dev/null +++ b/lib/sh/netopen.c~ @@ -0,0 +1,340 @@ +/* + * netopen.c -- functions to make tcp/udp connections + * + * Chet Ramey + * chet@ins.CWRU.Edu + */ + +/* Copyright (C) 1987-2002 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include + +#if defined (HAVE_NETWORK) + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include +#include + +#if defined (HAVE_SYS_SOCKET_H) +# include +#endif + +#if defined (HAVE_NETINET_IN_H) +# include +#endif + +#if defined (HAVE_NETDB_H) +# include +#endif + +#if defined (HAVE_ARPA_INET_H) +# include +#endif + +#include +#include + +#include + +#include +#include + +#ifndef errno +extern int errno; +#endif + +#if !defined (HAVE_INET_ATON) +extern int inet_aton __P((const char *, struct in_addr *)); +#endif + +#ifndef HAVE_GETADDRINFO +/* Stuff the internet address corresponding to HOST into AP, in network + byte order. Return 1 on success, 0 on failure. */ + +static int +_getaddr (host, ap) + char *host; + struct in_addr *ap; +{ + struct hostent *h; + int r; + + r = 0; + if (host[0] >= '0' && host[0] <= '9') + { + /* If the first character is a digit, guess that it's an + Internet address and return immediately if inet_aton succeeds. */ + r = inet_aton (host, ap); + if (r) + return r; + } +#if !defined (HAVE_GETHOSTBYNAME) + return 0; +#else + h = gethostbyname (host); + if (h && h->h_addr) + { + bcopy(h->h_addr, (char *)ap, h->h_length); + return 1; + } +#endif + return 0; + +} + +/* Return 1 if SERV is a valid port number and stuff the converted value into + PP in network byte order. */ +static int +_getserv (serv, proto, pp) + char *serv; + int proto; + unsigned short *pp; +{ + intmax_t l; + unsigned short s; + + if (legal_number (serv, &l)) + { + s = (unsigned short)(l & 0xFFFF); + if (s != l) + return (0); + s = htons (s); + if (pp) + *pp = s; + return 1; + } + else +#if defined (HAVE_GETSERVBYNAME) + { + struct servent *se; + + se = getservbyname (serv, (proto == 't') ? "tcp" : "udp"); + if (se == 0) + return 0; + if (pp) + *pp = se->s_port; /* ports returned in network byte order */ + return 1; + } +#else /* !HAVE_GETSERVBYNAME */ + return 0; +#endif /* !HAVE_GETSERVBYNAME */ +} + +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses the + * traditional BSD mechanisms. Returns the connected socket or -1 on error. + */ +static int +_netopen4(host, serv, typ) + char *host, *serv; + int typ; +{ + struct in_addr ina; + struct sockaddr_in sin; + unsigned short p; + int s, e; + + if (_getaddr(host, &ina) == 0) + { + internal_error (_("%s: host unknown"), host); + errno = EINVAL; + return -1; + } + + if (_getserv(serv, typ, &p) == 0) + { + internal_error(_("%s: invalid service"), serv); + errno = EINVAL; + return -1; + } + + memset ((char *)&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = p; + sin.sin_addr = ina; + + s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0); + if (s < 0) + { + sys_error ("socket"); + return (-1); + } + + if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) + { + e = errno; + sys_error("connect"); + close(s); + errno = e; + return (-1); + } + + return(s); +} +#endif /* ! HAVE_GETADDRINFO */ + +#ifdef HAVE_GETADDRINFO +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) + * which provides support for IPv6. Returns the connected socket or -1 + * on error. + */ +static int +_netopen6 (host, serv, typ) + char *host, *serv; + int typ; +{ + int s, e; + struct addrinfo hints, *res, *res0; + int gerr; + + memset ((char *)&hints, 0, sizeof (hints)); + /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */ +#ifdef DEBUG /* PF_INET is the one that works for me */ + hints.ai_family = PF_INET; +#else + hints.ai_family = PF_UNSPEC; +#endif + hints.ai_socktype = (typ == 't') ? SOCK_STREAM : SOCK_DGRAM; + + gerr = getaddrinfo (host, serv, &hints, &res0); + if (gerr) + { + if (gerr == EAI_SERVICE) + internal_error ("%s: %s", serv, gai_strerror (gerr)); + else + internal_error ("%s: %s", host, gai_strerror (gerr)); + errno = EINVAL; + return -1; + } + + for (res = res0; res; res = res->ai_next) + { + if ((s = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) + { + if (res->ai_next) + continue; + sys_error ("socket"); + freeaddrinfo (res0); + return -1; + } + if (connect (s, res->ai_addr, res->ai_addrlen) < 0) + { + if (res->ai_next) + { + close (s); + continue; + } + e = errno; + sys_error ("connect"); + close (s); + freeaddrinfo (res0); + errno = e; + return -1; + } + freeaddrinfo (res0); + break; + } + return s; +} +#endif /* HAVE_GETADDRINFO */ + +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) + * if available, falling back to the traditional BSD mechanisms otherwise. + * Returns the connected socket or -1 on error. + */ +static int +_netopen(host, serv, typ) + char *host, *serv; + int typ; +{ +#ifdef HAVE_GETADDRINFO + return (_netopen6 (host, serv, typ)); +#else + return (_netopen4 (host, serv, typ)); +#endif +} + +/* + * Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to + * host `host' on port `port' and return the connected socket. + */ +int +netopen (path) + char *path; +{ + char *np, *s, *t; + int fd; + + np = (char *)xmalloc (strlen (path) + 1); + strcpy (np, path); + + s = np + 9; + t = strchr (s, '/'); + if (t == 0) + { + internal_error (_("%s: bad network path specification"), path); + return -1; + } + *t++ = '\0'; + fd = _netopen (s, t, path[5]); + free (np); + + return fd; +} + +#if 0 +/* + * Open a TCP connection to host `host' on the port defined for service + * `serv' and return the connected socket. + */ +int +tcpopen (host, serv) + char *host, *serv; +{ + return (_netopen (host, serv, 't')); +} + +/* + * Open a UDP connection to host `host' on the port defined for service + * `serv' and return the connected socket. + */ +int +udpopen (host, serv) + char *host, *serv; +{ + return _netopen (host, serv, 'u'); +} +#endif + +#else /* !HAVE_NETWORK */ + +int +netopen (path) + char *path; +{ + internal_error (_("network operations not supported")); + return -1; +} + +#endif /* !HAVE_NETWORK */ diff --git a/lib/sh/shmatch.c b/lib/sh/shmatch.c index 4508ed020..f03a2ee52 100644 --- a/lib/sh/shmatch.c +++ b/lib/sh/shmatch.c @@ -57,8 +57,7 @@ sh_regmatch (string, pattern, flags) char *subexp_str; int subexp_len; #endif - int result; - + int result; #if defined (ARRAY_VARS) rematch = (SHELL_VAR *)NULL; diff --git a/lib/sh/shmatch.c~ b/lib/sh/shmatch.c~ new file mode 100644 index 000000000..4508ed020 --- /dev/null +++ b/lib/sh/shmatch.c~ @@ -0,0 +1,121 @@ +/* Copyright (C) 2003 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +/* + * shmatch.c -- shell interface to posix regular expression matching. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined (HAVE_POSIX_REGEXP) + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "bashansi.h" + +#include +#include + +#include "shell.h" +#include "variables.h" +#include "externs.h" + +extern int glob_ignore_case, match_ignore_case; + +int +sh_regmatch (string, pattern, flags) + const char *string; + const char *pattern; + int flags; +{ + regex_t regex = { 0 }; + regmatch_t *matches; + int rflags; +#if defined (ARRAY_VARS) + SHELL_VAR *rematch; + ARRAY *amatch; + int subexp_ind; + char *subexp_str; + int subexp_len; +#endif + int result; + + +#if defined (ARRAY_VARS) + rematch = (SHELL_VAR *)NULL; +#endif + + rflags = REG_EXTENDED; + if (glob_ignore_case || match_ignore_case) + rflags |= REG_ICASE; +#if !defined (ARRAY_VARS) + rflags |= REG_NOSUB; +#endif + + if (regcomp (®ex, pattern, rflags)) + return 2; /* flag for printing a warning here. */ + +#if defined (ARRAY_VARS) + matches = (regmatch_t *)malloc (sizeof (regmatch_t) * (regex.re_nsub + 1)); +#else + matches = NULL; +#endif + + if (regexec (®ex, string, regex.re_nsub + 1, matches, 0)) + result = EXECUTION_FAILURE; + else + result = EXECUTION_SUCCESS; /* match */ + +#if defined (ARRAY_VARS) + subexp_len = strlen (string) + 10; + subexp_str = malloc (subexp_len + 1); + + /* Store the parenthesized subexpressions in the array BASH_REMATCH. + Element 0 is the portion that matched the entire regexp. Element 1 + is the part that matched the first subexpression, and so on. */ + unbind_variable ("BASH_REMATCH"); + rematch = make_new_array_variable ("BASH_REMATCH"); + amatch = array_cell (rematch); + + if ((flags & SHMAT_SUBEXP) && result == EXECUTION_SUCCESS && subexp_str) + { + for (subexp_ind = 0; subexp_ind <= regex.re_nsub; subexp_ind++) + { + memset (subexp_str, 0, subexp_len); + strncpy (subexp_str, string + matches[subexp_ind].rm_so, + matches[subexp_ind].rm_eo - matches[subexp_ind].rm_so); + array_insert (amatch, subexp_ind, subexp_str); + } + } + + VSETATTR (rematch, att_readonly); + + free (subexp_str); + free (matches); +#endif /* ARRAY_VARS */ + + regfree (®ex); + + return result; +} + +#endif /* HAVE_POSIX_REGEXP */ diff --git a/lib/sh/strtrans.c b/lib/sh/strtrans.c index 3f33d41b1..1f0290e3d 100644 --- a/lib/sh/strtrans.c +++ b/lib/sh/strtrans.c @@ -176,7 +176,7 @@ ansic_quote (str, flags, rlen) int flags, *rlen; { char *r, *ret, *s; - int l, rsize, t; + int l, rsize; unsigned char c; if (str == 0 || *str == 0) diff --git a/lib/sh/tmpfile.c b/lib/sh/tmpfile.c index fb7b732dd..5f2279d0e 100644 --- a/lib/sh/tmpfile.c +++ b/lib/sh/tmpfile.c @@ -47,6 +47,9 @@ extern int errno; extern pid_t dollar_dollar_pid; +static char *get_sys_tmpdir __P((void)); +static char *get_tmpdir __P((int)); + static char *sys_tmpdir = (char *)NULL; static int ntmpfiles; static int tmpnamelen = -1; @@ -55,8 +58,6 @@ static unsigned long filenum = 1L; static char * get_sys_tmpdir () { - struct stat sb; - if (sys_tmpdir) return sys_tmpdir; diff --git a/lib/sh/tmpfile.c~ b/lib/sh/tmpfile.c~ new file mode 100644 index 000000000..f6e04fd5e --- /dev/null +++ b/lib/sh/tmpfile.c~ @@ -0,0 +1,214 @@ +/* + * tmpfile.c - functions to create and safely open temp files for the shell. + */ + +/* Copyright (C) 2000 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include + +#include +#include +#include +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include +#include + +#include + +#ifndef errno +extern int errno; +#endif + +#define BASEOPENFLAGS (O_CREAT | O_TRUNC | O_EXCL) + +#define DEFAULT_TMPDIR "." /* bogus default, should be changed */ +#define DEFAULT_NAMEROOT "shtmp" + +extern pid_t dollar_dollar_pid; + +static char *sys_tmpdir = (char *)NULL; +static int ntmpfiles; +static int tmpnamelen = -1; +static unsigned long filenum = 1L; + +static char * +get_sys_tmpdir () +{ + if (sys_tmpdir) + return sys_tmpdir; + +#ifdef P_tmpdir + sys_tmpdir = P_tmpdir; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; +#endif + + sys_tmpdir = "/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = "/var/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = "/usr/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = DEFAULT_TMPDIR; + + return sys_tmpdir; +} + +static char * +get_tmpdir (flags) + int flags; +{ + char *tdir; + + tdir = (flags & MT_USETMPDIR) ? get_string_value ("TMPDIR") : (char *)NULL; + if (tdir == 0) + tdir = get_sys_tmpdir (); + +#if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX) + if (tmpnamelen == -1) + tmpnamelen = pathconf (tdir, _PC_NAME_MAX); +#else + tmpnamelen = 0; +#endif + + return tdir; +} + +char * +sh_mktmpname (nameroot, flags) + char *nameroot; + int flags; +{ + char *filename, *tdir, *lroot; + struct stat sb; + int r, tdlen; + + filename = (char *)xmalloc (PATH_MAX + 1); + tdir = get_tmpdir (flags); + tdlen = strlen (tdir); + + lroot = nameroot ? nameroot : DEFAULT_NAMEROOT; + +#ifdef USE_MKTEMP + sprintf (filename, "%s/%s.XXXXXX", tdir, lroot); + if (mktemp (filename) == 0) + { + free (filename); + filename = NULL; + } +#else /* !USE_MKTEMP */ + while (1) + { + filenum = (filenum << 1) ^ + (unsigned long) time ((time_t *)0) ^ + (unsigned long) dollar_dollar_pid ^ + (unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++); + sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum); + if (tmpnamelen > 0 && tmpnamelen < 32) + filename[tdlen + 1 + tmpnamelen] = '\0'; +# ifdef HAVE_LSTAT + r = lstat (filename, &sb); +# else + r = stat (filename, &sb); +# endif + if (r < 0 && errno == ENOENT) + break; + } +#endif /* !USE_MKTEMP */ + + return filename; +} + +int +sh_mktmpfd (nameroot, flags, namep) + char *nameroot; + int flags; + char **namep; +{ + char *filename, *tdir, *lroot; + int fd, tdlen; + + filename = (char *)xmalloc (PATH_MAX + 1); + tdir = get_tmpdir (flags); + tdlen = strlen (tdir); + + lroot = nameroot ? nameroot : DEFAULT_NAMEROOT; + +#ifdef USE_MKSTEMP + sprintf (filename, "%s/%s.XXXXXX", tdir, lroot); + fd = mkstemp (filename); + if (fd < 0 || namep == 0) + { + free (filename); + filename = NULL; + } + if (namep) + *namep = filename; + return fd; +#else /* !USE_MKSTEMP */ + do + { + filenum = (filenum << 1) ^ + (unsigned long) time ((time_t *)0) ^ + (unsigned long) dollar_dollar_pid ^ + (unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++); + sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum); + if (tmpnamelen > 0 && tmpnamelen < 32) + filename[tdlen + 1 + tmpnamelen] = '\0'; + fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600); + } + while (fd < 0 && errno == EEXIST); + + if (namep) + *namep = filename; + else + free (filename); + + return fd; +#endif /* !USE_MKSTEMP */ +} + +FILE * +sh_mktmpfp (nameroot, flags, namep) + char *nameroot; + int flags; + char **namep; +{ + int fd; + FILE *fp; + + fd = sh_mktmpfd (nameroot, flags, namep); + if (fd < 0) + return ((FILE *)NULL); + fp = fdopen (fd, (flags & MT_READWRITE) ? "w+" : "w"); + if (fp == 0) + close (fd); + return fp; +} diff --git a/lib/sh/winsize.c b/lib/sh/winsize.c index 8b39c99e7..f4696de0a 100644 --- a/lib/sh/winsize.c +++ b/lib/sh/winsize.c @@ -55,6 +55,7 @@ extern int shell_tty; #if defined (READLINE) extern void rl_set_screen_size __P((int, int)); #endif +extern void sh_set_lines_and_columns __P((int, int)); void get_new_window_size (from_sig, rp, cp) diff --git a/lib/sh/winsize.c~ b/lib/sh/winsize.c~ new file mode 100644 index 000000000..8b39c99e7 --- /dev/null +++ b/lib/sh/winsize.c~ @@ -0,0 +1,82 @@ +/* Handle window size changes and information. */ + +/* Copyright (C) 2005 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#include "config.h" + +#include + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include + +#if !defined (STRUCT_WINSIZE_IN_SYS_IOCTL) +/* For struct winsize on SCO */ +/* sys/ptem.h has winsize but needs mblk_t from sys/stream.h */ +# if defined (HAVE_SYS_PTEM_H) && defined (TIOCGWINSZ) && defined (SIGWINCH) +# if defined (HAVE_SYS_STREAM_H) +# include +# endif +# include +# endif /* HAVE_SYS_PTEM_H && TIOCGWINSZ && SIGWINCH */ +#endif /* !STRUCT_WINSIZE_IN_SYS_IOCTL */ + +#include + +/* Return the fd from which we are actually getting input. */ +#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr) + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int shell_tty; + +#if defined (READLINE) +extern void rl_set_screen_size __P((int, int)); +#endif + +void +get_new_window_size (from_sig, rp, cp) + int from_sig; + int *rp, *cp; +{ +#if defined (TIOCGWINSZ) + struct winsize win; + int tty; + + tty = input_tty (); + if (tty >= 0 && (ioctl (tty, TIOCGWINSZ, &win) == 0) && + win.ws_row > 0 && win.ws_col > 0) + { + sh_set_lines_and_columns (win.ws_row, win.ws_col); +#if defined (READLINE) + rl_set_screen_size (win.ws_row, win.ws_col); + if (rp) + *rp = win.ws_row; + if (cp) + *cp = win.ws_col; +#endif + } +#endif +} diff --git a/locale.c b/locale.c index 9eaa24ec2..1a222dc46 100644 --- a/locale.c +++ b/locale.c @@ -78,7 +78,6 @@ void set_default_locale_vars () { char *val; - int r; #if defined (HAVE_SETLOCALE) diff --git a/make_cmd.c b/make_cmd.c index df2001056..82047ea52 100644 --- a/make_cmd.c +++ b/make_cmd.c @@ -750,7 +750,6 @@ make_function_def (name, command, lineno, lstart) #if defined (ARRAY_VARS) SHELL_VAR *bash_source_v; ARRAY *bash_source_a; - char *t; #endif temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF)); diff --git a/parse.y b/parse.y index 87781e519..c7dd436f6 100644 --- a/parse.y +++ b/parse.y @@ -205,10 +205,6 @@ static void reset_readline_prompt __P((void)); #endif static void print_prompt __P((void)); -#if defined (HISTORY) -char *history_delimiting_chars __P((void)); -#endif - #if defined (HANDLE_MULTIBYTE) static void set_line_mbstate __P((void)); static char *shell_input_line_property = NULL; @@ -1876,7 +1872,6 @@ shell_getc (remove_quoted_newline) register int i; int c; unsigned char uc; - static int mustpop = 0; QUIT; @@ -2956,8 +2951,8 @@ static int parse_dparen (c) int c; { - int cmdtyp, len, sline; - char *wval, *wv2; + int cmdtyp, sline; + char *wval; WORD_DESC *wd; #if defined (ARITH_FOR_COMMAND) @@ -4160,7 +4155,7 @@ decode_prompt_string (string) case 'W': { /* Use the value of PWD because it is much more efficient. */ - char t_string[PATH_MAX], *t; + char t_string[PATH_MAX]; int tlen; temp = get_string_value ("PWD"); @@ -4371,15 +4366,15 @@ yyerror (msg) } static char * -error_token_from_token (token) - int token; +error_token_from_token (tok) + int tok; { char *t; - if (t = find_token_in_alist (token, word_token_alist, 0)) + if (t = find_token_in_alist (tok, word_token_alist, 0)) return t; - if (t = find_token_in_alist (token, other_token_alist, 0)) + if (t = find_token_in_alist (tok, other_token_alist, 0)) return t; t = (char *)NULL; diff --git a/parse.y~ b/parse.y~ index 1034c8040..f654d1031 100644 --- a/parse.y~ +++ b/parse.y~ @@ -1876,7 +1876,6 @@ shell_getc (remove_quoted_newline) register int i; int c; unsigned char uc; - static int mustpop = 0; QUIT; @@ -2563,7 +2562,7 @@ read_token (command) #endif /* ALIAS */ /* Read a single word from input. Start by skipping blanks. */ - while ((character = shell_getc (1)) != EOF && whitespace (character)) + while ((character = shell_getc (1)) != EOF && shellblank (character)) ; if (character == EOF) @@ -2956,8 +2955,8 @@ static int parse_dparen (c) int c; { - int cmdtyp, len, sline; - char *wval, *wv2; + int cmdtyp, sline; + char *wval; WORD_DESC *wd; #if defined (ARITH_FOR_COMMAND) @@ -4160,7 +4159,7 @@ decode_prompt_string (string) case 'W': { /* Use the value of PWD because it is much more efficient. */ - char t_string[PATH_MAX], *t; + char t_string[PATH_MAX]; int tlen; temp = get_string_value ("PWD"); @@ -4371,15 +4370,15 @@ yyerror (msg) } static char * -error_token_from_token (token) - int token; +error_token_from_token (tok) + int tok; { char *t; - if (t = find_token_in_alist (token, word_token_alist, 0)) + if (t = find_token_in_alist (tok, word_token_alist, 0)) return t; - if (t = find_token_in_alist (token, other_token_alist, 0)) + if (t = find_token_in_alist (tok, other_token_alist, 0)) return t; t = (char *)NULL; diff --git a/pcomplete.c b/pcomplete.c index ee462dc88..471fb54f9 100644 --- a/pcomplete.c +++ b/pcomplete.c @@ -815,7 +815,7 @@ gen_wordlist_matches (cs, text) { WORD_LIST *l, *l2; STRINGLIST *sl; - int nw, tlen, qc; + int nw, tlen; char *ntxt; /* dequoted TEXT to use in comparisons */ if (cs->words == 0 || cs->words[0] == '\0') diff --git a/subst.c b/subst.c index 5f06dd575..6a180aa77 100644 --- a/subst.c +++ b/subst.c @@ -1583,7 +1583,8 @@ split_at_delims (string, slen, delims, sentinel, nwp, cwp) while (delims[i]) { #if defined (HANDLE_MULTIBYTE) - mbstate_t state_bak = state; + mbstate_t state_bak; + state_bak = state; mblength = MBRLEN (delims + i, slength, &state); if (MB_INVALIDCH (mblength)) state = state_bak; @@ -2035,7 +2036,7 @@ list_string (string, separators, quoted) sindex++; /* An IFS character that is not IFS white space, along with any adjacent IFS white space, shall delimit a field. (SUSv3) */ - while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + while (string[sindex] && spctabnl (string[sindex]) && isifs (string[sindex])) sindex++; } } @@ -2223,7 +2224,7 @@ do_compound_assignment (name, value, flags) int flags; { SHELL_VAR *v; - int off, mklocal; + int mklocal; WORD_LIST *list; mklocal = flags & ASS_MKLOCAL; @@ -2254,7 +2255,7 @@ do_assignment_internal (word, expand) int expand; { int offset, tlen, appendop, assign_list, aflags; - char *name, *value, *ovalue, *nvalue; + char *name, *value; SHELL_VAR *entry; #if defined (ARRAY_VARS) char *t; @@ -2286,11 +2287,7 @@ do_assignment_internal (word, expand) tlen = STRLEN (temp); #if defined (ARRAY_VARS) -# if 0 - if (expand && temp[0] == LPAREN && temp[tlen-1] == RPAREN) -#else if (expand && (word->flags & W_COMPASSIGN)) -#endif { assign_list = ni = 1; value = extract_array_assignment_list (temp, &ni); @@ -3346,9 +3343,8 @@ remove_wpattern (wparam, wstrlen, wpattern, op) wchar_t *wpattern; int op; { - wchar_t wc; - int n, n1; - wchar_t *ret; + wchar_t wc, *ret; + int n; switch (op) { @@ -3802,7 +3798,9 @@ getpattern (value, quoted, expandpat) { char *pat, *tword; WORD_LIST *l; +#if 0 int i; +#endif /* There is a problem here: how to handle single or double quotes in the pattern string when the whole expression is between double quotes? @@ -5655,7 +5653,7 @@ pos_params_pat_subst (string, pat, rep, mflags) { WORD_LIST *save, *params; WORD_DESC *w; - char *ret, *tt; + char *ret; save = params = list_rest_of_args (); if (save == 0) @@ -7576,9 +7574,6 @@ word_list_split (list) for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) { tresult = word_split (t->word, ifs_value); -#if 0 - result = (WORD_LIST *) list_append (result, tresult); -#else if (result == 0) result = e = tresult; else @@ -7587,7 +7582,6 @@ word_list_split (list) while (e->next) e = e->next; } -#endif } return (result); } diff --git a/subst.c~ b/subst.c~ index cc1cfda8d..4a7d266e6 100644 --- a/subst.c~ +++ b/subst.c~ @@ -949,8 +949,8 @@ string_extract_verbatim (string, slen, sindex, charlist) len = mbstowcs (wcharlist, charlist, 0); if (len == -1) len = 0; - wcharlist = (wchar_t *)xmalloc ((sizeof (wchar_t) * len) + 1); - mbstowcs (wcharlist, charlist, len); + wcharlist = (wchar_t *)xmalloc (sizeof (wchar_t) * (len + 1)); + mbstowcs (wcharlist, charlist, len + 1); } if (wcschr (wcharlist, wc)) @@ -1583,7 +1583,8 @@ split_at_delims (string, slen, delims, sentinel, nwp, cwp) while (delims[i]) { #if defined (HANDLE_MULTIBYTE) - mbstate_t state_bak = state; + mbstate_t state_bak; + state_bak = state; mblength = MBRLEN (delims + i, slength, &state); if (MB_INVALIDCH (mblength)) state = state_bak; @@ -2035,7 +2036,7 @@ list_string (string, separators, quoted) sindex++; /* An IFS character that is not IFS white space, along with any adjacent IFS white space, shall delimit a field. (SUSv3) */ - while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + while (string[sindex] && spctabnl (string[sindex]) && isifs (string[sindex])) sindex++; } } @@ -2223,7 +2224,7 @@ do_compound_assignment (name, value, flags) int flags; { SHELL_VAR *v; - int off, mklocal; + int mklocal; WORD_LIST *list; mklocal = flags & ASS_MKLOCAL; @@ -2254,7 +2255,7 @@ do_assignment_internal (word, expand) int expand; { int offset, tlen, appendop, assign_list, aflags; - char *name, *value, *ovalue, *nvalue; + char *name, *value; SHELL_VAR *entry; #if defined (ARRAY_VARS) char *t; @@ -2286,11 +2287,7 @@ do_assignment_internal (word, expand) tlen = STRLEN (temp); #if defined (ARRAY_VARS) -# if 0 - if (expand && temp[0] == LPAREN && temp[tlen-1] == RPAREN) -#else if (expand && (word->flags & W_COMPASSIGN)) -#endif { assign_list = ni = 1; value = extract_array_assignment_list (temp, &ni); @@ -3346,9 +3343,8 @@ remove_wpattern (wparam, wstrlen, wpattern, op) wchar_t *wpattern; int op; { - wchar_t wc; - int n, n1; - wchar_t *ret; + wchar_t wc, *ret; + int n; switch (op) { @@ -3802,7 +3798,9 @@ getpattern (value, quoted, expandpat) { char *pat, *tword; WORD_LIST *l; +#if 0 int i; +#endif /* There is a problem here: how to handle single or double quotes in the pattern string when the whole expression is between double quotes? @@ -5655,7 +5653,7 @@ pos_params_pat_subst (string, pat, rep, mflags) { WORD_LIST *save, *params; WORD_DESC *w; - char *ret, *tt; + char *ret; save = params = list_rest_of_args (); if (save == 0) diff --git a/trap.c b/trap.c index 64cdbd086..2d5934f1a 100644 --- a/trap.c +++ b/trap.c @@ -76,7 +76,6 @@ static void restore_signal __P((int)); static void reset_or_restore_signal_handlers __P((sh_resetsig_func_t *)); /* Variables used here but defined in other files. */ -extern int interrupt_immediately; extern int last_command_exit_value; extern int line_number; diff --git a/trap.c~ b/trap.c~ index 9ada606d5..64cdbd086 100644 --- a/trap.c~ +++ b/trap.c~ @@ -888,18 +888,13 @@ reset_or_restore_signal_handlers (reset) `functrace' or `errtrace' options have been set, then let command substitutions inherit them. Let command substitution inherit the RETURN trap if we're in the debugger and tracing functions. */ -#if defined (DEBUGGER) - if (debugging_mode == 0 && function_trace_mode == 0) -#endif - sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED; -#if defined (DEBUGGER) - if (debugging_mode == 0 && error_trace_mode == 0) -#endif + if (function_trace_mode == 0) + { + sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED; + sigmodes[RETURN_TRAP] &= ~SIG_TRAPPED; + } + if (error_trace_mode == 0) sigmodes[ERROR_TRAP] &= ~SIG_TRAPPED; -#if defined (DEBUGGER) - if (debugging_mode == 0 && function_trace_mode == 0) -#endif - sigmodes[RETURN_TRAP] &= ~SIG_TRAPPED; } /* Reset trapped signals to their original values, but don't free the diff --git a/unwind_prot.c b/unwind_prot.c index 4bb7a78f0..4fd194e8f 100644 --- a/unwind_prot.c +++ b/unwind_prot.c @@ -73,8 +73,6 @@ typedef union uwp { } UNWIND_ELT; -extern int interrupt_immediately; - static void without_interrupts __P((VFunction *, char *, char *)); static void unwind_frame_discard_internal __P((char *, char *)); static void unwind_frame_run_internal __P((char *, char *)); diff --git a/unwind_prot.c~ b/unwind_prot.c~ new file mode 100644 index 000000000..4bb7a78f0 --- /dev/null +++ b/unwind_prot.c~ @@ -0,0 +1,320 @@ +/* I can't stand it anymore! Please can't we just write the + whole Unix system in lisp or something? */ + +/* Copyright (C) 1987-2002 Free Software Foundation, Inc. + +This file is part of GNU Bash, the Bourne Again SHell. + +Bash is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +/* **************************************************************** */ +/* */ +/* Unwind Protection Scheme for Bash */ +/* */ +/* **************************************************************** */ +#include "config.h" + +#include "bashtypes.h" +#include "bashansi.h" + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#if STDC_HEADERS +# include +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#include "command.h" +#include "general.h" +#include "unwind_prot.h" +#include "quit.h" +#include "sig.h" + +/* Structure describing a saved variable and the value to restore it to. */ +typedef struct { + char *variable; + int size; + char desired_setting[1]; /* actual size is `size' */ +} SAVED_VAR; + +/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to. + If HEAD.CLEANUP is restore_variable, then SV.V contains the saved + variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */ +typedef union uwp { + struct uwp_head { + union uwp *next; + Function *cleanup; + } head; + struct { + struct uwp_head uwp_head; + char *v; + } arg; + struct { + struct uwp_head uwp_head; + SAVED_VAR v; + } sv; +} UNWIND_ELT; + + +extern int interrupt_immediately; + +static void without_interrupts __P((VFunction *, char *, char *)); +static void unwind_frame_discard_internal __P((char *, char *)); +static void unwind_frame_run_internal __P((char *, char *)); +static void add_unwind_protect_internal __P((Function *, char *)); +static void remove_unwind_protect_internal __P((char *, char *)); +static void run_unwind_protects_internal __P((char *, char *)); +static void clear_unwind_protects_internal __P((char *, char *)); +static inline void restore_variable __P((SAVED_VAR *)); +static void unwind_protect_mem_internal __P((char *, char *)); + +static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL; + +#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT)) +#define uwpfree(elt) free(elt) + +/* Run a function without interrupts. This relies on the fact that the + FUNCTION cannot change the value of interrupt_immediately. (I.e., does + not call QUIT (). */ +static void +without_interrupts (function, arg1, arg2) + VFunction *function; + char *arg1, *arg2; +{ + int old_interrupt_immediately; + + old_interrupt_immediately = interrupt_immediately; + interrupt_immediately = 0; + + (*function)(arg1, arg2); + + interrupt_immediately = old_interrupt_immediately; +} + +/* Start the beginning of a region. */ +void +begin_unwind_frame (tag) + char *tag; +{ + add_unwind_protect ((Function *)NULL, tag); +} + +/* Discard the unwind protects back to TAG. */ +void +discard_unwind_frame (tag) + char *tag; +{ + if (unwind_protect_list) + without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL); +} + +/* Run the unwind protects back to TAG. */ +void +run_unwind_frame (tag) + char *tag; +{ + if (unwind_protect_list) + without_interrupts (unwind_frame_run_internal, tag, (char *)NULL); +} + +/* Add the function CLEANUP with ARG to the list of unwindable things. */ +void +add_unwind_protect (cleanup, arg) + Function *cleanup; + char *arg; +{ + without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg); +} + +/* Remove the top unwind protect from the list. */ +void +remove_unwind_protect () +{ + if (unwind_protect_list) + without_interrupts + (remove_unwind_protect_internal, (char *)NULL, (char *)NULL); +} + +/* Run the list of cleanup functions in unwind_protect_list. */ +void +run_unwind_protects () +{ + if (unwind_protect_list) + without_interrupts + (run_unwind_protects_internal, (char *)NULL, (char *)NULL); +} + +/* Erase the unwind-protect list. If flags is 1, free the elements. */ +void +clear_unwind_protect_list (flags) + int flags; +{ + char *flag; + + if (unwind_protect_list) + { + flag = flags ? "" : (char *)NULL; + without_interrupts + (clear_unwind_protects_internal, flag, (char *)NULL); + } +} + +/* **************************************************************** */ +/* */ +/* The Actual Functions */ +/* */ +/* **************************************************************** */ + +static void +add_unwind_protect_internal (cleanup, arg) + Function *cleanup; + char *arg; +{ + UNWIND_ELT *elt; + + uwpalloc (elt); + elt->head.next = unwind_protect_list; + elt->head.cleanup = cleanup; + elt->arg.v = arg; + unwind_protect_list = elt; +} + +static void +remove_unwind_protect_internal (ignore1, ignore2) + char *ignore1, *ignore2; +{ + UNWIND_ELT *elt; + + elt = unwind_protect_list; + if (elt) + { + unwind_protect_list = unwind_protect_list->head.next; + uwpfree (elt); + } +} + +static void +run_unwind_protects_internal (ignore1, ignore2) + char *ignore1, *ignore2; +{ + unwind_frame_run_internal ((char *) NULL, (char *) NULL); +} + +static void +clear_unwind_protects_internal (flag, ignore) + char *flag, *ignore; +{ + if (flag) + { + while (unwind_protect_list) + remove_unwind_protect_internal ((char *)NULL, (char *)NULL); + } + unwind_protect_list = (UNWIND_ELT *)NULL; +} + +static void +unwind_frame_discard_internal (tag, ignore) + char *tag, *ignore; +{ + UNWIND_ELT *elt; + + while (elt = unwind_protect_list) + { + unwind_protect_list = unwind_protect_list->head.next; + if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag))) + { + uwpfree (elt); + break; + } + else + uwpfree (elt); + } +} + +/* Restore the value of a variable, based on the contents of SV. + sv->desired_setting is a block of memory SIZE bytes long holding the + value itself. This block of memory is copied back into the variable. */ +static inline void +restore_variable (sv) + SAVED_VAR *sv; +{ + FASTCOPY (sv->desired_setting, sv->variable, sv->size); +} + +static void +unwind_frame_run_internal (tag, ignore) + char *tag, *ignore; +{ + UNWIND_ELT *elt; + + while (elt = unwind_protect_list) + { + unwind_protect_list = elt->head.next; + + /* If tag, then compare. */ + if (!elt->head.cleanup) + { + if (tag && STREQ (elt->arg.v, tag)) + { + uwpfree (elt); + break; + } + } + else + { + if (elt->head.cleanup == (Function *) restore_variable) + restore_variable (&elt->sv.v); + else + (*(elt->head.cleanup)) (elt->arg.v); + } + + uwpfree (elt); + } +} + +static void +unwind_protect_mem_internal (var, psize) + char *var; + char *psize; +{ + int size, allocated; + UNWIND_ELT *elt; + + size = *(int *) psize; + allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]); + elt = (UNWIND_ELT *)xmalloc (allocated); + elt->head.next = unwind_protect_list; + elt->head.cleanup = (Function *) restore_variable; + elt->sv.v.variable = var; + elt->sv.v.size = size; + FASTCOPY (var, elt->sv.v.desired_setting, size); + unwind_protect_list = elt; +} + +/* Save the value of a variable so it will be restored when unwind-protects + are run. VAR is a pointer to the variable. SIZE is the size in + bytes of VAR. */ +void +unwind_protect_mem (var, size) + char *var; + int size; +{ + without_interrupts (unwind_protect_mem_internal, var, (char *) &size); +} diff --git a/variables.c b/variables.c index f9b9de79e..fd228b5ee 100644 --- a/variables.c +++ b/variables.c @@ -1306,20 +1306,11 @@ static SHELL_VAR * get_comp_wordbreaks (var) SHELL_VAR *var; { - char *p; - /* If we don't have anything yet, assign a default value. */ if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0) enable_hostname_completion (perform_hostname_completion); -#if 0 - FREE (value_cell (var)); - p = savestring (rl_completer_word_break_characters); - - var_setvalue (var, p); -#else var_setvalue (var, rl_completer_word_break_characters); -#endif return (var); } @@ -2033,16 +2024,11 @@ bind_int_variable (lhs, rhs) char *lhs, *rhs; { register SHELL_VAR *v; - char *t; int isint, isarr; isint = isarr = 0; #if defined (ARRAY_VARS) -# if 0 - if (t = xstrchr (lhs, '[')) /*]*/ -# else if (valid_array_reference (lhs)) -# endif { isarr = 1; v = array_variable_part (lhs, (char **)0, (int *)0); diff --git a/variables.h b/variables.h index eec26c31a..00e6ca272 100644 --- a/variables.h +++ b/variables.h @@ -330,7 +330,6 @@ extern int get_random_number __P((void)); extern void sv_ifs __P((char *)); extern void sv_path __P((char *)); extern void sv_mail __P((char *)); -extern void sv_comp_wordbreaks __P((char *)); extern void sv_globignore __P((char *)); extern void sv_ignoreeof __P((char *)); extern void sv_strict_posix __P((char *)); -- 2.47.3