mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-23 05:47:59 +02:00
commit bash-20060330 snapshot
This commit is contained in:
@@ -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
|
||||
|
||||
+24
-3
@@ -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 <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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
@@ -359,6 +359,7 @@ execute_command (command)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
QUIT;
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 <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));
|
||||
}
|
||||
+12
-2
@@ -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
|
||||
|
||||
@@ -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 <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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 *));
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ))]}
|
||||
|
||||
@@ -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 ))]}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
Reference in New Issue
Block a user