mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-29 16:39:53 +02:00
commit bash-20040301 snapshot
This commit is contained in:
@@ -9202,3 +9202,27 @@ bashline.c
|
||||
- fix conditional binding of emacs-mode M-~ -- there is a default
|
||||
binding for it (rl_tilde_expand), so a straight call to
|
||||
rl_bind_key_if_unbound_in_map doesn't do the right thing
|
||||
|
||||
2/27
|
||||
----
|
||||
[bash-3.0-beta1 released]
|
||||
|
||||
2/29
|
||||
----
|
||||
subst.c
|
||||
- fixed expansion so referencing $a, when a is an array variable
|
||||
without an element assigned to index 0, exits the shell when
|
||||
`-u' is enabled
|
||||
|
||||
expr.c
|
||||
- make the exponentiation operator (**) associative, so things like
|
||||
2**3**4 work right (change `if' to `while')
|
||||
|
||||
3/2
|
||||
---
|
||||
lib/sh/strftime.c
|
||||
- SCO Unix 3.2, like Solaris, requires that the system's `timezone'
|
||||
variable be declared as long
|
||||
|
||||
lib/readline/{bind,histfile,input,parens}.c
|
||||
- changes for Tandem (including `floss.h' (?))
|
||||
|
||||
+77
-4
@@ -9144,9 +9144,82 @@ lib/glob/strmatch.c
|
||||
strmatch()
|
||||
|
||||
subst.c
|
||||
- old match_pattern is now internal_match_pattern
|
||||
- match_pattern now either calls internal_match_pattern or converts
|
||||
mbstrings to wide chars and calls internal_match_wpattern
|
||||
- internal_match_pattern reverted to old non-multibyte code
|
||||
- old match_pattern is now match_upattern
|
||||
- match_pattern now either calls match_upattern or converts
|
||||
mbstrings to wide chars and calls match_wpattern
|
||||
- match_upattern reverted to old non-multibyte code
|
||||
- new function: match_pattern_wchar, wide character version of
|
||||
match_pattern_char
|
||||
|
||||
2/1
|
||||
---
|
||||
subst.c
|
||||
- old remove_pattern is now remove_upattern
|
||||
- remove_upattern reverted to old non-multibyte code (pre-Waugh patch)
|
||||
- new multibyte version of remove_pattern: remove_wpattern
|
||||
- remove_pattern now calls either remove_upattern or converts a
|
||||
multibyte string to a wide character string and calls
|
||||
remove_wpattern
|
||||
- new function, wcsdup, wide-character version of strdup(3)
|
||||
|
||||
2/4
|
||||
---
|
||||
print_cmd.c
|
||||
- temporarily translate a >&filename redirection from
|
||||
r_duplicating_output_word to r_err_and_out (as the expansion code
|
||||
in redir.c does) so it prints without a leading `1' (file
|
||||
descriptor)
|
||||
|
||||
2/5
|
||||
---
|
||||
aclocal.m4
|
||||
- add a check for wcsdup to BASH_CHECK_MULTIBYTE
|
||||
|
||||
config.h.in
|
||||
- add HAVE_WCSDUP define
|
||||
|
||||
2/9
|
||||
---
|
||||
builtins/shift.def
|
||||
- fix a call to sh_erange that possibly dereferences a NULL pointer
|
||||
|
||||
2/12
|
||||
----
|
||||
general.c
|
||||
- start at a general set of file property checking functions:
|
||||
file_isdir(), file_iswdir() (is writable directory)
|
||||
|
||||
general.h
|
||||
- extern declarations for new functions
|
||||
|
||||
lib/sh/tmpfile.c
|
||||
- use file_iswdir() to make sure the temporary directory used for
|
||||
here documents and other temp files is writable in get_sys_tmpdir()
|
||||
|
||||
2/17
|
||||
----
|
||||
bashline.c
|
||||
- fix conditional binding of emacs-mode M-~ -- there is a default
|
||||
binding for it (rl_tilde_expand), so a straight call to
|
||||
rl_bind_key_if_unbound_in_map doesn't do the right thing
|
||||
|
||||
2/27
|
||||
----
|
||||
[bash-3.0-beta1 released]
|
||||
|
||||
2/29
|
||||
----
|
||||
subst.c
|
||||
- fixed expansion so referencing $a, when a is an array variable
|
||||
without an element assigned to index 0, exits the shell when
|
||||
`-u' is enabled
|
||||
|
||||
expr.c
|
||||
- make the exponentiation operator (**) associative, so things like
|
||||
2**3**4 work right (change `if' to `while')
|
||||
|
||||
3/2
|
||||
---
|
||||
lib/sh/strftime.c
|
||||
- SCO Unix 3.2, like Solaris, requires that the system's `timezone'
|
||||
variable be declared as long
|
||||
|
||||
@@ -283,7 +283,12 @@ does not provide the necessary support.
|
||||
builtins and functions to be timed.
|
||||
|
||||
`--enable-cond-command'
|
||||
Include support for the `[[' conditional command (*note
|
||||
Include support for the `[[' conditional command. (*note
|
||||
Conditional Constructs::).
|
||||
|
||||
`--enable-cond-regexp'
|
||||
Include support for matching POSIX regular expressions using the
|
||||
`=~' binary operator in the `[[' conditional command. (*note
|
||||
Conditional Constructs::).
|
||||
|
||||
`--enable-directory-stack'
|
||||
|
||||
@@ -128,6 +128,15 @@ ll. The error message printed when bash cannot open a shell script supplied
|
||||
as argument 1 now includes the name of the shell, to better identify
|
||||
the error as coming from bash.
|
||||
|
||||
mm. A bug that caused here documents to not work if the directory the shell
|
||||
used for the temporary files was not writable has been fixed.
|
||||
|
||||
nn. The parameter pattern removal and substitution expansions are now much
|
||||
faster and more efficient when using multibyte characters.
|
||||
|
||||
oo. Fixed a bug in the `shift' builtin that could cause core dumps when
|
||||
reporting an out-of-range argument.
|
||||
|
||||
2. New Features in Readline
|
||||
|
||||
a. History expansion has a new `a' modifier equivalent to the `g' modifier
|
||||
|
||||
+1
-1
@@ -286,4 +286,4 @@ rbash: bashref.texi
|
||||
cmp -s RBASH ../RBASH || mv RBASH ../RBASH
|
||||
$(RM) RBASH
|
||||
|
||||
xdist: inst posix rbash
|
||||
xdist: pdf inst posix rbash
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -42,6 +42,7 @@
|
||||
"||"
|
||||
"expr ? expr : expr"
|
||||
"=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="
|
||||
, [comma]
|
||||
|
||||
(Note that most of these operators have special meaning to bash, and an
|
||||
entire expression should be quoted, e.g. "a=$a+1" or "a=a+1" to ensure
|
||||
@@ -765,7 +766,7 @@ exppower ()
|
||||
register intmax_t val1, val2, c;
|
||||
|
||||
val1 = exp1 ();
|
||||
if (curtok == POWER)
|
||||
while (curtok == POWER)
|
||||
{
|
||||
readtok ();
|
||||
val2 = exp1 ();
|
||||
|
||||
@@ -19,8 +19,13 @@
|
||||
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. */
|
||||
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,13 @@
|
||||
/* 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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#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));
|
||||
}
|
||||
@@ -21,6 +21,10 @@
|
||||
59 Temple Place, Suite 330, Boston, MA 02111 USA. */
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
/* input.c -- character input functions for readline. */
|
||||
|
||||
/* Copyright (C) 1994 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Readline Library, a library for
|
||||
reading lines of text with interactive input and history editing.
|
||||
|
||||
The GNU Readline 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 GNU Readline 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. */
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#if defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif /* HAVE_SYS_FILE_H */
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
|
||||
#if defined (HAVE_STDLIB_H)
|
||||
# include <stdlib.h>
|
||||
#else
|
||||
# include "ansi_stdlib.h"
|
||||
#endif /* HAVE_STDLIB_H */
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
# if !defined (HAVE_SYS_SELECT_H) || !defined (M_UNIX)
|
||||
# include <sys/time.h>
|
||||
# endif
|
||||
#endif /* HAVE_SELECT */
|
||||
#if defined (HAVE_SYS_SELECT_H)
|
||||
# include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#if defined (FIONREAD_IN_SYS_IOCTL)
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
/* System-specific feature definitions and include files. */
|
||||
#include "rldefs.h"
|
||||
#include "rlmbutil.h"
|
||||
|
||||
/* Some standard library routines. */
|
||||
#include "readline.h"
|
||||
|
||||
#include "rlprivate.h"
|
||||
#include "rlshell.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* What kind of non-blocking I/O do we have? */
|
||||
#if !defined (O_NDELAY) && defined (O_NONBLOCK)
|
||||
# define O_NDELAY O_NONBLOCK /* Posix style */
|
||||
#endif
|
||||
|
||||
/* Non-null means it is a pointer to a function to run while waiting for
|
||||
character input. */
|
||||
rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
rl_getc_func_t *rl_getc_function = rl_getc;
|
||||
|
||||
static int _keyboard_input_timeout = 100000; /* 0.1 seconds; it's in usec */
|
||||
|
||||
static int ibuffer_space PARAMS((void));
|
||||
static int rl_get_char PARAMS((int *));
|
||||
static int rl_gather_tyi PARAMS((void));
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input Buffering */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static int pop_index, push_index;
|
||||
static unsigned char ibuffer[512];
|
||||
static int ibuffer_len = sizeof (ibuffer) - 1;
|
||||
|
||||
#define any_typein (push_index != pop_index)
|
||||
|
||||
int
|
||||
_rl_any_typein ()
|
||||
{
|
||||
return any_typein;
|
||||
}
|
||||
|
||||
/* Return the amount of space available in the buffer for stuffing
|
||||
characters. */
|
||||
static int
|
||||
ibuffer_space ()
|
||||
{
|
||||
if (pop_index > push_index)
|
||||
return (pop_index - push_index - 1);
|
||||
else
|
||||
return (ibuffer_len - (push_index - pop_index));
|
||||
}
|
||||
|
||||
/* Get a key from the buffer of characters to be read.
|
||||
Return the key in KEY.
|
||||
Result is KEY if there was a key, or 0 if there wasn't. */
|
||||
static int
|
||||
rl_get_char (key)
|
||||
int *key;
|
||||
{
|
||||
if (push_index == pop_index)
|
||||
return (0);
|
||||
|
||||
*key = ibuffer[pop_index++];
|
||||
|
||||
if (pop_index >= ibuffer_len)
|
||||
pop_index = 0;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Stuff KEY into the *front* of the input buffer.
|
||||
Returns non-zero if successful, zero if there is
|
||||
no space left in the buffer. */
|
||||
int
|
||||
_rl_unget_char (key)
|
||||
int key;
|
||||
{
|
||||
if (ibuffer_space ())
|
||||
{
|
||||
pop_index--;
|
||||
if (pop_index < 0)
|
||||
pop_index = ibuffer_len - 1;
|
||||
ibuffer[pop_index] = key;
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_rl_pushed_input_available ()
|
||||
{
|
||||
return (push_index != pop_index);
|
||||
}
|
||||
|
||||
/* If a character is available to be read, then read it and stuff it into
|
||||
IBUFFER. Otherwise, just return. Returns number of characters read
|
||||
(0 if none available) and -1 on error (EIO). */
|
||||
static int
|
||||
rl_gather_tyi ()
|
||||
{
|
||||
int tty;
|
||||
register int tem, result;
|
||||
int chars_avail, k;
|
||||
char input;
|
||||
#if defined(HAVE_SELECT)
|
||||
fd_set readfds, exceptfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = _keyboard_input_timeout;
|
||||
result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
|
||||
if (result <= 0)
|
||||
return 0; /* Nothing to read. */
|
||||
#endif
|
||||
|
||||
result = -1;
|
||||
#if defined (FIONREAD)
|
||||
errno = 0;
|
||||
result = ioctl (tty, FIONREAD, &chars_avail);
|
||||
if (result == -1 && errno == EIO)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
#if defined (O_NDELAY)
|
||||
if (result == -1)
|
||||
{
|
||||
tem = fcntl (tty, F_GETFL, 0);
|
||||
|
||||
fcntl (tty, F_SETFL, (tem | O_NDELAY));
|
||||
chars_avail = read (tty, &input, 1);
|
||||
|
||||
fcntl (tty, F_SETFL, tem);
|
||||
if (chars_avail == -1 && errno == EAGAIN)
|
||||
return 0;
|
||||
}
|
||||
#endif /* O_NDELAY */
|
||||
|
||||
/* If there's nothing available, don't waste time trying to read
|
||||
something. */
|
||||
if (chars_avail <= 0)
|
||||
return 0;
|
||||
|
||||
tem = ibuffer_space ();
|
||||
|
||||
if (chars_avail > tem)
|
||||
chars_avail = tem;
|
||||
|
||||
/* One cannot read all of the available input. I can only read a single
|
||||
character at a time, or else programs which require input can be
|
||||
thwarted. If the buffer is larger than one character, I lose.
|
||||
Damn! */
|
||||
if (tem < ibuffer_len)
|
||||
chars_avail = 0;
|
||||
|
||||
if (result != -1)
|
||||
{
|
||||
while (chars_avail--)
|
||||
{
|
||||
k = (*rl_getc_function) (rl_instream);
|
||||
rl_stuff_char (k);
|
||||
if (k == NEWLINE || k == RETURN)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chars_avail)
|
||||
rl_stuff_char (input);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
rl_set_keyboard_input_timeout (u)
|
||||
int u;
|
||||
{
|
||||
int o;
|
||||
|
||||
o = _keyboard_input_timeout;
|
||||
if (u > 0)
|
||||
_keyboard_input_timeout = u;
|
||||
return (o);
|
||||
}
|
||||
|
||||
/* Is there input available to be read on the readline input file
|
||||
descriptor? Only works if the system has select(2) or FIONREAD.
|
||||
Uses the value of _keyboard_input_timeout as the timeout; if another
|
||||
readline function wants to specify a timeout and not leave it up to
|
||||
the user, it should use _rl_input_queued(timeout_value_in_microseconds)
|
||||
instead. */
|
||||
int
|
||||
_rl_input_available ()
|
||||
{
|
||||
#if defined(HAVE_SELECT)
|
||||
fd_set readfds, exceptfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
#if !defined (HAVE_SELECT) && defined(FIONREAD)
|
||||
int chars_avail;
|
||||
#endif
|
||||
int tty;
|
||||
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = _keyboard_input_timeout;
|
||||
return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
|
||||
#else
|
||||
|
||||
#if defined (FIONREAD)
|
||||
if (ioctl (tty, FIONREAD, &chars_avail) == 0)
|
||||
return (chars_avail);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_rl_input_queued (t)
|
||||
int t;
|
||||
{
|
||||
int old_timeout, r;
|
||||
|
||||
old_timeout = rl_set_keyboard_input_timeout (t);
|
||||
r = _rl_input_available ();
|
||||
rl_set_keyboard_input_timeout (old_timeout);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
_rl_insert_typein (c)
|
||||
int c;
|
||||
{
|
||||
int key, t, i;
|
||||
char *string;
|
||||
|
||||
i = key = 0;
|
||||
string = (char *)xmalloc (ibuffer_len + 1);
|
||||
string[i++] = (char) c;
|
||||
|
||||
while ((t = rl_get_char (&key)) &&
|
||||
_rl_keymap[key].type == ISFUNC &&
|
||||
_rl_keymap[key].function == rl_insert)
|
||||
string[i++] = key;
|
||||
|
||||
if (t)
|
||||
_rl_unget_char (key);
|
||||
|
||||
string[i] = '\0';
|
||||
rl_insert_text (string);
|
||||
free (string);
|
||||
}
|
||||
|
||||
/* Add KEY to the buffer of characters to be read. Returns 1 if the
|
||||
character was stuffed correctly; 0 otherwise. */
|
||||
int
|
||||
rl_stuff_char (key)
|
||||
int key;
|
||||
{
|
||||
if (ibuffer_space () == 0)
|
||||
return 0;
|
||||
|
||||
if (key == EOF)
|
||||
{
|
||||
key = NEWLINE;
|
||||
rl_pending_input = EOF;
|
||||
RL_SETSTATE (RL_STATE_INPUTPENDING);
|
||||
}
|
||||
ibuffer[push_index++] = key;
|
||||
if (push_index >= ibuffer_len)
|
||||
push_index = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make C be the next command to be executed. */
|
||||
int
|
||||
rl_execute_next (c)
|
||||
int c;
|
||||
{
|
||||
rl_pending_input = c;
|
||||
RL_SETSTATE (RL_STATE_INPUTPENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear any pending input pushed with rl_execute_next() */
|
||||
int
|
||||
rl_clear_pending_input ()
|
||||
{
|
||||
rl_pending_input = 0;
|
||||
RL_UNSETSTATE (RL_STATE_INPUTPENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Read a key, including pending input. */
|
||||
int
|
||||
rl_read_key ()
|
||||
{
|
||||
int c;
|
||||
|
||||
rl_key_sequence_length++;
|
||||
|
||||
if (rl_pending_input)
|
||||
{
|
||||
c = rl_pending_input;
|
||||
rl_clear_pending_input ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If input is coming from a macro, then use that. */
|
||||
if (c = _rl_next_macro_key ())
|
||||
return (c);
|
||||
|
||||
/* If the user has an event function, then call it periodically. */
|
||||
if (rl_event_hook)
|
||||
{
|
||||
while (rl_event_hook && rl_get_char (&c) == 0)
|
||||
{
|
||||
(*rl_event_hook) ();
|
||||
if (rl_done) /* XXX - experimental */
|
||||
return ('\n');
|
||||
if (rl_gather_tyi () < 0) /* XXX - EIO */
|
||||
{
|
||||
rl_done = 1;
|
||||
return ('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rl_get_char (&c) == 0)
|
||||
c = (*rl_getc_function) (rl_instream);
|
||||
}
|
||||
}
|
||||
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
rl_getc (stream)
|
||||
FILE *stream;
|
||||
{
|
||||
int result;
|
||||
unsigned char c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
result = read (fileno (stream), &c, sizeof (unsigned char));
|
||||
|
||||
if (result == sizeof (unsigned char))
|
||||
return (c);
|
||||
|
||||
/* If zero characters are returned, then the file that we are
|
||||
reading from is empty! Return EOF in that case. */
|
||||
if (result == 0)
|
||||
return (EOF);
|
||||
|
||||
#if defined (__BEOS__)
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
#if defined (EWOULDBLOCK)
|
||||
# define X_EWOULDBLOCK EWOULDBLOCK
|
||||
#else
|
||||
# define X_EWOULDBLOCK -99
|
||||
#endif
|
||||
|
||||
#if defined (EAGAIN)
|
||||
# define X_EAGAIN EAGAIN
|
||||
#else
|
||||
# define X_EAGAIN -99
|
||||
#endif
|
||||
|
||||
if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
|
||||
{
|
||||
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
|
||||
return (EOF);
|
||||
continue;
|
||||
}
|
||||
|
||||
#undef X_EWOULDBLOCK
|
||||
#undef X_EAGAIN
|
||||
|
||||
/* If the error that we received was SIGINT, then try again,
|
||||
this is simply an interrupted system call to read ().
|
||||
Otherwise, some error ocurred, also signifying EOF. */
|
||||
if (errno != EINTR)
|
||||
return (EOF);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
/* read multibyte char */
|
||||
int
|
||||
_rl_read_mbchar (mbchar, size)
|
||||
char *mbchar;
|
||||
int size;
|
||||
{
|
||||
int mb_len = 0;
|
||||
size_t mbchar_bytes_length;
|
||||
wchar_t wc;
|
||||
mbstate_t ps, ps_back;
|
||||
|
||||
memset(&ps, 0, sizeof (mbstate_t));
|
||||
memset(&ps_back, 0, sizeof (mbstate_t));
|
||||
|
||||
while (mb_len < size)
|
||||
{
|
||||
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||
mbchar[mb_len++] = rl_read_key ();
|
||||
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
||||
|
||||
mbchar_bytes_length = mbrtowc (&wc, mbchar, mb_len, &ps);
|
||||
if (mbchar_bytes_length == (size_t)(-1))
|
||||
break; /* invalid byte sequence for the current locale */
|
||||
else if (mbchar_bytes_length == (size_t)(-2))
|
||||
{
|
||||
/* shorted bytes */
|
||||
ps = ps_back;
|
||||
continue;
|
||||
}
|
||||
else if (mbchar_bytes_length > (size_t)(0))
|
||||
break;
|
||||
}
|
||||
|
||||
return mb_len;
|
||||
}
|
||||
|
||||
/* Read a multibyte-character string whose first character is FIRST into
|
||||
the buffer MB of length MBLEN. Returns the last character read, which
|
||||
may be FIRST. Used by the search functions, among others. Very similar
|
||||
to _rl_read_mbchar. */
|
||||
int
|
||||
_rl_read_mbstring (first, mb, mblen)
|
||||
int first;
|
||||
char *mb;
|
||||
int mblen;
|
||||
{
|
||||
int i, c;
|
||||
mbstate_t ps;
|
||||
|
||||
c = first;
|
||||
memset (mb, 0, mblen);
|
||||
for (i = 0; i < mblen; i++)
|
||||
{
|
||||
mb[i] = (char)c;
|
||||
memset (&ps, 0, sizeof (mbstate_t));
|
||||
if (_rl_get_char_len (mb, &ps) == -2)
|
||||
{
|
||||
/* Read more for multibyte character */
|
||||
RL_SETSTATE (RL_STATE_MOREINPUT);
|
||||
c = rl_read_key ();
|
||||
RL_UNSETSTATE (RL_STATE_MOREINPUT);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
#endif /* HANDLE_MULTIBYTE */
|
||||
@@ -21,6 +21,10 @@
|
||||
59 Temple Place, Suite 330, Boston, MA 02111 USA. */
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#include "rlconf.h"
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/* parens.c -- Implementation of matching parentheses feature. */
|
||||
|
||||
/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Readline Library, a library for
|
||||
reading lines of text with interactive input and history editing.
|
||||
|
||||
The GNU Readline 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 GNU Readline 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. */
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#include "rlconf.h"
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined (FD_SET) && !defined (HAVE_SELECT)
|
||||
# define HAVE_SELECT
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
# include <sys/time.h>
|
||||
#endif /* HAVE_SELECT */
|
||||
#if defined (HAVE_SYS_SELECT_H)
|
||||
# include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_STRING_H)
|
||||
# include <string.h>
|
||||
#else /* !HAVE_STRING_H */
|
||||
# include <strings.h>
|
||||
#endif /* !HAVE_STRING_H */
|
||||
|
||||
#if !defined (strchr) && !defined (__STDC__)
|
||||
extern char *strchr (), *strrchr ();
|
||||
#endif /* !strchr && !__STDC__ */
|
||||
|
||||
#include "readline.h"
|
||||
#include "rlprivate.h"
|
||||
|
||||
static int find_matching_open PARAMS((char *, int, int));
|
||||
|
||||
/* Non-zero means try to blink the matching open parenthesis when the
|
||||
close parenthesis is inserted. */
|
||||
#if defined (HAVE_SELECT)
|
||||
int rl_blink_matching_paren = 1;
|
||||
#else /* !HAVE_SELECT */
|
||||
int rl_blink_matching_paren = 0;
|
||||
#endif /* !HAVE_SELECT */
|
||||
|
||||
static int _paren_blink_usec = 500000;
|
||||
|
||||
/* Change emacs_standard_keymap to have bindings for paren matching when
|
||||
ON_OR_OFF is 1, change them back to self_insert when ON_OR_OFF == 0. */
|
||||
void
|
||||
_rl_enable_paren_matching (on_or_off)
|
||||
int on_or_off;
|
||||
{
|
||||
if (on_or_off)
|
||||
{ /* ([{ */
|
||||
rl_bind_key_in_map (')', rl_insert_close, emacs_standard_keymap);
|
||||
rl_bind_key_in_map (']', rl_insert_close, emacs_standard_keymap);
|
||||
rl_bind_key_in_map ('}', rl_insert_close, emacs_standard_keymap);
|
||||
}
|
||||
else
|
||||
{ /* ([{ */
|
||||
rl_bind_key_in_map (')', rl_insert, emacs_standard_keymap);
|
||||
rl_bind_key_in_map (']', rl_insert, emacs_standard_keymap);
|
||||
rl_bind_key_in_map ('}', rl_insert, emacs_standard_keymap);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
rl_set_paren_blink_timeout (u)
|
||||
int u;
|
||||
{
|
||||
int o;
|
||||
|
||||
o = _paren_blink_usec;
|
||||
if (u > 0)
|
||||
_paren_blink_usec = u;
|
||||
return (o);
|
||||
}
|
||||
|
||||
int
|
||||
rl_insert_close (count, invoking_key)
|
||||
int count, invoking_key;
|
||||
{
|
||||
if (rl_explicit_arg || !rl_blink_matching_paren)
|
||||
_rl_insert_char (count, invoking_key);
|
||||
else
|
||||
{
|
||||
#if defined (HAVE_SELECT)
|
||||
int orig_point, match_point, ready;
|
||||
struct timeval timer;
|
||||
fd_set readfds;
|
||||
|
||||
_rl_insert_char (1, invoking_key);
|
||||
(*rl_redisplay_function) ();
|
||||
match_point =
|
||||
find_matching_open (rl_line_buffer, rl_point - 2, invoking_key);
|
||||
|
||||
/* Emacs might message or ring the bell here, but I don't. */
|
||||
if (match_point < 0)
|
||||
return -1;
|
||||
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (fileno (rl_instream), &readfds);
|
||||
timer.tv_sec = 0;
|
||||
timer.tv_usec = _paren_blink_usec;
|
||||
|
||||
orig_point = rl_point;
|
||||
rl_point = match_point;
|
||||
(*rl_redisplay_function) ();
|
||||
ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
|
||||
rl_point = orig_point;
|
||||
#else /* !HAVE_SELECT */
|
||||
_rl_insert_char (count, invoking_key);
|
||||
#endif /* !HAVE_SELECT */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
find_matching_open (string, from, closer)
|
||||
char *string;
|
||||
int from, closer;
|
||||
{
|
||||
register int i;
|
||||
int opener, level, delimiter;
|
||||
|
||||
switch (closer)
|
||||
{
|
||||
case ']': opener = '['; break;
|
||||
case '}': opener = '{'; break;
|
||||
case ')': opener = '('; break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
level = 1; /* The closer passed in counts as 1. */
|
||||
delimiter = 0; /* Delimited state unknown. */
|
||||
|
||||
for (i = from; i > -1; i--)
|
||||
{
|
||||
if (delimiter && (string[i] == delimiter))
|
||||
delimiter = 0;
|
||||
else if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, string[i]))
|
||||
delimiter = string[i];
|
||||
else if (!delimiter && (string[i] == closer))
|
||||
level++;
|
||||
else if (!delimiter && (string[i] == opener))
|
||||
level--;
|
||||
|
||||
if (!level)
|
||||
break;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
+1
-1
@@ -95,7 +95,7 @@ static int iso8601wknum(const struct tm *timeptr);
|
||||
#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
|
||||
extern char *tzname[2];
|
||||
extern int daylight;
|
||||
#if defined(SOLARIS) || defined(mips)
|
||||
#if defined(SOLARIS) || defined(mips) || defined (M_UNIX)
|
||||
extern long int timezone, altzone;
|
||||
#else
|
||||
extern int timezone, altzone;
|
||||
|
||||
@@ -0,0 +1,859 @@
|
||||
/*
|
||||
* Modified slightly by Chet Ramey for inclusion in Bash
|
||||
*/
|
||||
|
||||
/*
|
||||
* strftime.c
|
||||
*
|
||||
* Public-domain implementation of ISO C library routine.
|
||||
*
|
||||
* If you can't do prototypes, get GCC.
|
||||
*
|
||||
* The C99 standard now specifies just about all of the formats
|
||||
* that were additional in the earlier versions of this file.
|
||||
*
|
||||
* For extensions from SunOS, add SUNOS_EXT.
|
||||
* For extensions from HP/UX, add HPUX_EXT.
|
||||
* For VMS dates, add VMS_EXT.
|
||||
* For complete POSIX semantics, add POSIX_SEMANTICS.
|
||||
*
|
||||
* The code for %c, %x, and %X follows the C99 specification for
|
||||
* the "C" locale.
|
||||
*
|
||||
* This version ignores LOCALE information.
|
||||
* It also doesn't worry about multi-byte characters.
|
||||
* So there.
|
||||
*
|
||||
* This file is also shipped with GAWK (GNU Awk), gawk specific bits of
|
||||
* code are included if GAWK is defined.
|
||||
*
|
||||
* Arnold Robbins
|
||||
* January, February, March, 1991
|
||||
* Updated March, April 1992
|
||||
* Updated April, 1993
|
||||
* Updated February, 1994
|
||||
* Updated May, 1994
|
||||
* Updated January, 1995
|
||||
* Updated September, 1995
|
||||
* Updated January, 1996
|
||||
* Updated July, 1997
|
||||
* Updated October, 1999
|
||||
* Updated September, 2000
|
||||
*
|
||||
* Fixes from ado@elsie.nci.nih.gov,
|
||||
* February 1991, May 1992
|
||||
* Fixes from Tor Lillqvist tml@tik.vtt.fi,
|
||||
* May 1993
|
||||
* Further fixes from ado@elsie.nci.nih.gov,
|
||||
* February 1994
|
||||
* %z code from chip@chinacat.unicom.com,
|
||||
* Applied September 1995
|
||||
* %V code fixed (again) and %G, %g added,
|
||||
* January 1996
|
||||
* %v code fixed, better configuration,
|
||||
* July 1997
|
||||
* Moved to C99 specification.
|
||||
* September 2000
|
||||
*/
|
||||
#include <config.h>
|
||||
|
||||
#ifndef GAWK
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
#if defined(TM_IN_SYS_TIME)
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* defaults: season to taste */
|
||||
#define SUNOS_EXT 1 /* stuff in SunOS strftime routine */
|
||||
#define VMS_EXT 1 /* include %v for VMS date format */
|
||||
#define HPUX_EXT 1 /* non-conflicting stuff in HP-UX date */
|
||||
#ifndef GAWK
|
||||
#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */
|
||||
#endif
|
||||
|
||||
#undef strchr /* avoid AIX weirdness */
|
||||
|
||||
extern void tzset(void);
|
||||
static int weeknumber(const struct tm *timeptr, int firstweekday);
|
||||
static int iso8601wknum(const struct tm *timeptr);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define inline __inline__
|
||||
#else
|
||||
#define inline /**/
|
||||
#endif
|
||||
|
||||
#define range(low, item, hi) max(low, min(item, hi))
|
||||
|
||||
#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
|
||||
extern char *tzname[2];
|
||||
extern int daylight;
|
||||
#if defined(SOLARIS) || defined(mips)
|
||||
extern long int timezone, altzone;
|
||||
#else
|
||||
extern int timezone, altzone;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef min /* just in case */
|
||||
|
||||
/* min --- return minimum of two numbers */
|
||||
|
||||
static inline int
|
||||
min(int a, int b)
|
||||
{
|
||||
return (a < b ? a : b);
|
||||
}
|
||||
|
||||
#undef max /* also, just in case */
|
||||
|
||||
/* max --- return maximum of two numbers */
|
||||
|
||||
static inline int
|
||||
max(int a, int b)
|
||||
{
|
||||
return (a > b ? a : b);
|
||||
}
|
||||
|
||||
/* strftime --- produce formatted time */
|
||||
|
||||
size_t
|
||||
strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
|
||||
{
|
||||
char *endp = s + maxsize;
|
||||
char *start = s;
|
||||
auto char tbuf[100];
|
||||
long off;
|
||||
int i, w, y;
|
||||
static short first = 1;
|
||||
#ifdef POSIX_SEMANTICS
|
||||
static char *savetz = NULL;
|
||||
static int savetzlen = 0;
|
||||
char *tz;
|
||||
#endif /* POSIX_SEMANTICS */
|
||||
#ifndef HAVE_TM_ZONE
|
||||
#ifndef HAVE_TM_NAME
|
||||
#ifndef HAVE_TZNAME
|
||||
extern char *timezone();
|
||||
struct timeval tv;
|
||||
struct timezone zone;
|
||||
#endif /* HAVE_TZNAME */
|
||||
#endif /* HAVE_TM_NAME */
|
||||
#endif /* HAVE_TM_ZONE */
|
||||
|
||||
/* various tables, useful in North America */
|
||||
static const char *days_a[] = {
|
||||
"Sun", "Mon", "Tue", "Wed",
|
||||
"Thu", "Fri", "Sat",
|
||||
};
|
||||
static const char *days_l[] = {
|
||||
"Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
"Thursday", "Friday", "Saturday",
|
||||
};
|
||||
static const char *months_a[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
};
|
||||
static const char *months_l[] = {
|
||||
"January", "February", "March", "April",
|
||||
"May", "June", "July", "August", "September",
|
||||
"October", "November", "December",
|
||||
};
|
||||
static const char *ampm[] = { "AM", "PM", };
|
||||
|
||||
if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
|
||||
return 0;
|
||||
|
||||
/* quick check if we even need to bother */
|
||||
if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
|
||||
return 0;
|
||||
|
||||
#ifndef POSIX_SEMANTICS
|
||||
if (first) {
|
||||
tzset();
|
||||
first = 0;
|
||||
}
|
||||
#else /* POSIX_SEMANTICS */
|
||||
#if defined (SHELL)
|
||||
tz = get_string_value ("TZ");
|
||||
#else
|
||||
tz = getenv("TZ");
|
||||
#endif
|
||||
if (first) {
|
||||
if (tz != NULL) {
|
||||
int tzlen = strlen(tz);
|
||||
|
||||
savetz = (char *) malloc(tzlen + 1);
|
||||
if (savetz != NULL) {
|
||||
savetzlen = tzlen + 1;
|
||||
strcpy(savetz, tz);
|
||||
}
|
||||
}
|
||||
tzset();
|
||||
first = 0;
|
||||
}
|
||||
/* if we have a saved TZ, and it is different, recapture and reset */
|
||||
if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
|
||||
i = strlen(tz) + 1;
|
||||
if (i > savetzlen) {
|
||||
savetz = (char *) realloc(savetz, i);
|
||||
if (savetz) {
|
||||
savetzlen = i;
|
||||
strcpy(savetz, tz);
|
||||
}
|
||||
} else
|
||||
strcpy(savetz, tz);
|
||||
tzset();
|
||||
}
|
||||
#endif /* POSIX_SEMANTICS */
|
||||
|
||||
for (; *format && s < endp - 1; format++) {
|
||||
tbuf[0] = '\0';
|
||||
if (*format != '%') {
|
||||
*s++ = *format;
|
||||
continue;
|
||||
}
|
||||
again:
|
||||
switch (*++format) {
|
||||
case '\0':
|
||||
*s++ = '%';
|
||||
goto out;
|
||||
|
||||
case '%':
|
||||
*s++ = '%';
|
||||
continue;
|
||||
|
||||
case 'a': /* abbreviated weekday name */
|
||||
if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
|
||||
strcpy(tbuf, "?");
|
||||
else
|
||||
strcpy(tbuf, days_a[timeptr->tm_wday]);
|
||||
break;
|
||||
|
||||
case 'A': /* full weekday name */
|
||||
if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
|
||||
strcpy(tbuf, "?");
|
||||
else
|
||||
strcpy(tbuf, days_l[timeptr->tm_wday]);
|
||||
break;
|
||||
|
||||
case 'b': /* abbreviated month name */
|
||||
short_month:
|
||||
if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
|
||||
strcpy(tbuf, "?");
|
||||
else
|
||||
strcpy(tbuf, months_a[timeptr->tm_mon]);
|
||||
break;
|
||||
|
||||
case 'B': /* full month name */
|
||||
if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
|
||||
strcpy(tbuf, "?");
|
||||
else
|
||||
strcpy(tbuf, months_l[timeptr->tm_mon]);
|
||||
break;
|
||||
|
||||
case 'c': /* appropriate date and time representation */
|
||||
/*
|
||||
* This used to be:
|
||||
*
|
||||
* strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
|
||||
*
|
||||
* Now, per the ISO 1999 C standard, it this:
|
||||
*/
|
||||
strftime(tbuf, sizeof tbuf, "%A %B %d %T %Y", timeptr);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
century:
|
||||
sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
|
||||
break;
|
||||
|
||||
case 'd': /* day of the month, 01 - 31 */
|
||||
i = range(1, timeptr->tm_mday, 31);
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 'D': /* date as %m/%d/%y */
|
||||
strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
|
||||
break;
|
||||
|
||||
case 'e': /* day of month, blank padded */
|
||||
sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
/* POSIX (now C99) locale extensions, ignored for now */
|
||||
goto again;
|
||||
|
||||
case 'F': /* ISO 8601 date representation */
|
||||
strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'G':
|
||||
/*
|
||||
* Year of ISO week.
|
||||
*
|
||||
* If it's December but the ISO week number is one,
|
||||
* that week is in next year.
|
||||
* If it's January but the ISO week number is 52 or
|
||||
* 53, that week is in last year.
|
||||
* Otherwise, it's this year.
|
||||
*/
|
||||
w = iso8601wknum(timeptr);
|
||||
if (timeptr->tm_mon == 11 && w == 1)
|
||||
y = 1900 + timeptr->tm_year + 1;
|
||||
else if (timeptr->tm_mon == 0 && w >= 52)
|
||||
y = 1900 + timeptr->tm_year - 1;
|
||||
else
|
||||
y = 1900 + timeptr->tm_year;
|
||||
|
||||
if (*format == 'G')
|
||||
sprintf(tbuf, "%d", y);
|
||||
else
|
||||
sprintf(tbuf, "%02d", y % 100);
|
||||
break;
|
||||
|
||||
case 'h': /* abbreviated month name */
|
||||
goto short_month;
|
||||
|
||||
case 'H': /* hour, 24-hour clock, 00 - 23 */
|
||||
i = range(0, timeptr->tm_hour, 23);
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 'I': /* hour, 12-hour clock, 01 - 12 */
|
||||
i = range(0, timeptr->tm_hour, 23);
|
||||
if (i == 0)
|
||||
i = 12;
|
||||
else if (i > 12)
|
||||
i -= 12;
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 'j': /* day of the year, 001 - 366 */
|
||||
sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
|
||||
break;
|
||||
|
||||
case 'm': /* month, 01 - 12 */
|
||||
i = range(0, timeptr->tm_mon, 11);
|
||||
sprintf(tbuf, "%02d", i + 1);
|
||||
break;
|
||||
|
||||
case 'M': /* minute, 00 - 59 */
|
||||
i = range(0, timeptr->tm_min, 59);
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 'n': /* same as \n */
|
||||
tbuf[0] = '\n';
|
||||
tbuf[1] = '\0';
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
/* POSIX (now C99) locale extensions, ignored for now */
|
||||
goto again;
|
||||
|
||||
case 'p': /* am or pm based on 12-hour clock */
|
||||
i = range(0, timeptr->tm_hour, 23);
|
||||
if (i < 12)
|
||||
strcpy(tbuf, ampm[0]);
|
||||
else
|
||||
strcpy(tbuf, ampm[1]);
|
||||
break;
|
||||
|
||||
case 'r': /* time as %I:%M:%S %p */
|
||||
strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
|
||||
break;
|
||||
|
||||
case 'R': /* time as %H:%M */
|
||||
strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
|
||||
break;
|
||||
|
||||
#if defined(HAVE_MKTIME) || defined(GAWK)
|
||||
case 's': /* time as seconds since the Epoch */
|
||||
{
|
||||
struct tm non_const_timeptr;
|
||||
|
||||
non_const_timeptr = *timeptr;
|
||||
sprintf(tbuf, "%ld", mktime(& non_const_timeptr));
|
||||
break;
|
||||
}
|
||||
#endif /* defined(HAVE_MKTIME) || defined(GAWK) */
|
||||
|
||||
case 'S': /* second, 00 - 60 */
|
||||
i = range(0, timeptr->tm_sec, 60);
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 't': /* same as \t */
|
||||
tbuf[0] = '\t';
|
||||
tbuf[1] = '\0';
|
||||
break;
|
||||
|
||||
case 'T': /* time as %H:%M:%S */
|
||||
the_time:
|
||||
strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
/* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
|
||||
sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
|
||||
timeptr->tm_wday);
|
||||
break;
|
||||
|
||||
case 'U': /* week of year, Sunday is first day of week */
|
||||
sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
|
||||
break;
|
||||
|
||||
case 'V': /* week of year according ISO 8601 */
|
||||
sprintf(tbuf, "%02d", iso8601wknum(timeptr));
|
||||
break;
|
||||
|
||||
case 'w': /* weekday, Sunday == 0, 0 - 6 */
|
||||
i = range(0, timeptr->tm_wday, 6);
|
||||
sprintf(tbuf, "%d", i);
|
||||
break;
|
||||
|
||||
case 'W': /* week of year, Monday is first day of week */
|
||||
sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
|
||||
break;
|
||||
|
||||
case 'x': /* appropriate date representation */
|
||||
strftime(tbuf, sizeof tbuf, "%A %B %d %Y", timeptr);
|
||||
break;
|
||||
|
||||
case 'X': /* appropriate time representation */
|
||||
goto the_time;
|
||||
break;
|
||||
|
||||
case 'y': /* year without a century, 00 - 99 */
|
||||
year:
|
||||
i = timeptr->tm_year % 100;
|
||||
sprintf(tbuf, "%02d", i);
|
||||
break;
|
||||
|
||||
case 'Y': /* year with century */
|
||||
fullyear:
|
||||
sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
|
||||
break;
|
||||
|
||||
/*
|
||||
* From: Chip Rosenthal <chip@chinacat.unicom.com>
|
||||
* Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
|
||||
*
|
||||
* Warning: the %z [code] is implemented by inspecting the
|
||||
* timezone name conditional compile settings, and
|
||||
* inferring a method to get timezone offsets. I've tried
|
||||
* this code on a couple of machines, but I don't doubt
|
||||
* there is some system out there that won't like it.
|
||||
* Maybe the easiest thing to do would be to bracket this
|
||||
* with an #ifdef that can turn it off. The %z feature
|
||||
* would be an admittedly obscure one that most folks can
|
||||
* live without, but it would be a great help to those of
|
||||
* us that muck around with various message processors.
|
||||
*/
|
||||
case 'z': /* time zone offset east of GMT e.g. -0600 */
|
||||
#ifdef HAVE_TM_NAME
|
||||
/*
|
||||
* Systems with tm_name probably have tm_tzadj as
|
||||
* secs west of GMT. Convert to mins east of GMT.
|
||||
*/
|
||||
off = -timeptr->tm_tzadj / 60;
|
||||
#else /* !HAVE_TM_NAME */
|
||||
#ifdef HAVE_TM_ZONE
|
||||
/*
|
||||
* Systems with tm_zone probably have tm_gmtoff as
|
||||
* secs east of GMT. Convert to mins east of GMT.
|
||||
*/
|
||||
off = timeptr->tm_gmtoff / 60;
|
||||
#else /* !HAVE_TM_ZONE */
|
||||
#if HAVE_TZNAME
|
||||
/*
|
||||
* Systems with tzname[] probably have timezone as
|
||||
* secs west of GMT. Convert to mins east of GMT.
|
||||
*/
|
||||
off = -(daylight ? timezone : altzone) / 60;
|
||||
#else /* !HAVE_TZNAME */
|
||||
off = -zone.tz_minuteswest;
|
||||
#endif /* !HAVE_TZNAME */
|
||||
#endif /* !HAVE_TM_ZONE */
|
||||
#endif /* !HAVE_TM_NAME */
|
||||
if (off < 0) {
|
||||
tbuf[0] = '-';
|
||||
off = -off;
|
||||
} else {
|
||||
tbuf[0] = '+';
|
||||
}
|
||||
sprintf(tbuf+1, "%02d%02d", off/60, off%60);
|
||||
break;
|
||||
|
||||
case 'Z': /* time zone name or abbrevation */
|
||||
#ifdef HAVE_TZNAME
|
||||
i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
|
||||
strcpy(tbuf, tzname[i]);
|
||||
#else
|
||||
#ifdef HAVE_TM_ZONE
|
||||
strcpy(tbuf, timeptr->tm_zone);
|
||||
#else
|
||||
#ifdef HAVE_TM_NAME
|
||||
strcpy(tbuf, timeptr->tm_name);
|
||||
#else
|
||||
gettimeofday(& tv, & zone);
|
||||
strcpy(tbuf, timezone(zone.tz_minuteswest,
|
||||
timeptr->tm_isdst > 0));
|
||||
#endif /* HAVE_TM_NAME */
|
||||
#endif /* HAVE_TM_ZONE */
|
||||
#endif /* HAVE_TZNAME */
|
||||
break;
|
||||
|
||||
#ifdef SUNOS_EXT
|
||||
case 'k': /* hour, 24-hour clock, blank pad */
|
||||
sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
|
||||
break;
|
||||
|
||||
case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */
|
||||
i = range(0, timeptr->tm_hour, 23);
|
||||
if (i == 0)
|
||||
i = 12;
|
||||
else if (i > 12)
|
||||
i -= 12;
|
||||
sprintf(tbuf, "%2d", i);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef HPUX_EXT
|
||||
case 'N': /* Emperor/Era name */
|
||||
/* this is essentially the same as the century */
|
||||
goto century; /* %C */
|
||||
|
||||
case 'o': /* Emperor/Era year */
|
||||
goto year; /* %y */
|
||||
#endif /* HPUX_EXT */
|
||||
|
||||
|
||||
#ifdef VMS_EXT
|
||||
case 'v': /* date as dd-bbb-YYYY */
|
||||
sprintf(tbuf, "%2d-%3.3s-%4d",
|
||||
range(1, timeptr->tm_mday, 31),
|
||||
months_a[range(0, timeptr->tm_mon, 11)],
|
||||
timeptr->tm_year + 1900);
|
||||
for (i = 3; i < 6; i++)
|
||||
if (islower(tbuf[i]))
|
||||
tbuf[i] = toupper(tbuf[i]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
tbuf[0] = '%';
|
||||
tbuf[1] = *format;
|
||||
tbuf[2] = '\0';
|
||||
break;
|
||||
}
|
||||
i = strlen(tbuf);
|
||||
if (i) {
|
||||
if (s + i < endp - 1) {
|
||||
strcpy(s, tbuf);
|
||||
s += i;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (s < endp && *format == '\0') {
|
||||
*s = '\0';
|
||||
return (s - start);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* isleap --- is a year a leap year? */
|
||||
|
||||
static int
|
||||
isleap(int year)
|
||||
{
|
||||
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
|
||||
}
|
||||
|
||||
|
||||
/* iso8601wknum --- compute week number according to ISO 8601 */
|
||||
|
||||
static int
|
||||
iso8601wknum(const struct tm *timeptr)
|
||||
{
|
||||
/*
|
||||
* From 1003.2:
|
||||
* If the week (Monday to Sunday) containing January 1
|
||||
* has four or more days in the new year, then it is week 1;
|
||||
* otherwise it is the highest numbered week of the previous
|
||||
* year (52 or 53), and the next week is week 1.
|
||||
*
|
||||
* ADR: This means if Jan 1 was Monday through Thursday,
|
||||
* it was week 1, otherwise week 52 or 53.
|
||||
*
|
||||
* XPG4 erroneously included POSIX.2 rationale text in the
|
||||
* main body of the standard. Thus it requires week 53.
|
||||
*/
|
||||
|
||||
int weeknum, jan1day, diff;
|
||||
|
||||
/* get week number, Monday as first day of the week */
|
||||
weeknum = weeknumber(timeptr, 1);
|
||||
|
||||
/*
|
||||
* With thanks and tip of the hatlo to tml@tik.vtt.fi
|
||||
*
|
||||
* What day of the week does January 1 fall on?
|
||||
* We know that
|
||||
* (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
|
||||
* (timeptr->tm_wday - jan1.tm_wday) MOD 7
|
||||
* and that
|
||||
* jan1.tm_yday == 0
|
||||
* and that
|
||||
* timeptr->tm_wday MOD 7 == timeptr->tm_wday
|
||||
* from which it follows that. . .
|
||||
*/
|
||||
jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
|
||||
if (jan1day < 0)
|
||||
jan1day += 7;
|
||||
|
||||
/*
|
||||
* If Jan 1 was a Monday through Thursday, it was in
|
||||
* week 1. Otherwise it was last year's highest week, which is
|
||||
* this year's week 0.
|
||||
*
|
||||
* What does that mean?
|
||||
* If Jan 1 was Monday, the week number is exactly right, it can
|
||||
* never be 0.
|
||||
* If it was Tuesday through Thursday, the weeknumber is one
|
||||
* less than it should be, so we add one.
|
||||
* Otherwise, Friday, Saturday or Sunday, the week number is
|
||||
* OK, but if it is 0, it needs to be 52 or 53.
|
||||
*/
|
||||
switch (jan1day) {
|
||||
case 1: /* Monday */
|
||||
break;
|
||||
case 2: /* Tuesday */
|
||||
case 3: /* Wednesday */
|
||||
case 4: /* Thursday */
|
||||
weeknum++;
|
||||
break;
|
||||
case 5: /* Friday */
|
||||
case 6: /* Saturday */
|
||||
case 0: /* Sunday */
|
||||
if (weeknum == 0) {
|
||||
#ifdef USE_BROKEN_XPG4
|
||||
/* XPG4 (as of March 1994) says 53 unconditionally */
|
||||
weeknum = 53;
|
||||
#else
|
||||
/* get week number of last week of last year */
|
||||
struct tm dec31ly; /* 12/31 last year */
|
||||
dec31ly = *timeptr;
|
||||
dec31ly.tm_year--;
|
||||
dec31ly.tm_mon = 11;
|
||||
dec31ly.tm_mday = 31;
|
||||
dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
|
||||
dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
|
||||
weeknum = iso8601wknum(& dec31ly);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (timeptr->tm_mon == 11) {
|
||||
/*
|
||||
* The last week of the year
|
||||
* can be in week 1 of next year.
|
||||
* Sigh.
|
||||
*
|
||||
* This can only happen if
|
||||
* M T W
|
||||
* 29 30 31
|
||||
* 30 31
|
||||
* 31
|
||||
*/
|
||||
int wday, mday;
|
||||
|
||||
wday = timeptr->tm_wday;
|
||||
mday = timeptr->tm_mday;
|
||||
if ( (wday == 1 && (mday >= 29 && mday <= 31))
|
||||
|| (wday == 2 && (mday == 30 || mday == 31))
|
||||
|| (wday == 3 && mday == 31))
|
||||
weeknum = 1;
|
||||
}
|
||||
|
||||
return weeknum;
|
||||
}
|
||||
|
||||
/* weeknumber --- figure how many weeks into the year */
|
||||
|
||||
/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
|
||||
|
||||
static int
|
||||
weeknumber(const struct tm *timeptr, int firstweekday)
|
||||
{
|
||||
int wday = timeptr->tm_wday;
|
||||
int ret;
|
||||
|
||||
if (firstweekday == 1) {
|
||||
if (wday == 0) /* sunday */
|
||||
wday = 6;
|
||||
else
|
||||
wday--;
|
||||
}
|
||||
ret = ((timeptr->tm_yday + 7 - wday) / 7);
|
||||
if (ret < 0)
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* ADR --- I'm loathe to mess with ado's code ... */
|
||||
|
||||
Date: Wed, 24 Apr 91 20:54:08 MDT
|
||||
From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
|
||||
To: arnold@audiofax.com
|
||||
|
||||
Hi Arnold,
|
||||
in a process of fixing of strftime() in libraries on Atari ST I grabbed
|
||||
some pieces of code from your own strftime. When doing that it came
|
||||
to mind that your weeknumber() function compiles a little bit nicer
|
||||
in the following form:
|
||||
/*
|
||||
* firstweekday is 0 if starting in Sunday, non-zero if in Monday
|
||||
*/
|
||||
{
|
||||
return (timeptr->tm_yday - timeptr->tm_wday +
|
||||
(firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
|
||||
}
|
||||
How nicer it depends on a compiler, of course, but always a tiny bit.
|
||||
|
||||
Cheers,
|
||||
Michal
|
||||
ntomczak@vm.ucs.ualberta.ca
|
||||
#endif
|
||||
|
||||
#ifdef TEST_STRFTIME
|
||||
|
||||
/*
|
||||
* NAME:
|
||||
* tst
|
||||
*
|
||||
* SYNOPSIS:
|
||||
* tst
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* "tst" is a test driver for the function "strftime".
|
||||
*
|
||||
* OPTIONS:
|
||||
* None.
|
||||
*
|
||||
* AUTHOR:
|
||||
* Karl Vogel
|
||||
* Control Data Systems, Inc.
|
||||
* vogelke@c-17igp.wpafb.af.mil
|
||||
*
|
||||
* BUGS:
|
||||
* None noticed yet.
|
||||
*
|
||||
* COMPILE:
|
||||
* cc -o tst -DTEST_STRFTIME strftime.c
|
||||
*/
|
||||
|
||||
/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
|
||||
|
||||
#ifndef NULL
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAXTIME 132
|
||||
|
||||
/*
|
||||
* Array of time formats.
|
||||
*/
|
||||
|
||||
static char *array[] =
|
||||
{
|
||||
"(%%A) full weekday name, var length (Sunday..Saturday) %A",
|
||||
"(%%B) full month name, var length (January..December) %B",
|
||||
"(%%C) Century %C",
|
||||
"(%%D) date (%%m/%%d/%%y) %D",
|
||||
"(%%E) Locale extensions (ignored) %E",
|
||||
"(%%F) full month name, var length (January..December) %F",
|
||||
"(%%H) hour (24-hour clock, 00..23) %H",
|
||||
"(%%I) hour (12-hour clock, 01..12) %I",
|
||||
"(%%M) minute (00..59) %M",
|
||||
"(%%N) Emporer/Era Name %N",
|
||||
"(%%O) Locale extensions (ignored) %O",
|
||||
"(%%R) time, 24-hour (%%H:%%M) %R",
|
||||
"(%%S) second (00..60) %S",
|
||||
"(%%T) time, 24-hour (%%H:%%M:%%S) %T",
|
||||
"(%%U) week of year, Sunday as first day of week (00..53) %U",
|
||||
"(%%V) week of year according to ISO 8601 %V",
|
||||
"(%%W) week of year, Monday as first day of week (00..53) %W",
|
||||
"(%%X) appropriate locale time representation (%H:%M:%S) %X",
|
||||
"(%%Y) year with century (1970...) %Y",
|
||||
"(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
|
||||
"(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
|
||||
"(%%b) locale's abbreviated month name (Jan..Dec) %b",
|
||||
"(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
|
||||
"(%%d) day of the month (01..31) %d",
|
||||
"(%%e) day of the month, blank-padded ( 1..31) %e",
|
||||
"(%%h) should be same as (%%b) %h",
|
||||
"(%%j) day of the year (001..366) %j",
|
||||
"(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
|
||||
"(%%l) hour, 12-hour clock, blank pad ( 0..12) %l",
|
||||
"(%%m) month (01..12) %m",
|
||||
"(%%o) Emporer/Era Year %o",
|
||||
"(%%p) locale's AM or PM based on 12-hour clock %p",
|
||||
"(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
|
||||
"(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
|
||||
"(%%v) VMS date (dd-bbb-YYYY) %v",
|
||||
"(%%w) day of week (0..6, Sunday == 0) %w",
|
||||
"(%%x) appropriate locale date representation %x",
|
||||
"(%%y) last two digits of year (00..99) %y",
|
||||
"(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z",
|
||||
(char *) NULL
|
||||
};
|
||||
|
||||
/* main routine. */
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
long time();
|
||||
|
||||
char *next;
|
||||
char string[MAXTIME];
|
||||
|
||||
int k;
|
||||
int length;
|
||||
|
||||
struct tm *tm;
|
||||
|
||||
long clock;
|
||||
|
||||
/* Call the function. */
|
||||
|
||||
clock = time((long *) 0);
|
||||
tm = localtime(&clock);
|
||||
|
||||
for (k = 0; next = array[k]; k++) {
|
||||
length = strftime(string, MAXTIME, next, tm);
|
||||
printf("%s\n", string);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
#endif /* TEST_STRFTIME */
|
||||
@@ -5827,10 +5827,17 @@ param_expand (string, sindex, quoted, expanded_something,
|
||||
string might need it (consider "\"$@\""), but we need some
|
||||
way to signal that the final split on the first character
|
||||
of $IFS should be done, even though QUOTED is 1. */
|
||||
#if 0
|
||||
if (list && list->next)
|
||||
{
|
||||
#endif
|
||||
if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
||||
*quoted_dollar_at_p = 1;
|
||||
if (contains_dollar_at)
|
||||
*contains_dollar_at = 1;
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We want to separate the positional parameters with the first
|
||||
character of $IFS in case $IFS is something other than a space.
|
||||
@@ -5974,6 +5981,8 @@ comsub:
|
||||
temp = array_reference (array_cell (var), 0);
|
||||
if (temp)
|
||||
temp = quote_escapes (temp);
|
||||
else if (unbound_vars_is_error)
|
||||
goto unbound_variable;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -5985,6 +5994,7 @@ comsub:
|
||||
|
||||
temp = (char *)NULL;
|
||||
|
||||
unbound_variable:
|
||||
if (unbound_vars_is_error)
|
||||
err_unboundvar (temp1);
|
||||
else
|
||||
|
||||
@@ -212,8 +212,21 @@ static char *pos_params __P((char *, int, int, int));
|
||||
|
||||
static unsigned char *mb_getcharlens __P((char *, int));
|
||||
|
||||
static char *remove_upattern __P((char *, char *, int));
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
# if !defined (HAVE_WCSDUP)
|
||||
static wchar_t *wcsdup __P((wchar_t *));
|
||||
# endif
|
||||
static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int));
|
||||
#endif
|
||||
static char *remove_pattern __P((char *, char *, int));
|
||||
|
||||
static int match_pattern_char __P((char *, char *));
|
||||
static int match_upattern __P((char *, char *, int, char **, char **));
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
static int match_pattern_wchar __P((wchar_t *, wchar_t *));
|
||||
static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **));
|
||||
#endif
|
||||
static int match_pattern __P((char *, char *, int, char **, char **));
|
||||
static int getpatspec __P((int, char *));
|
||||
static char *getpattern __P((char *, int, int));
|
||||
@@ -1171,9 +1184,9 @@ de_backslash (string)
|
||||
prev_i = i;
|
||||
ADVANCE_CHAR (string, slen, i);
|
||||
if (j < prev_i)
|
||||
do string[j++] = string[prev_i++]; while (prev_i < i);
|
||||
do string[j++] = string[prev_i++]; while (prev_i < i);
|
||||
else
|
||||
j = i;
|
||||
j = i;
|
||||
}
|
||||
string[j] = '\0';
|
||||
|
||||
@@ -1565,6 +1578,10 @@ string_list_internal (list, sep)
|
||||
if (list == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
/* Short-circuit quickly if we don't need to separate anything. */
|
||||
if (list->next == 0)
|
||||
return (savestring (list->word->word));
|
||||
|
||||
/* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
|
||||
sep_len = STRLEN (sep);
|
||||
result_size = 0;
|
||||
@@ -2795,17 +2812,17 @@ remove_quoted_nulls (string)
|
||||
while (i < slen)
|
||||
{
|
||||
if (string[i] == CTLESC)
|
||||
{
|
||||
{
|
||||
/* Old code had j++, but we cannot assume that i == j at this
|
||||
point -- what if a CTLNUL has already been removed from the
|
||||
string? We don't want to drop the CTLESC or recopy characters
|
||||
that we've already copied down. */
|
||||
i++; string[j++] = CTLESC;
|
||||
if (i == slen)
|
||||
break;
|
||||
}
|
||||
if (i == slen)
|
||||
break;
|
||||
}
|
||||
else if (string[i] == CTLNUL)
|
||||
i++;
|
||||
i++;
|
||||
|
||||
prev_i = i;
|
||||
ADVANCE_CHAR (string, slen, i);
|
||||
@@ -2840,13 +2857,14 @@ word_list_remove_quoted_nulls (list)
|
||||
/* **************************************************************** */
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
#if 0 /* Currently unused */
|
||||
static unsigned char *
|
||||
mb_getcharlens (string, len)
|
||||
char *string;
|
||||
int len;
|
||||
{
|
||||
int i, offset;
|
||||
unsigned char last, *ret;
|
||||
int i, offset, last;
|
||||
unsigned char *ret;
|
||||
char *p;
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
@@ -2863,6 +2881,7 @@ mb_getcharlens (string, len)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Remove the portion of PARAM matched by PATTERN according to OP, where OP
|
||||
can have one of 4 values:
|
||||
@@ -2878,130 +2897,208 @@ mb_getcharlens (string, len)
|
||||
#define RP_SHORT_RIGHT 4
|
||||
|
||||
static char *
|
||||
remove_pattern (param, pattern, op)
|
||||
remove_upattern (param, pattern, op)
|
||||
char *param, *pattern;
|
||||
int op;
|
||||
{
|
||||
register int len;
|
||||
register char *end;
|
||||
register char *p, *ret, c;
|
||||
int offset;
|
||||
unsigned char *mblen;
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
len = STRLEN (param);
|
||||
end = param + len;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case RP_LONG_LEFT: /* remove longest match at start */
|
||||
for (p = end; p >= param; p--)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
*p = c;
|
||||
return (savestring (p));
|
||||
}
|
||||
*p = c;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_SHORT_LEFT: /* remove shortest match at start */
|
||||
for (p = param; p <= end; p++)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
*p = c;
|
||||
return (savestring (p));
|
||||
}
|
||||
*p = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_LONG_RIGHT: /* remove longest match at end */
|
||||
for (p = param; p <= end; p++)
|
||||
{
|
||||
if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
ret = savestring (param);
|
||||
*p = c;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_SHORT_RIGHT: /* remove shortest match at end */
|
||||
for (p = end; p >= param; p--)
|
||||
{
|
||||
if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
ret = savestring (param);
|
||||
*p = c;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (savestring (param)); /* no match, return original string */
|
||||
}
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
|
||||
#if !defined (HAVE_WCSDUP)
|
||||
static wchar_t *
|
||||
wcsdup (ws)
|
||||
wchar_t *ws;
|
||||
{
|
||||
wchar_t *ret;
|
||||
size_t len;
|
||||
|
||||
len = wcslen (ws);
|
||||
ret = xmalloc ((len + 1) * sizeof (wchar_t));
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
return (wcscpy (ret, ws));
|
||||
}
|
||||
#endif /* !HAVE_WCSDUP */
|
||||
|
||||
static wchar_t *
|
||||
remove_wpattern (wparam, wstrlen, wpattern, op)
|
||||
wchar_t *wparam;
|
||||
size_t wstrlen;
|
||||
wchar_t *wpattern;
|
||||
int op;
|
||||
{
|
||||
wchar_t wc;
|
||||
int n, n1;
|
||||
wchar_t *ret;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case RP_LONG_LEFT: /* remove longest match at start */
|
||||
for (n = wstrlen; n >= 0; n--)
|
||||
{
|
||||
wc = wparam[n]; wparam[n] = L'\0';
|
||||
if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
wparam[n] = wc;
|
||||
return (wcsdup (wparam + n));
|
||||
}
|
||||
wparam[n] = wc;
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_SHORT_LEFT: /* remove shortest match at start */
|
||||
for (n = 0; n <= wstrlen; n++)
|
||||
{
|
||||
wc = wparam[n]; wparam[n] = L'\0';
|
||||
if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
wparam[n] = wc;
|
||||
return (wcsdup (wparam + n));
|
||||
}
|
||||
wparam[n] = wc;
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_LONG_RIGHT: /* remove longest match at end */
|
||||
for (n = 0; n <= wstrlen; n++)
|
||||
{
|
||||
if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
wc = wparam[n]; wparam[n] = L'\0';
|
||||
ret = wcsdup (wparam);
|
||||
wparam[n] = wc;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_SHORT_RIGHT: /* remove shortest match at end */
|
||||
for (n = wstrlen; n >= 0; n--)
|
||||
{
|
||||
if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
wc = wparam[n]; wparam[n] = L'\0';
|
||||
ret = wcsdup (wparam);
|
||||
wparam[n] = wc;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (wcsdup (wparam)); /* no match, return original string */
|
||||
}
|
||||
#endif /* HANDLE_MULTIBYTE */
|
||||
|
||||
static char *
|
||||
remove_pattern (param, pattern, op)
|
||||
char *param, *pattern;
|
||||
int op;
|
||||
{
|
||||
if (param == NULL)
|
||||
return (param);
|
||||
if (*param == '\0' || pattern == NULL || *pattern == '\0') /* minor optimization */
|
||||
return (savestring (param));
|
||||
|
||||
len = STRLEN (param);
|
||||
end = param + len;
|
||||
|
||||
mblen = (unsigned char *)0;
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
if (MB_CUR_MAX > 1 && (op == RP_LONG_LEFT || op == RP_SHORT_RIGHT))
|
||||
mblen = mb_getcharlens (param, len);
|
||||
#endif
|
||||
|
||||
switch (op)
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
case RP_LONG_LEFT: /* remove longest match at start */
|
||||
p = end;
|
||||
while (p >= param)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
*p = c;
|
||||
FREE (mblen);
|
||||
return (savestring (p));
|
||||
}
|
||||
*p = c;
|
||||
wchar_t *ret, *oret;
|
||||
size_t n;
|
||||
wchar_t *wparam, *wpattern;
|
||||
mbstate_t ps;
|
||||
char *xret;
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
while (p >= param)
|
||||
if (mblen[--p - param])
|
||||
break;
|
||||
}
|
||||
else
|
||||
p--;
|
||||
}
|
||||
break;
|
||||
n = xdupmbstowcs (&wpattern, NULL, pattern);
|
||||
if (n == (size_t)-1)
|
||||
return (remove_upattern (param, pattern, op));
|
||||
n = xdupmbstowcs (&wparam, NULL, param);
|
||||
if (n == (size_t)-1)
|
||||
{
|
||||
free (wpattern);
|
||||
return (remove_upattern (param, pattern, op));
|
||||
}
|
||||
oret = ret = remove_wpattern (wparam, n, wpattern, op);
|
||||
|
||||
case RP_SHORT_LEFT: /* remove shortest match at start */
|
||||
p = param;
|
||||
offset = 0;
|
||||
while (p <= end)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
*p = c;
|
||||
return (savestring (p));
|
||||
}
|
||||
*p = c;
|
||||
free (wparam);
|
||||
free (wpattern);
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
ADVANCE_CHAR (param, len, offset);
|
||||
p = param + offset;
|
||||
}
|
||||
else
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_LONG_RIGHT: /* remove longest match at end */
|
||||
p = param;
|
||||
offset = 0;
|
||||
while (p <= end)
|
||||
{
|
||||
if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
ret = savestring (param);
|
||||
*p = c;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
ADVANCE_CHAR (param, len, offset);
|
||||
p = param + offset;
|
||||
}
|
||||
else
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
|
||||
case RP_SHORT_RIGHT: /* remove shortest match at end */
|
||||
p = end;
|
||||
while (p >= param)
|
||||
{
|
||||
if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
ret = savestring (param);
|
||||
*p = c;
|
||||
FREE (mblen);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
while (p >= param)
|
||||
if (mblen[--p - param])
|
||||
break;
|
||||
}
|
||||
else
|
||||
p--;
|
||||
}
|
||||
break;
|
||||
n = strlen (param);
|
||||
xret = xmalloc (n + 1);
|
||||
memset (&ps, '\0', sizeof (mbstate_t));
|
||||
n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps);
|
||||
xret[n] = '\0'; /* just to make sure */
|
||||
free (oret);
|
||||
return xret;
|
||||
}
|
||||
|
||||
FREE (mblen);
|
||||
return (savestring (param)); /* no match, return original string */
|
||||
else
|
||||
#endif
|
||||
return (remove_upattern (param, pattern, op));
|
||||
}
|
||||
|
||||
/* Return 1 of the first character of STRING could match the first
|
||||
@@ -3041,7 +3138,7 @@ match_pattern_char (pat, string)
|
||||
MATCH_BEG and MATCH_END anchor the match at the beginning and end
|
||||
of the string, respectively. The longest match is returned. */
|
||||
static int
|
||||
match_pattern (string, pat, mtype, sp, ep)
|
||||
match_upattern (string, pat, mtype, sp, ep)
|
||||
char *string, *pat;
|
||||
int mtype;
|
||||
char **sp, **ep;
|
||||
@@ -3049,33 +3146,18 @@ match_pattern (string, pat, mtype, sp, ep)
|
||||
int c, len;
|
||||
register char *p, *p1;
|
||||
char *end;
|
||||
int offset;
|
||||
unsigned char *mblen;
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
|
||||
return (0);
|
||||
|
||||
len = STRLEN (string);
|
||||
end = string + len;
|
||||
|
||||
mblen = (unsigned char *)0;
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
if (MB_CUR_MAX > 1 && (mtype == MATCH_ANY || mtype == MATCH_BEG))
|
||||
mblen = mb_getcharlens (string, len);
|
||||
#endif
|
||||
|
||||
switch (mtype)
|
||||
{
|
||||
case MATCH_ANY:
|
||||
p = string;
|
||||
offset = 0;
|
||||
while (p <= end)
|
||||
for (p = string; p <= end; p++)
|
||||
{
|
||||
if (match_pattern_char (pat, p))
|
||||
{
|
||||
p1 = end;
|
||||
while (p1 >= p)
|
||||
for (p1 = end; p1 >= p; p1--)
|
||||
{
|
||||
c = *p1; *p1 = '\0';
|
||||
if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
|
||||
@@ -3083,40 +3165,20 @@ match_pattern (string, pat, mtype, sp, ep)
|
||||
*p1 = c;
|
||||
*sp = p;
|
||||
*ep = p1;
|
||||
FREE (mblen);
|
||||
return 1;
|
||||
}
|
||||
*p1 = c;
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
while (p1 >= p)
|
||||
if (mblen[--p1 - string])
|
||||
break;
|
||||
}
|
||||
else
|
||||
p1--;
|
||||
}
|
||||
}
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
ADVANCE_CHAR (string, len, offset);
|
||||
p = string + offset;
|
||||
}
|
||||
else
|
||||
p++;
|
||||
}
|
||||
|
||||
FREE (mblen);
|
||||
return (0);
|
||||
|
||||
case MATCH_BEG:
|
||||
if (match_pattern_char (pat, string) == 0)
|
||||
return (0);
|
||||
|
||||
p = end;
|
||||
while (p >= string)
|
||||
for (p = end; p >= string; p--)
|
||||
{
|
||||
c = *p; *p = '\0';
|
||||
if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0)
|
||||
@@ -3124,28 +3186,15 @@ match_pattern (string, pat, mtype, sp, ep)
|
||||
*p = c;
|
||||
*sp = string;
|
||||
*ep = p;
|
||||
FREE (mblen);
|
||||
return 1;
|
||||
}
|
||||
*p = c;
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
while (p >= string)
|
||||
if (mblen[--p - string])
|
||||
break;
|
||||
}
|
||||
else
|
||||
p--;
|
||||
}
|
||||
|
||||
FREE (mblen);
|
||||
return (0);
|
||||
|
||||
case MATCH_END:
|
||||
p = string;
|
||||
offset = 0;
|
||||
while (p <= end)
|
||||
for (p = string; p <= end; p++)
|
||||
{
|
||||
if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
|
||||
{
|
||||
@@ -3154,21 +3203,167 @@ match_pattern (string, pat, mtype, sp, ep)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
ADVANCE_CHAR (string, len, offset);
|
||||
p = string + offset;
|
||||
}
|
||||
else
|
||||
p++;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
FREE (mblen);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
/* Return 1 of the first character of WSTRING could match the first
|
||||
character of pattern WPAT. Wide character version. */
|
||||
static int
|
||||
match_pattern_wchar (wpat, wstring)
|
||||
wchar_t *wpat, *wstring;
|
||||
{
|
||||
wchar_t wc;
|
||||
|
||||
if (*wstring == 0)
|
||||
return (0);
|
||||
|
||||
switch (wc = *wpat++)
|
||||
{
|
||||
default:
|
||||
return (*wstring == wc);
|
||||
case L'\\':
|
||||
return (*wstring == *wpat);
|
||||
case L'?':
|
||||
return (*wpat == LPAREN ? 1 : (*wstring != L'\0'));
|
||||
case L'*':
|
||||
return (1);
|
||||
case L'+':
|
||||
case L'!':
|
||||
case L'@':
|
||||
return (*wpat == LPAREN ? 1 : (*wstring == wc));
|
||||
case L'[':
|
||||
return (*wstring != L'\0');
|
||||
}
|
||||
}
|
||||
|
||||
/* Match WPAT anywhere in WSTRING and return the match boundaries.
|
||||
This returns 1 in case of a successful match, 0 otherwise. Wide
|
||||
character version. */
|
||||
static int
|
||||
match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
|
||||
wchar_t *wstring;
|
||||
char **indices;
|
||||
size_t wstrlen;
|
||||
wchar_t *wpat;
|
||||
int mtype;
|
||||
char **sp, **ep;
|
||||
{
|
||||
wchar_t wc;
|
||||
int len;
|
||||
#if 0
|
||||
size_t n, n1; /* Apple's gcc seems to miscompile this badly */
|
||||
#else
|
||||
int n, n1;
|
||||
#endif
|
||||
|
||||
switch (mtype)
|
||||
{
|
||||
case MATCH_ANY:
|
||||
for (n = 0; n <= wstrlen; n++)
|
||||
{
|
||||
if (match_pattern_wchar (wpat, wstring + n))
|
||||
{
|
||||
for (n1 = wstrlen; n1 >= n; n1--)
|
||||
{
|
||||
wc = wstring[n1]; wstring[n1] = L'\0';
|
||||
if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
|
||||
{
|
||||
wstring[n1] = wc;
|
||||
*sp = indices[n];
|
||||
*ep = indices[n1];
|
||||
return 1;
|
||||
}
|
||||
wstring[n1] = wc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
case MATCH_BEG:
|
||||
if (match_pattern_wchar (wpat, wstring) == 0)
|
||||
return (0);
|
||||
|
||||
for (n = wstrlen; n >= 0; n--)
|
||||
{
|
||||
wc = wstring[n]; wstring[n] = L'\0';
|
||||
if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0)
|
||||
{
|
||||
wstring[n] = wc;
|
||||
*sp = indices[0];
|
||||
*ep = indices[n];
|
||||
return 1;
|
||||
}
|
||||
wstring[n] = wc;
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
case MATCH_END:
|
||||
for (n = 0; n <= wstrlen; n++)
|
||||
{
|
||||
if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
|
||||
{
|
||||
*sp = indices[n];
|
||||
*ep = indices[wstrlen];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* HANDLE_MULTIBYTE */
|
||||
|
||||
static int
|
||||
match_pattern (string, pat, mtype, sp, ep)
|
||||
char *string, *pat;
|
||||
int mtype;
|
||||
char **sp, **ep;
|
||||
{
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
int ret;
|
||||
size_t n;
|
||||
wchar_t *wstring, *wpat;
|
||||
char **indices;
|
||||
#endif
|
||||
|
||||
if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
|
||||
return (0);
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
if (MB_CUR_MAX > 1)
|
||||
{
|
||||
n = xdupmbstowcs (&wpat, NULL, pat);
|
||||
if (n == (size_t)-1)
|
||||
return (match_upattern (string, pat, mtype, sp, ep));
|
||||
n = xdupmbstowcs (&wstring, &indices, string);
|
||||
if (n == (size_t)-1)
|
||||
{
|
||||
free (wpat);
|
||||
return (match_upattern (string, pat, mtype, sp, ep));
|
||||
}
|
||||
ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep);
|
||||
|
||||
free (wpat);
|
||||
free (wstring);
|
||||
free (indices);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return (match_upattern (string, pat, mtype, sp, ep));
|
||||
}
|
||||
|
||||
static int
|
||||
getpatspec (c, value)
|
||||
int c;
|
||||
@@ -4850,8 +5045,8 @@ parameter_brace_substring (varname, value, substr, quoted)
|
||||
#if defined (ARRAY_VARS)
|
||||
case VT_ARRAYVAR:
|
||||
/* We want E2 to be the number of elements desired (arrays can be sparse,
|
||||
so verify_substring_values just returns the numbers specified and we
|
||||
rely on array_subrange to understand how to deal with them). */
|
||||
so verify_substring_values just returns the numbers specified and we
|
||||
rely on array_subrange to understand how to deal with them). */
|
||||
tt = array_subrange (array_cell (v), e1, e2, starsub, quoted);
|
||||
if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
|
||||
{
|
||||
@@ -5632,10 +5827,13 @@ param_expand (string, sindex, quoted, expanded_something,
|
||||
string might need it (consider "\"$@\""), but we need some
|
||||
way to signal that the final split on the first character
|
||||
of $IFS should be done, even though QUOTED is 1. */
|
||||
if (list && list->next)
|
||||
{
|
||||
if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
||||
*quoted_dollar_at_p = 1;
|
||||
if (contains_dollar_at)
|
||||
*contains_dollar_at = 1;
|
||||
}
|
||||
|
||||
/* We want to separate the positional parameters with the first
|
||||
character of $IFS in case $IFS is something other than a space.
|
||||
@@ -5779,6 +5977,8 @@ comsub:
|
||||
temp = array_reference (array_cell (var), 0);
|
||||
if (temp)
|
||||
temp = quote_escapes (temp);
|
||||
else if (unbound_vars_is_error)
|
||||
goto unbound_variable;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -5790,6 +5990,7 @@ comsub:
|
||||
|
||||
temp = (char *)NULL;
|
||||
|
||||
unbound_variable:
|
||||
if (unbound_vars_is_error)
|
||||
err_unboundvar (temp1);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user