- command_word_completion_function returns what it's passed as a
possible match if it's the name of a directory in the current
directory (only non-absolute pathnames are so tested).
+
+ 3/27
+ ----
+subst.c
+ - expand_arith_string takes a new argument: quoted. Either 0 (outside
+ subst.c) or Q_DOUBLE_QUOTES (substitution functions); changed callers
+
+subst.h
+ - changed extern declaration for expand_arith_string
+
+arrayfunc.c
+ - changed call to expand_arith_string in array_expand_index
+
+ 3/31
+ ----
+lib/readline/histfile.c
+ - change read_history_range to allow windows-like \r\n line endings
+
+execute_cmd.c
+ - add new variable, line_number_for_err_trap, currently set but not
+ used
+
+ 4/2
+ ---
+lib/sh/strtrans.c
+ - add code to echo -e and echo with xpg_echo enabled to require
+ a leading 0 to specify octal constants
----
bashline.c
- command_word_completion_function keeps track of when it's searching
- $PATH and doesn't return directory names as matches in that case
+ $PATH and doesn't return directory names as matches in that case.
+ Problem reported by Pascal Terjan <pterjan@mandriva.com>
- command_word_completion_function returns what it's passed as a
possible match if it's the name of a directory in the current
- directory (only non-absolute pathnames are so tested). Problem
- reported by Pascal Terjan <pterjan@mandriva.com>
+ directory (only non-absolute pathnames are so tested).
+
+ 3/27
+ ----
+subst.c
+ - expand_arith_string takes a new argument: quoted. Either 0 (outside
+ subst.c) or Q_DOUBLE_QUOTES (substitution functions); changed callers
+
+subst.h
+ - changed extern declaration for expand_arith_string
+
+arrayfunc.c
+ - changed call to expand_arith_string in array_expand_index
+
+ 3/31
+ ----
+lib/readline/histfile.c
+ - change read_history_range to allow windows-like \r\n line endings
+
+execute_cmd.c
+ - add new variable, line_number_for_err_trap, currently set but not
+ used
exp = (char *)xmalloc (len);
strncpy (exp, s, len - 1);
exp[len - 1] = '\0';
- t = expand_arith_string (exp);
+ t = expand_arith_string (exp, 0);
this_command_name = (char *)NULL;
val = evalexp (t, &expok);
free (t);
report the correct line number. Kind of a hack. */
static int showing_function_line;
+static int line_number_for_err_trap;
+
/* For catching RETURN in a function. */
int return_catch_flag;
int return_catch_value;
if (command->flags & CMD_STDIN_REDIR)
command->value.Simple->flags |= CMD_STDIN_REDIR;
- line_number = command->value.Simple->line;
+ line_number_for_err_trap = line_number = command->value.Simple->line;
exec_result =
execute_simple_command (command->value.Simple, pipe_in, pipe_out,
asynchronous, fds_to_close);
unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
+ QUIT;
return (result);
}
for (line_end = line_start; line_end < bufend; line_end++)
if (*line_end == '\n')
{
- *line_end = '\0';
+ /* Change to allow Windows-like \r\n end of line delimiter. */
+ if (line_end > line_start && line_end[-1] == '\r')
+ line_end[-1] = '\0';
+ else
+ *line_end = '\0';
if (*line_start)
{
--- /dev/null
+/* histfile.c - functions to manipulate the history file. */
+
+/* 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 (__TANDEM)
+# include <floss.h>
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include "posixstat.h"
+#include <fcntl.h>
+
+#if defined (HAVE_STDLIB_H)
+# include <stdlib.h>
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (__EMX__) || defined (__CYGWIN__)
+# undef HAVE_MMAP
+#endif
+
+#ifdef HISTORY_USE_MMAP
+# include <sys/mman.h>
+
+# ifdef MAP_FILE
+# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
+# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
+# else
+# define MAP_RFLAGS MAP_PRIVATE
+# define MAP_WFLAGS MAP_SHARED
+# endif
+
+# ifndef MAP_FAILED
+# define MAP_FAILED ((void *)-1)
+# endif
+
+#endif /* HISTORY_USE_MMAP */
+
+/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
+ on win 95/98/nt), we want to open files with O_BINARY mode so that there
+ is no \n -> \r\n conversion performed. On other systems, we don't want to
+ mess around with O_BINARY at all, so we ensure that it's defined to 0. */
+#if defined (__EMX__) || defined (__CYGWIN__)
+# ifndef O_BINARY
+# define O_BINARY 0
+# endif
+#else /* !__EMX__ && !__CYGWIN__ */
+# undef O_BINARY
+# define O_BINARY 0
+#endif /* !__EMX__ && !__CYGWIN__ */
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#include "history.h"
+#include "histlib.h"
+
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* If non-zero, we write timestamps to the history file in history_do_write() */
+int history_write_timestamps = 0;
+
+/* Does S look like the beginning of a history timestamp entry? Placeholder
+ for more extensive tests. */
+#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char)
+
+/* Return the string that should be used in the place of this
+ filename. This only matters when you don't specify the
+ filename to read_history (), or write_history (). */
+static char *
+history_filename (filename)
+ const char *filename;
+{
+ char *return_val;
+ const char *home;
+ int home_len;
+
+ return_val = filename ? savestring (filename) : (char *)NULL;
+
+ if (return_val)
+ return (return_val);
+
+ home = sh_get_env_value ("HOME");
+
+ if (home == 0)
+ {
+ home = ".";
+ home_len = 1;
+ }
+ else
+ home_len = strlen (home);
+
+ return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
+ strcpy (return_val, home);
+ return_val[home_len] = '/';
+#if defined (__MSDOS__)
+ strcpy (return_val + home_len + 1, "_history");
+#else
+ strcpy (return_val + home_len + 1, ".history");
+#endif
+
+ return (return_val);
+}
+
+/* Add the contents of FILENAME to the history list, a line at a time.
+ If FILENAME is NULL, then read from ~/.history. Returns 0 if
+ successful, or errno if not. */
+int
+read_history (filename)
+ const char *filename;
+{
+ return (read_history_range (filename, 0, -1));
+}
+
+/* Read a range of lines from FILENAME, adding them to the history list.
+ Start reading at the FROM'th line and end at the TO'th. If FROM
+ is zero, start at the beginning. If TO is less than FROM, read
+ until the end of the file. If FILENAME is NULL, then read from
+ ~/.history. Returns 0 if successful, or errno if not. */
+int
+read_history_range (filename, from, to)
+ const char *filename;
+ int from, to;
+{
+ register char *line_start, *line_end, *p;
+ char *input, *buffer, *bufend, *last_ts;
+ int file, current_line, chars_read;
+ struct stat finfo;
+ size_t file_size;
+#if defined (EFBIG)
+ int overflow_errno = EFBIG;
+#elif defined (EOVERFLOW)
+ int overflow_errno = EOVERFLOW;
+#else
+ int overflow_errno = EIO;
+#endif
+
+ buffer = last_ts = (char *)NULL;
+ input = history_filename (filename);
+ file = open (input, O_RDONLY|O_BINARY, 0666);
+
+ if ((file < 0) || (fstat (file, &finfo) == -1))
+ goto error_and_exit;
+
+ file_size = (size_t)finfo.st_size;
+
+ /* check for overflow on very large files */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+
+#ifdef HISTORY_USE_MMAP
+ /* We map read/write and private so we can change newlines to NULs without
+ affecting the underlying object. */
+ buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
+ if ((void *)buffer == MAP_FAILED)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+ chars_read = file_size;
+#else
+ buffer = (char *)malloc (file_size + 1);
+ if (buffer == 0)
+ {
+ errno = overflow_errno;
+ goto error_and_exit;
+ }
+
+ chars_read = read (file, buffer, file_size);
+#endif
+ if (chars_read < 0)
+ {
+ error_and_exit:
+ if (errno != 0)
+ chars_read = errno;
+ else
+ chars_read = EIO;
+ if (file >= 0)
+ close (file);
+
+ FREE (input);
+#ifndef HISTORY_USE_MMAP
+ FREE (buffer);
+#endif
+
+ return (chars_read);
+ }
+
+ close (file);
+
+ /* Set TO to larger than end of file if negative. */
+ if (to < 0)
+ to = chars_read;
+
+ /* Start at beginning of file, work to end. */
+ bufend = buffer + chars_read;
+ current_line = 0;
+
+ /* Skip lines until we are at FROM. */
+ for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
+ if (*line_end == '\n')
+ {
+ p = line_end + 1;
+ /* If we see something we think is a timestamp, continue with this
+ line. We should check more extensively here... */
+ if (HIST_TIMESTAMP_START(p) == 0)
+ current_line++;
+ line_start = p;
+ }
+
+ /* If there are lines left to gobble, then gobble them now. */
+ for (line_end = line_start; line_end < bufend; line_end++)
+ if (*line_end == '\n')
+ {
+ *line_end = '\0';
+
+ if (*line_start)
+ {
+ if (HIST_TIMESTAMP_START(line_start) == 0)
+ {
+ add_history (line_start);
+ if (last_ts)
+ {
+ add_history_time (last_ts);
+ last_ts = NULL;
+ }
+ }
+ else
+ {
+ last_ts = line_start;
+ current_line--;
+ }
+ }
+
+ current_line++;
+
+ if (current_line >= to)
+ break;
+
+ line_start = line_end + 1;
+ }
+
+ FREE (input);
+#ifndef HISTORY_USE_MMAP
+ FREE (buffer);
+#else
+ munmap (buffer, file_size);
+#endif
+
+ return (0);
+}
+
+/* Truncate the history file FNAME, leaving only LINES trailing lines.
+ If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
+ on failure. */
+int
+history_truncate_file (fname, lines)
+ const char *fname;
+ int lines;
+{
+ char *buffer, *filename, *bp, *bp1; /* bp1 == bp+1 */
+ int file, chars_read, rv;
+ struct stat finfo;
+ size_t file_size;
+
+ buffer = (char *)NULL;
+ filename = history_filename (fname);
+ file = open (filename, O_RDONLY|O_BINARY, 0666);
+ rv = 0;
+
+ /* Don't try to truncate non-regular files. */
+ if (file == -1 || fstat (file, &finfo) == -1)
+ {
+ rv = errno;
+ if (file != -1)
+ close (file);
+ goto truncate_exit;
+ }
+
+ if (S_ISREG (finfo.st_mode) == 0)
+ {
+ close (file);
+#ifdef EFTYPE
+ rv = EFTYPE;
+#else
+ rv = EINVAL;
+#endif
+ goto truncate_exit;
+ }
+
+ file_size = (size_t)finfo.st_size;
+
+ /* check for overflow on very large files */
+ if (file_size != finfo.st_size || file_size + 1 < file_size)
+ {
+ close (file);
+#if defined (EFBIG)
+ rv = errno = EFBIG;
+#elif defined (EOVERFLOW)
+ rv = errno = EOVERFLOW;
+#else
+ rv = errno = EINVAL;
+#endif
+ goto truncate_exit;
+ }
+
+ buffer = (char *)malloc (file_size + 1);
+ if (buffer == 0)
+ {
+ close (file);
+ goto truncate_exit;
+ }
+
+ chars_read = read (file, buffer, file_size);
+ close (file);
+
+ if (chars_read <= 0)
+ {
+ rv = (chars_read < 0) ? errno : 0;
+ goto truncate_exit;
+ }
+
+ /* Count backwards from the end of buffer until we have passed
+ LINES lines. bp1 is set funny initially. But since bp[1] can't
+ be a comment character (since it's off the end) and *bp can't be
+ both a newline and the history comment character, it should be OK. */
+ for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
+ {
+ if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+ lines--;
+ bp1 = bp;
+ }
+
+ /* If this is the first line, then the file contains exactly the
+ number of lines we want to truncate to, so we don't need to do
+ anything. It's the first line if we don't find a newline between
+ the current value of i and 0. Otherwise, write from the start of
+ this line until the end of the buffer. */
+ for ( ; bp > buffer; bp--)
+ {
+ if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+ {
+ bp++;
+ break;
+ }
+ bp1 = bp;
+ }
+
+ /* Write only if there are more lines in the file than we want to
+ truncate to. */
+ if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
+ {
+ write (file, bp, chars_read - (bp - buffer));
+
+#if defined (__BEOS__)
+ /* BeOS ignores O_TRUNC. */
+ ftruncate (file, chars_read - (bp - buffer));
+#endif
+
+ close (file);
+ }
+
+ truncate_exit:
+
+ FREE (buffer);
+
+ free (filename);
+ return rv;
+}
+
+/* Workhorse function for writing history. Writes NELEMENT entries
+ from the history list to FILENAME. OVERWRITE is non-zero if you
+ wish to replace FILENAME with the entries. */
+static int
+history_do_write (filename, nelements, overwrite)
+ const char *filename;
+ int nelements, overwrite;
+{
+ register int i;
+ char *output;
+ int file, mode, rv;
+#ifdef HISTORY_USE_MMAP
+ size_t cursize;
+
+ mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
+#else
+ mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
+#endif
+ output = history_filename (filename);
+ rv = 0;
+
+ if ((file = open (output, mode, 0600)) == -1)
+ {
+ FREE (output);
+ return (errno);
+ }
+
+#ifdef HISTORY_USE_MMAP
+ cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
+#endif
+
+ if (nelements > history_length)
+ nelements = history_length;
+
+ /* Build a buffer of all the lines to write, and write them in one syscall.
+ Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
+ {
+ HIST_ENTRY **the_history; /* local */
+ register int j;
+ int buffer_size;
+ char *buffer;
+
+ the_history = history_list ();
+ /* Calculate the total number of bytes to write. */
+ for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
+#if 0
+ buffer_size += 2 + HISTENT_BYTES (the_history[i]);
+#else
+ {
+ if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+ buffer_size += strlen (the_history[i]->timestamp) + 1;
+ buffer_size += strlen (the_history[i]->line) + 1;
+ }
+#endif
+
+ /* Allocate the buffer, and fill it. */
+#ifdef HISTORY_USE_MMAP
+ if (ftruncate (file, buffer_size+cursize) == -1)
+ goto mmap_error;
+ buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
+ if ((void *)buffer == MAP_FAILED)
+ {
+mmap_error:
+ rv = errno;
+ FREE (output);
+ close (file);
+ return rv;
+ }
+#else
+ buffer = (char *)malloc (buffer_size);
+ if (buffer == 0)
+ {
+ rv = errno;
+ FREE (output);
+ close (file);
+ return rv;
+ }
+#endif
+
+ for (j = 0, i = history_length - nelements; i < history_length; i++)
+ {
+ if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+ {
+ strcpy (buffer + j, the_history[i]->timestamp);
+ j += strlen (the_history[i]->timestamp);
+ buffer[j++] = '\n';
+ }
+ strcpy (buffer + j, the_history[i]->line);
+ j += strlen (the_history[i]->line);
+ buffer[j++] = '\n';
+ }
+
+#ifdef HISTORY_USE_MMAP
+ if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
+ rv = errno;
+#else
+ if (write (file, buffer, buffer_size) < 0)
+ rv = errno;
+ free (buffer);
+#endif
+ }
+
+ close (file);
+
+ FREE (output);
+
+ return (rv);
+}
+
+/* Append NELEMENT entries to FILENAME. The entries appended are from
+ the end of the list minus NELEMENTs up to the end of the list. */
+int
+append_history (nelements, filename)
+ int nelements;
+ const char *filename;
+{
+ return (history_do_write (filename, nelements, HISTORY_APPEND));
+}
+
+/* Overwrite FILENAME with the current history. If FILENAME is NULL,
+ then write the history list to ~/.history. Values returned
+ are as in read_history ().*/
+int
+write_history (filename)
+ const char *filename;
+{
+ return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
+}
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7':
+#if 1
+ if (flags & 1)
+ {
+ *r++ = '\\';
+ break;
+ }
+ /*FALLTHROUGH*/
+#endif
+ case '0':
/* If (FLAGS & 1), we're translating a string for echo -e (or
the equivalent xpg_echo option), so we obey the SUSv3/
POSIX-2001 requirement and accept 0-3 octal digits after
--- /dev/null
+/* strtrans.c - Translate and untranslate strings with ANSI-C escape
+ sequences. */
+
+/* Copyright (C) 2000
+ Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <bashansi.h>
+#include <stdio.h>
+#include <chartypes.h>
+
+#include "shell.h"
+
+#ifdef ESC
+#undef ESC
+#endif
+#define ESC '\033' /* ASCII */
+
+/* Convert STRING by expanding the escape sequences specified by the
+ ANSI C standard. If SAWC is non-null, recognize `\c' and use that
+ as a string terminator. If we see \c, set *SAWC to 1 before
+ returning. LEN is the length of STRING. If (FLAGS&1) is non-zero,
+ that we're translating a string for `echo -e', and therefore should not
+ treat a single quote as a character that may be escaped with a backslash.
+ If (FLAGS&2) is non-zero, we're expanding for the parser and want to
+ quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
+ to remove the backslash before any unrecognized escape sequence. */
+char *
+ansicstr (string, len, flags, sawc, rlen)
+ char *string;
+ int len, flags, *sawc, *rlen;
+{
+ int c, temp;
+ char *ret, *r, *s;
+
+ if (string == 0 || *string == '\0')
+ return ((char *)NULL);
+
+ ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
+ for (r = ret, s = string; s && *s; )
+ {
+ c = *s++;
+ if (c != '\\' || *s == '\0')
+ *r++ = c;
+ else
+ {
+ switch (c = *s++)
+ {
+#if defined (__STDC__)
+ case 'a': c = '\a'; break;
+ case 'v': c = '\v'; break;
+#else
+ case 'a': c = '\007'; break;
+ case 'v': c = (int) 0x0B; break;
+#endif
+ case 'b': c = '\b'; break;
+ case 'e': case 'E': /* ESC -- non-ANSI */
+ c = ESC; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7':
+#if 1
+ if (flags & 1)
+ {
+ *r++ = '\\';
+ break;
+ }
+#endif
+ /*FALLTHROUGH*/
+ case '0':
+ /* If (FLAGS & 1), we're translating a string for echo -e (or
+ the equivalent xpg_echo option), so we obey the SUSv3/
+ POSIX-2001 requirement and accept 0-3 octal digits after
+ a leading `0'. */
+ temp = 2 + ((flags & 1) && (c == '0'));
+ for (c -= '0'; ISOCTAL (*s) && temp--; s++)
+ c = (c * 8) + OCTVALUE (*s);
+ c &= 0xFF;
+ break;
+ case 'x': /* Hex digit -- non-ANSI */
+ if ((flags & 2) && *s == '{')
+ {
+ flags |= 16; /* internal flag value */
+ s++;
+ }
+ /* Consume at least two hex characters */
+ for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
+ c = (c * 16) + HEXVALUE (*s);
+ /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
+ until a non-xdigit or `}', so potentially more than two
+ chars are consumed. */
+ if (flags & 16)
+ {
+ for ( ; ISXDIGIT ((unsigned char)*s); s++)
+ c = (c * 16) + HEXVALUE (*s);
+ flags &= ~16;
+ if (*s == '}')
+ s++;
+ }
+ /* \x followed by non-hex digits is passed through unchanged */
+ else if (temp == 2)
+ {
+ *r++ = '\\';
+ c = 'x';
+ }
+ c &= 0xFF;
+ break;
+ case '\\':
+ break;
+ case '\'': case '"': case '?':
+ if (flags & 1)
+ *r++ = '\\';
+ break;
+ case 'c':
+ if (sawc)
+ {
+ *sawc = 1;
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+ }
+ else if ((flags & 1) == 0 && (c = *s))
+ {
+ s++;
+ c = TOCTRL(c);
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ if ((flags & 4) == 0)
+ *r++ = '\\';
+ break;
+ }
+ if ((flags & 2) && (c == CTLESC || c == CTLNUL))
+ *r++ = CTLESC;
+ *r++ = c;
+ }
+ }
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+}
+
+/* Take a string STR, possibly containing non-printing characters, and turn it
+ into a $'...' ANSI-C style quoted string. Returns a new string. */
+char *
+ansic_quote (str, flags, rlen)
+ char *str;
+ int flags, *rlen;
+{
+ char *r, *ret, *s;
+ int l, rsize, t;
+ unsigned char c;
+
+ if (str == 0 || *str == 0)
+ return ((char *)0);
+
+ l = strlen (str);
+ rsize = 4 * l + 4;
+ r = ret = (char *)xmalloc (rsize);
+
+ *r++ = '$';
+ *r++ = '\'';
+
+ for (s = str, l = 0; *s; s++)
+ {
+ c = *s;
+ l = 1; /* 1 == add backslash; 0 == no backslash */
+ switch (c)
+ {
+ case ESC: c = 'E'; break;
+#ifdef __STDC__
+ case '\a': c = 'a'; break;
+ case '\v': c = 'v'; break;
+#else
+ case '\007': c = 'a'; break;
+ case 0x0b: c = 'v'; break;
+#endif
+
+ case '\b': c = 'b'; break;
+ case '\f': c = 'f'; break;
+ case '\n': c = 'n'; break;
+ case '\r': c = 'r'; break;
+ case '\t': c = 't'; break;
+ case '\\':
+ case '\'':
+ break;
+ default:
+ if (ISPRINT (c) == 0)
+ {
+ *r++ = '\\';
+ *r++ = TOCHAR ((c >> 6) & 07);
+ *r++ = TOCHAR ((c >> 3) & 07);
+ *r++ = TOCHAR (c & 07);
+ continue;
+ }
+ l = 0;
+ break;
+ }
+ if (l)
+ *r++ = '\\';
+ *r++ = c;
+ }
+
+ *r++ = '\'';
+ *r = '\0';
+ if (rlen)
+ *rlen = r - ret;
+ return ret;
+}
+
+/* return 1 if we need to quote with $'...' because of non-printing chars. */
+int
+ansic_shouldquote (string)
+ const char *string;
+{
+ const char *s;
+ unsigned char c;
+
+ if (string == 0)
+ return 0;
+
+ for (s = string; c = *s; s++)
+ if (ISPRINT (c) == 0)
+ return 1;
+
+ return 0;
+}
+
+/* $'...' ANSI-C expand the portion of STRING between START and END and
+ return the result. The result cannot be longer than the input string. */
+char *
+ansiexpand (string, start, end, lenp)
+ char *string;
+ int start, end, *lenp;
+{
+ char *temp, *t;
+ int len, tlen;
+
+ temp = (char *)xmalloc (end - start + 1);
+ for (tlen = 0, len = start; len < end; )
+ temp[tlen++] = string[len++];
+ temp[tlen] = '\0';
+
+ if (*temp)
+ {
+ t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
+ free (temp);
+ return (t);
+ }
+ else
+ {
+ if (lenp)
+ *lenp = 0;
+ return (temp);
+ }
+}
command->value.Simple = temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
temp->flags = 0;
-itrace("make_bare_simple_command: line_number = %d", line_number);
temp->line = line_number;
temp->words = (WORD_LIST *)NULL;
temp->redirects = (REDIRECT *)NULL;
command = make_bare_simple_command ();
if (element.word)
-{
- itrace("make_simple_command: adding %s", element.word->word);
command->value.Simple->words = make_word_list (element.word, command->value.Simple->words);
-}
else if (element.redirect)
{
REDIRECT *r = element.redirect;
}
char *
-expand_arith_string (string)
+expand_arith_string (string, quoted)
char *string;
{
- return (expand_string_if_necessary (string, Q_DOUBLE_QUOTES, expand_string));
+ return (expand_string_if_necessary (string, quoted, expand_string));
}
#if defined (COND_COMMAND)
else
t = (char *)0;
-#if 0
- temp1 = expand_string_if_necessary (substr, Q_DOUBLE_QUOTES, expand_string);
-#else
- temp1 = expand_arith_string (substr);
-#endif
+ temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES);
*e1p = evalexp (temp1, &expok);
free (temp1);
if (expok == 0)
{
t++;
temp2 = savestring (t);
-#if 0
- temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
-#else
- temp1 = expand_arith_string (temp2);
-#endif
+ temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES);
free (temp2);
t[-1] = ':';
*e2p = evalexp (temp1, &expok);
temp2[t_index] = '\0';
/* Expand variables found inside the expression. */
-#if 0
- temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
-#else
- temp1 = expand_arith_string (temp2);
-#endif
+ temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES);
free (temp2);
arithsub:
zindex = t_index;
/* Do initial variable expansion. */
-#if 0
- temp1 = expand_string_if_necessary (temp, Q_DOUBLE_QUOTES, expand_string);
-#else
- temp1 = expand_arith_string (temp);
-#endif
+ temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES);
goto arithsub;
extern char *expand_assignment_string_to_string __P((char *, int));
/* Expand an arithmetic expression string */
-extern char *expand_arith_string __P((char *));
+extern char *expand_arith_string __P((char *, int));
/* De-quoted quoted characters in STRING. */
extern char *dequote_string __P((char *));
-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 8 9
8 11
8 11
+6
+nordholz
+8
+8
+8
a b c d e f g
for case if then else
typeset -i a b
a=(5+3) b=(4+7)
echo $a $b
+
+let a=(4*3)/2
+echo $a
+
+LNAME=nordholz
+echo ${LNAME}
+echo ${#LNAME}
+
+echo ${#LNAME[$(( 0 ))]}
+echo ${#LNAME[$(( 0+0 ))]}
--- /dev/null
+# compound assignment parsing problems in bash-3.1-release
+func()
+{
+ local -a x=() y=()
+}
+
+a=() b=()
+eval foo=()
+eval foo=() bar=() qux=( "bash" )
+
+foo=( "bash" )
+eval foo=( "bash" )
+eval bar=( "bash" ) bax=( "bash" )
+
+let a=(5 + 3) b=(4 + 7)
+echo $a $b
+
+typeset -i a b
+a=(5+3) b=(4+7)
+echo $a $b
+
+a=(4*3)/2
+echo $a
+
+LNAME=nordholz
+echo ${LNAME}
+echo ${#LNAME}
+
+echo ${#LNAME[$(( 0 ))]}
+echo ${#LNAME[$(( 0+0 ))]}
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
-20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0
+a-{bd}-c a-{be}-c
+a-{bdef-g-c a-{bdef-i-c
# do negative numbers work?
echo {-1..-10}
echo {-20..0}
+
+# weirdly-formed brace expansions -- fixed in post-bash-3.1
+echo a-{b{d,e}}-c
+
+echo a-{bdef-{g,i}-c
--- /dev/null
+echo ff{c,b,a}
+echo f{d,e,f}g
+echo {l,n,m}xyz
+echo {abc\,def}
+echo {abc}
+
+echo \{a,b,c,d,e}
+echo {x,y,\{a,b,c}}
+echo {x\,y,\{abc\},trie}
+
+echo /usr/{ucb/{ex,edit},lib/{ex,how_ex}}
+
+echo XXXX\{`echo a b c | tr ' ' ','`\}
+eval echo XXXX\{`echo a b c | tr ' ' ','`\}
+
+echo {}
+echo { }
+echo }
+echo {
+echo abcd{efgh
+
+echo foo {1,2} bar
+echo `zecho foo {1,2} bar`
+echo $(zecho foo {1,2} bar)
+
+var=baz
+varx=vx
+vary=vy
+
+echo foo{bar,${var}.}
+echo foo{bar,${var}}
+
+echo "${var}"{x,y}
+echo $var{x,y}
+echo ${var}{x,y}
+
+unset var varx vary
+
+# new sequence brace operators
+echo {1..10}
+
+# this doesn't work yet
+echo {0..10,braces}
+# but this does
+echo {{0..10},braces}
+echo x{{0..10},braces}y
+
+echo {3..3}
+echo x{3..3}y
+echo {10..1}
+echo {10..1}y
+echo x{10..1}y
+
+echo {a..f}
+echo {f..a}
+
+echo {a..A}
+echo {A..a}
+
+echo {f..f}
+
+# mixes are incorrectly-formed brace expansions
+echo {1..f}
+echo {f..1}
+
+echo 0{1..9} {10..20}
+
+# do negative numbers work?
+echo {-1..-10}
+echo {-20..0}