mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-08-07 02:32:27 +02:00
commit bash-20161028 snapshot
This commit is contained in:
+127
@@ -12013,3 +12013,130 @@ variables.c
|
||||
doc/{bash.1,bashref.texi}
|
||||
- BASHPID: note that assignments are ignored and unsetting BASHPID
|
||||
causes it to lose its special properties
|
||||
|
||||
10/28
|
||||
-----
|
||||
builtins/pushd.def
|
||||
- popd_builtin: make sure to check the normalized stack offset
|
||||
(i.e., negatives counting back from the end of the stack) is within
|
||||
bounds before trying to free that stack entry. Report from
|
||||
Fernando Muñoz <fernando@null-life.com>
|
||||
|
||||
lib/readline/histfile.c
|
||||
- chown: protect calls with HAVE_CHOWN. Fixes for MinGW{64,32} from
|
||||
J. Peter Mugaas <jpmugaas@suddenlink.net>
|
||||
|
||||
lib/readline/colors.c
|
||||
- S_ISDIR: add define if missing
|
||||
- S_ISUID, S_ISGID, S_ISLNK, S_ISSOCK: don't use unless defined.
|
||||
Fixes for MinGW{64,32} from J. Peter Mugaas <jpmugaas@suddenlink.net>
|
||||
|
||||
bashhist.c
|
||||
- current_command_line_comment: set to the most recent line in a
|
||||
possibly multi-line command that contains a shell comment; set in
|
||||
maybe_add_history and bash_add_history to either the current
|
||||
command number or -2
|
||||
- maybe_add_history: set current_command_line_comment for first line
|
||||
of command
|
||||
- bash_add_history: chars_to_add set to "\n" if the current command line
|
||||
is one greater than the previous line containing a comment
|
||||
(current_command_line_comment). Fixes complaint from back in
|
||||
January, 2016 from Dave Rutherford <dave@evilpettingzoo.com>
|
||||
|
||||
array.h
|
||||
- lastref: move last-referenced pointer into each array struct, so all
|
||||
arrays can have reference locality
|
||||
|
||||
array.c
|
||||
- array_create: set lastref element to 0
|
||||
- array_copy: if copying the lastref in array a, set the new lastref
|
||||
in the copy to the same element
|
||||
- IS_LASTREF, LASTREF_START, LASTREF, INVALIDATE_LASTREF, SET_LASTREF,
|
||||
UNSET_LASTREF: change to use new array `lastref' member
|
||||
|
||||
pathexp.c
|
||||
- quote_string_for_globbing: if quoting for a regexp, make sure to
|
||||
skip and copy a leading `^' in a bracket expression, and skip and
|
||||
copy a subsequent `]' (after an optional `^'), so that leading
|
||||
bracket doesn't close the bracket expression. Report from
|
||||
Stephane Chazelas <stephane.chazelas@gmail.com>
|
||||
|
||||
10/29
|
||||
-----
|
||||
subst.c
|
||||
- extract_delimited_string: add calls to CHECK_STRING_OVERRUN for
|
||||
$( inside $((, nested OPENERs, nested ALT_OPENERs, and backquotes
|
||||
- skip_to_delim: add calls to CHECK_STRING_OVERRUN for $(, ${,
|
||||
process substitution
|
||||
- extract_dollar_brace_string: add calls to CHECK_STRING_OVERRUN for
|
||||
$(
|
||||
- extract_dollar_brace_string: use skipsubscript to skip over the
|
||||
array subscript in ${var[sub]} (non-quoted case uses string_extract,
|
||||
which already uses skipsubscript). Tagged for bash-5.0
|
||||
|
||||
10/30
|
||||
-----
|
||||
variables.c
|
||||
- EPOCHSECONDS: new dynamic variable, time in seconds since Unix
|
||||
epoch; assignments are ignored. Can be used on systems where
|
||||
strftime() doesn't support '%s'
|
||||
- EPOCHREALTIME: new dynamic variable, time in seconds since Unix
|
||||
epoch with microsecond granularity
|
||||
|
||||
doc/{bash.1,bashref.texi}
|
||||
- EPOCHSECONDS documentation
|
||||
- EPOCHREALTIME documentation
|
||||
|
||||
execute_cmd.c
|
||||
- decpoint: moved to locale.c, renamed locale_decpoint; changed callers
|
||||
|
||||
{bashintl,externs}.h
|
||||
- locale_decpoint: extern declaration or #define if support for
|
||||
localeconv() not there
|
||||
|
||||
10/31
|
||||
-----
|
||||
lib/malloc/malloc.c
|
||||
- posix_memalign: add new posix-mandated interface
|
||||
|
||||
examples/loadables/rm.c
|
||||
- rm: minimal loadable builtin, removes files and directories, only
|
||||
handles -r and -f options. Original from Tim Ruehsen
|
||||
<tim.ruehsen@gmx.de>, heavily rewritten for inclusion as loadable
|
||||
|
||||
examples/loadables/Makefile.in
|
||||
- rm: add rules to build rm as one of the standard targets
|
||||
|
||||
examples/loadables/stat.c
|
||||
- stat: new loadable builtin that takes a filename and loads the info
|
||||
returned by stat(2) into an associative array specified by the -A
|
||||
argument (default STAT)
|
||||
|
||||
11/1
|
||||
----
|
||||
variables.c
|
||||
- BASH_ARGV0: new dynamic variable, returns $0 on reference and sets
|
||||
$0 on assignment. From a suggestion from Rocky Bernstein <rocky@gnu.org>
|
||||
a few years ago
|
||||
|
||||
doc/{bash.1,bashref.texi}
|
||||
- BASH_ARGV0: document
|
||||
|
||||
11/2
|
||||
----
|
||||
lib/glob/sm_loop.c
|
||||
- parse_collsym: make sure to not return an out-of-bounds read if a
|
||||
collating symbol is unterminated. Fixes OOB read reported by
|
||||
op7ic \x00 <op7ica@gmail.com>
|
||||
- brackmatch: after incrementing p, before checking whether it's a
|
||||
character range, check whether *p was NULL before the increment
|
||||
and short-circuit the bracket expression if it is
|
||||
|
||||
jobs.c
|
||||
- discard_last_procsub_child: new function, safely discards
|
||||
last_procsub_child and sets it to NULL
|
||||
|
||||
subst.c
|
||||
- process_substitute: call discard_last_procsub_child instead of
|
||||
calling discard_pipeline directly. Fixes bug reported by
|
||||
Christian Weisgerber <naddy@mips.inka.de>
|
||||
|
||||
@@ -681,6 +681,7 @@ examples/loadables/dirname.c f
|
||||
examples/loadables/tty.c f
|
||||
examples/loadables/pathchk.c f
|
||||
examples/loadables/tee.c f
|
||||
examples/loadables/rm.c f
|
||||
examples/loadables/rmdir.c f
|
||||
examples/loadables/head.c f
|
||||
examples/loadables/printenv.c f
|
||||
@@ -693,6 +694,7 @@ examples/loadables/mkdir.c f
|
||||
examples/loadables/ln.c f
|
||||
examples/loadables/mypid.c f
|
||||
examples/loadables/unlink.c f
|
||||
examples/loadables/stat.c f
|
||||
examples/loadables/perl/Makefile.in f
|
||||
examples/loadables/perl/README f
|
||||
examples/loadables/perl/bperl.c f
|
||||
|
||||
@@ -63,39 +63,17 @@
|
||||
|
||||
static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));
|
||||
|
||||
/* lastref should be moved into the array structure so each array can be
|
||||
optimized separately */
|
||||
|
||||
static ARRAY *lastarray = 0;
|
||||
static ARRAY_ELEMENT *lastref = 0;
|
||||
|
||||
#define IS_LASTREF(a) (lastarray && (a) == lastarray)
|
||||
#define IS_LASTREF(a) (a->lastref)
|
||||
|
||||
#define LASTREF_START(a, i) \
|
||||
(IS_LASTREF(a) && i >= element_index(lastref)) ? lastref \
|
||||
: element_forw(a->head)
|
||||
(IS_LASTREF(a) && i >= element_index(a->lastref)) ? a->lastref \
|
||||
: element_forw(a->head)
|
||||
|
||||
#define LASTREF(a) IS_LASTREF(a) ? lastref : element_forw(a->head)
|
||||
#define LASTREF(a) (a->lastref ? a->lastref : element_forw(a->head))
|
||||
|
||||
#define INVALIDATE_LASTREF(a) \
|
||||
do { \
|
||||
if ((a) == lastarray) { \
|
||||
lastarray = 0; \
|
||||
lastref = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SET_LASTREF(a, e) \
|
||||
do { \
|
||||
lastarray = (a); \
|
||||
lastref = (e); \
|
||||
} while (0)
|
||||
|
||||
#define UNSET_LASTREF(a) \
|
||||
do { \
|
||||
lastarray = 0; \
|
||||
lastref = 0; \
|
||||
} while (0)
|
||||
#define INVALIDATE_LASTREF(a) a->lastref = 0
|
||||
#define SET_LASTREF(a, e) a->lastref = (e)
|
||||
#define UNSET_LASTREF(a) a->lastref = 0;
|
||||
|
||||
ARRAY *
|
||||
array_create()
|
||||
@@ -107,6 +85,7 @@ array_create()
|
||||
r->type = array_indexed;
|
||||
r->max_index = -1;
|
||||
r->num_elements = 0;
|
||||
r->lastref = (ARRAY_ELEMENT *)0;
|
||||
head = array_create_element(-1, (char *)NULL); /* dummy head */
|
||||
head->prev = head->next = head;
|
||||
r->head = head;
|
||||
@@ -159,6 +138,8 @@ ARRAY *a;
|
||||
for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
|
||||
new = array_create_element(element_index(ae), element_value(ae));
|
||||
ADD_BEFORE(a1->head, new);
|
||||
if (ae == LASTREF(a))
|
||||
SET_LASTREF(a1, new);
|
||||
}
|
||||
return(a1);
|
||||
}
|
||||
@@ -722,7 +703,7 @@ arrayind_t i;
|
||||
if (a == 0 || array_empty(a))
|
||||
return((ARRAY_ELEMENT *) NULL);
|
||||
if (i > array_max_index(a) || i < array_first_index(a))
|
||||
return((char *)NULL); /* Keep roving pointer into array to optimize sequential access */
|
||||
return((ARRAY_ELEMENT *)NULL); /* Keep roving pointer into array to optimize sequential access */
|
||||
start = LASTREF(a);
|
||||
/* Use same strategy as array_reference to avoid paying large penalty
|
||||
for semi-random assignment pattern. */
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef struct array {
|
||||
enum atype type;
|
||||
arrayind_t max_index;
|
||||
int num_elements;
|
||||
struct array_element *lastref;
|
||||
struct array_element *head;
|
||||
} ARRAY;
|
||||
|
||||
|
||||
+4
-2
@@ -1086,15 +1086,17 @@ array_value_internal (s, quoted, flags, rtype, indp)
|
||||
return ((char *) NULL);
|
||||
}
|
||||
|
||||
/* Caller of array_value takes care of inspecting rtype and duplicating
|
||||
retval if rtype == 0, so this is not a memory leak */
|
||||
if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
||||
{
|
||||
temp = string_list_dollar_star (l);
|
||||
retval = quote_string (temp); /* XXX - leak here */
|
||||
retval = quote_string (temp);
|
||||
free (temp);
|
||||
}
|
||||
else /* ${name[@]} or unquoted ${name[*]} */
|
||||
/* XXX - bash-4.4/bash-5.0 test AV_ASSIGNRHS and pass PF_ASSIGNRHS */
|
||||
retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0); /* XXX - leak here */
|
||||
retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);
|
||||
|
||||
dispose_words (l);
|
||||
}
|
||||
|
||||
+24
-4
@@ -140,6 +140,11 @@ int command_oriented_history = 1;
|
||||
the history-manipluating builtins can see it. */
|
||||
int current_command_first_line_saved = 0;
|
||||
|
||||
/* Set to the number of the most recent line of a possibly-multi-line command
|
||||
that contains a shell comment. Used by bash_add_history() to determine
|
||||
whether to add a newline or a semicolon. */
|
||||
int current_command_line_comment = 0;
|
||||
|
||||
/* Non-zero means to store newlines in the history list when using
|
||||
command_oriented_history rather than trying to use semicolons. */
|
||||
int literal_history;
|
||||
@@ -597,16 +602,24 @@ pre_process_line (line, print_changes, addit)
|
||||
}
|
||||
|
||||
/* Return 1 if the first non-whitespace character in LINE is a `#', indicating
|
||||
* that the line is a shell comment. */
|
||||
that the line is a shell comment. Return 2 if there is a comment after the
|
||||
first non-whitespace character. Return 0 if the line does not contain a
|
||||
comment. */
|
||||
static int
|
||||
shell_comment (line)
|
||||
char *line;
|
||||
{
|
||||
char *p;
|
||||
int n;
|
||||
|
||||
if (line == 0)
|
||||
return 0;
|
||||
for (p = line; p && *p && whitespace (*p); p++)
|
||||
;
|
||||
return (p && *p == '#');
|
||||
if (p && *p == '#')
|
||||
return 1;
|
||||
n = skip_to_delim (line, p - line, "#", SD_NOJMP|SD_GLOB|SD_EXTGLOB|SD_COMPLETE);
|
||||
return (line[n] == '#') ? 2 : 0;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
@@ -708,13 +721,14 @@ maybe_add_history (line)
|
||||
if (current_command_line_count > 1)
|
||||
{
|
||||
if (current_command_first_line_saved &&
|
||||
((parser_state & PST_HEREDOC) || literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0))
|
||||
((parser_state & PST_HEREDOC) || literal_history || dstack.delimiter_depth != 0 || shell_comment (line) != 1))
|
||||
bash_add_history (line);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is the first line of a (possible multi-line) command. Note whether
|
||||
or not we should save the first line and remember it. */
|
||||
current_command_line_comment = shell_comment (line) ? current_command_line_count : -2;
|
||||
current_command_first_line_saved = check_add_history (line, 0);
|
||||
}
|
||||
|
||||
@@ -803,12 +817,18 @@ bash_add_history (line)
|
||||
so we have to duplicate some of what that function does here. */
|
||||
if ((parser_state & PST_HEREDOC) && literal_history && current_command_line_count > 2 && line[strlen (line) - 1] == '\n')
|
||||
chars_to_add = "";
|
||||
else if (current_command_line_count == current_command_line_comment+1)
|
||||
chars_to_add = "\n";
|
||||
else if (literal_history)
|
||||
chars_to_add = "\n";
|
||||
else
|
||||
chars_to_add = literal_history ? "\n" : history_delimiting_chars (line);
|
||||
chars_to_add = history_delimiting_chars (line);
|
||||
|
||||
using_history ();
|
||||
current = previous_history ();
|
||||
|
||||
current_command_line_comment = shell_comment (line) ? current_command_line_count : -2;
|
||||
|
||||
if (current)
|
||||
{
|
||||
/* If the previous line ended with an escaped newline (escaped
|
||||
|
||||
@@ -47,4 +47,8 @@
|
||||
# define setlocale(cat, loc)
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_LOCALE_H) || !defined (HAVE_LOCALECONV)
|
||||
# define locale_decpoint() '.'
|
||||
#endif
|
||||
|
||||
#endif /* !_BASHINTL_H_ */
|
||||
|
||||
@@ -139,19 +139,13 @@ optimize_subshell_command (command)
|
||||
((command->flags & CMD_TIME_PIPELINE) == 0) &&
|
||||
((command->flags & CMD_INVERT_RETURN) == 0))
|
||||
{
|
||||
itrace("optimize_subshell_command: optimizing simple command");
|
||||
command->flags |= CMD_NO_FORK;
|
||||
command->value.Simple->flags |= CMD_NO_FORK;
|
||||
}
|
||||
else if (command->type == cm_connection &&
|
||||
(command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR))
|
||||
{
|
||||
itrace("optimize_subshell_command: attempting to optimize connection");
|
||||
optimize_subshell_command (command->value.Connection->second);
|
||||
}
|
||||
else
|
||||
itrace("optimize_subshell_command: command type = %d", command->type);
|
||||
}
|
||||
|
||||
/* How to force parse_and_execute () to clean up after itself. */
|
||||
void
|
||||
|
||||
+6
-1
@@ -365,7 +365,7 @@ popd_builtin (list)
|
||||
break;
|
||||
}
|
||||
|
||||
if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
|
||||
if (which > directory_list_offset || (which < -directory_list_offset) || (directory_list_offset == 0 && which == 0))
|
||||
{
|
||||
pushd_error (directory_list_offset, which_word ? which_word : "");
|
||||
return (EXECUTION_FAILURE);
|
||||
@@ -387,6 +387,11 @@ popd_builtin (list)
|
||||
remove that directory from the list and shift the remainder
|
||||
of the list into place. */
|
||||
i = (direction == '+') ? directory_list_offset - which : which;
|
||||
if (i < 0 || i > directory_list_offset)
|
||||
{
|
||||
pushd_error (directory_list_offset, which_word ? which_word : "");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
free (pushd_directory_list[i]);
|
||||
directory_list_offset--;
|
||||
|
||||
|
||||
+45
-2
@@ -5,12 +5,12 @@
|
||||
.\" Case Western Reserve University
|
||||
.\" chet.ramey@case.edu
|
||||
.\"
|
||||
.\" Last Change: Tue Sep 27 14:02:28 EDT 2016
|
||||
.\" Last Change: Tue Nov 1 16:05:17 EDT 2016
|
||||
.\"
|
||||
.\" bash_builtins, strip all but Built-Ins section
|
||||
.if \n(zZ=1 .ig zZ
|
||||
.if \n(zY=1 .ig zY
|
||||
.TH BASH 1 "2016 September 27" "GNU Bash 4.4"
|
||||
.TH BASH 1 "2016 November 1" "GNU Bash 4.4"
|
||||
.\"
|
||||
.\" There's some problem with having a `@'
|
||||
.\" in a tagged paragraph with the BSD man macros.
|
||||
@@ -1509,6 +1509,19 @@ option to the
|
||||
.B shopt
|
||||
builtin below)
|
||||
.TP
|
||||
.B BASH_ARGV0
|
||||
When referenced, this variable expands to the name of the shell or shell
|
||||
script (identical to
|
||||
.BR $0 ;
|
||||
see the description of special parameter 0 above).
|
||||
Assignment to
|
||||
.B BASH_ARGV0
|
||||
causes the value assigned to also be assigned to \fB$0\fP.
|
||||
If
|
||||
.B BASH_ARGV0
|
||||
is unset, it loses its special properties, even if it is
|
||||
subsequently reset.
|
||||
.TP
|
||||
.B BASH_CMDS
|
||||
An associative array variable whose members correspond to the internal
|
||||
hash table of commands as maintained by the \fBhash\fP builtin.
|
||||
@@ -1695,6 +1708,33 @@ If
|
||||
is unset, it loses its special properties, even if it is
|
||||
subsequently reset.
|
||||
.TP
|
||||
.B EPOCHREALTIME
|
||||
Each time this parameter is referenced, it expands to the number of seconds
|
||||
since the Unix Epoch (see \fItime\fP\fR(3)\fP) as a floating point value
|
||||
with micro-second granularity.
|
||||
Assignments to
|
||||
.SM
|
||||
.B EPOCHREALTIME
|
||||
are ignored.
|
||||
If
|
||||
.SM
|
||||
.B EPOCHREALTIME
|
||||
is unset, it loses its special properties, even if it is
|
||||
subsequently reset.
|
||||
.TP
|
||||
.B EPOCHSECONDS
|
||||
Each time this parameter is referenced, it expands to the number of seconds
|
||||
since the Unix Epoch (see \fItime\fP\fR(3)\fP).
|
||||
Assignments to
|
||||
.SM
|
||||
.B EPOCHSECONDS
|
||||
are ignored.
|
||||
If
|
||||
.SM
|
||||
.B EPOCHSECONDS
|
||||
is unset, it loses its special properties, even if it is
|
||||
subsequently reset.
|
||||
.TP
|
||||
.B EUID
|
||||
Expands to the effective user ID of the current user, initialized at
|
||||
shell startup. This variable is readonly.
|
||||
@@ -3704,6 +3744,9 @@ than or equal to 10 and assign it to \fIvarname\fP.
|
||||
If >&- or <&- is preceded
|
||||
by {\fIvarname\fP}, the value of \fIvarname\fP defines the file
|
||||
descriptor to close.
|
||||
If {\fIvarname\fP} is supplied, the redirection persists beyond
|
||||
the scope of the command, allowing the shell programmer to manage
|
||||
the file descriptor himself.
|
||||
.PP
|
||||
In the following descriptions, if the file descriptor number is
|
||||
omitted, and the first character of the redirection operator is
|
||||
|
||||
@@ -2567,6 +2567,9 @@ In this case, for each redirection operator except
|
||||
than 10 and assign it to @{@var{varname}@}. If >&- or <&- is preceded
|
||||
by @{@var{varname}@}, the value of @var{varname} defines the file
|
||||
descriptor to close.
|
||||
If @{@var{varname}@} is supplied, the redirection persists beyond
|
||||
the scope of the command, allowing the shell programmer to manage
|
||||
the file descriptor himself.
|
||||
|
||||
In the following descriptions, if the file descriptor number is
|
||||
omitted, and the first character of the redirection operator is
|
||||
@@ -5519,6 +5522,16 @@ The shell sets @code{BASH_ARGV} only when in extended debugging mode
|
||||
for a description of the @code{extdebug} option to the @code{shopt}
|
||||
builtin).
|
||||
|
||||
@item BASH_ARGV0
|
||||
When referenced, this variable expands to the name of the shell or shell
|
||||
script (identical to @code{$0}; @xref{Special Parameters},
|
||||
for the description of special parameter 0).
|
||||
Assignment to @code{BASH_ARGV0}
|
||||
causes the value assigned to also be assigned to @code{$0}.
|
||||
If @code{BASH_ARGV0}
|
||||
is unset, it loses its special properties, even if it is
|
||||
subsequently reset.
|
||||
|
||||
@item BASH_CMDS
|
||||
An associative array variable whose members correspond to the internal
|
||||
hash table of commands as maintained by the @code{hash} builtin
|
||||
@@ -5734,6 +5747,23 @@ Emacs shell buffer and disables line editing.
|
||||
Similar to @code{BASH_ENV}; used when the shell is invoked in
|
||||
@sc{posix} Mode (@pxref{Bash POSIX Mode}).
|
||||
|
||||
@item EPOCHREALTIME
|
||||
Each time this parameter is referenced, it expands to the number of seconds
|
||||
since the Unix Epoch as a floating point value with micro-second granularity
|
||||
(see the documentation for the C library function @var{time} for the
|
||||
definition of Epoch).
|
||||
Assignments to @env{EPOCHREALTIME} are ignored.
|
||||
If @env{EPOCHREALTIME} is unset, it loses its special properties, even if
|
||||
it is subsequently reset.
|
||||
|
||||
@item EPOCHSECONDS
|
||||
Each time this parameter is referenced, it expands to the number of seconds
|
||||
since the Unix Epoch (see the documentation for the C library function
|
||||
@var{time} for the definition of Epoch).
|
||||
Assignments to @env{EPOCHSECONDS} are ignored.
|
||||
If @env{EPOCHSECONDS} is unset, it loses its special properties, even if
|
||||
it is subsequently reset.
|
||||
|
||||
@item EUID
|
||||
The numeric effective user id of the current user. This variable
|
||||
is readonly.
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@
|
||||
Copyright (C) 1988-2016 Free Software Foundation, Inc.
|
||||
@end ignore
|
||||
|
||||
@set LASTCHANGE Tue Sep 27 14:02:09 EDT 2016
|
||||
@set LASTCHANGE Tue Nov 1 16:08:43 EDT 2016
|
||||
|
||||
@set EDITION 4.4
|
||||
@set VERSION 4.4
|
||||
|
||||
@set UPDATED 27 September 2016
|
||||
@set UPDATED-MONTH September 2016
|
||||
@set UPDATED 1 November 2016
|
||||
@set UPDATED-MONTH November 2016
|
||||
|
||||
@@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
|
||||
ALLPROG = print truefalse sleep finfo logname basename dirname \
|
||||
tty pathchk tee head mkdir rmdir printenv id whoami \
|
||||
uname sync push ln unlink realpath strftime mypid setpgid
|
||||
OTHERPROG = necho hello cat pushd
|
||||
OTHERPROG = necho hello cat pushd stat rm
|
||||
|
||||
all: $(SHOBJ_STATUS)
|
||||
|
||||
@@ -142,6 +142,9 @@ finfo: finfo.o
|
||||
cat: cat.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS)
|
||||
|
||||
rm: rm.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rm.o $(SHOBJ_LIBS)
|
||||
|
||||
logname: logname.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS)
|
||||
|
||||
@@ -202,10 +205,12 @@ strftime: strftime.o
|
||||
mypid: mypid.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mypid.o $(SHOBJ_LIBS)
|
||||
|
||||
|
||||
setpgid: setpgid.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ setpgid.o $(SHOBJ_LIBS)
|
||||
|
||||
stat: stat.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS)
|
||||
|
||||
# pushd is a special case. We use the same source that the builtin version
|
||||
# uses, with special compilation options.
|
||||
#
|
||||
@@ -283,3 +288,4 @@ mkdir.o: mkdir.c
|
||||
realpath.o: realpath.c
|
||||
strftime.o: strftime.c
|
||||
setpgid.o: setpgid.c
|
||||
stat.o: stat.c
|
||||
|
||||
@@ -52,6 +52,7 @@ printenv.c Minimal builtin clone of BSD printenv(1).
|
||||
push.c Anyone remember TOPS-20?
|
||||
README README
|
||||
realpath.c Canonicalize pathnames, resolving symlinks.
|
||||
rm.c Remove files and directories.
|
||||
rmdir.c Remove directory.
|
||||
sleep.c sleep for fractions of a second.
|
||||
strftime.c Loadable builtin interface to strftime(3).
|
||||
|
||||
@@ -56,6 +56,7 @@ int fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cat_main (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
@@ -88,6 +89,7 @@ char **argv;
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
cat_builtin(list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/* rm - remove files and directories with -r */
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
static int rm_file(const char *fname);
|
||||
|
||||
static int force, recursive;
|
||||
|
||||
static int
|
||||
_remove_directory(const char *dirname)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *dp;
|
||||
size_t dirlen;
|
||||
int err;
|
||||
|
||||
dirlen = strlen (dirname);
|
||||
err = 0;
|
||||
|
||||
if ((dir = opendir(dirname)))
|
||||
{
|
||||
while ((dp = readdir(dir)))
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
char fname[dirlen + 1 + strlen (dp->d_name) + 1];
|
||||
#else
|
||||
char *fname;
|
||||
int fnsize;
|
||||
#endif
|
||||
|
||||
if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
|
||||
continue;
|
||||
|
||||
#ifdef __GNUC__
|
||||
snprintf(fname, sizeof (fname), "%s/%s", dirname, dp->d_name);
|
||||
#else
|
||||
fnsize = dirlen + 1 + strlen (dp->d_name) + 1;
|
||||
fname = xmalloc (fnsize);
|
||||
snprintf(fname, fnsize, "%s/%s", dirname, dp->d_name);
|
||||
#endif
|
||||
|
||||
if (rm_file (fname) && force == 0)
|
||||
err = 1;
|
||||
#ifndef __GNUC__
|
||||
free (fname);
|
||||
#endif
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if (err == 0 && rmdir (dirname) && force == 0)
|
||||
err = 1;
|
||||
}
|
||||
else if (force == 0)
|
||||
err = 1;
|
||||
|
||||
if (err)
|
||||
builtin_error ("%s: %s", dirname, strerror (errno));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
rm_file(const char *fname)
|
||||
{
|
||||
if (unlink (fname) == 0)
|
||||
return 0;
|
||||
|
||||
/* If FNAME is a directory glibc returns EISDIR but correct POSIX value
|
||||
would be EPERM. If we get that error and FNAME is a directory and -r
|
||||
was supplied, recursively remove the directory and its contents */
|
||||
if ((errno == EISDIR || errno == EPERM) && recursive && file_isdir (fname))
|
||||
return _remove_directory(fname);
|
||||
else if (force)
|
||||
return 0;
|
||||
|
||||
builtin_error ("%s: %s", fname, strerror (errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
rm_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
const char *name;
|
||||
WORD_LIST *l;
|
||||
int rval, opt;
|
||||
|
||||
recursive = force = 0;
|
||||
rval = EXECUTION_SUCCESS;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "Rrf")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'R':
|
||||
case 'r':
|
||||
recursive = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
for (l = list; l; l = l->next)
|
||||
{
|
||||
if (rm_file(l->word->word) && force == 0)
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
char *rm_doc[] = {
|
||||
"Remove files.",
|
||||
"",
|
||||
"rm removes the files specified as arguments.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
/* The standard structure describing a builtin command. bash keeps an array
|
||||
of these structures. */
|
||||
struct builtin rm_struct = {
|
||||
"rm", /* builtin name */
|
||||
rm_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
rm_doc, /* array of long documentation strings. */
|
||||
"rm [-rf] file ...", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -0,0 +1,430 @@
|
||||
/* stat - load up an associative array with stat information about a file */
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "posixstat.h"
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <errno.h>
|
||||
#include "posixtime.h"
|
||||
|
||||
#include "bashansi.h"
|
||||
#include "shell.h"
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#define ST_NAME 0
|
||||
#define ST_DEV 1
|
||||
#define ST_INO 2
|
||||
#define ST_MODE 3
|
||||
#define ST_NLINK 4
|
||||
#define ST_UID 5
|
||||
#define ST_GID 6
|
||||
#define ST_RDEV 7
|
||||
#define ST_SIZE 8
|
||||
#define ST_ATIME 9
|
||||
#define ST_MTIME 10
|
||||
#define ST_CTIME 11
|
||||
#define ST_BLKSIZE 12
|
||||
#define ST_BLOCKS 13
|
||||
#define ST_CHASELINK 14
|
||||
#define ST_PERMS 15
|
||||
|
||||
#define ST_END 16
|
||||
|
||||
static char *arraysubs[] =
|
||||
{
|
||||
"name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
|
||||
"size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
|
||||
0
|
||||
};
|
||||
|
||||
static int
|
||||
getstat (fname, flags, sp)
|
||||
const char *fname;
|
||||
int flags;
|
||||
struct stat *sp;
|
||||
{
|
||||
intmax_t lfd;
|
||||
int fd, r;
|
||||
|
||||
if (strncmp (fname, "/dev/fd/", 8) == 0)
|
||||
{
|
||||
if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
fd = lfd;
|
||||
r = fstat(fd, sp);
|
||||
}
|
||||
#ifdef HAVE_LSTAT
|
||||
else if (flags & 1)
|
||||
r = lstat(fname, sp);
|
||||
#endif
|
||||
else
|
||||
r = stat(fname, sp);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static char *
|
||||
statlink (fname, sp)
|
||||
char *fname;
|
||||
struct stat *sp;
|
||||
{
|
||||
#if defined (HAVE_READLINK)
|
||||
char linkbuf[PATH_MAX];
|
||||
int n;
|
||||
|
||||
if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
|
||||
{
|
||||
linkbuf[n] = '\0';
|
||||
return (savestring (linkbuf));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return (savestring (fname));
|
||||
}
|
||||
|
||||
static char *
|
||||
octalperms (m)
|
||||
int m;
|
||||
{
|
||||
int operms;
|
||||
char *ret;
|
||||
|
||||
operms = 0;
|
||||
|
||||
if (m & S_IRUSR)
|
||||
operms |= 0400;
|
||||
if (m & S_IWUSR)
|
||||
operms |= 0200;
|
||||
if (m & S_IXUSR)
|
||||
operms |= 0100;
|
||||
|
||||
if (m & S_IRGRP)
|
||||
operms |= 0040;
|
||||
if (m & S_IWGRP)
|
||||
operms |= 0020;
|
||||
if (m & S_IXGRP)
|
||||
operms |= 0010;
|
||||
|
||||
if (m & S_IROTH)
|
||||
operms |= 0004;
|
||||
if (m & S_IWOTH)
|
||||
operms |= 0002;
|
||||
if (m & S_IXOTH)
|
||||
operms |= 0001;
|
||||
|
||||
if (m & S_ISUID)
|
||||
operms |= 04000;
|
||||
if (m & S_ISGID)
|
||||
operms |= 02000;
|
||||
if (m & S_ISVTX)
|
||||
operms |= 01000;
|
||||
|
||||
ret = (char *)xmalloc (16);
|
||||
snprintf (ret, 16, "%04o", operms);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
statperms (m)
|
||||
int m;
|
||||
{
|
||||
char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
|
||||
int i;
|
||||
char *ret;
|
||||
|
||||
i = 0;
|
||||
if (m & S_IRUSR)
|
||||
ubits[i++] = 'r';
|
||||
if (m & S_IWUSR)
|
||||
ubits[i++] = 'w';
|
||||
if (m & S_IXUSR)
|
||||
ubits[i++] = 'x';
|
||||
ubits[i] = '\0';
|
||||
|
||||
i = 0;
|
||||
if (m & S_IRGRP)
|
||||
gbits[i++] = 'r';
|
||||
if (m & S_IWGRP)
|
||||
gbits[i++] = 'w';
|
||||
if (m & S_IXGRP)
|
||||
gbits[i++] = 'x';
|
||||
gbits[i] = '\0';
|
||||
|
||||
i = 0;
|
||||
if (m & S_IROTH)
|
||||
obits[i++] = 'r';
|
||||
if (m & S_IWOTH)
|
||||
obits[i++] = 'w';
|
||||
if (m & S_IXOTH)
|
||||
obits[i++] = 'x';
|
||||
obits[i] = '\0';
|
||||
|
||||
if (m & S_ISUID)
|
||||
ubits[2] = (m & S_IXUSR) ? 's' : 'S';
|
||||
if (m & S_ISGID)
|
||||
gbits[2] = (m & S_IXGRP) ? 's' : 'S';
|
||||
if (m & S_ISVTX)
|
||||
obits[2] = (m & S_IXOTH) ? 't' : 'T';
|
||||
|
||||
ret = (char *)xmalloc (32);
|
||||
snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
statmode(mode)
|
||||
int mode;
|
||||
{
|
||||
char *modestr, *m;
|
||||
|
||||
modestr = m = (char *)xmalloc (8);
|
||||
if (S_ISBLK (mode))
|
||||
*m++ = 'b';
|
||||
if (S_ISCHR (mode))
|
||||
*m++ = 'c';
|
||||
if (S_ISDIR (mode))
|
||||
*m++ = 'd';
|
||||
if (S_ISREG(mode))
|
||||
*m++ = '-';
|
||||
if (S_ISFIFO(mode))
|
||||
*m++ = 'p';
|
||||
if (S_ISLNK(mode))
|
||||
*m++ = 'l';
|
||||
if (S_ISSOCK(mode))
|
||||
*m++ = 's';
|
||||
|
||||
#ifdef S_ISDOOR
|
||||
if (S_ISDOOR (mode))
|
||||
*m++ = 'D';
|
||||
#endif
|
||||
#ifdef S_ISWHT
|
||||
if (S_ISWHT(mode))
|
||||
*m++ = 'W';
|
||||
#endif
|
||||
#ifdef S_ISNWK
|
||||
if (S_ISNWK(mode))
|
||||
*m++ = 'n';
|
||||
#endif
|
||||
#ifdef S_ISMPC
|
||||
if (S_ISMPC (mode))
|
||||
*m++ = 'm';
|
||||
#endif
|
||||
|
||||
*m = '\0';
|
||||
return (modestr);
|
||||
}
|
||||
|
||||
static char *
|
||||
stattime (t)
|
||||
time_t t;
|
||||
{
|
||||
char *tbuf, *ret;
|
||||
size_t tlen;
|
||||
|
||||
tbuf = ctime (&t);
|
||||
tlen = strlen (tbuf);
|
||||
ret = savestring (tbuf);
|
||||
ret[tlen-1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
statval (which, fname, flags, sp)
|
||||
int which;
|
||||
char *fname;
|
||||
int flags;
|
||||
struct stat *sp;
|
||||
{
|
||||
int temp;
|
||||
|
||||
switch (which)
|
||||
{
|
||||
case ST_NAME:
|
||||
return savestring (fname);
|
||||
case ST_DEV:
|
||||
return itos (sp->st_dev);
|
||||
case ST_INO:
|
||||
return itos (sp->st_ino);
|
||||
case ST_MODE:
|
||||
return (statmode (sp->st_mode));
|
||||
case ST_NLINK:
|
||||
return itos (sp->st_nlink);
|
||||
case ST_UID:
|
||||
return itos (sp->st_uid);
|
||||
case ST_GID:
|
||||
return itos (sp->st_gid);
|
||||
case ST_RDEV:
|
||||
return itos (sp->st_rdev);
|
||||
case ST_SIZE:
|
||||
return itos (sp->st_size);
|
||||
case ST_ATIME:
|
||||
return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime));
|
||||
case ST_MTIME:
|
||||
return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime));
|
||||
case ST_CTIME:
|
||||
return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime));
|
||||
case ST_BLKSIZE:
|
||||
return itos (sp->st_blksize);
|
||||
case ST_BLOCKS:
|
||||
return itos (sp->st_blocks);
|
||||
case ST_CHASELINK:
|
||||
return (statlink (fname, sp));
|
||||
case ST_PERMS:
|
||||
temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
|
||||
return (flags & 2) ? statperms (temp) : octalperms (temp);
|
||||
default:
|
||||
return savestring ("42");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
loadstat (vname, var, fname, flags, sp)
|
||||
char *vname;
|
||||
SHELL_VAR *var;
|
||||
char *fname;
|
||||
int flags;
|
||||
struct stat *sp;
|
||||
{
|
||||
int i;
|
||||
char *key, *value;
|
||||
SHELL_VAR *v;
|
||||
|
||||
for (i = 0; arraysubs[i]; i++)
|
||||
{
|
||||
key = savestring (arraysubs[i]);
|
||||
value = statval (i, fname, flags, sp);
|
||||
v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
stat_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, flags;
|
||||
char *aname, *fname;
|
||||
struct stat st;
|
||||
SHELL_VAR *v;
|
||||
|
||||
aname = "STAT";
|
||||
flags = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "A:Ll")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'A':
|
||||
aname = list_optarg;
|
||||
break;
|
||||
case 'L':
|
||||
flags |= 1; /* operate on links rather than resolving them */
|
||||
break;
|
||||
case 'l':
|
||||
flags |= 2;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
list = loptend;
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
fname = list->word->word;
|
||||
|
||||
if (getstat (fname, flags, &st) < 0)
|
||||
{
|
||||
builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
unbind_variable (aname);
|
||||
v = make_new_assoc_variable (aname);
|
||||
if (v == 0)
|
||||
{
|
||||
builtin_error ("%s: cannot create variable", aname);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
if (loadstat (aname, v, fname, flags, &st) < 0)
|
||||
{
|
||||
builtin_error ("%s: cannot assign file status information", aname);
|
||||
unbind_variable (aname);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* An array of strings forming the `long' documentation for a builtin xxx,
|
||||
which is printed by `help xxx'. It must end with a NULL. By convention,
|
||||
the first line is a short description. */
|
||||
char *stat_doc[] = {
|
||||
"Load an associative array with file status information.",
|
||||
"",
|
||||
"Take a filename and load the status information returned by a",
|
||||
"stat(2) call on that file into the associative array specified",
|
||||
"by the -A option. The default array name is STAT. If the -L",
|
||||
"option is supplied, stat does not resolve symbolic links and",
|
||||
"reports information about the link itself. The -l option results",
|
||||
"in longer-form listings for some of the fields. The exit status is 0",
|
||||
"unless the stat fails or assigning the array is unsuccessful.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
/* The standard structure describing a builtin command. bash keeps an array
|
||||
of these structures. The flags must include BUILTIN_ENABLED so the
|
||||
builtin can be used. */
|
||||
struct builtin stat_struct = {
|
||||
"stat", /* builtin name */
|
||||
stat_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
stat_doc, /* array of long documentation strings. */
|
||||
"stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
+1
-14
@@ -1110,19 +1110,6 @@ extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeva
|
||||
|
||||
static const int precs[] = { 0, 100, 10, 1 };
|
||||
|
||||
#if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV)
|
||||
static int
|
||||
decpoint ()
|
||||
{
|
||||
struct lconv *lv;
|
||||
|
||||
lv = localeconv ();
|
||||
return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.';
|
||||
}
|
||||
#else
|
||||
# define decpoint() '.'
|
||||
#endif
|
||||
|
||||
/* Expand one `%'-prefixed escape sequence from a time format string. */
|
||||
static int
|
||||
mkfmt (buf, prec, lng, sec, sec_fraction)
|
||||
@@ -1167,7 +1154,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction)
|
||||
and 999. */
|
||||
if (prec != 0)
|
||||
{
|
||||
buf[ind++] = decpoint ();
|
||||
buf[ind++] = locale_decpoint ();
|
||||
for (aind = 1; aind <= prec; aind++)
|
||||
{
|
||||
buf[ind++] = (sec_fraction / precs[aind]) + '0';
|
||||
|
||||
@@ -137,6 +137,9 @@ extern char *get_locale_var __P((char *));
|
||||
extern char *localetrans __P((char *, int, int *));
|
||||
extern char *mk_msgstr __P((char *, int *));
|
||||
extern char *localeexpand __P((char *, int, int, int, int *));
|
||||
#ifndef locale_decpoint
|
||||
extern int locale_decpoint __P((void));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in list.c. */
|
||||
extern void list_walk __P((GENERIC_LIST *, sh_glist_func_t *));
|
||||
|
||||
@@ -453,6 +453,21 @@ cleanup_the_pipeline ()
|
||||
discard_pipeline (disposer);
|
||||
}
|
||||
|
||||
void
|
||||
discard_last_procsub_child ()
|
||||
{
|
||||
PROCESS *disposer;
|
||||
sigset_t set, oset;
|
||||
|
||||
BLOCK_CHILD (set, oset);
|
||||
disposer = last_procsub_child;
|
||||
last_procsub_child = (PROCESS *)NULL;
|
||||
UNBLOCK_CHILD (oset);
|
||||
|
||||
if (disposer)
|
||||
discard_pipeline (disposer);
|
||||
}
|
||||
|
||||
struct pipeline_saver *
|
||||
alloc_pipeline_saver ()
|
||||
{
|
||||
|
||||
@@ -190,6 +190,7 @@ extern JOB **jobs;
|
||||
extern void making_children __P((void));
|
||||
extern void stop_making_children __P((void));
|
||||
extern void cleanup_the_pipeline __P((void));
|
||||
extern void discard_last_procsub_child __P((void));
|
||||
extern void save_pipeline __P((int));
|
||||
extern PROCESS *restore_pipeline __P((int));
|
||||
extern void start_pipeline __P((void));
|
||||
|
||||
@@ -330,6 +330,12 @@ PARSE_COLLSYM (p, vp)
|
||||
for (pc = 0; p[pc]; pc++)
|
||||
if (p[pc] == L('.') && p[pc+1] == L(']'))
|
||||
break;
|
||||
if (p[pc] == 0)
|
||||
{
|
||||
if (vp)
|
||||
*vp = INVALID;
|
||||
return (p + pc);
|
||||
}
|
||||
val = COLLSYM (p, pc);
|
||||
if (vp)
|
||||
*vp = val;
|
||||
@@ -483,6 +489,9 @@ BRACKMATCH (p, test, flags)
|
||||
c = *p++;
|
||||
c = FOLD (c);
|
||||
|
||||
if (c == L('\0'))
|
||||
return ((test == L('[')) ? savep : (CHAR *)0);
|
||||
|
||||
if ((flags & FNM_PATHNAME) && c == L('/'))
|
||||
/* [/] can never match when matching a pathname. */
|
||||
return (CHAR *)0;
|
||||
|
||||
@@ -100,6 +100,12 @@
|
||||
# include "watch.h"
|
||||
#endif
|
||||
|
||||
#ifdef powerof2
|
||||
# undef powerof2
|
||||
#endif
|
||||
/* Could also use (((x) & -(x)) == (x)) */
|
||||
#define powerof2(x) ((((x) - 1) & (x)) == 0)
|
||||
|
||||
/* System-specific omissions. */
|
||||
#ifdef HPUX
|
||||
# define NO_VALLOC
|
||||
@@ -1125,6 +1131,28 @@ internal_memalign (alignment, size, file, line, flags)
|
||||
return aligned;
|
||||
}
|
||||
|
||||
int
|
||||
posix_memalign (memptr, alignment, size)
|
||||
void **memptr;
|
||||
size_t alignment, size;
|
||||
{
|
||||
void *mem;
|
||||
|
||||
/* Perform posix-mandated error checking here */
|
||||
if ((alignment % sizeof (void *) != 0) || alignment == 0)
|
||||
return EINVAL;
|
||||
else if (powerof2 (alignment) == 0)
|
||||
return EINVAL;
|
||||
|
||||
mem = internal_memalign (alignment, size, (char *)0, 0, 0);
|
||||
if (mem != 0)
|
||||
{
|
||||
*memptr = mem;
|
||||
return 0;
|
||||
}
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
#if !defined (NO_VALLOC)
|
||||
/* This runs into trouble with getpagesize on HPUX, and Multimax machines.
|
||||
Patching out seems cleaner than the ugly fix needed. */
|
||||
|
||||
+16
-2
@@ -37,6 +37,10 @@
|
||||
#include "posixstat.h" // stat related macros (S_ISREG, ...)
|
||||
#include <fcntl.h> // S_ISUID
|
||||
|
||||
#ifndef S_ISDIR
|
||||
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
// strlen()
|
||||
#if defined (HAVE_STRING_H)
|
||||
# include <string.h>
|
||||
@@ -183,11 +187,17 @@ _rl_print_color_indicator (const char *f)
|
||||
{
|
||||
colored_filetype = C_FILE;
|
||||
|
||||
#if defined (S_ISUID)
|
||||
if ((mode & S_ISUID) != 0 && is_colored (C_SETUID))
|
||||
colored_filetype = C_SETUID;
|
||||
else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID))
|
||||
else
|
||||
#endif
|
||||
#if defined (S_ISGID)
|
||||
if ((mode & S_ISGID) != 0 && is_colored (C_SETGID))
|
||||
colored_filetype = C_SETGID;
|
||||
else if (is_colored (C_CAP) && 0) //f->has_capability)
|
||||
else
|
||||
#endif
|
||||
if (is_colored (C_CAP) && 0) //f->has_capability)
|
||||
colored_filetype = C_CAP;
|
||||
else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC))
|
||||
colored_filetype = C_EXEC;
|
||||
@@ -211,12 +221,16 @@ _rl_print_color_indicator (const char *f)
|
||||
colored_filetype = C_STICKY;
|
||||
#endif
|
||||
}
|
||||
#if defined (S_ISLNK)
|
||||
else if (S_ISLNK (mode))
|
||||
colored_filetype = C_LINK;
|
||||
#endif
|
||||
else if (S_ISFIFO (mode))
|
||||
colored_filetype = C_FIFO;
|
||||
#if defined (S_ISSOCK)
|
||||
else if (S_ISSOCK (mode))
|
||||
colored_filetype = C_SOCK;
|
||||
#endif
|
||||
else if (S_ISBLK (mode))
|
||||
colored_filetype = C_BLK;
|
||||
else if (S_ISCHR (mode))
|
||||
|
||||
@@ -606,12 +606,14 @@ history_truncate_file (fname, lines)
|
||||
history_lines_written_to_file = 0;
|
||||
}
|
||||
|
||||
#if defined (HAVE_CHOWN)
|
||||
/* Make sure the new filename is owned by the same user as the old. If one
|
||||
user is running this, it's a no-op. If the shell is running after sudo
|
||||
with a shared history file, we don't want to leave the history file
|
||||
owned by root. */
|
||||
if (rv == 0 && exists)
|
||||
r = chown (filename, finfo.st_uid, finfo.st_gid);
|
||||
#endif
|
||||
|
||||
xfree (filename);
|
||||
FREE (tempname);
|
||||
@@ -753,12 +755,14 @@ mmap_error:
|
||||
history_lines_written_to_file = 0;
|
||||
}
|
||||
|
||||
#if defined (HAVE_CHOWN)
|
||||
/* Make sure the new filename is owned by the same user as the old. If one
|
||||
user is running this, it's a no-op. If the shell is running after sudo
|
||||
with a shared history file, we don't want to leave the history file
|
||||
owned by root. */
|
||||
if (rv == 0 && exists)
|
||||
mode = chown (histname, finfo.st_uid, finfo.st_gid);
|
||||
#endif
|
||||
|
||||
FREE (histname);
|
||||
FREE (tempname);
|
||||
|
||||
@@ -561,3 +561,21 @@ locale_isutf8 (lspec)
|
||||
return (strstr (lspec, "UTF-8") || strstr (lspec, "utf8"));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (HAVE_LOCALECONV)
|
||||
int
|
||||
locale_decpoint ()
|
||||
{
|
||||
struct lconv *lv;
|
||||
|
||||
lv = localeconv ();
|
||||
return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.';
|
||||
}
|
||||
#else
|
||||
# undef locale_decpoint
|
||||
int
|
||||
locale_decpoint ()
|
||||
{
|
||||
return '.';
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2816,6 +2816,9 @@ mk_alexpansion (s)
|
||||
strcpy (r, s);
|
||||
/* If the last character in the alias is a newline, don't add a trailing
|
||||
space to the expansion. Works with shell_getc above. */
|
||||
/* Need to do something about the case where the alias expansion contains
|
||||
an unmatched quoted string, since appending this space affects the
|
||||
subsequent output. */
|
||||
if (r[l - 1] != ' ' && r[l - 1] != '\n' && shellmeta(r[l - 1]) == 0)
|
||||
r[l++] = ' ';
|
||||
r[l] = '\0';
|
||||
@@ -2839,9 +2842,11 @@ alias_expand_token (tokstr)
|
||||
return (NO_EXPANSION);
|
||||
|
||||
/* mk_alexpansion puts an extra space on the end of the alias expansion,
|
||||
so the lookahead by the parser works right. If this gets changed,
|
||||
make sure the code in shell_getc that deals with reaching the end of
|
||||
an expanded alias is changed with it. */
|
||||
so the lookahead by the parser works right (the alias needs to remain
|
||||
`in use' while parsing its last word to avoid alias recursion for
|
||||
something like "alias echo=echo"). If this gets changed, make sure
|
||||
the code in shell_getc that deals with reaching the end of an
|
||||
expanded alias is changed with it. */
|
||||
expanded = ap ? mk_alexpansion (ap->value) : (char *)NULL;
|
||||
|
||||
if (expanded)
|
||||
|
||||
@@ -230,6 +230,16 @@ quote_string_for_globbing (pathname, qflags)
|
||||
savej = j;
|
||||
savei = i;
|
||||
c = pathname[i++]; /* c == char after open bracket */
|
||||
if (c == '^') /* ignore pattern negation */
|
||||
{
|
||||
temp[j++] = c;
|
||||
c = pathname[i++];
|
||||
}
|
||||
if (c == ']') /* ignore right bracket if first char */
|
||||
{
|
||||
temp[j++] = c;
|
||||
c = pathname[i++];
|
||||
}
|
||||
do
|
||||
{
|
||||
if (c == 0)
|
||||
|
||||
@@ -1383,6 +1383,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
|
||||
{
|
||||
si = i + 2;
|
||||
t = extract_command_subst (string, &si, flags|SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1392,6 +1393,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
|
||||
{
|
||||
si = i + len_opener;
|
||||
t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1401,6 +1403,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
|
||||
{
|
||||
si = i + len_alt_opener;
|
||||
t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1420,6 +1423,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
|
||||
{
|
||||
si = i + 1;
|
||||
t = string_extract (string, &si, "`", flags|SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1473,6 +1477,8 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
|
||||
it should point to just after the first `{' found. On exit, SINDEX
|
||||
gets the position of the matching `}'. QUOTED is non-zero if this
|
||||
occurs inside double quotes. */
|
||||
/* XXX -- should this use skipsubscript to handle ${word[sub]}? (only do it
|
||||
if dolbrace_state == DOLBRACE_PARAM) */
|
||||
/* XXX -- this is very similar to extract_delimited_string -- XXX */
|
||||
static char *
|
||||
extract_dollar_brace_string (string, sindex, quoted, flags)
|
||||
@@ -1539,7 +1545,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
|
||||
t = string_extract (string, &si, "`", flags|SX_NOALLOC);
|
||||
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
|
||||
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1550,6 +1556,9 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
|
||||
{
|
||||
si = i + 2;
|
||||
t = extract_command_subst (string, &si, flags|SX_NOALLOC);
|
||||
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1577,6 +1586,17 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
/* XXX - bash-5.0 */
|
||||
if (c == '[' && dolbrace_state == DOLBRACE_PARAM)
|
||||
{
|
||||
si = skipsubscript (string, i, 0);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
if (string[si] == ']')
|
||||
c = string[i = si];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* move past this character, which was not special. */
|
||||
ADVANCE_CHAR (string, slen, i);
|
||||
|
||||
@@ -1905,6 +1925,7 @@ skip_to_delim (string, start, delims, flags)
|
||||
temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
|
||||
else
|
||||
temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si;
|
||||
if (string[i] == '\0') /* don't increment i past EOS in loop */
|
||||
break;
|
||||
@@ -1923,6 +1944,7 @@ skip_to_delim (string, start, delims, flags)
|
||||
temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si, 0);
|
||||
free (temp); /* XXX - not using SX_NOALLOC here yet */
|
||||
#endif
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si;
|
||||
if (string[i] == '\0')
|
||||
break;
|
||||
@@ -1942,6 +1964,7 @@ skip_to_delim (string, start, delims, flags)
|
||||
open[2] = '\0';
|
||||
temp = extract_delimited_string (string, &si, open, "(", ")", SX_NOALLOC); /* ) */
|
||||
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si;
|
||||
if (string[i] == '\0') /* don't increment i past EOS in loop */
|
||||
break;
|
||||
@@ -5810,10 +5833,7 @@ process_substitute (string, open_for_read_in_child)
|
||||
{
|
||||
#if defined (JOB_CONTROL)
|
||||
if (last_procsub_child)
|
||||
{
|
||||
discard_pipeline (last_procsub_child);
|
||||
last_procsub_child = (PROCESS *)NULL;
|
||||
}
|
||||
discard_last_procsub_child ();
|
||||
last_procsub_child = restore_pipeline (0);
|
||||
#endif
|
||||
|
||||
|
||||
+66
@@ -221,6 +221,9 @@ static SHELL_VAR *get_lineno __P((SHELL_VAR *));
|
||||
static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
|
||||
static SHELL_VAR *get_subshell __P((SHELL_VAR *));
|
||||
|
||||
static SHELL_VAR *get_epochseconds __P((SHELL_VAR *));
|
||||
static SHELL_VAR *get_epochrealtime __P((SHELL_VAR *));
|
||||
|
||||
static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
|
||||
|
||||
#if defined (HISTORY)
|
||||
@@ -1451,6 +1454,40 @@ get_subshell (var)
|
||||
return (var);
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
get_epochseconds (var)
|
||||
SHELL_VAR *var;
|
||||
{
|
||||
intmax_t now;
|
||||
char *p;
|
||||
|
||||
now = NOW;
|
||||
p = itos (now);
|
||||
|
||||
FREE (value_cell (var));
|
||||
var_setvalue (var, p);
|
||||
return (var);
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
get_epochrealtime (var)
|
||||
SHELL_VAR *var;
|
||||
{
|
||||
char buf[32];
|
||||
char *p;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
snprintf (buf, sizeof (buf), "%u%c%06u", (unsigned)tv.tv_sec,
|
||||
locale_decpoint (),
|
||||
(unsigned)tv.tv_usec);
|
||||
|
||||
p = savestring (buf);
|
||||
FREE (value_cell (var));
|
||||
var_setvalue (var, p);
|
||||
return (var);
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
get_bashpid (var)
|
||||
SHELL_VAR *var;
|
||||
@@ -1467,6 +1504,30 @@ get_bashpid (var)
|
||||
return (var);
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
get_bash_argv0 (var)
|
||||
SHELL_VAR *var;
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = savestring (dollar_vars[0]);
|
||||
FREE (value_cell (var));
|
||||
var_setvalue (var, p);
|
||||
return var;
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
assign_bash_argv0 (var, value, unused, key)
|
||||
SHELL_VAR *var;
|
||||
char *value;
|
||||
arrayind_t unused;
|
||||
char *key;
|
||||
{
|
||||
FREE (dollar_vars[0]);
|
||||
dollar_vars[0] = savestring (value);
|
||||
return var;
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
get_bash_command (var)
|
||||
SHELL_VAR *var;
|
||||
@@ -1758,6 +1819,8 @@ initialize_dynamic_variables ()
|
||||
|
||||
v = init_seconds_var ();
|
||||
|
||||
INIT_DYNAMIC_VAR ("BASH_ARGV0", (char *)NULL, get_bash_argv0, assign_bash_argv0);
|
||||
|
||||
INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
|
||||
INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
|
||||
|
||||
@@ -1769,6 +1832,9 @@ initialize_dynamic_variables ()
|
||||
INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
|
||||
VSETATTR (v, att_integer);
|
||||
|
||||
INIT_DYNAMIC_VAR ("EPOCHSECONDS", (char *)NULL, get_epochseconds, null_assign);
|
||||
INIT_DYNAMIC_VAR ("EPOCHREALTIME", (char *)NULL, get_epochrealtime, null_assign);
|
||||
|
||||
#if defined (HISTORY)
|
||||
INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
|
||||
VSETATTR (v, att_integer);
|
||||
|
||||
Reference in New Issue
Block a user