From 4c4d2fbfda13ca52d65390c2af4e31927a23b00e Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Sat, 3 Dec 2011 22:46:45 -0500 Subject: [PATCH] commit bash-20060330 snapshot --- CWRU/CWRU.chlog | 27 ++ CWRU/CWRU.chlog~ | 27 +- arrayfunc.c | 2 +- execute_cmd.c | 4 +- execute_cmd.c~ | 1 + lib/readline/histfile.c | 6 +- lib/readline/histfile.c~ | 542 +++++++++++++++++++++++++++++++++++++++ lib/sh/strtrans.c | 14 +- lib/sh/strtrans.c~ | 284 ++++++++++++++++++++ make_cmd.c | 4 - subst.c | 28 +- subst.h | 2 +- tests/RUN-ONE-TEST | 2 +- tests/array.right | 5 + tests/array4.sub | 10 + tests/array4.sub~ | 30 +++ tests/braces.right | 2 + tests/braces.tests | 5 + tests/braces.tests~ | 70 +++++ 19 files changed, 1029 insertions(+), 36 deletions(-) create mode 100644 lib/readline/histfile.c~ create mode 100644 lib/sh/strtrans.c~ create mode 100644 tests/array4.sub~ create mode 100644 tests/braces.tests~ diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 096db69e1..a6254a2e9 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13214,3 +13214,30 @@ bashline.c - 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 diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index 99e706897..f3dc5ece9 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -13209,8 +13209,29 @@ parse.y, eval.c, input.h ---- 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 - 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 + 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 diff --git a/arrayfunc.c b/arrayfunc.c index 1353b2d94..6073a3436 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -590,7 +590,7 @@ array_expand_index (s, len) 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); diff --git a/execute_cmd.c b/execute_cmd.c index 57e9d1136..e52905d54 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -214,6 +214,8 @@ static int special_builtin_failed; 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; @@ -665,7 +667,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, 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); diff --git a/execute_cmd.c~ b/execute_cmd.c~ index 8b446b254..57e9d1136 100644 --- a/execute_cmd.c~ +++ b/execute_cmd.c~ @@ -359,6 +359,7 @@ execute_command (command) unlink_fifo_list (); #endif /* PROCESS_SUBSTITUTION */ + QUIT; return (result); } diff --git a/lib/readline/histfile.c b/lib/readline/histfile.c index 717bbee6f..2f051a325 100644 --- a/lib/readline/histfile.c +++ b/lib/readline/histfile.c @@ -256,7 +256,11 @@ read_history_range (filename, from, to) 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) { diff --git a/lib/readline/histfile.c~ b/lib/readline/histfile.c~ new file mode 100644 index 000000000..717bbee6f --- /dev/null +++ b/lib/readline/histfile.c~ @@ -0,0 +1,542 @@ +/* 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 +#endif + +#if defined (HAVE_CONFIG_H) +# include +#endif + +#include + +#include +#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H) +# include +#endif +#include "posixstat.h" +#include + +#if defined (HAVE_STDLIB_H) +# include +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#if defined (__EMX__) || defined (__CYGWIN__) +# undef HAVE_MMAP +#endif + +#ifdef HISTORY_USE_MMAP +# include + +# 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 +#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)); +} diff --git a/lib/sh/strtrans.c b/lib/sh/strtrans.c index acf9d69ba..3f33d41b1 100644 --- a/lib/sh/strtrans.c +++ b/lib/sh/strtrans.c @@ -81,8 +81,18 @@ ansicstr (string, len, flags, sawc, rlen) 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 diff --git a/lib/sh/strtrans.c~ b/lib/sh/strtrans.c~ new file mode 100644 index 000000000..db7b1ba89 --- /dev/null +++ b/lib/sh/strtrans.c~ @@ -0,0 +1,284 @@ +/* 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 + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include +#include +#include + +#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); + } +} diff --git a/make_cmd.c b/make_cmd.c index 4607933e5..df2001056 100644 --- a/make_cmd.c +++ b/make_cmd.c @@ -507,7 +507,6 @@ make_bare_simple_command () 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; @@ -533,10 +532,7 @@ make_simple_command (element, command) 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; diff --git a/subst.c b/subst.c index 7950eed40..44ea7de1d 100644 --- a/subst.c +++ b/subst.c @@ -2599,10 +2599,10 @@ expand_assignment_string_to_string (string, quoted) } 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) @@ -5278,11 +5278,7 @@ verify_substring_values (value, substr, vtype, e1p, e2p) 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) @@ -5327,11 +5323,7 @@ verify_substring_values (value, substr, vtype, e1p, e2p) { 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); @@ -6481,11 +6473,7 @@ param_expand (string, sindex, quoted, expanded_something, 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: @@ -6527,11 +6515,7 @@ comsub: 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; diff --git a/subst.h b/subst.h index b7c6682bf..e07055b74 100644 --- a/subst.h +++ b/subst.h @@ -152,7 +152,7 @@ extern char *expand_string_unsplit_to_string __P((char *, int)); 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 *)); diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 3efcf32d6..72ec06a2c 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -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 diff --git a/tests/array.right b/tests/array.right index 900e43e3b..9545aaa72 100644 --- a/tests/array.right +++ b/tests/array.right @@ -139,6 +139,11 @@ value = new1 new2 new3 7 8 9 8 11 8 11 +6 +nordholz +8 +8 +8 a b c d e f g for case if then else diff --git a/tests/array4.sub b/tests/array4.sub index 9d7e1afb0..474eaeddf 100644 --- a/tests/array4.sub +++ b/tests/array4.sub @@ -18,3 +18,13 @@ echo $a $b 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 ))]} diff --git a/tests/array4.sub~ b/tests/array4.sub~ new file mode 100644 index 000000000..f61d7a96c --- /dev/null +++ b/tests/array4.sub~ @@ -0,0 +1,30 @@ +# 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 ))]} diff --git a/tests/braces.right b/tests/braces.right index 3d7ef8e76..f00d39a76 100644 --- a/tests/braces.right +++ b/tests/braces.right @@ -41,3 +41,5 @@ f 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 diff --git a/tests/braces.tests b/tests/braces.tests index 3f57829f0..5a57f2844 100644 --- a/tests/braces.tests +++ b/tests/braces.tests @@ -68,3 +68,8 @@ echo 0{1..9} {10..20} # 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 diff --git a/tests/braces.tests~ b/tests/braces.tests~ new file mode 100644 index 000000000..3f57829f0 --- /dev/null +++ b/tests/braces.tests~ @@ -0,0 +1,70 @@ +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} -- 2.47.3