summaryrefslogtreecommitdiff
path: root/builtins
diff options
context:
space:
mode:
authorChet Ramey <chet.ramey@case.edu>2011-12-03 22:51:04 -0500
committerChet Ramey <chet.ramey@case.edu>2011-12-03 22:51:04 -0500
commit5cef52a76316d98edb20e26bf793694592066f1e (patch)
treed0c024856bdbe73577e79165d814f980e7818727 /builtins
parent9697d2dc759bd96fcf257923ea7f0c888118be77 (diff)
downloadbash-5cef52a76316d98edb20e26bf793694592066f1e.tar.gz
commit bash-20060727 snapshot
Diffstat (limited to 'builtins')
-rw-r--r--builtins/caller.def1
-rw-r--r--builtins/cd.def1
-rw-r--r--builtins/cd.def~525
-rw-r--r--builtins/common.c3
-rw-r--r--builtins/echo.def2
-rw-r--r--builtins/echo.def~183
-rw-r--r--builtins/evalfile.c2
-rw-r--r--builtins/evalfile.c~1
-rw-r--r--builtins/evalstring.c5
-rw-r--r--builtins/evalstring.c~352
-rw-r--r--builtins/fc.def1
-rw-r--r--builtins/fc.def~631
-rw-r--r--builtins/printf.def3
-rw-r--r--builtins/printf.def~1008
-rw-r--r--builtins/set.def1
-rw-r--r--builtins/source.def4
-rw-r--r--builtins/source.def~174
-rw-r--r--builtins/suspend.def4
-rw-r--r--builtins/suspend.def~119
-rw-r--r--builtins/trap.def2
-rw-r--r--builtins/wait.def1
-rw-r--r--builtins/wait.def~177
22 files changed, 3182 insertions, 18 deletions
diff --git a/builtins/caller.def b/builtins/caller.def
index 5142cab9..b6e82388 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 025e4f5e..54e328e4 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 00000000..025e4f5e
--- /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 <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include "posixdir.h"
+#include "posixstat.h"
+#ifndef _MINIX
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include <errno.h>
+#include <tilde/tilde.h>
+
+#include "../shell.h"
+#include "../flags.h"
+#include "maxpath.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int posixly_correct;
+extern int array_needs_making;
+extern 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 9d8f09ab..7b9613e1 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 6792659a..0effc894 100644
--- a/builtins/echo.def
+++ b/builtins/echo.def
@@ -31,6 +31,8 @@ $PRODUCES echo.c
#include <stdio.h>
#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 00000000..6792659a
--- /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 <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "../bashansi.h"
+
+#include <stdio.h>
+#include "../shell.h"
+
+$BUILTIN echo
+$FUNCTION echo_builtin
+$DEPENDS_ON V9_ECHO
+$SHORT_DOC echo [-neE] [arg ...]
+Output the ARGs. If -n is specified, the trailing newline is
+suppressed. If the -e option is given, interpretation of the
+following backslash-escaped characters is turned on:
+ \a alert (bell)
+ \b backspace
+ \c suppress trailing newline
+ \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 -<nonopt> both mean to just echo the arguments. */
+ if (*temp == 0 || temp[i])
+ break;
+
+ /* All of the options in TEMP are valid options to ECHO.
+ Handle them. */
+ while (i = *temp++)
+ {
+ switch (i)
+ {
+ case 'n':
+ display_return = 0;
+ break;
+#if defined (V9_ECHO)
+ case 'e':
+ do_v9 = 1;
+ break;
+ case 'E':
+ do_v9 = 0;
+ break;
+#endif /* V9_ECHO */
+ default:
+ goto just_echo; /* XXX */
+ }
+ }
+ }
+
+just_echo:
+
+ clearerr (stdout); /* clear error before writing and testing success */
+
+ 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 81be017b..d05bc7bb 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 75314e2b..81be017b 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 04afac3d..36f0ad39 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 00000000..610fa9ca
--- /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 <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+
+#include <errno.h>
+
+#include "filecntl.h"
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "../jobs.h"
+#include "../builtins.h"
+#include "../flags.h"
+#include "../input.h"
+#include "../execute_cmd.h"
+#include "../redir.h"
+#include "../trap.h"
+
+#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 $( <file ). */
+ if (startup_state == 2 &&
+ (subshell_environment & SUBSHELL_COMSUB) &&
+ *bash_input.location.string == '\0' &&
+ command->type == cm_simple && !command->redirects &&
+ (command->flags & CMD_TIME_PIPELINE) == 0 &&
+ command->value.Simple->words == 0 &&
+ command->value.Simple->redirects &&
+ command->value.Simple->redirects->next == 0 &&
+ command->value.Simple->redirects->instruction == r_input_direction)
+ {
+ int r;
+ r = cat_file (command->value.Simple->redirects);
+ last_result = (r < 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+ }
+ else
+ last_result = execute_command_internal
+ (command, 0, NO_PIPE, NO_PIPE, bitmap);
+
+ dispose_command (command);
+ dispose_fd_bitmap (bitmap);
+ discard_unwind_frame ("pe_dispose");
+ }
+ }
+ else
+ {
+ last_result = EXECUTION_FAILURE;
+
+ /* 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 ebe36832..101eb008 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 00000000..ebe36832
--- /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 <config.h>
+
+#if defined (HISTORY)
+#ifndef _MINIX
+# include <sys/param.h>
+#endif
+#include "../bashtypes.h"
+#include "posixstat.h"
+#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <chartypes.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+#include <errno.h>
+
+#include "../shell.h"
+#include "../builtins.h"
+#include "../flags.h"
+#include "../bashhist.h"
+#include "maxpath.h"
+#include <readline/history.h>
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int 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 c2fa5c4e..3658b48a 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 00000000..67e978eb
--- /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 <config.h>
+
+#include "../bashtypes.h"
+
+#include <errno.h>
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#else
+ /* Assume 32-bit ints. */
+# define INT_MAX 2147483647
+# define INT_MIN (-2147483647-1)
+#endif
+
+#include <stdio.h>
+#include <chartypes.h>
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#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 3bb32704..d1086695 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 f9f812f8..9576f09e 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 00000000..f9f812f8
--- /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 <config.h>
+
+#include "../bashtypes.h"
+#include "posixstat.h"
+#include "filecntl.h"
+#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include <errno.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "../flags.h"
+#include "../findcmd.h"
+#include "common.h"
+#include "bashgetopt.h"
+#include "../trap.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#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 d616d775..ea86ae22 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 00000000..d616d775
--- /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 <config.h>
+
+#if defined (JOB_CONTROL)
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include <signal.h>
+#include "../bashintl.h"
+#include "../shell.h"
+#include "../jobs.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+static SigHandler *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 426833a1..2735791e 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 22a92bea..a3095952 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 00000000..22a92bea
--- /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 <config.h>
+
+#include "../bashtypes.h"
+#include <signal.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <chartypes.h>
+
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "../jobs.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+extern int 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> [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);
+}