- sys/ioctl.h: include unconditionally for ioctl declaration, avoid issues
with `implicit declaration' warnings. Issue originally raised on gdb
list by Chen Gang <xili_gchen_5257@hotmail.com>
+
+ 4/27
+ ----
+lib/malloc/table.c
+ - mem_table: now a circular buffer showing the state of the last
+ REG_TABLE_SIZE allocations rather than a hash table that quickly fills
+ up
+
+builtins/declare.def
+ - typeset_builtin: add -n to list of supported options. Omission reported
+ by Valentin Bajrami <valentin.bajrami@gmail.com>
+
+unwind_prot.c
+ - use object caches instead of malloc/free to allocate and deallocate
+ unwind-protect elements
+ - uwp_init: initialize unwind-protect element object cache
+
+unwind_prot.h
+ - uwp_init: extern declaration
+
+shell.c
+ - main: call uwp_init right after calling cmd_init -- initialize all the
+ object caches at the same place
+
+lib/malloc/table.[ch]
+ - mlocation_table: new table to keep track of allocation locations by
+ file and line, functions to initialize table, record an allocation,
+ and dump the table to stderr
+ - mregister_alloc: call mlocation_register_alloc to record the location
+ (source file/line) for each allocation to pinpoint malloc hot spots
+ by number of calls. More detail than gprof
+
+parse.y
+ - set_line_mbstate: replace free/xmalloc pair with xrealloc call
+ - read_token_word: call alloc_word_desc instead of xmalloc so we can take
+ advantage of the WORD_DESC cache
+
+ 4/28
+ ----
+execute_cmd.c
+ - execute_simple_command: if command execution fails because the command
+ is a directory, and the `autocd' option is set, add a `--' argument
+ to the constructed `cd' command to protect against command names with
+ the same name as options to `cd'. Report and fix from
+ isabella parakiss <izaberina@gmail.com>
+
+ 4/30
+ ----
+jobs.c
+ - printable_job_status, j_strsignal: change calls to strcpy and sprintf
+ that write to retcode_name_buffer to use strncpy and snprintf to
+ avoid buffer overflows caused by malicious translations. Bug and fix
+ from Trammell Hudson <Trammell.Hudson@twosigma.com>
+
+ 5/1
+ ---
+strtrans.c
+ - ansicstr: make sure the buffer is at least 12 bytes to ensure enough
+ space for any eventual call to u32cesc for one multibyte char
+
+ 5/4
+ ---
+jobs.c
+ - wait_for: if an interactive shell is running a loop and waiting for
+ a non-builtin command to exit, and the command exits due to SIGINT,
+ act as if the shell received the SIGINT as well and break the loop.
+ This matches the behavior when the shell is running a builtin command
+ in a loop, and when running a non-builtin command outside a loop, and
+ seems more broadly useful than running the trap handler over and over
+ again. Report originally from Kaz Kylheku <kkylheku@gnu.org>
+
+builtins/set.def
+ - unset_builtin: use different variables for keeping the state of the
+ -f and -v options than the loop uses to decide whether or not to
+ treat a name as a function or a variable. Fixes problem with
+ unset_function setting `sticking' after you unset a function when
+ invoked with no options. Bug report from Dreamcat4
+ <dreamcat4@gmail.com>
+
+shell.c
+ - open_shell_script: set running_shell_script to 1, set to 0 in every
+ other case (new variable)
+ - main: when checking whether or not to call start_debugger, test
+ running_shell_script instead of dollar_vars[1]. The goal is to not
+ invoke the debugger for interactive shells but allow it to run for
+ things like `bash --debugger -i /tmp/script'. Problem reported by
+ Rocky Bernstein <rocky@gnu.org>
+
+lib/readline/histexpand.c
+ - history_event_delimiter_chars: new (as yet undocumented) variable
+ containing by default characters that can delimit a history event
+ specifier without requiring a `:': "^$*%-" as the documentation has
+ always said. Fixes bug reported by Anders Granlund
+ <anders.granlund.0@gmail.com>
+
+ 5/10
+ ----
+lib/glob/gmisc.c
+ - match_pattern_char, match_pattern_wchar: if passed an empty string,
+ return a match if the first character of the pattern is `*'
+
+subst.c
+ - pat_subst: change to allow empty strings to be replaced as long as
+ pattern matches empty string. Report and fix from isabella parakiss
+ <izaberina@gmail.com>
tests/builtins3.sub f
tests/builtins4.sub f
tests/builtins5.sub f
+tests/builtins6.sub f
tests/source1.sub f
tests/source2.sub f
tests/source3.sub f
tests/comsub.tests f
tests/comsub.right f
tests/comsub1.sub f
+tests/comsub2.sub f
tests/comsub-eof.tests f
tests/comsub-eof0.sub f
tests/comsub-eof1.sub f
tests/errors1.sub f
tests/errors2.sub f
tests/errors3.sub f
+tests/errors4.sub f
+tests/errors5.sub f
tests/execscript f
tests/exec.right f
tests/exec1.sub f 755
tests/nameref6.sub f
tests/nameref7.sub f
tests/nameref8.sub f
+tests/nameref9.sub f
tests/nameref.right f
tests/new-exp.tests f
tests/new-exp1.sub f
tests/vredir4.sub f
tests/vredir5.sub f
tests/vredir6.sub f
+tests/vredir7.sub f
tests/misc/dev-tcp.tests f
tests/misc/perf-script f
tests/misc/perftest f
/* Build the actions and compspec options from the options specified in LIST.
ACTP is a pointer to an unsigned long in which to place the bitmap of
actions. OPTP is a pointer to an unsigned long in which to place the
- btmap of compspec options (arguments to `-o'). PP, if non-null, gets 1
+ bitmap of compspec options (arguments to `-o'). PP, if non-null, gets 1
if -p is supplied; RP, if non-null, gets 1 if -r is supplied.
If either is null, the corresponding option generates an error.
This also sets variables corresponding to options that take arguments as
$BUILTIN typeset
$FUNCTION declare_builtin
-$SHORT_DOC typeset [-aAfFgilrtux] [-p] name[=value] ...
+$SHORT_DOC typeset [-aAfFgilnrtux] [-p] name[=value] ...
Set variable values and attributes.
Obsolete. See `help declare'.
WORD_LIST *list;
{
int unset_function, unset_variable, unset_array, opt, nameref, any_failed;
+ int global_unset_func, global_unset_var;
char *name;
unset_function = unset_variable = unset_array = nameref = any_failed = 0;
+ global_unset_func = global_unset_var = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "fnv")) != -1)
switch (opt)
{
case 'f':
- unset_function = 1;
+ global_unset_func = 1;
break;
case 'v':
- unset_variable = 1;
+ global_unset_var = 1;
break;
case 'n':
nameref = 1;
list = loptend;
- if (unset_function && unset_variable)
+ if (global_unset_func && global_unset_var)
{
builtin_error (_("cannot simultaneously unset a function and a variable"));
return (EXECUTION_FAILURE);
name = list->word->word;
+ unset_function = global_unset_func;
+ unset_variable = global_unset_var;
+
#if defined (ARRAY_VARS)
unset_array = 0;
if (!unset_function && valid_array_reference (name, 0))
trap -p [sigspec ...]
trap [--]
- Set things up so that ARG is executed when SIGNAL(s) N is recieved.
+ Set things up so that ARG is executed when SIGNAL(s) N is received.
If ARG is the empty string, then ignore the SIGNAL(s). If there is
no ARG, then set the trap for SIGNAL(s) to its original value. Just
plain "trap" means to print out the list of commands associated with
if (s)
subshell_exit (last_command_exit_value);
else
- exit (last_command_exit_value);
+ sh_exit (last_command_exit_value);
/* NOTREACHED */
}
else
int ret;
temp = search_for_command (pathname, 0);
- ret = (temp ? file_isdir (temp) : file_isdir (pathname));
+ ret = temp ? file_isdir (temp) : file_isdir (pathname);
free (temp);
return ret;
}
pipe_in, pipe_out,
already_forked ? 0 : async);
if (already_forked)
- exit (result);
+ sh_exit (result);
else
{
bind_lastarg ((char *)NULL);
if (autocd && interactive && words->word && is_dirname (words->word->word))
{
+ words = make_word_list (make_word ("--"), words);
words = make_word_list (make_word ("cd"), words);
xtrace_print_word_list (words, 0);
goto run_builtin;
fflush (stdout);
if (r == EX_USAGE)
r = EX_BADUSAGE;
- exit (r);
+ sh_exit (r);
}
}
else
{
r = execute_function (var, words, flags, fds_to_close, async, 1);
fflush (stdout);
- exit (r);
+ sh_exit (r);
}
}
int fd;
{
errno = 0;
- if (lseek ((fd), 0L, SEEK_CUR) < 0)
- return (errno == ESPIPE);
- return 0;
+ return ((lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE));
}
/* There is a bug in the NeXT 2.1 rlogind that causes opens
int token;
} STRING_INT_ALIST;
-/* A macro to avoid making an unneccessary function call. */
+/* A macro to avoid making an unnecessary function call. */
#define REVERSE_LIST(list, type) \
((list && list->next) ? (type)list_reverse ((GENERIC_LIST *)list) \
: (type)(list))
if (x == 0)
{
x = retcode_name_buffer;
- sprintf (x, _("Signal %d"), s);
+ snprintf (x, sizeof(retcode_name_buffer), _("Signal %d"), s);
}
return x;
}
else
{
temp = retcode_name_buffer;
- sprintf (temp, _("Stopped(%s)"), signal_name (WSTOPSIG (p->status)));
+ snprintf (temp, sizeof(retcode_name_buffer), _("Stopped(%s)"), signal_name (WSTOPSIG (p->status)));
}
}
else if (RUNNING (j))
temp = retcode_name_buffer;
es = WEXITSTATUS (p->status);
if (es == 0)
- strcpy (temp, _("Done"));
+ {
+ strncpy (temp, _("Done"), sizeof (retcode_name_buffer) - 1);
+ temp[sizeof (retcode_name_buffer) - 1] = '\0';
+ }
else if (posixly_correct)
- sprintf (temp, _("Done(%d)"), es);
+ snprintf (temp, sizeof(retcode_name_buffer), _("Done(%d)"), es);
else
- sprintf (temp, _("Exit %d"), es);
+ snprintf (temp, sizeof(retcode_name_buffer), _("Exit %d"), es);
}
else
temp = _("Unknown status");
SIGINT signal handler; maybe it should. */
if (signal_is_trapped (SIGINT) == 0 && (loop_level || (shell_compatibility_level > 32 && executing_list)))
ADDINTERRUPT;
+ /* Call any SIGINT trap handler if the shell is running a loop, so
+ the loop can be broken. This seems more useful and matches the
+ behavior when the shell is running a builtin command in a loop
+ when it is interrupted. Change ADDINTERRUPT to
+ trap_handler (SIGINT) to run the trap without interrupting the
+ loop. */
+ else if (signal_is_trapped (SIGINT) && loop_level)
+ ADDINTERRUPT;
else
{
putchar ('\n');
wchar_t wc;
if (*wstring == 0)
- return (0);
+ return (*wpat == L'*'); /* XXX - allow only * to match empty string */
switch (wc = *wpat++)
{
char c;
if (*string == 0)
- return (0);
+ return (*pat == '*'); /* XXX - allow only * to match empty string */
switch (c = *pat++)
{
#ifdef MALLOC_REGISTER
-#define FIND_ALLOC 0x01 /* allocate new entry or find existing */
-#define FIND_EXIST 0x02 /* find existing entry */
+extern FILE *_imalloc_fopen __P((char *, char *, char *, char *, size_t));
+
+#define FIND_ALLOC 0x01 /* find slot for new allocation */
+#define FIND_EXIST 0x02 /* find slot for existing entry for free() or search */
static int table_count = 0;
static int table_allocated = 0;
+static int table_bucket_index = REG_TABLE_SIZE-1;
static mr_table_t mem_table[REG_TABLE_SIZE];
static mr_table_t mem_overflow;
+#ifndef STREQ
+#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
+#endif
+
+static int location_table_index = 0;
+static int location_table_count = 0;
+static ma_table_t mlocation_table[REG_TABLE_SIZE];
+
/*
* NOTE: taken from dmalloc (http://dmalloc.com) and modified.
*/
{
return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1));
}
+
#else
#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1));
+
+#define next_bucket() ((table_bucket_index + 1) & (REG_TABLE_SIZE-1))
+#define next_entry(mem) ((mem == mem_table + REG_TABLE_SIZE - 1) ? mem_table : ++mem)
+
+#define prev_bucket() (table_bucket_index == 0 ? REG_TABLE_SIZE-1 : table_bucket_index-1)
+#define prev_entry(mem) ((mem == mem_table) ? mem_table + REG_TABLE_SIZE - 1 : mem - 1)
#endif
static mr_table_t *
{
unsigned int bucket;
register mr_table_t *tp;
- mr_table_t *endp, *lastp;
+ mr_table_t *endp;
if (mem_overflow.mem == mem)
return (&mem_overflow);
- bucket = which_bucket (mem); /* get initial hash */
- tp = endp = mem_table + bucket;
- lastp = mem_table + REG_TABLE_SIZE;
+ /* If we want to insert an allocation entry just use the next slot */
+ if (flags & FIND_ALLOC)
+ {
+ table_bucket_index = next_bucket();
+ table_count++;
+ tp = mem_table + table_bucket_index;
+ memset(tp, 0, sizeof (mr_table_t)); /* overwrite next existing entry */
+ return tp;
+ }
+
+ tp = endp = mem_table + table_bucket_index;
+ /* search for last allocation corresponding to MEM, return entry pointer */
while (1)
{
if (tp->mem == mem)
return (tp);
- if (tp->mem == 0 && (flags & FIND_ALLOC))
- {
- table_count++;
- return (tp);
- }
-
- tp++;
- if (tp == lastp) /* wrap around */
- tp = mem_table;
+ tp = prev_entry (tp);
- if (tp == endp && (flags & FIND_EXIST))
+ /* if we went all the way around and didn't find it, return NULL */
+ if (tp == endp)
return ((mr_table_t *)NULL);
-
- if (tp == endp && (flags & FIND_ALLOC))
- break;
}
- /* oops. table is full. replace an existing free entry. */
- do
- {
- /* If there are no free entries, punt right away without searching. */
- if (table_allocated == REG_TABLE_SIZE)
- break;
-
- if (tp->flags & MT_FREE)
- {
- memset(tp, 0, sizeof (mr_table_t));
- return (tp);
- }
- tp++;
-
- if (tp == lastp)
- tp = mem_table;
- }
- while (tp != endp);
-
- /* wow. entirely full. return mem_overflow dummy entry. */
- tp = &mem_overflow;
- memset (tp, 0, sizeof (mr_table_t));
- return tp;
+ return (mr_table_t *)NULL;
}
mr_table_t *
blocked_sigs = 1;
}
+ mlocation_register_alloc (file, line);
+
tentry = find_entry (mem, FIND_ALLOC);
if (tentry == 0)
{
entry = mem_table[i];
if (entry.mem)
- fprintf (fp, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", i,
+ fprintf (fp, "%s[%d] %p:%d:%s:%s:%s:%d:%d:%d\n",
+ (i == table_bucket_index) ? "*" : "",
+ i,
entry.mem, entry.size,
_entry_flags(entry.flags),
entry.func ? entry.func : "unknown",
table_count = 0;
}
+/* Simple for now */
+
+static ma_table_t *
+find_location_entry (file, line)
+ const char *file;
+ int line;
+{
+ register ma_table_t *tp, *endp;
+
+ endp = mlocation_table + location_table_count;
+ for (tp = mlocation_table; tp <= endp; tp++)
+ {
+ if (tp->line == line && STREQ (file, tp->file))
+ return tp;
+ }
+ return (ma_table_t *)NULL;
+}
+
+void
+mlocation_register_alloc (file, line)
+ const char *file;
+ int line;
+{
+ ma_table_t *lentry;
+ char *nfile;
+
+ if (file == 0)
+ {
+ mlocation_table[0].nalloc++;
+ return;
+ }
+
+ nfile = strrchr (file, '/');
+ if (nfile)
+ nfile++;
+ else
+ nfile = file;
+
+ lentry = find_location_entry (nfile, line);
+ if (lentry == 0)
+ {
+ location_table_index++;
+ if (location_table_index == REG_TABLE_SIZE)
+ location_table_index = 1; /* slot 0 reserved */
+ lentry = mlocation_table + location_table_index;
+ lentry->file = nfile;
+ lentry->line = line;
+ lentry->nalloc = 1;
+ if (location_table_count < REG_TABLE_SIZE)
+ location_table_count++; /* clamp at REG_TABLE_SIZE for now */
+ }
+ else
+ lentry->nalloc++;
+}
+
+static void
+_location_dump_table (fp)
+ FILE *fp;
+{
+ register ma_table_t *tp, *endp;
+
+ endp = mlocation_table + location_table_count;
+ for (tp = mlocation_table; tp < endp; tp++)
+ fprintf (fp, "%s:%d\t%d\n", tp->file ? tp->file : "unknown",
+ tp->line ? tp->line : 0,
+ tp->nalloc);
+}
+
+void
+mlocation_dump_table ()
+{
+ _location_dump_table (stderr);
+}
+
+#define LOCROOT "/var/tmp/maltrace/locations."
+
+void
+mlocation_write_table ()
+{
+ FILE *fp;
+ char defname[sizeof (LOCROOT) + 64];
+
+ fp = _imalloc_fopen ((char *)NULL, (char *)NULL, LOCROOT, defname, sizeof (defname));
+ if (fp == 0)
+ return; /* XXX - no error message yet */
+ _location_dump_table (fp);
+ fclose (fp);
+}
+
+void
+mlocation_table_init ()
+{
+ memset (mlocation_table, 0, sizeof (ma_table_t) * REG_TABLE_SIZE);
+ mlocation_table[0].file = ""; /* reserve slot 0 for unknown locations */
+ mlocation_table[0].line = 0;
+ mlocation_table[0].nalloc = 0;
+ location_table_count = 1;
+}
+
#endif /* MALLOC_REGISTER */
int
extern void mregister_dump_table __P((void));
extern void mregister_table_init __P((void));
+typedef struct ma_table {
+ char *file;
+ int line;
+ int nalloc;
+} ma_table_t;
+
+extern void mlocation_register_alloc __P((const char *, int));
+extern void mlocation_table_init __P((void));
+extern void mlocation_dump_table __P((void));
+extern void mlocation_write_table __P((void));
+
/* NOTE: HASH_MIX taken from dmalloc (http://dmalloc.com) */
/*
#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>"
#define HISTORY_QUOTE_CHARACTERS "\"'`"
+#define HISTORY_EVENT_DELIMITERS "^$*%-"
#define slashify_in_quotes "\\`\"$"
static int subst_lhs_len;
static int subst_rhs_len;
+/* Characters that delimit history event specifications and separate event
+ specifications from word designators. Static for now */
+static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS;
+
static char *get_history_word_specifier PARAMS((char *, char *, int *));
static int history_tokenize_word PARAMS((const char *, int));
static char **history_tokenize_internal PARAMS((const char *, int, int *));
/* The last string searched for by a !?string? search. */
static char *search_string;
-
/* The last string matched by a !?string? search. */
static char *search_match;
#endif /* HANDLE_MULTIBYTE */
if ((!substring_okay && (whitespace (c) || c == ':' ||
+ (history_event_delimiter_chars && member (c, history_event_delimiter_chars)) ||
(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
string[i] == delimiting_quote)) ||
string[i] == '\n' ||
1) If expansions did take place
2) If the `p' modifier was given and the caller should print the result
- If an error ocurred in expansion, then OUTPUT contains a descriptive
+ If an error occurred in expansion, then OUTPUT contains a descriptive
error message. */
#define ADD_STRING(s) \
return ((char *)NULL);
#if defined (HANDLE_MULTIBYTE)
- ret = (char *)xmalloc (4*len + 1);
+ temp = 4*len + 1;
+ if (temp < 12)
+ temp = 12; /* ensure enough for eventual u32cesc */
+ ret = (char *)xmalloc (temp);
#else
ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
#endif
}
/* convert a single unicode-32 character into a multibyte string and put the
- result in S, which must be large enough (at least MB_LEN_MAX bytes) */
+ result in S, which must be large enough (at least max(10,MB_LEN_MAX) bytes) */
int
u32cconv (c, s)
unsigned long c;
#endif
CHECK_FOR_RESERVED_WORD (token);
- the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC));
+ the_word = alloc_word_desc ();
the_word->word = (char *)xmalloc (1 + token_index);
the_word->flags = 0;
strcpy (the_word->word, token);
if (shell_input_line == NULL)
return;
len = strlen (shell_input_line); /* XXX - shell_input_line_len ? */
- FREE (shell_input_line_property);
- shell_input_line_property = (char *)xmalloc (len + 1);
+ shell_input_line_property = (char *)xrealloc (shell_input_line_property, len + 1);
memset (&prevs, '\0', sizeof (mbstate_t));
for (i = previ = 0; i < len; i++)
allocname = 0;
if ((temp->rflags & REDIR_VARASSIGN) && error < 0)
filename = allocname = savestring (temp->redirector.filename->word);
- else if (temp->redirector.dest < 0)
+ else if ((temp->rflags & REDIR_VARASSIGN) == 0 && temp->redirector.dest < 0)
/* This can happen when read_token_word encounters overflow, like in
exec 4294967297>x */
+{
+itrace("redirection_error: temp->redirector.dest = %d", temp->redirector.dest);
filename = _("file descriptor out of range");
+}
#ifdef EBADF
/* This error can never involve NOCLOBBER */
else if (error != NOCLOBBER_REDIRECT && temp->redirector.dest >= 0 && error == EBADF)
This is a superset of the information provided by interactive_shell.
*/
int startup_state = 0;
+int reading_shell_script = 0;
/* Special debugging helper. */
int debugging_login_shell = 0;
xtrace_init ();
#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
- malloc_set_register (0); /* XXX - change to 1 for malloc debugging */
+ malloc_set_register (1); /* XXX - change to 1 for malloc debugging */
#endif
check_dev_tty ();
}
#endif
- cmd_init(); /* initialize the command object caches */
+ cmd_init (); /* initialize the command object caches */
+ uwp_init ();
if (command_execution_string)
{
/* Bind remaining args to $1 ... $n */
arg_index = bind_args (argv, arg_index, argc, 1);
- if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (dollar_vars[1] || interactive_shell == 0))
+ if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (reading_shell_script || interactive_shell == 0))
start_debugger ();
/* Do the things that should be done only for interactive shells. */
#if defined (MALLOC_DEBUG) && defined (USING_BASH_MALLOC)
if (malloc_trace_at_exit)
trace_malloc_stats (get_name_for_error (), (char *)NULL);
+ /* mlocation_write_table (); */
#endif
exit (s);
{
e = errno;
file_error (filename);
- exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT);
+ sh_exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT);
}
free (dollar_vars[0]);
errno = EINVAL;
#endif
file_error (filename);
- exit (EX_NOINPUT);
+ sh_exit (EX_NOINPUT);
}
#if defined (ARRAY_VARS)
init_interactive_script ();
free (filename);
+
+ reading_shell_script = 1;
return (fd);
}
return ((char *)NULL);
}
+#if defined (INCLUDE_UNUSED)
/* Return 1 if CHARACTER appears in an unquoted portion of
STRING. Return 0 otherwise. CHARACTER must be a single-byte character. */
static int
}
return (0);
}
+#endif
/* Most of the substitutions must be done in parallel. In order
to avoid using tons of unclear goto's, I have some functions
size_t slen, plen, mslen, mplen;
#endif
- if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
+ if (string == 0 || pat == 0 || *pat == 0)
return (0);
#if defined (HANDLE_MULTIBYTE)
val = sh_quote_reusable (s, 0);
i = var_attribute_string (v, 0, flags);
- ret = (char *)xmalloc (i + strlen (val) + strlen (v->name) + 16);
+ ret = (char *)xmalloc (i + strlen (val) + strlen (v->name) + 16 + MAX_ATTRIBUTES);
if (i > 0)
sprintf (ret, "declare -%s %s=%s", flags, v->name, val);
else
/* */
/****************************************************************/
+#if 0 /* Unused */
static int
shouldexp_replacement (s)
char *s;
}
return 0;
}
+#endif
char *
pat_subst (string, pat, rep, mflags)
* with REP and return the result.
* 2. A null pattern with mtype == MATCH_END means to append REP to
* STRING and return the result.
+ * 3. A null STRING with a matching pattern means to append REP to
+ * STRING and return the result.
* These don't understand or process `&' in the replacement string.
*/
if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
}
return (ret);
}
+ else if (*string == 0 && (match_pattern (string, pat, mtype, &s, &e) != 0))
+ {
+ ret = (char *)xmalloc (STRLEN (rep) + 1);
+ strcpy (ret, rep);
+ return (ret);
+ }
ret = (char *)xmalloc (rsize = 64);
ret[0] = '\0';
- for (replen = STRLEN (rep), rptr = 0, str = string;;)
+ for (replen = STRLEN (rep), rptr = 0, str = string; *str;)
{
if (match_pattern (str, pat, mtype, &s, &e) == 0)
break;
-BUILD_DIR=/usr/local/build/chet/bash/bash-current
+BUILD_DIR=/usr/local/build/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
-7
7
7
+2
+2
./arith1.sub: line 2: 4-- : syntax error: operand expected (error token is "- ")
./arith1.sub: line 3: 4++ : syntax error: operand expected (error token is "+ ")
./arith1.sub: line 4: 4 -- : syntax error: operand expected (error token is "- ")
0, 0
0, 1
8 12
-./arith.tests: line 286: ((: x=9 y=41 : syntax error in expression (error token is "y=41 ")
-./arith.tests: line 290: a b: syntax error in expression (error token is "b")
-./arith.tests: line 291: ((: a b: syntax error in expression (error token is "b")
+./arith.tests: line 290: ((: x=9 y=41 : syntax error in expression (error token is "y=41 ")
+./arith.tests: line 294: a b: syntax error in expression (error token is "b")
+./arith.tests: line 295: ((: a b: syntax error in expression (error token is "b")
42
42
42
42
42
42
-./arith.tests: line 302: b[c]d: syntax error in expression (error token is "d")
+./arith.tests: line 306: b[c]d: syntax error in expression (error token is "d")
echo $(( ++7 ))
echo $(( --7 ))
+# combinations of expansions
+echo $(( "`echo 1+1`" ))
+echo $(( `echo 1+1` ))
+
${THIS_SH} ./arith1.sub
${THIS_SH} ./arith2.sub
${THIS_SH} ./arith3.sub
declare -Ar waste=([version]="4.0-devel" [source]="./assoc.tests" [lineno]="28" [pid]="42134" )
declare -A wheat=([two]="b" [three]="c" [one]="a" [zero]="0" )
declare -A chaff=([one]="10" ["hello world"]="flip" [zero]="5" )
-./assoc.tests: line 38: unset: waste: cannot unset: readonly variable
-./assoc.tests: line 39: chaff[*]: bad array subscript
-./assoc.tests: line 40: [*]=12: invalid associative array key
+./assoc.tests: line 38: waste: readonly variable
+./assoc.tests: line 39: unset: waste: cannot unset: readonly variable
+./assoc.tests: line 40: chaff[*]: bad array subscript
+./assoc.tests: line 41: [*]=12: invalid associative array key
declare -A chaff=([one]="a" ["hello world"]="flip" )
flip
argv[1] = <a>
argv[3] = <multiple>
argv[4] = <words>
argv[1] = <a flip multiple words>
-./assoc.tests: line 57: declare: chaff: cannot destroy array variables in this way
-./assoc.tests: line 59: chaff[*]: bad array subscript
-./assoc.tests: line 60: [*]=12: invalid associative array key
+./assoc.tests: line 58: declare: chaff: cannot destroy array variables in this way
+./assoc.tests: line 60: chaff[*]: bad array subscript
+./assoc.tests: line 61: [*]=12: invalid associative array key
declare -A wheat=([six]="6" ["foo bar"]="qux qix" )
argv[1] = <qux>
argv[2] = <qix>
bar
after printf
after use: 0
+declare -A assoc=([0]="assoc" )
+assoc
+declare -A assoc=([two]="twoless" [three]="three" [one]="onemore" )
+declare -Ar assoc=([two]="twoless" [three]="three" [one]="onemore" )
declare -p chaff
# TEST - errors
+waste[stuff]=other
unset waste
chaff[*]=12
chaff=( [one]=a [*]=12 )
${THIS_SH} ./assoc6.sub
${THIS_SH} ./assoc7.sub
+
+# test converting between scalars and assoc arrays
+unset assoc
+assoc=assoc
+declare -A assoc
+declare -p assoc
+echo ${assoc[@]}
+
+# weird syntax required to append to multiple existing array elements using
+# compound assignment syntax
+unset assoc
+declare -A assoc
+assoc=( [one]=one [two]=two [three]=three )
+assoc+=( [one]+=more [two]+=less )
+declare -p assoc
+
+readonly -A assoc
+declare -p assoc
scalar: 1
scalar: 0
scalar: 0
-./builtins.tests: line 263: exit: status: numeric argument required
+all set:
+one
+two
+f1 ()
+{
+ echo f1
+}
+f2 ()
+{
+ echo f2
+}
+all unset:
+unset1
+unset2
+./builtins6.sub: line 28: declare: f1: not found
+./builtins6.sub: line 28: declare: f2: not found
+all reset:
+one-one
+two-one
+f1 ()
+{
+ echo f1
+}
+f2 ()
+{
+ echo f2
+}
+vars unset:
+unset1
+unset2
+f1 ()
+{
+ echo f1
+}
+f2 ()
+{
+ echo f2
+}
+funcs unset:
+one-two
+two-two
+./builtins.tests: line 266: exit: status: numeric argument required
# test behavior of set and unset array variables
${THIS_SH} ./builtins5.sub
+# test behavior of unset builtin with -f and -v options
+${THIS_SH} ./builtins6.sub
+
# this must be last -- it is a fatal error
exit status
--- /dev/null
+f1()
+{
+ echo f1
+}
+
+f2()
+{
+ echo f2
+}
+
+v1=one
+v2=two
+
+echo all set:
+
+echo ${v1-unset1}
+echo ${v2-unset2}
+
+declare -f -p f1 f2
+
+unset v1 f1 v2 f2
+
+echo all unset:
+
+echo ${v1-unset1}
+echo ${v2-unset2}
+
+declare -f -p f1 f2
+
+f1()
+{
+ echo f1
+}
+
+f2()
+{
+ echo f2
+}
+
+v1=one-one
+v2=two-one
+
+echo all reset:
+echo ${v1-unset1}
+echo ${v2-unset2}
+
+declare -f -p f1 f2
+
+unset -v v1 f1 v2 f2
+
+echo vars unset:
+
+echo ${v1-unset1}
+echo ${v2-unset2}
+
+declare -f -p f1 f2
+
+v1=one-two
+v2=two-two
+
+unset -f v1 f1 v2 f2
+
+echo funcs unset:
+
+echo ${v1-unset1}
+echo ${v2-unset2}
+
+declare -f f1 f2
BE CONSERVATIVE IN WHAT YOU SEND AND LIBERAL IN WHAT YOU ACCEPT
Be conservative in what you send and liberal in what you accept
BE CONSERVATIVE IN WHAT YOU SEND AND LIBERAL IN WHAT YOU ACCEPT
+AcknOwlEdgEmEnt acknOwlEdgEmEnt
+oeNoPHiLe OEnOphIlE
+abcdexyz
+ABCDEXYZ
echo ${TEXT2^}
echo ${TEXT2^^}
+
+M1=${S1^^[aeiou]}
+M2=${U2,,[AEIOU]}
+
+echo ${M1} ${M1~}
+echo ${M2} ${M2~~}
+
+declare -l lower=aBcDe
+lower+=XyZ
+echo $lower
+
+declare -u upper=aBcDe
+upper+=xYZ
+echo $upper
# fixed after bash-4.0 released
: $(case a in a) echo ;; # comment
esac)
+
+# recommended to be parsed as a nested comsub instead of arithsub
+echo $(( echo ab cde ) )
ok 4
ok 5
ok 6
+xyz
+\/tmp\/foo\/bar
+/tmp/foo/bar
+/tmp/foo/bar
+/tmp/foo/bar
bar')
${THIS_SH} ./comsub1.sub
+${THIS_SH} ./comsub2.sub
echo $(case a in (a) echo ok 6 # comment
;;
esac)
+
+echo $( # we just took and pasted in some
+# code from another script inside a
+# command substitution
+echo xyz
+)
--- /dev/null
+qpath='\/tmp\/foo\/bar'
+
+echo "$qpath"
+
+# it's crazy that all three of these produce the same result
+echo ${qpath//\\/}
+echo ${qpath//"`echo \\`"/}
+echo ${qpath//`echo "\\\\\\\\"`/}
./errors3.sub: line 5: no_such_file: No such file or directory
TEST
./errors3.sub: line 7: no_such_file: No such file or directory
-./errors.tests: line 271: `!!': not a valid identifier
+1
+2
+./errors4.sub: line 7: var: readonly variable
+after readonly assignment
+./errors4.sub: line 13: break: x: numeric argument required
+1
+2
+./errors4.sub: line 7: var: readonly variable
+./errors5.sub: line 6: array: unbound variable
+./errors5.sub: line 7: array: unbound variable
+./errors5.sub: line 10: 7: unbound variable
+./errors5.sub: line 11: 7: unbound variable
+./errors.tests: line 275: `!!': not a valid identifier
${THIS_SH} ./errors1.sub
${THIS_SH} ./errors2.sub
${THIS_SH} ./errors3.sub
+${THIS_SH} ./errors4.sub
+${THIS_SH} -o posix ./errors4.sub
+
+${THIS_SH} ./errors5.sub
# this must be last!
# in posix mode, a function name must be a valid identifier
--- /dev/null
+# test effect of assigning to readonly vars on loops and non-interactive shells
+# fatal error when in posix mode
+var=foo
+readonly var
+for num in 1 2 3 4 5; do
+ if [ $num -eq 3 ]; then
+ var=bar
+ fi
+ echo $num
+done
+echo after readonly assignment
+
+# non-numeric arguments to break are fatal errors for all non-interactive shells
+for f in 1 2 3 4 5
+do
+ break x
+done
+echo after loop
--- /dev/null
+array[1]=one
+array[2]=two
+
+set -u
+
+( echo ${#array} )
+( echo ${array} )
+
+set -- 1 2 3
+( echo ${#7} )
+( echo ${7} )
argv[1] = <^?>
argv[1] = <^A>
argv[1] = <^?>
+argv[1] = <bar>
+argv[1] = <^A>
+argv[1] = <^?>
+argv[1] = <^A>
+argv[1] = <^?>
argv[1] = <abcdefgh>
argv[1] = <abcdefgh>
argv[1] = <abcdefgh>
expect "<^?>"
recho `echo \7f`
+expect "bar"
+recho ${foo:-"`echo bar`"}
+expect "<^A>"
+recho ${foo:-"`echo \ 1`"}
+expect "<^?>"
+recho ${foo:-"`echo \7f`"}
+
+expect "<^A>"
+recho "`echo \ 1`"
+expect "<^?>"
+recho "`echo \7f`"
+
# Test null strings without variable expansion
expect "<abcdefgh>"
recho abcd""efgh
ok 1
ok 2
ok 3
+echo shopt a
+shopt a
+echo a b c d 2 > /dev/null
# Bash-2.01[.1] fails this test -- it attempts history expansion after the
# history_comment_char
echo ok 3 # !1200
+
+shopt a b c d 2>/dev/null
+echo !shopt-1
+
+echo !shopt*
./nameref.tests: line 106: foo: readonly variable
./nameref.tests: line 103: foo: readonly variable
one
+abxde
+abxde
one
bar
./nameref8.sub: line 54: warning: x: circular name reference
./nameref8.sub: line 55: warning: x: circular name reference
x =
+idx2
+idX2
+idx2
+idX2
assignvar foo two three four
echo $foo
+var=abcde
+x=var
+declare -n v=var
+# these two should display the same
+echo ${!x//c/x}
+echo ${v//c/x}
+
${THIS_SH} ./nameref1.sub
${THIS_SH} ./nameref2.sub
${THIS_SH} ./nameref3.sub
${THIS_SH} ./nameref6.sub
${THIS_SH} ./nameref7.sub
${THIS_SH} ./nameref8.sub
+${THIS_SH} ./nameref9.sub
--- /dev/null
+arr=( idx1 idx2 )
+i='arr[1]'
+echo ${!i}
+echo ${!i/x/X}
+
+typeset -n f='arr[1]'
+echo ${f}
+echo ${f/x/X}
argv[1] = <op>
argv[1] = <abcdefghijklmnop>
argv[1] = <abcdefghijklmnop>
-./new-exp.tests: line 172: ABX: unbound variable
-./new-exp.tests: line 176: $6: cannot assign in this way
+argv[1] = <a>
+argv[2] = <b>
+argv[3] = <c>
+argv[4] = <d>
+argv[1] = <a>
+argv[2] = <b c>
+argv[3] = <d>
+./new-exp.tests: line 180: ABX: unbound variable
+./new-exp.tests: line 184: $6: cannot assign in this way
argv[1] = <xxcde>
argv[1] = <axxde>
argv[1] = <abxyz>
./new-exp2.sub: line 42: 1111111111111111111111: command not found
argv[1] = <6>
-./new-exp.tests: line 277: ${#:}: bad substitution
+./new-exp.tests: line 285: ${#:}: bad substitution
argv[1] = <'>
argv[1] = <">
argv[1] = <"hello">
argv[7] = <x>
argv[8] = <y>
argv[9] = <z>
-./new-exp.tests: line 482: $9: unbound variable
-./new-exp.tests: line 483: 9: unbound variable
-./new-exp.tests: line 484: UNSET: unbound variable
-./new-exp.tests: line 485: UNSET: unbound variable
-./new-exp.tests: line 486: UNSET: unbound variable
-./new-exp.tests: line 487: UNSET: unbound variable
-./new-exp.tests: line 488: UNSET: unbound variable
+./new-exp.tests: line 490: $9: unbound variable
+./new-exp.tests: line 491: 9: unbound variable
+./new-exp.tests: line 492: UNSET: unbound variable
+./new-exp.tests: line 493: UNSET: unbound variable
+./new-exp.tests: line 494: UNSET: unbound variable
+./new-exp.tests: line 495: UNSET: unbound variable
+./new-exp.tests: line 496: UNSET: unbound variable
argv[1] = <5>
argv[1] = <#>
argv[1] = <#>
Case06---1---A B C::---
Case07---3---A:B:C---
Case08---3---A:B:C---
-./new-exp.tests: line 508: ${$(($#-1))}: bad substitution
+./new-exp.tests: line 516: ${$(($#-1))}: bad substitution
argv[1] = <a>
argv[2] = <b>
argv[3] = <c>
argv[1] = <a>
argv[2] = <b>
argv[1] = <>
-./new-exp.tests: line 527: $(($# - 2)): substring expression < 0
+./new-exp.tests: line 535: $(($# - 2)): substring expression < 0
argv[1] = <bin>
argv[2] = <bin>
argv[3] = <ucb>
a5b
argv[1] = </>
argv[1] = </>
-./new-exp.tests: line 587: ABXD: parameter unset
+./new-exp.tests: line 595: ABXD: parameter unset
expect nothing
recho ${!1-$z}
+set -- a 'b c' d
+unset foo
+foo=@
+expect '<a> <b> <c> <d>'
+recho ${!foo}
+expect '<a> <b c> <d>'
+recho "${!foo}"
+
set -u
expect $0: ABX: unbound variable
( recho ${ABX} )
declare -ir myvar="1"
declare -rx tempvar1='foo'
declare -rx tempvar2='qux'
+./varenv7.sub: line 44: local: var: readonly variable
+inside: outside
+outside: outside
a=z
a=b
a=z
tempvar2=bar declare -r tempvar2=qux
echo ${tempvar2@A}
+
+unset foo
+readonly var=outside
+
+func()
+{
+ local var=inside
+ echo "inside: $var"
+}
+
+func
+echo outside: $var
+
./vredir6.sub: redirection error: cannot duplicate fd: Invalid argument
./vredir6.sub: line 13: /dev/null: Invalid argument
unset
+12 10
+a
+a
+swizzle is a function
+swizzle ()
+{
+ exec {fd[0]}<&0;
+ exec {fd[1]}>&1;
+ exec {stdin}<&${fd[0]}-;
+ exec {stdout}>&${fd[1]}-
+}
${THIS_SH} ./vredir6.sub
+${THIS_SH} ./vredir7.sub
+
exit 0
--- /dev/null
+swizzle()
+{
+exec {fd[0]}<&0
+exec {fd[1]}>&1
+
+exec {stdin}<&${fd[0]}-
+exec {stdout}>&${fd[1]}-
+}
+
+swizzle
+
+echo $stdin $stdout
+
+read line <&$stdin <<EOF
+a
+EOF
+
+echo $line
+echo $line >&$stdout
+
+type swizzle
+
+exit 0
#include "sig.h"
#include "quit.h"
#include "error.h" /* for internal_warning */
+#include "ocache.h"
/* Structure describing a saved variable and the value to restore it to. */
typedef struct {
} sv;
} UNWIND_ELT;
-
static void without_interrupts __P((VFunction *, char *, char *));
static void unwind_frame_discard_internal __P((char *, char *));
static void unwind_frame_run_internal __P((char *, char *));
static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
+/* Allocating from a cache of unwind-protect elements */
+#define UWCACHESIZE 128
+
+sh_obj_cache_t uwcache = {0, 0, 0};
+
+#if 0
#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
#define uwpfree(elt) free(elt)
+#else
+#define uwpalloc(elt) ocache_alloc (uwcache, UNWIND_ELT, elt)
+#define uwpfree(elt) ocache_free (uwcache, UNWIND_ELT, elt)
+#endif
+
+void
+uwp_init ()
+{
+ ocache_create (uwcache, UNWIND_ELT, UWCACHESIZE);
+}
/* Run a function without interrupts. This relies on the fact that the
FUNCTION cannot change the value of interrupt_immediately. (I.e., does
#if !defined (_UNWIND_PROT_H)
#define _UNWIND_PROT_H
+extern void uwp_init __P((void));
+
/* Run a function without interrupts. */
extern void begin_unwind_frame __P((char *));
extern void discard_unwind_frame __P((char *));