Makefile.in
- use $(SIZE) instead of size; set SIZE from configure
+ 1/31
+ ----
+arrayfunc.c
+ - in array_value_internal, return NULL right away if the variable's
+ value is NULL, instead of passing a null string to add_string_to_list
+
2/1
---
jobs.h
a job index
- new function, reset_job_indices, called from delete_job if
js.j_lastj or js.j_firstj are removed
+ - change various functions to keep counters and stats in struct jobstats
pcomplete.c, builtins/common.c, builtins/{exit,fg_bg,jobs,kill,wait}.def
- change global variables (e.g., job_slots) to struct members
- use get_job_by_jid and J_JOBSTATE where appropriate
trap.c
- - change reset_or_restore_signal_handler to not free the trap
+ - change reset_or_restore_signal_handler to not free the exit trap
string if the function pointer is reset_signal, which is used when
the trap strings shouldn't be freed, like in command substitution
(echo 6))
work right
+
+ 2/8
+ ---
+jobs.h
+ - new structs for holding status of exited background processes, as
+ POSIX specifies
+ - new job flag: J_ASYNC
+
+jobs.c
+ - new functions to manipulate struct holding status of exited
+ background processes
+ - new members in struct jobstats to hold pointer to last created job
+ and last created asynchronous job
+ - initialize js.c_childmax in initialize_job_control
+ - if the `async' arg to stop_pipeline is non-null, set the J_ASYNC
+ flag in the job struct
+ - set js.j_last_made_job and js.j_last_asynchronous_job in
+ stop_pipeline
+ - new function: find_last_proc, returns the PROCESS * to the last proc
+ in a job's pipeline
+ - changed find_last_pid to call find_last_proc
+ - change delete_job to call bgp_add on the last proc of the job being
+ deleted
+ - change delete_all_jobs and wait_for_background_pids to call bgp_clear
+
+ 2/9
+ ---
+jobs.c
+ - change wait_for_single_pid to look for pid in bgpids.list (using
+ bgp_search()) if find_pipeline returns NULL
+
+ 2/10
+ ----
+support/shobj-conf
+ - change the solaris-gcc stanza so that it auto-selects the appropriate
+ options for ld depending on which `ld' gcc says it's going to run
+
+ 2/11
+ ----
+jobs.h
+ - add support for PS_RECYCLED as a process state, add PRECYCLED macro
+ to test it. Change PALIVE and PRUNNING macros to not count processes
+ in PS_RECYCLED state
+
+execute_cmd.c
+ - restore use of last_pid as sentinel value; use NO_PID as sentinel
+ only if RECYCLES_PIDS is defined
+
+jobs.c
+ - change find_job to return a pointer to the PROCESS the desired pid
+ belongs to, analogous to find_pipeline returning pointer to JOB
+ - change find_job callers to add extra argument
+ - change running_only arguments to find_pipeline and find_job to
+ alive_only, since we don't want recycled pids returned here and it
+ better describes the result
+ - new function find_process, calls find_pipeline and searches the
+ returned pipeline for the PROCESS * describing the desired pid
+ - in make_child, if fork() returns the same pid as the value of
+ last_asynchronous_pid when RECYCLES_PIDS is defined, avoid pid
+ aliasing by resetting last_asynchronous_pid to 1
+ - use PRUNNING instead of child->running, since we, for the most
+ part, don't want to consider recycled pids (e.g., in make_child())
+ - call find_process instead of find_pipeline in waitchld()
+ - use PEXITED(p) instead of testing p->running == PS_DONE
+ - in make_child, call bgp_delete to remove a just-created pid from the
+ last of saved pid statuses
+ - in add_process, check whether or not pid being added is already in
+ the_pipeline or the jobs list (using find_process) and mark it as
+ recycled if so
+ - This set of fixes mostly came from Pierre Humblet
+ <pierre.humblet@ieee.org> to fix pid aliasing and reuse problems on
+ cygwin
+
+variables.c
+ - set $_ from the environment if we get it there, set to $0 by
+ default if not in env
+
+doc/{bashref.texi,bash.1}
+ - a couple of clarifying changes to the description of $_ based on
+ comments from Glenn Morris <gmorris+mail@ast.cam.ac.uk>
Makefile.in
- use $(SIZE) instead of size; set SIZE from configure
+ 1/31
+ ----
+arrayfunc.c
+ - in array_value_internal, return NULL right away if the variable's
+ value is NULL, instead of passing a null string to add_string_to_list
+
2/1
---
jobs.h
a job index
- new function, reset_job_indices, called from delete_job if
js.j_lastj or js.j_firstj are removed
+ - change various functions to keep counters and stats in struct jobstats
pcomplete.c, builtins/common.c, builtins/{exit,fg_bg,jobs,kill,wait}.def
- change global variables (e.g., job_slots) to struct members
- use get_job_by_jid and J_JOBSTATE where appropriate
trap.c
- - change reset_or_restore_signal_handler to not free the trap
+ - change reset_or_restore_signal_handler to not free the exit trap
string if the function pointer is reset_signal, which is used when
the trap strings shouldn't be freed, like in command substitution
list if it reaches MAX_JOBS_IN_ARRAY in size
parse.y
- - move test for backslash-newline after pop_string in read_token so
+ - move test for backslash-newline after pop_string in shell_getc so
that things like
((echo 5) \
(echo 6))
work right
+
+ 2/8
+ ---
+jobs.h
+ - new structs for holding status of exited background processes, as
+ POSIX specifies
+ - new job flag: J_ASYNC
+
+jobs.c
+ - new functions to manipulate struct holding status of exited
+ background processes
+ - new members in struct jobstats to hold pointer to last created job
+ and last created asynchronous job
+ - initialize js.c_childmax in initialize_job_control
+ - if the `async' arg to stop_pipeline is non-null, set the J_ASYNC
+ flag in the job struct
+ - set js.j_last_made_job and js.j_last_asynchronous_job in
+ stop_pipeline
+ - new function: find_last_proc, returns the PROCESS * to the last proc
+ in a job's pipeline
+ - changed find_last_pid to call find_last_proc
+ - change delete_job to call bgp_add on the last proc of the job being
+ deleted
+ - change delete_all_jobs and wait_for_background_pids to call bgp_clear
+
+ 2/9
+ ---
+jobs.c
+ - change wait_for_single_pid to look for pid in bgpids.list (using
+ bgp_search()) if find_pipeline returns NULL
+
+ 2/10
+ ----
+support/shobj-conf
+ - change the solaris-gcc stanza so that it auto-selects the appropriate
+ options for ld depending on which `ld' gcc says it's going to run
+
+ 2/11
+ ----
+jobs.h
+ - add support for PS_RECYCLED as a process state, add PRECYCLED macro
+ to test it. Change PALIVE and PRUNNING macros to not count processes
+ in PS_RECYCLED state
+
+execute_cmd.c
+ - restore use of last_pid as sentinel value; use NO_PID as sentinel
+ only if RECYCLES_PIDS is defined
+
+jobs.c
+ - change find_job to return a pointer to the PROCESS the desired pid
+ belongs to, analogous to find_pipeline returning pointer to JOB
+ - change find_job callers to add extra argument
+ - change running_only arguments to find_pipeline and find_job to
+ alive_only, since we don't want recycled pids returned here and it
+ better describes the result
+ - new function find_process, calls find_pipeline and searches the
+ returned pipeline for the PROCESS * describing the desired pid
+ - in make_child, if fork() returns the same pid as the value of
+ last_asynchronous_pid when RECYCLES_PIDS is defined, avoid pid
+ aliasing by resetting last_asynchronous_pid to 1
+ - use PRUNNING instead of child->running, since we, for the most
+ part, don't want to consider recycled pids (e.g., in make_child())
+ - call find_process instead of find_pipeline in waitchld()
+ - use PEXITED(p) instead of testing p->running == PS_DONE
+ - in make_child, call bgp_delete to remove a just-created pid from the
+ last of saved pid statuses
+ - in add_process, check whether or not pid being added is already in
+ the_pipeline or the jobs list (using find_process) and mark it as
+ recycled if so
+ - This set of fixes mostly came from Pierre Humblet
+ <pierre.humblet@ieee.org> to fix pid aliasing and reuse problems on
+ cygwin
+
+doc/{bashref.texi,bash.1}
+ - a couple of clarifying changes to the description of $_ based on
+ comments from Glenn Morris <gmorris+mail@ast.cam.ac.uk>
/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
-/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
if (integer_p (var))
this_command_name = (char *)NULL; /* no command name for errors */
-#if 0
- nval = make_variable_value (var, val, flags);
- if (var->assign_func)
- (*var->assign_func) (var, nval, ind);
- else
- array_insert (a, ind, nval);
- FREE (nval);
-#else
bind_array_var_internal (var, ind, val, flags);
-#endif
last_ind++;
}
err_badarraysub (s);
return ((char *)NULL);
}
- else if (var == 0)
+ else if (var == 0 || value_cell (var) == 0)
return ((char *)NULL);
else if (array_p (var) == 0)
l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
# This Makefile for building libbuiltins.a is in -*- text -*- for Emacs.
#
-# Copyright (C) 1996-2003 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
exec.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/flags.h
exec.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
exec.o: $(srcdir)/common.h $(topdir)/execute_cmd.h $(BASHINCDIR)/maxpath.h
-exec.o: $(topdir)/findcmd.h
+exec.o: $(topdir)/findcmd.h $(topdir)/jobs.h
exit.o: $(topdir)/bashtypes.h
exit.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
exit.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
exit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
-exit.o: $(topdir)/subst.h $(topdir)/externs.h
+exit.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/jobs.h
exit.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
exit.o: $(BASHINCDIR)/maxpath.h ./builtext.h
fc.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h
fg_bg.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
fg_bg.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
fg_bg.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+fg_bg.o: $(topdir)/jobs.h
getopts.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
getopts.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
getopts.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
inlib.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
jobs.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
jobs.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(srcdir)/bashgetopt.h
-jobs.o: $(BASHINCDIR)/maxpath.h $(topdir)/externs.h
+jobs.o: $(BASHINCDIR)/maxpath.h $(topdir)/externs.h $(topdir)/jobs.h
jobs.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
jobs.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
kill.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
kill.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
kill.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/trap.h $(topdir)/unwind_prot.h
kill.o: $(topdir)/variables.h $(topdir)/conftypes.h $(BASHINCDIR)/maxpath.h
+kill.o: $(topdir)/jobs.h
let.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
let.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
let.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
suspend.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
suspend.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
suspend.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+suspend.o: $(topdir)/jobs.h
test.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
test.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
test.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
wait.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
wait.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
wait.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+wait.o: $(topdir)/jobs.h
wait.o: $(BASHINCDIR)/chartypes.h
shopt.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
shopt.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
{
register int i, wl, cl, match, job;
register PROCESS *p;
+ register JOB *j;
job = NO_JOB;
wl = strlen (name);
- for (i = job_slots - 1; i >= 0; i--)
+ for (i = js.j_jobslots - 1; i >= 0; i--)
{
- if (jobs[i] == 0 || ((flags & JM_STOPPED) && JOBSTATE(i) != JSTOPPED))
+ j = get_job_by_jid (i);
+ if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED))
continue;
- p = jobs[i]->pipe;
+ p = j->pipe;
do
{
if (flags & JM_EXACT)
else
job = i;
}
- while (p != jobs[i]->pipe);
+ while (p != j->pipe);
}
return (job);
int job, jflags;
if (list == 0)
- return (current_job);
+ return (js.j_current);
word = list->word->word;
if (DIGIT (*word) && all_digits (word))
{
job = atoi (word);
- return (job > job_slots ? NO_JOB : job - 1);
+ return (job > js.j_jobslots ? NO_JOB : job - 1);
}
jflags = 0;
switch (*word)
{
case 0:
- return NO_JOB;
case '%':
case '+':
- return (current_job);
+ return (js.j_current);
case '-':
- return (previous_job);
+ return (js.j_previous);
case '?': /* Substring search requested. */
jflags |= JM_SUBSTRING;
This file is exit.def, from which is created exit.c.
It implements the builtins "exit", and "logout" in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
if (!exit_immediate_okay)
{
register int i;
- for (i = 0; i < job_slots; i++)
+ for (i = 0; i < js.j_jobslots; i++)
if (jobs[i] && STOPPED (i))
{
fprintf (stderr, _("There are stopped jobs.\n"));
This file is fg_bg.def, from which is created fg_bg.c.
It implements the builtins "bg" and "fg" in Bash.
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
{
sigset_t set, oset;
int job, status, old_async_pid;
+ JOB *j;
BLOCK_CHILD (set, oset);
job = get_job_spec (list);
goto failure;
}
- /* Or if jobs[job]->pgrp == shell_pgrp. */
+ j = get_job_by_jid (job);
+ /* Or if j->pgrp == shell_pgrp. */
if (IS_JOBCONTROL (job) == 0)
{
builtin_error (_("job %d started without job control"), job + 1);
if (foreground == 0)
{
old_async_pid = last_asynchronous_pid;
- last_asynchronous_pid = jobs[job]->pgrp; /* As per Posix.2 5.4.2 */
+ last_asynchronous_pid = j->pgrp; /* As per Posix.2 5.4.2 */
}
status = start_job (job, foreground);
This file is jobs.def, from which is created jobs.c.
It implements the builtins "jobs" and "disown" in Bash.
-Copyright (C) 1987-2004 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
BLOCK_CHILD (set, oset);
job = get_job_spec (list);
- if ((job == NO_JOB) || !jobs || !jobs[job])
+ if ((job == NO_JOB) || jobs == 0 || get_job_by_jid (job) == 0)
{
sh_badjob (list->word->word);
any_failed++;
register WORD_LIST *l;
int job, result;
COMMAND *command;
+ JOB *j;
/* First do the replacement of job specifications with pids. */
for (l = list; l; l = l->next)
if (INVALID_JOB (job))
continue;
+ j = get_job_by_jid (job);
free (l->word->word);
- l->word->word = itos (jobs[job]->pgrp);
+ l->word->word = itos (j->pgrp);
}
}
This file is kill.def, from which is created kill.c.
It implements the builtin "kill" in Bash.
-Copyright (C) 1987-2004 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
{ /* Must be a job spec. Check it out. */
int job;
sigset_t set, oset;
+ JOB *j;
BLOCK_CHILD (set, oset);
job = get_job_spec (list);
CONTINUE_OR_FAIL;
}
+ j = get_job_by_jid (job);
/* Job spec used. Kill the process group. If the job was started
without job control, then its pgrp == shell_pgrp, so we have
to be careful. We take the pid of the first job in the pipeline
in that case. */
- pid = IS_JOBCONTROL (job) ? jobs[job]->pgrp : jobs[job]->pipe->pid;
+ pid = IS_JOBCONTROL (job) ? j->pgrp : j->pipe->pid;
UNBLOCK_CHILD (oset);
This file is trap.def, from which is created trap.c.
It implements the builtin "trap" in Bash.
-Copyright (C) 1987-2004 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
char *t, *p, *sn;
p = trap_list[i];
-itrace("showtrap: %d", i);
if (p == (char *)DEFAULT_SIG)
return;
{
int result, i;
-itrace("trap_builtin");
if (list == 0)
{
for (i = 0; i < BASH_NSIG; i++)
This file is wait.def, from which is created wait.c.
It implements the builtin "wait" in Bash.
-Copyright (C) 1987-2004 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
BLOCK_CHILD (set, oset);
job = get_job_spec (list);
-itrace("wait_builtin: get_job_spec returns %d, js.j_current = %d", job, js.j_current);
-
if (INVALID_JOB (job))
{
if (job != DUP_JOB)
/* command.h -- The structures used internally to represent commands, and
the extern declarations of the functions used to create them. */
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#define W_NOEXPAND 0x04000 /* Don't expand at all -- do quote removal */
#define W_COMPASSIGN 0x08000 /* Compound assignment */
#define W_ASSNBLTIN 0x10000 /* word is a builtin command that takes assignments */
+#define W_ASSIGNARG 0x20000 /* word is assignment argument to command */
+#define W_HASQUOTEDNULL 0x40000 /* word contains a quoted null character */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
/* dispose_command.c -- dispose of a COMMAND structure. */
-/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
dispose_word_desc (w)
WORD_DESC *w;
{
+ w->word = 0;
ocache_free (wdcache, WORD_DESC, w);
}
/* dispose_cmd.h -- Functions appearing in dispose_cmd.c. */
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
--- /dev/null
+/* dispose_cmd.h -- Functions appearing in dispose_cmd.c. */
+
+/* Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_DISPOSE_CMD_H_)
+#define _DISPOSE_CMD_H_
+
+#include "stdc.h"
+
+extern void dispose_command __P((COMMAND *));
+extern void dispose_word_desc __P((WORD_DESC *));
+extern void dispose_word __P((WORD_DESC *));
+extern void dispose_words __P((WORD_LIST *));
+extern void dispose_word_array __P((char **));
+extern void dispose_redirects __P((REDIRECT *));
+
+#if defined (COND_COMMAND)
+extern void dispose_cond_node __P((COND_COM *));
+#endif
+
+extern void dispose_function_def_contents __P((FUNCTION_DEF *));
+extern void dispose_function_def __P((FUNCTION_DEF *));
+
+#endif /* !_DISPOSE_CMD_H_ */
.\" Case Western Reserve University
.\" chet@po.CWRU.Edu
.\"
-.\" Last Change: Tue Jan 4 17:23:59 EST 2005
+.\" Last Change: Fri Feb 11 15:43:40 EST 2005
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2005 Jan 4" "GNU Bash-3.1-devel"
+.TH BASH 1 "2005 Feb 11" "GNU Bash-3.1-devel"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
as given by argument zero.
.TP
.B _
-At shell startup, set to the absolute file name of the shell or shell
-script being executed as passed in the argument list.
+At shell startup, set to the absolute pathname used to invoke the
+shell or shell script being executed as passed in the environment
+or argument list.
Subsequently, expands to the last argument to the previous command,
after expansion.
-Also set to the full file name of each command executed and placed in
-the environment exported to that command.
+Also set to the full pathname used to invoke each command executed
+and placed in the environment exported to that command.
When checking mail, this parameter holds the name of the mail file
currently being checked.
.PD
-.\"
+\"
.\" MAN PAGE COMMENTS to
.\"
.\" Chet Ramey
.\" Case Western Reserve University
.\" chet@po.CWRU.Edu
.\"
-.\" Last Change: Sat Nov 13 15:05:55 EST 2004
+.\" Last Change: Tue Jan 4 17:23:59 EST 2005
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2004 Nov 13" "GNU Bash-3.1-devel"
+.TH BASH 1 "2005 Jan 4" "GNU Bash-3.1-devel"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
[options]
[file]
.SH COPYRIGHT
-.if n Bash is Copyright (C) 1989-2004 by the Free Software Foundation, Inc.
-.if t Bash is Copyright \(co 1989-2004 by the Free Software Foundation, Inc.
+.if n Bash is Copyright (C) 1989-2005 by the Free Software Foundation, Inc.
+.if t Bash is Copyright \(co 1989-2005 by the Free Software Foundation, Inc.
.SH DESCRIPTION
.B Bash
is an \fBsh\fR-compatible command language interpreter that
has special meaning to the shell and must be quoted if it is to
represent itself.
.PP
-When the command history expansion facilities are being used, the
+When the command history expansion facilities are being used
+(see
+.SM
+.B HISTORY EXPANSION
+below), the
\fIhistory expansion\fP character, usually \fB!\fP, must be quoted
to prevent history expansion.
.PP
of all characters within the quotes, with the exception of
.BR $ ,
.BR ` ,
-and
-.BR \e .
+.BR \e ,
+and, when history expansion is enabled,
+.BR ! .
The characters
.B $
and
.BR <newline> .
A double quote may be quoted within double quotes by preceding it with
a backslash.
-When command history is being used, the double quote may not be used to
-quote the history expansion character.
+If enabled, history expansion will be performed unless an
+.B !
+appearing in double quotes is escaped using a backslash.
+The backslash preceding the
+.B !
+is not removed.
.PP
The special parameters
.B *
.PP
Words of the form \fB$\fP'\fIstring\fP' are treated specially. The
word expands to \fIstring\fP, with backslash-escaped characters replaced
-as specifed by the ANSI C standard. Backslash escape sequences, if
+as specified by the ANSI C standard. Backslash escape sequences, if
present, are decoded as follows:
.RS
.PD 0
.PP
When braces are used, the matching ending brace is the first `\fB}\fP'
not escaped by a backslash or within a quoted string, and not within an
-embedded arithmetic expansion, command substitution, or paramter
+embedded arithmetic expansion, command substitution, or parameter
expansion.
.PP
.PD 0
with the exception that the
.SM
.B DEBUG
-trap (see the description of the
+and
+.B RETURN
+traps (see the description of the
.B trap
builtin under
.SM
.B SHELL BUILTIN COMMANDS
-below) is not inherited unless the function has been given the
+below) are not inherited unless the function has been given the
\fBtrace\fP attribute (see the description of the
.SM
.B declare
builtin below) or the
\fB\-o functrace\fP shell option has been enabled with
the \fBset\fP builtin
-(in which case all functions inherit the \fBDEBUG\fP trap).
+(in which case all functions inherit the \fBDEBUG\fP and \fBRETURN\fP traps).
.PP
Variables local to the function may be declared with the
.B local
The digits greater than 9 are represented by the lowercase letters,
the uppercase letters, @, and _, in that order.
If \fIbase\fP is less than or equal to 36, lowercase and uppercase
-letters may be used interchangably to represent numbers between 10
+letters may be used interchangeably to represent numbers between 10
and 35.
.PP
Operators are evaluated in order of precedence. Sub-expressions in
.TP
.B history-preserve-point
If set to \fBon\fP, the history code attempts to place point at the
-same location on each history line retrived with \fBprevious-history\fP
+same location on each history line retrieved with \fBprevious-history\fP
or \fBnext-history\fP.
.TP
.B horizontal\-scroll\-mode (Off)
insert the \fIn\fPth word from the previous command (the words
in the previous command begin with word 0). A negative argument
inserts the \fIn\fPth word from the end of the previous command.
+Once the argument \fIn\fP is computed, the argument is extracted
+as if the "!\fIn\fP" history expansion had been specified.
.TP
.B
yank\-last\-arg (M\-.\^, M\-_\^)
behave exactly like \fByank\-nth\-arg\fP.
Successive calls to \fByank\-last\-arg\fP move back through the history
list, inserting the last argument of each line in turn.
+The history expansion facilities are used to extract the last argument,
+as if the "!$" history expansion had been specified.
.TP
.B shell\-expand\-line (M\-C\-e)
Expand the line as the shell does. This
Shell quoting is honored.
Each word is then expanded using
brace expansion, tilde expansion, parameter and variable expansion,
-command substitution, arithmetic expansion, and pathname expansion,
+command substitution, and arithmetic expansion,
as described above under
.SM
.BR EXPANSION .
.TP
.B \-t
Give each \fIname\fP the \fItrace\fP attribute.
-Traced functions inherit the \fBDEBUG\fP trap from the calling shell.
+Traced functions inherit the \fBDEBUG\fP and \fBRETURN\fP traps from
+the calling shell.
The trace attribute has no special meaning for variables.
.TP
.B \-x
.TP
\fBset\fP [\fB\-\-abefhkmnptuvxBCHP\fP] [\fB\-o\fP \fIoption\fP] [\fIarg\fP ...]
Without options, the name and value of each shell variable are displayed
-in a format that can be reused as input.
+in a format that can be reused as input
+for setting or resetting the currently-set variables.
+Read-only variables cannot be reset.
+In \fIposix mode\fP, only shell variables are listed.
The output is sorted according to the current locale.
When options are specified, they set or unset shell attributes.
Any arguments remaining after the options are processed are treated
Change the behavior of
.B bash
where the default operation differs
-from the POSIX 1003.2 standard to match the standard (\fI`posix mode\fP).
+from the POSIX 1003.2 standard to match the standard (\fIposix mode\fP).
.TP 8
.B privileged
Same as
which change the current directory.
.TP 8
.B \-T
-If set, any trap on \fBDEBUG\fP is inherited by shell functions, command
-substitutions, and commands executed in a subshell environment.
-The \fBDEBUG\fP trap is normally not inherited in such cases.
+If set, any traps on \fBDEBUG\fP and \fBRETURN\fP are inherited by shell
+functions, command substitutions, and commands executed in a
+subshell environment.
+The \fBDEBUG\fP and \fBRETURN\fP traps are normally not inherited
+in such cases.
.TP 8
.B \-\-
If no arguments follow this option, then the positional parameters are
Read-only variables may not be unset.
If
.B \-f
-is specifed,
-each
+is specified, each
.I name
refers to a shell function, and the function definition
is removed.
@item _
(An underscore.)
-At shell startup, set to the absolute filename of the shell or shell
-script being executed as passed in the argument list.
+At shell startup, set to the absolute pathname used to invoke the
+shell or shell script being executed as passed in the environment
+or argument list.
Subsequently, expands to the last argument to the previous command,
after expansion.
-Also set to the full pathname of each command executed and placed in
-the environment exported to that command.
+Also set to the full pathname used to invoke each command executed
+and placed in the environment exported to that command.
When checking mail, this parameter holds the name of the mail file.
@end vtable
of @cite{The GNU Bash Reference Manual},
for @code{Bash}, Version @value{VERSION}.
-Copyright @copyright{} 1988-2004 Free Software Foundation, Inc.
+Copyright @copyright{} 1988-2005 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
Each of the shell metacharacters (@pxref{Definitions})
has special meaning to the shell and must be quoted if it is to
represent itself.
-When the command history expansion facilities are being used, the
+When the command history expansion facilities are being used
+(@pxref{History Interaction}), the
@var{history expansion} character, usually @samp{!}, must be quoted
to prevent history expansion. @xref{Bash History Facilities}, for
more details concerning history expansion.
Enclosing characters in double quotes (@samp{"}) preserves the literal value
of all characters within the quotes, with the exception of
-@samp{$}, @samp{`}, and @samp{\}.
+@samp{$}, @samp{`}, @samp{\},
+and, when history expansion is enabled, @samp{!}.
The characters @samp{$} and @samp{`}
retain their special meaning within double quotes (@pxref{Shell Expansions}).
The backslash retains its special meaning only when followed by one of
special meaning are left unmodified.
A double quote may be quoted within double quotes by preceding it with
a backslash.
-When command history is being used, the double quote may not be used to
-quote the history expansion character.
+If enabled, history expansion will be performed unless an @samp{!}
+appearing in double quotes is escaped using a backslash.
+The backslash preceding the @samp{!} is not removed.
The special parameters @samp{*} and @samp{@@} have special meaning
when in double quotes (@pxref{Shell Parameter Expansion}).
name of the function while the function is executing.
All other aspects of the shell execution
environment are identical between a function and its caller
-with the exception that the @env{DEBUG} trap
-below) is not inherited unless the function has been given the
+with the exception that the @env{DEBUG} and @env{RETURN} traps
+are not inherited unless the function has been given the
@code{trace} attribute using the @code{declare} builtin or
the @code{-o functrace} option has been enabled with
the @code{set} builtin,
-(in which case all functions inherit the @code{DEBUG} trap).
+(in which case all functions inherit the @env{DEBUG} and @env{RETURN} traps).
@xref{Bourne Shell Builtins}, for the description of the
@code{trap} builtin.
@item _
(An underscore.)
-At shell startup, set to the absolute filename of the shell or shell
-script being executed as passed in the argument list.
+At shell startup, set to the absolute file name used to invoke the
+shell or shell script being executed as passed in the environment
+or argument list.
Subsequently, expands to the last argument to the previous command,
after expansion.
-Also set to the full pathname of each command executed and placed in
-the environment exported to that command.
+Also set to the full pathname used to invoke each command executed
+and placed in the environment exported to that command.
When checking mail, this parameter holds the name of the mail file.
@end vtable
@item -t
Give each @var{name} the @code{trace} attribute.
-Traced functions inherit the @code{DEBUG} trap from the calling shell.
+Traced functions inherit the @code{DEBUG} and @code{RETURN} traps from
+the calling shell.
The trace attribute has no special meaning for variables.
@item -x
If no options or arguments are supplied, @code{set} displays the names
and values of all shell variables and functions, sorted according to the
-current locale, in a format that may be reused as input.
+current locale, in a format that may be reused as input
+for setting or resetting the currently-set variables.
+Read-only variables cannot be reset.
+In @sc{posix} mode, only shell variables are listed.
When options are supplied, they set or unset shell attributes.
Options, if specified, have the following meanings:
@end example
@item -T
-If set, any trap on @code{DEBUG} is inherited by shell functions, command
-substitutions, and commands executed in a subshell environment.
-The @code{DEBUG} trap is normally not inherited in such cases.
+If set, any trap on @code{DEBUG} and @code{RETURN} are inherited by
+shell functions, command substitutions, and commands executed
+in a subshell environment.
+The @code{DEBUG} and @code{RETURN} traps are normally not inherited
+in such cases.
@item --
If no arguments follow this option, then the positional parameters are
The digits greater than 9 are represented by the lowercase letters,
the uppercase letters, @samp{@@}, and @samp{_}, in that order.
If @var{base} is less than or equal to 36, lowercase and uppercase
-letters may be used interchangably to represent numbers between 10
+letters may be used interchangeably to represent numbers between 10
and 35.
Operators are evaluated in order of precedence. Sub-expressions in
@item --with-installed-readline[=@var{PREFIX}]
Define this to make Bash link with a locally-installed version of Readline
rather than the version in @file{lib/readline}. This works only with
-Readline 4.3 and later versions. If @var{PREFIX} is @code{yes} or not
+Readline 5.0 and later versions. If @var{PREFIX} is @code{yes} or not
supplied, @code{configure} uses the values of the make variables
@code{includedir} and @code{libdir}, which are subdirectories of @code{prefix}
by default, to find the installed version of Readline if it is not in
@samp{=~} binary operator in the @code{[[} conditional command.
(@pxref{Conditional Constructs}).
+@item --enable-debugger
+Include support for the bash debugger (distributed separately).
+
@item --enable-directory-stack
Include support for a @code{csh}-like directory stack and the
@code{pushd}, @code{popd}, and @code{dirs} builtins
This enables process substitution (@pxref{Process Substitution}) if
the operating system provides the necessary support.
+@item --enable-progcomp
+Enable the programmable completion facilities
+(@pxref{Programmable Completion}).
+If Readline is not enabled, this option has no effect.
+
@item --enable-prompt-string-decoding
Turn on the interpretation of a number of backslash-escaped characters
in the @env{$PS1}, @env{$PS2}, @env{$PS3}, and @env{$PS4} prompt
strings. See @ref{Printing a Prompt}, for a complete list of prompt
string escape sequences.
-@item --enable-progcomp
-Enable the programmable completion facilities
-(@pxref{Programmable Completion}).
-If Readline is not enabled, this option has no effect.
-
@item --enable-readline
Include support for command-line editing and history with the Bash
version of the Readline library (@pxref{Command Line Editing}).
Include the @code{select} builtin, which allows the generation of simple
menus (@pxref{Conditional Constructs}).
+@item --enable-separate-helpfiles
+Use external files for the documentation displayed by the @code{help} builtin
+instead of storing the text internally.
+
+@item --enable-single-help-strings
+Store the text displayed by the @code{help} builtin as a single string for
+each help topic. This aids in translating the text to different languages.
+You may need to disable this if your compiler cannot handle very long string
+literals.
+
@item --enable-usg-echo-default
A synonym for @code{--enable-xpg-echo-default}.
without requiring the @option{-e} option.
This sets the default value of the @code{xpg_echo} shell option to @code{on},
which makes the Bash @code{echo} behave more like the version specified in
-the Single Unix Specification, version 2.
+the Single Unix Specification, version 3.
@xref{Bash Builtins}, for a description of the escape sequences that
@code{echo} recognizes.
Commands specified with an @code{RETURN} trap are executed before
execution resumes after a shell function or a shell script executed with
@code{.} or @code{source} returns.
-The @code{RETURN} trap is not inherited by shell functions.
+The @code{RETURN} trap is not inherited by shell functions unless the
+function has been given the @code{trace} attribute or the
+@code{functrace} option has been enabled using the @code{shopt} builtin.
@item
The Bash @code{type} builtin is more extensive and gives more information
Copyright (C) 1988-2005 Free Software Foundation, Inc.
@end ignore
-@set LASTCHANGE Thu Dec 30 17:00:22 EST 2004
+@set LASTCHANGE Fri Feb 11 15:43:59 EST 2005
@set EDITION 3.1-devel
@set VERSION 3.1-devel
-@set UPDATED 30 December 2004
-@set UPDATED-MONTH December 2004
+@set UPDATED 11 February 2005
+@set UPDATED-MONTH February 2005
@ignore
-Copyright (C) 1988-2004 Free Software Foundation, Inc.
+Copyright (C) 1988-2005 Free Software Foundation, Inc.
@end ignore
-@set LASTCHANGE Sat Nov 13 15:06:31 EST 2004
+@set LASTCHANGE Thu Dec 30 17:00:22 EST 2004
@set EDITION 3.1-devel
@set VERSION 3.1-devel
-@set UPDATED 13 November 2004
-@set UPDATED-MONTH November 2004
-
+@set UPDATED 30 December 2004
+@set UPDATED-MONTH December 2004
/* execute_command.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
{
int exec_result, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
+ volatile int last_pid;
volatile int save_line_number;
if (command == 0 || breaking || continuing || read_but_dont_execute)
/* We can't rely on variables retaining their values across a
call to execute_simple_command if a longjmp occurs as the
result of a `return' builtin. This is true for sure with gcc. */
+#if defined (RECYCLES_PIDS)
last_made_pid = NO_PID;
+#endif
+ last_pid = last_made_pid;
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return && command->value.Simple)
/* XXX - this is something to watch out for if there are problems
when the shell is compiled without job control. */
if (already_making_children && pipe_out == NO_PIPE &&
- last_made_pid != NO_PID)
+ last_made_pid != last_pid)
{
stop_pipeline (asynchronous, (COMMAND *)NULL);
{
int exec_result, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
+ volatile int last_pid;
volatile int save_line_number;
if (command == 0 || breaking || continuing || read_but_dont_execute)
/* We can't rely on variables retaining their values across a
call to execute_simple_command if a longjmp occurs as the
result of a `return' builtin. This is true for sure with gcc. */
+#if defined (RECYCLES_PIDS)
last_made_pid = NO_PID;
+#endif
+ last_pid = last_made_pid;
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return && command->value.Simple)
/* XXX - this is something to watch out for if there are problems
when the shell is compiled without job control. */
if (already_making_children && pipe_out == NO_PIPE &&
- last_made_pid != NO_PID)
+ last_made_pid != last_pid)
{
stop_pipeline (asynchronous, (COMMAND *)NULL);
if (dofork)
{
-#if 0
- /* XXX memory leak if expand_words() error causes a jump_to_top_level */
- command_line = savestring (the_printed_command);
-#endif
-
/* Do this now, because execute_disk_command will do it anyway in the
vast majority of cases. */
maybe_make_export_env ();
-#if 0
- if (make_child (command_line, async) == 0)
-#else
if (make_child (savestring (the_printed_command), async) == 0)
-#endif
{
already_forked = 1;
simple_command->flags |= CMD_NO_FORK;
-#if 0
- subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
- ? (SUBSHELL_PIPE|SUBSHELL_FORK)
- : (SUBSHELL_ASYNC|SUBSHELL_FORK);
-#else
subshell_environment = SUBSHELL_FORK;
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
subshell_environment |= SUBSHELL_PIPE;
if (async)
subshell_environment |= SUBSHELL_ASYNC;
-#endif
/* We need to do this before piping to handle some really
pathological cases where one of the pipe file descriptors
restore_default_signal (ERROR_TRAP);
}
+ /* Shell functions inherit the RETURN trap if function tracing is on
+ globally or on individually for this function. */
+#if 0
if (return_trap && ((trace_p (var) == 0) && function_trace_mode == 0))
+#else
+ if (return_trap && (signal_in_progress (DEBUG_TRAP) || ((trace_p (var) == 0) && function_trace_mode == 0)))
+#endif
{
if (subshell == 0)
{
if (return_val)
{
result = return_catch_value;
-#if 0
- /* Run the RETURN trap in the function's context */
+ /* Run the RETURN trap in the function's context. */
save_current = currently_executing_command;
run_return_trap ();
currently_executing_command = save_current;
-#endif
}
else
{
--- /dev/null
+/* findcmd.c -- Functions to search for commands by name. */
+
+/* Copyright (C) 1997 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. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include "chartypes.h"
+#include "bashtypes.h"
+#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include "filecntl.h"
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#include "memalloc.h"
+#include "shell.h"
+#include "flags.h"
+#include "hashlib.h"
+#include "pathexp.h"
+#include "hashcmd.h"
+#include "findcmd.h" /* matching prototypes and declarations */
+
+extern int posixly_correct;
+
+/* Static functions defined and used in this file. */
+static char *_find_user_command_internal __P((const char *, int));
+static char *find_user_command_internal __P((const char *, int));
+static char *find_user_command_in_path __P((const char *, char *, int));
+static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
+static char *find_absolute_program __P((const char *, int));
+
+static char *get_next_path_element __P((char *, int *));
+
+/* The file name which we would try to execute, except that it isn't
+ possible to execute it. This is the first file that matches the
+ name that we are looking for while we are searching $PATH for a
+ suitable one to execute. If we cannot find a suitable executable
+ file, then we use this one. */
+static char *file_to_lose_on;
+
+/* Non-zero if we should stat every command found in the hash table to
+ make sure it still exists. */
+int check_hashed_filenames;
+
+/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
+ encounters a `.' as the directory pathname while scanning the
+ list of possible pathnames; i.e., if `.' comes before the directory
+ containing the file of interest. */
+int dot_found_in_search = 0;
+
+#define u_mode_bits(x) (((x) & 0000700) >> 6)
+#define g_mode_bits(x) (((x) & 0000070) >> 3)
+#define o_mode_bits(x) (((x) & 0000007) >> 0)
+#define X_BIT(x) ((x) & 1)
+
+/* Return some flags based on information about this file.
+ The EXISTS bit is non-zero if the file is found.
+ The EXECABLE bit is non-zero the file is executble.
+ Zero is returned if the file is not found. */
+int
+file_status (name)
+ const char *name;
+{
+ struct stat finfo;
+
+ /* Determine whether this file exists or not. */
+ if (stat (name, &finfo) < 0)
+ return (0);
+
+ /* If the file is a directory, then it is not "executable" in the
+ sense of the shell. */
+ if (S_ISDIR (finfo.st_mode))
+ return (FS_EXISTS|FS_DIRECTORY);
+
+#if defined (AFS)
+ /* We have to use access(2) to determine access because AFS does not
+ support Unix file system semantics. This may produce wrong
+ answers for non-AFS files when ruid != euid. I hate AFS. */
+ return ((access (name, X_OK) == 0) ? (FS_EXISTS|FS_EXECABLE) : FS_EXISTS);
+#else /* !AFS */
+
+ /* Find out if the file is actually executable. By definition, the
+ only other criteria is that the file has an execute bit set that
+ we can use. */
+
+ /* Root only requires execute permission for any of owner, group or
+ others to be able to exec a file. */
+ if (current_user.euid == (uid_t)0)
+ {
+ int bits;
+
+ bits = (u_mode_bits (finfo.st_mode) |
+ g_mode_bits (finfo.st_mode) |
+ o_mode_bits (finfo.st_mode));
+
+ return ((X_BIT (bits)) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+ }
+
+ /* If we are the owner of the file, the owner execute bit applies. */
+ if (current_user.euid == finfo.st_uid)
+ return ((X_BIT (u_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+
+ /* If we are in the owning group, the group permissions apply. */
+ else if (group_member (finfo.st_gid))
+ return ((X_BIT (g_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+
+ /* Else we check whether `others' have permission to execute the file */
+ else
+ return ((X_BIT (o_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+#endif /* !AFS */
+}
+
+/* Return non-zero if FILE exists and is executable.
+ Note that this function is the definition of what an
+ executable file is; do not change this unless YOU know
+ what an executable file is. */
+int
+executable_file (file)
+ const char *file;
+{
+ int s;
+
+ s = file_status (file);
+ return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
+}
+
+int
+is_directory (file)
+ const char *file;
+{
+ return (file_status (file) & FS_DIRECTORY);
+}
+
+int
+executable_or_directory (file)
+ const char *file;
+{
+ int s;
+
+ s = file_status (file);
+ return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
+}
+
+/* Locate the executable file referenced by NAME, searching along
+ the contents of the shell PATH variable. Return a new string
+ which is the full pathname to the file, or NULL if the file
+ couldn't be found. If a file is found that isn't executable,
+ and that is the only match, then return that. */
+char *
+find_user_command (name)
+ const char *name;
+{
+ return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
+}
+
+/* Locate the file referenced by NAME, searching along the contents
+ of the shell PATH variable. Return a new string which is the full
+ pathname to the file, or NULL if the file couldn't be found. This
+ returns the first file found. */
+char *
+find_path_file (name)
+ const char *name;
+{
+ return (find_user_command_internal (name, FS_EXISTS));
+}
+
+static char *
+_find_user_command_internal (name, flags)
+ const char *name;
+ int flags;
+{
+ char *path_list, *cmd;
+ SHELL_VAR *var;
+
+ /* Search for the value of PATH in both the temporary environments and
+ in the regular list of variables. */
+ if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
+ path_list = value_cell (var);
+ else
+ path_list = (char *)NULL;
+
+ if (path_list == 0 || *path_list == '\0')
+ return (savestring (name));
+
+ cmd = find_user_command_in_path (name, path_list, flags);
+
+ return (cmd);
+}
+
+static char *
+find_user_command_internal (name, flags)
+ const char *name;
+ int flags;
+{
+#ifdef __WIN32__
+ char *res, *dotexe;
+
+ dotexe = (char *)xmalloc (strlen (name) + 5);
+ strcpy (dotexe, name);
+ strcat (dotexe, ".exe");
+ res = _find_user_command_internal (dotexe, flags);
+ free (dotexe);
+ if (res == 0)
+ res = _find_user_command_internal (name, flags);
+ return res;
+#else
+ return (_find_user_command_internal (name, flags));
+#endif
+}
+
+/* Return the next element from PATH_LIST, a colon separated list of
+ paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
+ the index is modified by this function.
+ Return the next element of PATH_LIST or NULL if there are no more. */
+static char *
+get_next_path_element (path_list, path_index_pointer)
+ char *path_list;
+ int *path_index_pointer;
+{
+ char *path;
+
+ path = extract_colon_unit (path_list, path_index_pointer);
+
+ if (path == 0)
+ return (path);
+
+ if (*path == '\0')
+ {
+ free (path);
+ path = savestring (".");
+ }
+
+ return (path);
+}
+
+/* Look for PATHNAME in $PATH. Returns either the hashed command
+ corresponding to PATHNAME or the first instance of PATHNAME found
+ in $PATH. Returns a newly-allocated string. */
+char *
+search_for_command (pathname)
+ const char *pathname;
+{
+ char *hashed_file, *command;
+ int temp_path, st;
+ SHELL_VAR *path;
+
+ hashed_file = command = (char *)NULL;
+
+ /* If PATH is in the temporary environment for this command, don't use the
+ hash table to search for the full pathname. */
+ path = find_variable_internal ("PATH", 1);
+ temp_path = path && tempvar_p (path);
+ if (temp_path == 0 && path)
+ path = (SHELL_VAR *)NULL;
+
+ /* Don't waste time trying to find hashed data for a pathname
+ that is already completely specified or if we're using a command-
+ specific value for PATH. */
+ if (path == 0 && absolute_program (pathname) == 0)
+ hashed_file = phash_search (pathname);
+
+ /* If a command found in the hash table no longer exists, we need to
+ look for it in $PATH. Thank you Posix.2. This forces us to stat
+ every command found in the hash table. */
+
+ if (hashed_file && (posixly_correct || check_hashed_filenames))
+ {
+ st = file_status (hashed_file);
+ if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
+ {
+ phash_remove (pathname);
+ free (hashed_file);
+ hashed_file = (char *)NULL;
+ }
+ }
+
+ if (hashed_file)
+ command = hashed_file;
+ else if (absolute_program (pathname))
+ /* A command containing a slash is not looked up in PATH or saved in
+ the hash table. */
+ command = savestring (pathname);
+ else
+ {
+ /* If $PATH is in the temporary environment, we've already retrieved
+ it, so don't bother trying again. */
+ if (temp_path)
+ {
+ command = find_user_command_in_path (pathname, value_cell (path),
+ FS_EXEC_PREFERRED|FS_NODIRS);
+ }
+ else
+ command = find_user_command (pathname);
+ if (command && hashing_enabled && temp_path == 0)
+ phash_insert ((char *)pathname, command, dot_found_in_search, 1); /* XXX fix const later */
+ }
+ return (command);
+}
+
+char *
+user_command_matches (name, flags, state)
+ const char *name;
+ int flags, state;
+{
+ register int i;
+ int path_index, name_len;
+ char *path_list, *path_element, *match;
+ struct stat dotinfo;
+ static char **match_list = NULL;
+ static int match_list_size = 0;
+ static int match_index = 0;
+
+ if (state == 0)
+ {
+ /* Create the list of matches. */
+ if (match_list == 0)
+ {
+ match_list_size = 5;
+ match_list = strvec_create (match_list_size);
+ }
+
+ /* Clear out the old match list. */
+ for (i = 0; i < match_list_size; i++)
+ match_list[i] = 0;
+
+ /* We haven't found any files yet. */
+ match_index = 0;
+
+ if (absolute_program (name))
+ {
+ match_list[0] = find_absolute_program (name, flags);
+ match_list[1] = (char *)NULL;
+ path_list = (char *)NULL;
+ }
+ else
+ {
+ name_len = strlen (name);
+ file_to_lose_on = (char *)NULL;
+ dot_found_in_search = 0;
+ stat (".", &dotinfo);
+ path_list = get_string_value ("PATH");
+ path_index = 0;
+ }
+
+ while (path_list && path_list[path_index])
+ {
+ path_element = get_next_path_element (path_list, &path_index);
+
+ if (path_element == 0)
+ break;
+
+ match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
+
+ free (path_element);
+
+ if (match == 0)
+ continue;
+
+ if (match_index + 1 == match_list_size)
+ {
+ match_list_size += 10;
+ match_list = strvec_resize (match_list, (match_list_size + 1));
+ }
+
+ match_list[match_index++] = match;
+ match_list[match_index] = (char *)NULL;
+ FREE (file_to_lose_on);
+ file_to_lose_on = (char *)NULL;
+ }
+
+ /* We haven't returned any strings yet. */
+ match_index = 0;
+ }
+
+ match = match_list[match_index];
+
+ if (match)
+ match_index++;
+
+ return (match);
+}
+
+static char *
+find_absolute_program (name, flags)
+ const char *name;
+ int flags;
+{
+ int st;
+
+ st = file_status (name);
+
+ /* If the file doesn't exist, quit now. */
+ if ((st & FS_EXISTS) == 0)
+ return ((char *)NULL);
+
+ /* If we only care about whether the file exists or not, return
+ this filename. Otherwise, maybe we care about whether this
+ file is executable. If it is, and that is what we want, return it. */
+ if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
+ return (savestring (name));
+
+ return (NULL);
+}
+
+static char *
+find_in_path_element (name, path, flags, name_len, dotinfop)
+ const char *name;
+ char *path;
+ int flags, name_len;
+ struct stat *dotinfop;
+{
+ int status;
+ char *full_path, *xpath;
+
+ xpath = (*path == '~') ? bash_tilde_expand (path, 0) : path;
+
+ /* Remember the location of "." in the path, in all its forms
+ (as long as they begin with a `.', e.g. `./.') */
+ if (dot_found_in_search == 0 && *xpath == '.')
+ dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
+
+ full_path = sh_makepath (xpath, name, 0);
+
+ status = file_status (full_path);
+itrace("find_in_path_element: %s (%s) -> %d", full_path, xpath, status);
+ if (xpath != path)
+ free (xpath);
+
+ if ((status & FS_EXISTS) == 0)
+ {
+ free (full_path);
+ return ((char *)NULL);
+ }
+
+ /* The file exists. If the caller simply wants the first file, here it is. */
+ if (flags & FS_EXISTS)
+ return (full_path);
+
+ /* If the file is executable, then it satisfies the cases of
+ EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
+ if ((status & FS_EXECABLE) &&
+ (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
+ {
+ FREE (file_to_lose_on);
+ file_to_lose_on = (char *)NULL;
+ return (full_path);
+ }
+
+ /* The file is not executable, but it does exist. If we prefer
+ an executable, then remember this one if it is the first one
+ we have found. */
+ if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
+ file_to_lose_on = savestring (full_path);
+
+ /* If we want only executable files, or we don't want directories and
+ this file is a directory, fail. */
+ if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
+ ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
+ {
+ free (full_path);
+ return ((char *)NULL);
+ }
+ else
+ return (full_path);
+}
+
+/* This does the dirty work for find_user_command_internal () and
+ user_command_matches ().
+ NAME is the name of the file to search for.
+ PATH_LIST is a colon separated list of directories to search.
+ FLAGS contains bit fields which control the files which are eligible.
+ Some values are:
+ FS_EXEC_ONLY: The file must be an executable to be found.
+ FS_EXEC_PREFERRED: If we can't find an executable, then the
+ the first file matching NAME will do.
+ FS_EXISTS: The first file found will do.
+ FS_NODIRS: Don't find any directories.
+*/
+static char *
+find_user_command_in_path (name, path_list, flags)
+ const char *name;
+ char *path_list;
+ int flags;
+{
+ char *full_path, *path;
+ int path_index, name_len;
+ struct stat dotinfo;
+
+ /* We haven't started looking, so we certainly haven't seen
+ a `.' as the directory path yet. */
+ dot_found_in_search = 0;
+
+ if (absolute_program (name))
+ {
+ full_path = find_absolute_program (name, flags);
+ return (full_path);
+ }
+
+ if (path_list == 0 || *path_list == '\0')
+ return (savestring (name)); /* XXX */
+
+ file_to_lose_on = (char *)NULL;
+ name_len = strlen (name);
+ stat (".", &dotinfo);
+ path_index = 0;
+
+ while (path_list[path_index])
+ {
+ /* Allow the user to interrupt out of a lengthy path search. */
+ QUIT;
+
+ path = get_next_path_element (path_list, &path_index);
+ if (path == 0)
+ break;
+
+ /* Side effects: sets dot_found_in_search, possibly sets
+ file_to_lose_on. */
+ full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
+ free (path);
+
+ /* This should really be in find_in_path_element, but there isn't the
+ right combination of flags. */
+ if (full_path && is_directory (full_path))
+ {
+ free (full_path);
+ continue;
+ }
+
+ if (full_path)
+ {
+ FREE (file_to_lose_on);
+ return (full_path);
+ }
+ }
+
+ /* We didn't find exactly what the user was looking for. Return
+ the contents of FILE_TO_LOSE_ON which is NULL when the search
+ required an executable, or non-NULL if a file was found and the
+ search would accept a non-executable as a last resort. If the
+ caller specified FS_NODIRS, and file_to_lose_on is a directory,
+ return NULL. */
+ if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
+ {
+ free (file_to_lose_on);
+ file_to_lose_on = (char *)NULL;
+ }
+
+ return (file_to_lose_on);
+}
extern int wait_signal_received;
extern WORD_LIST *subst_assign_varlist;
-struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB };
+struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
+
+struct bgpids bgpids = { 0, 0, 0 };
/* The array of known jobs. */
JOB **jobs = (JOB **)NULL;
static int waitchld __P((pid_t, int));
static PROCESS *find_pipeline __P((pid_t, int, int *));
+static PROCESS *find_process __P((pid_t, int, int *));
static char *current_working_directory __P((void));
static char *job_working_directory __P((void));
static char *j_strsignal __P((int));
static char *printable_job_status __P((int, PROCESS *, int));
+static PROCESS *find_last_proc __P((int, int));
static pid_t find_last_pid __P((int, int));
static int set_new_line_discipline __P((int));
static int job_last_stopped __P((int));
static int job_last_running __P((int));
static int most_recent_job_in_state __P((int, JOB_STATE));
-static int find_job __P((pid_t, int));
+static int find_job __P((pid_t, int, PROCESS **));
static int print_job __P((JOB *, int, int, int));
static int process_exit_status __P((WAIT));
static int process_exit_signal __P((WAIT));
static void pipe_close __P((int *));
#endif
+static struct pidstat *bgp_alloc __P((pid_t, int));
+static struct pidstat *bgp_add __P((pid_t, int));
+static int bgp_delete __P((pid_t));
+static void bgp_clear __P((void));
+static int bgp_search __P((pid_t));
+static void bgp_prune __P((void));
+
#if defined (ARRAY_VARS)
static int *pstatuses; /* list of pipeline statuses */
static int statsize;
if (the_pipeline)
{
register PROCESS *p;
- int any_alive, any_stopped, n;
+ int any_running, any_stopped, n;
newjob = (JOB *)xmalloc (sizeof (JOB));
/* Set the state of this pipeline. */
p = newjob->pipe;
- any_alive = any_stopped = 0;
+ any_running = any_stopped = 0;
do
{
- any_alive |= p->running;
- any_stopped |= WIFSTOPPED (p->status);
+ any_running |= PRUNNING (p);
+ any_stopped |= PSTOPPED (p);
p = p->next;
}
while (p != newjob->pipe);
- newjob->state = any_alive ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
+ newjob->state = any_running ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
newjob->wd = job_working_directory ();
newjob->deferred = deferred;
else
newjob = (JOB *)NULL;
+ if (newjob)
+ js.j_lastmade = newjob;
+
if (async)
{
if (newjob)
- newjob->flags &= ~J_FOREGROUND;
+ {
+ newjob->flags &= ~J_FOREGROUND;
+ newjob->flags |= J_ASYNC;
+ js.j_lastasync = newjob;
+ }
reset_current ();
}
else
return (js.j_current);
}
+/* Functions to manage the list of exited background pids whose status has
+ been saved. */
+
+static struct pidstat *
+bgp_alloc (pid, status)
+ pid_t pid;
+ int status;
+{
+ struct pidstat *ps;
+
+ ps = (struct pidstat *)xmalloc (sizeof (struct pidstat));
+ ps->pid = pid;
+ ps->status = status;
+ ps->next = (struct pidstat *)0;
+ return ps;
+}
+
+static struct pidstat *
+bgp_add (pid, status)
+ pid_t pid;
+ int status;
+{
+ struct pidstat *ps;
+
+ ps = bgp_alloc (pid, status);
+
+ if (bgpids.list == 0)
+ {
+ bgpids.list = bgpids.end = ps;
+ bgpids.npid = 0; /* just to make sure */
+ }
+ else
+ {
+ bgpids.end->next = ps;
+ bgpids.end = ps;
+ }
+ bgpids.npid++;
+
+ if (bgpids.npid > js.c_childmax)
+ bgp_prune ();
+
+ return ps;
+}
+
+static int
+bgp_delete (pid)
+ pid_t pid;
+{
+ struct pidstat *prev, *p;
+
+ for (prev = p = bgpids.list; p; prev = p, p = p->next)
+ if (p->pid == pid)
+ {
+ prev->next = p->next; /* remove from list */
+ break;
+ }
+
+ if (p == 0)
+ return 0; /* not found */
+
+itrace("bgp_delete: deleting %d", pid);
+
+ /* Housekeeping in the border cases. */
+ if (p == bgpids.list)
+ bgpids.list = bgpids.list->next;
+ else if (p == bgpids.end)
+ bgpids.end = prev;
+
+ bgpids.npid--;
+ if (bgpids.npid == 0)
+ bgpids.list = bgpids.end = 0;
+ else if (bgpids.npid == 1)
+ bgpids.end = bgpids.list; /* just to make sure */
+
+ free (p);
+ return 1;
+}
+
+/* Clear out the list of saved statuses */
+static void
+bgp_clear ()
+{
+ struct pidstat *ps, *p;
+
+ for (ps = bgpids.list; ps; )
+ {
+ p = ps;
+ ps = ps->next;
+ free (p);
+ }
+ bgpids.list = bgpids.end = 0;
+ bgpids.npid = 0;
+}
+
+/* Search for PID in the list of saved background pids; return its status if
+ found. If not found, return -1. */
+static int
+bgp_search (pid)
+ pid_t pid;
+{
+ struct pidstat *ps;
+
+ for (ps = bgpids.list ; ps; ps = ps->next)
+ if (ps->pid == pid)
+ return ps->status;
+ return -1;
+}
+
+static void
+bgp_prune ()
+{
+ struct pidstat *ps, *p;
+
+ while (bgpids.npid > js.c_childmax)
+ {
+ ps = bgpids.list;
+ bgpids.list = bgpids.list->next;
+ free (ps);
+ bgpids.npid--;
+ }
+}
+
/* Reset the values of js.j_lastj and js.j_firstj after one or both have
been deleted. The caller should check whether js.j_njobs is 0 before
- calling this. */
+ calling this. This wraps around, but the rest of the code does not. At
+ this point, it should not matter. */
static void
reset_job_indices ()
{
int job_index, warn_stopped;
{
register JOB *temp;
- int ndel;
+ PROCESS *proc;
+ int ndel, status;
+ pid_t pid;
if (js.j_jobslots == 0 || jobs_list_frozen)
return;
if (warn_stopped && subshell_environment == 0 && STOPPED (job_index))
internal_warning (_("deleting stopped job %d with process group %ld"), job_index+1, (long)jobs[job_index]->pgrp);
-
temp = jobs[job_index];
if (job_index == js.j_current || job_index == js.j_previous)
reset_current ();
+ proc = find_last_proc (job_index, 0);
+ /* Could do this just for J_ASYNC jobs, but we save all. */
+ bgp_add (proc->pid, process_exit_status (proc->status));
+
jobs[job_index] = (JOB *)NULL;
+ if (temp == js.j_lastmade)
+ js.j_lastmade = 0;
+ else if (temp == js.j_lastasync)
+ js.j_lastasync = 0;
+
free (temp->wd);
ndel = discard_pipeline (temp->pipe);
{
PROCESS *t, *p;
+#if defined (RECYCLES_PIDS)
+ int j;
+ p = find_process (pid, 0, &j);
+ if (p)
+ {
+# ifdef DEBUG
+ if (j == NO_JOB)
+ internal_warning ("add_process: process %5ld (%s) in the_pipeline", (long)p->pid, p->command);
+# endif
+ if (PALIVE (p))
+ internal_warning ("add_process: pid %5ld (%s) marked as still alive", (long)p->pid, p->command);
+ p->running = PS_RECYCLED; /* mark as recycled */
+ }
+#endif
+
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t->next = the_pipeline;
t->pid = pid;
}
/* Return the pipeline that PID belongs to. Note that the pipeline
- doesn't have to belong to a job. Must be called with SIGCHLD blocked. */
+ doesn't have to belong to a job. Must be called with SIGCHLD blocked.
+ If JOBP is non-null, return the index of the job containing PID. */
static PROCESS *
-find_pipeline (pid, running_only, jobp)
+find_pipeline (pid, alive_only, jobp)
pid_t pid;
- int running_only;
+ int alive_only;
int *jobp; /* index into jobs list or NO_JOB */
{
int job;
- register PROCESS *p;
+ PROCESS *p;
/* See if this process is in the pipeline that we are building. */
if (jobp)
p = the_pipeline;
do
{
- /* Return it if we found it. */
- if (p->pid == pid)
- {
- if ((running_only && PALIVE(p)) || (running_only == 0))
- return (p);
- }
+ /* Return it if we found it. Don't ever return a recycled pid. */
+ if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
+ return (p);
p = p->next;
}
while (p != the_pipeline);
}
- job = find_job (pid, running_only);
+ job = find_job (pid, alive_only, &p);
if (jobp)
*jobp = job;
return (job == NO_JOB) ? (PROCESS *)NULL : jobs[job]->pipe;
}
+/* Return the PROCESS * describing PID. If JOBP is non-null return the index
+ into the jobs array of the job containing PID. Must be called with
+ SIGCHLD blocked. */
+static PROCESS *
+find_process (pid, alive_only, jobp)
+ pid_t pid;
+ int alive_only;
+ int *jobp; /* index into jobs list or NO_JOB */
+{
+ PROCESS *p;
+
+ p = find_pipeline (pid, alive_only, jobp);
+ while (p && p->pid != pid)
+ p = p->next;
+ return p;
+}
+
/* Return the job index that PID belongs to, or NO_JOB if it doesn't
belong to any job. Must be called with SIGCHLD blocked. */
static int
-find_job (pid, running_only)
+find_job (pid, alive_only, procp)
pid_t pid;
- int running_only;
+ int alive_only;
+ PROCESS **procp;
{
register int i;
- register PROCESS *p;
+ PROCESS *p;
/* XXX could use js.j_firstj here */
for (i = 0; i < js.j_jobslots; i++)
do
{
- if (p->pid == pid)
+ if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
{
- if ((running_only && PALIVE(p)) || (running_only == 0))
- return (i);
+ if (procp)
+ *procp = p;
+ return (i);
}
p = p->next;
if (block)
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (block)
UNBLOCK_CHILD (oset);
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (job != NO_JOB)
fprintf (stderr, "[%d] %ld\n", job + 1, (long)pid);
#endif /* PGRP_PIPE */
if (async_p)
- last_asynchronous_pid = getpid ();
+ last_asynchronous_pid = mypid;
+#if defined (RECYCLES_PIDS)
+ else if (last_asynchronous_pid == mypid)
+ /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
+ last_asynchronous_pid = 1;
+#endif
}
else
{
if (async_p)
last_asynchronous_pid = pid;
+#if defined (RECYCLES_PIDS)
+ else if (last_asynchronous_pid == pid)
+ /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
+ last_asynchronous_pid = 1;
+#endif
+
+#if !defined (RECYCLES_PIDS)
+ /* Only check for saved status if we've saved more than CHILD_MAX
+ statuses, unless the system recycles pids. */
+ if ((js.c_reaped + bgpids.npid) >= js.c_childmax)
+#endif
+ bgp_delete (pid); /* new process, discard any saved status */
last_made_pid = pid;
return 0;
}
-/* Given an index into the jobs array JOB, return the pid of the last
+/* Given an index into the jobs array JOB, return the PROCESS struct of the last
process in that job's pipeline. This is the one whose exit status
counts. Must be called with SIGCHLD blocked or queued. */
-static pid_t
-find_last_pid (job, block)
+static PROCESS *
+find_last_proc (job, block)
int job;
int block;
{
if (block)
UNBLOCK_CHILD (oset);
- return (p->pid);
+ return (p);
}
+static pid_t
+find_last_pid (job, block)
+ int job;
+ int block;
+{
+ PROCESS *p;
+
+ p = find_last_proc (job, block);
+ /* Possible race condition here. */
+ return p->pid;
+}
+
/* Wait for a particular child of the shell to finish executing.
This low-level function prints an error message if PID is not
a child of this shell. It returns -1 if it fails, or whatever
if (child == 0)
{
+ r = bgp_search (pid);
+ if (r >= 0)
+ return r;
+ }
+
+ if (child == 0)
+ {
internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
return (127);
}
/* POSIX.2: if we just waited for a job, we can remove it from the jobs
table. */
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (job != NO_JOB && jobs[job] && DEADJOB (job))
jobs[job]->flags |= J_NOTIFIED;
UNBLOCK_CHILD (oset);
`wait' is called with no arguments. */
mark_dead_jobs_as_notified (1);
cleanup_dead_jobs ();
+ bgp_clear ();
}
/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
We check for JDEAD in case the job state has been set by waitchld
after receipt of a SIGCHLD. */
if (job == NO_JOB)
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
/* waitchld() takes care of setting the state of the job. If the job
has already exited before this is called, sigchld_handler will have
called waitchld and the state will be set to JDEAD. */
- if (child->running || (job != NO_JOB && RUNNING (job)))
+ if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
{
#if defined (WAITPID_BROKEN) /* SCOv4 */
sigset_t suspend_set;
if (interactive && job_control == 0)
QUIT;
}
- while (child->running || (job != NO_JOB && RUNNING (job)));
+ while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
/* The exit state of the command is either the termination state of the
child, or the termination state of the job. If a job, the status
do
{
+ if (PALIVE (p) == 0)
+ continue; /* avoid pid recycling problems */
+
kill (p->pid, sig);
- if (p->running == PS_DONE && (sig == SIGTERM || sig == SIGHUP))
+ if (PEXITED (p) && (sig == SIGTERM || sig == SIGHUP))
kill (p->pid, SIGCONT);
p = p->next;
}
children_exited++;
/* Locate our PROCESS for this pid. */
- child = find_pipeline (pid, 1, &job); /* want running procs only */
+ child = find_process (pid, 1, &job); /* want living procs only */
/* It is not an error to have a child terminate that we did
not have a record of. This child could have been part of
if (child == 0)
continue;
- while (child->pid != pid)
- child = child->next;
-
/* Remember status, and whether or not the process is running. */
child->status = status;
child->running = WIFCONTINUED(status) ? PS_RUNNING : PS_DONE;
- if (child->running == PS_DONE)
+ if (PEXITED (child))
{
js.c_totreaped++;
if (job != NO_JOB)
job_state = any_stopped = any_tstped = 0;
do
{
- job_state |= child->running;
- if (child->running == PS_DONE && (WIFSTOPPED (child->status)))
+ job_state |= PRUNNING (child);
+#if 0
+ if (PEXITED (child) && (WIFSTOPPED (child->status)))
+#else
+ /* Only checking for WIFSTOPPED now, not for PS_DONE */
+ if (PSTOPPED (child))
+#endif
{
any_stopped = 1;
any_tstped |= interactive && job_control &&
if (interactive)
get_tty_state ();
+ if (js.c_childmax < 0)
+ js.c_childmax = getmaxchild ();
+ if (js.c_childmax < 0)
+ js.c_childmax = DEFAULT_CHILD_MAX;
+
return job_control;
}
}
}
+ if (running_only == 0)
+ bgp_clear ();
+
UNBLOCK_CHILD (oset);
}
}
/* Mark enough dead jobs as notified to keep CHILD_MAX processes left in the
- array with the corresponding not marked as notified. */
+ array with the corresponding not marked as notified. This is a better
+ way to avoid pid aliasing and reuse problems than keeping the POSIX-
+ mandated CHILD_MAX jobs around. delete_job() takes care of keeping the
+ bgpids list regulated. */
/* Count the number of dead jobs */
/* XXX could use js.j_firstj here */
}
}
-if (ndeadproc != js.c_reaped)
- itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
-if (ndead != js.j_ndead)
- itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
+#ifdef DEBUG
+ if (ndeadproc != js.c_reaped)
+ itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
+ if (ndead != js.j_ndead)
+ itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
+#endif
if (js.c_childmax < 0)
js.c_childmax = getmaxchild ();
return;
}
+#if 0
+itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", js.c_childmax, ndead, ndeadproc);
+#endif
+
/* Mark enough dead jobs as notified that we keep CHILD_MAX jobs in
the list. This isn't exactly right yet; changes need to be made
to stop_pipeline so we don't mark the newer jobs after we've
extern int wait_signal_received;
extern WORD_LIST *subst_assign_varlist;
-struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB };
+struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
+
+struct bgpids bgpids = { 0, 0, 0 };
/* The array of known jobs. */
JOB **jobs = (JOB **)NULL;
static int waitchld __P((pid_t, int));
static PROCESS *find_pipeline __P((pid_t, int, int *));
+static PROCESS *find_process __P((pid_t, int, int *));
static char *current_working_directory __P((void));
static char *job_working_directory __P((void));
static char *j_strsignal __P((int));
static char *printable_job_status __P((int, PROCESS *, int));
+static PROCESS *find_last_proc __P((int, int));
static pid_t find_last_pid __P((int, int));
static int set_new_line_discipline __P((int));
static int job_last_stopped __P((int));
static int job_last_running __P((int));
static int most_recent_job_in_state __P((int, JOB_STATE));
-static int find_job __P((pid_t, int));
+static int find_job __P((pid_t, int, PROCESS **));
static int print_job __P((JOB *, int, int, int));
static int process_exit_status __P((WAIT));
static int process_exit_signal __P((WAIT));
static void pipe_close __P((int *));
#endif
+static struct pidstat *bgp_alloc __P((pid_t, int));
+static struct pidstat *bgp_add __P((pid_t, int));
+static int bgp_delete __P((pid_t));
+static void bgp_clear __P((void));
+static int bgp_search __P((pid_t));
+static void bgp_prune __P((void));
+
#if defined (ARRAY_VARS)
static int *pstatuses; /* list of pipeline statuses */
static int statsize;
if (the_pipeline)
{
register PROCESS *p;
- int any_alive, any_stopped, n;
+ int any_running, any_stopped, n;
newjob = (JOB *)xmalloc (sizeof (JOB));
/* Set the state of this pipeline. */
p = newjob->pipe;
- any_alive = any_stopped = 0;
+ any_running = any_stopped = 0;
do
{
- any_alive |= p->running;
- any_stopped |= WIFSTOPPED (p->status);
+ any_running |= PRUNNING (p);
+ any_stopped |= PSTOPPED (p);
p = p->next;
}
while (p != newjob->pipe);
- newjob->state = any_alive ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
+ newjob->state = any_running ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
newjob->wd = job_working_directory ();
newjob->deferred = deferred;
if (newjob->state == JDEAD)
{
js.c_reaped += n; /* wouldn't have been done since this was not part of a job */
-itrace("stop_pipeline: job %d: js.c_reaped (%d) just added (%d)", i, js.c_reaped, n);
js.j_ndead++;
}
js.c_injobs += n;
else
newjob = (JOB *)NULL;
+ if (newjob)
+ js.j_lastmade = newjob;
+
if (async)
{
if (newjob)
- newjob->flags &= ~J_FOREGROUND;
+ {
+ newjob->flags &= ~J_FOREGROUND;
+ newjob->flags |= J_ASYNC;
+ js.j_lastasync = newjob;
+ }
reset_current ();
}
else
return (js.j_current);
}
+/* Functions to manage the list of exited background pids whose status has
+ been saved. */
+
+static struct pidstat *
+bgp_alloc (pid, status)
+ pid_t pid;
+ int status;
+{
+ struct pidstat *ps;
+
+ ps = (struct pidstat *)xmalloc (sizeof (struct pidstat));
+ ps->pid = pid;
+ ps->status = status;
+ ps->next = (struct pidstat *)0;
+ return ps;
+}
+
+static struct pidstat *
+bgp_add (pid, status)
+ pid_t pid;
+ int status;
+{
+ struct pidstat *ps;
+
+ ps = bgp_alloc (pid, status);
+
+ if (bgpids.list == 0)
+ {
+ bgpids.list = bgpids.end = ps;
+ bgpids.npid = 0; /* just to make sure */
+ }
+ else
+ {
+ bgpids.end->next = ps;
+ bgpids.end = ps;
+ }
+ bgpids.npid++;
+
+ if (bgpids.npid > js.c_childmax)
+ bgp_prune ();
+
+ return ps;
+}
+
+static int
+bgp_delete (pid)
+ pid_t pid;
+{
+ struct pidstat *prev, *p;
+
+ for (prev = p = bgpids.list; p; prev = p, p = p->next)
+ if (p->pid == pid)
+ {
+ prev->next = p->next; /* remove from list */
+ break;
+ }
+
+ if (p == 0)
+ return 0; /* not found */
+
+itrace("bgp_delete: deleting %d", pid);
+
+ /* Housekeeping in the border cases. */
+ if (p == bgpids.list)
+ bgpids.list = bgpids.list->next;
+ else if (p == bgpids.end)
+ bgpids.end = prev;
+
+ bgpids.npid--;
+ if (bgpids.npid == 0)
+ bgpids.list = bgpids.end = 0;
+ else if (bgpids.npid == 1)
+ bgpids.end = bgpids.list; /* just to make sure */
+
+ free (p);
+ return 1;
+}
+
+/* Clear out the list of saved statuses */
+static void
+bgp_clear ()
+{
+ struct pidstat *ps, *p;
+
+ for (ps = bgpids.list; ps; )
+ {
+ p = ps;
+ ps = ps->next;
+ free (p);
+ }
+ bgpids.list = bgpids.end = 0;
+ bgpids.npid = 0;
+}
+
+/* Search for PID in the list of saved background pids; return its status if
+ found. If not found, return -1. */
+static int
+bgp_search (pid)
+ pid_t pid;
+{
+ struct pidstat *ps;
+
+ for (ps = bgpids.list ; ps; ps = ps->next)
+ if (ps->pid == pid)
+ return ps->status;
+ return -1;
+}
+
+static void
+bgp_prune ()
+{
+ struct pidstat *ps, *p;
+
+ while (bgpids.npid > js.c_childmax)
+ {
+ ps = bgpids.list;
+itrace("bgp_prune: deleting %d -> %d", ps->pid, bgpids.npid - 1);
+ bgpids.list = bgpids.list->next;
+ free (ps);
+ bgpids.npid--;
+ }
+}
+
/* Reset the values of js.j_lastj and js.j_firstj after one or both have
been deleted. The caller should check whether js.j_njobs is 0 before
- calling this. */
+ calling this. This wraps around, but the rest of the code does not. At
+ this point, it should not matter. */
static void
reset_job_indices ()
{
int job_index, warn_stopped;
{
register JOB *temp;
- int ndel;
+ PROCESS *proc;
+ int ndel, status;
+ pid_t pid;
if (js.j_jobslots == 0 || jobs_list_frozen)
return;
if (warn_stopped && subshell_environment == 0 && STOPPED (job_index))
internal_warning (_("deleting stopped job %d with process group %ld"), job_index+1, (long)jobs[job_index]->pgrp);
-
temp = jobs[job_index];
if (job_index == js.j_current || job_index == js.j_previous)
reset_current ();
+ proc = find_last_proc (job_index, 0);
+ /* Could do this just for J_ASYNC jobs, but we save all. */
+ bgp_add (proc->pid, process_exit_status (proc->status));
+
jobs[job_index] = (JOB *)NULL;
+ if (temp == js.j_lastmade)
+ js.j_lastmade = 0;
+ else if (temp == js.j_lastasync)
+ js.j_lastasync = 0;
+
free (temp->wd);
ndel = discard_pipeline (temp->pipe);
{
PROCESS *t, *p;
+#if defined (RECYCLES_PIDS)
+ int j;
+ p = find_process (pid, 0, &j);
+ if (p)
+ {
+# ifdef DEBUG
+ if (j == NO_JOB)
+ internal_warning ("add_process: process %5ld (%s) in the_pipeline", (long)p->pid, p->command);
+# endif
+ if (PALIVE (p))
+ internal_warning ("add_process: pid %5ld (%s) marked as still alive", (long)p->pid, p->command);
+ p->running = PS_RECYCLED; /* mark as recycled */
+ }
+#endif
+
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t->next = the_pipeline;
t->pid = pid;
}
/* Return the pipeline that PID belongs to. Note that the pipeline
- doesn't have to belong to a job. Must be called with SIGCHLD blocked. */
+ doesn't have to belong to a job. Must be called with SIGCHLD blocked.
+ If JOBP is non-null, return the index of the job containing PID. */
static PROCESS *
-find_pipeline (pid, running_only, jobp)
+find_pipeline (pid, alive_only, jobp)
pid_t pid;
- int running_only;
+ int alive_only;
int *jobp; /* index into jobs list or NO_JOB */
{
int job;
- register PROCESS *p;
+ PROCESS *p;
/* See if this process is in the pipeline that we are building. */
if (jobp)
p = the_pipeline;
do
{
- /* Return it if we found it. */
- if (p->pid == pid)
- {
- if ((running_only && PALIVE(p)) || (running_only == 0))
- return (p);
- }
+ /* Return it if we found it. Don't ever return a recycled pid. */
+ if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
+ return (p);
p = p->next;
}
while (p != the_pipeline);
}
- job = find_job (pid, running_only);
+ job = find_job (pid, alive_only, &p);
if (jobp)
*jobp = job;
return (job == NO_JOB) ? (PROCESS *)NULL : jobs[job]->pipe;
}
+/* Return the PROCESS * describing PID. If JOBP is non-null return the index
+ into the jobs array of the job containing PID. Must be called with
+ SIGCHLD blocked. */
+static PROCESS *
+find_process (pid, alive_only, jobp)
+ pid_t pid;
+ int alive_only;
+ int *jobp; /* index into jobs list or NO_JOB */
+{
+ PROCESS *p;
+
+ p = find_pipeline (pid, alive_only, jobp);
+ while (p && p->pid != pid)
+ p = p->next;
+ return p;
+}
+
/* Return the job index that PID belongs to, or NO_JOB if it doesn't
belong to any job. Must be called with SIGCHLD blocked. */
static int
-find_job (pid, running_only)
+find_job (pid, alive_only, procp)
pid_t pid;
- int running_only;
+ int alive_only;
+ PROCESS **procp;
{
register int i;
- register PROCESS *p;
+ PROCESS *p;
/* XXX could use js.j_firstj here */
for (i = 0; i < js.j_jobslots; i++)
do
{
- if (p->pid == pid)
+ if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
{
- if ((running_only && PALIVE(p)) || (running_only == 0))
- return (i);
+ if (procp)
+ *procp = p;
+ return (i);
}
p = p->next;
if (block)
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (block)
UNBLOCK_CHILD (oset);
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (job != NO_JOB)
fprintf (stderr, "[%d] %ld\n", job + 1, (long)pid);
#endif /* PGRP_PIPE */
if (async_p)
- last_asynchronous_pid = getpid ();
+ last_asynchronous_pid = mypid;
+#if defined (RECYCLES_PIDS)
+ else if (last_asynchronous_pid == mypid)
+ /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
+ last_asynchronous_pid = 1;
+#endif
}
else
{
if (async_p)
last_asynchronous_pid = pid;
+#if defined (RECYCLES_PIDS)
+ else if (last_asynchronous_pid == pid)
+ /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
+ last_asynchronous_pid = 1;
+#endif
+
+#if !defined (RECYCLES_PIDS)
+ /* Only check for saved status if we've saved more than CHILD_MAX
+ statuses, unless the system recycles pids. */
+ if ((js.c_reaped + bgpids.npid) >= js.c_childmax)
+#endif
+ bgp_delete (pid); /* new process, discard any saved status */
last_made_pid = pid;
return 0;
}
-/* Given an index into the jobs array JOB, return the pid of the last
+/* Given an index into the jobs array JOB, return the PROCESS struct of the last
process in that job's pipeline. This is the one whose exit status
counts. Must be called with SIGCHLD blocked or queued. */
-static pid_t
-find_last_pid (job, block)
+static PROCESS *
+find_last_proc (job, block)
int job;
int block;
{
if (block)
UNBLOCK_CHILD (oset);
- return (p->pid);
+ return (p);
}
+static pid_t
+find_last_pid (job, block)
+ int job;
+ int block;
+{
+ PROCESS *p;
+
+ p = find_last_proc (job, block);
+ /* Possible race condition here. */
+ return p->pid;
+}
+
/* Wait for a particular child of the shell to finish executing.
This low-level function prints an error message if PID is not
a child of this shell. It returns -1 if it fails, or whatever
if (child == 0)
{
+ r = bgp_search (pid);
+ if (r >= 0)
+ return r;
+ }
+
+ if (child == 0)
+ {
internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
return (127);
}
/* POSIX.2: if we just waited for a job, we can remove it from the jobs
table. */
BLOCK_CHILD (set, oset);
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
if (job != NO_JOB && jobs[job] && DEADJOB (job))
jobs[job]->flags |= J_NOTIFIED;
UNBLOCK_CHILD (oset);
`wait' is called with no arguments. */
mark_dead_jobs_as_notified (1);
cleanup_dead_jobs ();
+ bgp_clear ();
}
/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
We check for JDEAD in case the job state has been set by waitchld
after receipt of a SIGCHLD. */
if (job == NO_JOB)
- job = find_job (pid, 0);
+ job = find_job (pid, 0, NULL);
/* waitchld() takes care of setting the state of the job. If the job
has already exited before this is called, sigchld_handler will have
called waitchld and the state will be set to JDEAD. */
- if (child->running || (job != NO_JOB && RUNNING (job)))
+ if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
{
#if defined (WAITPID_BROKEN) /* SCOv4 */
sigset_t suspend_set;
if (interactive && job_control == 0)
QUIT;
}
- while (child->running || (job != NO_JOB && RUNNING (job)));
+ while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
/* The exit state of the command is either the termination state of the
child, or the termination state of the job. If a job, the status
do
{
+ if (PALIVE (p) == 0)
+ continue; /* avoid pid recycling problems */
+
kill (p->pid, sig);
- if (p->running == PS_DONE && (sig == SIGTERM || sig == SIGHUP))
+ if (PEXITED (p) && (sig == SIGTERM || sig == SIGHUP))
kill (p->pid, SIGCONT);
p = p->next;
}
children_exited++;
/* Locate our PROCESS for this pid. */
- child = find_pipeline (pid, 1, &job); /* want running procs only */
+ child = find_process (pid, 1, &job); /* want living procs only */
/* It is not an error to have a child terminate that we did
not have a record of. This child could have been part of
if (child == 0)
continue;
- while (child->pid != pid)
- child = child->next;
-
/* Remember status, and whether or not the process is running. */
child->status = status;
child->running = WIFCONTINUED(status) ? PS_RUNNING : PS_DONE;
- if (child->running == PS_DONE)
+ if (PEXITED (child))
{
js.c_totreaped++;
if (job != NO_JOB)
-{
js.c_reaped++;
-itrace("waitchld: job %d js.c_reaped++ = %d", job, js.c_reaped);
-}
}
if (job == NO_JOB)
job_state = any_stopped = any_tstped = 0;
do
{
- job_state |= child->running;
- if (child->running == PS_DONE && (WIFSTOPPED (child->status)))
+ job_state |= PRUNNING (child);
+#if 0
+ if (PEXITED (child) && (WIFSTOPPED (child->status)))
+#else
+ /* Only checking for WIFSTOPPED now, not for PS_DONE */
+ if (PSTOPPED (child))
+#endif
{
any_stopped = 1;
any_tstped |= interactive && job_control &&
if (interactive)
get_tty_state ();
+ if (js.c_childmax < 0)
+ js.c_childmax = getmaxchild ();
+ if (js.c_childmax < 0)
+ js.c_childmax = DEFAULT_CHILD_MAX;
+
return job_control;
}
}
}
+ if (running_only == 0)
+ bgp_clear ();
+
UNBLOCK_CHILD (oset);
}
}
/* Mark enough dead jobs as notified to keep CHILD_MAX processes left in the
- array with the corresponding not marked as notified. */
+ array with the corresponding not marked as notified. This is a better
+ way to avoid pid aliasing and reuse problems than keeping the POSIX-
+ mandated CHILD_MAX jobs around. delete_job() takes care of keeping the
+ bgpids list regulated. */
/* Count the number of dead jobs */
/* XXX could use js.j_firstj here */
}
}
-if (ndeadproc != js.c_reaped)
- itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
-if (ndead != js.j_ndead)
- itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
+#ifdef DEBUG
+ if (ndeadproc != js.c_reaped)
+ itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
+ if (ndead != js.j_ndead)
+ itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
+#endif
+
if (js.c_childmax < 0)
js.c_childmax = getmaxchild ();
if (js.c_childmax < 0)
js.c_childmax = DEFAULT_CHILD_MAX;
-itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", js.c_childmax, ndead, ndeadproc);
/* Don't do anything if the number of dead processes is less than CHILD_MAX
and we're not forcing a cleanup. */
if (ndeadproc <= js.c_childmax)
return;
}
+#if 0
+itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", js.c_childmax, ndead, ndeadproc);
+#endif
+
/* Mark enough dead jobs as notified that we keep CHILD_MAX jobs in
the list. This isn't exactly right yet; changes need to be made
to stop_pipeline so we don't mark the newer jobs after we've
if ((ndeadproc -= processes_in_job (i)) <= js.c_childmax)
break;
jobs[i]->flags |= J_NOTIFIED;
-itrace("marking job %d as notified", i);
}
}
/* jobs.h -- structures and stuff used by the jobs.c file. */
-/* Copyright (C) 1993-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#define PS_DONE 0
#define PS_RUNNING 1
#define PS_STOPPED 2
+#define PS_RECYCLED 4
-/* Each child of the shell is remembered in a STRUCT PROCESS. A chain of
- such structures is a pipeline. The chain is circular. */
+/* Each child of the shell is remembered in a STRUCT PROCESS. A circular
+ chain of such structures is a pipeline. */
typedef struct process {
struct process *next; /* Next process in the pipeline. A circular chain. */
pid_t pid; /* Process ID. */
char *command; /* The particular program that is running. */
} PROCESS;
-/* PRUNNING really means `not exited' */
-#define PALIVE(p) ((p)->running || WIFSTOPPED((p)->status))
+/* PALIVE really means `not exited' */
#define PSTOPPED(p) (WIFSTOPPED((p)->status))
-#define PDEADPROC(p) ((p)->running == PS_DONE)
+#define PRUNNING(p) ((p)->running == PS_RUNNING)
+#define PALIVE(p) (PRUNNING(p) || PSTOPPED(p))
+
+#define PEXITED(p) ((p)->running == PS_DONE)
+#if defined (RECYCLES_PIDS)
+# define PRECYCLED(p) ((p)->running == PS_RECYCLED)
+#else
+# define PRECYCLED(p) (0)
+#endif
+#define PDEADPROC(p) (PEXITED(p) || PRECYCLED(p))
#define get_job_by_jid(ind) (jobs[(ind)])
#define RUNNING(j) (jobs[(j)]->state == JRUNNING)
#define DEADJOB(j) (jobs[(j)]->state == JDEAD)
-#define INVALID_JOB(j) ((j) < 0 || (j) >= js.j_jobslots || jobs[(j)] == 0)
+#define INVALID_JOB(j) ((j) < 0 || (j) >= js.j_jobslots || get_job_by_jid(j) == 0)
/* Values for the FLAGS field in the JOB struct below. */
#define J_FOREGROUND 0x01 /* Non-zero if this is running in the foreground. */
#define J_JOBCONTROL 0x04 /* Non-zero if this job started under job control. */
#define J_NOHUP 0x08 /* Don't send SIGHUP to job if shell gets SIGHUP. */
#define J_STATSAVED 0x10 /* A process in this job had had status saved via $! */
+#define J_ASYNC 0x20 /* Job was started asynchronously */
#define IS_FOREGROUND(j) ((jobs[j]->flags & J_FOREGROUND) != 0)
#define IS_NOTIFIED(j) ((jobs[j]->flags & J_NOTIFIED) != 0)
#define IS_JOBCONTROL(j) ((jobs[j]->flags & J_JOBCONTROL) != 0)
+#define IS_ASYNC(j) ((jobs[j]->flags & J_ASYNC) != 0)
typedef struct job {
char *wd; /* The working directory at time of invocation. */
/* */
int j_current; /* current job */
int j_previous; /* previous job */
+ /* */
+ JOB *j_lastmade; /* last job allocated by stop_pipeline */
+ JOB *j_lastasync; /* last async job allocated by stop_pipeline */
+};
+
+struct pidstat {
+ struct pidstat *next;
+ pid_t pid;
+ int status;
+};
+
+struct bgpids {
+ struct pidstat *list;
+ struct pidstat *end;
+ int npid;
};
#define NO_JOB -1 /* An impossible job array index. */
extern pid_t original_pgrp, shell_pgrp, pipeline_pgrp;
extern pid_t last_made_pid, last_asynchronous_pid;
-extern int current_job, previous_job;
extern int asynchronous_notification;
+
extern JOB **jobs;
-extern int job_slots;
extern void making_children __P((void));
extern void stop_making_children __P((void));
char *command; /* The particular program that is running. */
} PROCESS;
-/* PRUNNING really means `not exited' */
+/* PALIVE really means `not exited' */
#define PALIVE(p) ((p)->running || WIFSTOPPED((p)->status))
#define PSTOPPED(p) (WIFSTOPPED((p)->status))
#define PDEADPROC(p) ((p)->running == PS_DONE)
#define RUNNING(j) (jobs[(j)]->state == JRUNNING)
#define DEADJOB(j) (jobs[(j)]->state == JDEAD)
-#define INVALID_JOB(j) ((j) < 0 || (j) >= js.j_jobslots || jobs[(j)] == 0)
+#define INVALID_JOB(j) ((j) < 0 || (j) >= js.j_jobslots || get_job_by_jid(j) == 0)
/* Values for the FLAGS field in the JOB struct below. */
#define J_FOREGROUND 0x01 /* Non-zero if this is running in the foreground. */
#define J_JOBCONTROL 0x04 /* Non-zero if this job started under job control. */
#define J_NOHUP 0x08 /* Don't send SIGHUP to job if shell gets SIGHUP. */
#define J_STATSAVED 0x10 /* A process in this job had had status saved via $! */
+#define J_ASYNC 0x20 /* Job was started asynchronously */
#define IS_FOREGROUND(j) ((jobs[j]->flags & J_FOREGROUND) != 0)
#define IS_NOTIFIED(j) ((jobs[j]->flags & J_NOTIFIED) != 0)
#define IS_JOBCONTROL(j) ((jobs[j]->flags & J_JOBCONTROL) != 0)
+#define IS_ASYNC(j) ((jobs[j]->flags & J_ASYNC) != 0)
typedef struct job {
char *wd; /* The working directory at time of invocation. */
int j_lastj; /* last (newest) job allocated */
int j_firstj; /* first (oldest) job allocated */
int j_njobs; /* number of non-NULL jobs in jobs array */
+ int j_ndead; /* number of JDEAD jobs in jobs array */
/* */
int j_current; /* current job */
int j_previous; /* previous job */
+ /* */
+ JOB *j_lastmade; /* last job allocated by stop_pipeline */
+ JOB *j_lastasync; /* last async job allocated by stop_pipeline */
+};
+
+struct pidstat {
+ struct pidstat *next;
+ pid_t pid;
+ int status;
+};
+
+struct bgpids {
+ struct pidstat *list;
+ struct pidstat *end;
+ int npid;
};
#define NO_JOB -1 /* An impossible job array index. */
extern pid_t original_pgrp, shell_pgrp, pipeline_pgrp;
extern pid_t last_made_pid, last_asynchronous_pid;
-extern int current_job, previous_job;
extern int asynchronous_notification;
+
extern JOB **jobs;
-extern int job_slots;
extern void making_children __P((void));
extern void stop_making_children __P((void));
# simple makefile to create texindex
#
-# Copyright (C) 1996 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+# simple makefile to create texindex
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+topdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+CC = @CC@
+RM = rm
+
+SHELL = @MAKE_SHELL@
+
+DEFS = @DEFS@
+CFLAGS = @CFLAGS@ @LOCAL_CFLAGS@
+LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+INCLUDES = -I. -I../.. -I$(topdir)
+
+CCFLAGS = $(CFLAGS) $(DEFS) $(CPPFLAGS) ${INCLUDES}
+
+.c.o:
+ $(RM) -f $@
+ $(CC) $(CCFLAGS) -c $<
+
+OBJECTS = texindex.o getopt.o getopt1.o
+SOURCES = texindex.c getopt.c getopt1.c
+
+all: texindex
+
+texindex: $(OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)
+
+clean:
+ $(RM) $(OBJECTS) texindex
+
+distclean mostlyclean realclean maintainer-clean: clean
+ $(RM) Makefile
# #
####################################################################
#
-# Copyright (C) 1996 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+## -*- text -*- ####################################################
+# #
+# Makefile for the GNU Glob Library. #
+# #
+####################################################################
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+PROFILE_FLAGS = @PROFILE_FLAGS@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@
+
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+BASHINCDIR = ${topdir}/include
+
+INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib
+
+CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) ${INCLUDES} \
+ $(LOCAL_CFLAGS) $(CFLAGS)
+
+# Here is a rule for making .o files from .c files that doesn't force
+# the type of the machine (like -sun3) into the flags.
+.c.o:
+ $(CC) -c $(CCFLAGS) $<
+
+# The name of the library target.
+LIBRARY_NAME = libglob.a
+
+# The C code source files for this library.
+CSOURCES = $(srcdir)/glob.c $(srcdir)/strmatch.c $(srcdir)/smatch.c \
+ $(srcdir)/xmbsrtowcs.c
+
+# The header files for this library.
+HSOURCES = $(srcdir)/strmatch.h
+
+OBJECTS = glob.o strmatch.o smatch.o xmbsrtowcs.o
+
+# The texinfo files which document this library.
+DOCSOURCE = doc/glob.texi
+DOCOBJECT = doc/glob.dvi
+DOCSUPPORT = doc/Makefile
+DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT)
+
+SUPPORT = Makefile ChangeLog $(DOCSUPPORT)
+
+SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE)
+
+THINGS_TO_TAR = $(SOURCES) $(SUPPORT)
+
+######################################################################
+
+all: $(LIBRARY_NAME)
+
+$(LIBRARY_NAME): $(OBJECTS)
+ $(RM) -f $@
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+what-tar:
+ @for file in $(THINGS_TO_TAR); do \
+ echo $(selfdir)$$file; \
+ done
+
+documentation: force
+ -(cd doc; $(MAKE) $(MFLAGS))
+force:
+
+# The rule for 'includes' is written funny so that the if statement
+# always returns TRUE unless there really was an error installing the
+# include files.
+install:
+
+clean:
+ rm -f $(OBJECTS) $(LIBRARY_NAME)
+ -(cd doc && $(MAKE) $(MFLAGS) $@ )
+
+realclean distclean maintainer-clean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+ $(RM) -f Makefile
+
+mostlyclean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+
+######################################################################
+# #
+# Dependencies for the object files which make up this library. #
+# #
+######################################################################
+
+smatch.o: strmatch.h
+smatch.o: $(BUILD_DIR)/config.h
+smatch.o: $(BASHINCDIR)/chartypes.h
+smatch.o: $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h
+smatch.o: $(BASHINCDIR)/shmbutil.h
+smatch.o: $(topdir)/xmalloc.h
+
+strmatch.o: strmatch.h
+strmatch.o: $(BUILD_DIR)/config.h
+strmatch.o: $(BASHINCDIR)/stdc.h
+
+glob.o: $(BUILD_DIR)/config.h
+glob.o: $(topdir)/bashtypes.h $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h
+glob.o: $(BASHINCDIR)/posixstat.h $(BASHINCDIR)/memalloc.h
+glob.o: strmatch.h glob.h
+glob.o: $(BASHINCDIR)/shmbutil.h
+glob.o: $(topdir)/xmalloc.h
+
+xmbsrtowcs.o: ${BUILD_DIR}/config.h
+xmbsrtowcs.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+xmbsrtowcs.o: ${BASHINCDIR}/shmbutil.h
+
+# Rules for deficient makes, like SunOS and Solaris
+glob.o: glob.c
+strmatch.o: strmatch.c
+smatch.o: smatch.c
+xmbsrtowcs.o: xmbsrtowcs.c
+
+# dependencies for C files that include other C files
+glob.o: glob_loop.c
+smatch.o: sm_loop.c
# Skeleton Makefile for the GNU malloc code
#
#
-# Copyright (C) 1996 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+# Skeleton Makefile for the GNU malloc code
+#
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm -f
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+PROFILE_FLAGS = @PROFILE_FLAGS@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+LIBBUILD = ${BUILD_DIR}/lib
+
+BASHINCDIR = ${topdir}/include
+
+INTL_LIBSRC = ${topdir}/lib/intl
+INTL_BUILDDIR = ${LIBBUILD}/intl
+INTL_INC = @INTL_INC@
+LIBINTL_H = @LIBINTL_H@
+
+INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib $(INTL_INC)
+
+CCFLAGS = ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) \
+ $(CFLAGS) $(MALLOC_CFLAGS) $(CPPFLAGS)
+
+.c.o:
+ $(CC) $(CCFLAGS) -c $<
+
+.s.o:
+ $(CC) $(CCFLAGS) -c $<
+
+MALLOC_SOURCE = malloc.c
+STUB_SOURCE = stub.c
+
+ALLOCA_SOURCE = alloca.c
+ALLOCA_OBJECT = alloca.o
+
+MALLOC_SRC = @MALLOC_SRC@
+MALLOC = @MALLOC@
+ALLOCA = @ALLOCA@
+
+MALLOC_OBJS = malloc.o $(ALLOCA) trace.o stats.o table.o watch.o
+STUB_OBJS = $(ALLOCA) stub.o
+
+.PHONY: malloc stubmalloc
+
+all: malloc
+
+malloc: ${MALLOC_OBJS}
+ ${RM} libmalloc.a
+ ${AR} ${ARFLAGS} libmalloc.a ${MALLOC_OBJS}
+ -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a
+
+stubmalloc: ${STUB_OBJS}
+ ${RM} libmalloc.a
+ ${AR} ${ARFLAGS} libmalloc.a ${STUB_OBJS}
+ -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a
+
+alloca: ${ALLOCA}
+ ${RM} libmalloc.a
+ ${AR} ${ARFLAGS} libmalloc.a ${ALLOCA}
+ -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a
+
+alloca.o: $(srcdir)/$(ALLOCA_SOURCE)
+ $(CC) $(CCFLAGS) -c $(srcdir)/$(ALLOCA_SOURCE)
+ @- if test "$(ALLOCA_OBJECT)" != alloca.o ; then \
+ mv $(ALLOCA_OBJECT) alloca.o >/dev/null 2>&1 ; \
+ fi
+
+mostlyclean clean:
+ $(RM) *.o libmalloc.a
+
+distclean realclean maintainer-clean: clean
+ $(RM) Makefile
+
+alloca.o: $(BUILD_DIR)/config.h
+malloc.o: $(BUILD_DIR)/config.h $(topdir)/bashtypes.h getpagesize.h
+xmalloc.o: $(BUILD_DIR)/config.h $(BASHINCDIR)/ansi_stdlib.h
+trace.o: ${BUILD_DIR}/config.h
+table.o: ${BUILD_DIR}/config.h
+
+malloc.o: ${srcdir}/imalloc.h ${srcdir}/mstats.h
+malloc.o: ${srcdir}/table.h ${srcdir}/watch.h
+stats.o: ${srcdir}/imalloc.h ${srcdir}/mstats.h
+trace.o: ${srcdir}/imalloc.h
+table.o: ${srcdir}/imalloc.h ${srcdir}/table.h
+watch.o: ${srcdir}/imalloc.h ${srcdir}/watch.h
+
+malloc.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+stats.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+trace.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+table.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+watch.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h
+
+# Rules for deficient makes, like SunOS and Solaris
+stub.o: stub.c
+malloc.o: malloc.c
+table.o: table.c
+trace.o: trace.c
+stats.o: stats.c
+watch.o: watch.c
# #
#############################################################################
-# Copyright (C) 1994 Free Software Foundation, Inc.
+# Copyright (C) 1994-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+## -*- text -*- #############################################################
+# #
+# Makefile for the Bash versions of the GNU Readline and History Libraries. #
+# #
+#############################################################################
+
+# Copyright (C) 1994 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm -f
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+# Programs to make tags files.
+ETAGS = etags -tw
+CTAGS = ctags -tw
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+INCLUDES = -I. -I$(BUILD_DIR) -I$(topdir) -I$(topdir)/lib
+
+CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(APP_CFLAGS) $(CPPFLAGS) ${INCLUDES} $(LOCAL_CFLAGS) $(CFLAGS)
+
+.c.o:
+ ${RM} $@
+ $(CC) -c $(CCFLAGS) $<
+
+# The name of the main library target.
+LIBRARY_NAME = libreadline.a
+
+# The C code source files for this library.
+CSOURCES = $(srcdir)/readline.c $(srcdir)/funmap.c $(srcdir)/keymaps.c \
+ $(srcdir)/vi_mode.c $(srcdir)/parens.c $(srcdir)/rltty.c \
+ $(srcdir)/complete.c $(srcdir)/bind.c $(srcdir)/isearch.c \
+ $(srcdir)/display.c $(srcdir)/signals.c $(srcdir)/emacs_keymap.c \
+ $(srcdir)/vi_keymap.c $(srcdir)/util.c $(srcdir)/kill.c \
+ $(srcdir)/undo.c $(srcdir)/macro.c $(srcdir)/input.c \
+ $(srcdir)/callback.c $(srcdir)/terminal.c $(srcdir)/xmalloc.c \
+ $(srcdir)/history.c $(srcdir)/histsearch.c $(srcdir)/histexpand.c \
+ $(srcdir)/histfile.c $(srcdir)/nls.c $(srcdir)/search.c \
+ $(srcdir)/shell.c $(srcdir)/tilde.c $(srcdir)/savestring.c \
+ $(srcdir)/text.c $(srcdir)/misc.c $(srcdir)/compat.c \
+ $(srcdir)/mbutil.c
+
+# The header files for this library.
+HSOURCES = readline.h rldefs.h chardefs.h keymaps.h history.h histlib.h \
+ posixstat.h posixdir.h posixjmp.h tilde.h rlconf.h rltty.h \
+ ansi_stdlib.h rlstdc.h tcap.h xmalloc.h rlprivate.h rlshell.h \
+ rltypedefs.h rlmbutil.h
+
+HISTOBJ = history.o histexpand.o histfile.o histsearch.o shell.o savestring.o \
+ mbutil.o
+TILDEOBJ = tilde.o
+OBJECTS = readline.o vi_mode.o funmap.o keymaps.o parens.o search.o \
+ rltty.o complete.o bind.o isearch.o display.o signals.o \
+ util.o kill.o undo.o macro.o input.o callback.o terminal.o \
+ text.o nls.o misc.o $(HISTOBJ) $(TILDEOBJ) xmalloc.o compat.o
+
+# The texinfo files which document this library.
+DOCSOURCE = doc/rlman.texinfo doc/rltech.texinfo doc/rluser.texinfo
+DOCOBJECT = doc/readline.dvi
+DOCSUPPORT = doc/Makefile
+DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT)
+
+SUPPORT = Makefile ChangeLog $(DOCSUPPORT) examples/[-a-z.]*
+
+SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE)
+
+THINGS_TO_TAR = $(SOURCES) $(SUPPORT)
+
+INSTALLED_HEADERS = readline.h chardefs.h keymaps.h history.h tilde.h \
+ rlstdc.h rlconf.h rltypedefs.h
+
+##########################################################################
+
+all: libreadline.a libhistory.a
+
+libreadline.a: $(OBJECTS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+libhistory.a: $(HISTOBJ) xmalloc.o
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(HISTOBJ) xmalloc.o
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+documentation: force
+ test -d doc || mkdir doc
+ -( cd doc && $(MAKE) $(MFLAGS) )
+
+# Since tilde.c is shared between readline and bash, make sure we compile
+# it with the right flags when it's built as part of readline
+tilde.o: tilde.c
+ rm -f $@
+ $(CC) $(CCFLAGS) -DREADLINE_LIBRARY -c $(srcdir)/tilde.c
+
+force:
+
+install:
+ @echo "This version of the readline library should not be installed."
+
+uninstall:
+ @echo "This version of the readline library should not be installed."
+
+TAGS: force
+ $(ETAGS) $(CSOURCES) $(HSOURCES)
+
+tags: force
+ $(CTAGS) $(CSOURCES) $(HSOURCES)
+
+clean: force
+ $(RM) $(OBJECTS) *.a
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+
+mostlyclean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+
+distclean maintainer-clean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+ $(RM) Makefile
+ $(RM) TAGS tags
+
+# Dependencies
+bind.o: ansi_stdlib.h posixstat.h
+bind.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+bind.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+bind.o: history.h rlstdc.h
+callback.o: rlconf.h ansi_stdlib.h
+callback.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+callback.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+compat.o: rlstdc.h
+complete.o: ansi_stdlib.h posixdir.h posixstat.h
+complete.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+complete.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+display.o: ansi_stdlib.h posixstat.h
+display.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+display.o: tcap.h
+display.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+display.o: history.h rlstdc.h
+funmap.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+funmap.o: rlconf.h ansi_stdlib.h rlstdc.h
+funmap.o: ${BUILD_DIR}/config.h
+histexpand.o: ansi_stdlib.h
+histexpand.o: history.h histlib.h rlstdc.h
+histexpand.o: ${BUILD_DIR}/config.h
+histfile.o: ansi_stdlib.h
+histfile.o: history.h histlib.h rlstdc.h
+histfile.o: ${BUILD_DIR}/config.h
+history.o: ansi_stdlib.h
+history.o: history.h histlib.h rlstdc.h
+history.o: ${BUILD_DIR}/config.h
+histsearch.o: ansi_stdlib.h
+histsearch.o: history.h histlib.h rlstdc.h
+histsearch.o: ${BUILD_DIR}/config.h
+input.o: ansi_stdlib.h
+input.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+input.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+isearch.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+isearch.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+isearch.o: ansi_stdlib.h history.h rlstdc.h
+keymaps.o: emacs_keymap.c vi_keymap.c
+keymaps.o: keymaps.h rltypedefs.h chardefs.h rlconf.h ansi_stdlib.h
+keymaps.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+keymaps.o: ${BUILD_DIR}/config.h rlstdc.h
+kill.o: ansi_stdlib.h
+kill.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+kill.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+kill.o: history.h rlstdc.h
+macro.o: ansi_stdlib.h
+macro.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+macro.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+macro.o: history.h rlstdc.h
+mbutil.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h rlmbutil.h
+mbutil.o: readline.h keymaps.h rltypedefs.h chardefs.h rlstdc.h
+misc.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+misc.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+misc.o: history.h rlstdc.h ansi_stdlib.h
+nls.o: ansi_stdlib.h
+nls.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+nls.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+nls.o: history.h rlstdc.h
+parens.o: rlconf.h
+parens.o: ${BUILD_DIR}/config.h
+parens.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+readline.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+readline.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+readline.o: history.h rlstdc.h
+readline.o: posixstat.h ansi_stdlib.h posixjmp.h
+rltty.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+rltty.o: rltty.h
+rltty.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+search.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+search.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+search.o: ansi_stdlib.h history.h rlstdc.h
+shell.o: ${BUILD_DIR}/config.h ansi_stdlib.h
+signals.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+signals.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+signals.o: history.h rlstdc.h
+terminal.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+terminal.o: tcap.h
+terminal.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+terminal.o: history.h rlstdc.h
+text.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+text.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+text.o: history.h rlstdc.h ansi_stdlib.h
+rltty.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+tilde.o: ansi_stdlib.h
+tilde.o: ${BUILD_DIR}/config.h
+tilde.o: tilde.h
+undo.o: ansi_stdlib.h
+undo.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+undo.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+undo.o: history.h rlstdc.h xmalloc.h
+util.o: posixjmp.h ansi_stdlib.h
+util.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+util.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h
+vi_mode.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
+vi_mode.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
+vi_mode.o: history.h ansi_stdlib.h rlstdc.h
+xmalloc.o: ${BUILD_DIR}/config.h ansi_stdlib.h
+
+bind.o: rlshell.h
+histfile.o: rlshell.h
+nls.o: rlshell.h
+readline.o: rlshell.h
+shell.o: rlshell.h
+terminal.o: rlshell.h
+histexpand.o: rlshell.h
+
+bind.o: rlprivate.h
+callback.o: rlprivate.h
+complete.o: rlprivate.h
+display.o: rlprivate.h
+input.o: rlprivate.h
+isearch.o: rlprivate.h
+kill.o: rlprivate.h
+macro.o: rlprivate.h
+mbutil.o: rlprivate.h
+misc.o: rlprivate.h
+nls.o: rlprivate.h
+parens.o: rlprivate.h
+readline.o: rlprivate.h
+rltty.o: rlprivate.h
+search.o: rlprivate.h
+signals.o: rlprivate.h
+terminal.o: rlprivate.h
+text.o: rlprivate.h
+undo.o: rlprivate.h
+util.o: rlprivate.h
+vi_mode.o: rlprivate.h
+
+bind.o: xmalloc.h
+complete.o: xmalloc.h
+display.o: xmalloc.h
+funmap.o: xmalloc.h
+histexpand.o: xmalloc.h
+histfile.o: xmalloc.h
+history.o: xmalloc.h
+input.o: xmalloc.h
+isearch.o: xmalloc.h
+keymaps.o: xmalloc.h
+kill.o: xmalloc.h
+macro.o: xmalloc.h
+mbutil.o: xmalloc.h
+misc.o: xmalloc.h
+readline.o: xmalloc.h
+savestring.o: xmalloc.h
+search.o: xmalloc.h
+shell.o: xmalloc.h
+terminal.o: xmalloc.h
+text.o: xmalloc.h
+tilde.o: xmalloc.h
+undo.o: xmalloc.h
+util.o: xmalloc.h
+vi_mode.o: xmalloc.h
+xmalloc.o: xmalloc.h
+
+complete.o: rlmbutil.h
+display.o: rlmbutil.h
+histexpand.o: rlmbutil.h
+input.o: rlmbutil.h
+isearch.o: rlmbutil.h
+mbutil.o: rlmbutil.h
+misc.o: rlmbutil.h
+readline.o: rlmbutil.h
+search.o: rlmbutil.h
+text.o: rlmbutil.h
+vi_mode.o: rlmbutil.h
+
+# Rules for deficient makes, like SunOS and Solaris
+bind.o: bind.c
+callback.o: callback.c
+compat.o: compat.c
+complete.o: complete.c
+display.o: display.c
+funmap.o: funmap.c
+input.o: input.c
+isearch.o: isearch.c
+keymaps.o: keymaps.c emacs_keymap.c vi_keymap.c
+kill.o: kill.c
+macro.o: macro.c
+mbutil.o: mbutil.c
+misc.o: misc.c
+nls.o: nls.c
+parens.o: parens.c
+readline.o: readline.c
+rltty.o: rltty.c
+savestring.o: savestring.c
+search.o: search.c
+shell.o: shell.c
+signals.o: signals.c
+terminal.o: terminal.c
+text.o: text.c
+tilde.o: tilde.c
+undo.o: undo.c
+util.o: util.c
+vi_mode.o: vi_mode.c
+xmalloc.o: xmalloc.c
+
+histexpand.o: histexpand.c
+histfile.o: histfile.c
+history.o: history.c
+histsearch.o: histsearch.c
/* display.c -- readline redisplay facility. */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 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.
#endif
va_end (args);
- rl_display_prompt = msg_buf;
if (saved_local_prompt == 0)
{
rl_save_prompt ();
msg_saved_prompt = 1;
}
+ rl_display_prompt = msg_buf;
+ local_prompt = expand_prompt (msg_buf, &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)NULL;
(*rl_redisplay_function) ();
return 0;
rl_save_prompt ();
msg_saved_prompt = 1;
}
+ local_prompt = expand_prompt (msg_buf, &prompt_visible_length,
+ &prompt_last_invisible,
+ &prompt_invis_chars_first_line,
+ &prompt_physical_chars);
+ local_prompt_prefix = (char *)NULL;
(*rl_redisplay_function) ();
return 0;
/* history.c -- standalone history library */
-/* Copyright (C) 1989-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2005 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.
--- /dev/null
+/* history.c -- standalone history library */
+
+/* Copyright (C) 1989-2003 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. */
+
+/* The goal is to make the implementation transparent, so that you
+ don't have to know what data types are used, just what functions
+ you can call. I think I have done that. */
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#if defined (HAVE_STDLIB_H)
+# include <stdlib.h>
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "history.h"
+#include "histlib.h"
+
+#include "xmalloc.h"
+
+/* The number of slots to increase the_history by. */
+#define DEFAULT_HISTORY_GROW_SIZE 50
+
+static char *hist_inittime PARAMS((void));
+
+/* **************************************************************** */
+/* */
+/* History Functions */
+/* */
+/* **************************************************************** */
+
+/* An array of HIST_ENTRY. This is where we store the history. */
+static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
+
+/* Non-zero means that we have enforced a limit on the amount of
+ history that we save. */
+static int history_stifled;
+
+/* The current number of slots allocated to the input_history. */
+static int history_size;
+
+/* If HISTORY_STIFLED is non-zero, then this is the maximum number of
+ entries to remember. */
+int history_max_entries;
+int max_input_history; /* backwards compatibility */
+
+/* The current location of the interactive history pointer. Just makes
+ life easier for outside callers. */
+int history_offset;
+
+/* The number of strings currently stored in the history list. */
+int history_length;
+
+/* The logical `base' of the history array. It defaults to 1. */
+int history_base = 1;
+
+/* Return the current HISTORY_STATE of the history. */
+HISTORY_STATE *
+history_get_history_state ()
+{
+ HISTORY_STATE *state;
+
+ state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE));
+ state->entries = the_history;
+ state->offset = history_offset;
+ state->length = history_length;
+ state->size = history_size;
+ state->flags = 0;
+ if (history_stifled)
+ state->flags |= HS_STIFLED;
+
+ return (state);
+}
+
+/* Set the state of the current history array to STATE. */
+void
+history_set_history_state (state)
+ HISTORY_STATE *state;
+{
+ the_history = state->entries;
+ history_offset = state->offset;
+ history_length = state->length;
+ history_size = state->size;
+ if (state->flags & HS_STIFLED)
+ history_stifled = 1;
+}
+
+/* Begin a session in which the history functions might be used. This
+ initializes interactive variables. */
+void
+using_history ()
+{
+ history_offset = history_length;
+}
+
+/* Return the number of bytes that the primary history entries are using.
+ This just adds up the lengths of the_history->lines and the associated
+ timestamps. */
+int
+history_total_bytes ()
+{
+ register int i, result;
+
+ for (i = result = 0; the_history && the_history[i]; i++)
+ result += HISTENT_BYTES (the_history[i]);
+
+ return (result);
+}
+
+/* Returns the magic number which says what history element we are
+ looking at now. In this implementation, it returns history_offset. */
+int
+where_history ()
+{
+ return (history_offset);
+}
+
+/* Make the current history item be the one at POS, an absolute index.
+ Returns zero if POS is out of range, else non-zero. */
+int
+history_set_pos (pos)
+ int pos;
+{
+ if (pos > history_length || pos < 0 || !the_history)
+ return (0);
+ history_offset = pos;
+ return (1);
+}
+
+/* Return the current history array. The caller has to be carefull, since this
+ is the actual array of data, and could be bashed or made corrupt easily.
+ The array is terminated with a NULL pointer. */
+HIST_ENTRY **
+history_list ()
+{
+ return (the_history);
+}
+
+/* Return the history entry at the current position, as determined by
+ history_offset. If there is no entry there, return a NULL pointer. */
+HIST_ENTRY *
+current_history ()
+{
+ return ((history_offset == history_length) || the_history == 0)
+ ? (HIST_ENTRY *)NULL
+ : the_history[history_offset];
+}
+
+/* Back up history_offset to the previous history entry, and return
+ a pointer to that entry. If there is no previous entry then return
+ a NULL pointer. */
+HIST_ENTRY *
+previous_history ()
+{
+ return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL;
+}
+
+/* Move history_offset forward to the next history entry, and return
+ a pointer to that entry. If there is no next entry then return a
+ NULL pointer. */
+HIST_ENTRY *
+next_history ()
+{
+ return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset];
+}
+
+/* Return the history entry which is logically at OFFSET in the history array.
+ OFFSET is relative to history_base. */
+HIST_ENTRY *
+history_get (offset)
+ int offset;
+{
+ int local_index;
+
+ local_index = offset - history_base;
+ return (local_index >= history_length || local_index < 0 || !the_history)
+ ? (HIST_ENTRY *)NULL
+ : the_history[local_index];
+}
+
+time_t
+history_get_time (hist)
+ HIST_ENTRY *hist;
+{
+ char *ts;
+ time_t t;
+
+ if (hist == 0 || hist->timestamp == 0)
+ return 0;
+ ts = hist->timestamp;
+ if (ts[0] != history_comment_char)
+ return 0;
+ t = (time_t) atol (ts + 1); /* XXX - should use strtol() here */
+ return t;
+}
+
+static char *
+hist_inittime ()
+{
+ time_t t;
+ char ts[64], *ret;
+
+ t = (time_t) time ((time_t *)0);
+#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */
+ snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t);
+#else
+ sprintf (ts, "X%lu", (unsigned long) t);
+#endif
+ ret = savestring (ts);
+ ret[0] = history_comment_char;
+
+ return ret;
+}
+
+/* Place STRING at the end of the history list. The data field
+ is set to NULL. */
+void
+add_history (string)
+ const char *string;
+{
+ HIST_ENTRY *temp;
+
+ if (history_stifled && (history_length == history_max_entries))
+ {
+ register int i;
+
+ /* If the history is stifled, and history_length is zero,
+ and it equals history_max_entries, we don't save items. */
+ if (history_length == 0)
+ return;
+
+ /* If there is something in the slot, then remove it. */
+ if (the_history[0])
+ (void) free_history_entry (the_history[0]);
+
+ /* Copy the rest of the entries, moving down one slot. */
+ for (i = 0; i < history_length; i++)
+ the_history[i] = the_history[i + 1];
+
+ history_base++;
+ }
+ else
+ {
+ if (history_size == 0)
+ {
+ history_size = DEFAULT_HISTORY_GROW_SIZE;
+ the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *));
+ history_length = 1;
+ }
+ else
+ {
+ if (history_length == (history_size - 1))
+ {
+ history_size += DEFAULT_HISTORY_GROW_SIZE;
+ the_history = (HIST_ENTRY **)
+ xrealloc (the_history, history_size * sizeof (HIST_ENTRY *));
+ }
+ history_length++;
+ }
+ }
+
+ temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ temp->line = savestring (string);
+ temp->data = (char *)NULL;
+
+ temp->timestamp = hist_inittime ();
+
+ the_history[history_length] = (HIST_ENTRY *)NULL;
+ the_history[history_length - 1] = temp;
+}
+
+/* Change the time stamp of the most recent history entry to STRING. */
+void
+add_history_time (string)
+ const char *string;
+{
+ HIST_ENTRY *hs;
+
+ hs = the_history[history_length - 1];
+ FREE (hs->timestamp);
+ hs->timestamp = savestring (string);
+}
+
+/* Free HIST and return the data so the calling application can free it
+ if necessary and desired. */
+histdata_t
+free_history_entry (hist)
+ HIST_ENTRY *hist;
+{
+ histdata_t x;
+
+ if (hist == 0)
+ return ((histdata_t) 0);
+ FREE (hist->line);
+ FREE (hist->timestamp);
+ x = hist->data;
+ free (hist);
+ return (x);
+}
+
+/* Make the history entry at WHICH have LINE and DATA. This returns
+ the old entry so you can dispose of the data. In the case of an
+ invalid WHICH, a NULL pointer is returned. */
+HIST_ENTRY *
+replace_history_entry (which, line, data)
+ int which;
+ const char *line;
+ histdata_t data;
+{
+ HIST_ENTRY *temp, *old_value;
+
+ if (which < 0 || which >= history_length)
+ return ((HIST_ENTRY *)NULL);
+
+ temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ old_value = the_history[which];
+
+ temp->line = savestring (line);
+ temp->data = data;
+ temp->timestamp = savestring (old_value->timestamp);
+ the_history[which] = temp;
+
+ return (old_value);
+}
+
+/* Remove history element WHICH from the history. The removed
+ element is returned to you so you can free the line, data,
+ and containing structure. */
+HIST_ENTRY *
+remove_history (which)
+ int which;
+{
+ HIST_ENTRY *return_value;
+ register int i;
+
+ if (which < 0 || which >= history_length || history_length == 0)
+ return ((HIST_ENTRY *)NULL);
+
+ return_value = the_history[which];
+
+ for (i = which; i < history_length; i++)
+ the_history[i] = the_history[i + 1];
+
+ history_length--;
+
+ return (return_value);
+}
+
+/* Stifle the history list, remembering only MAX number of lines. */
+void
+stifle_history (max)
+ int max;
+{
+ register int i, j;
+
+ if (max < 0)
+ max = 0;
+
+ if (history_length > max)
+ {
+ /* This loses because we cannot free the data. */
+ for (i = 0, j = history_length - max; i < j; i++)
+ free_history_entry (the_history[i]);
+
+ history_base = i;
+ for (j = 0, i = history_length - max; j < max; i++, j++)
+ the_history[j] = the_history[i];
+ the_history[j] = (HIST_ENTRY *)NULL;
+ history_length = j;
+ }
+
+ history_stifled = 1;
+ max_input_history = history_max_entries = max;
+}
+
+/* Stop stifling the history. This returns the previous maximum
+ number of history entries. The value is positive if the history
+ was stifled, negative if it wasn't. */
+int
+unstifle_history ()
+{
+ if (history_stifled)
+ {
+ history_stifled = 0;
+ return (history_max_entries);
+ }
+ else
+ return (-history_max_entries);
+}
+
+int
+history_is_stifled ()
+{
+ return (history_stifled);
+}
+
+void
+clear_history ()
+{
+ register int i;
+
+ /* This loses because we cannot free the data. */
+ for (i = 0; i < history_length; i++)
+ {
+ free_history_entry (the_history[i]);
+ the_history[i] = (HIST_ENTRY *)NULL;
+ }
+
+ history_offset = history_length = 0;
+}
/* mbutil.c -- readline multibyte character utility functions */
-/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2005 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.
char *string;
int seed, count, find_non_zero;
{
- size_t tmp = 0;
+ size_t tmp;
mbstate_t ps;
- int point = 0;
+ int point;
wchar_t wc;
+ tmp = 0;
+
memset(&ps, 0, sizeof (mbstate_t));
if (seed < 0)
seed = 0;
if (count <= 0)
return seed;
- point = seed + _rl_adjust_point(string, seed, &ps);
+ point = seed + _rl_adjust_point (string, seed, &ps);
/* if this is true, means that seed was not pointed character
started byte. So correct the point and consume count */
if (seed < point)
break;
}
}
- return point;
+
+ return point;
}
static int
/* readline.c -- a general facility for reading lines of input
with emacs style editing and completion. */
-/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 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.
already taken care of pushing any necessary input back onto
the input queue with _rl_unget_char. */
{
-#if 0
- r = _rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key));
-#else
- /* XXX - experimental code -- might never be executed. Save
- for later. */
Keymap m = FUNCTION_TO_KEYMAP (map, key);
int type = m[ANYOTHERKEY].type;
func = m[ANYOTHERKEY].function;
}
else
r = _rl_dispatch (ANYOTHERKEY, m);
-#endif
}
else if (r && map[ANYOTHERKEY].function)
{
# Makefile for the Bash library
#
#
-# Copyright (C) 1998-2002 Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
inet_aton.c netconn.c netopen.c strpbrk.c timeval.c makepath.c \
pathcanon.c pathphys.c tmpfile.c stringlist.c stringvec.c spell.c \
shquote.c strtrans.c strindex.c snprintf.c mailstat.c \
- fmtulong.c fmtullong.c fmtumax.c shmatch.c \
+ fmtulong.c fmtullong.c fmtumax.c shmatch.c strnlen.c \
strtoll.c strtoull.c strtoimax.c strtoumax.c memset.c strstr.c \
mktime.c strftime.c xstrchr.c zcatfd.c
# The object files contained in $(LIBRARY_NAME)
LIBOBJS = @LIBOBJS@
-OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o \
+OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \
itos.o zread.o zwrite.o shtty.o shmatch.o \
netconn.o netopen.o timeval.o makepath.o pathcanon.o \
pathphys.o tmpfile.o stringlist.o stringvec.o spell.o shquote.o \
strindex.o: strindex.c
stringlist.o: stringlist.c
stringvec.o: stringvec.c
+strnlen.o: strnlen.c
strpbrk.o: strpbrk.c
strtod.o: strtod.c
strtoimax.o: strtoimax.c
strindex.o: ${BUILD_DIR}/config.h
stringlist.o: ${BUILD_DIR}/config.h
stringvec.o: ${BUILD_DIR}/config.h
+strnlen.o: ${BUILD_DIR}/config.h
strpbrk.o: ${BUILD_DIR}/config.h
strtod.o: ${BUILD_DIR}/config.h
strtoimax.o: ${BUILD_DIR}/config.h
stringvec.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
stringvec.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h ${BUILD_DIR}/version.h
+strnlen.o: ${BASHINCDIR}/stdc.h
+
strpbrk.o: ${BASHINCDIR}/stdc.h
strtod.o: ${topdir}/bashansi.h
/* netconn.c -- is a particular file descriptor a network connection?. */
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
l = sizeof(sa);
rv = getpeername(fd, &sa, &l);
- /* Solaris 2.5 getpeername() returns EINVAL if the fd is not a socket. */
- return ((rv < 0 && (errno == ENOTSOCK || errno == EINVAL)) ? 0 : 1);
+ /* Posix.2 says getpeername can return these errors. */
+ return ((rv < 0 && (errno == ENOTSOCK || errno == ENOTCONN || errno == EINVAL)) ? 0 : 1);
#else /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
# if defined (SVR4) || defined (SVR4_2)
/* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */
# #
####################################################################
-# Copyright (C) 1996 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+## -*- text -*- ####################################################
+# #
+# Makefile for termcap replacement libbrary. #
+# #
+####################################################################
+
+# Copyright (C) 1996 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+libdir = @libdir@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm -f
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+
+DEFS = @DEFS@
+
+INCLUDES = -I. -I../.. -I$(topdir) -I$(topdir)/lib -I$(srcdir)
+
+CCFLAGS = $(CFLAGS) $(DEFS) $(CPPFLAGS) ${INCLUDES}
+
+# Here is a rule for making .o files from .c files that doesn't force
+# the type of the machine (like -sun3) into the flags.
+.c.o:
+ $(CC) -c $(CCFLAGS) $<
+
+SOURCES = termcap.c tparam.c
+OBJECTS = termcap.o tparam.o
+
+DOCUMENTATION = termcap.texinfo
+
+THINGS_TO_TAR = $(SOURCES) $(DOCUMENTATION)
+
+##########################################################################
+
+all: libtermcap.a
+
+libtermcap.a: $(OBJECTS)
+ $(RM) -f $@
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+install:
+
+clean:
+ $(RM) *.o *.a *.log *.cp *.tp *.vr *.fn *.aux *.pg *.toc
+
+mostlyclean: clean
+
+distclean maintainer-clean: clean
+ $(RM) Makefile
+
+$(DESTDIR)$(libdir)/libtermcap.a: libtermcap.a
+ ${INSTALL_DATA} -c -m 644 libtermcap.a $@
+ -test -n "$(RANLIB)" && $(RANLIB) -t $@
+
+termcap.o: $(BUILD_DIR)/config.h
+tparam.o: $(BUILD_DIR)/config.h
+version.o: $(BUILD_DIR)/config.h
# #
####################################################################
-# Copyright (C) 1996 Free Software Foundation, Inc.
+# Copyright (C) 1996-2005 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null
+## -*- text -*- ####################################################
+# #
+# Makefile for the GNU Tilde Library. #
+# #
+####################################################################
+
+# Copyright (C) 1996 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+BUILD_DIR = @BUILD_DIR@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm
+CP = cp
+MV = mv
+
+SHELL = @MAKE_SHELL@
+
+PROFILE_FLAGS = @PROFILE_FLAGS@
+
+CFLAGS = @CFLAGS@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@
+
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+BASHINCDIR = ${topdir}/include
+
+INCLUDES = -I. -I../.. -I$(topdir) -I${BASHINCDIR} -I$(topdir)/lib
+
+CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) \
+ ${INCLUDES} $(LOCAL_CFLAGS) $(CFLAGS)
+
+.c.o:
+ $(CC) -c $(CCFLAGS) $<
+
+# The name of the library target.
+LIBRARY_NAME = libtilde.a
+
+# The C code source files for this library.
+CSOURCES = $(srcdir)/tilde.c
+
+# The header files for this library.
+HSOURCES = $(srcdir)/tilde.h
+
+OBJECTS = tilde.o
+
+# The texinfo files which document this library.
+DOCSOURCE = doc/tilde.texi
+DOCOBJECT = doc/tilde.dvi
+DOCSUPPORT = doc/Makefile
+DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT)
+
+SUPPORT = Makefile ChangeLog $(DOCSUPPORT)
+
+SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE)
+
+THINGS_TO_TAR = $(SOURCES) $(SUPPORT)
+
+######################################################################
+
+all: $(LIBRARY_NAME)
+
+$(LIBRARY_NAME): $(OBJECTS)
+ $(RM) -f $@
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+ -test -n "$(RANLIB)" && $(RANLIB) $@
+
+documentation: force
+ -(cd doc; $(MAKE) $(MFLAGS))
+
+force:
+
+# The rule for 'includes' is written funny so that the if statement
+# always returns TRUE unless there really was an error installing the
+# include files.
+install:
+ $(INSTALL_DATA) -c -m 644 $(LIBRARY_NAME) $(libdir)/$(LIBRARY_NAME)
+ -test -n "$(RANLIB)" && $(RANLIB) -t $(libdir)/$(LIBRARY_NAME)
+
+clean:
+ $(RM) -f $(OBJECTS) $(LIBRARY_NAME)
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+
+realclean distclean maintainer-clean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+ $(RM) -f Makefile
+
+mostlyclean: clean
+ -( cd doc && $(MAKE) $(MFLAGS) $@ )
+
+######################################################################
+# #
+# Dependencies for the object files which make up this library. #
+# #
+######################################################################
+
+tilde.o: tilde.h $(BASHINCDIR)/ansi_stdlib.h
+tilde.o: $(BUILD_DIR)/config.h
+
+# Rules for deficient makes, like SunOS and Solaris
+tilde.o: tilde.c
/* make_cmd.c -- Functions for making instances of the various
parser constructs. */
-/* Copyright (C) 1989-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
--- /dev/null
+/* make_cmd.c -- Functions for making instances of the various
+ parser constructs. */
+
+/* Copyright (C) 1989-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 <stdio.h>
+#include "bashtypes.h"
+#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include "filecntl.h"
+#include "bashansi.h"
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashintl.h"
+
+#include "syntax.h"
+#include "command.h"
+#include "general.h"
+#include "error.h"
+#include "flags.h"
+#include "make_cmd.h"
+#include "dispose_cmd.h"
+#include "variables.h"
+#include "subst.h"
+#include "input.h"
+#include "ocache.h"
+#include "externs.h"
+
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif
+
+#include "shmbutil.h"
+
+extern int line_number, current_command_line_count;
+extern int last_command_exit_value;
+
+/* Object caching */
+sh_obj_cache_t wdcache = {0, 0, 0};
+sh_obj_cache_t wlcache = {0, 0, 0};
+
+#define WDCACHESIZE 60
+#define WLCACHESIZE 60
+
+static COMMAND *make_for_or_select __P((enum command_type, WORD_DESC *, WORD_LIST *, COMMAND *, int));
+#if defined (ARITH_FOR_COMMAND)
+static WORD_LIST *make_arith_for_expr __P((char *));
+#endif
+static COMMAND *make_until_or_while __P((enum command_type, COMMAND *, COMMAND *));
+
+void
+cmd_init ()
+{
+ ocache_create (wdcache, WORD_DESC, WDCACHESIZE);
+ ocache_create (wlcache, WORD_LIST, WLCACHESIZE);
+}
+
+WORD_DESC *
+alloc_word_desc ()
+{
+ WORD_DESC *temp;
+
+ ocache_alloc (wdcache, WORD_DESC, temp);
+ temp->flags = 0;
+ temp->word = 0;
+ return temp;
+}
+
+WORD_DESC *
+make_bare_word (string)
+ const char *string;
+{
+ WORD_DESC *temp;
+
+ temp = alloc_word_desc ();
+
+ if (*string)
+ temp->word = savestring (string);
+ else
+ {
+ temp->word = (char *)xmalloc (1);
+ temp->word[0] = '\0';
+ }
+
+ return (temp);
+}
+
+WORD_DESC *
+make_word_flags (w, string)
+ WORD_DESC *w;
+ const char *string;
+{
+ register int i;
+ size_t slen;
+ DECLARE_MBSTATE;
+
+ i = 0;
+ slen = strlen (string);
+ while (i < slen)
+ {
+ switch (string[i])
+ {
+ case '$':
+ w->flags |= W_HASDOLLAR;
+ break;
+ case '\\':
+ break; /* continue the loop */
+ case '\'':
+ case '`':
+ case '"':
+ w->flags |= W_QUOTED;
+ break;
+ }
+
+ ADVANCE_CHAR (string, slen, i);
+ }
+
+ return (w);
+}
+
+WORD_DESC *
+make_word (string)
+ const char *string;
+{
+ WORD_DESC *temp;
+
+ temp = make_bare_word (string);
+ return (make_word_flags (temp, string));
+}
+
+WORD_DESC *
+make_word_from_token (token)
+ int token;
+{
+ char tokenizer[2];
+
+ tokenizer[0] = token;
+ tokenizer[1] = '\0';
+
+ return (make_word (tokenizer));
+}
+
+WORD_LIST *
+make_word_list (word, wlink)
+ WORD_DESC *word;
+ WORD_LIST *wlink;
+{
+ WORD_LIST *temp;
+
+ ocache_alloc (wlcache, WORD_LIST, temp);
+
+ temp->word = word;
+ temp->next = wlink;
+ return (temp);
+}
+
+COMMAND *
+make_command (type, pointer)
+ enum command_type type;
+ SIMPLE_COM *pointer;
+{
+ COMMAND *temp;
+
+ temp = (COMMAND *)xmalloc (sizeof (COMMAND));
+ temp->type = type;
+ temp->value.Simple = pointer;
+ temp->value.Simple->flags = temp->flags = 0;
+ temp->redirects = (REDIRECT *)NULL;
+ return (temp);
+}
+
+COMMAND *
+command_connect (com1, com2, connector)
+ COMMAND *com1, *com2;
+ int connector;
+{
+ CONNECTION *temp;
+
+ temp = (CONNECTION *)xmalloc (sizeof (CONNECTION));
+ temp->connector = connector;
+ temp->first = com1;
+ temp->second = com2;
+ return (make_command (cm_connection, (SIMPLE_COM *)temp));
+}
+
+static COMMAND *
+make_for_or_select (type, name, map_list, action, lineno)
+ enum command_type type;
+ WORD_DESC *name;
+ WORD_LIST *map_list;
+ COMMAND *action;
+ int lineno;
+{
+ FOR_COM *temp;
+
+ temp = (FOR_COM *)xmalloc (sizeof (FOR_COM));
+ temp->flags = 0;
+ temp->name = name;
+ temp->line = lineno;
+ temp->map_list = map_list;
+ temp->action = action;
+ return (make_command (type, (SIMPLE_COM *)temp));
+}
+
+COMMAND *
+make_for_command (name, map_list, action, lineno)
+ WORD_DESC *name;
+ WORD_LIST *map_list;
+ COMMAND *action;
+ int lineno;
+{
+ return (make_for_or_select (cm_for, name, map_list, action, lineno));
+}
+
+COMMAND *
+make_select_command (name, map_list, action, lineno)
+ WORD_DESC *name;
+ WORD_LIST *map_list;
+ COMMAND *action;
+ int lineno;
+{
+#if defined (SELECT_COMMAND)
+ return (make_for_or_select (cm_select, name, map_list, action, lineno));
+#else
+ last_command_exit_value = 2;
+ return ((COMMAND *)NULL);
+#endif
+}
+
+#if defined (ARITH_FOR_COMMAND)
+static WORD_LIST *
+make_arith_for_expr (s)
+ char *s;
+{
+ WORD_LIST *result;
+ WORD_DESC *wd;
+
+ if (s == 0 || *s == '\0')
+ return ((WORD_LIST *)NULL);
+ wd = make_word (s);
+ wd->flags |= W_NOGLOB|W_NOSPLIT|W_QUOTED; /* no word splitting or globbing */
+ result = make_word_list (wd, (WORD_LIST *)NULL);
+ return result;
+}
+#endif
+
+/* Note that this function calls dispose_words on EXPRS, since it doesn't
+ use the word list directly. We free it here rather than at the caller
+ because no other function in this file requires that the caller free
+ any arguments. */
+COMMAND *
+make_arith_for_command (exprs, action, lineno)
+ WORD_LIST *exprs;
+ COMMAND *action;
+ int lineno;
+{
+#if defined (ARITH_FOR_COMMAND)
+ ARITH_FOR_COM *temp;
+ WORD_LIST *init, *test, *step;
+ char *s, *t, *start;
+ int nsemi;
+
+ init = test = step = (WORD_LIST *)NULL;
+ /* Parse the string into the three component sub-expressions. */
+ start = t = s = exprs->word->word;
+ for (nsemi = 0; ;)
+ {
+ /* skip whitespace at the start of each sub-expression. */
+ while (whitespace (*s))
+ s++;
+ start = s;
+ /* skip to the semicolon or EOS */
+ while (*s && *s != ';')
+ s++;
+
+ t = (s > start) ? substring (start, 0, s - start) : (char *)NULL;
+
+ nsemi++;
+ switch (nsemi)
+ {
+ case 1:
+ init = make_arith_for_expr (t);
+ break;
+ case 2:
+ test = make_arith_for_expr (t);
+ break;
+ case 3:
+ step = make_arith_for_expr (t);
+ break;
+ }
+
+ FREE (t);
+ if (*s == '\0')
+ break;
+ s++; /* skip over semicolon */
+ }
+
+ if (nsemi != 3)
+ {
+ if (nsemi < 3)
+ parser_error (lineno, _("syntax error: arithmetic expression required"));
+ else
+ parser_error (lineno, _("syntax error: `;' unexpected"));
+ parser_error (lineno, _("syntax error: `((%s))'"), exprs->word->word);
+ last_command_exit_value = 2;
+ return ((COMMAND *)NULL);
+ }
+
+ temp = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
+ temp->flags = 0;
+ temp->line = lineno;
+ temp->init = init ? init : make_arith_for_expr ("1");
+ temp->test = test ? test : make_arith_for_expr ("1");
+ temp->step = step ? step : make_arith_for_expr ("1");
+ temp->action = action;
+
+ dispose_words (exprs);
+ return (make_command (cm_arith_for, (SIMPLE_COM *)temp));
+#else
+ dispose_words (exprs);
+ last_command_exit_value = 2;
+ return ((COMMAND *)NULL);
+#endif /* ARITH_FOR_COMMAND */
+}
+
+COMMAND *
+make_group_command (command)
+ COMMAND *command;
+{
+ GROUP_COM *temp;
+
+ temp = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
+ temp->command = command;
+ return (make_command (cm_group, (SIMPLE_COM *)temp));
+}
+
+COMMAND *
+make_case_command (word, clauses, lineno)
+ WORD_DESC *word;
+ PATTERN_LIST *clauses;
+ int lineno;
+{
+ CASE_COM *temp;
+
+ temp = (CASE_COM *)xmalloc (sizeof (CASE_COM));
+ temp->flags = 0;
+ temp->line = lineno;
+ temp->word = word;
+ temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *);
+ return (make_command (cm_case, (SIMPLE_COM *)temp));
+}
+
+PATTERN_LIST *
+make_pattern_list (patterns, action)
+ WORD_LIST *patterns;
+ COMMAND *action;
+{
+ PATTERN_LIST *temp;
+
+ temp = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
+ temp->patterns = REVERSE_LIST (patterns, WORD_LIST *);
+ temp->action = action;
+ temp->next = NULL;
+ return (temp);
+}
+
+COMMAND *
+make_if_command (test, true_case, false_case)
+ COMMAND *test, *true_case, *false_case;
+{
+ IF_COM *temp;
+
+ temp = (IF_COM *)xmalloc (sizeof (IF_COM));
+ temp->flags = 0;
+ temp->test = test;
+ temp->true_case = true_case;
+ temp->false_case = false_case;
+ return (make_command (cm_if, (SIMPLE_COM *)temp));
+}
+
+static COMMAND *
+make_until_or_while (which, test, action)
+ enum command_type which;
+ COMMAND *test, *action;
+{
+ WHILE_COM *temp;
+
+ temp = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
+ temp->flags = 0;
+ temp->test = test;
+ temp->action = action;
+ return (make_command (which, (SIMPLE_COM *)temp));
+}
+
+COMMAND *
+make_while_command (test, action)
+ COMMAND *test, *action;
+{
+ return (make_until_or_while (cm_while, test, action));
+}
+
+COMMAND *
+make_until_command (test, action)
+ COMMAND *test, *action;
+{
+ return (make_until_or_while (cm_until, test, action));
+}
+
+COMMAND *
+make_arith_command (exp)
+ WORD_LIST *exp;
+{
+#if defined (DPAREN_ARITHMETIC)
+ COMMAND *command;
+ ARITH_COM *temp;
+
+ command = (COMMAND *)xmalloc (sizeof (COMMAND));
+ command->value.Arith = temp = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));
+
+ temp->flags = 0;
+ temp->line = line_number;
+ temp->exp = exp;
+
+ command->type = cm_arith;
+ command->redirects = (REDIRECT *)NULL;
+ command->flags = 0;
+
+ return (command);
+#else
+ last_command_exit_value = 2;
+ return ((COMMAND *)NULL);
+#endif
+}
+
+#if defined (COND_COMMAND)
+struct cond_com *
+make_cond_node (type, op, left, right)
+ int type;
+ WORD_DESC *op;
+ struct cond_com *left, *right;
+{
+ COND_COM *temp;
+
+ temp = (COND_COM *)xmalloc (sizeof (COND_COM));
+ temp->flags = 0;
+ temp->line = line_number;
+ temp->type = type;
+ temp->op = op;
+ temp->left = left;
+ temp->right = right;
+
+ return (temp);
+}
+#endif
+
+COMMAND *
+make_cond_command (cond_node)
+ COND_COM *cond_node;
+{
+#if defined (COND_COMMAND)
+ COMMAND *command;
+
+ command = (COMMAND *)xmalloc (sizeof (COMMAND));
+ command->value.Cond = cond_node;
+
+ command->type = cm_cond;
+ command->redirects = (REDIRECT *)NULL;
+ command->flags = 0;
+ command->line = cond_node ? cond_node->line : 0;
+
+ return (command);
+#else
+ last_command_exit_value = 2;
+ return ((COMMAND *)NULL);
+#endif
+}
+
+COMMAND *
+make_bare_simple_command ()
+{
+ COMMAND *command;
+ SIMPLE_COM *temp;
+
+ command = (COMMAND *)xmalloc (sizeof (COMMAND));
+ command->value.Simple = temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
+
+ temp->flags = 0;
+ temp->line = line_number;
+ temp->words = (WORD_LIST *)NULL;
+ temp->redirects = (REDIRECT *)NULL;
+
+ command->type = cm_simple;
+ command->redirects = (REDIRECT *)NULL;
+ command->flags = 0;
+
+ return (command);
+}
+
+/* Return a command which is the connection of the word or redirection
+ in ELEMENT, and the command * or NULL in COMMAND. */
+COMMAND *
+make_simple_command (element, command)
+ ELEMENT element;
+ COMMAND *command;
+{
+ /* If we are starting from scratch, then make the initial command
+ structure. Also note that we have to fill in all the slots, since
+ malloc doesn't return zeroed space. */
+ if (!command)
+ command = make_bare_simple_command ();
+
+ if (element.word)
+ command->value.Simple->words = make_word_list (element.word, command->value.Simple->words);
+ else if (element.redirect)
+ {
+ REDIRECT *r = element.redirect;
+ /* Due to the way <> is implemented, there may be more than a single
+ redirection in element.redirect. We just follow the chain as far
+ as it goes, and hook onto the end. */
+ while (r->next)
+ r = r->next;
+ r->next = command->value.Simple->redirects;
+ command->value.Simple->redirects = element.redirect;
+ }
+ return (command);
+}
+
+/* Because we are Bourne compatible, we read the input for this
+ << or <<- redirection now, from wherever input is coming from.
+ We store the input read into a WORD_DESC. Replace the text of
+ the redirectee.word with the new input text. If <<- is on,
+ then remove leading TABS from each line. */
+void
+make_here_document (temp)
+ REDIRECT *temp;
+{
+ int kill_leading, redir_len;
+ char *redir_word, *document, *full_line;
+ int document_index, document_size, delim_unquoted;
+
+ if (temp->instruction != r_deblank_reading_until &&
+ temp->instruction != r_reading_until)
+ {
+ internal_error (_("make_here_document: bad instruction type %d"), temp->instruction);
+ return;
+ }
+
+ kill_leading = temp->instruction == r_deblank_reading_until;
+
+ document = (char *)NULL;
+ document_index = document_size = 0;
+
+ /* Quote removal is the only expansion performed on the delimiter
+ for here documents, making it an extremely special case. */
+ redir_word = string_quote_removal (temp->redirectee.filename->word, 0);
+
+ /* redirection_expand will return NULL if the expansion results in
+ multiple words or no words. Check for that here, and just abort
+ this here document if it does. */
+ if (redir_word)
+ redir_len = strlen (redir_word);
+ else
+ {
+ temp->here_doc_eof = (char *)xmalloc (1);
+ temp->here_doc_eof[0] = '\0';
+ goto document_done;
+ }
+
+ free (temp->redirectee.filename->word);
+ temp->here_doc_eof = redir_word;
+
+ /* Read lines from wherever lines are coming from.
+ For each line read, if kill_leading, then kill the
+ leading tab characters.
+ If the line matches redir_word exactly, then we have
+ manufactured the document. Otherwise, add the line to the
+ list of lines in the document. */
+
+ /* If the here-document delimiter was quoted, the lines should
+ be read verbatim from the input. If it was not quoted, we
+ need to perform backslash-quoted newline removal. */
+ delim_unquoted = (temp->redirectee.filename->flags & W_QUOTED) == 0;
+ while (full_line = read_secondary_line (delim_unquoted))
+ {
+ register char *line;
+ int len;
+
+ line = full_line;
+ line_number++;
+
+ if (kill_leading && *line)
+ {
+ /* Hack: To be compatible with some Bourne shells, we
+ check the word before stripping the whitespace. This
+ is a hack, though. */
+ if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
+ goto document_done;
+
+ while (*line == '\t')
+ line++;
+ }
+
+ if (*line == 0)
+ continue;
+
+ if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
+ goto document_done;
+
+ len = strlen (line);
+ if (len + document_index >= document_size)
+ {
+ document_size = document_size ? 2 * (document_size + len) : len + 2;
+ document = (char *)xrealloc (document, document_size);
+ }
+
+ /* len is guaranteed to be > 0 because of the check for line
+ being an empty string before the call to strlen. */
+ FASTCOPY (line, document + document_index, len);
+ document_index += len;
+ }
+
+document_done:
+ if (document)
+ document[document_index] = '\0';
+ else
+ {
+ document = (char *)xmalloc (1);
+ document[0] = '\0';
+ }
+ temp->redirectee.filename->word = document;
+}
+
+/* Generate a REDIRECT from SOURCE, DEST, and INSTRUCTION.
+ INSTRUCTION is the instruction type, SOURCE is a file descriptor,
+ and DEST is a file descriptor or a WORD_DESC *. */
+REDIRECT *
+make_redirection (source, instruction, dest_and_filename)
+ int source;
+ enum r_instruction instruction;
+ REDIRECTEE dest_and_filename;
+{
+ REDIRECT *temp;
+ WORD_DESC *w;
+ int wlen;
+ intmax_t lfd;
+
+ temp = (REDIRECT *)xmalloc (sizeof (REDIRECT));
+
+ /* First do the common cases. */
+ temp->redirector = source;
+ temp->redirectee = dest_and_filename;
+ temp->instruction = instruction;
+ temp->flags = 0;
+ temp->next = (REDIRECT *)NULL;
+
+ switch (instruction)
+ {
+
+ case r_output_direction: /* >foo */
+ case r_output_force: /* >| foo */
+ case r_err_and_out: /* command &>filename */
+ temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
+ break;
+
+ case r_appending_to: /* >>foo */
+ temp->flags = O_APPEND | O_WRONLY | O_CREAT;
+ break;
+
+ case r_input_direction: /* <foo */
+ case r_inputa_direction: /* foo & makes this. */
+ temp->flags = O_RDONLY;
+ break;
+
+ case r_input_output: /* <>foo */
+ temp->flags = O_RDWR | O_CREAT;
+ break;
+
+ case r_deblank_reading_until: /* <<-foo */
+ case r_reading_until: /* << foo */
+ case r_reading_string: /* <<< foo */
+ case r_close_this: /* <&- */
+ case r_duplicating_input: /* 1<&2 */
+ case r_duplicating_output: /* 1>&2 */
+ break;
+
+ /* the parser doesn't pass these. */
+ case r_move_input: /* 1<&2- */
+ case r_move_output: /* 1>&2- */
+ case r_move_input_word: /* 1<&$foo- */
+ case r_move_output_word: /* 1>&$foo- */
+ break;
+
+ /* The way the lexer works we have to do this here. */
+ case r_duplicating_input_word: /* 1<&$foo */
+ case r_duplicating_output_word: /* 1>&$foo */
+ w = dest_and_filename.filename;
+ wlen = strlen (w->word) - 1;
+ if (w->word[wlen] == '-') /* Yuck */
+ {
+ w->word[wlen] = '\0';
+ if (all_digits (w->word) && legal_number (w->word, &lfd) && lfd == (int)lfd)
+ {
+ dispose_word (w);
+ temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input : r_move_output;
+ temp->redirectee.dest = lfd;
+ }
+ else
+ temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input_word : r_move_output_word;
+ }
+
+ break;
+
+ default:
+ programming_error (_("make_redirection: redirection instruction `%d' out of range"), instruction);
+ abort ();
+ break;
+ }
+ return (temp);
+}
+
+COMMAND *
+make_function_def (name, command, lineno, lstart)
+ WORD_DESC *name;
+ COMMAND *command;
+ int lineno, lstart;
+{
+ FUNCTION_DEF *temp;
+#if defined (ARRAY_VARS)
+ SHELL_VAR *bash_source_v;
+ ARRAY *bash_source_a;
+ char *t;
+#endif
+
+ temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
+ temp->command = command;
+ temp->name = name;
+ temp->line = lineno;
+ temp->flags = 0;
+ command->line = lstart;
+
+ /* Information used primarily for debugging. */
+ temp->source_file = 0;
+#if defined (ARRAY_VARS)
+ GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
+ if (bash_source_a && array_num_elements (bash_source_a) > 0)
+ temp->source_file = array_reference (bash_source_a, 0);
+#endif
+ bind_function_def (name->word, temp);
+
+ temp->source_file = 0;
+ return (make_command (cm_function_def, (SIMPLE_COM *)temp));
+}
+
+COMMAND *
+make_subshell_command (command)
+ COMMAND *command;
+{
+ SUBSHELL_COM *temp;
+
+ temp = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
+ temp->command = command;
+ temp->flags = CMD_WANT_SUBSHELL;
+ return (make_command (cm_subshell, (SIMPLE_COM *)temp));
+}
+
+/* Reverse the word list and redirection list in the simple command
+ has just been parsed. It seems simpler to do this here the one
+ time then by any other method that I can think of. */
+COMMAND *
+clean_simple_command (command)
+ COMMAND *command;
+{
+ if (command->type != cm_simple)
+ command_error ("clean_simple_command", CMDERR_BADTYPE, command->type, 0);
+ else
+ {
+ command->value.Simple->words =
+ REVERSE_LIST (command->value.Simple->words, WORD_LIST *);
+ command->value.Simple->redirects =
+ REVERSE_LIST (command->value.Simple->redirects, REDIRECT *);
+ }
+
+ return (command);
+}
+
+/* The Yacc grammar productions have a problem, in that they take a
+ list followed by an ampersand (`&') and do a simple command connection,
+ making the entire list effectively asynchronous, instead of just
+ the last command. This means that when the list is executed, all
+ the commands have stdin set to /dev/null when job control is not
+ active, instead of just the last. This is wrong, and needs fixing
+ up. This function takes the `&' and applies it to the last command
+ in the list. This is done only for lists connected by `;'; it makes
+ `;' bind `tighter' than `&'. */
+COMMAND *
+connect_async_list (command, command2, connector)
+ COMMAND *command, *command2;
+ int connector;
+{
+ COMMAND *t, *t1, *t2;
+
+ t1 = command;
+ t = command->value.Connection->second;
+
+ if (!t || (command->flags & CMD_WANT_SUBSHELL) ||
+ command->value.Connection->connector != ';')
+ {
+ t = command_connect (command, command2, connector);
+ return t;
+ }
+
+ /* This is just defensive programming. The Yacc precedence rules
+ will generally hand this function a command where t points directly
+ to the command we want (e.g. given a ; b ; c ; d &, t1 will point
+ to the `a ; b ; c' list and t will be the `d'). We only want to do
+ this if the list is not being executed as a unit in the background
+ with `( ... )', so we have to check for CMD_WANT_SUBSHELL. That's
+ the only way to tell. */
+ while (((t->flags & CMD_WANT_SUBSHELL) == 0) && t->type == cm_connection &&
+ t->value.Connection->connector == ';')
+ {
+ t1 = t;
+ t = t->value.Connection->second;
+ }
+ /* Now we have t pointing to the last command in the list, and
+ t1->value.Connection->second == t. */
+ t2 = command_connect (t, command2, connector);
+ t1->value.Connection->second = t2;
+ return command;
+}
/* make_cmd.h -- Declarations of functions found in make_cmd.c */
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
--- /dev/null
+/* make_cmd.h -- Declarations of functions found in make_cmd.c */
+
+/* Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_MAKE_CMD_H_)
+#define _MAKE_CMD_H_
+
+#include "stdc.h"
+
+extern void cmd_init __P((void));
+
+extern WORD_DESC *alloc_word_desc __P((void));
+extern WORD_DESC *make_bare_word __P((const char *));
+extern WORD_DESC *make_word_flags __P((WORD_DESC *, const char *));
+extern WORD_DESC *make_word __P((const char *));
+extern WORD_DESC *make_word_from_token __P((int));
+
+extern WORD_LIST *make_word_list __P((WORD_DESC *, WORD_LIST *));
+
+#define add_string_to_list(s, l) make_word_list (make_word(s), (l))
+
+extern COMMAND *make_command __P((enum command_type, SIMPLE_COM *));
+extern COMMAND *command_connect __P((COMMAND *, COMMAND *, int));
+extern COMMAND *make_for_command __P((WORD_DESC *, WORD_LIST *, COMMAND *, int));
+extern COMMAND *make_group_command __P((COMMAND *));
+extern COMMAND *make_case_command __P((WORD_DESC *, PATTERN_LIST *, int));
+extern PATTERN_LIST *make_pattern_list __P((WORD_LIST *, COMMAND *));
+extern COMMAND *make_if_command __P((COMMAND *, COMMAND *, COMMAND *));
+extern COMMAND *make_while_command __P((COMMAND *, COMMAND *));
+extern COMMAND *make_until_command __P((COMMAND *, COMMAND *));
+extern COMMAND *make_bare_simple_command __P((void));
+extern COMMAND *make_simple_command __P((ELEMENT, COMMAND *));
+extern void make_here_document __P((REDIRECT *));
+extern REDIRECT *make_redirection __P((int, enum r_instruction, REDIRECTEE));
+extern COMMAND *make_function_def __P((WORD_DESC *, COMMAND *, int, int));
+extern COMMAND *clean_simple_command __P((COMMAND *));
+
+extern COMMAND *make_arith_command __P((WORD_LIST *));
+
+extern COMMAND *make_select_command __P((WORD_DESC *, WORD_LIST *, COMMAND *, int));
+
+#if defined (COND_COMMAND)
+extern COND_COM *make_cond_node __P((int, WORD_DESC *, COND_COM *, COND_COM *));
+extern COMMAND *make_cond_command __P((COND_COM *));
+#endif
+
+extern COMMAND *make_arith_for_command __P((WORD_LIST *, COMMAND *, int));
+
+extern COMMAND *make_subshell_command __P((COMMAND *));
+
+extern COMMAND *connect_async_list __P((COMMAND *, COMMAND *, int));
+
+#endif /* !_MAKE_CMD_H */
/* Yacc grammar for bash. */
-/* Copyright (C) 1989-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
if (uc)
shell_input_line_index++;
-#if 0
- if MBTEST(uc == '\\' && remove_quoted_newline && shell_input_line[shell_input_line_index] == '\n')
- {
- if (SHOULD_PROMPT ())
- prompt_again ();
- line_number++;
- goto restart_read;
- }
-#endif
-
#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
/* If UC is NULL, we have reached the end of the current input string. If
pushed_string_list is non-empty, it's time to pop to the previous string
}
#endif /* ALIAS || DPAREN_ARITHMETIC */
-#if 1
if MBTEST(uc == '\\' && remove_quoted_newline && shell_input_line[shell_input_line_index] == '\n')
{
if (SHOULD_PROMPT ())
line_number++;
goto restart_read;
}
-#endif
if (!uc && shell_input_line_terminator == EOF)
return ((shell_input_line_index != 0) ? '\n' : EOF);
/* pcomplete.c - functions to generate lists of matches for programmable
completion. */
-/* Copyright (C) 1999-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
register int i;
register PROCESS *p;
char *s, *t;
+ JOB *j;
JOB_STATE ws; /* wanted state */
if (jstate == 0)
sl = strlist_create (js.j_jobslots);
for (i = js.j_jobslots - 1; i >= 0; i--)
{
- if (jobs[i] == 0)
+ j = get_job_by_jid (i);
+ if (j == 0)
continue;
- p = jobs[i]->pipe;
+ p = j->pipe;
if (jstate == -1 || JOBSTATE(i) == ws)
{
s = savestring (p->command);
/* print_command -- A way to make readable commands from a command tree. */
-/* Copyright (C) 1989-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
--- /dev/null
+/* print_command -- A way to make readable commands from a command tree. */
+
+/* Copyright (C) 1989-2004 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 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 <stdio.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#if defined (PREFER_STDARG)
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "bashansi.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include "flags.h"
+#include <y.tab.h> /* use <...> so we pick it up from the build directory */
+
+#include "shmbutil.h"
+
+#include "builtins/common.h"
+
+#if !HAVE_DECL_PRINTF
+extern int printf __P((const char *, ...)); /* Yuck. Double yuck. */
+#endif
+
+extern int indirection_level;
+
+static int indentation;
+static int indentation_amount = 4;
+
+#if defined (PREFER_STDARG)
+typedef void PFUNC __P((const char *, ...));
+
+static void cprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2)));
+static void xprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2)));
+#else
+#define PFUNC VFunction
+static void cprintf ();
+static void xprintf ();
+#endif
+
+static void reset_locals __P((void));
+static void newline __P((char *));
+static void indent __P((int));
+static void semicolon __P((void));
+static void the_printed_command_resize __P((int));
+
+static void make_command_string_internal __P((COMMAND *));
+static void _print_word_list __P((WORD_LIST *, char *, PFUNC *));
+static void command_print_word_list __P((WORD_LIST *, char *));
+static void print_case_clauses __P((PATTERN_LIST *));
+static void print_redirection_list __P((REDIRECT *));
+static void print_redirection __P((REDIRECT *));
+
+static void print_for_command __P((FOR_COM *));
+#if defined (ARITH_FOR_COMMAND)
+static void print_arith_for_command __P((ARITH_FOR_COM *));
+#endif
+#if defined (SELECT_COMMAND)
+static void print_select_command __P((SELECT_COM *));
+#endif
+static void print_group_command __P((GROUP_COM *));
+static void print_case_command __P((CASE_COM *));
+static void print_while_command __P((WHILE_COM *));
+static void print_until_command __P((WHILE_COM *));
+static void print_until_or_while __P((WHILE_COM *, char *));
+static void print_if_command __P((IF_COM *));
+#if defined (COND_COMMAND)
+static void print_cond_node __P((COND_COM *));
+#endif
+static void print_function_def __P((FUNCTION_DEF *));
+
+#define PRINTED_COMMAND_INITIAL_SIZE 64
+#define PRINTED_COMMAND_GROW_SIZE 128
+
+char *the_printed_command = (char *)NULL;
+int the_printed_command_size = 0;
+int command_string_index = 0;
+
+/* Non-zero means the stuff being printed is inside of a function def. */
+static int inside_function_def;
+static int skip_this_indent;
+static int was_heredoc;
+
+/* The depth of the group commands that we are currently printing. This
+ includes the group command that is a function body. */
+static int group_command_nesting;
+
+/* A buffer to indicate the indirection level (PS4) when set -x is enabled. */
+static char indirection_string[100];
+
+/* Print COMMAND (a command tree) on standard output. */
+void
+print_command (command)
+ COMMAND *command;
+{
+ command_string_index = 0;
+ printf ("%s", make_command_string (command));
+}
+
+/* Make a string which is the printed representation of the command
+ tree in COMMAND. We return this string. However, the string is
+ not consed, so you have to do that yourself if you want it to
+ remain around. */
+char *
+make_command_string (command)
+ COMMAND *command;
+{
+ command_string_index = was_heredoc = 0;
+ make_command_string_internal (command);
+ return (the_printed_command);
+}
+
+/* The internal function. This is the real workhorse. */
+static void
+make_command_string_internal (command)
+ COMMAND *command;
+{
+ if (command == 0)
+ cprintf ("");
+ else
+ {
+ if (skip_this_indent)
+ skip_this_indent--;
+ else
+ indent (indentation);
+
+ if (command->flags & CMD_TIME_PIPELINE)
+ {
+ cprintf ("time ");
+ if (command->flags & CMD_TIME_POSIX)
+ cprintf ("-p ");
+ }
+
+ if (command->flags & CMD_INVERT_RETURN)
+ cprintf ("! ");
+
+ switch (command->type)
+ {
+ case cm_for:
+ print_for_command (command->value.For);
+ break;
+
+#if defined (ARITH_FOR_COMMAND)
+ case cm_arith_for:
+ print_arith_for_command (command->value.ArithFor);
+ break;
+#endif
+
+#if defined (SELECT_COMMAND)
+ case cm_select:
+ print_select_command (command->value.Select);
+ break;
+#endif
+
+ case cm_case:
+ print_case_command (command->value.Case);
+ break;
+
+ case cm_while:
+ print_while_command (command->value.While);
+ break;
+
+ case cm_until:
+ print_until_command (command->value.While);
+ break;
+
+ case cm_if:
+ print_if_command (command->value.If);
+ break;
+
+#if defined (DPAREN_ARITHMETIC)
+ case cm_arith:
+ print_arith_command (command->value.Arith->exp);
+ break;
+#endif
+
+#if defined (COND_COMMAND)
+ case cm_cond:
+ print_cond_command (command->value.Cond);
+ break;
+#endif
+
+ case cm_simple:
+ print_simple_command (command->value.Simple);
+ break;
+
+ case cm_connection:
+
+ skip_this_indent++;
+ make_command_string_internal (command->value.Connection->first);
+
+ switch (command->value.Connection->connector)
+ {
+ case '&':
+ case '|':
+ {
+ char c = command->value.Connection->connector;
+ cprintf (" %c", c);
+ if (c != '&' || command->value.Connection->second)
+ {
+ cprintf (" ");
+ skip_this_indent++;
+ }
+ }
+ break;
+
+ case AND_AND:
+ cprintf (" && ");
+ if (command->value.Connection->second)
+ skip_this_indent++;
+ break;
+
+ case OR_OR:
+ cprintf (" || ");
+ if (command->value.Connection->second)
+ skip_this_indent++;
+ break;
+
+ case ';':
+ if (was_heredoc == 0)
+ cprintf (";");
+ else
+ was_heredoc = 0;
+
+ if (inside_function_def)
+ cprintf ("\n");
+ else
+ {
+ cprintf (" ");
+ if (command->value.Connection->second)
+ skip_this_indent++;
+ }
+ break;
+
+ default:
+ cprintf (_("print_command: bad connector `%d'"),
+ command->value.Connection->connector);
+ break;
+ }
+
+ make_command_string_internal (command->value.Connection->second);
+ break;
+
+ case cm_function_def:
+ print_function_def (command->value.Function_def);
+ break;
+
+ case cm_group:
+ print_group_command (command->value.Group);
+ break;
+
+ case cm_subshell:
+ cprintf ("( ");
+ skip_this_indent++;
+ make_command_string_internal (command->value.Subshell->command);
+ cprintf (" )");
+ break;
+
+ default:
+ command_error ("print_command", CMDERR_BADTYPE, command->type, 0);
+ break;
+ }
+
+
+ if (command->redirects)
+ {
+ cprintf (" ");
+ print_redirection_list (command->redirects);
+ }
+ }
+}
+
+static void
+_print_word_list (list, separator, pfunc)
+ WORD_LIST *list;
+ char *separator;
+ PFUNC *pfunc;
+{
+ WORD_LIST *w;
+
+ for (w = list; w; w = w->next)
+ (*pfunc) ("%s%s", w->word->word, w->next ? separator : "");
+}
+
+void
+print_word_list (list, separator)
+ WORD_LIST *list;
+ char *separator;
+{
+ _print_word_list (list, separator, xprintf);
+}
+
+/* Return a string denoting what our indirection level is. */
+
+char *
+indirection_level_string ()
+{
+ register int i, j;
+ char *ps4;
+ char ps4_firstc[MB_LEN_MAX+1];
+ int ps4_firstc_len, ps4_len;
+
+ indirection_string[0] = '\0';
+ ps4 = get_string_value ("PS4");
+
+ if (ps4 == 0 || *ps4 == '\0')
+ return (indirection_string);
+
+ change_flag ('x', FLAG_OFF);
+ ps4 = decode_prompt_string (ps4);
+ change_flag ('x', FLAG_ON);
+
+ if (ps4 == 0 || *ps4 == '\0')
+ return (indirection_string);
+
+#if defined (HANDLE_MULTIBYTE)
+ ps4_len = strnlen (ps4, MB_CUR_MAX);
+ ps4_firstc_len = MBLEN (ps4, ps4_len);
+ if (ps4_firstc_len == 1 || ps4_firstc_len == 0 || MB_INVALIDCH (ps4_firstc_len))
+ {
+ ps4_firstc[0] = ps4[0];
+ ps4_firstc[ps4_firstc_len = 1] = '\0';
+ }
+ else
+ memcpy (ps4_firstc, ps4, ps4_firstc_len);
+#else
+ ps4_firstc[0] = ps4[0];
+ ps4_firstc[ps4_firstc_len = 1] = '\0';
+#endif
+
+ for (i = j = 0; ps4_firstc[0] && j < indirection_level && i < 99; i += ps4_firstc_len, j++)
+ {
+ if (ps4_firstc_len == 1)
+ indirection_string[i] = ps4_firstc[0];
+ else
+ memcpy (indirection_string+i, ps4_firstc, ps4_firstc_len);
+ }
+
+ for (j = ps4_firstc_len; *ps4 && ps4[j] && i < 99; i++, j++)
+ indirection_string[i] = ps4[j];
+
+ indirection_string[i] = '\0';
+ free (ps4);
+ return (indirection_string);
+}
+
+void
+xtrace_print_assignment (name, value, assign_list, xflags)
+ char *name, *value;
+ int assign_list, xflags;
+{
+ char *nval;
+
+ if (xflags)
+ fprintf (stderr, "%s", indirection_level_string ());
+
+ /* VALUE should not be NULL when this is called. */
+ if (*value == '\0' || assign_list)
+ nval = value;
+ else if (sh_contains_shell_metas (value))
+ nval = sh_single_quote (value);
+ else if (ansic_shouldquote (value))
+ nval = ansic_quote (value, 0, (int *)0);
+ else
+ nval = value;
+
+ if (assign_list)
+ fprintf (stderr, "%s=(%s)\n", name, nval);
+ else
+ fprintf (stderr, "%s=%s\n", name, nval);
+
+ if (nval != value)
+ FREE (nval);
+
+ fflush (stderr);
+}
+
+/* A function to print the words of a simple command when set -x is on. */
+void
+xtrace_print_word_list (list, xtflags)
+ WORD_LIST *list;
+ int xtflags;
+{
+ WORD_LIST *w;
+ char *t, *x;
+
+ if (xtflags)
+ fprintf (stderr, "%s", indirection_level_string ());
+
+ for (w = list; w; w = w->next)
+ {
+ t = w->word->word;
+ if (t == 0 || *t == '\0')
+ fprintf (stderr, "''%s", w->next ? " " : "");
+ else if (sh_contains_shell_metas (t))
+ {
+ x = sh_single_quote (t);
+ fprintf (stderr, "%s%s", x, w->next ? " " : "");
+ free (x);
+ }
+ else if (ansic_shouldquote (t))
+ {
+ x = ansic_quote (t, 0, (int *)0);
+ fprintf (stderr, "%s%s", x, w->next ? " " : "");
+ free (x);
+ }
+ else
+ fprintf (stderr, "%s%s", t, w->next ? " " : "");
+ }
+ fprintf (stderr, "\n");
+}
+
+static void
+command_print_word_list (list, separator)
+ WORD_LIST *list;
+ char *separator;
+{
+ _print_word_list (list, separator, cprintf);
+}
+
+void
+print_for_command_head (for_command)
+ FOR_COM *for_command;
+{
+ cprintf ("for %s in ", for_command->name->word);
+ command_print_word_list (for_command->map_list, " ");
+}
+
+void
+xtrace_print_for_command_head (for_command)
+ FOR_COM *for_command;
+{
+ fprintf (stderr, "%s", indirection_level_string ());
+ fprintf (stderr, "for %s in ", for_command->name->word);
+ xtrace_print_word_list (for_command->map_list, 0);
+}
+
+static void
+print_for_command (for_command)
+ FOR_COM *for_command;
+{
+ print_for_command_head (for_command);
+
+ cprintf (";");
+ newline ("do\n");
+ indentation += indentation_amount;
+ make_command_string_internal (for_command->action);
+ semicolon ();
+ indentation -= indentation_amount;
+ newline ("done");
+}
+
+#if defined (ARITH_FOR_COMMAND)
+static void
+print_arith_for_command (arith_for_command)
+ ARITH_FOR_COM *arith_for_command;
+{
+ cprintf ("for ((");
+ command_print_word_list (arith_for_command->init, " ");
+ cprintf (" ; ");
+ command_print_word_list (arith_for_command->test, " ");
+ cprintf (" ; ");
+ command_print_word_list (arith_for_command->step, " ");
+ cprintf ("))");
+ newline ("do\n");
+ indentation += indentation_amount;
+ make_command_string_internal (arith_for_command->action);
+ semicolon ();
+ indentation -= indentation_amount;
+ newline ("done");
+}
+#endif /* ARITH_FOR_COMMAND */
+
+#if defined (SELECT_COMMAND)
+void
+print_select_command_head (select_command)
+ SELECT_COM *select_command;
+{
+ cprintf ("select %s in ", select_command->name->word);
+ command_print_word_list (select_command->map_list, " ");
+}
+
+void
+xtrace_print_select_command_head (select_command)
+ SELECT_COM *select_command;
+{
+ fprintf (stderr, "%s", indirection_level_string ());
+ fprintf (stderr, "select %s in ", select_command->name->word);
+ xtrace_print_word_list (select_command->map_list, 0);
+}
+
+static void
+print_select_command (select_command)
+ SELECT_COM *select_command;
+{
+ print_select_command_head (select_command);
+
+ cprintf (";");
+ newline ("do\n");
+ indentation += indentation_amount;
+ make_command_string_internal (select_command->action);
+ semicolon ();
+ indentation -= indentation_amount;
+ newline ("done");
+}
+#endif /* SELECT_COMMAND */
+
+static void
+print_group_command (group_command)
+ GROUP_COM *group_command;
+{
+ group_command_nesting++;
+ cprintf ("{ ");
+
+ if (inside_function_def == 0)
+ skip_this_indent++;
+ else
+ {
+ /* This is a group command { ... } inside of a function
+ definition, and should be printed as a multiline group
+ command, using the current indentation. */
+ cprintf ("\n");
+ indentation += indentation_amount;
+ }
+
+ make_command_string_internal (group_command->command);
+
+ if (inside_function_def)
+ {
+ cprintf ("\n");
+ indentation -= indentation_amount;
+ indent (indentation);
+ }
+ else
+ {
+ semicolon ();
+ cprintf (" ");
+ }
+
+ cprintf ("}");
+
+ group_command_nesting--;
+}
+
+void
+print_case_command_head (case_command)
+ CASE_COM *case_command;
+{
+ cprintf ("case %s in ", case_command->word->word);
+}
+
+void
+xtrace_print_case_command_head (case_command)
+ CASE_COM *case_command;
+{
+ fprintf (stderr, "%s", indirection_level_string ());
+ fprintf (stderr, "case %s in\n", case_command->word->word);
+}
+
+static void
+print_case_command (case_command)
+ CASE_COM *case_command;
+{
+ print_case_command_head (case_command);
+
+ if (case_command->clauses)
+ print_case_clauses (case_command->clauses);
+ newline ("esac");
+}
+
+static void
+print_case_clauses (clauses)
+ PATTERN_LIST *clauses;
+{
+ indentation += indentation_amount;
+ while (clauses)
+ {
+ newline ("");
+ command_print_word_list (clauses->patterns, " | ");
+ cprintf (")\n");
+ indentation += indentation_amount;
+ make_command_string_internal (clauses->action);
+ indentation -= indentation_amount;
+ newline (";;");
+ clauses = clauses->next;
+ }
+ indentation -= indentation_amount;
+}
+
+static void
+print_while_command (while_command)
+ WHILE_COM *while_command;
+{
+ print_until_or_while (while_command, "while");
+}
+
+static void
+print_until_command (while_command)
+ WHILE_COM *while_command;
+{
+ print_until_or_while (while_command, "until");
+}
+
+static void
+print_until_or_while (while_command, which)
+ WHILE_COM *while_command;
+ char *which;
+{
+ cprintf ("%s ", which);
+ skip_this_indent++;
+ make_command_string_internal (while_command->test);
+ semicolon ();
+ cprintf (" do\n"); /* was newline ("do\n"); */
+ indentation += indentation_amount;
+ make_command_string_internal (while_command->action);
+ indentation -= indentation_amount;
+ semicolon ();
+ newline ("done");
+}
+
+static void
+print_if_command (if_command)
+ IF_COM *if_command;
+{
+ cprintf ("if ");
+ skip_this_indent++;
+ make_command_string_internal (if_command->test);
+ semicolon ();
+ cprintf (" then\n");
+ indentation += indentation_amount;
+ make_command_string_internal (if_command->true_case);
+ indentation -= indentation_amount;
+
+ if (if_command->false_case)
+ {
+ semicolon ();
+ newline ("else\n");
+ indentation += indentation_amount;
+ make_command_string_internal (if_command->false_case);
+ indentation -= indentation_amount;
+ }
+ semicolon ();
+ newline ("fi");
+}
+
+#if defined (DPAREN_ARITHMETIC)
+void
+print_arith_command (arith_cmd_list)
+ WORD_LIST *arith_cmd_list;
+{
+ cprintf ("((");
+ command_print_word_list (arith_cmd_list, " ");
+ cprintf ("))");
+}
+#endif
+
+#if defined (COND_COMMAND)
+static void
+print_cond_node (cond)
+ COND_COM *cond;
+{
+ if (cond->flags & CMD_INVERT_RETURN)
+ cprintf ("! ");
+
+ if (cond->type == COND_EXPR)
+ {
+ cprintf ("( ");
+ print_cond_node (cond->left);
+ cprintf (" )");
+ }
+ else if (cond->type == COND_AND)
+ {
+ print_cond_node (cond->left);
+ cprintf (" && ");
+ print_cond_node (cond->right);
+ }
+ else if (cond->type == COND_OR)
+ {
+ print_cond_node (cond->left);
+ cprintf (" || ");
+ print_cond_node (cond->right);
+ }
+ else if (cond->type == COND_UNARY)
+ {
+ cprintf ("%s", cond->op->word);
+ cprintf (" ");
+ print_cond_node (cond->left);
+ }
+ else if (cond->type == COND_BINARY)
+ {
+ print_cond_node (cond->left);
+ cprintf (" ");
+ cprintf ("%s", cond->op->word);
+ cprintf (" ");
+ print_cond_node (cond->right);
+ }
+ else if (cond->type == COND_TERM)
+ {
+ cprintf ("%s", cond->op->word); /* need to add quoting here */
+ }
+}
+
+void
+print_cond_command (cond)
+ COND_COM *cond;
+{
+ cprintf ("[[ ");
+ print_cond_node (cond);
+ cprintf (" ]]");
+}
+
+#ifdef DEBUG
+void
+debug_print_cond_command (cond)
+ COND_COM *cond;
+{
+ fprintf (stderr, "DEBUG: ");
+ command_string_index = 0;
+ print_cond_command (cond);
+ fprintf (stderr, "%s\n", the_printed_command);
+}
+#endif
+
+void
+xtrace_print_cond_term (type, invert, op, arg1, arg2)
+ int type, invert;
+ WORD_DESC *op;
+ char *arg1, *arg2;
+{
+ command_string_index = 0;
+ fprintf (stderr, "%s", indirection_level_string ());
+ fprintf (stderr, "[[ ");
+ if (invert)
+ fprintf (stderr, "! ");
+
+ if (type == COND_UNARY)
+ {
+ fprintf (stderr, "%s ", op->word);
+ fprintf (stderr, "%s", (arg1 && *arg1) ? arg1 : "''");
+ }
+ else if (type == COND_BINARY)
+ {
+ fprintf (stderr, "%s", (arg1 && *arg1) ? arg1 : "''");
+ fprintf (stderr, " %s ", op->word);
+ fprintf (stderr, "%s", (arg2 && *arg2) ? arg2 : "''");
+ }
+
+ fprintf (stderr, " ]]\n");
+}
+#endif /* COND_COMMAND */
+
+#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
+/* A function to print the words of an arithmetic command when set -x is on. */
+void
+xtrace_print_arith_cmd (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *w;
+
+ fprintf (stderr, "%s", indirection_level_string ());
+ fprintf (stderr, "(( ");
+ for (w = list; w; w = w->next)
+ fprintf (stderr, "%s%s", w->word->word, w->next ? " " : "");
+ fprintf (stderr, " ))\n");
+}
+#endif
+
+void
+print_simple_command (simple_command)
+ SIMPLE_COM *simple_command;
+{
+ command_print_word_list (simple_command->words, " ");
+
+ if (simple_command->redirects)
+ {
+ cprintf (" ");
+ print_redirection_list (simple_command->redirects);
+ }
+}
+
+static void
+print_redirection_list (redirects)
+ REDIRECT *redirects;
+{
+ REDIRECT *heredocs, *hdtail, *newredir;
+
+ heredocs = (REDIRECT *)NULL;
+ hdtail = heredocs;
+
+ was_heredoc = 0;
+ while (redirects)
+ {
+ /* Defer printing the here documents until we've printed the
+ rest of the redirections. */
+ if (redirects->instruction == r_reading_until || redirects->instruction == r_deblank_reading_until)
+ {
+ newredir = copy_redirect (redirects);
+ newredir->next = (REDIRECT *)NULL;
+ if (heredocs)
+ {
+ hdtail->next = newredir;
+ hdtail = newredir;
+ }
+ else
+ hdtail = heredocs = newredir;
+ }
+ else if (redirects->instruction == r_duplicating_output_word && redirects->redirector == 1)
+ {
+ /* Temporarily translate it as the execution code does. */
+ redirects->instruction = r_err_and_out;
+ print_redirection (redirects);
+ redirects->instruction = r_duplicating_output_word;
+ }
+ else
+ print_redirection (redirects);
+
+ redirects = redirects->next;
+ if (redirects)
+ cprintf (" ");
+ }
+
+ /* Now that we've printed all the other redirections (on one line),
+ print the here documents. */
+ if (heredocs)
+ {
+ cprintf (" ");
+ for (hdtail = heredocs; hdtail; hdtail = hdtail->next)
+ {
+ print_redirection (hdtail);
+ cprintf ("\n");
+ }
+ dispose_redirects (heredocs);
+ was_heredoc = 1;
+ }
+}
+
+static void
+print_redirection (redirect)
+ REDIRECT *redirect;
+{
+ int kill_leading, redirector, redir_fd;
+ WORD_DESC *redirectee;
+
+ kill_leading = 0;
+ redirectee = redirect->redirectee.filename;
+ redirector = redirect->redirector;
+ redir_fd = redirect->redirectee.dest;
+
+ switch (redirect->instruction)
+ {
+ case r_output_direction:
+ if (redirector != 1)
+ cprintf ("%d", redirector);
+ cprintf (">%s", redirectee->word);
+ break;
+
+ case r_input_direction:
+ if (redirector != 0)
+ cprintf ("%d", redirector);
+ cprintf ("<%s", redirectee->word);
+ break;
+
+ case r_inputa_direction: /* Redirection created by the shell. */
+ cprintf ("&");
+ break;
+
+ case r_appending_to:
+ if (redirector != 1)
+ cprintf ("%d", redirector);
+ cprintf (">>%s", redirectee->word);
+ break;
+
+ case r_deblank_reading_until:
+ kill_leading++;
+ /* ... */
+ case r_reading_until:
+ if (redirector != 0)
+ cprintf ("%d", redirector);
+ /* If the here document delimiter is quoted, single-quote it. */
+ if (redirect->redirectee.filename->flags & W_QUOTED)
+ {
+ char *x;
+ x = sh_single_quote (redirect->here_doc_eof);
+ cprintf ("<<%s%s\n", kill_leading? "-" : "", x);
+ free (x);
+ }
+ else
+ cprintf ("<<%s%s\n", kill_leading? "-" : "", redirect->here_doc_eof);
+ cprintf ("%s%s",
+ redirect->redirectee.filename->word, redirect->here_doc_eof);
+ break;
+
+ case r_reading_string:
+ if (redirector != 0)
+ cprintf ("%d", redirector);
+ if (ansic_shouldquote (redirect->redirectee.filename->word))
+ {
+ char *x;
+ x = ansic_quote (redirect->redirectee.filename->word, 0, (int *)0);
+ cprintf ("<<< %s", x);
+ free (x);
+ }
+ else
+ cprintf ("<<< %s", redirect->redirectee.filename->word);
+ break;
+
+ case r_duplicating_input:
+ cprintf ("%d<&%d", redirector, redir_fd);
+ break;
+
+ case r_duplicating_output:
+ cprintf ("%d>&%d", redirector, redir_fd);
+ break;
+
+ case r_duplicating_input_word:
+ cprintf ("%d<&%s", redirector, redirectee->word);
+ break;
+
+ case r_duplicating_output_word:
+ cprintf ("%d>&%s", redirector, redirectee->word);
+ break;
+
+ case r_move_input:
+ cprintf ("%d<&%d-", redirector, redir_fd);
+ break;
+
+ case r_move_output:
+ cprintf ("%d>&%d-", redirector, redir_fd);
+ break;
+
+ case r_move_input_word:
+ cprintf ("%d<&%s-", redirector, redirectee->word);
+ break;
+
+ case r_move_output_word:
+ cprintf ("%d>&%s-", redirector, redirectee->word);
+ break;
+
+ case r_close_this:
+ cprintf ("%d>&-", redirector);
+ break;
+
+ case r_err_and_out:
+ cprintf (">&%s", redirectee->word);
+ break;
+
+ case r_input_output:
+ if (redirector != 1)
+ cprintf ("%d", redirector);
+ cprintf ("<>%s", redirectee->word);
+ break;
+
+ case r_output_force:
+ if (redirector != 1)
+ cprintf ("%d", redirector);
+ cprintf (">|%s", redirectee->word);
+ break;
+ }
+}
+
+static void
+reset_locals ()
+{
+ inside_function_def = 0;
+ indentation = 0;
+}
+
+static void
+print_function_def (func)
+ FUNCTION_DEF *func;
+{
+ COMMAND *cmdcopy;
+ REDIRECT *func_redirects;
+
+ func_redirects = NULL;
+ cprintf ("function %s () \n", func->name->word);
+ add_unwind_protect (reset_locals, 0);
+
+ indent (indentation);
+ cprintf ("{ \n");
+
+ inside_function_def++;
+ indentation += indentation_amount;
+
+ cmdcopy = copy_command (func->command);
+ if (cmdcopy->type == cm_group)
+ {
+ func_redirects = cmdcopy->redirects;
+ cmdcopy->redirects = (REDIRECT *)NULL;
+ }
+ make_command_string_internal (cmdcopy->type == cm_group
+ ? cmdcopy->value.Group->command
+ : cmdcopy);
+
+ remove_unwind_protect ();
+ indentation -= indentation_amount;
+ inside_function_def--;
+
+ if (func_redirects)
+ { /* { */
+ newline ("} ");
+ print_redirection_list (func_redirects);
+ cmdcopy->redirects = func_redirects;
+ }
+ else
+ newline ("}");
+
+ dispose_command (cmdcopy);
+}
+
+/* Return the string representation of the named function.
+ NAME is the name of the function.
+ COMMAND is the function body. It should be a GROUP_COM.
+ MULTI_LINE is non-zero to pretty-print, or zero for all on one line.
+ */
+char *
+named_function_string (name, command, multi_line)
+ char *name;
+ COMMAND *command;
+ int multi_line;
+{
+ char *result;
+ int old_indent, old_amount;
+ COMMAND *cmdcopy;
+ REDIRECT *func_redirects;
+
+ old_indent = indentation;
+ old_amount = indentation_amount;
+ command_string_index = was_heredoc = 0;
+
+ if (name && *name)
+ cprintf ("%s ", name);
+
+ cprintf ("() ");
+
+ if (multi_line == 0)
+ {
+ indentation = 1;
+ indentation_amount = 0;
+ }
+ else
+ {
+ cprintf ("\n");
+ indentation += indentation_amount;
+ }
+
+ inside_function_def++;
+
+ cprintf (multi_line ? "{ \n" : "{ ");
+
+ cmdcopy = copy_command (command);
+ /* Take any redirections specified in the function definition (which should
+ apply to the function as a whole) and save them for printing later. */
+ func_redirects = (REDIRECT *)NULL;
+ if (cmdcopy->type == cm_group)
+ {
+ func_redirects = cmdcopy->redirects;
+ cmdcopy->redirects = (REDIRECT *)NULL;
+ }
+ make_command_string_internal (cmdcopy->type == cm_group
+ ? cmdcopy->value.Group->command
+ : cmdcopy);
+
+ indentation = old_indent;
+ indentation_amount = old_amount;
+ inside_function_def--;
+
+ if (func_redirects)
+ { /* { */
+ newline ("} ");
+ print_redirection_list (func_redirects);
+ cmdcopy->redirects = func_redirects;
+ }
+ else
+ newline ("}");
+
+ result = the_printed_command;
+
+ if (!multi_line)
+ {
+#if 0
+ register int i;
+ for (i = 0; result[i]; i++)
+ if (result[i] == '\n')
+ {
+ strcpy (result + i, result + i + 1);
+ --i;
+ }
+#else
+ if (result[2] == '\n') /* XXX -- experimental */
+ strcpy (result + 2, result + 3);
+#endif
+ }
+
+ dispose_command (cmdcopy);
+
+ return (result);
+}
+
+static void
+newline (string)
+ char *string;
+{
+ cprintf ("\n");
+ indent (indentation);
+ if (string && *string)
+ cprintf ("%s", string);
+}
+
+static char *indentation_string;
+static int indentation_size;
+
+static void
+indent (amount)
+ int amount;
+{
+ register int i;
+
+ RESIZE_MALLOCED_BUFFER (indentation_string, 0, amount, indentation_size, 16);
+
+ for (i = 0; amount > 0; amount--)
+ indentation_string[i++] = ' ';
+ indentation_string[i] = '\0';
+ cprintf (indentation_string);
+}
+
+static void
+semicolon ()
+{
+ if (command_string_index > 0 &&
+ (the_printed_command[command_string_index - 1] == '&' ||
+ the_printed_command[command_string_index - 1] == '\n'))
+ return;
+ cprintf (";");
+}
+
+/* How to make the string. */
+static void
+#if defined (PREFER_STDARG)
+cprintf (const char *control, ...)
+#else
+cprintf (control, va_alist)
+ const char *control;
+ va_dcl
+#endif
+{
+ register const char *s;
+ char char_arg[2], *argp, intbuf[INT_STRLEN_BOUND (int) + 1];
+ int digit_arg, arg_len, c;
+ va_list args;
+
+ SH_VA_START (args, control);
+
+ arg_len = strlen (control);
+ the_printed_command_resize (arg_len + 1);
+
+ char_arg[1] = '\0';
+ s = control;
+ while (s && *s)
+ {
+ c = *s++;
+ argp = (char *)NULL;
+ if (c != '%' || !*s)
+ {
+ char_arg[0] = c;
+ argp = char_arg;
+ arg_len = 1;
+ }
+ else
+ {
+ c = *s++;
+ switch (c)
+ {
+ case '%':
+ char_arg[0] = c;
+ argp = char_arg;
+ arg_len = 1;
+ break;
+
+ case 's':
+ argp = va_arg (args, char *);
+ arg_len = strlen (argp);
+ break;
+
+ case 'd':
+ /* Represent an out-of-range file descriptor with an out-of-range
+ integer value. We can do this because the only use of `%d' in
+ the calls to cprintf is to output a file descriptor number for
+ a redirection. */
+ digit_arg = va_arg (args, int);
+ if (digit_arg < 0)
+ {
+ sprintf (intbuf, "%u", (unsigned)-1);
+ argp = intbuf;
+ }
+ else
+ argp = inttostr (digit_arg, intbuf, sizeof (intbuf));
+ arg_len = strlen (argp);
+ break;
+
+ case 'c':
+ char_arg[0] = va_arg (args, int);
+ argp = char_arg;
+ arg_len = 1;
+ break;
+
+ default:
+ programming_error (_("cprintf: `%c': invalid format character"), c);
+ /*NOTREACHED*/
+ }
+ }
+
+ if (argp && arg_len)
+ {
+ the_printed_command_resize (arg_len + 1);
+ FASTCOPY (argp, the_printed_command + command_string_index, arg_len);
+ command_string_index += arg_len;
+ }
+ }
+
+ the_printed_command[command_string_index] = '\0';
+}
+
+/* Ensure that there is enough space to stuff LENGTH characters into
+ THE_PRINTED_COMMAND. */
+static void
+the_printed_command_resize (length)
+ int length;
+{
+ if (the_printed_command == 0)
+ {
+ the_printed_command_size = (length + PRINTED_COMMAND_INITIAL_SIZE - 1) & ~(PRINTED_COMMAND_INITIAL_SIZE - 1);
+ the_printed_command = (char *)xmalloc (the_printed_command_size);
+ command_string_index = 0;
+ }
+ else if ((command_string_index + length) >= the_printed_command_size)
+ {
+ int new;
+ new = command_string_index + length + 1;
+
+ /* Round up to the next multiple of PRINTED_COMMAND_GROW_SIZE. */
+ new = (new + PRINTED_COMMAND_GROW_SIZE - 1) & ~(PRINTED_COMMAND_GROW_SIZE - 1);
+ the_printed_command_size = new;
+
+ the_printed_command = (char *)xrealloc (the_printed_command, the_printed_command_size);
+ }
+}
+
+#if defined (HAVE_VPRINTF)
+/* ``If vprintf is available, you may assume that vfprintf and vsprintf are
+ also available.'' */
+
+static void
+#if defined (PREFER_STDARG)
+xprintf (const char *format, ...)
+#else
+xprintf (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+ va_list args;
+
+ SH_VA_START (args, format);
+
+ vfprintf (stdout, format, args);
+ va_end (args);
+}
+
+#else
+
+static void
+xprintf (format, arg1, arg2, arg3, arg4, arg5)
+ const char *format;
+{
+ printf (format, arg1, arg2, arg3, arg4, arg5);
+}
+
+#endif /* !HAVE_VPRINTF */
-/* subst.c -- The part of the shell that does parameter, command, and
- globbing substitutions. */
+/* subst.c -- The part of the shell that does parameter, command, arithmetic,
+ and globbing substitutions. */
/* ``Have a little faith, there's magic in the night. You ain't a
beauty, but, hey, you're alright.'' */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
cases: a quoted null character as above and when
CTLNUL is contained in the (non-null) expansion
of some variable. We use the had_quoted_null flag to
- pass the value through this function to its return value. */
+ pass the value through this function to its caller. */
if ((tflag & W_HASQUOTEDNULL) && QUOTED_NULL (temp) == 0)
remove_quoted_nulls (temp); /* XXX */
}
sunos5*-*gcc*|solaris2*-*gcc*)
SHOBJ_CFLAGS=-fpic
SHOBJ_LD='${CC}'
- # This line works for the Solaris linker in /usr/ccs/bin/ld
- SHOBJ_LDFLAGS='-shared -Wl,-i -Wl,-h,$@'
- # This line works for the GNU ld
-# SHOBJ_LDFLAGS='-shared -Wl,-h,$@'
+ ld_used=`gcc -print-prog-name=ld`
+ if ${ld_used} -V 2>&1 | grep GNU >/dev/null 2>&1; then
+ # This line works for the GNU ld
+ SHOBJ_LDFLAGS='-shared -Wl,-h,$@'
+ else
+ # This line works for the Solaris linker in /usr/ccs/bin/ld
+ SHOBJ_LDFLAGS='-shared -Wl,-i -Wl,-h,$@'
+ fi
# SHLIB_XLDFLAGS='-R $(libdir)'
SHLIB_LIBVERSION='$(SHLIB_LIBSUFF).$(SHLIB_MAJOR)'
-BUILD_DIR=/usr/local/build/bash/bash-current
+BUILD_DIR=/usr/local/build/chet/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
/* trap.c -- Not the trap command, but useful functions for manipulating
those objects. The trap command is in builtins/trap.def. */
-/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
/* Take care of the exit trap first */
if (sigmodes[EXIT_TRAP] & SIG_TRAPPED)
{
- free_trap_command (EXIT_TRAP);
- trap_list[EXIT_TRAP] = (char *)NULL;
sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
+ if (reset != reset_signal)
+ {
+ free_trap_command (EXIT_TRAP);
+ trap_list[EXIT_TRAP] = (char *)NULL;
+ }
}
for (i = 1; i < NSIG; i++)
/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
set_pwd ();
/* Set up initial value of $_ */
+#if 0
temp_var = bind_variable ("_", dollar_vars[0], 0);
+#else
+ temp_var = set_if_not ("_", dollar_vars[0]);
+#endif
/* Remember this pid. */
dollar_dollar_pid = getpid ();
/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
set_pwd ();
/* Set up initial value of $_ */
+#if 1
temp_var = bind_variable ("_", dollar_vars[0], 0);
+#else
+ temp_var = set_if_not ("_", dollar_vars[0]);
+#endif
/* Remember this pid. */
dollar_dollar_pid = getpid ();
/* Reset for command and process substitution. */
if (subshell_environment && seeded_subshell == 0)
{
-itrace("seeding random number gen in subshell");
sbrand (rseed + getpid() + NOW);
seeded_subshell = 1;
}
/* variables.h -- data structures for shell variables. */
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
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 *));