Imported from ../bash-2.05b.tar.gz.

This commit is contained in:
Jari Aalto 2002-07-17 14:10:11 +00:00
commit 7117c2d221
362 changed files with 34387 additions and 15063 deletions

View file

@ -39,6 +39,8 @@ MV = mv
SHELL = @MAKE_SHELL@
PROFILE_FLAGS = @PROFILE_FLAGS@
CFLAGS = @CFLAGS@
LOCAL_CFLAGS = @LOCAL_CFLAGS@
CPPFLAGS = @CPPFLAGS@
@ -51,7 +53,8 @@ BASHINCDIR = ${topdir}/include
INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) ${INCLUDES} $(LOCAL_CFLAGS) $(CFLAGS)
CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) ${INCLUDES} \
$(LOCAL_CFLAGS) $(CFLAGS)
# Here is a rule for making .o files from .c files that doesn't force
# the type of the machine (like -sun3) into the flags.
@ -62,12 +65,13 @@ CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) ${INCLUDES} $(LOCAL_CFLAGS) $(CFLAGS
LIBRARY_NAME = libglob.a
# The C code source files for this library.
CSOURCES = $(srcdir)/glob.c $(srcdir)/strmatch.c
CSOURCES = $(srcdir)/glob.c $(srcdir)/strmatch.c $(srcdir)/smatch.c \
$(srcdir)/xmbsrtowcs.c
# The header files for this library.
HSOURCES = $(srcdir)/strmatch.h
OBJECTS = glob.o strmatch.o
OBJECTS = glob.o strmatch.o smatch.o xmbsrtowcs.o
# The texinfo files which document this library.
DOCSOURCE = doc/glob.texi
@ -121,15 +125,34 @@ mostlyclean: clean
# #
######################################################################
smatch.o: strmatch.h
smatch.o: $(BUILD_DIR)/config.h
smatch.o: $(BASHINCDIR)/chartypes.h
smatch.o: $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h
smatch.o: $(BASHINCDIR)/shmbutil.h
smatch.o: $(topdir)/xmalloc.h
strmatch.o: strmatch.h
strmatch.o: $(BUILD_DIR)/config.h
strmatch.o: $(BASHINCDIR)/chartypes.h
strmatch.o: $(BASHINCDIR)/stdc.h
glob.o: $(BUILD_DIR)/config.h
glob.o: $(topdir)/bashtypes.h $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h
glob.o: $(BASHINCDIR)/posixstat.h $(BASHINCDIR)/memalloc.h
glob.o: strmatch.h
glob.o: strmatch.h glob.h
glob.o: $(BASHINCDIR)/shmbutil.h
glob.o: $(topdir)/xmalloc.h
xmbsrtowcs.o: ${BUILD_DIR}/config.h
xmbsrtowcs.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
xmbsrtowcs.o: ${BASHINCDIR}/shmbutil.h
# Rules for deficient makes, like SunOS and Solaris
strmatch.o: strmatch.c
glob.o: glob.c
strmatch.o: strmatch.c
smatch.o: smatch.c
xmbsrtowcs.o: xmbsrtowcs.c
# dependencies for C files that include other C files
glob.o: glob_loop.c
smatch.o: sm_loop.c

View file

@ -1,141 +1,140 @@
/* collsyms.h -- collating symbol names and their corresponding characters
(in ascii) as given by POSIX.2 in table 2.8. */
/* Copyright (C) 1997 Free Software Foundation, Inc.
/* Copyright (C) 1997-2002 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. */
#ifndef _COLLSYMS_H_
# define _COLLSYSMS_H_
/* The upper-case letters, lower-case letters, and digits are omitted from
this table. The digits are not included in the table in the POSIX.2
spec. The upper and lower case letters are translated by the code
in fnmatch.c:collsym(). */
in smatch.c:collsym(). */
typedef struct _collsym {
char *name;
char code;
} COLLSYM;
typedef struct _COLLSYM {
XCHAR *name;
CHAR code;
} __COLLSYM;
static COLLSYM posix_collsyms[] =
static __COLLSYM POSIXCOLL [] =
{
{ "NUL", '\0' },
{ "SOH", '\001' },
{ "STX", '\002' },
{ "ETX", '\003' },
{ "EOT", '\004' },
{ "ENQ", '\005' },
{ "ACK", '\006' },
{ L("NUL"), L('\0') },
{ L("SOH"), L('\001') },
{ L("STX"), L('\002') },
{ L("ETX"), L('\003') },
{ L("EOT"), L('\004') },
{ L("ENQ"), L('\005') },
{ L("ACK"), L('\006') },
#ifdef __STDC__
{ "alert", '\a' },
{ L("alert"), L('\a') },
#else
{ "alert", '\007' },
{ L("alert"), L('\007') },
#endif
{ "BS", '\010' },
{ "backspace", '\b' },
{ "HT", '\011' },
{ "tab", '\t' },
{ "LF", '\012' },
{ "newline", '\n' },
{ "VT", '\013' },
{ "vertical-tab", '\v' },
{ "FF", '\014' },
{ "form-feed", '\f' },
{ "CR", '\015' },
{ "carriage-return", '\r' },
{ "SO", '\016' },
{ "SI", '\017' },
{ "DLE", '\020' },
{ "DC1", '\021' },
{ "DC2", '\022' },
{ "DC3", '\023' },
{ "DC4", '\024' },
{ "NAK", '\025' },
{ "SYN", '\026' },
{ "ETB", '\027' },
{ "CAN", '\030' },
{ "EM", '\031' },
{ "SUB", '\032' },
{ "ESC", '\033' },
{ "IS4", '\034' },
{ "FS", '\034' },
{ "IS3", '\035' },
{ "GS", '\035' },
{ "IS2", '\036' },
{ "RS", '\036' },
{ "IS1", '\037' },
{ "US", '\037' },
{ "space", ' ' },
{ "exclamation-mark", '!' },
{ "quotation-mark", '"' },
{ "number-sign", '#' },
{ "dollar-sign", '$' },
{ "percent-sign", '%' },
{ "ampersand", '&' },
{ "apostrophe", '\'' },
{ "left-parenthesis", '(' },
{ "right-parenthesis", ')' },
{ "asterisk", '*' },
{ "plus-sign", '+' },
{ "comma", ',' },
{ "hyphen", '-' },
{ "hyphen-minus", '-' },
{ "minus", '-' }, /* extension from POSIX.2 */
{ "dash", '-' }, /* extension from POSIX.2 */
{ "period", '.' },
{ "full-stop", '.' },
{ "slash", '/' },
{ "solidus", '/' }, /* extension from POSIX.2 */
{ "zero", '0' },
{ "one", '1' },
{ "two", '2' },
{ "three", '3' },
{ "four", '4' },
{ "five", '5' },
{ "six", '6' },
{ "seven", '7' },
{ "eight", '8' },
{ "nine", '9' },
{ "colon", ':' },
{ "semicolon", ';' },
{ "less-than-sign", '<' },
{ "equals-sign", '=' },
{ "greater-than-sign", '>' },
{ "question-mark", '?' },
{ "commercial-at", '@' },
{ L("BS"), L('\010') },
{ L("backspace"), L('\b') },
{ L("HT"), L('\011') },
{ L("tab"), L('\t') },
{ L("LF"), L('\012') },
{ L("newline"), L('\n') },
{ L("VT"), L('\013') },
{ L("vertical-tab"), L('\v') },
{ L("FF"), L('\014') },
{ L("form-feed"), L('\f') },
{ L("CR"), L('\015') },
{ L("carriage-return"), L('\r') },
{ L("SO"), L('\016') },
{ L("SI"), L('\017') },
{ L("DLE"), L('\020') },
{ L("DC1"), L('\021') },
{ L("DC2"), L('\022') },
{ L("DC3"), L('\023') },
{ L("DC4"), L('\024') },
{ L("NAK"), L('\025') },
{ L("SYN"), L('\026') },
{ L("ETB"), L('\027') },
{ L("CAN"), L('\030') },
{ L("EM"), L('\031') },
{ L("SUB"), L('\032') },
{ L("ESC"), L('\033') },
{ L("IS4"), L('\034') },
{ L("FS"), L('\034') },
{ L("IS3"), L('\035') },
{ L("GS"), L('\035') },
{ L("IS2"), L('\036') },
{ L("RS"), L('\036') },
{ L("IS1"), L('\037') },
{ L("US"), L('\037') },
{ L("space"), L(' ') },
{ L("exclamation-mark"), L('!') },
{ L("quotation-mark"), L('"') },
{ L("number-sign"), L('#') },
{ L("dollar-sign"), L('$') },
{ L("percent-sign"), L('%') },
{ L("ampersand"), L('&') },
{ L("apostrophe"), L('\'') },
{ L("left-parenthesis"), L('(') },
{ L("right-parenthesis"), L(')') },
{ L("asterisk"), L('*') },
{ L("plus-sign"), L('+') },
{ L("comma"), L(',') },
{ L("hyphen"), L('-') },
{ L("hyphen-minus"), L('-') },
{ L("minus"), L('-') }, /* extension from POSIX.2 */
{ L("dash"), L('-') }, /* extension from POSIX.2 */
{ L("period"), L('.') },
{ L("full-stop"), L('.') },
{ L("slash"), L('/') },
{ L("solidus"), L('/') }, /* extension from POSIX.2 */
{ L("zero"), L('0') },
{ L("one"), L('1') },
{ L("two"), L('2') },
{ L("three"), L('3') },
{ L("four"), L('4') },
{ L("five"), L('5') },
{ L("six"), L('6') },
{ L("seven"), L('7') },
{ L("eight"), L('8') },
{ L("nine"), L('9') },
{ L("colon"), L(':') },
{ L("semicolon"), L(';') },
{ L("less-than-sign"), L('<') },
{ L("equals-sign"), L('=') },
{ L("greater-than-sign"), L('>') },
{ L("question-mark"), L('?') },
{ L("commercial-at"), L('@') },
/* upper-case letters omitted */
{ "left-square-bracket",'[' },
{ "backslash", '\\' },
{ "reverse-solidus", '\\' },
{ "right-square-bracket",']' },
{ "circumflex", '^' },
{ "circumflex-accent", '^' }, /* extension from POSIX.2 */
{ "underscore", '_' },
{ "grave-accent", '`' },
{ L("left-square-bracket"), L('[') },
{ L("backslash"), L('\\') },
{ L("reverse-solidus"), L('\\') },
{ L("right-square-bracket"), L(']') },
{ L("circumflex"), L('^') },
{ L("circumflex-accent"), L('^') }, /* extension from POSIX.2 */
{ L("underscore"), L('_') },
{ L("grave-accent"), L('`') },
/* lower-case letters omitted */
{ "left-brace", '{' }, /* extension from POSIX.2 */
{ "left-curly-bracket",'{' },
{ "vertical-line", '|' },
{ "right-brace", '}' }, /* extension from POSIX.2 */
{ "right-curly-bracket",'}' },
{ "tilde", '~' },
{ "DEL", '\177' },
{ L("left-brace"), L('{') }, /* extension from POSIX.2 */
{ L("left-curly-bracket"), L('{') },
{ L("vertical-line"), L('|') },
{ L("right-brace"), L('}') }, /* extension from POSIX.2 */
{ L("right-curly-bracket"), L('}') },
{ L("tilde"), L('~') },
{ L("DEL"), L('\177') },
{ 0, 0 },
};
#endif
#undef _COLLSYM
#undef __COLLSYM
#undef POSIXCOLL

View file

@ -1,5 +1,6 @@
/* File-name wildcard pattern matching for GNU.
Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
/* glob.c -- file-name wildcard pattern matching for Bash.
Copyright (C) 1985-2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,81 +26,33 @@
#pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */
#if defined (SHELL)
# include "bashtypes.h"
#else
# include <sys/types.h>
#endif
#include "bashtypes.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (SHELL)
# include "bashansi.h"
#else
# if defined (HAVE_STDLIB_H)
# include <stdlib.h>
# endif
# if defined (HAVE_STRING_H)
# include <string.h>
# else /* !HAVE_STRING_H */
# include <strings.h>
# endif /* !HAVE_STRING_H */
#endif
#if defined (HAVE_DIRENT_H)
# include <dirent.h>
# define D_NAMLEN(d) strlen ((d)->d_name)
#else /* !HAVE_DIRENT_H */
# define D_NAMLEN(d) ((d)->d_namlen)
# if defined (HAVE_SYS_NDIR_H)
# include <sys/ndir.h>
# endif
# if defined (HAVE_SYS_DIR_H)
# include <sys/dir.h>
# endif /* HAVE_SYS_DIR_H */
# if defined (HAVE_NDIR_H)
# include <ndir.h>
# endif
# if !defined (dirent)
# define dirent direct
# endif
#endif /* !HAVE_DIRENT_H */
#if defined (_POSIX_SOURCE) && !defined (STRUCT_DIRENT_HAS_D_INO) || defined (BROKEN_DIRENT_D_INO)
/* Posix does not require that the d_ino field be present, and some
systems do not provide it. */
# define REAL_DIR_ENTRY(dp) 1
#else
# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
#endif /* _POSIX_SOURCE */
#if !defined (HAVE_BCOPY) && !defined (bcopy)
# define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
#endif /* !HAVE_BCOPY && !bcopy */
#if defined (SHELL)
# include "posixstat.h"
#else /* !SHELL */
# include <sys/stat.h>
#endif /* !SHELL */
#include "bashansi.h"
#include "posixdir.h"
#include "posixstat.h"
#include "shmbutil.h"
#include "xmalloc.h"
#include "filecntl.h"
#if !defined (F_OK)
# define F_OK 0
#endif
#if defined (SHELL)
# include "memalloc.h"
#endif
#include "stdc.h"
#include "memalloc.h"
#include "quit.h"
#include "glob.h"
#include "strmatch.h"
#if !defined (HAVE_STDLIB_H) && !defined (SHELL)
extern char *malloc (), *realloc ();
extern void free ();
#endif /* !HAVE_STDLIB_H */
#if !defined (HAVE_BCOPY) && !defined (bcopy)
# define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
#endif /* !HAVE_BCOPY && !bcopy */
#if !defined (NULL)
# if defined (__STDC__)
@ -109,13 +62,10 @@ extern void free ();
# endif /* __STDC__ */
#endif /* !NULL */
#if defined (SHELL)
extern void throw_to_top_level ();
extern int test_eaccess ();
extern void throw_to_top_level __P((void));
extern int test_eaccess __P((char *, int));
extern int interrupt_state;
extern int extended_glob;
#endif /* SHELL */
/* Global variable which controls whether or not * matches .*.
Non-zero means don't match .*. */
@ -128,51 +78,152 @@ int glob_ignore_case = 0;
/* Global variable to return to signify an error in globbing. */
char *glob_error_return;
/* Return nonzero if PATTERN has any special globbing chars in it. */
/* Some forward declarations. */
static int skipname __P((char *, char *));
#if HANDLE_MULTIBYTE
static int mbskipname __P((char *, char *));
#endif
#if HANDLE_MULTIBYTE
static void udequote_pathname __P((char *));
static void wdequote_pathname __P((char *));
#else
# define dequote_pathname udequote_pathname
#endif
static void dequote_pathname __P((char *));
static int glob_testdir __P((char *));
static char **glob_dir_to_array __P((char *, char **, int));
/* Compile `glob_loop.c' for single-byte characters. */
#define CHAR unsigned char
#define INT int
#define L(CS) CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_pattern_p
#include "glob_loop.c"
/* Compile `glob_loop.c' again for multibyte characters. */
#if HANDLE_MULTIBYTE
#define CHAR wchar_t
#define INT wint_t
#define L(CS) L##CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_wpattern_p
#include "glob_loop.c"
#endif /* HANDLE_MULTIBYTE */
/* And now a function that calls either the single-byte or multibyte version
of internal_glob_pattern_p. */
int
glob_pattern_p (pattern)
const char *pattern;
{
register const char *p;
register char c;
int bopen;
#if HANDLE_MULTIBYTE
mbstate_t ps;
size_t n;
wchar_t *wpattern;
int r;
p = pattern;
bopen = 0;
if (MB_CUR_MAX == 1)
return (internal_glob_pattern_p (pattern));
while ((c = *p++) != '\0')
switch (c)
{
case '?':
case '*':
return (1);
case '[': /* Only accept an open brace if there is a close */
bopen++; /* brace to match it. Bracket expressions must be */
continue; /* complete, according to Posix.2 */
case ']':
if (bopen)
return (1);
continue;
case '+': /* extended matching operators */
case '@':
case '!':
if (*p == '(') /*) */
return (1);
continue;
case '\\':
if (*p++ == '\0')
return (0);
}
return (0);
/* Convert strings to wide chars, and call the multibyte version. */
memset (&ps, '\0', sizeof (ps));
n = xmbsrtowcs (NULL, (const char **)&pattern, 0, &ps);
if (n == (size_t)-1)
/* Oops. Invalid multibyte sequence. Try it as single-byte sequence. */
return (internal_glob_pattern_p (pattern));
wpattern = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
(void) xmbsrtowcs (wpattern, (const char **)&pattern, n + 1, &ps);
r = internal_glob_wpattern_p (wpattern);
free (wpattern);
return r;
#else
return (internal_glob_pattern_p (pattern));
#endif
}
/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned
with matching leading `.'. */
static int
skipname (pat, dname)
char *pat;
char *dname;
{
/* If a leading dot need not be explicitly matched, and the pattern
doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.') &&
(dname[0] == '.' &&
(dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
return 1;
/* If a dot must be explicity matched, check to see if they do. */
else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.'))
return 1;
return 0;
}
#if HANDLE_MULTIBYTE
/* Return 1 if DNAME should be skipped according to PAT. Handles multibyte
characters in PAT and DNAME. Mostly concerned with matching leading `.'. */
static int
mbskipname (pat, dname)
char *pat, *dname;
{
char *pat_bak, *dn_bak;
wchar_t *pat_wc, *dn_wc;
mbstate_t pat_ps, dn_ps;
size_t pat_n, dn_n, n;
n = strlen(pat);
pat_bak = (char *) alloca (n + 1);
memcpy (pat_bak, pat, n + 1);
n = strlen(dname);
dn_bak = (char *) alloca (n + 1);
memcpy (dn_bak, dname, n + 1);
memset(&pat_ps, '\0', sizeof(mbstate_t));
memset(&dn_ps, '\0', sizeof(mbstate_t));
pat_n = xmbsrtowcs (NULL, (const char **)&pat_bak, 0, &pat_ps);
dn_n = xmbsrtowcs (NULL, (const char **)&dn_bak, 0, &dn_ps);
if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
{
pat_wc = (wchar_t *) alloca ((pat_n + 1) * sizeof(wchar_t));
dn_wc = (wchar_t *) alloca ((dn_n + 1) * sizeof(wchar_t));
(void) xmbsrtowcs (pat_wc, (const char **)&pat_bak, pat_n + 1, &pat_ps);
(void) xmbsrtowcs (dn_wc, (const char **)&dn_bak, dn_n + 1, &dn_ps);
/* If a leading dot need not be explicitly matched, and the
pattern doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.') &&
(dn_wc[0] == L'.' &&
(dn_wc[1] == L'\0' || (dn_wc[1] == L'.' && dn_wc[2] == L'\0'))))
return 1;
/* If a leading dot must be explicity matched, check to see if the
pattern and dirname both have one. */
else if (noglob_dot_filenames && dn_wc[0] == L'.' &&
pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.'))
return 1;
}
return 0;
}
#endif /* HANDLE_MULTIBYTE */
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
dequote_pathname (pathname)
udequote_pathname (pathname)
char *pathname;
{
register int i, j;
@ -190,18 +241,71 @@ dequote_pathname (pathname)
pathname[j] = '\0';
}
#if HANDLE_MULTIBYTE
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
wdequote_pathname (pathname)
char *pathname;
{
mbstate_t ps;
size_t len, n;
wchar_t *wpathname;
char *pathname_bak;
int i, j;
len = strlen (pathname);
pathname_bak = (char *) alloca (len + 1);
memcpy (pathname_bak, pathname , len + 1);
/* Convert the strings into wide characters. */
memset (&ps, '\0', sizeof (ps));
n = xmbsrtowcs (NULL, (const char **)&pathname_bak, 0, &ps);
if (n == (size_t) -1)
/* Something wrong. */
return;
wpathname = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
(void) xmbsrtowcs (wpathname, (const char **)&pathname_bak, n + 1, &ps);
for (i = j = 0; wpathname && wpathname[i]; )
{
if (wpathname[i] == L'\\')
i++;
wpathname[j++] = wpathname[i++];
if (!wpathname[i - 1])
break;
}
wpathname[j] = L'\0';
/* Convert the wide character string into unibyte character set. */
memset (&ps, '\0', sizeof(mbstate_t));
n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
pathname[len] = '\0';
}
static void
dequote_pathname (pathname)
char *pathname;
{
if (MB_CUR_MAX > 1)
wdequote_pathname (pathname);
else
udequote_pathname (pathname);
}
#endif /* HANDLE_MULTIBYTE */
/* Test whether NAME exists. */
#if defined (HAVE_LSTAT)
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
# if defined (SHELL) && !defined (AFS)
# if !defined (AFS)
# define GLOB_TESTNAME(name) (test_eaccess (nextname, F_OK))
# else /* !SHELL || AFS */
# else /* AFS */
# define GLOB_TESTNAME(name) (access (nextname, F_OK))
# endif /* !SHELL || AFS */
# endif /* AFS */
#endif /* !HAVE_LSTAT */
/* Return 0 if DIR is a directory, -1 otherwise. */
@ -237,9 +341,10 @@ glob_testdir (dir)
Look in errno for more information. */
char **
glob_vector (pat, dir)
glob_vector (pat, dir, flags)
char *pat;
char *dir;
int flags;
{
struct globval
{
@ -256,7 +361,7 @@ glob_vector (pat, dir)
int lose, skip;
register char **name_vector;
register unsigned int i;
int flags; /* Flags passed to strmatch (). */
int mflags; /* Flags passed to strmatch (). */
lastlink = 0;
count = lose = skip = 0;
@ -344,17 +449,15 @@ glob_vector (pat, dir)
/* Compute the flags that will be passed to strmatch(). We don't
need to do this every time through the loop. */
flags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
#ifdef FNM_CASEFOLD
if (glob_ignore_case)
flags |= FNM_CASEFOLD;
mflags |= FNM_CASEFOLD;
#endif
#ifdef SHELL
if (extended_glob)
flags |= FNM_EXTMATCH;
#endif
mflags |= FNM_EXTMATCH;
/* Scan the directory, finding all names that match.
For each name that matches, allocate a struct globval
@ -362,14 +465,12 @@ glob_vector (pat, dir)
Chain those structs together; lastlink is the front of the chain. */
while (1)
{
#if defined (SHELL)
/* Make globbing interruptible in the shell. */
if (interrupt_state)
{
lose = 1;
break;
}
#endif /* SHELL */
dp = readdir (d);
if (dp == NULL)
@ -379,22 +480,15 @@ glob_vector (pat, dir)
if (REAL_DIR_ENTRY (dp) == 0)
continue;
/* If a leading dot need not be explicitly matched, and the pattern
doesn't start with a `.', don't match `.' or `..' */
#define dname dp->d_name
if (noglob_dot_filenames == 0 && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.') &&
(dname[0] == '.' &&
(dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
#undef dname
#if HANDLE_MULTIBYTE
if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name))
continue;
else
#endif
if (skipname (pat, dp->d_name))
continue;
/* If a dot must be explicity matched, check to see if they do. */
if (noglob_dot_filenames && dp->d_name[0] == '.' && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.'))
continue;
if (strmatch (pat, dp->d_name, flags) != FNM_NOMATCH)
if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
{
nextlink = (struct globval *) alloca (sizeof (struct globval));
nextlink->next = lastlink;
@ -429,10 +523,8 @@ glob_vector (pat, dir)
free (lastlink->name);
lastlink = lastlink->next;
}
#if defined (SHELL)
if (interrupt_state)
throw_to_top_level ();
#endif /* SHELL */
QUIT;
return ((char **)NULL);
}
@ -447,22 +539,40 @@ glob_vector (pat, dir)
name_vector[count] = NULL;
return (name_vector);
}
/* Return a new array which is the concatenation of each string in ARRAY
to DIR. This function expects you to pass in an allocated ARRAY, and
it takes care of free()ing that array. Thus, you might think of this
function as side-effecting ARRAY. */
function as side-effecting ARRAY. This should handle GX_MARKDIRS. */
static char **
glob_dir_to_array (dir, array)
glob_dir_to_array (dir, array, flags)
char *dir, **array;
int flags;
{
register unsigned int i, l;
int add_slash;
char **result;
char **result, *new;
struct stat sb;
l = strlen (dir);
if (l == 0)
return (array);
{
if (flags & GX_MARKDIRS)
for (i = 0; array[i]; i++)
{
if ((stat (array[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
l = strlen (array[i]);
new = (char *)realloc (array[i], l + 2);
if (new == 0)
return NULL;
new[l] = '/';
new[l+1] = '\0';
array[i] = new;
}
}
return (array);
}
add_slash = dir[l - 1] != '/';
@ -476,8 +586,9 @@ glob_dir_to_array (dir, array)
for (i = 0; array[i] != NULL; i++)
{
result[i] = (char *) malloc (l + (add_slash ? 1 : 0)
+ strlen (array[i]) + 1);
/* 3 == 1 for NUL, 1 for slash at end of DIR, 1 for GX_MARKDIRS */
result[i] = (char *) malloc (l + strlen (array[i]) + 3);
if (result[i] == NULL)
return (NULL);
@ -485,6 +596,16 @@ glob_dir_to_array (dir, array)
if (add_slash)
result[i][l] = '/';
strcpy (result[i] + l + add_slash, array[i]);
if (flags & GX_MARKDIRS)
{
if ((stat (result[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
size_t rlen;
rlen = strlen (result[i]);
result[i][rlen] = '/';
result[i][rlen+1] = '\0';
}
}
}
result[i] = NULL;
@ -495,15 +616,16 @@ glob_dir_to_array (dir, array)
return (result);
}
/* Do globbing on PATHNAME. Return an array of pathnames that match,
marking the end of the array with a null-pointer as an element.
If no pathnames match, then the array is empty (first element is null).
If there isn't enough memory, then return NULL.
If a file system error occurs, return -1; `errno' has the error code. */
char **
glob_filename (pathname)
glob_filename (pathname, flags)
char *pathname;
int flags;
{
char **result;
unsigned int result_size;
@ -545,7 +667,7 @@ glob_filename (pathname)
if (directory_name[directory_len - 1] == '/')
directory_name[directory_len - 1] = '\0';
directories = glob_filename (directory_name);
directories = glob_filename (directory_name, flags & ~GX_MARKDIRS);
if (directories == NULL)
goto memory_error;
@ -571,7 +693,7 @@ glob_filename (pathname)
/* Scan directory even on a NULL pathname. That way, `*h/'
returns only directories ending in `h', instead of all
files ending in `h' with a `/' appended. */
temp_results = glob_vector (filename, directories[i]);
temp_results = glob_vector (filename, directories[i], flags & ~GX_MARKDIRS);
/* Handle error cases. */
if (temp_results == NULL)
@ -584,7 +706,7 @@ glob_filename (pathname)
char **array;
register unsigned int l;
array = glob_dir_to_array (directories[i], temp_results);
array = glob_dir_to_array (directories[i], temp_results, flags);
l = 0;
while (array[l] != NULL)
++l;
@ -619,6 +741,7 @@ glob_filename (pathname)
result = (char **) realloc ((char *) result, 2 * sizeof (char *));
if (result == NULL)
return (NULL);
/* Handle GX_MARKDIRS here. */
result[0] = (char *) malloc (directory_len + 1);
if (result[0] == NULL)
goto memory_error;
@ -642,13 +765,14 @@ glob_filename (pathname)
/* Just return what glob_vector () returns appended to the
directory name. */
temp_results =
glob_vector (filename, (directory_len == 0 ? "." : directory_name));
temp_results = glob_vector (filename,
(directory_len == 0 ? "." : directory_name),
flags & ~GX_MARKDIRS);
if (temp_results == NULL || temp_results == (char **)&glob_error_return)
return (temp_results);
return (glob_dir_to_array (directory_name, temp_results));
return (glob_dir_to_array (directory_name, temp_results, flags));
}
/* We get to memory_error if the program has run out of memory, or
@ -661,13 +785,12 @@ glob_filename (pathname)
free (result[i]);
free ((char *) result);
}
#if defined (SHELL)
if (interrupt_state)
throw_to_top_level ();
#endif /* SHELL */
QUIT;
return (NULL);
}
#if defined (TEST)
main (argc, argv)
@ -678,7 +801,7 @@ main (argc, argv)
for (i = 1; i < argc; ++i)
{
char **value = glob_filename (argv[i]);
char **value = glob_filename (argv[i], 0);
if (value == NULL)
puts ("Out of memory.");
else if (value == &glob_error_return)

View file

@ -20,9 +20,13 @@
#include "stdc.h"
#define GX_MARKDIRS 0x01 /* mark directory names with trailing `/' */
#define GX_NOCASE 0x02 /* ignore case */
#define GX_MATCHDOT 0x04 /* match `.' literally */
extern int glob_pattern_p __P((const char *));
extern char **glob_vector __P((char *, char *));
extern char **glob_filename __P((char *));
extern char **glob_vector __P((char *, char *, int));
extern char **glob_filename __P((char *, int));
extern char *glob_error_return;
extern int noglob_dot_filenames;

67
lib/glob/glob_loop.c Normal file
View file

@ -0,0 +1,67 @@
/* Copyright (C) 1991-2002 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. */
static int INTERNAL_GLOB_PATTERN_P __P((const CHAR *));
/* Return nonzero if PATTERN has any special globbing chars in it.
Compiled twice, once each for single-byte and multibyte characters. */
static int
INTERNAL_GLOB_PATTERN_P (pattern)
const CHAR *pattern;
{
register const CHAR *p;
register CHAR c;
int bopen;
p = pattern;
bopen = 0;
while ((c = *p++) != L('\0'))
switch (c)
{
case L('?'):
case L('*'):
return 1;
case L('['): /* Only accept an open brace if there is a close */
bopen++; /* brace to match it. Bracket expressions must be */
continue; /* complete, according to Posix.2 */
case L(']'):
if (bopen)
return 1;
continue;
case L('+'): /* extended matching operators */
case L('@'):
case L('!'):
if (*p == L('(')) /*) */
return 1;
continue;
case L('\\'):
if (*p++ == L('\0'))
return 0;
}
return 0;
}
#undef INTERNAL_GLOB_PATTERN_P
#undef L
#undef INT
#undef CHAR

737
lib/glob/sm_loop.c Normal file
View file

@ -0,0 +1,737 @@
/* Copyright (C) 1991-2002 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. */
static int FCT __P((CHAR *, CHAR *, int));
static int GMATCH __P((CHAR *, CHAR *, CHAR *, CHAR *, int));
static CHAR *PARSE_COLLSYM __P((CHAR *, INT *));
static CHAR *BRACKMATCH __P((CHAR *, U_CHAR, int));
static int EXTMATCH __P((INT, CHAR *, CHAR *, CHAR *, CHAR *, int));
static CHAR *PATSCAN __P((CHAR *, CHAR *, INT));
static int
FCT (pattern, string, flags)
CHAR *pattern;
CHAR *string;
int flags;
{
CHAR *se, *pe;
if (string == 0 || pattern == 0)
return FNM_NOMATCH;
se = string + STRLEN ((XCHAR *)string);
pe = pattern + STRLEN ((XCHAR *)pattern);
return (GMATCH (string, se, pattern, pe, flags));
}
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, FNM_NOMATCH if not. */
static int
GMATCH (string, se, pattern, pe, flags)
CHAR *string, *se;
CHAR *pattern, *pe;
int flags;
{
CHAR *p, *n; /* pattern, string */
INT c; /* current pattern character - XXX U_CHAR? */
INT sc; /* current string character - XXX U_CHAR? */
p = pattern;
n = string;
if (string == 0 || pattern == 0)
return FNM_NOMATCH;
#if DEBUG_MATCHING
fprintf(stderr, "gmatch: string = %s; se = %s\n", string, se);
fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe);
#endif
while (p < pe)
{
c = *p++;
c = FOLD (c);
sc = n < se ? *n : '\0';
#ifdef EXTENDED_GLOB
/* EXTMATCH () will handle recursively calling GMATCH, so we can
just return what EXTMATCH() returns. */
if ((flags & FNM_EXTMATCH) && *p == L('(') &&
(c == L('+') || c == L('*') || c == L('?') || c == L('@') || c == L('!'))) /* ) */
{
int lflags;
/* If we're not matching the start of the string, we're not
concerned about the special cases for matching `.' */
lflags = (n == string) ? flags : (flags & ~FNM_PERIOD);
return (EXTMATCH (c, n, se, p, pe, lflags));
}
#endif /* EXTENDED_GLOB */
switch (c)
{
case L('?'): /* Match single character */
if (sc == '\0')
return FNM_NOMATCH;
else if ((flags & FNM_PATHNAME) && sc == L('/'))
/* If we are matching a pathname, `?' can never match a `/'. */
return FNM_NOMATCH;
else if ((flags & FNM_PERIOD) && sc == L('.') &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
/* `?' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
return FNM_NOMATCH;
break;
case L('\\'): /* backslash escape removes special meaning */
if (p == pe)
return FNM_NOMATCH;
if ((flags & FNM_NOESCAPE) == 0)
{
c = *p++;
/* A trailing `\' cannot match. */
if (p > pe)
return FNM_NOMATCH;
c = FOLD (c);
}
if (FOLD (sc) != (U_CHAR)c)
return FNM_NOMATCH;
break;
case '*': /* Match zero or more characters */
if (p == pe)
return 0;
if ((flags & FNM_PERIOD) && sc == L('.') &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
/* `*' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
return FNM_NOMATCH;
/* Collapse multiple consecutive `*' and `?', but make sure that
one character of the string is consumed for each `?'. */
for (c = *p++; (c == L('?') || c == L('*')); c = *p++)
{
if ((flags & FNM_PATHNAME) && sc == L('/'))
/* A slash does not match a wildcard under FNM_PATHNAME. */
return FNM_NOMATCH;
else if (c == L('?'))
{
if (sc == L('\0'))
return FNM_NOMATCH;
/* One character of the string is consumed in matching
this ? wildcard, so *??? won't match if there are
fewer than three characters. */
n++;
sc = n < se ? *n : '\0';
}
#ifdef EXTENDED_GLOB
/* Handle ******(patlist) */
if ((flags & FNM_EXTMATCH) && c == L('*') && *p == L('(')) /*)*/
{
CHAR *newn;
/* We need to check whether or not the extended glob
pattern matches the remainder of the string.
If it does, we match the entire pattern. */
for (newn = n; newn < se; ++newn)
{
if (EXTMATCH (c, newn, se, p, pe, flags) == 0)
return (0);
}
/* We didn't match the extended glob pattern, but
that's OK, since we can match 0 or more occurrences.
We need to skip the glob pattern and see if we
match the rest of the string. */
newn = PATSCAN (p + 1, pe, 0);
/* If NEWN is 0, we have an ill-formed pattern. */
p = newn ? newn : pe;
}
#endif
if (p == pe)
break;
}
/* If we've hit the end of the pattern and the last character of
the pattern was handled by the loop above, we've succeeded.
Otherwise, we need to match that last character. */
if (p == pe && (c == L('?') || c == L('*')))
return (0);
/* General case, use recursion. */
{
U_CHAR c1;
c1 = ((flags & FNM_NOESCAPE) == 0 && c == L('\\')) ? *p : c;
c1 = FOLD (c1);
for (--p; n < se; ++n)
{
/* Only call strmatch if the first character indicates a
possible match. We can check the first character if
we're not doing an extended glob match. */
if ((flags & FNM_EXTMATCH) == 0 && c != L('[') && FOLD (*n) != c1) /*]*/
continue;
/* If we're doing an extended glob match and the pattern is not
one of the extended glob patterns, we can check the first
character. */
if ((flags & FNM_EXTMATCH) && p[1] != L('(') && /*)*/
STRCHR (L("?*+@!"), *p) == 0 && c != L('[') && FOLD (*n) != c1) /*]*/
continue;
/* Otherwise, we just recurse. */
if (GMATCH (n, se, p, pe, flags & ~FNM_PERIOD) == 0)
return (0);
}
return FNM_NOMATCH;
}
case L('['):
{
if (sc == L('\0') || n == se)
return FNM_NOMATCH;
/* A character class cannot match a `.' if it is the first
character of the string or if it is the first character
following a slash and we are matching a pathname. */
if ((flags & FNM_PERIOD) && sc == L('.') &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
return (FNM_NOMATCH);
p = BRACKMATCH (p, sc, flags);
if (p == 0)
return FNM_NOMATCH;
}
break;
default:
if ((U_CHAR)c != FOLD (sc))
return (FNM_NOMATCH);
}
++n;
}
if (n == se)
return (0);
if ((flags & FNM_LEADING_DIR) && *n == L('/'))
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
return 0;
return (FNM_NOMATCH);
}
/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find
the value of the symbol, and move P past the collating symbol expression.
The value is returned in *VP, if VP is not null. */
static CHAR *
PARSE_COLLSYM (p, vp)
CHAR *p;
INT *vp;
{
register int pc;
INT val;
p++; /* move past the `.' */
for (pc = 0; p[pc]; pc++)
if (p[pc] == L('.') && p[pc+1] == L(']'))
break;
val = COLLSYM (p, pc);
if (vp)
*vp = val;
return (p + pc + 2);
}
/* Use prototype definition here because of type promotion. */
static CHAR *
#if defined (PROTOTYPES)
BRACKMATCH (CHAR *p, U_CHAR test, int flags)
#else
BRACKMATCH (p, test, flags)
CHAR *p;
U_CHAR test;
int flags;
#endif
{
register CHAR cstart, cend, c;
register int not; /* Nonzero if the sense of the character class is inverted. */
int brcnt;
INT pc;
CHAR *savep;
test = FOLD (test);
savep = p;
/* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the
circumflex (`^') in its role in a `nonmatching list'. A bracket
expression starting with an unquoted circumflex character produces
unspecified results. This implementation treats the two identically. */
if (not = (*p == L('!') || *p == L('^')))
++p;
c = *p++;
for (;;)
{
/* Initialize cstart and cend in case `-' is the last
character of the pattern. */
cstart = cend = c;
/* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find
the end of the equivalence class, move the pattern pointer past
it, and check for equivalence. XXX - this handles only
single-character equivalence classes, which is wrong, or at
least incomplete. */
if (c == L('[') && *p == L('=') && p[2] == L('=') && p[3] == L(']'))
{
pc = FOLD (p[1]);
p += 4;
if (COLLEQUIV (test, pc))
{
/*[*/ /* Move past the closing `]', since the first thing we do at
the `matched:' label is back p up one. */
p++;
goto matched;
}
else
{
c = *p++;
if (c == L('\0'))
return ((test == L('[')) ? savep : (CHAR *)0); /*]*/
c = FOLD (c);
continue;
}
}
/* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */
if (c == L('[') && *p == L(':'))
{
CHAR *close, *ccname;
pc = 0; /* make sure invalid char classes don't match. */
/* Find end of character class name */
for (close = p + 1; *close != '\0'; close++)
if (*close == L(':') && *(close+1) == L(']'))
break;
if (*close != L('\0'))
{
ccname = (CHAR *)malloc ((close - p) * sizeof (CHAR));
if (ccname == 0)
pc = 0;
else
{
bcopy (p + 1, ccname, (close - p - 1) * sizeof (CHAR));
*(ccname + (close - p - 1)) = L('\0');
pc = IS_CCLASS (test, ccname);
}
if (pc == -1)
pc = 0;
else
p = close + 2;
free (ccname);
}
if (pc)
{
/*[*/ /* Move past the closing `]', since the first thing we do at
the `matched:' label is back p up one. */
p++;
goto matched;
}
else
{
/* continue the loop here, since this expression can't be
the first part of a range expression. */
c = *p++;
if (c == L('\0'))
return ((test == L('[')) ? savep : (CHAR *)0);
else if (c == L(']'))
break;
c = FOLD (c);
continue;
}
}
/* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of
the symbol name, make sure it is terminated by `.]', translate
the name to a character using the external table, and do the
comparison. */
if (c == L('[') && *p == L('.'))
{
p = PARSE_COLLSYM (p, &pc);
/* An invalid collating symbol cannot be the first point of a
range. If it is, we set cstart to one greater than `test',
so any comparisons later will fail. */
cstart = (pc == INVALID) ? test + 1 : pc;
}
if (!(flags & FNM_NOESCAPE) && c == L('\\'))
{
if (*p == '\0')
return (CHAR *)0;
cstart = cend = *p++;
}
cstart = cend = FOLD (cstart);
/* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
is not preceded by a backslash and is not part of a bracket
expression produces undefined results.' This implementation
treats the `[' as just a character to be matched if there is
not a closing `]'. */
if (c == L('\0'))
return ((test == L('[')) ? savep : (CHAR *)0);
c = *p++;
c = FOLD (c);
if ((flags & FNM_PATHNAME) && c == L('/'))
/* [/] can never match when matching a pathname. */
return (CHAR *)0;
/* This introduces a range, unless the `-' is the last
character of the class. Find the end of the range
and move past it. */
if (c == L('-') && *p != L(']'))
{
cend = *p++;
if (!(flags & FNM_NOESCAPE) && cend == L('\\'))
cend = *p++;
if (cend == L('\0'))
return (CHAR *)0;
if (cend == L('[') && *p == L('.'))
{
p = PARSE_COLLSYM (p, &pc);
/* An invalid collating symbol cannot be the second part of a
range expression. If we get one, we set cend to one fewer
than the test character to make sure the range test fails. */
cend = (pc == INVALID) ? test - 1 : pc;
}
cend = FOLD (cend);
c = *p++;
/* POSIX.2 2.8.3.2: ``The ending range point shall collate
equal to or higher than the starting range point; otherwise
the expression shall be treated as invalid.'' Note that this
applies to only the range expression; the rest of the bracket
expression is still checked for matches. */
if (RANGECMP (cstart, cend) > 0)
{
if (c == L(']'))
break;
c = FOLD (c);
continue;
}
}
if (RANGECMP (test, cstart) >= 0 && RANGECMP (test, cend) <= 0)
goto matched;
if (c == L(']'))
break;
}
/* No match. */
return (!not ? (CHAR *)0 : p);
matched:
/* Skip the rest of the [...] that already matched. */
c = *--p;
brcnt = 1;
while (brcnt > 0)
{
/* A `[' without a matching `]' is just another character to match. */
if (c == L('\0'))
return ((test == L('[')) ? savep : (CHAR *)0);
c = *p++;
if (c == L('[') && (*p == L('=') || *p == L(':') || *p == L('.')))
brcnt++;
else if (c == L(']'))
brcnt--;
else if (!(flags & FNM_NOESCAPE) && c == L('\\'))
{
if (*p == '\0')
return (CHAR *)0;
/* XXX 1003.2d11 is unclear if this is right. */
++p;
}
}
return (not ? (CHAR *)0 : p);
}
#if defined (EXTENDED_GLOB)
/* ksh-like extended pattern matching:
[?*+@!](pat-list)
where pat-list is a list of one or patterns separated by `|'. Operation
is as follows:
?(patlist) match zero or one of the given patterns
*(patlist) match zero or more of the given patterns
+(patlist) match one or more of the given patterns
@(patlist) match exactly one of the given patterns
!(patlist) match anything except one of the given patterns
*/
/* Scan a pattern starting at STRING and ending at END, keeping track of
embedded () and []. If DELIM is 0, we scan until a matching `)'
because we're scanning a `patlist'. Otherwise, we scan until we see
DELIM. In all cases, we never scan past END. The return value is the
first character after the matching DELIM. */
static CHAR *
PATSCAN (string, end, delim)
CHAR *string, *end;
INT delim;
{
int pnest, bnest;
INT cchar;
CHAR *s, c, *bfirst;
pnest = bnest = 0;
cchar = 0;
bfirst = NULL;
for (s = string; c = *s; s++)
{
if (s >= end)
return (s);
switch (c)
{
case L('\0'):
return ((CHAR *)NULL);
/* `[' is not special inside a bracket expression, but it may
introduce one of the special POSIX bracket expressions
([.SYM.], [=c=], [: ... :]) that needs special handling. */
case L('['):
if (bnest == 0)
{
bfirst = s + 1;
if (*bfirst == L('!') || *bfirst == L('^'))
bfirst++;
bnest++;
}
else if (s[1] == L(':') || s[1] == L('.') || s[1] == L('='))
cchar = s[1];
break;
/* `]' is not special if it's the first char (after a leading `!'
or `^') in a bracket expression or if it's part of one of the
special POSIX bracket expressions ([.SYM.], [=c=], [: ... :]) */
case L(']'):
if (bnest)
{
if (cchar && s[-1] == cchar)
cchar = 0;
else if (s != bfirst)
{
bnest--;
bfirst = 0;
}
}
break;
case L('('):
if (bnest == 0)
pnest++;
break;
case L(')'):
if (bnest == 0 && pnest-- <= 0)
return ++s;
break;
case L('|'):
if (bnest == 0 && pnest == 0 && delim == L('|'))
return ++s;
break;
}
}
return (NULL);
}
/* Return 0 if dequoted pattern matches S in the current locale. */
static int
STRCOMPARE (p, pe, s, se)
CHAR *p, *pe, *s, *se;
{
int ret;
CHAR c1, c2;
c1 = *pe;
c2 = *se;
*pe = *se = '\0';
#if HAVE_MULTIBYTE || defined (HAVE_STRCOLL)
ret = STRCOLL ((XCHAR *)p, (XCHAR *)s);
#else
ret = STRCMP ((XCHAR *)p, (XCHAR *)s);
#endif
*pe = c1;
*se = c2;
return (ret == 0 ? ret : FNM_NOMATCH);
}
/* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or
0 on success. This is handed the entire rest of the pattern and string
the first time an extended pattern specifier is encountered, so it calls
gmatch recursively. */
static int
EXTMATCH (xc, s, se, p, pe, flags)
INT xc; /* select which operation */
CHAR *s, *se;
CHAR *p, *pe;
int flags;
{
CHAR *prest; /* pointer to rest of pattern */
CHAR *psub; /* pointer to sub-pattern */
CHAR *pnext; /* pointer to next sub-pattern */
CHAR *srest; /* pointer to rest of string */
int m1, m2;
#if DEBUG_MATCHING
fprintf(stderr, "extmatch: xc = %c\n", xc);
fprintf(stderr, "extmatch: s = %s; se = %s\n", s, se);
fprintf(stderr, "extmatch: p = %s; pe = %s\n", p, pe);
#endif
prest = PATSCAN (p + (*p == L('(')), pe, 0); /* ) */
if (prest == 0)
/* If PREST is 0, we failed to scan a valid pattern. In this
case, we just want to compare the two as strings. */
return (STRCOMPARE (p - 1, pe, s, se));
switch (xc)
{
case L('+'): /* match one or more occurrences */
case L('*'): /* match zero or more occurrences */
/* If we can get away with no matches, don't even bother. Just
call GMATCH on the rest of the pattern and return success if
it succeeds. */
if (xc == L('*') && (GMATCH (s, se, prest, pe, flags) == 0))
return 0;
/* OK, we have to do this the hard way. First, we make sure one of
the subpatterns matches, then we try to match the rest of the
string. */
for (psub = p + 1; ; psub = pnext)
{
pnext = PATSCAN (psub, pe, L('|'));
for (srest = s; srest <= se; srest++)
{
/* Match this substring (S -> SREST) against this
subpattern (psub -> pnext - 1) */
m1 = GMATCH (s, srest, psub, pnext - 1, flags) == 0;
/* OK, we matched a subpattern, so make sure the rest of the
string matches the rest of the pattern. Also handle
multiple matches of the pattern. */
if (m1)
m2 = (GMATCH (srest, se, prest, pe, flags) == 0) ||
(s != srest && GMATCH (srest, se, p - 1, pe, flags) == 0);
if (m1 && m2)
return (0);
}
if (pnext == prest)
break;
}
return (FNM_NOMATCH);
case L('?'): /* match zero or one of the patterns */
case L('@'): /* match exactly one of the patterns */
/* If we can get away with no matches, don't even bother. Just
call gmatch on the rest of the pattern and return success if
it succeeds. */
if (xc == L('?') && (GMATCH (s, se, prest, pe, flags) == 0))
return 0;
/* OK, we have to do this the hard way. First, we see if one of
the subpatterns matches, then, if it does, we try to match the
rest of the string. */
for (psub = p + 1; ; psub = pnext)
{
pnext = PATSCAN (psub, pe, L('|'));
srest = (prest == pe) ? se : s;
for ( ; srest <= se; srest++)
{
if (GMATCH (s, srest, psub, pnext - 1, flags) == 0 &&
GMATCH (srest, se, prest, pe, flags) == 0)
return (0);
}
if (pnext == prest)
break;
}
return (FNM_NOMATCH);
case '!': /* match anything *except* one of the patterns */
for (srest = s; srest <= se; srest++)
{
m1 = 0;
for (psub = p + 1; ; psub = pnext)
{
pnext = PATSCAN (psub, pe, L('|'));
/* If one of the patterns matches, just bail immediately. */
if (m1 = (GMATCH (s, srest, psub, pnext - 1, flags) == 0))
break;
if (pnext == prest)
break;
}
if (m1 == 0 && GMATCH (srest, se, prest, pe, flags) == 0)
return (0);
}
return (FNM_NOMATCH);
}
return (FNM_NOMATCH);
}
#endif /* EXTENDED_GLOB */
#undef IS_CCLASS
#undef FOLD
#undef CHAR
#undef U_CHAR
#undef XCHAR
#undef INT
#undef INVALID
#undef FCT
#undef GMATCH
#undef COLLSYM
#undef PARSE_COLLSYM
#undef PATSCAN
#undef STRCOMPARE
#undef EXTMATCH
#undef BRACKMATCH
#undef STRCHR
#undef STRCOLL
#undef STRLEN
#undef STRCMP
#undef COLLEQUIV
#undef RANGECMP
#undef L

410
lib/glob/smatch.c Normal file
View file

@ -0,0 +1,410 @@
/* strmatch.c -- ksh-like extended pattern matching for the shell and filename
globbing. */
/* Copyright (C) 1991-2002 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>
#include <stdio.h> /* for debugging */
#include "strmatch.h"
#include <chartypes.h>
#include "bashansi.h"
#include "shmbutil.h"
#include "xmalloc.h"
/* First, compile `sm_loop.c' for single-byte characters. */
#define CHAR unsigned char
#define U_CHAR unsigned char
#define XCHAR char
#define INT int
#define L(CS) CS
#define INVALID -1
#undef STREQ
#undef STREQN
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
/* We use strcoll(3) for range comparisons in bracket expressions,
even though it can have unwanted side effects in locales
other than POSIX or US. For instance, in the de locale, [A-Z] matches
all characters. */
#if defined (HAVE_STRCOLL)
/* Helper function for collating symbol equivalence. */
static int rangecmp (c1, c2)
int c1, c2;
{
static char s1[2] = { ' ', '\0' };
static char s2[2] = { ' ', '\0' };
int ret;
/* Eight bits only. Period. */
c1 &= 0xFF;
c2 &= 0xFF;
if (c1 == c2)
return (0);
s1[0] = c1;
s2[0] = c2;
if ((ret = strcoll (s1, s2)) != 0)
return ret;
return (c1 - c2);
}
#else /* !HAVE_STRCOLL */
# define rangecmp(c1, c2) ((int)(c1) - (int)(c2))
#endif /* !HAVE_STRCOLL */
#if defined (HAVE_STRCOLL)
static int
collequiv (c1, c2)
int c1, c2;
{
return (rangecmp (c1, c2) == 0);
}
#else
# define collequiv(c1, c2) ((c1) == (c2))
#endif
#define _COLLSYM _collsym
#define __COLLSYM __collsym
#define POSIXCOLL posix_collsyms
#include "collsyms.h"
static int
collsym (s, len)
char *s;
int len;
{
register struct _collsym *csp;
for (csp = posix_collsyms; csp->name; csp++)
{
if (STREQN(csp->name, s, len) && csp->name[len] == '\0')
return (csp->code);
}
if (len == 1)
return s[0];
return INVALID;
}
/* unibyte character classification */
#if !defined (isascii) && !defined (HAVE_ISASCII)
# define isascii(c) ((unsigned int)(c) <= 0177)
#endif
enum char_class
{
CC_NO_CLASS = 0,
CC_ASCII, CC_ALNUM, CC_ALPHA, CC_BLANK, CC_CNTRL, CC_DIGIT, CC_GRAPH,
CC_LOWER, CC_PRINT, CC_PUNCT, CC_SPACE, CC_UPPER, CC_WORD, CC_XDIGIT
};
static char const *const cclass_name[] =
{
"",
"ascii", "alnum", "alpha", "blank", "cntrl", "digit", "graph",
"lower", "print", "punct", "space", "upper", "word", "xdigit"
};
#define N_CHAR_CLASS (sizeof(cclass_name) / sizeof (cclass_name[0]))
static int
is_cclass (c, name)
int c;
const char *name;
{
enum char_class char_class = CC_NO_CLASS;
int i, result;
for (i = 1; i < N_CHAR_CLASS; i++)
{
if (STREQ (name, cclass_name[i]))
{
char_class = (enum char_class)i;
break;
}
}
if (char_class == 0)
return -1;
switch (char_class)
{
case CC_ASCII:
result = isascii (c);
break;
case CC_ALNUM:
result = ISALNUM (c);
break;
case CC_ALPHA:
result = ISALPHA (c);
break;
case CC_BLANK:
result = ISBLANK (c);
break;
case CC_CNTRL:
result = ISCNTRL (c);
break;
case CC_DIGIT:
result = ISDIGIT (c);
break;
case CC_GRAPH:
result = ISGRAPH (c);
break;
case CC_LOWER:
result = ISLOWER (c);
break;
case CC_PRINT:
result = ISPRINT (c);
break;
case CC_PUNCT:
result = ISPUNCT (c);
break;
case CC_SPACE:
result = ISSPACE (c);
break;
case CC_UPPER:
result = ISUPPER (c);
break;
case CC_WORD:
result = (ISALNUM (c) || c == '_');
break;
case CC_XDIGIT:
result = ISXDIGIT (c);
break;
default:
result = -1;
break;
}
return result;
}
/* Now include `sm_loop.c' for single-byte characters. */
/* The result of FOLD is an `unsigned char' */
# define FOLD(c) ((flags & FNM_CASEFOLD) \
? TOLOWER ((unsigned char)c) \
: ((unsigned char)c))
#define FCT internal_strmatch
#define GMATCH gmatch
#define COLLSYM collsym
#define PARSE_COLLSYM parse_collsym
#define BRACKMATCH brackmatch
#define PATSCAN patscan
#define STRCOMPARE strcompare
#define EXTMATCH extmatch
#define STRCHR(S, C) strchr((S), (C))
#define STRCOLL(S1, S2) strcoll((S1), (S2))
#define STRLEN(S) strlen(S)
#define STRCMP(S1, S2) strcmp((S1), (S2))
#define RANGECMP(C1, C2) rangecmp((C1), (C2))
#define COLLEQUIV(C1, C2) collequiv((C1), (C2))
#define CTYPE_T enum char_class
#define IS_CCLASS(C, S) is_cclass((C), (S))
#include "sm_loop.c"
#if HANDLE_MULTIBYTE
# define CHAR wchar_t
# define U_CHAR wint_t
# define XCHAR wchar_t
# define INT wint_t
# define L(CS) L##CS
# define INVALID WEOF
# undef STREQ
# undef STREQN
# define STREQ(s1, s2) ((wcscmp (s1, s2) == 0))
# define STREQN(a, b, n) ((a)[0] == (b)[0] && wcsncmp(a, b, n) == 0)
static int
rangecmp_wc (c1, c2)
wint_t c1, c2;
{
static wchar_t s1[2] = { L' ', L'\0' };
static wchar_t s2[2] = { L' ', L'\0' };
int ret;
if (c1 == c2)
return 0;
s1[0] = c1;
s2[0] = c2;
return (wcscoll (s1, s2));
}
static int
collequiv_wc (c, equiv)
wint_t c, equiv;
{
return (!(c - equiv));
}
/* Helper function for collating symbol. */
# define _COLLSYM _collwcsym
# define __COLLSYM __collwcsym
# define POSIXCOLL posix_collwcsyms
# include "collsyms.h"
static wint_t
collwcsym (s, len)
wchar_t *s;
int len;
{
register struct _collwcsym *csp;
for (csp = posix_collwcsyms; csp->name; csp++)
{
if (STREQN(csp->name, s, len) && csp->name[len] == L'\0')
return (csp->code);
}
if (len == 1)
return s[0];
return INVALID;
}
static int
is_wcclass (wc, name)
wint_t wc;
wchar_t *name;
{
char *mbs;
mbstate_t state;
size_t mbslength;
wctype_t desc;
int want_word;
if ((wctype ("ascii") == (wctype_t)0) && (wcscmp (name, L"ascii") == 0))
{
int c;
if ((c = wctob (wc)) == EOF)
return 0;
else
return (c <= 0x7F);
}
want_word = (wcscmp (name, L"word") == 0);
if (want_word)
name = L"alnum";
memset (&state, '\0', sizeof (mbstate_t));
mbs = (char *) malloc (wcslen(name) * MB_CUR_MAX + 1);
mbslength = wcsrtombs(mbs, (const wchar_t **)&name, (wcslen(name) * MB_CUR_MAX + 1), &state);
if (mbslength == (size_t)-1 || mbslength == (size_t)-2)
{
free (mbs);
return -1;
}
desc = wctype (mbs);
free (mbs);
if (desc == (wctype_t)0)
return -1;
if (want_word)
return (iswctype (wc, desc) || wc == L'_');
else
return (iswctype (wc, desc));
}
/* Now include `sm_loop.c' for multibyte characters. */
#define FOLD(c) ((flags & FNM_CASEFOLD) && iswupper (c) ? towlower (c) : (c))
#define FCT internal_wstrmatch
#define GMATCH gmatch_wc
#define COLLSYM collwcsym
#define PARSE_COLLSYM parse_collwcsym
#define BRACKMATCH brackmatch_wc
#define PATSCAN patscan_wc
#define STRCOMPARE wscompare
#define EXTMATCH extmatch_wc
#define STRCHR(S, C) wcschr((S), (C))
#define STRCOLL(S1, S2) wcscoll((S1), (S2))
#define STRLEN(S) wcslen(S)
#define STRCMP(S1, S2) wcscmp((S1), (S2))
#define RANGECMP(C1, C2) rangecmp_wc((C1), (C2))
#define COLLEQUIV(C1, C2) collequiv_wc((C1), (C2))
#define CTYPE_T enum char_class
#define IS_CCLASS(C, S) is_wcclass((C), (S))
#include "sm_loop.c"
#endif /* HAVE_MULTIBYTE */
int
xstrmatch (pattern, string, flags)
char *pattern;
char *string;
int flags;
{
#if HANDLE_MULTIBYTE
int ret;
mbstate_t ps;
size_t n;
char *pattern_bak;
wchar_t *wpattern, *wstring;
if (MB_CUR_MAX == 1)
return (internal_strmatch (pattern, string, flags));
pattern_bak = (char *)xmalloc (strlen (pattern) + 1);
strcpy (pattern_bak, pattern);
memset (&ps, '\0', sizeof (mbstate_t));
n = xmbsrtowcs (NULL, (const char **)&pattern, 0, &ps);
if (n == (size_t)-1 || n == (size_t)-2)
{
free (pattern_bak);
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
}
wpattern = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
(void) xmbsrtowcs (wpattern, (const char **)&pattern, n + 1, &ps);
memset (&ps, '\0', sizeof (mbstate_t));
n = xmbsrtowcs (NULL, (const char **)&string, 0, &ps);
if (n == (size_t)-1 || n == (size_t)-2)
{
free (wpattern);
ret = internal_strmatch (pattern_bak, string, flags);
free (pattern_bak);
return ret;
}
wstring = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
(void) xmbsrtowcs (wstring, (const char **)&string, n + 1, &ps);
ret = internal_wstrmatch (wpattern, wstring, flags);
free (pattern_bak);
free (wpattern);
free (wstring);
return ret;
#else
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
#endif /* !HANDLE_MULTIBYTE */
}

View file

@ -1,7 +1,7 @@
/* strmatch.c -- ksh-like extended pattern matching for the shell and filename
globbing. */
/* Copyright (C) 1991, 1997 Free Software Foundation, Inc.
/* Copyright (C) 1991-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -21,805 +21,29 @@
#include <config.h>
#include <stdio.h> /* for debugging */
#include "stdc.h"
#include "strmatch.h"
#include "collsyms.h"
#include <chartypes.h>
#if defined (HAVE_STRING_H)
# include <string.h>
/* Structured this way so that if HAVE_LIBC_FNM_EXTMATCH is defined, the
matching portion of the library (smatch.c) is not linked into the shell. */
#ifndef HAVE_LIBC_FNM_EXTMATCH
extern int xstrmatch __P((char *, char *, int));
#else
# include <strings.h>
#endif /* HAVE_STRING_H */
static int gmatch ();
static char *brackmatch ();
#ifdef EXTENDED_GLOB
static int extmatch ();
static char *patscan ();
#endif
#if !defined (isascii) && !defined (HAVE_ISASCII)
# define isascii(c) ((unsigned int)(c) <= 0177)
# define xstrmatch fnmatch
#endif
/* The result of FOLD is an `unsigned char' */
# define FOLD(c) ((flags & FNM_CASEFOLD) \
? TOLOWER ((unsigned char)c) \
: ((unsigned char)c))
#ifndef STREQ
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
#endif
/* We use strcoll(3) for range comparisons in bracket expressions,
even though it can have unwanted side effects in locales
other than POSIX or US. For instance, in the de locale, [A-Z] matches
all characters. */
#if defined (HAVE_STRCOLL)
/* Helper function for collating symbol equivalence. */
static int rangecmp (c1, c2)
int c1, c2;
{
static char s1[2] = { ' ', '\0' };
static char s2[2] = { ' ', '\0' };
int ret;
/* Eight bits only. Period. */
c1 &= 0xFF;
c2 &= 0xFF;
if (c1 == c2)
return (0);
s1[0] = c1;
s2[0] = c2;
if ((ret = strcoll (s1, s2)) != 0)
return ret;
return (c1 - c2);
}
#else /* !HAVE_STRCOLL */
# define rangecmp(c1, c2) ((int)(c1) - (int)(c2))
#endif /* !HAVE_STRCOLL */
#if defined (HAVE_STRCOLL)
static int collequiv (c1, c2)
int c1, c2;
{
return (rangecmp (c1, c2) == 0);
}
#else
# define collequiv(c1, c2) ((c1) == (c2))
#endif
static int
collsym (s, len)
char *s;
int len;
{
register struct _collsym *csp;
for (csp = posix_collsyms; csp->name; csp++)
{
if (STREQN(csp->name, s, len) && csp->name[len] == '\0')
return (csp->code);
}
if (len == 1)
return s[0];
return -1;
}
#ifdef HAVE_LIBC_FNM_EXTMATCH
int
strmatch (pattern, string, flags)
char *pattern;
char *string;
int flags;
{
char *se, *pe;
if (string == 0 || pattern == 0)
return FNM_NOMATCH;
return (fnmatch (pattern, string, flags));
return (xstrmatch (pattern, string, flags));
}
#else /* !HAVE_LIBC_FNM_EXTMATCH */
int
strmatch (pattern, string, flags)
char *pattern;
char *string;
int flags;
{
char *se, *pe;
if (string == 0 || pattern == 0)
return FNM_NOMATCH;
se = string + strlen (string);
pe = pattern + strlen (pattern);
return (gmatch (string, se, pattern, pe, flags));
}
#endif /* !HAVE_LIBC_FNM_EXTMATCH */
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, FNM_NOMATCH if not. */
static int
gmatch (string, se, pattern, pe, flags)
char *string, *se;
char *pattern, *pe;
int flags;
{
register char *p, *n; /* pattern, string */
register char c; /* current pattern character */
register char sc; /* current string character */
p = pattern;
n = string;
if (string == 0 || pattern == 0)
return FNM_NOMATCH;
#if DEBUG_MATCHING
fprintf(stderr, "gmatch: string = %s; se = %s\n", string, se);
fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe);
#endif
while (p < pe)
{
c = *p++;
c = FOLD (c);
sc = n < se ? *n : '\0';
#ifdef EXTENDED_GLOB
/* extmatch () will handle recursively calling gmatch, so we can
just return what extmatch() returns. */
if ((flags & FNM_EXTMATCH) && *p == '(' &&
(c == '+' || c == '*' || c == '?' || c == '@' || c == '!')) /* ) */
{
int lflags;
/* If we're not matching the start of the string, we're not
concerned about the special cases for matching `.' */
lflags = (n == string) ? flags : (flags & ~FNM_PERIOD);
return (extmatch (c, n, se, p, pe, lflags));
}
#endif
switch (c)
{
case '?': /* Match single character */
if (sc == '\0')
return FNM_NOMATCH;
else if ((flags & FNM_PATHNAME) && sc == '/')
/* If we are matching a pathname, `?' can never match a `/'. */
return FNM_NOMATCH;
else if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
/* `?' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
return FNM_NOMATCH;
break;
case '\\': /* backslash escape removes special meaning */
if (p == pe)
return FNM_NOMATCH;
if ((flags & FNM_NOESCAPE) == 0)
{
c = *p++;
/* A trailing `\' cannot match. */
if (p > pe)
return FNM_NOMATCH;
c = FOLD (c);
}
if (FOLD (sc) != (unsigned char)c)
return FNM_NOMATCH;
break;
case '*': /* Match zero or more characters */
if (p == pe)
return 0;
if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
/* `*' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
return FNM_NOMATCH;
/* Collapse multiple consecutive `*' and `?', but make sure that
one character of the string is consumed for each `?'. */
for (c = *p++; (c == '?' || c == '*'); c = *p++)
{
if ((flags & FNM_PATHNAME) && sc == '/')
/* A slash does not match a wildcard under FNM_PATHNAME. */
return FNM_NOMATCH;
else if (c == '?')
{
if (sc == '\0')
return FNM_NOMATCH;
/* One character of the string is consumed in matching
this ? wildcard, so *??? won't match if there are
fewer than three characters. */
n++;
sc = n < se ? *n : '\0';
}
#ifdef EXTENDED_GLOB
/* Handle ******(patlist) */
if ((flags & FNM_EXTMATCH) && c == '*' && *p == '(') /*)*/
{
char *newn;
/* We need to check whether or not the extended glob
pattern matches the remainder of the string.
If it does, we match the entire pattern. */
for (newn = n; newn < se; ++newn)
{
if (extmatch (c, newn, se, p, pe, flags) == 0)
return (0);
}
/* We didn't match the extended glob pattern, but
that's OK, since we can match 0 or more occurrences.
We need to skip the glob pattern and see if we
match the rest of the string. */
newn = patscan (p + 1, pe, 0);
/* If NEWN is 0, we have an ill-formed pattern. */
p = newn ? newn : pe;
}
#endif
if (p == pe)
break;
}
/* If we've hit the end of the pattern and the last character of
the pattern was handled by the loop above, we've succeeded.
Otherwise, we need to match that last character. */
if (p == pe && (c == '?' || c == '*'))
return (0);
/* General case, use recursion. */
{
unsigned char c1;
c1 = (unsigned char)((flags & FNM_NOESCAPE) == 0 && c == '\\') ? *p : c;
c1 = FOLD (c1);
for (--p; n < se; ++n)
{
/* Only call strmatch if the first character indicates a
possible match. We can check the first character if
we're not doing an extended glob match. */
if ((flags & FNM_EXTMATCH) == 0 && c != '[' && FOLD (*n) != c1) /*]*/
continue;
/* If we're doing an extended glob match and the pattern is not
one of the extended glob patterns, we can check the first
character. */
if ((flags & FNM_EXTMATCH) && p[1] != '(' && /*)*/
strchr ("?*+@!", *p) == 0 && c != '[' && FOLD (*n) != c1) /*]*/
continue;
/* Otherwise, we just recurse. */
if (gmatch (n, se, p, pe, flags & ~FNM_PERIOD) == 0)
return (0);
}
return FNM_NOMATCH;
}
case '[':
{
if (sc == '\0' || n == se)
return FNM_NOMATCH;
/* A character class cannot match a `.' if it is the first
character of the string or if it is the first character
following a slash and we are matching a pathname. */
if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
return (FNM_NOMATCH);
p = brackmatch (p, sc, flags);
if (p == 0)
return FNM_NOMATCH;
}
break;
default:
if ((unsigned char)c != FOLD (sc))
return (FNM_NOMATCH);
}
++n;
}
if (n == se)
return (0);
if ((flags & FNM_LEADING_DIR) && *n == '/')
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
return 0;
return (FNM_NOMATCH);
}
/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find
the value of the symbol, and move P past the collating symbol expression.
The value is returned in *VP, if VP is not null. */
static char *
parse_collsym (p, vp)
char *p;
int *vp;
{
register int pc;
int val;
p++; /* move past the `.' */
for (pc = 0; p[pc]; pc++)
if (p[pc] == '.' && p[pc+1] == ']')
break;
val = collsym (p, pc);
if (vp)
*vp = val;
return (p + pc + 2);
}
static char *
brackmatch (p, test, flags)
char *p;
unsigned char test;
int flags;
{
register char cstart, cend, c;
register int not; /* Nonzero if the sense of the character class is inverted. */
int pc, brcnt;
char *savep;
test = FOLD (test);
savep = p;
/* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the
circumflex (`^') in its role in a `nonmatching list'. A bracket
expression starting with an unquoted circumflex character produces
unspecified results. This implementation treats the two identically. */
if (not = (*p == '!' || *p == '^'))
++p;
c = *p++;
for (;;)
{
/* Initialize cstart and cend in case `-' is the last
character of the pattern. */
cstart = cend = c;
/* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find
the end of the equivalence class, move the pattern pointer past
it, and check for equivalence. XXX - this handles only
single-character equivalence classes, which is wrong, or at
least incomplete. */
if (c == '[' && *p == '=' && p[2] == '=' && p[3] == ']')
{
pc = FOLD (p[1]);
p += 4;
if (collequiv (test, pc))
{
/*[*/ /* Move past the closing `]', since the first thing we do at
the `matched:' label is back p up one. */
p++;
goto matched;
}
else
{
c = *p++;
if (c == '\0')
return ((test == '[') ? savep : (char *)0); /*]*/
c = FOLD (c);
continue;
}
}
/* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */
if (c == '[' && *p == ':') /*]*/
{
pc = 0; /* make sure invalid char classes don't match. */
if (STREQN (p+1, "alnum:]", 7))
{ pc = ISALNUM (test); p += 8; }
else if (STREQN (p+1, "alpha:]", 7))
{ pc = ISALPHA (test); p += 8; }
else if (STREQN (p+1, "blank:]", 7))
{ pc = ISBLANK (test); p += 8; }
else if (STREQN (p+1, "cntrl:]", 7))
{ pc = ISCNTRL (test); p += 8; }
else if (STREQN (p+1, "digit:]", 7))
{ pc = ISDIGIT (test); p += 8; }
else if (STREQN (p+1, "graph:]", 7))
{ pc = ISGRAPH (test); p += 8; }
else if (STREQN (p+1, "lower:]", 7))
{ pc = ISLOWER (test); p += 8; }
else if (STREQN (p+1, "print:]", 7))
{ pc = ISPRINT (test); p += 8; }
else if (STREQN (p+1, "punct:]", 7))
{ pc = ISPUNCT (test); p += 8; }
else if (STREQN (p+1, "space:]", 7))
{ pc = ISSPACE (test); p += 8; }
else if (STREQN (p+1, "upper:]", 7))
{ pc = ISUPPER (test); p += 8; }
else if (STREQN (p+1, "xdigit:]", 8))
{ pc = ISXDIGIT (test); p += 9; }
else if (STREQN (p+1, "ascii:]", 7))
{ pc = isascii (test); p += 8; }
if (pc)
{
/*[*/ /* Move past the closing `]', since the first thing we do at
the `matched:' label is back p up one. */
p++;
goto matched;
}
else
{
/* continue the loop here, since this expression can't be
the first part of a range expression. */
c = *p++;
if (c == '\0')
return ((test == '[') ? savep : (char *)0);
else if (c == ']')
break;
c = FOLD (c);
continue;
}
}
/* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of
the symbol name, make sure it is terminated by `.]', translate
the name to a character using the external table, and do the
comparison. */
if (c == '[' && *p == '.')
{
p = parse_collsym (p, &pc);
/* An invalid collating symbol cannot be the first point of a
range. If it is, we set cstart to one greater than `test',
so any comparisons later will fail. */
cstart = (pc == -1) ? test + 1 : pc;
}
if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return (char *)0;
cstart = cend = *p++;
}
cstart = cend = FOLD (cstart);
/* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
is not preceded by a backslash and is not part of a bracket
expression produces undefined results.' This implementation
treats the `[' as just a character to be matched if there is
not a closing `]'. */
if (c == '\0')
return ((test == '[') ? savep : (char *)0);
c = *p++;
c = FOLD (c);
if ((flags & FNM_PATHNAME) && c == '/')
/* [/] can never match when matching a pathname. */
return (char *)0;
/* This introduces a range, unless the `-' is the last
character of the class. Find the end of the range
and move past it. */
if (c == '-' && *p != ']')
{
cend = *p++;
if (!(flags & FNM_NOESCAPE) && cend == '\\')
cend = *p++;
if (cend == '\0')
return (char *)0;
if (cend == '[' && *p == '.')
{
p = parse_collsym (p, &pc);
/* An invalid collating symbol cannot be the second part of a
range expression. If we get one, we set cend to one fewer
than the test character to make sure the range test fails. */
cend = (pc == -1) ? test - 1 : pc;
}
cend = FOLD (cend);
c = *p++;
/* POSIX.2 2.8.3.2: ``The ending range point shall collate
equal to or higher than the starting range point; otherwise
the expression shall be treated as invalid.'' Note that this
applies to only the range expression; the rest of the bracket
expression is still checked for matches. */
if (rangecmp (cstart, cend) > 0)
{
if (c == ']')
break;
c = FOLD (c);
continue;
}
}
if (rangecmp (test, cstart) >= 0 && rangecmp (test, cend) <= 0)
goto matched;
if (c == ']')
break;
}
/* No match. */
return (!not ? (char *)0 : p);
matched:
/* Skip the rest of the [...] that already matched. */
#if 0
brcnt = (c != ']') + (c == '[' && (*p == '=' || *p == ':' || *p == '.'));
#else
c = *--p;
brcnt = 1;
#endif
while (brcnt > 0)
{
/* A `[' without a matching `]' is just another character to match. */
if (c == '\0')
return ((test == '[') ? savep : (char *)0);
c = *p++;
if (c == '[' && (*p == '=' || *p == ':' || *p == '.'))
brcnt++;
else if (c == ']')
brcnt--;
else if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return (char *)0;
/* XXX 1003.2d11 is unclear if this is right. */
++p;
}
}
return (not ? (char *)0 : p);
}
#if defined (EXTENDED_GLOB)
/* ksh-like extended pattern matching:
[?*+@!](pat-list)
where pat-list is a list of one or patterns separated by `|'. Operation
is as follows:
?(patlist) match zero or one of the given patterns
*(patlist) match zero or more of the given patterns
+(patlist) match one or more of the given patterns
@(patlist) match exactly one of the given patterns
!(patlist) match anything except one of the given patterns
*/
/* Scan a pattern starting at STRING and ending at END, keeping track of
embedded () and []. If DELIM is 0, we scan until a matching `)'
because we're scanning a `patlist'. Otherwise, we scan until we see
DELIM. In all cases, we never scan past END. The return value is the
first character after the matching DELIM. */
static char *
patscan (string, end, delim)
char *string, *end;
int delim;
{
int pnest, bnest, cchar;
char *s, c, *bfirst;
pnest = bnest = cchar = 0;
bfirst = 0;
for (s = string; c = *s; s++)
{
if (s >= end)
return (s);
switch (c)
{
case '\0':
return ((char *)0);
/* `[' is not special inside a bracket expression, but it may
introduce one of the special POSIX bracket expressions
([.SYM.], [=c=], [: ... :]) that needs special handling. */
case '[':
if (bnest == 0)
{
bfirst = s + 1;
if (*bfirst == '!' || *bfirst == '^')
bfirst++;
bnest++;
}
else if (s[1] == ':' || s[1] == '.' || s[1] == '=')
cchar = s[1];
break;
/* `]' is not special if it's the first char (after a leading `!'
or `^') in a bracket expression or if it's part of one of the
special POSIX bracket expressions ([.SYM.], [=c=], [: ... :]) */
case ']':
if (bnest)
{
if (cchar && s[-1] == cchar)
cchar = 0;
else if (s != bfirst)
{
bnest--;
bfirst = 0;
}
}
break;
case '(':
if (bnest == 0)
pnest++;
break;
case ')':
#if 0
if (bnest == 0)
pnest--;
if (pnest <= 0)
return ++s;
#else
if (bnest == 0 && pnest-- <= 0)
return ++s;
#endif
break;
case '|':
if (bnest == 0 && pnest == 0 && delim == '|')
return ++s;
break;
}
}
return (char *)0;
}
/* Return 0 if dequoted pattern matches S in the current locale. */
static int
strcompare (p, pe, s, se)
char *p, *pe, *s, *se;
{
int ret;
char c1, c2;
c1 = *pe;
c2 = *se;
*pe = *se = '\0';
#if defined (HAVE_STRCOLL)
ret = strcoll (p, s);
#else
ret = strcmp (p, s);
#endif
*pe = c1;
*se = c2;
return (ret == 0 ? ret : FNM_NOMATCH);
}
/* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or
0 on success. This is handed the entire rest of the pattern and string
the first time an extended pattern specifier is encountered, so it calls
gmatch recursively. */
static int
extmatch (xc, s, se, p, pe, flags)
int xc; /* select which operation */
char *s, *se;
char *p, *pe;
int flags;
{
char *prest; /* pointer to rest of pattern */
char *psub; /* pointer to sub-pattern */
char *pnext; /* pointer to next sub-pattern */
char *srest; /* pointer to rest of string */
int m1, m2;
#if DEBUG_MATCHING
fprintf(stderr, "extmatch: xc = %c\n", xc);
fprintf(stderr, "extmatch: s = %s; se = %s\n", s, se);
fprintf(stderr, "extmatch: p = %s; pe = %s\n", p, pe);
#endif
prest = patscan (p + (*p == '('), pe, 0); /* ) */
if (prest == 0)
/* If PREST is 0, we failed to scan a valid pattern. In this
case, we just want to compare the two as strings. */
return (strcompare (p - 1, pe, s, se));
switch (xc)
{
case '+': /* match one or more occurrences */
case '*': /* match zero or more occurrences */
/* If we can get away with no matches, don't even bother. Just
call gmatch on the rest of the pattern and return success if
it succeeds. */
if (xc == '*' && (gmatch (s, se, prest, pe, flags) == 0))
return 0;
/* OK, we have to do this the hard way. First, we make sure one of
the subpatterns matches, then we try to match the rest of the
string. */
for (psub = p + 1; ; psub = pnext)
{
pnext = patscan (psub, pe, '|');
for (srest = s; srest <= se; srest++)
{
/* Match this substring (S -> SREST) against this
subpattern (psub -> pnext - 1) */
m1 = gmatch (s, srest, psub, pnext - 1, flags) == 0;
/* OK, we matched a subpattern, so make sure the rest of the
string matches the rest of the pattern. Also handle
multiple matches of the pattern. */
if (m1)
m2 = (gmatch (srest, se, prest, pe, flags) == 0) ||
(s != srest && gmatch (srest, se, p - 1, pe, flags) == 0);
if (m1 && m2)
return (0);
}
if (pnext == prest)
break;
}
return (FNM_NOMATCH);
case '?': /* match zero or one of the patterns */
case '@': /* match exactly one of the patterns */
/* If we can get away with no matches, don't even bother. Just
call gmatch on the rest of the pattern and return success if
it succeeds. */
if (xc == '?' && (gmatch (s, se, prest, pe, flags) == 0))
return 0;
/* OK, we have to do this the hard way. First, we see if one of
the subpatterns matches, then, if it does, we try to match the
rest of the string. */
for (psub = p + 1; ; psub = pnext)
{
pnext = patscan (psub, pe, '|');
srest = (prest == pe) ? se : s;
for ( ; srest <= se; srest++)
{
if (gmatch (s, srest, psub, pnext - 1, flags) == 0 &&
gmatch (srest, se, prest, pe, flags) == 0)
return (0);
}
if (pnext == prest)
break;
}
return (FNM_NOMATCH);
case '!': /* match anything *except* one of the patterns */
for (srest = s; srest <= se; srest++)
{
m1 = 0;
for (psub = p + 1; ; psub = pnext)
{
pnext = patscan (psub, pe, '|');
/* If one of the patterns matches, just bail immediately. */
if (m1 = (gmatch (s, srest, psub, pnext - 1, flags) == 0))
break;
if (pnext == prest)
break;
}
if (m1 == 0 && gmatch (srest, se, prest, pe, flags) == 0)
return (0);
}
return (FNM_NOMATCH);
}
return (FNM_NOMATCH);
}
#endif /* EXTENDED_GLOB */
#ifdef TEST
main (c, v)

116
lib/glob/xmbsrtowcs.c Normal file
View file

@ -0,0 +1,116 @@
/* xmbsrtowcs.c -- replacement function for mbsrtowcs */
/* Copyright (C) 2002 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>
#include <bashansi.h>
/* <wchar.h>, <wctype.h> and <stdlib.h> are included in "shmbutil.h".
If <wchar.h>, <wctype.h>, mbsrtowcs(), exist, HANDLE_MULTIBYTE
is defined as 1. */
#include <shmbutil.h>
#if HANDLE_MULTIBYTE
/* On some locales (ex. ja_JP.sjis), mbsrtowc doesn't convert 0x5c to U<0x5c>.
So, this function is made for converting 0x5c to U<0x5c>. */
static mbstate_t local_state;
static int local_state_use = 0;
size_t
xmbsrtowcs (dest, src, len, pstate)
wchar_t *dest;
const char **src;
size_t len;
mbstate_t *pstate;
{
mbstate_t *ps;
size_t mblength, wclength, n;
ps = pstate;
if (pstate == NULL)
{
if (!local_state_use)
{
memset (&local_state, '\0', sizeof(mbstate_t));
local_state_use = 1;
}
ps = &local_state;
}
n = strlen(*src) + 1;
if (dest == NULL)
{
wchar_t *wsbuf;
char *mbsbuf, *mbsbuf_top;
mbstate_t psbuf;
wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t));
mbsbuf_top = mbsbuf = (char *) malloc (n + 1);
memcpy(mbsbuf, *src, n + 1);
psbuf = *ps;
wclength = mbsrtowcs (wsbuf, (const char **)&mbsbuf, n, &psbuf);
free (wsbuf);
free (mbsbuf_top);
return wclength;
}
for(wclength = 0; wclength < len; wclength++, dest++)
{
if(mbsinit(ps))
{
if (**src == '\0')
{
*dest = L'\0';
*src = NULL;
return (wclength);
}
else if (**src == '\\')
{
*dest = L'\\';
mblength = 1;
}
else
mblength = mbrtowc(dest, *src, n, ps);
}
else
mblength = mbrtowc(dest, *src, n, ps);
/* Cannot convert multibyte character to wide character. */
if (mblength == (size_t)-1 || mblength == (size_t)-2)
return (size_t)-1;
*src += mblength;
n -= mblength;
/* The multibyte string has been completely converted,
including the terminating '\0'. */
if (*dest == L'\0')
{
*src = NULL;
break;
}
}
return (wclength);
}
#endif /* HANDLE_MULTIBYTE */

View file

@ -36,7 +36,7 @@ MV = mv
SHELL = @MAKE_SHELL@
PROFILE_FLAGS =
PROFILE_FLAGS = @PROFILE_FLAGS@
CFLAGS = @CFLAGS@
LOCAL_CFLAGS = @LOCAL_CFLAGS@
@ -69,7 +69,7 @@ MALLOC_SRC = @MALLOC_SRC@
MALLOC = @MALLOC@
ALLOCA = @ALLOCA@
MALLOC_OBJS = malloc.o $(ALLOCA) trace.o stats.o table.o
MALLOC_OBJS = malloc.o $(ALLOCA) trace.o stats.o table.o watch.o
STUB_OBJS = $(ALLOCA) stub.o
.PHONY: malloc stubmalloc
@ -110,9 +110,11 @@ trace.o: ${BUILD_DIR}/config.h
table.o: ${BUILD_DIR}/config.h
malloc.o: ${srcdir}/imalloc.h ${srcdir}/mstats.h
malloc.o: ${srcdir}/table.h ${srcdir}/watch.h
stats.o: ${srcdir}/imalloc.h ${srcdir}/mstats.h
trace.o: ${srcdir}/imalloc.h
table.o: ${srcdir}/imalloc.h ${srcdir}/table.h
watch.o: ${srcdir}/imalloc.h ${srcdir}/watch.h
# Rules for deficient makes, like SunOS and Solaris
stub.o: stub.c
@ -120,3 +122,4 @@ malloc.o: malloc.c
table.o: table.c
trace.o: trace.c
stats.o: stats.c
watch.o: watch.c

View file

@ -20,15 +20,18 @@
/* Must be included *after* config.h */
#ifndef _IMALLOC_H_
#ifndef _IMALLOC_H
#define _IMALLOC_H
#ifdef MALLOC_DEBUG
#define MALLOC_STATS
#define MALLOC_TRACE
#define MALLOC_REGISTER
#define MALLOC_WATCH
#endif
#define MALLOC_WRAPFUNCS
/* Generic pointer type. */
#ifndef PTR_T
# if defined (__STDC__)
@ -64,4 +67,96 @@
# endif /* HAVE_BCOPY */
#endif /* !__GNUC__ */
#if !defined (__P)
# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus) || defined (PROTOTYPES)
# define __P(protos) protos
# else
# define __P(protos) ()
# endif
#endif
/* Use Duff's device for good zeroing/copying performance. DO NOT call the
Duff's device macros with NBYTES == 0. */
#define MALLOC_BZERO(charp, nbytes) \
do { \
if ((nbytes) <= 32) { \
size_t * mzp = (size_t *)(charp); \
unsigned long mctmp = (nbytes)/sizeof(size_t); \
long mcn; \
if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp &= 7; } \
switch (mctmp) { \
case 0: for(;;) { *mzp++ = 0; \
case 7: *mzp++ = 0; \
case 6: *mzp++ = 0; \
case 5: *mzp++ = 0; \
case 4: *mzp++ = 0; \
case 3: *mzp++ = 0; \
case 2: *mzp++ = 0; \
case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \
} \
else \
memset ((charp), 0, (nbytes)); \
} while(0)
#define MALLOC_ZERO(charp, nbytes) \
do { \
size_t mzsz = (nbytes); \
if (mzsz <= 9 * sizeof(mzsz) { \
size_t *mz = (size_t *)(charp); \
if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \
*mz++ = 0; \
if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \
*mz++ = 0; \
if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \
*mz++ = 0; }}} \
*mz++ = 0; \
*mz++ = 0; \
*mz = 0; \
} else \
memset ((charp), 0, mzsz); \
} while (0)
#define MALLOC_MEMSET(charp, xch, nbytes) \
do { \
if ((nbytes) <= 32) { \
register char * mzp = (charp); \
unsigned long mctmp = (nbytes); \
register long mcn; \
if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp &= 7; } \
switch (mctmp) { \
case 0: for(;;) { *mzp++ = xch; \
case 7: *mzp++ = xch; \
case 6: *mzp++ = xch; \
case 5: *mzp++ = xch; \
case 4: *mzp++ = xch; \
case 3: *mzp++ = xch; \
case 2: *mzp++ = xch; \
case 1: *mzp++ = xch; if(mcn <= 0) break; mcn--; } \
} \
} else \
memset ((charp), (xch), (nbytes)); \
} while(0)
#define MALLOC_MEMCPY(dest,src,nbytes) \
do { \
if ((nbytes) <= 32) { \
size_t* mcsrc = (size_t*) src; \
size_t* mcdst = (size_t*) dest; \
unsigned long mctmp = (nbytes)/sizeof(size_t); \
long mcn; \
if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp &= 7; } \
switch (mctmp) { \
case 0: for(;;) { *mcdst++ = *mcsrc++; \
case 7: *mcdst++ = *mcsrc++; \
case 6: *mcdst++ = *mcsrc++; \
case 5: *mcdst++ = *mcsrc++; \
case 4: *mcdst++ = *mcsrc++; \
case 3: *mcdst++ = *mcsrc++; \
case 2: *mcdst++ = *mcsrc++; \
case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \
} else \
memcpy ((dest), (src), (nbytes)) \
} while(0)
#endif /* _IMALLOC_H */

View file

@ -53,12 +53,12 @@ what you give them. Help stamp out software-hoarding! */
* to the second.
*/
/* Define this to have free() write 0xcf into memory as it's freed, to
uncover callers that refer to freed memory. */
/* SCO 3.2v4 getcwd and possibly other libc routines fail with MEMSCRAMBLE */
#if !defined (NO_MEMSCRAMBLE)
# define MEMSCRAMBLE
#endif
/* Define MEMSCRAMBLE to have free() write 0xcf into memory as it's freed, to
uncover callers that refer to freed memory, and to have malloc() write 0xdf
into memory as it's allocated to avoid referring to previous contents. */
/* SCO 3.2v4 getcwd and possibly other libc routines fail with MEMSCRAMBLE;
handled by configure. */
#if defined (HAVE_CONFIG_H)
# include <config.h>
@ -69,15 +69,6 @@ what you give them. Help stamp out software-hoarding! */
# include "stdc.h"
#else
# include <sys/types.h>
# ifndef __P
# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus)
# define __P(protos) protos
# else
# define __P(protos) ()
# endif
# endif
#endif
#if defined (HAVE_UNISTD_H)
@ -107,6 +98,9 @@ what you give them. Help stamp out software-hoarding! */
#ifdef MALLOC_REGISTER
# include "table.h"
#endif
#ifdef MALLOC_WATCH
# include "watch.h"
#endif
/* System-specific omissions. */
#ifdef HPUX
@ -145,6 +139,14 @@ union mhead {
#define mh_nbytes minfo.mi_nbytes
#define mh_magic2 minfo.mi_magic2
#define MOVERHEAD sizeof(union mhead)
#define MALIGN_MASK 7 /* one less than desired alignment */
typedef union _malloc_guard {
char s[4];
u_bits32_t i;
} mguard_t;
/* Access free-list pointer of a block.
It is stored at block + sizeof (char *).
This is not a field in the minfo structure member of union mhead
@ -159,16 +161,15 @@ union mhead {
and end of each allocated block, and make sure they are undisturbed
whenever a free or a realloc occurs. */
/* Written in each of the 4 bytes following the block's real space */
#define MAGIC1 0x55
/* Written in the 2 bytes before the block's real space (-4 bytes) */
#define MAGIC2 0x5555
#define MSLOP 4 /* 4 bytes extra for MAGIC1s */
#define MSLOP 4 /* 4 bytes extra for u_bits32_t size */
/* How many bytes are actually allocated for a request of size N --
rounded up to nearest multiple of 8 after accounting for malloc
overhead. */
#define ALLOCATED_BYTES(n) (((n) + sizeof (union mhead) + MSLOP + 7) & ~7)
#define ALLOCATED_BYTES(n) \
(((n) + MOVERHEAD + MSLOP + MALIGN_MASK) & ~MALIGN_MASK)
#define ASSERT(p) \
do \
@ -179,15 +180,18 @@ union mhead {
/* Minimum and maximum bucket indices for block splitting (and to bound
the search for a block to split). */
#define SPLIT_MIN 3
#define SPLIT_MID 11 /* XXX - was 9 */
#define SPLIT_MAX 14 /* XXX - was 12 */
#define SPLIT_MIN 2 /* XXX - was 3 */
#define SPLIT_MID 11
#define SPLIT_MAX 14
/* Minimum and maximum bucket indices for block coalescing. */
#define COMBINE_MIN 6
#define COMBINE_MAX (pagebucket - 1)
#define COMBINE_MIN 2
#define COMBINE_MAX (pagebucket - 1) /* XXX */
#define MIN_COMBINE_FREE 4
#define LESSCORE_MIN 10
#define LESSCORE_FRC 13
#define STARTBUCK 1
/* Flags for the internal functions. */
#define MALLOC_WRAPPER 0x01 /* wrapper function */
@ -202,9 +206,16 @@ union mhead {
#define ERR_ASSERT_FAILED 0x08
/* Evaluates to true if NB is appropriate for bucket NU. NB is adjusted
appropriately by the caller to account for malloc overhead. */
#define IN_BUCKET(nb, nu) \
((nb) > (4 << (nu)) && ((nb) <= (8 << (nu))))
appropriately by the caller to account for malloc overhead. This only
checks that the recorded size is not too big for the bucket. We
can't check whether or not it's in between NU and NU-1 because we
might have encountered a busy bucket when allocating and moved up to
the next size. */
#define IN_BUCKET(nb, nu) ((nb) <= binsizes[(nu)])
/* Use this when we want to be sure that NB is in bucket NU. */
#define RIGHT_BUCKET(nb, nu) \
(((nb) > binsizes[(nu)-1]) && ((nb) <= binsizes[(nu)]))
/* nextf[i] is free list of blocks of size 2**(i + 3) */
@ -218,6 +229,19 @@ static int pagesz; /* system page size. */
static int pagebucket; /* bucket for requests a page in size */
static int maxbuck; /* highest bucket receiving allocation request. */
static char *memtop; /* top of heap */
static unsigned long binsizes[NBUCKETS] = {
8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL,
1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL,
67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL,
2147483648UL, 4294967296UL-1
};
/* binsizes[x] == (1 << ((x) + 3)) */
#define binsize(x) binsizes[(x)]
/* Declarations for internal functions */
static PTR_T internal_malloc __P((size_t, const char *, int, int));
static PTR_T internal_realloc __P((PTR_T, size_t, const char *, int, int));
@ -238,10 +262,6 @@ static void botch __P((const char *, const char *, int));
#endif
static void xbotch __P((PTR_T, int, const char *, const char *, int));
#ifdef MALLOC_STATS
extern struct _malstats _mstats;
#endif /* MALLOC_STATS */
#if !HAVE_DECL_SBRK
extern char *sbrk ();
#endif /* !HAVE_DECL_SBRK */
@ -251,11 +271,23 @@ extern int interrupt_immediately;
extern int signal_is_trapped __P((int));
#endif
#ifdef MALLOC_STATS
struct _malstats _mstats;
#endif /* MALLOC_STATS */
/* Debugging variables available to applications. */
int malloc_flags = 0; /* future use */
int malloc_trace = 0; /* trace allocations and frees to stderr */
int malloc_register = 0; /* future use */
#ifdef MALLOC_TRACE
char _malloc_trace_buckets[NBUCKETS];
/* These should really go into a header file. */
extern void mtrace_alloc __P((const char *, PTR_T, size_t, const char *, int));
extern void mtrace_free __P((PTR_T, int, const char *, int));
#endif
#if !defined (botch)
static void
botch (s, file, line)
@ -286,53 +318,54 @@ xbotch (mem, e, s, file, line)
botch(s, file, line);
}
#if 0
/* Coalesce two adjacent free blocks off the free list for size NU - 1,
as long as there are at least MIN_COMBINE_FREE free blocks and we
can find two adjacent free blocks. nextf[NU -1] is assumed to not
be busy; the caller (morecore()) checks for this. */
as long as we can find two adjacent free blocks. nextf[NU -1] is
assumed to not be busy; the caller (morecore()) checks for this. */
static void
bcoalesce (nu)
register int nu;
{
register union mhead *mp, *mp1, *mp2;
register int nfree, nbuck;
register int nbuck;
unsigned long siz;
nbuck = nu - 1;
if (nextf[nbuck] == 0)
return;
nfree = 1;
mp1 = nextf[nbuck];
siz = binsize (nbuck);
mp2 = mp1 = nextf[nbuck];
mp = CHAIN (mp1);
mp2 = (union mhead *)0;
while (CHAIN (mp))
while (mp && mp != (union mhead *)((char *)mp1 + siz))
{
mp2 = mp1;
mp1 = mp;
mp = CHAIN (mp);
nfree++;
/* We may not want to run all the way through the free list here;
if we do not, we need to check a threshold value here and break
if nfree exceeds it. */
}
if (nfree < MIN_COMBINE_FREE)
if (mp == 0)
return;
/* OK, now we have mp1 pointing to the block we want to add to nextf[NU].
CHAIN(mp2) must equal mp1. Check that mp1 and mp are adjacent. */
if (CHAIN(mp2) != mp1)
if (mp2 != mp1 && CHAIN(mp2) != mp1)
xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0);
siz = 1 << (nbuck + 3);
#ifdef MALLOC_DEBUG
if (CHAIN (mp1) != (union mhead *)((char *)mp1 + siz))
return; /* not adjacent */
#endif
#ifdef MALLOC_STATS
_mstats.tbcoalesce++;
_mstats.ncoalesce[nbuck]++;
#endif
/* Since they are adjacent, remove them from the free list */
CHAIN (mp2) = CHAIN (mp);
if (mp1 == nextf[nbuck])
nextf[nbuck] = CHAIN (mp);
else
CHAIN (mp2) = CHAIN (mp);
/* And add the combined two blocks to nextf[NU]. */
mp1->mh_alloc = ISFREE;
@ -340,7 +373,6 @@ bcoalesce (nu)
CHAIN (mp1) = nextf[nu];
nextf[nu] = mp1;
}
#endif
/* Split a block at index > NU (but less than SPLIT_MAX) into a set of
blocks of the correct size, and attach them to nextf[NU]. nextf[NU]
@ -387,8 +419,8 @@ bsplit (nu)
#endif
/* Figure out how many blocks we'll get. */
siz = (1 << (nu + 3));
nblks = (1 << (nbuck + 3)) / siz;
siz = binsize (nu);
nblks = binsize (nbuck) / siz;
/* Remove the block from the chain of larger blocks. */
mp = nextf[nbuck];
@ -434,6 +466,27 @@ unblock_signals (setp, osetp)
# endif
#endif
}
/* Return some memory to the system by reducing the break. This is only
called with NU > pagebucket, so we're always assured of giving back
more than one page of memory. */
static void
lesscore (nu) /* give system back some memory */
register int nu; /* size index we're discarding */
{
long siz;
siz = binsize (nu);
/* Should check for errors here, I guess. */
sbrk (-siz);
memtop -= siz;
#ifdef MALLOC_STATS
_mstats.nsbrk++;
_mstats.tsbrk -= siz;
_mstats.nlesscore[nu]++;
#endif
}
static void
morecore (nu) /* ask system for more memory */
@ -456,7 +509,7 @@ morecore (nu) /* ask system for more memory */
blocked_sigs = 1;
}
siz = 1 << (nu + 3); /* size of desired block for nextf[nu] */
siz = binsize (nu); /* size of desired block for nextf[nu] */
if (siz < 0)
goto morecore_done; /* oops */
@ -474,7 +527,6 @@ morecore (nu) /* ask system for more memory */
goto morecore_done;
}
#if 0
/* Try to coalesce two adjacent blocks from the free list on nextf[nu - 1],
if we can, and we're withing the range of the block coalescing limits. */
if (nu >= COMBINE_MIN && nu < COMBINE_MAX && busy[nu - 1] == 0 && nextf[nu - 1])
@ -483,7 +535,6 @@ morecore (nu) /* ask system for more memory */
if (nextf[nu] != 0)
goto morecore_done;
}
#endif
/* Take at least a page, and figure out how many blocks of the requested
size we're getting. */
@ -499,7 +550,7 @@ morecore (nu) /* ask system for more memory */
an amount. If it is, we can just request it. If not, we want
the smallest integral multiple of pagesize that is larger than
`siz' and will satisfy the request. */
sbrk_amt = siz % pagesz;
sbrk_amt = siz & (pagesz - 1);
if (sbrk_amt == 0)
sbrk_amt = siz;
else
@ -518,10 +569,12 @@ morecore (nu) /* ask system for more memory */
if ((long)mp == -1)
goto morecore_done;
memtop += sbrk_amt;
/* shouldn't happen, but just in case -- require 8-byte alignment */
if ((long)mp & 7)
if ((long)mp & MALIGN_MASK)
{
mp = (union mhead *) (((long)mp + 7) & ~7);
mp = (union mhead *) (((long)mp + MALIGN_MASK) & ~MALIGN_MASK);
nblks--;
}
@ -542,28 +595,82 @@ morecore_done:
unblock_signals (&set, &oset);
}
#if defined (MEMSCRAMBLE) || !defined (NO_CALLOC)
static char *
zmemset (s, c, n)
char *s;
int c;
register int n;
{
register char *sp;
sp = s;
while (--n >= 0)
*sp++ = c;
return (s);
}
#endif /* MEMSCRAMBLE || !NO_CALLOC */
static void
malloc_debug_dummy ()
{
write (1, "malloc_debug_dummy\n", 19);
}
#define PREPOP_BIN 2
#define PREPOP_SIZE 32
static int
pagealign ()
{
register int nunits;
register union mhead *mp;
long sbrk_needed;
char *curbrk;
pagesz = getpagesize ();
if (pagesz < 1024)
pagesz = 1024;
/* OK, how much do we need to allocate to make things page-aligned?
Some of this partial page will be wasted space, but we'll use as
much as we can. Once we figure out how much to advance the break
pointer, go ahead and do it. */
memtop = curbrk = sbrk (0);
sbrk_needed = pagesz - ((long)curbrk & (pagesz - 1)); /* sbrk(0) % pagesz */
if (sbrk_needed < 0)
sbrk_needed += pagesz;
/* Now allocate the wasted space. */
if (sbrk_needed)
{
#ifdef MALLOC_STATS
_mstats.nsbrk++;
_mstats.tsbrk += sbrk_needed;
#endif
curbrk = sbrk (sbrk_needed);
if ((long)curbrk == -1)
return -1;
memtop += sbrk_needed;
/* Take the memory which would otherwise be wasted and populate the most
popular bin (2 == 32 bytes) with it. Add whatever we need to curbrk
to make things 32-byte aligned, compute how many 32-byte chunks we're
going to get, and set up the bin. */
curbrk += sbrk_needed & (PREPOP_SIZE - 1);
sbrk_needed -= sbrk_needed & (PREPOP_SIZE - 1);
nunits = sbrk_needed / PREPOP_SIZE;
if (nunits > 0)
{
mp = (union mhead *)curbrk;
nextf[PREPOP_BIN] = mp;
while (1)
{
mp->mh_alloc = ISFREE;
mp->mh_index = PREPOP_BIN;
if (--nunits <= 0) break;
CHAIN(mp) = (union mhead *)((char *)mp + PREPOP_SIZE);
mp = (union mhead *)((char *)mp + PREPOP_SIZE);
}
CHAIN(mp) = 0;
}
}
/* compute which bin corresponds to the page size. */
for (nunits = 7; nunits < NBUCKETS; nunits++)
if (pagesz <= binsize(nunits))
break;
pagebucket = nunits;
return 0;
}
static PTR_T
internal_malloc (n, file, line, flags) /* get a block */
size_t n;
@ -571,71 +678,27 @@ internal_malloc (n, file, line, flags) /* get a block */
int line, flags;
{
register union mhead *p;
register long nbytes;
register int nunits;
register char *m, *z;
long nbytes;
mguard_t mg;
/* Get the system page size and align break pointer so everything will
/* Get the system page size and align break pointer so future sbrks will
be page-aligned. The page size must be at least 1K -- anything
smaller is increased. */
if (pagesz == 0)
{
register long sbrk_needed;
pagesz = getpagesize ();
if (pagesz < 1024)
pagesz = 1024;
/* OK, how much do we need to allocate to make things page-aligned?
This partial page is wasted space. Once we figure out how much
to advance the break pointer, go ahead and do it. */
sbrk_needed = pagesz - ((long)sbrk (0) & (pagesz - 1)); /* sbrk(0) % pagesz */
if (sbrk_needed < 0)
sbrk_needed += pagesz;
/* Now allocate the wasted space. */
if (sbrk_needed)
{
#ifdef MALLOC_STATS
_mstats.nsbrk++;
_mstats.tsbrk += sbrk_needed;
#endif
if ((long)sbrk (sbrk_needed) == -1)
return (NULL);
}
nunits = 0;
nbytes = 8;
while (pagesz > nbytes)
{
nbytes <<= 1;
nunits++;
}
pagebucket = nunits;
}
if (pagealign () < 0)
return ((PTR_T)NULL);
/* Figure out how many bytes are required, rounding up to the nearest
multiple of 8, then figure out which nextf[] area to use. Try to
be smart about where to start searching -- if the number of bytes
needed is greater than the page size, we can start at pagebucket. */
nbytes = ALLOCATED_BYTES(n);
nunits = 0;
if (nbytes <= (pagesz >> 1))
{
register unsigned int shiftr;
shiftr = (nbytes - 1) >> 2; /* == (nbytes - 1) / 4 */
while (shiftr >>= 1) /* == (nbytes - 1) / {8,16,32,...} */
nunits++;
}
else
{
register u_bits32_t amt;
nunits = pagebucket;
amt = pagesz;
while (nbytes > amt)
{
amt <<= 1;
nunits++;
}
}
nunits = (nbytes <= (pagesz >> 1)) ? STARTBUCK : pagebucket;
for ( ; nunits < NBUCKETS; nunits++)
if (nbytes <= binsize(nunits))
break;
/* Silently reject too-large requests. */
if (nunits >= NBUCKETS)
@ -671,30 +734,35 @@ internal_malloc (n, file, line, flags) /* get a block */
/* If not for this check, we would gobble a clobbered free chain ptr
and bomb out on the NEXT allocate of this size block */
if (p->mh_alloc != ISFREE || p->mh_index != nunits)
xbotch ((PTR_T)0, 0, "malloc: block on free list clobbered", file, line);
xbotch ((PTR_T)(p+1), 0, "malloc: block on free list clobbered", file, line);
/* Fill in the info, and set up the magic numbers for range checking. */
p->mh_alloc = ISALLOC;
p->mh_magic2 = MAGIC2;
p->mh_nbytes = n;
{
register char *m = (char *) (p + 1) + n;
*m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1;
}
/* End guard */
mg.i = n;
z = mg.s;
m = (char *) (p + 1) + n;
*m++ = *z++, *m++ = *z++, *m++ = *z++, *m++ = *z++;
#ifdef MEMSCRAMBLE
zmemset ((char *)(p + 1), 0xdf, n); /* scramble previous contents */
if (n)
MALLOC_MEMSET ((char *)(p + 1), 0xdf, n); /* scramble previous contents */
#endif
#ifdef MALLOC_STATS
_mstats.nmalloc[nunits]++;
_mstats.tmalloc[nunits]++;
_mstats.nmal++;
_mstats.bytesreq += n;
#endif /* MALLOC_STATS */
#ifdef MALLOC_TRACE
if (malloc_trace && (flags & MALLOC_NOTRACE) == 0)
mtrace_alloc ("malloc", p + 1, n, file, line);
else if (_malloc_trace_buckets[nunits])
mtrace_alloc ("malloc", p + 1, n, file, line);
#endif
#ifdef MALLOC_REGISTER
@ -702,7 +770,12 @@ internal_malloc (n, file, line, flags) /* get a block */
mregister_alloc ("malloc", p + 1, n, file, line);
#endif
return (char *) (p + 1); /* XXX - should be cast to PTR_T? */
#ifdef MALLOC_WATCH
if (_malloc_nwatch > 0)
_malloc_ckwatch (p + 1, file, line, W_ALLOC, n);
#endif
return (PTR_T) (p + 1);
}
static void
@ -712,10 +785,11 @@ internal_free (mem, file, line, flags)
int line, flags;
{
register union mhead *p;
register char *ap;
register char *ap, *z;
register int nunits;
register unsigned int nbytes;
int ubytes; /* caller-requested size */
mguard_t mg;
if ((ap = (char *)mem) == 0)
return;
@ -753,18 +827,42 @@ internal_free (mem, file, line, flags)
We sanity-check the value of mh_nbytes against the size of the blocks
in the appropriate bucket before we use it. This can still cause problems
and obscure errors if mh_nbytes is wrong but still within range; the
checks against MAGIC1 will probably fail then. Using MALLOC_REGISTER
will help here, since it saves the original number of bytes requested. */
checks against the size recorded at the end of the chunk will probably
fail then. Using MALLOC_REGISTER will help here, since it saves the
original number of bytes requested. */
if (IN_BUCKET(nbytes, nunits) == 0)
xbotch (mem, ERR_UNDERFLOW,
"free: underflow detected; mh_nbytes out of range", file, line);
ap += p->mh_nbytes;
ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1);
ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1);
z = mg.s;
*z++ = *ap++, *z++ = *ap++, *z++ = *ap++, *z++ = *ap++;
if (mg.i != p->mh_nbytes)
xbotch (mem, ERR_ASSERT_FAILED, "free: start and end chunk sizes differ", file, line);
#if 1
if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop))
#else
if (((char *)p + binsize(nunits) == memtop) && nunits >= LESSCORE_MIN)
#endif
{
/* If above LESSCORE_FRC, give back unconditionally. This should be set
high enough to be infrequently encountered. If between LESSCORE_MIN
and LESSCORE_FRC, call lesscore if the bucket is marked as busy (in
which case we would punt below and leak memory) or if there's already
a block on the free list. */
if ((nunits >= LESSCORE_FRC) || busy[nunits] || nextf[nunits] != 0)
{
lesscore (nunits);
/* keeps the tracing and registering code in one place */
goto free_return;
}
}
#ifdef MEMSCRAMBLE
zmemset (mem, 0xcf, p->mh_nbytes);
if (p->mh_nbytes)
MALLOC_MEMSET (mem, 0xcf, p->mh_nbytes);
#endif
ASSERT (nunits < NBUCKETS);
@ -780,6 +878,8 @@ internal_free (mem, file, line, flags)
nextf[nunits] = p;
busy[nunits] = 0;
free_return:
#ifdef MALLOC_STATS
_mstats.nmalloc[nunits]--;
_mstats.nfre++;
@ -788,12 +888,19 @@ internal_free (mem, file, line, flags)
#ifdef MALLOC_TRACE
if (malloc_trace && (flags & MALLOC_NOTRACE) == 0)
mtrace_free (mem, ubytes, file, line);
else if (_malloc_trace_buckets[nunits])
mtrace_free (mem, ubytes, file, line);
#endif
#ifdef MALLOC_REGISTER
if (malloc_register && (flags & MALLOC_NOREG) == 0)
mregister_free (mem, ubytes, file, line);
#endif
#ifdef MALLOC_WATCH
if (_malloc_nwatch > 0)
_malloc_ckwatch (mem, file, line, W_FREE, ubytes);
#endif
}
static PTR_T
@ -807,7 +914,8 @@ internal_realloc (mem, n, file, line, flags)
register u_bits32_t tocopy;
register unsigned int nbytes;
register int nunits;
register char *m;
register char *m, *z;
mguard_t mg;
#ifdef MALLOC_STATS
_mstats.nrealloc++;
@ -837,37 +945,56 @@ internal_realloc (mem, n, file, line, flags)
We sanity-check the value of mh_nbytes against the size of the blocks
in the appropriate bucket before we use it. This can still cause problems
and obscure errors if mh_nbytes is wrong but still within range; the
checks against MAGIC1 will probably fail then. Using MALLOC_REGISTER
will help here, since it saves the original number of bytes requested. */
checks against the size recorded at the end of the chunk will probably
fail then. Using MALLOC_REGISTER will help here, since it saves the
original number of bytes requested. */
if (IN_BUCKET(nbytes, nunits) == 0)
xbotch (mem, ERR_UNDERFLOW,
"realloc: underflow detected; mh_nbytes out of range", file, line);
m = (char *)mem + (tocopy = p->mh_nbytes);
ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1);
ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1);
z = mg.s;
*z++ = *m++, *z++ = *m++, *z++ = *m++, *z++ = *m++;
if (mg.i != p->mh_nbytes)
xbotch (mem, ERR_ASSERT_FAILED, "realloc: start and end chunk sizes differ", file, line);
#ifdef MALLOC_WATCH
if (_malloc_nwatch > 0)
_malloc_ckwatch (p + 1, file, line, W_REALLOC, n);
#endif
#ifdef MALLOC_STATS
_mstats.bytesreq += (n < tocopy) ? 0 : n - tocopy;
#endif
/* See if desired size rounds to same power of 2 as actual size. */
nbytes = ALLOCATED_BYTES(n);
/* If ok, use the same block, just marking its size as changed. */
if (IN_BUCKET(nbytes, nunits))
if (RIGHT_BUCKET(nbytes, nunits))
{
m = (char *)mem + tocopy;
#if 0
m = (char *)mem + p->mh_nbytes;
#else
/* Compensate for increment above. */
m -= 4;
#endif
*m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0;
p->mh_nbytes = n;
m = (char *)mem + n;
*m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1;
m = (char *)mem + (p->mh_nbytes = n);
mg.i = n;
z = mg.s;
*m++ = *z++, *m++ = *z++, *m++ = *z++, *m++ = *z++;
return mem;
}
if (n < tocopy)
tocopy = n;
#ifdef MALLOC_STATS
_mstats.nrcopy++;
#endif
if (n < tocopy)
tocopy = n;
if ((m = internal_malloc (n, file, line, MALLOC_INTERNAL|MALLOC_NOTRACE|MALLOC_NOREG)) == 0)
return 0;
FASTCOPY (mem, m, tocopy);
@ -876,6 +1003,8 @@ internal_realloc (mem, n, file, line, flags)
#ifdef MALLOC_TRACE
if (malloc_trace && (flags & MALLOC_NOTRACE) == 0)
mtrace_alloc ("realloc", m, n, file, line);
else if (_malloc_trace_buckets[nunits])
mtrace_alloc ("realloc", m, n, file, line);
#endif
#ifdef MALLOC_REGISTER
@ -883,6 +1012,11 @@ internal_realloc (mem, n, file, line, flags)
mregister_alloc ("realloc", m, n, file, line);
#endif
#ifdef MALLOC_WATCH
if (_malloc_nwatch > 0)
_malloc_ckwatch (m, file, line, W_RESIZED, n);
#endif
return m;
}
@ -946,7 +1080,7 @@ internal_calloc (n, s, file, line, flags)
total = n * s;
result = internal_malloc (total, file, line, flags|MALLOC_INTERNAL);
if (result)
zmemset (result, 0, total);
memset (result, 0, total);
return result;
}
@ -961,7 +1095,6 @@ internal_cfree (p, file, line, flags)
#endif /* !NO_CALLOC */
#ifdef MALLOC_STATS
int
malloc_free_blocks (size)
int size;
@ -977,7 +1110,7 @@ malloc_free_blocks (size)
}
#endif
#if defined (SHELL)
#if defined (MALLOC_WRAPFUNCS)
PTR_T
sh_malloc (bytes, file, line)
size_t bytes;
@ -1045,9 +1178,9 @@ sh_valloc (size, file, line)
{
return internal_valloc (size, file, line, MALLOC_WRAPPER);
}
#endif
#endif /* !NO_VALLOC */
#endif
#endif /* MALLOC_WRAPFUNCS */
/* Externally-available functions that call their internal counterparts. */

View file

@ -31,18 +31,28 @@
* NMALLOC[i] is the difference between the number of mallocs and frees
* for a given block size. TMALLOC[i] is the total number of mallocs for
* a given block size. NMORECORE[i] is the total number of calls to
* morecore(i). NMAL and NFRE are counts of the number of calls to malloc()
* and free(), respectively. NREALLOC is the total number of calls to
* realloc(); NRCOPY is the number of times realloc() had to allocate new
* memory and copy to it. NRECURSE is a count of the number of recursive
* calls to malloc() for the same bucket size, which can be caused by calls
* to malloc() from a signal handler. NSBRK is the number of calls to sbrk()
* (whether by morecore() or for alignment); TSBRK is the total number of
* bytes requested from the kernel with sbrk(). BYTESUSED is the total
* number of bytes consumed by blocks currently in use; BYTESFREE is the
* total number of bytes currently on all of the free lists. TBSPLIT is
* the number of times a larger block was split to satisfy a smaller request.
* NSPLIT[i] is the number of times a block of size I was split.
* morecore(i). NLESSCORE[i] is the total number of calls to lesscore(i).
*
* NMAL and NFRE are counts of the number of calls to malloc() and free(),
* respectively. NREALLOC is the total number of calls to realloc();
* NRCOPY is the number of times realloc() had to allocate new memory and
* copy to it. NRECURSE is a count of the number of recursive calls to
* malloc() for the same bucket size, which can be caused by calls to
* malloc() from a signal handler.
*
* NSBRK is the number of calls to sbrk() (whether by morecore() or for
* alignment); TSBRK is the total number of bytes requested from the kernel
* with sbrk().
*
* BYTESUSED is the total number of bytes consumed by blocks currently in
* use; BYTESFREE is the total number of bytes currently on all of the free
* lists. BYTESREQ is the total number of bytes requested by the caller
* via calls to malloc() and realloc().
*
* TBSPLIT is the number of times a larger block was split to satisfy a
* smaller request. NSPLIT[i] is the number of times a block of size I was
* split.
*
* TBCOALESCE is the number of times two adjacent smaller blocks off the free
* list were combined to satisfy a larger request.
*/
@ -50,6 +60,7 @@ struct _malstats {
int nmalloc[NBUCKETS];
int tmalloc[NBUCKETS];
int nmorecore[NBUCKETS];
int nlesscore[NBUCKETS];
int nmal;
int nfre;
int nrealloc;
@ -59,31 +70,38 @@ struct _malstats {
bits32_t tsbrk;
bits32_t bytesused;
bits32_t bytesfree;
u_bits32_t bytesreq;
int tbsplit;
int nsplit[NBUCKETS];
int tbcoalesce;
int ncoalesce[NBUCKETS];
};
/* Return statistics describing allocation of blocks of size BLOCKSIZE.
NFREE is the number of free blocks for this allocation size. NUSED
is the number of blocks in use. NMAL is the number of requests for
blocks of size BLOCKSIZE. NMORECORE is the number of times we had
to call MORECORE to repopulate the free list for this bucket. NSPLIT
is the number of times a block of this size was split to satisfy a
smaller request. */
to call MORECORE to repopulate the free list for this bucket.
NLESSCORE is the number of times we gave memory back to the system
from this bucket. NSPLIT is the number of times a block of this size
was split to satisfy a smaller request. NCOALESCE is the number of
times two blocks of this size were combined to satisfy a larger
request. */
struct bucket_stats {
u_bits32_t blocksize;
int nfree;
int nused;
int nmal;
int nmorecore;
int nlesscore;
int nsplit;
int ncoalesce;
};
extern struct bucket_stats malloc_bucket_stats ();
extern struct _malstats malloc_stats ();
extern void print_malloc_stats ();
extern void trace_malloc_stats ();
extern struct bucket_stats malloc_bucket_stats __P((int));
extern struct _malstats malloc_stats __P((void));
extern void print_malloc_stats __P((char *));
extern void trace_malloc_stats __P((char *, char *));
#endif /* MALLOC_STATS */

View file

@ -62,6 +62,6 @@ extern int malloc_set_register __P((int));
/* stats.c */
extern void print_malloc_stats __P((char *));
extern void fprint_malloc_stats (); /* full prototype requires stdio.h */
extern void trace_malloc_stats __P((char *));
extern void trace_malloc_stats __P((char *, char *));
#endif

View file

@ -25,9 +25,15 @@
#ifdef MALLOC_STATS
#include <stdio.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "mstats.h"
struct _malstats _mstats;
extern int malloc_free_blocks __P((int));
extern struct _malstats _mstats;
struct bucket_stats
malloc_bucket_stats (size)
@ -40,7 +46,7 @@ malloc_bucket_stats (size)
if (size < 0 || size >= NBUCKETS)
{
v.blocksize = 0;
v.nused = v.nmal = v.nmorecore = v.nsplit = 0;
v.nused = v.nmal = v.nmorecore = v.nlesscore = v.nsplit = 0;
return v;
}
@ -48,7 +54,9 @@ malloc_bucket_stats (size)
v.nused = _mstats.nmalloc[size];
v.nmal = _mstats.tmalloc[size];
v.nmorecore = _mstats.nmorecore[size];
v.nlesscore = _mstats.nlesscore[size];
v.nsplit = _mstats.nsplit[size];
v.ncoalesce = _mstats.ncoalesce[size];
v.nfree = malloc_free_blocks (size); /* call back to malloc.c */
@ -86,16 +94,18 @@ _print_malloc_stats (s, fp)
unsigned long totused, totfree;
struct bucket_stats v;
fprintf (fp, "Memory allocation statistics: %s\n\tsize\tfree\tin use\ttotal\tmorecore\tsplit\n", s ? s : "");
fprintf (fp, "Memory allocation statistics: %s\n size\tfree\tin use\ttotal\tmorecore lesscore split\tcoalesce\n", s ? s : "");
for (i = totused = totfree = 0; i < NBUCKETS; i++)
{
v = malloc_bucket_stats (i);
fprintf (fp, "%12lu\t%4d\t%6d\t%5d\t%8d\t%5d\n", (unsigned long)v.blocksize, v.nfree, v.nused, v.nmal, v.nmorecore, v.nsplit);
if (v.nmal > 0)
fprintf (fp, "%8lu\t%4d\t%6d\t%5d\t%8d\t%d %5d %8d\n", (unsigned long)v.blocksize, v.nfree, v.nused, v.nmal, v.nmorecore, v.nlesscore, v.nsplit, v.ncoalesce);
totfree += v.nfree * v.blocksize;
totused += v.nused * v.blocksize;
}
fprintf (fp, "\nTotal bytes in use: %lu, total bytes free: %lu\n",
totused, totfree);
fprintf (fp, "\nTotal bytes requested by application: %lu\n", _mstats.bytesreq);
fprintf (fp, "Total mallocs: %d, total frees: %d, total reallocs: %d (%d copies)\n",
_mstats.nmal, _mstats.nfre, _mstats.nrealloc, _mstats.nrcopy);
fprintf (fp, "Total sbrks: %d, total bytes via sbrk: %d\n",
@ -120,24 +130,51 @@ fprint_malloc_stats (s, fp)
}
#define TRACEROOT "/var/tmp/maltrace/trace."
extern char *inttostr ();
static char mallbuf[1024];
void
trace_malloc_stats (s)
char *s;
trace_malloc_stats (s, fn)
char *s, *fn;
{
char ibuf[32], *ip;
char fname[64];
long p;
char defname[sizeof (TRACEROOT) + 64];
char fname[1024];
long l;
FILE *fp;
p = getpid();
ip = inttostr(p, ibuf, sizeof(ibuf));
strcpy (fname, TRACEROOT);
strcat (fname, ip);
fp = fopen(fname, "w");
l = (long)getpid ();
if (fn == 0)
{
sprintf (defname, "%s%ld", TRACEROOT, l);
fp = fopen(defname, "w");
}
else
{
char *p, *q, *r;
char pidbuf[32];
int sp;
sprintf (pidbuf, "%ld", l);
if ((strlen (pidbuf) + strlen (fn) + 2) >= sizeof (fname))
return;
for (sp = 0, p = fname, q = fn; *q; )
{
if (sp == 0 && *q == '%' && q[1] == 'p')
{
sp = 1;
for (r = pidbuf; *r; )
*p++ = *r++;
q += 2;
}
else
*p++ = *q++;
}
*p = '\0';
fp = fopen (fname, "w");
}
if (fp)
{
setvbuf (fp, mallbuf, _IOFBF, sizeof (mallbuf));
_print_malloc_stats (s, fp);
fflush(fp);
fclose(fp);

View file

@ -1,3 +1,21 @@
/* Copyright (C) 1993-2002 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. */
void
bash_malloc_stub()
{

View file

@ -34,7 +34,9 @@ extern int malloc_register;
#define FIND_EXIST 0x02 /* find existing entry */
static int table_count = 0;
static int table_allocated = 0;
static mr_table_t mem_table[REG_TABLE_SIZE];
static mr_table_t mem_overflow;
/*
* NOTE: taken from dmalloc (http://dmalloc.com) and modified.
@ -43,7 +45,7 @@ static unsigned int
mt_hash (key)
const PTR_T key;
{
unsigned int a, b, c, len;
unsigned int a, b, c;
unsigned long x;
/* set up the internal state */
@ -61,10 +63,10 @@ static unsigned int
which_bucket (mem)
PTR_T mem;
{
return (mt_hash ((unsigned char *)mem) % REG_TABLE_SIZE);
return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1));
}
#else
#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) % REG_TABLE_SIZE);
#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1));
#endif
static mr_table_t *
@ -76,6 +78,9 @@ find_entry (mem, flags)
register mr_table_t *tp;
mr_table_t *endp, *lastp;
if (mem_overflow.mem == mem)
return (&mem_overflow);
bucket = which_bucket (mem); /* get initial hash */
tp = endp = mem_table + bucket;
lastp = mem_table + REG_TABLE_SIZE;
@ -105,17 +110,26 @@ find_entry (mem, flags)
/* oops. table is full. replace an existing free entry. */
do
{
/* If there are no free entries, punt right away without searching. */
if (table_allocated == REG_TABLE_SIZE)
break;
if (tp->flags & MT_FREE)
{
memset(tp, 0, sizeof (mr_table_t));
return (tp);
}
tp++;
if (tp == lastp)
tp = mem_table;
}
while (tp != endp);
/* wow. entirely full. return NULL. */
return ((mr_table_t *)NULL);
/* wow. entirely full. return mem_overflow dummy entry. */
tp = &mem_overflow;
memset (tp, 0, sizeof (mr_table_t));
return tp;
}
mr_table_t *
@ -158,7 +172,7 @@ mregister_alloc (tag, mem, size, file, line)
if (tentry == 0)
{
/* oops. table is full. punt. */
fprintf (stderr, "register_alloc: alloc table is full?\n");
fprintf (stderr, "register_alloc: alloc table is full with FIND_ALLOC?\n");
return;
}
@ -175,6 +189,9 @@ mregister_alloc (tag, mem, size, file, line)
tentry->file = file;
tentry->line = line;
tentry->nalloc++;
if (tentry != &mem_overflow)
table_allocated++;
}
void
@ -190,7 +207,9 @@ mregister_free (mem, size, file, line)
if (tentry == 0)
{
/* oops. not found. */
#if 0
fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
#endif
return;
}
if (tentry->flags & MT_FREE)
@ -204,6 +223,9 @@ mregister_free (mem, size, file, line)
tentry->file = file;
tentry->line = line;
tentry->nfree++;
if (tentry != &mem_overflow)
table_allocated--;
}
/* If we ever add more flags, this will require changes. */
@ -250,6 +272,7 @@ void
mregister_table_init ()
{
memset (mem_table, 0, sizeof(mr_table_t) * REG_TABLE_SIZE);
memset (&mem_overflow, 0, sizeof (mr_table_t));
table_count = 0;
}

View file

@ -55,12 +55,12 @@ typedef struct mr_table {
#define REG_TABLE_SIZE 8192
extern mr_table_t *mr_table_entry ();
extern void mregister_alloc ();
extern void mregister_free ();
extern mr_table_t *mr_table_entry __P((PTR_T));
extern void mregister_alloc __P((const char *, PTR_T, size_t, const char *, int));
extern void mregister_free __P((PTR_T, int, const char *, int));
extern void mregister_describe_mem ();
extern void mregister_dump_table ();
extern void mregister_table_init ();
extern void mregister_dump_table __P((void));
extern void mregister_table_init __P((void));
/* NOTE: HASH_MIX taken from dmalloc (http://dmalloc.com) */

View file

@ -32,6 +32,7 @@ static int _mtrace_verbose = 0;
#ifdef MALLOC_TRACE
FILE *_mtrace_fp = NULL;
extern char _malloc_trace_buckets[];
void
mtrace_alloc (tag, mem, size, file, line)
@ -72,7 +73,7 @@ mtrace_free (mem, size, file, line)
#endif /* MALLOC_TRACE */
int
malloc_set_trace(n)
malloc_set_trace (n)
int n;
{
int old;
@ -84,10 +85,19 @@ malloc_set_trace(n)
}
void
malloc_set_tracefp(fp)
malloc_set_tracefp (fp)
FILE *fp;
{
#ifdef MALLOC_TRACE
_mtrace_fp = fp ? fp : stderr;
#endif
}
void
malloc_trace_bin (n)
int n;
{
#ifdef MALLOC_TRACE
_malloc_trace_buckets[n] = 1;
#endif
}

150
lib/malloc/watch.c Normal file
View file

@ -0,0 +1,150 @@
/* watch.c - watchpoint functions for malloc */
/* Copyright (C) 2001 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. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include "imalloc.h"
#ifdef MALLOC_WATCH
#include "watch.h"
#define WATCH_MAX 32
int _malloc_nwatch;
static PTR_T _malloc_watch_list[WATCH_MAX];
static void
watch_warn (addr, file, line, type, data)
PTR_T addr;
const char *file;
int line, type;
unsigned long data;
{
char *tag;
if (type == W_ALLOC)
tag = "allocated";
else if (type == W_FREE)
tag = "freed";
else if (type == W_REALLOC)
tag = "requesting resize";
else if (type == W_RESIZED)
tag = "just resized";
else
tag = "bug: unknown operation";
fprintf (stderr, "malloc: watch alert: %p %s ", addr, tag);
if (data != (unsigned long)-1)
fprintf (stderr, "(size %lu) ", data);
fprintf (stderr, "from '%s:%d'\n", file ? file : "unknown", line);
}
void
_malloc_ckwatch (addr, file, line, type, data)
PTR_T addr;
const char *file;
int line, type;
unsigned long data;
{
register int i;
for (i = _malloc_nwatch - 1; i >= 0; i--)
{
if (_malloc_watch_list[i] == addr)
{
watch_warn (addr, file, line, type, data);
return;
}
}
}
#endif /* MALLOC_WATCH */
PTR_T
malloc_watch (addr)
PTR_T addr;
{
register int i;
PTR_T ret;
if (addr == 0)
return addr;
ret = (PTR_T)0;
#ifdef MALLOC_WATCH
for (i = _malloc_nwatch - 1; i >= 0; i--)
{
if (_malloc_watch_list[i] == addr)
break;
}
if (i < 0)
{
if (_malloc_nwatch == WATCH_MAX) /* full, take out first */
{
ret = _malloc_watch_list[0];
_malloc_nwatch--;
for (i = 0; i < _malloc_nwatch; i++)
_malloc_watch_list[i] = _malloc_watch_list[i+1];
}
_malloc_watch_list[_malloc_nwatch++] = addr;
}
#endif
return ret;
}
/* Remove a watchpoint set on ADDR. If ADDR is NULL, remove all
watchpoints. Returns ADDR if everything went OK, NULL if ADDR was
not being watched. */
PTR_T
malloc_unwatch (addr)
PTR_T addr;
{
#ifdef MALLOC_WATCH
register int i;
if (addr == 0)
{
for (i = 0; i < _malloc_nwatch; i++)
_malloc_watch_list[i] = (PTR_T)0;
_malloc_nwatch = 0;
return ((PTR_T)0);
}
else
{
for (i = 0; i < _malloc_nwatch; i++)
{
if (_malloc_watch_list[i] == addr)
break;
}
if (i == _malloc_nwatch)
return ((PTR_T)0); /* not found */
/* shuffle everything from i+1 to end down 1 */
_malloc_nwatch--;
for ( ; i < _malloc_nwatch; i++)
_malloc_watch_list[i] = _malloc_watch_list[i+1];
return addr;
}
#else
return ((PTR_T)0);
#endif
}

39
lib/malloc/watch.h Normal file
View file

@ -0,0 +1,39 @@
/* watch.h - definitions for tables for keeping track of allocated memory */
/* Copyright (C) 2001 Free Software Foundation, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#ifndef _MWATCH_H
#define _MWATCH_H
#include "imalloc.h"
#ifdef MALLOC_WATCH
/* `Events' for watchpoints */
#define W_ALLOC 0x01
#define W_FREE 0x02
#define W_REALLOC 0x04
#define W_RESIZED 0x08
extern int _malloc_nwatch;
extern void _malloc_ckwatch __P((PTR_T, const char *, int, int, unsigned long));
#endif /* MALLOC_WATCH */
#endif /* _MWATCH_H */

View file

@ -73,20 +73,22 @@ CSOURCES = $(srcdir)/readline.c $(srcdir)/funmap.c $(srcdir)/keymaps.c \
$(srcdir)/history.c $(srcdir)/histsearch.c $(srcdir)/histexpand.c \
$(srcdir)/histfile.c $(srcdir)/nls.c $(srcdir)/search.c \
$(srcdir)/shell.c $(srcdir)/tilde.c $(srcdir)/savestring.c \
$(srcdir)/compat.c
$(srcdir)/text.c $(srcdir)/misc.c $(srcdir)/compat.c \
$(srcdir)/mbutil.c
# The header files for this library.
HSOURCES = readline.h rldefs.h chardefs.h keymaps.h history.h histlib.h \
posixstat.h posixdir.h posixjmp.h tilde.h rlconf.h rltty.h \
ansi_stdlib.h rlstdc.h tcap.h xmalloc.h rlprivate.h rlshell.h \
rltypedefs.h
rltypedefs.h rlmbutil.h
HISTOBJ = history.o histexpand.o histfile.o histsearch.o shell.o savestring.o
HISTOBJ = history.o histexpand.o histfile.o histsearch.o shell.o savestring.o \
mbutil.o
TILDEOBJ = tilde.o
OBJECTS = readline.o vi_mode.o funmap.o keymaps.o parens.o search.o \
rltty.o complete.o bind.o isearch.o display.o signals.o \
util.o kill.o undo.o macro.o input.o callback.o terminal.o \
nls.o $(HISTOBJ) $(TILDEOBJ) xmalloc.o compat.o
text.o nls.o misc.o $(HISTOBJ) $(TILDEOBJ) xmalloc.o compat.o
# The texinfo files which document this library.
DOCSOURCE = doc/rlman.texinfo doc/rltech.texinfo doc/rluser.texinfo
@ -203,6 +205,11 @@ macro.o: ansi_stdlib.h
macro.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
macro.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
macro.o: history.h rlstdc.h
mbutil.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h rlmbutil.h
mbutil.o: readline.h keymaps.h rltypedefs.h chardefs.h rlstdc.h
misc.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
misc.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
misc.o: history.h rlstdc.h ansi_stdlib.h
nls.o: ansi_stdlib.h
nls.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
nls.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
@ -228,6 +235,10 @@ terminal.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
terminal.o: tcap.h
terminal.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
terminal.o: history.h rlstdc.h
text.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h
text.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
text.o: history.h rlstdc.h ansi_stdlib.h
rltty.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h
tilde.o: ansi_stdlib.h
tilde.o: ${BUILD_DIR}/config.h
tilde.o: tilde.h
@ -259,6 +270,8 @@ input.o: rlprivate.h
isearch.o: rlprivate.h
kill.o: rlprivate.h
macro.o: rlprivate.h
mbutil.o: rlprivate.h
misc.o: rlprivate.h
nls.o: rlprivate.h
parens.o: rlprivate.h
readline.o: rlprivate.h
@ -266,6 +279,7 @@ rltty.o: rlprivate.h
search.o: rlprivate.h
signals.o: rlprivate.h
terminal.o: rlprivate.h
text.o: rlprivate.h
undo.o: rlprivate.h
util.o: rlprivate.h
vi_mode.o: rlprivate.h
@ -282,14 +296,31 @@ isearch.o: xmalloc.h
keymaps.o: xmalloc.h
kill.o: xmalloc.h
macro.o: xmalloc.h
mbutil.o: xmalloc.h
misc.o: xmalloc.h
readline.o: xmalloc.h
savestring.o: xmalloc.h
search.o: xmalloc.h
shell.o: xmalloc.h
tilde.o: xmalloc.h
terminal.o: xmalloc.h
text.o: xmalloc.h
tilde.o: xmalloc.h
undo.o: xmalloc.h
util.o: xmalloc.h
vi_mode.o: xmalloc.h
xmalloc.o: xmalloc.h
complete.o: rlmbutil.h
display.o: rlmbutil.h
histexpand.o: rlmbutil.h
input.o: rlmbutil.h
isearch.o: rlmbutil.h
mbutil.o: rlmbutil.h
misc.o: rlmbutil.h
readline.o: rlmbutil.h
search.o: rlmbutil.h
text.o: rlmbutil.h
vi_mode.o: rlmbutil.h
# Rules for deficient makes, like SunOS and Solaris
bind.o: bind.c
@ -298,15 +329,13 @@ compat.o: compat.c
complete.o: complete.c
display.o: display.c
funmap.o: funmap.c
histexpand.o: histexpand.c
histfile.o: histfile.c
history.o: history.c
histsearch.o: histsearch.c
input.o: input.c
isearch.o: isearch.c
keymaps.o: keymaps.c emacs_keymap.c vi_keymap.c
kill.o: kill.c
macro.o: macro.c
mbutil.o: mbutil.c
misc.o: misc.c
nls.o: nls.c
parens.o: parens.c
readline.o: readline.c
@ -316,8 +345,14 @@ search.o: search.c
shell.o: shell.c
signals.o: signals.c
terminal.o: terminal.c
text.o: terminal.c
tilde.o: tilde.c
undo.o: undo.c
util.o: util.c
vi_mode.o: vi_mode.c
xmalloc.o: xmalloc.c
histexpand.o: histexpand.c
histfile.o: histfile.c
history.o: history.c
histsearch.o: histsearch.c

View file

@ -68,6 +68,8 @@ extern char *strchr (), *strrchr ();
/* Variables exported by this file. */
Keymap rl_binding_keymap;
static char *_rl_read_file PARAMS((char *, size_t *));
static void _rl_init_file_error PARAMS((const char *));
static int _rl_read_init_file PARAMS((const char *, int));
static int glean_key_from_name PARAMS((char *));
static int substring_member_of_array PARAMS((char *, const char **));
@ -246,6 +248,9 @@ rl_generic_bind (type, keyseq, data, map)
char *keys;
int keys_len;
register int i;
KEYMAP_ENTRY k;
k.function = 0;
/* If no keys to bind to, exit right away. */
if (!keyseq || !*keyseq)
@ -269,7 +274,12 @@ rl_generic_bind (type, keyseq, data, map)
/* Bind keys, making new keymaps as necessary. */
for (i = 0; i < keys_len; i++)
{
unsigned char ic = keys[i];
unsigned char uc = keys[i];
int ic;
ic = uc;
if (ic < 0 || ic >= KEYMAP_SIZE)
return -1;
if (_rl_convert_meta_chars_to_ascii && META_CHAR (ic))
{
@ -282,18 +292,40 @@ rl_generic_bind (type, keyseq, data, map)
{
if (map[ic].type != ISKMAP)
{
if (map[ic].type == ISMACR)
free ((char *)map[ic].function);
/* We allow subsequences of keys. If a keymap is being
created that will `shadow' an existing function or macro
key binding, we save that keybinding into the ANYOTHERKEY
index in the new map. The dispatch code will look there
to find the function to execute if the subsequence is not
matched. ANYOTHERKEY was chosen to be greater than
UCHAR_MAX. */
k = map[ic];
map[ic].type = ISKMAP;
map[ic].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap());
}
map = FUNCTION_TO_KEYMAP (map, ic);
/* The dispatch code will return this function if no matching
key sequence is found in the keymap. This (with a little
help from the dispatch code in readline.c) allows `a' to be
mapped to something, `abc' to be mapped to something else,
and the function bound to `a' to be executed when the user
types `abx', leaving `bx' in the input queue. */
if (k.function /* && k.type == ISFUNC */)
{
map[ANYOTHERKEY] = k;
k.function = 0;
}
}
else
{
if (map[ic].type == ISMACR)
free ((char *)map[ic].function);
else if (map[ic].type == ISKMAP)
{
map = FUNCTION_TO_KEYMAP (map, ic);
ic = ANYOTHERKEY;
}
map[ic].function = KEYMAP_TO_FUNCTION (data);
map[ic].type = type;
@ -331,7 +363,7 @@ rl_translate_keyseq (seq, array, len)
/* Handle special case of backwards define. */
if (strncmp (&seq[i], "C-\\M-", 5) == 0)
{
array[l++] = ESC;
array[l++] = ESC; /* ESC is meta-prefix */
i += 5;
array[l++] = CTRL (_rl_to_upper (seq[i]));
if (seq[i] == '\0')
@ -340,7 +372,7 @@ rl_translate_keyseq (seq, array, len)
else if (c == 'M')
{
i++;
array[l++] = ESC; /* XXX */
array[l++] = ESC; /* ESC is meta-prefix */
}
else if (c == 'C')
{
@ -632,25 +664,15 @@ _rl_read_file (filename, sizep)
i = read (file, buffer, file_size);
close (file);
#if 0
if (i < file_size)
#else
if (i < 0)
#endif
{
free (buffer);
return ((char *)NULL);
}
#if 0
buffer[file_size] = '\0';
if (sizep)
*sizep = file_size;
#else
buffer[i] = '\0';
if (sizep)
*sizep = i;
#endif
return (buffer);
}
@ -767,7 +789,7 @@ _rl_read_init_file (filename, include_level)
static void
_rl_init_file_error (msg)
char *msg;
const char *msg;
{
if (currently_reading_init_file)
fprintf (stderr, "readline: %s: line %d: %s\n", current_readline_init_file,
@ -1075,7 +1097,7 @@ rl_parse_and_bind (string)
/* Make VAR point to start of variable name. */
while (*var && whitespace (*var)) var++;
/* Make value point to start of value string. */
/* Make VALUE point to start of value string. */
value = var;
while (*value && !whitespace (*value)) value++;
if (*value)
@ -1240,6 +1262,7 @@ static struct {
int flags;
} boolean_varlist [] = {
{ "blink-matching-paren", &rl_blink_matching_paren, V_SPECIAL },
{ "byte-oriented", &rl_byte_oriented, 0 },
{ "completion-ignore-case", &_rl_completion_case_fold, 0 },
{ "convert-meta", &_rl_convert_meta_chars_to_ascii, 0 },
{ "disable-completion", &rl_inhibit_completion, 0 },
@ -1250,9 +1273,11 @@ static struct {
{ "input-meta", &_rl_meta_flag, 0 },
{ "mark-directories", &_rl_complete_mark_directories, 0 },
{ "mark-modified-lines", &_rl_mark_modified_lines, 0 },
{ "mark-symlinked-directories", &_rl_complete_mark_symlink_dirs, 0 },
{ "match-hidden-files", &_rl_match_hidden_files, 0 },
{ "meta-flag", &_rl_meta_flag, 0 },
{ "output-meta", &_rl_output_meta_chars, 0 },
{ "page-completions", &_rl_page_completions, 0 },
{ "prefer-visible-bell", &_rl_prefer_visible_bell, V_SPECIAL },
{ "print-completions-horizontally", &_rl_print_completions_horizontally, 0 },
{ "show-all-if-ambiguous", &_rl_complete_show_all, 0 },
@ -1264,7 +1289,7 @@ static struct {
static int
find_boolean_var (name)
char *name;
const char *name;
{
register int i;
@ -1333,7 +1358,7 @@ static struct {
static int
find_string_var (name)
char *name;
const char *name;
{
register int i;
@ -1659,17 +1684,18 @@ _rl_get_keyname (key)
pairs for possible inclusion in an inputrc file, we don't want to
do any special meta processing on KEY. */
#if 0
#if 1
/* XXX - Experimental */
/* We might want to do this, but the old version of the code did not. */
/* If this is an escape character, we don't want to do any more processing.
Just add the special ESC key sequence and return. */
if (c == ESC)
{
keyseq[0] = '\\';
keyseq[1] = 'e';
keyseq[2] = '\0';
return keyseq;
keyname[0] = '\\';
keyname[1] = 'e';
keyname[2] = '\0';
return keyname;
}
#endif
@ -1780,7 +1806,12 @@ rl_invoking_keyseqs_in_map (function, map)
char *keyname = (char *)xmalloc (6 + strlen (seqs[i]));
if (key == ESC)
#if 0
sprintf (keyname, "\\e");
#else
/* XXX - experimental */
sprintf (keyname, "\\M-");
#endif
else if (CTRL_CHAR (key))
sprintf (keyname, "\\C-%c", _rl_to_lower (UNCTRL (key)));
else if (key == RUBOUT)
@ -1927,11 +1958,8 @@ _rl_macro_dumper_internal (print_readably, map, prefix)
{
case ISMACR:
keyname = _rl_get_keyname (key);
#if 0
out = (char *)map[key].function;
#else
out = _rl_untranslate_macro_value ((char *)map[key].function);
#endif
if (print_readably)
fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "",
keyname,
@ -1941,9 +1969,7 @@ _rl_macro_dumper_internal (print_readably, map, prefix)
keyname,
out ? out : "");
free (keyname);
#if 1
free (out);
#endif
break;
case ISFUNC:
break;
@ -2033,7 +2059,7 @@ rl_variable_dumper (print_readably)
if (print_readably)
fprintf (rl_outstream, "set comment-begin %s\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
else
fprintf (rl_outstream, "comment-begin is set to `%s'\n", _rl_comment_begin ? _rl_comment_begin : "");
fprintf (rl_outstream, "comment-begin is set to `%s'\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
/* completion-query-items */
if (print_readably)
@ -2047,15 +2073,6 @@ rl_variable_dumper (print_readably)
else
fprintf (rl_outstream, "editing-mode is set to `%s'\n", (rl_editing_mode == emacs_mode) ? "emacs" : "vi");
/* keymap */
kname = rl_get_keymap_name (_rl_keymap);
if (kname == 0)
kname = rl_get_keymap_name_from_edit_mode ();
if (print_readably)
fprintf (rl_outstream, "set keymap %s\n", kname ? kname : "none");
else
fprintf (rl_outstream, "keymap is set to `%s'\n", kname ? kname : "none");
/* isearch-terminators */
if (_rl_isearch_terminators)
{
@ -2070,6 +2087,15 @@ rl_variable_dumper (print_readably)
free (disp);
}
/* keymap */
kname = rl_get_keymap_name (_rl_keymap);
if (kname == 0)
kname = rl_get_keymap_name_from_edit_mode ();
if (print_readably)
fprintf (rl_outstream, "set keymap %s\n", kname ? kname : "none");
else
fprintf (rl_outstream, "keymap is set to `%s'\n", kname ? kname : "none");
}
/* Print all of the current variables and their values to
@ -2086,7 +2112,9 @@ rl_dump_variables (count, key)
return (0);
}
/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. */
/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right
now, this is always used to attempt to bind the arrow keys, hence the
check for rl_vi_movement_mode. */
void
_rl_bind_if_unbound (keyseq, default_func)
const char *keyseq;
@ -2097,7 +2125,11 @@ _rl_bind_if_unbound (keyseq, default_func)
if (keyseq)
{
func = rl_function_of_keyseq (keyseq, _rl_keymap, (int *)NULL);
#if defined (VI_MODE)
if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode)
#else
if (!func || func == rl_do_lowercase_version)
#endif
rl_set_key (keyseq, default_func, _rl_keymap);
}
}

View file

@ -44,7 +44,10 @@
#endif
#ifdef CTRL
#undef CTRL
# undef CTRL
#endif
#ifdef UNCTRL
# undef UNCTRL
#endif
/* Some character stuff. */
@ -76,6 +79,9 @@
#define NON_NEGATIVE(c) ((unsigned char)(c) == (c))
/* Some systems define these; we want our definitions. */
#undef ISPRINT
#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))

View file

@ -55,6 +55,7 @@ extern int errno;
/* System-specific feature definitions and include files. */
#include "rldefs.h"
#include "rlmbutil.h"
/* Some standard library routines. */
#include "readline.h"
@ -100,10 +101,11 @@ static int stat_char PARAMS((char *));
static char *rl_quote_filename PARAMS((char *, int, char *));
static int get_y_or_n PARAMS((void));
static void set_completion_defaults PARAMS((int));
static int get_y_or_n PARAMS((int));
static int _rl_internal_pager PARAMS((int));
static char *printable_part PARAMS((char *));
static int print_filename PARAMS((char *, char *));
static char find_completion_word PARAMS((int *, int *));
static char **gen_completion_matches PARAMS((char *, int, int, rl_compentry_func_t *, int, int));
@ -116,7 +118,6 @@ static int compute_lcd_of_matches PARAMS((char **, int, const char *));
static int postprocess_matches PARAMS((char ***, int));
static char *make_quoted_replacement PARAMS((char *, int, char *));
static void free_match_list PARAMS((char **));
/* **************************************************************** */
/* */
@ -132,6 +133,12 @@ int _rl_complete_show_all = 0;
/* If non-zero, completed directory names have a slash appended. */
int _rl_complete_mark_directories = 1;
/* If non-zero, the symlinked directory completion behavior introduced in
readline-4.2a is disabled, and symlinks that point to directories have
a slash appended (subject to the value of _rl_complete_mark_directories).
This is user-settable via the mark-symlinked-directories variable. */
int _rl_complete_mark_symlink_dirs = 0;
/* If non-zero, completions are printed horizontally in alphabetical order,
like `ls -x'. */
int _rl_print_completions_horizontally;
@ -194,10 +201,12 @@ int rl_completion_type = 0;
she is sure she wants to see them all. */
int rl_completion_query_items = 100;
int _rl_page_completions = 1;
/* The basic list of characters that signal a break between words for the
completer routine. The contents of this variable is what breaks words
in the shell, i.e. " \t\n\"\\'`@$><=" */
const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; /* }) */
/* List of basic quoting characters. */
const char *rl_basic_quote_characters = "\"'";
@ -264,10 +273,26 @@ rl_dequote_func_t *rl_filename_dequoting_function = (rl_dequote_func_t *)NULL;
completer. */
rl_linebuf_func_t *rl_char_is_quoted_p = (rl_linebuf_func_t *)NULL;
/* If non-zero, the completion functions don't append anything except a
possible closing quote. This is set to 0 by rl_complete_internal and
may be changed by an application-specific completion function. */
int rl_completion_suppress_append = 0;
/* Character appended to completed words when at the end of the line. The
default is a space. */
int rl_completion_append_character = ' ';
/* If non-zero, a slash will be appended to completed filenames that are
symbolic links to directory names, subject to the value of the
mark-directories variable (which is user-settable). This exists so
that application completion functions can override the user's preference
(set via the mark-symlinked-directories variable) if appropriate.
It's set to the value of _rl_complete_mark_symlink_dirs in
rl_complete_internal before any application-specific completion
function is called, so without that function doing anything, the user's
preferences are honored. */
int rl_completion_mark_symlink_dirs;
/* If non-zero, inhibit completion (temporarily). */
int rl_inhibit_completion;
@ -290,7 +315,7 @@ rl_complete (ignore, invoking_key)
int ignore, invoking_key;
{
if (rl_inhibit_completion)
return (rl_insert (ignore, invoking_key));
return (_rl_insert_char (ignore, invoking_key));
else if (rl_last_func == rl_complete && !completion_changed_buffer)
return (rl_complete_internal ('?'));
else if (_rl_complete_show_all)
@ -314,15 +339,49 @@ rl_insert_completions (ignore, invoking_key)
return (rl_complete_internal ('*'));
}
/* Return the correct value to pass to rl_complete_internal performing
the same tests as rl_complete. This allows consecutive calls to an
application's completion function to list possible completions and for
an application-specific completion function to honor the
show-all-if-ambiguous readline variable. */
int
rl_completion_mode (cfunc)
rl_command_func_t *cfunc;
{
if (rl_last_func == cfunc && !completion_changed_buffer)
return '?';
else if (_rl_complete_show_all)
return '!';
else
return TAB;
}
/************************************/
/* */
/* Completion utility functions */
/* */
/************************************/
/* Set default values for readline word completion. These are the variables
that application completion functions can change or inspect. */
static void
set_completion_defaults (what_to_do)
int what_to_do;
{
/* Only the completion entry function can change these. */
rl_filename_completion_desired = 0;
rl_filename_quoting_desired = 1;
rl_completion_type = what_to_do;
rl_completion_suppress_append = 0;
/* The completion entry function may optionally change this. */
rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs;
}
/* The user must press "y" or "n". Non-zero return means "y" pressed. */
static int
get_y_or_n ()
get_y_or_n (for_pager)
int for_pager;
{
int c;
@ -338,10 +397,32 @@ get_y_or_n ()
return (0);
if (c == ABORT_CHAR)
_rl_abort_internal ();
if (for_pager && (c == NEWLINE || c == RETURN))
return (2);
if (for_pager && (c == 'q' || c == 'Q'))
return (0);
rl_ding ();
}
}
static int
_rl_internal_pager (lines)
int lines;
{
int i;
fprintf (rl_outstream, "--More--");
fflush (rl_outstream);
i = get_y_or_n (1);
_rl_erase_entire_line ();
if (i == 0)
return -1;
else if (i == 2)
return (lines - 1);
else
return 0;
}
#if defined (VISIBLE_STATS)
/* Return the character which best describes FILENAME.
`@' for symbolic links
@ -402,19 +483,41 @@ stat_char (filename)
/* Return the portion of PATHNAME that should be output when listing
possible completions. If we are hacking filename completion, we
are only interested in the basename, the portion following the
final slash. Otherwise, we return what we were passed. */
final slash. Otherwise, we return what we were passed. Since
printing empty strings is not very informative, if we're doing
filename completion, and the basename is the empty string, we look
for the previous slash and return the portion following that. If
there's no previous slash, we just return what we were passed. */
static char *
printable_part (pathname)
char *pathname;
{
char *temp;
char *temp, *x;
temp = rl_filename_completion_desired ? strrchr (pathname, '/') : (char *)NULL;
if (rl_filename_completion_desired == 0) /* don't need to do anything */
return (pathname);
temp = strrchr (pathname, '/');
#if defined (__MSDOS__)
if (rl_filename_completion_desired && temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
temp = pathname + 1;
#endif
return (temp ? ++temp : pathname);
if (temp == 0 || *temp == '\0')
return (pathname);
/* If the basename is NULL, we might have a pathname like '/usr/src/'.
Look for a previous slash and, if one is found, return the portion
following that slash. If there's no previous slash, just return the
pathname we were passed. */
else if (temp[1] == '\0')
{
for (x = temp - 1; x > pathname; x--)
if (*x == '/')
break;
return ((*x == '/') ? x + 1 : pathname);
}
else
return ++temp;
}
/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we
@ -543,8 +646,8 @@ rl_quote_filename (s, rtype, qcp)
quote, or backslash) anywhere in the string. DP, if non-null, is set to
the value of the delimiter character that caused a word break. */
static char
find_completion_word (fp, dp)
char
_rl_find_completion_word (fp, dp)
int *fp, *dp;
{
int scan, end, found_quote, delimiter, pass_next, isbrk;
@ -599,6 +702,8 @@ find_completion_word (fp, dp)
found_quote |= RL_QF_SINGLE_QUOTE;
else if (quote_char == '"')
found_quote |= RL_QF_DOUBLE_QUOTE;
else
found_quote |= RL_QF_OTHER_QUOTE;
}
}
}
@ -608,7 +713,11 @@ find_completion_word (fp, dp)
/* We didn't find an unclosed quoted substring upon which to do
completion, so use the word break characters to find the
substring on which to complete. */
#if defined (HANDLE_MULTIBYTE)
while (rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_ANY))
#else
while (--rl_point)
#endif
{
scan = rl_line_buffer[rl_point];
@ -780,6 +889,11 @@ compute_lcd_of_matches (match_list, matches, text)
{
register int i, c1, c2, si;
int low; /* Count of max-matched characters. */
#if defined (HANDLE_MULTIBYTE)
int v;
mbstate_t ps1, ps2;
wchar_t wc1, wc2;
#endif
/* If only one match, just use that. Otherwise, compare each
member of the list with the next, finding out where they
@ -793,12 +907,33 @@ compute_lcd_of_matches (match_list, matches, text)
for (i = 1, low = 100000; i < matches; i++)
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
memset (&ps1, 0, sizeof (mbstate_t));
memset (&ps2, 0, sizeof (mbstate_t));
}
#endif
if (_rl_completion_case_fold)
{
for (si = 0;
(c1 = _rl_to_lower(match_list[i][si])) &&
(c2 = _rl_to_lower(match_list[i + 1][si]));
si++)
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
v = mbrtowc (&wc1, match_list[i]+si, strlen (match_list[i]+si), &ps1);
mbrtowc (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), &ps2);
wc1 = towlower (wc1);
wc2 = towlower (wc2);
if (wc1 != wc2)
break;
else if (v > 1)
si += v - 1;
}
else
#endif
if (c1 != c2)
break;
}
@ -808,6 +943,17 @@ compute_lcd_of_matches (match_list, matches, text)
(c1 = match_list[i][si]) &&
(c2 = match_list[i + 1][si]);
si++)
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
mbstate_t ps_back = ps1;
if (!_rl_compare_chars (match_list[i], si, &ps1, match_list[i+1], si, &ps2))
break;
else if ((v = _rl_get_char_len (&match_list[i][si], &ps_back)) > 1)
si += v - 1;
}
else
#endif
if (c1 != c2)
break;
}
@ -828,6 +974,8 @@ compute_lcd_of_matches (match_list, matches, text)
{
match_list[0] = (char *)xmalloc (low + 1);
/* XXX - this might need changes in the presence of multibyte chars */
/* If we are ignoring case, try to preserve the case of the string
the user typed in the face of multiple matches differing in case. */
if (_rl_completion_case_fold)
@ -871,6 +1019,9 @@ postprocess_matches (matchesp, matching_filenames)
matches = *matchesp;
if (matches == 0)
return 0;
/* It seems to me that in all the cases we handle we would like
to ignore duplicate possiblilities. Scan for the text to
insert being identical to the other completions. */
@ -923,7 +1074,7 @@ rl_display_match_list (matches, len, max)
char **matches;
int len, max;
{
int count, limit, printed_len;
int count, limit, printed_len, lines;
int i, j, k, l;
char *temp;
@ -951,6 +1102,7 @@ rl_display_match_list (matches, len, max)
rl_crlf ();
lines = 0;
if (_rl_print_completions_horizontally == 0)
{
/* Print the sorted items, up-and-down alphabetically, like ls. */
@ -972,6 +1124,13 @@ rl_display_match_list (matches, len, max)
l += count;
}
rl_crlf ();
lines++;
if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
return;
}
}
}
else
@ -985,7 +1144,16 @@ rl_display_match_list (matches, len, max)
if (matches[i+1])
{
if (i && (limit > 1) && (i % limit) == 0)
rl_crlf ();
{
rl_crlf ();
lines++;
if (_rl_page_completions && lines >= _rl_screenheight - 1)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
return;
}
}
else
for (k = 0; k < max - printed_len; k++)
putc (' ', rl_outstream);
@ -1057,7 +1225,7 @@ display_matches (matches)
rl_crlf ();
fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len);
fflush (rl_outstream);
if (get_y_or_n () == 0)
if (get_y_or_n (0) == 0)
{
rl_crlf ();
@ -1155,7 +1323,11 @@ insert_match (match, start, mtype, qc)
default trailing character is a space. Returns the number of characters
appended. If NONTRIVIAL_MATCH is set, we test for a symlink (if the OS
has them) and don't add a suffix for a symlink to a directory. A
nontrivial match is one that actually adds to the word being completed. */
nontrivial match is one that actually adds to the word being completed.
The variable rl_completion_mark_symlink_dirs controls this behavior
(it's initially set to the what the user has chosen, indicated by the
value of _rl_complete_mark_symlink_dirs, but may be modified by an
application's completion function). */
static int
append_to_match (text, delimiter, quote_char, nontrivial_match)
char *text;
@ -1171,7 +1343,7 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
if (delimiter)
temp_string[temp_string_index++] = delimiter;
else if (rl_completion_append_character)
else if (rl_completion_suppress_append == 0 && rl_completion_append_character)
temp_string[temp_string_index++] = rl_completion_append_character;
temp_string[temp_string_index++] = '\0';
@ -1179,11 +1351,21 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
if (rl_filename_completion_desired)
{
filename = tilde_expand (text);
s = nontrivial_match ? LSTAT (filename, &finfo) : stat (filename, &finfo);
s = (nontrivial_match && rl_completion_mark_symlink_dirs == 0)
? LSTAT (filename, &finfo)
: stat (filename, &finfo);
if (s == 0 && S_ISDIR (finfo.st_mode))
{
if (_rl_complete_mark_directories && rl_line_buffer[rl_point] != '/')
rl_insert_text ("/");
if (_rl_complete_mark_directories)
{
/* This is clumsy. Avoid putting in a double slash if point
is at the end of the line and the previous character is a
slash. */
if (rl_point && rl_line_buffer[rl_point] == '\0' && rl_line_buffer[rl_point - 1] == '/')
;
else if (rl_line_buffer[rl_point] != '/')
rl_insert_text ("/");
}
}
#ifdef S_ISLNK
/* Don't add anything if the filename is a symlink and resolves to a
@ -1194,14 +1376,14 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
#endif
else
{
if (rl_point == rl_end)
if (rl_point == rl_end && temp_string_index)
rl_insert_text (temp_string);
}
free (filename);
}
else
{
if (rl_point == rl_end)
if (rl_point == rl_end && temp_string_index)
rl_insert_text (temp_string);
}
@ -1247,12 +1429,15 @@ insert_all_matches (matches, point, qc)
rl_end_undo_group ();
}
static void
free_match_list (matches)
void
_rl_free_match_list (matches)
char **matches;
{
register int i;
if (matches == 0)
return;
for (i = 0; matches[i]; i++)
free (matches[i]);
free (matches);
@ -1276,10 +1461,8 @@ rl_complete_internal (what_to_do)
char quote_char;
RL_SETSTATE(RL_STATE_COMPLETING);
/* Only the completion entry function can change these. */
rl_filename_completion_desired = 0;
rl_filename_quoting_desired = 1;
rl_completion_type = what_to_do;
set_completion_defaults (what_to_do);
saved_line_buffer = rl_line_buffer ? savestring (rl_line_buffer) : (char *)NULL;
our_func = rl_completion_entry_function
@ -1294,7 +1477,7 @@ rl_complete_internal (what_to_do)
if (rl_point)
/* This (possibly) changes rl_point. If it returns a non-zero char,
we know we have an open quote. */
quote_char = find_completion_word (&found_quote, &delimiter);
quote_char = _rl_find_completion_word (&found_quote, &delimiter);
start = rl_point;
rl_point = end;
@ -1310,6 +1493,7 @@ rl_complete_internal (what_to_do)
{
rl_ding ();
FREE (saved_line_buffer);
completion_changed_buffer = 0;
RL_UNSETSTATE(RL_STATE_COMPLETING);
return (0);
}
@ -1375,7 +1559,7 @@ rl_complete_internal (what_to_do)
return 1;
}
free_match_list (matches);
_rl_free_match_list (matches);
/* Check to see if the line has changed through all of this manipulation. */
if (saved_line_buffer)
@ -1735,15 +1919,13 @@ rl_menu_complete (count, ignore)
/* Clean up from previous call, if any. */
FREE (orig_text);
if (matches)
free_match_list (matches);
_rl_free_match_list (matches);
match_list_index = match_list_size = 0;
matches = (char **)NULL;
/* Only the completion entry function can change these. */
rl_filename_completion_desired = 0;
rl_filename_quoting_desired = 1;
rl_completion_type = '%';
set_completion_defaults ('%');
our_func = rl_completion_entry_function
? rl_completion_entry_function
@ -1757,7 +1939,7 @@ rl_menu_complete (count, ignore)
if (rl_point)
/* This (possibly) changes rl_point. If it returns a non-zero char,
we know we have an open quote. */
quote_char = find_completion_word (&found_quote, &delimiter);
quote_char = _rl_find_completion_word (&found_quote, &delimiter);
orig_start = rl_point;
rl_point = orig_end;

View file

@ -43,6 +43,7 @@
/* System-specific feature definitions and include files. */
#include "rldefs.h"
#include "rlmbutil.h"
/* Termcap library stuff. */
#include "tcap.h"
@ -65,9 +66,16 @@ extern char *_rl_term_forward_char;
static void update_line PARAMS((char *, char *, int, int, int, int));
static void space_to_eol PARAMS((int));
static void delete_chars PARAMS((int));
static void insert_some_chars PARAMS((char *, int));
static void insert_some_chars PARAMS((char *, int, int));
static void cr PARAMS((void));
#if defined (HANDLE_MULTIBYTE)
static int _rl_col_width PARAMS((char *, int, int));
static int *_rl_wrapped_line;
#else
# define _rl_col_width(l, s, e) (((e) <= (s)) ? 0 : (e) - (s))
#endif
static int *inv_lbreaks, *vis_lbreaks;
static int inv_lbsize, vis_lbsize;
@ -359,6 +367,9 @@ init_line_structures (minsize)
inv_lbsize = vis_lbsize = 256;
inv_lbreaks = (int *)xmalloc (inv_lbsize * sizeof (int));
vis_lbreaks = (int *)xmalloc (vis_lbsize * sizeof (int));
#if defined (HANDLE_MULTIBYTE)
_rl_wrapped_line = (int *)xmalloc (vis_lbsize * sizeof (int));
#endif
inv_lbreaks[0] = vis_lbreaks[0] = 0;
}
}
@ -372,6 +383,13 @@ rl_redisplay ()
int c_pos, inv_botlin, lb_botlin, lb_linenum;
int newlines, lpos, temp;
char *prompt_this_line;
#if defined (HANDLE_MULTIBYTE)
wchar_t wc;
size_t wc_bytes;
int wc_width;
mbstate_t ps;
int _rl_wrapped_multicolumn = 0;
#endif
if (!readline_echoing_p)
return;
@ -472,7 +490,25 @@ rl_redisplay ()
inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
} \
} while (0)
#if defined (HANDLE_MULTIBYTE)
#define CHECK_LPOS() \
do { \
lpos++; \
if (lpos >= _rl_screenwidth) \
{ \
if (newlines >= (inv_lbsize - 2)) \
{ \
inv_lbsize *= 2; \
inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \
_rl_wrapped_line = (int *)xrealloc (_rl_wrapped_line, inv_lbsize * sizeof (int)); \
} \
inv_lbreaks[++newlines] = out; \
_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn; \
lpos = 0; \
} \
} while (0)
#else
#define CHECK_LPOS() \
do { \
lpos++; \
@ -487,10 +523,14 @@ rl_redisplay ()
lpos = 0; \
} \
} while (0)
#endif
/* inv_lbreaks[i] is where line i starts in the buffer. */
inv_lbreaks[newlines = 0] = 0;
lpos = out - wrap_offset;
#if defined (HANDLE_MULTIBYTE)
memset (_rl_wrapped_line, 0, vis_lbsize);
#endif
/* prompt_invis_chars_first_line is the number of invisible characters in
the first physical line of the prompt.
@ -508,7 +548,11 @@ rl_redisplay ()
probably too much work for the benefit gained. How many people have
prompts that exceed two physical lines? */
temp = ((newlines + 1) * _rl_screenwidth) +
#if 0
((newlines == 0) ? prompt_invis_chars_first_line : 0) +
#else
((newlines == 0 && local_prompt_prefix == 0) ? prompt_invis_chars_first_line : 0) +
#endif
((newlines == 1) ? wrap_offset : 0);
inv_lbreaks[++newlines] = temp;
@ -523,10 +567,44 @@ rl_redisplay ()
It maintains an array of line breaks for display (inv_lbreaks).
This handles expanding tabs for display and displaying meta characters. */
lb_linenum = 0;
#if defined (HANDLE_MULTIBYTE)
in = 0;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
memset (&ps, 0, sizeof (mbstate_t));
wc_bytes = mbrtowc (&wc, rl_line_buffer, rl_end, &ps);
}
else
wc_bytes = 1;
while (in < rl_end)
#else
for (in = 0; in < rl_end; in++)
#endif
{
c = (unsigned char)rl_line_buffer[in];
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
if (wc_bytes == (size_t)-1 || wc_bytes == (size_t)-2)
{
/* Byte sequence is invalid or shortened. Assume that the
first byte represents a character. */
wc_bytes = 1;
/* Assume that a character occupies a single column. */
wc_width = 1;
memset (&ps, 0, sizeof (mbstate_t));
}
else if (wc_bytes == (size_t)0)
break; /* Found '\0' */
else
{
temp = wcwidth (wc);
wc_width = (temp < 0) ? 1 : temp;
}
}
#endif
if (out + 8 >= line_size) /* XXX - 8 for \t */
{
line_size *= 2;
@ -541,7 +619,11 @@ rl_redisplay ()
lb_linenum = newlines;
}
#if defined (HANDLE_MULTIBYTE)
if (META_CHAR (c) && _rl_output_meta_chars == 0) /* XXX - clean up */
#else
if (META_CHAR (c))
#endif
{
if (_rl_output_meta_chars == 0)
{
@ -610,9 +692,52 @@ rl_redisplay ()
}
else
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
register int i;
_rl_wrapped_multicolumn = 0;
if (_rl_screenwidth < lpos + wc_width)
for (i = lpos; i < _rl_screenwidth; i++)
{
/* The space will be removed in update_line() */
line[out++] = ' ';
_rl_wrapped_multicolumn++;
CHECK_LPOS();
}
if (in == rl_point)
{
c_pos = out;
lb_linenum = newlines;
}
for (i = in; i < in+wc_bytes; i++)
line[out++] = rl_line_buffer[i];
for (i = 0; i < wc_width; i++)
CHECK_LPOS();
}
else
{
line[out++] = c;
CHECK_LPOS();
}
#else
line[out++] = c;
CHECK_LPOS();
#endif
}
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
in += wc_bytes;
wc_bytes = mbrtowc (&wc, rl_line_buffer + in, rl_end - in, &ps);
}
else
in++;
#endif
}
line[out] = '\0';
if (c_pos < 0)
@ -650,7 +775,12 @@ rl_redisplay ()
only display a screenful. We should display the last screen,
not the first. */
if (out >= _rl_screenchars)
out = _rl_screenchars - 1;
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
out = _rl_find_prev_mbchar (line, _rl_screenchars, MB_FIND_ANY);
else
out = _rl_screenchars - 1;
}
/* The first line is at character position 0 in the buffer. The
second and subsequent lines start at inv_lbreaks[N], offset by
@ -736,7 +866,10 @@ rl_redisplay ()
tputs (_rl_term_cr, 1, _rl_output_character_function);
#endif
_rl_output_some_chars (local_prompt, nleft);
_rl_last_c_pos = nleft;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_last_c_pos = _rl_col_width(local_prompt, 0, nleft);
else
_rl_last_c_pos = nleft;
}
/* Where on that line? And where does that line start
@ -752,10 +885,15 @@ rl_redisplay ()
if (wrap_offset && cursor_linenum == 0 && nleft < _rl_last_c_pos)
{
_rl_backspace (_rl_last_c_pos - nleft);
_rl_last_c_pos = nleft;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_last_c_pos = _rl_col_width (&visible_line[pos], 0, nleft);
else
_rl_last_c_pos = nleft;
}
if (nleft != _rl_last_c_pos)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_move_cursor_relative (nleft, &invisible_line[pos]);
else if (nleft != _rl_last_c_pos)
_rl_move_cursor_relative (nleft, &invisible_line[pos]);
}
}
@ -900,6 +1038,11 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
register char *ofd, *ols, *oe, *nfd, *nls, *ne;
int temp, lendiff, wsatend, od, nd;
int current_invis_chars;
int col_lendiff, col_temp;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps_new, ps_old;
int new_offset, old_offset, tmp;
#endif
/* If we're at the right edge of a terminal that supports xn, we're
ready to wrap around, so do so. This fixes problems with knowing
@ -908,19 +1051,97 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
position of the cursor. */
temp = _rl_last_c_pos - W_OFFSET(_rl_last_v_pos, visible_wrap_offset);
if (temp == _rl_screenwidth && _rl_term_autowrap && !_rl_horizontal_scroll_mode
&& _rl_last_v_pos == current_line - 1)
&& _rl_last_v_pos == current_line - 1)
{
if (new[0])
putc (new[0], rl_outstream);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
wchar_t wc;
mbstate_t ps;
int tempwidth, bytes;
size_t ret;
/* This fixes only double-column characters, but if the wrapped
character comsumes more than three columns, spaces will be
inserted in the string buffer. */
if (_rl_wrapped_line[current_line] > 0)
_rl_clear_to_eol (_rl_wrapped_line[current_line]);
memset (&ps, 0, sizeof (mbstate_t));
ret = mbrtowc (&wc, new, MB_CUR_MAX, &ps);
if (ret == (size_t)-1 || ret == (size_t)-2)
{
tempwidth = 1;
ret = 1;
}
else if (ret == 0)
tempwidth = 0;
else
tempwidth = wcwidth (wc);
if (tempwidth > 0)
{
int count;
bytes = ret;
for (count = 0; count < bytes; count++)
putc (new[count], rl_outstream);
_rl_last_c_pos = tempwidth;
_rl_last_v_pos++;
memset (&ps, 0, sizeof (mbstate_t));
ret = mbrtowc (&wc, old, MB_CUR_MAX, &ps);
if (ret != 0 && bytes != 0)
{
if (ret == (size_t)-1 || ret == (size_t)-2)
memmove (old+bytes, old+1, strlen (old+1));
else
memmove (old+bytes, old+ret, strlen (old+ret));
memcpy (old, new, bytes);
}
}
else
{
putc (' ', rl_outstream);
_rl_last_c_pos = 1;
_rl_last_v_pos++;
if (old[0] && new[0])
old[0] = new[0];
}
}
else
putc (' ', rl_outstream);
_rl_last_c_pos = 1; /* XXX */
_rl_last_v_pos++;
if (old[0] && new[0])
old[0] = new[0];
#endif
{
if (new[0])
putc (new[0], rl_outstream);
else
putc (' ', rl_outstream);
_rl_last_c_pos = 1; /* XXX */
_rl_last_v_pos++;
if (old[0] && new[0])
old[0] = new[0];
}
}
/* Find first difference. */
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
memset (&ps_new, 0, sizeof(mbstate_t));
memset (&ps_old, 0, sizeof(mbstate_t));
new_offset = old_offset = 0;
for (ofd = old, nfd = new;
(ofd - old < omax) && *ofd &&
_rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new); )
{
old_offset = _rl_find_next_mbchar (old, old_offset, 1, MB_FIND_ANY);
new_offset = _rl_find_next_mbchar (new, new_offset, 1, MB_FIND_ANY);
ofd = old + old_offset;
nfd = new + new_offset;
}
}
else
#endif
for (ofd = old, nfd = new;
(ofd - old < omax) && *ofd && (*ofd == *nfd);
ofd++, nfd++)
@ -937,6 +1158,33 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
return;
wsatend = 1; /* flag for trailing whitespace */
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY);
nls = new + _rl_find_prev_mbchar (new, ne - new, MB_FIND_ANY);
while ((ols > ofd) && (nls > nfd))
{
memset (&ps_old, 0, sizeof (mbstate_t));
memset (&ps_new, 0, sizeof (mbstate_t));
_rl_adjust_point (old, ols - old, &ps_old);
_rl_adjust_point (new, nls - new, &ps_new);
if (_rl_compare_chars (old, ols - old, &ps_old, new, nls - new, &ps_new) == 0)
break;
if (*ols == ' ')
wsatend = 0;
ols = old + _rl_find_prev_mbchar (old, ols - old, MB_FIND_ANY);
nls = new + _rl_find_prev_mbchar (new, nls - new, MB_FIND_ANY);
}
}
else
{
#endif /* HANDLE_MULTIBYTE */
ols = oe - 1; /* find last same */
nls = ne - 1;
while ((ols > ofd) && (nls > nfd) && (*ols == *nls))
@ -946,18 +1194,38 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
ols--;
nls--;
}
#if defined (HANDLE_MULTIBYTE)
}
#endif
if (wsatend)
{
ols = oe;
nls = ne;
}
#if defined (HANDLE_MULTIBYTE)
/* This may not work for stateful encoding, but who cares? To handle
stateful encoding properly, we have to scan each string from the
beginning and compare. */
else if (_rl_compare_chars (ols, 0, NULL, nls, 0, NULL) == 0)
#else
else if (*ols != *nls)
#endif
{
if (*ols) /* don't step past the NUL */
ols++;
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
ols = old + _rl_find_next_mbchar (old, ols - old, 1, MB_FIND_ANY);
else
ols++;
}
if (*nls)
nls++;
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
nls = new + _rl_find_next_mbchar (new, nls - new, 1, MB_FIND_ANY);
else
nls++;
}
}
/* count of invisible characters in the current invisible line. */
@ -993,24 +1261,50 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
tputs (_rl_term_cr, 1, _rl_output_character_function);
#endif
_rl_output_some_chars (local_prompt, lendiff);
_rl_last_c_pos = lendiff;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_last_c_pos = _rl_col_width (local_prompt, 0, lendiff);
else
_rl_last_c_pos = lendiff;
}
_rl_move_cursor_relative (od, old);
/* if (len (new) > len (old)) */
/* if (len (new) > len (old))
lendiff == difference in buffer
col_lendiff == difference on screen
When not using multibyte characters, these are equal */
lendiff = (nls - nfd) - (ols - ofd);
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
col_lendiff = _rl_col_width (new, nfd - new, nls - new) - _rl_col_width (old, ofd - old, ols - old);
else
col_lendiff = lendiff;
/* If we are changing the number of invisible characters in a line, and
the spot of first difference is before the end of the invisible chars,
lendiff needs to be adjusted. */
if (current_line == 0 && !_rl_horizontal_scroll_mode &&
current_invis_chars != visible_wrap_offset)
lendiff += visible_wrap_offset - current_invis_chars;
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
lendiff += visible_wrap_offset - current_invis_chars;
col_lendiff += visible_wrap_offset - current_invis_chars;
}
else
{
lendiff += visible_wrap_offset - current_invis_chars;
col_lendiff = lendiff;
}
}
/* Insert (diff (len (old), len (new)) ch. */
temp = ne - nfd;
if (lendiff > 0)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
col_temp = _rl_col_width (new, nfd - new, ne - new);
else
col_temp = temp;
if (col_lendiff > 0) /* XXX - was lendiff */
{
/* Non-zero if we're increasing the number of lines. */
int gl = current_line >= _rl_vis_botlin && inv_botlin > _rl_vis_botlin;
@ -1018,7 +1312,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
use the terminal's capabilities. If we're growing the number
of lines, make sure we actually cause the new line to wrap
around on auto-wrapping terminals. */
if (_rl_terminal_can_insert && ((2 * temp) >= lendiff || _rl_term_IC) && (!_rl_term_autowrap || !gl))
if (_rl_terminal_can_insert && ((2 * col_temp) >= col_lendiff || _rl_term_IC) && (!_rl_term_autowrap || !gl))
{
/* If lendiff > prompt_visible_length and _rl_last_c_pos == 0 and
_rl_horizontal_scroll_mode == 1, inserting the characters with
@ -1027,8 +1321,8 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
if (*ols && (!_rl_horizontal_scroll_mode || _rl_last_c_pos > 0 ||
lendiff <= prompt_visible_length || !current_invis_chars))
{
insert_some_chars (nfd, lendiff);
_rl_last_c_pos += lendiff;
insert_some_chars (nfd, lendiff, col_lendiff);
_rl_last_c_pos += col_lendiff;
}
else if (*ols == 0)
{
@ -1037,7 +1331,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
/* However, this screws up the rest of this block, which
assumes you've done the insert because you can. */
_rl_output_some_chars (nfd, lendiff);
_rl_last_c_pos += lendiff;
_rl_last_c_pos += col_lendiff;
}
else
{
@ -1045,7 +1339,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
the end. We have invisible characters in this line. This
is a dumb update. */
_rl_output_some_chars (nfd, temp);
_rl_last_c_pos += temp;
_rl_last_c_pos += col_temp;
return;
}
/* Copy (new) chars to screen from first diff to last match. */
@ -1053,37 +1347,41 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
if ((temp - lendiff) > 0)
{
_rl_output_some_chars (nfd + lendiff, temp - lendiff);
_rl_last_c_pos += temp - lendiff;
#if 0
_rl_last_c_pos += _rl_col_width (nfd+lendiff, 0, temp-lendiff) - col_lendiff;
#else
_rl_last_c_pos += _rl_col_width (nfd+lendiff, 0, temp-col_lendiff);
#endif
}
}
else
{
/* cannot insert chars, write to EOL */
_rl_output_some_chars (nfd, temp);
_rl_last_c_pos += temp;
_rl_last_c_pos += col_temp;
}
}
else /* Delete characters from line. */
{
/* If possible and inexpensive to use terminal deletion, then do so. */
if (_rl_term_dc && (2 * temp) >= -lendiff)
if (_rl_term_dc && (2 * col_temp) >= -col_lendiff)
{
/* If all we're doing is erasing the invisible characters in the
prompt string, don't bother. It screws up the assumptions
about what's on the screen. */
if (_rl_horizontal_scroll_mode && _rl_last_c_pos == 0 &&
-lendiff == visible_wrap_offset)
lendiff = 0;
col_lendiff = 0;
if (lendiff)
delete_chars (-lendiff); /* delete (diff) characters */
if (col_lendiff)
delete_chars (-col_lendiff); /* delete (diff) characters */
/* Copy (new) chars to screen from first diff to last match */
temp = nls - nfd;
if (temp > 0)
{
_rl_output_some_chars (nfd, temp);
_rl_last_c_pos += temp;
_rl_last_c_pos += _rl_col_width (nfd, 0, temp);;
}
}
/* Otherwise, print over the existing material. */
@ -1092,15 +1390,20 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
if (temp > 0)
{
_rl_output_some_chars (nfd, temp);
_rl_last_c_pos += temp;
_rl_last_c_pos += col_temp;
}
lendiff = (oe - old) - (ne - new);
if (lendiff)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
col_lendiff = _rl_col_width (old, 0, oe - old) - _rl_col_width (new, 0, ne - new);
else
col_lendiff = lendiff;
if (col_lendiff)
{
if (_rl_term_autowrap && current_line < inv_botlin)
space_to_eol (lendiff);
space_to_eol (col_lendiff);
else
_rl_clear_to_eol (lendiff);
_rl_clear_to_eol (col_lendiff);
}
}
}
@ -1146,7 +1449,10 @@ rl_on_new_line_with_prompt ()
prompt_last_line = rl_prompt;
l = strlen (prompt_last_line);
_rl_last_c_pos = l;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_last_c_pos = _rl_col_width (prompt_last_line, 0, l);
else
_rl_last_c_pos = l;
/* Dissect prompt_last_line into screen lines. Note that here we have
to use the real screenwidth. Readline's notion of screenwidth might be
@ -1201,7 +1507,14 @@ _rl_move_cursor_relative (new, data)
register int i;
/* If we don't have to do anything, then return. */
#if defined (HANDLE_MULTIBYTE)
/* If we have multibyte characters, NEW is indexed by the buffer point in
a multibyte string, but _rl_last_c_pos is the display position. In
this case, NEW's display position is not obvious. */
if ((MB_CUR_MAX == 1 || rl_byte_oriented ) && _rl_last_c_pos == new) return;
#else
if (_rl_last_c_pos == new) return;
#endif
/* It may be faster to output a CR, and then move forwards instead
of moving backwards. */
@ -1231,19 +1544,69 @@ _rl_move_cursor_relative (new, data)
data is underneath the cursor. */
#if defined (HACK_TERMCAP_MOTION)
if (_rl_term_forward_char)
for (i = _rl_last_c_pos; i < new; i++)
tputs (_rl_term_forward_char, 1, _rl_output_character_function);
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int width;
width = _rl_col_width (data, _rl_last_c_pos, new);
for (i = 0; i < width; i++)
tputs (_rl_term_forward_char, 1, _rl_output_character_function);
}
else
{
for (i = _rl_last_c_pos; i < new; i++)
tputs (_rl_term_forward_char, 1, _rl_output_character_function);
}
}
else if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
tputs (_rl_term_cr, 1, _rl_output_character_function);
for (i = 0; i < new; i++)
putc (data[i], rl_outstream);
}
else
for (i = _rl_last_c_pos; i < new; i++)
putc (data[i], rl_outstream);
#else
for (i = _rl_last_c_pos; i < new; i++)
putc (data[i], rl_outstream);
#endif /* HACK_TERMCAP_MOTION */
#else /* !HACK_TERMCAP_MOTION */
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
tputs (_rl_term_cr, 1, _rl_output_character_function);
for (i = 0; i < new; i++)
putc (data[i], rl_outstream);
}
else
for (i = _rl_last_c_pos; i < new; i++)
putc (data[i], rl_outstream);
#endif /* !HACK_TERMCAP_MOTION */
}
#if defined (HANDLE_MULTIBYTE)
/* NEW points to the buffer point, but _rl_last_c_pos is the display point.
The byte length of the string is probably bigger than the column width
of the string, which means that if NEW == _rl_last_c_pos, then NEW's
display point is less than _rl_last_c_pos. */
else if (_rl_last_c_pos >= new)
#else
else if (_rl_last_c_pos > new)
_rl_backspace (_rl_last_c_pos - new);
_rl_last_c_pos = new;
#endif
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
tputs (_rl_term_cr, 1, _rl_output_character_function);
for (i = 0; i < new; i++)
putc (data[i], rl_outstream);
}
else
_rl_backspace (_rl_last_c_pos - new);
}
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
_rl_last_c_pos = _rl_col_width (data, 0, new);
else
_rl_last_c_pos = new;
}
/* PWP: move the cursor up or down. */
@ -1514,17 +1877,23 @@ _rl_clear_screen ()
rl_crlf ();
}
/* Insert COUNT characters from STRING to the output stream. */
/* Insert COUNT characters from STRING to the output stream at column COL. */
static void
insert_some_chars (string, count)
insert_some_chars (string, count, col)
char *string;
int count;
int count, col;
{
/* DEBUGGING */
if (MB_CUR_MAX == 1 || rl_byte_oriented)
if (count != col)
fprintf(stderr, "readline: debug: insert_some_chars: count (%d) != col (%d)\n", count, col);
/* If IC is defined, then we do not have to "enter" insert mode. */
if (_rl_term_IC)
{
char *buffer;
buffer = tgoto (_rl_term_IC, 0, count);
buffer = tgoto (_rl_term_IC, 0, col);
tputs (buffer, 1, _rl_output_character_function);
_rl_output_some_chars (string, count);
}
@ -1540,7 +1909,7 @@ insert_some_chars (string, count)
use that first to open up the space. */
if (_rl_term_ic && *_rl_term_ic)
{
for (i = count; i--; )
for (i = col; i--; )
tputs (_rl_term_ic, 1, _rl_output_character_function);
}
@ -1595,11 +1964,8 @@ _rl_update_final ()
if (full_lines && _rl_term_autowrap && (VIS_LLEN(_rl_vis_botlin) == _rl_screenwidth))
{
char *last_line;
#if 0
last_line = &visible_line[inv_lbreaks[_rl_vis_botlin]];
#else
last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]];
#endif
_rl_move_cursor_relative (_rl_screenwidth - 1, last_line);
_rl_clear_to_eol (0);
putc (last_line[_rl_screenwidth - 1], rl_outstream);
@ -1744,3 +2110,87 @@ _rl_current_display_line ()
return ret;
}
#if defined (HANDLE_MULTIBYTE)
/* Calculate the number of screen columns occupied by STR from START to END.
In the case of multibyte characters with stateful encoding, we have to
scan from the beginning of the string to take the state into account. */
static int
_rl_col_width (str, start, end)
char *str;
int start, end;
{
wchar_t wc;
mbstate_t ps = {0};
int tmp, point, width, max;
if (end <= start)
return 0;
point = 0;
max = end;
while (point < start)
{
tmp = mbrlen (str + point, max, &ps);
if ((size_t)tmp == (size_t)-1 || (size_t)tmp == (size_t)-2)
{
/* In this case, the bytes are invalid or too short to compose a
multibyte character, so we assume that the first byte represents
a single character. */
point++;
max--;
/* Clear the state of the byte sequence, because in this case the
effect of mbstate is undefined. */
memset (&ps, 0, sizeof (mbstate_t));
}
else if (tmp == 0)
break; /* Found '\0' */
else
{
point += tmp;
max -= tmp;
}
}
/* If START is not a byte that starts a character, then POINT will be
greater than START. In this case, assume that (POINT - START) gives
a byte count that is the number of columns of difference. */
width = point - start;
while (point < end)
{
tmp = mbrtowc (&wc, str + point, max, &ps);
if ((size_t)tmp == (size_t)-1 || (size_t)tmp == (size_t)-2)
{
/* In this case, the bytes are invalid or too short to compose a
multibyte character, so we assume that the first byte represents
a single character. */
point++;
max--;
/* and assume that the byte occupies a single column. */
width++;
/* Clear the state of the byte sequence, because in this case the
effect of mbstate is undefined. */
memset (&ps, 0, sizeof (mbstate_t));
}
else if (tmp == 0)
break; /* Found '\0' */
else
{
point += tmp;
max -= tmp;
tmp = wcwidth(wc);
width += (tmp >= 0) ? tmp : 1;
}
}
width += point - end;
return width;
}
#endif /* HANDLE_MULTIBYTE */

View file

@ -1,6 +1,23 @@
# Derived by hand from the generated readline-src/doc/Makefile
# This makefile for Readline library documentation is in -*- text -*- mode.
# Emacs likes it that way.
# Copyright (C) 1996-2002 Free Software Foundation, Inc.
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
topdir = .
srcdir = .
VPATH = .

View file

@ -18,7 +18,7 @@ This document describes the GNU History library, a programming tool that
provides a consistent user interface for recalling lines of previously
typed input.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
@ -73,7 +73,7 @@ except that this permission notice may be stated in a translation approved
by the Free Software Foundation.
@vskip 0pt plus 1filll
Copyright @copyright{} 1988-2001 Free Software Foundation, Inc.
Copyright @copyright{} 1988-2002 Free Software Foundation, Inc.
@end titlepage
@ifinfo

View file

@ -1,7 +1,7 @@
@ignore
This file documents the user interface to the GNU History library.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Authored by Brian Fox and Chet Ramey.
Permission is granted to make and distribute verbatim copies of this manual
@ -188,8 +188,9 @@ Stifle the history list, remembering only the last @var{max} entries.
@end deftypefun
@deftypefun int unstifle_history (void)
Stop stifling the history. This returns the previous amount the
history was stifled. The value is positive if the history was
Stop stifling the history. This returns the previously-set
maximum number of history entries (as set by @code{stifle_history()}).
The value is positive if the history was
stifled, negative if it wasn't.
@end deftypefun

View file

@ -1,7 +1,7 @@
@ignore
This file documents the user interface to the GNU History library.
Copyright (C) 1988-1999 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Authored by Brian Fox and Chet Ramey.
Permission is granted to make and distribute verbatim copies of this manual

View file

@ -1,6 +1,10 @@
@set EDITION 4.2a
@set VERSION 4.2a
@set UPDATED 2001 October 9
@set UPDATE-MONTH October 2001
@ignore
Copyright (C) 1988-2002 Free Software Foundation, Inc.
@end ignore
@set LASTCHANGE Tue Oct 9 15:03:34 EDT 2001
@set EDITION 4.3
@set VERSION 4.3
@set UPDATED 2002 March 4
@set UPDATE-MONTH March 2002
@set LASTCHANGE Mon Mar 4 12:00:16 EST 2002

View file

@ -18,7 +18,7 @@ This document describes the GNU Readline Library, a utility which aids
in the consistency of user interface across discrete programs that need
to provide a command line interface.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
@ -73,7 +73,7 @@ except that this permission notice may be stated in a translation approved
by the Free Software Foundation.
@vskip 0pt plus 1filll
Copyright @copyright{} 1988-2001 Free Software Foundation, Inc.
Copyright @copyright{} 1988-2002 Free Software Foundation, Inc.
@end titlepage
@ifinfo

View file

@ -8,7 +8,7 @@ This document describes the GNU Readline Library, a utility for aiding
in the consitency of user interface across discrete programs that need
to provide a command line interface.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
@ -111,12 +111,13 @@ function, and has the advantage of no static buffer to overflow:
/* A static variable for holding the line. */
static char *line_read = (char *)NULL;
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
/* Read a string, and return a pointer to it.
Returns NULL on EOF. */
char *
rl_gets ()
@{
/* If the buffer has already been allocated, return the memory
to the free pool. */
/* If the buffer has already been allocated,
return the memory to the free pool. */
if (line_read)
@{
free (line_read);
@ -126,7 +127,8 @@ rl_gets ()
/* Get a line from the user. */
line_read = readline ("");
/* If the line has any text in it, save it on the history. */
/* If the line has any text in it,
save it on the history. */
if (line_read && *line_read)
add_history (line_read);
@ -263,7 +265,7 @@ variables that describe the current state of the line read so far.
The calling sequence for a command @code{foo} looks like
@example
@code{foo (int count, int key)}
@code{int foo (int count, int key)}
@end example
@noindent
@ -280,6 +282,9 @@ to do something useful with both negative and positive arguments.
At the very least, it should be aware that it can be passed a
negative argument.
A command function should return 0 if its action completes successfully,
and a non-zero value if some error occurs.
@node Readline Variables
@section Readline Variables
@ -385,10 +390,12 @@ The value allows conditional parsing of the inputrc file
@deftypevar {FILE *} rl_instream
The stdio stream from which Readline reads input.
If @code{NULL}, Readline defaults to @var{stdin}.
@end deftypevar
@deftypevar {FILE *} rl_outstream
The stdio stream to which Readline performs output.
If @code{NULL}, Readline defaults to @var{stdout}.
@end deftypevar
@deftypevar {rl_command_func_t *} rl_last_func
@ -766,9 +773,9 @@ This is done with @code{rl_begin_undo_group()} and
The types of events that can be undone are:
@example
@smallexample
enum undo_code @{ UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END @};
@end example
@end smallexample
Notice that @code{UNDO_DELETE} means to insert some text, and
@code{UNDO_INSERT} means to delete some text. That is, the undo code
@ -901,10 +908,12 @@ to the result.
@deftypefun int rl_insert_text (const char *text)
Insert @var{text} into the line at the current cursor position.
Returns the number of characters inserted.
@end deftypefun
@deftypefun int rl_delete_text (int start, int end)
Delete the text between @var{start} and @var{end} in the current line.
Returns the number of characters deleted.
@end deftypefun
@deftypefun {char *} rl_copy_text (int start, int end)
@ -947,7 +956,9 @@ be the keyboard.
@deftypefun int rl_stuff_char (int c)
Insert @var{c} into the Readline input stream. It will be "read"
before Readline attempts to read characters from the terminal with
@code{rl_read_key()}.
@code{rl_read_key()}. Up to 512 characters may be pushed back.
@code{rl_stuff_char} returns 1 if the character was successfully inserted;
0 otherwise.
@end deftypefun
@deftypefun int rl_execute_next (int c)
@ -1000,6 +1011,13 @@ environment variable is used.
@node Utility Functions
@subsection Utility Functions
@deftypefun void rl_replace_line (const char *text, int clear_undo)
Replace the contents of @code{rl_line_buffer} with @var{text}.
The point and mark are preserved, if possible.
If @var{clear_undo} is non-zero, the undo list associated with the
current line is cleared.
@end deftypefun
@deftypefun int rl_extend_line_buffer (int len)
Ensure that @code{rl_line_buffer} has enough space to hold @var{len}
characters, possibly reallocating it if necessary.
@ -1123,16 +1141,26 @@ The function takes the text of the line as an argument.
@deftypefun void rl_callback_read_char (void)
Whenever an application determines that keyboard input is available, it
should call @code{rl_callback_read_char()}, which will read the next
character from the current input source. If that character completes the
line, @code{rl_callback_read_char} will invoke the @var{lhandler}
function saved by @code{rl_callback_handler_install} to process the
line. @code{EOF} is indicated by calling @var{lhandler} with a
character from the current input source.
If that character completes the line, @code{rl_callback_read_char} will
invoke the @var{lhandler} function saved by @code{rl_callback_handler_install}
to process the line.
Before calling the @var{lhandler} function, the terminal settings are
reset to the values they had before calling
@code{rl_callback_handler_install}.
If the @var{lhandler} function returns,
the terminal settings are modified for Readline's use again.
@code{EOF} is indicated by calling @var{lhandler} with a
@code{NULL} line.
@end deftypefun
@deftypefun void rl_callback_handler_remove (void)
Restore the terminal to its initial state and remove the line handler.
This may be called from within a callback as well as independently.
If the @var{lhandler} installed by @code{rl_callback_handler_install}
does not exit the program, either this function or the function referred
to by the value of @code{rl_deprep_term_function} should be called before
the program exits to reset the terminal settings.
@end deftypefun
@node A Readline Example
@ -1185,8 +1213,8 @@ invert_case_line (count, key)
end = temp;
@}
/* Tell readline that we are modifying the line, so it will save
the undo information. */
/* Tell readline that we are modifying the line,
so it will save the undo information. */
rl_modifying (start, end);
for (i = start; i != end; i++)
@ -1442,6 +1470,14 @@ partially-completed word. See description of @code{rl_complete()}.
This calls @code{rl_complete_internal()} with an argument of @samp{*}.
@end deftypefun
@deftypefun int rl_completion_mode (rl_command_func_t *cfunc)
Returns the apppriate value to pass to @code{rl_complete_internal()}
depending on whether @var{cfunc} was called twice in succession and
the value of the @code{show-all-if-ambiguous} variable.
Application-specific completion functions may use this function to present
the same interface as @code{rl_complete()}.
@end deftypefun
@deftypefun {char **} rl_completion_matches (const char *text, rl_compentry_func_t *entry_func)
Returns an array of strings which is a list of completions for
@var{text}. If there are no completions, returns @code{NULL}.
@ -1528,10 +1564,41 @@ character found in @code{rl_completer_word_break_characters} should be
used to break words for the completer.
@end deftypevar
@deftypevar int rl_completion_query_items
Up to this many items will be displayed in response to a
possible-completions call. After that, we ask the user if she is sure
she wants to see them all. The default value is 100.
@deftypevar {rl_compignore_func_t *} rl_ignore_some_completions_function
This function, if defined, is called by the completer when real filename
completion is done, after all the matching names have been generated.
It is passed a @code{NULL} terminated array of matches.
The first element (@code{matches[0]}) is the
maximal substring common to all matches. This function can
re-arrange the list of matches as required, but each element deleted
from the array must be freed.
@end deftypevar
@deftypevar {rl_icppfunc_t *} rl_directory_completion_hook
This function, if defined, is allowed to modify the directory portion
of filenames Readline completes. It is called with the address of a
string (the current directory name) as an argument, and may modify that string.
If the string is replaced with a new string, the old value should be freed.
Any modified directory name should have a trailing slash.
The modified value will be displayed as part of the completion, replacing
the directory portion of the pathname the user typed.
It returns an integer that should be non-zero if the function modifies
its directory argument.
It could be used to expand symbolic links or shell variables in pathnames.
@end deftypevar
@deftypevar {rl_compdisp_func_t *} rl_completion_display_matches_hook
If non-zero, then this is the address of a function to call when
completing a word would normally display the list of possible matches.
This function is called in lieu of Readline displaying the list.
It takes three arguments:
(@code{char **}@var{matches}, @code{int} @var{num_matches}, @code{int} @var{max_length})
where @var{matches} is the array of matching strings,
@var{num_matches} is the number of strings in that array, and
@var{max_length} is the length of the longest string in that array.
Readline provides a convenience function, @code{rl_display_match_list},
that takes care of doing the display to Readline's output stream. That
function may be called from this hook.
@end deftypevar
@deftypevar {const char *} rl_basic_word_break_characters
@ -1571,6 +1638,12 @@ For instance, Bash sets this variable to "$@@" so that it can complete
shell variables and hostnames.
@end deftypevar
@deftypevar int rl_completion_query_items
Up to this many items will be displayed in response to a
possible-completions call. After that, we ask the user if she is sure
she wants to see them all. The default value is 100.
@end deftypevar
@deftypevar {int} rl_completion_append_character
When a single completion alternative matches at the end of the command
line, this character is appended to the inserted completion text. The
@ -1581,6 +1654,24 @@ provide the ``most sensible word separator character'' according to
an application-specific command line syntax specification.
@end deftypevar
@deftypevar int rl_completion_suppress_append
If non-zero, @var{rl_completion_append_character} is not appended to
matches at the end of the command line, as described above. It is
set to 0 before any application-specific completion function is called.
@end deftypevar
@deftypevar int rl_completion_mark_symlink_dirs
If non-zero, a slash will be appended to completed filenames that are
symbolic links to directory names, subject to the value of the
user-settable @var{mark-directories} variable.
This variable exists so that application completion functions can
override the user's global preference (set via the
@var{mark-symlinked-directories} Readline variable) if appropriate.
This variable is set to the user's preference before any
application completion function is called, so unless that function
modifies the value, the user's preferences are honored.
@end deftypevar
@deftypevar int rl_ignore_completion_duplicates
If non-zero, then duplicates in the matches are removed.
The default is 1.
@ -1625,43 +1716,6 @@ If this variable is non-zero, completion is inhibited. The completion
character will be inserted as any other bound to @code{self-insert}.
@end deftypevar
@deftypevar {rl_compignore_func_t *} rl_ignore_some_completions_function
This function, if defined, is called by the completer when real filename
completion is done, after all the matching names have been generated.
It is passed a @code{NULL} terminated array of matches.
The first element (@code{matches[0]}) is the
maximal substring common to all matches. This function can
re-arrange the list of matches as required, but each element deleted
from the array must be freed.
@end deftypevar
@deftypevar {rl_icppfunc_t *} rl_directory_completion_hook
This function, if defined, is allowed to modify the directory portion
of filenames Readline completes. It is called with the address of a
string (the current directory name) as an argument, and may modify that string.
If the string is replaced with a new string, the old value should be freed.
Any modified directory name should have a trailing slash.
The modified value will be displayed as part of the completion, replacing
the directory portion of the pathname the user typed.
It returns an integer that should be non-zero if the function modifies
its directory argument.
It could be used to expand symbolic links or shell variables in pathnames.
@end deftypevar
@deftypevar {rl_compdisp_func_t *} rl_completion_display_matches_hook
If non-zero, then this is the address of a function to call when
completing a word would normally display the list of possible matches.
This function is called in lieu of Readline displaying the list.
It takes three arguments:
(@code{char **}@var{matches}, @code{int} @var{num_matches}, @code{int} @var{max_length})
where @var{matches} is the array of matching strings,
@var{num_matches} is the number of strings in that array, and
@var{max_length} is the length of the longest string in that array.
Readline provides a convenience function, @code{rl_display_match_list},
that takes care of doing the display to Readline's output stream. That
function may be called from this hook.
@end deftypevar
@node A Short Completion Example
@subsection A Short Completion Example
@ -2089,12 +2143,13 @@ too_dangerous (caller)
char *caller;
@{
fprintf (stderr,
"%s: Too dangerous for me to distribute. Write it yourself.\n",
"%s: Too dangerous for me to distribute.\n"
caller);
fprintf (stderr, "Write it yourself.\n");
@}
/* Return non-zero if ARG is a valid argument for CALLER, else print
an error message and return zero. */
/* Return non-zero if ARG is a valid argument for CALLER,
else print an error message and return zero. */
int
valid_argument (caller, arg)
char *caller, *arg;

View file

@ -10,7 +10,7 @@ use these features. There is a document entitled "readline.texinfo"
which contains both end-user and programmer documentation for the
GNU Readline Library.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Authored by Brian Fox and Chet Ramey.
@ -512,6 +512,13 @@ This variable, when set to @samp{on}, causes Readline to display an
asterisk (@samp{*}) at the start of history lines which have been modified.
This variable is @samp{off} by default.
@item mark-symlinked-directories
@vindex mark-symlinked-directories
If set to @samp{on}, completed names which are symbolic links
to directories have a slash appended (subject to the value of
@code{mark-directories}).
The default is @samp{off}.
@item match-hidden-files
@vindex match-hidden-files
This variable, when set to @samp{on}, causes Readline to match files whose
@ -526,6 +533,12 @@ If set to @samp{on}, Readline will display characters with the
eighth bit set directly rather than as a meta-prefixed escape
sequence. The default is @samp{off}.
@item page-completions
@vindex page-completions
If set to @samp{on}, Readline uses an internal @code{more}-like pager
to display a screenful of possible completions at a time.
This variable is @samp{on} by default.
@item print-completions-horizontally
If set to @samp{on}, Readline will display completions with matches
sorted horizontally in alphabetical order, rather than down the screen.
@ -757,14 +770,14 @@ binding, variable assignment, and conditional syntax.
@example
@page
# This file controls the behaviour of line input editing for
# programs that use the Gnu Readline library. Existing programs
# include FTP, Bash, and Gdb.
# programs that use the GNU Readline library. Existing
# programs include FTP, Bash, and GDB.
#
# You can re-read the inputrc file with C-x C-r.
# Lines beginning with '#' are comments.
#
# First, include any systemwide bindings and variable assignments from
# /etc/Inputrc
# First, include any systemwide bindings and variable
# assignments from /etc/Inputrc
$include /etc/Inputrc
#
@ -816,10 +829,12 @@ TAB: complete
$if Bash
# edit the path
"\C-xp": "PATH=$@{PATH@}\e\C-e\C-a\ef\C-f"
# prepare to type a quoted word -- insert open and close double quotes
# prepare to type a quoted word --
# insert open and close double quotes
# and move to just after the open quote
"\C-x\"": "\"\"\C-b"
# insert a backslash (testing backslash escapes in sequences and macros)
# insert a backslash (testing backslash escapes
# in sequences and macros)
"\C-x\\": "\\"
# Quote the current or previous word
"\C-xq": "\eb\"\ef\""
@ -835,16 +850,16 @@ set bell-style visible
# don't strip characters to 7 bits when reading
set input-meta on
# allow iso-latin1 characters to be inserted rather than converted to
# prefix-meta sequences
# allow iso-latin1 characters to be inserted rather
# than converted to prefix-meta sequences
set convert-meta off
# display characters with the eighth bit set directly rather than
# as meta-prefixed characters
# display characters with the eighth bit set directly
# rather than as meta-prefixed characters
set output-meta on
# if there are more than 150 possible completions for a word, ask the
# user if he wants to see all of them
# if there are more than 150 possible completions for
# a word, ask the user if he wants to see all of them
set completion-query-items 150
# For FTP
@ -1053,6 +1068,20 @@ lowercase the previous word, but do not move the cursor.
Capitalize the current (or following) word. With a negative argument,
capitalize the previous word, but do not move the cursor.
@item overwrite-mode ()
Toggle overwrite mode. With an explicit positive numeric argument,
switches to overwrite mode. With an explicit non-positive numeric
argument, switches to insert mode. This command affects only
@code{emacs} mode; @code{vi} mode does overwrite differently.
Each call to @code{readline()} starts in insert mode.
In overwrite mode, characters bound to @code{self-insert} replace
the text at point rather than pushing the text to the right.
Characters bound to @code{backward-delete-char} replace the character
before point with a space.
By default, this command is unbound.
@end ftable
@node Commands For Killing
@ -1312,12 +1341,19 @@ of that character. A negative count searches for subsequent
occurrences.
@item insert-comment (M-#)
The value of the @code{comment-begin}
variable is inserted at the beginning of the current line,
and the line is accepted as if a newline had been typed.
Without a numeric argument, the value of the @code{comment-begin}
variable is inserted at the beginning of the current line.
If a numeric argument is supplied, this command acts as a toggle: if
the characters at the beginning of the line do not match the value
of @code{comment-begin}, the value is inserted, otherwise
the characters in @code{comment-begin} are deleted from the beginning of
the line.
In either case, the line is accepted as if a newline had been typed.
@ifset BashFeatures
The default value of @code{comment-begin} causes this command
to make the current line a shell comment.
If a numeric argument causes the comment character to be removed, the line
will be executed by the shell.
@end ifset
@item dump-functions ()
@ -1339,13 +1375,22 @@ the output is formatted in such a way that it can be made part
of an @var{inputrc} file. This command is unbound by default.
@ifset BashFeatures
@item glob-complete-word (M-g)
The word before point is treated as a pattern for pathname expansion,
with an asterisk implicitly appended. This pattern is used to
generate a list of matching file names for possible completions.
@item glob-expand-word (C-x *)
The word before point is treated as a pattern for pathname expansion,
and the list of matching file names is inserted, replacing the word.
If a numeric argument is supplied, a @samp{*} is appended before
pathname expansion.
@item glob-list-expansions (C-x g)
The list of expansions that would have been generated by
@code{glob-expand-word} is displayed, and the line is redrawn.
If a numeric argument is supplied, a @samp{*} is appended before
pathname expansion.
@item display-shell-version (C-x C-v)
Display version information about the current instance of Bash.
@ -1376,13 +1421,26 @@ Accept the current line for execution and fetch the next line
relative to the current line from the history for editing. Any
argument is ignored.
@item emacs-editing-mode (C-e)
When in @code{vi} editing mode, this causes a switch back to
@code{emacs} editing mode, as if the command @samp{set -o emacs} had
been executed.
@item edit-and-execute-command (C-xC-e)
Invoke an editor on the current command line, and execute the result as shell
commands.
Bash attempts to invoke
@code{$FCEDIT}, @code{$EDITOR}, and @code{emacs}
as the editor, in that order.
@end ifset
@ifclear BashFeatures
@item emacs-editing-mode (C-e)
When in @code{vi} command mode, this causes a switch to @code{emacs}
editing mode.
@item vi-editing-mode (M-C-j)
When in @code{emacs} editing mode, this causes a switch to @code{vi}
editing mode.
@end ifclear
@end ftable
@node Readline vi Mode
@ -1518,6 +1576,12 @@ If the @option{-o default} option was supplied to @code{complete} when the
compspec was defined, Readline's default completion will be performed
if the compspec generates no matches.
When a compspec indicates that directory name completion is desired,
the programmable completion functions force Readline to append a slash
to completed names which are symbolic links to directories, subject to
the value of the @var{mark-directories} Readline variable, regardless
of the setting of the @var{mark-symlinked-directories} Readline variable.
@node Programmable Completion Builtins
@section Programmable Completion Builtins
@cindex completion builtins
@ -1553,7 +1617,7 @@ matches were generated.
@item complete
@btindex complete
@example
@code{complete [-abcdefgjkvu] [-o @var{comp-option}] [-A @var{action}] [-G @var{globpat}] [-W @var{wordlist}]
@code{complete [-abcdefgjksuv] [-o @var{comp-option}] [-A @var{action}] [-G @var{globpat}] [-W @var{wordlist}]
[-P @var{prefix}] [-S @var{suffix}] [-X @var{filterpat}] [-F @var{function}]
[-C @var{command}] @var{name} [@var{name} @dots{}]}
@code{complete -pr [@var{name} @dots{}]}
@ -1586,7 +1650,8 @@ beyond the simple generation of completions.
@table @code
@item default
Use readline's default completion if the compspec generates no matches.
Use Readline's default filename completion if the compspec generates
no matches.
@item dirnames
Perform directory name completion if the compspec generates no matches.
@ -1596,6 +1661,10 @@ Tell Readline that the compspec generates filenames, so it can perform any
filename\-specific processing (like adding a slash to directory names or
suppressing trailing spaces). This option is intended to be used with
shell functions specified with @option{-F}.
@item nospace
Tell Readline not to append a space (the default) to words completed at
the end of the line.
@end table
@item -A @var{action}
@ -1655,6 +1724,9 @@ Shell reserved words. May also be specified as @option{-k}.
@item running
Names of running jobs, if job control is active.
@item service
Service names. May also be specified as @option{-s}.
@item setopt
Valid arguments for the @option{-o} option to the @code{set} builtin
(@pxref{The Set Builtin}).

View file

@ -17,7 +17,7 @@ This document describes the end user interface of the GNU Readline Library,
a utility which aids in the consistency of user interface across discrete
programs that need to provide a command line interface.
Copyright (C) 1988-2001 Free Software Foundation, Inc.
Copyright (C) 1988-2002 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
@ -72,7 +72,7 @@ except that this permission notice may be stated in a translation approved
by the Free Software Foundation.
@vskip 0pt plus 1filll
Copyright @copyright{} 1988-2001 Free Software Foundation, Inc.
Copyright @copyright{} 1988-2002 Free Software Foundation, Inc.
@end titlepage
@ifinfo

View file

@ -35,11 +35,11 @@ KEYMAP_ENTRY_ARRAY emacs_standard_keymap = {
/* Control keys. */
{ ISFUNC, rl_set_mark }, /* Control-@ */
{ ISFUNC, rl_beg_of_line }, /* Control-a */
{ ISFUNC, rl_backward }, /* Control-b */
{ ISFUNC, rl_backward_char }, /* Control-b */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */
{ ISFUNC, rl_delete }, /* Control-d */
{ ISFUNC, rl_end_of_line }, /* Control-e */
{ ISFUNC, rl_forward }, /* Control-f */
{ ISFUNC, rl_forward_char }, /* Control-f */
{ ISFUNC, rl_abort }, /* Control-g */
{ ISFUNC, rl_rubout }, /* Control-h */
{ ISFUNC, rl_complete }, /* Control-i */

View file

@ -4,6 +4,22 @@
# on which program is running, or what terminal is active.
#
# Copyright (C) 1989-2002 Free Software Foundation, Inc.
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
# In all programs, all terminals, make sure this is bound.
"\C-x\C-r": re-read-init-file

View file

@ -35,6 +35,10 @@ fileman: fileman.o
rltest: rltest.o
$(CC) $(LDFLAGS) -o $@ rltest.o -lreadline -ltermcap
rlcat: rlcat.o
$(CC) $(LDFLAGS) -o $@ rlcat.o -lreadline -ltermcap
fileman.o: fileman.c
rltest.o: rltest.c
rl.o: rl.c
rlcat.o: rlcat.c

View file

@ -1,3 +1,23 @@
/* Copyright (C) 1987-2002 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. */
/* fileman.c -- A tiny application which demonstrates how to use the
GNU Readline library. This application interactively allows users
to manipulate files and their modes. */

View file

@ -1,3 +1,23 @@
/* Copyright (C) 1987-2002 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. */
#include <stdio.h>
#ifdef READLINE_LIBRARY

View file

@ -1,5 +1,25 @@
/* manexamp.c -- The examples which appear in the documentation are here. */
/* Copyright (C) 1987-2002 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. */
#include <stdio.h>
#include <readline/readline.h>

View file

@ -5,6 +5,26 @@
* usage: rl [-p prompt] [-u unit] [-d default] [-n nchars]
*/
/* Copyright (C) 1987-2002 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. */
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif

View file

@ -0,0 +1,174 @@
/*
* rlcat - cat(1) using readline
*
* usage: rlcat
*/
/* Copyright (C) 1987-2002 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. */
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/types.h>
#include "posixstat.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif
#if defined (READLINE_LIBRARY)
# include "readline.h"
# include "history.h"
#else
# include <readline/readline.h>
# include <readline/history.h>
#endif
extern int optind;
extern char *optarg;
static int stdcat();
static char *progname;
static int vflag;
static void
usage()
{
fprintf (stderr, "%s: usage: %s [-vEVN] [filename]\n", progname, progname);
}
int
main (argc, argv)
int argc;
char **argv;
{
char *temp;
int opt, Vflag, Nflag;
progname = strrchr(argv[0], '/');
if (progname == 0)
progname = argv[0];
else
progname++;
vflag = Vflag = Nflag = 0;
while ((opt = getopt(argc, argv, "vEVN")) != EOF)
{
switch (opt)
{
case 'v':
vflag = 1;
break;
case 'V':
Vflag = 1;
break;
case 'E':
Vflag = 0;
break;
case 'N':
Nflag = 1;
break;
default:
usage ();
exit (2);
}
}
argc -= optind;
argv += optind;
if (isatty(0) == 0 || argc || Nflag)
return stdcat(argc, argv);
rl_variable_bind ("editing-mode", Vflag ? "vi" : "emacs");
while (temp = readline (""))
{
if (*temp)
add_history (temp);
printf ("%s\n", temp);
}
return (ferror (stdout));
}
static int
fcopy(fp)
FILE *fp;
{
int c;
char *x;
while ((c = getc(fp)) != EOF)
{
if (vflag && isascii ((unsigned char)c) && isprint((unsigned char)c) == 0)
{
x = rl_untranslate_keyseq (c);
if (fputs (x, stdout) != 0)
return 1;
}
else if (putchar (c) == EOF)
return 1;
}
return (ferror (stdout));
}
int
stdcat (argc, argv)
int argc;
char **argv;
{
int i, fd, r;
char *s;
FILE *fp;
if (argc == 0)
return (fcopy(stdin));
for (i = 0, r = 1; i < argc; i++)
{
if (*argv[i] == '-' && argv[i][1] == 0)
fp = stdin;
else
{
fp = fopen (argv[i], "r");
if (fp == 0)
{
fprintf (stderr, "%s: %s: cannot open: %s\n", progname, argv[i], strerror(errno));
continue;
}
}
r = fcopy (fp);
if (fp != stdin)
fclose(fp);
}
return r;
}

View file

@ -4,6 +4,26 @@
/* */
/* **************************************************************** */
/* Copyright (C) 1987-2002 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. */
#if defined (HAVE_CONFIG_H)
#include <config.h>
#endif

View file

@ -60,7 +60,8 @@ static FUNMAP default_funmap[] = {
{ "abort", rl_abort },
{ "accept-line", rl_newline },
{ "arrow-key-prefix", rl_arrow_keys },
{ "backward-char", rl_backward },
{ "backward-byte", rl_backward_byte },
{ "backward-char", rl_backward_char },
{ "backward-delete-char", rl_rubout },
{ "backward-kill-line", rl_backward_kill_line },
{ "backward-kill-word", rl_backward_kill_word },
@ -91,7 +92,8 @@ static FUNMAP default_funmap[] = {
{ "end-of-line", rl_end_of_line },
{ "exchange-point-and-mark", rl_exchange_point_and_mark },
{ "forward-backward-delete-char", rl_rubout_or_delete },
{ "forward-char", rl_forward },
{ "forward-byte", rl_forward_byte },
{ "forward-char", rl_forward_char },
{ "forward-search-history", rl_forward_search_history },
{ "forward-word", rl_forward_word },
{ "history-search-backward", rl_history_search_backward },
@ -108,6 +110,7 @@ static FUNMAP default_funmap[] = {
{ "non-incremental-reverse-search-history", rl_noninc_reverse_search },
{ "non-incremental-forward-search-history-again", rl_noninc_forward_search_again },
{ "non-incremental-reverse-search-history-again", rl_noninc_reverse_search_again },
{ "overwrite-mode", rl_overwrite_mode },
#ifdef __CYGWIN__
{ "paste-from-clipboard", rl_paste_from_clipboard },
#endif

View file

@ -41,11 +41,7 @@
# include <unistd.h>
#endif
#if defined (HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif /* !HAVE_STRING_H */
#include "rlmbutil.h"
#include "history.h"
#include "histlib.h"
@ -58,6 +54,8 @@
typedef int _hist_search_func_t PARAMS((const char *, int));
extern int rl_byte_oriented; /* declared in mbutil.c */
static char error_pointer;
static char *subst_lhs;
@ -204,12 +202,30 @@ get_history_event (string, caller_index, delimiting_quote)
/* Only a closing `?' or a newline delimit a substring search string. */
for (local_index = i; c = string[i]; i++)
if ((!substring_okay && (whitespace (c) || c == ':' ||
(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
string[i] == delimiting_quote)) ||
string[i] == '\n' ||
(substring_okay && string[i] == '?'))
break;
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int v;
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t));
/* These produce warnings because we're passing a const string to a
function that takes a non-const string. */
_rl_adjust_point (string, i, &ps);
if ((v = _rl_get_char_len (string + i, &ps)) > 1)
{
i += v - 1;
continue;
}
}
else
#endif /* HANDLE_MULTIBYTE */
if ((!substring_okay && (whitespace (c) || c == ':' ||
(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
string[i] == delimiting_quote)) ||
string[i] == '\n' ||
(substring_okay && string[i] == '?'))
break;
which = i - local_index;
temp = (char *)xmalloc (1 + which);
@ -405,13 +421,33 @@ get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
int *iptr, delimiter, is_rhs, *lenptr;
{
register int si, i, j, k;
char *s = (char *) NULL;
char *s;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps;
#endif
s = (char *)NULL;
i = *iptr;
#if defined (HANDLE_MULTIBYTE)
memset (&ps, 0, sizeof (mbstate_t));
_rl_adjust_point (str, i, &ps);
#endif
for (si = i; str[si] && str[si] != delimiter; si++)
if (str[si] == '\\' && str[si + 1] == delimiter)
si++;
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int v;
if ((v = _rl_get_char_len (str + si, &ps)) > 1)
si += v - 1;
else if (str[si] == '\\' && str[si + 1] == delimiter)
si++;
}
else
#endif /* HANDLE_MULTIBYTE */
if (str[si] == '\\' && str[si + 1] == delimiter)
si++;
if (si > i || is_rhs)
{
@ -484,6 +520,11 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
int substitute_globally, want_quotes, print_only;
char *event, *temp, *result, *tstr, *t, c, *word_spec;
int result_len;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t));
#endif
result = (char *)xmalloc (result_len = 128);
@ -514,8 +555,21 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
quote, then this expansion takes place inside of the
quoted string. If we have to search for some text ("!foo"),
allow the delimiter to end the search string. */
if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
quoted_search_delimiter = string[i - 1];
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int c, l;
l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
c = string[l];
/* XXX - original patch had i - 1 ??? If i == 0 it would fail. */
if (i && (c == '\'' || c == '"'))
quoted_search_delimiter = c;
}
else
#endif /* HANDLE_MULTIBYTE */
if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
quoted_search_delimiter = string[i - 1];
event = get_history_event (string, &i, quoted_search_delimiter);
}
@ -634,7 +688,20 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
if (c == 's')
{
if (i + 2 < (int)strlen (string))
delimiter = string[i + 2];
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
_rl_adjust_point (string, i + 2, &ps);
if (_rl_get_char_len (string + i + 2, &ps) > 1)
delimiter = 0;
else
delimiter = string[i + 2];
}
else
#endif /* HANDLE_MULTIBYTE */
delimiter = string[i + 2];
}
else
break; /* no search delimiter */
@ -819,6 +886,11 @@ history_expand (hstring, output)
int result_len;
char *result;
#if defined (HANDLE_MULTIBYTE)
char mb[MB_LEN_MAX];
mbstate_t ps;
#endif
/* Used when adding the string. */
char *temp;
@ -861,6 +933,10 @@ history_expand (hstring, output)
}
else
{
#if defined (HANDLE_MULTIBYTE)
memset (&ps, 0, sizeof (mbstate_t));
#endif
string = hstring;
/* If not quick substitution, still maybe have to do expansion. */
@ -868,8 +944,21 @@ history_expand (hstring, output)
is NOT an expansion. */
for (i = 0; string[i]; i++)
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int v;
v = _rl_get_char_len (string + i, &ps);
if (v > 1)
{
i += v - 1;
continue;
}
}
#endif /* HANDLE_MULTIBYTE */
cc = string[i + 1];
/* The history_comment_char, if set, appearing that the beginning
/* The history_comment_char, if set, appearing at the beginning
of a word signifies that the rest of the line should not have
history expansion performed on it.
Skip the rest of the line and break out of the loop. */
@ -932,6 +1021,30 @@ history_expand (hstring, output)
continue;
}
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int k, c;
c = tchar;
memset (mb, 0, sizeof (mb));
for (k = 0; k < MB_LEN_MAX; k++)
{
mb[k] = (char)c;
memset (&ps, 0, sizeof (mbstate_t));
if (_rl_get_char_len (mb, &ps) == -2)
c = string[++i];
else
break;
}
if (strlen (mb) > 1)
{
ADD_STRING (mb);
break;
}
}
#endif /* HANDLE_MULTIBYTE */
if (tchar == history_expansion_char)
tchar = -3;
else if (tchar == history_comment_char)

View file

@ -48,12 +48,26 @@
# include <unistd.h>
#endif
#if defined (HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif /* !HAVE_STRING_H */
#if defined (__EMX__) || defined (__CYGWIN__)
# undef HAVE_MMAP
#endif
#ifdef HAVE_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 /* HAVE_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
@ -137,8 +151,8 @@ read_history_range (filename, from, to)
const char *filename;
int from, to;
{
register int line_start, line_end;
char *input, *buffer;
register char *line_start, *line_end;
char *input, *buffer, *bufend;
int file, current_line, chars_read;
struct stat finfo;
size_t file_size;
@ -157,23 +171,39 @@ read_history_range (filename, from, to)
{
#if defined (EFBIG)
errno = EFBIG;
#elif defined (EOVERFLOW)
errno = EOVERFLOW;
#endif
goto error_and_exit;
}
buffer = (char *)xmalloc (file_size + 1);
#ifdef HAVE_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)
goto error_and_exit;
chars_read = file_size;
#else
buffer = (char *)malloc (file_size + 1);
if (buffer == 0)
goto error_and_exit;
chars_read = read (file, buffer, file_size);
#endif
if (chars_read < 0)
{
error_and_exit:
chars_read = errno;
if (file >= 0)
close (file);
FREE (input);
#ifndef HAVE_MMAP
FREE (buffer);
#endif
return (errno);
return (chars_read);
}
close (file);
@ -183,29 +213,25 @@ read_history_range (filename, from, to)
to = chars_read;
/* Start at beginning of file, work to end. */
line_start = line_end = current_line = 0;
bufend = buffer + chars_read;
current_line = 0;
/* Skip lines until we are at FROM. */
while (line_start < chars_read && current_line < from)
{
for (line_end = line_start; line_end < chars_read; line_end++)
if (buffer[line_end] == '\n')
{
current_line++;
line_start = line_end + 1;
if (current_line == from)
break;
}
}
for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
if (*line_end == '\n')
{
current_line++;
line_start = line_end + 1;
}
/* If there are lines left to gobble, then gobble them now. */
for (line_end = line_start; line_end < chars_read; line_end++)
if (buffer[line_end] == '\n')
for (line_end = line_start; line_end < bufend; line_end++)
if (*line_end == '\n')
{
buffer[line_end] = '\0';
*line_end = '\0';
if (buffer[line_start])
add_history (buffer + line_start);
if (*line_start)
add_history (line_start);
current_line++;
@ -216,7 +242,11 @@ read_history_range (filename, from, to)
}
FREE (input);
#ifndef HAVE_MMAP
FREE (buffer);
#else
munmap (buffer, file_size);
#endif
return (0);
}
@ -229,9 +259,8 @@ history_truncate_file (fname, lines)
const char *fname;
int lines;
{
register int i;
char *buffer, *filename, *bp;
int file, chars_read, rv;
char *buffer, *filename;
struct stat finfo;
size_t file_size;
@ -276,7 +305,13 @@ history_truncate_file (fname, lines)
goto truncate_exit;
}
buffer = (char *)xmalloc (file_size + 1);
buffer = (char *)malloc (file_size + 1);
if (buffer == 0)
{
close (file);
goto truncate_exit;
}
chars_read = read (file, buffer, file_size);
close (file);
@ -288,9 +323,9 @@ history_truncate_file (fname, lines)
/* Count backwards from the end of buffer until we have passed
LINES lines. */
for (i = chars_read - 1; lines && i; i--)
for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
{
if (buffer[i] == '\n')
if (*bp == '\n')
lines--;
}
@ -299,22 +334,22 @@ history_truncate_file (fname, lines)
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 ( ; i; i--)
if (buffer[i] == '\n')
for ( ; bp > buffer; bp--)
if (*bp == '\n')
{
i++;
bp++;
break;
}
/* Write only if there are more lines in the file than we want to
truncate to. */
if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
{
write (file, buffer + i, chars_read - i);
write (file, bp, chars_read - (bp - buffer));
#if defined (__BEOS__)
/* BeOS ignores O_TRUNC. */
ftruncate (file, chars_read - i);
ftruncate (file, chars_read - (bp - buffer));
#endif
close (file);
@ -339,8 +374,13 @@ history_do_write (filename, nelements, overwrite)
register int i;
char *output;
int file, mode, rv;
size_t cursize;
#ifdef HAVE_MMAP
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;
@ -350,6 +390,10 @@ history_do_write (filename, nelements, overwrite)
return (errno);
}
#ifdef HAVE_MMAP
cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
#endif
if (nelements > history_length)
nelements = history_length;
@ -367,7 +411,28 @@ history_do_write (filename, nelements, overwrite)
buffer_size += 1 + strlen (the_history[i]->line);
/* Allocate the buffer, and fill it. */
buffer = (char *)xmalloc (buffer_size);
#ifdef HAVE_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++)
{
@ -376,9 +441,14 @@ history_do_write (filename, nelements, overwrite)
buffer[j++] = '\n';
}
#ifdef HAVE_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);

View file

@ -22,6 +22,12 @@
#if !defined (_HISTLIB_H_)
#define _HISTLIB_H_
#if defined (HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif /* !HAVE_STRING_H */
#if !defined (STREQ)
#define STREQ(a, b) (((a)[0] == (b)[0]) && (strcmp ((a), (b)) == 0))
#define STREQN(a, b, n) (((n) == 0) ? (1) \
@ -29,9 +35,6 @@
#endif
#ifndef savestring
# ifndef strcpy
extern char *strcpy ();
# endif
#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
#endif

View file

@ -44,12 +44,6 @@
# include <unistd.h>
#endif
#if defined (HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif /* !HAVE_STRING_H */
#include "history.h"
#include "histlib.h"
@ -349,19 +343,19 @@ stifle_history (max)
max_input_history = history_max_entries = max;
}
/* Stop stifling the history. This returns the previous amount the
history was stifled by. The value is positive if the history was
stifled, negative if it wasn't. */
/* Stop stifling the history. This returns the previous maximum
number of history entries. The value is positive if the history
was stifled, negative if it wasn't. */
int
unstifle_history ()
{
if (history_stifled)
{
history_stifled = 0;
return (-history_max_entries);
return (history_max_entries);
}
return (history_max_entries);
else
return (-history_max_entries);
}
int

View file

@ -32,17 +32,13 @@
#else
# include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */
#if defined (HAVE_UNISTD_H)
# ifdef _MINIX
# include <sys/types.h>
# endif
# include <unistd.h>
#endif
#if defined (HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif /* !HAVE_STRING_H */
#include "history.h"
#include "histlib.h"

View file

@ -63,6 +63,7 @@ extern int errno;
/* System-specific feature definitions and include files. */
#include "rldefs.h"
#include "rlmbutil.h"
/* Some standard library routines. */
#include "readline.h"
@ -86,8 +87,7 @@ 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_unget_char PARAMS((int));
static void rl_gather_tyi PARAMS((void));
static int rl_gather_tyi PARAMS((void));
/* **************************************************************** */
/* */
@ -139,8 +139,8 @@ rl_get_char (key)
/* Stuff KEY into the *front* of the input buffer.
Returns non-zero if successful, zero if there is
no space left in the buffer. */
static int
rl_unget_char (key)
int
_rl_unget_char (key)
int key;
{
if (ibuffer_space ())
@ -154,9 +154,10 @@ rl_unget_char (key)
return (0);
}
/* If a character is available to be read, then read it
and stuff it into IBUFFER. Otherwise, just return. */
static void
/* 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;
@ -177,13 +178,17 @@ rl_gather_tyi ()
FD_SET (tty, &exceptfds);
timeout.tv_sec = 0;
timeout.tv_usec = _keyboard_input_timeout;
if (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) <= 0)
return; /* Nothing to read. */
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)
@ -196,14 +201,14 @@ rl_gather_tyi ()
fcntl (tty, F_SETFL, tem);
if (chars_avail == -1 && errno == EAGAIN)
return;
return 0;
}
#endif /* O_NDELAY */
/* If there's nothing available, don't waste time trying to read
something. */
if (chars_avail <= 0)
return;
return 0;
tem = ibuffer_space ();
@ -227,6 +232,8 @@ rl_gather_tyi ()
if (chars_avail)
rl_stuff_char (input);
}
return 1;
}
int
@ -242,7 +249,11 @@ rl_set_keyboard_input_timeout (u)
}
/* Is there input available to be read on the readline input file
descriptor? Only works if the system has select(2) or FIONREAD. */
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 ()
{
@ -277,6 +288,18 @@ _rl_input_available ()
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;
@ -294,7 +317,7 @@ _rl_insert_typein (c)
string[i++] = key;
if (t)
rl_unget_char (key);
_rl_unget_char (key);
string[i] = '\0';
rl_insert_text (string);
@ -375,7 +398,11 @@ rl_read_key ()
(*rl_event_hook) ();
if (rl_done) /* XXX - experimental */
return ('\n');
rl_gather_tyi ();
if (rl_gather_tyi () < 0) /* XXX - EIO */
{
rl_done = 1;
return ('\n');
}
}
}
else
@ -441,3 +468,73 @@ rl_getc (stream)
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 */

View file

@ -4,7 +4,7 @@
/* */
/* **************************************************************** */
/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
This file contains the Readline Library (the Library), a set of
routines for providing Emacs style line input to programs that ask
@ -45,6 +45,8 @@
#endif
#include "rldefs.h"
#include "rlmbutil.h"
#include "readline.h"
#include "history.h"
@ -165,8 +167,12 @@ rl_search_history (direction, invoking_key)
HIST_ENTRY **hlist;
register int i;
int orig_point, orig_line, last_found_line;
int orig_point, orig_mark, orig_line, last_found_line;
int c, found, failed, sline_len;
int n, wstart, wlen;
#if defined (HANDLE_MULTIBYTE)
char mb[MB_LEN_MAX];
#endif
/* The line currently being searched. */
char *sline;
@ -184,6 +190,7 @@ rl_search_history (direction, invoking_key)
RL_SETSTATE(RL_STATE_ISEARCH);
orig_point = rl_point;
orig_mark = rl_mark;
last_found_line = orig_line = where_history ();
reverse = direction < 0;
hlist = history_list ();
@ -246,6 +253,12 @@ rl_search_history (direction, invoking_key)
c = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
c = _rl_read_mbstring (c, mb, MB_LEN_MAX);
#endif
/* Translate the keys we do something with to opcodes. */
if (c >= 0 && _rl_keymap[c].type == ISFUNC)
{
f = _rl_keymap[c].function;
@ -254,33 +267,53 @@ rl_search_history (direction, invoking_key)
c = reverse ? -1 : -2;
else if (f == rl_forward_search_history)
c = !reverse ? -1 : -2;
else if (f == rl_rubout)
c = -3;
else if (c == CTRL ('G'))
c = -4;
else if (c == CTRL ('W')) /* XXX */
c = -5;
else if (c == CTRL ('Y')) /* XXX */
c = -6;
}
#if 0
/* Let NEWLINE (^J) terminate the search for people who don't like
using ESC. ^M can still be used to terminate the search and
immediately execute the command. */
if (c == ESC || c == NEWLINE)
#else
/* The characters in isearch_terminators (set from the user-settable
variable isearch-terminators) are used to terminate the search but
not subsequently execute the character as a command. The default
value is "\033\012" (ESC and C-J). */
if (strchr (isearch_terminators, c))
#endif
{
/* ESC still terminates the search, but if there is pending
input or if input arrives within 0.1 seconds (on systems
with select(2)) it is used as a prefix character
with rl_execute_next. WATCH OUT FOR THIS! This is intended
to allow the arrow keys to be used like ^F and ^B are used
to terminate the search and execute the movement command. */
if (c == ESC && _rl_input_available ()) /* XXX */
to terminate the search and execute the movement command.
XXX - since _rl_input_available depends on the application-
settable keyboard timeout value, this could alternatively
use _rl_input_queued(100000) */
if (c == ESC && _rl_input_available ())
rl_execute_next (ESC);
break;
}
if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
#define ENDSRCH_CHAR(c) \
((CTRL_CHAR (c) || META_CHAR (c) || (c) == RUBOUT) && ((c) != CTRL ('G')))
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
if (c >= 0 && strlen (mb) == 1 && ENDSRCH_CHAR (c))
{
/* This sets rl_pending_input to c; it will be picked up the next
time rl_read_key is called. */
rl_execute_next (c);
break;
}
}
else
#endif
if (c >= 0 && ENDSRCH_CHAR (c))
{
/* This sets rl_pending_input to c; it will be picked up the next
time rl_read_key is called. */
@ -318,10 +351,23 @@ rl_search_history (direction, invoking_key)
reverse = direction < 0;
break;
case CTRL ('G'):
strcpy (rl_line_buffer, lines[orig_line]);
/* delete character from search string. */
case -3: /* C-H, DEL */
/* This is tricky. To do this right, we need to keep a
stack of search positions for the current search, with
sentinels marking the beginning and end. But this will
do until we have a real isearch-undo. */
if (search_string_index == 0)
rl_ding ();
else
search_string[--search_string_index] = '\0';
break;
case -4: /* C-G */
rl_replace_line (lines[orig_line], 0);
rl_point = orig_point;
rl_end = strlen (rl_line_buffer);
rl_mark = orig_mark;
rl_restore_prompt();
rl_clear_message ();
if (allocated_line)
@ -330,20 +376,53 @@ rl_search_history (direction, invoking_key)
RL_UNSETSTATE(RL_STATE_ISEARCH);
return 0;
#if 0
/* delete character from search string. */
case -3:
if (search_string_index == 0)
rl_ding ();
else
case -5: /* C-W */
/* skip over portion of line we already matched */
wstart = rl_point + search_string_index;
if (wstart >= rl_end)
{
search_string[--search_string_index] = '\0';
/* This is tricky. To do this right, we need to keep a
stack of search positions for the current search, with
sentinels marking the beginning and end. */
rl_ding ();
break;
}
/* if not in a word, move to one. */
if (rl_alphabetic(rl_line_buffer[wstart]) == 0)
{
rl_ding ();
break;
}
n = wstart;
while (n < rl_end && rl_alphabetic(rl_line_buffer[n]))
n++;
wlen = n - wstart + 1;
if (search_string_index + wlen + 1 >= search_string_size)
{
search_string_size += wlen + 1;
search_string = (char *)xrealloc (search_string, search_string_size);
}
for (; wstart < n; wstart++)
search_string[search_string_index++] = rl_line_buffer[wstart];
search_string[search_string_index] = '\0';
break;
case -6: /* C-Y */
/* skip over portion of line we already matched */
wstart = rl_point + search_string_index;
if (wstart >= rl_end)
{
rl_ding ();
break;
}
n = rl_end - wstart + 1;
if (search_string_index + n + 1 >= search_string_size)
{
search_string_size += n + 1;
search_string = (char *)xrealloc (search_string, search_string_size);
}
for (n = wstart; n < rl_end; n++)
search_string[search_string_index++] = rl_line_buffer[n];
search_string[search_string_index] = '\0';
break;
#endif
default:
/* Add character to search string and continue search. */
@ -352,7 +431,16 @@ rl_search_history (direction, invoking_key)
search_string_size += 128;
search_string = (char *)xrealloc (search_string, search_string_size);
}
search_string[search_string_index++] = c;
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int j, l;
for (j = 0, l = strlen (mb); j < l; )
search_string[search_string_index++] = mb[j++];
}
else
#endif
search_string[search_string_index++] = c;
search_string[search_string_index] = '\0';
break;
}
@ -417,17 +505,9 @@ rl_search_history (direction, invoking_key)
the location. */
if (found)
{
int line_len;
prev_line_found = lines[i];
line_len = strlen (lines[i]);
if (line_len >= rl_line_buffer_len)
rl_extend_line_buffer (line_len);
strcpy (rl_line_buffer, lines[i]);
rl_replace_line (lines[i], 0);
rl_point = line_index;
rl_end = line_len;
last_found_line = i;
rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
}
@ -443,25 +523,32 @@ rl_search_history (direction, invoking_key)
rl_restore_prompt ();
#if 1
/* Save the search string for possible later use. */
FREE (last_isearch_string);
last_isearch_string = search_string;
last_isearch_string_len = search_string_index;
#else
/* Free the search string. */
free (search_string);
#endif
if (last_found_line < orig_line)
rl_get_previous_history (orig_line - last_found_line, 0);
else
rl_get_next_history (last_found_line - orig_line, 0);
/* If the string was not found, put point at the end of the line. */
/* If the string was not found, put point at the end of the last matching
line. If last_found_line == orig_line, we didn't find any matching
history lines at all, so put point back in its original position. */
if (line_index < 0)
line_index = strlen (rl_line_buffer);
{
if (last_found_line == orig_line)
line_index = orig_point;
else
line_index = strlen (rl_line_buffer);
rl_mark = orig_mark;
}
rl_point = line_index;
/* Don't worry about where to put the mark here; rl_get_previous_history
and rl_get_next_history take care of it. */
rl_clear_message ();
FREE (allocated_line);

View file

@ -49,8 +49,9 @@ typedef struct _keymap_entry {
/* This must be large enough to hold bindings for all of the characters
in a desired character set (e.g, 128 for ASCII, 256 for ISO Latin-x,
and so on). */
#define KEYMAP_SIZE 256
and so on) plus one for subsequence matching. */
#define KEYMAP_SIZE 257
#define ANYOTHERKEY KEYMAP_SIZE-1
/* I wanted to make the above structure contain a union of:
union { rl_command_func_t *function; struct _keymap_entry *keymap; } value;

View file

@ -201,18 +201,21 @@ int
rl_kill_word (count, key)
int count, key;
{
int orig_point = rl_point;
int orig_point;
if (count < 0)
return (rl_backward_kill_word (-count, key));
else
{
orig_point = rl_point;
rl_forward_word (count, key);
if (rl_point != orig_point)
rl_kill_text (orig_point, rl_point);
rl_point = orig_point;
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
return 0;
}
@ -222,16 +225,20 @@ int
rl_backward_kill_word (count, ignore)
int count, ignore;
{
int orig_point = rl_point;
int orig_point;
if (count < 0)
return (rl_kill_word (-count, ignore));
else
{
orig_point = rl_point;
rl_backward_word (count, ignore);
if (rl_point != orig_point)
rl_kill_text (orig_point, rl_point);
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
return 0;
}
@ -242,16 +249,19 @@ int
rl_kill_line (direction, ignore)
int direction, ignore;
{
int orig_point = rl_point;
int orig_point;
if (direction < 0)
return (rl_backward_kill_line (1, ignore));
else
{
orig_point = rl_point;
rl_end_of_line (1, ignore);
if (orig_point != rl_point)
rl_kill_text (orig_point, rl_point);
rl_point = orig_point;
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
return 0;
}
@ -262,7 +272,7 @@ int
rl_backward_kill_line (direction, ignore)
int direction, ignore;
{
int orig_point = rl_point;
int orig_point;
if (direction < 0)
return (rl_kill_line (1, ignore));
@ -272,8 +282,12 @@ rl_backward_kill_line (direction, ignore)
rl_ding ();
else
{
orig_point = rl_point;
rl_beg_of_line (1, ignore);
rl_kill_text (orig_point, rl_point);
if (rl_point != orig_point)
rl_kill_text (orig_point, rl_point);
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
}
return 0;
@ -287,6 +301,7 @@ rl_kill_full_line (count, ignore)
rl_begin_undo_group ();
rl_point = 0;
rl_kill_text (rl_point, rl_end);
rl_mark = 0;
rl_end_undo_group ();
return 0;
}
@ -321,6 +336,8 @@ rl_unix_word_rubout (count, key)
}
rl_kill_text (orig_point, rl_point);
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
return 0;
}
@ -341,6 +358,8 @@ rl_unix_line_discard (count, key)
{
rl_kill_text (rl_point, 0);
rl_point = 0;
if (rl_editing_mode == emacs_mode)
rl_mark = rl_point;
}
return 0;
}
@ -353,17 +372,14 @@ region_kill_internal (delete)
{
char *text;
if (rl_mark == rl_point)
if (rl_mark != rl_point)
{
_rl_last_command_was_kill++;
return 0;
text = rl_copy_text (rl_point, rl_mark);
if (delete)
rl_delete_text (rl_point, rl_mark);
_rl_copy_to_kill_ring (text, rl_point < rl_mark);
}
text = rl_copy_text (rl_point, rl_mark);
if (delete)
rl_delete_text (rl_point, rl_mark);
_rl_copy_to_kill_ring (text, rl_point < rl_mark);
_rl_last_command_was_kill++;
return 0;
}
@ -530,6 +546,8 @@ rl_yank_nth_arg_internal (count, ignore, history_skip)
rl_begin_undo_group ();
_rl_set_mark_at_pos (rl_point);
#if defined (VI_MODE)
/* Vi mode always inserts a space before yanking the argument, and it
inserts it right *after* rl_point. */
@ -623,6 +641,7 @@ rl_paste_from_clipboard (count, key)
}
else
ptr = data;
_rl_set_mark_at_pos (rl_point);
rl_insert_text (ptr);
if (ptr != data)
free (ptr);

View file

@ -49,8 +49,6 @@
#include "rlprivate.h"
#include "xmalloc.h"
#define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0)
/* **************************************************************** */
/* */
/* Hacking Keyboard Macros */
@ -61,9 +59,6 @@
then it is a malloc ()'ed string where input is coming from. */
char *rl_executing_macro = (char *)NULL;
/* Non-zero means to save keys that we dispatch on in a kbd macro. */
int _rl_defining_kbd_macro = 0;
/* The offset in the above string to the next character to be read. */
static int executing_macro_index;
@ -186,7 +181,6 @@ _rl_kill_kbd_macro ()
rl_executing_macro = (char *) NULL;
executing_macro_index = 0;
_rl_defining_kbd_macro = 0;
RL_UNSETSTATE(RL_STATE_MACRODEF);
}
@ -200,7 +194,7 @@ int
rl_start_kbd_macro (ignore1, ignore2)
int ignore1, ignore2;
{
if (_rl_defining_kbd_macro)
if (RL_ISSTATE (RL_STATE_MACRODEF))
{
_rl_abort_internal ();
return -1;
@ -214,7 +208,6 @@ rl_start_kbd_macro (ignore1, ignore2)
else
current_macro_index = 0;
_rl_defining_kbd_macro = 1;
RL_SETSTATE(RL_STATE_MACRODEF);
return 0;
}
@ -226,7 +219,7 @@ int
rl_end_kbd_macro (count, ignore)
int count, ignore;
{
if (_rl_defining_kbd_macro == 0)
if (RL_ISSTATE (RL_STATE_MACRODEF) == 0)
{
_rl_abort_internal ();
return -1;
@ -235,7 +228,6 @@ rl_end_kbd_macro (count, ignore)
current_macro_index -= rl_key_sequence_length - 1;
current_macro[current_macro_index] = '\0';
_rl_defining_kbd_macro = 0;
RL_UNSETSTATE(RL_STATE_MACRODEF);
return (rl_call_last_kbd_macro (--count, 0));
@ -250,7 +242,7 @@ rl_call_last_kbd_macro (count, ignore)
if (current_macro == 0)
_rl_abort_internal ();
if (_rl_defining_kbd_macro)
if (RL_ISSTATE (RL_STATE_MACRODEF))
{
rl_ding (); /* no recursive macros */
current_macro[--current_macro_index] = '\0'; /* erase this char */

337
lib/readline/mbutil.c Normal file
View file

@ -0,0 +1,337 @@
/* mbutil.c -- readline multibyte character utility functions */
/* Copyright (C) 2001 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>
#include "posixjmp.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h> /* for _POSIX_VERSION */
#endif /* HAVE_UNISTD_H */
#if defined (HAVE_STDLIB_H)
# include <stdlib.h>
#else
# include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */
#include <stdio.h>
#include <ctype.h>
/* System-specific feature definitions and include files. */
#include "rldefs.h"
#include "rlmbutil.h"
#if defined (TIOCSTAT_IN_SYS_IOCTL)
# include <sys/ioctl.h>
#endif /* TIOCSTAT_IN_SYS_IOCTL */
/* Some standard library routines. */
#include "readline.h"
#include "rlprivate.h"
#include "xmalloc.h"
/* Declared here so it can be shared between the readline and history
libraries. */
#if defined (HANDLE_MULTIBYTE)
int rl_byte_oriented = 0;
#else
int rl_byte_oriented = 1;
#endif
/* **************************************************************** */
/* */
/* Multibyte Character Utility Functions */
/* */
/* **************************************************************** */
#if defined(HANDLE_MULTIBYTE)
static int
_rl_find_next_mbchar_internal (string, seed, count, find_non_zero)
char *string;
int seed, count, find_non_zero;
{
size_t tmp = 0;
mbstate_t ps;
int point = 0;
wchar_t wc;
memset(&ps, 0, sizeof (mbstate_t));
if (seed < 0)
seed = 0;
if (count <= 0)
return seed;
point = seed + _rl_adjust_point(string, seed, &ps);
/* if this is true, means that seed was not pointed character
started byte. So correct the point and consume count */
if (seed < point)
count --;
while (count > 0)
{
tmp = mbrtowc (&wc, string+point, strlen(string + point), &ps);
if ((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2)
{
/* invalid bytes. asume a byte represents a character */
point++;
count--;
/* reset states. */
memset(&ps, 0, sizeof(mbstate_t));
}
else if (tmp == (size_t)0)
/* found '\0' char */
break;
else
{
/* valid bytes */
point += tmp;
if (find_non_zero)
{
if (wcwidth (wc) == 0)
continue;
else
count--;
}
else
count--;
}
}
if (find_non_zero)
{
tmp = mbrtowc (&wc, string + point, strlen (string + point), &ps);
while (wcwidth (wc) == 0)
{
point += tmp;
tmp = mbrtowc (&wc, string + point, strlen (string + point), &ps);
if (tmp == (size_t)(0) || tmp == (size_t)(-1) || tmp == (size_t)(-2))
break;
}
}
return point;
}
static int
_rl_find_prev_mbchar_internal (string, seed, find_non_zero)
char *string;
int seed, find_non_zero;
{
mbstate_t ps;
int prev, non_zero_prev, point, length;
size_t tmp;
wchar_t wc;
memset(&ps, 0, sizeof(mbstate_t));
length = strlen(string);
if (seed < 0)
return 0;
else if (length < seed)
return length;
prev = non_zero_prev = point = 0;
while (point < seed)
{
tmp = mbrtowc (&wc, string + point, length - point, &ps);
if ((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2)
{
/* in this case, bytes are invalid or shorted to compose
multibyte char, so assume that the first byte represents
a single character anyway. */
tmp = 1;
/* clear the state of the byte sequence, because
in this case effect of mbstate is undefined */
memset(&ps, 0, sizeof (mbstate_t));
}
else if (tmp == 0)
break; /* Found '\0' char. Can this happen? */
else
{
if (find_non_zero)
{
if (wcwidth (wc) != 0)
prev = point;
}
else
prev = point;
}
point += tmp;
}
return prev;
}
/* return the number of bytes parsed from the multibyte sequence starting
at src, if a non-L'\0' wide character was recognized. It returns 0,
if a L'\0' wide character was recognized. It returns (size_t)(-1),
if an invalid multibyte sequence was encountered. It returns (size_t)(-2)
if it couldn't parse a complete multibyte character. */
int
_rl_get_char_len (src, ps)
char *src;
mbstate_t *ps;
{
size_t tmp;
tmp = mbrlen((const char *)src, (size_t)strlen (src), ps);
if (tmp == (size_t)(-2))
{
/* shorted to compose multibyte char */
memset (ps, 0, sizeof(mbstate_t));
return -2;
}
else if (tmp == (size_t)(-1))
{
/* invalid to compose multibyte char */
/* initialize the conversion state */
memset (ps, 0, sizeof(mbstate_t));
return -1;
}
else if (tmp == (size_t)0)
return 0;
else
return (int)tmp;
}
/* compare the specified two characters. If the characters matched,
return 1. Otherwise return 0. */
int
_rl_compare_chars (buf1, pos1, ps1, buf2, pos2, ps2)
char *buf1, *buf2;
mbstate_t *ps1, *ps2;
int pos1, pos2;
{
int i, w1, w2;
if ((w1 = _rl_get_char_len (&buf1[pos1], ps1)) <= 0 ||
(w2 = _rl_get_char_len (&buf2[pos2], ps2)) <= 0 ||
(w1 != w2) ||
(buf1[pos1] != buf2[pos2]))
return 0;
for (i = 1; i < w1; i++)
if (buf1[pos1+i] != buf2[pos2+i])
return 0;
return 1;
}
/* adjust pointed byte and find mbstate of the point of string.
adjusted point will be point <= adjusted_point, and returns
differences of the byte(adjusted_point - point).
if point is invalied (point < 0 || more than string length),
it returns -1 */
int
_rl_adjust_point(string, point, ps)
char *string;
int point;
mbstate_t *ps;
{
size_t tmp = 0;
int length;
int pos = 0;
length = strlen(string);
if (point < 0)
return -1;
if (length < point)
return -1;
while (pos < point)
{
tmp = mbrlen (string + pos, length - pos, ps);
if((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2)
{
/* in this case, bytes are invalid or shorted to compose
multibyte char, so assume that the first byte represents
a single character anyway. */
pos++;
/* clear the state of the byte sequence, because
in this case effect of mbstate is undefined */
memset (ps, 0, sizeof (mbstate_t));
}
else
pos += tmp;
}
return (pos - point);
}
int
_rl_is_mbchar_matched (string, seed, end, mbchar, length)
char *string;
int seed, end;
char *mbchar;
int length;
{
int i;
if ((end - seed) < length)
return 0;
for (i = 0; i < length; i++)
if (string[seed + i] != mbchar[i])
return 0;
return 1;
}
#endif /* HANDLE_MULTIBYTE */
/* Find next `count' characters started byte point of the specified seed.
If flags is MB_FIND_NONZERO, we look for non-zero-width multibyte
characters. */
#undef _rl_find_next_mbchar
int
_rl_find_next_mbchar (string, seed, count, flags)
char *string;
int seed, count, flags;
{
#if defined (HANDLE_MULTIBYTE)
return _rl_find_next_mbchar_internal (string, seed, count, flags);
#else
return (seed + count);
#endif
}
/* Find previous character started byte point of the specified seed.
Returned point will be point <= seed. If flags is MB_FIND_NONZERO,
we look for non-zero-width multibyte characters. */
#undef _rl_find_prev_mbchar
int
_rl_find_prev_mbchar (string, seed, flags)
char *string;
int seed, flags;
{
#if defined (HANDLE_MULTIBYTE)
return _rl_find_prev_mbchar_internal (string, seed, flags);
#else
return ((seed == 0) ? seed : seed - 1);
#endif
}

496
lib/readline/misc.c Normal file
View file

@ -0,0 +1,496 @@
/* misc.c -- miscellaneous bindable readline functions. */
/* Copyright (C) 1987-2002 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
#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_LOCALE_H)
# include <locale.h>
#endif
#include <stdio.h>
/* System-specific feature definitions and include files. */
#include "rldefs.h"
#include "rlmbutil.h"
/* Some standard library routines. */
#include "readline.h"
#include "history.h"
#include "rlprivate.h"
#include "rlshell.h"
#include "xmalloc.h"
static int rl_digit_loop PARAMS((void));
static void _rl_history_set_point PARAMS((void));
/* Forward declarations used in this file */
void _rl_free_history_entry PARAMS((HIST_ENTRY *));
/* If non-zero, rl_get_previous_history and rl_get_next_history attempt
to preserve the value of rl_point from line to line. */
int _rl_history_preserve_point = 0;
/* Saved target point for when _rl_history_preserve_point is set. Special
value of -1 means that point is at the end of the line. */
int _rl_history_saved_point = -1;
/* **************************************************************** */
/* */
/* Numeric Arguments */
/* */
/* **************************************************************** */
/* Handle C-u style numeric args, as well as M--, and M-digits. */
static int
rl_digit_loop ()
{
int key, c, sawminus, sawdigits;
rl_save_prompt ();
RL_SETSTATE(RL_STATE_NUMERICARG);
sawminus = sawdigits = 0;
while (1)
{
if (rl_numeric_arg > 1000000)
{
sawdigits = rl_explicit_arg = rl_numeric_arg = 0;
rl_ding ();
rl_restore_prompt ();
rl_clear_message ();
RL_UNSETSTATE(RL_STATE_NUMERICARG);
return 1;
}
rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
RL_SETSTATE(RL_STATE_MOREINPUT);
key = c = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
if (c < 0)
{
_rl_abort_internal ();
return -1;
}
/* If we see a key bound to `universal-argument' after seeing digits,
it ends the argument but is otherwise ignored. */
if (_rl_keymap[c].type == ISFUNC &&
_rl_keymap[c].function == rl_universal_argument)
{
if (sawdigits == 0)
{
rl_numeric_arg *= 4;
continue;
}
else
{
RL_SETSTATE(RL_STATE_MOREINPUT);
key = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
rl_restore_prompt ();
rl_clear_message ();
RL_UNSETSTATE(RL_STATE_NUMERICARG);
return (_rl_dispatch (key, _rl_keymap));
}
}
c = UNMETA (c);
if (_rl_digit_p (c))
{
rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + c - '0' : c - '0';
sawdigits = rl_explicit_arg = 1;
}
else if (c == '-' && rl_explicit_arg == 0)
{
rl_numeric_arg = sawminus = 1;
rl_arg_sign = -1;
}
else
{
/* Make M-- command equivalent to M--1 command. */
if (sawminus && rl_numeric_arg == 1 && rl_explicit_arg == 0)
rl_explicit_arg = 1;
rl_restore_prompt ();
rl_clear_message ();
RL_UNSETSTATE(RL_STATE_NUMERICARG);
return (_rl_dispatch (key, _rl_keymap));
}
}
/*NOTREACHED*/
}
/* Add the current digit to the argument in progress. */
int
rl_digit_argument (ignore, key)
int ignore, key;
{
rl_execute_next (key);
return (rl_digit_loop ());
}
/* What to do when you abort reading an argument. */
int
rl_discard_argument ()
{
rl_ding ();
rl_clear_message ();
_rl_init_argument ();
return 0;
}
/* Create a default argument. */
int
_rl_init_argument ()
{
rl_numeric_arg = rl_arg_sign = 1;
rl_explicit_arg = 0;
return 0;
}
/* C-u, universal argument. Multiply the current argument by 4.
Read a key. If the key has nothing to do with arguments, then
dispatch on it. If the key is the abort character then abort. */
int
rl_universal_argument (count, key)
int count, key;
{
rl_numeric_arg *= 4;
return (rl_digit_loop ());
}
/* **************************************************************** */
/* */
/* History Utilities */
/* */
/* **************************************************************** */
/* We already have a history library, and that is what we use to control
the history features of readline. This is our local interface to
the history mechanism. */
/* While we are editing the history, this is the saved
version of the original line. */
HIST_ENTRY *_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
/* Set the history pointer back to the last entry in the history. */
void
_rl_start_using_history ()
{
using_history ();
if (_rl_saved_line_for_history)
_rl_free_history_entry (_rl_saved_line_for_history);
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
}
/* Free the contents (and containing structure) of a HIST_ENTRY. */
void
_rl_free_history_entry (entry)
HIST_ENTRY *entry;
{
if (entry == 0)
return;
if (entry->line)
free (entry->line);
free (entry);
}
/* Perhaps put back the current line if it has changed. */
int
rl_maybe_replace_line ()
{
HIST_ENTRY *temp;
temp = current_history ();
/* If the current line has changed, save the changes. */
if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list))
{
temp = replace_history_entry (where_history (), rl_line_buffer, (histdata_t)rl_undo_list);
free (temp->line);
free (temp);
}
return 0;
}
/* Restore the _rl_saved_line_for_history if there is one. */
int
rl_maybe_unsave_line ()
{
if (_rl_saved_line_for_history)
{
rl_replace_line (_rl_saved_line_for_history->line, 0);
rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data;
_rl_free_history_entry (_rl_saved_line_for_history);
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
rl_point = rl_end; /* rl_replace_line sets rl_end */
}
else
rl_ding ();
return 0;
}
/* Save the current line in _rl_saved_line_for_history. */
int
rl_maybe_save_line ()
{
if (_rl_saved_line_for_history == 0)
{
_rl_saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
_rl_saved_line_for_history->line = savestring (rl_line_buffer);
_rl_saved_line_for_history->data = (char *)rl_undo_list;
}
return 0;
}
int
_rl_free_saved_history_line ()
{
if (_rl_saved_line_for_history)
{
_rl_free_history_entry (_rl_saved_line_for_history);
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
}
return 0;
}
static void
_rl_history_set_point ()
{
rl_point = (_rl_history_preserve_point && _rl_history_saved_point != -1)
? _rl_history_saved_point
: rl_end;
if (rl_point > rl_end)
rl_point = rl_end;
#if defined (VI_MODE)
if (rl_editing_mode == vi_mode)
rl_point = 0;
#endif /* VI_MODE */
if (rl_editing_mode == emacs_mode)
rl_mark = (rl_point == rl_end ? 0 : rl_end);
}
void
rl_replace_from_history (entry, flags)
HIST_ENTRY *entry;
int flags; /* currently unused */
{
rl_replace_line (entry->line, 0);
rl_undo_list = (UNDO_LIST *)entry->data;
rl_point = rl_end;
rl_mark = 0;
#if defined (VI_MODE)
if (rl_editing_mode == vi_mode)
{
rl_point = 0;
rl_mark = rl_end;
}
#endif
}
/* **************************************************************** */
/* */
/* History Commands */
/* */
/* **************************************************************** */
/* Meta-< goes to the start of the history. */
int
rl_beginning_of_history (count, key)
int count, key;
{
return (rl_get_previous_history (1 + where_history (), key));
}
/* Meta-> goes to the end of the history. (The current line). */
int
rl_end_of_history (count, key)
int count, key;
{
rl_maybe_replace_line ();
using_history ();
rl_maybe_unsave_line ();
return 0;
}
/* Move down to the next history line. */
int
rl_get_next_history (count, key)
int count, key;
{
HIST_ENTRY *temp;
if (count < 0)
return (rl_get_previous_history (-count, key));
if (count == 0)
return 0;
rl_maybe_replace_line ();
/* either not saved by rl_newline or at end of line, so set appropriately. */
if (_rl_history_saved_point == -1 && (rl_point || rl_end))
_rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
temp = (HIST_ENTRY *)NULL;
while (count)
{
temp = next_history ();
if (!temp)
break;
--count;
}
if (temp == 0)
rl_maybe_unsave_line ();
else
{
rl_replace_from_history (temp, 0);
_rl_history_set_point ();
}
return 0;
}
/* Get the previous item out of our interactive history, making it the current
line. If there is no previous history, just ding. */
int
rl_get_previous_history (count, key)
int count, key;
{
HIST_ENTRY *old_temp, *temp;
if (count < 0)
return (rl_get_next_history (-count, key));
if (count == 0)
return 0;
/* either not saved by rl_newline or at end of line, so set appropriately. */
if (_rl_history_saved_point == -1 && (rl_point || rl_end))
_rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
/* If we don't have a line saved, then save this one. */
rl_maybe_save_line ();
/* If the current line has changed, save the changes. */
rl_maybe_replace_line ();
temp = old_temp = (HIST_ENTRY *)NULL;
while (count)
{
temp = previous_history ();
if (temp == 0)
break;
old_temp = temp;
--count;
}
/* If there was a large argument, and we moved back to the start of the
history, that is not an error. So use the last value found. */
if (!temp && old_temp)
temp = old_temp;
if (temp == 0)
rl_ding ();
else
{
rl_replace_from_history (temp, 0);
_rl_history_set_point ();
}
return 0;
}
/* **************************************************************** */
/* */
/* Editing Modes */
/* */
/* **************************************************************** */
/* How to toggle back and forth between editing modes. */
int
rl_vi_editing_mode (count, key)
int count, key;
{
#if defined (VI_MODE)
_rl_set_insert_mode (RL_IM_INSERT, 1); /* vi mode ignores insert mode */
rl_editing_mode = vi_mode;
rl_vi_insertion_mode (1, key);
#endif /* VI_MODE */
return 0;
}
int
rl_emacs_editing_mode (count, key)
int count, key;
{
rl_editing_mode = emacs_mode;
_rl_set_insert_mode (RL_IM_INSERT, 1); /* emacs mode default is insert mode */
_rl_keymap = emacs_standard_keymap;
return 0;
}
/* Function for the rest of the library to use to set insert/overwrite mode. */
void
_rl_set_insert_mode (im, force)
int im, force;
{
#ifdef CURSOR_MODE
_rl_set_cursor (im, force);
#endif
rl_insert_mode = im;
}
/* Toggle overwrite mode. A positive explicit argument selects overwrite
mode. A negative or zero explicit argument selects insert mode. */
int
rl_overwrite_mode (count, key)
int count, key;
{
if (rl_explicit_arg == 0)
_rl_set_insert_mode (rl_insert_mode ^ 1, 0);
else if (count > 0)
_rl_set_insert_mode (RL_IM_OVERWRITE, 0);
else
_rl_set_insert_mode (RL_IM_INSERT, 0);
return 0;
}

View file

@ -30,6 +30,10 @@
#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
@ -103,7 +107,7 @@ rl_insert_close (count, invoking_key)
int count, invoking_key;
{
if (rl_explicit_arg || !rl_blink_matching_paren)
rl_insert (count, invoking_key);
_rl_insert_char (count, invoking_key);
else
{
#if defined (HAVE_SELECT)
@ -111,7 +115,7 @@ rl_insert_close (count, invoking_key)
struct timeval timer;
fd_set readfds;
rl_insert (1, invoking_key);
_rl_insert_char (1, invoking_key);
(*rl_redisplay_function) ();
match_point =
find_matching_open (rl_line_buffer, rl_point - 2, invoking_key);
@ -131,7 +135,7 @@ rl_insert_close (count, invoking_key)
ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
rl_point = orig_point;
#else /* !HAVE_SELECT */
rl_insert (count, invoking_key);
_rl_insert_char (count, invoking_key);
#endif /* !HAVE_SELECT */
}
return 0;

View file

@ -46,4 +46,12 @@
# define d_fileno d_ino
#endif
#if defined (_POSIX_SOURCE) && (!defined (STRUCT_DIRENT_HAS_D_INO) || defined (BROKEN_DIRENT_D_INO))
/* Posix does not require that the d_ino field be present, and some
systems do not provide it. */
# define REAL_DIR_ENTRY(dp) 1
#else
# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
#endif /* _POSIX_SOURCE */
#endif /* !_POSIXDIR_H_ */

File diff suppressed because it is too large Load diff

View file

@ -29,18 +29,20 @@ extern "C" {
#if defined (READLINE_LIBRARY)
# include "rlstdc.h"
# include "rltypedefs.h"
# include "keymaps.h"
# include "tilde.h"
#else
# include <readline/rlstdc.h>
# include <readline/rltypedefs.h>
# include <readline/keymaps.h>
# include <readline/tilde.h>
#endif
/* Hex-encoded Readline version number. */
#define RL_READLINE_VERSION 0x0402 /* Readline 4.2 */
#define RL_READLINE_VERSION 0x0403 /* Readline 4.3 */
#define RL_VERSION_MAJOR 4
#define RL_VERSION_MINOR 2
#define RL_VERSION_MINOR 3
/* Readline data structures. */
@ -82,7 +84,11 @@ extern int rl_digit_argument PARAMS((int, int));
extern int rl_universal_argument PARAMS((int, int));
/* Bindable commands for moving the cursor. */
extern int rl_forward_byte PARAMS((int, int));
extern int rl_forward_char PARAMS((int, int));
extern int rl_forward PARAMS((int, int));
extern int rl_backward_byte PARAMS((int, int));
extern int rl_backward_char PARAMS((int, int));
extern int rl_backward PARAMS((int, int));
extern int rl_beg_of_line PARAMS((int, int));
extern int rl_end_of_line PARAMS((int, int));
@ -132,6 +138,9 @@ extern int rl_exchange_point_and_mark PARAMS((int, int));
extern int rl_vi_editing_mode PARAMS((int, int));
extern int rl_emacs_editing_mode PARAMS((int, int));
/* Bindable commands to change the insert mode (insert or overwrite) */
extern int rl_overwrite_mode PARAMS((int, int));
/* Bindable commands for managing key bindings. */
extern int rl_re_read_init_file PARAMS((int, int));
extern int rl_dump_functions PARAMS((int, int));
@ -365,6 +374,7 @@ extern void rl_save_prompt PARAMS((void));
extern void rl_restore_prompt PARAMS((void));
/* Modifying text. */
extern void rl_replace_line PARAMS((const char *, int));
extern int rl_insert_text PARAMS((const char *));
extern int rl_delete_text PARAMS((int, int));
extern int rl_kill_text PARAMS((int, int));
@ -417,6 +427,8 @@ extern char **rl_completion_matches PARAMS((const char *, rl_compentry_func_t *)
extern char *rl_username_completion_function PARAMS((const char *, int));
extern char *rl_filename_completion_function PARAMS((const char *, int));
extern int rl_completion_mode PARAMS((rl_command_func_t *));
#if 0
/* Backwards compatibility (compat.c). These will go away sometime. */
extern void free_undo_list PARAMS((void));
@ -453,6 +465,10 @@ extern int rl_readline_state;
0 means vi mode. */
extern int rl_editing_mode;
/* Insert or overwrite mode for emacs mode. 1 means insert mode; 0 means
overwrite mode. Reset to insert mode on each input line. */
extern int rl_insert_mode;
/* The name of the calling program. You should initialize this to
whatever was in argv[0]. It is used when parsing conditionals. */
extern const char *rl_readline_name;
@ -675,18 +691,33 @@ extern int rl_completion_type;
default is a space. Nothing is added if this is '\0'. */
extern int rl_completion_append_character;
/* If set to non-zero by an application completion function,
rl_completion_append_character will not be appended. */
extern int rl_completion_suppress_append;
/* Up to this many items will be displayed in response to a
possible-completions call. After that, we ask the user if she
is sure she wants to see them all. The default value is 100. */
extern int rl_completion_query_items;
/* If non-zero, a slash will be appended to completed filenames that are
symbolic links to directory names, subject to the value of the
mark-directories variable (which is user-settable). This exists so
that application completion functions can override the user's preference
(set via the mark-symlinked-directories variable) if appropriate.
It's set to the value of _rl_complete_mark_symlink_dirs in
rl_complete_internal before any application-specific completion
function is called, so without that function doing anything, the user's
preferences are honored. */
extern int rl_completion_mark_symlink_dirs;
/* If non-zero, then disallow duplicates in the matches. */
extern int rl_ignore_completion_duplicates;
/* If this is non-zero, completion is (temporarily) inhibited, and the
completion character will be inserted as any other. */
extern int rl_inhibit_completion;
/* Definitions available for use by readline clients. */
#define RL_PROMPT_START_IGNORE '\001'
#define RL_PROMPT_END_IGNORE '\002'
@ -725,6 +756,42 @@ extern int rl_inhibit_completion;
#define RL_UNSETSTATE(x) (rl_readline_state &= ~(x))
#define RL_ISSTATE(x) (rl_readline_state & (x))
struct readline_state {
/* line state */
int point;
int end;
int mark;
char *buffer;
int buflen;
UNDO_LIST *ul;
char *prompt;
/* global state */
int rlstate;
int done;
Keymap kmap;
/* input state */
rl_command_func_t *lastfunc;
int insmode;
int edmode;
int kseqlen;
FILE *inf;
FILE *outf;
int pendingin;
char *macro;
/* signal state */
int catchsigs;
int catchsigwinch;
/* reserved for future expansion, so the struct size doesn't change */
char reserved[64];
};
extern int rl_save_state PARAMS((struct readline_state *));
extern int rl_restore_state PARAMS((struct readline_state *));
#ifdef __cplusplus
}
#endif

View file

@ -54,4 +54,7 @@
X `callback' style. */
#define READLINE_CALLBACKS
/* Define this if you want the cursor to indicate insert or overwrite mode. */
/* #define CURSOR_MODE */
#endif /* _RLCONF_H_ */

View file

@ -74,7 +74,7 @@ extern char *strchr (), *strrchr ();
#define _rl_strnicmp strncasecmp
#else
extern int _rl_stricmp PARAMS((char *, char *));
extern int _rl_strnicmp PARAMS((char *, char *));
extern int _rl_strnicmp PARAMS((char *, char *, int));
#endif
#if defined (HAVE_STRPBRK)
@ -89,6 +89,13 @@ extern char *_rl_strpbrk PARAMS((const char *, const char *));
# define emacs_mode 1
#endif
#if !defined (RL_IM_INSERT)
# define RL_IM_INSERT 1
# define RL_IM_OVERWRITE 0
#
# define RL_IM_DEFAULT RL_IM_INSERT
#endif
/* If you cast map[key].function to type (Keymap) on a Cray,
the compiler takes the value of map[key].function and
divides it by 4 to convert between pointer types (pointers
@ -121,9 +128,10 @@ extern char *_rl_strpbrk PARAMS((const char *, const char *));
/* Possible values for the found_quote flags word used by the completion
functions. It says what kind of (shell-like) quoting we found anywhere
in the line. */
#define RL_QF_SINGLE_QUOTE 0x1
#define RL_QF_DOUBLE_QUOTE 0x2
#define RL_QF_BACKSLASH 0x4
#define RL_QF_SINGLE_QUOTE 0x01
#define RL_QF_DOUBLE_QUOTE 0x02
#define RL_QF_BACKSLASH 0x04
#define RL_QF_OTHER_QUOTE 0x08
/* Default readline line buffer length. */
#define DEFAULT_BUFFER_SIZE 256
@ -138,6 +146,10 @@ extern char *_rl_strpbrk PARAMS((const char *, const char *));
# define FREE(x) if (x) free (x)
#endif
#if !defined (SWAP)
# define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0)
#endif
/* CONFIGURATION SECTION */
#include "rlconf.h"

108
lib/readline/rlmbutil.h Normal file
View file

@ -0,0 +1,108 @@
/* rlmbutil.h -- utility functions for multibyte characters. */
/* Copyright (C) 2001 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. */
#if !defined (_RL_MBUTIL_H_)
#define _RL_MBUTIL_H_
#include "rlstdc.h"
/************************************************/
/* check multibyte capability for I18N code */
/************************************************/
/* For platforms which support the ISO C amendement 1 functionality we
support user defined character classes. */
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
#if defined (HAVE_WCTYPE_H) && defined (HAVE_WCHAR_H)
# include <wchar.h>
# include <wctype.h>
# if defined (HAVE_MBSRTOWCS) /* system is supposed to support XPG5 */
# define HANDLE_MULTIBYTE 1
# endif
#endif
/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */
#if HANDLE_MULTIBYTE && !defined (HAVE_MBSTATE_T)
# define wcsrtombs(dest, src, len, ps) (wcsrtombs) (dest, src, len, 0)
# define mbsrtowcs(dest, src, len, ps) (mbsrtowcs) (dest, src, len, 0)
# define wcrtomb(s, wc, ps) (wcrtomb) (s, wc, 0)
# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
# define mbrlen(s, n, ps) (mbrlen) (s, n, 0)
# define mbstate_t int
#endif
/* Make sure MB_LEN_MAX is at least 16 on systems that claim to be able to
handle multibyte chars (some systems define MB_LEN_MAX as 1) */
#ifdef HANDLE_MULTIBYTE
# include <limits.h>
# if defined(MB_LEN_MAX) && (MB_LEN_MAX < 16)
# undef MB_LEN_MAX
# endif
# if !defined (MB_LEN_MAX)
# define MB_LEN_MAX 16
# endif
#endif
/************************************************/
/* end of multibyte capability checks for I18N */
/************************************************/
/*
* Flags for _rl_find_prev_mbchar and _rl_find_next_mbchar:
*
* MB_FIND_ANY find any multibyte character
* MB_FIND_NONZERO find a non-zero-width multibyte character
*/
#define MB_FIND_ANY 0x00
#define MB_FIND_NONZERO 0x01
extern int _rl_find_prev_mbchar PARAMS((char *, int, int));
extern int _rl_find_next_mbchar PARAMS((char *, int, int, int));
#ifdef HANDLE_MULTIBYTE
extern int _rl_compare_chars PARAMS((char *, int, mbstate_t *, char *, int, mbstate_t *));
extern int _rl_get_char_len PARAMS((char *, mbstate_t *));
extern int _rl_adjust_point PARAMS((char *, int, mbstate_t *));
extern int _rl_read_mbchar PARAMS((char *, int));
extern int _rl_read_mbstring PARAMS((int, char *, int));
extern int _rl_is_mbchar_matched PARAMS((char *, int, int, char *, int));
#else /* !HANDLE_MULTIBYTE */
#undef MB_LEN_MAX
#undef MB_CUR_MAX
#define MB_LEN_MAX 1
#define MB_CUR_MAX 1
#define _rl_find_prev_mbchar(b, i, f) (((i) == 0) ? (i) : ((i) - 1))
#define _rl_find_next_mbchar(b, i1, i2, f) ((i1) + (i2))
#endif /* !HANDLE_MULTIBYTE */
extern int rl_byte_oriented;
#endif /* _RL_MBUTIL_H_ */

View file

@ -52,6 +52,7 @@ extern int rl_arg_sign;
extern int rl_visible_prompt_length;
extern int readline_echoing_p;
extern int rl_key_sequence_length;
extern int rl_byte_oriented;
/* display.c */
extern int rl_display_fixed;
@ -65,18 +66,9 @@ extern int rl_blink_matching_paren;
* *
*************************************************************************/
/* bind.c */
extern char *rl_untranslate_keyseq PARAMS((int));
/* kill.c */
extern int rl_set_retained_kills PARAMS((int));
/* readline.c */
extern int rl_discard_argument PARAMS((void));
/* rltty.c */
extern int rl_stop_output PARAMS((int, int));
/* terminal.c */
extern void _rl_set_screen_size PARAMS((int, int));
@ -113,6 +105,10 @@ extern int readline_internal_char PARAMS((void));
/* bind.c */
extern void _rl_bind_if_unbound PARAMS((const char *, rl_command_func_t *));
/* complete.c */
extern char _rl_find_completion_word PARAMS((int *, int *));
extern void _rl_free_match_list PARAMS((char **));
/* display.c */
extern char *_rl_strip_prompt PARAMS((char *));
extern void _rl_move_cursor_relative PARAMS((int, const char *));
@ -132,7 +128,9 @@ extern int _rl_current_display_line PARAMS((void));
/* input.c */
extern int _rl_any_typein PARAMS((void));
extern int _rl_input_available PARAMS((void));
extern int _rl_input_queued PARAMS((int));
extern void _rl_insert_typein PARAMS((int));
extern int _rl_unget_char PARAMS((int));
/* macro.c */
extern void _rl_with_macro_input PARAMS((char *));
@ -142,6 +140,12 @@ extern void _rl_pop_executing_macro PARAMS((void));
extern void _rl_add_macro_char PARAMS((int));
extern void _rl_kill_kbd_macro PARAMS((void));
/* misc.c */
extern int _rl_init_argument PARAMS((void));
extern void _rl_start_using_history PARAMS((void));
extern int _rl_free_saved_history_line PARAMS((void));
extern void _rl_set_insert_mode PARAMS((int, int));
/* nls.c */
extern int _rl_init_eightbit PARAMS((void));
@ -152,12 +156,7 @@ extern void _rl_enable_paren_matching PARAMS((int));
extern void _rl_init_line_state PARAMS((void));
extern void _rl_set_the_line PARAMS((void));
extern int _rl_dispatch PARAMS((int, Keymap));
extern int _rl_init_argument PARAMS((void));
extern void _rl_fix_point PARAMS((int));
extern void _rl_replace_text PARAMS((const char *, int, int));
extern int _rl_char_search_internal PARAMS((int, int, int));
extern int _rl_set_mark_at_pos PARAMS((int));
extern int _rl_free_saved_history_line PARAMS((void));
extern int _rl_dispatch_subseq PARAMS((int, Keymap, int));
/* rltty.c */
extern int _rl_disable_tty_signals PARAMS((void));
@ -175,9 +174,23 @@ extern void _rl_output_some_chars PARAMS((const char *, int));
extern int _rl_backspace PARAMS((int));
extern void _rl_enable_meta_key PARAMS((void));
extern void _rl_control_keypad PARAMS((int));
extern void _rl_set_cursor PARAMS((int, int));
/* text.c */
extern void _rl_fix_point PARAMS((int));
extern int _rl_replace_text PARAMS((const char *, int, int));
extern int _rl_insert_char PARAMS((int, int));
extern int _rl_overwrite_char PARAMS((int, int));
extern int _rl_overwrite_rubout PARAMS((int, int));
extern int _rl_rubout_char PARAMS((int, int));
#if defined (HANDLE_MULTIBYTE)
extern int _rl_char_search_internal PARAMS((int, int, char *, int));
#else
extern int _rl_char_search_internal PARAMS((int, int, int));
#endif
extern int _rl_set_mark_at_pos PARAMS((int));
/* util.c */
extern int rl_alphabetic PARAMS((int));
extern int _rl_abort_internal PARAMS((void));
extern char *_rl_strindex PARAMS((const char *, const char *));
extern int _rl_qsort_string_compare PARAMS((char **, char **));
@ -207,9 +220,11 @@ extern const char *_rl_possible_meta_prefixes[];
/* complete.c */
extern int _rl_complete_show_all;
extern int _rl_complete_mark_directories;
extern int _rl_complete_mark_symlink_dirs;
extern int _rl_print_completions_horizontally;
extern int _rl_completion_case_fold;
extern int _rl_match_hidden_files;
extern int _rl_page_completions;
/* display.c */
extern int _rl_vis_botlin;
@ -221,9 +236,12 @@ extern char *rl_display_prompt;
extern char *_rl_isearch_terminators;
/* macro.c */
extern int _rl_defining_kbd_macro;
extern char *_rl_executing_macro;
/* misc.c */
extern int _rl_history_preserve_point;
extern int _rl_history_saved_point;
/* readline.c */
extern int _rl_horizontal_scroll_mode;
extern int _rl_mark_modified_lines;
@ -231,7 +249,6 @@ extern int _rl_bell_preference;
extern int _rl_meta_flag;
extern int _rl_convert_meta_chars_to_ascii;
extern int _rl_output_meta_chars;
extern int _rl_history_preserve_point;
extern char *_rl_comment_begin;
extern unsigned char _rl_parsing_conditionalized_out;
extern Keymap _rl_keymap;

View file

@ -647,7 +647,6 @@ rl_prep_terminal (meta_flag)
if (get_tty_settings (tty, &tio) < 0)
{
release_sigint ();
fprintf(stderr, "readline: warning: rl_prep_terminal: cannot get terminal settings");
return;
}

View file

@ -40,6 +40,8 @@
#endif
#include "rldefs.h"
#include "rlmbutil.h"
#include "readline.h"
#include "history.h"
@ -80,15 +82,8 @@ static void
make_history_line_current (entry)
HIST_ENTRY *entry;
{
int line_len;
line_len = strlen (entry->line);
if (line_len >= rl_line_buffer_len)
rl_extend_line_buffer (line_len);
strcpy (rl_line_buffer, entry->line);
rl_replace_line (entry->line, 0);
rl_undo_list = (UNDO_LIST *)entry->data;
rl_end = line_len;
if (_rl_saved_line_for_history)
_rl_free_history_entry (_rl_saved_line_for_history);
@ -169,6 +164,8 @@ noninc_dosearch (string, dir)
make_history_line_current (entry);
rl_point = 0;
rl_mark = rl_end;
rl_clear_message ();
}
@ -182,11 +179,15 @@ noninc_search (dir, pchar)
int dir;
int pchar;
{
int saved_point, c;
int saved_point, saved_mark, c;
char *p;
#if defined (HANDLE_MULTIBYTE)
char mb[MB_LEN_MAX];
#endif
rl_maybe_save_line ();
saved_point = rl_point;
saved_mark = rl_mark;
/* Use the line buffer to read the search string. */
rl_line_buffer[0] = 0;
@ -206,6 +207,11 @@ noninc_search (dir, pchar)
c = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
c = _rl_read_mbstring (c, mb, MB_LEN_MAX);
#endif
if (c == 0)
break;
@ -218,9 +224,10 @@ noninc_search (dir, pchar)
rl_maybe_unsave_line ();
rl_clear_message ();
rl_point = saved_point;
rl_mark = saved_mark;
SEARCH_RETURN;
}
rl_rubout (1, c);
_rl_rubout_char (1, c);
break;
case CTRL('W'):
@ -242,17 +249,25 @@ noninc_search (dir, pchar)
rl_maybe_unsave_line ();
rl_clear_message ();
rl_point = saved_point;
rl_mark = saved_mark;
rl_ding ();
SEARCH_RETURN;
default:
rl_insert (1, c);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
rl_insert_text (mb);
else
#endif
_rl_insert_char (1, c);
break;
}
(*rl_redisplay_function) ();
}
dosearch:
rl_mark = saved_mark;
/* If rl_point == 0, we want to re-use the previous search string and
start from the saved history position. If there's no previous search
string, punt. */
@ -373,9 +388,11 @@ rl_history_search_internal (count, dir)
{
rl_point = rl_end = rl_history_search_len;
rl_line_buffer[rl_end] = '\0';
rl_mark = 0;
}
#else
rl_point = rl_history_search_len; /* rl_maybe_unsave_line changes it */
rl_mark = rl_end;
#endif
return 1;
}
@ -384,6 +401,8 @@ rl_history_search_internal (count, dir)
make_history_line_current (temp);
rl_point = rl_history_search_len;
rl_mark = rl_end;
return 0;
}

View file

@ -74,6 +74,7 @@ typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt
#endif /* !HAVE_POSIX_SIGNALS */
static SigHandler *rl_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
static void rl_maybe_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
/* Exported variables for use by applications. */
@ -232,7 +233,7 @@ rl_set_sighandler (sig, handler, ohandler)
struct sigaction act;
act.sa_handler = handler;
act.sa_flags = 0;
act.sa_flags = 0; /* XXX - should we set SA_RESTART for SIGWINCH? */
sigemptyset (&act.sa_mask);
sigemptyset (&ohandler->sa_mask);
sigaction (sig, &act, &old_handler);

View file

@ -66,6 +66,9 @@
#include "rlshell.h"
#include "xmalloc.h"
#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay)
#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc)
/* **************************************************************** */
/* */
/* Terminal and Termcap */
@ -140,6 +143,16 @@ static char *_rl_term_ke;
/* The key sequences sent by the Home and End keys, if any. */
static char *_rl_term_kh;
static char *_rl_term_kH;
static char *_rl_term_at7; /* @7 */
/* Insert key */
static char *_rl_term_kI;
/* Cursor control */
static char *_rl_term_vs; /* very visible */
static char *_rl_term_ve; /* normal */
static void bind_termcap_arrow_keys PARAMS((Keymap));
/* Variables that hold the screen dimensions, used by the display code. */
int _rl_screenwidth, _rl_screenheight, _rl_screenchars;
@ -274,7 +287,10 @@ rl_resize_terminal ()
if (readline_echoing_p)
{
_rl_get_screen_size (fileno (rl_instream), 1);
_rl_redisplay_after_sigwinch ();
if (CUSTOM_REDISPLAY_FUNC ())
rl_forced_update_display ();
else
_rl_redisplay_after_sigwinch ();
}
}
@ -287,6 +303,7 @@ struct _tc_string {
search algorithm to something smarter. */
static struct _tc_string tc_strings[] =
{
{ "@7", &_rl_term_at7 },
{ "DC", &_rl_term_DC },
{ "IC", &_rl_term_IC },
{ "ce", &_rl_term_clreol },
@ -296,14 +313,15 @@ static struct _tc_string tc_strings[] =
{ "ei", &_rl_term_ei },
{ "ic", &_rl_term_ic },
{ "im", &_rl_term_im },
{ "kH", &_rl_term_kH }, /* home down ?? */
{ "kI", &_rl_term_kI }, /* insert */
{ "kd", &_rl_term_kd },
{ "ke", &_rl_term_ke }, /* end keypad mode */
{ "kh", &_rl_term_kh }, /* home */
{ "kH", &_rl_term_kH }, /* end */
{ "kl", &_rl_term_kl },
{ "kr", &_rl_term_kr },
{ "ks", &_rl_term_ks }, /* start keypad mode */
{ "ku", &_rl_term_ku },
{ "ks", &_rl_term_ks },
{ "ke", &_rl_term_ke },
{ "le", &_rl_term_backspace },
{ "mm", &_rl_term_mm },
{ "mo", &_rl_term_mo },
@ -313,6 +331,8 @@ static struct _tc_string tc_strings[] =
{ "pc", &_rl_term_pc },
{ "up", &_rl_term_up },
{ "vb", &_rl_visible_bell },
{ "vs", &_rl_term_vs },
{ "ve", &_rl_term_ve },
};
#define NUM_TC_STRINGS (sizeof (tc_strings) / sizeof (struct _tc_string))
@ -336,9 +356,6 @@ get_term_capabilities (bp)
tcap_initialized = 1;
}
#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay)
#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc)
int
_rl_init_terminal_io (terminal_name)
const char *terminal_name;
@ -346,7 +363,6 @@ _rl_init_terminal_io (terminal_name)
const char *term;
char *buffer;
int tty, tgetent_ret;
Keymap xkeymap;
term = terminal_name ? terminal_name : sh_get_env_value ("TERM");
_rl_term_clrpag = _rl_term_cr = _rl_term_clreol = (char *)NULL;
@ -404,7 +420,10 @@ _rl_init_terminal_io (terminal_name)
_rl_term_im = _rl_term_ei = _rl_term_ic = _rl_term_IC = (char *)NULL;
_rl_term_up = _rl_term_dc = _rl_term_DC = _rl_visible_bell = (char *)NULL;
_rl_term_ku = _rl_term_kd = _rl_term_kl = _rl_term_kr = (char *)NULL;
_rl_term_kh = _rl_term_kH = _rl_term_kI = (char *)NULL;
_rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL;
_rl_term_mm = _rl_term_mo = (char *)NULL;
_rl_term_ve = _rl_term_vs = (char *)NULL;
#if defined (HACK_TERMCAP_MOTION)
term_forward_char = (char *)NULL;
#endif
@ -449,31 +468,36 @@ _rl_init_terminal_io (terminal_name)
/* Attempt to find and bind the arrow keys. Do not override already
bound keys in an overzealous attempt, however. */
xkeymap = _rl_keymap;
_rl_keymap = emacs_standard_keymap;
_rl_bind_if_unbound (_rl_term_ku, rl_get_previous_history);
_rl_bind_if_unbound (_rl_term_kd, rl_get_next_history);
_rl_bind_if_unbound (_rl_term_kr, rl_forward);
_rl_bind_if_unbound (_rl_term_kl, rl_backward);
_rl_bind_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */
_rl_bind_if_unbound (_rl_term_kH, rl_end_of_line); /* End */
bind_termcap_arrow_keys (emacs_standard_keymap);
#if defined (VI_MODE)
_rl_keymap = vi_movement_keymap;
bind_termcap_arrow_keys (vi_movement_keymap);
bind_termcap_arrow_keys (vi_insertion_keymap);
#endif /* VI_MODE */
return 0;
}
/* Bind the arrow key sequences from the termcap description in MAP. */
static void
bind_termcap_arrow_keys (map)
Keymap map;
{
Keymap xkeymap;
xkeymap = _rl_keymap;
_rl_keymap = map;
_rl_bind_if_unbound (_rl_term_ku, rl_get_previous_history);
_rl_bind_if_unbound (_rl_term_kd, rl_get_next_history);
_rl_bind_if_unbound (_rl_term_kr, rl_forward);
_rl_bind_if_unbound (_rl_term_kl, rl_backward);
_rl_bind_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */
_rl_bind_if_unbound (_rl_term_kH, rl_end_of_line); /* End */
#endif /* VI_MODE */
_rl_bind_if_unbound (_rl_term_at7, rl_end_of_line); /* End */
_rl_keymap = xkeymap;
return 0;
}
char *
@ -610,3 +634,29 @@ _rl_control_keypad (on)
tputs (_rl_term_ke, 1, _rl_output_character_function);
#endif
}
/* **************************************************************** */
/* */
/* Controlling the Cursor */
/* */
/* **************************************************************** */
/* Set the cursor appropriately depending on IM, which is one of the
insert modes (insert or overwrite). Insert mode gets the normal
cursor. Overwrite mode gets a very visible cursor. Only does
anything if we have both capabilities. */
void
_rl_set_cursor (im, force)
int im, force;
{
if (_rl_term_ve && _rl_term_vs)
{
if (force || im != rl_insert_mode)
{
if (im == RL_IM_OVERWRITE)
tputs (_rl_term_vs, 1, _rl_output_character_function);
else
tputs (_rl_term_ve, 1, _rl_output_character_function);
}
}
}

1540
lib/readline/text.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -59,9 +59,6 @@ extern struct passwd *getpwnam PARAMS((const char *));
#endif /* !HAVE_GETPW_DECLS */
#if !defined (savestring)
# ifndef strcpy
extern char *strcpy ();
# endif
#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
#endif /* !savestring */

View file

@ -24,10 +24,6 @@
#if !defined (_TILDE_H_)
# define _TILDE_H_
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -50,8 +50,6 @@
#include "rlprivate.h"
#include "xmalloc.h"
#define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0)
/* Non-zero tells rl_delete_text and rl_insert_text to not add to
the undo list. */
int _rl_doing_an_undo = 0;

View file

@ -55,8 +55,6 @@
#include "rlprivate.h"
#include "xmalloc.h"
#define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0)
/* **************************************************************** */
/* */
/* Utility Functions */
@ -89,7 +87,7 @@ _rl_abort_internal ()
_rl_init_argument ();
rl_clear_pending_input ();
_rl_defining_kbd_macro = 0;
RL_UNSETSTATE (RL_STATE_MACRODEF);
while (rl_executing_macro)
_rl_pop_executing_macro ();
@ -233,6 +231,12 @@ _rl_strpbrk (string1, string2)
const char *string1, *string2;
{
register const char *scan;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps;
register int i, v;
memset (&ps, 0, sizeof (mbstate_t));
#endif
for (; *string1; string1++)
{
@ -241,6 +245,14 @@ _rl_strpbrk (string1, string2)
if (*string1 == *scan)
return ((char *)string1);
}
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
v = _rl_get_char_len (string1, &ps);
if (v > 1)
string += v - 1; /* -1 to account for auto-increment in loop */
}
#endif
}
return ((char *)NULL);
}

View file

@ -41,7 +41,7 @@ KEYMAP_ENTRY_ARRAY vi_movement_keymap = {
{ ISFUNC, rl_emacs_editing_mode }, /* Control-e */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Control-f */
{ ISFUNC, rl_abort }, /* Control-g */
{ ISFUNC, rl_backward }, /* Control-h */
{ ISFUNC, rl_backward_char }, /* Control-h */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Control-i */
{ ISFUNC, rl_newline }, /* Control-j */
{ ISFUNC, rl_kill_line }, /* Control-k */
@ -68,7 +68,7 @@ KEYMAP_ENTRY_ARRAY vi_movement_keymap = {
{ ISFUNC, rl_vi_undo }, /* Control-_ */
/* The start of printing characters. */
{ ISFUNC, rl_forward }, /* SPACE */
{ ISFUNC, rl_forward_char }, /* SPACE */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* ! */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* " */
{ ISFUNC, rl_insert_comment }, /* # */
@ -150,11 +150,11 @@ KEYMAP_ENTRY_ARRAY vi_movement_keymap = {
{ ISFUNC, rl_vi_end_word }, /* e */
{ ISFUNC, rl_vi_char_search }, /* f */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* g */
{ ISFUNC, rl_backward }, /* h */
{ ISFUNC, rl_backward_char }, /* h */
{ ISFUNC, rl_vi_insertion_mode }, /* i */
{ ISFUNC, rl_get_next_history }, /* j */
{ ISFUNC, rl_get_previous_history }, /* k */
{ ISFUNC, rl_forward }, /* l */
{ ISFUNC, rl_forward_char }, /* l */
{ ISFUNC, rl_vi_set_mark }, /* m */
{ ISFUNC, rl_vi_search_again }, /* n */
{ ISFUNC, (rl_command_func_t *)0x0 }, /* o */

View file

@ -51,6 +51,8 @@
/* Some standard library routines. */
#include "rldefs.h"
#include "rlmbutil.h"
#include "readline.h"
#include "history.h"
@ -61,10 +63,6 @@
#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
#endif
#ifndef exchange
#define exchange(x, y) do {int temp = x; x = y; y = temp;} while (0)
#endif
/* Non-zero means enter insertion mode. */
static int _rl_vi_doing_insert;
@ -89,7 +87,11 @@ static int _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */
static int _rl_vi_last_repeat = 1;
static int _rl_vi_last_arg_sign = 1;
static int _rl_vi_last_motion;
#if defined (HANDLE_MULTIBYTE)
static char _rl_vi_last_search_mbchar[MB_LEN_MAX];
#else
static int _rl_vi_last_search_char;
#endif
static int _rl_vi_last_replacement;
static int _rl_vi_last_key_before_insert;
@ -158,12 +160,15 @@ int
rl_vi_redo (count, c)
int count, c;
{
int r;
if (!rl_explicit_arg)
{
rl_numeric_arg = _rl_vi_last_repeat;
rl_arg_sign = _rl_vi_last_arg_sign;
}
r = 0;
vi_redoing = 1;
/* If we're redoing an insert with `i', stuff in the inserted text
and do not go into insertion mode. */
@ -175,10 +180,10 @@ rl_vi_redo (count, c)
rl_point--;
}
else
_rl_dispatch (_rl_vi_last_command, _rl_keymap);
r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
vi_redoing = 0;
return (0);
return (r);
}
/* A placeholder for further expansion. */
@ -547,7 +552,17 @@ rl_vi_append_mode (count, key)
int count, key;
{
if (rl_point < rl_end)
rl_point++;
{
if (MB_CUR_MAX == 1 || rl_byte_oriented)
rl_point++;
else
{
int point = rl_point;
rl_forward_char (1, key);
if (point == rl_point)
rl_point = rl_end;
}
}
rl_vi_insertion_mode (1, key);
return (0);
}
@ -612,6 +627,7 @@ _rl_vi_done_inserting ()
{
if (_rl_vi_doing_insert)
{
/* The `C', `s', and `S' commands set this. */
rl_end_undo_group ();
/* Now, the text between rl_undo_list->next->start and
rl_undo_list->next->end is what was inserted while in insert
@ -640,7 +656,7 @@ rl_vi_movement_mode (count, key)
int count, key;
{
if (rl_point > 0)
rl_backward (1, key);
rl_backward_char (1, key);
_rl_keymap = vi_movement_keymap;
_rl_vi_done_inserting ();
@ -657,6 +673,51 @@ rl_vi_arg_digit (count, c)
return (rl_digit_argument (count, c));
}
/* Change the case of the next COUNT characters. */
#if defined (HANDLE_MULTIBYTE)
static int
_rl_vi_change_mbchar_case (count)
int count;
{
wchar_t wc;
char mb[MB_LEN_MAX];
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t));
if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
count--;
while (count-- && rl_point < rl_end)
{
mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
if (iswupper (wc))
wc = towlower (wc);
else if (iswlower (wc))
wc = towupper (wc);
else
{
/* Just skip over chars neither upper nor lower case */
rl_forward_char (1, 0);
continue;
}
/* Vi is kind of strange here. */
if (wc)
{
wctomb (mb, wc);
rl_begin_undo_group ();
rl_delete (1, 0);
rl_insert_text (mb);
rl_end_undo_group ();
rl_vi_check ();
}
else
rl_forward_char (1, 0);
}
return 0;
}
#endif
int
rl_vi_change_case (count, ignore)
int count, ignore;
@ -667,6 +728,11 @@ rl_vi_change_case (count, ignore)
if (rl_point >= rl_end)
return (0);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
return (_rl_vi_change_mbchar_case (count));
#endif
while (count-- && rl_point < rl_end)
{
if (_rl_uppercase_p (rl_line_buffer[rl_point]))
@ -676,7 +742,7 @@ rl_vi_change_case (count, ignore)
else
{
/* Just skip over characters neither upper nor lower case. */
rl_forward (1, c);
rl_forward_char (1, c);
continue;
}
@ -685,12 +751,12 @@ rl_vi_change_case (count, ignore)
{
rl_begin_undo_group ();
rl_delete (1, c);
rl_insert (1, c);
_rl_insert_char (1, c);
rl_end_undo_group ();
rl_vi_check ();
}
else
rl_forward (1, c);
rl_forward_char (1, c);
}
return (0);
}
@ -700,10 +766,10 @@ rl_vi_put (count, key)
int count, key;
{
if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
rl_point++;
rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
rl_yank (1, key);
rl_backward (1, key);
rl_backward_char (1, key);
return (0);
}
@ -711,7 +777,12 @@ int
rl_vi_check ()
{
if (rl_point && rl_point == rl_end)
rl_point--;
{
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
else
rl_point--;
}
return (0);
}
@ -813,7 +884,7 @@ rl_vi_domove (key, nextkey)
}
if (rl_mark < rl_point)
exchange (rl_point, rl_mark);
SWAP (rl_point, rl_mark);
return (0);
}
@ -991,7 +1062,10 @@ rl_vi_delete (count, key)
return -1;
}
end = rl_point + count;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
else
end = rl_point + count;
if (end >= rl_end)
end = rl_end;
@ -999,7 +1073,7 @@ rl_vi_delete (count, key)
rl_kill_text (rl_point, end);
if (rl_point > 0 && rl_point == rl_end)
rl_backward (1, key);
rl_backward_char (1, key);
return (0);
}
@ -1024,7 +1098,12 @@ int
rl_vi_char_search (count, key)
int count, key;
{
#if defined (HANDLE_MULTIBYTE)
static char *target;
static int mb_len;
#else
static char target;
#endif
static int orig_dir, dir;
if (key == ';' || key == ',')
@ -1032,12 +1111,21 @@ rl_vi_char_search (count, key)
else
{
if (vi_redoing)
#if defined (HANDLE_MULTIBYTE)
target = _rl_vi_last_search_mbchar;
#else
target = _rl_vi_last_search_char;
#endif
else
{
#if defined (HANDLE_MULTIBYTE)
mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
target = _rl_vi_last_search_mbchar;
#else
RL_SETSTATE(RL_STATE_MOREINPUT);
_rl_vi_last_search_char = target = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
#endif
}
switch (key)
@ -1060,7 +1148,11 @@ rl_vi_char_search (count, key)
}
}
#if defined (HANDLE_MULTIBYTE)
return (_rl_char_search_internal (count, dir, target, mb_len));
#else
return (_rl_char_search_internal (count, dir, target));
#endif
}
/* Match brackets */
@ -1068,14 +1160,25 @@ int
rl_vi_match (ignore, key)
int ignore, key;
{
int count = 1, brack, pos;
int count = 1, brack, pos, tmp, pre;
pos = rl_point;
if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
{
while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
rl_point < rl_end - 1)
rl_forward (1, key);
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
{
pre = rl_point;
rl_forward_char (1, key);
if (pre == rl_point)
break;
}
}
else
while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
rl_point < rl_end - 1)
rl_forward_char (1, key);
if (brack <= 0)
{
@ -1091,7 +1194,16 @@ rl_vi_match (ignore, key)
{
while (count)
{
if (--pos >= 0)
tmp = pos;
if (MB_CUR_MAX == 1 || rl_byte_oriented)
pos--;
else
{
pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
if (tmp == pos)
pos--;
}
if (pos >= 0)
{
int b = rl_vi_bracktype (rl_line_buffer[pos]);
if (b == -brack)
@ -1110,7 +1222,12 @@ rl_vi_match (ignore, key)
{ /* brack > 0 */
while (count)
{
if (++pos < rl_end)
if (MB_CUR_MAX == 1 || rl_byte_oriented)
pos++;
else
pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
if (pos < rl_end)
{
int b = rl_vi_bracktype (rl_line_buffer[pos]);
if (b == -brack)
@ -1145,6 +1262,11 @@ rl_vi_bracktype (c)
}
}
/* XXX - think about reading an entire mbchar with _rl_read_mbchar and
inserting it in one bunch instead of the loop below (like in
rl_vi_char_search or _rl_vi_change_mbchar_case. Set c to mbchar[0]
for test against 033 or ^C. Make sure that _rl_read_mbchar does
this right. */
int
rl_vi_change_char (count, key)
int count, key;
@ -1168,9 +1290,19 @@ rl_vi_change_char (count, key)
rl_begin_undo_group ();
rl_delete (1, c);
rl_insert (1, c);
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
while (_rl_insert_char (1, c))
{
RL_SETSTATE (RL_STATE_MOREINPUT);
c = rl_read_key ();
RL_UNSETSTATE (RL_STATE_MOREINPUT);
}
else
#endif
_rl_insert_char (1, c);
if (count == 0)
rl_backward (1, c);
rl_backward_char (1, c);
rl_end_undo_group ();
}
@ -1181,66 +1313,29 @@ int
rl_vi_subst (count, key)
int count, key;
{
rl_begin_undo_group ();
/* If we are redoing, rl_vi_change_to will stuff the last motion char */
if (vi_redoing == 0)
rl_stuff_char ((key == 'S') ? 'c' : ' '); /* `S' == `cc', `s' == `c ' */
if (_rl_uppercase_p (key))
{
rl_beg_of_line (1, key);
rl_kill_line (1, key);
}
else
rl_delete_text (rl_point, rl_point+count);
rl_end_undo_group ();
_rl_vi_set_last (key, count, rl_arg_sign);
if (vi_redoing)
{
int o = _rl_doing_an_undo;
_rl_doing_an_undo = 1;
if (vi_insert_buffer && *vi_insert_buffer)
rl_insert_text (vi_insert_buffer);
_rl_doing_an_undo = o;
}
else
{
rl_begin_undo_group ();
_rl_vi_doing_insert = 1;
rl_vi_insertion_mode (1, key);
}
return (0);
return (rl_vi_change_to (count, 'c'));
}
int
rl_vi_overstrike (count, key)
int count, key;
{
int i;
if (_rl_vi_doing_insert == 0)
{
_rl_vi_doing_insert = 1;
rl_begin_undo_group ();
}
for (i = 0; i < count; i++)
if (count > 0)
{
vi_replace_count++;
rl_begin_undo_group ();
if (rl_point < rl_end)
{
rl_delete (1, key);
rl_insert (1, key);
}
else
rl_insert (1, key);
rl_end_undo_group ();
_rl_overwrite_char (count, key);
vi_replace_count += count;
}
return (0);
}
@ -1263,7 +1358,7 @@ rl_vi_overstrike_delete (count, key)
vi_replace_count--;
if (rl_point == s)
rl_backward (1, key);
rl_backward_char (1, key);
}
if (vi_replace_count == 0 && _rl_vi_doing_insert)

View file

@ -2,7 +2,7 @@
# Makefile for the Bash library
#
#
# Copyright (C) 1998 Free Software Foundation, Inc.
# Copyright (C) 1998-2002 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -68,10 +68,12 @@ LIBRARY_NAME = libsh.a
CSOURCES = clktck.c clock.c getcwd.c getenv.c oslib.c setlinebuf.c \
strcasecmp.c strerror.c strtod.c strtol.c strtoul.c \
vprint.c itos.c rename.c zread.c zwrite.c shtty.c \
inet_aton.c netopen.c strpbrk.c timeval.c makepath.c pathcanon.c \
pathphys.c tmpfile.c stringlist.c stringvec.c spell.c \
shquote.c strtrans.c strindex.c snprintf.c mailstat.c fmtulong.c \
fmtullong.c strtoll.c strtoull.c strtoimax.c strtoumax.c
inet_aton.c netconn.c netopen.c strpbrk.c timeval.c makepath.c \
pathcanon.c pathphys.c tmpfile.c stringlist.c stringvec.c spell.c \
shquote.c strtrans.c strindex.c snprintf.c mailstat.c \
fmtulong.c fmtullong.c fmtumax.c \
strtoll.c strtoull.c strtoimax.c strtoumax.c memset.c \
mktime.c strftime.c xstrchr.c zcatfd.c
# The header files for this library.
HSOURCES =
@ -80,10 +82,10 @@ HSOURCES =
LIBOBJS = @LIBOBJS@
OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o \
itos.o zread.o zwrite.o shtty.o \
netopen.o timeval.o makepath.o pathcanon.o \
netconn.o netopen.o timeval.o makepath.o pathcanon.o \
pathphys.o tmpfile.o stringlist.o stringvec.o spell.o shquote.o \
strtrans.o strindex.o snprintf.o mailstat.o fmtulong.o \
fmtullong.o ${LIBOBJS}
fmtullong.o fmtumax.o xstrchr.o zcatfd.o ${LIBOBJS}
SUPPORT = Makefile
@ -116,12 +118,16 @@ clktck.o: clktck.c
clock.o: clock.c
fmtullong.o: fmtullong.c
fmtulong.o: fmtulong.c
fmtumax.o: fmtumax.c
getcwd.o: getcwd.c
getenv.o: getenv.c
inet_aton.o: inet_aton.c
itos.o: itos.c
mailstat.o: mailstat.c
makepath.o: makepath.c
memset.o: memset.c
mktime.o: mktime.c
netconn.o: netconn.c
netopen.o: netopen.c
oslib.o: oslib.c
pathcanon.o: pathcanon.c
@ -134,6 +140,7 @@ snprintf.o: snprintf.c
spell.o: spell.c
strcasecmp.o: strcasecmp.c
strerror.o: strerror.c
strftime.o: strftime.c
strindex.o: strindex.c
stringlist.o: stringlist.c
stringvec.o: stringvec.c
@ -150,11 +157,14 @@ times.o: times.c
timeval.o: timeval.c
tmpfile.o: tmpfile.c
vprint.o: vprint.c
xstrchr.o: xstrchr.c
zcatfd.o: zcatfd.c
zread.o: zread.c
zwrite.o: zwrite.c
# dependencies for c files that include other c files
fmtullong.o: fmtulong.c
fmtumax.o: fmtulong.c
strtoll.o: strtol.c
strtoul.o: strtol.c
strtoull.o: strtol.c
@ -164,12 +174,16 @@ clktck.o: ${BUILD_DIR}/config.h
clock.o: ${BUILD_DIR}/config.h
fmtullong.o: ${BUILD_DIR}/config.h
fmtulong.o: ${BUILD_DIR}/config.h
fmtumax.o: ${BUILD_DIR}/config.h
getcwd.o: ${BUILD_DIR}/config.h
getenv.o: ${BUILD_DIR}/config.h
inet_aton.o: ${BUILD_DIR}/config.h
itos.o: ${BUILD_DIR}/config.h
mailstat.o: ${BUILD_DIR}/config.h
makepath.o: ${BUILD_DIR}/config.h
memset.o: ${BUILD_DIR}/config.h
mktime.o: ${BUILD_DIR}/config.h
netconn.o: ${BUILD_DIR}/config.h
netopen.o: ${BUILD_DIR}/config.h
oslib.o: ${BUILD_DIR}/config.h
pathcanon.o: ${BUILD_DIR}/config.h
@ -182,6 +196,7 @@ snprintf.o: ${BUILD_DIR}/config.h
spell.o: ${BUILD_DIR}/config.h
strcasecmp.o: ${BUILD_DIR}/config.h
strerror.o: ${BUILD_DIR}/config.h
strftime.o: ${BUILD_DIR}/config.h
strindex.o: ${BUILD_DIR}/config.h
stringlist.o: ${BUILD_DIR}/config.h
stringvec.o: ${BUILD_DIR}/config.h
@ -198,6 +213,8 @@ times.o: ${BUILD_DIR}/config.h
timeval.o: ${BUILD_DIR}/config.h
tmpfile.o: ${BUILD_DIR}/config.h
vprint.o: ${BUILD_DIR}/config.h
xstrchr.o: ${BUILD_DIR}/config.h
zcatfd.o: ${BUILD_DIR}/config.h
zread.o: ${BUILD_DIR}/config.h
zwrite.o: ${BUILD_DIR}/config.h
@ -237,6 +254,9 @@ makepath.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h
makepath.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h
makepath.o: ${topdir}/pathnames.h ${topdir}/externs.h
netconn.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
netconn.o: ${topdir}/bashtypes.h
netopen.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h
netopen.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h
netopen.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h
@ -280,6 +300,7 @@ pathphys.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h
pathphys.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
rename.o: ${topdir}/bashtypes.h ${BASHINCDIR}/stdc.h
rename.o: ${BASHINCDIR}/posixstat.h
setlinebuf.o: ${topdir}/xmalloc.h ${topdir}/bashansi.h
setlinebuf.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/stdc.h
@ -395,3 +416,13 @@ fmtullong.o: ${BASHINCDIR}/ansi_stdlib.h
fmtullong.o: ${BASHINCDIR}/chartypes.h
fmtullong.o: ${BASHINCDIR}/stdc.h
fmtullong.o: ${BASHINCDIR}/typemax.h
fmtumax.o: ${topdir}/bashansi.h
fmtumax.o: ${BASHINCDIR}/ansi_stdlib.h
fmtumax.o: ${BASHINCDIR}/chartypes.h
fmtumax.o: ${BASHINCDIR}/stdc.h
fmtumax.o: ${BASHINCDIR}/typemax.h
xstrchr.o: ${topdir}/bashansi.h
xstrchr.o: ${BASHINCDIR}/ansi_stdlib.h
xstrchr.o: ${BASHINCDIR}/shmbutil.h

View file

@ -27,6 +27,10 @@
# include <unistd.h>
#endif
#if defined (HAVE_LIMITS_H)
# include <limits.h>
#endif
#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
# if !defined (CLK_TCK)
# if defined (HZ)

View file

@ -1,4 +1,6 @@
/* Copyright (C) 2001 Free Software Foundation, Inc.
/* fmtullong.c - convert `long long int' to string */
/* Copyright (C) 2001-2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@ -18,7 +20,10 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifdef HAVE_LONG_LONG
#define QUAD 1
#define LONG long long
#define UNSIGNED_LONG unsigned long long
#define fmtulong fmtullong
#include "fmtulong.c"
#endif

View file

@ -1,6 +1,6 @@
/* fmtulong.c -- Convert unsigned long int to string. */
/* Copyright (C) 1998, Free Software Foundation, Inc.
/* Copyright (C) 1998-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -67,20 +67,9 @@ extern int errno;
# define FL_UNSIGNED 0x08 /* don't add any sign */
#endif
#ifdef QUAD
/* fmtullong */
# define LONG long long
# define FMTUL_LONG_MAX LLONG_MAX
# define FMTUL_ULONG_MAX ULLONG_MAX
#else
#ifndef LONG
# define LONG long
# define FMTUL_LONG_MAX LONG_MAX
# define FMTUL_ULONG_MAX ULONG_MAX
#endif
/* Set the name */
#ifdef QUAD
# define fmtulong fmtullong
# define UNSIGNED_LONG unsigned long
#endif
/* `unsigned long' (or unsigned long long) to string conversion for a given
@ -88,7 +77,7 @@ extern int errno;
check for buffer underflow, but currently does not. */
char *
fmtulong (ui, base, buf, len, flags)
unsigned LONG ui;
UNSIGNED_LONG ui;
int base;
char *buf;
size_t len;
@ -134,7 +123,7 @@ fmtulong (ui, base, buf, len, flags)
}
/* Favor signed arithmetic over unsigned arithmetic; it is faster on
many machines. */
if (ui > FMTUL_LONG_MAX)
if ((LONG)ui < 0)
{
*p-- = TOCHAR (ui % 10);
si = ui / 10;

25
lib/sh/fmtumax.c Normal file
View file

@ -0,0 +1,25 @@
/* fmtumax.c -- Convert uintmax_t to string. */
/* Copyright (C) 2002 Free Software Foundation, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include <config.h>
#define LONG intmax_t
#define UNSIGNED_LONG uintmax_t
#define fmtulong fmtumax
#include "fmtulong.c"

View file

@ -1,7 +1,7 @@
/* getenv.c - get environment variable value from the shell's variable
list. */
/* Copyright (C) 1997 Free Software Foundation, Inc.
/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -28,8 +28,13 @@
#endif
#include <bashansi.h>
#include <errno.h>
#include <shell.h>
#ifndef errno
extern int errno;
#endif
extern char **environ;
/* We supply our own version of getenv () because we want library
@ -43,21 +48,19 @@ static char *last_tempenv_value = (char *)NULL;
char *
getenv (name)
#if defined (__linux__) || defined (__bsdi__) || defined (convex)
const char *name;
#else
char const *name;
#endif /* !__linux__ && !__bsdi__ && !convex */
{
SHELL_VAR *var;
if (name == 0 || *name == '\0')
return ((char *)NULL);
var = find_tempenv_variable ((char *)name);
if (var)
{
FREE (last_tempenv_value);
last_tempenv_value = value_cell (var) ? savestring (value_cell (var)) : (char *)NULL;
dispose_variable (var);
return (last_tempenv_value);
}
else if (shell_variables)
@ -88,12 +91,143 @@ getenv (name)
/* Some versions of Unix use _getenv instead. */
char *
_getenv (name)
#if defined (__linux__) || defined (__bsdi__) || defined (convex)
const char *name;
#else
char const *name;
#endif /* !__linux__ && !__bsdi__ && !convex */
{
return (getenv (name));
}
/* SUSv3 says argument is a `char *'; BSD implementations disagree */
int
putenv (str)
#ifndef HAVE_STD_PUTENV
const char *str;
#else
char *str;
#endif
{
SHELL_VAR *var;
char *name, *value;
int offset;
if (str == 0 || *str == '\0')
{
errno = EINVAL;
return -1;
}
offset = assignment (str);
if (str[offset] != '=')
{
errno = EINVAL;
return -1;
}
name = savestring (str);
name[offset] = 0;
value = name + offset + 1;
/* XXX - should we worry about readonly here? */
var = bind_variable (name, value);
if (var == 0)
{
errno = EINVAL;
return -1;
}
VUNSETATTR (var, att_invisible);
VSETATTR (var, att_exported);
return 0;
}
#if 0
int
_putenv (name)
#ifndef HAVE_STD_PUTENV
const char *name;
#else
char *name;
#endif
{
return putenv (name);
}
#endif
int
setenv (name, value, rewrite)
const char *name;
const char *value;
int rewrite;
{
SHELL_VAR *var;
char *v;
if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
{
errno = EINVAL;
return -1;
}
var = 0;
v = value;
/* XXX - should we worry about readonly here? */
if (rewrite == 0)
var = find_variable (name);
if (var == 0)
var = bind_variable (name, v);
if (var == 0)
return -1;
VUNSETATTR (var, att_invisible);
VSETATTR (var, att_exported);
return 0;
}
#if 0
int
_setenv (name, value, rewrite)
const char *name;
const char *value;
int rewrite;
{
return setenv (name, value, rewrite);
}
#endif
/* SUSv3 says unsetenv returns int; existing implementations (BSD) disagree. */
#ifdef HAVE_STD_UNSETENV
#define UNSETENV_RETURN(N) return(N)
#define UNSETENV_RETTYPE int
#else
#define UNSETENV_RETURN(N) return
#define UNSETENV_RETTYPE void
#endif
UNSETENV_RETTYPE
unsetenv (name)
const char *name;
{
if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
{
errno = EINVAL;
UNSETENV_RETURN(-1);
}
/* XXX - should we just remove the export attribute here? */
#if 1
unbind_variable (name);
#else
SHELL_VAR *v;
v = find_variable (name);
if (v)
VUNSETATTR (v, att_exported);
#endif
UNSETENV_RETURN(0);
}
#endif /* CAN_REDEFINE_GETENV */

View file

@ -1,6 +1,6 @@
/* itos.c -- Convert integer to string. */
/* Copyright (C) 1998, Free Software Foundation, Inc.
/* Copyright (C) 1998-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -27,47 +27,46 @@
#endif
#include <bashansi.h>
#include <chartypes.h>
#include "shell.h"
char *
inttostr (i, buf, len)
long i;
intmax_t i;
char *buf;
size_t len;
{
return (fmtulong (i, 10, buf, len, 0));
return (fmtumax (i, 10, buf, len, 0));
}
/* Integer to string conversion. This conses the string; the
caller should free it. */
char *
itos (i)
long i;
intmax_t i;
{
char *p, lbuf[INT_STRLEN_BOUND(long) + 1];
char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1];
p = fmtulong (i, 10, lbuf, sizeof(lbuf), 0);
p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0);
return (savestring (p));
}
char *
uinttostr (i, buf, len)
unsigned long i;
uintmax_t i;
char *buf;
size_t len;
{
return (fmtulong (i, 10, buf, len, FL_UNSIGNED));
return (fmtumax (i, 10, buf, len, FL_UNSIGNED));
}
/* Integer to string conversion. This conses the string; the
caller should free it. */
char *
uitos (i)
unsigned long i;
uintmax_t i;
{
char *p, lbuf[INT_STRLEN_BOUND(long) + 1];
char *p, lbuf[INT_STRLEN_BOUND(uintmax_t) + 1];
p = fmtulong (i, 10, lbuf, sizeof(lbuf), FL_UNSIGNED);
p = fmtumax (i, 10, lbuf, sizeof(lbuf), FL_UNSIGNED);
return (savestring (p));
}

View file

@ -91,7 +91,7 @@ sh_makepath (path, dir, flags)
}
else
{
xpath = ((flags & MP_DOTILDE) && *path == '~') ? bash_tilde_expand (path) : (char *)path;
xpath = ((flags & MP_DOTILDE) && *path == '~') ? bash_tilde_expand (path, 0) : (char *)path;
pathlen = strlen (xpath);
}

26
lib/sh/memset.c Normal file
View file

@ -0,0 +1,26 @@
/* memset.c -- set an area of memory to a given value
Copyright (C) 1991-2002 Free Software Foundation, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
char *
memset (char *str, int c, unsigned int len)
{
register char *st = str;
while (len-- > 0)
*st++ = c;
return str;
}

425
lib/sh/mktime.c Normal file
View file

@ -0,0 +1,425 @@
/* Copyright (C) 1993-2002 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Paul Eggert (eggert@twinsun.com).
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Define this to have a standalone program to test this implementation of
mktime. */
/* #define DEBUG 1 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef _LIBC
# define HAVE_LIMITS_H 1
# define HAVE_LOCALTIME_R 1
# define STDC_HEADERS 1
#endif
/* Assume that leap seconds are possible, unless told otherwise.
If the host has a `zic' command with a `-L leapsecondfilename' option,
then it supports leap seconds; otherwise it probably doesn't. */
#ifndef LEAP_SECONDS_POSSIBLE
#define LEAP_SECONDS_POSSIBLE 1
#endif
#ifndef VMS
#include <sys/types.h> /* Some systems define `time_t' here. */
#else
#include <stddef.h>
#endif
#include <time.h>
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#if DEBUG
#include <stdio.h>
#if STDC_HEADERS
#include <stdlib.h>
#endif
/* Make it work even if the system's libc has its own mktime routine. */
#define mktime my_mktime
#endif /* DEBUG */
#ifndef __P
#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
#define __P(args) args
#else
#define __P(args) ()
#endif /* GCC. */
#endif /* Not __P. */
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
#ifndef INT_MIN
#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
#endif
#ifndef INT_MAX
#define INT_MAX (~0 - INT_MIN)
#endif
#ifndef TIME_T_MIN
#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
: ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
#endif
#ifndef TIME_T_MAX
#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
#endif
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#ifndef __isleap
/* Nonzero if YEAR is a leap year (every 4 years,
except every 100th isn't, and every 400th is). */
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif
/* How many days come before each month (0-12). */
const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
time_t __mktime_internal __P ((struct tm *,
struct tm *(*) (const time_t *, struct tm *),
time_t *));
static struct tm *my_localtime_r __P ((const time_t *, struct tm *));
static struct tm *
my_localtime_r (t, tp)
const time_t *t;
struct tm *tp;
{
struct tm *l = localtime (t);
if (! l)
return 0;
*tp = *l;
return tp;
}
/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
measured in seconds, ignoring leap seconds.
YEAR uses the same numbering as TM->tm_year.
All values are in range, except possibly YEAR.
If overflow occurs, yield the low order bits of the correct answer. */
static time_t
ydhms_tm_diff (year, yday, hour, min, sec, tp)
int year, yday, hour, min, sec;
const struct tm *tp;
{
/* Compute intervening leap days correctly even if year is negative.
Take care to avoid int overflow. time_t overflow is OK, since
only the low order bits of the correct time_t answer are needed.
Don't convert to time_t until after all divisions are done, since
time_t might be unsigned. */
int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
int a100 = a4 / 25 - (a4 % 25 < 0);
int b100 = b4 / 25 - (b4 % 25 < 0);
int a400 = a100 >> 2;
int b400 = b100 >> 2;
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
time_t years = year - (time_t) tp->tm_year;
time_t days = (365 * years + intervening_leap_days
+ (yday - tp->tm_yday));
return (60 * (60 * (24 * days + (hour - tp->tm_hour))
+ (min - tp->tm_min))
+ (sec - tp->tm_sec));
}
static time_t localtime_offset;
/* Convert *TP to a time_t value. */
time_t
mktime (tp)
struct tm *tp;
{
#ifdef _LIBC
/* POSIX.1 8.1.1 requires that whenever mktime() is called, the
time zone names contained in the external variable `tzname' shall
be set as if the tzset() function had been called. */
__tzset ();
#endif
return __mktime_internal (tp, my_localtime_r, &localtime_offset);
}
/* Convert *TP to a time_t value, inverting
the monotonic and mostly-unit-linear conversion function CONVERT.
Use *OFFSET to keep track of a guess at the offset of the result,
compared to what the result would be for UTC without leap seconds.
If *OFFSET's guess is correct, only one CONVERT call is needed. */
time_t
__mktime_internal (tp, convert, offset)
struct tm *tp;
struct tm *(*convert) __P ((const time_t *, struct tm *));
time_t *offset;
{
time_t t, dt, t0;
struct tm tm;
/* The maximum number of probes (calls to CONVERT) should be enough
to handle any combinations of time zone rule changes, solar time,
and leap seconds. Posix.1 prohibits leap seconds, but some hosts
have them anyway. */
int remaining_probes = 4;
/* Time requested. Copy it in case CONVERT modifies *TP; this can
occur if TP is localtime's returned value and CONVERT is localtime. */
int sec = tp->tm_sec;
int min = tp->tm_min;
int hour = tp->tm_hour;
int mday = tp->tm_mday;
int mon = tp->tm_mon;
int year_requested = tp->tm_year;
int isdst = tp->tm_isdst;
/* Ensure that mon is in range, and set year accordingly. */
int mon_remainder = mon % 12;
int negative_mon_remainder = mon_remainder < 0;
int mon_years = mon / 12 - negative_mon_remainder;
int year = year_requested + mon_years;
/* The other values need not be in range:
the remaining code handles minor overflows correctly,
assuming int and time_t arithmetic wraps around.
Major overflows are caught at the end. */
/* Calculate day of year from year, month, and day of month.
The result need not be in range. */
int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
[mon_remainder + 12 * negative_mon_remainder])
+ mday - 1);
#if LEAP_SECONDS_POSSIBLE
/* Handle out-of-range seconds specially,
since ydhms_tm_diff assumes every minute has 60 seconds. */
int sec_requested = sec;
if (sec < 0)
sec = 0;
if (59 < sec)
sec = 59;
#endif
/* Invert CONVERT by probing. First assume the same offset as last time.
Then repeatedly use the error to improve the guess. */
tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
for (t = t0 + *offset;
(dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
t += dt)
if (--remaining_probes == 0)
return -1;
/* Check whether tm.tm_isdst has the requested value, if any. */
if (0 <= isdst && 0 <= tm.tm_isdst)
{
int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
if (dst_diff)
{
/* Move two hours in the direction indicated by the disagreement,
probe some more, and switch to a new time if found.
The largest known fallback due to daylight savings is two hours:
once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
time_t ot = t - 2 * 60 * 60 * dst_diff;
while (--remaining_probes != 0)
{
struct tm otm;
if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
(*convert) (&ot, &otm))))
{
t = ot;
tm = otm;
break;
}
if ((ot += dt) == t)
break; /* Avoid a redundant probe. */
}
}
}
*offset = t - t0;
#if LEAP_SECONDS_POSSIBLE
if (sec_requested != tm.tm_sec)
{
/* Adjust time to reflect the tm_sec requested, not the normalized value.
Also, repair any damage from a false match due to a leap second. */
t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
(*convert) (&t, &tm);
}
#endif
if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
{
/* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
so check for major overflows. A gross check suffices,
since if t has overflowed, it is off by a multiple of
TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
the difference that is bounded by a small value. */
double dyear = (double) year_requested + mon_years - tm.tm_year;
double dday = 366 * dyear + mday;
double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
return -1;
}
*tp = tm;
return t;
}
#ifdef weak_alias
weak_alias (mktime, timelocal)
#endif
#if DEBUG
static int
not_equal_tm (a, b)
struct tm *a;
struct tm *b;
{
return ((a->tm_sec ^ b->tm_sec)
| (a->tm_min ^ b->tm_min)
| (a->tm_hour ^ b->tm_hour)
| (a->tm_mday ^ b->tm_mday)
| (a->tm_mon ^ b->tm_mon)
| (a->tm_year ^ b->tm_year)
| (a->tm_mday ^ b->tm_mday)
| (a->tm_yday ^ b->tm_yday)
| (a->tm_isdst ^ b->tm_isdst));
}
static void
print_tm (tp)
struct tm *tp;
{
printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec,
tp->tm_yday, tp->tm_wday, tp->tm_isdst);
}
static int
check_result (tk, tmk, tl, tml)
time_t tk;
struct tm tmk;
time_t tl;
struct tm tml;
{
if (tk != tl || not_equal_tm (&tmk, &tml))
{
printf ("mktime (");
print_tm (&tmk);
printf (")\nyields (");
print_tm (&tml);
printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
return 1;
}
return 0;
}
int
main (argc, argv)
int argc;
char **argv;
{
int status = 0;
struct tm tm, tmk, tml;
time_t tk, tl;
char trailer;
if ((argc == 3 || argc == 4)
&& (sscanf (argv[1], "%d-%d-%d%c",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
== 3)
&& (sscanf (argv[2], "%d:%d:%d%c",
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
== 3))
{
tm.tm_year -= TM_YEAR_BASE;
tm.tm_mon--;
tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
tmk = tm;
tl = mktime (&tmk);
tml = *localtime (&tl);
printf ("mktime returns %ld == ", (long) tl);
print_tm (&tmk);
printf ("\n");
status = check_result (tl, tmk, tl, tml);
}
else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
{
time_t from = atol (argv[1]);
time_t by = atol (argv[2]);
time_t to = atol (argv[3]);
if (argc == 4)
for (tl = from; tl <= to; tl += by)
{
tml = *localtime (&tl);
tmk = tml;
tk = mktime (&tmk);
status |= check_result (tk, tmk, tl, tml);
}
else
for (tl = from; tl <= to; tl += by)
{
/* Null benchmark. */
tml = *localtime (&tl);
tmk = tml;
tk = tl;
status |= check_result (tk, tmk, tl, tml);
}
}
else
printf ("Usage:\
\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
argv[0], argv[0], argv[0]);
return status;
}
#endif /* DEBUG */
/*
Local Variables:
compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
End:
*/

83
lib/sh/netconn.c Normal file
View file

@ -0,0 +1,83 @@
/* netconn.c -- is a particular file descriptor a network connection?. */
/* Copyright (C) 2002 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>
#include <bashtypes.h>
#ifndef _MINIX
# include <sys/file.h>
#endif
#include <posixstat.h>
#include <filecntl.h>
#include <errno.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
/* The second and subsequent conditions must match those used to decide
whether or not to call getpeername() in isnetconn(). */
#if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2)
# include <sys/socket.h>
#endif
/* Is FD a socket or network connection? */
int
isnetconn (fd)
int fd;
{
#if defined (HAVE_GETPEERNAME) && !defined (SVR4_2) && !defined (__BEOS__)
int rv;
socklen_t l;
struct sockaddr sa;
l = sizeof(sa);
rv = getpeername(fd, &sa, &l);
/* Solaris 2.5 getpeername() returns EINVAL if the fd is not a socket. */
return ((rv < 0 && (errno == ENOTSOCK || errno == EINVAL)) ? 0 : 1);
#else /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
# if defined (SVR4) || defined (SVR4_2)
/* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */
struct stat sb;
if (isatty (fd))
return (0);
if (fstat (fd, &sb) < 0)
return (0);
# if defined (S_ISFIFO)
if (S_ISFIFO (sb.st_mode))
return (0);
# endif /* S_ISFIFO */
return (S_ISCHR (sb.st_mode));
# else /* !SVR4 && !SVR4_2 */
# if defined (S_ISSOCK) && !defined (__BEOS__)
struct stat sb;
if (fstat (fd, &sb) < 0)
return (0);
return (S_ISSOCK (sb.st_mode));
# else /* !S_ISSOCK || __BEOS__ */
return (0);
# endif /* !S_ISSOCK || __BEOS__ */
# endif /* !SVR4 && !SVR4_2 */
#endif /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
}

View file

@ -5,7 +5,7 @@
* chet@ins.CWRU.Edu
*/
/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -107,7 +107,7 @@ _getserv (serv, proto, pp)
int proto;
unsigned short *pp;
{
long l;
intmax_t l;
unsigned short s;
if (legal_number (serv, &l))
@ -165,7 +165,7 @@ _netopen4(host, serv, typ)
return -1;
}
bzero ((char *)&sin, sizeof(sin));
memset ((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = p;
sin.sin_addr = ina;
@ -205,7 +205,7 @@ _netopen6 (host, serv, typ)
struct addrinfo hints, *res, *res0;
int gerr;
bzero ((char *)&hints, sizeof (hints));
memset ((char *)&hints, 0, sizeof (hints));
/* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */
#ifdef DEBUG /* PF_INET is the one that works for me */
hints.ai_family = PF_INET;

View file

@ -29,6 +29,10 @@
# include <unistd.h>
#endif
#if defined (HAVE_LIMITS_H)
# include <limits.h>
#endif
#include <posixstat.h>
#include <filecntl.h>
#include <bashansi.h>
@ -237,3 +241,56 @@ mkfifo (path, mode)
#endif /* !S_IFIFO */
}
#endif /* !HAVE_MKFIFO && PROCESS_SUBSTITUTION */
#define DEFAULT_MAXGROUPS 64
int
getmaxgroups ()
{
static int maxgroups = -1;
if (maxgroups > 0)
return maxgroups;
#if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
maxgroups = sysconf (_SC_NGROUPS_MAX);
#else
# if defined (NGROUPS_MAX)
maxgroups = NGROUPS_MAX;
# else /* !NGROUPS_MAX */
# if defined (NGROUPS)
maxgroups = NGROUPS;
# else /* !NGROUPS */
maxgroups = DEFAULT_MAXGROUPS;
# endif /* !NGROUPS */
# endif /* !NGROUPS_MAX */
#endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */
if (maxgroups <= 0)
maxgroups = DEFAULT_MAXGROUPS;
return maxgroups;
}
long
getmaxchild ()
{
static long maxchild = -1L;
if (maxchild > 0)
return maxchild;
#if defined (HAVE_SYSCONF) && defined (_SC_CHILD_MAX)
maxchild = sysconf (_SC_CHILD_MAX);
#else
# if defined (CHILD_MAX)
maxchild = CHILD_MAX;
# else
# if defined (MAXUPRC)
maxchild = MAXUPRC;
# endif /* MAXUPRC */
# endif /* CHILD_MAX */
#endif /* !HAVE_SYSCONF || !_SC_CHILD_MAX */
return (maxchild);
}

View file

@ -37,6 +37,34 @@
#include "shell.h"
#if defined (__CYGWIN__)
#include <sys/cygwin.h>
static int
_is_cygdrive (path)
char *path;
{
static char user[MAXPATHLEN];
static char system[MAXPATHLEN];
static int first_time = 1;
/* If the path is the first part of a network path, treat it as
existing. */
if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/'))
return 1;
/* Otherwise check for /cygdrive prefix. */
if (first_time)
{
char user_flags[MAXPATHLEN];
char system_flags[MAXPATHLEN];
/* Get the cygdrive info */
cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags);
first_time = 0;
}
return !strcasecmp (path, user) || !strcasecmp (path, system);
}
#endif /* __CYGWIN__ */
/* Return 1 if PATH corresponds to a directory. A function for debugging. */
static int
_path_isdir (path)
@ -46,6 +74,10 @@ _path_isdir (path)
struct stat sb;
l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode);
#if defined (__CYGWIN__)
if (l == 0)
l = _is_cygdrive (path);
#endif
return l;
}

View file

@ -25,21 +25,52 @@
#if !defined (HAVE_RENAME)
#include <bashtypes.h>
#include <posixstat.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#include <stdc.h>
#ifndef errno
extern int errno;
#endif
int
rename (from, to)
const char *from, *to;
{
unlink (to);
struct stat fb, tb;
if (stat (from, &fb) < 0)
return -1;
if (stat (to, &tb) < 0)
{
if (errno != ENOENT)
return -1;
}
else
{
if (fb.st_dev == tb.st_dev && fb.st_ino == tb.st_ino)
return 0; /* same file */
if (unlink (to) < 0 && errno != ENOENT)
return -1;
}
if (link (from, to) < 0)
return (-1);
unlink (from);
if (unlink (from) < 0 && errno != ENOENT)
{
int e = errno;
unlink (to);
errno = e;
return (-1);
}
return (0);
}
#endif /* !HAVE_RENAME */

View file

@ -147,6 +147,7 @@ sh_backslash_quote (string)
case '*': case '[': case '?': case ']': /* globbing chars */
case '^':
case '$': case '`': /* expansion chars */
case ',': /* brace expansion */
*r++ = '\\';
*r++ = c;
break;

View file

@ -47,9 +47,7 @@
* Currently doesn't handle (and bash/readline doesn't use):
* *M$ width, precision specifications
* %N$ numbered argument conversions
* inf, nan floating values (could use isinf(), isnan())
* `,', `'' flags
* `C', `S' conversions
* inf, nan floating values imperfect (if isinf(), isnan() not in libc)
* support for `F' is imperfect, since underlying printf may not handle it
*/
@ -65,10 +63,12 @@
#ifdef __linux__
#define HAVE_PRINTF_A_FORMAT
#endif
#define HAVE_ISINF_IN_LIBC
#define PREFER_STDARG
#define HAVE_STRINGIZE
#define HAVE_LIMITS_H
#define HAVE_STDDEF_H
#define HAVE_LOCALE_H
#define intmax_t long
#endif
@ -96,12 +96,18 @@
#endif
#ifdef FLOATING_POINT
# include <float.h> /* for manifest constants */
# include <stdio.h> /* for sprintf */
#endif
#include <typemax.h>
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include "stdc.h"
#include <shmbutil.h>
#ifndef DRIVER
# include "shell.h"
@ -114,6 +120,10 @@ extern char *fmtulong __P((unsigned long int, int, char *, size_t, int));
extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
#endif
#ifndef FREE
# define FREE(x) if (x) free (x)
#endif
/* Bound on length of the string representing an integer value of type T.
Subtract one for the sign bit if T is signed;
302 / 1000 is log10 (2) rounded up;
@ -130,7 +140,7 @@ extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
#define PF_ZEROPAD 0x00008 /* 0 */
#define PF_PLUS 0x00010 /* + */
#define PF_SPACE 0x00020 /* ' ' */
#define PF_COMMA 0x00040 /* , */
#define PF_THOUSANDS 0x00040 /* ' */
#define PF_DOT 0x00080 /* `.precision' */
#define PF_STAR_P 0x00100 /* `*' after precision */
@ -158,6 +168,10 @@ extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
static char intbuf[INT_STRLEN_BOUND(unsigned long) + 1];
static int decpoint;
static int thoussep;
static char *grouping;
/*
* For the FLOATING POINT FORMAT :
* the challenge was finding a way to
@ -196,25 +210,27 @@ static char intbuf[INT_STRLEN_BOUND(unsigned long) + 1];
#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}
#define GETARG(type) (va_arg(args, type))
/* Macros that do proper sign extension and handle length modifiers. Used
for the integer conversion specifiers. */
#define GETSIGNED(p) \
(((p)->flags & PF_LONGINT) \
? va_arg(args, long) \
: (((p)->flags & PF_SHORTINT) ? (long)(short)va_arg(args, int) \
: (long)va_arg(args, int)))
? GETARG (long) \
: (((p)->flags & PF_SHORTINT) ? (long)(short)GETARG (int) \
: (long)GETARG (int)))
#define GETUNSIGNED(p) \
(((p)->flags & PF_LONGINT) \
? va_arg(args, unsigned long) \
: (((p)->flags & PF_SHORTINT) ? (unsigned long)(unsigned short)va_arg(args, int) \
: (unsigned long)va_arg(args, unsigned int)))
? GETARG (unsigned long) \
: (((p)->flags & PF_SHORTINT) ? (unsigned long)(unsigned short)GETARG (int) \
: (unsigned long)GETARG (unsigned int)))
#ifdef HAVE_LONG_DOUBLE
#define GETLDOUBLE(p) va_arg(args, long double)
#define GETLDOUBLE(p) GETARG (long double)
#endif
#define GETDOUBLE(p) va_arg(args, double)
#define GETDOUBLE(p) GETARG (double)
#define SET_SIZE_FLAGS(p, type) \
if (sizeof (type) > sizeof (int)) \
@ -271,6 +287,8 @@ static void ldfallback __P((struct DATA *, const char *, const char *, long doub
static void dfallback __P((struct DATA *, const char *, const char *, double));
#endif
static char *groupnum __P((char *));
#ifdef DRIVER
static void memory_error_and_abort ();
static void *xmalloc __P((size_t));
@ -317,6 +335,20 @@ static void xfree __P((void *));
} \
while (0)
/* Output a string. P->WIDTH has already been adjusted for padding. */
#define PUT_STRING(string, len, p) \
do \
{ \
PAD_RIGHT (p); \
while ((len)-- > 0) \
{ \
PUT_CHAR (*(string), (p)); \
(string)++; \
} \
PAD_LEFT (p); \
} \
while (0)
#define PUT_PLUS(d, p, zero) \
if ((d) > zero && (p)->justify == RIGHT) \
PUT_CHAR('+', p)
@ -340,9 +372,34 @@ static void xfree __P((void *));
/* if width and prec. in the args */
#define STAR_ARGS(p) \
if ((p)->flags & PF_STAR_W) \
(p)->width = va_arg(args, int); \
(p)->width = GETARG (int); \
if ((p)->flags & PF_STAR_P) \
(p)->precision = va_arg(args, int)
(p)->precision = GETARG (int)
#if defined (HAVE_LOCALE_H)
# define GETLOCALEDATA(d, t, g) \
do \
{ \
struct lconv *lv; \
if ((d) == 0) { \
(d) = '.'; (t) = -1; (g) = 0; /* defaults */ \
lv = localeconv(); \
if (lv) \
{ \
if (lv->decimal_point && lv->decimal_point[0]) \
(d) = lv->decimal_point[0]; \
if (lv->thousands_sep && lv->thousands_sep[0]) \
(t) = lv->thousands_sep[0]; \
(g) = lv->grouping ? lv->grouping : ""; \
if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \
} \
} \
} \
while (0);
#else
# define GETLOCALEDATA(d, t, g) \
( (d) = '.', (t) = ',', g = "\003" )
#endif
#ifdef FLOATING_POINT
/*
@ -496,6 +553,8 @@ numtoa(number, base, precision, fract)
integral_part[1] = '\0';
fraction_part[0] = '0';
fraction_part[1] = '\0';
if (fract)
*fract = fraction_part;
return integral_part;
}
@ -570,7 +629,7 @@ number(p, d, base)
unsigned long d;
int base;
{
char *tmp;
char *tmp, *t;
long sd;
int flags;
@ -580,6 +639,14 @@ number(p, d, base)
flags |= FL_HEXUPPER;
tmp = fmtulong (d, base, intbuf, sizeof(intbuf), flags);
t = 0;
if ((p->flags & PF_THOUSANDS))
{
GETLOCALEDATA(decpoint, thoussep, grouping);
if (grouping && (t = groupnum (tmp)))
tmp = t;
}
p->width -= strlen(tmp);
PAD_RIGHT(p);
@ -609,6 +676,7 @@ number(p, d, base)
}
PAD_LEFT(p);
FREE (t);
}
#ifdef HAVE_LONG_LONG
@ -621,7 +689,7 @@ lnumber(p, d, base)
unsigned long long d;
int base;
{
char *tmp;
char *tmp, *t;
long long sd;
int flags;
@ -631,6 +699,14 @@ lnumber(p, d, base)
flags |= FL_HEXUPPER;
tmp = fmtullong (d, base, intbuf, sizeof(intbuf), flags);
t = 0;
if ((p->flags & PF_THOUSANDS))
{
GETLOCALEDATA(decpoint, thoussep, grouping);
if (grouping && (t = groupnum (tmp)))
tmp = t;
}
p->width -= strlen(tmp);
PAD_RIGHT(p);
@ -660,6 +736,7 @@ lnumber(p, d, base)
}
PAD_LEFT(p);
FREE (t);
}
#endif
@ -692,34 +769,162 @@ strings(p, tmp)
struct DATA *p;
char *tmp;
{
int i;
size_t len;
i = strlen(tmp);
len = strlen(tmp);
if (p->precision != NOT_FOUND) /* the smallest number */
i = (i < p->precision ? i : p->precision);
p->width -= i;
PAD_RIGHT(p);
while (i-- > 0)
{ /* put the sting */
PUT_CHAR(*tmp, p);
tmp++;
}
PAD_LEFT(p);
len = (len < p->precision ? len : p->precision);
p->width -= len;
PUT_STRING (tmp, len, p);
}
#if HANDLE_MULTIBYTE
/* %ls wide-character strings */
static void
wstrings(p, tmp)
struct DATA *p;
wchar_t *tmp;
{
size_t len;
mbstate_t mbs;
char *os;
const wchar_t *ws;
memset (&mbs, '\0', sizeof (mbstate_t));
ws = (const wchar_t *)tmp;
os = (char *)NULL;
if (p->precision != NOT_FOUND)
{
os = (char *)xmalloc (p->precision + 1);
len = wcsrtombs (os, &ws, p->precision, &mbs);
}
else
{
len = wcsrtombs (NULL, &ws, 0, &mbs);
if (len != (size_t)-1)
{
memset (&mbs, '\0', sizeof (mbstate_t));
os = (char *)xmalloc (len + 1);
(void)wcsrtombs (os, &ws, len + 1, &mbs);
}
}
if (len == (size_t)-1)
{
/* invalid multibyte sequence; bail now. */
FREE (os);
return;
}
p->width -= len;
PUT_STRING (os, len, p);
free (os);
}
static void
wchars (p, wc)
struct DATA *p;
wint_t wc;
{
char *lbuf, *l;
mbstate_t mbs;
size_t len;
lbuf = (char *)malloc (MB_CUR_MAX+1);
if (lbuf == 0)
return;
memset (&mbs, '\0', sizeof (mbstate_t));
len = wcrtomb (lbuf, wc, &mbs);
if (len == (size_t)-1)
/* conversion failed; bail now. */
return;
p->width -= len;
l = lbuf;
PUT_STRING (l, len, p);
free (lbuf);
}
#endif /* HANDLE_MULTIBYTE */
#ifdef FLOATING_POINT
#ifndef HAVE_ISINF_IN_LIBC
/* Half-assed versions, since we don't want to link with libm. */
static int
isinf(d)
double d;
{
#ifdef DBL_MAX
if (d < DBL_MIN)
return -1;
else if (d > DBL_MAX)
return 1;
else
#endif
return 0;
}
static int
isnan(d)
double d;
{
return 0;
}
#endif
/* Check for [+-]infinity and NaN. If MODE == 1, we check for Infinity, else
(mode == 2) we check for NaN. This does the necessary printing. Returns
1 if Inf or Nan, 0 if not. */
static int
chkinfnan(p, d, mode)
struct DATA *p;
double d;
int mode; /* == 1 for inf, == 2 for nan */
{
int i;
char *tmp;
char *big, *small;
i = (mode == 1) ? isinf(d) : isnan(d);
if (i == 0)
return 0;
big = (mode == 1) ? "INF" : "NAN";
small = (mode == 1) ? "inf" : "nan";
tmp = (*p->pf == 'F' || *p->pf == 'G' || *p->pf == 'E') ? big : small;
if (i < 0)
PUT_CHAR('-', p);
while (*tmp)
{
PUT_CHAR (*tmp, p);
tmp++;
}
return 1;
}
/* %f %F %g %G floating point representation */
static void
floating(p, d)
struct DATA *p;
double d;
{
char *tmp, *tmp2;
char *tmp, *tmp2, *t;
int i;
if (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))
return; /* already printed nan or inf */
GETLOCALEDATA(decpoint, thoussep, grouping);
DEF_PREC(p);
d = ROUND(d, p);
tmp = dtoa(d, p->precision, &tmp2);
t = 0;
if ((p->flags & PF_THOUSANDS) && grouping && (t = groupnum (tmp)))
tmp = t;
/* calculate the padding. 1 for the dot */
p->width = p->width -
((d > 0. && p->justify == RIGHT) ? 1:0) -
@ -728,17 +933,22 @@ floating(p, d)
PAD_RIGHT(p);
PUT_PLUS(d, p, 0.);
PUT_SPACE(d, p, 0.);
while (*tmp)
{ /* the integral */
PUT_CHAR(*tmp, p);
tmp++;
}
FREE (t);
if (p->precision != 0 || (p->flags & PF_ALTFORM))
PUT_CHAR('.', p); /* put the '.' */
PUT_CHAR(decpoint, p); /* put the '.' */
if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
/* smash the trailing zeros unless altform */
for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
tmp2[i] = '\0';
tmp2[i] = '\0';
for (; *tmp2; tmp2++)
PUT_CHAR(*tmp2, p); /* the fraction */
@ -752,69 +962,55 @@ exponent(p, d)
double d;
{
char *tmp, *tmp2;
int j, i, nsig, ndig;
int j, i;
if (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))
return; /* already printed nan or inf */
GETLOCALEDATA(decpoint, thoussep, grouping);
DEF_PREC(p);
j = log_10(d);
d = d / pow_10(j); /* get the Mantissa */
d = ROUND(d, p);
tmp = dtoa(d, p->precision, &tmp2);
/* 1 for unit, 1 for the '.', 1 for 'e|E',
* 1 for '+|-', 2 for 'exp' */
/* calculate how much padding need */
p->width = p->width -
((d > 0. && p->justify == RIGHT) ? 1:0) -
((p->flags & PF_SPACE) ? 1:0) - p->precision - 6;
PAD_RIGHT(p);
PUT_PLUS(d, p, 0.);
PUT_SPACE(d, p, 0.);
/*
* When supplied %g or %G, an optional precision is the number of
* significant digits to print.
*
* nsig = number of significant digits we've printed (leading zeros are
* never significant)
* ndig = if non-zero, max number of significant digits to print (only
* applicable to %g/%G)
*/
nsig = ndig = 0;
if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_DOT))
ndig = (p->precision == 0) ? 1 : p->precision;
while (*tmp)
{
PUT_CHAR(*tmp, p);
tmp++;
if (ndig && (++nsig >= ndig))
break;
}
if ((p->precision != 0 || (p->flags & PF_ALTFORM)) && (ndig == 0 || nsig < ndig))
PUT_CHAR('.', p); /* the '.' */
if (p->precision != 0 || (p->flags & PF_ALTFORM))
PUT_CHAR(decpoint, p); /* the '.' */
if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
/* smash the trailing zeros unless altform */
for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
tmp2[i] = '\0';
tmp2[i] = '\0';
for (; *tmp2; tmp2++)
{
if (ndig && (nsig++ >= ndig))
break;
PUT_CHAR(*tmp2, p); /* the fraction */
}
PUT_CHAR(*tmp2, p); /* the fraction */
/* the exponent put the 'e|E' */
if (*p->pf == 'g' || *p->pf == 'e')
{
PUT_CHAR('e', p);
}
PUT_CHAR('e', p);
else
PUT_CHAR('E', p);
PUT_CHAR('E', p);
/* the sign of the exp */
if (j > 0)
{
PUT_CHAR('+', p);
}
PUT_CHAR('+', p);
else
{
PUT_CHAR('-', p);
@ -825,9 +1021,7 @@ exponent(p, d)
/* pad out to at least two spaces. pad with `0' if the exponent is a
single digit. */
if (j <= 9)
{
PUT_CHAR('0', p);
}
PUT_CHAR('0', p);
/* the exponent */
while (*tmp)
@ -839,6 +1033,69 @@ exponent(p, d)
}
#endif
/* Return a new string with the digits in S grouped according to the locale's
grouping info and thousands separator. If no grouping should be performed,
this returns NULL; the caller needs to check for it. */
static char *
groupnum (s)
char *s;
{
char *se, *ret, *re, *g;
int len, slen;
if (grouping == 0 || *grouping <= 0 || *grouping == CHAR_MAX)
return ((char *)NULL);
/* find min grouping to size returned string */
for (len = *grouping, g = grouping; *g; g++)
if (*g > 0 && *g < len)
len = *g;
slen = strlen (s);
len = slen / len + 1;
ret = (char *)xmalloc (slen + len + 1);
re = ret + slen + len;
*re = '\0';
g = grouping;
se = s + slen;
len = *g;
while (se > s)
{
*--re = *--se;
/* handle `-' inserted by numtoa() and the fmtu* family here. */
if (se > s && se[-1] == '-')
continue;
/* begin new group. */
if (--len == 0 && se > s)
{
*--re = thoussep;
len = *++g; /* was g++, but that uses first char twice (glibc bug, too) */
if (*g == '\0')
len = *--g; /* use previous grouping */
else if (*g == CHAR_MAX)
{
do
*--re = *--se;
while (se > s);
break;
}
}
}
if (re > ret)
#ifdef HAVE_MEMMOVE
memmove (ret, re, strlen (re) + 1);
#else
strcpy (ret, re);
#endif
return ret;
}
/* initialize the conversion specifiers */
static void
init_conv_flag (p)
@ -887,18 +1144,26 @@ vsnprintf_internal(data, string, length, format, args)
#endif
int state, i, c, n;
char *s;
#if HANDLE_MULTIBYTE
wchar_t *ws;
wint_t wc;
#endif
const char *convstart;
/* Sanity check, the string must be > 1. C99 actually says that LENGTH
can be zero here, in the case of snprintf/vsnprintf (it's never 0 in
the case of asprintf/vasprintf), and the return value is the number
/* Sanity check, the string length must be >= 0. C99 actually says that
LENGTH can be zero here, in the case of snprintf/vsnprintf (it's never
0 in the case of asprintf/vasprintf), and the return value is the number
of characters that would have been written. */
if (length < 1)
if (length < 0)
return -1;
if (format == 0)
return 0;
/* Reset these for each call because the locale might have changed. */
decpoint = thoussep = 0;
grouping = 0;
for (; c = *(data->pf); data->pf++)
{
if (c != '%')
@ -966,8 +1231,8 @@ vsnprintf_internal(data, string, length, format, args)
data->flags |= PF_PLUS;
data->justify = RIGHT;
continue;
case ',':
data->flags |= PF_COMMA; /* not implemented yet */
case '\'':
data->flags |= PF_THOUSANDS;
continue;
case '1': case '2': case '3':
@ -1043,9 +1308,18 @@ conv_break:
* else use %e|%E
*/
if (-4 < i && i < data->precision)
floating(data, d);
{
/* reset precision */
data->precision -= i + 1;
floating(data, d);
}
else
exponent(data, d);
{
/* reduce precision by 1 because of leading digit before
decimal point in e format. */
data->precision--;
exponent(data, d);
}
state = 0;
break;
case 'e':
@ -1073,7 +1347,7 @@ conv_break:
#ifdef HAVE_LONG_LONG
if (data->flags & PF_LONGLONG)
{
ull = va_arg(args, unsigned long long);
ull = GETARG (unsigned long long);
lnumber(data, ull, 10);
}
else
@ -1093,7 +1367,7 @@ conv_break:
#ifdef HAVE_LONG_LONG
if (data->flags & PF_LONGLONG)
{
ull = va_arg(args, long long);
ull = GETARG (long long);
lnumber(data, ull, 10);
}
else
@ -1109,7 +1383,7 @@ conv_break:
#ifdef HAVE_LONG_LONG
if (data->flags & PF_LONGLONG)
{
ull = va_arg(args, unsigned long long);
ull = GETARG (unsigned long long);
lnumber(data, ull, 8);
}
else
@ -1126,7 +1400,7 @@ conv_break:
#ifdef HAVE_LONG_LONG
if (data->flags & PF_LONGLONG)
{
ull = va_arg(args, unsigned long long);
ull = GETARG (unsigned long long);
lnumber(data, ull, 16);
}
else
@ -1139,33 +1413,64 @@ conv_break:
break;
case 'p':
STAR_ARGS(data);
ul = (unsigned long)va_arg(args, void *);
ul = (unsigned long)GETARG (void *);
pointer(data, ul);
state = 0;
break;
#if HANDLE_MULTIBYTE
case 'C':
data->flags |= PF_LONGINT;
/* FALLTHROUGH */
#endif
case 'c': /* character */
ul = va_arg(args, int);
PUT_CHAR(ul, data);
STAR_ARGS(data);
#if HANDLE_MULTIBYTE
if (data->flags & PF_LONGINT)
{
wc = GETARG (wint_t);
wchars (data, wc);
}
else
#endif
{
ul = GETARG (int);
PUT_CHAR(ul, data);
}
state = 0;
break;
#if HANDLE_MULTIBYTE
case 'S':
data->flags |= PF_LONGINT;
/* FALLTHROUGH */
#endif
case 's': /* string */
STAR_ARGS(data);
s = va_arg(args, char *);
strings(data, s);
#if HANDLE_MULTIBYTE
if (data->flags & PF_LONGINT)
{
ws = GETARG (wchar_t *);
wstrings (data, ws);
}
else
#endif
{
s = GETARG (char *);
strings(data, s);
}
state = 0;
break;
case 'n':
#ifdef HAVE_LONG_LONG
if (data->flags & PF_LONGLONG)
*(va_arg(args, long long *)) = data->counter;
*(GETARG (long long *)) = data->counter;
else
#endif
if (data->flags & PF_LONGINT)
*(va_arg(args, long *)) = data->counter;
*(GETARG (long *)) = data->counter;
else if (data->flags & PF_SHORTINT)
*(va_arg(args, short *)) = data->counter;
*(GETARG (short *)) = data->counter;
else
*(va_arg(args, int *)) = data->counter;
*(GETARG (int *)) = data->counter;
state = 0;
break;
case '%': /* nothing just % */
@ -1201,7 +1506,7 @@ ldfallback (data, fs, fe, ld)
char fmtbuf[FALLBACK_FMTSIZE], *obuf;
int fl;
obuf = xmalloc(LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2);
obuf = (char *)xmalloc(LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2);
fl = fe - fs + 1;
strncpy (fmtbuf, fs, fl);
fmtbuf[fl] = '\0';
@ -1248,6 +1553,8 @@ vsnprintf(string, length, format, args)
{
struct DATA data;
if (string == 0 && length != 0)
return 0;
init_data (&data, string, length, format, PFM_SN);
return (vsnprintf_internal(&data, string, length, format, args));
}
@ -1267,12 +1574,10 @@ snprintf(string, length, format, va_alist)
int rval;
va_list args;
#if defined(PREFER_STDARG)
va_start(args, format);
#else
va_start(args);
#endif
SH_VA_START(args, format);
if (string == 0 && length != 0)
return 0;
init_data (&data, string, length, format, PFM_SN);
rval = vsnprintf_internal (&data, string, length, format, args);
@ -1319,11 +1624,7 @@ asprintf(stringp, format, va_alist)
int rval;
va_list args;
#if defined(PREFER_STDARG)
va_start(args, format);
#else
va_start(args);
#endif
SH_VA_START(args, format);
rval = vasprintf (stringp, format, args);
@ -1378,10 +1679,6 @@ xfree(x)
free (x);
}
#ifdef FLOATING_POINT
# include <float.h>
#endif
/* set of small tests for snprintf() */
main()
{
@ -1389,6 +1686,18 @@ main()
char *h;
int i, si, ai;
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "");
#endif
#if 1
si = snprintf((char *)NULL, 0, "abcde\n");
printf("snprintf returns %d with NULL first argument and size of 0\n", si);
si = snprintf(holder, 0, "abcde\n");
printf("snprintf returns %d with non-NULL first argument and size of 0\n", si);
si = snprintf((char *)NULL, 16, "abcde\n");
printf("snprintf returns %d with NULL first argument and non-zero size\n", si);
/*
printf("Suite of test for snprintf:\n");
printf("a_format\n");
@ -1660,6 +1969,92 @@ main()
printf ("<%d> <%s>\n", si, holder);
printf ("<%d> <%s>\n\n", ai, h);
/* huh? */
printf("/%%g/, 421.2345\n");
snprintf(holder, sizeof holder, "/%g/\n", 421.2345);
asprintf(&h, "/%g/\n", 421.2345);
printf("/%g/\n", 421.2345);
printf("%s", holder);
printf("%s\n", h);
printf("/%%g/, 4214.2345\n");
snprintf(holder, sizeof holder, "/%g/\n", 4214.2345);
asprintf(&h, "/%g/\n", 4214.2345);
printf("/%g/\n", 4214.2345);
printf("%s", holder);
printf("%s\n", h);
printf("/%%.5g/, 4214.2345\n");
snprintf(holder, sizeof holder, "/%.5g/\n", 4214.2345);
asprintf(&h, "/%.5g/\n", 4214.2345);
printf("/%.5g/\n", 4214.2345);
printf("%s", holder);
printf("%s\n", h);
printf("/%%.4g/, 4214.2345\n");
snprintf(holder, sizeof holder, "/%.4g/\n", 4214.2345);
asprintf(&h, "/%.4g/\n", 4214.2345);
printf("/%.4g/\n", 4214.2345);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'ld %%'ld/, 12345, 1234567\n");
snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 12345, 1234567);
asprintf(&h, "/%'ld %'ld/\n", 12345, 1234567);
printf("/%'ld %'ld/\n", 12345, 1234567);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'ld %%'ld/, 336, 3336\n");
snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 336, 3336);
asprintf(&h, "/%'ld %'ld/\n", 336, 3336);
printf("/%'ld %'ld/\n", 336, 3336);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'ld %%'ld/, -42786, -142786\n");
snprintf(holder, sizeof holder, "/%'ld %'ld/\n", -42786, -142786);
asprintf(&h, "/%'ld %'ld/\n", -42786, -142786);
printf("/%'ld %'ld/\n", -42786, -142786);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'f %%'f/, 421.2345, 421234.56789\n");
snprintf(holder, sizeof holder, "/%'f %'f/\n", 421.2345, 421234.56789);
asprintf(&h, "/%'f %'f/\n", 421.2345, 421234.56789);
printf("/%'f %'f/\n", 421.2345, 421234.56789);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'f %%'f/, -421.2345, -421234.56789\n");
snprintf(holder, sizeof holder, "/%'f %'f/\n", -421.2345, -421234.56789);
asprintf(&h, "/%'f %'f/\n", -421.2345, -421234.56789);
printf("/%'f %'f/\n", -421.2345, -421234.56789);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'g %%'g/, 421.2345, 421234.56789\n");
snprintf(holder, sizeof holder, "/%'g %'g/\n", 421.2345, 421234.56789);
asprintf(&h, "/%'g %'g/\n", 421.2345, 421234.56789);
printf("/%'g %'g/\n", 421.2345, 421234.56789);
printf("%s", holder);
printf("%s\n", h);
printf("/%%'g %%'g/, -421.2345, -421234.56789\n");
snprintf(holder, sizeof holder, "/%'g %'g/\n", -421.2345, -421234.56789);
asprintf(&h, "/%'g %'g/\n", -421.2345, -421234.56789);
printf("/%'g %'g/\n", -421.2345, -421234.56789);
printf("%s", holder);
printf("%s\n", h);
#endif
printf("/%%'g/, 4213455.8392\n");
snprintf(holder, sizeof holder, "/%'g/\n", 4213455.8392);
asprintf(&h, "/%'g/\n", 4213455.8392);
printf("/%'g/\n", 4213455.8392);
printf("%s", holder);
printf("%s\n", h);
exit (0);
}
#endif

View file

@ -38,8 +38,10 @@ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include <bashansi.h>
#include <maxpath.h>
#include <stdc.h>
static int mindist (), spdist ();
static int mindist __P((char *, char *, char *));
static int spdist __P((char *, char *));
/*
* `spname' and its helpers are inspired by the code in "The UNIX

859
lib/sh/strftime.c Normal file
View file

@ -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 */

View file

@ -1,6 +1,6 @@
/* stringlist.c - functions to handle a generic `list of strings' structure */
/* Copyright (C) 2000 Free Software Foundation, Inc.
/* Copyright (C) 2000-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -37,7 +37,7 @@
/* Allocate a new STRINGLIST, with room for N strings. */
STRINGLIST *
alloc_stringlist (n)
strlist_create (n)
int n;
{
STRINGLIST *ret;
@ -46,7 +46,7 @@ alloc_stringlist (n)
ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
if (n)
{
ret->list = alloc_array (n+1);
ret->list = strvec_create (n+1);
ret->list_size = n;
for (i = 0; i < n; i++)
ret->list[i] = (char *)NULL;
@ -61,38 +61,64 @@ alloc_stringlist (n)
}
STRINGLIST *
realloc_stringlist (sl, n)
strlist_resize (sl, n)
STRINGLIST *sl;
int n;
{
register int i;
if (sl == 0)
return (sl = alloc_stringlist(n));
return (sl = strlist_create (n));
if (n > sl->list_size)
{
sl->list = (char **)xrealloc (sl->list, (n+1) * sizeof (char *));
sl->list = strvec_resize (sl->list, n + 1);
for (i = sl->list_size; i <= n; i++)
sl->list[i] = (char *)NULL;
sl->list_size = n;
}
return sl;
}
void
strlist_flush (sl)
STRINGLIST *sl;
{
if (sl == 0 || sl->list == 0)
return;
strvec_flush (sl->list);
sl->list_len = 0;
}
void
free_stringlist (sl)
strlist_dispose (sl)
STRINGLIST *sl;
{
if (sl == 0)
return;
if (sl->list)
free_array (sl->list);
strvec_dispose (sl->list);
free (sl);
}
int
strlist_remove (sl, s)
STRINGLIST *sl;
char *s;
{
int r;
if (sl == 0 || sl->list == 0 || sl->list_len == 0)
return 0;
r = strvec_remove (sl->list, s);
if (r)
sl->list_len--;
return r;
}
STRINGLIST *
copy_stringlist (sl)
strlist_copy (sl)
STRINGLIST *sl;
{
STRINGLIST *new;
@ -100,8 +126,8 @@ copy_stringlist (sl)
if (sl == 0)
return ((STRINGLIST *)0);
new = alloc_stringlist (sl->list_size);
/* I'd like to use copy_array, but that doesn't copy everything. */
new = strlist_create (sl->list_size);
/* I'd like to use strvec_copy, but that doesn't copy everything. */
if (sl->list)
{
for (i = 0; i < sl->list_size; i++)
@ -118,7 +144,7 @@ copy_stringlist (sl)
/* Return a new STRINGLIST with everything from M1 and M2. */
STRINGLIST *
merge_stringlists (m1, m2)
strlist_merge (m1, m2)
STRINGLIST *m1, *m2;
{
STRINGLIST *sl;
@ -127,7 +153,7 @@ merge_stringlists (m1, m2)
l1 = m1 ? m1->list_len : 0;
l2 = m2 ? m2->list_len : 0;
sl = alloc_stringlist (l1 + l2 + 1);
sl = strlist_create (l1 + l2 + 1);
for (i = n = 0; i < l1; i++, n++)
sl->list[n] = STRDUP (m1->list[i]);
for (i = 0; i < l2; i++, n++)
@ -139,20 +165,20 @@ merge_stringlists (m1, m2)
/* Make STRINGLIST M1 contain everything in M1 and M2. */
STRINGLIST *
append_stringlist (m1, m2)
strlist_append (m1, m2)
STRINGLIST *m1, *m2;
{
register int i, n, len1, len2;
if (m1 == 0)
return (m2 ? copy_stringlist (m2) : (STRINGLIST *)0);
return (m2 ? strlist_copy (m2) : (STRINGLIST *)0);
len1 = m1->list_len;
len2 = m2 ? m2->list_len : 0;
if (len2)
{
m1 = realloc_stringlist (m1, len1 + len2 + 1);
m1 = strlist_resize (m1, len1 + len2 + 1);
for (i = 0, n = len1; i < len2; i++, n++)
m1->list[n] = STRDUP (m2->list[i]);
m1->list[n] = (char *)NULL;
@ -163,7 +189,7 @@ append_stringlist (m1, m2)
}
STRINGLIST *
prefix_suffix_stringlist (sl, prefix, suffix)
strlist_prefix_suffix (sl, prefix, suffix)
STRINGLIST *sl;
char *prefix, *suffix;
{
@ -197,7 +223,7 @@ prefix_suffix_stringlist (sl, prefix, suffix)
}
void
print_stringlist (sl, prefix)
strlist_print (sl, prefix)
STRINGLIST *sl;
char *prefix;
{
@ -210,18 +236,32 @@ print_stringlist (sl, prefix)
}
void
sort_stringlist (sl)
strlist_walk (sl, func)
STRINGLIST *sl;
sh_strlist_map_func_t *func;
{
register int i;
if (sl == 0)
return;
for (i = 0; i < sl->list_len; i++)
if ((*func)(sl->list[i]) < 0)
break;
}
void
strlist_sort (sl)
STRINGLIST *sl;
{
if (sl == 0 || sl->list_len == 0 || sl->list == 0)
return;
sort_char_array (sl->list);
strvec_sort (sl->list);
}
STRINGLIST *
word_list_to_stringlist (list, copy, starting_index, ip)
strlist_from_word_list (list, alloc, starting_index, ip)
WORD_LIST *list;
int copy, starting_index, *ip;
int alloc, starting_index, *ip;
{
STRINGLIST *ret;
int slen, len;
@ -234,7 +274,7 @@ word_list_to_stringlist (list, copy, starting_index, ip)
}
slen = list_length (list);
ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
ret->list = word_list_to_argv (list, copy, starting_index, &len);
ret->list = strvec_from_word_list (list, alloc, starting_index, &len);
ret->list_size = slen + starting_index;
ret->list_len = len;
if (ip)
@ -243,15 +283,15 @@ word_list_to_stringlist (list, copy, starting_index, ip)
}
WORD_LIST *
stringlist_to_word_list (sl, copy, starting_index)
strlist_to_word_list (sl, alloc, starting_index)
STRINGLIST *sl;
int copy, starting_index;
int alloc, starting_index;
{
WORD_LIST *list;
if (sl == 0 || sl->list == 0)
return ((WORD_LIST *)NULL);
list = argv_to_word_list (sl->list, copy, starting_index);
list = strvec_to_word_list (sl->list, alloc, starting_index);
return list;
}

View file

@ -1,6 +1,6 @@
/* stringvec.c - function for managing arrays of strings. */
/* stringvec.c - functions for managing arrays of strings. */
/* Copyright (C) 2000 Free Software Foundation, Inc.
/* Copyright (C) 2000-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@ -32,34 +32,25 @@
#include "shell.h"
#ifdef INCLUDE_UNUSED
/* Find NAME in ARRAY. Return the index of NAME, or -1 if not present.
ARRAY should be NULL terminated. */
int
find_name_in_array (name, array)
char *name, **array;
{
int i;
for (i = 0; array[i]; i++)
if (STREQ (name, array[i]))
return (i);
return (-1);
}
#endif
/* Allocate an array of strings with room for N members. */
char **
alloc_array (n)
strvec_create (n)
int n;
{
return ((char **)xmalloc ((n) * sizeof (char *)));
}
char **
strvec_resize (array, nsize)
char **array;
int nsize;
{
return ((char **)xrealloc (array, nsize * sizeof (char *)));
}
/* Return the length of ARRAY, a NULL terminated array of char *. */
int
array_len (array)
strvec_len (array)
char **array;
{
register int i;
@ -70,7 +61,7 @@ array_len (array)
/* Free the contents of ARRAY, a NULL terminated array of char *. */
void
free_array_members (array)
strvec_flush (array)
char **array;
{
register int i;
@ -83,26 +74,65 @@ free_array_members (array)
}
void
free_array (array)
strvec_dispose (array)
char **array;
{
if (array == 0)
return;
free_array_members (array);
strvec_flush (array);
free (array);
}
int
strvec_remove (array, name)
char **array, *name;
{
register int i, j;
char *x;
if (array == 0)
return 0;
for (i = 0; array[i]; i++)
if (STREQ (name, array[i]))
{
x = array[i];
for (j = i; array[j]; j++)
array[j] = array[j + 1];
free (x);
return 1;
}
return 0;
}
#ifdef INCLUDE_UNUSED
/* Find NAME in ARRAY. Return the index of NAME, or -1 if not present.
ARRAY should be NULL terminated. */
int
strvec_search (array, name)
char **array, *name;
{
int i;
for (i = 0; array[i]; i++)
if (STREQ (name, array[i]))
return (i);
return (-1);
}
#endif
/* Allocate and return a new copy of ARRAY and its contents. */
char **
copy_array (array)
strvec_copy (array)
char **array;
{
register int i;
int len;
char **ret;
len = array_len (array);
len = strvec_len (array);
ret = (char **)xmalloc ((len + 1) * sizeof (char *));
for (i = 0; array[i]; i++)
@ -115,7 +145,7 @@ copy_array (array)
/* Comparison routine for use with qsort() on arrays of strings. Uses
strcoll(3) if available, otherwise it uses strcmp(3). */
int
qsort_string_compare (s1, s2)
strvec_strcmp (s1, s2)
register char **s1, **s2;
{
#if defined (HAVE_STRCOLL)
@ -132,8 +162,71 @@ qsort_string_compare (s1, s2)
/* Sort ARRAY, a null terminated array of pointers to strings. */
void
sort_char_array (array)
strvec_sort (array)
char **array;
{
qsort (array, array_len (array), sizeof (char *), (QSFUNC *)qsort_string_compare);
qsort (array, strvec_len (array), sizeof (char *), (QSFUNC *)strvec_strcmp);
}
/* Cons up a new array of words. The words are taken from LIST,
which is a WORD_LIST *. If ALLOC is true, everything is malloc'ed,
so you should free everything in this array when you are done.
The array is NULL terminated. If IP is non-null, it gets the
number of words in the returned array. STARTING_INDEX says where
to start filling in the returned array; it can be used to reserve
space at the beginning of the array. */
char **
strvec_from_word_list (list, alloc, starting_index, ip)
WORD_LIST *list;
int alloc, starting_index, *ip;
{
int count;
char **array;
count = list_length (list);
array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *));
for (count = 0; count < starting_index; count++)
array[count] = (char *)NULL;
for (count = starting_index; list; count++, list = list->next)
array[count] = alloc ? savestring (list->word->word) : list->word->word;
array[count] = (char *)NULL;
if (ip)
*ip = count;
return (array);
}
/* Convert an array of strings into the form used internally by the shell.
ALLOC means to allocate new storage for each WORD_DESC in the returned
list rather than copy the values in ARRAY. STARTING_INDEX says where
in ARRAY to begin. */
WORD_LIST *
strvec_to_word_list (array, alloc, starting_index)
char **array;
int alloc, starting_index;
{
WORD_LIST *list;
WORD_DESC *w;
int i, count;
if (array == 0 || array[0] == 0)
return (WORD_LIST *)NULL;
for (count = 0; array[count]; count++)
;
for (i = starting_index, list = (WORD_LIST *)NULL; i < count; i++)
{
w = make_bare_word (alloc ? array[i] : "");
if (alloc == 0)
{
free (w->word);
w->word = array[i];
}
list = make_word_list (w, list);
}
return (REVERSE_LIST (list, WORD_LIST *));
}

View file

@ -40,14 +40,15 @@
/* 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. FOR_ECHO is a flag that
means, if 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. */
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 */
char *
ansicstr (string, len, for_echo, sawc, rlen)
ansicstr (string, len, flags, sawc, rlen)
char *string;
int len, for_echo, *sawc, *rlen;
int len, flags, *sawc, *rlen;
{
int c, temp;
char *ret, *r, *s;
@ -55,7 +56,7 @@ ansicstr (string, len, for_echo, sawc, rlen)
if (string == 0 || *string == '\0')
return ((char *)NULL);
ret = (char *)xmalloc (len + 1);
ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
for (r = ret, s = string; s && *s; )
{
c = *s++;
@ -81,7 +82,12 @@ ansicstr (string, len, for_echo, sawc, rlen)
case 't': c = '\t'; break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
for (temp = 2, c -= '0'; ISOCTAL (*s) && temp--; s++)
/* 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;
@ -99,7 +105,7 @@ ansicstr (string, len, for_echo, sawc, rlen)
case '\\':
break;
case '\'':
if (for_echo)
if (flags & 1)
*r++ = '\\';
break;
case 'c':
@ -111,8 +117,17 @@ ansicstr (string, len, for_echo, sawc, rlen)
*rlen = r - ret;
return ret;
}
else if ((flags & 1) == 0 && (c = *s))
{
s++;
c = TOCTRL(c);
break;
}
/*FALLTHROUGH*/
default: *r++ = '\\'; break;
}
if ((flags & 2) && (c == CTLESC || c == CTLNUL))
*r++ = CTLESC;
*r++ = c;
}
}
@ -129,7 +144,7 @@ ansic_quote (str, flags, rlen)
char *str;
int flags, *rlen;
{
char *r, *ret, *s, obuf[8];
char *r, *ret, *s;
int l, rsize, t;
unsigned char c;
@ -137,7 +152,7 @@ ansic_quote (str, flags, rlen)
return ((char *)0);
l = strlen (str);
rsize = 2 * l + 4;
rsize = 4 * l + 4;
r = ret = (char *)xmalloc (rsize);
*r++ = '$';
@ -169,12 +184,10 @@ ansic_quote (str, flags, rlen)
default:
if (ISPRINT (c) == 0)
{
sprintf (obuf, "\\%.3o", c);
t = r - ret;
RESIZE_MALLOCED_BUFFER (ret, t, 5, rsize, 16);
r = ret + t; /* in case reallocated */
for (t = 0; t < 4; t++)
*r++ = obuf[t];
*r++ = '\\';
*r++ = TOCHAR ((c >> 6) & 07);
*r++ = TOCHAR ((c >> 3) & 07);
*r++ = TOCHAR (c & 07);
continue;
}
l = 0;
@ -193,6 +206,7 @@ ansic_quote (str, flags, rlen)
}
/* return 1 if we need to quote with $'...' because of non-printing chars. */
int
ansic_shouldquote (string)
const char *string;
{
@ -208,3 +222,32 @@ ansic_shouldquote (string)
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);
}
}

View file

@ -24,6 +24,7 @@
#include <bashtypes.h>
#include <posixstat.h>
#include <posixtime.h>
#include <filecntl.h>
#if defined (HAVE_UNISTD_H)

78
lib/sh/xstrchr.c Normal file
View file

@ -0,0 +1,78 @@
/* xstrchr.c - strchr(3) that handles multibyte characters. */
/* Copyright (C) 2002 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>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include "bashansi.h"
#include "shmbutil.h"
#undef xstrchr
/* In some locales, the non-first byte of some multibyte characters have
the same value as some ascii character. Faced with these strings, a
legacy strchr() might return the wrong value. */
char *
#if defined (PROTOTYPES)
xstrchr (const char *s, int c)
#else
xstrchr (s, c)
const char *s;
int c;
#endif
{
#if HANDLE_MULTIBYTE
char *pos;
mbstate_t state;
size_t strlength, mblength;
/* The locale encodings with said weird property are BIG5, BIG5-HKSCS,
GBK, GB18030, SHIFT_JIS, and JOHAB. They exhibit the problem only
when c >= 0x30. We can therefore use the faster bytewise search if
c <= 0x30. */
if ((unsigned char)c >= '0' && MB_CUR_MAX > 1)
{
pos = (char *)s;
memset (&state, '\0', sizeof(mbstate_t));
strlength = strlen (s);
while (strlength > 0)
{
mblength = mbrlen (pos, strlength, &state);
if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
mblength = 1;
if (c == (unsigned char)*pos)
return pos;
strlength -= mblength;
pos += mblength;
}
return ((char *)NULL);
}
else
#endif
return (strchr (s, c));
}

Some files were not shown because too many files have changed in this diff Show more