diff options
Diffstat (limited to 'lib')
248 files changed, 84346 insertions, 0 deletions
diff --git a/lib/glob/Makefile.in b/lib/glob/Makefile.in new file mode 100644 index 0000000..27e34f9 --- /dev/null +++ b/lib/glob/Makefile.in @@ -0,0 +1,169 @@ +## -*- text -*- #################################################### +# # +# Makefile for the GNU Glob Library. # +# # +#################################################################### +# +# Copyright (C) 1996-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +PROFILE_FLAGS = @PROFILE_FLAGS@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +STYLE_CFLAGS = @STYLE_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +BASHINCDIR = ${topdir}/include + +INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib + +CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) ${INCLUDES} $(CPPFLAGS) \ + $(STYLE_CFLAGS) $(LOCAL_CFLAGS) $(CFLAGS) ${ADDON_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. +.c.o: + $(RM) $@ + $(CC) -c $(CCFLAGS) $< + +# The name of the library target. +LIBRARY_NAME = libglob.a + +# The C code source files for this library. +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 smatch.o xmbsrtowcs.o gmisc.o + +# The texinfo files which document this library. +DOCSOURCE = doc/glob.texi +DOCOBJECT = doc/glob.dvi +DOCSUPPORT = doc/Makefile +DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT) + +SUPPORT = Makefile ChangeLog $(DOCSUPPORT) + +SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE) + +THINGS_TO_TAR = $(SOURCES) $(SUPPORT) + +###################################################################### + +all: $(LIBRARY_NAME) + +$(LIBRARY_NAME): $(OBJECTS) + $(RM) -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +what-tar: + @for file in $(THINGS_TO_TAR); do \ + echo $(selfdir)$$file; \ + done + +documentation: force + -(cd doc; $(MAKE) $(MFLAGS)) +force: + +# The rule for 'includes' is written funny so that the if statement +# always returns TRUE unless there really was an error installing the +# include files. +install: + +clean: + rm -f $(OBJECTS) $(LIBRARY_NAME) + -(cd doc && $(MAKE) $(MFLAGS) $@ ) + +realclean distclean maintainer-clean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + $(RM) -f Makefile + +mostlyclean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +${BUILD_DIR}/pathnames.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile + -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} pathnames.h ) + +###################################################################### +# # +# Dependencies for the object files which make up this library. # +# # +###################################################################### + +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)/stdc.h + +glob.o: $(BUILD_DIR)/config.h +glob.o: $(topdir)/shell.h $(BUILD_DIR)/pathnames.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.h +glob.o: $(BASHINCDIR)/shmbutil.h +glob.o: $(topdir)/xmalloc.h + +gmisc.o: $(BUILD_DIR)/config.h +gmisc.o: $(topdir)/bashtypes.h $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h +gmisc.o: $(BASHINCDIR)/shmbutil.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 +glob.o: glob.c +gmisc.o: gmisc.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 +gmisc.o: gm_loop.c +smatch.o: sm_loop.c diff --git a/lib/glob/collsyms.h b/lib/glob/collsyms.h new file mode 100644 index 0000000..d56df61 --- /dev/null +++ b/lib/glob/collsyms.h @@ -0,0 +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-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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* 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 smatch.c:collsym(). */ + +typedef struct _COLLSYM { + XCHAR *name; + CHAR code; +} __COLLSYM; + +static __COLLSYM POSIXCOLL [] = +{ + { 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__ + { L("alert"), L('\a') }, +#else + { L("alert"), L('\007') }, +#endif + { 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 */ + { 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 */ + { 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 }, +}; + +#undef _COLLSYM +#undef __COLLSYM +#undef POSIXCOLL diff --git a/lib/glob/doc/Makefile b/lib/glob/doc/Makefile new file mode 100644 index 0000000..8dca606 --- /dev/null +++ b/lib/glob/doc/Makefile @@ -0,0 +1,5 @@ +all: + cp glob.texi glob.info + +clean distclean mostlyclean maintainer-clean: + rm -f glob.?? glob.info diff --git a/lib/glob/doc/glob.texi b/lib/glob/doc/glob.texi new file mode 100644 index 0000000..0262ef1 --- /dev/null +++ b/lib/glob/doc/glob.texi @@ -0,0 +1 @@ +Nothing happens here. diff --git a/lib/glob/glob.c b/lib/glob/glob.c new file mode 100644 index 0000000..b66af85 --- /dev/null +++ b/lib/glob/glob.c @@ -0,0 +1,1609 @@ +/* glob.c -- file-name wildcard pattern matching for Bash. + + Copyright (C) 1985-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* To whomever it may concern: I have never seen the code which most + Unix programs use to perform this function. I wrote this from scratch + based on specifications for the pattern matching. --RMS. */ + +#include <config.h> + +#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) + #pragma alloca +#endif /* _AIX && RISC6000 && !__GNUC__ */ + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#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 + +#include "stdc.h" +#include "memalloc.h" + +#include <signal.h> + +#include "shell.h" +#include "general.h" + +#include "glob.h" +#include "strmatch.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__) +# define NULL ((void *) 0) +# else +# define NULL 0x0 +# endif /* __STDC__ */ +#endif /* !NULL */ + +#if !defined (FREE) +# define FREE(x) if (x) free (x) +#endif + +/* Don't try to alloca() more than this much memory for `struct globval' + in glob_vector() */ +#ifndef ALLOCA_MAX +# define ALLOCA_MAX 100000 +#endif + +struct globval + { + struct globval *next; + char *name; + }; + +extern void throw_to_top_level PARAMS((void)); +extern int sh_eaccess PARAMS((const char *, int)); +extern char *sh_makepath PARAMS((const char *, const char *, int)); +extern int signal_is_pending PARAMS((int)); +extern void run_pending_traps PARAMS((void)); + +extern int extended_glob; + +/* Global variable which controls whether or not * matches .*. + Non-zero means don't match .*. */ +int noglob_dot_filenames = 1; + +/* Global variable which controls whether or not filename globbing + is done without regard to case. */ +int glob_ignore_case = 0; + +/* Global variable controlling whether globbing ever returns . or .. + regardless of the pattern. If set to 1, no glob pattern will ever + match `.' or `..'. Disabled by default. */ +int glob_always_skip_dot_and_dotdot = 1; + +/* Global variable to return to signify an error in globbing. */ +char *glob_error_return; + +static struct globval finddirs_error_return; + +/* Some forward declarations. */ +static int skipname PARAMS((char *, char *, int)); +#if HANDLE_MULTIBYTE +static int mbskipname PARAMS((char *, char *, int)); +#endif +void udequote_pathname PARAMS((char *)); +#if HANDLE_MULTIBYTE +void wcdequote_pathname PARAMS((wchar_t *)); +static void wdequote_pathname PARAMS((char *)); +static void dequote_pathname PARAMS((char *)); +#else +# define dequote_pathname(p) udequote_pathname(p) +#endif +static int glob_testdir PARAMS((char *, int)); +static char **glob_dir_to_array PARAMS((char *, char **, int)); + +/* Make sure these names continue to agree with what's in smatch.c */ +extern char *glob_patscan PARAMS((char *, char *, int)); +extern wchar_t *glob_patscan_wc PARAMS((wchar_t *, wchar_t *, int)); + +/* And this from gmisc.c/gm_loop.c */ +extern int wextglob_pattern_p PARAMS((wchar_t *)); + +extern char *glob_dirscan PARAMS((char *, int)); + +/* Compile `glob_loop.c' for single-byte characters. */ +#define GCHAR unsigned char +#define CHAR 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 GCHAR wchar_t +#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; +{ +#if HANDLE_MULTIBYTE + size_t n; + wchar_t *wpattern; + int r; + + if (MB_CUR_MAX == 1 || mbsmbchar (pattern) == 0) + return (internal_glob_pattern_p ((unsigned char *)pattern)); + + /* Convert strings to wide chars, and call the multibyte version. */ + n = xdupmbstowcs (&wpattern, NULL, pattern); + if (n == (size_t)-1) + /* Oops. Invalid multibyte sequence. Try it as single-byte sequence. */ + return (internal_glob_pattern_p ((unsigned char *)pattern)); + + r = internal_glob_wpattern_p (wpattern); + free (wpattern); + + return r; +#else + return (internal_glob_pattern_p ((unsigned char *)pattern)); +#endif +} + +#if EXTENDED_GLOB + +#if defined (HANDLE_MULTIBYTE) +# define XSKIPNAME(p, d, f) mbskipname(p, d, f) +#else +# define XSKIPNAME(p, d, f) skipname(p, d, f) +#endif + +/* Return 1 if all subpatterns in the extended globbing pattern PAT indicate + that the name should be skipped. XXX - doesn't handle pattern negation, + not sure if it should */ +static int +extglob_skipname (pat, dname, flags) + char *pat, *dname; + int flags; +{ + char *pp, *pe, *t, *se; + int n, r, negate, wild, nullpat, xflags; + + negate = *pat == '!'; + wild = *pat == '*' || *pat == '?'; + pp = pat + 2; + se = pp + strlen (pp); /* end of pattern string */ + pe = glob_patscan (pp, se, 0); /* end of extglob pattern */ + + /* if pe == 0, this is an invalid extglob pattern */ + if (pe == 0) + return 0; + + xflags = flags | ( negate ? GX_NEGATE : 0); + + /* if pe != se we have more of the pattern at the end of the extglob + pattern. Check the easy case first ( */ + if (pe == se && *pe == 0 && pe[-1] == ')' && (t = strchr (pp, '|')) == 0) + { + pe[-1] = '\0'; + /* This is where we check whether the pattern is being negated and + match all files beginning with `.' if the pattern begins with a + literal `.'. */ + r = XSKIPNAME (pp, dname, xflags); /*(*/ + pe[-1] = ')'; + return r; + } + + /* Is the extglob pattern between the parens the null pattern? The null + pattern can match nothing, so should we check any remaining portion of + the pattern? */ + nullpat = pe >= (pat + 2) && pe[-2] == '(' && pe[-1] == ')'; + + /* check every subpattern */ + while (t = glob_patscan (pp, pe, '|')) + { + /* If T == PE and *T == 0 (&& PE[-1] == RPAREN), we have hit the end + of a pattern with no trailing characters. */ + n = t[-1]; /* ( */ + if (extglob_pattern_p (pp) && n == ')') /* nested extglob? */ + t[-1] = n; /* no-op for now */ + else + t[-1] = '\0'; + r = XSKIPNAME (pp, dname, xflags); + t[-1] = n; + if (r == 0) /* if any pattern says not skip, we don't skip */ + return r; + pp = t; + if (pp == pe) + break; + } + + /* glob_patscan might find end of string */ + if (pp == se) + return r; + + /* but if it doesn't then we didn't match a leading dot */ + if (wild && *pe) /* if we can match zero instances, check further */ + return (XSKIPNAME (pe, dname, flags)); + + return 1; +} +#endif + +/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned + with matching leading `.'. */ +static int +skipname (pat, dname, flags) + char *pat; + char *dname; + int flags; +{ + int i; + +#if EXTENDED_GLOB + if (extglob_pattern_p (pat)) /* XXX */ + return (extglob_skipname (pat, dname, flags)); +#endif + + if (glob_always_skip_dot_and_dotdot && DOT_OR_DOTDOT (dname)) + return 1; + + /* 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] != '.') && + DOT_OR_DOTDOT (dname)) + return 1; + +#if 0 + /* This is where we check whether the pattern is being negated and + match all files beginning with `.' if the pattern begins with a + literal `.'. This is the negation of the next clause. */ + else if ((flags & GX_NEGATE) && noglob_dot_filenames == 0 && + dname[0] == '.' && + (pat[0] == '.' || (pat[0] == '\\' && pat[1] == '.'))) + return 0; +#endif + + /* If a dot must be explicitly 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 + +static int +wskipname (pat, dname, flags) + wchar_t *pat, *dname; + int flags; +{ + int i; + + if (glob_always_skip_dot_and_dotdot && WDOT_OR_DOTDOT (dname)) + return 1; + + /* 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] != L'.' && + (pat[0] != L'\\' || pat[1] != L'.') && + WDOT_OR_DOTDOT (dname)) + return 1; + +#if 0 + /* This is where we check whether the pattern is being negated and + match all files beginning with `.' if the pattern begins with a + literal `.'. This is the negation of the next clause. */ + else if ((flags & GX_NEGATE) && noglob_dot_filenames == 0 && + dname[0] == L'.' && + (pat[0] == L'.' || (pat[0] == L'\\' && pat[1] == L'.'))) + return 0; +#endif + + /* If a leading dot must be explicitly matched, check to see if the + pattern and dirname both have one. */ + else if (noglob_dot_filenames && dname[0] == L'.' && + pat[0] != L'.' && (pat[0] != L'\\' || pat[1] != L'.')) + return 1; + + return 0; +} + +static int +wextglob_skipname (pat, dname, flags) + wchar_t *pat, *dname; + int flags; +{ +#if EXTENDED_GLOB + wchar_t *pp, *pe, *t, *se, n; + int r, negate, wild, nullpat, xflags; + + negate = *pat == L'!'; + wild = *pat == L'*' || *pat == L'?'; + pp = pat + 2; + se = pp + wcslen (pp); + pe = glob_patscan_wc (pp, se, 0); + + /* if pe == 0, this is an invalid extglob pattern */ + if (pe == 0) + return 0; + + xflags = flags | ( negate ? GX_NEGATE : 0); + + /* if pe != se we have more of the pattern at the end of the extglob + pattern. Check the easy case first ( */ + if (pe == se && *pe == L'\0' && pe[-1] == L')' && (t = wcschr (pp, L'|')) == 0) + { + pe[-1] = L'\0'; + r = wskipname (pp, dname, xflags); /*(*/ + pe[-1] = L')'; + return r; + } + + /* Is the extglob pattern between the parens the null pattern? The null + pattern can match nothing, so should we check any remaining portion of + the pattern? */ + nullpat = pe >= (pat + 2) && pe[-2] == L'(' && pe[-1] == L')'; + + /* check every subpattern */ + while (t = glob_patscan_wc (pp, pe, '|')) + { + n = t[-1]; /* ( */ + if (wextglob_pattern_p (pp) && n == L')') /* nested extglob? */ + t[-1] = n; /* no-op for now */ + else + t[-1] = L'\0'; + r = wskipname (pp, dname, xflags); + t[-1] = n; + if (r == 0) + return 0; + pp = t; + if (pp == pe) + break; + } + + /* glob_patscan_wc might find end of string */ + if (pp == se) + return r; + + /* but if it doesn't then we didn't match a leading dot */ + if (wild && *pe != L'\0') + return (wskipname (pe, dname, flags)); + + return 1; +#else + return (wskipname (pat, dname, flags)); +#endif +} + +/* 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, flags) + char *pat, *dname; + int flags; +{ + int ret, ext; + wchar_t *pat_wc, *dn_wc; + size_t pat_n, dn_n; + + if (mbsmbchar (dname) == 0 && mbsmbchar (pat) == 0) + return (skipname (pat, dname, flags)); + + ext = 0; +#if EXTENDED_GLOB + ext = extglob_pattern_p (pat); +#endif + + pat_wc = dn_wc = (wchar_t *)NULL; + + pat_n = xdupmbstowcs (&pat_wc, NULL, pat); + if (pat_n != (size_t)-1) + dn_n = xdupmbstowcs (&dn_wc, NULL, dname); + + ret = 0; + if (pat_n != (size_t)-1 && dn_n !=(size_t)-1) + ret = ext ? wextglob_skipname (pat_wc, dn_wc, flags) : wskipname (pat_wc, dn_wc, flags); + else + ret = skipname (pat, dname, flags); + + FREE (pat_wc); + FREE (dn_wc); + + return ret; +} +#endif /* HANDLE_MULTIBYTE */ + +/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */ +void +udequote_pathname (pathname) + char *pathname; +{ + register int i, j; + + for (i = j = 0; pathname && pathname[i]; ) + { + if (pathname[i] == '\\') + i++; + + pathname[j++] = pathname[i++]; + + if (pathname[i - 1] == 0) + break; + } + if (pathname) + pathname[j] = '\0'; +} + +#if HANDLE_MULTIBYTE +/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */ +void +wcdequote_pathname (wpathname) + wchar_t *wpathname; +{ + int i, j; + + for (i = j = 0; wpathname && wpathname[i]; ) + { + if (wpathname[i] == L'\\') + i++; + + wpathname[j++] = wpathname[i++]; + + if (wpathname[i - 1] == L'\0') + break; + } + if (wpathname) + wpathname[j] = L'\0'; +} + +static void +wdequote_pathname (pathname) + char *pathname; +{ + mbstate_t ps; + size_t len, n; + wchar_t *wpathname; + int i, j; + wchar_t *orig_wpathname; + + if (mbsmbchar (pathname) == 0) + { + udequote_pathname (pathname); + return; + } + + len = strlen (pathname); + /* Convert the strings into wide characters. */ + n = xdupmbstowcs (&wpathname, NULL, pathname); + if (n == (size_t) -1) + { + /* Something wrong. Fall back to single-byte */ + udequote_pathname (pathname); + return; + } + orig_wpathname = wpathname; + + wcdequote_pathname (wpathname); + + /* Convert the wide character string into unibyte character set. */ + memset (&ps, '\0', sizeof(mbstate_t)); + n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps); + if (n == (size_t)-1 || (wpathname && *wpathname != 0)) /* what? now you tell me? */ + { + wpathname = orig_wpathname; + memset (&ps, '\0', sizeof(mbstate_t)); + n = xwcsrtombs (pathname, (const wchar_t **)&wpathname, len, &ps); + } + pathname[len] = '\0'; + + /* Can't just free wpathname here; wcsrtombs changes it in many cases. */ + free (orig_wpathname); +} + +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 (AFS) +# define GLOB_TESTNAME(name) (sh_eaccess (name, F_OK)) +# else /* AFS */ +# define GLOB_TESTNAME(name) (access (name, F_OK)) +# endif /* AFS */ +#endif /* !HAVE_LSTAT */ + +/* Return 0 if DIR is a directory, -2 if DIR is a symlink, -1 otherwise. */ +static int +glob_testdir (dir, flags) + char *dir; + int flags; +{ + struct stat finfo; + int r; + +/*itrace("glob_testdir: testing %s" flags = %d, dir, flags);*/ +#if defined (HAVE_LSTAT) + r = (flags & GX_ALLDIRS) ? lstat (dir, &finfo) : stat (dir, &finfo); +#else + r = stat (dir, &finfo); +#endif + if (r < 0) + return (-1); + +#if defined (S_ISLNK) + if (S_ISLNK (finfo.st_mode)) + return (-2); +#endif + + if (S_ISDIR (finfo.st_mode) == 0) + return (-1); + + return (0); +} + +/* Recursively scan SDIR for directories matching PAT (PAT is always `**'). + FLAGS is simply passed down to the recursive call to glob_vector. Returns + a list of matching directory names. EP, if non-null, is set to the last + element of the returned list. NP, if non-null, is set to the number of + directories in the returned list. These two variables exist for the + convenience of the caller (always glob_vector). */ +static struct globval * +finddirs (pat, sdir, flags, ep, np) + char *pat; + char *sdir; + int flags; + struct globval **ep; + int *np; +{ + char **r, *n; + int ndirs; + struct globval *ret, *e, *g; + +/*itrace("finddirs: pat = `%s' sdir = `%s' flags = 0x%x", pat, sdir, flags);*/ + e = ret = 0; + r = glob_vector (pat, sdir, flags); + if (r == 0 || r[0] == 0) + { + if (np) + *np = 0; + if (ep) + *ep = 0; + if (r && r != &glob_error_return) + free (r); + return (struct globval *)0; + } + for (ndirs = 0; r[ndirs] != 0; ndirs++) + { + g = (struct globval *) malloc (sizeof (struct globval)); + if (g == 0) + { + while (ret) /* free list built so far */ + { + g = ret->next; + free (ret); + ret = g; + } + + free (r); + if (np) + *np = 0; + if (ep) + *ep = 0; + return (&finddirs_error_return); + } + if (e == 0) + e = g; + + g->next = ret; + ret = g; + + g->name = r[ndirs]; + } + + free (r); + if (ep) + *ep = e; + if (np) + *np = ndirs; + + return ret; +} + +/* Return a vector of names of files in directory DIR + whose names match glob pattern PAT. + The names are not in any particular order. + Wildcards at the beginning of PAT do not match an initial period. + + The vector is terminated by an element that is a null pointer. + + To free the space allocated, first free the vector's elements, + then free the vector. + + Return 0 if cannot get enough memory to hold the pointer + and the names. + + Return -1 if cannot access directory DIR. + Look in errno for more information. */ + +char ** +glob_vector (pat, dir, flags) + char *pat; + char *dir; + int flags; +{ + DIR *d; + register struct dirent *dp; + struct globval *lastlink, *e, *dirlist; + register struct globval *nextlink; + register char *nextname, *npat, *subdir; + unsigned int count; + int lose, skip, ndirs, isdir, sdlen, add_current, patlen; + register char **name_vector; + register unsigned int i; + int mflags; /* Flags passed to strmatch (). */ + int pflags; /* flags passed to sh_makepath () */ + int hasglob; /* return value from glob_pattern_p */ + int nalloca; + struct globval *firstmalloc, *tmplink; + char *convfn; + + lastlink = 0; + count = lose = skip = add_current = 0; + + firstmalloc = 0; + nalloca = 0; + + name_vector = NULL; + +/*itrace("glob_vector: pat = `%s' dir = `%s' flags = 0x%x", pat, dir, flags);*/ + /* If PAT is empty, skip the loop, but return one (empty) filename. */ + if (pat == 0 || *pat == '\0') + { + if (glob_testdir (dir, 0) < 0) + return ((char **) &glob_error_return); + + nextlink = (struct globval *)alloca (sizeof (struct globval)); + if (nextlink == NULL) + return ((char **) NULL); + + nextlink->next = (struct globval *)0; + nextname = (char *) malloc (1); + if (nextname == 0) + lose = 1; + else + { + lastlink = nextlink; + nextlink->name = nextname; + nextname[0] = '\0'; + count = 1; + } + + skip = 1; + } + + patlen = (pat && *pat) ? strlen (pat) : 0; + + /* If the filename pattern (PAT) does not contain any globbing characters, + or contains a pattern with only backslash escapes (hasglob == 2), + we can dispense with reading the directory, and just see if there is + a filename `DIR/PAT'. If there is, and we can access it, just make the + vector to return and bail immediately. */ + hasglob = 0; + if (skip == 0 && ((hasglob = glob_pattern_p (pat)) == 0 || hasglob == 2)) + { + int dirlen; + struct stat finfo; + + if (glob_testdir (dir, 0) < 0) + return ((char **) &glob_error_return); + + dirlen = strlen (dir); + nextname = (char *)malloc (dirlen + patlen + 2); + npat = (char *)malloc (patlen + 1); + if (nextname == 0 || npat == 0) + { + FREE (nextname); + FREE (npat); + lose = 1; + } + else + { + strcpy (npat, pat); + dequote_pathname (npat); + + strcpy (nextname, dir); + nextname[dirlen++] = '/'; + strcpy (nextname + dirlen, npat); + + if (GLOB_TESTNAME (nextname) >= 0) + { + free (nextname); + nextlink = (struct globval *)alloca (sizeof (struct globval)); + if (nextlink) + { + nextlink->next = (struct globval *)0; + lastlink = nextlink; + nextlink->name = npat; + count = 1; + } + else + { + free (npat); + lose = 1; + } + } + else + { + free (nextname); + free (npat); + } + } + + skip = 1; + } + + if (skip == 0) + { + /* Open the directory, punting immediately if we cannot. If opendir + is not robust (i.e., it opens non-directories successfully), test + that DIR is a directory and punt if it's not. */ +#if defined (OPENDIR_NOT_ROBUST) + if (glob_testdir (dir, 0) < 0) + return ((char **) &glob_error_return); +#endif + + d = opendir (dir); + if (d == NULL) + return ((char **) &glob_error_return); + + /* Compute the flags that will be passed to strmatch(). We don't + need to do this every time through the loop. */ + mflags = (noglob_dot_filenames ? FNM_PERIOD : FNM_DOTDOT) | FNM_PATHNAME; + +#ifdef FNM_CASEFOLD + if (glob_ignore_case) + mflags |= FNM_CASEFOLD; +#endif + + if (extended_glob) + mflags |= FNM_EXTMATCH; + + add_current = ((flags & (GX_ALLDIRS|GX_ADDCURDIR)) == (GX_ALLDIRS|GX_ADDCURDIR)); + + /* Scan the directory, finding all names that match For each name that matches, allocate a struct globval + on the stack and store the name in it. + Chain those structs together; lastlink is the front of the chain. */ + while (1) + { + /* Make globbing interruptible in the shell. */ + if (interrupt_state || terminating_signal) + { + lose = 1; + break; + } + else if (signal_is_pending (SIGINT)) /* XXX - make SIGINT traps responsive */ + { + lose = 1; + break; + } + + dp = readdir (d); + if (dp == NULL) + break; + + /* If this directory entry is not to be used, try again. */ + if (REAL_DIR_ENTRY (dp) == 0) + continue; + +#if 0 + if (dp->d_name == 0 || *dp->d_name == 0) + continue; +#endif + +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name, flags)) + continue; + else +#endif + if (skipname (pat, dp->d_name, flags)) + continue; + + /* If we're only interested in directories, don't bother with files */ + if (flags & (GX_MATCHDIRS|GX_ALLDIRS)) + { + pflags = (flags & GX_ALLDIRS) ? MP_RMDOT : 0; + if (flags & GX_NULLDIR) + pflags |= MP_IGNDOT; + subdir = sh_makepath (dir, dp->d_name, pflags); + isdir = glob_testdir (subdir, flags); + if (isdir < 0 && (flags & GX_MATCHDIRS)) + { + free (subdir); + continue; + } + } + + if (flags & GX_ALLDIRS) + { + if (isdir == 0) + { + dirlist = finddirs (pat, subdir, (flags & ~GX_ADDCURDIR), &e, &ndirs); + if (dirlist == &finddirs_error_return) + { + free (subdir); + lose = 1; + break; + } + if (ndirs) /* add recursive directories to list */ + { + if (firstmalloc == 0) + firstmalloc = e; + e->next = lastlink; + lastlink = dirlist; + count += ndirs; + } + } + + /* XXX - should we even add this if it's not a directory? */ + nextlink = (struct globval *) malloc (sizeof (struct globval)); + if (firstmalloc == 0) + firstmalloc = nextlink; + sdlen = strlen (subdir); + nextname = (char *) malloc (sdlen + 1); + if (nextlink == 0 || nextname == 0) + { + if (firstmalloc && firstmalloc == nextlink) + firstmalloc = 0; + /* If we reset FIRSTMALLOC we can free this here. */ + FREE (nextlink); + FREE (nextname); + free (subdir); + lose = 1; + break; + } + nextlink->next = lastlink; + lastlink = nextlink; + nextlink->name = nextname; + bcopy (subdir, nextname, sdlen + 1); + free (subdir); + ++count; + continue; + } + else if (flags & GX_MATCHDIRS) + free (subdir); + + convfn = fnx_fromfs (dp->d_name, D_NAMLEN (dp)); + if (strmatch (pat, convfn, mflags) != FNM_NOMATCH) + { + if (nalloca < ALLOCA_MAX) + { + nextlink = (struct globval *) alloca (sizeof (struct globval)); + nalloca += sizeof (struct globval); + } + else + { + nextlink = (struct globval *) malloc (sizeof (struct globval)); + if (firstmalloc == 0) + firstmalloc = nextlink; + } + + nextname = (char *) malloc (D_NAMLEN (dp) + 1); + if (nextlink == 0 || nextname == 0) + { + /* We free NEXTLINK here, since it won't be added to the + LASTLINK chain. If we used malloc, and it returned non- + NULL, firstmalloc will be set to something valid. If it's + NEXTLINK, reset it before we free NEXTLINK to avoid + duplicate frees. If not, it will be taken care of by the + loop below with TMPLINK. */ + if (firstmalloc) + { + if (firstmalloc == nextlink) + firstmalloc = 0; + FREE (nextlink); + } + FREE (nextname); + lose = 1; + break; + } + nextlink->next = lastlink; + lastlink = nextlink; + nextlink->name = nextname; + bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1); + ++count; + } + } + + (void) closedir (d); + } + + /* compat: if GX_ADDCURDIR, add the passed directory also. Add an empty + directory name as a placeholder if GX_NULLDIR (in which case the passed + directory name is "."). */ + if (add_current && lose == 0) + { + sdlen = strlen (dir); + nextname = (char *)malloc (sdlen + 1); + nextlink = (struct globval *) malloc (sizeof (struct globval)); + if (nextlink == 0 || nextname == 0) + { + FREE (nextlink); + FREE (nextname); + lose = 1; + } + else + { + nextlink->name = nextname; + nextlink->next = lastlink; + lastlink = nextlink; + if (flags & GX_NULLDIR) + nextname[0] = '\0'; + else + bcopy (dir, nextname, sdlen + 1); + ++count; + } + } + + if (lose == 0) + { + name_vector = (char **) malloc ((count + 1) * sizeof (char *)); + lose |= name_vector == NULL; + } + + /* Have we run out of memory or been interrupted? */ + if (lose) + { + tmplink = 0; + + /* Here free the strings we have got. */ + while (lastlink) + { + /* Since we build the list in reverse order, the first N entries + will be allocated with malloc, if firstmalloc is set, from + lastlink to firstmalloc. */ + if (firstmalloc) + { + if (lastlink == firstmalloc) + firstmalloc = 0; + tmplink = lastlink; + } + else + tmplink = 0; + free (lastlink->name); + lastlink = lastlink->next; + FREE (tmplink); + } + + /* Don't call QUIT; here; let higher layers deal with it. */ + + return ((char **)NULL); + } + + /* Copy the name pointers from the linked list into the vector. */ + for (tmplink = lastlink, i = 0; i < count; ++i) + { + name_vector[i] = tmplink->name; + tmplink = tmplink->next; + } + + name_vector[count] = NULL; + + /* If we allocated some of the struct globvals, free them now. */ + if (firstmalloc) + { + tmplink = 0; + while (lastlink) + { + tmplink = lastlink; + if (lastlink == firstmalloc) + lastlink = firstmalloc = 0; + else + lastlink = lastlink->next; + free (tmplink); + } + } + + 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. This should handle GX_MARKDIRS. */ +static char ** +glob_dir_to_array (dir, array, flags) + char *dir, **array; + int flags; +{ + register unsigned int i, l; + int add_slash; + char **result, *new; + struct stat sb; + + l = strlen (dir); + if (l == 0) + { + 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] != '/'; + + i = 0; + while (array[i] != NULL) + ++i; + + result = (char **) malloc ((i + 1) * sizeof (char *)); + if (result == NULL) + return (NULL); + + for (i = 0; array[i] != NULL; i++) + { + /* 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) + { + int ind; + for (ind = 0; ind < i; ind++) + free (result[ind]); + free (result); + return (NULL); + } + + strcpy (result[i], dir); + if (add_slash) + result[i][l] = '/'; + if (array[i][0]) + { + 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'; + } + } + } + else + result[i][l+add_slash] = '\0'; + } + result[i] = NULL; + + /* Free the input array. */ + for (i = 0; array[i] != NULL; i++) + free (array[i]); + free ((char *) 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, flags) + char *pathname; + int flags; +{ + char **result, **new_result; + unsigned int result_size; + char *directory_name, *filename, *dname, *fn; + unsigned int directory_len; + int free_dirname; /* flag */ + int dflags, hasglob; + + result = (char **) malloc (sizeof (char *)); + result_size = 1; + if (result == NULL) + return (NULL); + + result[0] = NULL; + + directory_name = NULL; + + /* Find the filename. */ + filename = strrchr (pathname, '/'); +#if defined (EXTENDED_GLOB) + if (filename && extended_glob) + { + fn = glob_dirscan (pathname, '/'); +#if DEBUG_MATCHING + if (fn != filename) + fprintf (stderr, "glob_filename: glob_dirscan: fn (%s) != filename (%s)\n", fn ? fn : "(null)", filename); +#endif + filename = fn; + } +#endif + + if (filename == NULL) + { + filename = pathname; + directory_name = ""; + directory_len = 0; + free_dirname = 0; + } + else + { + directory_len = (filename - pathname) + 1; + directory_name = (char *) malloc (directory_len + 1); + + if (directory_name == 0) /* allocation failed? */ + { + free (result); + return (NULL); + } + + bcopy (pathname, directory_name, directory_len); + directory_name[directory_len] = '\0'; + ++filename; + free_dirname = 1; + } + + hasglob = 0; + /* If directory_name contains globbing characters, then we + have to expand the previous levels. Just recurse. + If glob_pattern_p returns != [0,1] we have a pattern that has backslash + quotes but no unquoted glob pattern characters. We dequote it below. */ + if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1) + { + char **directories, *d, *p; + register unsigned int i; + int all_starstar, last_starstar; + + all_starstar = last_starstar = 0; + d = directory_name; + dflags = flags & ~GX_MARKDIRS; + /* Collapse a sequence of ** patterns separated by one or more slashes + to a single ** terminated by a slash or NUL */ + if ((flags & GX_GLOBSTAR) && d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0')) + { + p = d; + while (d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0')) + { + p = d; + if (d[2]) + { + d += 3; + while (*d == '/') + d++; + if (*d == 0) + break; + } + } + if (*d == 0) + all_starstar = 1; + d = p; + dflags |= GX_ALLDIRS|GX_ADDCURDIR; + directory_len = strlen (d); + } + + /* If there is a non [star][star]/ component in directory_name, we + still need to collapse trailing sequences of [star][star]/ into + a single one and note that the directory name ends with [star][star], + so we can compensate if filename is [star][star] */ + if ((flags & GX_GLOBSTAR) && all_starstar == 0) + { + int dl, prev; + prev = dl = directory_len; + while (dl >= 4 && d[dl - 1] == '/' && + d[dl - 2] == '*' && + d[dl - 3] == '*' && + d[dl - 4] == '/') + prev = dl, dl -= 3; + if (dl != directory_len) + last_starstar = 1; + directory_len = prev; + } + + /* If the directory name ends in [star][star]/ but the filename is + [star][star], just remove the final [star][star] from the directory + so we don't have to scan everything twice. */ + if (last_starstar && directory_len > 4 && + filename[0] == '*' && filename[1] == '*' && filename[2] == 0) + { + directory_len -= 3; + } + + if (d[directory_len - 1] == '/') + d[directory_len - 1] = '\0'; + + directories = glob_filename (d, dflags|GX_RECURSE); + + if (free_dirname) + { + free (directory_name); + directory_name = NULL; + } + + if (directories == NULL) + goto memory_error; + else if (directories == (char **)&glob_error_return) + { + free ((char *) result); + return ((char **) &glob_error_return); + } + else if (*directories == NULL) + { + free ((char *) directories); + free ((char *) result); + return ((char **) &glob_error_return); + } + + /* If we have something like [star][star]/[star][star], it's no use to + glob **, then do it again, and throw half the results away. */ + if (all_starstar && filename[0] == '*' && filename[1] == '*' && filename[2] == 0) + { + free ((char *) directories); + free (directory_name); + directory_name = NULL; + directory_len = 0; + goto only_filename; + } + + /* We have successfully globbed the preceding directory name. + For each name in DIRECTORIES, call glob_vector on it and + FILENAME. Concatenate the results together. */ + for (i = 0; directories[i] != NULL; ++i) + { + char **temp_results; + int shouldbreak; + + shouldbreak = 0; + /* XXX -- we've recursively scanned any directories resulting from + a `**', so turn off the flag. We turn it on again below if + filename is `**' */ + /* Scan directory even on a NULL filename. That way, `*h/' + returns only directories ending in `h', instead of all + files ending in `h' with a `/' appended. */ + dname = directories[i]; + dflags = flags & ~(GX_MARKDIRS|GX_ALLDIRS|GX_ADDCURDIR); + /* last_starstar? */ + if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + dflags |= GX_ALLDIRS|GX_ADDCURDIR; + if (dname[0] == '\0' && filename[0]) + { + dflags |= GX_NULLDIR; + dname = "."; /* treat null directory name and non-null filename as current directory */ + } + + /* Special handling for symlinks to directories with globstar on */ + if (all_starstar && (dflags & GX_NULLDIR) == 0) + { + int dlen; + + /* If we have a directory name that is not null (GX_NULLDIR above) + and is a symlink to a directory, we return the symlink if + we're not `descending' into it (filename[0] == 0) and return + glob_error_return (which causes the code below to skip the + name) otherwise. I should fold this into a test that does both + checks instead of calling stat twice. */ + if (glob_testdir (dname, flags|GX_ALLDIRS) == -2 && glob_testdir (dname, 0) == 0) + { + if (filename[0] != 0) + temp_results = (char **)&glob_error_return; /* skip */ + else + { + /* Construct array to pass to glob_dir_to_array */ + temp_results = (char **)malloc (2 * sizeof (char *)); + if (temp_results == NULL) + goto memory_error; + temp_results[0] = (char *)malloc (1); + if (temp_results[0] == 0) + { + free (temp_results); + goto memory_error; + } + **temp_results = '\0'; + temp_results[1] = NULL; + dflags |= GX_SYMLINK; /* mostly for debugging */ + } + } + else + temp_results = glob_vector (filename, dname, dflags); + } + else + temp_results = glob_vector (filename, dname, dflags); + + /* Handle error cases. */ + if (temp_results == NULL) + goto memory_error; + else if (temp_results == (char **)&glob_error_return) + /* This filename is probably not a directory. Ignore it. */ + ; + else + { + char **array; + register unsigned int l; + + /* If we're expanding **, we don't need to glue the directory + name to the results; we've already done it in glob_vector */ + if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && (filename[2] == '\0' || filename[2] == '/')) + { + /* When do we remove null elements from temp_results? And + how to avoid duplicate elements in the final result? */ + /* If (dflags & GX_NULLDIR) glob_filename potentially left a + NULL placeholder in the temp results just in case + glob_vector/glob_dir_to_array did something with it, but + if it didn't, and we're not supposed to be passing them + through for some reason ((flags & GX_NULLDIR) == 0) we + need to remove all the NULL elements from the beginning + of TEMP_RESULTS. */ + /* If we have a null directory name and ** as the filename, + we have just searched for everything from the current + directory on down. Break now (shouldbreak = 1) to avoid + duplicate entries in the final result. */ +#define NULL_PLACEHOLDER(x) ((x) && *(x) && **(x) == 0) + if ((dflags & GX_NULLDIR) && (flags & GX_NULLDIR) == 0 && + NULL_PLACEHOLDER (temp_results)) +#undef NULL_PLACEHOLDER + { + register int i, n; + for (n = 0; temp_results[n] && *temp_results[n] == 0; n++) + ; + i = n; + do + temp_results[i - n] = temp_results[i]; + while (temp_results[i++] != 0); + array = temp_results; + shouldbreak = 1; + } + else + array = temp_results; + } + else if (dflags & GX_SYMLINK) + array = glob_dir_to_array (directories[i], temp_results, flags); + else + array = glob_dir_to_array (directories[i], temp_results, flags); + l = 0; + while (array[l] != NULL) + ++l; + + new_result = (char **)realloc (result, (result_size + l) * sizeof (char *)); + + if (new_result == NULL) + { + for (l = 0; array[l]; ++l) + free (array[l]); + free ((char *)array); + goto memory_error; + } + result = new_result; + + for (l = 0; array[l] != NULL; ++l) + result[result_size++ - 1] = array[l]; + + result[result_size - 1] = NULL; + + /* Note that the elements of ARRAY are not freed. */ + if (array != temp_results) + free ((char *) array); + else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + free (temp_results); /* expanding ** case above */ + + if (shouldbreak) + break; + } + } + /* Free the directories. */ + for (i = 0; directories[i]; i++) + free (directories[i]); + + free ((char *) directories); + + return (result); + } + +only_filename: + /* If there is only a directory name, return it. */ + if (*filename == '\0') + { + result = (char **) realloc ((char *) result, 2 * sizeof (char *)); + if (result == NULL) + { + if (free_dirname) + free (directory_name); + return (NULL); + } + /* If we have a directory name with quoted characters, and we are + being called recursively to glob the directory portion of a pathname, + we need to dequote the directory name before returning it so the + caller can read the directory */ + if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0) + { + dequote_pathname (directory_name); + directory_len = strlen (directory_name); + } + + /* We could check whether or not the dequoted directory_name is a + directory and return it here, returning the original directory_name + if not, but we don't do that. We do return the dequoted directory + name if we're not being called recursively and the dequoted name + corresponds to an actual directory. For better backwards compatibility, + we can return &glob_error_return unconditionally in this case. */ + + if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) == 0) + { + dequote_pathname (directory_name); + if (glob_testdir (directory_name, 0) < 0) + { + if (free_dirname) + free (directory_name); + free ((char *) result); + return ((char **)&glob_error_return); + } + } + + /* Handle GX_MARKDIRS here. */ + result[0] = (char *) malloc (directory_len + 1); + if (result[0] == NULL) + goto memory_error; + bcopy (directory_name, result[0], directory_len + 1); + if (free_dirname) + free (directory_name); + result[1] = NULL; + return (result); + } + else + { + char **temp_results; + + /* There are no unquoted globbing characters in DIRECTORY_NAME. + Dequote it before we try to open the directory since there may + be quoted globbing characters which should be treated verbatim. */ + if (directory_len > 0) + dequote_pathname (directory_name); + + /* We allocated a small array called RESULT, which we won't be using. + Free that memory now. */ + free (result); + + /* Just return what glob_vector () returns appended to the + directory name. */ + /* If flags & GX_ALLDIRS, we're called recursively */ + dflags = flags & ~GX_MARKDIRS; + if (directory_len == 0) + dflags |= GX_NULLDIR; + if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + { + dflags |= GX_ALLDIRS|GX_ADDCURDIR; +#if 0 + /* If we want all directories (dflags & GX_ALLDIRS) and we're not + being called recursively as something like `echo [star][star]/[star].o' + ((flags & GX_ALLDIRS) == 0), we want to prevent glob_vector from + adding a null directory name to the front of the temp_results + array. We turn off ADDCURDIR if not called recursively and + dlen == 0 */ +#endif + if (directory_len == 0 && (flags & GX_ALLDIRS) == 0) + dflags &= ~GX_ADDCURDIR; + } + temp_results = glob_vector (filename, + (directory_len == 0 ? "." : directory_name), + dflags); + + if (temp_results == NULL || temp_results == (char **)&glob_error_return) + { + if (free_dirname) + free (directory_name); + QUIT; /* XXX - shell */ + run_pending_traps (); + return (temp_results); + } + + result = glob_dir_to_array ((dflags & GX_ALLDIRS) ? "" : directory_name, temp_results, flags); + + if (free_dirname) + free (directory_name); + return (result); + } + + /* We get to memory_error if the program has run out of memory, or + if this is the shell, and we have been interrupted. */ + memory_error: + if (result != NULL) + { + register unsigned int i; + for (i = 0; result[i] != NULL; ++i) + free (result[i]); + free ((char *) result); + } + + if (free_dirname && directory_name) + free (directory_name); + + QUIT; + run_pending_traps (); + + return (NULL); +} + +#if defined (TEST) + +main (argc, argv) + int argc; + char **argv; +{ + unsigned int i; + + for (i = 1; i < argc; ++i) + { + char **value = glob_filename (argv[i], 0); + if (value == NULL) + puts ("Out of memory."); + else if (value == &glob_error_return) + perror (argv[i]); + else + for (i = 0; value[i] != NULL; i++) + puts (value[i]); + } + + exit (0); +} +#endif /* TEST. */ diff --git a/lib/glob/glob.h b/lib/glob/glob.h new file mode 100644 index 0000000..4741057 --- /dev/null +++ b/lib/glob/glob.h @@ -0,0 +1,47 @@ +/* File-name wildcard pattern matching for GNU. + Copyright (C) 1985-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _GLOB_H_ +#define _GLOB_H_ + +#include "stdc.h" + +#define GX_MARKDIRS 0x001 /* mark directory names with trailing `/' */ +#define GX_NOCASE 0x002 /* ignore case */ +#define GX_MATCHDOT 0x004 /* match `.' literally */ +#define GX_MATCHDIRS 0x008 /* match only directory names */ +#define GX_ALLDIRS 0x010 /* match all directory names, no others */ +#define GX_NULLDIR 0x100 /* internal -- no directory preceding pattern */ +#define GX_ADDCURDIR 0x200 /* internal -- add passed directory name */ +#define GX_GLOBSTAR 0x400 /* turn on special handling of ** */ +#define GX_RECURSE 0x800 /* internal -- glob_filename called recursively */ +#define GX_SYMLINK 0x1000 /* internal -- symlink to a directory */ +#define GX_NEGATE 0x2000 /* internal -- extglob pattern being negated */ + +extern int glob_pattern_p PARAMS((const char *)); +extern char **glob_vector PARAMS((char *, char *, int)); +extern char **glob_filename PARAMS((char *, int)); + +extern int extglob_pattern_p PARAMS((const char *)); + +extern char *glob_error_return; +extern int noglob_dot_filenames; +extern int glob_ignore_case; + +#endif /* _GLOB_H_ */ diff --git a/lib/glob/glob_loop.c b/lib/glob/glob_loop.c new file mode 100644 index 0000000..467e7ae --- /dev/null +++ b/lib/glob/glob_loop.c @@ -0,0 +1,84 @@ +/* Copyright (C) 1991-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +static int INTERNAL_GLOB_PATTERN_P PARAMS((const GCHAR *)); + +/* 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 GCHAR *pattern; +{ + register const GCHAR *p; + register GCHAR c; + int bopen, bsquote; + + p = pattern; + bopen = bsquote = 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('\\'): + /* Don't let the pattern end in a backslash (GMATCH returns no match + if the pattern ends in a backslash anyway), but otherwise note that + we have seen this, since the matching engine uses backslash as an + escape character and it can be removed. We return 2 later if we + have seen only backslash-escaped characters, so interested callers + know they can shortcut and just dequote the pathname. */ + if (*p != L('\0')) + { + p++; + bsquote = 1; + continue; + } + else /* (*p == L('\0')) */ + return 0; + } + +#if 0 + return bsquote ? 2 : 0; +#else + return (0); +#endif +} + +#undef INTERNAL_GLOB_PATTERN_P +#undef L +#undef INT +#undef CHAR +#undef GCHAR diff --git a/lib/glob/gm_loop.c b/lib/glob/gm_loop.c new file mode 100644 index 0000000..ac516f8 --- /dev/null +++ b/lib/glob/gm_loop.c @@ -0,0 +1,208 @@ +/* Copyright (C) 1991-2017 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if EXTENDED_GLOB +int +EXTGLOB_PATTERN_P (pat) + const CHAR *pat; +{ + switch (pat[0]) + { + case L('*'): + case L('+'): + case L('!'): + case L('@'): + case L('?'): + return (pat[1] == L('(')); /* ) */ + default: + return 0; + } + + return 0; +} +#endif + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Compiled to both single and wiide character + versions. FLAGS is a subset of strmatch flags; used to do case-insensitive + matching for now. */ +int +MATCH_PATTERN_CHAR (pat, string, flags) + CHAR *pat, *string; + int flags; +{ + CHAR c; + + if (*string == 0) + return (*pat == L('*')); /* XXX - allow only * to match empty string */ + + switch (c = *pat++) + { + default: + return (FOLD(*string) == FOLD(c)); + case L('\\'): + return (FOLD(*string) == FOLD(*pat)); + case L('?'): + return (*pat == L('(') ? 1 : (*string != L'\0')); + case L('*'): + return (1); + case L('+'): + case L('!'): + case L('@'): + return (*pat == L('(') ? 1 : (FOLD(*string) == FOLD(c))); + case L('['): + return (*string != L('\0')); + } +} + +int +MATCHLEN (pat, max) + CHAR *pat; + size_t max; +{ + CHAR c; + int matlen, bracklen, t, in_cclass, in_collsym, in_equiv; + + if (*pat == 0) + return (0); + + matlen = in_cclass = in_collsym = in_equiv = 0; + while (c = *pat++) + { + switch (c) + { + default: + matlen++; + break; + case L('\\'): + if (*pat == 0) + return ++matlen; + else + { + matlen++; + pat++; + } + break; + case L('?'): + if (*pat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L('*'): + return (matlen = -1); + case L('+'): + case L('!'): + case L('@'): + if (*pat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L('['): + /* scan for ending `]', skipping over embedded [:...:] */ + bracklen = 1; + c = *pat++; + do + { + if (c == 0) + { + pat--; /* back up to NUL */ + matlen += bracklen; + goto bad_bracket; + } + else if (c == L('\\')) + { + /* *pat == backslash-escaped character */ + bracklen++; + /* If the backslash or backslash-escape ends the string, + bail. The ++pat skips over the backslash escape */ + if (*pat == 0 || *++pat == 0) + { + matlen += bracklen; + goto bad_bracket; + } + } + else if (c == L('[') && *pat == L(':')) /* character class */ + { + pat++; + bracklen++; + in_cclass = 1; + } + else if (in_cclass && c == L(':') && *pat == L(']')) + { + pat++; + bracklen++; + in_cclass = 0; + } + else if (c == L('[') && *pat == L('.')) /* collating symbol */ + { + pat++; + bracklen++; + if (*pat == L(']')) /* right bracket can appear as collating symbol */ + { + pat++; + bracklen++; + } + in_collsym = 1; + } + else if (in_collsym && c == L('.') && *pat == L(']')) + { + pat++; + bracklen++; + in_collsym = 0; + } + else if (c == L('[') && *pat == L('=')) /* equivalence class */ + { + pat++; + bracklen++; + if (*pat == L(']')) /* right bracket can appear as equivalence class */ + { + pat++; + bracklen++; + } + in_equiv = 1; + } + else if (in_equiv && c == L('=') && *pat == L(']')) + { + pat++; + bracklen++; + in_equiv = 0; + } + else + bracklen++; + } + while ((c = *pat++) != L(']')); + matlen++; /* bracket expression can only match one char */ +bad_bracket: + break; + } + } + + return matlen; +} + +#undef EXTGLOB_PATTERN_P +#undef MATCH_PATTERN_CHAR +#undef MATCHLEN +#undef FOLD +#undef L +#undef LPAREN +#undef RPAREN +#undef INT +#undef CHAR diff --git a/lib/glob/gmisc.c b/lib/glob/gmisc.c new file mode 100644 index 0000000..f3d74ce --- /dev/null +++ b/lib/glob/gmisc.c @@ -0,0 +1,108 @@ +/* gmisc.c -- miscellaneous pattern matching utility functions for Bash. + + Copyright (C) 2010-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "shmbutil.h" +#include "chartypes.h" + +#include "stdc.h" + +#ifndef FNM_CASEFOLD +# include "strmatch.h" +#endif +#include "glob.h" + +/* Make sure these names continue to agree with what's in smatch.c */ +extern char *glob_patscan PARAMS((char *, char *, int)); + +/* Compile `gm_loop.c' for single-byte characters. */ +#define CHAR char +#define INT int +#define L(CS) CS +#define EXTGLOB_PATTERN_P extglob_pattern_p +#define MATCH_PATTERN_CHAR match_pattern_char +#define MATCHLEN umatchlen +#define FOLD(c) ((flags & FNM_CASEFOLD) \ + ? TOLOWER ((unsigned char)c) \ + : ((unsigned char)c)) +#ifndef LPAREN +#define LPAREN '(' +#define RPAREN ')' +#endif +#include "gm_loop.c" + +/* Compile `gm_loop.c' again for multibyte characters. */ +#if HANDLE_MULTIBYTE + +#define CHAR wchar_t +#define INT wint_t +#define L(CS) L##CS +#define EXTGLOB_PATTERN_P wextglob_pattern_p +#define MATCH_PATTERN_CHAR match_pattern_wchar +#define MATCHLEN wmatchlen + +#define FOLD(c) ((flags & FNM_CASEFOLD) && iswupper (c) ? towlower (c) : (c)) +#define LPAREN L'(' +#define RPAREN L')' +#include "gm_loop.c" + +#endif /* HANDLE_MULTIBYTE */ + + +#if defined (EXTENDED_GLOB) +/* Skip characters in PAT and return the final occurrence of DIRSEP. This + is only called when extended_glob is set, so we have to skip over extglob + patterns x(...) */ +char * +glob_dirscan (pat, dirsep) + char *pat; + int dirsep; +{ + char *p, *d, *pe, *se; + + d = pe = se = 0; + for (p = pat; p && *p; p++) + { + if (extglob_pattern_p (p)) + { + if (se == 0) + se = p + strlen (p) - 1; + pe = glob_patscan (p + 2, se, 0); + if (pe == 0) + continue; + else if (*pe == 0) + break; + p = pe - 1; /* will do increment above */ + continue; + } + if (*p == dirsep) + d = p; + } + return d; +} +#endif /* EXTENDED_GLOB */ diff --git a/lib/glob/ndir.h b/lib/glob/ndir.h new file mode 100644 index 0000000..31261eb --- /dev/null +++ b/lib/glob/ndir.h @@ -0,0 +1,50 @@ +/* <dir.h> -- definitions for 4.2BSD-compatible directory access. + last edit: 09-Jul-1983 D A Gwyn. */ + +#if defined (VMS) +# if !defined (FAB$C_BID) +# include <fab.h> +# endif +# if !defined (NAM$C_BID) +# include <nam.h> +# endif +# if !defined (RMS$_SUC) +# include <rmsdef.h> +# endif +# include "dir.h" +#endif /* VMS */ + +/* Size of directory block. */ +#define DIRBLKSIZ 512 + +/* NOTE: MAXNAMLEN must be one less than a multiple of 4 */ + +#if defined (VMS) +# define MAXNAMLEN (DIR$S_NAME + 7) /* 80 plus room for version #. */ +# define MAXFULLSPEC NAM$C_MAXRSS /* Maximum full spec */ +#else +# define MAXNAMLEN 15 /* Maximum filename length. */ +#endif /* VMS */ + +/* Data from readdir (). */ +struct direct { + long d_ino; /* Inode number of entry. */ + unsigned short d_reclen; /* Length of this record. */ + unsigned short d_namlen; /* Length of string in d_name. */ + char d_name[MAXNAMLEN + 1]; /* Name of file. */ +}; + +/* Stream data from opendir (). */ +typedef struct { + int dd_fd; /* File descriptor. */ + int dd_loc; /* Offset in block. */ + int dd_size; /* Amount of valid data. */ + char dd_buf[DIRBLKSIZ]; /* Directory block. */ +} DIR; + +extern DIR *opendir (); +extern struct direct *readdir (); +extern long telldir (); +extern void seekdir (), closedir (); + +#define rewinddir(dirp) seekdir (dirp, 0L) diff --git a/lib/glob/sm_loop.c b/lib/glob/sm_loop.c new file mode 100644 index 0000000..247ba28 --- /dev/null +++ b/lib/glob/sm_loop.c @@ -0,0 +1,981 @@ +/* Copyright (C) 1991-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +extern int interrupt_state, terminating_signal; + +struct STRUCT +{ + CHAR *pattern; + CHAR *string; +}; + +int FCT PARAMS((CHAR *, CHAR *, int)); + +static int GMATCH PARAMS((CHAR *, CHAR *, CHAR *, CHAR *, struct STRUCT *, int)); +static CHAR *PARSE_COLLSYM PARAMS((CHAR *, INT *)); +static CHAR *BRACKMATCH PARAMS((CHAR *, U_CHAR, int)); +static int EXTMATCH PARAMS((INT, CHAR *, CHAR *, CHAR *, CHAR *, int)); + +extern void DEQUOTE_PATHNAME PARAMS((CHAR *)); + +/*static*/ CHAR *PATSCAN PARAMS((CHAR *, CHAR *, INT)); + +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, (struct STRUCT *)NULL, flags)); +} + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, FNM_NOMATCH if not. */ +static int +GMATCH (string, se, pattern, pe, ends, flags) + CHAR *string, *se; + CHAR *pattern, *pe; + struct STRUCT *ends; + 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'; + + if (interrupt_state || terminating_signal) + return FNM_NOMATCH; + +#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|FNM_DOTDOT)); + 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; + + /* `?' cannot match `.' or `..' 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_DOTDOT) && + ((n == string && SDOT_OR_DOTDOT(n)) || + ((flags & FNM_PATHNAME) && n[-1] == L('/') && PDOT_OR_DOTDOT(n)))) + return FNM_NOMATCH; + + break; + + case L('\\'): /* backslash escape removes special meaning */ + if (p == pe && sc == '\\' && (n+1 == se)) + break; + + 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 L('*'): /* Match zero or more characters */ + /* See below for the reason for using this. It avoids backtracking + back to a previous `*'. Picked up from glibc. */ + if (ends != NULL) + { + ends->pattern = p - 1; + ends->string = n; + 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; + + /* `*' cannot match `.' or `..' 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_DOTDOT) && + ((n == string && SDOT_OR_DOTDOT(n)) || + ((flags & FNM_PATHNAME) && n[-1] == L('/') && PDOT_OR_DOTDOT(n)))) + return FNM_NOMATCH; + + if (p == pe) + return 0; + + /* 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; +#ifdef EXTENDED_GLOB + else if ((flags & FNM_EXTMATCH) && c == L('?') && *p == L('(')) /* ) */ + { + CHAR *newn; + + /* We can match 0 or 1 times. If we match, return success */ + if (EXTMATCH (c, n, 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 1 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 + 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; + } + + /* The wildcards are the last element of the pattern. The name + cannot match completely if we are looking for a pathname and + it contains another slash, unless FNM_LEADING_DIR is set. */ + if (c == L('\0')) + { + int r = (flags & FNM_PATHNAME) == 0 ? 0 : FNM_NOMATCH; + if (flags & FNM_PATHNAME) + { + if (flags & FNM_LEADING_DIR) + r = 0; + else if (MEMCHR (n, L('/'), se - n) == NULL) + r = 0; + } + return r; + } + + /* 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); + + /* If we've hit the end of the string and the rest of the pattern + is something that matches the empty string, we can succeed. */ +#if defined (EXTENDED_GLOB) + if (n == se && ((flags & FNM_EXTMATCH) && (c == L('!') || c == L('?')) && *p == L('('))) + { + --p; + if (EXTMATCH (c, n, se, p, pe, flags) == 0) + return (c == L('!') ? FNM_NOMATCH : 0); + return (c == L('!') ? 0 : FNM_NOMATCH); + } +#endif + + /* If we stop at a slash in the pattern and we are looking for a + pathname ([star]/foo), then consume enough of the string to stop + at any slash and then try to match the rest of the pattern. If + the string doesn't contain a slash, fail */ + if (c == L('/') && (flags & FNM_PATHNAME)) + { + while (n < se && *n != L('/')) + ++n; + if (n < se && *n == L('/') && (GMATCH (n+1, se, p, pe, NULL, flags) == 0)) + return 0; + return FNM_NOMATCH; /* XXX */ + } + + /* General case, use recursion. */ + { + U_CHAR c1; + const CHAR *endp; + struct STRUCT end; + + end.pattern = NULL; + endp = MEMCHR (n, (flags & FNM_PATHNAME) ? L('/') : L('\0'), se - n); + if (endp == 0) + endp = se; + + c1 = ((flags & FNM_NOESCAPE) == 0 && c == L('\\')) ? *p : c; + c1 = FOLD (c1); + for (--p; n < endp; ++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, &end, flags & ~(FNM_PERIOD|FNM_DOTDOT)) == 0) + { + if (end.pattern == NULL) + return (0); + break; + } + } + /* This is a clever idea from glibc, used to avoid backtracking + to a `*' that appears earlier in the pattern. We get away + without saving se and pe because they are always the same, + even in the recursive calls to gmatch */ + if (end.pattern != NULL) + { + p = end.pattern; + n = end.string; + continue; + } + + 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); + + /* `?' cannot match `.' or `..' 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_DOTDOT) && + ((n == string && SDOT_OR_DOTDOT(n)) || + ((flags & FNM_PATHNAME) && n[-1] == L('/') && PDOT_OR_DOTDOT(n)))) + 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; + if (p[pc] == 0) + { + if (vp) + *vp = INVALID; + return (p + pc); + } + 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, forcecoll, isrange; + INT pc; + CHAR *savep; + CHAR *brchrp; + U_CHAR orig_test; + + orig_test = test; + test = FOLD (orig_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; + forcecoll = 0; + + /* 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'); + /* As a result of a POSIX discussion, char class names are + allowed to be quoted (?) */ + DEQUOTE_PATHNAME (ccname); + pc = IS_CCLASS (orig_test, (XCHAR *)ccname); + } + if (pc == -1) + { + /* CCNAME is not a valid character class in the current + locale. In addition to noting no match (pc = 0), we have + a choice about what to do with the invalid charclass. + Posix leaves the behavior unspecified, but we're going + to skip over the charclass and keep going instead of + testing ORIG_TEST against each character in the class + string. If we don't want to do that, take out the update + of P. */ + pc = 0; + p = close + 2; + } + else + p = close + 2; /* move past the closing `]' */ + + 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; + forcecoll = 1; + } + + if (!(flags & FNM_NOESCAPE) && c == L('\\')) + { + if (*p == '\0') + return (CHAR *)0; + cstart = cend = *p++; + } + + cstart = cend = FOLD (cstart); + isrange = 0; + + /* 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 (c == L('\0')) + return ((test == L('[')) ? savep : (CHAR *)0); + + 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; + forcecoll = 1; + } + 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, forcecoll) > 0) + { + if (c == L(']')) + break; + c = FOLD (c); + continue; + } + isrange = 1; + } + + if (isrange == 0 && test == cstart) + goto matched; + if (isrange && RANGECMP (test, cstart, forcecoll) >= 0 && RANGECMP (test, cend, forcecoll) <= 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; + brchrp = 0; + while (brcnt > 0) + { + int oc; + + /* A `[' without a matching `]' is just another character to match. */ + if (c == L('\0')) + return ((test == L('[')) ? savep : (CHAR *)0); + + oc = c; + c = *p++; + if (c == L('[') && (*p == L('=') || *p == L(':') || *p == L('.'))) + { + brcnt++; + brchrp = p++; /* skip over the char after the left bracket */ + if ((c = *p) == L('\0')) + return ((test == L('[')) ? savep : (CHAR *)0); + /* If *brchrp == ':' we should check that the rest of the characters + form a valid character class name. We don't do that yet, but we + keep BRCHRP in case we want to. */ + } + /* We only want to check brchrp if we set it above. */ + else if (c == L(']') && brcnt > 1 && brchrp != 0 && oc == *brchrp) + { + brcnt--; + brchrp = 0; /* just in case */ + } + /* Left bracket loses its special meaning inside a bracket expression. + It is only valid when followed by a `.', `=', or `:', which we check + for above. Technically the right bracket can appear in a collating + symbol, so we check for that here. Otherwise, it terminates the + bracket expression. */ + else if (c == L(']') && (brchrp == 0 || *brchrp != L('.')) && brcnt >= 1) + brcnt = 0; + 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 or NULL if the pattern is + empty or invalid. */ +/*static*/ CHAR * +PATSCAN (string, end, delim) + CHAR *string, *end; + INT delim; +{ + int pnest, bnest, skip; + INT cchar; + CHAR *s, c, *bfirst; + + pnest = bnest = skip = 0; + cchar = 0; + bfirst = NULL; + + if (string == end) + return (NULL); + + for (s = string; c = *s; s++) + { + if (s >= end) + return (s); + if (skip) + { + skip = 0; + continue; + } + switch (c) + { + case L('\\'): + skip = 1; + break; + + 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; + int l1, l2; + + l1 = pe - p; + l2 = se - s; + + if (l1 != l2) + return (FNM_NOMATCH); /* unequal lengths, can't be identical */ + + c1 = *pe; + c2 = *se; + + if (c1 != 0) + *pe = '\0'; + if (c2 != 0) + *se = '\0'; + +#if HAVE_MULTIBYTE || defined (HAVE_STRCOLL) + ret = STRCOLL ((XCHAR *)p, (XCHAR *)s); +#else + ret = STRCMP ((XCHAR *)p, (XCHAR *)s); +#endif + + if (c1 != 0) + *pe = c1; + if (c2 != 0) + *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, xflags; /* xflags = flags passed to recursive matches */ + +#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); +fprintf(stderr, "extmatch: flags = %d\n", flags); +#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, NULL, 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, NULL, 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) + { + /* if srest > s, we are not at start of string */ + xflags = (srest > s) ? (flags & ~(FNM_PERIOD|FNM_DOTDOT)) : flags; + m2 = (GMATCH (srest, se, prest, pe, NULL, xflags) == 0) || + (s != srest && GMATCH (srest, se, p - 1, pe, NULL, xflags) == 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 one (or more) 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, NULL, 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 srest > s, we are not at start of string */ + xflags = (srest > s) ? (flags & ~(FNM_PERIOD|FNM_DOTDOT)) : flags; + if (GMATCH (s, srest, psub, pnext - 1, NULL, flags) == 0 && + GMATCH (srest, se, prest, pe, NULL, xflags) == 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, NULL, flags) == 0)) + break; + if (pnext == prest) + break; + } + + /* If nothing matched, but the string starts with a period and we + need to match periods explicitly, don't return this as a match, + even for negation. */ + if (m1 == 0 && (flags & FNM_PERIOD) && *s == '.') + return (FNM_NOMATCH); + + if (m1 == 0 && (flags & FNM_DOTDOT) && + (SDOT_OR_DOTDOT (s) || + ((flags & FNM_PATHNAME) && s[-1] == L('/') && PDOT_OR_DOTDOT(s)))) + return (FNM_NOMATCH); + + /* if srest > s, we are not at start of string */ + xflags = (srest > s) ? (flags & ~(FNM_PERIOD|FNM_DOTDOT)) : flags; + if (m1 == 0 && GMATCH (srest, se, prest, pe, NULL, xflags) == 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 DEQUOTE_PATHNAME +#undef STRUCT +#undef BRACKMATCH +#undef STRCHR +#undef STRCOLL +#undef STRLEN +#undef STRCMP +#undef MEMCHR +#undef COLLEQUIV +#undef RANGECMP +#undef ISDIRSEP +#undef PATHSEP +#undef PDOT_OR_DOTDOT +#undef SDOT_OR_DOTDOT +#undef L diff --git a/lib/glob/smatch.c b/lib/glob/smatch.c new file mode 100644 index 0000000..379c2d2 --- /dev/null +++ b/lib/glob/smatch.c @@ -0,0 +1,638 @@ +/* strmatch.c -- ksh-like extended pattern matching for the shell and filename + globbing. */ + +/* Copyright (C) 1991-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <stdio.h> /* for debugging */ + +#include "strmatch.h" +#include <chartypes.h> + +#include "bashansi.h" +#include "shmbutil.h" +#include "xmalloc.h" + +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif + +#if FNMATCH_EQUIV_FALLBACK +/* We don't include <fnmatch.h> in order to avoid namespace collisions; the + internal strmatch still uses the FNM_ constants. */ +extern int fnmatch (const char *, const char *, int); +#endif + +/* 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) + +#ifndef GLOBASCII_DEFAULT +# define GLOBASCII_DEFAULT 0 +#endif + +int glob_asciirange = GLOBASCII_DEFAULT; + +#if FNMATCH_EQUIV_FALLBACK +/* Construct a string w1 = "c1" and a pattern w2 = "[[=c2=]]" and pass them + to fnmatch to see if wide characters c1 and c2 collate as members of the + same equivalence class. We can't really do this portably any other way */ +static int +_fnmatch_fallback (s, p) + int s, p; /* string char, patchar */ +{ + char s1[2]; /* string */ + char s2[8]; /* constructed pattern */ + + s1[0] = (unsigned char)s; + s1[1] = '\0'; + + /* reconstruct the pattern */ + s2[0] = s2[1] = '['; + s2[2] = '='; + s2[3] = (unsigned char)p; + s2[4] = '='; + s2[5] = s2[6] = ']'; + s2[7] = '\0'; + + return (fnmatch ((const char *)s2, (const char *)s1, 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 GLOB_ASCIIRANGE is non-zero, and we're not forcing + the use of strcoll (e.g., for explicit collating symbols), we use + straight ordering as if in the C locale. */ + +#if defined (HAVE_STRCOLL) +/* Helper functions for collating symbol equivalence. */ + +/* Return 0 if C1 == C2 or collates equally if FORCECOLL is non-zero. */ +static int +charcmp (c1, c2, forcecoll) + int c1, c2; + int forcecoll; +{ + 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); + + if (forcecoll == 0 && glob_asciirange) + return (c1 - c2); + + s1[0] = c1; + s2[0] = c2; + + return (strcoll (s1, s2)); +} + +static int +rangecmp (c1, c2, forcecoll) + int c1, c2; + int forcecoll; +{ + int r; + + r = charcmp (c1, c2, forcecoll); + + /* We impose a total ordering here by returning c1-c2 if charcmp returns 0 */ + if (r != 0) + return r; + return (c1 - c2); /* impose total ordering */ +} +#else /* !HAVE_STRCOLL */ +# define rangecmp(c1, c2, f) ((int)(c1) - (int)(c2)) +#endif /* !HAVE_STRCOLL */ + +#if defined (HAVE_STRCOLL) +/* Returns 1 if chars C and EQUIV collate equally in the current locale. */ +static int +collequiv (c, equiv) + int c, equiv; +{ + if (charcmp (c, equiv, 1) == 0) + return 1; + +#if FNMATCH_EQUIV_FALLBACK + return (_fnmatch_fallback (c, equiv) == 0); +#else + return 0; +#endif + +} +#else +# define collequiv(c, equiv) ((c) == (equiv)) +#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; + char *x; + + x = (char *)s; + for (csp = posix_collsyms; csp->name; csp++) + { + if (STREQN(csp->name, x, 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 enum char_class +is_valid_cclass (name) + const char *name; +{ + enum char_class ret; + int i; + + ret = CC_NO_CLASS; + + for (i = 1; i < N_CHAR_CLASS; i++) + { + if (STREQ (name, cclass_name[i])) + { + ret = (enum char_class)i; + break; + } + } + + return ret; +} + +static int +cclass_test (c, char_class) + int c; + enum char_class char_class; +{ + int result; + + 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; +} + +static int +is_cclass (c, name) + int c; + const char *name; +{ + enum char_class char_class; + int result; + + char_class = is_valid_cclass (name); + if (char_class == CC_NO_CLASS) + return -1; + + result = cclass_test (c, char_class); + 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)) + +#if !defined (__CYGWIN__) +# define ISDIRSEP(c) ((c) == '/') +#else +# define ISDIRSEP(c) ((c) == '/' || (c) == '\\') +#endif /* __CYGWIN__ */ +#define PATHSEP(c) (ISDIRSEP(c) || (c) == 0) + +# define PDOT_OR_DOTDOT(s) (s[0] == '.' && (PATHSEP (s[1]) || (s[1] == '.' && PATHSEP (s[2])))) +# define SDOT_OR_DOTDOT(s) (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0))) + +#define FCT internal_strmatch +#define GMATCH gmatch +#define COLLSYM collsym +#define PARSE_COLLSYM parse_collsym +#define BRACKMATCH brackmatch +#define PATSCAN glob_patscan +#define STRCOMPARE strcompare +#define EXTMATCH extmatch +#define DEQUOTE_PATHNAME udequote_pathname +#define STRUCT smat_struct +#define STRCHR(S, C) strchr((S), (C)) +#define MEMCHR(S, C, N) memchr((S), (C), (N)) +#define STRCOLL(S1, S2) strcoll((S1), (S2)) +#define STRLEN(S) strlen(S) +#define STRCMP(S1, S2) strcmp((S1), (S2)) +#define RANGECMP(C1, C2, F) rangecmp((C1), (C2), (F)) +#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) + +extern char *mbsmbchar PARAMS((const char *)); + +#if FNMATCH_EQUIV_FALLBACK +/* Construct a string w1 = "c1" and a pattern w2 = "[[=c2=]]" and pass them + to fnmatch to see if wide characters c1 and c2 collate as members of the + same equivalence class. We can't really do this portably any other way */ +static int +_fnmatch_fallback_wc (c1, c2) + wchar_t c1, c2; /* string char, patchar */ +{ + char w1[MB_LEN_MAX+1]; /* string */ + char w2[MB_LEN_MAX+8]; /* constructed pattern */ + int l1, l2; + + l1 = wctomb (w1, c1); + if (l1 == -1) + return (2); + w1[l1] = '\0'; + + /* reconstruct the pattern */ + w2[0] = w2[1] = '['; + w2[2] = '='; + l2 = wctomb (w2+3, c2); + if (l2 == -1) + return (2); + w2[l2+3] = '='; + w2[l2+4] = w2[l2+5] = ']'; + w2[l2+6] = '\0'; + + return (fnmatch ((const char *)w2, (const char *)w1, 0)); +} +#endif + +static int +charcmp_wc (c1, c2, forcecoll) + wint_t c1, c2; + int forcecoll; +{ + static wchar_t s1[2] = { L' ', L'\0' }; + static wchar_t s2[2] = { L' ', L'\0' }; + int r; + + if (c1 == c2) + return 0; + + if (forcecoll == 0 && glob_asciirange && c1 <= UCHAR_MAX && c2 <= UCHAR_MAX) + return ((int)(c1 - c2)); + + s1[0] = c1; + s2[0] = c2; + + return (wcscoll (s1, s2)); +} + +static int +rangecmp_wc (c1, c2, forcecoll) + wint_t c1, c2; + int forcecoll; +{ + int r; + + r = charcmp_wc (c1, c2, forcecoll); + + /* We impose a total ordering here by returning c1-c2 if charcmp returns 0, + as we do above in the single-byte case. */ + if (r != 0 || forcecoll) + return r; + return ((int)(c1 - c2)); /* impose total ordering */ +} + +/* Returns 1 if wide chars C and EQUIV collate equally in the current locale. */ +static int +collequiv_wc (c, equiv) + wint_t c, equiv; +{ + wchar_t s, p; + + if (charcmp_wc (c, equiv, 1) == 0) + return 1; + +#if FNMATCH_EQUIV_FALLBACK +/* We check explicitly for success (fnmatch returns 0) to avoid problems if + our local definition of FNM_NOMATCH (strmatch.h) doesn't match the + system's (fnmatch.h). We don't care about error return values here. */ + + s = c; + p = equiv; + return (_fnmatch_fallback_wc (s, p) == 0); +#else + return 0; +#endif +} + +/* 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); + if (mbs == 0) + return -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)); +} + +/* Return 1 if there are no char class [:class:] expressions (degenerate case) + or only posix-specified (C locale supported) char class expressions in + PATTERN. These are the ones where it's safe to punt to the single-byte + code, since wide character support allows locale-defined char classes. + This only uses single-byte code, but is only needed to support multibyte + locales. */ +static int +posix_cclass_only (pattern) + char *pattern; +{ + char *p, *p1; + char cc[16]; /* sufficient for all valid posix char class names */ + enum char_class valid; + + p = pattern; + while (p = strchr (p, '[')) + { + if (p[1] != ':') + { + p++; + continue; + } + p += 2; /* skip past "[:" */ + /* Find end of char class expression */ + for (p1 = p; *p1; p1++) + if (*p1 == ':' && p1[1] == ']') + break; + if (*p1 == 0) /* no char class expression found */ + break; + /* Find char class name and validate it against posix char classes */ + if ((p1 - p) >= sizeof (cc)) + return 0; + bcopy (p, cc, p1 - p); + cc[p1 - p] = '\0'; + valid = is_valid_cclass (cc); + if (valid == CC_NO_CLASS) + return 0; /* found unrecognized char class name */ + + p = p1 + 2; /* found posix char class name */ + } + + return 1; /* no char class names or only posix */ +} + +/* Now include `sm_loop.c' for multibyte characters. */ +#define FOLD(c) ((flags & FNM_CASEFOLD) && iswupper (c) ? towlower (c) : (c)) + +# if !defined (__CYGWIN__) +# define ISDIRSEP(c) ((c) == L'/') +# else +# define ISDIRSEP(c) ((c) == L'/' || (c) == L'\\') +# endif /* __CYGWIN__ */ +# define PATHSEP(c) (ISDIRSEP(c) || (c) == L'\0') + +# define PDOT_OR_DOTDOT(w) (w[0] == L'.' && (PATHSEP(w[1]) || (w[1] == L'.' && PATHSEP(w[2])))) +# define SDOT_OR_DOTDOT(w) (w[0] == L'.' && (w[1] == L'\0' || (w[1] == L'.' && w[2] == L'\0'))) + +#define FCT internal_wstrmatch +#define GMATCH gmatch_wc +#define COLLSYM collwcsym +#define PARSE_COLLSYM parse_collwcsym +#define BRACKMATCH brackmatch_wc +#define PATSCAN glob_patscan_wc +#define STRCOMPARE wscompare +#define EXTMATCH extmatch_wc +#define DEQUOTE_PATHNAME wcdequote_pathname +#define STRUCT wcsmat_struct +#define STRCHR(S, C) wcschr((S), (C)) +#define MEMCHR(S, C, N) wmemchr((S), (C), (N)) +#define STRCOLL(S1, S2) wcscoll((S1), (S2)) +#define STRLEN(S) wcslen(S) +#define STRCMP(S1, S2) wcscmp((S1), (S2)) +#define RANGECMP(C1, C2, F) rangecmp_wc((C1), (C2), (F)) +#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; + size_t n; + wchar_t *wpattern, *wstring; + size_t plen, slen, mplen, mslen; + + if (MB_CUR_MAX == 1) + return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); + + if (mbsmbchar (string) == 0 && mbsmbchar (pattern) == 0 && posix_cclass_only (pattern)) + return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); + + n = xdupmbstowcs (&wpattern, NULL, pattern); + if (n == (size_t)-1 || n == (size_t)-2) + return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); + + n = xdupmbstowcs (&wstring, NULL, string); + if (n == (size_t)-1 || n == (size_t)-2) + { + free (wpattern); + return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); + } + + ret = internal_wstrmatch (wpattern, wstring, flags); + + free (wpattern); + free (wstring); + + return ret; +#else + return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); +#endif /* !HANDLE_MULTIBYTE */ +} diff --git a/lib/glob/strmatch.c b/lib/glob/strmatch.c new file mode 100644 index 0000000..5b7b49f --- /dev/null +++ b/lib/glob/strmatch.c @@ -0,0 +1,79 @@ +/* strmatch.c -- ksh-like extended pattern matching for the shell and filename + globbing. */ + +/* Copyright (C) 1991-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "stdc.h" +#include "strmatch.h" + +extern int xstrmatch PARAMS((char *, char *, int)); +#if defined (HANDLE_MULTIBYTE) +extern int internal_wstrmatch PARAMS((wchar_t *, wchar_t *, int)); +#endif + +int +strmatch (pattern, string, flags) + char *pattern; + char *string; + int flags; +{ + if (string == 0 || pattern == 0) + return FNM_NOMATCH; + + return (xstrmatch (pattern, string, flags)); +} + +#if defined (HANDLE_MULTIBYTE) +int +wcsmatch (wpattern, wstring, flags) + wchar_t *wpattern; + wchar_t *wstring; + int flags; +{ + if (wstring == 0 || wpattern == 0) + return (FNM_NOMATCH); + + return (internal_wstrmatch (wpattern, wstring, flags)); +} +#endif + +#ifdef TEST +main (c, v) + int c; + char **v; +{ + char *string, *pat; + + string = v[1]; + pat = v[2]; + + if (strmatch (pat, string, 0) == 0) + { + printf ("%s matches %s\n", string, pat); + exit (0); + } + else + { + printf ("%s does not match %s\n", string, pat); + exit (1); + } +} +#endif diff --git a/lib/glob/strmatch.h b/lib/glob/strmatch.h new file mode 100644 index 0000000..d6a6b0f --- /dev/null +++ b/lib/glob/strmatch.h @@ -0,0 +1,65 @@ +/* Copyright (C) 1991-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _STRMATCH_H +#define _STRMATCH_H 1 + +#include <config.h> + +#include "stdc.h" + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in <unistd.h>. */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `strmatch'. */ + +/* standard flags are like fnmatch(3). */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +/* extended flags not available in most libc fnmatch versions, but we undef + them to avoid any possible warnings. */ +#undef FNM_LEADING_DIR +#undef FNM_CASEFOLD +#undef FNM_EXTMATCH + +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */ + +#define FNM_FIRSTCHAR (1 << 6) /* Match only the first character */ +#define FNM_DOTDOT (1 << 7) /* force `.' and `..' to match explicitly even if FNM_PERIOD not supplied. */ + +/* Value returned by `strmatch' if STRING does not match PATTERN. */ +#undef FNM_NOMATCH + +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int strmatch PARAMS((char *, char *, int)); + +#if HANDLE_MULTIBYTE +extern int wcsmatch PARAMS((wchar_t *, wchar_t *, int)); +#endif + +#endif /* _STRMATCH_H */ diff --git a/lib/glob/xmbsrtowcs.c b/lib/glob/xmbsrtowcs.c new file mode 100644 index 0000000..17250c3 --- /dev/null +++ b/lib/glob/xmbsrtowcs.c @@ -0,0 +1,523 @@ +/* xmbsrtowcs.c -- replacement function for mbsrtowcs */ + +/* Copyright (C) 2002-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Ask for GNU extensions to get extern declaration for mbsnrtowcs if + available via glibc. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#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 + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif + +#define WSBUF_INC 32 + +#ifndef FREE +# define FREE(x) do { if (x) free (x); } while (0) +#endif + +#if ! HAVE_STRCHRNUL +extern char *strchrnul PARAMS((const char *, int)); +#endif + +/* 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); + + if (dest == NULL) + { + wchar_t *wsbuf; + const char *mbs; + mbstate_t psbuf; + + /* It doesn't matter if malloc fails here, since mbsrtowcs should do + the right thing with a NULL first argument. */ + wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t)); + mbs = *src; + psbuf = *ps; + + wclength = mbsrtowcs (wsbuf, &mbs, n, &psbuf); + + if (wsbuf) + free (wsbuf); + 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); +} + +#if HAVE_MBSNRTOWCS +/* Convert a multibyte string to a wide character string. Memory for the + new wide character string is obtained with malloc. + + Fast multiple-character version of xdupmbstowcs used when the indices are + not required and mbsnrtowcs is available. */ + +static size_t +xdupmbstowcs2 (destp, src) + wchar_t **destp; /* Store the pointer to the wide character string */ + const char *src; /* Multibyte character string */ +{ + const char *p; /* Conversion start position of src */ + wchar_t *wsbuf; /* Buffer for wide characters. */ + size_t wsbuf_size; /* Size of WSBUF */ + size_t wcnum; /* Number of wide characters in WSBUF */ + mbstate_t state; /* Conversion State */ + size_t n, wcslength; /* Number of wide characters produced by the conversion. */ + const char *end_or_backslash; + size_t nms; /* Number of multibyte characters to convert at one time. */ + mbstate_t tmp_state; + const char *tmp_p; + + memset (&state, '\0', sizeof(mbstate_t)); + + wsbuf_size = 0; + wsbuf = NULL; + + p = src; + wcnum = 0; + do + { + end_or_backslash = strchrnul(p, '\\'); + nms = end_or_backslash - p; + if (*end_or_backslash == '\0') + nms++; + + /* Compute the number of produced wide-characters. */ + tmp_p = p; + tmp_state = state; + + if (nms == 0 && *p == '\\') /* special initial case */ + nms = wcslength = 1; + else + wcslength = mbsnrtowcs (NULL, &tmp_p, nms, 0, &tmp_state); + + if (wcslength == 0) + { + tmp_p = p; /* will need below */ + tmp_state = state; + wcslength = 1; /* take a single byte */ + } + + /* Conversion failed. */ + if (wcslength == (size_t)-1) + { + free (wsbuf); + *destp = NULL; + return (size_t)-1; + } + + /* Resize the buffer if it is not large enough. */ + if (wsbuf_size < wcnum+wcslength+1) /* 1 for the L'\0' or the potential L'\\' */ + { + wchar_t *wstmp; + + while (wsbuf_size < wcnum+wcslength+1) /* 1 for the L'\0' or the potential L'\\' */ + wsbuf_size += WSBUF_INC; + + wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t)); + if (wstmp == NULL) + { + free (wsbuf); + *destp = NULL; + return (size_t)-1; + } + wsbuf = wstmp; + } + + /* Perform the conversion. This is assumed to return 'wcslength'. + It may set 'p' to NULL. */ + n = mbsnrtowcs(wsbuf+wcnum, &p, nms, wsbuf_size-wcnum, &state); + + if (n == 0 && p == 0) + { + wsbuf[wcnum] = L'\0'; + break; + } + + /* Compensate for taking single byte on wcs conversion failure above. */ + if (wcslength == 1 && (n == 0 || n == (size_t)-1)) + { + state = tmp_state; + p = tmp_p; + wsbuf[wcnum] = *p; + if (*p == 0) + break; + else + { + wcnum++; p++; + } + } + else + wcnum += wcslength; + + if (mbsinit (&state) && (p != NULL) && (*p == '\\')) + { + wsbuf[wcnum++] = L'\\'; + p++; + } + } + while (p != NULL); + + *destp = wsbuf; + + /* Return the length of the wide character string, not including `\0'. */ + return wcnum; +} +#endif /* HAVE_MBSNRTOWCS */ + +/* Convert a multibyte string to a wide character string. Memory for the + new wide character string is obtained with malloc. + + The return value is the length of the wide character string. Returns a + pointer to the wide character string in DESTP. If INDICESP is not NULL, + INDICESP stores the pointer to the pointer array. Each pointer is to + the first byte of each multibyte character. Memory for the pointer array + is obtained with malloc, too. + If conversion is failed, the return value is (size_t)-1 and the values + of DESTP and INDICESP are NULL. */ + +size_t +xdupmbstowcs (destp, indicesp, src) + wchar_t **destp; /* Store the pointer to the wide character string */ + char ***indicesp; /* Store the pointer to the pointer array. */ + const char *src; /* Multibyte character string */ +{ + const char *p; /* Conversion start position of src */ + wchar_t wc; /* Created wide character by conversion */ + wchar_t *wsbuf; /* Buffer for wide characters. */ + char **indices; /* Buffer for indices. */ + size_t wsbuf_size; /* Size of WSBUF */ + size_t wcnum; /* Number of wide characters in WSBUF */ + mbstate_t state; /* Conversion State */ + + /* In case SRC or DESP is NULL, conversion doesn't take place. */ + if (src == NULL || destp == NULL) + { + if (destp) + *destp = NULL; + if (indicesp) + *indicesp = NULL; + return (size_t)-1; + } + +#if HAVE_MBSNRTOWCS + if (indicesp == NULL) + return (xdupmbstowcs2 (destp, src)); +#endif + + memset (&state, '\0', sizeof(mbstate_t)); + wsbuf_size = WSBUF_INC; + + wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t)); + if (wsbuf == NULL) + { + *destp = NULL; + if (indicesp) + *indicesp = NULL; + return (size_t)-1; + } + + indices = NULL; + if (indicesp) + { + indices = (char **) malloc (wsbuf_size * sizeof(char *)); + if (indices == NULL) + { + free (wsbuf); + *destp = NULL; + *indicesp = NULL; + return (size_t)-1; + } + } + + p = src; + wcnum = 0; + do + { + size_t mblength; /* Byte length of one multibyte character. */ + + if (mbsinit (&state)) + { + if (*p == '\0') + { + wc = L'\0'; + mblength = 1; + } + else if (*p == '\\') + { + wc = L'\\'; + mblength = 1; + } + else + mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state); + } + else + mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state); + + /* Conversion failed. */ + if (MB_INVALIDCH (mblength)) + { + free (wsbuf); + FREE (indices); + *destp = NULL; + if (indicesp) + *indicesp = NULL; + return (size_t)-1; + } + + ++wcnum; + + /* Resize buffers when they are not large enough. */ + if (wsbuf_size < wcnum) + { + wchar_t *wstmp; + char **idxtmp; + + wsbuf_size += WSBUF_INC; + + wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t)); + if (wstmp == NULL) + { + free (wsbuf); + FREE (indices); + *destp = NULL; + if (indicesp) + *indicesp = NULL; + return (size_t)-1; + } + wsbuf = wstmp; + + if (indicesp) + { + idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char *)); + if (idxtmp == NULL) + { + free (wsbuf); + free (indices); + *destp = NULL; + if (indicesp) + *indicesp = NULL; + return (size_t)-1; + } + indices = idxtmp; + } + } + + wsbuf[wcnum - 1] = wc; + if (indices) + indices[wcnum - 1] = (char *)p; + p += mblength; + } + while (MB_NULLWCH (wc) == 0); + + /* Return the length of the wide character string, not including `\0'. */ + *destp = wsbuf; + if (indicesp != NULL) + *indicesp = indices; + + return (wcnum - 1); +} + +/* Convert wide character string to multibyte character string. Treat invalid + wide characters as bytes. Used only in unusual circumstances. + + Written by Bruno Haible <bruno@clisp.org>, 2008, adapted by Chet Ramey + for use in Bash. */ + +/* Convert wide character string *SRCP to a multibyte character string and + store the result in DEST. Store at most LEN bytes in DEST. */ +size_t +xwcsrtombs (char *dest, const wchar_t **srcp, size_t len, mbstate_t *ps) +{ + const wchar_t *src; + size_t cur_max; /* XXX - locale_cur_max */ + char buf[64], *destptr, *tmp_dest; + unsigned char uc; + mbstate_t prev_state; + + cur_max = MB_CUR_MAX; + if (cur_max > sizeof (buf)) /* Holy cow. */ + return (size_t)-1; + + src = *srcp; + + if (dest != NULL) + { + destptr = dest; + + for (; len > 0; src++) + { + wchar_t wc; + size_t ret; + + wc = *src; + /* If we have room, store directly into DEST. */ + tmp_dest = destptr; + ret = wcrtomb (len >= cur_max ? destptr : buf, wc, ps); + + if (ret == (size_t)(-1)) /* XXX */ + { + /* Since this is used for globbing and other uses of filenames, + treat invalid wide character sequences as bytes. This is + intended to be symmetric with xdupmbstowcs2. */ +handle_byte: + destptr = tmp_dest; /* in case wcrtomb modified it */ + uc = wc; + ret = 1; + if (len >= cur_max) + *destptr = uc; + else + buf[0] = uc; + if (ps) + memset (ps, 0, sizeof (mbstate_t)); + } + + if (ret > cur_max) /* Holy cow */ + goto bad_input; + + if (len < ret) + break; + + if (len < cur_max) + memcpy (destptr, buf, ret); + + if (wc == 0) + { + src = NULL; + /* Here mbsinit (ps). */ + break; + } + destptr += ret; + len -= ret; + } + *srcp = src; + return destptr - dest; + } + else + { + /* Ignore dest and len, don't store *srcp at the end, and + don't clobber *ps. */ + mbstate_t state = *ps; + size_t totalcount = 0; + + for (;; src++) + { + wchar_t wc; + size_t ret; + + wc = *src; + ret = wcrtomb (buf, wc, &state); + + if (ret == (size_t)(-1)) + goto bad_input2; + if (wc == 0) + { + /* Here mbsinit (&state). */ + break; + } + totalcount += ret; + } + return totalcount; + } + +bad_input: + *srcp = src; +bad_input2: + errno = EILSEQ; + return (size_t)(-1); +} + +#endif /* HANDLE_MULTIBYTE */ diff --git a/lib/intl/ChangeLog b/lib/intl/ChangeLog new file mode 100644 index 0000000..eed2d21 --- /dev/null +++ b/lib/intl/ChangeLog @@ -0,0 +1,4 @@ +2003-05-22 GNU <bug-gnu-gettext@gnu.org> + + * Version 0.12.1 released. + diff --git a/lib/intl/Makefile.in b/lib/intl/Makefile.in new file mode 100644 index 0000000..00fdb61 --- /dev/null +++ b/lib/intl/Makefile.in @@ -0,0 +1,472 @@ +# Makefile for directory with message catalog handling library of GNU gettext +# Copyright (C) 1995-1998, 2000-2003, 2008,2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @BUILD_DIR@ +VPATH = $(srcdir) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +transform = @program_transform_name@ + +datarootdir = @datarootdir@ + +libdir = @libdir@ +includedir = @includedir@ +datadir = @datadir@ +localedir = @localedir@ + +gettextsrcdir = $(datadir)/gettext/intl +aliaspath = $(localedir) +subdir = intl + +@SET_MAKE@ + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +mkinstalldirs = $(SHELL) $(MKINSTALLDIRS) + +l = @INTL_LIBTOOL_SUFFIX_PREFIX@ + +AR = @AR@ +CC = @CC@ +LIBTOOL = @LIBTOOL@ +RANLIB = @RANLIB@ +YACC = @INTLBISON@ -y -d +YFLAGS = --name-prefix=__gettext + +ARFLAGS = @ARFLAGS@ + +LOCAL_DEFS = @LOCAL_DEFS@ + +DEFS = -DLOCALEDIR=\"$(localedir)\" -DLOCALE_ALIAS_PATH=\"$(aliaspath)\" \ +-DLIBDIR=\"$(prefix)/libdata\" -DIN_LIBINTL \ +-DENABLE_RELOCATABLE=1 -DIN_LIBRARY -DINSTALLDIR=\"$(libdir)\" -DNO_XMALLOC \ +-Dset_relocation_prefix=libintl_set_relocation_prefix \ +-Drelocate=libintl_relocate \ +-DDEPENDS_ON_LIBICONV=1 @DEFS@ ${LOCAL_DEFS} +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) + +HEADERS = \ + gmo.h \ + gettextP.h \ + hash-string.h \ + loadinfo.h \ + plural-exp.h \ + eval-plural.h \ + localcharset.h \ + relocatable.h \ + os2compat.h \ + libgnuintl.h.in +SOURCES = \ + bindtextdom.c \ + dcgettext.c \ + dgettext.c \ + gettext.c \ + finddomain.c \ + loadmsgcat.c \ + localealias.c \ + textdomain.c \ + l10nflist.c \ + explodename.c \ + dcigettext.c \ + dcngettext.c \ + dngettext.c \ + ngettext.c \ + plural.y \ + plural-exp.c \ + localcharset.c \ + relocatable.c \ + localename.c \ + log.c \ + osdep.c \ + os2compat.c \ + intl-compat.c +OBJECTS = \ + bindtextdom.$lo \ + dcgettext.$lo \ + dgettext.$lo \ + gettext.$lo \ + finddomain.$lo \ + loadmsgcat.$lo \ + localealias.$lo \ + textdomain.$lo \ + l10nflist.$lo \ + explodename.$lo \ + dcigettext.$lo \ + dcngettext.$lo \ + dngettext.$lo \ + ngettext.$lo \ + plural.$lo \ + plural-exp.$lo \ + localcharset.$lo \ + relocatable.$lo \ + localename.$lo \ + log.$lo \ + osdep.$lo \ + intl-compat.$lo +DISTFILES.common = Makefile.in \ +config.charset locale.alias ref-add.sin ref-del.sin $(HEADERS) $(SOURCES) +DISTFILES.generated = plural.c +DISTFILES.normal = VERSION +DISTFILES.gettext = COPYING.LIB-2.0 COPYING.LIB-2.1 libintl.glibc \ +Makefile.vms libgnuintl.h.msvc-shared README.woe32 Makefile.msvc +DISTFILES.obsolete = xopen-msg.sed linux-msg.sed po2tbl.sed.in cat-compat.c \ +COPYING.LIB-2 gettext.h libgettext.h plural-eval.c libgnuintl.h + +all: all-@USE_INCLUDED_LIBINTL@ +all-yes: libintl.$la libintl.h charset.alias ref-add.sed ref-del.sed +all-no: all-no-@BUILD_INCLUDED_LIBINTL@ +all-no-yes: libgnuintl.$la +all-no-no: + +libintl.a libgnuintl.a: $(OBJECTS) + rm -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + $(RANLIB) $@ + +libintl.la libgnuintl.la: $(OBJECTS) + $(LIBTOOL) --mode=link \ + $(CC) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) $(LDFLAGS) -o $@ \ + $(OBJECTS) @LTLIBICONV@ $(LIBS) \ + -version-info $(LTV_CURRENT):$(LTV_REVISION):$(LTV_AGE) \ + -rpath $(libdir) \ + -no-undefined + +# Libtool's library version information for libintl. +# Before making a gettext release, the gettext maintainer must change this +# according to the libtool documentation, section "Library interface versions". +# Maintainers of other packages that include the intl directory must *not* +# change these values. +LTV_CURRENT=5 +LTV_REVISION=0 +LTV_AGE=3 + +.SUFFIXES: +.SUFFIXES: .c .y .o .lo .sin .sed + +.c.o: + $(COMPILE) $< + +.y.c: + $(YACC) $(YFLAGS) --output $@ $< + rm -f $*.h + +bindtextdom.lo: $(srcdir)/bindtextdom.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/bindtextdom.c +dcgettext.lo: $(srcdir)/dcgettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcgettext.c +dgettext.lo: $(srcdir)/dgettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dgettext.c +gettext.lo: $(srcdir)/gettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/gettext.c +finddomain.lo: $(srcdir)/finddomain.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/finddomain.c +loadmsgcat.lo: $(srcdir)/loadmsgcat.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/loadmsgcat.c +localealias.lo: $(srcdir)/localealias.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localealias.c +textdomain.lo: $(srcdir)/textdomain.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/textdomain.c +l10nflist.lo: $(srcdir)/l10nflist.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/l10nflist.c +explodename.lo: $(srcdir)/explodename.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/explodename.c +dcigettext.lo: $(srcdir)/dcigettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcigettext.c +dcngettext.lo: $(srcdir)/dcngettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcngettext.c +dngettext.lo: $(srcdir)/dngettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dngettext.c +ngettext.lo: $(srcdir)/ngettext.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/ngettext.c +plural.lo: $(srcdir)/plural.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural.c +plural-exp.lo: $(srcdir)/plural-exp.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural-exp.c +localcharset.lo: $(srcdir)/localcharset.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localcharset.c +relocatable.lo: $(srcdir)/relocatable.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/relocatable.c +localename.lo: $(srcdir)/localename.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localename.c +log.lo: $(srcdir)/log.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/log.c +osdep.lo: $(srcdir)/osdep.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/osdep.c +intl-compat.lo: $(srcdir)/intl-compat.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/intl-compat.c + +ref-add.sed: $(srcdir)/ref-add.sin + sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-add.sin > t-ref-add.sed + mv t-ref-add.sed ref-add.sed +ref-del.sed: $(srcdir)/ref-del.sin + sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-del.sin > t-ref-del.sed + mv t-ref-del.sed ref-del.sed + +INCLUDES = -I. -I$(srcdir) -I${top_builddir} -I${top_srcdir} + +libgnuintl.h: $(srcdir)/libgnuintl.h.in + cp $(srcdir)/libgnuintl.h.in libgnuintl.h + +libintl.h: libgnuintl.h + cmp libgnuintl.h libintl.h || cp libgnuintl.h libintl.h + +charset.alias: $(srcdir)/config.charset + $(SHELL) $(srcdir)/config.charset '@host@' > t-$@ + mv t-$@ $@ + +check: all + +# We must not install the libintl.h/libintl.a files if we are on a +# system which has the GNU gettext() function in its C library or in a +# separate library. +# If you want to use the one which comes with this version of the +# package, you have to use `configure --with-included-gettext'. +install: install-exec install-data +install-exec: all + if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \ + $(INSTALL_DATA) libintl.h $(DESTDIR)$(includedir)/libintl.h; \ + $(LIBTOOL) --mode=install \ + $(INSTALL_DATA) libintl.$la $(DESTDIR)$(libdir)/libintl.$la; \ + if test "@RELOCATABLE@" = yes; then \ + dependencies=`sed -n -e 's,^dependency_libs=\(.*\),\1,p' < $(DESTDIR)$(libdir)/libintl.la | sed -e "s,^',," -e "s,'\$$,,"`; \ + if test -n "$dependencies"; then \ + rm -f $(DESTDIR)$(libdir)/libintl.la; \ + fi; \ + fi; \ + else \ + : ; \ + fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir); \ + $(LIBTOOL) --mode=install \ + $(INSTALL_DATA) libgnuintl.$la $(DESTDIR)$(libdir)/libgnuintl.$la; \ + rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + $(INSTALL_DATA) $(DESTDIR)$(libdir)/libgnuintl.so $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + $(LIBTOOL) --mode=uninstall \ + rm -f $(DESTDIR)$(libdir)/libgnuintl.$la; \ + else \ + : ; \ + fi + if test '@USE_INCLUDED_LIBINTL@' = yes; then \ + $(mkinstalldirs) $(DESTDIR)$(localedir); \ + test -f $(DESTDIR)$(localedir)/locale.alias \ + && orig=$(DESTDIR)$(localedir)/locale.alias \ + || orig=$(srcdir)/locale.alias; \ + temp=$(DESTDIR)$(localedir)/t-locale.alias; \ + dest=$(DESTDIR)$(localedir)/locale.alias; \ + sed -f ref-add.sed $$orig > $$temp; \ + $(INSTALL_DATA) $$temp $$dest; \ + rm -f $$temp; \ + else \ + : ; \ + fi +install-data: all + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \ + $(INSTALL_DATA) VERSION $(DESTDIR)$(gettextsrcdir)/VERSION; \ + $(INSTALL_DATA) ChangeLog.inst $(DESTDIR)$(gettextsrcdir)/ChangeLog; \ + dists="COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common)"; \ + for file in $$dists; do \ + $(INSTALL_DATA) $(srcdir)/$$file \ + $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + chmod a+x $(DESTDIR)$(gettextsrcdir)/config.charset; \ + dists="$(DISTFILES.generated)"; \ + for file in $$dists; do \ + if test -f $$file; then dir=.; else dir=$(srcdir); fi; \ + $(INSTALL_DATA) $$dir/$$file \ + $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + dists="$(DISTFILES.obsolete)"; \ + for file in $$dists; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi + +install-strip: install + +installdirs: + if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \ + else \ + : ; \ + fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir); \ + else \ + : ; \ + fi + if test '@USE_INCLUDED_LIBINTL@' = yes; then \ + test @GLIBC21@ != no || $(mkinstalldirs) $(DESTDIR)$(libdir); \ + $(mkinstalldirs) $(DESTDIR)$(localedir); \ + else \ + : ; \ + fi + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \ + else \ + : ; \ + fi + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: + if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ + rm -f $(DESTDIR)$(includedir)/libintl.h; \ + $(LIBTOOL) --mode=uninstall \ + rm -f $(DESTDIR)$(libdir)/libintl.$la; \ + else \ + : ; \ + fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + else \ + : ; \ + fi + if test '@USE_INCLUDED_LIBINTL@' = yes; then \ + if test -f $(DESTDIR)$(prefix)/libdata/charset.alias; then \ + temp=$(DESTDIR)$(prefix)/libdata/t-charset.alias; \ + dest=$(DESTDIR)$(prefix)/libdata/charset.alias; \ + sed -f ref-del.sed $$dest > $$temp; \ + if grep '^# Packages using this file: $$' $$temp > /dev/null; then \ + rm -f $$dest; \ + else \ + $(INSTALL_DATA) $$temp $$dest; \ + fi; \ + rm -f $$temp; \ + fi; \ + if test -f $(DESTDIR)$(localedir)/locale.alias; then \ + temp=$(DESTDIR)$(localedir)/t-locale.alias; \ + dest=$(DESTDIR)$(localedir)/locale.alias; \ + sed -f ref-del.sed $$dest > $$temp; \ + if grep '^# Packages using this file: $$' $$temp > /dev/null; then \ + rm -f $$dest; \ + else \ + $(INSTALL_DATA) $$temp $$dest; \ + fi; \ + rm -f $$temp; \ + fi; \ + else \ + : ; \ + fi + if test "$(PACKAGE)" = "gettext-tools"; then \ + for file in VERSION ChangeLog COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common) $(DISTFILES.generated); do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi + +info dvi ps pdf html: + +$(OBJECTS): ${top_builddir}/config.h libgnuintl.h +bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h +dcigettext.$lo loadmsgcat.$lo: $(srcdir)/hash-string.h +explodename.$lo l10nflist.$lo: $(srcdir)/loadinfo.h +dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo: $(srcdir)/plural-exp.h +dcigettext.$lo: $(srcdir)/eval-plural.h +localcharset.$lo: $(srcdir)/localcharset.h +localealias.$lo localcharset.$lo relocatable.$lo: $(srcdir)/relocatable.h + +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) + here=`pwd`; cd $(srcdir) && etags -o $$here/TAGS $(HEADERS) $(SOURCES) + +ctags: CTAGS + +CTAGS: $(HEADERS) $(SOURCES) + here=`pwd`; cd $(srcdir) && ctags -o $$here/CTAGS $(HEADERS) $(SOURCES) + +id: ID + +ID: $(HEADERS) $(SOURCES) + here=`pwd`; cd $(srcdir) && mkid -f$$here/ID $(HEADERS) $(SOURCES) + + +mostlyclean: + rm -f *.a *.la *.o *.obj *.lo core core.* + rm -f libgnuintl.h libintl.h charset.alias ref-add.sed ref-del.sed + rm -f -r .libs _libs + +clean: mostlyclean + +distclean: clean + rm -f Makefile ID TAGS + if test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; then \ + rm -f ChangeLog.inst $(DISTFILES.normal); \ + else \ + : ; \ + fi + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + + +# GNU gettext needs not contain the file `VERSION' but contains some +# other files which should not be distributed in other packages. +distdir = ../$(PACKAGE)-$(VERSION)/$(subdir) +dist distdir: Makefile + if test "$(PACKAGE)" = "gettext-tools"; then \ + : ; \ + else \ + if test "$(PACKAGE)" = "gettext-runtime"; then \ + additional="$(DISTFILES.gettext)"; \ + else \ + additional="$(DISTFILES.normal)"; \ + fi; \ + $(MAKE) $(DISTFILES.common) $(DISTFILES.generated) $$additional; \ + for file in ChangeLog $(DISTFILES.common) $(DISTFILES.generated) $$additional; do \ + if test -f $$file; then dir=.; else dir=$(srcdir); fi; \ + cp -p $$dir/$$file $(distdir); \ + done; \ + fi + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status +# This would be more efficient, but doesn't work any more with autoconf-2.57, +# when AC_CONFIG_FILES([intl/Makefile:somedir/Makefile.in]) is used. +# cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/intl/VERSION b/lib/intl/VERSION new file mode 100644 index 0000000..f49e0dd --- /dev/null +++ b/lib/intl/VERSION @@ -0,0 +1,3 @@ +GNU gettext library from gettext-0.12.1 + +This is here only in the case the system doesn't provide it diff --git a/lib/intl/bindtextdom.c b/lib/intl/bindtextdom.c new file mode 100644 index 0000000..ef5479e --- /dev/null +++ b/lib/intl/bindtextdom.c @@ -0,0 +1,376 @@ +/* bindtextdom.c - Implementation of the bindtextdomain(3) function */ + +/* Copyright (C) 1995-1998, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif +#include "gettextP.h" + +#ifdef _LIBC +/* We have to handle multi-threaded applications. */ +# include <bits/libc-lock.h> +#else +/* Provide dummy implementation if this is outside glibc. */ +# define __libc_rwlock_define(CLASS, NAME) +# define __libc_rwlock_wrlock(NAME) +# define __libc_rwlock_unlock(NAME) +#endif + +/* The internal variables in the standalone libintl.a must have different + names than the internal variables in GNU libc, otherwise programs + using libintl.a cannot be linked statically. */ +#if !defined _LIBC +# define _nl_default_dirname libintl_nl_default_dirname +# define _nl_domain_bindings libintl_nl_domain_bindings +#endif + +/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */ +#ifndef offsetof +# define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) +#endif + +/* @@ end of prolog @@ */ + +/* Contains the default location of the message catalogs. */ +extern const char _nl_default_dirname[]; +#ifdef _LIBC +extern const char _nl_default_dirname_internal[] attribute_hidden; +#else +# define INTUSE(name) name +#endif + +/* List with bindings of specific domains. */ +extern struct binding *_nl_domain_bindings; + +/* Lock variable to protect the global data in the gettext implementation. */ +__libc_rwlock_define (extern, _nl_state_lock attribute_hidden) + + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define BINDTEXTDOMAIN __bindtextdomain +# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset +# ifndef strdup +# define strdup(str) __strdup (str) +# endif +#else +# define BINDTEXTDOMAIN libintl_bindtextdomain +# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset +#endif + +/* Prototypes for local functions. */ +static void set_binding_values PARAMS ((const char *domainname, + const char **dirnamep, + const char **codesetp)); + +/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP + to be used for the DOMAINNAME message catalog. + If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not + modified, only the current value is returned. + If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither + modified nor returned. */ +static void +set_binding_values (domainname, dirnamep, codesetp) + const char *domainname; + const char **dirnamep; + const char **codesetp; +{ + struct binding *binding; + int modified; + + /* Some sanity checks. */ + if (domainname == NULL || domainname[0] == '\0') + { + if (dirnamep) + *dirnamep = NULL; + if (codesetp) + *codesetp = NULL; + return; + } + + __libc_rwlock_wrlock (_nl_state_lock); + + modified = 0; + + for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) + { + int compare = strcmp (domainname, binding->domainname); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It is not in the list. */ + binding = NULL; + break; + } + } + + if (binding != NULL) + { + if (dirnamep) + { + const char *dirname = *dirnamep; + + if (dirname == NULL) + /* The current binding has be to returned. */ + *dirnamep = binding->dirname; + else + { + /* The domain is already bound. If the new value and the old + one are equal we simply do nothing. Otherwise replace the + old binding. */ + char *result = binding->dirname; + if (strcmp (dirname, result) != 0) + { + if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0) + result = (char *) INTUSE(_nl_default_dirname); + else + { +#if defined _LIBC || defined HAVE_STRDUP + result = strdup (dirname); +#else + size_t len = strlen (dirname) + 1; + result = (char *) malloc (len); + if (__builtin_expect (result != NULL, 1)) + memcpy (result, dirname, len); +#endif + } + + if (__builtin_expect (result != NULL, 1)) + { + if (binding->dirname != INTUSE(_nl_default_dirname)) + free (binding->dirname); + + binding->dirname = result; + modified = 1; + } + } + *dirnamep = result; + } + } + + if (codesetp) + { + const char *codeset = *codesetp; + + if (codeset == NULL) + /* The current binding has be to returned. */ + *codesetp = binding->codeset; + else + { + /* The domain is already bound. If the new value and the old + one are equal we simply do nothing. Otherwise replace the + old binding. */ + char *result = binding->codeset; + if (result == NULL || strcmp (codeset, result) != 0) + { +#if defined _LIBC || defined HAVE_STRDUP + result = strdup (codeset); +#else + size_t len = strlen (codeset) + 1; + result = (char *) malloc (len); + if (__builtin_expect (result != NULL, 1)) + memcpy (result, codeset, len); +#endif + + if (__builtin_expect (result != NULL, 1)) + { + if (binding->codeset != NULL) + free (binding->codeset); + + binding->codeset = result; + binding->codeset_cntr++; + modified = 1; + } + } + *codesetp = result; + } + } + } + else if ((dirnamep == NULL || *dirnamep == NULL) + && (codesetp == NULL || *codesetp == NULL)) + { + /* Simply return the default values. */ + if (dirnamep) + *dirnamep = INTUSE(_nl_default_dirname); + if (codesetp) + *codesetp = NULL; + } + else + { + /* We have to create a new binding. */ + size_t len = strlen (domainname) + 1; + struct binding *new_binding = + (struct binding *) malloc (offsetof (struct binding, domainname) + len); + + if (__builtin_expect (new_binding == NULL, 0)) + goto failed; + + memcpy (new_binding->domainname, domainname, len); + + if (dirnamep) + { + const char *dirname = *dirnamep; + + if (dirname == NULL) + /* The default value. */ + dirname = INTUSE(_nl_default_dirname); + else + { + if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0) + dirname = INTUSE(_nl_default_dirname); + else + { + char *result; +#if defined _LIBC || defined HAVE_STRDUP + result = strdup (dirname); + if (__builtin_expect (result == NULL, 0)) + goto failed_dirname; +#else + size_t len = strlen (dirname) + 1; + result = (char *) malloc (len); + if (__builtin_expect (result == NULL, 0)) + goto failed_dirname; + memcpy (result, dirname, len); +#endif + dirname = result; + } + } + *dirnamep = dirname; + new_binding->dirname = (char *) dirname; + } + else + /* The default value. */ + new_binding->dirname = (char *) INTUSE(_nl_default_dirname); + + new_binding->codeset_cntr = 0; + + if (codesetp) + { + const char *codeset = *codesetp; + + if (codeset != NULL) + { + char *result; + +#if defined _LIBC || defined HAVE_STRDUP + result = strdup (codeset); + if (__builtin_expect (result == NULL, 0)) + goto failed_codeset; +#else + size_t len = strlen (codeset) + 1; + result = (char *) malloc (len); + if (__builtin_expect (result == NULL, 0)) + goto failed_codeset; + memcpy (result, codeset, len); +#endif + codeset = result; + new_binding->codeset_cntr++; + } + *codesetp = codeset; + new_binding->codeset = (char *) codeset; + } + else + new_binding->codeset = NULL; + + /* Now enqueue it. */ + if (_nl_domain_bindings == NULL + || strcmp (domainname, _nl_domain_bindings->domainname) < 0) + { + new_binding->next = _nl_domain_bindings; + _nl_domain_bindings = new_binding; + } + else + { + binding = _nl_domain_bindings; + while (binding->next != NULL + && strcmp (domainname, binding->next->domainname) > 0) + binding = binding->next; + + new_binding->next = binding->next; + binding->next = new_binding; + } + + modified = 1; + + /* Here we deal with memory allocation failures. */ + if (0) + { + failed_codeset: + if (new_binding->dirname != INTUSE(_nl_default_dirname)) + free (new_binding->dirname); + failed_dirname: + free (new_binding); + failed: + if (dirnamep) + *dirnamep = NULL; + if (codesetp) + *codesetp = NULL; + } + } + + /* If we modified any binding, we flush the caches. */ + if (modified) + ++_nl_msg_cat_cntr; + + __libc_rwlock_unlock (_nl_state_lock); +} + +/* Specify that the DOMAINNAME message catalog will be found + in DIRNAME rather than in the system locale data base. */ +char * +BINDTEXTDOMAIN (domainname, dirname) + const char *domainname; + const char *dirname; +{ + set_binding_values (domainname, &dirname, NULL); + return (char *) dirname; +} + +/* Specify the character encoding in which the messages from the + DOMAINNAME message catalog will be returned. */ +char * +BIND_TEXTDOMAIN_CODESET (domainname, codeset) + const char *domainname; + const char *codeset; +{ + set_binding_values (domainname, NULL, &codeset); + return (char *) codeset; +} + +#ifdef _LIBC +/* Aliases for function names in GNU C Library. */ +weak_alias (__bindtextdomain, bindtextdomain); +weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset); +#endif diff --git a/lib/intl/config.charset b/lib/intl/config.charset new file mode 100644 index 0000000..10c4439 --- /dev/null +++ b/lib/intl/config.charset @@ -0,0 +1,465 @@ +#! /bin/sh +# Output a system dependent table of character encoding aliases. +# +# Copyright (C) 2000-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# +# The table consists of lines of the form +# ALIAS CANONICAL +# +# ALIAS is the (system dependent) result of "nl_langinfo (CODESET)". +# ALIAS is compared in a case sensitive way. +# +# CANONICAL is the GNU canonical name for this character encoding. +# It must be an encoding supported by libiconv. Support by GNU libc is +# also desirable. CANONICAL is case insensitive. Usually an upper case +# MIME charset name is preferred. +# The current list of GNU canonical charset names is as follows. +# +# name used by which systems a MIME name? +# ASCII, ANSI_X3.4-1968 glibc solaris freebsd +# ISO-8859-1 glibc aix hpux irix osf solaris freebsd yes +# ISO-8859-2 glibc aix hpux irix osf solaris freebsd yes +# ISO-8859-3 glibc solaris yes +# ISO-8859-4 osf solaris freebsd yes +# ISO-8859-5 glibc aix hpux irix osf solaris freebsd yes +# ISO-8859-6 glibc aix hpux solaris yes +# ISO-8859-7 glibc aix hpux irix osf solaris yes +# ISO-8859-8 glibc aix hpux osf solaris yes +# ISO-8859-9 glibc aix hpux irix osf solaris yes +# ISO-8859-13 glibc +# ISO-8859-14 glibc +# ISO-8859-15 glibc aix osf solaris freebsd +# KOI8-R glibc solaris freebsd yes +# KOI8-U glibc freebsd yes +# KOI8-T glibc +# CP437 dos +# CP775 dos +# CP850 aix osf dos +# CP852 dos +# CP855 dos +# CP856 aix +# CP857 dos +# CP861 dos +# CP862 dos +# CP864 dos +# CP865 dos +# CP866 freebsd dos +# CP869 dos +# CP874 woe32 dos +# CP922 aix +# CP932 aix woe32 dos +# CP943 aix +# CP949 osf woe32 dos +# CP950 woe32 dos +# CP1046 aix +# CP1124 aix +# CP1125 dos +# CP1129 aix +# CP1250 woe32 +# CP1251 glibc solaris woe32 +# CP1252 aix woe32 +# CP1253 woe32 +# CP1254 woe32 +# CP1255 glibc woe32 +# CP1256 woe32 +# CP1257 woe32 +# GB2312 glibc aix hpux irix solaris freebsd yes +# EUC-JP glibc aix hpux irix osf solaris freebsd yes +# EUC-KR glibc aix hpux irix osf solaris freebsd yes +# EUC-TW glibc aix hpux irix osf solaris +# BIG5 glibc aix hpux osf solaris freebsd yes +# BIG5-HKSCS glibc solaris +# GBK glibc aix osf solaris woe32 dos +# GB18030 glibc solaris +# SHIFT_JIS hpux osf solaris freebsd yes +# JOHAB glibc solaris woe32 +# TIS-620 glibc aix hpux osf solaris +# VISCII glibc yes +# TCVN5712-1 glibc +# GEORGIAN-PS glibc +# HP-ROMAN8 hpux +# HP-ARABIC8 hpux +# HP-GREEK8 hpux +# HP-HEBREW8 hpux +# HP-TURKISH8 hpux +# HP-KANA8 hpux +# DEC-KANJI osf +# DEC-HANYU osf +# UTF-8 glibc aix hpux osf solaris yes +# +# Note: Names which are not marked as being a MIME name should not be used in +# Internet protocols for information interchange (mail, news, etc.). +# +# Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names. Applications +# must understand both names and treat them as equivalent. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM + +host="$1" +os=`echo "$host" | sed -e 's/^[^-]*-[^-]*-\(.*\)$/\1/'` +echo "# This file contains a table of character encoding aliases," +echo "# suitable for operating system '${os}'." +echo "# It was automatically generated from config.charset." +# List of references, updated during installation: +echo "# Packages using this file: " +case "$os" in + linux* | *-gnu*) + # With glibc-2.1 or newer, we don't need any canonicalization, + # because glibc has iconv and both glibc and libiconv support all + # GNU canonical names directly. Therefore, the Makefile does not + # need to install the alias file at all. + # The following applies only to glibc-2.0.x and older libcs. + echo "ISO_646.IRV:1983 ASCII" + ;; + aix*) + echo "ISO8859-1 ISO-8859-1" + echo "ISO8859-2 ISO-8859-2" + echo "ISO8859-5 ISO-8859-5" + echo "ISO8859-6 ISO-8859-6" + echo "ISO8859-7 ISO-8859-7" + echo "ISO8859-8 ISO-8859-8" + echo "ISO8859-9 ISO-8859-9" + echo "ISO8859-15 ISO-8859-15" + echo "IBM-850 CP850" + echo "IBM-856 CP856" + echo "IBM-921 ISO-8859-13" + echo "IBM-922 CP922" + echo "IBM-932 CP932" + echo "IBM-943 CP943" + echo "IBM-1046 CP1046" + echo "IBM-1124 CP1124" + echo "IBM-1129 CP1129" + echo "IBM-1252 CP1252" + echo "IBM-eucCN GB2312" + echo "IBM-eucJP EUC-JP" + echo "IBM-eucKR EUC-KR" + echo "IBM-eucTW EUC-TW" + echo "big5 BIG5" + echo "GBK GBK" + echo "TIS-620 TIS-620" + echo "UTF-8 UTF-8" + ;; + hpux*) + echo "iso88591 ISO-8859-1" + echo "iso88592 ISO-8859-2" + echo "iso88595 ISO-8859-5" + echo "iso88596 ISO-8859-6" + echo "iso88597 ISO-8859-7" + echo "iso88598 ISO-8859-8" + echo "iso88599 ISO-8859-9" + echo "iso885915 ISO-8859-15" + echo "roman8 HP-ROMAN8" + echo "arabic8 HP-ARABIC8" + echo "greek8 HP-GREEK8" + echo "hebrew8 HP-HEBREW8" + echo "turkish8 HP-TURKISH8" + echo "kana8 HP-KANA8" + echo "tis620 TIS-620" + echo "big5 BIG5" + echo "eucJP EUC-JP" + echo "eucKR EUC-KR" + echo "eucTW EUC-TW" + echo "hp15CN GB2312" + #echo "ccdc ?" # what is this? + echo "SJIS SHIFT_JIS" + echo "utf8 UTF-8" + ;; + irix*) + echo "ISO8859-1 ISO-8859-1" + echo "ISO8859-2 ISO-8859-2" + echo "ISO8859-5 ISO-8859-5" + echo "ISO8859-7 ISO-8859-7" + echo "ISO8859-9 ISO-8859-9" + echo "eucCN GB2312" + echo "eucJP EUC-JP" + echo "eucKR EUC-KR" + echo "eucTW EUC-TW" + ;; + osf*) + echo "ISO8859-1 ISO-8859-1" + echo "ISO8859-2 ISO-8859-2" + echo "ISO8859-4 ISO-8859-4" + echo "ISO8859-5 ISO-8859-5" + echo "ISO8859-7 ISO-8859-7" + echo "ISO8859-8 ISO-8859-8" + echo "ISO8859-9 ISO-8859-9" + echo "ISO8859-15 ISO-8859-15" + echo "cp850 CP850" + echo "big5 BIG5" + echo "dechanyu DEC-HANYU" + echo "dechanzi GB2312" + echo "deckanji DEC-KANJI" + echo "deckorean EUC-KR" + echo "eucJP EUC-JP" + echo "eucKR EUC-KR" + echo "eucTW EUC-TW" + echo "GBK GBK" + echo "KSC5601 CP949" + echo "sdeckanji EUC-JP" + echo "SJIS SHIFT_JIS" + echo "TACTIS TIS-620" + echo "UTF-8 UTF-8" + ;; + solaris*) + echo "646 ASCII" + echo "ISO8859-1 ISO-8859-1" + echo "ISO8859-2 ISO-8859-2" + echo "ISO8859-3 ISO-8859-3" + echo "ISO8859-4 ISO-8859-4" + echo "ISO8859-5 ISO-8859-5" + echo "ISO8859-6 ISO-8859-6" + echo "ISO8859-7 ISO-8859-7" + echo "ISO8859-8 ISO-8859-8" + echo "ISO8859-9 ISO-8859-9" + echo "ISO8859-15 ISO-8859-15" + echo "koi8-r KOI8-R" + echo "ansi-1251 CP1251" + echo "BIG5 BIG5" + echo "Big5-HKSCS BIG5-HKSCS" + echo "gb2312 GB2312" + echo "GBK GBK" + echo "GB18030 GB18030" + echo "cns11643 EUC-TW" + echo "5601 EUC-KR" + echo "ko_KR.johap92 JOHAB" + echo "eucJP EUC-JP" + echo "PCK SHIFT_JIS" + echo "TIS620.2533 TIS-620" + #echo "sun_eu_greek ?" # what is this? + echo "UTF-8 UTF-8" + ;; + freebsd* | os2*) + # FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore + # localcharset.c falls back to using the full locale name + # from the environment variables. + # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just + # reuse FreeBSD's locale data for OS/2. + echo "C ASCII" + echo "US-ASCII ASCII" + for l in la_LN lt_LN; do + echo "$l.ASCII ASCII" + done + for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \ + fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT la_LN \ + lt_LN nl_BE nl_NL no_NO pt_PT sv_SE; do + echo "$l.ISO_8859-1 ISO-8859-1" + echo "$l.DIS_8859-15 ISO-8859-15" + done + for l in cs_CZ hr_HR hu_HU la_LN lt_LN pl_PL sl_SI; do + echo "$l.ISO_8859-2 ISO-8859-2" + done + for l in la_LN lt_LT; do + echo "$l.ISO_8859-4 ISO-8859-4" + done + for l in ru_RU ru_SU; do + echo "$l.KOI8-R KOI8-R" + echo "$l.ISO_8859-5 ISO-8859-5" + echo "$l.CP866 CP866" + done + echo "uk_UA.KOI8-U KOI8-U" + echo "zh_TW.BIG5 BIG5" + echo "zh_TW.Big5 BIG5" + echo "zh_CN.EUC GB2312" + echo "ja_JP.EUC EUC-JP" + echo "ja_JP.SJIS SHIFT_JIS" + echo "ja_JP.Shift_JIS SHIFT_JIS" + echo "ko_KR.EUC EUC-KR" + ;; + netbsd*) + echo "646 ASCII" + echo "ISO8859-1 ISO-8859-1" + echo "ISO8859-2 ISO-8859-2" + echo "ISO8859-4 ISO-8859-4" + echo "ISO8859-5 ISO-8859-5" + echo "ISO8859-15 ISO-8859-15" + echo "eucCN GB2312" + echo "eucJP EUC-JP" + echo "eucKR EUC-KR" + echo "eucTW EUC-TW" + echo "BIG5 BIG5" + echo "SJIS SHIFT_JIS" + ;; + beos*) + # BeOS has a single locale, and it has UTF-8 encoding. + echo "* UTF-8" + ;; + msdosdjgpp*) + # DJGPP 2.03 doesn't have nl_langinfo(CODESET); therefore + # localcharset.c falls back to using the full locale name + # from the environment variables. + echo "#" + echo "# The encodings given here may not all be correct." + echo "# If you find that the encoding given for your language and" + echo "# country is not the one your DOS machine actually uses, just" + echo "# correct it in this file, and send a mail to" + echo "# Juan Manuel Guerrero <st001906@hrz1.hrz.tu-darmstadt.de>" + echo "# and Bruno Haible <bruno@clisp.org>." + echo "#" + echo "C ASCII" + # ISO-8859-1 languages + echo "ca CP850" + echo "ca_ES CP850" + echo "da CP865" # not CP850 ?? + echo "da_DK CP865" # not CP850 ?? + echo "de CP850" + echo "de_AT CP850" + echo "de_CH CP850" + echo "de_DE CP850" + echo "en CP850" + echo "en_AU CP850" # not CP437 ?? + echo "en_CA CP850" + echo "en_GB CP850" + echo "en_NZ CP437" + echo "en_US CP437" + echo "en_ZA CP850" # not CP437 ?? + echo "es CP850" + echo "es_AR CP850" + echo "es_BO CP850" + echo "es_CL CP850" + echo "es_CO CP850" + echo "es_CR CP850" + echo "es_CU CP850" + echo "es_DO CP850" + echo "es_EC CP850" + echo "es_ES CP850" + echo "es_GT CP850" + echo "es_HN CP850" + echo "es_MX CP850" + echo "es_NI CP850" + echo "es_PA CP850" + echo "es_PY CP850" + echo "es_PE CP850" + echo "es_SV CP850" + echo "es_UY CP850" + echo "es_VE CP850" + echo "et CP850" + echo "et_EE CP850" + echo "eu CP850" + echo "eu_ES CP850" + echo "fi CP850" + echo "fi_FI CP850" + echo "fr CP850" + echo "fr_BE CP850" + echo "fr_CA CP850" + echo "fr_CH CP850" + echo "fr_FR CP850" + echo "ga CP850" + echo "ga_IE CP850" + echo "gd CP850" + echo "gd_GB CP850" + echo "gl CP850" + echo "gl_ES CP850" + echo "id CP850" # not CP437 ?? + echo "id_ID CP850" # not CP437 ?? + echo "is CP861" # not CP850 ?? + echo "is_IS CP861" # not CP850 ?? + echo "it CP850" + echo "it_CH CP850" + echo "it_IT CP850" + echo "lt CP775" + echo "lt_LT CP775" + echo "lv CP775" + echo "lv_LV CP775" + echo "nb CP865" # not CP850 ?? + echo "nb_NO CP865" # not CP850 ?? + echo "nl CP850" + echo "nl_BE CP850" + echo "nl_NL CP850" + echo "nn CP865" # not CP850 ?? + echo "nn_NO CP865" # not CP850 ?? + echo "no CP865" # not CP850 ?? + echo "no_NO CP865" # not CP850 ?? + echo "pt CP850" + echo "pt_BR CP850" + echo "pt_PT CP850" + echo "sv CP850" + echo "sv_SE CP850" + # ISO-8859-2 languages + echo "cs CP852" + echo "cs_CZ CP852" + echo "hr CP852" + echo "hr_HR CP852" + echo "hu CP852" + echo "hu_HU CP852" + echo "pl CP852" + echo "pl_PL CP852" + echo "ro CP852" + echo "ro_RO CP852" + echo "sk CP852" + echo "sk_SK CP852" + echo "sl CP852" + echo "sl_SI CP852" + echo "sq CP852" + echo "sq_AL CP852" + echo "sr CP852" # CP852 or CP866 or CP855 ?? + echo "sr_YU CP852" # CP852 or CP866 or CP855 ?? + # ISO-8859-3 languages + echo "mt CP850" + echo "mt_MT CP850" + # ISO-8859-5 languages + echo "be CP866" + echo "be_BE CP866" + echo "bg CP866" # not CP855 ?? + echo "bg_BG CP866" # not CP855 ?? + echo "mk CP866" # not CP855 ?? + echo "mk_MK CP866" # not CP855 ?? + echo "ru CP866" + echo "ru_RU CP866" + echo "uk CP1125" + echo "uk_UA CP1125" + # ISO-8859-6 languages + echo "ar CP864" + echo "ar_AE CP864" + echo "ar_DZ CP864" + echo "ar_EG CP864" + echo "ar_IQ CP864" + echo "ar_IR CP864" + echo "ar_JO CP864" + echo "ar_KW CP864" + echo "ar_MA CP864" + echo "ar_OM CP864" + echo "ar_QA CP864" + echo "ar_SA CP864" + echo "ar_SY CP864" + # ISO-8859-7 languages + echo "el CP869" + echo "el_GR CP869" + # ISO-8859-8 languages + echo "he CP862" + echo "he_IL CP862" + # ISO-8859-9 languages + echo "tr CP857" + echo "tr_TR CP857" + # Japanese + echo "ja CP932" + echo "ja_JP CP932" + # Chinese + echo "zh_CN GBK" + echo "zh_TW CP950" # not CP938 ?? + # Korean + echo "kr CP949" # not CP934 ?? + echo "kr_KR CP949" # not CP934 ?? + # Thai + echo "th CP874" + echo "th_TH CP874" + # Other + echo "eo CP850" + echo "eo_EO CP850" + ;; +esac diff --git a/lib/intl/dcgettext.c b/lib/intl/dcgettext.c new file mode 100644 index 0000000..c156ca2 --- /dev/null +++ b/lib/intl/dcgettext.c @@ -0,0 +1,61 @@ +/* dcgettext.c - Implementation of the dcgettext(3) function. */ + +/* Copyright (C) 1995-1999, 2000, 2001, 2002, 2006-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define DCGETTEXT __dcgettext +# define DCIGETTEXT __dcigettext +#else +# define DCGETTEXT libintl_dcgettext +# define DCIGETTEXT libintl_dcigettext +#endif + +/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY + locale. */ +char * +DCGETTEXT (domainname, msgid, category) + const char *domainname; + const char *msgid; + int category; +{ + return DCIGETTEXT (domainname, msgid, NULL, 0, 0, category); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +INTDEF(__dcgettext) +weak_alias (__dcgettext, dcgettext); +#endif diff --git a/lib/intl/dcigettext.c b/lib/intl/dcigettext.c new file mode 100644 index 0000000..c0f347e --- /dev/null +++ b/lib/intl/dcigettext.c @@ -0,0 +1,1248 @@ +/* dcigettext.c - Implementation of the internal dcigettext function. */ + +/* Copyright (C) 1995-1999, 2000-2003, 2006-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tell glibc's <string.h> to provide a prototype for mempcpy(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> + +#ifdef __GNUC__ +# define alloca __builtin_alloca +# define HAVE_ALLOCA 1 +#else +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca +# else +# if defined HAVE_ALLOCA_H || defined _LIBC +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#include <errno.h> +#ifndef errno +extern int errno; +#endif +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#if defined HAVE_UNISTD_H || defined _LIBC +# include <unistd.h> +#endif + +#include <locale.h> + +#ifdef _LIBC + /* Guess whether integer division by zero raises signal SIGFPE. + Set to 1 only if you know for sure. In case of doubt, set to 0. */ +# if defined __alpha__ || defined __arm__ || defined __i386__ \ + || defined __m68k__ || defined __s390__ +# define INTDIV0_RAISES_SIGFPE 1 +# else +# define INTDIV0_RAISES_SIGFPE 0 +# endif +#endif +#if !INTDIV0_RAISES_SIGFPE +# include <signal.h> +#endif + +#if defined HAVE_SYS_PARAM_H || defined _LIBC +# include <sys/param.h> +#endif + +#include "gettextP.h" +#include "plural-exp.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif +#include "hash-string.h" + +/* Thread safetyness. */ +#ifdef _LIBC +# include <bits/libc-lock.h> +#else +/* Provide dummy implementation if this is outside glibc. */ +# define __libc_lock_define_initialized(CLASS, NAME) +# define __libc_lock_lock(NAME) +# define __libc_lock_unlock(NAME) +# define __libc_rwlock_define_initialized(CLASS, NAME) +# define __libc_rwlock_rdlock(NAME) +# define __libc_rwlock_unlock(NAME) +#endif + +/* Alignment of types. */ +#if defined __GNUC__ && __GNUC__ >= 2 +# define alignof(TYPE) __alignof__ (TYPE) +#else +# define alignof(TYPE) \ + ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2) +#endif + +/* The internal variables in the standalone libintl.a must have different + names than the internal variables in GNU libc, otherwise programs + using libintl.a cannot be linked statically. */ +#if !defined _LIBC +# define _nl_default_default_domain libintl_nl_default_default_domain +# define _nl_current_default_domain libintl_nl_current_default_domain +# define _nl_default_dirname libintl_nl_default_dirname +# define _nl_domain_bindings libintl_nl_domain_bindings +#endif + +/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */ +#ifndef offsetof +# define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) +#endif + +/* @@ end of prolog @@ */ + +#if defined (SHELL) && !defined (HAVE_GETCWD) +# define HAVE_GETCWD +#endif + +#ifdef _LIBC +/* Rename the non ANSI C functions. This is required by the standard + because some ANSI C functions will require linking with this object + file and the name space must not be polluted. */ +# define getcwd __getcwd +# ifndef stpcpy +# define stpcpy __stpcpy +# endif +# define tfind __tfind +#else +# if !defined HAVE_GETCWD +char *getwd (); +# define getcwd(buf, max) getwd (buf) +# else +char *getcwd (); +# endif +# ifndef HAVE_STPCPY +static char *stpcpy PARAMS ((char *dest, const char *src)); +# endif +# ifndef HAVE_MEMPCPY +static void *mempcpy PARAMS ((void *dest, const void *src, size_t n)); +# endif +#endif + +/* Amount to increase buffer size by in each try. */ +#define PATH_INCR 32 + +/* The following is from pathmax.h. */ +/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define + PATH_MAX but might cause redefinition warnings when sys/param.h is + later included (as on MORE/BSD 4.3). */ +#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__) +# include <limits.h> +#endif + +#ifndef _POSIX_PATH_MAX +# define _POSIX_PATH_MAX 255 +#endif + +#if !defined PATH_MAX && defined _PC_PATH_MAX +# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) +#endif + +/* Don't include sys/param.h if it already has been. */ +#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN +# include <sys/param.h> +#endif + +#if !defined PATH_MAX && defined MAXPATHLEN +# define PATH_MAX MAXPATHLEN +#endif + +#ifndef PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +#endif + +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not, + it may be concatenated to a directory pathname. + IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. + */ +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +# define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ + && (P)[1] == ':') +# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P)) +# define IS_PATH_WITH_DIR(P) \ + (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0]) +# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) +#endif + +/* This is the type used for the search tree where known translations + are stored. */ +struct known_translation_t +{ + /* Domain in which to search. */ + char *domainname; + + /* The category. */ + int category; + + /* State of the catalog counter at the point the string was found. */ + int counter; + + /* Catalog where the string was found. */ + struct loaded_l10nfile *domain; + + /* And finally the translation. */ + const char *translation; + size_t translation_length; + + /* Pointer to the string in question. */ + char msgid[ZERO]; +}; + +/* Root of the search tree with known translations. We can use this + only if the system provides the `tsearch' function family. */ +#if defined HAVE_TSEARCH || defined _LIBC +# include <search.h> + +static void *root; + +# ifdef _LIBC +# define tsearch __tsearch +# endif + +/* Function to compare two entries in the table of known translations. */ +static int transcmp PARAMS ((const void *p1, const void *p2)); +static int +transcmp (p1, p2) + const void *p1; + const void *p2; +{ + const struct known_translation_t *s1; + const struct known_translation_t *s2; + int result; + + s1 = (const struct known_translation_t *) p1; + s2 = (const struct known_translation_t *) p2; + + result = strcmp (s1->msgid, s2->msgid); + if (result == 0) + { + result = strcmp (s1->domainname, s2->domainname); + if (result == 0) + /* We compare the category last (though this is the cheapest + operation) since it is hopefully always the same (namely + LC_MESSAGES). */ + result = s1->category - s2->category; + } + + return result; +} +#endif + +#ifndef INTVARDEF +# define INTVARDEF(name) +#endif +#ifndef INTUSE +# define INTUSE(name) name +#endif + +/* Name of the default domain used for gettext(3) prior any call to + textdomain(3). The default value for this is "messages". */ +const char _nl_default_default_domain[] attribute_hidden = "messages"; + +/* Value used as the default domain for gettext(3). */ +const char *_nl_current_default_domain attribute_hidden + = _nl_default_default_domain; + +/* Contains the default location of the message catalogs. */ +#if defined __EMX__ +extern const char _nl_default_dirname[]; +#else +const char _nl_default_dirname[] = LOCALEDIR; +INTVARDEF (_nl_default_dirname) +#endif + +/* List with bindings of specific domains created by bindtextdomain() + calls. */ +struct binding *_nl_domain_bindings; + +/* Prototypes for local functions. */ +static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain, + unsigned long int n, + const char *translation, + size_t translation_len)) + internal_function; +static const char *guess_category_value PARAMS ((int category, + const char *categoryname)) + internal_function; +#ifdef _LIBC +# include "../locale/localeinfo.h" +# define category_to_name(category) _nl_category_names[category] +#else +static const char *category_to_name PARAMS ((int category)) internal_function; +#endif + + +/* For those loosing systems which don't have `alloca' we have to add + some additional code emulating it. */ +#ifdef HAVE_ALLOCA +/* Nothing has to be done. */ +# define freea(p) /* nothing */ +# define ADD_BLOCK(list, address) /* nothing */ +# define FREE_BLOCKS(list) /* nothing */ +#else +struct block_list +{ + void *address; + struct block_list *next; +}; +# define ADD_BLOCK(list, addr) \ + do { \ + struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ + /* If we cannot get a free block we cannot add the new element to \ + the list. */ \ + if (newp != NULL) { \ + newp->address = (addr); \ + newp->next = (list); \ + (list) = newp; \ + } \ + } while (0) +# define FREE_BLOCKS(list) \ + do { \ + while (list != NULL) { \ + struct block_list *old = list; \ + list = list->next; \ + free (old->address); \ + free (old); \ + } \ + } while (0) +# undef alloca +# define alloca(size) (malloc (size)) +# define freea(p) free (p) +#endif /* have alloca */ + + +#ifdef _LIBC +/* List of blocks allocated for translations. */ +typedef struct transmem_list +{ + struct transmem_list *next; + char data[ZERO]; +} transmem_block_t; +static struct transmem_list *transmem_list; +#else +typedef unsigned char transmem_block_t; +#endif + + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define DCIGETTEXT __dcigettext +#else +# define DCIGETTEXT libintl_dcigettext +#endif + +/* Lock variable to protect the global data in the gettext implementation. */ +#ifdef _LIBC +__libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden) +#endif + +/* Checking whether the binaries runs SUID must be done and glibc provides + easier methods therefore we make a difference here. */ +#ifdef _LIBC +# define ENABLE_SECURE __libc_enable_secure +# define DETERMINE_SECURE +#else +# ifndef HAVE_GETUID +# define getuid() 0 +# endif +# ifndef HAVE_GETGID +# define getgid() 0 +# endif +# ifndef HAVE_GETEUID +# define geteuid() getuid() +# endif +# ifndef HAVE_GETEGID +# define getegid() getgid() +# endif +static int enable_secure; +# define ENABLE_SECURE (enable_secure == 1) +# define DETERMINE_SECURE \ + if (enable_secure == 0) \ + { \ + if (getuid () != geteuid () || getgid () != getegid ()) \ + enable_secure = 1; \ + else \ + enable_secure = -1; \ + } +#endif + +#ifndef HAVE_RAISE +# define raise(x) kill (getpid (), (x)) +#endif + +/* Get the function to evaluate the plural expression. */ +#include "eval-plural.h" + +/* Look up MSGID in the DOMAINNAME message catalog for the current + CATEGORY locale and, if PLURAL is nonzero, search over string + depending on the plural form determined by N. */ +char * +DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) + const char *domainname; + const char *msgid1; + const char *msgid2; + int plural; + unsigned long int n; + int category; +{ +#ifndef HAVE_ALLOCA + struct block_list *block_list = NULL; +#endif + struct loaded_l10nfile *domain; + struct binding *binding; + const char *categoryname; + const char *categoryvalue; + char *dirname, *xdomainname; + char *single_locale; + char *retval; + size_t retlen; + int saved_errno; +#if defined HAVE_TSEARCH || defined _LIBC + struct known_translation_t *search; + struct known_translation_t **foundp = NULL; + size_t msgid_len; +#endif + size_t domainname_len; + + /* If no real MSGID is given return NULL. */ + if (msgid1 == NULL) + return NULL; + +#ifdef _LIBC + if (category < 0 || category >= __LC_LAST || category == LC_ALL) + /* Bogus. */ + return (plural == 0 + ? (char *) msgid1 + /* Use the Germanic plural rule. */ + : n == 1 ? (char *) msgid1 : (char *) msgid2); +#endif + + __libc_rwlock_rdlock (_nl_state_lock); + + /* If DOMAINNAME is NULL, we are interested in the default domain. If + CATEGORY is not LC_MESSAGES this might not make much sense but the + definition left this undefined. */ + if (domainname == NULL) + domainname = _nl_current_default_domain; + + /* OS/2 specific: backward compatibility with older libintl versions */ +#ifdef LC_MESSAGES_COMPAT + if (category == LC_MESSAGES_COMPAT) + category = LC_MESSAGES; +#endif + +#if defined HAVE_TSEARCH || defined _LIBC + msgid_len = strlen (msgid1) + 1; + + /* Try to find the translation among those which we found at + some time. */ + search = (struct known_translation_t *) + alloca (offsetof (struct known_translation_t, msgid) + msgid_len); + memcpy (search->msgid, msgid1, msgid_len); + search->domainname = (char *) domainname; + search->category = category; + + foundp = (struct known_translation_t **) tfind (search, &root, transcmp); + freea (search); + if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) + { + /* Now deal with plural. */ + if (plural) + retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation, + (*foundp)->translation_length); + else + retval = (char *) (*foundp)->translation; + + __libc_rwlock_unlock (_nl_state_lock); + return retval; + } +#endif + + /* Preserve the `errno' value. */ + saved_errno = errno; + + /* See whether this is a SUID binary or not. */ + DETERMINE_SECURE; + + /* First find matching binding. */ + for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) + { + int compare = strcmp (domainname, binding->domainname); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It is not in the list. */ + binding = NULL; + break; + } + } + + if (binding == NULL) + dirname = (char *) INTUSE(_nl_default_dirname); + else if (IS_ABSOLUTE_PATH (binding->dirname)) + dirname = binding->dirname; + else + { + /* We have a relative path. Make it absolute now. */ + size_t dirname_len = strlen (binding->dirname) + 1; + size_t path_max; + char *ret; + + path_max = (unsigned int) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + for (;;) + { + dirname = (char *) alloca (path_max + dirname_len); + ADD_BLOCK (block_list, dirname); + + __set_errno (0); + ret = getcwd (dirname, path_max); + if (ret != NULL || errno != ERANGE) + break; + + path_max += path_max / 2; + path_max += PATH_INCR; + } + + if (ret == NULL) + /* We cannot get the current working directory. Don't signal an + error but simply return the default string. */ + goto return_untranslated; + + stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); + } + + /* Now determine the symbolic name of CATEGORY and its value. */ + categoryname = category_to_name (category); + categoryvalue = guess_category_value (category, categoryname); + + domainname_len = strlen (domainname); + xdomainname = (char *) alloca (strlen (categoryname) + + domainname_len + 5); + ADD_BLOCK (block_list, xdomainname); + + stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), + domainname, domainname_len), + ".mo"); + + /* Creating working area. */ + single_locale = (char *) alloca (strlen (categoryvalue) + 1); + ADD_BLOCK (block_list, single_locale); + + + /* Search for the given string. This is a loop because we perhaps + got an ordered list of languages to consider for the translation. */ + while (1) + { + /* Make CATEGORYVALUE point to the next element of the list. */ + while (categoryvalue[0] != '\0' && categoryvalue[0] == ':') + ++categoryvalue; + if (categoryvalue[0] == '\0') + { + /* The whole contents of CATEGORYVALUE has been searched but + no valid entry has been found. We solve this situation + by implicitly appending a "C" entry, i.e. no translation + will take place. */ + single_locale[0] = 'C'; + single_locale[1] = '\0'; + } + else + { + char *cp = single_locale; + while (categoryvalue[0] != '\0' && categoryvalue[0] != ':') + *cp++ = *categoryvalue++; + *cp = '\0'; + + /* When this is a SUID binary we must not allow accessing files + outside the dedicated directories. */ + if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale)) + /* Ingore this entry. */ + continue; + } + + /* If the current locale value is C (or POSIX) we don't load a + domain. Return the MSGID. */ + if (strcmp (single_locale, "C") == 0 + || strcmp (single_locale, "POSIX") == 0) + break; + + /* Find structure describing the message catalog matching the + DOMAINNAME and CATEGORY. */ + domain = _nl_find_domain (dirname, single_locale, xdomainname, binding); + + if (domain != NULL) + { + retval = _nl_find_msg (domain, binding, msgid1, &retlen); + + if (retval == NULL) + { + int cnt; + + for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) + { + retval = _nl_find_msg (domain->successor[cnt], binding, + msgid1, &retlen); + + if (retval != NULL) + { + domain = domain->successor[cnt]; + break; + } + } + } + + if (retval != NULL) + { + /* Found the translation of MSGID1 in domain DOMAIN: + starting at RETVAL, RETLEN bytes. */ + FREE_BLOCKS (block_list); +#if defined HAVE_TSEARCH || defined _LIBC + if (foundp == NULL) + { + /* Create a new entry and add it to the search tree. */ + struct known_translation_t *newp; + + newp = (struct known_translation_t *) + malloc (offsetof (struct known_translation_t, msgid) + + msgid_len + domainname_len + 1); + if (newp != NULL) + { + newp->domainname = + mempcpy (newp->msgid, msgid1, msgid_len); + memcpy (newp->domainname, domainname, domainname_len + 1); + newp->category = category; + newp->counter = _nl_msg_cat_cntr; + newp->domain = domain; + newp->translation = retval; + newp->translation_length = retlen; + + /* Insert the entry in the search tree. */ + foundp = (struct known_translation_t **) + tsearch (newp, &root, transcmp); + if (foundp == NULL + || __builtin_expect (*foundp != newp, 0)) + /* The insert failed. */ + free (newp); + } + } + else + { + /* We can update the existing entry. */ + (*foundp)->counter = _nl_msg_cat_cntr; + (*foundp)->domain = domain; + (*foundp)->translation = retval; + (*foundp)->translation_length = retlen; + } +#endif + __set_errno (saved_errno); + + /* Now deal with plural. */ + if (plural) + retval = plural_lookup (domain, n, retval, retlen); + + __libc_rwlock_unlock (_nl_state_lock); + return retval; + } + } + } + + return_untranslated: + /* Return the untranslated MSGID. */ + FREE_BLOCKS (block_list); + __libc_rwlock_unlock (_nl_state_lock); +#ifndef _LIBC + if (!ENABLE_SECURE) + { + extern void _nl_log_untranslated PARAMS ((const char *logfilename, + const char *domainname, + const char *msgid1, + const char *msgid2, + int plural)); + const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED"); + + if (logfilename != NULL && logfilename[0] != '\0') + _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural); + } +#endif + __set_errno (saved_errno); + return (plural == 0 + ? (char *) msgid1 + /* Use the Germanic plural rule. */ + : n == 1 ? (char *) msgid1 : (char *) msgid2); +} + + +char * +internal_function +_nl_find_msg (domain_file, domainbinding, msgid, lengthp) + struct loaded_l10nfile *domain_file; + struct binding *domainbinding; + const char *msgid; + size_t *lengthp; +{ + struct loaded_domain *domain; + nls_uint32 nstrings; + size_t act; + char *result; + size_t resultlen; + + if (domain_file->decided == 0) + _nl_load_domain (domain_file, domainbinding); + + if (domain_file->data == NULL) + return NULL; + + domain = (struct loaded_domain *) domain_file->data; + + nstrings = domain->nstrings; + + /* Locate the MSGID and its translation. */ + if (domain->hash_tab != NULL) + { + /* Use the hashing table. */ + nls_uint32 len = strlen (msgid); + nls_uint32 hash_val = hash_string (msgid); + nls_uint32 idx = hash_val % domain->hash_size; + nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); + + while (1) + { + nls_uint32 nstr = + W (domain->must_swap_hash_tab, domain->hash_tab[idx]); + + if (nstr == 0) + /* Hash table entry is empty. */ + return NULL; + + nstr--; + + /* Compare msgid with the original string at index nstr. + We compare the lengths with >=, not ==, because plural entries + are represented by strings with an embedded NUL. */ + if (nstr < nstrings + ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len + && (strcmp (msgid, + domain->data + W (domain->must_swap, + domain->orig_tab[nstr].offset)) + == 0) + : domain->orig_sysdep_tab[nstr - nstrings].length > len + && (strcmp (msgid, + domain->orig_sysdep_tab[nstr - nstrings].pointer) + == 0)) + { + act = nstr; + goto found; + } + + if (idx >= domain->hash_size - incr) + idx -= domain->hash_size - incr; + else + idx += incr; + } + /* NOTREACHED */ + } + else + { + /* Try the default method: binary search in the sorted array of + messages. */ + size_t top, bottom; + + bottom = 0; + top = nstrings; + while (bottom < top) + { + int cmp_val; + + act = (bottom + top) / 2; + cmp_val = strcmp (msgid, (domain->data + + W (domain->must_swap, + domain->orig_tab[act].offset))); + if (cmp_val < 0) + top = act; + else if (cmp_val > 0) + bottom = act + 1; + else + goto found; + } + /* No translation was found. */ + return NULL; + } + + found: + /* The translation was found at index ACT. If we have to convert the + string to use a different character set, this is the time. */ + if (act < nstrings) + { + result = (char *) + (domain->data + W (domain->must_swap, domain->trans_tab[act].offset)); + resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1; + } + else + { + result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer; + resultlen = domain->trans_sysdep_tab[act - nstrings].length; + } + +#if defined _LIBC || HAVE_ICONV + if (domain->codeset_cntr + != (domainbinding != NULL ? domainbinding->codeset_cntr : 0)) + { + /* The domain's codeset has changed through bind_textdomain_codeset() + since the message catalog was initialized or last accessed. We + have to reinitialize the converter. */ + _nl_free_domain_conv (domain); + _nl_init_domain_conv (domain_file, domain, domainbinding); + } + + if ( +# ifdef _LIBC + domain->conv != (__gconv_t) -1 +# else +# if HAVE_ICONV + domain->conv != (iconv_t) -1 +# endif +# endif + ) + { + /* We are supposed to do a conversion. First allocate an + appropriate table with the same structure as the table + of translations in the file, where we can put the pointers + to the converted strings in. + There is a slight complication with plural entries. They + are represented by consecutive NUL terminated strings. We + handle this case by converting RESULTLEN bytes, including + NULs. */ + + if (domain->conv_tab == NULL + && ((domain->conv_tab = + (char **) calloc (nstrings + domain->n_sysdep_strings, + sizeof (char *))) + == NULL)) + /* Mark that we didn't succeed allocating a table. */ + domain->conv_tab = (char **) -1; + + if (__builtin_expect (domain->conv_tab == (char **) -1, 0)) + /* Nothing we can do, no more memory. */ + goto converted; + + if (domain->conv_tab[act] == NULL) + { + /* We haven't used this string so far, so it is not + translated yet. Do this now. */ + /* We use a bit more efficient memory handling. + We allocate always larger blocks which get used over + time. This is faster than many small allocations. */ + __libc_lock_define_initialized (static, lock) +# define INITIAL_BLOCK_SIZE 4080 + static unsigned char *freemem; + static size_t freemem_size; + + const unsigned char *inbuf; + unsigned char *outbuf; + int malloc_count; +# ifndef _LIBC + transmem_block_t *transmem_list = NULL; +# endif + + __libc_lock_lock (lock); + + inbuf = (const unsigned char *) result; + outbuf = freemem + sizeof (size_t); + + malloc_count = 0; + while (1) + { + transmem_block_t *newmem; +# ifdef _LIBC + size_t non_reversible; + int res; + + if (freemem_size < sizeof (size_t)) + goto resize_freemem; + + res = __gconv (domain->conv, + &inbuf, inbuf + resultlen, + &outbuf, + outbuf + freemem_size - sizeof (size_t), + &non_reversible); + + if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT) + break; + + if (res != __GCONV_FULL_OUTPUT) + { + __libc_lock_unlock (lock); + goto converted; + } + + inbuf = result; +# else +# if HAVE_ICONV + const char *inptr = (const char *) inbuf; + size_t inleft = resultlen; + char *outptr = (char *) outbuf; + size_t outleft; + + if (freemem_size < sizeof (size_t)) + goto resize_freemem; + + outleft = freemem_size - sizeof (size_t); + if (iconv (domain->conv, + (ICONV_CONST char **) &inptr, &inleft, + &outptr, &outleft) + != (size_t) (-1)) + { + outbuf = (unsigned char *) outptr; + break; + } + if (errno != E2BIG) + { + __libc_lock_unlock (lock); + goto converted; + } +# endif +# endif + + resize_freemem: + /* We must allocate a new buffer or resize the old one. */ + if (malloc_count > 0) + { + ++malloc_count; + freemem_size = malloc_count * INITIAL_BLOCK_SIZE; + newmem = (transmem_block_t *) realloc (transmem_list, + freemem_size); +# ifdef _LIBC + if (newmem != NULL) + transmem_list = transmem_list->next; + else + { + struct transmem_list *old = transmem_list; + + transmem_list = transmem_list->next; + free (old); + } +# endif + } + else + { + malloc_count = 1; + freemem_size = INITIAL_BLOCK_SIZE; + newmem = (transmem_block_t *) malloc (freemem_size); + } + if (__builtin_expect (newmem == NULL, 0)) + { + freemem = NULL; + freemem_size = 0; + __libc_lock_unlock (lock); + goto converted; + } + +# ifdef _LIBC + /* Add the block to the list of blocks we have to free + at some point. */ + newmem->next = transmem_list; + transmem_list = newmem; + + freemem = newmem->data; + freemem_size -= offsetof (struct transmem_list, data); +# else + transmem_list = newmem; + freemem = newmem; +# endif + + outbuf = freemem + sizeof (size_t); + } + + /* We have now in our buffer a converted string. Put this + into the table of conversions. */ + *(size_t *) freemem = outbuf - freemem - sizeof (size_t); + domain->conv_tab[act] = (char *) freemem; + /* Shrink freemem, but keep it aligned. */ + freemem_size -= outbuf - freemem; + freemem = outbuf; + freemem += freemem_size & (alignof (size_t) - 1); + freemem_size = freemem_size & ~ (alignof (size_t) - 1); + + __libc_lock_unlock (lock); + } + + /* Now domain->conv_tab[act] contains the translation of all + the plural variants. */ + result = domain->conv_tab[act] + sizeof (size_t); + resultlen = *(size_t *) domain->conv_tab[act]; + } + + converted: + /* The result string is converted. */ + +#endif /* _LIBC || HAVE_ICONV */ + + *lengthp = resultlen; + return result; +} + + +/* Look up a plural variant. */ +static char * +internal_function +plural_lookup (domain, n, translation, translation_len) + struct loaded_l10nfile *domain; + unsigned long int n; + const char *translation; + size_t translation_len; +{ + struct loaded_domain *domaindata = (struct loaded_domain *) domain->data; + unsigned long int index; + const char *p; + + index = plural_eval (domaindata->plural, n); + if (index >= domaindata->nplurals) + /* This should never happen. It means the plural expression and the + given maximum value do not match. */ + index = 0; + + /* Skip INDEX strings at TRANSLATION. */ + p = translation; + while (index-- > 0) + { +#ifdef _LIBC + p = __rawmemchr (p, '\0'); +#else + p = strchr (p, '\0'); +#endif + /* And skip over the NUL byte. */ + p++; + + if (p >= translation + translation_len) + /* This should never happen. It means the plural expression + evaluated to a value larger than the number of variants + available for MSGID1. */ + return (char *) translation; + } + return (char *) p; +} + +#ifndef _LIBC +/* Return string representation of locale CATEGORY. */ +static const char * +internal_function +category_to_name (category) + int category; +{ + const char *retval; + + switch (category) + { +#ifdef LC_COLLATE + case LC_COLLATE: + retval = "LC_COLLATE"; + break; +#endif +#ifdef LC_CTYPE + case LC_CTYPE: + retval = "LC_CTYPE"; + break; +#endif +#ifdef LC_MONETARY + case LC_MONETARY: + retval = "LC_MONETARY"; + break; +#endif +#ifdef LC_NUMERIC + case LC_NUMERIC: + retval = "LC_NUMERIC"; + break; +#endif +#ifdef LC_TIME + case LC_TIME: + retval = "LC_TIME"; + break; +#endif +#ifdef LC_MESSAGES + case LC_MESSAGES: + retval = "LC_MESSAGES"; + break; +#endif +#ifdef LC_RESPONSE + case LC_RESPONSE: + retval = "LC_RESPONSE"; + break; +#endif +#ifdef LC_ALL + case LC_ALL: + /* This might not make sense but is perhaps better than any other + value. */ + retval = "LC_ALL"; + break; +#endif + default: + /* If you have a better idea for a default value let me know. */ + retval = "LC_XXX"; + } + + return retval; +} +#endif + +/* Guess value of current locale from value of the environment variables. */ +static const char * +internal_function +guess_category_value (category, categoryname) + int category; + const char *categoryname; +{ + const char *language; + const char *retval; + + /* The highest priority value is the `LANGUAGE' environment + variable. But we don't use the value if the currently selected + locale is the C locale. This is a GNU extension. */ + language = getenv ("LANGUAGE"); + if (language != NULL && language[0] == '\0') + language = NULL; + + /* We have to proceed with the POSIX methods of looking to `LC_ALL', + `LC_xxx', and `LANG'. On some systems this can be done by the + `setlocale' function itself. */ +#ifdef _LIBC + retval = __current_locale_name (category); +#else + retval = _nl_locale_name (category, categoryname); +#endif + + /* Ignore LANGUAGE if the locale is set to "C" because + 1. "C" locale usually uses the ASCII encoding, and most international + messages use non-ASCII characters. These characters get displayed + as question marks (if using glibc's iconv()) or as invalid 8-bit + characters (because other iconv()s refuse to convert most non-ASCII + characters to ASCII). In any case, the output is ugly. + 2. The precise output of some programs in the "C" locale is specified + by POSIX and should not depend on environment variables like + "LANGUAGE". We allow such programs to use gettext(). */ + return language != NULL && strcmp (retval, "C") != 0 ? language : retval; +} + +/* @@ begin of epilog @@ */ + +/* We don't want libintl.a to depend on any other library. So we + avoid the non-standard function stpcpy. In GNU C Library this + function is available, though. Also allow the symbol HAVE_STPCPY + to be defined. */ +#if !_LIBC && !HAVE_STPCPY +static char * +stpcpy (dest, src) + char *dest; + const char *src; +{ + while ((*dest++ = *src++) != '\0') + /* Do nothing. */ ; + return dest - 1; +} +#endif + +#if !_LIBC && !HAVE_MEMPCPY +static void * +mempcpy (dest, src, n) + void *dest; + const void *src; + size_t n; +{ + return (void *) ((char *) memcpy (dest, src, n) + n); +} +#endif + + +#ifdef _LIBC +/* If we want to free all resources we have to do some work at + program's end. */ +libc_freeres_fn (free_mem) +{ + void *old; + + while (_nl_domain_bindings != NULL) + { + struct binding *oldp = _nl_domain_bindings; + _nl_domain_bindings = _nl_domain_bindings->next; + if (oldp->dirname != INTUSE(_nl_default_dirname)) + /* Yes, this is a pointer comparison. */ + free (oldp->dirname); + free (oldp->codeset); + free (oldp); + } + + if (_nl_current_default_domain != _nl_default_default_domain) + /* Yes, again a pointer comparison. */ + free ((char *) _nl_current_default_domain); + + /* Remove the search tree with the known translations. */ + __tdestroy (root, free); + root = NULL; + + while (transmem_list != NULL) + { + old = transmem_list; + transmem_list = transmem_list->next; + free (old); + } +} +#endif diff --git a/lib/intl/dcngettext.c b/lib/intl/dcngettext.c new file mode 100644 index 0000000..3d70b18 --- /dev/null +++ b/lib/intl/dcngettext.c @@ -0,0 +1,62 @@ +/* dcngettext.c - Implementation of the dcngettext(3) function. */ + +/* Copyright (C) 1995-1999, 2000, 2001, 2002, 2006-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define DCNGETTEXT __dcngettext +# define DCIGETTEXT __dcigettext +#else +# define DCNGETTEXT libintl_dcngettext +# define DCIGETTEXT libintl_dcigettext +#endif + +/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY + locale. */ +char * +DCNGETTEXT (domainname, msgid1, msgid2, n, category) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; + int category; +{ + return DCIGETTEXT (domainname, msgid1, msgid2, 1, n, category); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__dcngettext, dcngettext); +#endif diff --git a/lib/intl/dgettext.c b/lib/intl/dgettext.c new file mode 100644 index 0000000..5351631 --- /dev/null +++ b/lib/intl/dgettext.c @@ -0,0 +1,61 @@ +/* dgettext.c - Implementation of the dgettext(3) function. */ + +/* Copyright (C) 1995-1997, 2000, 2001, 2002, 2006-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <locale.h> + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define DGETTEXT __dgettext +# define DCGETTEXT INTUSE(__dcgettext) +#else +# define DGETTEXT libintl_dgettext +# define DCGETTEXT libintl_dcgettext +#endif + +/* Look up MSGID in the DOMAINNAME message catalog of the current + LC_MESSAGES locale. */ +char * +DGETTEXT (domainname, msgid) + const char *domainname; + const char *msgid; +{ + return DCGETTEXT (domainname, msgid, LC_MESSAGES); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__dgettext, dgettext); +#endif diff --git a/lib/intl/dngettext.c b/lib/intl/dngettext.c new file mode 100644 index 0000000..4276f3b --- /dev/null +++ b/lib/intl/dngettext.c @@ -0,0 +1,63 @@ +/* dngettext.c - Implementation of the dngettext(3) function. */ + +/* Copyright (C) 1995-1997, 2000, 2001, 2002, 2005, 2006, 2008,2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <locale.h> + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define DNGETTEXT __dngettext +# define DCNGETTEXT __dcngettext +#else +# define DNGETTEXT libintl_dngettext +# define DCNGETTEXT libintl_dcngettext +#endif + +/* Look up MSGID in the DOMAINNAME message catalog of the current + LC_MESSAGES locale and skip message according to the plural form. */ +char * +DNGETTEXT (domainname, msgid1, msgid2, n) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return DCNGETTEXT (domainname, msgid1, msgid2, n, LC_MESSAGES); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__dngettext, dngettext); +#endif diff --git a/lib/intl/eval-plural.h b/lib/intl/eval-plural.h new file mode 100644 index 0000000..3441c74 --- /dev/null +++ b/lib/intl/eval-plural.h @@ -0,0 +1,116 @@ +/* eval-plural.c - Plural expression evaluation. */ + +/* Copyright (C) 2000-2002, 2006-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef STATIC +#define STATIC static +#endif + +/* Evaluate the plural expression and return an index value. */ +STATIC unsigned long int plural_eval PARAMS ((struct expression *pexp, + unsigned long int n)) + internal_function; + +STATIC +unsigned long int +internal_function +plural_eval (pexp, n) + struct expression *pexp; + unsigned long int n; +{ + switch (pexp->nargs) + { + case 0: + switch (pexp->operation) + { + case var: + return n; + case num: + return pexp->val.num; + default: + break; + } + /* NOTREACHED */ + break; + case 1: + { + /* pexp->operation must be lnot. */ + unsigned long int arg = plural_eval (pexp->val.args[0], n); + return ! arg; + } + case 2: + { + unsigned long int leftarg = plural_eval (pexp->val.args[0], n); + if (pexp->operation == lor) + return leftarg || plural_eval (pexp->val.args[1], n); + else if (pexp->operation == land) + return leftarg && plural_eval (pexp->val.args[1], n); + else + { + unsigned long int rightarg = plural_eval (pexp->val.args[1], n); + + switch (pexp->operation) + { + case mult: + return leftarg * rightarg; + case divide: +#if !INTDIV0_RAISES_SIGFPE + if (rightarg == 0) + raise (SIGFPE); +#endif + return leftarg / rightarg; + case module: +#if !INTDIV0_RAISES_SIGFPE + if (rightarg == 0) + raise (SIGFPE); +#endif + return leftarg % rightarg; + case plus: + return leftarg + rightarg; + case minus: + return leftarg - rightarg; + case less_than: + return leftarg < rightarg; + case greater_than: + return leftarg > rightarg; + case less_or_equal: + return leftarg <= rightarg; + case greater_or_equal: + return leftarg >= rightarg; + case equal: + return leftarg == rightarg; + case not_equal: + return leftarg != rightarg; + default: + break; + } + } + /* NOTREACHED */ + break; + } + case 3: + { + /* pexp->operation must be qmop. */ + unsigned long int boolarg = plural_eval (pexp->val.args[0], n); + return plural_eval (pexp->val.args[boolarg ? 1 : 2], n); + } + } + /* NOTREACHED */ + return 0; +} diff --git a/lib/intl/explodename.c b/lib/intl/explodename.c new file mode 100644 index 0000000..adc3640 --- /dev/null +++ b/lib/intl/explodename.c @@ -0,0 +1,195 @@ +/* explodename.c */ + +/* Copyright (C) 1995-1998, 2000, 2001, 2005-2009 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "loadinfo.h" + +/* On some strange systems still no definition of NULL is found. Sigh! */ +#ifndef NULL +# if defined __STDC__ && __STDC__ +# define NULL ((void *) 0) +# else +# define NULL 0 +# endif +#endif + +/* @@ end of prolog @@ */ + +char * +_nl_find_language (name) + const char *name; +{ + while (name[0] != '\0' && name[0] != '_' && name[0] != '@' + && name[0] != '+' && name[0] != ',') + ++name; + + return (char *) name; +} + + +int +_nl_explode_name (name, language, modifier, territory, codeset, + normalized_codeset, special, sponsor, revision) + char *name; + const char **language; + const char **modifier; + const char **territory; + const char **codeset; + const char **normalized_codeset; + const char **special; + const char **sponsor; + const char **revision; +{ + enum { undecided, xpg, cen } syntax; + char *cp; + int mask; + + *modifier = NULL; + *territory = NULL; + *codeset = NULL; + *normalized_codeset = NULL; + *special = NULL; + *sponsor = NULL; + *revision = NULL; + + /* Now we determine the single parts of the locale name. First + look for the language. Termination symbols are `_' and `@' if + we use XPG4 style, and `_', `+', and `,' if we use CEN syntax. */ + mask = 0; + syntax = undecided; + *language = cp = name; + cp = _nl_find_language (*language); + + if (*language == cp) + /* This does not make sense: language has to be specified. Use + this entry as it is without exploding. Perhaps it is an alias. */ + cp = strchr (*language, '\0'); + else if (cp[0] == '_') + { + /* Next is the territory. */ + cp[0] = '\0'; + *territory = ++cp; + + while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@' + && cp[0] != '+' && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= TERRITORY; + + if (cp[0] == '.') + { + /* Next is the codeset. */ + syntax = xpg; + cp[0] = '\0'; + *codeset = ++cp; + + while (cp[0] != '\0' && cp[0] != '@') + ++cp; + + mask |= XPG_CODESET; + + if (*codeset != cp && (*codeset)[0] != '\0') + { + *normalized_codeset = _nl_normalize_codeset (*codeset, + cp - *codeset); + if (strcmp (*codeset, *normalized_codeset) == 0) + free ((char *) *normalized_codeset); + else + mask |= XPG_NORM_CODESET; + } + } + } + + if (cp[0] == '@' || (syntax != xpg && cp[0] == '+')) + { + /* Next is the modifier. */ + syntax = cp[0] == '@' ? xpg : cen; + cp[0] = '\0'; + *modifier = ++cp; + + while (syntax == cen && cp[0] != '\0' && cp[0] != '+' + && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= XPG_MODIFIER | CEN_AUDIENCE; + } + + if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_')) + { + syntax = cen; + + if (cp[0] == '+') + { + /* Next is special application (CEN syntax). */ + cp[0] = '\0'; + *special = ++cp; + + while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= CEN_SPECIAL; + } + + if (cp[0] == ',') + { + /* Next is sponsor (CEN syntax). */ + cp[0] = '\0'; + *sponsor = ++cp; + + while (cp[0] != '\0' && cp[0] != '_') + ++cp; + + mask |= CEN_SPONSOR; + } + + if (cp[0] == '_') + { + /* Next is revision (CEN syntax). */ + cp[0] = '\0'; + *revision = ++cp; + + mask |= CEN_REVISION; + } + } + + /* For CEN syntax values it might be important to have the + separator character in the file name, not for XPG syntax. */ + if (syntax == xpg) + { + if (*territory != NULL && (*territory)[0] == '\0') + mask &= ~TERRITORY; + + if (*codeset != NULL && (*codeset)[0] == '\0') + mask &= ~XPG_CODESET; + + if (*modifier != NULL && (*modifier)[0] == '\0') + mask &= ~XPG_MODIFIER; + } + + return mask; +} diff --git a/lib/intl/finddomain.c b/lib/intl/finddomain.c new file mode 100644 index 0000000..69a3586 --- /dev/null +++ b/lib/intl/finddomain.c @@ -0,0 +1,197 @@ +/* finddomain.c - Handle list of needed message catalogs */ + +/* Copyright (C) 1995-1999, 2000, 2001, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@gnu.org>, 1995. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#if defined HAVE_UNISTD_H || defined _LIBC +# include <unistd.h> +#endif + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ +/* List of already loaded domains. */ +static struct loaded_l10nfile *_nl_loaded_domains; + + +/* Return a data structure describing the message catalog described by + the DOMAINNAME and CATEGORY parameters with respect to the currently + established bindings. */ +struct loaded_l10nfile * +internal_function +_nl_find_domain (dirname, locale, domainname, domainbinding) + const char *dirname; + char *locale; + const char *domainname; + struct binding *domainbinding; +{ + struct loaded_l10nfile *retval; + const char *language; + const char *modifier; + const char *territory; + const char *codeset; + const char *normalized_codeset; + const char *special; + const char *sponsor; + const char *revision; + const char *alias_value; + int mask; + + /* LOCALE can consist of up to four recognized parts for the XPG syntax: + + language[_territory[.codeset]][@modifier] + + and six parts for the CEN syntax: + + language[_territory][+audience][+special][,[sponsor][_revision]] + + Beside the first part all of them are allowed to be missing. If + the full specified locale is not found, the less specific one are + looked for. The various parts will be stripped off according to + the following order: + (1) revision + (2) sponsor + (3) special + (4) codeset + (5) normalized codeset + (6) territory + (7) audience/modifier + */ + + /* If we have already tested for this locale entry there has to + be one data set in the list of loaded domains. */ + retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname, + strlen (dirname) + 1, 0, locale, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, domainname, 0); + if (retval != NULL) + { + /* We know something about this locale. */ + int cnt; + + if (retval->decided == 0) + _nl_load_domain (retval, domainbinding); + + if (retval->data != NULL) + return retval; + + for (cnt = 0; retval->successor[cnt] != NULL; ++cnt) + { + if (retval->successor[cnt]->decided == 0) + _nl_load_domain (retval->successor[cnt], domainbinding); + + if (retval->successor[cnt]->data != NULL) + break; + } + return cnt >= 0 ? retval : NULL; + /* NOTREACHED */ + } + + /* See whether the locale value is an alias. If yes its value + *overwrites* the alias name. No test for the original value is + done. */ + alias_value = _nl_expand_alias (locale); + if (alias_value != NULL) + { +#if defined _LIBC || defined HAVE_STRDUP + locale = strdup (alias_value); + if (locale == NULL) + return NULL; +#else + size_t len = strlen (alias_value) + 1; + locale = (char *) malloc (len); + if (locale == NULL) + return NULL; + + memcpy (locale, alias_value, len); +#endif + } + + /* Now we determine the single parts of the locale name. First + look for the language. Termination symbols are `_' and `@' if + we use XPG4 style, and `_', `+', and `,' if we use CEN syntax. */ + mask = _nl_explode_name (locale, &language, &modifier, &territory, + &codeset, &normalized_codeset, &special, + &sponsor, &revision); + + /* Create all possible locale entries which might be interested in + generalization. */ + retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname, + strlen (dirname) + 1, mask, language, territory, + codeset, normalized_codeset, modifier, special, + sponsor, revision, domainname, 1); + if (retval == NULL) + /* This means we are out of core. */ + return NULL; + + if (retval->decided == 0) + _nl_load_domain (retval, domainbinding); + if (retval->data == NULL) + { + int cnt; + for (cnt = 0; retval->successor[cnt] != NULL; ++cnt) + { + if (retval->successor[cnt]->decided == 0) + _nl_load_domain (retval->successor[cnt], domainbinding); + if (retval->successor[cnt]->data != NULL) + break; + } + } + + /* The room for an alias was dynamically allocated. Free it now. */ + if (alias_value != NULL) + free (locale); + + /* The space for normalized_codeset is dynamically allocated. Free it. */ + if (mask & XPG_NORM_CODESET) + free ((void *) normalized_codeset); + + return retval; +} + + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + struct loaded_l10nfile *runp = _nl_loaded_domains; + + while (runp != NULL) + { + struct loaded_l10nfile *here = runp; + if (runp->data != NULL) + _nl_unload_domain ((struct loaded_domain *) runp->data); + runp = runp->next; + free ((char *) here->filename); + free (here); + } +} +#endif diff --git a/lib/intl/gettext.c b/lib/intl/gettext.c new file mode 100644 index 0000000..fd3fa0f --- /dev/null +++ b/lib/intl/gettext.c @@ -0,0 +1,66 @@ +/* gettext.c - Implementation of gettext(3) function. */ + +/* Copyright (C) 1995, 1997, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _LIBC +# define __need_NULL +# include <stddef.h> +#else +# include <stdlib.h> /* Just for NULL. */ +#endif + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define GETTEXT __gettext +# define DCGETTEXT INTUSE(__dcgettext) +#else +# define GETTEXT libintl_gettext +# define DCGETTEXT libintl_dcgettext +#endif + +/* Look up MSGID in the current default message catalog for the current + LC_MESSAGES locale. If not found, returns MSGID itself (the default + text). */ +char * +GETTEXT (msgid) + const char *msgid; +{ + return DCGETTEXT (NULL, msgid, LC_MESSAGES); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__gettext, gettext); +#endif diff --git a/lib/intl/gettextP.h b/lib/intl/gettextP.h new file mode 100644 index 0000000..5c137e5 --- /dev/null +++ b/lib/intl/gettextP.h @@ -0,0 +1,226 @@ +/* gettextP.h - Header describing internals of libintl library. */ + +/* Copyright (C) 1995-1999, 2000-2003, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@cygnus.com>, 1995. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _GETTEXTP_H +#define _GETTEXTP_H + +#include <stddef.h> /* Get size_t. */ + +#ifdef _LIBC +# include "../iconv/gconv_int.h" +#else +# if HAVE_ICONV +# include <iconv.h> +# endif +#endif + +#include "loadinfo.h" + +#include "gmo.h" /* Get nls_uint32. */ + +/* @@ end of prolog @@ */ + +#ifndef PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif +#endif + +#ifndef internal_function +# define internal_function +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +/* Tell the compiler when a conditional or integer expression is + almost always true or almost always false. */ +#ifndef HAVE_BUILTIN_EXPECT +# define __builtin_expect(expr, val) (expr) +#endif + +#ifndef W +# define W(flag, data) ((flag) ? SWAP (data) : (data)) +#endif + + +#ifdef _LIBC +# include <byteswap.h> +# define SWAP(i) bswap_32 (i) +#else +static inline nls_uint32 +SWAP (i) + nls_uint32 i; +{ + return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); +} +#endif + + +/* In-memory representation of system dependent string. */ +struct sysdep_string_desc +{ + /* Length of addressed string, including the trailing NUL. */ + size_t length; + /* Pointer to addressed string. */ + const char *pointer; +}; + +/* The representation of an opened message catalog. */ +struct loaded_domain +{ + /* Pointer to memory containing the .mo file. */ + const char *data; + /* 1 if the memory is mmap()ed, 0 if the memory is malloc()ed. */ + int use_mmap; + /* Size of mmap()ed memory. */ + size_t mmap_size; + /* 1 if the .mo file uses a different endianness than this machine. */ + int must_swap; + /* Pointer to additional malloc()ed memory. */ + void *malloced; + + /* Number of static strings pairs. */ + nls_uint32 nstrings; + /* Pointer to descriptors of original strings in the file. */ + const struct string_desc *orig_tab; + /* Pointer to descriptors of translated strings in the file. */ + const struct string_desc *trans_tab; + + /* Number of system dependent strings pairs. */ + nls_uint32 n_sysdep_strings; + /* Pointer to descriptors of original sysdep strings. */ + const struct sysdep_string_desc *orig_sysdep_tab; + /* Pointer to descriptors of translated sysdep strings. */ + const struct sysdep_string_desc *trans_sysdep_tab; + + /* Size of hash table. */ + nls_uint32 hash_size; + /* Pointer to hash table. */ + const nls_uint32 *hash_tab; + /* 1 if the hash table uses a different endianness than this machine. */ + int must_swap_hash_tab; + + int codeset_cntr; +#ifdef _LIBC + __gconv_t conv; +#else +# if HAVE_ICONV + iconv_t conv; +# endif +#endif + char **conv_tab; + + struct expression *plural; + unsigned long int nplurals; +}; + +/* We want to allocate a string at the end of the struct. But ISO C + doesn't allow zero sized arrays. */ +#ifdef __GNUC__ +# define ZERO 0 +#else +# define ZERO 1 +#endif + +/* A set of settings bound to a message domain. Used to store settings + from bindtextdomain() and bind_textdomain_codeset(). */ +struct binding +{ + struct binding *next; + char *dirname; + int codeset_cntr; /* Incremented each time codeset changes. */ + char *codeset; + char domainname[ZERO]; +}; + +/* A counter which is incremented each time some previous translations + become invalid. + This variable is part of the external ABI of the GNU libintl. */ +extern int _nl_msg_cat_cntr; + +#ifndef _LIBC +const char *_nl_locale_name PARAMS ((int category, const char *categoryname)); +#endif + +struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname, + char *__locale, + const char *__domainname, + struct binding *__domainbinding)) + internal_function; +void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain, + struct binding *__domainbinding)) + internal_function; +void _nl_unload_domain PARAMS ((struct loaded_domain *__domain)) + internal_function; +const char *_nl_init_domain_conv PARAMS ((struct loaded_l10nfile *__domain_file, + struct loaded_domain *__domain, + struct binding *__domainbinding)) + internal_function; +void _nl_free_domain_conv PARAMS ((struct loaded_domain *__domain)) + internal_function; + +char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file, + struct binding *domainbinding, + const char *msgid, size_t *lengthp)) + internal_function; + +#ifdef _LIBC +extern char *__gettext PARAMS ((const char *__msgid)); +extern char *__dgettext PARAMS ((const char *__domainname, + const char *__msgid)); +extern char *__dcgettext PARAMS ((const char *__domainname, + const char *__msgid, int __category)); +extern char *__ngettext PARAMS ((const char *__msgid1, const char *__msgid2, + unsigned long int __n)); +extern char *__dngettext PARAMS ((const char *__domainname, + const char *__msgid1, const char *__msgid2, + unsigned long int n)); +extern char *__dcngettext PARAMS ((const char *__domainname, + const char *__msgid1, const char *__msgid2, + unsigned long int __n, int __category)); +extern char *__dcigettext PARAMS ((const char *__domainname, + const char *__msgid1, const char *__msgid2, + int __plural, unsigned long int __n, + int __category)); +extern char *__textdomain PARAMS ((const char *__domainname)); +extern char *__bindtextdomain PARAMS ((const char *__domainname, + const char *__dirname)); +extern char *__bind_textdomain_codeset PARAMS ((const char *__domainname, + const char *__codeset)); +#else +/* Declare the exported libintl_* functions, in a way that allows us to + call them under their real name. */ +# define _INTL_REDIRECT_MACROS +# include "libgnuintl.h" +extern char *libintl_dcigettext PARAMS ((const char *__domainname, + const char *__msgid1, + const char *__msgid2, + int __plural, unsigned long int __n, + int __category)); +#endif + +/* @@ begin of epilog @@ */ + +#endif /* gettextP.h */ diff --git a/lib/intl/gmo.h b/lib/intl/gmo.h new file mode 100644 index 0000000..2c57851 --- /dev/null +++ b/lib/intl/gmo.h @@ -0,0 +1,150 @@ +/* gmo.h - Description of GNU message catalog format: general file layout. */ + +/* Copyright (C) 1995, 1997, 2000-2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _GETTEXT_H +#define _GETTEXT_H 1 + +#include <limits.h> + +/* @@ end of prolog @@ */ + +/* The magic number of the GNU message catalog format. */ +#define _MAGIC 0x950412de +#define _MAGIC_SWAPPED 0xde120495 + +/* Revision number of the currently used .mo (binary) file format. */ +#define MO_REVISION_NUMBER 0 + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + as of version autoconf-2.13, the AC_CHECK_SIZEOF macro doesn't work + when cross-compiling. */ + +#if __STDC__ +# define UINT_MAX_32_BITS 4294967295U +#else +# define UINT_MAX_32_BITS 0xFFFFFFFF +#endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have <limits.h>) have 64+-bit integral types. */ + +#ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +#endif + +#if UINT_MAX == UINT_MAX_32_BITS +typedef unsigned nls_uint32; +#else +# if USHRT_MAX == UINT_MAX_32_BITS +typedef unsigned short nls_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS +typedef unsigned long nls_uint32; +# else + /* The following line is intended to throw an error. Using #error is + not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +#endif + + +/* Header for binary .mo file format. */ +struct mo_file_header +{ + /* The magic number. */ + nls_uint32 magic; + /* The revision number of the file format. */ + nls_uint32 revision; + + /* The following are only used in .mo files with major revision 0. */ + + /* The number of strings pairs. */ + nls_uint32 nstrings; + /* Offset of table with start offsets of original strings. */ + nls_uint32 orig_tab_offset; + /* Offset of table with start offsets of translated strings. */ + nls_uint32 trans_tab_offset; + /* Size of hash table. */ + nls_uint32 hash_tab_size; + /* Offset of first hash table entry. */ + nls_uint32 hash_tab_offset; + + /* The following are only used in .mo files with minor revision >= 1. */ + + /* The number of system dependent segments. */ + nls_uint32 n_sysdep_segments; + /* Offset of table describing system dependent segments. */ + nls_uint32 sysdep_segments_offset; + /* The number of system dependent strings pairs. */ + nls_uint32 n_sysdep_strings; + /* Offset of table with start offsets of original sysdep strings. */ + nls_uint32 orig_sysdep_tab_offset; + /* Offset of table with start offsets of translated sysdep strings. */ + nls_uint32 trans_sysdep_tab_offset; +}; + +/* Descriptor for static string contained in the binary .mo file. */ +struct string_desc +{ + /* Length of addressed string, not including the trailing NUL. */ + nls_uint32 length; + /* Offset of string in file. */ + nls_uint32 offset; +}; + +/* The following are only used in .mo files with minor revision >= 1. */ + +/* Descriptor for system dependent string segment. */ +struct sysdep_segment +{ + /* Length of addressed string, including the trailing NUL. */ + nls_uint32 length; + /* Offset of string in file. */ + nls_uint32 offset; +}; + +/* Descriptor for system dependent string. */ +struct sysdep_string +{ + /* Offset of static string segments in file. */ + nls_uint32 offset; + /* Alternating sequence of static and system dependent segments. + The last segment is a static segment, including the trailing NUL. */ + struct segment_pair + { + /* Size of static segment. */ + nls_uint32 segsize; + /* Reference to system dependent string segment, or ~0 at the end. */ + nls_uint32 sysdepref; + } segments[1]; +}; + +/* Marker for the end of the segments[] array. This has the value 0xFFFFFFFF, + regardless whether 'int' is 16 bit, 32 bit, or 64 bit. */ +#define SEGMENTS_END ((nls_uint32) ~0) + +/* @@ begin of epilog @@ */ + +#endif /* gettext.h */ diff --git a/lib/intl/hash-string.h b/lib/intl/hash-string.h new file mode 100644 index 0000000..b5a7d41 --- /dev/null +++ b/lib/intl/hash-string.h @@ -0,0 +1,61 @@ +/* hash-string.h - Description of GNU message catalog format: string hashing function. */ + +/* Copyright (C) 1995, 1997, 1998, 2000, 2001, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* @@ end of prolog @@ */ + +#ifndef PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +/* We assume to have `unsigned long int' value with at least 32 bits. */ +#define HASHWORDBITS 32 + + +/* Defines the so called `hashpjw' function by P.J. Weinberger + [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, + 1986, 1987 Bell Telephone Laboratories, Inc.] */ +static unsigned long int hash_string PARAMS ((const char *__str_param)); + +static inline unsigned long int +hash_string (str_param) + const char *str_param; +{ + unsigned long int hval, g; + const char *str = str_param; + + /* Compute the hash value for the given string. */ + hval = 0; + while (*str != '\0') + { + hval <<= 4; + hval += (unsigned long int) *str++; + g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); + if (g != 0) + { + hval ^= g >> (HASHWORDBITS - 8); + hval ^= g; + } + } + return hval; +} diff --git a/lib/intl/intl-compat.c b/lib/intl/intl-compat.c new file mode 100644 index 0000000..7f32349 --- /dev/null +++ b/lib/intl/intl-compat.c @@ -0,0 +1,152 @@ +/* intl-compat.c - Stub functions to call gettext functions from GNU gettext library. */ + +/* Copyright (C) 1995, 2000-2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "gettextP.h" + +/* @@ end of prolog @@ */ + +/* This file redirects the gettext functions (without prefix) to those + defined in the included GNU libintl library (with "libintl_" prefix). + It is compiled into libintl in order to make the AM_GNU_GETTEXT test + of gettext <= 0.11.2 work with the libintl library >= 0.11.3 which + has the redirections primarily in the <libintl.h> include file. + It is also compiled into libgnuintl so that libgnuintl.so can be used + as LD_PRELOADable library on glibc systems, to provide the extra + features that the functions in the libc don't have (namely, logging). */ + + +#undef gettext +#undef dgettext +#undef dcgettext +#undef ngettext +#undef dngettext +#undef dcngettext +#undef textdomain +#undef bindtextdomain +#undef bind_textdomain_codeset + + +/* When building a DLL, we must export some functions. Note that because + the functions are only defined for binary backward compatibility, we + don't need to use __declspec(dllimport) in any case. */ +#if defined _MSC_VER && BUILDING_DLL +# define DLL_EXPORTED __declspec(dllexport) +#else +# define DLL_EXPORTED +#endif + + +DLL_EXPORTED +char * +gettext (msgid) + const char *msgid; +{ + return libintl_gettext (msgid); +} + + +DLL_EXPORTED +char * +dgettext (domainname, msgid) + const char *domainname; + const char *msgid; +{ + return libintl_dgettext (domainname, msgid); +} + + +DLL_EXPORTED +char * +dcgettext (domainname, msgid, category) + const char *domainname; + const char *msgid; + int category; +{ + return libintl_dcgettext (domainname, msgid, category); +} + + +DLL_EXPORTED +char * +ngettext (msgid1, msgid2, n) + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return libintl_ngettext (msgid1, msgid2, n); +} + + +DLL_EXPORTED +char * +dngettext (domainname, msgid1, msgid2, n) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return libintl_dngettext (domainname, msgid1, msgid2, n); +} + + +DLL_EXPORTED +char * +dcngettext (domainname, msgid1, msgid2, n, category) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; + int category; +{ + return libintl_dcngettext (domainname, msgid1, msgid2, n, category); +} + + +DLL_EXPORTED +char * +textdomain (domainname) + const char *domainname; +{ + return libintl_textdomain (domainname); +} + + +DLL_EXPORTED +char * +bindtextdomain (domainname, dirname) + const char *domainname; + const char *dirname; +{ + return libintl_bindtextdomain (domainname, dirname); +} + + +DLL_EXPORTED +char * +bind_textdomain_codeset (domainname, codeset) + const char *domainname; + const char *codeset; +{ + return libintl_bind_textdomain_codeset (domainname, codeset); +} diff --git a/lib/intl/l10nflist.c b/lib/intl/l10nflist.c new file mode 100644 index 0000000..4ce284a --- /dev/null +++ b/lib/intl/l10nflist.c @@ -0,0 +1,459 @@ +/* l10nflist.c - make localization file list. */ + +/* Copyright (C) 1995-1999, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tell glibc's <string.h> to provide a prototype for stpcpy(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#if defined _LIBC || defined HAVE_ARGZ_H +# include <argz.h> +#endif +#include <ctype.h> +#include <sys/types.h> +#include <stdlib.h> + +#include "loadinfo.h" + +/* On some strange systems still no definition of NULL is found. Sigh! */ +#ifndef NULL +# if defined __STDC__ && __STDC__ +# define NULL ((void *) 0) +# else +# define NULL 0 +# endif +#endif + +/* @@ end of prolog @@ */ + +#ifdef _LIBC +/* Rename the non ANSI C functions. This is required by the standard + because some ANSI C functions will require linking with this object + file and the name space must not be polluted. */ +# ifndef stpcpy +# define stpcpy(dest, src) __stpcpy(dest, src) +# endif +#else +# ifndef HAVE_STPCPY +static char *stpcpy PARAMS ((char *dest, const char *src)); +# endif +#endif + +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not, + it may be concatenated to a directory pathname. + */ +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +# define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ + && (P)[1] == ':') +# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P)) +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0]) +#endif + +/* Define function which are usually not available. */ + +#if !defined _LIBC && !defined HAVE___ARGZ_COUNT +/* Returns the number of strings in ARGZ. */ +static size_t argz_count__ PARAMS ((const char *argz, size_t len)); + +static size_t +argz_count__ (argz, len) + const char *argz; + size_t len; +{ + size_t count = 0; + while (len > 0) + { + size_t part_len = strlen (argz); + argz += part_len + 1; + len -= part_len + 1; + count++; + } + return count; +} +# undef __argz_count +# define __argz_count(argz, len) argz_count__ (argz, len) +#else +# ifdef _LIBC +# define __argz_count(argz, len) INTUSE(__argz_count) (argz, len) +# endif +#endif /* !_LIBC && !HAVE___ARGZ_COUNT */ + +#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY +/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's + except the last into the character SEP. */ +static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep)); + +static void +argz_stringify__ (argz, len, sep) + char *argz; + size_t len; + int sep; +{ + while (len > 0) + { + size_t part_len = strlen (argz); + argz += part_len; + len -= part_len + 1; + if (len > 0) + *argz++ = sep; + } +} +# undef __argz_stringify +# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep) +#else +# ifdef _LIBC +# define __argz_stringify(argz, len, sep) \ + INTUSE(__argz_stringify) (argz, len, sep) +# endif +#endif /* !_LIBC && !HAVE___ARGZ_STRINGIFY */ + +#if !defined _LIBC && !defined HAVE___ARGZ_NEXT +static char *argz_next__ PARAMS ((char *argz, size_t argz_len, + const char *entry)); + +static char * +argz_next__ (argz, argz_len, entry) + char *argz; + size_t argz_len; + const char *entry; +{ + if (entry) + { + if (entry < argz + argz_len) + entry = strchr (entry, '\0') + 1; + + return entry >= argz + argz_len ? NULL : (char *) entry; + } + else + if (argz_len > 0) + return argz; + else + return 0; +} +# undef __argz_next +# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry) +#endif /* !_LIBC && !HAVE___ARGZ_NEXT */ + + +/* Return number of bits set in X. */ +static int pop PARAMS ((int x)); + +static inline int +pop (x) + int x; +{ + /* We assume that no more than 16 bits are used. */ + x = ((x & ~0x5555) >> 1) + (x & 0x5555); + x = ((x & ~0x3333) >> 2) + (x & 0x3333); + x = ((x >> 4) + x) & 0x0f0f; + x = ((x >> 8) + x) & 0xff; + + return x; +} + + +struct loaded_l10nfile * +_nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language, + territory, codeset, normalized_codeset, modifier, special, + sponsor, revision, filename, do_allocate) + struct loaded_l10nfile **l10nfile_list; + const char *dirlist; + size_t dirlist_len; + int mask; + const char *language; + const char *territory; + const char *codeset; + const char *normalized_codeset; + const char *modifier; + const char *special; + const char *sponsor; + const char *revision; + const char *filename; + int do_allocate; +{ + char *abs_filename; + struct loaded_l10nfile **lastp; + struct loaded_l10nfile *retval; + char *cp; + size_t dirlist_count; + size_t entries; + int cnt; + + /* If LANGUAGE contains an absolute directory specification, we ignore + DIRLIST. */ + if (IS_ABSOLUTE_PATH (language)) + dirlist_len = 0; + + /* Allocate room for the full file name. */ + abs_filename = (char *) malloc (dirlist_len + + strlen (language) + + ((mask & TERRITORY) != 0 + ? strlen (territory) + 1 : 0) + + ((mask & XPG_CODESET) != 0 + ? strlen (codeset) + 1 : 0) + + ((mask & XPG_NORM_CODESET) != 0 + ? strlen (normalized_codeset) + 1 : 0) + + (((mask & XPG_MODIFIER) != 0 + || (mask & CEN_AUDIENCE) != 0) + ? strlen (modifier) + 1 : 0) + + ((mask & CEN_SPECIAL) != 0 + ? strlen (special) + 1 : 0) + + (((mask & CEN_SPONSOR) != 0 + || (mask & CEN_REVISION) != 0) + ? (1 + ((mask & CEN_SPONSOR) != 0 + ? strlen (sponsor) : 0) + + ((mask & CEN_REVISION) != 0 + ? strlen (revision) + 1 : 0)) : 0) + + 1 + strlen (filename) + 1); + + if (abs_filename == NULL) + return NULL; + + /* Construct file name. */ + cp = abs_filename; + if (dirlist_len > 0) + { + memcpy (cp, dirlist, dirlist_len); + __argz_stringify (cp, dirlist_len, PATH_SEPARATOR); + cp += dirlist_len; + cp[-1] = '/'; + } + + cp = stpcpy (cp, language); + + if ((mask & TERRITORY) != 0) + { + *cp++ = '_'; + cp = stpcpy (cp, territory); + } + if ((mask & XPG_CODESET) != 0) + { + *cp++ = '.'; + cp = stpcpy (cp, codeset); + } + if ((mask & XPG_NORM_CODESET) != 0) + { + *cp++ = '.'; + cp = stpcpy (cp, normalized_codeset); + } + if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0) + { + /* This component can be part of both syntaxes but has different + leading characters. For CEN we use `+', else `@'. */ + *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@'; + cp = stpcpy (cp, modifier); + } + if ((mask & CEN_SPECIAL) != 0) + { + *cp++ = '+'; + cp = stpcpy (cp, special); + } + if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0) + { + *cp++ = ','; + if ((mask & CEN_SPONSOR) != 0) + cp = stpcpy (cp, sponsor); + if ((mask & CEN_REVISION) != 0) + { + *cp++ = '_'; + cp = stpcpy (cp, revision); + } + } + + *cp++ = '/'; + stpcpy (cp, filename); + + /* Look in list of already loaded domains whether it is already + available. */ + lastp = l10nfile_list; + for (retval = *l10nfile_list; retval != NULL; retval = retval->next) + if (retval->filename != NULL) + { + int compare = strcmp (retval->filename, abs_filename); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It's not in the list. */ + retval = NULL; + break; + } + + lastp = &retval->next; + } + + if (retval != NULL || do_allocate == 0) + { + free (abs_filename); + return retval; + } + + dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1); + + /* Allocate a new loaded_l10nfile. */ + retval = + (struct loaded_l10nfile *) + malloc (sizeof (*retval) + + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0)) + * sizeof (struct loaded_l10nfile *))); + if (retval == NULL) + { + free (abs_filename); + return NULL; + } + + retval->filename = abs_filename; + + /* We set retval->data to NULL here; it is filled in later. + Setting retval->decided to 1 here means that retval does not + correspond to a real file (dirlist_count > 1) or is not worth + looking up (if an unnormalized codeset was specified). */ + retval->decided = (dirlist_count > 1 + || ((mask & XPG_CODESET) != 0 + && (mask & XPG_NORM_CODESET) != 0)); + retval->data = NULL; + + retval->next = *lastp; + *lastp = retval; + + entries = 0; + /* Recurse to fill the inheritance list of RETVAL. + If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL + entry does not correspond to a real file; retval->filename contains + colons. In this case we loop across all elements of DIRLIST and + across all bit patterns dominated by MASK. + If the DIRLIST is a single directory or entirely redundant (i.e. + DIRLIST_COUNT == 1), we loop across all bit patterns dominated by + MASK, excluding MASK itself. + In either case, we loop down from MASK to 0. This has the effect + that the extra bits in the locale name are dropped in this order: + first the modifier, then the territory, then the codeset, then the + normalized_codeset. */ + for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt) + if ((cnt & ~mask) == 0 + && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0) + && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0)) + { + if (dirlist_count > 1) + { + /* Iterate over all elements of the DIRLIST. */ + char *dir = NULL; + + while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir)) + != NULL) + retval->successor[entries++] + = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, + cnt, language, territory, codeset, + normalized_codeset, modifier, special, + sponsor, revision, filename, 1); + } + else + retval->successor[entries++] + = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, + cnt, language, territory, codeset, + normalized_codeset, modifier, special, + sponsor, revision, filename, 1); + } + retval->successor[entries] = NULL; + + return retval; +} + +/* Normalize codeset name. There is no standard for the codeset + names. Normalization allows the user to use any of the common + names. The return value is dynamically allocated and has to be + freed by the caller. */ +const char * +_nl_normalize_codeset (codeset, name_len) + const char *codeset; + size_t name_len; +{ + int len = 0; + int only_digit = 1; + char *retval; + char *wp; + size_t cnt; + + for (cnt = 0; cnt < name_len; ++cnt) + if (isalnum ((unsigned char) codeset[cnt])) + { + ++len; + + if (isalpha ((unsigned char) codeset[cnt])) + only_digit = 0; + } + + retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1); + + if (retval != NULL) + { + if (only_digit) + wp = stpcpy (retval, "iso"); + else + wp = retval; + + for (cnt = 0; cnt < name_len; ++cnt) + if (isalpha ((unsigned char) codeset[cnt])) + *wp++ = tolower ((unsigned char) codeset[cnt]); + else if (isdigit ((unsigned char) codeset[cnt])) + *wp++ = codeset[cnt]; + + *wp = '\0'; + } + + return (const char *) retval; +} + + +/* @@ begin of epilog @@ */ + +/* We don't want libintl.a to depend on any other library. So we + avoid the non-standard function stpcpy. In GNU C Library this + function is available, though. Also allow the symbol HAVE_STPCPY + to be defined. */ +#if !_LIBC && !HAVE_STPCPY +static char * +stpcpy (dest, src) + char *dest; + const char *src; +{ + while ((*dest++ = *src++) != '\0') + /* Do nothing. */ ; + return dest - 1; +} +#endif diff --git a/lib/intl/libgnuintl.h.in b/lib/intl/libgnuintl.h.in new file mode 100644 index 0000000..14f9cf9 --- /dev/null +++ b/lib/intl/libgnuintl.h.in @@ -0,0 +1,311 @@ +/* libgnuintl.h - Message catalogs for internationalization. */ + +/* Copyright (C) 1995-1997, 2000-2003, 2004-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIBINTL_H +#define _LIBINTL_H 1 + +#include <locale.h> + +/* The LC_MESSAGES locale category is the category used by the functions + gettext() and dgettext(). It is specified in POSIX, but not in ANSI C. + On systems that don't define it, use an arbitrary value instead. + On Solaris, <locale.h> defines __LOCALE_H (or _LOCALE_H in Solaris 2.5) + then includes <libintl.h> (i.e. this file!) and then only defines + LC_MESSAGES. To avoid a redefinition warning, don't define LC_MESSAGES + in this case. */ +#if !defined LC_MESSAGES && !(defined __LOCALE_H || (defined _LOCALE_H && defined __sun)) +# define LC_MESSAGES 1729 +#endif + +/* We define an additional symbol to signal that we use the GNU + implementation of gettext. */ +#define __USE_GNU_GETTEXT 1 + +/* Provide information about the supported file formats. Returns the + maximum minor revision number supported for a given major revision. */ +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) \ + ((major) == 0 ? 1 : -1) + +/* Resolve a platform specific conflict on DJGPP. GNU gettext takes + precedence over _conio_gettext. */ +#ifdef __DJGPP__ +# undef gettext +#endif + +/* Use _INTL_PARAMS, not PARAMS, in order to avoid clashes with identifiers + used by programs. Similarly, test __PROTOTYPES, not PROTOTYPES. */ +#ifndef _INTL_PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define _INTL_PARAMS(args) args +# else +# define _INTL_PARAMS(args) () +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* We redirect the functions to those prefixed with "libintl_". This is + necessary, because some systems define gettext/textdomain/... in the C + library (namely, Solaris 2.4 and newer, and GNU libc 2.0 and newer). + If we used the unprefixed names, there would be cases where the + definition in the C library would override the one in the libintl.so + shared library. Recall that on ELF systems, the symbols are looked + up in the following order: + 1. in the executable, + 2. in the shared libraries specified on the link command line, in order, + 3. in the dependencies of the shared libraries specified on the link + command line, + 4. in the dlopen()ed shared libraries, in the order in which they were + dlopen()ed. + The definition in the C library would override the one in libintl.so if + either + * -lc is given on the link command line and -lintl isn't, or + * -lc is given on the link command line before -lintl, or + * libintl.so is a dependency of a dlopen()ed shared library but not + linked to the executable at link time. + Since Solaris gettext() behaves differently than GNU gettext(), this + would be unacceptable. + + The redirection happens by default through macros in C, so that &gettext + is independent of the compilation unit, but through inline functions in + C++, in order not to interfere with the name mangling of class fields or + class methods called 'gettext'. */ + +/* The user can define _INTL_REDIRECT_INLINE or _INTL_REDIRECT_MACROS. + If he doesn't, we choose the method. A third possible method is + _INTL_REDIRECT_ASM, supported only by GCC. */ +#if !(defined _INTL_REDIRECT_INLINE || defined _INTL_REDIRECT_MACROS) +# if __GNUC__ >= 2 && !defined __APPLE_CC__ && (defined __STDC__ || defined __cplusplus) +# define _INTL_REDIRECT_ASM +# else +# ifdef __cplusplus +# define _INTL_REDIRECT_INLINE +# else +# define _INTL_REDIRECT_MACROS +# endif +# endif +#endif +/* Auxiliary macros. */ +#ifdef _INTL_REDIRECT_ASM +# define _INTL_ASM(cname) __asm__ (_INTL_ASMNAME (__USER_LABEL_PREFIX__, #cname)) +# define _INTL_ASMNAME(prefix,cnamestring) _INTL_STRINGIFY (prefix) cnamestring +# define _INTL_STRINGIFY(prefix) #prefix +#else +# define _INTL_ASM(cname) +#endif + +/* Look up MSGID in the current default message catalog for the current + LC_MESSAGES locale. If not found, returns MSGID itself (the default + text). */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_gettext (const char *__msgid); +static inline char *gettext (const char *__msgid) +{ + return libintl_gettext (__msgid); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define gettext libintl_gettext +#endif +extern char *gettext _INTL_PARAMS ((const char *__msgid)) + _INTL_ASM (libintl_gettext); +#endif + +/* Look up MSGID in the DOMAINNAME message catalog for the current + LC_MESSAGES locale. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_dgettext (const char *__domainname, const char *__msgid); +static inline char *dgettext (const char *__domainname, const char *__msgid) +{ + return libintl_dgettext (__domainname, __msgid); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define dgettext libintl_dgettext +#endif +extern char *dgettext _INTL_PARAMS ((const char *__domainname, + const char *__msgid)) + _INTL_ASM (libintl_dgettext); +#endif + +/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY + locale. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_dcgettext (const char *__domainname, const char *__msgid, + int __category); +static inline char *dcgettext (const char *__domainname, const char *__msgid, + int __category) +{ + return libintl_dcgettext (__domainname, __msgid, __category); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define dcgettext libintl_dcgettext +#endif +extern char *dcgettext _INTL_PARAMS ((const char *__domainname, + const char *__msgid, + int __category)) + _INTL_ASM (libintl_dcgettext); +#endif + + +/* Similar to `gettext' but select the plural form corresponding to the + number N. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_ngettext (const char *__msgid1, const char *__msgid2, + unsigned long int __n); +static inline char *ngettext (const char *__msgid1, const char *__msgid2, + unsigned long int __n) +{ + return libintl_ngettext (__msgid1, __msgid2, __n); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define ngettext libintl_ngettext +#endif +extern char *ngettext _INTL_PARAMS ((const char *__msgid1, + const char *__msgid2, + unsigned long int __n)) + _INTL_ASM (libintl_ngettext); +#endif + +/* Similar to `dgettext' but select the plural form corresponding to the + number N. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_dngettext (const char *__domainname, const char *__msgid1, + const char *__msgid2, unsigned long int __n); +static inline char *dngettext (const char *__domainname, const char *__msgid1, + const char *__msgid2, unsigned long int __n) +{ + return libintl_dngettext (__domainname, __msgid1, __msgid2, __n); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define dngettext libintl_dngettext +#endif +extern char *dngettext _INTL_PARAMS ((const char *__domainname, + const char *__msgid1, + const char *__msgid2, + unsigned long int __n)) + _INTL_ASM (libintl_dngettext); +#endif + +/* Similar to `dcgettext' but select the plural form corresponding to the + number N. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_dcngettext (const char *__domainname, + const char *__msgid1, const char *__msgid2, + unsigned long int __n, int __category); +static inline char *dcngettext (const char *__domainname, + const char *__msgid1, const char *__msgid2, + unsigned long int __n, int __category) +{ + return libintl_dcngettext (__domainname, __msgid1, __msgid2, __n, __category); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define dcngettext libintl_dcngettext +#endif +extern char *dcngettext _INTL_PARAMS ((const char *__domainname, + const char *__msgid1, + const char *__msgid2, + unsigned long int __n, + int __category)) + _INTL_ASM (libintl_dcngettext); +#endif + + +/* Set the current default message catalog to DOMAINNAME. + If DOMAINNAME is null, return the current default. + If DOMAINNAME is "", reset to the default of "messages". */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_textdomain (const char *__domainname); +static inline char *textdomain (const char *__domainname) +{ + return libintl_textdomain (__domainname); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define textdomain libintl_textdomain +#endif +extern char *textdomain _INTL_PARAMS ((const char *__domainname)) + _INTL_ASM (libintl_textdomain); +#endif + +/* Specify that the DOMAINNAME message catalog will be found + in DIRNAME rather than in the system locale data base. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_bindtextdomain (const char *__domainname, + const char *__dirname); +static inline char *bindtextdomain (const char *__domainname, + const char *__dirname) +{ + return libintl_bindtextdomain (__domainname, __dirname); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define bindtextdomain libintl_bindtextdomain +#endif +extern char *bindtextdomain _INTL_PARAMS ((const char *__domainname, + const char *__dirname)) + _INTL_ASM (libintl_bindtextdomain); +#endif + +/* Specify the character encoding in which the messages from the + DOMAINNAME message catalog will be returned. */ +#ifdef _INTL_REDIRECT_INLINE +extern char *libintl_bind_textdomain_codeset (const char *__domainname, + const char *__codeset); +static inline char *bind_textdomain_codeset (const char *__domainname, + const char *__codeset) +{ + return libintl_bind_textdomain_codeset (__domainname, __codeset); +} +#else +#ifdef _INTL_REDIRECT_MACROS +# define bind_textdomain_codeset libintl_bind_textdomain_codeset +#endif +extern char *bind_textdomain_codeset _INTL_PARAMS ((const char *__domainname, + const char *__codeset)) + _INTL_ASM (libintl_bind_textdomain_codeset); +#endif + + +/* Support for relocatable packages. */ + +/* Sets the original and the current installation prefix of the package. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +#define libintl_set_relocation_prefix libintl_set_relocation_prefix +extern void + libintl_set_relocation_prefix _INTL_PARAMS ((const char *orig_prefix, + const char *curr_prefix)); + + +#ifdef __cplusplus +} +#endif + +#endif /* libintl.h */ diff --git a/lib/intl/loadinfo.h b/lib/intl/loadinfo.h new file mode 100644 index 0000000..d06a277 --- /dev/null +++ b/lib/intl/loadinfo.h @@ -0,0 +1,159 @@ +/* loadinfo.c */ + +/* Copyright (C) 1996-1999, 2000-2002, 2005-2009 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LOADINFO_H +#define _LOADINFO_H 1 + +/* Declarations of locale dependent catalog lookup functions. + Implemented in + + localealias.c Possibly replace a locale name by another. + explodename.c Split a locale name into its various fields. + l10nflist.c Generate a list of filenames of possible message catalogs. + finddomain.c Find and open the relevant message catalogs. + + The main function _nl_find_domain() in finddomain.c is declared + in gettextP.h. + */ + +#ifndef PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif +#endif + +#ifndef internal_function +# define internal_function +#endif + +/* Tell the compiler when a conditional or integer expression is + almost always true or almost always false. */ +#ifndef HAVE_BUILTIN_EXPECT +# define __builtin_expect(expr, val) (expr) +#endif + +/* Separator in PATH like lists of pathnames. */ +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS */ +# define PATH_SEPARATOR ';' +#else + /* Unix */ +# define PATH_SEPARATOR ':' +#endif + +/* Encoding of locale name parts. */ +#define CEN_REVISION 1 +#define CEN_SPONSOR 2 +#define CEN_SPECIAL 4 +#define XPG_NORM_CODESET 8 +#define XPG_CODESET 16 +#define TERRITORY 32 +#define CEN_AUDIENCE 64 +#define XPG_MODIFIER 128 + +#define CEN_SPECIFIC (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE) +#define XPG_SPECIFIC (XPG_CODESET|XPG_NORM_CODESET|XPG_MODIFIER) + + +struct loaded_l10nfile +{ + const char *filename; + int decided; + + const void *data; + + struct loaded_l10nfile *next; + struct loaded_l10nfile *successor[1]; +}; + + +/* Normalize codeset name. There is no standard for the codeset + names. Normalization allows the user to use any of the common + names. The return value is dynamically allocated and has to be + freed by the caller. */ +extern const char *_nl_normalize_codeset PARAMS ((const char *codeset, + size_t name_len)); + +/* Lookup a locale dependent file. + *L10NFILE_LIST denotes a pool of lookup results of locale dependent + files of the same kind, sorted in decreasing order of ->filename. + DIRLIST and DIRLIST_LEN are an argz list of directories in which to + look, containing at least one directory (i.e. DIRLIST_LEN > 0). + MASK, LANGUAGE, TERRITORY, CODESET, NORMALIZED_CODESET, MODIFIER, + SPECIAL, SPONSOR, REVISION are the pieces of the locale name, as + produced by _nl_explode_name(). FILENAME is the filename suffix. + The return value is the lookup result, either found in *L10NFILE_LIST, + or - if DO_ALLOCATE is nonzero - freshly allocated, or possibly NULL. + If the return value is non-NULL, it is added to *L10NFILE_LIST, and + its ->next field denotes the chaining inside *L10NFILE_LIST, and + furthermore its ->successor[] field contains a list of other lookup + results from which this lookup result inherits. */ +extern struct loaded_l10nfile * +_nl_make_l10nflist PARAMS ((struct loaded_l10nfile **l10nfile_list, + const char *dirlist, size_t dirlist_len, int mask, + const char *language, const char *territory, + const char *codeset, + const char *normalized_codeset, + const char *modifier, const char *special, + const char *sponsor, const char *revision, + const char *filename, int do_allocate)); + +/* Lookup the real locale name for a locale alias NAME, or NULL if + NAME is not a locale alias (but possibly a real locale name). + The return value is statically allocated and must not be freed. */ +extern const char *_nl_expand_alias PARAMS ((const char *name)); + +/* Split a locale name NAME into its pieces: language, modifier, + territory, codeset, special, sponsor, revision. + NAME gets destructively modified: NUL bytes are inserted here and + there. *LANGUAGE gets assigned NAME. Each of *MODIFIER, *TERRITORY, + *CODESET, *SPECIAL, *SPONSOR, *REVISION gets assigned either a + pointer into the old NAME string, or NULL. *NORMALIZED_CODESET + gets assigned the expanded *CODESET, if it is different from *CODESET; + this one is dynamically allocated and has to be freed by the caller. + The return value is a bitmask, where each bit corresponds to one + filled-in value: + XPG_MODIFIER, CEN_AUDIENCE for *MODIFIER, + TERRITORY for *TERRITORY, + XPG_CODESET for *CODESET, + XPG_NORM_CODESET for *NORMALIZED_CODESET, + CEN_SPECIAL for *SPECIAL, + CEN_SPONSOR for *SPONSOR, + CEN_REVISION for *REVISION. + */ +extern int _nl_explode_name PARAMS ((char *name, const char **language, + const char **modifier, + const char **territory, + const char **codeset, + const char **normalized_codeset, + const char **special, + const char **sponsor, + const char **revision)); + +/* Split a locale name NAME into a leading language part and all the + rest. Return a pointer to the first character after the language, + i.e. to the first byte of the rest. */ +extern char *_nl_find_language PARAMS ((const char *name)); + +#endif /* loadinfo.h */ diff --git a/lib/intl/loadmsgcat.c b/lib/intl/loadmsgcat.c new file mode 100644 index 0000000..adbd7b4 --- /dev/null +++ b/lib/intl/loadmsgcat.c @@ -0,0 +1,1336 @@ +/* loadmsgcat.c - Load needed message catalogs. */ + +/* Copyright (C) 1995-1999, 2000-2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tell glibc's <string.h> to provide a prototype for mempcpy(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef __GNUC__ +# undef alloca +# define alloca __builtin_alloca +# define HAVE_ALLOCA 1 +#else +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca +# else +# if defined HAVE_ALLOCA_H || defined _LIBC +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#include <stdlib.h> +#include <string.h> + +#if defined HAVE_UNISTD_H || defined _LIBC +# include <unistd.h> +#endif + +#ifdef _LIBC +# include <langinfo.h> +# include <locale.h> +#endif + +#if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \ + || (defined _LIBC && defined _POSIX_MAPPED_FILES) +# include <sys/mman.h> +# undef HAVE_MMAP +# define HAVE_MMAP 1 +#else +# undef HAVE_MMAP +#endif + +#if defined HAVE_STDINT_H_WITH_UINTMAX || defined _LIBC +# include <stdint.h> +#endif +#if defined HAVE_INTTYPES_H || defined _LIBC +# include <inttypes.h> +#endif + +#include "gmo.h" +#include "gettextP.h" +#include "hash-string.h" +#include "plural-exp.h" + +#ifdef _LIBC +# include "../locale/localeinfo.h" +#endif + +/* Provide fallback values for macros that ought to be defined in <inttypes.h>. + Note that our fallback values need not be literal strings, because we don't + use them with preprocessor string concatenation. */ +#if !defined PRId8 || PRI_MACROS_BROKEN +# undef PRId8 +# define PRId8 "d" +#endif +#if !defined PRIi8 || PRI_MACROS_BROKEN +# undef PRIi8 +# define PRIi8 "i" +#endif +#if !defined PRIo8 || PRI_MACROS_BROKEN +# undef PRIo8 +# define PRIo8 "o" +#endif +#if !defined PRIu8 || PRI_MACROS_BROKEN +# undef PRIu8 +# define PRIu8 "u" +#endif +#if !defined PRIx8 || PRI_MACROS_BROKEN +# undef PRIx8 +# define PRIx8 "x" +#endif +#if !defined PRIX8 || PRI_MACROS_BROKEN +# undef PRIX8 +# define PRIX8 "X" +#endif +#if !defined PRId16 || PRI_MACROS_BROKEN +# undef PRId16 +# define PRId16 "d" +#endif +#if !defined PRIi16 || PRI_MACROS_BROKEN +# undef PRIi16 +# define PRIi16 "i" +#endif +#if !defined PRIo16 || PRI_MACROS_BROKEN +# undef PRIo16 +# define PRIo16 "o" +#endif +#if !defined PRIu16 || PRI_MACROS_BROKEN +# undef PRIu16 +# define PRIu16 "u" +#endif +#if !defined PRIx16 || PRI_MACROS_BROKEN +# undef PRIx16 +# define PRIx16 "x" +#endif +#if !defined PRIX16 || PRI_MACROS_BROKEN +# undef PRIX16 +# define PRIX16 "X" +#endif +#if !defined PRId32 || PRI_MACROS_BROKEN +# undef PRId32 +# define PRId32 "d" +#endif +#if !defined PRIi32 || PRI_MACROS_BROKEN +# undef PRIi32 +# define PRIi32 "i" +#endif +#if !defined PRIo32 || PRI_MACROS_BROKEN +# undef PRIo32 +# define PRIo32 "o" +#endif +#if !defined PRIu32 || PRI_MACROS_BROKEN +# undef PRIu32 +# define PRIu32 "u" +#endif +#if !defined PRIx32 || PRI_MACROS_BROKEN +# undef PRIx32 +# define PRIx32 "x" +#endif +#if !defined PRIX32 || PRI_MACROS_BROKEN +# undef PRIX32 +# define PRIX32 "X" +#endif +#if !defined PRId64 || PRI_MACROS_BROKEN +# undef PRId64 +# define PRId64 (sizeof (long) == 8 ? "ld" : "lld") +#endif +#if !defined PRIi64 || PRI_MACROS_BROKEN +# undef PRIi64 +# define PRIi64 (sizeof (long) == 8 ? "li" : "lli") +#endif +#if !defined PRIo64 || PRI_MACROS_BROKEN +# undef PRIo64 +# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo") +#endif +#if !defined PRIu64 || PRI_MACROS_BROKEN +# undef PRIu64 +# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu") +#endif +#if !defined PRIx64 || PRI_MACROS_BROKEN +# undef PRIx64 +# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx") +#endif +#if !defined PRIX64 || PRI_MACROS_BROKEN +# undef PRIX64 +# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX") +#endif +#if !defined PRIdLEAST8 || PRI_MACROS_BROKEN +# undef PRIdLEAST8 +# define PRIdLEAST8 "d" +#endif +#if !defined PRIiLEAST8 || PRI_MACROS_BROKEN +# undef PRIiLEAST8 +# define PRIiLEAST8 "i" +#endif +#if !defined PRIoLEAST8 || PRI_MACROS_BROKEN +# undef PRIoLEAST8 +# define PRIoLEAST8 "o" +#endif +#if !defined PRIuLEAST8 || PRI_MACROS_BROKEN +# undef PRIuLEAST8 +# define PRIuLEAST8 "u" +#endif +#if !defined PRIxLEAST8 || PRI_MACROS_BROKEN +# undef PRIxLEAST8 +# define PRIxLEAST8 "x" +#endif +#if !defined PRIXLEAST8 || PRI_MACROS_BROKEN +# undef PRIXLEAST8 +# define PRIXLEAST8 "X" +#endif +#if !defined PRIdLEAST16 || PRI_MACROS_BROKEN +# undef PRIdLEAST16 +# define PRIdLEAST16 "d" +#endif +#if !defined PRIiLEAST16 || PRI_MACROS_BROKEN +# undef PRIiLEAST16 +# define PRIiLEAST16 "i" +#endif +#if !defined PRIoLEAST16 || PRI_MACROS_BROKEN +# undef PRIoLEAST16 +# define PRIoLEAST16 "o" +#endif +#if !defined PRIuLEAST16 || PRI_MACROS_BROKEN +# undef PRIuLEAST16 +# define PRIuLEAST16 "u" +#endif +#if !defined PRIxLEAST16 || PRI_MACROS_BROKEN +# undef PRIxLEAST16 +# define PRIxLEAST16 "x" +#endif +#if !defined PRIXLEAST16 || PRI_MACROS_BROKEN +# undef PRIXLEAST16 +# define PRIXLEAST16 "X" +#endif +#if !defined PRIdLEAST32 || PRI_MACROS_BROKEN +# undef PRIdLEAST32 +# define PRIdLEAST32 "d" +#endif +#if !defined PRIiLEAST32 || PRI_MACROS_BROKEN +# undef PRIiLEAST32 +# define PRIiLEAST32 "i" +#endif +#if !defined PRIoLEAST32 || PRI_MACROS_BROKEN +# undef PRIoLEAST32 +# define PRIoLEAST32 "o" +#endif +#if !defined PRIuLEAST32 || PRI_MACROS_BROKEN +# undef PRIuLEAST32 +# define PRIuLEAST32 "u" +#endif +#if !defined PRIxLEAST32 || PRI_MACROS_BROKEN +# undef PRIxLEAST32 +# define PRIxLEAST32 "x" +#endif +#if !defined PRIXLEAST32 || PRI_MACROS_BROKEN +# undef PRIXLEAST32 +# define PRIXLEAST32 "X" +#endif +#if !defined PRIdLEAST64 || PRI_MACROS_BROKEN +# undef PRIdLEAST64 +# define PRIdLEAST64 PRId64 +#endif +#if !defined PRIiLEAST64 || PRI_MACROS_BROKEN +# undef PRIiLEAST64 +# define PRIiLEAST64 PRIi64 +#endif +#if !defined PRIoLEAST64 || PRI_MACROS_BROKEN +# undef PRIoLEAST64 +# define PRIoLEAST64 PRIo64 +#endif +#if !defined PRIuLEAST64 || PRI_MACROS_BROKEN +# undef PRIuLEAST64 +# define PRIuLEAST64 PRIu64 +#endif +#if !defined PRIxLEAST64 || PRI_MACROS_BROKEN +# undef PRIxLEAST64 +# define PRIxLEAST64 PRIx64 +#endif +#if !defined PRIXLEAST64 || PRI_MACROS_BROKEN +# undef PRIXLEAST64 +# define PRIXLEAST64 PRIX64 +#endif +#if !defined PRIdFAST8 || PRI_MACROS_BROKEN +# undef PRIdFAST8 +# define PRIdFAST8 "d" +#endif +#if !defined PRIiFAST8 || PRI_MACROS_BROKEN +# undef PRIiFAST8 +# define PRIiFAST8 "i" +#endif +#if !defined PRIoFAST8 || PRI_MACROS_BROKEN +# undef PRIoFAST8 +# define PRIoFAST8 "o" +#endif +#if !defined PRIuFAST8 || PRI_MACROS_BROKEN +# undef PRIuFAST8 +# define PRIuFAST8 "u" +#endif +#if !defined PRIxFAST8 || PRI_MACROS_BROKEN +# undef PRIxFAST8 +# define PRIxFAST8 "x" +#endif +#if !defined PRIXFAST8 || PRI_MACROS_BROKEN +# undef PRIXFAST8 +# define PRIXFAST8 "X" +#endif +#if !defined PRIdFAST16 || PRI_MACROS_BROKEN +# undef PRIdFAST16 +# define PRIdFAST16 "d" +#endif +#if !defined PRIiFAST16 || PRI_MACROS_BROKEN +# undef PRIiFAST16 +# define PRIiFAST16 "i" +#endif +#if !defined PRIoFAST16 || PRI_MACROS_BROKEN +# undef PRIoFAST16 +# define PRIoFAST16 "o" +#endif +#if !defined PRIuFAST16 || PRI_MACROS_BROKEN +# undef PRIuFAST16 +# define PRIuFAST16 "u" +#endif +#if !defined PRIxFAST16 || PRI_MACROS_BROKEN +# undef PRIxFAST16 +# define PRIxFAST16 "x" +#endif +#if !defined PRIXFAST16 || PRI_MACROS_BROKEN +# undef PRIXFAST16 +# define PRIXFAST16 "X" +#endif +#if !defined PRIdFAST32 || PRI_MACROS_BROKEN +# undef PRIdFAST32 +# define PRIdFAST32 "d" +#endif +#if !defined PRIiFAST32 || PRI_MACROS_BROKEN +# undef PRIiFAST32 +# define PRIiFAST32 "i" +#endif +#if !defined PRIoFAST32 || PRI_MACROS_BROKEN +# undef PRIoFAST32 +# define PRIoFAST32 "o" +#endif +#if !defined PRIuFAST32 || PRI_MACROS_BROKEN +# undef PRIuFAST32 +# define PRIuFAST32 "u" +#endif +#if !defined PRIxFAST32 || PRI_MACROS_BROKEN +# undef PRIxFAST32 +# define PRIxFAST32 "x" +#endif +#if !defined PRIXFAST32 || PRI_MACROS_BROKEN +# undef PRIXFAST32 +# define PRIXFAST32 "X" +#endif +#if !defined PRIdFAST64 || PRI_MACROS_BROKEN +# undef PRIdFAST64 +# define PRIdFAST64 PRId64 +#endif +#if !defined PRIiFAST64 || PRI_MACROS_BROKEN +# undef PRIiFAST64 +# define PRIiFAST64 PRIi64 +#endif +#if !defined PRIoFAST64 || PRI_MACROS_BROKEN +# undef PRIoFAST64 +# define PRIoFAST64 PRIo64 +#endif +#if !defined PRIuFAST64 || PRI_MACROS_BROKEN +# undef PRIuFAST64 +# define PRIuFAST64 PRIu64 +#endif +#if !defined PRIxFAST64 || PRI_MACROS_BROKEN +# undef PRIxFAST64 +# define PRIxFAST64 PRIx64 +#endif +#if !defined PRIXFAST64 || PRI_MACROS_BROKEN +# undef PRIXFAST64 +# define PRIXFAST64 PRIX64 +#endif +#if !defined PRIdMAX || PRI_MACROS_BROKEN +# undef PRIdMAX +# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld") +#endif +#if !defined PRIiMAX || PRI_MACROS_BROKEN +# undef PRIiMAX +# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli") +#endif +#if !defined PRIoMAX || PRI_MACROS_BROKEN +# undef PRIoMAX +# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo") +#endif +#if !defined PRIuMAX || PRI_MACROS_BROKEN +# undef PRIuMAX +# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu") +#endif +#if !defined PRIxMAX || PRI_MACROS_BROKEN +# undef PRIxMAX +# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx") +#endif +#if !defined PRIXMAX || PRI_MACROS_BROKEN +# undef PRIXMAX +# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX") +#endif +#if !defined PRIdPTR || PRI_MACROS_BROKEN +# undef PRIdPTR +# define PRIdPTR \ + (sizeof (void *) == sizeof (long) ? "ld" : \ + sizeof (void *) == sizeof (int) ? "d" : \ + "lld") +#endif +#if !defined PRIiPTR || PRI_MACROS_BROKEN +# undef PRIiPTR +# define PRIiPTR \ + (sizeof (void *) == sizeof (long) ? "li" : \ + sizeof (void *) == sizeof (int) ? "i" : \ + "lli") +#endif +#if !defined PRIoPTR || PRI_MACROS_BROKEN +# undef PRIoPTR +# define PRIoPTR \ + (sizeof (void *) == sizeof (long) ? "lo" : \ + sizeof (void *) == sizeof (int) ? "o" : \ + "llo") +#endif +#if !defined PRIuPTR || PRI_MACROS_BROKEN +# undef PRIuPTR +# define PRIuPTR \ + (sizeof (void *) == sizeof (long) ? "lu" : \ + sizeof (void *) == sizeof (int) ? "u" : \ + "llu") +#endif +#if !defined PRIxPTR || PRI_MACROS_BROKEN +# undef PRIxPTR +# define PRIxPTR \ + (sizeof (void *) == sizeof (long) ? "lx" : \ + sizeof (void *) == sizeof (int) ? "x" : \ + "llx") +#endif +#if !defined PRIXPTR || PRI_MACROS_BROKEN +# undef PRIXPTR +# define PRIXPTR \ + (sizeof (void *) == sizeof (long) ? "lX" : \ + sizeof (void *) == sizeof (int) ? "X" : \ + "llX") +#endif + +/* @@ end of prolog @@ */ + +#ifdef _LIBC +/* Rename the non ISO C functions. This is required by the standard + because some ISO C functions will require linking with this object + file and the name space must not be polluted. */ +# define open __open +# define close __close +# define read __read +# define mmap __mmap +# define munmap __munmap +#endif + +/* For those losing systems which don't have `alloca' we have to add + some additional code emulating it. */ +#ifdef HAVE_ALLOCA +# define freea(p) /* nothing */ +#else +# define alloca(n) malloc (n) +# define freea(p) free (p) +#endif + +/* For systems that distinguish between text and binary I/O. + O_BINARY is usually declared in <fcntl.h>. */ +#if !defined O_BINARY && defined _O_BINARY + /* For MSC-compatible compilers. */ +# define O_BINARY _O_BINARY +# define O_TEXT _O_TEXT +#endif +#ifdef __BEOS__ + /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */ +# undef O_BINARY +# undef O_TEXT +#endif +/* On reasonable systems, binary I/O is the default. */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif + + +/* Prototypes for local functions. Needed to ensure compiler checking of + function argument counts despite of K&R C function definition syntax. */ +static const char *get_sysdep_segment_value PARAMS ((const char *name)); + + +/* We need a sign, whether a new catalog was loaded, which can be associated + with all translations. This is important if the translations are + cached by one of GCC's features. */ +int _nl_msg_cat_cntr; + + +/* Expand a system dependent string segment. Return NULL if unsupported. */ +static const char * +get_sysdep_segment_value (name) + const char *name; +{ + /* Test for an ISO C 99 section 7.8.1 format string directive. + Syntax: + P R I { d | i | o | u | x | X } + { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ + /* We don't use a table of 14 times 6 'const char *' strings here, because + data relocations cost startup time. */ + if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I') + { + if (name[3] == 'd' || name[3] == 'i' || name[3] == 'o' || name[3] == 'u' + || name[3] == 'x' || name[3] == 'X') + { + if (name[4] == '8' && name[5] == '\0') + { + if (name[3] == 'd') + return PRId8; + if (name[3] == 'i') + return PRIi8; + if (name[3] == 'o') + return PRIo8; + if (name[3] == 'u') + return PRIu8; + if (name[3] == 'x') + return PRIx8; + if (name[3] == 'X') + return PRIX8; + abort (); + } + if (name[4] == '1' && name[5] == '6' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId16; + if (name[3] == 'i') + return PRIi16; + if (name[3] == 'o') + return PRIo16; + if (name[3] == 'u') + return PRIu16; + if (name[3] == 'x') + return PRIx16; + if (name[3] == 'X') + return PRIX16; + abort (); + } + if (name[4] == '3' && name[5] == '2' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId32; + if (name[3] == 'i') + return PRIi32; + if (name[3] == 'o') + return PRIo32; + if (name[3] == 'u') + return PRIu32; + if (name[3] == 'x') + return PRIx32; + if (name[3] == 'X') + return PRIX32; + abort (); + } + if (name[4] == '6' && name[5] == '4' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId64; + if (name[3] == 'i') + return PRIi64; + if (name[3] == 'o') + return PRIo64; + if (name[3] == 'u') + return PRIu64; + if (name[3] == 'x') + return PRIx64; + if (name[3] == 'X') + return PRIX64; + abort (); + } + if (name[4] == 'L' && name[5] == 'E' && name[6] == 'A' + && name[7] == 'S' && name[8] == 'T') + { + if (name[9] == '8' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST8; + if (name[3] == 'i') + return PRIiLEAST8; + if (name[3] == 'o') + return PRIoLEAST8; + if (name[3] == 'u') + return PRIuLEAST8; + if (name[3] == 'x') + return PRIxLEAST8; + if (name[3] == 'X') + return PRIXLEAST8; + abort (); + } + if (name[9] == '1' && name[10] == '6' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST16; + if (name[3] == 'i') + return PRIiLEAST16; + if (name[3] == 'o') + return PRIoLEAST16; + if (name[3] == 'u') + return PRIuLEAST16; + if (name[3] == 'x') + return PRIxLEAST16; + if (name[3] == 'X') + return PRIXLEAST16; + abort (); + } + if (name[9] == '3' && name[10] == '2' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST32; + if (name[3] == 'i') + return PRIiLEAST32; + if (name[3] == 'o') + return PRIoLEAST32; + if (name[3] == 'u') + return PRIuLEAST32; + if (name[3] == 'x') + return PRIxLEAST32; + if (name[3] == 'X') + return PRIXLEAST32; + abort (); + } + if (name[9] == '6' && name[10] == '4' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST64; + if (name[3] == 'i') + return PRIiLEAST64; + if (name[3] == 'o') + return PRIoLEAST64; + if (name[3] == 'u') + return PRIuLEAST64; + if (name[3] == 'x') + return PRIxLEAST64; + if (name[3] == 'X') + return PRIXLEAST64; + abort (); + } + } + if (name[4] == 'F' && name[5] == 'A' && name[6] == 'S' + && name[7] == 'T') + { + if (name[8] == '8' && name[9] == '\0') + { + if (name[3] == 'd') + return PRIdFAST8; + if (name[3] == 'i') + return PRIiFAST8; + if (name[3] == 'o') + return PRIoFAST8; + if (name[3] == 'u') + return PRIuFAST8; + if (name[3] == 'x') + return PRIxFAST8; + if (name[3] == 'X') + return PRIXFAST8; + abort (); + } + if (name[8] == '1' && name[9] == '6' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST16; + if (name[3] == 'i') + return PRIiFAST16; + if (name[3] == 'o') + return PRIoFAST16; + if (name[3] == 'u') + return PRIuFAST16; + if (name[3] == 'x') + return PRIxFAST16; + if (name[3] == 'X') + return PRIXFAST16; + abort (); + } + if (name[8] == '3' && name[9] == '2' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST32; + if (name[3] == 'i') + return PRIiFAST32; + if (name[3] == 'o') + return PRIoFAST32; + if (name[3] == 'u') + return PRIuFAST32; + if (name[3] == 'x') + return PRIxFAST32; + if (name[3] == 'X') + return PRIXFAST32; + abort (); + } + if (name[8] == '6' && name[9] == '4' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST64; + if (name[3] == 'i') + return PRIiFAST64; + if (name[3] == 'o') + return PRIoFAST64; + if (name[3] == 'u') + return PRIuFAST64; + if (name[3] == 'x') + return PRIxFAST64; + if (name[3] == 'X') + return PRIXFAST64; + abort (); + } + } + if (name[4] == 'M' && name[5] == 'A' && name[6] == 'X' + && name[7] == '\0') + { + if (name[3] == 'd') + return PRIdMAX; + if (name[3] == 'i') + return PRIiMAX; + if (name[3] == 'o') + return PRIoMAX; + if (name[3] == 'u') + return PRIuMAX; + if (name[3] == 'x') + return PRIxMAX; + if (name[3] == 'X') + return PRIXMAX; + abort (); + } + if (name[4] == 'P' && name[5] == 'T' && name[6] == 'R' + && name[7] == '\0') + { + if (name[3] == 'd') + return PRIdPTR; + if (name[3] == 'i') + return PRIiPTR; + if (name[3] == 'o') + return PRIoPTR; + if (name[3] == 'u') + return PRIuPTR; + if (name[3] == 'x') + return PRIxPTR; + if (name[3] == 'X') + return PRIXPTR; + abort (); + } + } + } + /* Other system dependent strings are not valid. */ + return NULL; +} + +/* Initialize the codeset dependent parts of an opened message catalog. + Return the header entry. */ +const char * +internal_function +_nl_init_domain_conv (domain_file, domain, domainbinding) + struct loaded_l10nfile *domain_file; + struct loaded_domain *domain; + struct binding *domainbinding; +{ + /* Find out about the character set the file is encoded with. + This can be found (in textual form) in the entry "". If this + entry does not exist or if this does not contain the `charset=' + information, we will assume the charset matches the one the + current locale and we don't have to perform any conversion. */ + char *nullentry; + size_t nullentrylen; + + /* Preinitialize fields, to avoid recursion during _nl_find_msg. */ + domain->codeset_cntr = + (domainbinding != NULL ? domainbinding->codeset_cntr : 0); +#ifdef _LIBC + domain->conv = (__gconv_t) -1; +#else +# if HAVE_ICONV + domain->conv = (iconv_t) -1; +# endif +#endif + domain->conv_tab = NULL; + + /* Get the header entry. */ + nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen); + + if (nullentry != NULL) + { +#if defined _LIBC || HAVE_ICONV + const char *charsetstr; + + charsetstr = strstr (nullentry, "charset="); + if (charsetstr != NULL) + { + size_t len; + char *charset; + const char *outcharset; + + charsetstr += strlen ("charset="); + len = strcspn (charsetstr, " \t\n"); + + charset = (char *) alloca (len + 1); +# if defined _LIBC || HAVE_MEMPCPY + *((char *) mempcpy (charset, charsetstr, len)) = '\0'; +# else + memcpy (charset, charsetstr, len); + charset[len] = '\0'; +# endif + + /* The output charset should normally be determined by the + locale. But sometimes the locale is not used or not correctly + set up, so we provide a possibility for the user to override + this. Moreover, the value specified through + bind_textdomain_codeset overrides both. */ + if (domainbinding != NULL && domainbinding->codeset != NULL) + outcharset = domainbinding->codeset; + else + { + outcharset = getenv ("OUTPUT_CHARSET"); + if (outcharset == NULL || outcharset[0] == '\0') + { +# ifdef _LIBC + outcharset = _NL_CURRENT (LC_CTYPE, CODESET); +# else +# if HAVE_ICONV + extern const char *locale_charset PARAMS ((void)); + outcharset = locale_charset (); +# endif +# endif + } + } + +# ifdef _LIBC + /* We always want to use transliteration. */ + outcharset = norm_add_slashes (outcharset, "TRANSLIT"); + charset = norm_add_slashes (charset, NULL); + if (__gconv_open (outcharset, charset, &domain->conv, + GCONV_AVOID_NOCONV) + != __GCONV_OK) + domain->conv = (__gconv_t) -1; +# else +# if HAVE_ICONV + /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5, + we want to use transliteration. */ +# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \ + || _LIBICONV_VERSION >= 0x0105 + if (strchr (outcharset, '/') == NULL) + { + char *tmp; + + len = strlen (outcharset); + tmp = (char *) alloca (len + 10 + 1); + memcpy (tmp, outcharset, len); + memcpy (tmp + len, "//TRANSLIT", 10 + 1); + outcharset = tmp; + + domain->conv = iconv_open (outcharset, charset); + + freea (outcharset); + } + else +# endif + domain->conv = iconv_open (outcharset, charset); +# endif +# endif + + freea (charset); + } +#endif /* _LIBC || HAVE_ICONV */ + } + + return nullentry; +} + +/* Frees the codeset dependent parts of an opened message catalog. */ +void +internal_function +_nl_free_domain_conv (domain) + struct loaded_domain *domain; +{ + if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1) + free (domain->conv_tab); + +#ifdef _LIBC + if (domain->conv != (__gconv_t) -1) + __gconv_close (domain->conv); +#else +# if HAVE_ICONV + if (domain->conv != (iconv_t) -1) + iconv_close (domain->conv); +# endif +#endif +} + +/* Load the message catalogs specified by FILENAME. If it is no valid + message catalog do nothing. */ +void +internal_function +_nl_load_domain (domain_file, domainbinding) + struct loaded_l10nfile *domain_file; + struct binding *domainbinding; +{ + int fd; + size_t size; +#ifdef _LIBC + struct stat64 st; +#else + struct stat st; +#endif + struct mo_file_header *data = (struct mo_file_header *) -1; + int use_mmap = 0; + struct loaded_domain *domain; + int revision; + const char *nullentry; + + domain_file->decided = 1; + domain_file->data = NULL; + + /* Note that it would be useless to store domainbinding in domain_file + because domainbinding might be == NULL now but != NULL later (after + a call to bind_textdomain_codeset). */ + + /* If the record does not represent a valid locale the FILENAME + might be NULL. This can happen when according to the given + specification the locale file name is different for XPG and CEN + syntax. */ + if (domain_file->filename == NULL) + return; + + /* Try to open the addressed file. */ + fd = open (domain_file->filename, O_RDONLY | O_BINARY); + if (fd == -1) + return; + + /* We must know about the size of the file. */ + if ( +#ifdef _LIBC + __builtin_expect (fstat64 (fd, &st) != 0, 0) +#else + __builtin_expect (fstat (fd, &st) != 0, 0) +#endif + || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0) + || __builtin_expect (size < sizeof (struct mo_file_header), 0)) + { + /* Something went wrong. */ + close (fd); + return; + } + +#ifdef HAVE_MMAP + /* Now we are ready to load the file. If mmap() is available we try + this first. If not available or it failed we try to load it. */ + data = (struct mo_file_header *) mmap (NULL, size, PROT_READ, + MAP_PRIVATE, fd, 0); + + if (__builtin_expect (data != (struct mo_file_header *) -1, 1)) + { + /* mmap() call was successful. */ + close (fd); + use_mmap = 1; + } +#endif + + /* If the data is not yet available (i.e. mmap'ed) we try to load + it manually. */ + if (data == (struct mo_file_header *) -1) + { + size_t to_read; + char *read_ptr; + + data = (struct mo_file_header *) malloc (size); + if (data == NULL) + { + if (use_mmap == 0) + close (fd); + return; + } + + to_read = size; + read_ptr = (char *) data; + do + { + long int nb = (long int) read (fd, read_ptr, to_read); + if (nb <= 0) + { +#ifdef EINTR + if (nb == -1 && errno == EINTR) + continue; +#endif + close (fd); + return; + } + read_ptr += nb; + to_read -= nb; + } + while (to_read > 0); + + close (fd); + } + + /* Using the magic number we can test whether it really is a message + catalog file. */ + if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED, + 0)) + { + /* The magic number is wrong: not a message catalog file. */ +#ifdef HAVE_MMAP + if (use_mmap) + munmap ((caddr_t) data, size); + else +#endif + free (data); + return; + } + + domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); + if (domain == NULL) + { +#ifdef HAVE_MMAP + if (use_mmap) + munmap ((caddr_t) data, size); + else +#endif + free (data); + return; + } + domain_file->data = domain; + + domain->data = (char *) data; + domain->use_mmap = use_mmap; + domain->mmap_size = size; + domain->must_swap = data->magic != _MAGIC; + domain->malloced = NULL; + + /* Fill in the information about the available tables. */ + revision = W (domain->must_swap, data->revision); + /* We support only the major revision 0. */ + switch (revision >> 16) + { + case 0: + domain->nstrings = W (domain->must_swap, data->nstrings); + domain->orig_tab = (const struct string_desc *) + ((char *) data + W (domain->must_swap, data->orig_tab_offset)); + domain->trans_tab = (const struct string_desc *) + ((char *) data + W (domain->must_swap, data->trans_tab_offset)); + domain->hash_size = W (domain->must_swap, data->hash_tab_size); + domain->hash_tab = + (domain->hash_size > 2 + ? (const nls_uint32 *) + ((char *) data + W (domain->must_swap, data->hash_tab_offset)) + : NULL); + domain->must_swap_hash_tab = domain->must_swap; + + /* Now dispatch on the minor revision. */ + switch (revision & 0xffff) + { + case 0: + domain->n_sysdep_strings = 0; + domain->orig_sysdep_tab = NULL; + domain->trans_sysdep_tab = NULL; + break; + case 1: + default: + { + nls_uint32 n_sysdep_strings; + + if (domain->hash_tab == NULL) + /* This is invalid. These minor revisions need a hash table. */ + goto invalid; + + n_sysdep_strings = + W (domain->must_swap, data->n_sysdep_strings); + if (n_sysdep_strings > 0) + { + nls_uint32 n_sysdep_segments; + const struct sysdep_segment *sysdep_segments; + const char **sysdep_segment_values; + const nls_uint32 *orig_sysdep_tab; + const nls_uint32 *trans_sysdep_tab; + size_t memneed; + char *mem; + struct sysdep_string_desc *inmem_orig_sysdep_tab; + struct sysdep_string_desc *inmem_trans_sysdep_tab; + nls_uint32 *inmem_hash_tab; + unsigned int i; + + /* Get the values of the system dependent segments. */ + n_sysdep_segments = + W (domain->must_swap, data->n_sysdep_segments); + sysdep_segments = (const struct sysdep_segment *) + ((char *) data + + W (domain->must_swap, data->sysdep_segments_offset)); + sysdep_segment_values = + alloca (n_sysdep_segments * sizeof (const char *)); + for (i = 0; i < n_sysdep_segments; i++) + { + const char *name = + (char *) data + + W (domain->must_swap, sysdep_segments[i].offset); + nls_uint32 namelen = + W (domain->must_swap, sysdep_segments[i].length); + + if (!(namelen > 0 && name[namelen - 1] == '\0')) + { + freea (sysdep_segment_values); + goto invalid; + } + + sysdep_segment_values[i] = get_sysdep_segment_value (name); + } + + orig_sysdep_tab = (const nls_uint32 *) + ((char *) data + + W (domain->must_swap, data->orig_sysdep_tab_offset)); + trans_sysdep_tab = (const nls_uint32 *) + ((char *) data + + W (domain->must_swap, data->trans_sysdep_tab_offset)); + + /* Compute the amount of additional memory needed for the + system dependent strings and the augmented hash table. */ + memneed = 2 * n_sysdep_strings + * sizeof (struct sysdep_string_desc) + + domain->hash_size * sizeof (nls_uint32); + for (i = 0; i < 2 * n_sysdep_strings; i++) + { + const struct sysdep_string *sysdep_string = + (const struct sysdep_string *) + ((char *) data + + W (domain->must_swap, + i < n_sysdep_strings + ? orig_sysdep_tab[i] + : trans_sysdep_tab[i - n_sysdep_strings])); + size_t need = 0; + const struct segment_pair *p = sysdep_string->segments; + + if (W (domain->must_swap, p->sysdepref) != SEGMENTS_END) + for (p = sysdep_string->segments;; p++) + { + nls_uint32 sysdepref; + + need += W (domain->must_swap, p->segsize); + + sysdepref = W (domain->must_swap, p->sysdepref); + if (sysdepref == SEGMENTS_END) + break; + + if (sysdepref >= n_sysdep_segments) + { + /* Invalid. */ + freea (sysdep_segment_values); + goto invalid; + } + + need += strlen (sysdep_segment_values[sysdepref]); + } + + memneed += need; + } + + /* Allocate additional memory. */ + mem = (char *) malloc (memneed); + if (mem == NULL) + goto invalid; + + domain->malloced = mem; + inmem_orig_sysdep_tab = (struct sysdep_string_desc *) mem; + mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); + inmem_trans_sysdep_tab = (struct sysdep_string_desc *) mem; + mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); + inmem_hash_tab = (nls_uint32 *) mem; + mem += domain->hash_size * sizeof (nls_uint32); + + /* Compute the system dependent strings. */ + for (i = 0; i < 2 * n_sysdep_strings; i++) + { + const struct sysdep_string *sysdep_string = + (const struct sysdep_string *) + ((char *) data + + W (domain->must_swap, + i < n_sysdep_strings + ? orig_sysdep_tab[i] + : trans_sysdep_tab[i - n_sysdep_strings])); + const char *static_segments = + (char *) data + + W (domain->must_swap, sysdep_string->offset); + const struct segment_pair *p = sysdep_string->segments; + + /* Concatenate the segments, and fill + inmem_orig_sysdep_tab[i] (for i < n_sysdep_strings) and + inmem_trans_sysdep_tab[i-n_sysdep_strings] (for + i >= n_sysdep_strings). */ + + if (W (domain->must_swap, p->sysdepref) == SEGMENTS_END) + { + /* Only one static segment. */ + inmem_orig_sysdep_tab[i].length = + W (domain->must_swap, p->segsize); + inmem_orig_sysdep_tab[i].pointer = static_segments; + } + else + { + inmem_orig_sysdep_tab[i].pointer = mem; + + for (p = sysdep_string->segments;; p++) + { + nls_uint32 segsize = + W (domain->must_swap, p->segsize); + nls_uint32 sysdepref = + W (domain->must_swap, p->sysdepref); + size_t n; + + if (segsize > 0) + { + memcpy (mem, static_segments, segsize); + mem += segsize; + static_segments += segsize; + } + + if (sysdepref == SEGMENTS_END) + break; + + n = strlen (sysdep_segment_values[sysdepref]); + memcpy (mem, sysdep_segment_values[sysdepref], n); + mem += n; + } + + inmem_orig_sysdep_tab[i].length = + mem - inmem_orig_sysdep_tab[i].pointer; + } + } + + /* Compute the augmented hash table. */ + for (i = 0; i < domain->hash_size; i++) + inmem_hash_tab[i] = + W (domain->must_swap_hash_tab, domain->hash_tab[i]); + for (i = 0; i < n_sysdep_strings; i++) + { + const char *msgid = inmem_orig_sysdep_tab[i].pointer; + nls_uint32 hash_val = hash_string (msgid); + nls_uint32 idx = hash_val % domain->hash_size; + nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); + + for (;;) + { + if (inmem_hash_tab[idx] == 0) + { + /* Hash table entry is empty. Use it. */ + inmem_hash_tab[idx] = 1 + domain->nstrings + i; + break; + } + + if (idx >= domain->hash_size - incr) + idx -= domain->hash_size - incr; + else + idx += incr; + } + } + + freea (sysdep_segment_values); + + domain->n_sysdep_strings = n_sysdep_strings; + domain->orig_sysdep_tab = inmem_orig_sysdep_tab; + domain->trans_sysdep_tab = inmem_trans_sysdep_tab; + + domain->hash_tab = inmem_hash_tab; + domain->must_swap_hash_tab = 0; + } + else + { + domain->n_sysdep_strings = 0; + domain->orig_sysdep_tab = NULL; + domain->trans_sysdep_tab = NULL; + } + } + break; + } + break; + default: + /* This is an invalid revision. */ + invalid: + /* This is an invalid .mo file. */ + if (domain->malloced) + free (domain->malloced); +#ifdef HAVE_MMAP + if (use_mmap) + munmap ((caddr_t) data, size); + else +#endif + free (data); + free (domain); + domain_file->data = NULL; + return; + } + + /* Now initialize the character set converter from the character set + the file is encoded with (found in the header entry) to the domain's + specified character set or the locale's character set. */ + nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding); + + /* Also look for a plural specification. */ + EXTRACT_PLURAL_EXPRESSION (nullentry, &domain->plural, &domain->nplurals); +} + + +#ifdef _LIBC +void +internal_function +_nl_unload_domain (domain) + struct loaded_domain *domain; +{ + if (domain->plural != &__gettext_germanic_plural) + __gettext_free_exp (domain->plural); + + _nl_free_domain_conv (domain); + + if (domain->malloced) + free (domain->malloced); + +# ifdef _POSIX_MAPPED_FILES + if (domain->use_mmap) + munmap ((caddr_t) domain->data, domain->mmap_size); + else +# endif /* _POSIX_MAPPED_FILES */ + free ((void *) domain->data); + + free (domain); +} +#endif diff --git a/lib/intl/localcharset.c b/lib/intl/localcharset.c new file mode 100644 index 0000000..6983d0b --- /dev/null +++ b/lib/intl/localcharset.c @@ -0,0 +1,399 @@ +/* localcharset.c - Determine a canonical name for the current locale's character encoding. */ + +/* Copyright (C) 2000-2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Written by Bruno Haible <bruno@clisp.org>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* Specification. */ +#include "localcharset.h" + +#if HAVE_STDDEF_H +# include <stddef.h> +#endif + +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#else +# include <strings.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#if defined _WIN32 || defined __WIN32__ +# undef WIN32 /* avoid warning on mingw32 */ +# define WIN32 +#endif + +#if defined __EMX__ +/* Assume EMX program runs on OS/2, even if compiled under DOS. */ +# define OS2 +#endif + +#if !defined WIN32 +# if HAVE_LANGINFO_CODESET +# include <langinfo.h> +# else +# if HAVE_SETLOCALE +# include <locale.h> +# endif +# endif +#elif defined WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif +#if defined OS2 +# define INCL_DOS +# include <os2.h> +#endif + +#if ENABLE_RELOCATABLE +# include "relocatable.h" +#else +# define relocate(pathname) (pathname) +#endif + +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +#endif + +#ifndef DIRECTORY_SEPARATOR +# define DIRECTORY_SEPARATOR '/' +#endif + +#ifndef ISSLASH +# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) +#endif + +#ifdef HAVE_GETC_UNLOCKED +# undef getc +# define getc getc_unlocked +#endif + +/* The following static variable is declared 'volatile' to avoid a + possible multithread problem in the function get_charset_aliases. If we + are running in a threaded environment, and if two threads initialize + 'charset_aliases' simultaneously, both will produce the same value, + and everything will be ok if the two assignments to 'charset_aliases' + are atomic. But I don't know what will happen if the two assignments mix. */ +#if __STDC__ != 1 +# define volatile /* empty */ +#endif +/* Pointer to the contents of the charset.alias file, if it has already been + read, else NULL. Its format is: + ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0' */ +static const char * volatile charset_aliases; + +/* Return a pointer to the contents of the charset.alias file. */ +static const char * +get_charset_aliases () +{ + const char *cp; + + cp = charset_aliases; + if (cp == NULL) + { +#if !(defined VMS || defined WIN32) + FILE *fp; + const char *dir = relocate (LIBDIR); + const char *base = "charset.alias"; + char *file_name; + + /* Concatenate dir and base into freshly allocated file_name. */ + { + size_t dir_len = strlen (dir); + size_t base_len = strlen (base); + int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1])); + file_name = (char *) malloc (dir_len + add_slash + base_len + 1); + if (file_name != NULL) + { + memcpy (file_name, dir, dir_len); + if (add_slash) + file_name[dir_len] = DIRECTORY_SEPARATOR; + memcpy (file_name + dir_len + add_slash, base, base_len + 1); + } + } + + if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL) + /* Out of memory or file not found, treat it as empty. */ + cp = ""; + else + { + /* Parse the file's contents. */ + int c; + char buf1[50+1]; + char buf2[50+1]; + char *res_ptr = NULL; + size_t res_size = 0; + size_t l1, l2; + + for (;;) + { + c = getc (fp); + if (c == EOF) + break; + if (c == '\n' || c == ' ' || c == '\t') + continue; + if (c == '#') + { + /* Skip comment, to end of line. */ + do + c = getc (fp); + while (!(c == EOF || c == '\n')); + if (c == EOF) + break; + continue; + } + ungetc (c, fp); + if (fscanf (fp, "%50s %50s", buf1, buf2) < 2) + break; + l1 = strlen (buf1); + l2 = strlen (buf2); + if (res_size == 0) + { + res_size = l1 + 1 + l2 + 1; + res_ptr = (char *) malloc (res_size + 1); + } + else + { + res_size += l1 + 1 + l2 + 1; + res_ptr = (char *) realloc (res_ptr, res_size + 1); + } + if (res_ptr == NULL) + { + /* Out of memory. */ + res_size = 0; + break; + } + strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1); + strcpy (res_ptr + res_size - (l2 + 1), buf2); + } + fclose (fp); + if (res_size == 0) + cp = ""; + else + { + *(res_ptr + res_size) = '\0'; + cp = res_ptr; + } + } + + if (file_name != NULL) + free (file_name); + +#else + +# if defined VMS + /* To avoid the troubles of an extra file charset.alias_vms in the + sources of many GNU packages, simply inline the aliases here. */ + /* The list of encodings is taken from the OpenVMS 7.3-1 documentation + "Compaq C Run-Time Library Reference Manual for OpenVMS systems" + section 10.7 "Handling Different Character Sets". */ + cp = "ISO8859-1" "\0" "ISO-8859-1" "\0" + "ISO8859-2" "\0" "ISO-8859-2" "\0" + "ISO8859-5" "\0" "ISO-8859-5" "\0" + "ISO8859-7" "\0" "ISO-8859-7" "\0" + "ISO8859-8" "\0" "ISO-8859-8" "\0" + "ISO8859-9" "\0" "ISO-8859-9" "\0" + /* Japanese */ + "eucJP" "\0" "EUC-JP" "\0" + "SJIS" "\0" "SHIFT_JIS" "\0" + "DECKANJI" "\0" "DEC-KANJI" "\0" + "SDECKANJI" "\0" "EUC-JP" "\0" + /* Chinese */ + "eucTW" "\0" "EUC-TW" "\0" + "DECHANYU" "\0" "DEC-HANYU" "\0" + "DECHANZI" "\0" "GB2312" "\0" + /* Korean */ + "DECKOREAN" "\0" "EUC-KR" "\0"; +# endif + +# if defined WIN32 + /* To avoid the troubles of installing a separate file in the same + directory as the DLL and of retrieving the DLL's directory at + runtime, simply inline the aliases here. */ + + cp = "CP936" "\0" "GBK" "\0" + "CP1361" "\0" "JOHAB" "\0" + "CP20127" "\0" "ASCII" "\0" + "CP20866" "\0" "KOI8-R" "\0" + "CP21866" "\0" "KOI8-RU" "\0" + "CP28591" "\0" "ISO-8859-1" "\0" + "CP28592" "\0" "ISO-8859-2" "\0" + "CP28593" "\0" "ISO-8859-3" "\0" + "CP28594" "\0" "ISO-8859-4" "\0" + "CP28595" "\0" "ISO-8859-5" "\0" + "CP28596" "\0" "ISO-8859-6" "\0" + "CP28597" "\0" "ISO-8859-7" "\0" + "CP28598" "\0" "ISO-8859-8" "\0" + "CP28599" "\0" "ISO-8859-9" "\0" + "CP28605" "\0" "ISO-8859-15" "\0"; +# endif +#endif + + charset_aliases = cp; + } + + return cp; +} + +/* Determine the current locale's character encoding, and canonicalize it + into one of the canonical names listed in config.charset. + The result must not be freed; it is statically allocated. + If the canonical name cannot be determined, the result is a non-canonical + name. */ + +#ifdef STATIC +STATIC +#endif +const char * +locale_charset () +{ + const char *codeset; + const char *aliases; + +#if !(defined WIN32 || defined OS2) + +# if HAVE_LANGINFO_CODESET + + /* Most systems support nl_langinfo (CODESET) nowadays. */ + codeset = nl_langinfo (CODESET); + +# else + + /* On old systems which lack it, use setlocale or getenv. */ + const char *locale = NULL; + + /* But most old systems don't have a complete set of locales. Some + (like SunOS 4 or DJGPP) have only the C locale. Therefore we don't + use setlocale here; it would return "C" when it doesn't support the + locale name the user has set. */ +# if HAVE_SETLOCALE && 0 + locale = setlocale (LC_CTYPE, NULL); +# endif + if (locale == NULL || locale[0] == '\0') + { + locale = getenv ("LC_ALL"); + if (locale == NULL || locale[0] == '\0') + { + locale = getenv ("LC_CTYPE"); + if (locale == NULL || locale[0] == '\0') + locale = getenv ("LANG"); + } + } + + /* On some old systems, one used to set locale = "iso8859_1". On others, + you set it to "language_COUNTRY.charset". In any case, we resolve it + through the charset.alias file. */ + codeset = locale; + +# endif + +#elif defined WIN32 + + static char buf[2 + 10 + 1]; + + /* Woe32 has a function returning the locale's codepage as a number. */ + sprintf (buf, "CP%u", GetACP ()); + codeset = buf; + +#elif defined OS2 + + const char *locale; + static char buf[2 + 10 + 1]; + ULONG cp[3]; + ULONG cplen; + + /* Allow user to override the codeset, as set in the operating system, + with standard language environment variables. */ + locale = getenv ("LC_ALL"); + if (locale == NULL || locale[0] == '\0') + { + locale = getenv ("LC_CTYPE"); + if (locale == NULL || locale[0] == '\0') + locale = getenv ("LANG"); + } + if (locale != NULL && locale[0] != '\0') + { + /* If the locale name contains an encoding after the dot, return it. */ + const char *dot = strchr (locale, '.'); + + if (dot != NULL) + { + const char *modifier; + + dot++; + /* Look for the possible @... trailer and remove it, if any. */ + modifier = strchr (dot, '@'); + if (modifier == NULL) + return dot; + if (modifier - dot < sizeof (buf)) + { + memcpy (buf, dot, modifier - dot); + buf [modifier - dot] = '\0'; + return buf; + } + } + + /* Resolve through the charset.alias file. */ + codeset = locale; + } + else + { + /* OS/2 has a function returning the locale's codepage as a number. */ + if (DosQueryCp (sizeof (cp), cp, &cplen)) + codeset = ""; + else + { + sprintf (buf, "CP%u", cp[0]); + codeset = buf; + } + } + +#endif + + if (codeset == NULL) + /* The canonical name cannot be determined. */ + codeset = ""; + + /* Resolve alias. */ + for (aliases = get_charset_aliases (); + *aliases != '\0'; + aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1) + if (strcmp (codeset, aliases) == 0 + || (aliases[0] == '*' && aliases[1] == '\0')) + { + codeset = aliases + strlen (aliases) + 1; + break; + } + + /* Don't return an empty string. GNU libc and GNU libiconv interpret + the empty string as denoting "the locale's character encoding", + thus GNU libiconv would call this function a second time. */ + if (codeset[0] == '\0') + codeset = "ASCII"; + + return codeset; +} diff --git a/lib/intl/localcharset.h b/lib/intl/localcharset.h new file mode 100644 index 0000000..e529958 --- /dev/null +++ b/lib/intl/localcharset.h @@ -0,0 +1,43 @@ +/* localcharset.h - Determine a canonical name for the current locale's character encoding. */ + +/* Copyright (C) 2000-2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LOCALCHARSET_H +#define _LOCALCHARSET_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Determine the current locale's character encoding, and canonicalize it + into one of the canonical names listed in config.charset. + The result must not be freed; it is statically allocated. + If the canonical name cannot be determined, the result is a non-canonical + name. */ +extern const char * locale_charset (void); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _LOCALCHARSET_H */ diff --git a/lib/intl/locale.alias b/lib/intl/locale.alias new file mode 100644 index 0000000..51dd21e --- /dev/null +++ b/lib/intl/locale.alias @@ -0,0 +1,78 @@ +# locale.alias - Locale name alias data base. +# +# Copyright (C) 1996,1997,1998,1999,2000,2001,2005-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# + +# The format of this file is the same as for the corresponding file of +# the X Window System, which normally can be found in +# /usr/lib/X11/locale/locale.alias +# A single line contains two fields: an alias and a substitution value. +# All entries are case independent. + +# Note: This file is far from being complete. If you have a value for +# your own site which you think might be useful for others too, share +# it with the rest of us. Send it using the `glibcbug' script to +# bugs@gnu.org. + +# Packages using this file: + +bokmal no_NO.ISO-8859-1 +bokmål no_NO.ISO-8859-1 +catalan ca_ES.ISO-8859-1 +croatian hr_HR.ISO-8859-2 +czech cs_CZ.ISO-8859-2 +danish da_DK.ISO-8859-1 +dansk da_DK.ISO-8859-1 +deutsch de_DE.ISO-8859-1 +dutch nl_NL.ISO-8859-1 +eesti et_EE.ISO-8859-1 +estonian et_EE.ISO-8859-1 +finnish fi_FI.ISO-8859-1 +français fr_FR.ISO-8859-1 +french fr_FR.ISO-8859-1 +galego gl_ES.ISO-8859-1 +galician gl_ES.ISO-8859-1 +german de_DE.ISO-8859-1 +greek el_GR.ISO-8859-7 +hebrew he_IL.ISO-8859-8 +hrvatski hr_HR.ISO-8859-2 +hungarian hu_HU.ISO-8859-2 +icelandic is_IS.ISO-8859-1 +italian it_IT.ISO-8859-1 +japanese ja_JP.eucJP +japanese.euc ja_JP.eucJP +ja_JP ja_JP.eucJP +ja_JP.ujis ja_JP.eucJP +japanese.sjis ja_JP.SJIS +korean ko_KR.eucKR +korean.euc ko_KR.eucKR +ko_KR ko_KR.eucKR +lithuanian lt_LT.ISO-8859-13 +nb_NO no_NO.ISO-8859-1 +nb_NO.ISO-8859-1 no_NO.ISO-8859-1 +norwegian no_NO.ISO-8859-1 +nynorsk nn_NO.ISO-8859-1 +polish pl_PL.ISO-8859-2 +portuguese pt_PT.ISO-8859-1 +romanian ro_RO.ISO-8859-2 +russian ru_RU.ISO-8859-5 +slovak sk_SK.ISO-8859-2 +slovene sl_SI.ISO-8859-2 +slovenian sl_SI.ISO-8859-2 +spanish es_ES.ISO-8859-1 +swedish sv_SE.ISO-8859-1 +thai th_TH.TIS-620 +turkish tr_TR.ISO-8859-9 diff --git a/lib/intl/localealias.c b/lib/intl/localealias.c new file mode 100644 index 0000000..7a730db --- /dev/null +++ b/lib/intl/localealias.c @@ -0,0 +1,427 @@ +/* localealias.c - Handle aliases for locale names. */ + +/* Copyright (C) 1995-1999, 2000-2001, 2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tell glibc's <string.h> to provide a prototype for mempcpy(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <stdio.h> +#if defined _LIBC || defined HAVE___FSETLOCKING +# include <stdio_ext.h> +#endif +#include <sys/types.h> + +#ifdef __GNUC__ +# undef alloca +# define alloca __builtin_alloca +# define HAVE_ALLOCA 1 +#else +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca +# else +# if defined HAVE_ALLOCA_H || defined _LIBC +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#include <stdlib.h> +#include <string.h> + +#include "gettextP.h" + +#if ENABLE_RELOCATABLE +# include "relocatable.h" +#else +# define relocate(pathname) (pathname) +#endif + +/* @@ end of prolog @@ */ + +#ifdef _LIBC +/* Rename the non ANSI C functions. This is required by the standard + because some ANSI C functions will require linking with this object + file and the name space must not be polluted. */ +# define strcasecmp __strcasecmp + +# ifndef mempcpy +# define mempcpy __mempcpy +# endif +# define HAVE_MEMPCPY 1 +# define HAVE___FSETLOCKING 1 + +/* We need locking here since we can be called from different places. */ +# include <bits/libc-lock.h> + +__libc_lock_define_initialized (static, lock); +#endif + +#ifndef internal_function +# define internal_function +#endif + +/* Some optimizations for glibc. */ +#ifdef _LIBC +# define FEOF(fp) feof_unlocked (fp) +# define FGETS(buf, n, fp) fgets_unlocked (buf, n, fp) +#else +# define FEOF(fp) feof (fp) +# define FGETS(buf, n, fp) fgets (buf, n, fp) +#endif + +/* For those losing systems which don't have `alloca' we have to add + some additional code emulating it. */ +#ifdef HAVE_ALLOCA +# define freea(p) /* nothing */ +#else +# define alloca(n) malloc (n) +# define freea(p) free (p) +#endif + +#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED +# undef fgets +# define fgets(buf, len, s) fgets_unlocked (buf, len, s) +#endif +#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED +# undef feof +# define feof(s) feof_unlocked (s) +#endif + + +struct alias_map +{ + const char *alias; + const char *value; +}; + + +#ifndef _LIBC +# define libc_freeres_ptr(decl) decl +#endif + +libc_freeres_ptr (static char *string_space); +static size_t string_space_act; +static size_t string_space_max; +libc_freeres_ptr (static struct alias_map *map); +static size_t nmap; +static size_t maxmap; + + +/* Prototypes for local functions. */ +static size_t read_alias_file PARAMS ((const char *fname, int fname_len)) + internal_function; +static int extend_alias_table PARAMS ((void)); +static int alias_compare PARAMS ((const struct alias_map *map1, + const struct alias_map *map2)); + + +const char * +_nl_expand_alias (name) + const char *name; +{ + static const char *locale_alias_path; + struct alias_map *retval; + const char *result = NULL; + size_t added; + +#ifdef _LIBC + __libc_lock_lock (lock); +#endif + + if (locale_alias_path == NULL) + locale_alias_path = LOCALE_ALIAS_PATH; + + do + { + struct alias_map item; + + item.alias = name; + + if (nmap > 0) + retval = (struct alias_map *) bsearch (&item, map, nmap, + sizeof (struct alias_map), + (int (*) PARAMS ((const void *, + const void *)) + ) alias_compare); + else + retval = NULL; + + /* We really found an alias. Return the value. */ + if (retval != NULL) + { + result = retval->value; + break; + } + + /* Perhaps we can find another alias file. */ + added = 0; + while (added == 0 && locale_alias_path[0] != '\0') + { + const char *start; + + while (locale_alias_path[0] == PATH_SEPARATOR) + ++locale_alias_path; + start = locale_alias_path; + + while (locale_alias_path[0] != '\0' + && locale_alias_path[0] != PATH_SEPARATOR) + ++locale_alias_path; + + if (start < locale_alias_path) + added = read_alias_file (start, locale_alias_path - start); + } + } + while (added != 0); + +#ifdef _LIBC + __libc_lock_unlock (lock); +#endif + + return result; +} + + +static size_t +internal_function +read_alias_file (fname, fname_len) + const char *fname; + int fname_len; +{ + FILE *fp; + char *full_fname; + size_t added; + static const char aliasfile[] = "/locale.alias"; + + full_fname = (char *) alloca (fname_len + sizeof aliasfile); +#ifdef HAVE_MEMPCPY + mempcpy (mempcpy (full_fname, fname, fname_len), + aliasfile, sizeof aliasfile); +#else + memcpy (full_fname, fname, fname_len); + memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile); +#endif + + fp = fopen (relocate (full_fname), "r"); + freea (full_fname); + if (fp == NULL) + return 0; + +#ifdef HAVE___FSETLOCKING + /* No threads present. */ + __fsetlocking (fp, FSETLOCKING_BYCALLER); +#endif + + added = 0; + while (!FEOF (fp)) + { + /* It is a reasonable approach to use a fix buffer here because + a) we are only interested in the first two fields + b) these fields must be usable as file names and so must not + be that long + We avoid a multi-kilobyte buffer here since this would use up + stack space which we might not have if the program ran out of + memory. */ + char buf[400]; + char *alias; + char *value; + char *cp; + + if (FGETS (buf, sizeof buf, fp) == NULL) + /* EOF reached. */ + break; + + cp = buf; + /* Ignore leading white space. */ + while (isspace ((unsigned char) cp[0])) + ++cp; + + /* A leading '#' signals a comment line. */ + if (cp[0] != '\0' && cp[0] != '#') + { + alias = cp++; + while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) + ++cp; + /* Terminate alias name. */ + if (cp[0] != '\0') + *cp++ = '\0'; + + /* Now look for the beginning of the value. */ + while (isspace ((unsigned char) cp[0])) + ++cp; + + if (cp[0] != '\0') + { + size_t alias_len; + size_t value_len; + + value = cp++; + while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) + ++cp; + /* Terminate value. */ + if (cp[0] == '\n') + { + /* This has to be done to make the following test + for the end of line possible. We are looking for + the terminating '\n' which do not overwrite here. */ + *cp++ = '\0'; + *cp = '\n'; + } + else if (cp[0] != '\0') + *cp++ = '\0'; + + if (nmap >= maxmap) + if (__builtin_expect (extend_alias_table (), 0)) + { + fclose (fp); + return added; + } + + alias_len = strlen (alias) + 1; + value_len = strlen (value) + 1; + + if (string_space_act + alias_len + value_len > string_space_max) + { + /* Increase size of memory pool. */ + size_t new_size = (string_space_max + + (alias_len + value_len > 1024 + ? alias_len + value_len : 1024)); + char *new_pool = (char *) realloc (string_space, new_size); + if (new_pool == NULL) + { + fclose (fp); + return added; + } + + if (__builtin_expect (string_space != new_pool, 0)) + { + size_t i; + + for (i = 0; i < nmap; i++) + { + map[i].alias += new_pool - string_space; + map[i].value += new_pool - string_space; + } + } + + string_space = new_pool; + string_space_max = new_size; + } + + map[nmap].alias = memcpy (&string_space[string_space_act], + alias, alias_len); + string_space_act += alias_len; + + map[nmap].value = memcpy (&string_space[string_space_act], + value, value_len); + string_space_act += value_len; + + ++nmap; + ++added; + } + } + + /* Possibly not the whole line fits into the buffer. Ignore + the rest of the line. */ + while (strchr (buf, '\n') == NULL) + if (FGETS (buf, sizeof buf, fp) == NULL) + /* Make sure the inner loop will be left. The outer loop + will exit at the `feof' test. */ + break; + } + + /* Should we test for ferror()? I think we have to silently ignore + errors. --drepper */ + fclose (fp); + + if (added > 0) + qsort (map, nmap, sizeof (struct alias_map), + (int (*) PARAMS ((const void *, const void *))) alias_compare); + + return added; +} + + +static int +extend_alias_table () +{ + size_t new_size; + struct alias_map *new_map; + + new_size = maxmap == 0 ? 100 : 2 * maxmap; + new_map = (struct alias_map *) realloc (map, (new_size + * sizeof (struct alias_map))); + if (new_map == NULL) + /* Simply don't extend: we don't have any more core. */ + return -1; + + map = new_map; + maxmap = new_size; + return 0; +} + + +static int +alias_compare (map1, map2) + const struct alias_map *map1; + const struct alias_map *map2; +{ +#if defined _LIBC || defined HAVE_STRCASECMP + return strcasecmp (map1->alias, map2->alias); +#else + const unsigned char *p1 = (const unsigned char *) map1->alias; + const unsigned char *p2 = (const unsigned char *) map2->alias; + unsigned char c1, c2; + + if (p1 == p2) + return 0; + + do + { + /* I know this seems to be odd but the tolower() function in + some systems libc cannot handle nonalpha characters. */ + c1 = isupper (*p1) ? tolower (*p1) : *p1; + c2 = isupper (*p2) ? tolower (*p2) : *p2; + if (c1 == '\0') + break; + ++p1; + ++p2; + } + while (c1 == c2); + + return c1 - c2; +#endif +} diff --git a/lib/intl/localename.c b/lib/intl/localename.c new file mode 100644 index 0000000..795a34f --- /dev/null +++ b/lib/intl/localename.c @@ -0,0 +1,774 @@ +/* localename.c - Determine the current selected locale. */ + +/* Copyright (C) 1995-1999, 2000-2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Written by Ulrich Drepper <drepper@gnu.org>, 1995. */ +/* Win32 code written by Tor Lillqvist <tml@iki.fi>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <locale.h> + +#if defined _WIN32 || defined __WIN32__ +# undef WIN32 /* avoid warning on mingw32 */ +# define WIN32 +#endif + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +/* Mingw headers don't have latest language and sublanguage codes. */ +# ifndef LANG_AFRIKAANS +# define LANG_AFRIKAANS 0x36 +# endif +# ifndef LANG_ALBANIAN +# define LANG_ALBANIAN 0x1c +# endif +# ifndef LANG_ARABIC +# define LANG_ARABIC 0x01 +# endif +# ifndef LANG_ARMENIAN +# define LANG_ARMENIAN 0x2b +# endif +# ifndef LANG_ASSAMESE +# define LANG_ASSAMESE 0x4d +# endif +# ifndef LANG_AZERI +# define LANG_AZERI 0x2c +# endif +# ifndef LANG_BASQUE +# define LANG_BASQUE 0x2d +# endif +# ifndef LANG_BELARUSIAN +# define LANG_BELARUSIAN 0x23 +# endif +# ifndef LANG_BENGALI +# define LANG_BENGALI 0x45 +# endif +# ifndef LANG_CATALAN +# define LANG_CATALAN 0x03 +# endif +# ifndef LANG_DIVEHI +# define LANG_DIVEHI 0x65 +# endif +# ifndef LANG_ESTONIAN +# define LANG_ESTONIAN 0x25 +# endif +# ifndef LANG_FAEROESE +# define LANG_FAEROESE 0x38 +# endif +# ifndef LANG_FARSI +# define LANG_FARSI 0x29 +# endif +# ifndef LANG_GALICIAN +# define LANG_GALICIAN 0x56 +# endif +# ifndef LANG_GEORGIAN +# define LANG_GEORGIAN 0x37 +# endif +# ifndef LANG_GUJARATI +# define LANG_GUJARATI 0x47 +# endif +# ifndef LANG_HEBREW +# define LANG_HEBREW 0x0d +# endif +# ifndef LANG_HINDI +# define LANG_HINDI 0x39 +# endif +# ifndef LANG_INDONESIAN +# define LANG_INDONESIAN 0x21 +# endif +# ifndef LANG_KANNADA +# define LANG_KANNADA 0x4b +# endif +# ifndef LANG_KASHMIRI +# define LANG_KASHMIRI 0x60 +# endif +# ifndef LANG_KAZAK +# define LANG_KAZAK 0x3f +# endif +# ifndef LANG_KONKANI +# define LANG_KONKANI 0x57 +# endif +# ifndef LANG_KYRGYZ +# define LANG_KYRGYZ 0x40 +# endif +# ifndef LANG_LATVIAN +# define LANG_LATVIAN 0x26 +# endif +# ifndef LANG_LITHUANIAN +# define LANG_LITHUANIAN 0x27 +# endif +# ifndef LANG_MACEDONIAN +# define LANG_MACEDONIAN 0x2f +# endif +# ifndef LANG_MALAY +# define LANG_MALAY 0x3e +# endif +# ifndef LANG_MALAYALAM +# define LANG_MALAYALAM 0x4c +# endif +# ifndef LANG_MANIPURI +# define LANG_MANIPURI 0x58 +# endif +# ifndef LANG_MARATHI +# define LANG_MARATHI 0x4e +# endif +# ifndef LANG_MONGOLIAN +# define LANG_MONGOLIAN 0x50 +# endif +# ifndef LANG_NEPALI +# define LANG_NEPALI 0x61 +# endif +# ifndef LANG_ORIYA +# define LANG_ORIYA 0x48 +# endif +# ifndef LANG_PUNJABI +# define LANG_PUNJABI 0x46 +# endif +# ifndef LANG_SANSKRIT +# define LANG_SANSKRIT 0x4f +# endif +# ifndef LANG_SERBIAN +# define LANG_SERBIAN 0x1a +# endif +# ifndef LANG_SINDHI +# define LANG_SINDHI 0x59 +# endif +# ifndef LANG_SLOVAK +# define LANG_SLOVAK 0x1b +# endif +# ifndef LANG_SORBIAN +# define LANG_SORBIAN 0x2e +# endif +# ifndef LANG_SWAHILI +# define LANG_SWAHILI 0x41 +# endif +# ifndef LANG_SYRIAC +# define LANG_SYRIAC 0x5a +# endif +# ifndef LANG_TAMIL +# define LANG_TAMIL 0x49 +# endif +# ifndef LANG_TATAR +# define LANG_TATAR 0x44 +# endif +# ifndef LANG_TELUGU +# define LANG_TELUGU 0x4a +# endif +# ifndef LANG_THAI +# define LANG_THAI 0x1e +# endif +# ifndef LANG_UKRAINIAN +# define LANG_UKRAINIAN 0x22 +# endif +# ifndef LANG_URDU +# define LANG_URDU 0x20 +# endif +# ifndef LANG_UZBEK +# define LANG_UZBEK 0x43 +# endif +# ifndef LANG_VIETNAMESE +# define LANG_VIETNAMESE 0x2a +# endif +# ifndef SUBLANG_ARABIC_SAUDI_ARABIA +# define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 +# endif +# ifndef SUBLANG_ARABIC_IRAQ +# define SUBLANG_ARABIC_IRAQ 0x02 +# endif +# ifndef SUBLANG_ARABIC_EGYPT +# define SUBLANG_ARABIC_EGYPT 0x03 +# endif +# ifndef SUBLANG_ARABIC_LIBYA +# define SUBLANG_ARABIC_LIBYA 0x04 +# endif +# ifndef SUBLANG_ARABIC_ALGERIA +# define SUBLANG_ARABIC_ALGERIA 0x05 +# endif +# ifndef SUBLANG_ARABIC_MOROCCO +# define SUBLANG_ARABIC_MOROCCO 0x06 +# endif +# ifndef SUBLANG_ARABIC_TUNISIA +# define SUBLANG_ARABIC_TUNISIA 0x07 +# endif +# ifndef SUBLANG_ARABIC_OMAN +# define SUBLANG_ARABIC_OMAN 0x08 +# endif +# ifndef SUBLANG_ARABIC_YEMEN +# define SUBLANG_ARABIC_YEMEN 0x09 +# endif +# ifndef SUBLANG_ARABIC_SYRIA +# define SUBLANG_ARABIC_SYRIA 0x0a +# endif +# ifndef SUBLANG_ARABIC_JORDAN +# define SUBLANG_ARABIC_JORDAN 0x0b +# endif +# ifndef SUBLANG_ARABIC_LEBANON +# define SUBLANG_ARABIC_LEBANON 0x0c +# endif +# ifndef SUBLANG_ARABIC_KUWAIT +# define SUBLANG_ARABIC_KUWAIT 0x0d +# endif +# ifndef SUBLANG_ARABIC_UAE +# define SUBLANG_ARABIC_UAE 0x0e +# endif +# ifndef SUBLANG_ARABIC_BAHRAIN +# define SUBLANG_ARABIC_BAHRAIN 0x0f +# endif +# ifndef SUBLANG_ARABIC_QATAR +# define SUBLANG_ARABIC_QATAR 0x10 +# endif +# ifndef SUBLANG_AZERI_LATIN +# define SUBLANG_AZERI_LATIN 0x01 +# endif +# ifndef SUBLANG_AZERI_CYRILLIC +# define SUBLANG_AZERI_CYRILLIC 0x02 +# endif +# ifndef SUBLANG_CHINESE_MACAU +# define SUBLANG_CHINESE_MACAU 0x05 +# endif +# ifndef SUBLANG_ENGLISH_SOUTH_AFRICA +# define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 +# endif +# ifndef SUBLANG_ENGLISH_JAMAICA +# define SUBLANG_ENGLISH_JAMAICA 0x08 +# endif +# ifndef SUBLANG_ENGLISH_CARIBBEAN +# define SUBLANG_ENGLISH_CARIBBEAN 0x09 +# endif +# ifndef SUBLANG_ENGLISH_BELIZE +# define SUBLANG_ENGLISH_BELIZE 0x0a +# endif +# ifndef SUBLANG_ENGLISH_TRINIDAD +# define SUBLANG_ENGLISH_TRINIDAD 0x0b +# endif +# ifndef SUBLANG_ENGLISH_ZIMBABWE +# define SUBLANG_ENGLISH_ZIMBABWE 0x0c +# endif +# ifndef SUBLANG_ENGLISH_PHILIPPINES +# define SUBLANG_ENGLISH_PHILIPPINES 0x0d +# endif +# ifndef SUBLANG_FRENCH_LUXEMBOURG +# define SUBLANG_FRENCH_LUXEMBOURG 0x05 +# endif +# ifndef SUBLANG_FRENCH_MONACO +# define SUBLANG_FRENCH_MONACO 0x06 +# endif +# ifndef SUBLANG_GERMAN_LUXEMBOURG +# define SUBLANG_GERMAN_LUXEMBOURG 0x04 +# endif +# ifndef SUBLANG_GERMAN_LIECHTENSTEIN +# define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 +# endif +# ifndef SUBLANG_KASHMIRI_INDIA +# define SUBLANG_KASHMIRI_INDIA 0x02 +# endif +# ifndef SUBLANG_MALAY_MALAYSIA +# define SUBLANG_MALAY_MALAYSIA 0x01 +# endif +# ifndef SUBLANG_MALAY_BRUNEI_DARUSSALAM +# define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 +# endif +# ifndef SUBLANG_NEPALI_INDIA +# define SUBLANG_NEPALI_INDIA 0x02 +# endif +# ifndef SUBLANG_SERBIAN_LATIN +# define SUBLANG_SERBIAN_LATIN 0x02 +# endif +# ifndef SUBLANG_SERBIAN_CYRILLIC +# define SUBLANG_SERBIAN_CYRILLIC 0x03 +# endif +# ifndef SUBLANG_SPANISH_GUATEMALA +# define SUBLANG_SPANISH_GUATEMALA 0x04 +# endif +# ifndef SUBLANG_SPANISH_COSTA_RICA +# define SUBLANG_SPANISH_COSTA_RICA 0x05 +# endif +# ifndef SUBLANG_SPANISH_PANAMA +# define SUBLANG_SPANISH_PANAMA 0x06 +# endif +# ifndef SUBLANG_SPANISH_DOMINICAN_REPUBLIC +# define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 +# endif +# ifndef SUBLANG_SPANISH_VENEZUELA +# define SUBLANG_SPANISH_VENEZUELA 0x08 +# endif +# ifndef SUBLANG_SPANISH_COLOMBIA +# define SUBLANG_SPANISH_COLOMBIA 0x09 +# endif +# ifndef SUBLANG_SPANISH_PERU +# define SUBLANG_SPANISH_PERU 0x0a +# endif +# ifndef SUBLANG_SPANISH_ARGENTINA +# define SUBLANG_SPANISH_ARGENTINA 0x0b +# endif +# ifndef SUBLANG_SPANISH_ECUADOR +# define SUBLANG_SPANISH_ECUADOR 0x0c +# endif +# ifndef SUBLANG_SPANISH_CHILE +# define SUBLANG_SPANISH_CHILE 0x0d +# endif +# ifndef SUBLANG_SPANISH_URUGUAY +# define SUBLANG_SPANISH_URUGUAY 0x0e +# endif +# ifndef SUBLANG_SPANISH_PARAGUAY +# define SUBLANG_SPANISH_PARAGUAY 0x0f +# endif +# ifndef SUBLANG_SPANISH_BOLIVIA +# define SUBLANG_SPANISH_BOLIVIA 0x10 +# endif +# ifndef SUBLANG_SPANISH_EL_SALVADOR +# define SUBLANG_SPANISH_EL_SALVADOR 0x11 +# endif +# ifndef SUBLANG_SPANISH_HONDURAS +# define SUBLANG_SPANISH_HONDURAS 0x12 +# endif +# ifndef SUBLANG_SPANISH_NICARAGUA +# define SUBLANG_SPANISH_NICARAGUA 0x13 +# endif +# ifndef SUBLANG_SPANISH_PUERTO_RICO +# define SUBLANG_SPANISH_PUERTO_RICO 0x14 +# endif +# ifndef SUBLANG_SWEDISH_FINLAND +# define SUBLANG_SWEDISH_FINLAND 0x02 +# endif +# ifndef SUBLANG_URDU_PAKISTAN +# define SUBLANG_URDU_PAKISTAN 0x01 +# endif +# ifndef SUBLANG_URDU_INDIA +# define SUBLANG_URDU_INDIA 0x02 +# endif +# ifndef SUBLANG_UZBEK_LATIN +# define SUBLANG_UZBEK_LATIN 0x01 +# endif +# ifndef SUBLANG_UZBEK_CYRILLIC +# define SUBLANG_UZBEK_CYRILLIC 0x02 +# endif +#endif + +/* XPG3 defines the result of 'setlocale (category, NULL)' as: + "Directs 'setlocale()' to query 'category' and return the current + setting of 'local'." + However it does not specify the exact format. Neither do SUSV2 and + ISO C 99. So we can use this feature only on selected systems (e.g. + those using GNU C Library). */ +#if defined _LIBC || (defined __GNU_LIBRARY__ && __GNU_LIBRARY__ >= 2) +# define HAVE_LOCALE_NULL +#endif + +/* Determine the current locale's name, and canonicalize it into XPG syntax + language[_territory[.codeset]][@modifier] + The codeset part in the result is not reliable; the locale_charset() + should be used for codeset information instead. + The result must not be freed; it is statically allocated. */ + +const char * +_nl_locale_name (category, categoryname) + int category; + const char *categoryname; +{ + const char *retval; + +#ifndef WIN32 + + /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'. + On some systems this can be done by the 'setlocale' function itself. */ +# if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL + retval = setlocale (category, NULL); +# else + /* Setting of LC_ALL overwrites all other. */ + retval = getenv ("LC_ALL"); + if (retval == NULL || retval[0] == '\0') + { + /* Next comes the name of the desired category. */ + retval = getenv (categoryname); + if (retval == NULL || retval[0] == '\0') + { + /* Last possibility is the LANG environment variable. */ + retval = getenv ("LANG"); + if (retval == NULL || retval[0] == '\0') + /* We use C as the default domain. POSIX says this is + implementation defined. */ + retval = "C"; + } + } +# endif + + return retval; + +#else /* WIN32 */ + + /* Return an XPG style locale name language[_territory][@modifier]. + Don't even bother determining the codeset; it's not useful in this + context, because message catalogs are not specific to a single + codeset. */ + + LCID lcid; + LANGID langid; + int primary, sub; + + /* Let the user override the system settings through environment + variables, as on POSIX systems. */ + retval = getenv ("LC_ALL"); + if (retval != NULL && retval[0] != '\0') + return retval; + retval = getenv (categoryname); + if (retval != NULL && retval[0] != '\0') + return retval; + retval = getenv ("LANG"); + if (retval != NULL && retval[0] != '\0') + return retval; + + /* Use native Win32 API locale ID. */ + lcid = GetThreadLocale (); + + /* Strip off the sorting rules, keep only the language part. */ + langid = LANGIDFROMLCID (lcid); + + /* Split into language and territory part. */ + primary = PRIMARYLANGID (langid); + sub = SUBLANGID (langid); + + /* Dispatch on language. + See also http://www.unicode.org/unicode/onlinedat/languages.html . + For details about languages, see http://www.ethnologue.com/ . */ + switch (primary) + { + case LANG_AFRIKAANS: return "af_ZA"; + case LANG_ALBANIAN: return "sq_AL"; + case 0x5e: /* AMHARIC */ return "am_ET"; + case LANG_ARABIC: + switch (sub) + { + case SUBLANG_ARABIC_SAUDI_ARABIA: return "ar_SA"; + case SUBLANG_ARABIC_IRAQ: return "ar_IQ"; + case SUBLANG_ARABIC_EGYPT: return "ar_EG"; + case SUBLANG_ARABIC_LIBYA: return "ar_LY"; + case SUBLANG_ARABIC_ALGERIA: return "ar_DZ"; + case SUBLANG_ARABIC_MOROCCO: return "ar_MA"; + case SUBLANG_ARABIC_TUNISIA: return "ar_TN"; + case SUBLANG_ARABIC_OMAN: return "ar_OM"; + case SUBLANG_ARABIC_YEMEN: return "ar_YE"; + case SUBLANG_ARABIC_SYRIA: return "ar_SY"; + case SUBLANG_ARABIC_JORDAN: return "ar_JO"; + case SUBLANG_ARABIC_LEBANON: return "ar_LB"; + case SUBLANG_ARABIC_KUWAIT: return "ar_KW"; + case SUBLANG_ARABIC_UAE: return "ar_AE"; + case SUBLANG_ARABIC_BAHRAIN: return "ar_BH"; + case SUBLANG_ARABIC_QATAR: return "ar_QA"; + } + return "ar"; + case LANG_ARMENIAN: return "hy_AM"; + case LANG_ASSAMESE: return "as_IN"; + case LANG_AZERI: + switch (sub) + { + /* FIXME: Adjust this when Azerbaijani locales appear on Unix. */ + case SUBLANG_AZERI_LATIN: return "az_AZ@latin"; + case SUBLANG_AZERI_CYRILLIC: return "az_AZ@cyrillic"; + } + return "az"; + case LANG_BASQUE: + return "eu"; /* Ambiguous: could be "eu_ES" or "eu_FR". */ + case LANG_BELARUSIAN: return "be_BY"; + case LANG_BENGALI: return "bn_IN"; + case LANG_BULGARIAN: return "bg_BG"; + case 0x55: /* BURMESE */ return "my_MM"; + case 0x53: /* CAMBODIAN */ return "km_KH"; + case LANG_CATALAN: return "ca_ES"; + case 0x5c: /* CHEROKEE */ return "chr_US"; + case LANG_CHINESE: + switch (sub) + { + case SUBLANG_CHINESE_TRADITIONAL: return "zh_TW"; + case SUBLANG_CHINESE_SIMPLIFIED: return "zh_CN"; + case SUBLANG_CHINESE_HONGKONG: return "zh_HK"; + case SUBLANG_CHINESE_SINGAPORE: return "zh_SG"; + case SUBLANG_CHINESE_MACAU: return "zh_MO"; + } + return "zh"; + case LANG_CROATIAN: /* LANG_CROATIAN == LANG_SERBIAN + * What used to be called Serbo-Croatian + * should really now be two separate + * languages because of political reasons. + * (Says tml, who knows nothing about Serbian + * or Croatian.) + * (I can feel those flames coming already.) + */ + switch (sub) + { + case SUBLANG_DEFAULT: return "hr_HR"; + case SUBLANG_SERBIAN_LATIN: return "sr_YU"; + case SUBLANG_SERBIAN_CYRILLIC: return "sr_YU@cyrillic"; + } + return "hr"; + case LANG_CZECH: return "cs_CZ"; + case LANG_DANISH: return "da_DK"; + case LANG_DIVEHI: return "div_MV"; + case LANG_DUTCH: + switch (sub) + { + case SUBLANG_DUTCH: return "nl_NL"; + case SUBLANG_DUTCH_BELGIAN: /* FLEMISH, VLAAMS */ return "nl_BE"; + } + return "nl"; + case 0x66: /* EDO */ return "bin_NG"; + case LANG_ENGLISH: + switch (sub) + { + /* SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. Heh. I thought + * English was the language spoken in England. + * Oh well. + */ + case SUBLANG_ENGLISH_US: return "en_US"; + case SUBLANG_ENGLISH_UK: return "en_GB"; + case SUBLANG_ENGLISH_AUS: return "en_AU"; + case SUBLANG_ENGLISH_CAN: return "en_CA"; + case SUBLANG_ENGLISH_NZ: return "en_NZ"; + case SUBLANG_ENGLISH_EIRE: return "en_IE"; + case SUBLANG_ENGLISH_SOUTH_AFRICA: return "en_ZA"; + case SUBLANG_ENGLISH_JAMAICA: return "en_JM"; + case SUBLANG_ENGLISH_CARIBBEAN: return "en_GD"; /* Grenada? */ + case SUBLANG_ENGLISH_BELIZE: return "en_BZ"; + case SUBLANG_ENGLISH_TRINIDAD: return "en_TT"; + case SUBLANG_ENGLISH_ZIMBABWE: return "en_ZW"; + case SUBLANG_ENGLISH_PHILIPPINES: return "en_PH"; + } + return "en"; + case LANG_ESTONIAN: return "et_EE"; + case LANG_FAEROESE: return "fo_FO"; + case LANG_FARSI: return "fa_IR"; + case LANG_FINNISH: return "fi_FI"; + case LANG_FRENCH: + switch (sub) + { + case SUBLANG_FRENCH: return "fr_FR"; + case SUBLANG_FRENCH_BELGIAN: /* WALLOON */ return "fr_BE"; + case SUBLANG_FRENCH_CANADIAN: return "fr_CA"; + case SUBLANG_FRENCH_SWISS: return "fr_CH"; + case SUBLANG_FRENCH_LUXEMBOURG: return "fr_LU"; + case SUBLANG_FRENCH_MONACO: return "fr_MC"; + } + return "fr"; + case 0x62: /* FRISIAN */ return "fy_NL"; + case 0x67: /* FULFULDE */ return "ful_NG"; + case 0x3c: /* GAELIC */ + switch (sub) + { + case 0x01: /* SCOTTISH */ return "gd_GB"; + case 0x02: /* IRISH */ return "ga_IE"; + } + return "C"; + case LANG_GALICIAN: return "gl_ES"; + case LANG_GEORGIAN: return "ka_GE"; + case LANG_GERMAN: + switch (sub) + { + case SUBLANG_GERMAN: return "de_DE"; + case SUBLANG_GERMAN_SWISS: return "de_CH"; + case SUBLANG_GERMAN_AUSTRIAN: return "de_AT"; + case SUBLANG_GERMAN_LUXEMBOURG: return "de_LU"; + case SUBLANG_GERMAN_LIECHTENSTEIN: return "de_LI"; + } + return "de"; + case LANG_GREEK: return "el_GR"; + case 0x74: /* GUARANI */ return "gn_PY"; + case LANG_GUJARATI: return "gu_IN"; + case 0x68: /* HAUSA */ return "ha_NG"; + case 0x75: /* HAWAIIAN */ + /* FIXME: Do they mean Hawaiian ("haw_US", 1000 speakers) + or Hawaii Creole English ("cpe_US", 600000 speakers)? */ + return "cpe_US"; + case LANG_HEBREW: return "he_IL"; + case LANG_HINDI: return "hi_IN"; + case LANG_HUNGARIAN: return "hu_HU"; + case 0x69: /* IBIBIO */ return "nic_NG"; + case LANG_ICELANDIC: return "is_IS"; + case 0x70: /* IGBO */ return "ibo_NG"; + case LANG_INDONESIAN: return "id_ID"; + case 0x5d: /* INUKTITUT */ return "iu_CA"; + case LANG_ITALIAN: + switch (sub) + { + case SUBLANG_ITALIAN: return "it_IT"; + case SUBLANG_ITALIAN_SWISS: return "it_CH"; + } + return "it"; + case LANG_JAPANESE: return "ja_JP"; + case LANG_KANNADA: return "kn_IN"; + case 0x71: /* KANURI */ return "kau_NG"; + case LANG_KASHMIRI: + switch (sub) + { + case SUBLANG_DEFAULT: return "ks_PK"; + case SUBLANG_KASHMIRI_INDIA: return "ks_IN"; + } + return "ks"; + case LANG_KAZAK: return "kk_KZ"; + case LANG_KONKANI: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "kok_IN"; + case LANG_KOREAN: return "ko_KR"; + case LANG_KYRGYZ: return "ky_KG"; + case 0x54: /* LAO */ return "lo_LA"; + case 0x76: /* LATIN */ return "la_VA"; + case LANG_LATVIAN: return "lv_LV"; + case LANG_LITHUANIAN: return "lt_LT"; + case LANG_MACEDONIAN: return "mk_MK"; + case LANG_MALAY: + switch (sub) + { + case SUBLANG_MALAY_MALAYSIA: return "ms_MY"; + case SUBLANG_MALAY_BRUNEI_DARUSSALAM: return "ms_BN"; + } + return "ms"; + case LANG_MALAYALAM: return "ml_IN"; + case 0x3a: /* MALTESE */ return "mt_MT"; + case LANG_MANIPURI: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "mni_IN"; + case LANG_MARATHI: return "mr_IN"; + case LANG_MONGOLIAN: + return "mn"; /* Ambiguous: could be "mn_CN" or "mn_MN". */ + case LANG_NEPALI: + switch (sub) + { + case SUBLANG_DEFAULT: return "ne_NP"; + case SUBLANG_NEPALI_INDIA: return "ne_IN"; + } + return "ne"; + case LANG_NORWEGIAN: + switch (sub) + { + case SUBLANG_NORWEGIAN_BOKMAL: return "no_NO"; + case SUBLANG_NORWEGIAN_NYNORSK: return "nn_NO"; + } + return "no"; + case LANG_ORIYA: return "or_IN"; + case 0x72: /* OROMO */ return "om_ET"; + case 0x79: /* PAPIAMENTU */ return "pap_AN"; + case 0x63: /* PASHTO */ + return "ps"; /* Ambiguous: could be "ps_PK" or "ps_AF". */ + case LANG_POLISH: return "pl_PL"; + case LANG_PORTUGUESE: + switch (sub) + { + case SUBLANG_PORTUGUESE: return "pt_PT"; + /* Hmm. SUBLANG_PORTUGUESE_BRAZILIAN == SUBLANG_DEFAULT. + Same phenomenon as SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. */ + case SUBLANG_PORTUGUESE_BRAZILIAN: return "pt_BR"; + } + return "pt"; + case LANG_PUNJABI: return "pa_IN"; + case 0x17: /* RHAETO-ROMANCE */ return "rm_CH"; + case LANG_ROMANIAN: return "ro_RO"; + case LANG_RUSSIAN: + return "ru"; /* Ambiguous: could be "ru_RU" or "ru_UA". */ + case 0x3b: /* SAMI */ return "se_NO"; + case LANG_SANSKRIT: return "sa_IN"; + case LANG_SINDHI: return "sd"; + case 0x5b: /* SINHALESE */ return "si_LK"; + case LANG_SLOVAK: return "sk_SK"; + case LANG_SLOVENIAN: return "sl_SI"; + case 0x77: /* SOMALI */ return "so_SO"; + case LANG_SORBIAN: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "wen_DE"; + case LANG_SPANISH: + switch (sub) + { + case SUBLANG_SPANISH: return "es_ES"; + case SUBLANG_SPANISH_MEXICAN: return "es_MX"; + case SUBLANG_SPANISH_MODERN: + return "es_ES@modern"; /* not seen on Unix */ + case SUBLANG_SPANISH_GUATEMALA: return "es_GT"; + case SUBLANG_SPANISH_COSTA_RICA: return "es_CR"; + case SUBLANG_SPANISH_PANAMA: return "es_PA"; + case SUBLANG_SPANISH_DOMINICAN_REPUBLIC: return "es_DO"; + case SUBLANG_SPANISH_VENEZUELA: return "es_VE"; + case SUBLANG_SPANISH_COLOMBIA: return "es_CO"; + case SUBLANG_SPANISH_PERU: return "es_PE"; + case SUBLANG_SPANISH_ARGENTINA: return "es_AR"; + case SUBLANG_SPANISH_ECUADOR: return "es_EC"; + case SUBLANG_SPANISH_CHILE: return "es_CL"; + case SUBLANG_SPANISH_URUGUAY: return "es_UY"; + case SUBLANG_SPANISH_PARAGUAY: return "es_PY"; + case SUBLANG_SPANISH_BOLIVIA: return "es_BO"; + case SUBLANG_SPANISH_EL_SALVADOR: return "es_SV"; + case SUBLANG_SPANISH_HONDURAS: return "es_HN"; + case SUBLANG_SPANISH_NICARAGUA: return "es_NI"; + case SUBLANG_SPANISH_PUERTO_RICO: return "es_PR"; + } + return "es"; + case 0x30: /* SUTU */ return "bnt_TZ"; + case LANG_SWAHILI: return "sw_KE"; + case LANG_SWEDISH: + switch (sub) + { + case SUBLANG_DEFAULT: return "sv_SE"; + case SUBLANG_SWEDISH_FINLAND: return "sv_FI"; + } + return "sv"; + case LANG_SYRIAC: return "syr_TR"; /* An extinct language. */ + case 0x64: /* TAGALOG */ return "tl_PH"; + case 0x28: /* TAJIK */ return "tg_TJ"; + case 0x5f: /* TAMAZIGHT */ return "ber_MA"; + case LANG_TAMIL: + return "ta"; /* Ambiguous: could be "ta_IN" or "ta_LK" or "ta_SG". */ + case LANG_TATAR: return "tt_RU"; + case LANG_TELUGU: return "te_IN"; + case LANG_THAI: return "th_TH"; + case 0x51: /* TIBETAN */ return "bo_CN"; + case 0x73: /* TIGRINYA */ return "ti_ET"; + case 0x31: /* TSONGA */ return "ts_ZA"; + case LANG_TURKISH: return "tr_TR"; + case 0x42: /* TURKMEN */ return "tk_TM"; + case LANG_UKRAINIAN: return "uk_UA"; + case LANG_URDU: + switch (sub) + { + case SUBLANG_URDU_PAKISTAN: return "ur_PK"; + case SUBLANG_URDU_INDIA: return "ur_IN"; + } + return "ur"; + case LANG_UZBEK: + switch (sub) + { + /* FIXME: Adjust this when Uzbek locales appear on Unix. */ + case SUBLANG_UZBEK_LATIN: return "uz_UZ@latin"; + case SUBLANG_UZBEK_CYRILLIC: return "uz_UZ@cyrillic"; + } + return "uz"; + case 0x33: /* VENDA */ return "ven_ZA"; + case LANG_VIETNAMESE: return "vi_VN"; + case 0x52: /* WELSH */ return "cy_GB"; + case 0x34: /* XHOSA */ return "xh_ZA"; + case 0x78: /* YI */ return "sit_CN"; + case 0x3d: /* YIDDISH */ return "yi_IL"; + case 0x6a: /* YORUBA */ return "yo_NG"; + case 0x35: /* ZULU */ return "zu_ZA"; + default: return "C"; + } + +#endif +} diff --git a/lib/intl/log.c b/lib/intl/log.c new file mode 100644 index 0000000..e527e87 --- /dev/null +++ b/lib/intl/log.c @@ -0,0 +1,106 @@ +/* log.c - Log file output. */ + +/* Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Written by Bruno Haible <bruno@clisp.org>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Print an ASCII string with quotes and escape sequences where needed. */ +static void +print_escaped (stream, str) + FILE *stream; + const char *str; +{ + putc ('"', stream); + for (; *str != '\0'; str++) + if (*str == '\n') + { + fputs ("\\n\"", stream); + if (str[1] == '\0') + return; + fputs ("\n\"", stream); + } + else + { + if (*str == '"' || *str == '\\') + putc ('\\', stream); + putc (*str, stream); + } + putc ('"', stream); +} + +/* Add to the log file an entry denoting a failed translation. */ +void +_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural) + const char *logfilename; + const char *domainname; + const char *msgid1; + const char *msgid2; + int plural; +{ + static char *last_logfilename = NULL; + static FILE *last_logfile = NULL; + FILE *logfile; + + /* Can we reuse the last opened logfile? */ + if (last_logfilename == NULL || strcmp (logfilename, last_logfilename) != 0) + { + /* Close the last used logfile. */ + if (last_logfilename != NULL) + { + if (last_logfile != NULL) + { + fclose (last_logfile); + last_logfile = NULL; + } + free (last_logfilename); + last_logfilename = NULL; + } + /* Open the logfile. */ + last_logfilename = (char *) malloc (strlen (logfilename) + 1); + if (last_logfilename == NULL) + return; + strcpy (last_logfilename, logfilename); + last_logfile = fopen (logfilename, "a"); + if (last_logfile == NULL) + return; + } + logfile = last_logfile; + + fprintf (logfile, "domain "); + print_escaped (logfile, domainname); + fprintf (logfile, "\nmsgid "); + print_escaped (logfile, msgid1); + if (plural) + { + fprintf (logfile, "\nmsgid_plural "); + print_escaped (logfile, msgid2); + fprintf (logfile, "\nmsgstr[0] \"\"\n"); + } + else + fprintf (logfile, "\nmsgstr \"\"\n"); + putc ('\n', logfile); +} diff --git a/lib/intl/ngettext.c b/lib/intl/ngettext.c new file mode 100644 index 0000000..95aafcc --- /dev/null +++ b/lib/intl/ngettext.c @@ -0,0 +1,70 @@ +/* ngettext.c - Implementation of ngettext(3) function. */ + +/* Copyright (C) 1995, 1997, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _LIBC +# define __need_NULL +# include <stddef.h> +#else +# include <stdlib.h> /* Just for NULL. */ +#endif + +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif + +#include <locale.h> + +/* @@ end of prolog @@ */ + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define NGETTEXT __ngettext +# define DCNGETTEXT __dcngettext +#else +# define NGETTEXT libintl_ngettext +# define DCNGETTEXT libintl_dcngettext +#endif + +/* Look up MSGID in the current default message catalog for the current + LC_MESSAGES locale. If not found, returns MSGID itself (the default + text). */ +char * +NGETTEXT (msgid1, msgid2, n) + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return DCNGETTEXT (NULL, msgid1, msgid2, n, LC_MESSAGES); +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__ngettext, ngettext); +#endif diff --git a/lib/intl/os2compat.c b/lib/intl/os2compat.c new file mode 100644 index 0000000..1aa9dff --- /dev/null +++ b/lib/intl/os2compat.c @@ -0,0 +1,100 @@ +/* os2compat.c - OS/2 compatibility functions. */ + +/* Copyright (C) 2001-2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define OS2_AWARE +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> + +/* A version of getenv() that works from DLLs */ +extern unsigned long DosScanEnv (const unsigned char *pszName, unsigned char **ppszValue); + +char * +_nl_getenv (const char *name) +{ + unsigned char *value; + if (DosScanEnv (name, &value)) + return NULL; + else + return value; +} + +/* A fixed size buffer. */ +char libintl_nl_default_dirname[MAXPATHLEN+1]; + +char *_nlos2_libdir = NULL; +char *_nlos2_localealiaspath = NULL; +char *_nlos2_localedir = NULL; + +static __attribute__((constructor)) void +nlos2_initialize () +{ + char *root = getenv ("UNIXROOT"); + char *gnulocaledir = getenv ("GNULOCALEDIR"); + + _nlos2_libdir = gnulocaledir; + if (!_nlos2_libdir) + { + if (root) + { + size_t sl = strlen (root); + _nlos2_libdir = (char *) malloc (sl + strlen (LIBDIR) + 1); + memcpy (_nlos2_libdir, root, sl); + memcpy (_nlos2_libdir + sl, LIBDIR, strlen (LIBDIR) + 1); + } + else + _nlos2_libdir = LIBDIR; + } + + _nlos2_localealiaspath = gnulocaledir; + if (!_nlos2_localealiaspath) + { + if (root) + { + size_t sl = strlen (root); + _nlos2_localealiaspath = (char *) malloc (sl + strlen (LOCALE_ALIAS_PATH) + 1); + memcpy (_nlos2_localealiaspath, root, sl); + memcpy (_nlos2_localealiaspath + sl, LOCALE_ALIAS_PATH, strlen (LOCALE_ALIAS_PATH) + 1); + } + else + _nlos2_localealiaspath = LOCALE_ALIAS_PATH; + } + + _nlos2_localedir = gnulocaledir; + if (!_nlos2_localedir) + { + if (root) + { + size_t sl = strlen (root); + _nlos2_localedir = (char *) malloc (sl + strlen (LOCALEDIR) + 1); + memcpy (_nlos2_localedir, root, sl); + memcpy (_nlos2_localedir + sl, LOCALEDIR, strlen (LOCALEDIR) + 1); + } + else + _nlos2_localedir = LOCALEDIR; + } + + if (strlen (_nlos2_localedir) <= MAXPATHLEN) + strcpy (libintl_nl_default_dirname, _nlos2_localedir); +} diff --git a/lib/intl/os2compat.h b/lib/intl/os2compat.h new file mode 100644 index 0000000..8de3183 --- /dev/null +++ b/lib/intl/os2compat.h @@ -0,0 +1,48 @@ +/* os2compat.h - OS/2 compatibility defines. */ + +/* This file is intended to be included from config.h + Copyright (C) 2001-2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* When included from os2compat.h we need all the original definitions */ +#ifndef OS2_AWARE + +#undef LIBDIR +#define LIBDIR _nlos2_libdir +extern char *_nlos2_libdir; + +#undef LOCALEDIR +#define LOCALEDIR _nlos2_localedir +extern char *_nlos2_localedir; + +#undef LOCALE_ALIAS_PATH +#define LOCALE_ALIAS_PATH _nlos2_localealiaspath +extern char *_nlos2_localealiaspath; + +#endif + +#undef HAVE_STRCASECMP +#define HAVE_STRCASECMP 1 +#define strcasecmp stricmp +#define strncasecmp strnicmp + +/* We have our own getenv() which works even if library is compiled as DLL */ +#define getenv _nl_getenv + +/* Older versions of gettext used -1 as the value of LC_MESSAGES */ +#define LC_MESSAGES_COMPAT (-1) diff --git a/lib/intl/osdep.c b/lib/intl/osdep.c new file mode 100644 index 0000000..b030022 --- /dev/null +++ b/lib/intl/osdep.c @@ -0,0 +1,26 @@ +/* osdep.c - OS dependent parts of libintl. */ + +/* Copyright (C) 2001-2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined __EMX__ +# include "os2compat.c" +#else +/* Avoid AIX compiler warning. */ +typedef int dummy; +#endif diff --git a/lib/intl/plural-exp.c b/lib/intl/plural-exp.c new file mode 100644 index 0000000..9ed3b16 --- /dev/null +++ b/lib/intl/plural-exp.c @@ -0,0 +1,158 @@ +/* plural-exp.c - Expression parsing for plural form selection. */ + +/* Copyright (C) 2000, 2001, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@cygnus.com>, 2000. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "plural-exp.h" + +#if (defined __GNUC__ && !defined __APPLE_CC__) \ + || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + +/* These structs are the constant expression for the germanic plural + form determination. It represents the expression "n != 1". */ +static const struct expression plvar = +{ + .nargs = 0, + .operation = var, +}; +static const struct expression plone = +{ + .nargs = 0, + .operation = num, + .val = + { + .num = 1 + } +}; +struct expression GERMANIC_PLURAL = +{ + .nargs = 2, + .operation = not_equal, + .val = + { + .args = + { + [0] = (struct expression *) &plvar, + [1] = (struct expression *) &plone + } + } +}; + +# define INIT_GERMANIC_PLURAL() + +#else + +/* For compilers without support for ISO C 99 struct/union initializers: + Initialization at run-time. */ + +static struct expression plvar; +static struct expression plone; +struct expression GERMANIC_PLURAL; + +static void +init_germanic_plural () +{ + if (plone.val.num == 0) + { + plvar.nargs = 0; + plvar.operation = var; + + plone.nargs = 0; + plone.operation = num; + plone.val.num = 1; + + GERMANIC_PLURAL.nargs = 2; + GERMANIC_PLURAL.operation = not_equal; + GERMANIC_PLURAL.val.args[0] = &plvar; + GERMANIC_PLURAL.val.args[1] = &plone; + } +} + +# define INIT_GERMANIC_PLURAL() init_germanic_plural () + +#endif + +void +internal_function +EXTRACT_PLURAL_EXPRESSION (nullentry, pluralp, npluralsp) + const char *nullentry; + struct expression **pluralp; + unsigned long int *npluralsp; +{ + if (nullentry != NULL) + { + const char *plural; + const char *nplurals; + + plural = strstr (nullentry, "plural="); + nplurals = strstr (nullentry, "nplurals="); + if (plural == NULL || nplurals == NULL) + goto no_plural; + else + { + char *endp; + unsigned long int n; + struct parse_args args; + + /* First get the number. */ + nplurals += 9; + while (*nplurals != '\0' && isspace ((unsigned char) *nplurals)) + ++nplurals; + if (!(*nplurals >= '0' && *nplurals <= '9')) + goto no_plural; +#if defined HAVE_STRTOUL || defined _LIBC + n = strtoul (nplurals, &endp, 10); +#else + for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++) + n = n * 10 + (*endp - '0'); +#endif + if (nplurals == endp) + goto no_plural; + *npluralsp = n; + + /* Due to the restrictions bison imposes onto the interface of the + scanner function we have to put the input string and the result + passed up from the parser into the same structure which address + is passed down to the parser. */ + plural += 7; + args.cp = plural; + if (PLURAL_PARSE (&args) != 0) + goto no_plural; + *pluralp = args.res; + } + } + else + { + /* By default we are using the Germanic form: singular form only + for `one', the plural form otherwise. Yes, this is also what + English is using since English is a Germanic language. */ + no_plural: + INIT_GERMANIC_PLURAL (); + *pluralp = &GERMANIC_PLURAL; + *npluralsp = 2; + } +} diff --git a/lib/intl/plural-exp.h b/lib/intl/plural-exp.h new file mode 100644 index 0000000..dcb0dae --- /dev/null +++ b/lib/intl/plural-exp.h @@ -0,0 +1,128 @@ +/* plural-exp.h - defines for expression parsing and evaluation for plural form selection. */ + +/* Copyright (C) 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@cygnus.com>, 2000. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _PLURAL_EXP_H +#define _PLURAL_EXP_H + +#ifndef PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif +#endif + +#ifndef internal_function +# define internal_function +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + + +/* This is the representation of the expressions to determine the + plural form. */ +struct expression +{ + int nargs; /* Number of arguments. */ + enum operator + { + /* Without arguments: */ + var, /* The variable "n". */ + num, /* Decimal number. */ + /* Unary operators: */ + lnot, /* Logical NOT. */ + /* Binary operators: */ + mult, /* Multiplication. */ + divide, /* Division. */ + module, /* Modulo operation. */ + plus, /* Addition. */ + minus, /* Subtraction. */ + less_than, /* Comparison. */ + greater_than, /* Comparison. */ + less_or_equal, /* Comparison. */ + greater_or_equal, /* Comparison. */ + equal, /* Comparison for equality. */ + not_equal, /* Comparison for inequality. */ + land, /* Logical AND. */ + lor, /* Logical OR. */ + /* Ternary operators: */ + qmop /* Question mark operator. */ + } operation; + union + { + unsigned long int num; /* Number value for `num'. */ + struct expression *args[3]; /* Up to three arguments. */ + } val; +}; + +/* This is the data structure to pass information to the parser and get + the result in a thread-safe way. */ +struct parse_args +{ + const char *cp; + struct expression *res; +}; + + +/* Names for the libintl functions are a problem. This source code is used + 1. in the GNU C Library library, + 2. in the GNU libintl library, + 3. in the GNU gettext tools. + The function names in each situation must be different, to allow for + binary incompatible changes in 'struct expression'. Furthermore, + 1. in the GNU C Library library, the names have a __ prefix, + 2.+3. in the GNU libintl library and in the GNU gettext tools, the names + must follow ANSI C and not start with __. + So we have to distinguish the three cases. */ +#ifdef _LIBC +# define FREE_EXPRESSION __gettext_free_exp +# define PLURAL_PARSE __gettextparse +# define GERMANIC_PLURAL __gettext_germanic_plural +# define EXTRACT_PLURAL_EXPRESSION __gettext_extract_plural +#elif defined (IN_LIBINTL) +# define FREE_EXPRESSION libintl_gettext_free_exp +# define PLURAL_PARSE libintl_gettextparse +# define GERMANIC_PLURAL libintl_gettext_germanic_plural +# define EXTRACT_PLURAL_EXPRESSION libintl_gettext_extract_plural +#else +# define FREE_EXPRESSION free_plural_expression +# define PLURAL_PARSE parse_plural_expression +# define GERMANIC_PLURAL germanic_plural +# define EXTRACT_PLURAL_EXPRESSION extract_plural_expression +#endif + +extern void FREE_EXPRESSION PARAMS ((struct expression *exp)) + internal_function; +extern int PLURAL_PARSE PARAMS ((void *arg)); +extern struct expression GERMANIC_PLURAL attribute_hidden; +extern void EXTRACT_PLURAL_EXPRESSION PARAMS ((const char *nullentry, + struct expression **pluralp, + unsigned long int *npluralsp)) + internal_function; + +#if !defined (_LIBC) && !defined (IN_LIBINTL) +extern unsigned long int plural_eval PARAMS ((struct expression *pexp, + unsigned long int n)); +#endif + +#endif /* _PLURAL_EXP_H */ diff --git a/lib/intl/plural.c b/lib/intl/plural.c new file mode 100644 index 0000000..a003151 --- /dev/null +++ b/lib/intl/plural.c @@ -0,0 +1,1679 @@ +/* A Bison parser, made by GNU Bison 2.0. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse __gettextparse +#define yylex __gettextlex +#define yyerror __gettexterror +#define yylval __gettextlval +#define yychar __gettextchar +#define yydebug __gettextdebug +#define yynerrs __gettextnerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + EQUOP2 = 258, + CMPOP2 = 259, + ADDOP2 = 260, + MULOP2 = 261, + NUMBER = 262 + }; +#endif +#define EQUOP2 258 +#define CMPOP2 259 +#define ADDOP2 260 +#define MULOP2 261 +#define NUMBER 262 + + + + +/* Copy the first part of user declarations. */ +#line 1 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + +/* plural.y - Expression parsing for plural form selection. */ + +/* Copyright (C) 2000, 2001, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@cygnus.com>, 2000. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* The bison generated parser uses alloca. AIX 3 forces us to put this + declaration at the beginning of the file. The declaration in bison's + skeleton file comes too late. This must come before <config.h> + because <config.h> may include arbitrary system headers. */ +#if defined _AIX && !defined __GNUC__ + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stddef.h> +#include <stdlib.h> +#include "plural-exp.h" + +/* The main function generated by the parser is called __gettextparse, + but we want it to be called PLURAL_PARSE. */ +#ifndef _LIBC +# define __gettextparse PLURAL_PARSE +#endif + +#define YYLEX_PARAM &((struct parse_args *) arg)->cp +#define YYPARSE_PARAM arg + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 51 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" +typedef union YYSTYPE { + unsigned long int num; + enum operator op; + struct expression *exp; +} YYSTYPE; +/* Line 190 of yacc.c. */ +#line 152 "/usr/src/local/bash/bash-20080814/lib/intl/plural.c" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ +#line 57 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + +/* Prototypes for local functions. */ +static struct expression *new_exp PARAMS ((int nargs, enum operator op, + struct expression * const *args)); +static inline struct expression *new_exp_0 PARAMS ((enum operator op)); +static inline struct expression *new_exp_1 PARAMS ((enum operator op, + struct expression *right)); +static struct expression *new_exp_2 PARAMS ((enum operator op, + struct expression *left, + struct expression *right)); +static inline struct expression *new_exp_3 PARAMS ((enum operator op, + struct expression *bexp, + struct expression *tbranch, + struct expression *fbranch)); +static int yylex PARAMS ((YYSTYPE *lval, const char **pexp)); +static void yyerror PARAMS ((const char *str)); + +/* Allocation of expressions. */ + +static struct expression * +new_exp (nargs, op, args) + int nargs; + enum operator op; + struct expression * const *args; +{ + int i; + struct expression *newp; + + /* If any of the argument could not be malloc'ed, just return NULL. */ + for (i = nargs - 1; i >= 0; i--) + if (args[i] == NULL) + goto fail; + + /* Allocate a new expression. */ + newp = (struct expression *) malloc (sizeof (*newp)); + if (newp != NULL) + { + newp->nargs = nargs; + newp->operation = op; + for (i = nargs - 1; i >= 0; i--) + newp->val.args[i] = args[i]; + return newp; + } + + fail: + for (i = nargs - 1; i >= 0; i--) + FREE_EXPRESSION (args[i]); + + return NULL; +} + +static inline struct expression * +new_exp_0 (op) + enum operator op; +{ + return new_exp (0, op, NULL); +} + +static inline struct expression * +new_exp_1 (op, right) + enum operator op; + struct expression *right; +{ + struct expression *args[1]; + + args[0] = right; + return new_exp (1, op, args); +} + +static struct expression * +new_exp_2 (op, left, right) + enum operator op; + struct expression *left; + struct expression *right; +{ + struct expression *args[2]; + + args[0] = left; + args[1] = right; + return new_exp (2, op, args); +} + +static inline struct expression * +new_exp_3 (op, bexp, tbranch, fbranch) + enum operator op; + struct expression *bexp; + struct expression *tbranch; + struct expression *fbranch; +{ + struct expression *args[3]; + + args[0] = bexp; + args[1] = tbranch; + args[2] = fbranch; + return new_exp (3, op, args); +} + + + +/* Line 213 of yacc.c. */ +#line 262 "/usr/src/local/bash/bash-20080814/lib/intl/plural.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# else +# define YYSTACK_ALLOC alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short int yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short int yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 9 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 54 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 16 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 3 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 13 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 27 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 262 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 10, 2, 2, 2, 2, 5, 2, + 14, 15, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 12, 2, + 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 6, 7, + 8, 9, 11 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 5, 11, 15, 19, 23, 27, 31, + 35, 38, 40, 42 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 17, 0, -1, 18, -1, 18, 3, 18, 12, 18, + -1, 18, 4, 18, -1, 18, 5, 18, -1, 18, + 6, 18, -1, 18, 7, 18, -1, 18, 8, 18, + -1, 18, 9, 18, -1, 10, 18, -1, 13, -1, + 11, -1, 14, 18, 15, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned char yyrline[] = +{ + 0, 176, 176, 184, 188, 192, 196, 200, 204, 208, + 212, 216, 220, 225 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "'?'", "'|'", "'&'", "EQUOP2", "CMPOP2", + "ADDOP2", "MULOP2", "'!'", "NUMBER", "':'", "'n'", "'('", "')'", + "$accept", "start", "exp", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short int yytoknum[] = +{ + 0, 256, 257, 63, 124, 38, 258, 259, 260, 261, + 33, 262, 58, 110, 40, 41 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 16, 17, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 1, 5, 3, 3, 3, 3, 3, 3, + 2, 1, 1, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 0, 0, 12, 11, 0, 0, 2, 10, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 13, 0, 4, + 5, 6, 7, 8, 9, 0, 3 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 5, 6 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -10 +static const yysigned_char yypact[] = +{ + -9, -9, -10, -10, -9, 8, 36, -10, 13, -10, + -9, -9, -9, -9, -9, -9, -9, -10, 26, 41, + 45, 18, -2, 14, -10, -9, 36 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -10, -10, -1 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const unsigned char yytable[] = +{ + 7, 1, 2, 8, 3, 4, 15, 16, 9, 18, + 19, 20, 21, 22, 23, 24, 10, 11, 12, 13, + 14, 15, 16, 16, 26, 14, 15, 16, 17, 10, + 11, 12, 13, 14, 15, 16, 0, 0, 25, 10, + 11, 12, 13, 14, 15, 16, 12, 13, 14, 15, + 16, 13, 14, 15, 16 +}; + +static const yysigned_char yycheck[] = +{ + 1, 10, 11, 4, 13, 14, 8, 9, 0, 10, + 11, 12, 13, 14, 15, 16, 3, 4, 5, 6, + 7, 8, 9, 9, 25, 7, 8, 9, 15, 3, + 4, 5, 6, 7, 8, 9, -1, -1, 12, 3, + 4, 5, 6, 7, 8, 9, 5, 6, 7, 8, + 9, 6, 7, 8, 9 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 10, 11, 13, 14, 17, 18, 18, 18, 0, + 3, 4, 5, 6, 7, 8, 9, 15, 18, 18, + 18, 18, 18, 18, 18, 12, 18 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short int *bottom, short int *top) +#else +static void +yy_stack_print (bottom, top) + short int *bottom; + short int *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short int yyssa[YYINITDEPTH]; + short int *yyss = yyssa; + register short int *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + + yyvsp[0] = yylval; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short int *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short int *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a look-ahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to look-ahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 177 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + if ((yyvsp[0].exp) == NULL) + YYABORT; + ((struct parse_args *) arg)->res = (yyvsp[0].exp); + } + break; + + case 3: +#line 185 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_3 (qmop, (yyvsp[-4].exp), (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 4: +#line 189 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 (lor, (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 5: +#line 193 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 (land, (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 6: +#line 197 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 ((yyvsp[-1].op), (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 7: +#line 201 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 ((yyvsp[-1].op), (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 8: +#line 205 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 ((yyvsp[-1].op), (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 9: +#line 209 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_2 ((yyvsp[-1].op), (yyvsp[-2].exp), (yyvsp[0].exp)); + } + break; + + case 10: +#line 213 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_1 (lnot, (yyvsp[0].exp)); + } + break; + + case 11: +#line 217 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = new_exp_0 (var); + } + break; + + case 12: +#line 221 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + if (((yyval.exp) = new_exp_0 (num)) != NULL) + (yyval.exp)->val.num = (yyvsp[0].num); + } + break; + + case 13: +#line 226 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + { + (yyval.exp) = (yyvsp[-1].exp); + } + break; + + + } + +/* Line 1037 of yacc.c. */ +#line 1270 "/usr/src/local/bash/bash-20080814/lib/intl/plural.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + yydestruct ("Error: popping", + yystos[*yyssp], yyvsp); + } + } + else + { + yydestruct ("Error: discarding", yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + +yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yydestruct ("Error: discarding lookahead", + yytoken, &yylval); + yychar = YYEMPTY; + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 231 "/usr/src/local/bash/bash-20080814/lib/intl/plural.y" + + +void +internal_function +FREE_EXPRESSION (exp) + struct expression *exp; +{ + if (exp == NULL) + return; + + /* Handle the recursive case. */ + switch (exp->nargs) + { + case 3: + FREE_EXPRESSION (exp->val.args[2]); + /* FALLTHROUGH */ + case 2: + FREE_EXPRESSION (exp->val.args[1]); + /* FALLTHROUGH */ + case 1: + FREE_EXPRESSION (exp->val.args[0]); + /* FALLTHROUGH */ + default: + break; + } + + free (exp); +} + + +static int +yylex (lval, pexp) + YYSTYPE *lval; + const char **pexp; +{ + const char *exp = *pexp; + int result; + + while (1) + { + if (exp[0] == '\0') + { + *pexp = exp; + return YYEOF; + } + + if (exp[0] != ' ' && exp[0] != '\t') + break; + + ++exp; + } + + result = *exp++; + switch (result) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + unsigned long int n = result - '0'; + while (exp[0] >= '0' && exp[0] <= '9') + { + n *= 10; + n += exp[0] - '0'; + ++exp; + } + lval->num = n; + result = NUMBER; + } + break; + + case '=': + if (exp[0] == '=') + { + ++exp; + lval->op = equal; + result = EQUOP2; + } + else + result = YYERRCODE; + break; + + case '!': + if (exp[0] == '=') + { + ++exp; + lval->op = not_equal; + result = EQUOP2; + } + break; + + case '&': + case '|': + if (exp[0] == result) + ++exp; + else + result = YYERRCODE; + break; + + case '<': + if (exp[0] == '=') + { + ++exp; + lval->op = less_or_equal; + } + else + lval->op = less_than; + result = CMPOP2; + break; + + case '>': + if (exp[0] == '=') + { + ++exp; + lval->op = greater_or_equal; + } + else + lval->op = greater_than; + result = CMPOP2; + break; + + case '*': + lval->op = mult; + result = MULOP2; + break; + + case '/': + lval->op = divide; + result = MULOP2; + break; + + case '%': + lval->op = module; + result = MULOP2; + break; + + case '+': + lval->op = plus; + result = ADDOP2; + break; + + case '-': + lval->op = minus; + result = ADDOP2; + break; + + case 'n': + case '?': + case ':': + case '(': + case ')': + /* Nothing, just return the character. */ + break; + + case ';': + case '\n': + case '\0': + /* Be safe and let the user call this function again. */ + --exp; + result = YYEOF; + break; + + default: + result = YYERRCODE; +#if YYDEBUG != 0 + --exp; +#endif + break; + } + + *pexp = exp; + + return result; +} + + +static void +yyerror (str) + const char *str; +{ + /* Do nothing. We don't print error messages here. */ +} diff --git a/lib/intl/plural.y b/lib/intl/plural.y new file mode 100644 index 0000000..8b8cec7 --- /dev/null +++ b/lib/intl/plural.y @@ -0,0 +1,411 @@ +%{ +/* plural.y - Expression parsing for plural form selection. */ + +/* Copyright (C) 2000, 2001, 2005-2009 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@cygnus.com>, 2000. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* The bison generated parser uses alloca. AIX 3 forces us to put this + declaration at the beginning of the file. The declaration in bison's + skeleton file comes too late. This must come before <config.h> + because <config.h> may include arbitrary system headers. */ +#if defined _AIX && !defined __GNUC__ + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stddef.h> +#include <stdlib.h> +#include "plural-exp.h" + +/* The main function generated by the parser is called __gettextparse, + but we want it to be called PLURAL_PARSE. */ +#ifndef _LIBC +# define __gettextparse PLURAL_PARSE +#endif + +#define YYLEX_PARAM &((struct parse_args *) arg)->cp +#define YYPARSE_PARAM arg +%} +%pure_parser +%expect 7 + +%union { + unsigned long int num; + enum operator op; + struct expression *exp; +} + +%{ +/* Prototypes for local functions. */ +static struct expression *new_exp PARAMS ((int nargs, enum operator op, + struct expression * const *args)); +static inline struct expression *new_exp_0 PARAMS ((enum operator op)); +static inline struct expression *new_exp_1 PARAMS ((enum operator op, + struct expression *right)); +static struct expression *new_exp_2 PARAMS ((enum operator op, + struct expression *left, + struct expression *right)); +static inline struct expression *new_exp_3 PARAMS ((enum operator op, + struct expression *bexp, + struct expression *tbranch, + struct expression *fbranch)); +static int yylex PARAMS ((YYSTYPE *lval, const char **pexp)); +static void yyerror PARAMS ((const char *str)); + +/* Allocation of expressions. */ + +static struct expression * +new_exp (nargs, op, args) + int nargs; + enum operator op; + struct expression * const *args; +{ + int i; + struct expression *newp; + + /* If any of the argument could not be malloc'ed, just return NULL. */ + for (i = nargs - 1; i >= 0; i--) + if (args[i] == NULL) + goto fail; + + /* Allocate a new expression. */ + newp = (struct expression *) malloc (sizeof (*newp)); + if (newp != NULL) + { + newp->nargs = nargs; + newp->operation = op; + for (i = nargs - 1; i >= 0; i--) + newp->val.args[i] = args[i]; + return newp; + } + + fail: + for (i = nargs - 1; i >= 0; i--) + FREE_EXPRESSION (args[i]); + + return NULL; +} + +static inline struct expression * +new_exp_0 (op) + enum operator op; +{ + return new_exp (0, op, NULL); +} + +static inline struct expression * +new_exp_1 (op, right) + enum operator op; + struct expression *right; +{ + struct expression *args[1]; + + args[0] = right; + return new_exp (1, op, args); +} + +static struct expression * +new_exp_2 (op, left, right) + enum operator op; + struct expression *left; + struct expression *right; +{ + struct expression *args[2]; + + args[0] = left; + args[1] = right; + return new_exp (2, op, args); +} + +static inline struct expression * +new_exp_3 (op, bexp, tbranch, fbranch) + enum operator op; + struct expression *bexp; + struct expression *tbranch; + struct expression *fbranch; +{ + struct expression *args[3]; + + args[0] = bexp; + args[1] = tbranch; + args[2] = fbranch; + return new_exp (3, op, args); +} + +%} + +/* This declares that all operators have the same associativity and the + precedence order as in C. See [Harbison, Steele: C, A Reference Manual]. + There is no unary minus and no bitwise operators. + Operators with the same syntactic behaviour have been merged into a single + token, to save space in the array generated by bison. */ +%right '?' /* ? */ +%left '|' /* || */ +%left '&' /* && */ +%left EQUOP2 /* == != */ +%left CMPOP2 /* < > <= >= */ +%left ADDOP2 /* + - */ +%left MULOP2 /* * / % */ +%right '!' /* ! */ + +%token <op> EQUOP2 CMPOP2 ADDOP2 MULOP2 +%token <num> NUMBER +%type <exp> exp + +%% + +start: exp + { + if ($1 == NULL) + YYABORT; + ((struct parse_args *) arg)->res = $1; + } + ; + +exp: exp '?' exp ':' exp + { + $$ = new_exp_3 (qmop, $1, $3, $5); + } + | exp '|' exp + { + $$ = new_exp_2 (lor, $1, $3); + } + | exp '&' exp + { + $$ = new_exp_2 (land, $1, $3); + } + | exp EQUOP2 exp + { + $$ = new_exp_2 ($2, $1, $3); + } + | exp CMPOP2 exp + { + $$ = new_exp_2 ($2, $1, $3); + } + | exp ADDOP2 exp + { + $$ = new_exp_2 ($2, $1, $3); + } + | exp MULOP2 exp + { + $$ = new_exp_2 ($2, $1, $3); + } + | '!' exp + { + $$ = new_exp_1 (lnot, $2); + } + | 'n' + { + $$ = new_exp_0 (var); + } + | NUMBER + { + if (($$ = new_exp_0 (num)) != NULL) + $$->val.num = $1; + } + | '(' exp ')' + { + $$ = $2; + } + ; + +%% + +void +internal_function +FREE_EXPRESSION (exp) + struct expression *exp; +{ + if (exp == NULL) + return; + + /* Handle the recursive case. */ + switch (exp->nargs) + { + case 3: + FREE_EXPRESSION (exp->val.args[2]); + /* FALLTHROUGH */ + case 2: + FREE_EXPRESSION (exp->val.args[1]); + /* FALLTHROUGH */ + case 1: + FREE_EXPRESSION (exp->val.args[0]); + /* FALLTHROUGH */ + default: + break; + } + + free (exp); +} + + +static int +yylex (lval, pexp) + YYSTYPE *lval; + const char **pexp; +{ + const char *exp = *pexp; + int result; + + while (1) + { + if (exp[0] == '\0') + { + *pexp = exp; + return YYEOF; + } + + if (exp[0] != ' ' && exp[0] != '\t') + break; + + ++exp; + } + + result = *exp++; + switch (result) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + unsigned long int n = result - '0'; + while (exp[0] >= '0' && exp[0] <= '9') + { + n *= 10; + n += exp[0] - '0'; + ++exp; + } + lval->num = n; + result = NUMBER; + } + break; + + case '=': + if (exp[0] == '=') + { + ++exp; + lval->op = equal; + result = EQUOP2; + } + else + result = YYERRCODE; + break; + + case '!': + if (exp[0] == '=') + { + ++exp; + lval->op = not_equal; + result = EQUOP2; + } + break; + + case '&': + case '|': + if (exp[0] == result) + ++exp; + else + result = YYERRCODE; + break; + + case '<': + if (exp[0] == '=') + { + ++exp; + lval->op = less_or_equal; + } + else + lval->op = less_than; + result = CMPOP2; + break; + + case '>': + if (exp[0] == '=') + { + ++exp; + lval->op = greater_or_equal; + } + else + lval->op = greater_than; + result = CMPOP2; + break; + + case '*': + lval->op = mult; + result = MULOP2; + break; + + case '/': + lval->op = divide; + result = MULOP2; + break; + + case '%': + lval->op = module; + result = MULOP2; + break; + + case '+': + lval->op = plus; + result = ADDOP2; + break; + + case '-': + lval->op = minus; + result = ADDOP2; + break; + + case 'n': + case '?': + case ':': + case '(': + case ')': + /* Nothing, just return the character. */ + break; + + case ';': + case '\n': + case '\0': + /* Be safe and let the user call this function again. */ + --exp; + result = YYEOF; + break; + + default: + result = YYERRCODE; +#if YYDEBUG != 0 + --exp; +#endif + break; + } + + *pexp = exp; + + return result; +} + + +static void +yyerror (str) + const char *str; +{ + /* Do nothing. We don't print error messages here. */ +} diff --git a/lib/intl/ref-add.sin b/lib/intl/ref-add.sin new file mode 100644 index 0000000..6fd32ed --- /dev/null +++ b/lib/intl/ref-add.sin @@ -0,0 +1,29 @@ +# Add this package to a list of references stored in a text file. +# +# Copyright (C) 2000 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# +# Written by Bruno Haible <haible@clisp.cons.org>. +# +/^# Packages using this file: / { + s/# Packages using this file:// + ta + :a + s/ @PACKAGE@ / @PACKAGE@ / + tb + s/ $/ @PACKAGE@ / + :b + s/^/# Packages using this file:/ +} diff --git a/lib/intl/ref-del.sin b/lib/intl/ref-del.sin new file mode 100644 index 0000000..65ce9af --- /dev/null +++ b/lib/intl/ref-del.sin @@ -0,0 +1,24 @@ +# Remove this package from a list of references stored in a text file. +# +# Copyright (C) 2000 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# +# Written by Bruno Haible <haible@clisp.cons.org>. +# +/^# Packages using this file: / { + s/# Packages using this file:// + s/ @PACKAGE@ / / + s/^/# Packages using this file:/ +} diff --git a/lib/intl/relocatable.c b/lib/intl/relocatable.c new file mode 100644 index 0000000..34b2b18 --- /dev/null +++ b/lib/intl/relocatable.c @@ -0,0 +1,440 @@ +/* relocatable.c - Provide relocatable packages. */ + +/* Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2003. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tell glibc's <stdio.h> to provide a prototype for getline(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Specification. */ +#include "relocatable.h" + +#if ENABLE_RELOCATABLE + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef NO_XMALLOC +# define xmalloc malloc +#else +# include "xmalloc.h" +#endif + +#if DEPENDS_ON_LIBCHARSET +# include <libcharset.h> +#endif +#if DEPENDS_ON_LIBICONV && HAVE_ICONV +# include <iconv.h> +#endif +#if DEPENDS_ON_LIBINTL && ENABLE_NLS +# include <libintl.h> +#endif + +/* Faked cheap 'bool'. */ +#undef bool +#undef false +#undef true +#define bool int +#define false 0 +#define true 1 + +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. + */ +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +# define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ + && (P)[1] == ':') +# define IS_PATH_WITH_DIR(P) \ + (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) +# define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0) +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) +# define FILESYSTEM_PREFIX_LEN(P) 0 +#endif + +/* Original installation prefix. */ +static char *orig_prefix; +static size_t orig_prefix_len; +/* Current installation prefix. */ +static char *curr_prefix; +static size_t curr_prefix_len; +/* These prefixes do not end in a slash. Anything that will be concatenated + to them must start with a slash. */ + +/* Sets the original and the current installation prefix of this module. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +static void +set_this_relocation_prefix (const char *orig_prefix_arg, + const char *curr_prefix_arg) +{ + if (orig_prefix_arg != NULL && curr_prefix_arg != NULL + /* Optimization: if orig_prefix and curr_prefix are equal, the + relocation is a nop. */ + && strcmp (orig_prefix_arg, curr_prefix_arg) != 0) + { + /* Duplicate the argument strings. */ + char *memory; + + orig_prefix_len = strlen (orig_prefix_arg); + curr_prefix_len = strlen (curr_prefix_arg); + memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1); +#ifdef NO_XMALLOC + if (memory != NULL) +#endif + { + memcpy (memory, orig_prefix_arg, orig_prefix_len + 1); + orig_prefix = memory; + memory += orig_prefix_len + 1; + memcpy (memory, curr_prefix_arg, curr_prefix_len + 1); + curr_prefix = memory; + return; + } + } + orig_prefix = NULL; + curr_prefix = NULL; + /* Don't worry about wasted memory here - this function is usually only + called once. */ +} + +/* Sets the original and the current installation prefix of the package. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +void +set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg) +{ + set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg); + + /* Now notify all dependent libraries. */ +#if DEPENDS_ON_LIBCHARSET + libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109 + libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix + libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +} + +/* Convenience function: + Computes the current installation prefix, based on the original + installation prefix, the original installation directory of a particular + file, and the current pathname of this file. Returns NULL upon failure. */ +#ifdef IN_LIBRARY +#define compute_curr_prefix local_compute_curr_prefix +static +#endif +const char * +compute_curr_prefix (const char *orig_installprefix, + const char *orig_installdir, + const char *curr_pathname) +{ + const char *curr_installdir; + const char *rel_installdir; + + if (curr_pathname == NULL) + return NULL; + + /* Determine the relative installation directory, relative to the prefix. + This is simply the difference between orig_installprefix and + orig_installdir. */ + if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix)) + != 0) + /* Shouldn't happen - nothing should be installed outside $(prefix). */ + return NULL; + rel_installdir = orig_installdir + strlen (orig_installprefix); + + /* Determine the current installation directory. */ + { + const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname); + const char *p = curr_pathname + strlen (curr_pathname); + char *q; + + while (p > p_base) + { + p--; + if (ISSLASH (*p)) + break; + } + + q = (char *) xmalloc (p - curr_pathname + 1); +#ifdef NO_XMALLOC + if (q == NULL) + return NULL; +#endif + memcpy (q, curr_pathname, p - curr_pathname); + q[p - curr_pathname] = '\0'; + curr_installdir = q; + } + + /* Compute the current installation prefix by removing the trailing + rel_installdir from it. */ + { + const char *rp = rel_installdir + strlen (rel_installdir); + const char *cp = curr_installdir + strlen (curr_installdir); + const char *cp_base = + curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir); + + while (rp > rel_installdir && cp > cp_base) + { + bool same = false; + const char *rpi = rp; + const char *cpi = cp; + + while (rpi > rel_installdir && cpi > cp_base) + { + rpi--; + cpi--; + if (ISSLASH (*rpi) || ISSLASH (*cpi)) + { + if (ISSLASH (*rpi) && ISSLASH (*cpi)) + same = true; + break; + } +#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ + /* Win32, OS/2, DOS - case insignificant filesystem */ + if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi) + != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi)) + break; +#else + if (*rpi != *cpi) + break; +#endif + } + if (!same) + break; + /* The last pathname component was the same. opi and cpi now point + to the slash before it. */ + rp = rpi; + cp = cpi; + } + + if (rp > rel_installdir) + /* Unexpected: The curr_installdir does not end with rel_installdir. */ + return NULL; + + { + size_t curr_prefix_len = cp - curr_installdir; + char *curr_prefix; + + curr_prefix = (char *) xmalloc (curr_prefix_len + 1); +#ifdef NO_XMALLOC + if (curr_prefix == NULL) + return NULL; +#endif + memcpy (curr_prefix, curr_installdir, curr_prefix_len); + curr_prefix[curr_prefix_len] = '\0'; + + return curr_prefix; + } + } +} + +#if defined PIC && defined INSTALLDIR + +/* Full pathname of shared library, or NULL. */ +static char *shared_library_fullname; + +#if defined _WIN32 || defined __WIN32__ + +/* Determine the full pathname of the shared library when it is loaded. */ + +BOOL WINAPI +DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved) +{ + (void) reserved; + + if (event == DLL_PROCESS_ATTACH) + { + /* The DLL is being loaded into an application's address range. */ + static char location[MAX_PATH]; + + if (!GetModuleFileName (module_handle, location, sizeof (location))) + /* Shouldn't happen. */ + return FALSE; + + if (!IS_PATH_WITH_DIR (location)) + /* Shouldn't happen. */ + return FALSE; + + shared_library_fullname = strdup (location); + } + + return TRUE; +} + +#else /* Unix */ + +static void +find_shared_library_fullname () +{ +#ifdef __linux__ + FILE *fp; + + /* Open the current process' maps file. It describes one VMA per line. */ + fp = fopen ("/proc/self/maps", "r"); + if (fp) + { + unsigned long address = (unsigned long) &find_shared_library_fullname; + for (;;) + { + unsigned long start, end; + int c; + + if (fscanf (fp, "%lx-%lx", &start, &end) != 2) + break; + if (address >= start && address <= end - 1) + { + /* Found it. Now see if this line contains a filename. */ + while (c = getc (fp), c != EOF && c != '\n' && c != '/') + continue; + if (c == '/') + { + size_t size; + int len; + + ungetc (c, fp); + shared_library_fullname = NULL; size = 0; + len = getline (&shared_library_fullname, &size, fp); + if (len >= 0) + { + /* Success: filled shared_library_fullname. */ + if (len > 0 && shared_library_fullname[len - 1] == '\n') + shared_library_fullname[len - 1] = '\0'; + } + } + break; + } + while (c = getc (fp), c != EOF && c != '\n') + continue; + } + fclose (fp); + } +#endif +} + +#endif /* WIN32 / Unix */ + +/* Return the full pathname of the current shared library. + Return NULL if unknown. + Guaranteed to work only on Linux and Woe32. */ +static char * +get_shared_library_fullname () +{ +#if !(defined _WIN32 || defined __WIN32__) + static bool tried_find_shared_library_fullname; + if (!tried_find_shared_library_fullname) + { + find_shared_library_fullname (); + tried_find_shared_library_fullname = true; + } +#endif + return shared_library_fullname; +} + +#endif /* PIC */ + +/* Returns the pathname, relocated according to the current installation + directory. */ +const char * +relocate (const char *pathname) +{ +#if defined PIC && defined INSTALLDIR + static int initialized; + + /* Initialization code for a shared library. */ + if (!initialized) + { + /* At this point, orig_prefix and curr_prefix likely have already been + set through the main program's set_program_name_and_installdir + function. This is sufficient in the case that the library has + initially been installed in the same orig_prefix. But we can do + better, to also cover the cases that 1. it has been installed + in a different prefix before being moved to orig_prefix and (later) + to curr_prefix, 2. unlike the program, it has not moved away from + orig_prefix. */ + const char *orig_installprefix = INSTALLPREFIX; + const char *orig_installdir = INSTALLDIR; + const char *curr_prefix_better; + + curr_prefix_better = + compute_curr_prefix (orig_installprefix, orig_installdir, + get_shared_library_fullname ()); + if (curr_prefix_better == NULL) + curr_prefix_better = curr_prefix; + + set_relocation_prefix (orig_installprefix, curr_prefix_better); + + initialized = 1; + } +#endif + + /* Note: It is not necessary to perform case insensitive comparison here, + even for DOS-like filesystems, because the pathname argument was + typically created from the same Makefile variable as orig_prefix came + from. */ + if (orig_prefix != NULL && curr_prefix != NULL + && strncmp (pathname, orig_prefix, orig_prefix_len) == 0) + { + if (pathname[orig_prefix_len] == '\0') + /* pathname equals orig_prefix. */ + return curr_prefix; + if (ISSLASH (pathname[orig_prefix_len])) + { + /* pathname starts with orig_prefix. */ + const char *pathname_tail = &pathname[orig_prefix_len]; + char *result = + (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1); + +#ifdef NO_XMALLOC + if (result != NULL) +#endif + { + memcpy (result, curr_prefix, curr_prefix_len); + strcpy (result + curr_prefix_len, pathname_tail); + return result; + } + } + } + /* Nothing to relocate. */ + return pathname; +} + +#endif diff --git a/lib/intl/relocatable.h b/lib/intl/relocatable.h new file mode 100644 index 0000000..de57f44 --- /dev/null +++ b/lib/intl/relocatable.h @@ -0,0 +1,69 @@ +/* relocatable.h - Provide relocatable packages. */ + +/* Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2003. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _RELOCATABLE_H +#define _RELOCATABLE_H + +/* This can be enabled through the configure --enable-relocatable option. */ +#if ENABLE_RELOCATABLE + +/* When building a DLL, we must export some functions. Note that because + this is a private .h file, we don't need to use __declspec(dllimport) + in any case. */ +#if defined _MSC_VER && BUILDING_DLL +# define RELOCATABLE_DLL_EXPORTED __declspec(dllexport) +#else +# define RELOCATABLE_DLL_EXPORTED +#endif + +/* Sets the original and the current installation prefix of the package. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +extern RELOCATABLE_DLL_EXPORTED void + set_relocation_prefix (const char *orig_prefix, + const char *curr_prefix); + +/* Returns the pathname, relocated according to the current installation + directory. */ +extern const char * relocate (const char *pathname); + +/* Memory management: relocate() leaks memory, because it has to construct + a fresh pathname. If this is a problem because your program calls + relocate() frequently, think about caching the result. */ + +/* Convenience function: + Computes the current installation prefix, based on the original + installation prefix, the original installation directory of a particular + file, and the current pathname of this file. Returns NULL upon failure. */ +extern const char * compute_curr_prefix (const char *orig_installprefix, + const char *orig_installdir, + const char *curr_pathname); + +#else + +/* By default, we use the hardwired pathnames. */ +#define relocate(pathname) (pathname) + +#endif + +#endif /* _RELOCATABLE_H */ diff --git a/lib/intl/textdomain.c b/lib/intl/textdomain.c new file mode 100644 index 0000000..4bb0e80 --- /dev/null +++ b/lib/intl/textdomain.c @@ -0,0 +1,144 @@ +/* textdomain.c - Implementation of the textdomain(3) function. */ + +/* Copyright (C) 1995-1998, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgnuintl.h" +#endif +#include "gettextP.h" + +#ifdef _LIBC +/* We have to handle multi-threaded applications. */ +# include <bits/libc-lock.h> +#else +/* Provide dummy implementation if this is outside glibc. */ +# define __libc_rwlock_define(CLASS, NAME) +# define __libc_rwlock_wrlock(NAME) +# define __libc_rwlock_unlock(NAME) +#endif + +/* The internal variables in the standalone libintl.a must have different + names than the internal variables in GNU libc, otherwise programs + using libintl.a cannot be linked statically. */ +#if !defined _LIBC +# define _nl_default_default_domain libintl_nl_default_default_domain +# define _nl_current_default_domain libintl_nl_current_default_domain +#endif + +/* @@ end of prolog @@ */ + +/* Name of the default text domain. */ +extern const char _nl_default_default_domain[] attribute_hidden; + +/* Default text domain in which entries for gettext(3) are to be found. */ +extern const char *_nl_current_default_domain attribute_hidden; + + +/* Names for the libintl functions are a problem. They must not clash + with existing names and they should follow ANSI C. But this source + code is also used in GNU C Library where the names have a __ + prefix. So we have to make a difference here. */ +#ifdef _LIBC +# define TEXTDOMAIN __textdomain +# ifndef strdup +# define strdup(str) __strdup (str) +# endif +#else +# define TEXTDOMAIN libintl_textdomain +#endif + +/* Lock variable to protect the global data in the gettext implementation. */ +__libc_rwlock_define (extern, _nl_state_lock attribute_hidden) + +/* Set the current default message catalog to DOMAINNAME. + If DOMAINNAME is null, return the current default. + If DOMAINNAME is "", reset to the default of "messages". */ +char * +TEXTDOMAIN (domainname) + const char *domainname; +{ + char *new_domain; + char *old_domain; + + /* A NULL pointer requests the current setting. */ + if (domainname == NULL) + return (char *) _nl_current_default_domain; + + __libc_rwlock_wrlock (_nl_state_lock); + + old_domain = (char *) _nl_current_default_domain; + + /* If domain name is the null string set to default domain "messages". */ + if (domainname[0] == '\0' + || strcmp (domainname, _nl_default_default_domain) == 0) + { + _nl_current_default_domain = _nl_default_default_domain; + new_domain = (char *) _nl_current_default_domain; + } + else if (strcmp (domainname, old_domain) == 0) + /* This can happen and people will use it to signal that some + environment variable changed. */ + new_domain = old_domain; + else + { + /* If the following malloc fails `_nl_current_default_domain' + will be NULL. This value will be returned and so signals we + are out of core. */ +#if defined _LIBC || defined HAVE_STRDUP + new_domain = strdup (domainname); +#else + size_t len = strlen (domainname) + 1; + new_domain = (char *) malloc (len); + if (new_domain != NULL) + memcpy (new_domain, domainname, len); +#endif + + if (new_domain != NULL) + _nl_current_default_domain = new_domain; + } + + /* We use this possibility to signal a change of the loaded catalogs + since this is most likely the case and there is no other easy we + to do it. Do it only when the call was successful. */ + if (new_domain != NULL) + { + ++_nl_msg_cat_cntr; + + if (old_domain != new_domain && old_domain != _nl_default_default_domain) + free (old_domain); + } + + __libc_rwlock_unlock (_nl_state_lock); + + return new_domain; +} + +#ifdef _LIBC +/* Alias for function name in GNU C Library. */ +weak_alias (__textdomain, textdomain); +#endif diff --git a/lib/malloc/Makefile.in b/lib/malloc/Makefile.in new file mode 100644 index 0000000..7ec8f64 --- /dev/null +++ b/lib/malloc/Makefile.in @@ -0,0 +1,139 @@ +# Skeleton Makefile for the GNU malloc code +# +# Copyright (C) 1996-2021 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +PROFILE_FLAGS = @PROFILE_FLAGS@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +STYLE_CFLAGS = @STYLE_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +LIBBUILD = ${BUILD_DIR}/lib + +BASHINCDIR = ${topdir}/include + +INTL_LIBSRC = ${topdir}/lib/intl +INTL_BUILDDIR = ${LIBBUILD}/intl +INTL_INC = @INTL_INC@ +LIBINTL_H = @LIBINTL_H@ + +INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib $(INTL_INC) + +CCFLAGS = ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) \ + $(CFLAGS) $(MALLOC_CFLAGS) $(STYLE_CFLAGS) $(CPPFLAGS) + +.c.o: + $(CC) $(CCFLAGS) -c $< + +.s.o: + $(CC) $(CCFLAGS) -c $< + +MALLOC_SOURCE = malloc.c +STUB_SOURCE = stub.c + +ALLOCA_SOURCE = alloca.c +ALLOCA_OBJECT = alloca.o + +MALLOC_SRC = @MALLOC_SRC@ +MALLOC = @MALLOC@ +ALLOCA = @ALLOCA@ + +MALLOC_OBJS = malloc.o $(ALLOCA) trace.o stats.o table.o watch.o +STUB_OBJS = $(ALLOCA) stub.o + +.PHONY: malloc stubmalloc + +all: malloc + +malloc: ${MALLOC_OBJS} + ${RM} libmalloc.a + ${AR} ${ARFLAGS} libmalloc.a ${MALLOC_OBJS} + -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a + +stubmalloc: ${STUB_OBJS} + ${RM} libmalloc.a + ${AR} ${ARFLAGS} libmalloc.a ${STUB_OBJS} + -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a + +alloca: ${ALLOCA} + ${RM} libmalloc.a + ${AR} ${ARFLAGS} libmalloc.a ${ALLOCA} + -test -n "$(RANLIB)" && $(RANLIB) libmalloc.a + +alloca.o: $(srcdir)/$(ALLOCA_SOURCE) + $(CC) $(CCFLAGS) -c $(srcdir)/$(ALLOCA_SOURCE) + @- if test "$(ALLOCA_OBJECT)" != alloca.o ; then \ + mv $(ALLOCA_OBJECT) alloca.o >/dev/null 2>&1 ; \ + fi + +mostlyclean clean: + $(RM) *.o libmalloc.a + +distclean realclean maintainer-clean: clean + $(RM) Makefile + +alloca.o: $(BUILD_DIR)/config.h +malloc.o: $(BUILD_DIR)/config.h $(topdir)/bashtypes.h getpagesize.h +xmalloc.o: $(BUILD_DIR)/config.h $(BASHINCDIR)/ansi_stdlib.h +trace.o: ${BUILD_DIR}/config.h +stats.o: ${BUILD_DIR}/config.h +table.o: ${BUILD_DIR}/config.h +watch.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 + +malloc.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h +stats.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h +trace.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h +table.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h +watch.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h + +# Rules for deficient makes, like SunOS and Solaris +stub.o: stub.c +malloc.o: malloc.c +table.o: table.c +trace.o: trace.c +stats.o: stats.c +watch.o: watch.c diff --git a/lib/malloc/alloca.c b/lib/malloc/alloca.c new file mode 100644 index 0000000..26319c2 --- /dev/null +++ b/lib/malloc/alloca.c @@ -0,0 +1,482 @@ +/* alloca.c -- allocate automatically reclaimed memory + (Mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + J.Otto Tennant <jot@cray.com> contributed the Cray support. + + There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* If compiling with GCC 2, this file's not needed. */ +#if !defined (__GNUC__) || __GNUC__ < 2 + +#include <bashtypes.h> /* for size_t */ + +/* If alloca is defined somewhere, this file is not needed. */ +#ifndef alloca + +#ifdef emacs +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif /* static */ +#endif /* emacs */ + +/* If your stack is a linked list of frames, you have to + provide an "address metric" ADDRESS_FUNCTION macro. */ + +#if defined (CRAY) && defined (CRAY_STACKSEG_END) +long i00afunc (); +#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) +#else +#define ADDRESS_FUNCTION(arg) &(arg) +#endif /* CRAY && CRAY_STACKSEG_END */ + +#if __STDC__ +typedef void *pointer; +#else +typedef char *pointer; +#endif + +#define NULL 0 + +/* Different portions of Emacs need to call different versions of + malloc. The Emacs executable needs alloca to call xmalloc, because + ordinary malloc isn't protected from input signals. On the other + hand, the utilities in lib-src need alloca to call malloc; some of + them are very simple, and don't have an xmalloc routine. + + Non-Emacs programs expect this to call use xmalloc. + + Callers below should use malloc. */ + +#ifndef emacs +#define malloc xmalloc +extern pointer xmalloc (); +#endif + +/* Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* Direction unknown. */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ + +#else /* STACK_DIRECTION == 0; need run-time code. */ + +static int stack_dir; /* 1 or -1 once known. */ +#define STACK_DIR stack_dir + +static void +find_stack_direction () +{ + static char *addr = NULL; /* Address of first `dummy', once known. */ + auto char dummy; /* To get stack address. */ + + if (addr == NULL) + { /* Initial entry. */ + addr = ADDRESS_FUNCTION (dummy); + + find_stack_direction (); /* Recurse once. */ + } + else + { + /* Second entry. */ + if (ADDRESS_FUNCTION (dummy) > addr) + stack_dir = 1; /* Stack grew upward. */ + else + stack_dir = -1; /* Stack grew downward. */ + } +} + +#endif /* STACK_DIRECTION == 0 */ + +/* An "alloca header" is used to: + (a) chain together all alloca'ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc + alignment chunk size. The following default should work okay. */ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* To force sizeof(header). */ + struct + { + union hdr *next; /* For chaining headers. */ + char *deep; /* For stack depth measure. */ + } h; +} header; + +static header *last_alloca_header = NULL; /* -> last alloca header. */ + +/* Return a pointer to at least SIZE bytes of storage, + which will be automatically reclaimed upon exit from + the procedure that called alloca. Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. */ + +pointer +alloca (size) + size_t size; +{ + auto char probe; /* Probes stack depth: */ + register char *depth = ADDRESS_FUNCTION (probe); + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* Unknown growth direction. */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca'd storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* Traverses linked list. */ + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* Collect garbage. */ + + hp = np; /* -> next header. */ + } + else + break; /* Rest are not deeper. */ + + last_alloca_header = hp; /* -> last valid storage. */ + } + + if (size == 0) + return NULL; /* No allocation required. */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = malloc (sizeof (header) + size); + /* Address of header. */ + + ((header *) new)->h.next = last_alloca_header; + ((header *) new)->h.deep = depth; + + last_alloca_header = (header *) new; + + /* User storage begins just after header. */ + + return (pointer) ((char *) new + sizeof (header)); + } +} + +#if defined (CRAY) && defined (CRAY_STACKSEG_END) + +#ifdef DEBUG_I00AFUNC +#include <stdio.h> +#endif + +#ifndef CRAY_STACK +#define CRAY_STACK +#ifndef CRAY2 +/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ +struct stack_control_header + { + long shgrow:32; /* Number of times stack has grown. */ + long shaseg:32; /* Size of increments to stack. */ + long shhwm:32; /* High water mark of stack. */ + long shsize:32; /* Current size of stack (all segments). */ + }; + +/* The stack segment linkage control information occurs at + the high-address end of a stack segment. (The stack + grows from low addresses to high addresses.) The initial + part of the stack segment linkage control information is + 0200 (octal) words. This provides for register storage + for the routine which overflows the stack. */ + +struct stack_segment_linkage + { + long ss[0200]; /* 0200 overflow words. */ + long sssize:32; /* Number of words in this segment. */ + long ssbase:32; /* Offset to stack base. */ + long:32; + long sspseg:32; /* Offset to linkage control of previous + segment of stack. */ + long:32; + long sstcpt:32; /* Pointer to task common address block. */ + long sscsnm; /* Private control structure number for + microtasking. */ + long ssusr1; /* Reserved for user. */ + long ssusr2; /* Reserved for user. */ + long sstpid; /* Process ID for pid based multi-tasking. */ + long ssgvup; /* Pointer to multitasking thread giveup. */ + long sscray[7]; /* Reserved for Cray Research. */ + long ssa0; + long ssa1; + long ssa2; + long ssa3; + long ssa4; + long ssa5; + long ssa6; + long ssa7; + long sss0; + long sss1; + long sss2; + long sss3; + long sss4; + long sss5; + long sss6; + long sss7; + }; + +#else /* CRAY2 */ +/* The following structure defines the vector of words + returned by the STKSTAT library routine. */ +struct stk_stat + { + long now; /* Current total stack size. */ + long maxc; /* Amount of contiguous space which would + be required to satisfy the maximum + stack demand to date. */ + long high_water; /* Stack high-water mark. */ + long overflows; /* Number of stack overflow ($STKOFEN) calls. */ + long hits; /* Number of internal buffer hits. */ + long extends; /* Number of block extensions. */ + long stko_mallocs; /* Block allocations by $STKOFEN. */ + long underflows; /* Number of stack underflow calls ($STKRETN). */ + long stko_free; /* Number of deallocations by $STKRETN. */ + long stkm_free; /* Number of deallocations by $STKMRET. */ + long segments; /* Current number of stack segments. */ + long maxs; /* Maximum number of stack segments so far. */ + long pad_size; /* Stack pad size. */ + long current_address; /* Current stack segment address. */ + long current_size; /* Current stack segment size. This + number is actually corrupted by STKSTAT to + include the fifteen word trailer area. */ + long initial_address; /* Address of initial segment. */ + long initial_size; /* Size of initial segment. */ + }; + +/* The following structure describes the data structure which trails + any stack segment. I think that the description in 'asdef' is + out of date. I only describe the parts that I am sure about. */ + +struct stk_trailer + { + long this_address; /* Address of this block. */ + long this_size; /* Size of this block (does not include + this trailer). */ + long unknown2; + long unknown3; + long link; /* Address of trailer block of previous + segment. */ + long unknown5; + long unknown6; + long unknown7; + long unknown8; + long unknown9; + long unknown10; + long unknown11; + long unknown12; + long unknown13; + long unknown14; + }; + +#endif /* CRAY2 */ +#endif /* not CRAY_STACK */ + +#ifdef CRAY2 +/* Determine a "stack measure" for an arbitrary ADDRESS. + I doubt that "lint" will like this much. */ + +static long +i00afunc (long *address) +{ + struct stk_stat status; + struct stk_trailer *trailer; + long *block, size; + long result = 0; + + /* We want to iterate through all of the segments. The first + step is to get the stack status structure. We could do this + more quickly and more directly, perhaps, by referencing the + $LM00 common block, but I know that this works. */ + + STKSTAT (&status); + + /* Set up the iteration. */ + + trailer = (struct stk_trailer *) (status.current_address + + status.current_size + - 15); + + /* There must be at least one stack segment. Therefore it is + a fatal error if "trailer" is null. */ + + if (trailer == 0) + abort (); + + /* Discard segments that do not contain our argument address. */ + + while (trailer != 0) + { + block = (long *) trailer->this_address; + size = trailer->this_size; + if (block == 0 || size == 0) + abort (); + trailer = (struct stk_trailer *) trailer->link; + if ((block <= address) && (address < (block + size))) + break; + } + + /* Set the result to the offset in this segment and add the sizes + of all predecessor segments. */ + + result = address - block; + + if (trailer == 0) + { + return result; + } + + do + { + if (trailer->this_size <= 0) + abort (); + result += trailer->this_size; + trailer = (struct stk_trailer *) trailer->link; + } + while (trailer != 0); + + /* We are done. Note that if you present a bogus address (one + not in any segment), you will get a different number back, formed + from subtracting the address of the first block. This is probably + not what you want. */ + + return (result); +} + +#else /* not CRAY2 */ +/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. + Determine the number of the cell within the stack, + given the address of the cell. The purpose of this + routine is to linearize, in some sense, stack addresses + for alloca. */ + +static long +i00afunc (long address) +{ + long stkl = 0; + + long size, pseg, this_segment, stack; + long result = 0; + + struct stack_segment_linkage *ssptr; + + /* Register B67 contains the address of the end of the + current stack segment. If you (as a subprogram) store + your registers on the stack and find that you are past + the contents of B67, you have overflowed the segment. + + B67 also points to the stack segment linkage control + area, which is what we are really interested in. */ + + /* This might be _getb67() or GETB67 () or getb67 () */ + stkl = CRAY_STACKSEG_END (); + ssptr = (struct stack_segment_linkage *) stkl; + + /* If one subtracts 'size' from the end of the segment, + one has the address of the first word of the segment. + + If this is not the first segment, 'pseg' will be + nonzero. */ + + pseg = ssptr->sspseg; + size = ssptr->sssize; + + this_segment = stkl - size; + + /* It is possible that calling this routine itself caused + a stack overflow. Discard stack segments which do not + contain the target address. */ + + while (!(this_segment <= address && address <= stkl)) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); +#endif + if (pseg == 0) + break; + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + this_segment = stkl - size; + } + + result = address - this_segment; + + /* If you subtract pseg from the current end of the stack, + you get the address of the previous stack segment's end. + This seems a little convoluted to me, but I'll bet you save + a cycle somewhere. */ + + while (pseg != 0) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o\n", pseg, size); +#endif + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + result += size; + } + return (result); +} + +#endif /* not CRAY2 */ +#endif /* CRAY && CRAY_STACKSEG_END */ + +#endif /* no alloca */ +#endif /* !__GNUC__ || __GNUC__ < 2 */ diff --git a/lib/malloc/getpagesize.h b/lib/malloc/getpagesize.h new file mode 100644 index 0000000..a59eabe --- /dev/null +++ b/lib/malloc/getpagesize.h @@ -0,0 +1,60 @@ +/* Emulation of getpagesize() for systems that need it. + Copyright (C) 1991-2003 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +# if defined (_SC_PAGESIZE) +# define getpagesize() sysconf(_SC_PAGESIZE) +# else +# if defined (_SC_PAGE_SIZE) +# define getpagesize() sysconf(_SC_PAGE_SIZE) +# endif /* _SC_PAGE_SIZE */ +# endif /* _SC_PAGESIZE */ +#endif + +#if !defined (getpagesize) +# if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +# endif +# if defined (PAGESIZE) +# define getpagesize() PAGESIZE +# else /* !PAGESIZE */ +# if defined (EXEC_PAGESIZE) +# define getpagesize() EXEC_PAGESIZE +# else /* !EXEC_PAGESIZE */ +# if defined (NBPG) +# if !defined (CLSIZE) +# define CLSIZE 1 +# endif /* !CLSIZE */ +# define getpagesize() (NBPG * CLSIZE) +# else /* !NBPG */ +# if defined (NBPC) +# define getpagesize() NBPC +# endif /* NBPC */ +# endif /* !NBPG */ +# endif /* !EXEC_PAGESIZE */ +# endif /* !PAGESIZE */ +#endif /* !getpagesize */ + +#if !defined (getpagesize) +# define getpagesize() 4096 /* Just punt and use reasonable value */ +#endif diff --git a/lib/malloc/i386-alloca.s b/lib/malloc/i386-alloca.s new file mode 100644 index 0000000..01b2cfe --- /dev/null +++ b/lib/malloc/i386-alloca.s @@ -0,0 +1,16 @@ + .file "alloca.s" + .text + .align 4 + .def alloca; .val alloca; .scl 2; .type 044; .endef + .globl alloca +alloca: + popl %edx + popl %eax + addl $3,%eax + andl $0xfffffffc,%eax + subl %eax,%esp + movl %esp,%eax + pushl %eax + pushl %edx + ret + .def alloca; .val .; .scl -1; .endef diff --git a/lib/malloc/imalloc.h b/lib/malloc/imalloc.h new file mode 100644 index 0000000..bebd136 --- /dev/null +++ b/lib/malloc/imalloc.h @@ -0,0 +1,178 @@ +/* imalloc.h -- internal malloc definitions shared by source files. */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Must be included *after* config.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 + +/* If defined, as it is by default, use the lesscore() function to attempt + to reduce the top of the heap when freeing memory blocks larger than a + defined threshold. */ +#define USE_LESSCORE + +/* Generic pointer type. */ +#ifndef PTR_T +# if defined (__STDC__) +# define PTR_T void * +# else +# define PTR_T char * +# endif +#endif + +#if !defined (NULL) +# define NULL 0 +#endif + +#if !defined (CPP_STRING) +# if defined (HAVE_STRINGIZE) +# define CPP_STRING(x) #x +# else +# define CPP_STRING(x) "x" +# endif /* !HAVE_STRINGIZE */ +#endif /* !__STRING */ + +#if __GNUC__ > 1 +# define FASTCOPY(s, d, n) __builtin_memcpy (d, s, n) +#else /* !__GNUC__ */ +# if !defined (HAVE_BCOPY) +# if !defined (HAVE_MEMMOVE) +# define FASTCOPY(s, d, n) memcpy (d, s, n) +# else +# define FASTCOPY(s, d, n) memmove (d, s, n) +# endif /* !HAVE_MEMMOVE */ +# else /* HAVE_BCOPY */ +# define FASTCOPY(s, d, n) bcopy (s, d, n) +# endif /* HAVE_BCOPY */ +#endif /* !__GNUC__ */ + +#if !defined (PARAMS) +# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus) || defined (PROTOTYPES) +# define PARAMS(protos) protos +# else +# define PARAMS(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) + +#if defined (SHELL) +# include "bashintl.h" +#else +# define _(x) x +#endif + +#include <signal.h> + +extern void _malloc_block_signals PARAMS((sigset_t *, sigset_t *)); +extern void _malloc_unblock_signals PARAMS((sigset_t *, sigset_t *)); + +#endif /* _IMALLOC_H */ diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c new file mode 100644 index 0000000..f61f027 --- /dev/null +++ b/lib/malloc/malloc.c @@ -0,0 +1,1558 @@ +/* malloc.c - dynamic memory allocation for bash. */ + +/* Copyright (C) 1985-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * @(#)nmalloc.c 1 (Caltech) 2/21/82 + * + * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs + * + * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD. + * + * [VERY] old explanation: + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks + * that don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are (2^n)-4 (or -16) bytes long. + * This is designed for use in a program that uses vast quantities of + * memory, but bombs when it runs out. To make it a little better, it + * warns the user when he starts to get near the end. + * + * June 84, ACT: modified rcheck code to check the range given to malloc, + * rather than the range determined by the 2-power used. + * + * Jan 85, RMS: calls malloc_warning to issue warning on nearly full. + * No longer Emacs-specific; can serve as all-purpose malloc for GNU. + * You should call malloc_init to reinitialize after loading dumped Emacs. + * Call malloc_stats to get info on memory stats if MALLOC_STATS turned on. + * realloc knows how to return same block given, just changing its size, + * if the power of 2 is correct. + */ + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+5). The + * smallest allocatable block is 32 bytes. The overhead information will + * go in the first 16 bytes of the block, and the returned pointer will point + * to the rest. + */ + +/* 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> +#endif /* HAVE_CONFIG_H */ + +#if defined (SHELL) +# include "bashtypes.h" +# include "stdc.h" +#else +# include <sys/types.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +/* Determine which kind of system this is. */ +#include <signal.h> + +#if defined (HAVE_STRING_H) +# include <string.h> +#else +# include <strings.h> +#endif +#include <errno.h> +#include <stdio.h> + +#if !defined (botch) +#include <stdlib.h> +#endif + +#if defined (HAVE_MMAP) +#include <sys/mman.h> +#endif + +/* Define getpagesize () if the system does not. */ +#ifndef HAVE_GETPAGESIZE +# include "getpagesize.h" +#endif + +#include "imalloc.h" +#ifdef MALLOC_STATS +# include "mstats.h" +#endif +#ifdef MALLOC_REGISTER +# include "table.h" +#endif +#ifdef MALLOC_WATCH +# include "watch.h" +#endif + +#ifdef powerof2 +# undef powerof2 +#endif +/* Could also use (((x) & -(x)) == (x)) */ +#define powerof2(x) ((((x) - 1) & (x)) == 0) + +/* System-specific omissions. */ +#ifdef HPUX +# define NO_VALLOC +#endif + +#define MALLOC_PAGESIZE_MIN 4096 +#define MALLOC_INCR_PAGES 8192 + +#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */ +#define ISFREE ((char) 0x54) /* magic byte that implies free block */ + /* this is for error checking only */ +#define ISMEMALIGN ((char) 0xd6) /* Stored before the value returned by + memalign, with the rest of the word + being the distance to the true + beginning of the block. */ + + +/* We have a flag indicating whether memory is allocated, an index in + nextf[], a size field, and a sentinel value to determine whether or + not a caller wrote before the start of allocated memory; to realloc() + memory we either copy mh_nbytes or just change mh_nbytes if there is + enough room in the block for the new size. Range checking is always + done. */ +union mhead { + bits64_t mh_align[2]; /* 16 */ + struct { + char mi_alloc; /* ISALLOC or ISFREE */ /* 1 */ + char mi_index; /* index in nextf[] */ /* 1 */ + /* Remainder are valid only when block is allocated */ + u_bits16_t mi_magic2; /* should be == MAGIC2 */ /* 2 */ + u_bits32_t mi_nbytes; /* # of bytes allocated */ /* 4 */ + char mi_magic8[8]; /* MAGIC1 guard bytes */ /* 8 */ + } minfo; +}; +#define mh_alloc minfo.mi_alloc +#define mh_index minfo.mi_index +#define mh_nbytes minfo.mi_nbytes +#define mh_magic2 minfo.mi_magic2 +#define mh_magic8 minfo.mi_magic8 + +#define MAGIC8_NUMBYTES 8 +#define MALLOC_SIZE_T u_bits32_t + +#define MOVERHEAD sizeof(union mhead) + +#define MALIGN_MASK 15 /* one less than desired alignment */ + +/* Guard bytes we write at the end of the allocation, encoding the size. */ +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 + because we want sizeof (union mhead) + to describe the overhead for when the block is in use, + and we do not want the free-list pointer to count in that. */ +/* If we have mmap, this is not used for chunks larger than mmap_threshold, + since we munmap immediately on free(). */ + +/* If SIZEOF_CHAR_P == 8, this goes into the mh_magic8 buffer at the end of + the rest of the struct. This may need adjusting. */ +#define CHAIN(a) \ + (*(union mhead **) (sizeof (char *) + (char *) (a))) + +/* To implement range checking, we write magic values in at the beginning + and end of each allocated block, and make sure they are undisturbed + whenever a free or a realloc occurs. */ + +/* Written in the bytes before the block's real space (-SIZEOF_CHAR_P bytes) */ +#define MAGIC1 0x55 +#define MAGIC2 0x5555 + +#define MSLOP 4 /* 4 bytes extra for u_bits32_t end guard size */ + +/* How many bytes are actually allocated for a request of size N -- + rounded up to nearest multiple of 16 (alignment) after accounting for + malloc overhead. */ +#define ALLOCATED_BYTES(n) \ + (((n) + MOVERHEAD + MSLOP + MALIGN_MASK) & ~MALIGN_MASK) + +#define ASSERT(p) \ + do \ + { \ + if (!(p)) xbotch((PTR_T)0, ERR_ASSERT_FAILED, CPP_STRING(p), file, line); \ + } \ + while (0) + +/* Minimum and maximum bucket indices for block splitting (and to bound + the search for a block to split). */ +#define SPLIT_MIN 1 /* 64 */ +#define SPLIT_MID 9 /* 16384 */ +#define SPLIT_MAX 12 /* 131072 */ + +/* Minimum and maximum bucket indices for block coalescing. */ +#define COMBINE_MIN 1 /* 64 */ +#define COMBINE_MAX (pagebucket - 1) /* 2048 for 4096-byte pages */ + +#define LESSCORE_MIN 8 /* 8192 */ +#define LESSCORE_FRC 11 /* 65536 */ + +/* Which bin do we prepopulate with the initial sbrk memory? */ +#define PREPOP_BIN 1 +#define PREPOP_SIZE 64 + +#define STARTBUCK 0 + +/* Should we use mmap for large allocations? */ +#if defined (HAVE_MMAP) +# if defined (MAP_ANON) && !defined (MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +# endif +#endif + +#if defined (HAVE_MMAP) && defined (MAP_ANONYMOUS) +# define USE_MMAP 1 +#endif + +#if defined (USE_MMAP) +# define MMAP_THRESHOLD 12 /* must be >= SPLIT_MAX, COMBINE_MAX */ +#else +# define MMAP_THRESHOLD (8 * SIZEOF_LONG) +#endif + +/* We don't try to decipher the differences between the Linux-style and + BSD-style implementations of mremap here; we use the Linux one. */ +#if USE_MMAP == 1 && defined (HAVE_MREMAP) && defined (MREMAP_MAYMOVE) +# define USE_MREMAP 1 +#endif + +/* usable bins from STARTBUCK..NBUCKETS-1 */ + +#define NBUCKETS 28 + +/* Flags for the internal functions. */ +#define MALLOC_WRAPPER 0x01 /* wrapper function */ +#define MALLOC_INTERNAL 0x02 /* internal function calling another */ +#define MALLOC_NOTRACE 0x04 /* don't trace this allocation or free */ +#define MALLOC_NOREG 0x08 /* don't register this allocation or free */ + +/* Future use. */ +#define ERR_DUPFREE 0x01 +#define ERR_UNALLOC 0x02 +#define ERR_UNDERFLOW 0x04 +#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. 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 + 5) */ + +static union mhead *nextf[NBUCKETS]; + +/* busy[i] is nonzero while allocation or free of block size i is in progress. */ + +static char busy[NBUCKETS]; + +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 const unsigned long binsizes[NBUCKETS] = { + 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, 4294967295UL +}; + +/* binsizes[x] == (1 << ((x) + 5)) */ +#define binsize(x) binsizes[(x)] + +#define MAXALLOC_SIZE binsizes[NBUCKETS-1] + +#if !defined (errno) +extern int errno; +#endif + +/* Declarations for internal functions */ +static PTR_T internal_malloc PARAMS((size_t, const char *, int, int)); +static PTR_T internal_realloc PARAMS((PTR_T, size_t, const char *, int, int)); +static void internal_free PARAMS((PTR_T, const char *, int, int)); +static PTR_T internal_memalign PARAMS((size_t, size_t, const char *, int, int)); +#ifndef NO_CALLOC +static PTR_T internal_calloc PARAMS((size_t, size_t, const char *, int, int)); +static void internal_cfree PARAMS((PTR_T, const char *, int, int)); +#endif +#ifndef NO_VALLOC +static PTR_T internal_valloc PARAMS((size_t, const char *, int, int)); +#endif +static PTR_T internal_remap PARAMS((PTR_T, size_t, int, int)); + +#if defined (botch) +extern void botch (); +#else +static void botch PARAMS((const char *, const char *, int)); +#endif +static void xbotch PARAMS((PTR_T, int, const char *, const char *, int)); + +#if !HAVE_DECL_SBRK +extern char *sbrk (); +#endif /* !HAVE_DECL_SBRK */ + +#ifdef SHELL +extern int running_trap; +extern int signal_is_trapped PARAMS((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 */ + +/* Use a variable in case we want to dynamically adapt it in the future */ +int malloc_mmap_threshold = MMAP_THRESHOLD; + +#ifdef MALLOC_TRACE +char _malloc_trace_buckets[NBUCKETS]; + +/* These should really go into a header file. */ +extern void mtrace_alloc PARAMS((const char *, PTR_T, size_t, const char *, int)); +extern void mtrace_free PARAMS((PTR_T, int, const char *, int)); +#endif + +#if !defined (botch) +static void +botch (s, file, line) + const char *s; + const char *file; + int line; +{ + fprintf (stderr, _("malloc: failed assertion: %s\n"), s); + (void)fflush (stderr); + abort (); +} +#endif + +/* print the file and line number that caused the assertion failure and + call botch() to do whatever the application wants with the information */ +static void +xbotch (mem, e, s, file, line) + PTR_T mem; + int e; + const char *s; + const char *file; + int line; +{ + fprintf (stderr, _("\r\nmalloc: %s:%d: assertion botched\r\n"), + file ? file : _("unknown"), line); +#ifdef MALLOC_REGISTER + if (mem != NULL && malloc_register) + mregister_describe_mem (mem, stderr); +#endif + (void)fflush (stderr); + botch(s, file, line); +} + +/* Coalesce two adjacent free blocks off the free list for size NU - 1, + 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. + BUSY[NU] must be set to 1. */ +static void +bcoalesce (nu) + register int nu; +{ + register union mhead *mp, *mp1, *mp2; + register int nbuck; + unsigned long siz; + + nbuck = nu - 1; + if (nextf[nbuck] == 0 || busy[nbuck]) + return; + + busy[nbuck] = 1; + siz = binsize (nbuck); + + mp2 = mp1 = nextf[nbuck]; + mp = CHAIN (mp1); + while (mp && mp != (union mhead *)((char *)mp1 + siz)) + { + mp2 = mp1; + mp1 = mp; + mp = CHAIN (mp); + } + + if (mp == 0) + { + busy[nbuck] = 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 (mp2 != mp1 && CHAIN(mp2) != mp1) + { + busy[nbuck] = 0; + xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0); + } + +#ifdef MALLOC_DEBUG + if (CHAIN (mp1) != (union mhead *)((char *)mp1 + siz)) + { + busy[nbuck] = 0; + return; /* not adjacent */ + } +#endif + + /* Since they are adjacent, remove them from the free list */ + if (mp1 == nextf[nbuck]) + nextf[nbuck] = CHAIN (mp); + else + CHAIN (mp2) = CHAIN (mp); + busy[nbuck] = 0; + +#ifdef MALLOC_STATS + _mstats.tbcoalesce++; + _mstats.ncoalesce[nbuck]++; +#endif + + /* And add the combined two blocks to nextf[NU]. */ + mp1->mh_alloc = ISFREE; + mp1->mh_index = nu; + CHAIN (mp1) = nextf[nu]; + nextf[nu] = mp1; +} + +/* 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] + is assumed to be empty. Must be called with signals blocked (e.g., + by morecore()). BUSY[NU] must be set to 1. */ +static void +bsplit (nu) + register int nu; +{ + register union mhead *mp; + int nbuck, nblks, split_max; + unsigned long siz; + + split_max = (maxbuck > SPLIT_MAX) ? maxbuck : SPLIT_MAX; + + if (nu >= SPLIT_MID) + { + for (nbuck = split_max; nbuck > nu; nbuck--) + { + if (busy[nbuck] || nextf[nbuck] == 0) + continue; + break; + } + } + else + { + for (nbuck = nu + 1; nbuck <= split_max; nbuck++) + { + if (busy[nbuck] || nextf[nbuck] == 0) + continue; + break; + } + } + + if (nbuck > split_max || nbuck <= nu) + return; + + /* XXX might want to split only if nextf[nbuck] has >= 2 blocks free + and nbuck is below some threshold. */ + + /* Remove the block from the chain of larger blocks. */ + busy[nbuck] = 1; + mp = nextf[nbuck]; + nextf[nbuck] = CHAIN (mp); + busy[nbuck] = 0; + +#ifdef MALLOC_STATS + _mstats.tbsplit++; + _mstats.nsplit[nbuck]++; +#endif + + /* Figure out how many blocks we'll get. */ + siz = binsize (nu); + nblks = binsize (nbuck) / siz; + + /* Split the block and put it on the requested chain. */ + nextf[nu] = mp; + while (1) + { + mp->mh_alloc = ISFREE; + mp->mh_index = nu; + if (--nblks <= 0) break; + CHAIN (mp) = (union mhead *)((char *)mp + siz); + mp = (union mhead *)((char *)mp + siz); + } + CHAIN (mp) = 0; +} + +/* Take the memory block MP and add it to a chain < NU. NU is the right bucket, + but is busy. This avoids memory orphaning. */ +static void +xsplit (mp, nu) + union mhead *mp; + int nu; +{ + union mhead *nh; + int nbuck, nblks, split_max; + unsigned long siz; + + nbuck = nu - 1; + while (nbuck >= SPLIT_MIN && busy[nbuck]) + nbuck--; + if (nbuck < SPLIT_MIN) + return; + +#ifdef MALLOC_STATS + _mstats.tbsplit++; + _mstats.nsplit[nu]++; +#endif + + /* Figure out how many blocks we'll get. */ + siz = binsize (nu); /* original block size */ + nblks = siz / binsize (nbuck); /* should be 2 most of the time */ + + /* And add it to nextf[nbuck] */ + siz = binsize (nbuck); /* XXX - resetting here */ + nh = mp; + while (1) + { + mp->mh_alloc = ISFREE; + mp->mh_index = nbuck; + if (--nblks <= 0) break; + CHAIN (mp) = (union mhead *)((char *)mp + siz); + mp = (union mhead *)((char *)mp + siz); + } + busy[nbuck] = 1; + CHAIN (mp) = nextf[nbuck]; + nextf[nbuck] = nh; + busy[nbuck] = 0; +} + +void +_malloc_block_signals (setp, osetp) + sigset_t *setp, *osetp; +{ +#ifdef HAVE_POSIX_SIGNALS + sigfillset (setp); + sigemptyset (osetp); + sigprocmask (SIG_BLOCK, setp, osetp); +#else +# if defined (HAVE_BSD_SIGNALS) + *osetp = sigsetmask (-1); +# endif +#endif +} + +void +_malloc_unblock_signals (setp, osetp) + sigset_t *setp, *osetp; +{ +#ifdef HAVE_POSIX_SIGNALS + sigprocmask (SIG_SETMASK, osetp, (sigset_t *)NULL); +#else +# if defined (HAVE_BSD_SIGNALS) + sigsetmask (*osetp); +# endif +#endif +} + +#if defined (USE_LESSCORE) +/* 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 +} +#endif /* USE_LESSCORE */ + +/* Ask system for more memory; add to NEXTF[NU]. BUSY[NU] must be set to 1. */ +static void +morecore (nu) + register int nu; /* size index to get more of */ +{ + register union mhead *mp; + register int nblks; + register long siz; + long sbrk_amt; /* amount to get via sbrk() */ + sigset_t set, oset; + int blocked_sigs; + + /* Block all signals in case we are executed from a signal handler. */ + blocked_sigs = 0; +#ifdef SHELL +# if defined (SIGCHLD) + if (running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD)) +# else + if (running_trap || signal_is_trapped (SIGINT)) +# endif +#endif + { + _malloc_block_signals (&set, &oset); + blocked_sigs = 1; + } + + siz = binsize (nu); /* size of desired block for nextf[nu] */ + + if (siz < 0) + goto morecore_done; /* oops */ + +#ifdef MALLOC_STATS + _mstats.nmorecore[nu]++; +#endif + + /* Try to split a larger block here, if we're within the range of sizes + to split. */ + if (nu >= SPLIT_MIN && nu <= malloc_mmap_threshold) + { + bsplit (nu); + if (nextf[nu] != 0) + goto morecore_done; + } + + /* Try to coalesce two adjacent blocks from the free list on nextf[nu - 1], + if we can, and we're within the range of the block coalescing limits. */ + if (nu >= COMBINE_MIN && nu < COMBINE_MAX && nu <= malloc_mmap_threshold && busy[nu - 1] == 0 && nextf[nu - 1]) + { + bcoalesce (nu); + if (nextf[nu] != 0) + goto morecore_done; + } + + /* Take at least a page, and figure out how many blocks of the requested + size we're getting. */ + if (siz <= pagesz) + { + sbrk_amt = pagesz; + nblks = sbrk_amt / siz; + } + else + { + /* We always want to request an integral multiple of the page size + from the kernel, so let's compute whether or not `siz' is such + 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 - 1); + if (sbrk_amt == 0) + sbrk_amt = siz; + else + sbrk_amt = siz + pagesz - sbrk_amt; + nblks = 1; + } + +#if defined (USE_MMAP) + if (nu > malloc_mmap_threshold) + { + mp = (union mhead *)mmap (0, sbrk_amt, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if ((void *)mp == MAP_FAILED) + goto morecore_done; + nextf[nu] = mp; + mp->mh_alloc = ISFREE; + mp->mh_index = nu; + CHAIN (mp) = 0; +#ifdef MALLOC_STATS + _mstats.nmmap++; + _mstats.tmmap += sbrk_amt; +#endif + goto morecore_done; + } +#endif + + +#ifdef MALLOC_STATS + _mstats.nsbrk++; + _mstats.tsbrk += sbrk_amt; +#endif + + mp = (union mhead *) sbrk (sbrk_amt); + + /* Totally out of memory. */ + if ((long)mp == -1) + goto morecore_done; + + memtop += sbrk_amt; + + /* shouldn't happen, but just in case -- require 8- or 16-byte alignment */ + if ((long)mp & MALIGN_MASK) + { + mp = (union mhead *) (((long)mp + MALIGN_MASK) & ~MALIGN_MASK); + nblks--; + } + + /* save new header and link the nblks blocks together */ + nextf[nu] = mp; + while (1) + { + mp->mh_alloc = ISFREE; + mp->mh_index = nu; + if (--nblks <= 0) break; + CHAIN (mp) = (union mhead *)((char *)mp + siz); + mp = (union mhead *)((char *)mp + siz); + } + CHAIN (mp) = 0; + +morecore_done: + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); +} + +static void +malloc_debug_dummy () +{ + write (1, "malloc_debug_dummy\n", 19); +} + +static int +pagealign () +{ + register int nunits; + register union mhead *mp; + long sbrk_needed; + char *curbrk; + + pagesz = getpagesize (); + if (pagesz < MALLOC_PAGESIZE_MIN) + pagesz = MALLOC_PAGESIZE_MIN; + + /* 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 (3 == 64 bytes) with it. Add whatever we need to curbrk + to make things 64-byte aligned, compute how many 64-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; + const char *file; + int line, flags; +{ + register union mhead *p; + register int nunits; + register char *m, *z; + MALLOC_SIZE_T nbytes; + mguard_t mg; + + /* 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) + 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. */ +#if SIZEOF_SIZE_T == 8 + if (ALLOCATED_BYTES(n) > MAXALLOC_SIZE) + return ((PTR_T) NULL); +#endif + nbytes = ALLOCATED_BYTES(n); + nunits = (nbytes <= (pagesz >> 1)) ? STARTBUCK : pagebucket; + for ( ; nunits < NBUCKETS; nunits++) + if (nbytes <= binsize(nunits)) + break; + + /* Silently reject too-large requests. XXX - can increase this if HAVE_MMAP */ + if (nunits >= NBUCKETS) + return ((PTR_T) NULL); + + /* In case this is reentrant use of malloc from signal handler, + pick a block size that no other malloc level is currently + trying to allocate. That's the easiest harmless way not to + interfere with the other level of execution. */ +#ifdef MALLOC_STATS + if (busy[nunits]) _mstats.nrecurse++; +#endif + while (busy[nunits]) nunits++; + busy[nunits] = 1; + + if (nunits > maxbuck) + maxbuck = nunits; + + /* If there are no blocks of the appropriate size, go get some */ + if (nextf[nunits] == 0) + morecore (nunits); + + /* Get one block off the list, and set the new list head */ + if ((p = nextf[nunits]) == NULL) + { + busy[nunits] = 0; + return NULL; + } + nextf[nunits] = CHAIN (p); + busy[nunits] = 0; + + /* Check for free block clobbered */ + /* 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)(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; + + /* Begin guard */ + MALLOC_MEMSET ((char *)p->mh_magic8, MAGIC1, MAGIC8_NUMBYTES); + + /* End guard */ + mg.i = n; + z = mg.s; + m = (char *) (p + 1) + n; + *m++ = *z++, *m++ = *z++, *m++ = *z++, *m++ = *z++; + +#ifdef MEMSCRAMBLE + 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 + if (malloc_register && (flags & MALLOC_NOREG) == 0) + mregister_alloc ("malloc", p + 1, n, file, line); +#endif + +#ifdef MALLOC_WATCH + if (_malloc_nwatch > 0) + _malloc_ckwatch (p + 1, file, line, W_ALLOC, n); +#endif + +#if defined (MALLOC_DEBUG) + z = (char *) (p + 1); + /* Check alignment of returned pointer */ + if ((unsigned long)z & MALIGN_MASK) + fprintf (stderr, "malloc: %s:%d: warning: request for %d bytes not aligned on %d byte boundary\r\n", + file ? file : _("unknown"), line, p->mh_nbytes, MALIGN_MASK+1); +#endif + + return (PTR_T) (p + 1); +} + +static void +internal_free (mem, file, line, flags) + PTR_T mem; + const char *file; + int line, flags; +{ + register union mhead *p; + register char *ap, *z; + register int nunits; + register MALLOC_SIZE_T nbytes; + MALLOC_SIZE_T ubytes; /* caller-requested size */ + mguard_t mg; + + if ((ap = (char *)mem) == 0) + return; + + p = (union mhead *) ap - 1; + + if (p->mh_alloc == ISMEMALIGN) + { + ap -= p->mh_nbytes; + p = (union mhead *) ap - 1; + } + +#if defined (MALLOC_TRACE) || defined (MALLOC_REGISTER) || defined (MALLOC_WATCH) + if (malloc_trace || malloc_register || _malloc_nwatch > 0) + ubytes = p->mh_nbytes; +#endif + + if (p->mh_alloc != ISALLOC) + { + if (p->mh_alloc == ISFREE) + xbotch (mem, ERR_DUPFREE, + _("free: called with already freed block argument"), file, line); + else + xbotch (mem, ERR_UNALLOC, + _("free: called with unallocated block argument"), file, line); + } + + ASSERT (p->mh_magic2 == MAGIC2); + + nunits = p->mh_index; + nbytes = ALLOCATED_BYTES(p->mh_nbytes); + /* The MAGIC8_NUMBYTES bytes before the memory handed to the user are now + used for a simple check to catch things like p[-1] = 'x'. + 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 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); + { + int i; + for (i = 0, z = p->mh_magic8; i < MAGIC8_NUMBYTES; i++) + if (*z++ != MAGIC1) + xbotch (mem, ERR_UNDERFLOW, + _("free: underflow detected; magic8 corrupted"), file, line); + } + + ap += p->mh_nbytes; + 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 defined (USE_MMAP) + if (nunits > malloc_mmap_threshold) + { + munmap (p, binsize (nunits)); +#if defined (MALLOC_STATS) + _mstats.nlesscore[nunits]++; +#endif + goto free_return; + } +#endif + +#if defined (USE_LESSCORE) + /* We take care of the mmap case and munmap above */ + if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop)) + { + /* 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 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; + } + } +#endif /* USE_LESSCORE */ + +#ifdef MEMSCRAMBLE + if (p->mh_nbytes) + MALLOC_MEMSET (mem, 0xcf, p->mh_nbytes); +#endif + + ASSERT (nunits < NBUCKETS); + + if (busy[nunits] == 1) + { + xsplit (p, nunits); /* split block and add to different chain */ + goto free_return; + } + + p->mh_alloc = ISFREE; + /* Protect against signal handlers calling malloc. */ + busy[nunits] = 1; + /* Put this block on the free list. */ + CHAIN (p) = nextf[nunits]; + nextf[nunits] = p; + busy[nunits] = 0; + +free_return: + ; /* Empty statement in case this is the end of the function */ + +#ifdef MALLOC_STATS + _mstats.nmalloc[nunits]--; + _mstats.nfre++; +#endif /* MALLOC_STATS */ + +#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 +} + +#if USE_MREMAP == 1 +/* Assume the caller (internal_realloc) has already performed the sanity and + overflow tests. Basically we kill the old guard information, determine the + new size, call mremap with the new size, and add the bookkeeping and guard + information back in. */ +static PTR_T +internal_remap (mem, n, nunits, flags) + PTR_T mem; + register size_t n; + int nunits; + int flags; +{ + register union mhead *p, *np; + char *m, *z; + mguard_t mg; + MALLOC_SIZE_T nbytes; + + if (nunits >= NBUCKETS) /* Uh oh */ + return ((PTR_T) NULL); + + p = (union mhead *)mem - 1; + + m = (char *)mem + p->mh_nbytes; + z = mg.s; + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; /* erase guard */ + + nbytes = ALLOCATED_BYTES(n); + + busy[nunits] = 1; + np = (union mhead *)mremap (p, binsize (p->mh_index), binsize (nunits), MREMAP_MAYMOVE); + busy[nunits] = 0; + if (np == MAP_FAILED) + return (PTR_T)NULL; + + if (np != p) + { + np->mh_alloc = ISALLOC; + np->mh_magic2 = MAGIC2; + MALLOC_MEMSET ((char *)np->mh_magic8, MAGIC1, MAGIC8_NUMBYTES); + } + np->mh_index = nunits; + np->mh_nbytes = n; + + mg.i = n; + z = mg.s; + m = (char *)(np + 1) + n; + *m++ = *z++, *m++ = *z++, *m++ = *z++, *m++ = *z++; + + return ((PTR_T)(np + 1)); +} +#endif + +static PTR_T +internal_realloc (mem, n, file, line, flags) + PTR_T mem; + register size_t n; + const char *file; + int line, flags; +{ + register union mhead *p; + register MALLOC_SIZE_T tocopy; + register MALLOC_SIZE_T nbytes; + register int newunits, nunits; + register char *m, *z; + mguard_t mg; + +#ifdef MALLOC_STATS + _mstats.nrealloc++; +#endif + + if (n == 0) + { + internal_free (mem, file, line, MALLOC_INTERNAL); + return (NULL); + } + if ((p = (union mhead *) mem) == 0) + return internal_malloc (n, file, line, MALLOC_INTERNAL); + + p--; + nunits = p->mh_index; + ASSERT (nunits < NBUCKETS); + + if (p->mh_alloc != ISALLOC) + xbotch (mem, ERR_UNALLOC, + _("realloc: called with unallocated block argument"), file, line); + + ASSERT (p->mh_magic2 == MAGIC2); + nbytes = ALLOCATED_BYTES(p->mh_nbytes); + /* Since the sizeof(u_bits32_t) bytes before the memory handed to the user + are now used for the number of bytes allocated, a simple check of + mh_magic2 is no longer sufficient to catch things like p[-1] = 'x'. + 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 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); + { + int i; + for (i = 0, z = p->mh_magic8; i < MAGIC8_NUMBYTES; i++) + if (*z++ != MAGIC1) + xbotch (mem, ERR_UNDERFLOW, + _("realloc: underflow detected; magic8 corrupted"), file, line); + + } + + m = (char *)mem + (tocopy = p->mh_nbytes); + 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 + + /* If we're reallocating to the same size as previously, return now */ + if (n == p->mh_nbytes) + return mem; + +#if SIZEOF_SIZE_T == 8 + if (ALLOCATED_BYTES(n) > MAXALLOC_SIZE) + return ((PTR_T) NULL); +#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 (RIGHT_BUCKET(nbytes, nunits) || RIGHT_BUCKET(nbytes, nunits-1)) + { + /* Compensate for increment above. */ + m -= 4; + + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; + 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 USE_MREMAP == 1 + /* If we are using mmap and have mremap, we use it here. Make sure that + the old size and new size are above the threshold where we use mmap */ + if (nbytes > p->mh_nbytes) + newunits = nunits; + else + newunits = (nbytes <= (pagesz >> 1)) ? STARTBUCK : pagebucket; + for ( ; newunits < NBUCKETS; newunits++) + if (nbytes <= binsize(newunits)) + break; + + if (nunits > malloc_mmap_threshold && newunits > malloc_mmap_threshold) + { + m = internal_remap (mem, n, newunits, MALLOC_INTERNAL); + if (m == 0) + return 0; + } + else +#endif /* USE_MREMAP */ + { + if ((m = internal_malloc (n, file, line, MALLOC_INTERNAL|MALLOC_NOTRACE|MALLOC_NOREG)) == 0) + return 0; + FASTCOPY (mem, m, tocopy); + internal_free (mem, file, line, MALLOC_INTERNAL); + } + +#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 + if (malloc_register && (flags & MALLOC_NOREG) == 0) + 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; +} + +static PTR_T +internal_memalign (alignment, size, file, line, flags) + size_t alignment; + size_t size; + const char *file; + int line, flags; +{ + register char *ptr; + register char *aligned; + register union mhead *p; + + ptr = internal_malloc (size + alignment, file, line, MALLOC_INTERNAL); + + if (ptr == 0) + return 0; + /* If entire block has the desired alignment, just accept it. */ + if (((long) ptr & (alignment - 1)) == 0) + return ptr; + /* Otherwise, get address of byte in the block that has that alignment. */ + aligned = (char *) (((long) ptr + alignment - 1) & (~alignment + 1)); + + /* Store a suitable indication of how to free the block, + so that free can find the true beginning of it. */ + p = (union mhead *) aligned - 1; + p->mh_nbytes = aligned - ptr; + p->mh_alloc = ISMEMALIGN; + + return aligned; +} + +int +posix_memalign (memptr, alignment, size) + void **memptr; + size_t alignment, size; +{ + void *mem; + + /* Perform posix-mandated error checking here */ + if ((alignment % sizeof (void *) != 0) || alignment == 0) + return EINVAL; + else if (powerof2 (alignment) == 0) + return EINVAL; + + mem = internal_memalign (alignment, size, (char *)0, 0, 0); + if (mem != 0) + { + *memptr = mem; + return 0; + } + return ENOMEM; +} + +size_t +malloc_usable_size (mem) + void *mem; +{ + register union mhead *p; + register char *ap; + + if ((ap = (char *)mem) == 0) + return 0; + + /* Find the true start of the memory block to discover which bin */ + p = (union mhead *) ap - 1; + + if (p->mh_alloc == ISMEMALIGN) + { + ap -= p->mh_nbytes; + p = (union mhead *) ap - 1; + } + + /* return 0 if ISFREE */ + if (p->mh_alloc == ISFREE) + return 0; + + /* Since we use bounds checking, the usable size is the last requested size. */ + return (p->mh_nbytes); +} + +#if !defined (NO_VALLOC) +/* This runs into trouble with getpagesize on HPUX, and Multimax machines. + Patching out seems cleaner than the ugly fix needed. */ +static PTR_T +internal_valloc (size, file, line, flags) + size_t size; + const char *file; + int line, flags; +{ + return internal_memalign (getpagesize (), size, file, line, flags|MALLOC_INTERNAL); +} +#endif /* !NO_VALLOC */ + +#ifndef NO_CALLOC +static PTR_T +internal_calloc (n, s, file, line, flags) + size_t n, s; + const char *file; + int line, flags; +{ + size_t total; + PTR_T result; + + total = n * s; + result = internal_malloc (total, file, line, flags|MALLOC_INTERNAL); + if (result) + memset (result, 0, total); + return result; +} + +static void +internal_cfree (p, file, line, flags) + PTR_T p; + const char *file; + int line, flags; +{ + internal_free (p, file, line, flags|MALLOC_INTERNAL); +} +#endif /* !NO_CALLOC */ + +#ifdef MALLOC_STATS +int +malloc_free_blocks (size) + int size; +{ + int nfree; + register union mhead *p; + + nfree = 0; + for (p = nextf[size]; p; p = CHAIN (p)) + nfree++; + + return nfree; +} +#endif + +#if defined (MALLOC_WRAPFUNCS) +PTR_T +sh_malloc (bytes, file, line) + size_t bytes; + const char *file; + int line; +{ + return internal_malloc (bytes, file, line, MALLOC_WRAPPER); +} + +PTR_T +sh_realloc (ptr, size, file, line) + PTR_T ptr; + size_t size; + const char *file; + int line; +{ + return internal_realloc (ptr, size, file, line, MALLOC_WRAPPER); +} + +void +sh_free (mem, file, line) + PTR_T mem; + const char *file; + int line; +{ + internal_free (mem, file, line, MALLOC_WRAPPER); +} + +PTR_T +sh_memalign (alignment, size, file, line) + size_t alignment; + size_t size; + const char *file; + int line; +{ + return internal_memalign (alignment, size, file, line, MALLOC_WRAPPER); +} + +#ifndef NO_CALLOC +PTR_T +sh_calloc (n, s, file, line) + size_t n, s; + const char *file; + int line; +{ + return internal_calloc (n, s, file, line, MALLOC_WRAPPER); +} + +void +sh_cfree (mem, file, line) + PTR_T mem; + const char *file; + int line; +{ + internal_cfree (mem, file, line, MALLOC_WRAPPER); +} +#endif + +#ifndef NO_VALLOC +PTR_T +sh_valloc (size, file, line) + size_t size; + const char *file; + int line; +{ + return internal_valloc (size, file, line, MALLOC_WRAPPER); +} +#endif /* !NO_VALLOC */ + +#endif /* MALLOC_WRAPFUNCS */ + +/* Externally-available functions that call their internal counterparts. */ + +PTR_T +malloc (size) + size_t size; +{ + return internal_malloc (size, (char *)NULL, 0, 0); +} + +PTR_T +realloc (mem, nbytes) + PTR_T mem; + size_t nbytes; +{ + return internal_realloc (mem, nbytes, (char *)NULL, 0, 0); +} + +void +free (mem) + PTR_T mem; +{ + internal_free (mem, (char *)NULL, 0, 0); +} + +PTR_T +memalign (alignment, size) + size_t alignment; + size_t size; +{ + return internal_memalign (alignment, size, (char *)NULL, 0, 0); +} + +#ifndef NO_VALLOC +PTR_T +valloc (size) + size_t size; +{ + return internal_valloc (size, (char *)NULL, 0, 0); +} +#endif + +#ifndef NO_CALLOC +PTR_T +calloc (n, s) + size_t n, s; +{ + return internal_calloc (n, s, (char *)NULL, 0, 0); +} + +void +cfree (mem) + PTR_T mem; +{ + internal_cfree (mem, (char *)NULL, 0, 0); +} +#endif diff --git a/lib/malloc/mstats.h b/lib/malloc/mstats.h new file mode 100644 index 0000000..aa3fa33 --- /dev/null +++ b/lib/malloc/mstats.h @@ -0,0 +1,114 @@ +/* mstats.h - definitions for malloc statistics */ + +/* Copyright (C) 2001-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MSTATS_H +#define _MSTATS_H + +#include "imalloc.h" + +#ifdef MALLOC_STATS + +/* This needs to change if the definition in malloc.c changes */ +#ifndef NBUCKETS +# define NBUCKETS 28 +#endif + +/* + * 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). 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. + */ +struct _malstats { + int nmalloc[NBUCKETS]; + int tmalloc[NBUCKETS]; + int nmorecore[NBUCKETS]; + int nlesscore[NBUCKETS]; + int nmal; + int nfre; + int nrealloc; + int nrcopy; + int nrecurse; + int nsbrk; + bits32_t tsbrk; + bits32_t bytesused; + bits32_t bytesfree; + u_bits32_t bytesreq; + int tbsplit; + int nsplit[NBUCKETS]; + int tbcoalesce; + int ncoalesce[NBUCKETS]; + int nmmap; + bits32_t tmmap; +}; + +/* 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. + 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; + int nmmap; /* currently unused */ +}; + +extern struct bucket_stats malloc_bucket_stats PARAMS((int)); +extern struct _malstats malloc_stats PARAMS((void)); +extern void print_malloc_stats PARAMS((char *)); +extern void trace_malloc_stats PARAMS((char *, char *)); + +#endif /* MALLOC_STATS */ + +#endif /* _MSTATS_H */ diff --git a/lib/malloc/shmalloc.h b/lib/malloc/shmalloc.h new file mode 100644 index 0000000..d51193e --- /dev/null +++ b/lib/malloc/shmalloc.h @@ -0,0 +1,70 @@ +/* Functions (currently) for use by the shell to do malloc debugging and + tracking. */ +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SH_MALLOC_H +#define _SH_MALLOC_H + +#ifndef PARAMS +# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus) +# define PARAMS(protos) protos +# else +# define PARAMS(protos) () +# endif +#endif + +/* Generic pointer type. */ +#ifndef PTR_T + +#if defined (__STDC__) +# define PTR_T void * +#else +# define PTR_T char * +#endif + +#endif /* PTR_T */ + + +extern PTR_T sh_malloc PARAMS((size_t, const char *, int)); +extern PTR_T sh_realloc PARAMS((PTR_T, size_t, const char *, int)); +extern void sh_free PARAMS((PTR_T, const char *, int)); + +extern PTR_T sh_memalign PARAMS((size_t, size_t, const char *, int)); + +extern PTR_T sh_calloc PARAMS((size_t, size_t, const char *, int)); +extern void sh_cfree PARAMS((PTR_T, const char *, int)); + +extern PTR_T sh_valloc PARAMS((size_t, const char *, int)); + +/* trace.c */ +extern int malloc_set_trace PARAMS((int)); +extern void malloc_set_tracefp (); /* full prototype requires stdio.h */ +extern void malloc_set_tracefn PARAMS((char *, char *)); + +/* table.c */ +extern void mregister_dump_table PARAMS((void)); +extern void mregister_table_init PARAMS((void)); +extern int malloc_set_register PARAMS((int)); + +/* stats.c */ +extern void print_malloc_stats PARAMS((char *)); +extern void fprint_malloc_stats (); /* full prototype requires stdio.h */ +extern void trace_malloc_stats PARAMS((char *, char *)); + +#endif diff --git a/lib/malloc/stats.c b/lib/malloc/stats.c new file mode 100644 index 0000000..b38df9f --- /dev/null +++ b/lib/malloc/stats.c @@ -0,0 +1,213 @@ +/* stats.c - malloc statistics */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "imalloc.h" + +#ifdef MALLOC_STATS + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <string.h> + +#include "mstats.h" + +extern int malloc_free_blocks PARAMS((int)); + +extern int malloc_mmap_threshold; + +extern struct _malstats _mstats; + +extern FILE *_imalloc_fopen PARAMS((char *, char *, char *, char *, size_t)); + +struct bucket_stats +malloc_bucket_stats (size) + int size; +{ + struct bucket_stats v; + + v.nfree = 0; + + if (size < 0 || size >= NBUCKETS) + { + v.blocksize = 0; + v.nused = v.nmal = v.nmorecore = v.nlesscore = v.nsplit = 0; + return v; + } + + v.blocksize = 1 << (size + 3); + 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 */ + + return v; +} + +/* Return a copy of _MSTATS, with two additional fields filled in: + BYTESFREE is the total number of bytes on free lists. BYTESUSED + is the total number of bytes in use. These two fields are fairly + expensive to compute, so we do it only when asked to. */ +struct _malstats +malloc_stats () +{ + struct _malstats result; + struct bucket_stats v; + register int i; + + result = _mstats; + result.bytesused = result.bytesfree = 0; + for (i = 0; i < NBUCKETS; i++) + { + v = malloc_bucket_stats (i); + result.bytesfree += v.nfree * v.blocksize; + result.bytesused += v.nused * v.blocksize; + } + return (result); +} + +static void +_print_malloc_stats (s, fp) + char *s; + FILE *fp; +{ + register int i; + unsigned long totused, totfree; + struct bucket_stats v; + + 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); + /* Show where the mmap threshold is; sizes greater than this use mmap to + allocate and munmap to free (munmap shows up as lesscore). */ + if (i == malloc_mmap_threshold+1) + fprintf (fp, "--------\n"); + if (v.nmal > 0) + fprintf (fp, "%8lu\t%4d\t%6d\t%5d%8d\t%8d %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", (unsigned long)_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", + _mstats.nsbrk, _mstats.tsbrk); + fprintf (fp, "Total mmaps: %d, total bytes via mmap: %d\n", + _mstats.nmmap, _mstats.tmmap); + fprintf (fp, "Total blocks split: %d, total block coalesces: %d\n", + _mstats.tbsplit, _mstats.tbcoalesce); +} + +void +print_malloc_stats (s) + char *s; +{ + _print_malloc_stats (s, stderr); +} + +void +fprint_malloc_stats (s, fp) + char *s; + FILE *fp; +{ + _print_malloc_stats (s, fp); +} + +#define TRACEROOT "/var/tmp/maltrace/stats." + +void +trace_malloc_stats (s, fn) + char *s, *fn; +{ + FILE *fp; + char defname[sizeof (TRACEROOT) + 64]; + static char mallbuf[1024]; + + fp = _imalloc_fopen (s, fn, TRACEROOT, defname, sizeof (defname)); + if (fp) + { + setvbuf (fp, mallbuf, _IOFBF, sizeof (mallbuf)); + _print_malloc_stats (s, fp); + fflush(fp); + fclose(fp); + } +} + +#endif /* MALLOC_STATS */ + +#if defined (MALLOC_STATS) || defined (MALLOC_TRACE) +FILE * +_imalloc_fopen (s, fn, def, defbuf, defsiz) + char *s; + char *fn; + char *def; + char *defbuf; + size_t defsiz; +{ + char fname[1024]; + long l; + FILE *fp; + + l = (long)getpid (); + if (fn == 0) + { + sprintf (defbuf, "%s%ld", def, l); + fp = fopen(defbuf, "w"); + } + else + { + char *p, *q, *r; + char pidbuf[32]; + int sp; + + sprintf (pidbuf, "%ld", l); + if ((strlen (pidbuf) + strlen (fn) + 2) >= sizeof (fname)) + return ((FILE *)0); + 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"); + } + + return fp; +} +#endif /* MALLOC_STATS || MALLOC_TRACE */ diff --git a/lib/malloc/stub.c b/lib/malloc/stub.c new file mode 100644 index 0000000..a60a624 --- /dev/null +++ b/lib/malloc/stub.c @@ -0,0 +1,22 @@ +/* Copyright (C) 1993-2003 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +void +bash_malloc_stub() +{ +} diff --git a/lib/malloc/table.c b/lib/malloc/table.c new file mode 100644 index 0000000..e6acbf4 --- /dev/null +++ b/lib/malloc/table.c @@ -0,0 +1,429 @@ +/* table.c - bookkeeping functions for allocated memory */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include "imalloc.h" +#include "table.h" + +#ifdef SHELL +extern int running_trap; +extern int signal_is_trapped PARAMS((int)); +#endif + +extern int malloc_register; + +#ifdef MALLOC_REGISTER + +extern FILE *_imalloc_fopen PARAMS((char *, char *, char *, char *, size_t)); + +#define FIND_ALLOC 0x01 /* find slot for new allocation */ +#define FIND_EXIST 0x02 /* find slot for existing entry for free() or search */ + +static int table_count = 0; +static int table_allocated = 0; +static int table_bucket_index = REG_TABLE_SIZE-1; +static mr_table_t mem_table[REG_TABLE_SIZE]; +static mr_table_t mem_overflow; + +#ifndef STREQ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#endif + +static int location_table_index = 0; +static int location_table_count = 0; +static ma_table_t mlocation_table[REG_TABLE_SIZE]; + +/* + * NOTE: taken from dmalloc (http://dmalloc.com) and modified. + */ +static unsigned int +mt_hash (key) + const PTR_T key; +{ + unsigned int a, b, c; + unsigned long x; + + /* set up the internal state */ + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + x = (unsigned long)key; /* truncation is OK */ + b = x >> 8; + c = x >> 3; /* XXX - was >> 4 */ + + HASH_MIX(a, b, c); + return c; +} + +#if 0 +static unsigned int +which_bucket (mem) + PTR_T mem; +{ + return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1)); +} + +#else +#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1)); + +#define next_bucket() ((table_bucket_index + 1) & (REG_TABLE_SIZE-1)) +#define next_entry(mem) ((mem == mem_table + REG_TABLE_SIZE - 1) ? mem_table : ++mem) + +#define prev_bucket() (table_bucket_index == 0 ? REG_TABLE_SIZE-1 : table_bucket_index-1) +#define prev_entry(mem) ((mem == mem_table) ? mem_table + REG_TABLE_SIZE - 1 : mem - 1) +#endif + +static mr_table_t * +find_entry (mem, flags) + PTR_T mem; + int flags; +{ + unsigned int bucket; + register mr_table_t *tp; + mr_table_t *endp; + + if (mem_overflow.mem == mem) + return (&mem_overflow); + + /* If we want to insert an allocation entry just use the next slot */ + if (flags & FIND_ALLOC) + { + table_bucket_index = next_bucket(); + table_count++; + tp = mem_table + table_bucket_index; + memset(tp, 0, sizeof (mr_table_t)); /* overwrite next existing entry */ + return tp; + } + + tp = endp = mem_table + table_bucket_index; + + /* search for last allocation corresponding to MEM, return entry pointer */ + while (1) + { + if (tp->mem == mem) + return (tp); + + tp = prev_entry (tp); + + /* if we went all the way around and didn't find it, return NULL */ + if (tp == endp) + return ((mr_table_t *)NULL); + } + + return (mr_table_t *)NULL; +} + +mr_table_t * +mr_table_entry (mem) + PTR_T mem; +{ + return (find_entry (mem, FIND_EXIST)); +} + +void +mregister_describe_mem (mem, fp) + PTR_T mem; + FILE *fp; +{ + mr_table_t *entry; + + entry = find_entry (mem, FIND_EXIST); + if (entry == 0) + return; + fprintf (fp, "malloc: %p: %s: last %s from %s:%d\n", + mem, + (entry->flags & MT_ALLOC) ? "allocated" : "free", + (entry->flags & MT_ALLOC) ? "allocated" : "freed", + entry->file ? entry->file : "unknown", + entry->line); +} + +void +mregister_alloc (tag, mem, size, file, line) + const char *tag; + PTR_T mem; + size_t size; + const char *file; + int line; +{ + mr_table_t *tentry; + sigset_t set, oset; + int blocked_sigs; + + /* Block all signals in case we are executed from a signal handler. */ + blocked_sigs = 0; +#ifdef SHELL + if (running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD)) +#endif + { + _malloc_block_signals (&set, &oset); + blocked_sigs = 1; + } + + mlocation_register_alloc (file, line); + + tentry = find_entry (mem, FIND_ALLOC); + + if (tentry == 0) + { + /* oops. table is full. punt. */ + fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n")); + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); + return; + } + + if (tentry->flags & MT_ALLOC) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_alloc: %p already in table as allocated?\n"), mem); + } + + tentry->mem = mem; + tentry->size = size; + tentry->func = tag; + tentry->flags = MT_ALLOC; + tentry->file = file; + tentry->line = line; + tentry->nalloc++; + + if (tentry != &mem_overflow) + table_allocated++; + + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); +} + +void +mregister_free (mem, size, file, line) + PTR_T mem; + int size; + const char *file; + int line; +{ + mr_table_t *tentry; + sigset_t set, oset; + int blocked_sigs; + + /* Block all signals in case we are executed from a signal handler. */ + blocked_sigs = 0; +#ifdef SHELL + if (running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD)) +#endif + { + _malloc_block_signals (&set, &oset); + blocked_sigs = 1; + } + + tentry = find_entry (mem, FIND_EXIST); + if (tentry == 0) + { + /* oops. not found. */ +#if 0 + fprintf (stderr, "register_free: %p not in allocation table?\n", mem); +#endif + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); + return; + } + if (tentry->flags & MT_FREE) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_free: %p already in table as free?\n"), mem); + } + + tentry->flags = MT_FREE; + tentry->func = "free"; + tentry->file = file; + tentry->line = line; + tentry->nfree++; + + if (tentry != &mem_overflow) + table_allocated--; + + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); +} + +/* If we ever add more flags, this will require changes. */ +static char * +_entry_flags(x) + int x; +{ + if (x & MT_FREE) + return "free"; + else if (x & MT_ALLOC) + return "allocated"; + else + return "undetermined?"; +} + +static void +_register_dump_table(fp) + FILE *fp; +{ + register int i; + mr_table_t entry; + + for (i = 0; i < REG_TABLE_SIZE; i++) + { + entry = mem_table[i]; + if (entry.mem) + fprintf (fp, "%s[%d] %p:%zu:%s:%s:%s:%d:%d:%d\n", + (i == table_bucket_index) ? "*" : "", + i, + entry.mem, entry.size, + _entry_flags(entry.flags), + entry.func ? entry.func : "unknown", + entry.file ? entry.file : "unknown", + entry.line, + entry.nalloc, entry.nfree); + } +} + +void +mregister_dump_table() +{ + _register_dump_table (stderr); +} + +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; +} + +/* Simple for now */ + +static ma_table_t * +find_location_entry (file, line) + const char *file; + int line; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp <= endp; tp++) + { + if (tp->line == line && STREQ (file, tp->file)) + return tp; + } + return (ma_table_t *)NULL; +} + +void +mlocation_register_alloc (file, line) + const char *file; + int line; +{ + ma_table_t *lentry; + const char *nfile; + + if (file == 0) + { + mlocation_table[0].nalloc++; + return; + } + + nfile = strrchr (file, '/'); + if (nfile) + nfile++; + else + nfile = file; + + lentry = find_location_entry (nfile, line); + if (lentry == 0) + { + location_table_index++; + if (location_table_index == REG_TABLE_SIZE) + location_table_index = 1; /* slot 0 reserved */ + lentry = mlocation_table + location_table_index; + lentry->file = nfile; + lentry->line = line; + lentry->nalloc = 1; + if (location_table_count < REG_TABLE_SIZE) + location_table_count++; /* clamp at REG_TABLE_SIZE for now */ + } + else + lentry->nalloc++; +} + +static void +_location_dump_table (fp) + FILE *fp; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp < endp; tp++) + fprintf (fp, "%s:%d\t%d\n", tp->file ? tp->file : "unknown", + tp->line ? tp->line : 0, + tp->nalloc); +} + +void +mlocation_dump_table () +{ + _location_dump_table (stderr); +} + +#define LOCROOT "/var/tmp/maltrace/locations." + +void +mlocation_write_table () +{ + FILE *fp; + char defname[sizeof (LOCROOT) + 64]; + + fp = _imalloc_fopen ((char *)NULL, (char *)NULL, LOCROOT, defname, sizeof (defname)); + if (fp == 0) + return; /* XXX - no error message yet */ + _location_dump_table (fp); + fclose (fp); +} + +void +mlocation_table_init () +{ + memset (mlocation_table, 0, sizeof (ma_table_t) * REG_TABLE_SIZE); + mlocation_table[0].file = ""; /* reserve slot 0 for unknown locations */ + mlocation_table[0].line = 0; + mlocation_table[0].nalloc = 0; + location_table_count = 1; +} + +#endif /* MALLOC_REGISTER */ + +int +malloc_set_register(n) + int n; +{ + int old; + + old = malloc_register; + malloc_register = n; + return old; +} diff --git a/lib/malloc/table.h b/lib/malloc/table.h new file mode 100644 index 0000000..92866cf --- /dev/null +++ b/lib/malloc/table.h @@ -0,0 +1,116 @@ +/* table.h - definitions for tables for keeping track of allocated memory */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MTABLE_H +#define _MTABLE_H + +#include "imalloc.h" + +#ifdef MALLOC_REGISTER + +/* values for flags byte. */ +#define MT_ALLOC 0x01 +#define MT_FREE 0x02 + +/* + * Memory table entry. + * + * MEM is the address of the allocated pointer. + * SIZE is the requested allocation size. + * FLAGS includes either MT_ALLOC (MEM is allocated) or MT_FREE (MEM is + * not allocated). Other flags later. + * FUNC is set to the name of the function doing the allocation (from the + * `tag' argument to register_alloc(). + * FILE and LINE are the filename and line number of the last allocation + * and free (depending on STATUS) of MEM. + * NALLOC and NFREE are incremented on each allocation that returns MEM or + * each free of MEM, respectively (way to keep track of memory reuse + * and how well the free lists are working). + * + */ +typedef struct mr_table { + PTR_T mem; + size_t size; + char flags; + const char *func; + const char *file; + int line; + int nalloc, nfree; +} mr_table_t; + +#define REG_TABLE_SIZE 8192 + +extern mr_table_t *mr_table_entry PARAMS((PTR_T)); +extern void mregister_alloc PARAMS((const char *, PTR_T, size_t, const char *, int)); +extern void mregister_free PARAMS((PTR_T, int, const char *, int)); +extern void mregister_describe_mem (); +extern void mregister_dump_table PARAMS((void)); +extern void mregister_table_init PARAMS((void)); + +typedef struct ma_table { + const char *file; + int line; + int nalloc; +} ma_table_t; + +extern void mlocation_register_alloc PARAMS((const char *, int)); +extern void mlocation_table_init PARAMS((void)); +extern void mlocation_dump_table PARAMS((void)); +extern void mlocation_write_table PARAMS((void)); + +/* NOTE: HASH_MIX taken from dmalloc (http://dmalloc.com) */ + +/* + * void HASH_MIX + * + * DESCRIPTION: + * + * Mix 3 32-bit values reversibly. For every delta with one or two + * bits set, and the deltas of all three high bits or all three low + * bits, whether the original value of a,b,c is almost all zero or is + * uniformly distributed. + * + * If HASH_MIX() is run forward or backward, at least 32 bits in a,b,c + * have at least 1/4 probability of changing. If mix() is run + * forward, every bit of c will change between 1/3 and 2/3 of the + * time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + * + * HASH_MIX() takes 36 machine instructions, but only 18 cycles on a + * superscalar machine (like a Pentium or a Sparc). No faster mixer + * seems to work, that's the result of my brute-force search. There + * were about 2^68 hashes to choose from. I only tested about a + * billion of those. + */ +#define HASH_MIX(a, b, c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while(0) + +#endif /* MALLOC_REGISTER */ + +#endif /* _MTABLE_H */ diff --git a/lib/malloc/trace.c b/lib/malloc/trace.c new file mode 100644 index 0000000..391ca9d --- /dev/null +++ b/lib/malloc/trace.c @@ -0,0 +1,126 @@ +/* trace.c - tracing functions for malloc */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "imalloc.h" + +extern int malloc_trace; + +static int _mtrace_verbose = 0; + +#ifdef MALLOC_TRACE + +extern FILE *_imalloc_fopen PARAMS((char *, char *, char *, char *, size_t)); + +FILE *_mtrace_fp = NULL; +extern char _malloc_trace_buckets[]; + +void +mtrace_alloc (tag, mem, size, file, line) + const char *tag; + PTR_T mem; + size_t size; + const char *file; + int line; +{ + if (_mtrace_fp == NULL) + _mtrace_fp = stderr; + + if (_mtrace_verbose) + fprintf (_mtrace_fp, "alloc: %s: %p (%zu bytes) from '%s:%d'\n", + tag, mem, size, file ? file : "unknown", line); + else + fprintf (_mtrace_fp, "alloc:%p:%zu:%s:%d\n", + mem, size, file ? file : "unknown", line); +} + +void +mtrace_free (mem, size, file, line) + PTR_T mem; + int size; + const char *file; + int line; +{ + if (_mtrace_fp == NULL) + _mtrace_fp = stderr; + + if (_mtrace_verbose) + fprintf (_mtrace_fp, "free: %p (%d bytes) from '%s:%d'\n", + mem, size, file ? file : "unknown", line); + else + fprintf (_mtrace_fp, "free:%p:%d:%s:%d\n", + mem, size, file ? file : "unknown", line); +} +#endif /* MALLOC_TRACE */ + +int +malloc_set_trace (n) + int n; +{ + int old; + + old = malloc_trace; + malloc_trace = n; + _mtrace_verbose = (n > 1); + return old; +} + +void +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 +} + +#define TRACEROOT "/var/tmp/maltrace/trace." + +void +malloc_set_tracefn (s, fn) + char *s; + char *fn; +{ +#ifdef MALLOC_TRACE + FILE *fp; + char defname[sizeof (TRACEROOT) + 64]; + + fp = _imalloc_fopen (s, fn, TRACEROOT, defname, sizeof (defname)); + if (fp) + malloc_set_tracefp (fp); +#endif +} diff --git a/lib/malloc/watch.c b/lib/malloc/watch.c new file mode 100644 index 0000000..00c8a82 --- /dev/null +++ b/lib/malloc/watch.c @@ -0,0 +1,151 @@ +/* watch.c - watchpoint functions for malloc */ + +/* Copyright (C) 2001-2003 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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 +} diff --git a/lib/malloc/watch.h b/lib/malloc/watch.h new file mode 100644 index 0000000..2a0f497 --- /dev/null +++ b/lib/malloc/watch.h @@ -0,0 +1,41 @@ +/* watch.h - definitions for tables for keeping track of allocated memory */ + +/* Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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 PARAMS((PTR_T, const char *, int, int, unsigned long)); + +#endif /* MALLOC_WATCH */ + +#endif /* _MWATCH_H */ diff --git a/lib/malloc/x386-alloca.s b/lib/malloc/x386-alloca.s new file mode 100644 index 0000000..112d33c --- /dev/null +++ b/lib/malloc/x386-alloca.s @@ -0,0 +1,63 @@ +;; alloca386.s 1.2 +;; GNU-compatible stack allocation function for Xenix/386. +;; Written by Chip Salzenberg at ComDev. +;; Last modified 90/01/11 +;;> Is your alloca clearly better than the one in i386-alloca.s? I haven't +;;> looked at either. +;; +;;They're different because Xenix/386 has a different assembler. SCO +;;Xenix has the Microsoft C compiler and the Microsoft macro assembler, +;;called "masm". MASM's assembler syntax is quite different from AT&T's +;;in all sorts of ways. Xenix people can't use the AT&T version. +;;-- +;;Chip Salzenberg at ComDev/TCT <chip@tct.uucp>, <uunet!ateng!tct!chip> + + TITLE $alloca386 + + .386 +DGROUP GROUP CONST, _BSS, _DATA +_DATA SEGMENT DWORD USE32 PUBLIC 'DATA' +_DATA ENDS +_BSS SEGMENT DWORD USE32 PUBLIC 'BSS' +_BSS ENDS +CONST SEGMENT DWORD USE32 PUBLIC 'CONST' +CONST ENDS +_TEXT SEGMENT DWORD USE32 PUBLIC 'CODE' + ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP + + PUBLIC _alloca +_alloca PROC NEAR + +; Get argument. + pop edx ; edx -> return address + pop eax ; eax = amount to allocate + +; Validate allocation amount. + add eax,3 + and eax,not 3 + cmp eax,0 + jg aa_size_ok + mov eax,4 +aa_size_ok: + +; Allocate stack space. + mov ecx,esp ; ecx -> old stack pointer + sub esp,eax ; perform allocation + mov eax,esp ; eax -> new stack pointer + +; Copy the three saved register variables from old stack top to new stack top. +; They may not be there. So we waste twelve bytes. Big fat hairy deal. + push DWORD PTR 8[ecx] + push DWORD PTR 4[ecx] + push DWORD PTR 0[ecx] + +; Push something so the caller can pop it off. + push eax + +; Return to caller. + jmp edx + +_alloca ENDP + +_TEXT ENDS + END diff --git a/lib/malloc/xleaktrace b/lib/malloc/xleaktrace new file mode 100755 index 0000000..d7e3cd5 --- /dev/null +++ b/lib/malloc/xleaktrace @@ -0,0 +1,47 @@ +#! /usr/bin/awk -f +# +# xleaktrace - print unfreed memory using input generated by compact malloc +# tracing (malloc_set_trace(1)) +# +# NOTE: we ignore `realloc' tags because they're just extra information +# +# Copyright (c) 2001 Chester Ramey +# Permission is hereby granted to deal in this Software without restriction. +# THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. +# +# Chet Ramey +# chet@po.cwru.edu +# +BEGIN { + FS=":"; +} + +$1 == "alloc" { + alloc[$2] = 1; + + size[$2] = $3; + file[$2] = $4; + line[$2] = $5; + +# printf "allocated: %s %d %d %s %d\n", $2, alloc[$2], size[$2], file[$2], line[$2]; + } + +$1 == "free" { + if ($2 in alloc) { + alloc[$2] = 0; +# printf "freed: %s %d\n", $2, alloc[$2]; + } else + printf "freeing unallocated pointer: %s\n", $2; + + } + +END { + printf "unfreed memory\n"; + for (ptr in alloc) { + if (alloc[ptr] == 1) { + printf "%s (%d) from %s:%d\n", ptr, size[ptr], file[ptr], line[ptr]; + } + } +} + + diff --git a/lib/malloc/xmalloc.c b/lib/malloc/xmalloc.c new file mode 100644 index 0000000..f6dec67 --- /dev/null +++ b/lib/malloc/xmalloc.c @@ -0,0 +1,94 @@ +/* xmalloc.c -- safe versions of malloc and realloc */ + +/* Copyright (C) 1991-2003 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +/* Generic pointer type. */ +#ifndef PTR_T + +#if defined (__STDC__) +# define PTR_T void * +#else +# define PTR_T char * +#endif + +#endif /* PTR_T */ + +/* **************************************************************** */ +/* */ +/* Memory Allocation and Deallocation. */ +/* */ +/* **************************************************************** */ + +static void +memory_error_and_abort (fname) + char *fname; +{ + fprintf (stderr, "%s: out of virtual memory\n", fname); + exit (2); +} + +/* Return a pointer to free()able block of memory large enough + to hold BYTES number of bytes. If the memory cannot be allocated, + print an error message and abort. */ +PTR_T +xmalloc (bytes) + size_t bytes; +{ + PTR_T temp; + + temp = malloc (bytes); + if (temp == 0) + memory_error_and_abort ("xmalloc"); + return (temp); +} + +PTR_T +xrealloc (pointer, bytes) + PTR_T pointer; + size_t bytes; +{ + PTR_T temp; + + temp = pointer ? realloc (pointer, bytes) : malloc (bytes); + + if (temp == 0) + memory_error_and_abort ("xrealloc"); + return (temp); +} + +void +xfree (string) + PTR_T string; +{ + if (string) + free (string); +} diff --git a/lib/readline/COPYING b/lib/readline/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/readline/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/lib/readline/ChangeLog b/lib/readline/ChangeLog new file mode 100644 index 0000000..1cf0c00 --- /dev/null +++ b/lib/readline/ChangeLog @@ -0,0 +1,403 @@ +Tue Mar 23 14:36:51 1993 Brian Fox (bfox@eos.crseo.ucsb.edu) + + * readline.c (rl_copy): Changed name to rl_copy_text. + +Mon Mar 22 19:16:05 1993 Brian Fox (bfox@eos.crseo.ucsb.edu) + + * dispose_cmd.c, several other files. Declare dispose_xxx () as + "void". + + * builtins/hashcom.h: Make declarations of hashed_filenames be + "extern" to keep the SGI compiler happy. + + * readline.c (rl_initialize_everything): Assign values to + out_stream and in_stream immediately, since + output_character_function () can be called before + readline_internal () is called. + +Tue Dec 8 09:30:56 1992 Brian Fox (bfox@cubit) + + * readline.c (rl_init_terminal) Set PC from BC, not from *buffer. + +Mon Nov 30 09:35:47 1992 Brian Fox (bfox@cubit) + + * readline.c (invoking_keyseqs_in_map, rl_parse_and_bind) Allow + backslash to quote characters, such as backslash, double quote, + and space. Backslash quotes all character indiscriminately. + + * funmap.c (vi_keymap) Fix type in "vi-replace" declaration. + +Fri Nov 20 10:55:05 1992 Brian Fox (bfox@cubit) + + * readline.c (init_terminal_io, rl_prep_terminal): FINALLY! + Declare and use termcap variable `ospeed' when setting up terminal + parameters. + +Thu Oct 8 08:53:07 1992 Brian J. Fox (bfox@helios) + + * Makefile, this directory: Include (as links to the canonical + sources), tilde.c, tilde.h, posixstat.h and xmalloc.c. + +Tue Sep 29 13:07:21 1992 Brian J. Fox (bfox@helios) + + * readline.c (init_terminal_io) Don't set arrow keys if the key + sequences that represent them are already set. + + * readline.c (rl_function_of_keyseq) New function returns the first + function (or macro) found while searching a key sequence. + +Mon Sep 28 00:34:04 1992 Brian J. Fox (bfox@helios) + + * readline.c (LibraryVersion) New static char * contains current + version number. Version is at 2.0. + + * readline.c (rl_complete_internal): Incorporated clean changes + from gilmore (gnu@cygnus.com) to support quoted substrings within + completion functions. + + * readline.c (many locations) Added support for the _GO32_, + whatever that is. Patches supplied by Cygnus, typed in by hand, + with cleanups. + +Sun Aug 16 12:46:24 1992 Brian Fox (bfox@cubit) + + * readline.c (init_terminal_io): Find out the values of the keypad + arrows and bind them to appropriate RL functions if present. + +Mon Aug 10 18:13:24 1992 Brian Fox (bfox@cubit) + + * history.c (stifle_history): A negative argument to stifle + becomes zero. + +Tue Jul 28 09:28:41 1992 Brian Fox (bfox@cubit) + + * readline.c (rl_variable_bind): New local structure describes + booleans by name and address; code in rl_variable_bind () looks at + structure to set simple variables. + + * parens.c (rl_insert_close): New variable rl_blink_matching_paren + is non-zero if we want to blink the matching open when a close is + inserted. If FD_SET is defined, rl_blink_matching_paren defaults + to 1, else 0. If FD_SET is not defined, and + rl_blink_matching_paren is non-zero, the close character(s) are/is + simply inserted. + +Wed Jul 22 20:03:59 1992 Brian Fox (bfox@cubit) + + * history.c, readline.c, vi_mode.c: Cause the functions strchr () + and strrchr () to be used instead of index () and rindex () + throughout the source. + +Mon Jul 13 11:34:07 1992 Brian Fox (bfox@cubit) + + * readline.c: (rl_variable_bind) New variable "meta-flag" if "on" + means force the use of the 8th bit as Meta bit. Internal variable + is called meta_flag. + +Thu Jul 9 10:37:56 1992 Brian Fox (bfox@cubit) + + * history.c (get_history_event) Change INDEX to LOCAL_INDEX. If + compiling for the shell, allow shell metacharacters to separate + history tokens as they would for shell tokens. + +Sat Jul 4 19:29:12 1992 Brian Fox (bfox@cubit) + + * vi_keymap.c: According to Posix, TAB self-inserts instead of + doing completion. + + * vi_mode.c: (rl_vi_yank_arg) Enter VI insert mode after yanking + an arg from the previous line. + + * search.c: New file takes over vi style searching and implements + non-incremental searching the history. + + Makefile: Add search.c and search.o. + + funmap.c: Add names for non-incremental-forward-search-history and + non-incremental-reverse-search-history. + + readline.h: Add extern definitions for non-incremental searching. + + vi_mode.c: Remove old search code; add calls to code in search.c. + +Fri Jul 3 10:36:33 1992 Brian Fox (bfox@cubit) + + * readline.c (rl_delete_horizontal_space); New function deletes + all whitespace surrounding point. + + funmap.c: Add "delete-horizontal-space". + emacs_keymap.c: Put rl_delete_horizontal_space () on M-\. + + * readline.c (rl_set_signals, rl_clear_signals); New function + rl_set_sighandler () is either defined in a Posix way (if + HAVE_POSIX_SIGNALS is defined) or in a BSD way. Function is + called from rl_set_signals () and rl_clear_signals (). + +Fri May 8 12:50:15 1992 Brian Fox (bfox@cubit) + + * readline.c: (readline_default_bindings) Do comparisons with + _POSIX_VDISABLE casted to `unsigned char'. Change tty characters + to be unsigned char. + +Thu Apr 30 12:36:35 1992 Brian Fox (bfox@cubit) + + * readline.c: (rl_getc) Handle "read would block" error on + non-blocking IO streams. + + * readline.c: (rl_signal_handler): Unblock only the signal that we + have caught, not all signals. + +Sun Feb 23 03:33:09 1992 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: Many functions. Use only the macros META_CHAR and + UNMETA to deal with meta characters. Prior to this, we used + numeric values and tests. + + * readline.c (rl_complete_internal) Report exactly the number of + possible completions, not the number + 1. + + * vi_mode.c (rl_do_move) Do not change the cursor position when + using `cw' or `cW'. + + * vi_mode.c (rl_vi_complete) Enter insert mode after completing + with `*' or `\'. + +Fri Feb 21 05:58:18 1992 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_dispatch) Increment rl_key_sequence_length for + meta characters that map onto ESC map. + +Mon Feb 10 01:41:35 1992 Brian Fox (bfox at gnuwest.fsf.org) + + * history.c (history_do_write) Build a buffer of all of the lines + to write and write them in one fell swoop (lower overhead than + calling write () for each line). Suggested by Peter Ho. + + * readline.c: Include hbullx20 as well as hpux for determining + USGr3ness. + + * readline.c (rl_unix_word_rubout) As per the "Now REMEMBER" + comment, pass arguments to rl_kill_text () in the correct order to + preserve prepending and appending of killed text. + + * readline.c (rl_search_history) malloc (), realloc (), and free + () SEARCH_STRING so that there are no static limits on searching. + + * vi_mode.c (rl_vi_subst) Don't forget to end the undo group. + +Fri Jan 31 14:51:02 1992 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_signal_handler): Zero the current history entry's + pointer after freeing the undo_list when SIGINT received. + Reformat a couple of functions. + +Sat Jan 25 13:47:35 1992 Brian Fox (bfox at bears) + + * readline.c (parser_if): free () TNAME after use. + +Tue Jan 21 01:01:35 1992 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_redisplay) and (rl_character_len): Display + Control characters as "^c" and Meta characters as "\234", instead + of "C-C" and "M-C". + +Sun Dec 29 10:59:00 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (init_terminal_io) Default to environment variables + LINES and COLUMNS before termcap entry values. If all else fails, + then assume 80x24 terminal. + +Sat Dec 28 16:33:11 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: If this machine is USG and it is hpux, then define + USGr3. + + * history.c: Cosmetic fixes. + +Thu Nov 21 00:10:12 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * vi_mode.c: (rl_do_move) Place cursor at end of line, never at + next to last character. + +Thu Nov 14 05:08:01 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * history.c (get_history_event) Non-anchored searches can have a + return index of greater than zero from get_history_event (). + +Fri Nov 1 07:02:13 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_translate_keyseq) Make C-? translate to RUBOUT + unconditionally. + +Mon Oct 28 11:34:52 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c; Use Posix directory routines and macros. + + * funmap.c; Add entry for call-last-kbd-macro. + + * readline.c (rl_prep_term); Use system EOF character on POSIX + systems also. + +Thu Oct 3 16:19:53 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c; Make a distinction between having a TERMIOS tty + driver, and having POSIX signal handling. You might one without + the other. New defines used HAVE_POSIX_SIGNALS, and + TERMIOS_TTY_DRIVER. + +Tue Jul 30 22:37:26 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: rl_getc () If a call to read () returns without an + error, but with zero characters, the file is empty, so return EOF. + +Thu Jul 11 20:58:38 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: (rl_get_next_history, rl_get_previous_history) + Reallocate the buffer space if the line being moved to is longer + the the current space allocated. Amazing that no one has found + this bug until now. + +Sun Jul 7 02:37:05 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c:(rl_parse_and_bind) Allow leading whitespace. + Make sure TERMIO and TERMIOS systems treat CR and NL + disctinctly. + +Tue Jun 25 04:09:27 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: Rework parsing conditionals to pay attention to the + prior states of the conditional stack. This makes $if statements + work correctly. + +Mon Jun 24 20:45:59 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: support for displaying key binding information + includes the functions rl_list_funmap_names (), + invoking_keyseqs_in_map (), rl_invoking_keyseqs (), + rl_dump_functions (), and rl_function_dumper (). + + funmap.c: support for same includes rl_funmap_names (). + + readline.c, funmap.c: no longer define STATIC_MALLOC. However, + update both version of xrealloc () to handle a null pointer. + +Thu Apr 25 12:03:49 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * vi_mode.c (rl_vi_fword, fWord, etc. All functions use + the macro `isident()'. Fixed movement bug which prevents + continious movement through the text. + +Fri Jul 27 16:47:01 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (parser_if) Allow "$if term=foo" construct. + +Wed May 23 16:10:33 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_dispatch) Correctly remember the last command + executed. Fixed typo in username_completion_function (). + +Mon Apr 9 19:55:48 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: username_completion_function (); For text passed in + with a leading `~', remember that this could be a filename (after + it is completed). + +Thu Apr 5 13:44:24 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: rl_search_history (): Correctly handle case of an + unfound search string, but a graceful exit (as with ESC). + + * readline.c: rl_restart_output (); The Apollo passes the address + of the file descriptor to TIOCSTART, not the descriptor itself. + +Tue Mar 20 05:38:55 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: rl_complete (); second call in a row causes possible + completions to be listed. + + * readline.c: rl_redisplay (), added prompt_this_line variable + which is the first character character following \n in prompt. + +Sun Mar 11 04:32:03 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * Signals are now supposedly handled inside of SYSV compilation. + +Wed Jan 17 19:24:09 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * history.c: history_expand (); fixed overwriting memory error, + added needed argument to call to get_history_event (). + +Thu Jan 11 10:54:04 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * readline.c: added mark_modified_lines to control the + display of an asterisk on modified history lines. Also + added a user variable called mark-modified-lines to the + `set' command. + +Thu Jan 4 10:38:05 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * readline.c: start_insert (). Only use IC if we don't have an im + capability. + +Fri Sep 8 09:00:45 1989 Brian Fox (bfox at aurel) + + * readline.c: rl_prep_terminal (). Only turn on 8th bit + as meta-bit iff the terminal is not using parity. + +Sun Sep 3 08:57:40 1989 Brian Fox (bfox at aurel) + + * readline.c: start_insert (). Uses multiple + insertion call in cases where that makes sense. + + rl_insert (). Read type-ahead buffer for additional + keys that are bound to rl_insert, and insert them + all at once. Make insertion of single keys given + with an argument much more efficient. + +Tue Aug 8 18:13:57 1989 Brian Fox (bfox at aurel) + + * readline.c: Changed handling of EOF. readline () returns + (char *)EOF or consed string. The EOF character is read from the + tty, or if the tty doesn't have one, defaults to C-d. + + * readline.c: Added support for event driven programs. + rl_event_hook is the address of a function you want called + while Readline is waiting for input. + + * readline.c: Cleanup time. Functions without type declarations + do not use return with a value. + + * history.c: history_expand () has new variable which is the + characters to ignore immediately following history_expansion_char. + +Sun Jul 16 08:14:00 1989 Brian Fox (bfox at aurel) + + * rl_prep_terminal () + BSD version turns off C-s, C-q, C-y, C-v. + + * readline.c -- rl_prep_terminal () + SYSV version hacks readline_echoing_p. + BSD version turns on passing of the 8th bit for the duration + of reading the line. + +Tue Jul 11 06:25:01 1989 Brian Fox (bfox at aurel) + + * readline.c: new variable rl_tilde_expander. + If non-null, this contains the address of a function to call if + the standard meaning for expanding a tilde fails. The function is + called with the text sans tilde (as in "foo"), and returns a + malloc()'ed string which is the expansion, or a NULL pointer if + there is no expansion. + + * readline.h - new file chardefs.h + Separates things that only readline.c needs from the standard + header file publishing interesting things about readline. + + * readline.c: + readline_default_bindings () now looks at terminal chararacters + and binds those as well. + +Wed Jun 28 20:20:51 1989 Brian Fox (bfox at aurel) + + * Made readline and history into independent libraries. + diff --git a/lib/readline/Makefile.in b/lib/readline/Makefile.in new file mode 100644 index 0000000..3b16139 --- /dev/null +++ b/lib/readline/Makefile.in @@ -0,0 +1,397 @@ +## -*- text -*- ############################################################# +# # +# Makefile for the Bash versions of the GNU Readline and History Libraries. # +# # +############################################################################# + +# Copyright (C) 1994-2012 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ + +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +datarootdir = @datarootdir@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +# Programs to make tags files. +ETAGS = etags -tw +CTAGS = ctags -tw + +DEBUG = @DEBUG@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ ${DEBUG} +STYLE_CFLAGS = @STYLE_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +INCLUDES = -I. -I$(BUILD_DIR) -I$(topdir) -I$(topdir)/lib + +CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(APP_CFLAGS) $(CPPFLAGS) ${INCLUDES} \ + $(STYLE_CFLAGS) $(LOCAL_CFLAGS) $(CFLAGS) ${ADDON_CFLAGS} + +.c.o: + ${RM} $@ + $(CC) -c $(CCFLAGS) $< + +# The name of the main library target. +LIBRARY_NAME = libreadline.a + +# The C code source files for this library. +CSOURCES = $(srcdir)/readline.c $(srcdir)/funmap.c $(srcdir)/keymaps.c \ + $(srcdir)/vi_mode.c $(srcdir)/parens.c $(srcdir)/rltty.c \ + $(srcdir)/complete.c $(srcdir)/bind.c $(srcdir)/isearch.c \ + $(srcdir)/display.c $(srcdir)/signals.c $(srcdir)/emacs_keymap.c \ + $(srcdir)/vi_keymap.c $(srcdir)/util.c $(srcdir)/kill.c \ + $(srcdir)/undo.c $(srcdir)/macro.c $(srcdir)/input.c \ + $(srcdir)/callback.c $(srcdir)/terminal.c $(srcdir)/xmalloc.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)/text.c $(srcdir)/misc.c $(srcdir)/compat.c \ + $(srcdir)/colors.c $(srcdir)/parse-colors.c \ + $(srcdir)/mbutil.c $(srcdir)/xfree.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 rlmbutil.h colors.h parse-colors.h + +HISTOBJ = history.o histexpand.o histfile.o histsearch.o shell.o savestring.o \ + mbutil.o +TILDEOBJ = tilde.o +COLORSOBJ = colors.o parse-colors.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 \ + text.o nls.o misc.o $(HISTOBJ) $(TILDEOBJ) $(COLORSOBJ) \ + xmalloc.o xfree.o compat.o + +# The texinfo files which document this library. +DOCSOURCE = doc/rlman.texinfo doc/rltech.texinfo doc/rluser.texinfo +DOCOBJECT = doc/readline.dvi +DOCSUPPORT = doc/Makefile +DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT) + +SUPPORT = Makefile ChangeLog $(DOCSUPPORT) examples/[-a-z.]* + +SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE) + +THINGS_TO_TAR = $(SOURCES) $(SUPPORT) + +INSTALLED_HEADERS = readline.h chardefs.h keymaps.h history.h tilde.h \ + rlstdc.h rlconf.h rltypedefs.h + +########################################################################## + +all: libreadline.a libhistory.a + +libreadline.a: $(OBJECTS) + $(RM) $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +libhistory.a: $(HISTOBJ) xmalloc.o xfree.o + $(RM) $@ + $(AR) $(ARFLAGS) $@ $(HISTOBJ) xmalloc.o xfree.o + -test -n "$(RANLIB)" && $(RANLIB) $@ + +documentation: force + test -d doc || mkdir doc + -( cd doc && $(MAKE) $(MFLAGS) ) + +# Since tilde.c is shared between readline and bash, make sure we compile +# it with the right flags when it's built as part of readline +tilde.o: tilde.c + rm -f $@ + $(CC) $(CCFLAGS) -DREADLINE_LIBRARY -c $(srcdir)/tilde.c + +force: + +install: + @echo "This version of the readline library should not be installed." + +uninstall: + @echo "This version of the readline library should not be installed." + +TAGS: force + $(ETAGS) $(CSOURCES) $(HSOURCES) + +tags: force + $(CTAGS) $(CSOURCES) $(HSOURCES) + +clean: force + $(RM) $(OBJECTS) *.a + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +mostlyclean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +distclean maintainer-clean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + $(RM) Makefile + $(RM) TAGS tags + +# Dependencies +bind.o: ansi_stdlib.h posixstat.h +bind.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +bind.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +bind.o: history.h rlstdc.h +callback.o: rlconf.h ansi_stdlib.h +callback.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +callback.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +compat.o: ${BUILD_DIR}/config.h +compat.o: rlstdc.h rltypedefs.h +complete.o: ansi_stdlib.h posixdir.h posixstat.h +complete.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +complete.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +complete.o: colors.h +display.o: ansi_stdlib.h posixstat.h +display.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +display.o: tcap.h +display.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +display.o: history.h rlstdc.h +funmap.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +funmap.o: rlconf.h ansi_stdlib.h rlstdc.h +funmap.o: ${BUILD_DIR}/config.h +histexpand.o: ansi_stdlib.h +histexpand.o: history.h histlib.h rlstdc.h +histexpand.o: ${BUILD_DIR}/config.h +histfile.o: ansi_stdlib.h +histfile.o: history.h histlib.h rlstdc.h +histfile.o: ${BUILD_DIR}/config.h +history.o: ansi_stdlib.h +history.o: history.h histlib.h rlstdc.h +history.o: ${BUILD_DIR}/config.h +histsearch.o: ansi_stdlib.h +histsearch.o: history.h histlib.h rlstdc.h +histsearch.o: ${BUILD_DIR}/config.h +input.o: ansi_stdlib.h +input.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +input.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +isearch.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +isearch.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +isearch.o: ansi_stdlib.h history.h rlstdc.h +keymaps.o: emacs_keymap.c vi_keymap.c +keymaps.o: keymaps.h rltypedefs.h chardefs.h rlconf.h ansi_stdlib.h +keymaps.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +keymaps.o: ${BUILD_DIR}/config.h rlstdc.h +kill.o: ansi_stdlib.h +kill.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +kill.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +kill.o: history.h rlstdc.h +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 +nls.o: history.h rlstdc.h +parens.o: rlconf.h +parens.o: ${BUILD_DIR}/config.h +parens.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +readline.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +readline.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +readline.o: history.h rlstdc.h +readline.o: posixstat.h ansi_stdlib.h posixjmp.h +rltty.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +rltty.o: rltty.h +rltty.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +savestring.o: ${BUILD_DIR}/config.h +search.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +search.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +search.o: ansi_stdlib.h history.h rlstdc.h +shell.o: ${BUILD_DIR}/config.h ansi_stdlib.h +signals.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +signals.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +signals.o: history.h rlstdc.h +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 +undo.o: ansi_stdlib.h +undo.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +undo.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +undo.o: history.h rlstdc.h xmalloc.h +util.o: posixjmp.h ansi_stdlib.h +util.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +util.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +vi_mode.o: rldefs.h ${BUILD_DIR}/config.h rlconf.h +vi_mode.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h +vi_mode.o: history.h ansi_stdlib.h rlstdc.h +xmalloc.o: ${BUILD_DIR}/config.h ansi_stdlib.h +xfree.o: ${BUILD_DIR}/config.h ansi_stdlib.h + +colors.o: ${BUILD_DIR}/config.h colors.h +colors.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h +colors.o: rlconf.h +colors.o: ansi_stdlib.h posixstat.h +parse-colors.o: ${BUILD_DIR}/config.h colors.h parse-colors.h +parse-colors.o: rldefs.h rlconf.h +parse-colors.o: readline.h keymaps.h rltypedefs.h chardefs.h tilde.h rlstdc.h + +bind.o: rlshell.h +histfile.o: rlshell.h +nls.o: rlshell.h +readline.o: rlshell.h +shell.o: rlshell.h +terminal.o: rlshell.h +histexpand.o: rlshell.h + +bind.o: rlprivate.h +callback.o: rlprivate.h +complete.o: rlprivate.h +display.o: rlprivate.h +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 +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 +colors.o: rlprivate.h +parse-colors.o: rlprivate.h + +bind.o: xmalloc.h +complete.o: xmalloc.h +display.o: xmalloc.h +funmap.o: xmalloc.h +histexpand.o: xmalloc.h +histfile.o: xmalloc.h +history.o: xmalloc.h +input.o: xmalloc.h +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 +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 +xfree.o: xmalloc.h +xmalloc.o: xmalloc.h +colors.o: xmalloc.h +parse-colors.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 +colors.o: rlmbutil.h +parse-colors.o: rlmbutil.h + +# Rules for deficient makes, like SunOS and Solaris +bind.o: bind.c +callback.o: callback.c +compat.o: compat.c +complete.o: complete.c +display.o: display.c +funmap.o: funmap.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 +rltty.o: rltty.c +savestring.o: savestring.c +search.o: search.c +shell.o: shell.c +signals.o: signals.c +terminal.o: terminal.c +text.o: text.c +tilde.o: tilde.c +undo.o: undo.c +util.o: util.c +vi_mode.o: vi_mode.c +xfree.o: xfree.c +xmalloc.o: xmalloc.c + +colors.o: colors.c +parse-colors.o: parse-colors.c + +histexpand.o: histexpand.c +histfile.o: histfile.c +history.o: history.c +histsearch.o: histsearch.c diff --git a/lib/readline/README b/lib/readline/README new file mode 100644 index 0000000..131471c --- /dev/null +++ b/lib/readline/README @@ -0,0 +1,6 @@ +This is the distribution of the Gnu Readline library. See the file +STANDALONE for a description of the #defines that can be passed via +the makefile to build readline on different systems. + +The file rlconf.h contains defines that enable and disable certain +readline features. diff --git a/lib/readline/STANDALONE b/lib/readline/STANDALONE new file mode 100644 index 0000000..c6b5cbd --- /dev/null +++ b/lib/readline/STANDALONE @@ -0,0 +1,2 @@ +This is not to be built as a standalone library to be installed in some +public place; get the full readline distribution instead. diff --git a/lib/readline/ansi_stdlib.h b/lib/readline/ansi_stdlib.h new file mode 100644 index 0000000..7dc2ee0 --- /dev/null +++ b/lib/readline/ansi_stdlib.h @@ -0,0 +1,54 @@ +/* ansi_stdlib.h -- An ANSI Standard stdlib.h. */ +/* A minimal stdlib.h containing extern declarations for those functions + that bash uses. */ + +/* Copyright (C) 1993 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_STDLIB_H_) +#define _STDLIB_H_ 1 + +/* String conversion functions. */ +extern int atoi (); + +extern double atof (); +extern double strtod (); + +/* Memory allocation functions. */ +/* Generic pointer type. */ +#ifndef PTR_T + +#if defined (__STDC__) +# define PTR_T void * +#else +# define PTR_T char * +#endif + +#endif /* PTR_T */ + +extern PTR_T malloc (); +extern PTR_T realloc (); +extern void free (); + +/* Other miscellaneous functions. */ +extern void abort (); +extern void exit (); +extern char *getenv (); +extern void qsort (); + +#endif /* _STDLIB_H */ diff --git a/lib/readline/bind.c b/lib/readline/bind.c new file mode 100644 index 0000000..971116a --- /dev/null +++ b/lib/readline/bind.c @@ -0,0 +1,3082 @@ +/* bind.c -- key binding and startup file support for the readline library. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# include <floss.h> +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#include "posixstat.h" + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +/* Variables exported by this file. */ +Keymap rl_binding_keymap; + +static int _rl_skip_to_delim (char *, int, int); + +#if defined (USE_VARARGS) && defined (PREFER_STDARG) +static void _rl_init_file_error (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +#else +static void _rl_init_file_error (); +#endif + +static rl_command_func_t *_rl_function_of_keyseq_internal (const char *, size_t, Keymap, int *); + +static char *_rl_read_file (char *, size_t *); +static int _rl_read_init_file (const char *, int); +static int glean_key_from_name (char *); + +static int find_boolean_var (const char *); +static int find_string_var (const char *); + +static const char *boolean_varname (int); +static const char *string_varname (int); + +static char *_rl_get_string_variable_value (const char *); +static int substring_member_of_array (const char *, const char * const *); + +static int _rl_get_keymap_by_name (const char *); +static int _rl_get_keymap_by_map (Keymap); + +static int currently_reading_init_file; + +/* used only in this file */ +static int _rl_prefer_visible_bell = 1; + +#define OP_EQ 1 +#define OP_NE 2 +#define OP_GT 3 +#define OP_GE 4 +#define OP_LT 5 +#define OP_LE 6 + +#define OPSTART(c) ((c) == '=' || (c) == '!' || (c) == '<' || (c) == '>') +#define CMPSTART(c) ((c) == '=' || (c) == '!') + +/* **************************************************************** */ +/* */ +/* Binding keys */ +/* */ +/* **************************************************************** */ + +/* rl_add_defun (char *name, rl_command_func_t *function, int key) + Add NAME to the list of named functions. Make FUNCTION be the function + that gets called. If KEY is not -1, then bind it. */ +int +rl_add_defun (const char *name, rl_command_func_t *function, int key) +{ + if (key != -1) + rl_bind_key (key, function); + rl_add_funmap_entry (name, function); + return 0; +} + +/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ +int +rl_bind_key (int key, rl_command_func_t *function) +{ + char keyseq[4]; + int l; + + if (key < 0 || key > largest_char) + return (key); + + /* Want to make this a multi-character key sequence with an ESC prefix */ + if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) + { + if (_rl_keymap[ESC].type == ISKMAP) + { + Keymap escmap; + + escmap = FUNCTION_TO_KEYMAP (_rl_keymap, ESC); + key = UNMETA (key); + escmap[key].type = ISFUNC; + escmap[key].function = function; + return (0); + } + + /* Otherwise, let's just let rl_generic_bind handle the key sequence. + We start it off with ESC here and let the code below add the rest + of the sequence. */ + keyseq[0] = ESC; + l = 1; + key = UNMETA(key); + goto bind_keyseq; + } + + /* If it's bound to a function or macro, just overwrite. Otherwise we have + to treat it as a key sequence so rl_generic_bind handles shadow keymaps + for us. If we are binding '\' or \C-@ (NUL) make sure to escape it so + it makes it through the call to rl_translate_keyseq. */ + if (_rl_keymap[key].type != ISKMAP) + { + if (_rl_keymap[key].type == ISMACR) + xfree ((char *)_rl_keymap[key].function); + _rl_keymap[key].type = ISFUNC; + _rl_keymap[key].function = function; + } + else + { + l = 0; +bind_keyseq: + if (key == '\\') + { + keyseq[l++] = '\\'; + keyseq[l++] = '\\'; + } + else if (key == '\0') + { + keyseq[l++] = '\\'; + keyseq[l++] = '0'; + } + else + keyseq[l++] = key; + keyseq[l] = '\0'; + rl_bind_keyseq (keyseq, function); + } + rl_binding_keymap = _rl_keymap; + return (0); +} + +/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid + KEY. */ +int +rl_bind_key_in_map (int key, rl_command_func_t *function, Keymap map) +{ + int result; + Keymap oldmap; + + oldmap = _rl_keymap; + _rl_keymap = map; + result = rl_bind_key (key, function); + _rl_keymap = oldmap; + return (result); +} + +/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right + now, this is always used to attempt to bind the arrow keys. */ +int +rl_bind_key_if_unbound_in_map (int key, rl_command_func_t *default_func, Keymap kmap) +{ + char *keyseq; + + keyseq = rl_untranslate_keyseq ((unsigned char)key); + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap)); +} + +int +rl_bind_key_if_unbound (int key, rl_command_func_t *default_func) +{ + char *keyseq; + + keyseq = rl_untranslate_keyseq ((unsigned char)key); + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + +/* Make KEY do nothing in the currently selected keymap. + Returns non-zero in case of error. This is not the same as self-insert; + this makes it a dead key. */ +int +rl_unbind_key (int key) +{ + return (rl_bind_key (key, (rl_command_func_t *)NULL)); +} + +/* Make KEY do nothing in MAP. Returns non-zero in case of error. */ +int +rl_unbind_key_in_map (int key, Keymap map) +{ + return (rl_bind_key_in_map (key, (rl_command_func_t *)NULL, map)); +} + +/* Unbind all keys bound to FUNCTION in MAP. */ +int +rl_unbind_function_in_map (rl_command_func_t *func, Keymap map) +{ + register int i, rval; + + for (i = rval = 0; i < KEYMAP_SIZE; i++) + { + if (map[i].type == ISFUNC && map[i].function == func) + { + map[i].function = (rl_command_func_t *)NULL; + rval = 1; + } + else if (map[i].type == ISKMAP) /* TAG:readline-8.1 */ + { + int r; + r = rl_unbind_function_in_map (func, FUNCTION_TO_KEYMAP (map, i)); + if (r == 1) + rval = 1; + } + } + return rval; +} + +/* Unbind all keys bound to COMMAND, which is a bindable command name, in MAP */ +int +rl_unbind_command_in_map (const char *command, Keymap map) +{ + rl_command_func_t *func; + + func = rl_named_function (command); + if (func == 0) + return 0; + return (rl_unbind_function_in_map (func, map)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION, starting in the current keymap. This makes new + keymaps as necessary. */ +int +rl_bind_keyseq (const char *keyseq, rl_command_func_t *function) +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, _rl_keymap)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION. This makes new keymaps as necessary. The initial + place to do bindings is in MAP. */ +int +rl_bind_keyseq_in_map (const char *keyseq, rl_command_func_t *function, Keymap map) +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); +} + +/* Backwards compatibility; equivalent to rl_bind_keyseq_in_map() */ +int +rl_set_key (const char *keyseq, rl_command_func_t *function, Keymap map) +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); +} + +/* 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. */ +int +rl_bind_keyseq_if_unbound_in_map (const char *keyseq, rl_command_func_t *default_func, Keymap kmap) +{ + rl_command_func_t *func; + char *keys; + int keys_len; + + if (keyseq) + { + /* Handle key sequences that require translations and `raw' ones that + don't. This might be a problem with backslashes. */ + keys = (char *)xmalloc (1 + (2 * strlen (keyseq))); + if (rl_translate_keyseq (keyseq, keys, &keys_len)) + { + xfree (keys); + return -1; + } + func = rl_function_of_keyseq_len (keys, keys_len, kmap, (int *)NULL); + xfree (keys); +#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 + return (rl_bind_keyseq_in_map (keyseq, default_func, kmap)); + else + return 1; + } + return 0; +} + +int +rl_bind_keyseq_if_unbound (const char *keyseq, rl_command_func_t *default_func) +{ + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the string of characters MACRO. This makes new keymaps as + necessary. The initial place to do bindings is in MAP. */ +int +rl_macro_bind (const char *keyseq, const char *macro, Keymap map) +{ + char *macro_keys; + int macro_keys_len; + + macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1); + + if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) + { + xfree (macro_keys); + return -1; + } + rl_generic_bind (ISMACR, keyseq, macro_keys, map); + return 0; +} + +/* Bind the key sequence represented by the string KEYSEQ to + the arbitrary pointer DATA. TYPE says what kind of data is + pointed to by DATA, right now this can be a function (ISFUNC), + a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps + as necessary. The initial place to do bindings is in MAP. */ +int +rl_generic_bind (int type, const char *keyseq, char *data, Keymap map) +{ + char *keys; + int keys_len, prevkey, ic; + register int i; + KEYMAP_ENTRY k; + Keymap prevmap; + + k.function = 0; + + /* If no keys to bind to, exit right away. */ + if (keyseq == 0 || *keyseq == 0) + { + if (type == ISMACR) + xfree (data); + return -1; + } + + keys = (char *)xmalloc (1 + (2 * strlen (keyseq))); + + /* Translate the ASCII representation of KEYSEQ into an array of + characters. Stuff the characters into KEYS, and the length of + KEYS into KEYS_LEN. */ + if (rl_translate_keyseq (keyseq, keys, &keys_len)) + { + xfree (keys); + return -1; + } + + prevmap = map; + prevkey = keys[0]; + + /* Bind keys, making new keymaps as necessary. */ + for (i = 0; i < keys_len; i++) + { + unsigned char uc = keys[i]; + + if (i > 0) + prevkey = ic; + + ic = uc; + if (ic < 0 || ic >= KEYMAP_SIZE) + { + xfree (keys); + return -1; + } + + /* We now rely on rl_translate_keyseq to do this conversion, so this + check is superfluous. */ +#if 0 + if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii) + { + ic = UNMETA (ic); + if (map[ESC].type == ISKMAP) + { + prevmap = map; + map = FUNCTION_TO_KEYMAP (map, ESC); + } + } +#endif + + if ((i + 1) < keys_len) + { + if (map[ic].type != ISKMAP) + { + /* 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()); + } + prevmap = map; + 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 && k.function != rl_do_lowercase_version) || k.type == ISMACR)) + { + map[ANYOTHERKEY] = k; + k.function = 0; + } + } + else + { + if (map[ic].type == ISKMAP) + { + prevmap = map; + map = FUNCTION_TO_KEYMAP (map, ic); + ic = ANYOTHERKEY; + /* If we're trying to override a keymap with a null function + (e.g., trying to unbind it), we can't use a null pointer + here because that's indistinguishable from having not been + overridden. We use a special bindable function that does + nothing. */ + if (type == ISFUNC && data == 0) + data = (char *)_rl_null_function; + } + if (map[ic].type == ISMACR) + xfree ((char *)map[ic].function); + + map[ic].function = KEYMAP_TO_FUNCTION (data); + map[ic].type = type; + } + + rl_binding_keymap = map; + + } + + /* If we unbound a key (type == ISFUNC, data == 0), and the prev keymap + points to the keymap where we unbound the key (sanity check), and the + current binding keymap is empty (rl_empty_keymap() returns non-zero), + and the binding keymap has ANYOTHERKEY set with type == ISFUNC + (overridden function), delete the now-empty keymap, take the previously- + overridden function and remove the override. */ + /* Right now, this only works one level back. */ + if (type == ISFUNC && data == 0 && + prevmap[prevkey].type == ISKMAP && + (FUNCTION_TO_KEYMAP(prevmap, prevkey) == rl_binding_keymap) && + rl_binding_keymap[ANYOTHERKEY].type == ISFUNC && + rl_empty_keymap (rl_binding_keymap)) + { + prevmap[prevkey].type = rl_binding_keymap[ANYOTHERKEY].type; + prevmap[prevkey].function = rl_binding_keymap[ANYOTHERKEY].function; + rl_discard_keymap (rl_binding_keymap); + rl_binding_keymap = prevmap; + } + + xfree (keys); + return 0; +} + +/* Translate the ASCII representation of SEQ, stuffing the values into ARRAY, + an array of characters. LEN gets the final length of ARRAY. Return + non-zero if there was an error parsing SEQ. */ +int +rl_translate_keyseq (const char *seq, char *array, int *len) +{ + register int i, l, temp; + int has_control, has_meta; + unsigned char c; + + has_control = 0; + has_meta = 0; + + /* When there are incomplete prefixes \C- or \M- (has_control || has_meta) + without base character at the end of SEQ, they are processed as the + prefixes for '\0'. + */ + for (i = l = 0; (c = seq[i]) || has_control || has_meta; i++) + { + /* Only backslashes followed by a non-null character are handled + specially. Trailing backslash (backslash followed by '\0') is + processed as a normal character. + */ + if (c == '\\' && seq[i + 1] != '\0') + { + c = seq[++i]; + + /* Handle \C- and \M- prefixes. */ + if (c == 'C' && seq[i + 1] == '-') + { + i++; + has_control = 1; + continue; + } + else if (c == 'M' && seq[i + 1] == '-') + { + i++; + has_meta = 1; + continue; + } + + /* Translate other backslash-escaped characters. These are the + same escape sequences that bash's `echo' and `printf' builtins + handle, with the addition of \d -> RUBOUT. A backslash + preceding a character that is not special is stripped. */ + switch (c) + { + case 'a': + c = '\007'; + break; + case 'b': + c = '\b'; + break; + case 'd': + c = RUBOUT; /* readline-specific */ + break; + case 'e': + c = ESC; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = NEWLINE; + break; + case 'r': + c = RETURN; + break; + case 't': + c = TAB; + break; + case 'v': + c = 0x0B; + break; + case '\\': + c = '\\'; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + i++; + for (temp = 2, c -= '0'; ISOCTAL ((unsigned char)seq[i]) && temp--; i++) + c = (c * 8) + OCTVALUE (seq[i]); + i--; /* auto-increment in for loop */ + c &= largest_char; + break; + case 'x': + i++; + for (temp = 2, c = 0; ISXDIGIT ((unsigned char)seq[i]) && temp--; i++) + c = (c * 16) + HEXVALUE (seq[i]); + if (temp == 2) + c = 'x'; + i--; /* auto-increment in for loop */ + c &= largest_char; + break; + default: /* backslashes before non-special chars just add the char */ + c &= largest_char; + break; /* the backslash is stripped */ + } + } + + /* Process \C- and \M- flags */ + if (has_control) + { + /* Special treatment for C-? */ + c = (c == '?') ? RUBOUT : CTRL (_rl_to_upper (c)); + has_control = 0; + } + if (has_meta) + { + c = META (c); + has_meta = 0; + } + + /* If convert-meta is turned on, convert a meta char to a key sequence */ + if (META_CHAR (c) && _rl_convert_meta_chars_to_ascii) + { + array[l++] = ESC; /* ESC is meta-prefix */ + array[l++] = UNMETA (c); + } + else + array[l++] = (c); + + /* Null characters may be processed for incomplete prefixes at the end of + sequence */ + if (seq[i] == '\0') + break; + } + + *len = l; + array[l] = '\0'; + return (0); +} + +static int +_rl_isescape (int c) +{ + switch (c) + { + case '\007': + case '\b': + case '\f': + case '\n': + case '\r': + case TAB: + case 0x0b: return (1); + default: return (0); + } +} + +static int +_rl_escchar (int c) +{ + switch (c) + { + case '\007': return ('a'); + case '\b': return ('b'); + case '\f': return ('f'); + case '\n': return ('n'); + case '\r': return ('r'); + case TAB: return ('t'); + case 0x0b: return ('v'); + default: return (c); + } +} + +char * +rl_untranslate_keyseq (int seq) +{ + static char kseq[16]; + int i, c; + + i = 0; + c = seq; + if (META_CHAR (c)) + { + kseq[i++] = '\\'; + kseq[i++] = 'M'; + kseq[i++] = '-'; + c = UNMETA (c); + } + else if (c == ESC) + { + kseq[i++] = '\\'; + c = 'e'; + } + else if (CTRL_CHAR (c)) + { + kseq[i++] = '\\'; + kseq[i++] = 'C'; + kseq[i++] = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + else if (c == RUBOUT) + { + kseq[i++] = '\\'; + kseq[i++] = 'C'; + kseq[i++] = '-'; + c = '?'; + } + + if (c == ESC) + { + kseq[i++] = '\\'; + c = 'e'; + } + else if (c == '\\' || c == '"') + { + kseq[i++] = '\\'; + } + + kseq[i++] = (unsigned char) c; + kseq[i] = '\0'; + return kseq; +} + +char * +_rl_untranslate_macro_value (char *seq, int use_escapes) +{ + char *ret, *r, *s; + int c; + + r = ret = (char *)xmalloc (7 * strlen (seq) + 1); + for (s = seq; *s; s++) + { + c = *s; + if (META_CHAR (c)) + { + *r++ = '\\'; + *r++ = 'M'; + *r++ = '-'; + c = UNMETA (c); + } + else if (c == ESC) + { + *r++ = '\\'; + c = 'e'; + } + else if (CTRL_CHAR (c)) + { + *r++ = '\\'; + if (use_escapes && _rl_isescape (c)) + c = _rl_escchar (c); + else + { + *r++ = 'C'; + *r++ = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + } + else if (c == RUBOUT) + { + *r++ = '\\'; + *r++ = 'C'; + *r++ = '-'; + c = '?'; + } + + if (c == ESC) + { + *r++ = '\\'; + c = 'e'; + } + else if (c == '\\' || c == '"') + *r++ = '\\'; + + *r++ = (unsigned char)c; + } + *r = '\0'; + return ret; +} + +/* Return a pointer to the function that STRING represents. + If STRING doesn't have a matching function, then a NULL pointer + is returned. The string match is case-insensitive. */ +rl_command_func_t * +rl_named_function (const char *string) +{ + register int i; + + rl_initialize_funmap (); + + for (i = 0; funmap[i]; i++) + if (_rl_stricmp (funmap[i]->name, string) == 0) + return (funmap[i]->function); + return ((rl_command_func_t *)NULL); +} + +/* Return the function (or macro) definition which would be invoked via + KEYSEQ if executed in MAP. If MAP is NULL, then the current keymap is + used. TYPE, if non-NULL, is a pointer to an int which will receive the + type of the object pointed to. One of ISFUNC (function), ISKMAP (keymap), + or ISMACR (macro). */ +static rl_command_func_t * +_rl_function_of_keyseq_internal (const char *keyseq, size_t len, Keymap map, int *type) +{ + register int i; + + if (map == 0) + map = _rl_keymap; + + for (i = 0; keyseq && i < len; i++) + { + unsigned char ic = keyseq[i]; + + if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii) + { + if (map[ESC].type == ISKMAP) + { + map = FUNCTION_TO_KEYMAP (map, ESC); + ic = UNMETA (ic); + } + /* XXX - should we just return NULL here, since this obviously + doesn't match? */ + else + { + if (type) + *type = map[ESC].type; + + return (map[ESC].function); + } + } + + if (map[ic].type == ISKMAP) + { + /* If this is the last key in the key sequence, return the + map. */ + if (i + 1 == len) + { + if (type) + *type = ISKMAP; + + return (map[ic].function); + } + else + map = FUNCTION_TO_KEYMAP (map, ic); + } + /* If we're not at the end of the key sequence, and the current key + is bound to something other than a keymap, then the entire key + sequence is not bound. */ + else if (map[ic].type != ISKMAP && i+1 < len) + return ((rl_command_func_t *)NULL); + else /* map[ic].type != ISKMAP && i+1 == len */ + { + if (type) + *type = map[ic].type; + + return (map[ic].function); + } + } + return ((rl_command_func_t *) NULL); +} + +rl_command_func_t * +rl_function_of_keyseq (const char *keyseq, Keymap map, int *type) +{ + return _rl_function_of_keyseq_internal (keyseq, strlen (keyseq), map, type); +} + +rl_command_func_t * +rl_function_of_keyseq_len (const char *keyseq, size_t len, Keymap map, int *type) +{ + return _rl_function_of_keyseq_internal (keyseq, len, map, type); +} + +/* Assuming there is a numeric argument at the beginning of KEYSEQ (the + caller is responsible for checking), return the index of the portion of + the key sequence following the numeric argument. If there's no numeric + argument (?), or if KEYSEQ consists solely of a numeric argument (?), + return -1. */ +int +rl_trim_arg_from_keyseq (const char *keyseq, size_t len, Keymap map) +{ + register int i, j, parsing_digits; + unsigned char ic; + Keymap map0; + + if (map == 0) + map = _rl_keymap; + map0 = map; + + /* The digits following the initial one (e.g., the binding to digit-argument) + or the optional `-' in a binding to digit-argument or universal-argument + are not added to rl_executing_keyseq. This is basically everything read by + rl_digit_loop. The parsing_digits logic is here in case they ever are. */ + for (i = j = parsing_digits = 0; keyseq && i < len; i++) + { + ic = keyseq[i]; + + if (parsing_digits) + { + if (_rl_digit_p (ic)) + { + j = i + 1; + continue; + } + parsing_digits = 0; + } + + if (map[ic].type == ISKMAP) + { + if (i + 1 == len) + return -1; + map = FUNCTION_TO_KEYMAP (map, ic); + continue; + } + if (map[ic].type == ISFUNC) + { +#if defined (VI_MODE) + if (map[ic].function != rl_digit_argument && map[ic].function != rl_universal_argument && map[ic].function != rl_vi_arg_digit) +#else + if (map[ic].function != rl_digit_argument && map[ic].function != rl_universal_argument) +#endif + return (j); + + /* We don't bother with a keyseq that is only a numeric argument */ + if (i + 1 == len) + return -1; + + parsing_digits = 1; + + /* This logic should be identical to rl_digit_loop */ + /* We accept M-- as equivalent to M--1, C-u- as equivalent to C-u-1 + but set parsing_digits to 2 to note that we saw `-' */ + if (map[ic].function == rl_universal_argument && (i + 1 == '-')) + { + i++; + parsing_digits = 2; + } + if (map[ic].function == rl_digit_argument && ic == '-') + { + parsing_digits = 2; + } + + map = map0; + j = i + 1; + } + } + + /* If we're still parsing digits by the time we get here, we don't allow a + key sequence that consists solely of a numeric argument */ + return -1; +} + +/* The last key bindings file read. */ +static char *last_readline_init_file = (char *)NULL; + +/* The file we're currently reading key bindings from. */ +static const char *current_readline_init_file; +static int current_readline_init_include_level; +static int current_readline_init_lineno; + +/* Read FILENAME into a locally-allocated buffer and return the buffer. + The size of the buffer is returned in *SIZEP. Returns NULL if any + errors were encountered. */ +static char * +_rl_read_file (char *filename, size_t *sizep) +{ + struct stat finfo; + size_t file_size; + char *buffer; + int i, file; + + file = -1; + if (((file = open (filename, O_RDONLY, 0666)) < 0) || (fstat (file, &finfo) < 0)) + { + if (file >= 0) + close (file); + return ((char *)NULL); + } + + file_size = (size_t)finfo.st_size; + + /* check for overflow on very large files */ + if (file_size != finfo.st_size || file_size + 1 < file_size) + { + if (file >= 0) + close (file); +#if defined (EFBIG) + errno = EFBIG; +#endif + return ((char *)NULL); + } + + /* Read the file into BUFFER. */ + buffer = (char *)xmalloc (file_size + 1); + i = read (file, buffer, file_size); + close (file); + + if (i < 0) + { + xfree (buffer); + return ((char *)NULL); + } + + RL_CHECK_SIGNALS (); + + buffer[i] = '\0'; + if (sizep) + *sizep = i; + + return (buffer); +} + +/* Re-read the current keybindings file. */ +int +rl_re_read_init_file (int count, int ignore) +{ + int r; + r = rl_read_init_file ((const char *)NULL); + rl_set_keymap_from_edit_mode (); + return r; +} + +/* Do key bindings from a file. If FILENAME is NULL it defaults + to the first non-null filename from this list: + 1. the filename used for the previous call + 2. the value of the shell variable `INPUTRC' + 3. ~/.inputrc + 4. /etc/inputrc + If the file existed and could be opened and read, 0 is returned, + otherwise errno is returned. */ +int +rl_read_init_file (const char *filename) +{ + /* Default the filename. */ + if (filename == 0) + filename = last_readline_init_file; + if (filename == 0) + filename = sh_get_env_value ("INPUTRC"); + if (filename == 0 || *filename == 0) + { + filename = DEFAULT_INPUTRC; + /* Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure */ + if (_rl_read_init_file (filename, 0) == 0) + return 0; + filename = SYS_INPUTRC; + } + +#if defined (__MSDOS__) + if (_rl_read_init_file (filename, 0) == 0) + return 0; + filename = "~/_inputrc"; +#endif + return (_rl_read_init_file (filename, 0)); +} + +static int +_rl_read_init_file (const char *filename, int include_level) +{ + register int i; + char *buffer, *openname, *line, *end; + size_t file_size; + + current_readline_init_file = filename; + current_readline_init_include_level = include_level; + + openname = tilde_expand (filename); + buffer = _rl_read_file (openname, &file_size); + xfree (openname); + + RL_CHECK_SIGNALS (); + if (buffer == 0) + return (errno); + + if (include_level == 0 && filename != last_readline_init_file) + { + FREE (last_readline_init_file); + last_readline_init_file = savestring (filename); + } + + currently_reading_init_file = 1; + + /* Loop over the lines in the file. Lines that start with `#' are + comments; all other lines are commands for readline initialization. */ + current_readline_init_lineno = 1; + line = buffer; + end = buffer + file_size; + while (line < end) + { + /* Find the end of this line. */ + for (i = 0; line + i != end && line[i] != '\n'; i++); + +#if defined (__CYGWIN__) + /* ``Be liberal in what you accept.'' */ + if (line[i] == '\n' && line[i-1] == '\r') + line[i - 1] = '\0'; +#endif + + /* Mark end of line. */ + line[i] = '\0'; + + /* Skip leading whitespace. */ + while (*line && whitespace (*line)) + { + line++; + i--; + } + + /* If the line is not a comment, then parse it. */ + if (*line && *line != '#') + rl_parse_and_bind (line); + + /* Move to the next line. */ + line += i + 1; + current_readline_init_lineno++; + } + + xfree (buffer); + currently_reading_init_file = 0; + return (0); +} + +static void +#if defined (PREFER_STDARG) +_rl_init_file_error (const char *format, ...) +#else +_rl_init_file_error (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + fprintf (stderr, "readline: "); + if (currently_reading_init_file) + fprintf (stderr, "%s: line %d: ", current_readline_init_file, + current_readline_init_lineno); + + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + + va_end (args); +} + +/* **************************************************************** */ +/* */ +/* Parser Helper Functions */ +/* */ +/* **************************************************************** */ + +static int +parse_comparison_op (s, indp) + const char *s; + int *indp; +{ + int i, peekc, op; + + if (OPSTART (s[*indp]) == 0) + return -1; + i = *indp; + peekc = s[i] ? s[i+1] : 0; + op = -1; + + if (s[i] == '=') + { + op = OP_EQ; + if (peekc == '=') + i++; + i++; + } + else if (s[i] == '!' && peekc == '=') + { + op = OP_NE; + i += 2; + } + else if (s[i] == '<' && peekc == '=') + { + op = OP_LE; + i += 2; + } + else if (s[i] == '>' && peekc == '=') + { + op = OP_GE; + i += 2; + } + else if (s[i] == '<') + { + op = OP_LT; + i += 1; + } + else if (s[i] == '>') + { + op = OP_GT; + i += 1; + } + + *indp = i; + return op; +} + +/* **************************************************************** */ +/* */ +/* Parser Directives */ +/* */ +/* **************************************************************** */ + +typedef int _rl_parser_func_t (char *); + +/* Things that mean `Control'. */ +const char * const _rl_possible_control_prefixes[] = { + "Control-", "C-", "CTRL-", (const char *)NULL +}; + +const char * const _rl_possible_meta_prefixes[] = { + "Meta", "M-", (const char *)NULL +}; + +/* Forward declarations */ +static int parser_if (char *); +static int parser_else (char *); +static int parser_endif (char *); +static int parser_include (char *); + +/* Conditionals. */ + +/* Calling programs set this to have their argv[0]. */ +const char *rl_readline_name = "other"; + +/* Stack of previous values of parsing_conditionalized_out. */ +static unsigned char *if_stack = (unsigned char *)NULL; +static int if_stack_depth; +static int if_stack_size; + +/* Push _rl_parsing_conditionalized_out, and set parser state based + on ARGS. */ +static int +parser_if (char *args) +{ + int i, llen, boolvar, strvar; + + boolvar = strvar = -1; + + /* Push parser state. */ + if (if_stack_depth + 1 >= if_stack_size) + { + if (!if_stack) + if_stack = (unsigned char *)xmalloc (if_stack_size = 20); + else + if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20); + } + if_stack[if_stack_depth++] = _rl_parsing_conditionalized_out; + + /* If parsing is turned off, then nothing can turn it back on except + for finding the matching endif. In that case, return right now. */ + if (_rl_parsing_conditionalized_out) + return 0; + + llen = strlen (args); + + /* Isolate first argument. */ + for (i = 0; args[i] && !whitespace (args[i]); i++); + + if (args[i]) + args[i++] = '\0'; + + /* Handle "$if term=foo" and "$if mode=emacs" constructs. If this + isn't term=foo, or mode=emacs, then check to see if the first + word in ARGS is the same as the value stored in rl_readline_name. */ + if (rl_terminal_name && _rl_strnicmp (args, "term=", 5) == 0) + { + char *tem, *tname; + + /* Terminals like "aaa-60" are equivalent to "aaa". */ + tname = savestring (rl_terminal_name); + tem = strchr (tname, '-'); + if (tem) + *tem = '\0'; + + /* Test the `long' and `short' forms of the terminal name so that + if someone has a `sun-cmd' and does not want to have bindings + that will be executed if the terminal is a `sun', they can put + `$if term=sun-cmd' into their .inputrc. */ + _rl_parsing_conditionalized_out = _rl_stricmp (args + 5, tname) && + _rl_stricmp (args + 5, rl_terminal_name); + xfree (tname); + } +#if defined (VI_MODE) + else if (_rl_strnicmp (args, "mode=", 5) == 0) + { + int mode; + + if (_rl_stricmp (args + 5, "emacs") == 0) + mode = emacs_mode; + else if (_rl_stricmp (args + 5, "vi") == 0) + mode = vi_mode; + else + mode = no_mode; + + _rl_parsing_conditionalized_out = mode != rl_editing_mode; + } +#endif /* VI_MODE */ + else if (_rl_strnicmp (args, "version", 7) == 0) + { + int rlversion, versionarg, op, previ, major, minor, opresult; + + _rl_parsing_conditionalized_out = 1; + rlversion = RL_VERSION_MAJOR*10 + RL_VERSION_MINOR; + /* if "version" is separated from the operator by whitespace, or the + operand is separated from the operator by whitespace, restore it. + We're more liberal with allowed whitespace for this variable. */ + if (i > 0 && i <= llen && args[i-1] == '\0') + args[i-1] = ' '; + args[llen] = '\0'; /* just in case */ + for (i = 7; whitespace (args[i]); i++) + ; + if (OPSTART(args[i]) == 0) + { + _rl_init_file_error ("comparison operator expected, found `%s'", args[i] ? args + i : "end-of-line"); + return 0; + } + previ = i; + op = parse_comparison_op (args, &i); + if (op <= 0) + { + _rl_init_file_error ("comparison operator expected, found `%s'", args+previ); + return 0; + } + for ( ; args[i] && whitespace (args[i]); i++) + ; + if (args[i] == 0 || _rl_digit_p (args[i]) == 0) + { + _rl_init_file_error ("numeric argument expected, found `%s'", args+i); + return 0; + } + major = minor = 0; + previ = i; + for ( ; args[i] && _rl_digit_p (args[i]); i++) + major = major*10 + _rl_digit_value (args[i]); + if (args[i] == '.') + { + if (args[i + 1] && _rl_digit_p (args [i + 1]) == 0) + { + _rl_init_file_error ("numeric argument expected, found `%s'", args+previ); + return 0; + } + for (++i; args[i] && _rl_digit_p (args[i]); i++) + minor = minor*10 + _rl_digit_value (args[i]); + } + /* optional - check for trailing garbage on the line, allow whitespace + and a trailing comment */ + previ = i; + for ( ; args[i] && whitespace (args[i]); i++) + ; + if (args[i] && args[i] != '#') + { + _rl_init_file_error ("trailing garbage on line: `%s'", args+previ); + return 0; + } + versionarg = major*10 + minor; + + switch (op) + { + case OP_EQ: + opresult = rlversion == versionarg; + break; + case OP_NE: + opresult = rlversion != versionarg; + break; + case OP_GT: + opresult = rlversion > versionarg; + break; + case OP_GE: + opresult = rlversion >= versionarg; + break; + case OP_LT: + opresult = rlversion < versionarg; + break; + case OP_LE: + opresult = rlversion <= versionarg; + break; + } + _rl_parsing_conditionalized_out = 1 - opresult; + } + /* Check to see if the first word in ARGS is the same as the + value stored in rl_readline_name. */ + else if (_rl_stricmp (args, rl_readline_name) == 0) + _rl_parsing_conditionalized_out = 0; + else if ((boolvar = find_boolean_var (args)) >= 0 || (strvar = find_string_var (args)) >= 0) + { + int op, previ; + size_t vlen; + const char *vname; + char *valuearg, *vval, prevc; + + _rl_parsing_conditionalized_out = 1; + vname = (boolvar >= 0) ? boolean_varname (boolvar) : string_varname (strvar); + vlen = strlen (vname); + if (i > 0 && i <= llen && args[i-1] == '\0') + args[i-1] = ' '; + args[llen] = '\0'; /* just in case */ + for (i = vlen; whitespace (args[i]); i++) + ; + if (CMPSTART(args[i]) == 0) + { + _rl_init_file_error ("equality comparison operator expected, found `%s'", args[i] ? args + i : "end-of-line"); + return 0; + } + previ = i; + op = parse_comparison_op (args, &i); + if (op != OP_EQ && op != OP_NE) + { + _rl_init_file_error ("equality comparison operator expected, found `%s'", args+previ); + return 0; + } + for ( ; args[i] && whitespace (args[i]); i++) + ; + if (args[i] == 0) + { + _rl_init_file_error ("argument expected, found `%s'", args+i); + return 0; + } + previ = i; + valuearg = args + i; + for ( ; args[i] && whitespace (args[i]) == 0; i++) + ; + prevc = args[i]; + args[i] = '\0'; /* null-terminate valuearg */ + vval = rl_variable_value (vname); + if (op == OP_EQ) + _rl_parsing_conditionalized_out = _rl_stricmp (vval, valuearg) != 0; + else if (op == OP_NE) + _rl_parsing_conditionalized_out = _rl_stricmp (vval, valuearg) == 0; + args[i] = prevc; + } + else + _rl_parsing_conditionalized_out = 1; + return 0; +} + +/* Invert the current parser state if there is anything on the stack. */ +static int +parser_else (char *args) +{ + register int i; + + if (if_stack_depth == 0) + { + _rl_init_file_error ("$else found without matching $if"); + return 0; + } + +#if 0 + /* Check the previous (n - 1) levels of the stack to make sure that + we haven't previously turned off parsing. */ + for (i = 0; i < if_stack_depth - 1; i++) +#else + /* Check the previous (n) levels of the stack to make sure that + we haven't previously turned off parsing. */ + for (i = 0; i < if_stack_depth; i++) +#endif + if (if_stack[i] == 1) + return 0; + + /* Invert the state of parsing if at top level. */ + _rl_parsing_conditionalized_out = !_rl_parsing_conditionalized_out; + return 0; +} + +/* Terminate a conditional, popping the value of + _rl_parsing_conditionalized_out from the stack. */ +static int +parser_endif (char *args) +{ + if (if_stack_depth) + _rl_parsing_conditionalized_out = if_stack[--if_stack_depth]; + else + _rl_init_file_error ("$endif without matching $if"); + return 0; +} + +static int +parser_include (char *args) +{ + const char *old_init_file; + char *e; + int old_line_number, old_include_level, r; + + if (_rl_parsing_conditionalized_out) + return (0); + + old_init_file = current_readline_init_file; + old_line_number = current_readline_init_lineno; + old_include_level = current_readline_init_include_level; + + e = strchr (args, '\n'); + if (e) + *e = '\0'; + r = _rl_read_init_file ((const char *)args, old_include_level + 1); + + current_readline_init_file = old_init_file; + current_readline_init_lineno = old_line_number; + current_readline_init_include_level = old_include_level; + + return r; +} + +/* Associate textual names with actual functions. */ +static const struct { + const char * const name; + _rl_parser_func_t *function; +} parser_directives [] = { + { "if", parser_if }, + { "endif", parser_endif }, + { "else", parser_else }, + { "include", parser_include }, + { (char *)0x0, (_rl_parser_func_t *)0x0 } +}; + +/* Handle a parser directive. STATEMENT is the line of the directive + without any leading `$'. */ +static int +handle_parser_directive (char *statement) +{ + register int i; + char *directive, *args; + + /* Isolate the actual directive. */ + + /* Skip whitespace. */ + for (i = 0; whitespace (statement[i]); i++); + + directive = &statement[i]; + + for (; statement[i] && !whitespace (statement[i]); i++); + + if (statement[i]) + statement[i++] = '\0'; + + for (; statement[i] && whitespace (statement[i]); i++); + + args = &statement[i]; + + /* Lookup the command, and act on it. */ + for (i = 0; parser_directives[i].name; i++) + if (_rl_stricmp (directive, parser_directives[i].name) == 0) + { + (*parser_directives[i].function) (args); + return (0); + } + + /* display an error message about the unknown parser directive */ + _rl_init_file_error ("%s: unknown parser directive", directive); + return (1); +} + +/* Start at STRING[START] and look for DELIM. Return I where STRING[I] == + DELIM or STRING[I] == 0. DELIM is usually a double quote. */ +static int +_rl_skip_to_delim (char *string, int start, int delim) +{ + int i, c, passc; + + for (i = start,passc = 0; c = string[i]; i++) + { + if (passc) + { + passc = 0; + if (c == 0) + break; + continue; + } + + if (c == '\\') + { + passc = 1; + continue; + } + + if (c == delim) + break; + } + + return i; +} + +/* Read the binding command from STRING and perform it. + A key binding command looks like: Keyname: function-name\0, + a variable binding command looks like: set variable value. + A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */ +int +rl_parse_and_bind (char *string) +{ + char *funname, *kname; + register int c, i; + int key, equivalency, foundmod, foundsep; + + while (string && whitespace (*string)) + string++; + + if (string == 0 || *string == 0 || *string == '#') + return 0; + + /* If this is a parser directive, act on it. */ + if (*string == '$') + { + handle_parser_directive (&string[1]); + return 0; + } + + /* If we aren't supposed to be parsing right now, then we're done. */ + if (_rl_parsing_conditionalized_out) + return 0; + + i = 0; + /* If this keyname is a complex key expression surrounded by quotes, + advance to after the matching close quote. This code allows the + backslash to quote characters in the key expression. */ + if (*string == '"') + { + i = _rl_skip_to_delim (string, 1, '"'); + + /* If we didn't find a closing quote, abort the line. */ + if (string[i] == '\0') + { + _rl_init_file_error ("%s: no closing `\"' in key binding", string); + return 1; + } + else + i++; /* skip past closing double quote */ + } + + /* Advance to the colon (:) or whitespace which separates the two objects. */ + for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ ); + + if (i == 0) + { + _rl_init_file_error ("`%s': invalid key binding: missing key sequence", string); + return 1; + } + + equivalency = (c == ':' && string[i + 1] == '='); + + foundsep = c != 0; + + /* Mark the end of the command (or keyname). */ + if (string[i]) + string[i++] = '\0'; + + /* If doing assignment, skip the '=' sign as well. */ + if (equivalency) + string[i++] = '\0'; + + /* If this is a command to set a variable, then do that. */ + if (_rl_stricmp (string, "set") == 0) + { + char *var, *value, *e; + int s; + + var = string + i; + /* Make VAR point to start of variable name. */ + while (*var && whitespace (*var)) var++; + + /* Make VALUE point to start of value string. */ + value = var; + while (*value && whitespace (*value) == 0) value++; + if (*value) + *value++ = '\0'; + while (*value && whitespace (*value)) value++; + + /* Strip trailing whitespace from values of boolean variables. */ + if (find_boolean_var (var) >= 0) + { + /* just read a whitespace-delimited word or empty string */ + for (e = value; *e && whitespace (*e) == 0; e++) + ; + if (e > value) + *e = '\0'; /* cut off everything trailing */ + } + else if ((i = find_string_var (var)) >= 0) + { + /* Allow quoted strings in variable values */ + if (*value == '"') + { + i = _rl_skip_to_delim (value, 1, *value); + value[i] = '\0'; + value++; /* skip past the quote */ + } + else + { + /* remove trailing whitespace */ + e = value + strlen (value) - 1; + while (e >= value && whitespace (*e)) + e--; + e++; /* skip back to whitespace or EOS */ + + if (*e && e >= value) + *e = '\0'; + } + } + else + { + /* avoid calling rl_variable_bind just to find this out */ + _rl_init_file_error ("%s: unknown variable name", var); + return 1; + } + + rl_variable_bind (var, value); + return 0; + } + + /* Skip any whitespace between keyname and funname. */ + for (; string[i] && whitespace (string[i]); i++); + funname = &string[i]; + + /* Now isolate funname. + For straight function names just look for whitespace, since + that will signify the end of the string. But this could be a + macro definition. In that case, the string is quoted, so skip + to the matching delimiter. We allow the backslash to quote the + delimiter characters in the macro body. */ + /* This code exists to allow whitespace in macro expansions, which + would otherwise be gobbled up by the next `for' loop.*/ + /* XXX - it may be desirable to allow backslash quoting only if " is + the quoted string delimiter, like the shell. */ + if (*funname == '\'' || *funname == '"') + { + i = _rl_skip_to_delim (string, i+1, *funname); + if (string[i]) + i++; + else + { + _rl_init_file_error ("`%s': missing closing quote for macro", funname); + return 1; + } + } + + /* Advance to the end of the string. */ + for (; string[i] && whitespace (string[i]) == 0; i++); + + /* No extra whitespace at the end of the string. */ + string[i] = '\0'; + + /* Handle equivalency bindings here. Make the left-hand side be exactly + whatever the right-hand evaluates to, including keymaps. */ + if (equivalency) + { + return 0; + } + + if (foundsep == 0) + { + _rl_init_file_error ("%s: no key sequence terminator", string); + return 1; + } + + /* If this is a new-style key-binding, then do the binding with + rl_bind_keyseq (). Otherwise, let the older code deal with it. */ + if (*string == '"') + { + char *seq; + register int j, k, passc; + + seq = (char *)xmalloc (1 + strlen (string)); + for (j = 1, k = passc = 0; string[j]; j++) + { + /* Allow backslash to quote characters, but leave them in place. + This allows a string to end with a backslash quoting another + backslash, or with a backslash quoting a double quote. The + backslashes are left in place for rl_translate_keyseq (). */ + if (passc || (string[j] == '\\')) + { + seq[k++] = string[j]; + passc = !passc; + continue; + } + + if (string[j] == '"') + break; + + seq[k++] = string[j]; + } + seq[k] = '\0'; + + /* Binding macro? */ + if (*funname == '\'' || *funname == '"') + { + j = strlen (funname); + + /* Remove the delimiting quotes from each end of FUNNAME. */ + if (j && funname[j - 1] == *funname) + funname[j - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], _rl_keymap); + } + else + rl_bind_keyseq (seq, rl_named_function (funname)); + + xfree (seq); + return 0; + } + + /* Get the actual character we want to deal with. */ + kname = strrchr (string, '-'); + if (kname == 0) + kname = string; + else + kname++; + + key = glean_key_from_name (kname); + + /* Add in control and meta bits. */ + foundmod = 0; + if (substring_member_of_array (string, _rl_possible_control_prefixes)) + { + key = CTRL (_rl_to_upper (key)); + foundmod = 1; + } + + if (substring_member_of_array (string, _rl_possible_meta_prefixes)) + { + key = META (key); + foundmod = 1; + } + + if (foundmod == 0 && kname != string) + { + _rl_init_file_error ("%s: unknown key modifier", string); + return 1; + } + + /* Temporary. Handle old-style keyname with macro-binding. */ + if (*funname == '\'' || *funname == '"') + { + char useq[2]; + int fl = strlen (funname); + + useq[0] = key; useq[1] = '\0'; + if (fl && funname[fl - 1] == *funname) + funname[fl - 1] = '\0'; + + rl_macro_bind (useq, &funname[1], _rl_keymap); + } +#if defined (PREFIX_META_HACK) + /* Ugly, but working hack to keep prefix-meta around. */ + else if (_rl_stricmp (funname, "prefix-meta") == 0) + { + char seq[2]; + + seq[0] = key; + seq[1] = '\0'; + rl_generic_bind (ISKMAP, seq, (char *)emacs_meta_keymap, _rl_keymap); + } +#endif /* PREFIX_META_HACK */ + else + rl_bind_key (key, rl_named_function (funname)); + + return 0; +} + +/* Simple structure for boolean readline variables (i.e., those that can + have one of two values; either "On" or 1 for truth, or "Off" or 0 for + false. */ + +#define V_SPECIAL 0x1 + +static const struct { + const char * const name; + int *value; + int flags; +} boolean_varlist [] = { + { "bind-tty-special-chars", &_rl_bind_stty_chars, 0 }, + { "blink-matching-paren", &rl_blink_matching_paren, V_SPECIAL }, + { "byte-oriented", &rl_byte_oriented, 0 }, +#if defined (COLOR_SUPPORT) + { "colored-completion-prefix",&_rl_colored_completion_prefix, 0 }, + { "colored-stats", &_rl_colored_stats, 0 }, +#endif + { "completion-ignore-case", &_rl_completion_case_fold, 0 }, + { "completion-map-case", &_rl_completion_case_map, 0 }, + { "convert-meta", &_rl_convert_meta_chars_to_ascii, 0 }, + { "disable-completion", &rl_inhibit_completion, 0 }, + { "echo-control-characters", &_rl_echo_control_chars, 0 }, + { "enable-active-region", &_rl_enable_active_region, 0 }, + { "enable-bracketed-paste", &_rl_enable_bracketed_paste, V_SPECIAL }, + { "enable-keypad", &_rl_enable_keypad, 0 }, + { "enable-meta-key", &_rl_enable_meta, 0 }, + { "expand-tilde", &rl_complete_with_tilde_expansion, 0 }, + { "history-preserve-point", &_rl_history_preserve_point, 0 }, + { "horizontal-scroll-mode", &_rl_horizontal_scroll_mode, 0 }, + { "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 }, + { "menu-complete-display-prefix", &_rl_menu_complete_prefix_first, 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 }, + { "revert-all-at-newline", &_rl_revert_all_at_newline, 0 }, + { "show-all-if-ambiguous", &_rl_complete_show_all, 0 }, + { "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 }, + { "show-mode-in-prompt", &_rl_show_mode_in_prompt, 0 }, + { "skip-completed-text", &_rl_skip_completed_text, 0 }, +#if defined (VISIBLE_STATS) + { "visible-stats", &rl_visible_stats, 0 }, +#endif /* VISIBLE_STATS */ + { (char *)NULL, (int *)NULL, 0 } +}; + +static int +find_boolean_var (const char *name) +{ + register int i; + + for (i = 0; boolean_varlist[i].name; i++) + if (_rl_stricmp (name, boolean_varlist[i].name) == 0) + return i; + return -1; +} + +static const char * +boolean_varname (int i) +{ + return ((i >= 0) ? boolean_varlist[i].name : (char *)NULL); +} + +/* Hooks for handling special boolean variables, where a + function needs to be called or another variable needs + to be changed when they're changed. */ +static void +hack_special_boolean_var (int i) +{ + const char *name; + + name = boolean_varlist[i].name; + + if (_rl_stricmp (name, "blink-matching-paren") == 0) + _rl_enable_paren_matching (rl_blink_matching_paren); + else if (_rl_stricmp (name, "prefer-visible-bell") == 0) + { + if (_rl_prefer_visible_bell) + _rl_bell_preference = VISIBLE_BELL; + else + _rl_bell_preference = AUDIBLE_BELL; + } + else if (_rl_stricmp (name, "show-mode-in-prompt") == 0) + _rl_reset_prompt (); + else if (_rl_stricmp (name, "enable-bracketed-paste") == 0) + _rl_enable_active_region = _rl_enable_bracketed_paste; +} + +typedef int _rl_sv_func_t (const char *); + +/* These *must* correspond to the array indices for the appropriate + string variable. (Though they're not used right now.) */ +#define V_BELLSTYLE 0 +#define V_COMBEGIN 1 +#define V_EDITMODE 2 +#define V_ISRCHTERM 3 +#define V_KEYMAP 4 + +#define V_STRING 1 +#define V_INT 2 + +/* Forward declarations */ +static int sv_region_start_color (const char *); +static int sv_region_end_color (const char *); +static int sv_bell_style (const char *); +static int sv_combegin (const char *); +static int sv_dispprefix (const char *); +static int sv_compquery (const char *); +static int sv_compwidth (const char *); +static int sv_editmode (const char *); +static int sv_emacs_modestr (const char *); +static int sv_histsize (const char *); +static int sv_isrchterm (const char *); +static int sv_keymap (const char *); +static int sv_seqtimeout (const char *); +static int sv_viins_modestr (const char *); +static int sv_vicmd_modestr (const char *); + +static const struct { + const char * const name; + int flags; + _rl_sv_func_t *set_func; +} string_varlist[] = { + { "active-region-end-color", V_STRING, sv_region_end_color }, + { "active-region-start-color", V_STRING, sv_region_start_color }, + { "bell-style", V_STRING, sv_bell_style }, + { "comment-begin", V_STRING, sv_combegin }, + { "completion-display-width", V_INT, sv_compwidth }, + { "completion-prefix-display-length", V_INT, sv_dispprefix }, + { "completion-query-items", V_INT, sv_compquery }, + { "editing-mode", V_STRING, sv_editmode }, + { "emacs-mode-string", V_STRING, sv_emacs_modestr }, + { "history-size", V_INT, sv_histsize }, + { "isearch-terminators", V_STRING, sv_isrchterm }, + { "keymap", V_STRING, sv_keymap }, + { "keyseq-timeout", V_INT, sv_seqtimeout }, + { "vi-cmd-mode-string", V_STRING, sv_vicmd_modestr }, + { "vi-ins-mode-string", V_STRING, sv_viins_modestr }, + { (char *)NULL, 0, (_rl_sv_func_t *)0 } +}; + +static int +find_string_var (const char *name) +{ + register int i; + + for (i = 0; string_varlist[i].name; i++) + if (_rl_stricmp (name, string_varlist[i].name) == 0) + return i; + return -1; +} + +static const char * +string_varname (int i) +{ + return ((i >= 0) ? string_varlist[i].name : (char *)NULL); +} + +/* A boolean value that can appear in a `set variable' command is true if + the value is null or empty, `on' (case-insensitive), or "1". All other + values result in 0 (false). */ +static int +bool_to_int (const char *value) +{ + return (value == 0 || *value == '\0' || + (_rl_stricmp (value, "on") == 0) || + (value[0] == '1' && value[1] == '\0')); +} + +char * +rl_variable_value (const char *name) +{ + register int i; + + /* Check for simple variables first. */ + i = find_boolean_var (name); + if (i >= 0) + return (*boolean_varlist[i].value ? "on" : "off"); + + i = find_string_var (name); + if (i >= 0) + return (_rl_get_string_variable_value (string_varlist[i].name)); + + /* Unknown variable names return NULL. */ + return (char *)NULL; +} + +int +rl_variable_bind (const char *name, const char *value) +{ + register int i; + int v; + + /* Check for simple variables first. */ + i = find_boolean_var (name); + if (i >= 0) + { + *boolean_varlist[i].value = bool_to_int (value); + if (boolean_varlist[i].flags & V_SPECIAL) + hack_special_boolean_var (i); + return 0; + } + + i = find_string_var (name); + + /* For the time being, string names without a handler function are simply + ignored. */ + if (i < 0 || string_varlist[i].set_func == 0) + { + if (i < 0) + _rl_init_file_error ("%s: unknown variable name", name); + return 0; + } + + v = (*string_varlist[i].set_func) (value); + if (v != 0) + _rl_init_file_error ("%s: could not set value to `%s'", name, value); + return v; +} + +static int +sv_editmode (const char *value) +{ + if (_rl_strnicmp (value, "vi", 2) == 0) + { +#if defined (VI_MODE) + _rl_keymap = vi_insertion_keymap; + rl_editing_mode = vi_mode; +#endif /* VI_MODE */ + return 0; + } + else if (_rl_strnicmp (value, "emacs", 5) == 0) + { + _rl_keymap = emacs_standard_keymap; + rl_editing_mode = emacs_mode; + return 0; + } + return 1; +} + +static int +sv_combegin (const char *value) +{ + if (value && *value) + { + FREE (_rl_comment_begin); + _rl_comment_begin = savestring (value); + return 0; + } + return 1; +} + +static int +sv_dispprefix (const char *value) +{ + int nval = 0; + + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + _rl_completion_prefix_display_length = nval; + return 0; +} + +static int +sv_compquery (const char *value) +{ + int nval = 100; + + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + rl_completion_query_items = nval; + return 0; +} + +static int +sv_compwidth (const char *value) +{ + int nval = -1; + + if (value && *value) + nval = atoi (value); + + _rl_completion_columns = nval; + return 0; +} + +static int +sv_histsize (const char *value) +{ + int nval; + + nval = 500; + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + { + unstifle_history (); + return 0; + } + } + stifle_history (nval); + return 0; +} + +static int +sv_keymap (const char *value) +{ + Keymap kmap; + + kmap = rl_get_keymap_by_name (value); + if (kmap) + { + rl_set_keymap (kmap); + return 0; + } + return 1; +} + +static int +sv_seqtimeout (const char *value) +{ + int nval; + + nval = 0; + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + _rl_keyseq_timeout = nval; + return 0; +} + +static int +sv_region_start_color (const char *value) +{ + return (_rl_reset_region_color (0, value)); +} + +static int +sv_region_end_color (const char *value) +{ + return (_rl_reset_region_color (1, value)); +} + +static int +sv_bell_style (const char *value) +{ + if (value == 0 || *value == '\0') + _rl_bell_preference = AUDIBLE_BELL; + else if (_rl_stricmp (value, "none") == 0 || _rl_stricmp (value, "off") == 0) + _rl_bell_preference = NO_BELL; + else if (_rl_stricmp (value, "audible") == 0 || _rl_stricmp (value, "on") == 0) + _rl_bell_preference = AUDIBLE_BELL; + else if (_rl_stricmp (value, "visible") == 0) + _rl_bell_preference = VISIBLE_BELL; + else + return 1; + return 0; +} + +static int +sv_isrchterm (const char *value) +{ + int beg, end, delim; + char *v; + + if (value == 0) + return 1; + + /* Isolate the value and translate it into a character string. */ + v = savestring (value); + FREE (_rl_isearch_terminators); + if (v[0] == '"' || v[0] == '\'') + { + delim = v[0]; + for (beg = end = 1; v[end] && v[end] != delim; end++) + ; + } + else + { + for (beg = end = 0; v[end] && whitespace (v[end]) == 0; end++) + ; + } + + v[end] = '\0'; + + /* The value starts at v + beg. Translate it into a character string. */ + _rl_isearch_terminators = (char *)xmalloc (2 * strlen (v) + 1); + rl_translate_keyseq (v + beg, _rl_isearch_terminators, &end); + _rl_isearch_terminators[end] = '\0'; + + xfree (v); + return 0; +} + +extern char *_rl_emacs_mode_str; + +static int +sv_emacs_modestr (const char *value) +{ + if (value && *value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_emacs_mode_str, &_rl_emacs_modestr_len); + _rl_emacs_mode_str[_rl_emacs_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (1); + _rl_emacs_mode_str[_rl_emacs_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_emacs_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_viins_modestr (const char *value) +{ + if (value && *value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_ins_mode_str, &_rl_vi_ins_modestr_len); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (1); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_ins_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_vicmd_modestr (const char *value) +{ + if (value && *value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_cmd_mode_str, &_rl_vi_cmd_modestr_len); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (1); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_cmd_modestr_len = 0; + return 0; + } + return 1; +} + +/* Return the character which matches NAME. + For example, `Space' returns ' '. */ + +typedef struct { + const char * const name; + int value; +} assoc_list; + +static const assoc_list name_key_alist[] = { + { "DEL", 0x7f }, + { "ESC", '\033' }, + { "Escape", '\033' }, + { "LFD", '\n' }, + { "Newline", '\n' }, + { "RET", '\r' }, + { "Return", '\r' }, + { "Rubout", 0x7f }, + { "SPC", ' ' }, + { "Space", ' ' }, + { "Tab", 0x09 }, + { (char *)0x0, 0 } +}; + +static int +glean_key_from_name (char *name) +{ + register int i; + + for (i = 0; name_key_alist[i].name; i++) + if (_rl_stricmp (name, name_key_alist[i].name) == 0) + return (name_key_alist[i].value); + + return (*(unsigned char *)name); /* XXX was return (*name) */ +} + +/* Auxiliary functions to manage keymaps. */ +struct name_and_keymap { + char *name; + Keymap map; +}; + +static struct name_and_keymap builtin_keymap_names[] = { + { "emacs", emacs_standard_keymap }, + { "emacs-standard", emacs_standard_keymap }, + { "emacs-meta", emacs_meta_keymap }, + { "emacs-ctlx", emacs_ctlx_keymap }, +#if defined (VI_MODE) + { "vi", vi_movement_keymap }, + { "vi-move", vi_movement_keymap }, + { "vi-command", vi_movement_keymap }, + { "vi-insert", vi_insertion_keymap }, +#endif /* VI_MODE */ + { (char *)0x0, (Keymap)0x0 } +}; + +/* -1 for NULL entry */ +#define NUM_BUILTIN_KEYMAPS (sizeof (builtin_keymap_names) / sizeof (builtin_keymap_names[0]) - 1) + +static struct name_and_keymap *keymap_names = builtin_keymap_names; + +static int +_rl_get_keymap_by_name (const char *name) +{ + register int i; + + for (i = 0; keymap_names[i].name; i++) + if (_rl_stricmp (name, keymap_names[i].name) == 0) + return (i); + return -1; +} + +Keymap +rl_get_keymap_by_name (const char *name) +{ + int i; + + i = _rl_get_keymap_by_name (name); + return ((i >= 0) ? keymap_names[i].map : (Keymap) NULL); +} + +static int +_rl_get_keymap_by_map (Keymap map) +{ + register int i; + + for (i = 0; keymap_names[i].name; i++) + if (map == keymap_names[i].map) + return (i); + return -1; +} + +char * +rl_get_keymap_name (Keymap map) +{ + int i; + + i = _rl_get_keymap_by_map (map); + return ((i >= 0) ? keymap_names[i].name : (char *)NULL); +} + +int +rl_set_keymap_name (const char *name, Keymap map) +{ + int i, ni, mi; + + /* First check whether or not we're trying to rename a builtin keymap */ + mi = _rl_get_keymap_by_map (map); + if (mi >= 0 && mi < NUM_BUILTIN_KEYMAPS) + return -1; + + /* Then reject attempts to set one of the builtin names to a new map */ + ni = _rl_get_keymap_by_name (name); + if (ni >= 0 && ni < NUM_BUILTIN_KEYMAPS) + return -1; + + /* Renaming a keymap we already added */ + if (mi >= 0) /* XXX - could be >= NUM_BUILTIN_KEYMAPS */ + { + xfree (keymap_names[mi].name); + keymap_names[mi].name = savestring (name); + return mi; + } + + /* Associating new keymap with existing name */ + if (ni >= 0) + { + keymap_names[ni].map = map; + return ni; + } + + for (i = 0; keymap_names[i].name; i++) + ; + + if (keymap_names == builtin_keymap_names) + { + keymap_names = xmalloc ((i + 2) * sizeof (struct name_and_keymap)); + memcpy (keymap_names, builtin_keymap_names, i * sizeof (struct name_and_keymap)); + } + else + keymap_names = xrealloc (keymap_names, (i + 2) * sizeof (struct name_and_keymap)); + + keymap_names[i].name = savestring (name); + keymap_names[i].map = map; + + keymap_names[i+1].name = NULL; + keymap_names[i+1].map = NULL; + + return i; +} + +void +rl_set_keymap (Keymap map) +{ + if (map) + _rl_keymap = map; +} + +Keymap +rl_get_keymap (void) +{ + return (_rl_keymap); +} + +void +rl_set_keymap_from_edit_mode (void) +{ + if (rl_editing_mode == emacs_mode) + _rl_keymap = emacs_standard_keymap; +#if defined (VI_MODE) + else if (rl_editing_mode == vi_mode) + _rl_keymap = vi_insertion_keymap; +#endif /* VI_MODE */ +} + +char * +rl_get_keymap_name_from_edit_mode (void) +{ + if (rl_editing_mode == emacs_mode) + return "emacs"; +#if defined (VI_MODE) + else if (rl_editing_mode == vi_mode) + return "vi"; +#endif /* VI_MODE */ + else + return "none"; +} + +/* **************************************************************** */ +/* */ +/* Key Binding and Function Information */ +/* */ +/* **************************************************************** */ + +/* Each of the following functions produces information about the + state of keybindings and functions known to Readline. The info + is always printed to rl_outstream, and in such a way that it can + be read back in (i.e., passed to rl_parse_and_bind ()). */ + +/* Print the names of functions known to Readline. */ +void +rl_list_funmap_names (void) +{ + register int i; + const char **funmap_names; + + funmap_names = rl_funmap_names (); + + if (!funmap_names) + return; + + for (i = 0; funmap_names[i]; i++) + fprintf (rl_outstream, "%s\n", funmap_names[i]); + + xfree (funmap_names); +} + +static char * +_rl_get_keyname (int key) +{ + char *keyname; + int i, c; + + keyname = (char *)xmalloc (8); + + c = key; + /* Since this is going to be used to write out keysequence-function + pairs for possible inclusion in an inputrc file, we don't want to + do any special meta processing on KEY. */ + +#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) + { + keyname[0] = '\\'; + keyname[1] = 'e'; + keyname[2] = '\0'; + return keyname; + } +#endif + + /* RUBOUT is translated directly into \C-? */ + if (key == RUBOUT) + { + keyname[0] = '\\'; + keyname[1] = 'C'; + keyname[2] = '-'; + keyname[3] = '?'; + keyname[4] = '\0'; + return keyname; + } + + i = 0; + /* Now add special prefixes needed for control characters. This can + potentially change C. */ + if (CTRL_CHAR (c)) + { + keyname[i++] = '\\'; + keyname[i++] = 'C'; + keyname[i++] = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + + /* XXX experimental code. Turn the characters that are not ASCII or + ISO Latin 1 (128 - 159) into octal escape sequences (\200 - \237). + This changes C. */ + if (c >= 128 && c <= 159) + { + keyname[i++] = '\\'; + keyname[i++] = '2'; + c -= 128; + keyname[i++] = (c / 8) + '0'; + c = (c % 8) + '0'; + } + /* These characters are valid UTF-8; convert them into octal escape + sequences as well. This changes C. */ + else if (c >= 160) + { + keyname[i++] = '\\'; + keyname[i++] = '0' + ((((unsigned char)c) >> 6) & 0x07); + keyname[i++] = '0' + ((((unsigned char)c) >> 3) & 0x07); + c = (c % 8) + '0'; + } + + /* Now, if the character needs to be quoted with a backslash, do that. */ + if (c == '\\' || c == '"') + keyname[i++] = '\\'; + + /* Now add the key, terminate the string, and return it. */ + keyname[i++] = (char) c; + keyname[i] = '\0'; + + return keyname; +} + +/* Return a NULL terminated array of strings which represent the key + sequences that are used to invoke FUNCTION in MAP. */ +char ** +rl_invoking_keyseqs_in_map (rl_command_func_t *function, Keymap map) +{ + register int key; + char **result; + int result_index, result_size; + + result = (char **)NULL; + result_index = result_size = 0; + + for (key = 0; key < KEYMAP_SIZE; key++) + { + switch (map[key].type) + { + case ISMACR: + /* Macros match, if, and only if, the pointers are identical. + Thus, they are treated exactly like functions in here. */ + case ISFUNC: + /* If the function in the keymap is the one we are looking for, + then add the current KEY to the list of invoking keys. */ + if (map[key].function == function) + { + char *keyname; + + keyname = _rl_get_keyname (key); + + if (result_index + 2 > result_size) + { + result_size += 10; + result = (char **)xrealloc (result, result_size * sizeof (char *)); + } + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + break; + + case ISKMAP: + { + char **seqs; + register int i; + + /* Find the list of keyseqs in this map which have FUNCTION as + their target. Add the key sequences found to RESULT. */ + if (map[key].function) + seqs = + rl_invoking_keyseqs_in_map (function, FUNCTION_TO_KEYMAP (map, key)); + else + break; + + if (seqs == 0) + break; + + for (i = 0; seqs[i]; i++) + { + char *keyname = (char *)xmalloc (6 + strlen (seqs[i])); + + if (key == ESC) + { + /* If ESC is the meta prefix and we're converting chars + with the eighth bit set to ESC-prefixed sequences, then + we can use \M-. Otherwise we need to use the sequence + for ESC. */ + if (_rl_convert_meta_chars_to_ascii && map[ESC].type == ISKMAP) + sprintf (keyname, "\\M-"); + else + sprintf (keyname, "\\e"); + } + else + { + int c = key, l = 0; + if (CTRL_CHAR (c) || c == RUBOUT) + { + keyname[l++] = '\\'; + keyname[l++] = 'C'; + keyname[l++] = '-'; + c = (c == RUBOUT) ? '?' : _rl_to_lower (UNCTRL (c)); + } + + if (c == '\\' || c == '"') + keyname[l++] = '\\'; + + keyname[l++] = (char) c; + keyname[l++] = '\0'; + } + + strcat (keyname, seqs[i]); + xfree (seqs[i]); + + if (result_index + 2 > result_size) + { + result_size += 10; + result = (char **)xrealloc (result, result_size * sizeof (char *)); + } + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + + xfree (seqs); + } + break; + } + } + return (result); +} + +/* Return a NULL terminated array of strings which represent the key + sequences that can be used to invoke FUNCTION using the current keymap. */ +char ** +rl_invoking_keyseqs (rl_command_func_t *function) +{ + return (rl_invoking_keyseqs_in_map (function, _rl_keymap)); +} + +/* Print all of the functions and their bindings to rl_outstream. If + PRINT_READABLY is non-zero, then print the output in such a way + that it can be read back in. */ +void +rl_function_dumper (int print_readably) +{ + register int i; + const char **names; + const char *name; + + names = rl_funmap_names (); + + fprintf (rl_outstream, "\n"); + + for (i = 0; name = names[i]; i++) + { + rl_command_func_t *function; + char **invokers; + + function = rl_named_function (name); + invokers = rl_invoking_keyseqs_in_map (function, _rl_keymap); + + if (print_readably) + { + if (!invokers) + fprintf (rl_outstream, "# %s (not bound)\n", name); + else + { + register int j; + + for (j = 0; invokers[j]; j++) + { + fprintf (rl_outstream, "\"%s\": %s\n", + invokers[j], name); + xfree (invokers[j]); + } + + xfree (invokers); + } + } + else + { + if (!invokers) + fprintf (rl_outstream, "%s is not bound to any keys\n", + name); + else + { + register int j; + + fprintf (rl_outstream, "%s can be found on ", name); + + for (j = 0; invokers[j] && j < 5; j++) + { + fprintf (rl_outstream, "\"%s\"%s", invokers[j], + invokers[j + 1] ? ", " : ".\n"); + } + + if (j == 5 && invokers[j]) + fprintf (rl_outstream, "...\n"); + + for (j = 0; invokers[j]; j++) + xfree (invokers[j]); + + xfree (invokers); + } + } + } + + xfree (names); +} + +/* Print all of the current functions and their bindings to + rl_outstream. If an explicit argument is given, then print + the output in such a way that it can be read back in. */ +int +rl_dump_functions (int count, int key) +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_function_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +static void +_rl_macro_dumper_internal (int print_readably, Keymap map, char *prefix) +{ + register int key; + char *keyname, *out; + int prefix_len; + + for (key = 0; key < KEYMAP_SIZE; key++) + { + switch (map[key].type) + { + case ISMACR: + keyname = _rl_get_keyname (key); + out = _rl_untranslate_macro_value ((char *)map[key].function, 0); + + if (print_readably) + fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "", + keyname, + out ? out : ""); + else + fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "", + keyname, + out ? out : ""); + xfree (keyname); + xfree (out); + break; + case ISFUNC: + break; + case ISKMAP: + prefix_len = prefix ? strlen (prefix) : 0; + if (key == ESC) + { + keyname = (char *)xmalloc (3 + prefix_len); + if (prefix) + strcpy (keyname, prefix); + keyname[prefix_len] = '\\'; + keyname[prefix_len + 1] = 'e'; + keyname[prefix_len + 2] = '\0'; + } + else + { + keyname = _rl_get_keyname (key); + if (prefix) + { + out = (char *)xmalloc (strlen (keyname) + prefix_len + 1); + strcpy (out, prefix); + strcpy (out + prefix_len, keyname); + xfree (keyname); + keyname = out; + } + } + + _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname); + xfree (keyname); + break; + } + } +} + +void +rl_macro_dumper (int print_readably) +{ + _rl_macro_dumper_internal (print_readably, _rl_keymap, (char *)NULL); +} + +int +rl_dump_macros (int count, int key) +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_macro_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +static char * +_rl_get_string_variable_value (const char *name) +{ + static char numbuf[32]; + char *ret; + + if (_rl_stricmp (name, "bell-style") == 0) + { + switch (_rl_bell_preference) + { + case NO_BELL: + return "none"; + case VISIBLE_BELL: + return "visible"; + case AUDIBLE_BELL: + default: + return "audible"; + } + } + else if (_rl_stricmp (name, "comment-begin") == 0) + return (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT); + else if (_rl_stricmp (name, "completion-display-width") == 0) + { + sprintf (numbuf, "%d", _rl_completion_columns); + return (numbuf); + } + else if (_rl_stricmp (name, "completion-prefix-display-length") == 0) + { + sprintf (numbuf, "%d", _rl_completion_prefix_display_length); + return (numbuf); + } + else if (_rl_stricmp (name, "completion-query-items") == 0) + { + sprintf (numbuf, "%d", rl_completion_query_items); + return (numbuf); + } + else if (_rl_stricmp (name, "editing-mode") == 0) + return (rl_get_keymap_name_from_edit_mode ()); + else if (_rl_stricmp (name, "history-size") == 0) + { + sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0); + return (numbuf); + } + else if (_rl_stricmp (name, "isearch-terminators") == 0) + { + if (_rl_isearch_terminators == 0) + return 0; + ret = _rl_untranslate_macro_value (_rl_isearch_terminators, 0); + if (ret) + { + strncpy (numbuf, ret, sizeof (numbuf) - 1); + xfree (ret); + numbuf[sizeof(numbuf) - 1] = '\0'; + } + else + numbuf[0] = '\0'; + return numbuf; + } + else if (_rl_stricmp (name, "keymap") == 0) + { + ret = rl_get_keymap_name (_rl_keymap); + if (ret == 0) + ret = rl_get_keymap_name_from_edit_mode (); + return (ret ? ret : "none"); + } + else if (_rl_stricmp (name, "keyseq-timeout") == 0) + { + sprintf (numbuf, "%d", _rl_keyseq_timeout); + return (numbuf); + } + else if (_rl_stricmp (name, "emacs-mode-string") == 0) + return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT); + else if (_rl_stricmp (name, "vi-cmd-mode-string") == 0) + return (_rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT); + else if (_rl_stricmp (name, "vi-ins-mode-string") == 0) + return (_rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT); + else + return (0); +} + +void +rl_variable_dumper (int print_readably) +{ + int i; + char *v; + + for (i = 0; boolean_varlist[i].name; i++) + { + if (print_readably) + fprintf (rl_outstream, "set %s %s\n", boolean_varlist[i].name, + *boolean_varlist[i].value ? "on" : "off"); + else + fprintf (rl_outstream, "%s is set to `%s'\n", boolean_varlist[i].name, + *boolean_varlist[i].value ? "on" : "off"); + } + + for (i = 0; string_varlist[i].name; i++) + { + v = _rl_get_string_variable_value (string_varlist[i].name); + if (v == 0) /* _rl_isearch_terminators can be NULL */ + continue; + if (print_readably) + fprintf (rl_outstream, "set %s %s\n", string_varlist[i].name, v); + else + fprintf (rl_outstream, "%s is set to `%s'\n", string_varlist[i].name, v); + } +} + +/* Print all of the current variables and their values to + rl_outstream. If an explicit argument is given, then print + the output in such a way that it can be read back in. */ +int +rl_dump_variables (int count, int key) +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_variable_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +/* Return non-zero if any members of ARRAY are a substring in STRING. */ +static int +substring_member_of_array (const char *string, const char * const *array) +{ + while (*array) + { + if (_rl_strindex (string, *array)) + return (1); + array++; + } + return (0); +} diff --git a/lib/readline/callback.c b/lib/readline/callback.c new file mode 100644 index 0000000..69df77d --- /dev/null +++ b/lib/readline/callback.c @@ -0,0 +1,378 @@ +/* callback.c -- functions to use readline as an X `callback' mechanism. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include "rlconf.h" + +#if defined (READLINE_CALLBACKS) + +#include <sys/types.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif + +#include <stdio.h> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "readline.h" +#include "rlprivate.h" +#include "xmalloc.h" + +/* Private data for callback registration functions. See comments in + rl_callback_read_char for more details. */ +_rl_callback_func_t *_rl_callback_func = 0; +_rl_callback_generic_arg *_rl_callback_data = 0; + +/* Applications can set this to non-zero to have readline's signal handlers + installed during the entire duration of reading a complete line, as in + readline-6.2. This should be used with care, because it can result in + readline receiving signals and not handling them until it's called again + via rl_callback_read_char, thereby stealing them from the application. + By default, signal handlers are only active while readline is active. */ +int rl_persistent_signal_handlers = 0; + +/* **************************************************************** */ +/* */ +/* Callback Readline Functions */ +/* */ +/* **************************************************************** */ + +/* Allow using readline in situations where a program may have multiple + things to handle at once, and dispatches them via select(). Call + rl_callback_handler_install() with the prompt and a function to call + whenever a complete line of input is ready. The user must then + call rl_callback_read_char() every time some input is available, and + rl_callback_read_char() will call the user's function with the complete + text read in at each end of line. The terminal is kept prepped + all the time, except during calls to the user's function. Signal + handlers are only installed when the application calls back into + readline, so readline doesn't `steal' signals from the application. */ + +rl_vcpfunc_t *rl_linefunc; /* user callback function */ +static int in_handler; /* terminal_prepped and signals set? */ + +/* Make sure the terminal is set up, initialize readline, and prompt. */ +static void +_rl_callback_newline (void) +{ + rl_initialize (); + + if (in_handler == 0) + { + in_handler = 1; + + if (rl_prep_term_function) + (*rl_prep_term_function) (_rl_meta_flag); + +#if defined (HANDLE_SIGNALS) + if (rl_persistent_signal_handlers) + rl_set_signals (); +#endif + } + + readline_internal_setup (); + RL_CHECK_SIGNALS (); +} + +/* Install a readline handler, set up the terminal, and issue the prompt. */ +void +rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *linefunc) +{ + rl_set_prompt (prompt); + RL_SETSTATE (RL_STATE_CALLBACK); + rl_linefunc = linefunc; + _rl_callback_newline (); +} + +#if defined (HANDLE_SIGNALS) +#define CALLBACK_READ_RETURN() \ + do { \ + if (rl_persistent_signal_handlers == 0) \ + rl_clear_signals (); \ + return; \ + } while (0) +#else +#define CALLBACK_READ_RETURN() return +#endif + +/* Read one character, and dispatch to the handler if it ends the line. */ +void +rl_callback_read_char (void) +{ + char *line; + int eof, jcode; + static procenv_t olevel; + + if (rl_linefunc == NULL) + { + _rl_errmsg ("readline_callback_read_char() called with no handler!"); + abort (); + } + + eof = 0; + + memcpy ((void *)olevel, (void *)_rl_top_level, sizeof (procenv_t)); +#if defined (HAVE_POSIX_SIGSETJMP) + jcode = sigsetjmp (_rl_top_level, 0); +#else + jcode = setjmp (_rl_top_level); +#endif + if (jcode) + { + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t)); + + /* If we longjmped because of a timeout, handle it here. */ + if (RL_ISSTATE (RL_STATE_TIMEOUT)) + { + RL_SETSTATE (RL_STATE_DONE); + rl_done = 1; + } + + CALLBACK_READ_RETURN (); + } + +#if defined (HANDLE_SIGNALS) + /* Install signal handlers only when readline has control. */ + if (rl_persistent_signal_handlers == 0) + rl_set_signals (); +#endif + + do + { + RL_CHECK_SIGNALS (); + if (RL_ISSTATE (RL_STATE_ISEARCH)) + { + eof = _rl_isearch_callback (_rl_iscxt); + if (eof == 0 && (RL_ISSTATE (RL_STATE_ISEARCH) == 0) && RL_ISSTATE (RL_STATE_INPUTPENDING)) + rl_callback_read_char (); + + CALLBACK_READ_RETURN (); + } + else if (RL_ISSTATE (RL_STATE_NSEARCH)) + { + eof = _rl_nsearch_callback (_rl_nscxt); + + CALLBACK_READ_RETURN (); + } +#if defined (VI_MODE) + /* States that can occur while in state VIMOTION have to be checked + before RL_STATE_VIMOTION */ + else if (RL_ISSTATE (RL_STATE_CHARSEARCH)) + { + int k; + + k = _rl_callback_data->i2; + + eof = (*_rl_callback_func) (_rl_callback_data); + /* If the function `deregisters' itself, make sure the data is + cleaned up. */ + if (_rl_callback_func == 0) /* XXX - just sanity check */ + { + if (_rl_callback_data) + { + _rl_callback_data_dispose (_rl_callback_data); + _rl_callback_data = 0; + } + } + + /* Messy case where vi motion command can be char search */ + if (RL_ISSTATE (RL_STATE_VIMOTION)) + { + _rl_vi_domove_motion_cleanup (k, _rl_vimvcxt); + _rl_internal_char_cleanup (); + CALLBACK_READ_RETURN (); + } + + _rl_internal_char_cleanup (); + } + else if (RL_ISSTATE (RL_STATE_VIMOTION)) + { + eof = _rl_vi_domove_callback (_rl_vimvcxt); + /* Should handle everything, including cleanup, numeric arguments, + and turning off RL_STATE_VIMOTION */ + if (RL_ISSTATE (RL_STATE_NUMERICARG) == 0) + _rl_internal_char_cleanup (); + + CALLBACK_READ_RETURN (); + } +#endif + else if (RL_ISSTATE (RL_STATE_NUMERICARG)) + { + eof = _rl_arg_callback (_rl_argcxt); + if (eof == 0 && (RL_ISSTATE (RL_STATE_NUMERICARG) == 0) && RL_ISSTATE (RL_STATE_INPUTPENDING)) + rl_callback_read_char (); + /* XXX - this should handle _rl_last_command_was_kill better */ + else if (RL_ISSTATE (RL_STATE_NUMERICARG) == 0) + _rl_internal_char_cleanup (); + + CALLBACK_READ_RETURN (); + } + else if (RL_ISSTATE (RL_STATE_MULTIKEY)) + { + eof = _rl_dispatch_callback (_rl_kscxt); /* For now */ + while ((eof == -1 || eof == -2) && RL_ISSTATE (RL_STATE_MULTIKEY) && _rl_kscxt && (_rl_kscxt->flags & KSEQ_DISPATCHED)) + eof = _rl_dispatch_callback (_rl_kscxt); + if (RL_ISSTATE (RL_STATE_MULTIKEY) == 0) + { + _rl_internal_char_cleanup (); + _rl_want_redisplay = 1; + } + } + else if (_rl_callback_func) + { + /* This allows functions that simply need to read an additional + character (like quoted-insert) to register a function to be + called when input is available. _rl_callback_data is a + pointer to a struct that has the argument count originally + passed to the registering function and space for any additional + parameters. */ + eof = (*_rl_callback_func) (_rl_callback_data); + /* If the function `deregisters' itself, make sure the data is + cleaned up. */ + if (_rl_callback_func == 0) + { + if (_rl_callback_data) + { + _rl_callback_data_dispose (_rl_callback_data); + _rl_callback_data = 0; + } + _rl_internal_char_cleanup (); + } + } + else + eof = readline_internal_char (); + + RL_CHECK_SIGNALS (); + if (rl_done == 0 && _rl_want_redisplay) + { + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + } + + /* Make sure application hooks can see whether we saw EOF. */ + if (eof > 0) + { + rl_eof_found = eof; + RL_SETSTATE(RL_STATE_EOF); + } + + if (rl_done) + { + line = readline_internal_teardown (eof); + + if (rl_deprep_term_function) + (*rl_deprep_term_function) (); +#if defined (HANDLE_SIGNALS) + rl_clear_signals (); +#endif + in_handler = 0; + if (rl_linefunc) /* just in case */ + (*rl_linefunc) (line); + + /* If the user did not clear out the line, do it for him. */ + if (rl_line_buffer[0]) + _rl_init_line_state (); + + /* Redisplay the prompt if readline_handler_{install,remove} + not called. */ + if (in_handler == 0 && rl_linefunc) + _rl_callback_newline (); + } + } + while (rl_pending_input || _rl_pushed_input_available () || RL_ISSTATE (RL_STATE_MACROINPUT)); + + CALLBACK_READ_RETURN (); +} + +/* Remove the handler, and make sure the terminal is in its normal state. */ +void +rl_callback_handler_remove (void) +{ + rl_linefunc = NULL; + RL_UNSETSTATE (RL_STATE_CALLBACK); + RL_CHECK_SIGNALS (); + if (in_handler) + { + in_handler = 0; + if (rl_deprep_term_function) + (*rl_deprep_term_function) (); +#if defined (HANDLE_SIGNALS) + rl_clear_signals (); +#endif + } +} + +_rl_callback_generic_arg * +_rl_callback_data_alloc (int count) +{ + _rl_callback_generic_arg *arg; + + arg = (_rl_callback_generic_arg *)xmalloc (sizeof (_rl_callback_generic_arg)); + arg->count = count; + + arg->i1 = arg->i2 = 0; + + return arg; +} + +void +_rl_callback_data_dispose (_rl_callback_generic_arg *arg) +{ + xfree (arg); +} + +/* Make sure that this agrees with cases in rl_callback_read_char */ +void +rl_callback_sigcleanup (void) +{ + if (RL_ISSTATE (RL_STATE_CALLBACK) == 0) + return; + + if (RL_ISSTATE (RL_STATE_ISEARCH)) + _rl_isearch_cleanup (_rl_iscxt, 0); + else if (RL_ISSTATE (RL_STATE_NSEARCH)) + _rl_nsearch_cleanup (_rl_nscxt, 0); + else if (RL_ISSTATE (RL_STATE_VIMOTION)) + RL_UNSETSTATE (RL_STATE_VIMOTION); + else if (RL_ISSTATE (RL_STATE_NUMERICARG)) + { + _rl_argcxt = 0; + RL_UNSETSTATE (RL_STATE_NUMERICARG); + } + else if (RL_ISSTATE (RL_STATE_MULTIKEY)) + RL_UNSETSTATE (RL_STATE_MULTIKEY); + if (RL_ISSTATE (RL_STATE_CHARSEARCH)) + RL_UNSETSTATE (RL_STATE_CHARSEARCH); + + _rl_callback_func = 0; +} +#endif diff --git a/lib/readline/chardefs.h b/lib/readline/chardefs.h new file mode 100644 index 0000000..24a25f1 --- /dev/null +++ b/lib/readline/chardefs.h @@ -0,0 +1,165 @@ +/* chardefs.h -- Character definitions for readline. */ + +/* Copyright (C) 1994-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CHARDEFS_H_ +#define _CHARDEFS_H_ + +#include <ctype.h> + +#if defined (HAVE_CONFIG_H) +# if defined (HAVE_STRING_H) +# include <string.h> +# endif /* HAVE_STRING_H */ +# if defined (HAVE_STRINGS_H) +# include <strings.h> +# endif /* HAVE_STRINGS_H */ +#else +# include <string.h> +#endif /* !HAVE_CONFIG_H */ + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifdef CTRL +# undef CTRL +#endif +#ifdef UNCTRL +# undef UNCTRL +#endif + +/* Some character stuff. */ +#define control_character_threshold 0x020 /* Smaller than this is control. */ +#define control_character_mask 0x1f /* 0x20 - 1 */ +#define meta_character_threshold 0x07f /* Larger than this is Meta. */ +#define control_character_bit 0x40 /* 0x000000, must be off. */ +#define meta_character_bit 0x080 /* x0000000, must be on. */ +#define largest_char 255 /* Largest character value. */ + +#define CTRL_CHAR(c) ((c) < control_character_threshold && (((c) & 0x80) == 0)) +#define META_CHAR(c) ((c) > meta_character_threshold && (c) <= largest_char) + +#define CTRL(c) ((c) & control_character_mask) +#define META(c) ((c) | meta_character_bit) + +#define UNMETA(c) ((c) & (~meta_character_bit)) +#define UNCTRL(c) _rl_to_upper(((c)|control_character_bit)) + +#ifndef UCHAR_MAX +# define UCHAR_MAX 255 +#endif +#ifndef CHAR_MAX +# define CHAR_MAX 127 +#endif + +/* use this as a proxy for C89 */ +#if defined (HAVE_STDLIB_H) && defined (HAVE_STRING_H) +# define IN_CTYPE_DOMAIN(c) 1 +# define NON_NEGATIVE(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) ((c) >= 0 && (c) <= CHAR_MAX) +# define NON_NEGATIVE(c) ((unsigned char)(c) == (c)) +#endif + +#if !defined (isxdigit) && !defined (HAVE_ISXDIGIT) && !defined (__cplusplus) +# define isxdigit(c) (isdigit((unsigned char)(c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#endif + +/* Some systems define these; we want our definitions. */ +#undef ISPRINT + +/* Beware: these only work with single-byte ASCII characters. */ + +#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum ((unsigned char)c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha ((unsigned char)c)) +#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit ((unsigned char)c)) +#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower ((unsigned char)c)) +#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper ((unsigned char)c)) +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit ((unsigned char)c)) + +#define _rl_lowercase_p(c) (NON_NEGATIVE(c) && ISLOWER(c)) +#define _rl_uppercase_p(c) (NON_NEGATIVE(c) && ISUPPER(c)) +#define _rl_digit_p(c) ((c) >= '0' && (c) <= '9') + +#define _rl_alphabetic_p(c) (NON_NEGATIVE(c) && ISALNUM(c)) +#define _rl_pure_alphabetic(c) (NON_NEGATIVE(c) && ISALPHA(c)) + +#ifndef _rl_to_upper +# define _rl_to_upper(c) (_rl_lowercase_p(c) ? toupper((unsigned char)(c)) : (c)) +# define _rl_to_lower(c) (_rl_uppercase_p(c) ? tolower((unsigned char)(c)) : (c)) +#endif + +#ifndef _rl_digit_value +# define _rl_digit_value(x) ((x) - '0') +#endif + +#ifndef _rl_isident +# define _rl_isident(c) (ISALNUM(c) || (c) == '_') +#endif + +#ifndef ISOCTAL +# define ISOCTAL(c) ((c) >= '0' && (c) <= '7') +#endif +#define OCTVALUE(c) ((c) - '0') + +#define HEXVALUE(c) \ + (((c) >= 'a' && (c) <= 'f') \ + ? (c)-'a'+10 \ + : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0') + +#ifndef NEWLINE +#define NEWLINE '\n' +#endif + +#ifndef RETURN +#define RETURN CTRL('M') +#endif + +#ifndef RUBOUT +#define RUBOUT 0x7f +#endif + +#ifndef TAB +#define TAB '\t' +#endif + +#ifdef ABORT_CHAR +#undef ABORT_CHAR +#endif +#define ABORT_CHAR CTRL('G') + +#ifdef PAGE +#undef PAGE +#endif +#define PAGE CTRL('L') + +#ifdef SPACE +#undef SPACE +#endif +#define SPACE ' ' /* XXX - was 0x20 */ + +#ifdef ESC +#undef ESC +#endif +#define ESC CTRL('[') + +#endif /* _CHARDEFS_H_ */ diff --git a/lib/readline/colors.c b/lib/readline/colors.c new file mode 100644 index 0000000..dba81a2 --- /dev/null +++ b/lib/readline/colors.c @@ -0,0 +1,320 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2021 + 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include "rlconf.h" + +#if defined __TANDEM +# define _XOPEN_SOURCE_EXTENDED 1 +# define _TANDEM_SOURCE 1 +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include <stdio.h> + +#include "posixstat.h" // stat related macros (S_ISREG, ...) +#include <fcntl.h> // S_ISUID + +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +// strlen() +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +// abort() +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "readline.h" +#include "rldefs.h" + +#ifdef COLOR_SUPPORT + +#include "xmalloc.h" +#include "colors.h" + +static bool is_colored (enum indicator_no type); +static void restore_default_color (void); + +#define RL_COLOR_PREFIX_EXTENSION "readline-colored-completion-prefix" + +COLOR_EXT_TYPE *_rl_color_ext_list = 0; + +/* Output a color indicator (which may contain nulls). */ +void +_rl_put_indicator (const struct bin_str *ind) +{ + fwrite (ind->string, ind->len, 1, rl_outstream); +} + +static bool +is_colored (enum indicator_no colored_filetype) +{ + size_t len = _rl_color_indicator[colored_filetype].len; + char const *s = _rl_color_indicator[colored_filetype].string; + return ! (len == 0 + || (len == 1 && strncmp (s, "0", 1) == 0) + || (len == 2 && strncmp (s, "00", 2) == 0)); +} + +static void +restore_default_color (void) +{ + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); +} + +void +_rl_set_normal_color (void) +{ + if (is_colored (C_NORM)) + { + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_NORM]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + } +} + +static struct bin_str * +_rl_custom_readline_prefix (void) +{ + size_t len; + COLOR_EXT_TYPE *ext; + + len = strlen (RL_COLOR_PREFIX_EXTENSION); + for (ext = _rl_color_ext_list; ext; ext = ext->next) + if (ext->ext.len == len && STREQN (ext->ext.string, RL_COLOR_PREFIX_EXTENSION, len)) + return (&ext->seq); + return (NULL); +} + +bool +_rl_print_prefix_color (void) +{ + struct bin_str *s; + + /* What do we want to use for the prefix? Let's try cyan first, see colors.h */ + s = _rl_custom_readline_prefix (); + if (s == 0) + s = &_rl_color_indicator[C_PREFIX]; + if (s->string != NULL) + { + if (is_colored (C_NORM)) + restore_default_color (); + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (s); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + return 0; + } + else + return 1; +} + +/* Returns whether any color sequence was printed. */ +bool +_rl_print_color_indicator (const char *f) +{ + enum indicator_no colored_filetype; + COLOR_EXT_TYPE *ext; /* Color extension */ + size_t len; /* Length of name */ + + const char* name; + char *filename; + struct stat astat, linkstat; + mode_t mode; + int linkok; /* 1 == ok, 0 == dangling symlink, -1 == missing */ + int stat_ok; + + name = f; + + /* This should already have undergone tilde expansion */ + filename = 0; + if (rl_filename_stat_hook) + { + filename = savestring (f); + (*rl_filename_stat_hook) (&filename); + name = filename; + } + +#if defined (HAVE_LSTAT) + stat_ok = lstat(name, &astat); +#else + stat_ok = stat(name, &astat); +#endif + if (stat_ok == 0) + { + mode = astat.st_mode; +#if defined (HAVE_LSTAT) + if (S_ISLNK (mode)) + { + linkok = stat (name, &linkstat) == 0; + if (linkok && strncmp (_rl_color_indicator[C_LINK].string, "target", 6) == 0) + mode = linkstat.st_mode; + } + else +#endif + linkok = 1; + } + else + linkok = -1; + + /* Is this a nonexistent file? If so, linkok == -1. */ + + if (linkok == -1 && _rl_color_indicator[C_MISSING].string != NULL) + colored_filetype = C_MISSING; + else if (linkok == 0 && _rl_color_indicator[C_ORPHAN].string != NULL) + colored_filetype = C_ORPHAN; /* dangling symlink */ + else if(stat_ok != 0) + { + static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS; + colored_filetype = filetype_indicator[normal]; //f->filetype]; + } + else + { + if (S_ISREG (mode)) + { + colored_filetype = C_FILE; + +#if defined (S_ISUID) + if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) + colored_filetype = C_SETUID; + else +#endif +#if defined (S_ISGID) + if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) + colored_filetype = C_SETGID; + else +#endif + if (is_colored (C_CAP) && 0) //f->has_capability) + colored_filetype = C_CAP; + else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) + colored_filetype = C_EXEC; + else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK)) + colored_filetype = C_MULTIHARDLINK; + } + else if (S_ISDIR (mode)) + { + colored_filetype = C_DIR; + +#if defined (S_ISVTX) + if ((mode & S_ISVTX) && (mode & S_IWOTH) + && is_colored (C_STICKY_OTHER_WRITABLE)) + colored_filetype = C_STICKY_OTHER_WRITABLE; + else +#endif + if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE)) + colored_filetype = C_OTHER_WRITABLE; +#if defined (S_ISVTX) + else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY)) + colored_filetype = C_STICKY; +#endif + } +#if defined (S_ISLNK) + else if (S_ISLNK (mode)) + colored_filetype = C_LINK; +#endif + else if (S_ISFIFO (mode)) + colored_filetype = C_FIFO; +#if defined (S_ISSOCK) + else if (S_ISSOCK (mode)) + colored_filetype = C_SOCK; +#endif +#if defined (S_ISBLK) + else if (S_ISBLK (mode)) + colored_filetype = C_BLK; +#endif + else if (S_ISCHR (mode)) + colored_filetype = C_CHR; + else + { + /* Classify a file of some other type as C_ORPHAN. */ + colored_filetype = C_ORPHAN; + } + } + + /* Check the file's suffix only if still classified as C_FILE. */ + ext = NULL; + if (colored_filetype == C_FILE) + { + /* Test if NAME has a recognized suffix. */ + len = strlen (name); + name += len; /* Pointer to final \0. */ + for (ext = _rl_color_ext_list; ext != NULL; ext = ext->next) + { + if (ext->ext.len <= len + && strncmp (name - ext->ext.len, ext->ext.string, + ext->ext.len) == 0) + break; + } + } + + free (filename); /* NULL or savestring return value */ + + { + const struct bin_str *const s + = ext ? &(ext->seq) : &_rl_color_indicator[colored_filetype]; + if (s->string != NULL) + { + /* Need to reset so not dealing with attribute combinations */ + if (is_colored (C_NORM)) + restore_default_color (); + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (s); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + return 0; + } + else + return 1; + } +} + +void +_rl_prep_non_filename_text (void) +{ + if (_rl_color_indicator[C_END].string != NULL) + _rl_put_indicator (&_rl_color_indicator[C_END]); + else + { + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_RESET]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + } +} +#endif /* COLOR_SUPPORT */ diff --git a/lib/readline/colors.h b/lib/readline/colors.h new file mode 100644 index 0000000..6561ad9 --- /dev/null +++ b/lib/readline/colors.h @@ -0,0 +1,126 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2015 + 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#ifndef _COLORS_H_ +#define _COLORS_H_ + +#include <stdio.h> // size_t + +#if defined(__TANDEM) && defined(HAVE_STDBOOL_H) && (__STDC_VERSION__ < 199901L) +typedef int _Bool; +#endif + +#if defined (HAVE_STDBOOL_H) +# include <stdbool.h> // bool +#else +typedef int _rl_bool_t; + +#ifdef bool +# undef bool +#endif +#define bool _rl_bool_t + +#ifndef true +# define true 1 +# define false 0 +#endif + +#endif /* !HAVE_STDBOOL_H */ + +/* Null is a valid character in a color indicator (think about Epson + printers, for example) so we have to use a length/buffer string + type. */ +struct bin_str + { + size_t len; + const char *string; + }; + +/* file type indicators (dir, sock, fifo, ...) + Default value is initialized in parse-colors.c. + It is then modified from the values of $LS_COLORS. */ +extern struct bin_str _rl_color_indicator[]; + +/* The LS_COLORS variable is in a termcap-like format. */ +typedef struct _color_ext_type + { + struct bin_str ext; /* The extension we're looking for */ + struct bin_str seq; /* The sequence to output when we do */ + struct _color_ext_type *next; /* Next in list */ + } COLOR_EXT_TYPE; + +/* file extensions indicators (.txt, .log, .jpg, ...) + Values are taken from $LS_COLORS in rl_parse_colors(). */ +extern COLOR_EXT_TYPE *_rl_color_ext_list; + +#define FILETYPE_INDICATORS \ + { \ + C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE, \ + C_LINK, C_SOCK, C_FILE, C_DIR \ + } + +/* Whether we used any colors in the output so far. If so, we will + need to restore the default color later. If not, we will need to + call prep_non_filename_text before using color for the first time. */ + +enum indicator_no + { + C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK, + C_FIFO, C_SOCK, + C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID, + C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK, + C_CLR_TO_EOL + }; + + +#if !S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +enum filetype + { + unknown, + fifo, + chardev, + directory, + blockdev, + normal, + symbolic_link, + sock, + whiteout, + arg_directory + }; + +/* Prefix color, currently same as socket */ +#define C_PREFIX C_SOCK + +extern void _rl_put_indicator (const struct bin_str *ind); +extern void _rl_set_normal_color (void); +extern bool _rl_print_prefix_color (void); +extern bool _rl_print_color_indicator (const char *f); +extern void _rl_prep_non_filename_text (void); + +#endif /* !_COLORS_H_ */ diff --git a/lib/readline/compat.c b/lib/readline/compat.c new file mode 100644 index 0000000..fc2a48f --- /dev/null +++ b/lib/readline/compat.c @@ -0,0 +1,106 @@ +/* compat.c -- backwards compatibility functions. */ + +/* Copyright (C) 2000-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#include "rlstdc.h" +#include "rltypedefs.h" + +extern void rl_free_undo_list (void); +extern int rl_maybe_save_line (void); +extern int rl_maybe_unsave_line (void); +extern int rl_maybe_replace_line (void); + +extern int rl_crlf (void); +extern int rl_ding (void); +extern int rl_alphabetic (int); + +extern char **rl_completion_matches (const char *, rl_compentry_func_t *); +extern char *rl_username_completion_function (const char *, int); +extern char *rl_filename_completion_function (const char *, int); + +/* Provide backwards-compatible entry points for old function names. */ + +void +free_undo_list (void) +{ + rl_free_undo_list (); +} + +int +maybe_replace_line (void) +{ + return rl_maybe_replace_line (); +} + +int +maybe_save_line (void) +{ + return rl_maybe_save_line (); +} + +int +maybe_unsave_line (void) +{ + return rl_maybe_unsave_line (); +} + +int +ding (void) +{ + return rl_ding (); +} + +int +crlf (void) +{ + return rl_crlf (); +} + +int +alphabetic (int c) +{ + return rl_alphabetic (c); +} + +char ** +completion_matches (const char *s, rl_compentry_func_t *f) +{ + return rl_completion_matches (s, f); +} + +char * +username_completion_function (const char *s, int i) +{ + return rl_username_completion_function (s, i); +} + +char * +filename_completion_function (const char *s, int i) +{ + return rl_filename_completion_function (s, i); +} diff --git a/lib/readline/complete.c b/lib/readline/complete.c new file mode 100644 index 0000000..e5d224e --- /dev/null +++ b/lib/readline/complete.c @@ -0,0 +1,2992 @@ +/* complete.c -- filename completion for readline. */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# define _XOPEN_SOURCE_EXTENDED 1 +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#if defined (__TANDEM) +# include <sys/stat.h> +#endif +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif + +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <stdio.h> + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if defined (HAVE_PWD_H) +#include <pwd.h> +#endif + +#include "posixdir.h" +#include "posixstat.h" + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "xmalloc.h" +#include "rlprivate.h" + +#if defined (COLOR_SUPPORT) +# include "colors.h" +#endif + +#ifdef __STDC__ +typedef int QSFUNC (const void *, const void *); +#else +typedef int QSFUNC (); +#endif + +#ifdef HAVE_LSTAT +# define LSTAT lstat +#else +# define LSTAT stat +#endif + +/* Unix version of a hidden file. Could be different on other systems. */ +#define HIDDEN_FILE(fname) ((fname)[0] == '.') + +/* Most systems don't declare getpwent in <pwd.h> if _POSIX_SOURCE is + defined. */ +#if defined (HAVE_GETPWENT) && (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE)) +extern struct passwd *getpwent (void); +#endif /* HAVE_GETPWENT && (!HAVE_GETPW_DECLS || _POSIX_SOURCE) */ + +/* 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 instead of actually doing the display. + It takes three arguments: (char **matches, int num_matches, int max_length) + where MATCHES is the array of strings that matched, NUM_MATCHES is the + number of strings in that array, and MAX_LENGTH is the length of the + longest string in that array. */ +rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)NULL; + +#if defined (VISIBLE_STATS) || defined (COLOR_SUPPORT) +# if !defined (X_OK) +# define X_OK 1 +# endif +#endif + +#if defined (VISIBLE_STATS) +static int stat_char (char *); +#endif + +#if defined (COLOR_SUPPORT) +static int colored_stat_start (const char *); +static void colored_stat_end (void); +static int colored_prefix_start (void); +static void colored_prefix_end (void); +#endif + +static int path_isdir (const char *); + +static char *rl_quote_filename (char *, int, char *); + +static void _rl_complete_sigcleanup (int, void *); + +static void set_completion_defaults (int); +static int get_y_or_n (int); +static int _rl_internal_pager (int); +static char *printable_part (char *); +static int fnwidth (const char *); +static int fnprint (const char *, int, const char *); +static int print_filename (char *, char *, int); + +static char **gen_completion_matches (char *, int, int, rl_compentry_func_t *, int, int); + +static char **remove_duplicate_matches (char **); +static void insert_match (char *, int, int, char *); +static int append_to_match (char *, int, int, int); +static void insert_all_matches (char **, int, char *); +static int complete_fncmp (const char *, int, const char *, int); +static void display_matches (char **); +static int compute_lcd_of_matches (char **, int, const char *); +static int postprocess_matches (char ***, int); +static int compare_match (char *, const char *); +static int complete_get_screenwidth (void); + +static char *make_quoted_replacement (char *, int, char *); + +/* **************************************************************** */ +/* */ +/* Completion matching, from readline's point of view. */ +/* */ +/* **************************************************************** */ + +/* Variables known only to the readline library. */ + +/* If non-zero, non-unique completions always show the list of matches. */ +int _rl_complete_show_all = 0; + +/* If non-zero, non-unique completions show the list of matches, unless it + is not possible to do partial completion and modify the line. */ +int _rl_complete_show_unmodified = 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; + +/* Non-zero means that case is not significant in filename completion. */ +#if (defined (__MSDOS__) && !defined (__DJGPP__)) || (defined (_WIN32) && !defined (__CYGWIN__)) +int _rl_completion_case_fold = 1; +#else +int _rl_completion_case_fold = 0; +#endif + +/* Non-zero means that `-' and `_' are equivalent when comparing filenames + for completion. */ +int _rl_completion_case_map = 0; + +/* If zero, don't match hidden files (filenames beginning with a `.' on + Unix) when doing filename completion. */ +int _rl_match_hidden_files = 1; + +/* Length in characters of a common prefix replaced with an ellipsis (`...') + when displaying completion matches. Matches whose printable portion has + more than this number of displaying characters in common will have the common + display prefix replaced with an ellipsis. */ +int _rl_completion_prefix_display_length = 0; + +/* The readline-private number of screen columns to use when displaying + matches. If < 0 or > _rl_screenwidth, it is ignored. */ +int _rl_completion_columns = -1; + +#if defined (COLOR_SUPPORT) +/* Non-zero means to use colors to indicate file type when listing possible + completions. The colors used are taken from $LS_COLORS, if set. */ +int _rl_colored_stats = 0; + +/* Non-zero means to use a color (currently magenta) to indicate the common + prefix of a set of possible word completions. */ +int _rl_colored_completion_prefix = 0; +#endif + +/* If non-zero, when completing in the middle of a word, don't insert + characters from the match that match characters following point in + the word. This means, for instance, completing when the cursor is + after the `e' in `Makefile' won't result in `Makefilefile'. */ +int _rl_skip_completed_text = 0; + +/* If non-zero, menu completion displays the common prefix first in the + cycle of possible completions instead of the last. */ +int _rl_menu_complete_prefix_first = 0; + +/* Global variables available to applications using readline. */ + +#if defined (VISIBLE_STATS) +/* Non-zero means add an additional character to each filename displayed + during listing completion iff rl_filename_completion_desired which helps + to indicate the type of file being listed. */ +int rl_visible_stats = 0; +#endif /* VISIBLE_STATS */ + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. */ +rl_icppfunc_t *rl_directory_completion_hook = (rl_icppfunc_t *)NULL; + +rl_icppfunc_t *rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL; + +rl_icppfunc_t *rl_filename_stat_hook = (rl_icppfunc_t *)NULL; + +/* If non-zero, this is the address of a function to call when reading + directory entries from the filesystem for completion and comparing + them to the partial word to be completed. The function should + either return its first argument (if no conversion takes place) or + newly-allocated memory. This can, for instance, convert filenames + between character sets for comparison against what's typed at the + keyboard. The returned value is what is added to the list of + matches. The second argument is the length of the filename to be + converted. */ +rl_dequote_func_t *rl_filename_rewrite_hook = (rl_dequote_func_t *)NULL; + +/* Non-zero means readline completion functions perform tilde expansion. */ +int rl_complete_with_tilde_expansion = 0; + +/* Pointer to the generator function for completion_matches (). + NULL means to use rl_filename_completion_function (), the default filename + completer. */ +rl_compentry_func_t *rl_completion_entry_function = (rl_compentry_func_t *)NULL; + +/* Pointer to generator function for rl_menu_complete (). NULL means to use + *rl_completion_entry_function (see above). */ +rl_compentry_func_t *rl_menu_completion_entry_function = (rl_compentry_func_t *)NULL; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +rl_completion_func_t *rl_attempted_completion_function = (rl_completion_func_t *)NULL; + +/* Non-zero means to suppress normal filename completion after the + user-specified completion function has been called. */ +int rl_attempted_completion_over = 0; + +/* Set to a character indicating the type of completion being performed + by rl_complete_internal, available for use by application completion + functions. */ +int rl_completion_type = 0; + +/* 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. A negative value means + don't ask. */ +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\"\\'`@$><=;|&{("; /* }) */ + +/* List of basic quoting characters. */ +const char *rl_basic_quote_characters = "\"'"; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +const char *rl_completer_word_break_characters = 0; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL; + +/* List of characters which can be used to quote a substring of the line. + Completion occurs on the entire substring, and within the substring + rl_completer_word_break_characters are treated as any other character, + unless they also appear within this list. */ +const char *rl_completer_quote_characters = (const char *)NULL; + +/* List of characters that should be quoted in filenames by the completer. */ +const char *rl_filename_quote_characters = (const char *)NULL; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +const char *rl_special_prefixes = (const char *)NULL; + +/* If non-zero, then disallow duplicates in the matches. */ +int rl_ignore_completion_duplicates = 1; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +int rl_filename_completion_desired = 0; + +/* Non-zero means that the results of the matches are to be quoted using + double quotes (or an application-specific quoting mechanism) if the + filename contains any characters in rl_filename_quote_chars. This is + ALWAYS non-zero on entry, and can only be changed within a completion + entry finder function. */ +int rl_filename_quoting_desired = 1; + +/* 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 (char**) known as matches in the code below. + It consists of a NULL-terminated array of pointers to potential + matching strings. The 1st element (matches[0]) is the maximal + substring that is common to all matches. This function can re-arrange + the list of matches as required, but all elements of the array must be + free()'d if they are deleted. The main intent of this function is + to implement FIGNORE a la SunOS csh. */ +rl_compignore_func_t *rl_ignore_some_completions_function = (rl_compignore_func_t *)NULL; + +/* Set to a function to quote a filename in an application-specific fashion. + Called with the text to quote, the type of match found (single or multiple) + and a pointer to the quoting character to be used, which the function can + reset if desired. */ +rl_quote_func_t *rl_filename_quoting_function = rl_quote_filename; + +/* Function to call to remove quoting characters from a filename. Called + before completion is attempted, so the embedded quotes do not interfere + with matching names in the file system. Readline doesn't do anything + with this; it's set only by applications. */ +rl_dequote_func_t *rl_filename_dequoting_function = (rl_dequote_func_t *)NULL; + +/* Function to call to decide whether or not a word break character is + quoted. If a character is quoted, it does not break words for the + 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, the completion functions don't append any 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_quote = 0; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +int rl_completion_found_quote; + +/* 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; + +/* Set to the last key used to invoke one of the completion functions */ +int rl_completion_invoking_key; + +/* If non-zero, sort the completion matches. On by default. */ +int rl_sort_completion_matches = 1; + +/* Variables local to this file. */ + +/* Local variable states what happened during the last completion attempt. */ +static int completion_changed_buffer; +static int last_completion_failed = 0; + +/* The result of the query to the user about displaying completion matches */ +static int completion_y_or_n; + +static int _rl_complete_display_matches_interrupt = 0; + +/*************************************/ +/* */ +/* Bindable completion functions */ +/* */ +/*************************************/ + +/* Complete the word at or before point. You have supplied the function + that does the initial simple matching selection algorithm (see + rl_completion_matches ()). The default is to do filename completion. */ +int +rl_complete (int ignore, int invoking_key) +{ + rl_completion_invoking_key = invoking_key; + + if (rl_inhibit_completion) + return (_rl_insert_char (ignore, invoking_key)); +#if 0 + else if (rl_last_func == rl_complete && completion_changed_buffer == 0 && last_completion_failed == 0) +#else + else if (rl_last_func == rl_complete && completion_changed_buffer == 0) +#endif + return (rl_complete_internal ('?')); + else if (_rl_complete_show_all) + return (rl_complete_internal ('!')); + else if (_rl_complete_show_unmodified) + return (rl_complete_internal ('@')); + else + return (rl_complete_internal (TAB)); +} + +/* List the possible completions. See description of rl_complete (). */ +int +rl_possible_completions (int ignore, int invoking_key) +{ + rl_completion_invoking_key = invoking_key; + return (rl_complete_internal ('?')); +} + +int +rl_insert_completions (int ignore, int invoking_key) +{ + rl_completion_invoking_key = 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 (rl_command_func_t *cfunc) +{ + if (rl_last_func == cfunc && !completion_changed_buffer) + return '?'; + else if (_rl_complete_show_all) + return '!'; + else if (_rl_complete_show_unmodified) + return '@'; + else + return TAB; +} + +/************************************/ +/* */ +/* Completion utility functions */ +/* */ +/************************************/ + +/* Reset public readline state on a signal or other event. */ +void +_rl_reset_completion_state (void) +{ + rl_completion_found_quote = 0; + rl_completion_quote_character = 0; +} + +static void +_rl_complete_sigcleanup (int sig, void *ptr) +{ + if (sig == SIGINT) /* XXX - for now */ + { + _rl_free_match_list ((char **)ptr); + _rl_complete_display_matches_interrupt = 1; + } +} + +/* Set default values for readline word completion. These are the variables + that application completion functions can change or inspect. */ +static void +set_completion_defaults (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 = rl_completion_suppress_quote = 0; + rl_completion_append_character = ' '; + + /* The completion entry function may optionally change this. */ + rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs; + + /* Reset private state. */ + _rl_complete_display_matches_interrupt = 0; +} + +/* The user must press "y" or "n". Non-zero return means "y" pressed. */ +static int +get_y_or_n (int for_pager) +{ + int c; + + /* For now, disable pager in callback mode, until we later convert to state + driven functions. Have to wait until next major version to add new + state definition, since it will change value of RL_STATE_DONE. */ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + return 1; +#endif + + for (;;) + { + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (c == 'y' || c == 'Y' || c == ' ') + return (1); + if (c == 'n' || c == 'N' || c == RUBOUT) + return (0); + if (c == ABORT_CHAR || c < 0) + _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 (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; +} + +static int +path_isdir (const char *filename) +{ + struct stat finfo; + + return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode)); +} + +#if defined (VISIBLE_STATS) +/* Return the character which best describes FILENAME. + `@' for symbolic links + `/' for directories + `*' for executables + `=' for sockets + `|' for FIFOs + `%' for character special devices + `#' for block special devices */ +static int +stat_char (char *filename) +{ + struct stat finfo; + int character, r; + char *f; + const char *fn; + + /* Short-circuit a //server on cygwin, since that will always behave as + a directory. */ +#if __CYGWIN__ + if (filename[0] == '/' && filename[1] == '/' && strchr (filename+2, '/') == 0) + return '/'; +#endif + + f = 0; + if (rl_filename_stat_hook) + { + f = savestring (filename); + (*rl_filename_stat_hook) (&f); + fn = f; + } + else + fn = filename; + +#if defined (HAVE_LSTAT) && defined (S_ISLNK) + r = lstat (fn, &finfo); +#else + r = stat (fn, &finfo); +#endif + + if (r == -1) + { + xfree (f); + return (0); + } + + character = 0; + if (S_ISDIR (finfo.st_mode)) + character = '/'; +#if defined (S_ISCHR) + else if (S_ISCHR (finfo.st_mode)) + character = '%'; +#endif /* S_ISCHR */ +#if defined (S_ISBLK) + else if (S_ISBLK (finfo.st_mode)) + character = '#'; +#endif /* S_ISBLK */ +#if defined (S_ISLNK) + else if (S_ISLNK (finfo.st_mode)) + character = '@'; +#endif /* S_ISLNK */ +#if defined (S_ISSOCK) + else if (S_ISSOCK (finfo.st_mode)) + character = '='; +#endif /* S_ISSOCK */ +#if defined (S_ISFIFO) + else if (S_ISFIFO (finfo.st_mode)) + character = '|'; +#endif + else if (S_ISREG (finfo.st_mode)) + { +#if defined (_WIN32) && !defined (__CYGWIN__) + char *ext; + + /* Windows doesn't do access and X_OK; check file extension instead */ + ext = strrchr (fn, '.'); + if (ext && (_rl_stricmp (ext, ".exe") == 0 || + _rl_stricmp (ext, ".cmd") == 0 || + _rl_stricmp (ext, ".bat") == 0 || + _rl_stricmp (ext, ".com") == 0)) + character = '*'; +#else + if (access (filename, X_OK) == 0) + character = '*'; +#endif + } + + xfree (f); + return (character); +} +#endif /* VISIBLE_STATS */ + +#if defined (COLOR_SUPPORT) +static int +colored_stat_start (const char *filename) +{ + _rl_set_normal_color (); + return (_rl_print_color_indicator (filename)); +} + +static void +colored_stat_end (void) +{ + _rl_prep_non_filename_text (); + _rl_put_indicator (&_rl_color_indicator[C_CLR_TO_EOL]); +} + +static int +colored_prefix_start (void) +{ + _rl_set_normal_color (); + return (_rl_print_prefix_color ()); +} + +static void +colored_prefix_end (void) +{ + colored_stat_end (); /* for now */ +} +#endif + +/* 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. 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 (char *pathname) +{ + char *temp, *x; + + if (rl_filename_completion_desired == 0) /* don't need to do anything */ + return (pathname); + + temp = strrchr (pathname, '/'); +#if defined (__MSDOS__) || defined (_WIN32) + if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':') + temp = pathname + 1; +#endif + + if (temp == 0 || *temp == '\0') + return (pathname); + else if (temp[1] == 0 && temp == pathname) + 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; +} + +/* Compute width of STRING when displayed on screen by print_filename */ +static int +fnwidth (const char *string) +{ + int width, pos; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + int left, w; + size_t clen; + WCHAR_T wc; + + left = strlen (string) + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + width = pos = 0; + while (string[pos]) + { + if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT) + { + width += 2; + pos++; + } + else + { +#if defined (HANDLE_MULTIBYTE) + clen = MBRTOWC (&wc, string + pos, left - pos, &ps); + if (MB_INVALIDCH (clen)) + { + width++; + pos++; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (clen)) + break; + else + { + pos += clen; + w = WCWIDTH (wc); + width += (w >= 0) ? w : 1; + } +#else + width++; + pos++; +#endif + } + } + + return width; +} + +#define ELLIPSIS_LEN 3 + +static int +fnprint (const char *to_print, int prefix_bytes, const char *real_pathname) +{ + int printed_len, w; + const char *s; + int common_prefix_len, print_len; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + const char *end; + size_t tlen; + int width; + WCHAR_T wc; + + print_len = strlen (to_print); + end = to_print + print_len + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#else + print_len = strlen (to_print); +#endif + + printed_len = common_prefix_len = 0; + + /* Don't print only the ellipsis if the common prefix is one of the + possible completions. Only cut off prefix_bytes if we're going to be + printing the ellipsis, which takes precedence over coloring the + completion prefix (see print_filename() below). */ + if (_rl_completion_prefix_display_length > 0 && prefix_bytes >= print_len) + prefix_bytes = 0; + +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats && (prefix_bytes == 0 || _rl_colored_completion_prefix <= 0)) + colored_stat_start (real_pathname); +#endif + + if (prefix_bytes && _rl_completion_prefix_display_length > 0 && + prefix_bytes > _rl_completion_prefix_display_length) + { + char ellipsis; + + ellipsis = (to_print[prefix_bytes] == '.') ? '_' : '.'; + for (w = 0; w < ELLIPSIS_LEN; w++) + putc (ellipsis, rl_outstream); + printed_len = ELLIPSIS_LEN; + } +#if defined (COLOR_SUPPORT) + else if (prefix_bytes && _rl_colored_completion_prefix > 0) + { + common_prefix_len = prefix_bytes; + prefix_bytes = 0; + /* XXX - print color indicator start here */ + colored_prefix_start (); + } +#endif + + s = to_print + prefix_bytes; + while (*s) + { + if (CTRL_CHAR (*s)) + { + putc ('^', rl_outstream); + putc (UNCTRL (*s), rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else if (*s == RUBOUT) + { + putc ('^', rl_outstream); + putc ('?', rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else + { +#if defined (HANDLE_MULTIBYTE) + tlen = MBRTOWC (&wc, s, end - s, &ps); + if (MB_INVALIDCH (tlen)) + { + tlen = 1; + width = 1; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tlen)) + break; + else + { + w = WCWIDTH (wc); + width = (w >= 0) ? w : 1; + } + fwrite (s, 1, tlen, rl_outstream); + s += tlen; + printed_len += width; +#else + putc (*s, rl_outstream); + s++; + printed_len++; +#endif + } + if (common_prefix_len > 0 && (s - to_print) >= common_prefix_len) + { +#if defined (COLOR_SUPPORT) + /* printed bytes = s - to_print */ + /* printed bytes should never be > but check for paranoia's sake */ + colored_prefix_end (); + if (_rl_colored_stats) + colored_stat_start (real_pathname); /* XXX - experiment */ +#endif + common_prefix_len = 0; + } + } + +#if defined (COLOR_SUPPORT) + /* XXX - unconditional for now */ + if (_rl_colored_stats) + colored_stat_end (); +#endif + + return printed_len; +} + +/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we + are using it, check for and output a single character for `special' + filenames. Return the number of characters we output. */ + +static int +print_filename (char *to_print, char *full_pathname, int prefix_bytes) +{ + int printed_len, extension_char, slen, tlen; + char *s, c, *new_full_pathname, *dn; + + extension_char = 0; +#if defined (COLOR_SUPPORT) + /* Defer printing if we want to prefix with a color indicator */ + if (_rl_colored_stats == 0 || rl_filename_completion_desired == 0) +#endif + printed_len = fnprint (to_print, prefix_bytes, to_print); + + if (rl_filename_completion_desired && ( +#if defined (VISIBLE_STATS) + rl_visible_stats || +#endif +#if defined (COLOR_SUPPORT) + _rl_colored_stats || +#endif + _rl_complete_mark_directories)) + { + /* If to_print != full_pathname, to_print is the basename of the + path passed. In this case, we try to expand the directory + name before checking for the stat character. */ + if (to_print != full_pathname) + { + /* Terminate the directory name. */ + c = to_print[-1]; + to_print[-1] = '\0'; + + /* If setting the last slash in full_pathname to a NUL results in + full_pathname being the empty string, we are trying to complete + files in the root directory. If we pass a null string to the + bash directory completion hook, for example, it will expand it + to the current directory. We just want the `/'. */ + if (full_pathname == 0 || *full_pathname == 0) + dn = "/"; + else if (full_pathname[0] != '/') + dn = full_pathname; + else if (full_pathname[1] == 0) + dn = "//"; /* restore trailing slash to `//' */ + else if (full_pathname[1] == '/' && full_pathname[2] == 0) + dn = "/"; /* don't turn /// into // */ + else + dn = full_pathname; + s = tilde_expand (dn); + if (rl_directory_completion_hook) + (*rl_directory_completion_hook) (&s); + + slen = strlen (s); + tlen = strlen (to_print); + new_full_pathname = (char *)xmalloc (slen + tlen + 2); + strcpy (new_full_pathname, s); + if (s[slen - 1] == '/') + slen--; + else + new_full_pathname[slen] = '/'; + strcpy (new_full_pathname + slen + 1, to_print); + +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (new_full_pathname); + else +#endif + if (_rl_complete_mark_directories) + { + dn = 0; + if (rl_directory_completion_hook == 0 && rl_filename_stat_hook) + { + dn = savestring (new_full_pathname); + (*rl_filename_stat_hook) (&dn); + xfree (new_full_pathname); + new_full_pathname = dn; + } + if (path_isdir (new_full_pathname)) + extension_char = '/'; + } + + /* Move colored-stats code inside fnprint() */ +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats) + printed_len = fnprint (to_print, prefix_bytes, new_full_pathname); +#endif + + xfree (new_full_pathname); + to_print[-1] = c; + } + else + { + s = tilde_expand (full_pathname); +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (s); + else +#endif + if (_rl_complete_mark_directories && path_isdir (s)) + extension_char = '/'; + + /* Move colored-stats code inside fnprint() */ +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats) + printed_len = fnprint (to_print, prefix_bytes, s); +#endif + } + + xfree (s); + if (extension_char) + { + putc (extension_char, rl_outstream); + printed_len++; + } + } + + return printed_len; +} + +static char * +rl_quote_filename (char *s, int rtype, char *qcp) +{ + char *r; + + r = (char *)xmalloc (strlen (s) + 2); + *r = *rl_completer_quote_characters; + strcpy (r + 1, s); + if (qcp) + *qcp = *rl_completer_quote_characters; + return r; +} + +/* Find the bounds of the current word for completion purposes, and leave + rl_point set to the end of the word. This function skips quoted + substrings (characters between matched pairs of characters in + rl_completer_quote_characters). First we try to find an unclosed + quoted substring on which to do matching. If one is not found, we use + the word break characters to find the boundaries of the current word. + We call an application-specific function to decide whether or not a + particular word break character is quoted; if that function returns a + non-zero result, the character does not break a word. This function + returns the opening quote character if we found an unclosed quoted + substring, '\0' otherwise. FP, if non-null, is set to a value saying + which (shell-like) quote characters we found (single quote, double + 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. */ + +char +_rl_find_completion_word (int *fp, int *dp) +{ + int scan, end, found_quote, delimiter, pass_next, isbrk; + char quote_char; + const char *brkchars; + + end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + brkchars = 0; + if (rl_completion_word_break_hook) + brkchars = (*rl_completion_word_break_hook) (); + if (brkchars == 0) + brkchars = rl_completer_word_break_characters; + + if (rl_completer_quote_characters) + { + /* We have a list of characters which can be used in pairs to + quote substrings for the completer. Try to find the start + of an unclosed quoted substring. */ + /* FOUND_QUOTE is set so we know what kind of quotes we found. */ + for (scan = pass_next = 0; scan < end; scan = MB_NEXTCHAR (rl_line_buffer, scan, 1, MB_FIND_ANY)) + { + if (pass_next) + { + pass_next = 0; + continue; + } + + /* Shell-like semantics for single quotes -- don't allow backslash + to quote anything in single quotes, especially not the closing + quote. If you don't like this, take out the check on the value + of quote_char. */ + if (quote_char != '\'' && rl_line_buffer[scan] == '\\') + { + pass_next = 1; + found_quote |= RL_QF_BACKSLASH; + continue; + } + + if (quote_char != '\0') + { + /* Ignore everything until the matching close quote char. */ + if (rl_line_buffer[scan] == quote_char) + { + /* Found matching close. Abandon this substring. */ + quote_char = '\0'; + rl_point = end; + } + } + else if (strchr (rl_completer_quote_characters, rl_line_buffer[scan])) + { + /* Found start of a quoted substring. */ + quote_char = rl_line_buffer[scan]; + rl_point = scan + 1; + /* Shell-like quoting conventions. */ + if (quote_char == '\'') + found_quote |= RL_QF_SINGLE_QUOTE; + else if (quote_char == '"') + found_quote |= RL_QF_DOUBLE_QUOTE; + else + found_quote |= RL_QF_OTHER_QUOTE; + } + } + } + + if (rl_point == end && quote_char == '\0') + { + /* 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. */ + while (rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_ANY)) + { + scan = rl_line_buffer[rl_point]; + + if (strchr (brkchars, scan) == 0) + continue; + + /* Call the application-specific function to tell us whether + this word break character is quoted and should be skipped. */ + if (rl_char_is_quoted_p && found_quote && + (*rl_char_is_quoted_p) (rl_line_buffer, rl_point)) + continue; + + /* Convoluted code, but it avoids an n^2 algorithm with calls + to char_is_quoted. */ + break; + } + } + + /* If we are at an unquoted word break, then advance past it. */ + scan = rl_line_buffer[rl_point]; + + /* If there is an application-specific function to say whether or not + a character is quoted and we found a quote character, let that + function decide whether or not a character is a word break, even + if it is found in rl_completer_word_break_characters. Don't bother + if we're at the end of the line, though. */ + if (scan) + { + if (rl_char_is_quoted_p) + isbrk = (found_quote == 0 || + (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) && + strchr (brkchars, scan) != 0; + else + isbrk = strchr (brkchars, scan) != 0; + + if (isbrk) + { + /* If the character that caused the word break was a quoting + character, then remember it as the delimiter. */ + if (rl_basic_quote_characters && + strchr (rl_basic_quote_characters, scan) && + (end - rl_point) > 1) + delimiter = scan; + + /* If the character isn't needed to determine something special + about what kind of completion to perform, then advance past it. */ + if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0) + rl_point++; + } + } + + if (fp) + *fp = found_quote; + if (dp) + *dp = delimiter; + + return (quote_char); +} + +static char ** +gen_completion_matches (char *text, int start, int end, rl_compentry_func_t *our_func, int found_quote, int quote_char) +{ + char **matches; + + rl_completion_found_quote = found_quote; + rl_completion_quote_character = quote_char; + + /* If the user wants to TRY to complete, but then wants to give + up and use the default completion function, they set the + variable rl_attempted_completion_function. */ + if (rl_attempted_completion_function) + { + matches = (*rl_attempted_completion_function) (text, start, end); + if (RL_SIG_RECEIVED()) + { + _rl_free_match_list (matches); + matches = 0; + RL_CHECK_SIGNALS (); + } + + if (matches || rl_attempted_completion_over) + { + rl_attempted_completion_over = 0; + return (matches); + } + } + + /* XXX -- filename dequoting moved into rl_filename_completion_function */ + + /* rl_completion_matches will check for signals as well to avoid a long + delay while reading a directory. */ + matches = rl_completion_matches (text, our_func); + if (RL_SIG_RECEIVED()) + { + _rl_free_match_list (matches); + matches = 0; + RL_CHECK_SIGNALS (); + } + return matches; +} + +/* Filter out duplicates in MATCHES. This frees up the strings in + MATCHES. */ +static char ** +remove_duplicate_matches (char **matches) +{ + char *lowest_common; + int i, j, newlen; + char dead_slot; + char **temp_array; + + /* Sort the items. */ + for (i = 0; matches[i]; i++) + ; + + /* Sort the array without matches[0], since we need it to + stay in place no matter what. */ + if (i && rl_sort_completion_matches) + qsort (matches+1, i-1, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare); + + /* Remember the lowest common denominator for it may be unique. */ + lowest_common = savestring (matches[0]); + + for (i = newlen = 0; matches[i + 1]; i++) + { + if (strcmp (matches[i], matches[i + 1]) == 0) + { + xfree (matches[i]); + matches[i] = (char *)&dead_slot; + } + else + newlen++; + } + + /* We have marked all the dead slots with (char *)&dead_slot. + Copy all the non-dead entries into a new array. */ + temp_array = (char **)xmalloc ((3 + newlen) * sizeof (char *)); + for (i = j = 1; matches[i]; i++) + { + if (matches[i] != (char *)&dead_slot) + temp_array[j++] = matches[i]; + } + temp_array[j] = (char *)NULL; + + if (matches[0] != (char *)&dead_slot) + xfree (matches[0]); + + /* Place the lowest common denominator back in [0]. */ + temp_array[0] = lowest_common; + + /* If there is one string left, and it is identical to the + lowest common denominator, then the LCD is the string to + insert. */ + if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0) + { + xfree (temp_array[1]); + temp_array[1] = (char *)NULL; + } + return (temp_array); +} + +/* Find the common prefix of the list of matches, and put it into + matches[0]. */ +static int +compute_lcd_of_matches (char **match_list, int matches, const char *text) +{ + register int i, c1, c2, si; + int low; /* Count of max-matched characters. */ + int lx; + char *dtext; /* dequoted TEXT, if needed */ +#if defined (HANDLE_MULTIBYTE) + int v; + size_t v1, v2; + 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 + stop matching. */ + if (matches == 1) + { + match_list[0] = match_list[1]; + match_list[1] = (char *)NULL; + return 1; + } + + 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 + for (si = 0; (c1 = match_list[i][si]) && (c2 = match_list[i + 1][si]); si++) + { + if (_rl_completion_case_fold) + { + c1 = _rl_to_lower (c1); + c2 = _rl_to_lower (c2); + } +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + v1 = MBRTOWC (&wc1, match_list[i]+si, strlen (match_list[i]+si), &ps1); + v2 = MBRTOWC (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), &ps2); + if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (c1 != c2) /* do byte comparison */ + break; + continue; + } + if (_rl_completion_case_fold) + { + wc1 = towlower (wc1); + wc2 = towlower (wc2); + } + if (wc1 != wc2) + break; + else if (v1 > 1) + si += v1 - 1; + } + else +#endif + if (c1 != c2) + break; + } + + if (low > si) + low = si; + } + + /* If there were multiple matches, but none matched up to even the + first character, and the user typed something, use that as the + value of matches[0]. */ + if (low == 0 && text && *text) + { + match_list[0] = (char *)xmalloc (strlen (text) + 1); + strcpy (match_list[0], text); + } + else + { + 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) + { + /* We're making an assumption here: + IF we're completing filenames AND + the application has defined a filename dequoting function AND + we found a quote character AND + the application has requested filename quoting + THEN + we assume that TEXT was dequoted before checking against + the file system and needs to be dequoted here before we + check against the list of matches + FI */ + dtext = (char *)NULL; + if (rl_filename_completion_desired && + rl_filename_dequoting_function && + rl_completion_found_quote && + rl_filename_quoting_desired) + { + dtext = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); + text = dtext; + } + + /* sort the list to get consistent answers. */ + if (rl_sort_completion_matches) + qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare); + + si = strlen (text); + lx = (si <= low) ? si : low; /* check shorter of text and matches */ + /* Try to preserve the case of what the user typed in the presence of + multiple matches: check each match for something that matches + what the user typed taking case into account; use it up to common + length of matches if one is found. If not, just use first match. */ + for (i = 1; i <= matches; i++) + if (strncmp (match_list[i], text, lx) == 0) + { + strncpy (match_list[0], match_list[i], low); + break; + } + /* no casematch, use first entry */ + if (i > matches) + strncpy (match_list[0], match_list[1], low); + + FREE (dtext); + } + else + strncpy (match_list[0], match_list[1], low); + + match_list[0][low] = '\0'; + } + + return matches; +} + +static int +postprocess_matches (char ***matchesp, int matching_filenames) +{ + char *t, **matches, **temp_matches; + int nmatch, i; + + matches = *matchesp; + + if (matches == 0) + return 0; + + /* It seems to me that in all the cases we handle we would like + to ignore duplicate possibilities. Scan for the text to + insert being identical to the other completions. */ + if (rl_ignore_completion_duplicates) + { + temp_matches = remove_duplicate_matches (matches); + xfree (matches); + matches = temp_matches; + } + + /* If we are matching filenames, then here is our chance to + do clever processing by re-examining the list. Call the + ignore function with the array as a parameter. It can + munge the array, deleting matches as it desires. */ + if (rl_ignore_some_completions_function && matching_filenames) + { + for (nmatch = 1; matches[nmatch]; nmatch++) + ; + (void)(*rl_ignore_some_completions_function) (matches); + if (matches == 0 || matches[0] == 0) + { + FREE (matches); + *matchesp = (char **)0; + return 0; + } + else + { + /* If we removed some matches, recompute the common prefix. */ + for (i = 1; matches[i]; i++) + ; + if (i > 1 && i < nmatch) + { + t = matches[0]; + compute_lcd_of_matches (matches, i - 1, t); + FREE (t); + } + } + } + + *matchesp = matches; + return (1); +} + +static int +complete_get_screenwidth (void) +{ + int cols; + char *envcols; + + cols = _rl_completion_columns; + if (cols >= 0 && cols <= _rl_screenwidth) + return cols; + envcols = getenv ("COLUMNS"); + if (envcols && *envcols) + cols = atoi (envcols); + if (cols >= 0 && cols <= _rl_screenwidth) + return cols; + return _rl_screenwidth; +} + +/* A convenience function for displaying a list of strings in + columnar format on readline's output stream. MATCHES is the list + of strings, in argv format, LEN is the number of strings in MATCHES, + and MAX is the length of the longest string in MATCHES. */ +void +rl_display_match_list (char **matches, int len, int max) +{ + int count, limit, printed_len, lines, cols; + int i, j, k, l, common_length, sind; + char *temp, *t; + + /* Find the length of the prefix common to all items: length as displayed + characters (common_length) and as a byte index into the matches (sind) */ + common_length = sind = 0; + if (_rl_completion_prefix_display_length > 0) + { + t = printable_part (matches[0]); + /* check again in case of /usr/src/ */ + temp = rl_filename_completion_desired ? strrchr (t, '/') : 0; + common_length = temp ? fnwidth (temp) : fnwidth (t); + sind = temp ? strlen (temp) : strlen (t); + if (common_length > max || sind > max) + common_length = sind = 0; + + if (common_length > _rl_completion_prefix_display_length && common_length > ELLIPSIS_LEN) + max -= common_length - ELLIPSIS_LEN; + else if (_rl_colored_completion_prefix <= 0) + common_length = sind = 0; + } +#if defined (COLOR_SUPPORT) + else if (_rl_colored_completion_prefix > 0) + { + t = printable_part (matches[0]); + temp = rl_filename_completion_desired ? strrchr (t, '/') : 0; + common_length = temp ? fnwidth (temp) : fnwidth (t); + sind = temp ? RL_STRLEN (temp+1) : RL_STRLEN (t); /* want portion after final slash */ + if (common_length > max || sind > max) + common_length = sind = 0; + } +#endif + + /* How many items of MAX length can we fit in the screen window? */ + cols = complete_get_screenwidth (); + max += 2; + limit = cols / max; + if (limit != 1 && (limit * max == cols)) + limit--; + + /* If cols == 0, limit will end up -1 */ + if (cols < _rl_screenwidth && limit < 0) + limit = 1; + + /* Avoid a possible floating exception. If max > cols, + limit will be 0 and a divide-by-zero fault will result. */ + if (limit == 0) + limit = 1; + + /* How many iterations of the printing loop? */ + count = (len + (limit - 1)) / limit; + + /* Watch out for special case. If LEN is less than LIMIT, then + just do the inner printing loop. + 0 < len <= limit implies count = 1. */ + + /* Sort the items if they are not already sorted. */ + if (rl_ignore_completion_duplicates == 0 && rl_sort_completion_matches) + qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare); + + rl_crlf (); + + lines = 0; + if (_rl_print_completions_horizontally == 0) + { + /* Print the sorted items, up-and-down alphabetically, like ls. */ + for (i = 1; i <= count; i++) + { + for (j = 0, l = i; j < limit; j++) + { + if (l > len || matches[l] == 0) + break; + else + { + temp = printable_part (matches[l]); + printed_len = print_filename (temp, matches[l], sind); + + if (j + 1 < limit) + { + if (max <= printed_len) + putc (' ', rl_outstream); + else + for (k = 0; k < max - printed_len; k++) + putc (' ', rl_outstream); + } + } + l += count; + } + rl_crlf (); +#if defined (SIGWINCH) + if (RL_SIG_RECEIVED () && RL_SIGWINCH_RECEIVED() == 0) +#else + if (RL_SIG_RECEIVED ()) +#endif + return; + lines++; + if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count) + { + lines = _rl_internal_pager (lines); + if (lines < 0) + return; + } + } + } + else + { + /* Print the sorted items, across alphabetically, like ls -x. */ + for (i = 1; matches[i]; i++) + { + temp = printable_part (matches[i]); + printed_len = print_filename (temp, matches[i], sind); + /* Have we reached the end of this line? */ +#if defined (SIGWINCH) + if (RL_SIG_RECEIVED () && RL_SIGWINCH_RECEIVED() == 0) +#else + if (RL_SIG_RECEIVED ()) +#endif + return; + if (matches[i+1]) + { + if (limit == 1 || (i && (limit > 1) && (i % limit) == 0)) + { + rl_crlf (); + lines++; + if (_rl_page_completions && lines >= _rl_screenheight - 1) + { + lines = _rl_internal_pager (lines); + if (lines < 0) + return; + } + } + else if (max <= printed_len) + putc (' ', rl_outstream); + else + for (k = 0; k < max - printed_len; k++) + putc (' ', rl_outstream); + } + } + rl_crlf (); + } +} + +/* Display MATCHES, a list of matching filenames in argv format. This + handles the simple case -- a single match -- first. If there is more + than one match, we compute the number of strings in the list and the + length of the longest string, which will be needed by the display + function. If the application wants to handle displaying the list of + matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the + address of a function, and we just call it. If we're handling the + display ourselves, we just call rl_display_match_list. We also check + that the list of matches doesn't exceed the user-settable threshold, + and ask the user if he wants to see the list if there are more matches + than RL_COMPLETION_QUERY_ITEMS. */ +static void +display_matches (char **matches) +{ + int len, max, i; + char *temp; + + /* Move to the last visible line of a possibly-multiple-line command. */ + _rl_move_vert (_rl_vis_botlin); + + /* Handle simple case first. What if there is only one answer? */ + if (matches[1] == 0) + { + temp = printable_part (matches[0]); + rl_crlf (); + print_filename (temp, matches[0], 0); + rl_crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; + + return; + } + + /* There is more than one answer. Find out how many there are, + and find the maximum printed length of a single entry. */ + for (max = 0, i = 1; matches[i]; i++) + { + temp = printable_part (matches[i]); + len = fnwidth (temp); + + if (len > max) + max = len; + } + + len = i - 1; + + /* If the caller has defined a display hook, then call that now. */ + if (rl_completion_display_matches_hook) + { + (*rl_completion_display_matches_hook) (matches, len, max); + return; + } + + /* If there are many items, then ask the user if she really wants to + see them all. */ + if (rl_completion_query_items > 0 && len >= rl_completion_query_items) + { + rl_crlf (); + fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len); + fflush (rl_outstream); + if ((completion_y_or_n = get_y_or_n (0)) == 0) + { + rl_crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; + + return; + } + } + + rl_display_match_list (matches, len, max); + + rl_forced_update_display (); + rl_display_fixed = 1; +} + +/* qc == pointer to quoting character, if any */ +static char * +make_quoted_replacement (char *match, int mtype, char *qc) +{ + int should_quote, do_replace; + char *replacement; + + /* If we are doing completion on quoted substrings, and any matches + contain any of the completer_word_break_characters, then auto- + matically prepend the substring with a quote character (just pick + the first one from the list of such) if it does not already begin + with a quote string. FIXME: Need to remove any such automatically + inserted quote character when it no longer is necessary, such as + if we change the string we are completing on and the new set of + matches don't require a quoted substring. */ + replacement = match; + + should_quote = match && rl_completer_quote_characters && + rl_filename_completion_desired && + rl_filename_quoting_desired; + + if (should_quote) + should_quote = should_quote && (!qc || !*qc || + (rl_completer_quote_characters && strchr (rl_completer_quote_characters, *qc))); + + if (should_quote) + { + /* If there is a single match, see if we need to quote it. + This also checks whether the common prefix of several + matches needs to be quoted. */ + should_quote = rl_filename_quote_characters + ? (_rl_strpbrk (match, rl_filename_quote_characters) != 0) + : 0; + + do_replace = should_quote ? mtype : NO_MATCH; + /* Quote the replacement, since we found an embedded + word break character in a potential match. */ + if (do_replace != NO_MATCH && rl_filename_quoting_function) + replacement = (*rl_filename_quoting_function) (match, do_replace, qc); + } + return (replacement); +} + +static void +insert_match (char *match, int start, int mtype, char *qc) +{ + char *replacement, *r; + char oqc; + int end, rlen; + + oqc = qc ? *qc : '\0'; + replacement = make_quoted_replacement (match, mtype, qc); + + /* Now insert the match. */ + if (replacement) + { + rlen = strlen (replacement); + /* Don't double an opening quote character. */ + if (qc && *qc && start && rl_line_buffer[start - 1] == *qc && + replacement[0] == *qc) + start--; + /* If make_quoted_replacement changed the quoting character, remove + the opening quote and insert the (fully-quoted) replacement. */ + else if (qc && (*qc != oqc) && start && rl_line_buffer[start - 1] == oqc && + replacement[0] != oqc) + start--; + end = rl_point - 1; + /* Don't double a closing quote character */ + if (qc && *qc && end && rl_line_buffer[rl_point] == *qc && replacement[rlen - 1] == *qc) + end++; + if (_rl_skip_completed_text) + { + r = replacement; + while (start < rl_end && *r && rl_line_buffer[start] == *r) + { + start++; + r++; + } + if (start <= end || *r) + _rl_replace_text (r, start, end); + rl_point = start + strlen (r); + } + else + _rl_replace_text (replacement, start, end); + if (replacement != match) + xfree (replacement); + } +} + +/* Append any necessary closing quote and a separator character to the + just-inserted match. If the user has specified that directories + should be marked by a trailing `/', append one of those instead. The + 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. + 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 (char *text, int delimiter, int quote_char, int nontrivial_match) +{ + char temp_string[4], *filename, *fn; + int temp_string_index, s; + struct stat finfo; + + temp_string_index = 0; + if (quote_char && rl_point && rl_completion_suppress_quote == 0 && + rl_line_buffer[rl_point - 1] != quote_char) + temp_string[temp_string_index++] = quote_char; + + if (delimiter) + temp_string[temp_string_index++] = delimiter; + 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'; + + if (rl_filename_completion_desired) + { + filename = tilde_expand (text); + if (rl_filename_stat_hook) + { + fn = savestring (filename); + (*rl_filename_stat_hook) (&fn); + xfree (filename); + filename = fn; + } + 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_completion_suppress_append == 0 */) + { + /* 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 + directory. */ + else if (s == 0 && S_ISLNK (finfo.st_mode) && path_isdir (filename)) + ; +#endif + else + { + if (rl_point == rl_end && temp_string_index) + rl_insert_text (temp_string); + } + xfree (filename); + } + else + { + if (rl_point == rl_end && temp_string_index) + rl_insert_text (temp_string); + } + + return (temp_string_index); +} + +static void +insert_all_matches (char **matches, int point, char *qc) +{ + int i; + char *rp; + + rl_begin_undo_group (); + /* remove any opening quote character; make_quoted_replacement will add + it back. */ + if (qc && *qc && point && rl_line_buffer[point - 1] == *qc) + point--; + rl_delete_text (point, rl_point); + rl_point = point; + + if (matches[1]) + { + for (i = 1; matches[i]; i++) + { + rp = make_quoted_replacement (matches[i], SINGLE_MATCH, qc); + rl_insert_text (rp); + rl_insert_text (" "); + if (rp != matches[i]) + xfree (rp); + } + } + else + { + rp = make_quoted_replacement (matches[0], SINGLE_MATCH, qc); + rl_insert_text (rp); + rl_insert_text (" "); + if (rp != matches[0]) + xfree (rp); + } + rl_end_undo_group (); +} + +void +_rl_free_match_list (char **matches) +{ + register int i; + + if (matches == 0) + return; + + for (i = 0; matches[i]; i++) + xfree (matches[i]); + xfree (matches); +} + +/* Compare a possibly-quoted filename TEXT from the line buffer and a possible + MATCH that is the product of filename completion, which acts on the dequoted + text. */ +static int +compare_match (char *text, const char *match) +{ + char *temp; + int r; + + if (rl_filename_completion_desired && rl_filename_quoting_desired && + rl_completion_found_quote && rl_filename_dequoting_function) + { + temp = (*rl_filename_dequoting_function) (text, rl_completion_quote_character); + r = strcmp (temp, match); + xfree (temp); + return r; + } + return (strcmp (text, match)); +} + +/* Complete the word at or before point. + WHAT_TO_DO says what to do with the completion. + `?' means list the possible completions. + TAB means do standard completion. + `*' means insert all of the possible completions. + `!' means to do standard completion, and list all possible completions if + there is more than one. + `@' means to do standard completion, and list all possible completions if + there is more than one and partial completion is not possible. */ +int +rl_complete_internal (int what_to_do) +{ + char **matches; + rl_compentry_func_t *our_func; + int start, end, delimiter, found_quote, i, nontrivial_lcd; + char *text, *saved_line_buffer; + char quote_char; + int tlen, mlen, saved_last_completion_failed; + + RL_SETSTATE(RL_STATE_COMPLETING); + + saved_last_completion_failed = last_completion_failed; + + set_completion_defaults (what_to_do); + + saved_line_buffer = rl_line_buffer ? savestring (rl_line_buffer) : (char *)NULL; + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + /* We now look backwards for the start of a filename/variable word. */ + end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + start = rl_point; + rl_point = end; + + text = rl_copy_text (start, end); + matches = gen_completion_matches (text, start, end, our_func, found_quote, quote_char); + /* nontrivial_lcd is set if the common prefix adds something to the word + being completed. */ + nontrivial_lcd = matches && compare_match (text, matches[0]) != 0; + if (what_to_do == '!' || what_to_do == '@') + tlen = strlen (text); + xfree (text); + + if (matches == 0) + { + rl_ding (); + FREE (saved_line_buffer); + completion_changed_buffer = 0; + last_completion_failed = 1; + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + return (0); + } + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + i = rl_filename_completion_desired; + + if (postprocess_matches (&matches, i) == 0) + { + rl_ding (); + FREE (saved_line_buffer); + completion_changed_buffer = 0; + last_completion_failed = 1; + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + return (0); + } + + if (matches && matches[0] && *matches[0]) + last_completion_failed = 0; + + switch (what_to_do) + { + case TAB: + case '!': + case '@': + /* Insert the first match with proper quoting. */ + if (what_to_do == TAB) + { + if (*matches[0]) + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + } + else if (*matches[0] && matches[1] == 0) + /* should we perform the check only if there are multiple matches? */ + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + else if (*matches[0]) /* what_to_do != TAB && multiple matches */ + { + mlen = *matches[0] ? strlen (matches[0]) : 0; + if (mlen >= tlen) + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + } + + /* If there are more matches, ring the bell to indicate. + If we are in vi mode, Posix.2 says to not ring the bell. + If the `show-all-if-ambiguous' variable is set, display + all the matches immediately. Otherwise, if this was the + only match, and we are hacking files, check the file to + see if it was a directory. If so, and the `mark-directories' + variable is set, add a '/' to the name. If not, and we + are at the end of the line, then add a space. */ + if (matches[1]) + { + if (what_to_do == '!') + { + display_matches (matches); + break; + } + else if (what_to_do == '@') + { + if (nontrivial_lcd == 0) + display_matches (matches); + break; + } + else if (rl_editing_mode != vi_mode) + rl_ding (); /* There are other matches remaining. */ + } + else + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + + break; + + case '*': + insert_all_matches (matches, start, "e_char); + break; + + case '?': + /* Let's try to insert a single match here if the last completion failed + but this attempt returned a single match. */ + if (saved_last_completion_failed && matches[0] && *matches[0] && matches[1] == 0) + { + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + break; + } + + if (rl_completion_display_matches_hook == 0) + { + _rl_sigcleanup = _rl_complete_sigcleanup; + _rl_sigcleanarg = matches; + _rl_complete_display_matches_interrupt = 0; + } + display_matches (matches); + if (_rl_complete_display_matches_interrupt) + { + matches = 0; /* already freed by rl_complete_sigcleanup */ + _rl_complete_display_matches_interrupt = 0; + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); /* XXX */ + } + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + break; + + default: + _rl_ttymsg ("bad value %d for what_to_do in rl_complete", what_to_do); + rl_ding (); + FREE (saved_line_buffer); + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_free_match_list (matches); + _rl_reset_completion_state (); + return 1; + } + + _rl_free_match_list (matches); + + /* Check to see if the line has changed through all of this manipulation. */ + if (saved_line_buffer) + { + completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0; + xfree (saved_line_buffer); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + + RL_CHECK_SIGNALS (); + return 0; +} + +/***************************************************************/ +/* */ +/* Application-callable completion match generator functions */ +/* */ +/***************************************************************/ + +/* Return an array of (char *) which is a list of completions for TEXT. + If there are no completions, return a NULL pointer. + The first entry in the returned array is the substitution for TEXT. + The remaining entries are the possible completions. + The array is terminated with a NULL pointer. + + ENTRY_FUNCTION is a function of two args, and returns a (char *). + The first argument is TEXT. + The second is a state argument; it should be zero on the first call, and + non-zero on subsequent calls. It returns a NULL pointer to the caller + when there are no more matches. + */ +char ** +rl_completion_matches (const char *text, rl_compentry_func_t *entry_function) +{ + register int i; + + /* Number of slots in match_list. */ + int match_list_size; + + /* The list of matches. */ + char **match_list; + + /* Number of matches actually found. */ + int matches; + + /* Temporary string binder. */ + char *string; + + matches = 0; + match_list_size = 10; + match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *)); + match_list[1] = (char *)NULL; + + while (string = (*entry_function) (text, matches)) + { + if (RL_SIG_RECEIVED ()) + { + /* Start at 1 because we don't set matches[0] in this function. + Only free the list members if we're building match list from + rl_filename_completion_function, since we know that doesn't + free the strings it returns. */ + if (entry_function == rl_filename_completion_function) + { + for (i = 1; match_list[i]; i++) + xfree (match_list[i]); + } + xfree (match_list); + match_list = 0; + match_list_size = 0; + matches = 0; + RL_CHECK_SIGNALS (); + } + + if (matches + 1 >= match_list_size) + match_list = (char **)xrealloc + (match_list, ((match_list_size += 10) + 1) * sizeof (char *)); + + if (match_list == 0) + return (match_list); + + match_list[++matches] = string; + match_list[matches + 1] = (char *)NULL; + } + + /* If there were any matches, then look through them finding out the + lowest common denominator. That then becomes match_list[0]. */ + if (matches) + compute_lcd_of_matches (match_list, matches, text); + else /* There were no matches. */ + { + xfree (match_list); + match_list = (char **)NULL; + } + return (match_list); +} + +/* A completion function for usernames. + TEXT contains a partial username preceded by a random + character (usually `~'). */ +char * +rl_username_completion_function (const char *text, int state) +{ +#if defined (__WIN32__) || defined (__OPENNT) + return (char *)NULL; +#else /* !__WIN32__ && !__OPENNT) */ + static char *username = (char *)NULL; + static struct passwd *entry; + static int namelen, first_char, first_char_loc; + char *value; + + if (state == 0) + { + FREE (username); + + first_char = *text; + first_char_loc = first_char == '~'; + + username = savestring (&text[first_char_loc]); + namelen = strlen (username); +#if defined (HAVE_GETPWENT) + setpwent (); +#endif + } + +#if defined (HAVE_GETPWENT) + while (entry = getpwent ()) + { + /* Null usernames should result in all users as possible completions. */ + if (namelen == 0 || (STREQN (username, entry->pw_name, namelen))) + break; + } +#endif + + if (entry == 0) + { +#if defined (HAVE_GETPWENT) + endpwent (); +#endif + return ((char *)NULL); + } + else + { + value = (char *)xmalloc (2 + strlen (entry->pw_name)); + + *value = *text; + + strcpy (value + first_char_loc, entry->pw_name); + + if (first_char == '~') + rl_filename_completion_desired = 1; + + return (value); + } +#endif /* !__WIN32__ && !__OPENNT */ +} + +/* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME + (FILENAME_LEN). If _rl_completion_case_fold is set, compare without + regard to the alphabetic case of characters. If + _rl_completion_case_map is set, make `-' and `_' equivalent. CONVFN is + the possibly-converted directory entry; FILENAME is what the user typed. */ +static int +complete_fncmp (const char *convfn, int convlen, const char *filename, int filename_len) +{ + register char *s1, *s2; + int d, len; +#if defined (HANDLE_MULTIBYTE) + size_t v1, v2; + mbstate_t ps1, ps2; + WCHAR_T wc1, wc2; +#endif + +#if defined (HANDLE_MULTIBYTE) + memset (&ps1, 0, sizeof (mbstate_t)); + memset (&ps2, 0, sizeof (mbstate_t)); +#endif + + if (filename_len == 0) + return 1; + if (convlen < filename_len) + return 0; + + len = filename_len; + s1 = (char *)convfn; + s2 = (char *)filename; + + /* Otherwise, if these match up to the length of filename, then + it is a match. */ + if (_rl_completion_case_fold && _rl_completion_case_map) + { + /* Case-insensitive comparison treating _ and - as equivalent */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + do + { + v1 = MBRTOWC (&wc1, s1, convlen, &ps1); + v2 = MBRTOWC (&wc2, s2, filename_len, &ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (*s1 != *s2) /* do byte comparison */ + return 0; + else if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + return 0; + s1++; s2++; len--; + continue; + } + wc1 = towlower (wc1); + wc2 = towlower (wc2); + s1 += v1; + s2 += v1; + len -= v1; + if ((wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_')) + continue; + if (wc1 != wc2) + return 0; + } + while (len != 0); + } + else +#endif + { + do + { + d = _rl_to_lower (*s1) - _rl_to_lower (*s2); + /* *s1 == [-_] && *s2 == [-_] */ + if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + d = 0; + if (d != 0) + return 0; + s1++; s2++; /* already checked convlen >= filename_len */ + } + while (--len != 0); + } + + return 1; + } + else if (_rl_completion_case_fold) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + do + { + v1 = MBRTOWC (&wc1, s1, convlen, &ps1); + v2 = MBRTOWC (&wc2, s2, filename_len, &ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (*s1 != *s2) /* do byte comparison */ + return 0; + s1++; s2++; len--; + continue; + } + wc1 = towlower (wc1); + wc2 = towlower (wc2); + if (wc1 != wc2) + return 0; + s1 += v1; + s2 += v1; + len -= v1; + } + while (len != 0); + return 1; + } + else +#endif + if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) && + (convlen >= filename_len) && + (_rl_strnicmp (filename, convfn, filename_len) == 0)) + return 1; + } + else + { + if ((convfn[0] == filename[0]) && + (convlen >= filename_len) && + (strncmp (filename, convfn, filename_len) == 0)) + return 1; + } + return 0; +} + +/* Okay, now we write the entry_function for filename completion. In the + general case. Note that completion in the shell is a little different + because of all the pathnames that must be followed when looking up the + completion for a command. */ +char * +rl_filename_completion_function (const char *text, int state) +{ + static DIR *directory = (DIR *)NULL; + static char *filename = (char *)NULL; + static char *dirname = (char *)NULL; + static char *users_dirname = (char *)NULL; + static int filename_len; + char *temp, *dentry, *convfn; + int dirlen, dentlen, convlen; + int tilde_dirname; + struct dirent *entry; + + /* If we don't have any state, then do some initialization. */ + if (state == 0) + { + /* If we were interrupted before closing the directory or reading + all of its contents, close it. */ + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + FREE (dirname); + FREE (filename); + FREE (users_dirname); + + filename = savestring (text); + if (*text == 0) + text = "."; + dirname = savestring (text); + + temp = strrchr (dirname, '/'); + +#if defined (__MSDOS__) || defined (_WIN32) + /* special hack for //X/... */ + if (dirname[0] == '/' && dirname[1] == '/' && ISALPHA ((unsigned char)dirname[2]) && dirname[3] == '/') + temp = strrchr (dirname + 3, '/'); +#endif + + if (temp) + { + strcpy (filename, ++temp); + *temp = '\0'; + } +#if defined (__MSDOS__) || (defined (_WIN32) && !defined (__CYGWIN__)) + /* searches from current directory on the drive */ + else if (ISALPHA ((unsigned char)dirname[0]) && dirname[1] == ':') + { + strcpy (filename, dirname + 2); + dirname[2] = '\0'; + } +#endif + else + { + dirname[0] = '.'; + dirname[1] = '\0'; + } + + /* We aren't done yet. We also support the "~user" syntax. */ + + /* Save the version of the directory that the user typed, dequoting + it if necessary. */ + if (rl_completion_found_quote && rl_filename_dequoting_function) + users_dirname = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character); + else + users_dirname = savestring (dirname); + + tilde_dirname = 0; + if (*dirname == '~') + { + temp = tilde_expand (dirname); + xfree (dirname); + dirname = temp; + tilde_dirname = 1; + } + + /* We have saved the possibly-dequoted version of the directory name + the user typed. Now transform the directory name we're going to + pass to opendir(2). The directory rewrite hook modifies only the + directory name; the directory completion hook modifies both the + directory name passed to opendir(2) and the version the user + typed. Both the directory completion and rewrite hooks should perform + any necessary dequoting. The hook functions return 1 if they modify + the directory name argument. If either hook returns 0, it should + not modify the directory name pointer passed as an argument. */ + if (rl_directory_rewrite_hook) + (*rl_directory_rewrite_hook) (&dirname); + else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname)) + { + xfree (users_dirname); + users_dirname = savestring (dirname); + } + else if (tilde_dirname == 0 && rl_completion_found_quote && rl_filename_dequoting_function) + { + /* delete single and double quotes */ + xfree (dirname); + dirname = savestring (users_dirname); + } + directory = opendir (dirname); + + /* Now dequote a non-null filename. FILENAME will not be NULL, but may + be empty. */ + if (*filename && rl_completion_found_quote && rl_filename_dequoting_function) + { + /* delete single and double quotes */ + temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character); + xfree (filename); + filename = temp; + } + filename_len = strlen (filename); + + rl_filename_completion_desired = 1; + } + + /* At this point we should entertain the possibility of hacking wildcarded + filenames, like /usr/man/man<WILD>/te<TAB>. If the directory name + contains globbing characters, then build an array of directories, and + then map over that list while completing. */ + /* *** UNIMPLEMENTED *** */ + + /* Now that we have some state, we can read the directory. */ + + entry = (struct dirent *)NULL; + while (directory && (entry = readdir (directory))) + { + convfn = dentry = entry->d_name; + convlen = dentlen = D_NAMLEN (entry); + + if (rl_filename_rewrite_hook) + { + convfn = (*rl_filename_rewrite_hook) (dentry, dentlen); + convlen = (convfn == dentry) ? dentlen : strlen (convfn); + } + + /* Special case for no filename. If the user has disabled the + `match-hidden-files' variable, skip filenames beginning with `.'. + All other entries except "." and ".." match. */ + if (filename_len == 0) + { + if (_rl_match_hidden_files == 0 && HIDDEN_FILE (convfn)) + continue; + + if (convfn[0] != '.' || + (convfn[1] && (convfn[1] != '.' || convfn[2]))) + break; + } + else + { + if (complete_fncmp (convfn, convlen, filename, filename_len)) + break; + } + } + + if (entry == 0) + { + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + if (dirname) + { + xfree (dirname); + dirname = (char *)NULL; + } + if (filename) + { + xfree (filename); + filename = (char *)NULL; + } + if (users_dirname) + { + xfree (users_dirname); + users_dirname = (char *)NULL; + } + + return (char *)NULL; + } + else + { + /* dirname && (strcmp (dirname, ".") != 0) */ + if (dirname && (dirname[0] != '.' || dirname[1])) + { + if (rl_complete_with_tilde_expansion && *users_dirname == '~') + { + dirlen = strlen (dirname); + temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry)); + strcpy (temp, dirname); + /* Canonicalization cuts off any final slash present. We + may need to add it back. */ + if (dirname[dirlen - 1] != '/') + { + temp[dirlen++] = '/'; + temp[dirlen] = '\0'; + } + } + else + { + dirlen = strlen (users_dirname); + temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry)); + strcpy (temp, users_dirname); + /* Make sure that temp has a trailing slash here. */ + if (users_dirname[dirlen - 1] != '/') + temp[dirlen++] = '/'; + } + + strcpy (temp + dirlen, convfn); + } + else + temp = savestring (convfn); + + if (convfn != dentry) + xfree (convfn); + + return (temp); + } +} + +/* An initial implementation of a menu completion function a la tcsh. The + first time (if the last readline command was not rl_old_menu_complete), we + generate the list of matches. This code is very similar to the code in + rl_complete_internal -- there should be a way to combine the two. Then, + for each item in the list of matches, we insert the match in an undoable + fashion, with the appropriate character appended (this happens on the + second and subsequent consecutive calls to rl_old_menu_complete). When we + hit the end of the match list, we restore the original unmatched text, + ring the bell, and reset the counter to zero. */ +int +rl_old_menu_complete (int count, int invoking_key) +{ + rl_compentry_func_t *our_func; + int matching_filenames, found_quote; + + static char *orig_text; + static char **matches = (char **)0; + static int match_list_index = 0; + static int match_list_size = 0; + static int orig_start, orig_end; + static char quote_char; + static int delimiter; + + /* The first time through, we generate the list of matches and set things + up to insert them. */ + if (rl_last_func != rl_old_menu_complete) + { + /* Clean up from previous call, if any. */ + FREE (orig_text); + if (matches) + _rl_free_match_list (matches); + + match_list_index = match_list_size = 0; + matches = (char **)NULL; + + rl_completion_invoking_key = invoking_key; + + RL_SETSTATE(RL_STATE_COMPLETING); + + /* Only the completion entry function can change these. */ + set_completion_defaults ('%'); + + our_func = rl_menu_completion_entry_function; + if (our_func == 0) + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + + /* We now look backwards for the start of a filename/variable word. */ + orig_end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + orig_start = rl_point; + rl_point = orig_end; + + orig_text = rl_copy_text (orig_start, orig_end); + matches = gen_completion_matches (orig_text, orig_start, orig_end, + our_func, found_quote, quote_char); + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + matching_filenames = rl_filename_completion_desired; + + if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + FREE (orig_text); + orig_text = (char *)0; + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + return (0); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + + for (match_list_size = 0; matches[match_list_size]; match_list_size++) + ; + /* matches[0] is lcd if match_list_size > 1, but the circular buffer + code below should take care of it. */ + + if (match_list_size > 1 && _rl_complete_show_all) + display_matches (matches); + } + + /* Now we have the list of matches. Replace the text between + rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with + matches[match_list_index], and add any necessary closing char. */ + + if (matches == 0 || match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + completion_changed_buffer = 0; + return (0); + } + + match_list_index += count; + if (match_list_index < 0) + { + while (match_list_index < 0) + match_list_index += match_list_size; + } + else + match_list_index %= match_list_size; + + if (match_list_index == 0 && match_list_size > 1) + { + rl_ding (); + insert_match (orig_text, orig_start, MULT_MATCH, "e_char); + } + else + { + insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char); + append_to_match (matches[match_list_index], delimiter, quote_char, + compare_match (orig_text, matches[match_list_index])); + } + + completion_changed_buffer = 1; + return (0); +} + +/* The current version of menu completion. + The differences between this function and the original are: + +1. It honors the maximum number of completions variable (completion-query-items) +2. It appends to the word as usual if there is only one match +3. It displays the common prefix if there is one, and makes it the first menu + choice if the menu-complete-display-prefix option is enabled +*/ + +int +rl_menu_complete (int count, int ignore) +{ + rl_compentry_func_t *our_func; + int matching_filenames, found_quote; + + static char *orig_text; + static char **matches = (char **)0; + static int match_list_index = 0; + static int match_list_size = 0; + static int nontrivial_lcd = 0; + static int full_completion = 0; /* set to 1 if menu completion should reinitialize on next call */ + static int orig_start, orig_end; + static char quote_char; + static int delimiter, cstate; + + /* The first time through, we generate the list of matches and set things + up to insert them. */ + if ((rl_last_func != rl_menu_complete && rl_last_func != rl_backward_menu_complete) || full_completion) + { + /* Clean up from previous call, if any. */ + FREE (orig_text); + if (matches) + _rl_free_match_list (matches); + + match_list_index = match_list_size = 0; + matches = (char **)NULL; + + full_completion = 0; + + RL_SETSTATE(RL_STATE_COMPLETING); + + /* Only the completion entry function can change these. */ + set_completion_defaults ('%'); + + our_func = rl_menu_completion_entry_function; + if (our_func == 0) + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + + /* We now look backwards for the start of a filename/variable word. */ + orig_end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + orig_start = rl_point; + rl_point = orig_end; + + orig_text = rl_copy_text (orig_start, orig_end); + matches = gen_completion_matches (orig_text, orig_start, orig_end, + our_func, found_quote, quote_char); + + nontrivial_lcd = matches && compare_match (orig_text, matches[0]) != 0; + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + matching_filenames = rl_filename_completion_desired; + + if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + FREE (orig_text); + orig_text = (char *)0; + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + return (0); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + + for (match_list_size = 0; matches[match_list_size]; match_list_size++) + ; + + if (match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + match_list_index = 0; + completion_changed_buffer = 0; + return (0); + } + + /* matches[0] is lcd if match_list_size > 1, but the circular buffer + code below should take care of it. */ + if (*matches[0]) + { + insert_match (matches[0], orig_start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + orig_end = orig_start + strlen (matches[0]); + completion_changed_buffer = STREQ (orig_text, matches[0]) == 0; + } + + if (match_list_size > 1 && _rl_complete_show_all) + { + display_matches (matches); + /* If there are so many matches that the user has to be asked + whether or not he wants to see the matches, menu completion + is unwieldy. */ + if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + full_completion = 1; + return (0); + } + else if (_rl_menu_complete_prefix_first) + { + rl_ding (); + return (0); + } + } + else if (match_list_size <= 1) + { + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + full_completion = 1; + return (0); + } + else if (_rl_menu_complete_prefix_first && match_list_size > 1) + { + rl_ding (); + return (0); + } + } + + /* Now we have the list of matches. Replace the text between + rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with + matches[match_list_index], and add any necessary closing char. */ + + if (matches == 0 || match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + completion_changed_buffer = 0; + return (0); + } + + match_list_index += count; + if (match_list_index < 0) + { + while (match_list_index < 0) + match_list_index += match_list_size; + } + else + match_list_index %= match_list_size; + + if (match_list_index == 0 && match_list_size > 1) + { + rl_ding (); + insert_match (matches[0], orig_start, MULT_MATCH, "e_char); + } + else + { + insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char); + append_to_match (matches[match_list_index], delimiter, quote_char, + compare_match (orig_text, matches[match_list_index])); + } + + completion_changed_buffer = 1; + return (0); +} + +int +rl_backward_menu_complete (int count, int key) +{ + /* Positive arguments to backward-menu-complete translate into negative + arguments for menu-complete, and vice versa. */ + return (rl_menu_complete (-count, key)); +} diff --git a/lib/readline/display.c b/lib/readline/display.c new file mode 100644 index 0000000..c1135ec --- /dev/null +++ b/lib/readline/display.c @@ -0,0 +1,3583 @@ +/* display.c -- readline redisplay facility. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "posixstat.h" + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <stdio.h> + +#ifdef __MSDOS__ +# include <pc.h> +#endif + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +/* Termcap library stuff. */ +#include "tcap.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +static void putc_face (int, int, char *); +static void puts_face (const char *, const char *, int); +static void norm_face (char *, int); + +static void update_line (char *, char *, char *, char *, int, int, int, int); +static void space_to_eol (int); +static void delete_chars (int); +static void insert_some_chars (char *, int, int); +static void open_some_spaces (int); +static void cr (void); +static void redraw_prompt (char *); +static void _rl_move_cursor_relative (int, const char *, const char *); + +/* Values for FLAGS */ +#define PMT_MULTILINE 0x01 + +static char *expand_prompt (char *, int, int *, int *, int *, int *); + +#define DEFAULT_LINE_BUFFER_SIZE 1024 + +/* State of visible and invisible lines. */ +struct line_state + { + char *line; + char *lface; + int *lbreaks; + int lbsize; +#if defined (HANDLE_MULTIBYTE) + int wbsize; + int *wrapped_line; +#endif + }; + +/* The line display buffers. One is the line currently displayed on + the screen. The other is the line about to be displayed. */ +static struct line_state line_state_array[2]; +static struct line_state *line_state_visible = &line_state_array[0]; +static struct line_state *line_state_invisible = &line_state_array[1]; +static int line_structures_initialized = 0; + +/* Backwards-compatible names. */ +#define inv_lbreaks (line_state_invisible->lbreaks) +#define inv_lbsize (line_state_invisible->lbsize) +#define vis_lbreaks (line_state_visible->lbreaks) +#define vis_lbsize (line_state_visible->lbsize) + +#define visible_line (line_state_visible->line) +#define vis_face (line_state_visible->lface) +#define invisible_line (line_state_invisible->line) +#define inv_face (line_state_invisible->lface) + +#if defined (HANDLE_MULTIBYTE) +static int _rl_col_width (const char *, int, int, int); +#else +# define _rl_col_width(l, s, e, f) (((e) <= (s)) ? 0 : (e) - (s)) +#endif + +/* Heuristic used to decide whether it is faster to move from CUR to NEW + by backing up or outputting a carriage return and moving forward. CUR + and NEW are either both buffer positions or absolute screen positions. */ +#define CR_FASTER(new, cur) (((new) + 1) < ((cur) - (new))) + +/* _rl_last_c_pos is an absolute cursor position in multibyte locales and a + buffer index in others. This macro is used when deciding whether the + current cursor position is in the middle of a prompt string containing + invisible characters. XXX - might need to take `modmark' into account. */ +/* XXX - only valid when tested against _rl_last_c_pos; buffer indices need + to use prompt_last_invisible directly. */ +#define PROMPT_ENDING_INDEX \ + ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) ? prompt_physical_chars : prompt_last_invisible+1) + +#define FACE_NORMAL '0' +#define FACE_STANDOUT '1' +#define FACE_INVALID ((char)1) + +/* **************************************************************** */ +/* */ +/* Display stuff */ +/* */ +/* **************************************************************** */ + +/* This is the stuff that is hard for me. I never seem to write good + display routines in C. Let's see how I do this time. */ + +/* (PWP) Well... Good for a simple line updater, but totally ignores + the problems of input lines longer than the screen width. + + update_line and the code that calls it makes a multiple line, + automatically wrapping line update. Careful attention needs + to be paid to the vertical position variables. */ + +/* Keep two buffers; one which reflects the current contents of the + screen, and the other to draw what we think the new contents should + be. Then compare the buffers, and make whatever changes to the + screen itself that we should. Finally, make the buffer that we + just drew into be the one which reflects the current contents of the + screen, and place the cursor where it belongs. + + Commands that want to can fix the display themselves, and then let + this function know that the display has been fixed by setting the + RL_DISPLAY_FIXED variable. This is good for efficiency. */ + +/* Application-specific redisplay function. */ +rl_voidfunc_t *rl_redisplay_function = rl_redisplay; + +/* Global variables declared here. */ +/* What YOU turn on when you have handled all redisplay yourself. */ +int rl_display_fixed = 0; + +/* The stuff that gets printed out before the actual text of the line. + This is usually pointing to rl_prompt. */ +char *rl_display_prompt = (char *)NULL; + +/* Variables used to include the editing mode in the prompt. */ +char *_rl_emacs_mode_str; +int _rl_emacs_modestr_len; + +char *_rl_vi_ins_mode_str; +int _rl_vi_ins_modestr_len; + +char *_rl_vi_cmd_mode_str; +int _rl_vi_cmd_modestr_len; + +/* Pseudo-global variables declared here. */ + +/* Hints for other parts of readline to give to the display engine. */ +int _rl_suppress_redisplay = 0; +int _rl_want_redisplay = 0; + +/* The visible cursor position. If you print some text, adjust this. */ +/* NOTE: _rl_last_c_pos is used as a buffer index when not in a locale + supporting multibyte characters, and an absolute cursor position when + in such a locale. This is an artifact of the donated multibyte support. + Care must be taken when modifying its value. */ +int _rl_last_c_pos = 0; +int _rl_last_v_pos = 0; + +/* Number of physical lines consumed by the current line buffer currently + on screen minus 1. */ +int _rl_vis_botlin = 0; + +static int _rl_quick_redisplay = 0; + +/* This is a hint update_line gives to rl_redisplay that it has adjusted the + value of _rl_last_c_pos *and* taken the presence of any invisible chars in + the prompt into account. rl_redisplay notes this and does not do the + adjustment itself. */ +static int cpos_adjusted; + +/* The index into the line buffer corresponding to the cursor position */ +static int cpos_buffer_position; + +/* A flag to note when we're displaying the first line of the prompt */ +static int displaying_prompt_first_line; +/* The number of multibyte characters in the prompt, if any */ +static int prompt_multibyte_chars; + +static int _rl_inv_botlin = 0; + +/* Variables used only in this file. */ +/* The last left edge of text that was displayed. This is used when + doing horizontal scrolling. It shifts in thirds of a screenwidth. */ +static int last_lmargin; + +/* A buffer for `modeline' messages. */ +static char *msg_buf = 0; +static int msg_bufsiz = 0; + +/* Non-zero forces the redisplay even if we thought it was unnecessary. */ +static int forced_display; + +/* Default and initial buffer size. Can grow. */ +static int line_size = 0; + +/* Set to a non-zero value if horizontal scrolling has been enabled + automatically because the terminal was resized to height 1. */ +static int horizontal_scrolling_autoset = 0; /* explicit initialization */ + +/* Variables to keep track of the expanded prompt string, which may + include invisible characters. */ + +static char *local_prompt, *local_prompt_prefix; +static int local_prompt_len; +static int prompt_prefix_length; +/* Number of chars in the buffer that contribute to visible chars on the screen. + This might be different from the number of physical chars in the presence + of multibyte characters */ +static int prompt_visible_length; + +/* The number of invisible characters in the line currently being + displayed on the screen. */ +static int visible_wrap_offset; + +/* The number of invisible characters in the prompt string. Static so it + can be shared between rl_redisplay and update_line */ +static int wrap_offset; + +/* The index of the last invisible character in the prompt string. */ +static int prompt_last_invisible; + +/* The length (buffer offset) of the first line of the last (possibly + multi-line) buffer displayed on the screen. */ +static int visible_first_line_len; + +/* Number of invisible characters on the first physical line of the prompt. + Only valid when the number of physical characters in the prompt exceeds + (or is equal to) _rl_screenwidth. */ +static int prompt_invis_chars_first_line; + +static int prompt_last_screen_line; + +static int prompt_physical_chars; + +/* An array of indexes into the prompt string where we will break physical + screen lines. It's easier to compute in expand_prompt and use later in + rl_redisplay instead of having rl_redisplay try to guess about invisible + characters in the prompt or use heuristics about where they are. */ +static int *local_prompt_newlines; + +/* set to a non-zero value by rl_redisplay if we are marking modified history + lines and the current line is so marked. */ +static int modmark; + +static int line_totbytes; + +/* Variables to save and restore prompt and display information. */ + +/* These are getting numerous enough that it's time to create a struct. */ + +static char *saved_local_prompt; +static char *saved_local_prefix; +static int *saved_local_prompt_newlines; + +static int saved_last_invisible; +static int saved_visible_length; +static int saved_prefix_length; +static int saved_local_length; +static int saved_invis_chars_first_line; +static int saved_physical_chars; + +/* Return a string indicating the editing mode, for use in the prompt. */ + +static char * +prompt_modestr (int *lenp) +{ + if (rl_editing_mode == emacs_mode) + { + if (lenp) + *lenp = _rl_emacs_mode_str ? _rl_emacs_modestr_len : RL_EMACS_MODESTR_DEFLEN; + return _rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT; + } + else if (_rl_keymap == vi_insertion_keymap) + { + if (lenp) + *lenp = _rl_vi_ins_mode_str ? _rl_vi_ins_modestr_len : RL_VI_INS_MODESTR_DEFLEN; + return _rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT; /* vi insert mode */ + } + else + { + if (lenp) + *lenp = _rl_vi_cmd_mode_str ? _rl_vi_cmd_modestr_len : RL_VI_CMD_MODESTR_DEFLEN; + return _rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT; /* vi command mode */ + } +} + +/* Expand the prompt string S and return the number of visible + characters in *LP, if LP is not null. This is currently more-or-less + a placeholder for expansion. LIP, if non-null is a place to store the + index of the last invisible character in the returned string. NIFLP, + if non-zero, is a place to store the number of invisible characters in + the first prompt line. The previous are used as byte counts -- indexes + into a character buffer. *VLP gets the number of physical characters in + the expanded prompt (visible length) */ + +/* Current implementation: + \001 (^A) start non-visible characters + \002 (^B) end non-visible characters + all characters except \001 and \002 (following a \001) are copied to + the returned string; all characters except those between \001 and + \002 are assumed to be `visible'. */ + +/* Possible values for FLAGS: + PMT_MULTILINE caller indicates that this is part of a multiline prompt +*/ + +/* This approximates the number of lines the prompt will take when displayed */ +#define APPROX_DIV(n, d) (((n) < (d)) ? 1 : ((n) / (d)) + 1) + +static char * +expand_prompt (char *pmt, int flags, int *lp, int *lip, int *niflp, int *vlp) +{ + char *r, *ret, *p, *igstart, *nprompt, *ms; + int l, rl, last, ignoring, ninvis, invfl, invflset, ind, pind, physchars; + int mlen, newlines, newlines_guess, bound, can_add_invis; + int mb_cur_max; + + /* We only expand the mode string for the last line of a multiline prompt + (a prompt with embedded newlines). */ + ms = (((pmt == rl_prompt) ^ (flags & PMT_MULTILINE)) && _rl_show_mode_in_prompt) ? prompt_modestr (&mlen) : 0; + if (ms) + { + l = strlen (pmt); + nprompt = (char *)xmalloc (l + mlen + 1); + memcpy (nprompt, ms, mlen); + strcpy (nprompt + mlen, pmt); + } + else + nprompt = pmt; + + can_add_invis = 0; + mb_cur_max = MB_CUR_MAX; + + if (_rl_screenwidth == 0) + _rl_get_screen_size (0, 0); /* avoid division by zero */ + + /* Short-circuit if we can. We can do this if we are treating the prompt as + a sequence of bytes and there are no invisible characters in the prompt + to deal with. Since we populate local_prompt_newlines, we have to run + through the rest of the function if this prompt looks like it's going to + be longer than one screen line. */ + if ((mb_cur_max <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0) + { + l = strlen (nprompt); + if (l < (_rl_screenwidth > 0 ? _rl_screenwidth : 80)) + { + r = (nprompt == pmt) ? savestring (pmt) : nprompt; + if (lp) + *lp = l; + if (lip) + *lip = 0; + if (niflp) + *niflp = 0; + if (vlp) + *vlp = l; + + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * 2); + local_prompt_newlines[0] = 0; + local_prompt_newlines[1] = -1; + + return r; + } + } + + l = strlen (nprompt); /* XXX */ + r = ret = (char *)xmalloc (l + 1); + + /* Guess at how many screen lines the prompt will take to size the array that + keeps track of where the line wraps happen */ + newlines_guess = (_rl_screenwidth > 0) ? APPROX_DIV(l, _rl_screenwidth) : APPROX_DIV(l, 80); + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * (newlines_guess + 1)); + local_prompt_newlines[newlines = 0] = 0; + for (rl = 1; rl <= newlines_guess; rl++) + local_prompt_newlines[rl] = -1; + + rl = physchars = 0; /* mode string now part of nprompt */ + invfl = 0; /* invisible chars in first line of prompt */ + invflset = 0; /* we only want to set invfl once */ + igstart = 0; /* we're not ignoring any characters yet */ + + for (ignoring = last = ninvis = 0, p = nprompt; p && *p; p++) + { + /* This code strips the invisible character string markers + RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE */ + if (ignoring == 0 && *p == RL_PROMPT_START_IGNORE) /* XXX - check ignoring? */ + { + ignoring = 1; + igstart = p; + continue; + } + else if (ignoring && *p == RL_PROMPT_END_IGNORE) + { + ignoring = 0; + /* If we have a run of invisible characters, adjust local_prompt_newlines + to add them, since update_line expects them to be counted before + wrapping the line. */ + if (can_add_invis) + { + local_prompt_newlines[newlines] = r - ret; + /* If we're adding to the number of invisible characters on the + first line of the prompt, but we've already set the number of + invisible characters on that line, we need to adjust the + counter. */ + if (invflset && newlines == 1) + invfl = ninvis; + } + if (p != (igstart + 1)) + last = r - ret - 1; + continue; + } + else + { +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + pind = p - nprompt; + ind = _rl_find_next_mbchar (nprompt, pind, 1, MB_FIND_NONZERO); + l = ind - pind; + while (l--) + *r++ = *p++; + if (!ignoring) + { + /* rl ends up being assigned to prompt_visible_length, + which is the number of characters in the buffer that + contribute to characters on the screen, which might + not be the same as the number of physical characters + on the screen in the presence of multibyte characters */ + rl += ind - pind; + physchars += _rl_col_width (nprompt, pind, ind, 0); + } + else + ninvis += ind - pind; + p--; /* compensate for later increment */ + } + else +#endif + { + *r++ = *p; + if (!ignoring) + { + rl++; /* visible length byte counter */ + physchars++; + } + else + ninvis++; /* invisible chars byte counter */ + } + + if (invflset == 0 && physchars >= _rl_screenwidth) + { + invfl = ninvis; + invflset = 1; + } + + if (physchars >= (bound = (newlines + 1) * _rl_screenwidth) && local_prompt_newlines[newlines+1] == -1) + { + int new; + if (physchars > bound) /* should rarely happen */ + { +#if defined (HANDLE_MULTIBYTE) + *r = '\0'; /* need null-termination for strlen */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + new = _rl_find_prev_mbchar (ret, r - ret, MB_FIND_ANY); + else +#endif + new = r - ret - (physchars - bound); /* XXX */ + } + else + new = r - ret; + local_prompt_newlines[++newlines] = new; + } + + /* What if a physical character of width >= 2 is split? There is + code that wraps before the physical screen width if the character + width would exceed it, but it needs to be checked against this + code and local_prompt_newlines[]. */ + if (ignoring == 0) + can_add_invis = (physchars == bound); + } + } + + if (rl <= _rl_screenwidth) + invfl = ninvis; + + *r = '\0'; + if (lp) + *lp = rl; + if (lip) + *lip = last; + if (niflp) + *niflp = invfl; + if (vlp) + *vlp = physchars; + + if (nprompt != pmt) + xfree (nprompt); + + return ret; +} + +/* Just strip out RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE from + PMT and return the rest of PMT. */ +char * +_rl_strip_prompt (char *pmt) +{ + char *ret; + + ret = expand_prompt (pmt, 0, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL); + return ret; +} + +void +_rl_reset_prompt (void) +{ + rl_visible_prompt_length = rl_expand_prompt (rl_prompt); +} + +/* + * Expand the prompt string into the various display components, if + * necessary. + * + * local_prompt = expanded last line of string in rl_display_prompt + * (portion after the final newline) + * local_prompt_prefix = portion before last newline of rl_display_prompt, + * expanded via expand_prompt + * prompt_visible_length = number of visible characters in local_prompt + * prompt_prefix_length = number of visible characters in local_prompt_prefix + * + * It also tries to keep track of the number of invisible characters in the + * prompt string, and where they are. + * + * This function is called once per call to readline(). It may also be + * called arbitrarily to expand the primary prompt. + * + * The return value is the number of visible characters on the last line + * of the (possibly multi-line) prompt. In this case, multi-line means + * there are embedded newlines in the prompt string itself, not that the + * number of physical characters exceeds the screen width and the prompt + * wraps. + */ +int +rl_expand_prompt (char *prompt) +{ + char *p, *t; + int c; + + /* Clear out any saved values. */ + FREE (local_prompt); + FREE (local_prompt_prefix); + + local_prompt = local_prompt_prefix = (char *)0; + local_prompt_len = 0; + prompt_last_invisible = prompt_invis_chars_first_line = 0; + prompt_visible_length = prompt_physical_chars = 0; + + if (prompt == 0 || *prompt == 0) + return (0); + + p = strrchr (prompt, '\n'); + if (p == 0) + { + /* The prompt is only one logical line, though it might wrap. */ + local_prompt = expand_prompt (prompt, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)0; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + return (prompt_visible_length); + } + else + { + /* The prompt spans multiple lines. */ + t = ++p; + c = *t; *t = '\0'; + /* The portion of the prompt string up to and including the + final newline is now null-terminated. */ + local_prompt_prefix = expand_prompt (prompt, PMT_MULTILINE, + &prompt_prefix_length, + (int *)NULL, + (int *)NULL, + (int *)NULL); + *t = c; + + local_prompt = expand_prompt (p, PMT_MULTILINE, + &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + return (prompt_prefix_length); + } +} + +/* Allocate the various line structures, making sure they can hold MINSIZE + bytes. If the existing line size can accommodate MINSIZE bytes, don't do + anything. */ +static void +realloc_line (int minsize) +{ + int minimum_size; + int newsize, delta; + + minimum_size = DEFAULT_LINE_BUFFER_SIZE; + if (minsize < minimum_size) + minsize = minimum_size; + if (minsize <= _rl_screenwidth) /* XXX - for gdb */ + minsize = _rl_screenwidth + 1; + if (line_size >= minsize) + return; + + newsize = minimum_size; + while (newsize < minsize) + newsize *= 2; + + visible_line = (char *)xrealloc (visible_line, newsize); + vis_face = (char *)xrealloc (vis_face, newsize); + + invisible_line = (char *)xrealloc (invisible_line, newsize); + inv_face = (char *)xrealloc (inv_face, newsize); + + delta = newsize - line_size; + memset (visible_line + line_size, 0, delta); + memset (vis_face + line_size, FACE_NORMAL, delta); + memset (invisible_line + line_size, 1, delta); + memset (inv_face + line_size, FACE_INVALID, delta); + + line_size = newsize; +} + +/* Initialize the VISIBLE_LINE and INVISIBLE_LINE arrays, and their associated + arrays of line break markers. MINSIZE is the minimum size of VISIBLE_LINE + and INVISIBLE_LINE; if it is greater than LINE_SIZE, LINE_SIZE is + increased. If the lines have already been allocated, this ensures that + they can hold at least MINSIZE characters. */ +static void +init_line_structures (int minsize) +{ + if (invisible_line == 0) /* initialize it */ + { + if (line_size > minsize) + minsize = line_size; + } + realloc_line (minsize); + + if (vis_lbreaks == 0) + { + /* should be enough. */ + inv_lbsize = vis_lbsize = 256; + +#if defined (HANDLE_MULTIBYTE) + line_state_visible->wbsize = vis_lbsize; + line_state_visible->wrapped_line = (int *)xmalloc (line_state_visible->wbsize * sizeof (int)); + + line_state_invisible->wbsize = inv_lbsize; + line_state_invisible->wrapped_line = (int *)xmalloc (line_state_invisible->wbsize * sizeof (int)); +#endif + + inv_lbreaks = (int *)xmalloc (inv_lbsize * sizeof (int)); + vis_lbreaks = (int *)xmalloc (vis_lbsize * sizeof (int)); + inv_lbreaks[0] = vis_lbreaks[0] = 0; + } + + line_structures_initialized = 1; +} + +/* Convenience functions to add chars to the invisible line that update the + face information at the same time. */ +static void /* XXX - change this */ +invis_addc (int *outp, char c, char face) +{ + realloc_line (*outp + 1); + invisible_line[*outp] = c; + inv_face[*outp] = face; + *outp += 1; +} + +static void +invis_adds (int *outp, const char *str, int n, char face) +{ + int i; + + for (i = 0; i < n; i++) + invis_addc (outp, str[i], face); +} + +static void +invis_nul (int *outp) +{ + invis_addc (outp, '\0', 0); + *outp -= 1; +} + +static void +set_active_region (int *beg, int *end) +{ + if (rl_point >= 0 && rl_point <= rl_end && rl_mark >= 0 && rl_mark <= rl_end) + { + *beg = (rl_mark < rl_point) ? rl_mark : rl_point; + *end = (rl_mark < rl_point) ? rl_point : rl_mark; + } +} + +/* Do whatever tests are necessary and tell update_line that it can do a + quick, dumb redisplay on the assumption that there are so many + differences between the old and new lines that it would be a waste to + compute all the differences. + Right now, it just sets _rl_quick_redisplay if the current visible line + is a single line (so we don't have to move vertically or mess with line + wrapping). */ +void +_rl_optimize_redisplay (void) +{ + if (_rl_vis_botlin == 0) + _rl_quick_redisplay = 1; +} + +/* Basic redisplay algorithm. See comments inline. */ +void +rl_redisplay (void) +{ + int in, out, c, linenum, cursor_linenum; + int inv_botlin, lb_botlin, lb_linenum, o_cpos; + int newlines, lpos, temp, n0, num, prompt_lines_estimate; + char *prompt_this_line; + char cur_face; + int hl_begin, hl_end; + int mb_cur_max = MB_CUR_MAX; +#if defined (HANDLE_MULTIBYTE) + WCHAR_T wc; + size_t wc_bytes; + int wc_width; + mbstate_t ps; + int _rl_wrapped_multicolumn = 0; +#endif + + if (_rl_echoing_p == 0) + return; + + /* Block keyboard interrupts because this function manipulates global + data structures. */ + _rl_block_sigint (); + RL_SETSTATE (RL_STATE_REDISPLAYING); + + cur_face = FACE_NORMAL; + /* Can turn this into an array for multiple highlighted objects in addition + to the region */ + hl_begin = hl_end = -1; + + if (rl_mark_active_p ()) + set_active_region (&hl_begin, &hl_end); + + if (!rl_display_prompt) + rl_display_prompt = ""; + + if (line_structures_initialized == 0) + { + init_line_structures (0); + rl_on_new_line (); + } + else if (line_size <= _rl_screenwidth) + init_line_structures (_rl_screenwidth + 1); + + /* Enable horizontal scrolling automatically for terminals of height 1 + where wrapping lines doesn't work. Disable it as soon as the terminal + height is increased again if it was automatically enabled. */ + if (_rl_screenheight <= 1) + { + if (_rl_horizontal_scroll_mode == 0) + horizontal_scrolling_autoset = 1; + _rl_horizontal_scroll_mode = 1; + } + else if (horizontal_scrolling_autoset) + _rl_horizontal_scroll_mode = 0; + + /* Draw the line into the buffer. */ + cpos_buffer_position = -1; + + prompt_multibyte_chars = prompt_visible_length - prompt_physical_chars; + + out = inv_botlin = 0; + + /* Mark the line as modified or not. We only do this for history + lines. */ + modmark = 0; + if (_rl_mark_modified_lines && current_history () && rl_undo_list) + { + invis_addc (&out, '*', cur_face); + invis_nul (&out); + modmark = 1; + } + + /* If someone thought that the redisplay was handled, but the currently + visible line has a different modification state than the one about + to become visible, then correct the caller's misconception. */ + if (visible_line[0] != invisible_line[0]) + rl_display_fixed = 0; + + /* If the prompt to be displayed is the `primary' readline prompt (the + one passed to readline()), use the values we have already expanded. + If not, use what's already in rl_display_prompt. WRAP_OFFSET is the + number of non-visible characters (bytes) in the prompt string. */ + /* This is where we output the characters in the prompt before the last + newline, if any. If there aren't any embedded newlines, we don't + write anything. Copy the last line of the prompt string into the line in + any case */ + if (rl_display_prompt == rl_prompt || local_prompt) + { + if (local_prompt_prefix && forced_display) + _rl_output_some_chars (local_prompt_prefix, strlen (local_prompt_prefix)); + + if (local_prompt_len > 0) + invis_adds (&out, local_prompt, local_prompt_len, cur_face); + invis_nul (&out); + wrap_offset = local_prompt_len - prompt_visible_length; + } + else + { + int pmtlen; + prompt_this_line = strrchr (rl_display_prompt, '\n'); + if (!prompt_this_line) + prompt_this_line = rl_display_prompt; + else + { + prompt_this_line++; + pmtlen = prompt_this_line - rl_display_prompt; /* temp var */ + if (forced_display) + { + _rl_output_some_chars (rl_display_prompt, pmtlen); + /* Make sure we are at column zero even after a newline, + regardless of the state of terminal output processing. */ + if (pmtlen < 2 || prompt_this_line[-2] != '\r') + cr (); + } + } + + prompt_physical_chars = pmtlen = strlen (prompt_this_line); /* XXX */ + invis_adds (&out, prompt_this_line, pmtlen, cur_face); + invis_nul (&out); + wrap_offset = prompt_invis_chars_first_line = 0; + } + +#if defined (HANDLE_MULTIBYTE) +#define CHECK_INV_LBREAKS() \ + do { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + if (newlines >= (line_state_invisible->wbsize - 2)) \ + { \ + line_state_invisible->wbsize *= 2; \ + line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \ + } \ + } while (0) +#else +#define CHECK_INV_LBREAKS() \ + do { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + } while (0) +#endif /* !HANDLE_MULTIBYTE */ + +#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)); \ + } \ + inv_lbreaks[++newlines] = out; \ + if (newlines >= (line_state_invisible->wbsize - 2)) \ + { \ + line_state_invisible->wbsize *= 2; \ + line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \ + } \ + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; \ + lpos = 0; \ + } \ + } while (0) +#else +#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)); \ + } \ + inv_lbreaks[++newlines] = out; \ + lpos = 0; \ + } \ + } while (0) +#endif + + /* inv_lbreaks[i] is where line i starts in the buffer. */ + inv_lbreaks[newlines = 0] = 0; + /* lpos is a physical cursor position, so it needs to be adjusted by the + number of invisible characters in the prompt, per line. We compute + the line breaks in the prompt string in expand_prompt, taking invisible + characters into account, and if lpos exceeds the screen width, we copy + the data in the loop below. */ + lpos = prompt_physical_chars + modmark; + +#if defined (HANDLE_MULTIBYTE) + memset (line_state_invisible->wrapped_line, 0, line_state_invisible->wbsize * sizeof (int)); + num = 0; +#endif + + /* prompt_invis_chars_first_line is the number of invisible characters (bytes) + in the first physical line of the prompt. + wrap_offset - prompt_invis_chars_first_line is usually the number of + invis chars on the second (or, more generally, last) line. */ + + /* This is zero-based, used to set the newlines */ + prompt_lines_estimate = lpos / _rl_screenwidth; + + /* what if lpos is already >= _rl_screenwidth before we start drawing the + contents of the command line? */ + if (lpos >= _rl_screenwidth) + { + temp = 0; + + /* first copy the linebreaks array we computed in expand_prompt */ + while (local_prompt_newlines[newlines+1] != -1) + { + temp = local_prompt_newlines[newlines+1]; + inv_lbreaks[++newlines] = temp; + } + + /* Now set lpos from the last newline */ + if (mb_cur_max > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0) + lpos = _rl_col_width (local_prompt, temp, local_prompt_len, 1) - (wrap_offset - prompt_invis_chars_first_line); + else + lpos -= (_rl_screenwidth * newlines); + } + + prompt_last_screen_line = newlines; + + /* Draw the rest of the line (after the prompt) into invisible_line, keeping + track of where the cursor is (cpos_buffer_position), the number of the + line containing the cursor (lb_linenum), the last line number (lb_botlin + and inv_botlin). + 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)); + if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[0])) + { + wc = (WCHAR_T)rl_line_buffer[0]; + wc_bytes = 1; + } + else + 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 + { + if (in == hl_begin) + cur_face = FACE_STANDOUT; + else if (in == hl_end) + cur_face = FACE_NORMAL; + + c = (unsigned char)rl_line_buffer[in]; + +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + if (MB_INVALIDCH (wc_bytes)) + { + /* 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 (MB_NULLWCH (wc_bytes)) + break; /* Found '\0' */ + else + { + temp = WCWIDTH (wc); + wc_width = (temp >= 0) ? temp : 1; + } + } +#endif + + if (in == rl_point) + { + cpos_buffer_position = out; + 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) + { + char obuf[5]; + int olen; + + olen = sprintf (obuf, "\\%o", c); + + if (lpos + olen >= _rl_screenwidth) + { + temp = _rl_screenwidth - lpos; + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out + temp; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif + lpos = olen - temp; + } + else + lpos += olen; + + for (temp = 0; temp < olen; temp++) + { + invis_addc (&out, obuf[temp], cur_face); + CHECK_LPOS (); + } + } + else + { + invis_addc (&out, c, cur_face); + CHECK_LPOS(); + } + } +#if defined (DISPLAY_TABS) + else if (c == '\t') + { + register int newout; + + newout = out + 8 - lpos % 8; + temp = newout - out; + if (lpos + temp >= _rl_screenwidth) + { + register int temp2; + temp2 = _rl_screenwidth - lpos; + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out + temp2; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif + lpos = temp - temp2; + while (out < newout) + invis_addc (&out, ' ', cur_face); + } + else + { + while (out < newout) + invis_addc (&out, ' ', cur_face); + lpos += temp; + } + } +#endif + else if (c == '\n' && _rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up) + { + invis_addc (&out, '\0', cur_face); + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif + lpos = 0; + } + else if (CTRL_CHAR (c) || c == RUBOUT) + { + invis_addc (&out, '^', cur_face); + CHECK_LPOS(); + invis_addc (&out, CTRL_CHAR (c) ? UNCTRL (c) : '?', cur_face); + CHECK_LPOS(); + } + 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() */ + invis_addc (&out, ' ', cur_face); + _rl_wrapped_multicolumn++; + CHECK_LPOS(); + } + if (in == rl_point) + { + cpos_buffer_position = out; + lb_linenum = newlines; + } + for (i = in; i < in+wc_bytes; i++) + invis_addc (&out, rl_line_buffer[i], cur_face); + for (i = 0; i < wc_width; i++) + CHECK_LPOS(); + } + else + { + invis_addc (&out, c, cur_face); + CHECK_LPOS(); + } +#else + invis_addc (&out, c, cur_face); + CHECK_LPOS(); +#endif + } + +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + in += wc_bytes; + if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[in])) + { + wc = (WCHAR_T)rl_line_buffer[in]; + wc_bytes = 1; + memset (&ps, 0, sizeof (mbstate_t)); /* re-init state */ + } + else + wc_bytes = MBRTOWC (&wc, rl_line_buffer + in, rl_end - in, &ps); + } + else + in++; +#endif + } + invis_nul (&out); + line_totbytes = out; + if (cpos_buffer_position < 0) + { + cpos_buffer_position = out; + lb_linenum = newlines; + } + + /* If we are switching from one line to multiple wrapped lines, we don't + want to do a dumb update (or we want to make it smarter). */ + if (_rl_quick_redisplay && newlines > 0) + _rl_quick_redisplay = 0; + + inv_botlin = lb_botlin = _rl_inv_botlin = newlines; + CHECK_INV_LBREAKS (); + inv_lbreaks[newlines+1] = out; +#if defined (HANDLE_MULTIBYTE) + /* This should be 0 anyway */ + line_state_invisible->wrapped_line[newlines+1] = _rl_wrapped_multicolumn; +#endif + cursor_linenum = lb_linenum; + + /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed. + CURSOR_LINENUM == line number where the cursor should be placed. */ + + /* PWP: now is when things get a bit hairy. The visible and invisible + line buffers are really multiple lines, which would wrap every + (screenwidth - 1) characters. Go through each in turn, finding + the changed region and updating it. The line order is top to bottom. */ + + /* If we can move the cursor up and down, then use multiple lines, + otherwise, let long lines display in a single terminal line, and + horizontally scroll it. */ + displaying_prompt_first_line = 1; + if (_rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up) + { + int nleft, pos, changed_screen_line, tx; + + if (!rl_display_fixed || forced_display) + { + forced_display = 0; + + /* If we have more than a screenful of material to display, then + only display a screenful. We should display the last screen, + not the first. */ + if (out >= _rl_screenchars) + { +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + out = _rl_find_prev_mbchar (invisible_line, _rl_screenchars, MB_FIND_ANY); + else +#endif + 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 + OFFSET (which has already been calculated above). */ + +#define INVIS_FIRST() (prompt_physical_chars > _rl_screenwidth ? prompt_invis_chars_first_line : wrap_offset) +#define WRAP_OFFSET(line, offset) ((line == 0) \ + ? (offset ? INVIS_FIRST() : 0) \ + : ((line == prompt_last_screen_line) ? wrap_offset-prompt_invis_chars_first_line : 0)) +#define W_OFFSET(line, offset) ((line) == 0 ? offset : 0) +#define VIS_LLEN(l) ((l) > _rl_vis_botlin ? 0 : (vis_lbreaks[l+1] - vis_lbreaks[l])) +#define INV_LLEN(l) (inv_lbreaks[l+1] - inv_lbreaks[l]) +#define VIS_CHARS(line) (visible_line + vis_lbreaks[line]) +#define VIS_FACE(line) (vis_face + vis_lbreaks[line]) +#define VIS_LINE(line) ((line) > _rl_vis_botlin) ? "" : VIS_CHARS(line) +#define VIS_LINE_FACE(line) ((line) > _rl_vis_botlin) ? "" : VIS_FACE(line) +#define INV_LINE(line) (invisible_line + inv_lbreaks[line]) +#define INV_LINE_FACE(line) (inv_face + inv_lbreaks[line]) + +#define OLD_CPOS_IN_PROMPT() (cpos_adjusted == 0 && \ + _rl_last_c_pos != o_cpos && \ + _rl_last_c_pos > wrap_offset && \ + o_cpos < prompt_last_invisible) + + + /* We don't want to highlight anything that's going to be off the top + of the display; if the current line takes up more than an entire + screen, just mark the lines that won't be displayed as having a + `normal' face. + It's imperfect, but better than display corruption. */ + if (rl_mark_active_p () && inv_botlin > _rl_screenheight) + { + int extra; + + extra = inv_botlin - _rl_screenheight; + for (linenum = 0; linenum <= extra; linenum++) + norm_face (INV_LINE_FACE(linenum), INV_LLEN (linenum)); + } + + /* For each line in the buffer, do the updating display. */ + for (linenum = 0; linenum <= inv_botlin; linenum++) + { + /* This can lead us astray if we execute a program that changes + the locale from a non-multibyte to a multibyte one. */ + o_cpos = _rl_last_c_pos; + cpos_adjusted = 0; + update_line (VIS_LINE(linenum), VIS_LINE_FACE(linenum), + INV_LINE(linenum), INV_LINE_FACE(linenum), + linenum, + VIS_LLEN(linenum), INV_LLEN(linenum), inv_botlin); + + /* update_line potentially changes _rl_last_c_pos, but doesn't + take invisible characters into account, since _rl_last_c_pos + is an absolute cursor position in a multibyte locale. We + choose to (mostly) compensate for that here, rather than + change update_line itself. There are several cases in which + update_line adjusts _rl_last_c_pos itself (so it can pass + _rl_move_cursor_relative accurate values); it communicates + this back by setting cpos_adjusted. If we assume that + _rl_last_c_pos is correct (an absolute cursor position) each + time update_line is called, then we can assume in our + calculations that o_cpos does not need to be adjusted by + wrap_offset. */ + if (linenum == 0 && (mb_cur_max > 1 && rl_byte_oriented == 0) && OLD_CPOS_IN_PROMPT()) + _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */ + else if (cpos_adjusted == 0 && + linenum == prompt_last_screen_line && + prompt_physical_chars > _rl_screenwidth && + (mb_cur_max > 1 && rl_byte_oriented == 0) && + _rl_last_c_pos != o_cpos && + _rl_last_c_pos > (prompt_last_invisible - _rl_screenwidth - prompt_invis_chars_first_line)) /* XXX - rethink this last one */ + /* This assumes that all the invisible characters are split + between the first and last lines of the prompt, if the + prompt consumes more than two lines. It's usually right */ + /* XXX - not sure this is ever executed */ + _rl_last_c_pos -= (wrap_offset-prompt_invis_chars_first_line); + + /* If this is the line with the prompt, we might need to + compensate for invisible characters in the new line. Do + this only if there is not more than one new line (which + implies that we completely overwrite the old visible line) + and the new line is shorter than the old. Make sure we are + at the end of the new line before clearing. */ + if (linenum == 0 && + inv_botlin == 0 && _rl_last_c_pos == out && + (wrap_offset > visible_wrap_offset) && + (_rl_last_c_pos < visible_first_line_len)) + { + if (mb_cur_max > 1 && rl_byte_oriented == 0) + nleft = _rl_screenwidth - _rl_last_c_pos; + else + nleft = _rl_screenwidth + wrap_offset - _rl_last_c_pos; + if (nleft) + _rl_clear_to_eol (nleft); + } +#if 0 + /* This segment is intended to handle the case where the old + visible prompt has invisible characters and the new line + to be displayed needs to clear the rest of the old characters + out (e.g., when printing the i-search prompt): in general, + the case of the new line being shorter than the old. We need + to be at the end of the new line and the old line needs to be + longer than the current cursor position. It's not perfect, + since it uses the byte length of the first line, but this will + at worst result in some extra clear-to-end-of-lines. We can't + use the prompt length variables because they may not + correspond to the visible line (see printing the i-search + prompt above). The tests for differing numbers of invisible + characters may not matter and can probably be removed. */ + else if (linenum == 0 && + linenum == prompt_last_screen_line && + _rl_last_c_pos == out && + _rl_last_c_pos < visible_first_line_len && + visible_wrap_offset && + visible_wrap_offset != wrap_offset) + { + if (mb_cur_max > 1 && rl_byte_oriented == 0) + nleft = _rl_screenwidth - _rl_last_c_pos; + else + nleft = _rl_screenwidth + wrap_offset - _rl_last_c_pos; + if (nleft) + _rl_clear_to_eol (nleft); + } +#endif + + /* Since the new first line is now visible, save its length. */ + if (linenum == 0) + visible_first_line_len = (inv_botlin > 0) ? inv_lbreaks[1] : out - wrap_offset; + } + + /* We may have deleted some lines. If so, clear the left over + blank ones at the bottom out. */ + if (_rl_vis_botlin > inv_botlin) + { + char *tt; + for (; linenum <= _rl_vis_botlin; linenum++) + { + tt = VIS_CHARS (linenum); + _rl_move_vert (linenum); + _rl_move_cursor_relative (0, tt, VIS_FACE(linenum)); + _rl_clear_to_eol + ((linenum == _rl_vis_botlin) ? strlen (tt) : _rl_screenwidth); + } + } + _rl_vis_botlin = inv_botlin; + + /* CHANGED_SCREEN_LINE is set to 1 if we have moved to a + different screen line during this redisplay. */ + changed_screen_line = _rl_last_v_pos != cursor_linenum; + if (changed_screen_line) + { + _rl_move_vert (cursor_linenum); + /* If we moved up to the line with the prompt using _rl_term_up, + the physical cursor position on the screen stays the same, + but the buffer position needs to be adjusted to account + for invisible characters. */ + if ((mb_cur_max == 1 || rl_byte_oriented) && cursor_linenum == 0 && wrap_offset) + _rl_last_c_pos += wrap_offset; + } + + /* Now we move the cursor to where it needs to be. First, make + sure we are on the correct line (cursor_linenum). */ + + /* We have to reprint the prompt if it contains invisible + characters, since it's not generally OK to just reprint + the characters from the current cursor position. But we + only need to reprint it if the cursor is before the last + invisible character in the prompt string. */ + /* XXX - why not use local_prompt_len? */ + nleft = prompt_visible_length + wrap_offset; + if (cursor_linenum == 0 && wrap_offset > 0 && _rl_last_c_pos > 0 && + _rl_last_c_pos < PROMPT_ENDING_INDEX && local_prompt) + { + _rl_cr (); + if (modmark) + _rl_output_some_chars ("*", 1); + + _rl_output_some_chars (local_prompt, nleft); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + _rl_last_c_pos = _rl_col_width (local_prompt, 0, nleft, 1) - wrap_offset + modmark; + else + _rl_last_c_pos = nleft + modmark; + } + + /* Where on that line? And where does that line start + in the buffer? */ + pos = inv_lbreaks[cursor_linenum]; + /* nleft == number of characters (bytes) in the line buffer between + the start of the line and the desired cursor position. */ + nleft = cpos_buffer_position - pos; + + /* NLEFT is now a number of characters in a buffer. When in a + multibyte locale, however, _rl_last_c_pos is an absolute cursor + position that doesn't take invisible characters in the prompt + into account. We use a fudge factor to compensate. */ + + /* Since _rl_backspace() doesn't know about invisible characters in + the prompt, and there's no good way to tell it, we compensate for + those characters here and call _rl_backspace() directly if + necessary */ + if (wrap_offset && cursor_linenum == 0 && nleft < _rl_last_c_pos) + { + /* TX == new physical cursor position in multibyte locale. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + tx = _rl_col_width (&visible_line[pos], 0, nleft, 1) - visible_wrap_offset; + else + tx = nleft; + if (tx >= 0 && _rl_last_c_pos > tx) + { + _rl_backspace (_rl_last_c_pos - tx); /* XXX */ + _rl_last_c_pos = tx; + } + } + + /* We need to note that in a multibyte locale we are dealing with + _rl_last_c_pos as an absolute cursor position, but moving to a + point specified by a buffer position (NLEFT) that doesn't take + invisible characters into account. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + _rl_move_cursor_relative (nleft, &invisible_line[pos], &inv_face[pos]); + else if (nleft != _rl_last_c_pos) + _rl_move_cursor_relative (nleft, &invisible_line[pos], &inv_face[pos]); + } + } + else /* Do horizontal scrolling. Much simpler */ + { +#define M_OFFSET(margin, offset) ((margin) == 0 ? offset : 0) + int lmargin, ndisp, nleft, phys_c_pos, t; + + /* Always at top line. */ + _rl_last_v_pos = 0; + + /* Compute where in the buffer the displayed line should start. This + will be LMARGIN. */ + + /* The number of characters that will be displayed before the cursor. */ + ndisp = cpos_buffer_position - wrap_offset; + nleft = prompt_visible_length + wrap_offset; + /* Where the new cursor position will be on the screen. This can be + longer than SCREENWIDTH; if it is, lmargin will be adjusted. */ + phys_c_pos = cpos_buffer_position - (last_lmargin ? last_lmargin : wrap_offset); + t = _rl_screenwidth / 3; + + /* If the number of characters had already exceeded the screenwidth, + last_lmargin will be > 0. */ + + /* If the number of characters to be displayed is more than the screen + width, compute the starting offset so that the cursor is about + two-thirds of the way across the screen. */ + if (phys_c_pos > _rl_screenwidth - 2) + { + lmargin = cpos_buffer_position - (2 * t); + if (lmargin < 0) + lmargin = 0; + /* If the left margin would be in the middle of a prompt with + invisible characters, don't display the prompt at all. */ + if (wrap_offset && lmargin > 0 && lmargin < nleft) + lmargin = nleft; + } + else if (ndisp < _rl_screenwidth - 2) /* XXX - was -1 */ + lmargin = 0; + else if (phys_c_pos < 1) + { + /* If we are moving back towards the beginning of the line and + the last margin is no longer correct, compute a new one. */ + lmargin = ((cpos_buffer_position - 1) / t) * t; /* XXX */ + if (wrap_offset && lmargin > 0 && lmargin < nleft) + lmargin = nleft; + } + else + lmargin = last_lmargin; + + displaying_prompt_first_line = lmargin < nleft; + + /* If the first character on the screen isn't the first character + in the display line, indicate this with a special character. */ + if (lmargin > 0) + invisible_line[lmargin] = '<'; + + /* If SCREENWIDTH characters starting at LMARGIN do not encompass + the whole line, indicate that with a special character at the + right edge of the screen. If LMARGIN is 0, we need to take the + wrap offset into account. */ + t = lmargin + M_OFFSET (lmargin, wrap_offset) + _rl_screenwidth; + if (t > 0 && t < out) + invisible_line[t - 1] = '>'; + + if (rl_display_fixed == 0 || forced_display || lmargin != last_lmargin) + { + forced_display = 0; + o_cpos = _rl_last_c_pos; + cpos_adjusted = 0; + update_line (&visible_line[last_lmargin], &vis_face[last_lmargin], + &invisible_line[lmargin], &inv_face[lmargin], + 0, + _rl_screenwidth + visible_wrap_offset, + _rl_screenwidth + (lmargin ? 0 : wrap_offset), + 0); + + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && + displaying_prompt_first_line && OLD_CPOS_IN_PROMPT()) + _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */ + + /* If the visible new line is shorter than the old, but the number + of invisible characters is greater, and we are at the end of + the new line, we need to clear to eol. */ + t = _rl_last_c_pos - M_OFFSET (lmargin, wrap_offset); + if ((M_OFFSET (lmargin, wrap_offset) > visible_wrap_offset) && + (_rl_last_c_pos == out) && displaying_prompt_first_line && + t < visible_first_line_len) + { + nleft = _rl_screenwidth - t; + _rl_clear_to_eol (nleft); + } + visible_first_line_len = out - lmargin - M_OFFSET (lmargin, wrap_offset); + if (visible_first_line_len > _rl_screenwidth) + visible_first_line_len = _rl_screenwidth; + + _rl_move_cursor_relative (cpos_buffer_position - lmargin, &invisible_line[lmargin], &inv_face[lmargin]); + last_lmargin = lmargin; + } + } + fflush (rl_outstream); + + /* Swap visible and non-visible lines. */ + { + struct line_state *vtemp = line_state_visible; + + line_state_visible = line_state_invisible; + line_state_invisible = vtemp; + + rl_display_fixed = 0; + /* If we are displaying on a single line, and last_lmargin is > 0, we + are not displaying any invisible characters, so set visible_wrap_offset + to 0. */ + if (_rl_horizontal_scroll_mode && last_lmargin) + visible_wrap_offset = 0; + else + visible_wrap_offset = wrap_offset; + + _rl_quick_redisplay = 0; + } + + RL_UNSETSTATE (RL_STATE_REDISPLAYING); + _rl_release_sigint (); +} + +static void +putc_face (int c, int face, char *cur_face) +{ + char cf; + cf = *cur_face; + if (cf != face) + { + if (cf != FACE_NORMAL && cf != FACE_STANDOUT) + return; + if (face != FACE_NORMAL && face != FACE_STANDOUT) + return; + if (face == FACE_STANDOUT && cf == FACE_NORMAL) + _rl_region_color_on (); + if (face == FACE_NORMAL && cf == FACE_STANDOUT) + _rl_region_color_off (); + *cur_face = face; + } + if (c != EOF) + putc (c, rl_outstream); +} + +static void +puts_face (const char *str, const char *face, int n) +{ + int i; + char cur_face; + + for (cur_face = FACE_NORMAL, i = 0; i < n; i++) + putc_face ((unsigned char) str[i], face[i], &cur_face); + putc_face (EOF, FACE_NORMAL, &cur_face); +} + +static void +norm_face (char *face, int n) +{ + memset (face, FACE_NORMAL, n); +} + +#define ADJUST_CPOS(x) do { _rl_last_c_pos -= (x) ; cpos_adjusted = 1; } while (0) + +/* PWP: update_line() is based on finding the middle difference of each + line on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + All are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handled. + + Could be made even smarter, but this works well enough */ +static void +update_line (char *old, char *old_face, char *new, char *new_face, int current_line, int omax, int nmax, int inv_botlin) +{ + char *ofd, *ols, *oe, *nfd, *nls, *ne; + char *ofdf, *nfdf, *olsf, *nlsf; + int temp, lendiff, wsatend, od, nd, twidth, o_cpos; + int current_invis_chars; + int col_lendiff, col_temp; + int bytes_to_insert; + int mb_cur_max = MB_CUR_MAX; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps_new, ps_old; + int new_offset, old_offset; +#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 + the exact cursor position and cut-and-paste with certain terminal + emulators. In this calculation, TEMP is the physical screen + position of the cursor. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + temp = _rl_last_c_pos; + else + temp = _rl_last_c_pos - WRAP_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) + { + /* We're going to wrap around by writing the first character of NEW to + the screen and dealing with changes to what's visible by modifying + OLD to match it. Complicated by the presence of multi-width + characters at the end of the line or beginning of the new one. */ + /* old is always somewhere in visible_line; new is always somewhere in + invisible_line. These should always be null-terminated. */ +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + WCHAR_T wc; + mbstate_t ps; + int oldwidth, newwidth; + int oldbytes, newbytes; + size_t ret; + + /* This fixes only double-column characters, but if the wrapped + character consumes more than three columns, spaces will be + inserted in the string buffer. */ + /* XXX remember that we are working on the invisible line right now; + we don't swap visible and invisible until just before rl_redisplay + returns */ + /* This will remove the extra placeholder space we added with + _rl_wrapped_multicolumn */ + if (current_line < line_state_invisible->wbsize && line_state_invisible->wrapped_line[current_line] > 0) + _rl_clear_to_eol (line_state_invisible->wrapped_line[current_line]); + + /* 1. how many screen positions does first char in old consume? */ + memset (&ps, 0, sizeof (mbstate_t)); + ret = MBRTOWC (&wc, old, mb_cur_max, &ps); + oldbytes = ret; + if (MB_INVALIDCH (ret)) + { + oldwidth = 1; + oldbytes = 1; + } + else if (MB_NULLWCH (ret)) + oldwidth = 0; + else + oldwidth = WCWIDTH (wc); + if (oldwidth < 0) + oldwidth = 1; + + /* 2. how many screen positions does the first char in new consume? */ + memset (&ps, 0, sizeof (mbstate_t)); + ret = MBRTOWC (&wc, new, mb_cur_max, &ps); + newbytes = ret; + if (MB_INVALIDCH (ret)) + { + newwidth = 1; + newbytes = 1; + } + else if (MB_NULLWCH (ret)) + newwidth = 0; + else + newwidth = WCWIDTH (wc); + if (newwidth < 0) + newwidth = 1; + + /* 3. if the new width is less than the old width, we need to keep + going in new until we have consumed at least that many screen + positions, and figure out how many bytes that will take */ + while (newbytes < nmax && newwidth < oldwidth) + { + int t; + + ret = MBRTOWC (&wc, new+newbytes, mb_cur_max, &ps); + if (MB_INVALIDCH (ret)) + { + newwidth += 1; + newbytes += 1; + } + else if (MB_NULLWCH (ret)) + break; + else + { + t = WCWIDTH (wc); + newwidth += (t >= 0) ? t : 1; + newbytes += ret; + } + } + /* 4. If the new width is more than the old width, keep going in old + until we have consumed exactly that many screen positions, and + figure out how many bytes that will take. This is an optimization */ + while (oldbytes < omax && oldwidth < newwidth) + { + int t; + + ret = MBRTOWC (&wc, old+oldbytes, mb_cur_max, &ps); + if (MB_INVALIDCH (ret)) + { + oldwidth += 1; + oldbytes += 1; + } + else if (MB_NULLWCH (ret)) + break; + else + { + t = WCWIDTH (wc); + oldwidth += (t >= 0) ? t : 1; + oldbytes += ret; + } + } + /* 5. write the first newbytes of new, which takes newwidth. This is + where the screen wrapping takes place, and we are now writing + characters onto the new line. We need to fix up old so it + accurately reflects what is on the screen after the + _rl_output_some_chars below. */ + if (newwidth > 0) + { + int count, i, j; + char *optr; + + puts_face (new, new_face, newbytes); + _rl_last_c_pos = newwidth; + _rl_last_v_pos++; + + /* 5a. If the number of screen positions doesn't match, punt + and do a dumb update. + 5b. If the number of bytes is greater in the new line than + the old, do a dumb update, because there is no guarantee we + can extend the old line enough to fit the new bytes. */ + if (newwidth != oldwidth || newbytes > oldbytes) + { + oe = old + omax; + ne = new + nmax; + nd = newbytes; + nfd = new + nd; + ofdf = old_face + oldbytes; + nfdf = new_face + newbytes; + + goto dumb_update; + } + if (oldbytes != 0 && newbytes != 0) + { + /* We have written as many bytes from new as we need to + consume the first character of old. Fix up `old' so it + reflects the new screen contents. We use +1 in the + memmove call to copy the trailing NUL. */ + /* (strlen(old+oldbytes) == (omax - oldbytes - 1)) */ + + /* Don't bother trying to fit the bytes if the number of bytes + doesn't change. */ + if (oldbytes != newbytes) + { + memmove (old+newbytes, old+oldbytes, strlen (old+oldbytes) + 1); + memmove (old_face+newbytes, old_face+oldbytes, strlen (old+oldbytes) + 1); + } + memcpy (old, new, newbytes); + memcpy (old_face, new_face, newbytes); + j = newbytes - oldbytes; + omax += j; + /* Fix up indices if we copy data from one line to another */ + for (i = current_line+1; j != 0 && i <= inv_botlin+1 && i <=_rl_vis_botlin+1; i++) + vis_lbreaks[i] += j; + } + } + else + { + putc (' ', rl_outstream); + _rl_last_c_pos = 1; + _rl_last_v_pos++; + if (old[0] && new[0]) + { + old[0] = new[0]; + old_face[0] = new_face[0]; + } + } + } + else +#endif + { + if (new[0]) + puts_face (new, new_face, 1); + else + putc (' ', rl_outstream); + _rl_last_c_pos = 1; + _rl_last_v_pos++; + if (old[0] && new[0]) + { + old[0] = new[0]; + old_face[0] = new_face[0]; + } + } + } + + /* We know that we are dealing with a single screen line here */ + if (_rl_quick_redisplay) + { + nfd = new; + nfdf = new_face; + ofd = old; + ofdf = old_face; + for (od = 0, oe = ofd; od < omax && *oe; oe++, od++); + for (nd = 0, ne = nfd; nd < nmax && *ne; ne++, nd++); + od = nd = 0; + _rl_move_cursor_relative (0, old, old_face); + + bytes_to_insert = ne - nfd; + if (bytes_to_insert < local_prompt_len) /* ??? */ + goto dumb_update; + + /* output the prompt, output the line contents, clear the rest */ + _rl_output_some_chars (nfd, local_prompt_len); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + _rl_last_c_pos = prompt_physical_chars; + else + _rl_last_c_pos = local_prompt_len; + + bytes_to_insert -= local_prompt_len; + if (bytes_to_insert > 0) + { + puts_face (new+local_prompt_len, nfdf+local_prompt_len, bytes_to_insert); + if (mb_cur_max > 1 && rl_byte_oriented) + _rl_last_c_pos += _rl_col_width (new, local_prompt_len, ne-new, 1); + else + _rl_last_c_pos += bytes_to_insert; + } + + /* See comments at dumb_update: for an explanation of this heuristic */ + if (nmax < omax) + goto clear_rest_of_line; + else if ((nmax - W_OFFSET(current_line, wrap_offset)) < (omax - W_OFFSET (current_line, visible_wrap_offset))) + goto clear_rest_of_line; + else + return; + } + + /* Find first difference. */ +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + /* See if the old line is a subset of the new line, so that the + only change is adding characters. */ + temp = (omax < nmax) ? omax : nmax; + if (memcmp (old, new, temp) == 0 && memcmp (old_face, new_face, temp) == 0) + { + new_offset = old_offset = temp; /* adding at the end */ + ofd = old + temp; + ofdf = old_face + temp; + nfd = new + temp; + nfdf = new_face + temp; + } + else + { + memset (&ps_new, 0, sizeof(mbstate_t)); + memset (&ps_old, 0, sizeof(mbstate_t)); + + /* Are the old and new lines the same? */ + if (omax == nmax && memcmp (new, old, omax) == 0 && memcmp (new_face, old_face, omax) == 0) + { + old_offset = omax; + new_offset = nmax; + ofd = old + omax; + ofdf = old_face + omax; + nfd = new + nmax; + nfdf = new_face + nmax; + } + else + { + /* Go through the line from the beginning and find the first + difference. We assume that faces change at (possibly multi- + byte) character boundaries. */ + new_offset = old_offset = 0; + for (ofd = old, ofdf = old_face, nfd = new, nfdf = new_face; + (ofd - old < omax) && *ofd && + _rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new) && + *ofdf == *nfdf; ) + { + 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; + ofdf = old_face + old_offset; + nfd = new + new_offset; + nfdf = new_face + new_offset; + } + } + } + } + else +#endif + for (ofd = old, ofdf = old_face, nfd = new, nfdf = new_face; + (ofd - old < omax) && *ofd && (*ofd == *nfd) && (*ofdf == *nfdf); + ofd++, nfd++, ofdf++, nfdf++) + ; + + /* Move to the end of the screen line. ND and OD are used to keep track + of the distance between ne and new and oe and old, respectively, to + move a subtraction out of each loop. */ + for (od = ofd - old, oe = ofd; od < omax && *oe; oe++, od++); + for (nd = nfd - new, ne = nfd; nd < nmax && *ne; ne++, nd++); + + /* If no difference, continue to next line. */ + if (ofd == oe && nfd == ne) + return; + +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0 && _rl_utf8locale) + { + WCHAR_T wc; + mbstate_t ps = { 0 }; + int t; + + /* If the first character in the difference is a zero-width character, + assume it's a combining character and back one up so the two base + characters no longer compare equivalently. */ + t = MBRTOWC (&wc, ofd, mb_cur_max, &ps); + if (t > 0 && UNICODE_COMBINING_CHAR (wc) && WCWIDTH (wc) == 0) + { + old_offset = _rl_find_prev_mbchar (old, ofd - old, MB_FIND_ANY); + new_offset = _rl_find_prev_mbchar (new, nfd - new, MB_FIND_ANY); + ofd = old + old_offset; /* equal by definition */ + ofdf = old_face + old_offset; + nfd = new + new_offset; + nfdf = new_face + new_offset; + } + } +#endif + + wsatend = 1; /* flag for trailing whitespace */ + +#if defined (HANDLE_MULTIBYTE) + /* Find the last character that is the same between the two lines. This + bounds the region that needs to change. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY); + olsf = old_face + (ols - old); + nls = new + _rl_find_prev_mbchar (new, ne - new, MB_FIND_ANY); + nlsf = new_face + (nls - new); + + while ((ols > ofd) && (nls > nfd)) + { + memset (&ps_old, 0, sizeof (mbstate_t)); + memset (&ps_new, 0, sizeof (mbstate_t)); + + if (_rl_compare_chars (old, ols - old, &ps_old, new, nls - new, &ps_new) == 0 || + *olsf != *nlsf) + break; + + if (*ols == ' ') + wsatend = 0; + + ols = old + _rl_find_prev_mbchar (old, ols - old, MB_FIND_ANY); + olsf = old_face + (ols - old); + nls = new + _rl_find_prev_mbchar (new, nls - new, MB_FIND_ANY); + nlsf = new_face + (nls - new); + } + } + else + { +#endif /* HANDLE_MULTIBYTE */ + ols = oe - 1; /* find last same */ + olsf = old_face + (ols - old); + nls = ne - 1; + nlsf = new_face + (nls - new); + while ((ols > ofd) && (nls > nfd) && (*ols == *nls) && (*olsf == *nlsf)) + { + if (*ols != ' ') + wsatend = 0; + ols--; olsf--; + nls--; nlsf--; + } +#if defined (HANDLE_MULTIBYTE) + } +#endif + + if (wsatend) + { + ols = oe; + olsf = old_face + (ols - old); + nls = ne; + nlsf = new_face + (nls - new); + } +#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 || *olsf != *nlsf) +#else + else if (*ols != *nls || *olsf != *nlsf) +#endif + { + if (*ols) /* don't step past the NUL */ + { + 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) + { + if (mb_cur_max > 1 && rl_byte_oriented == 0) + nls = new + _rl_find_next_mbchar (new, nls - new, 1, MB_FIND_ANY); + else + nls++; + } + olsf = old_face + (ols - old); + nlsf = new_face + (nls - new); + } + + /* count of invisible characters in the current invisible line. */ + current_invis_chars = W_OFFSET (current_line, wrap_offset); + if (_rl_last_v_pos != current_line) + { + _rl_move_vert (current_line); + /* We have moved up to a new screen line. This line may or may not have + invisible characters on it, but we do our best to recalculate + visible_wrap_offset based on what we know. */ + if (current_line == 0) + visible_wrap_offset = prompt_invis_chars_first_line; /* XXX */ +#if 0 /* XXX - not yet */ + else if (current_line == prompt_last_screen_line && wrap_offset > prompt_invis_chars_first_line) + visible_wrap_offset = wrap_offset - prompt_invis_chars_first_line +#endif + if ((mb_cur_max == 1 || rl_byte_oriented) && current_line == 0 && visible_wrap_offset) + _rl_last_c_pos += visible_wrap_offset; + } + + /* If this is the first line and there are invisible characters in the + prompt string, and the prompt string has not changed, and the current + cursor position is before the last invisible character in the prompt, + and the index of the character to move to is past the end of the prompt + string, then redraw the entire prompt string. We can only do this + reliably if the terminal supports a `cr' capability. + + This can also happen if the prompt string has changed, and the first + difference in the line is in the middle of the prompt string, after a + sequence of invisible characters (worst case) and before the end of + the prompt. In this case, we have to redraw the entire prompt string + so that the entire sequence of invisible characters is drawn. We need + to handle the worst case, when the difference is after (or in the middle + of) a sequence of invisible characters that changes the text color and + before the sequence that restores the text color to normal. Then we have + to make sure that the lines still differ -- if they don't, we can + return immediately. + + This is not an efficiency hack -- there is a problem with redrawing + portions of the prompt string if they contain terminal escape + sequences (like drawing the `unbold' sequence without a corresponding + `bold') that manifests itself on certain terminals. */ + + lendiff = local_prompt_len; + if (lendiff > nmax) + lendiff = nmax; + od = ofd - old; /* index of first difference in visible line */ + nd = nfd - new; /* nd, od are buffer indexes */ + if (current_line == 0 && !_rl_horizontal_scroll_mode && + _rl_term_cr && lendiff > prompt_visible_length && _rl_last_c_pos > 0 && + (((od > 0 || nd > 0) && (od <= prompt_last_invisible || nd <= prompt_last_invisible)) || + ((od >= lendiff) && _rl_last_c_pos < PROMPT_ENDING_INDEX))) + { + _rl_cr (); + if (modmark) + _rl_output_some_chars ("*", 1); + _rl_output_some_chars (local_prompt, lendiff); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + /* If we just output the entire prompt string we can take advantage + of knowing the number of physical characters in the prompt. If + the prompt wraps lines (lendiff clamped at nmax), we can't. */ + if (lendiff == local_prompt_len) + _rl_last_c_pos = prompt_physical_chars + modmark; + else + /* We take wrap_offset into account here so we can pass correct + information to _rl_move_cursor_relative. */ + _rl_last_c_pos = _rl_col_width (local_prompt, 0, lendiff, 1) - wrap_offset + modmark; + cpos_adjusted = 1; + } + else + _rl_last_c_pos = lendiff + modmark; + + /* Now if we have printed the prompt string because the first difference + was within the prompt, see if we need to recompute where the lines + differ. Check whether where we are now is past the last place where + the old and new lines are the same and short-circuit now if we are. */ + if ((od <= prompt_last_invisible || nd <= prompt_last_invisible) && + omax == nmax && + lendiff > (ols-old) && lendiff > (nls-new)) + return; + + /* XXX - we need to fix up our calculations if we are now past the + old ofd/nfd and the prompt length (or line length) has changed. + We punt on the problem and do a dumb update. We'd like to be able + to just output the prompt from the beginning of the line up to the + first difference, but you don't know the number of invisible + characters in that case. + This needs a lot of work to be efficient, but it usually doesn't matter. */ + if ((od <= prompt_last_invisible || nd <= prompt_last_invisible)) + { + nfd = new + lendiff; /* number of characters we output above */ + nfdf = new_face + lendiff; + nd = lendiff; + + /* Do a dumb update and return */ +dumb_update: + temp = ne - nfd; + if (temp > 0) + { + puts_face (nfd, nfdf, temp); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + _rl_last_c_pos += _rl_col_width (new, nd, ne - new, 1); + /* Need to adjust here based on wrap_offset. Guess that if + this is the line containing the last line of the prompt + we need to adjust by + wrap_offset-prompt_invis_chars_first_line + on the assumption that this is the number of invisible + characters in the last line of the prompt. */ + if (wrap_offset > prompt_invis_chars_first_line && + current_line == prompt_last_screen_line && + prompt_physical_chars > _rl_screenwidth && + _rl_horizontal_scroll_mode == 0) + ADJUST_CPOS (wrap_offset - prompt_invis_chars_first_line); + + /* If we just output a new line including the prompt, and + the prompt includes invisible characters, we need to + account for them in the _rl_last_c_pos calculation, since + _rl_col_width does not. This happens when other code does + a goto dumb_update; */ + else if (current_line == 0 && + nfd == new && + prompt_invis_chars_first_line && + local_prompt_len <= temp && + wrap_offset >= prompt_invis_chars_first_line && + _rl_horizontal_scroll_mode == 0) + ADJUST_CPOS (prompt_invis_chars_first_line); + } + else + _rl_last_c_pos += temp; + } + /* This is a useful heuristic, but what we really want is to clear + if the new number of visible screen characters is less than the + old number of visible screen characters. If the prompt has changed, + we don't really have enough information about the visible line to + know for sure, so we use another heuristic calclulation below. */ + if (nmax < omax) + goto clear_rest_of_line; /* XXX */ + else if ((nmax - W_OFFSET(current_line, wrap_offset)) < (omax - W_OFFSET (current_line, visible_wrap_offset))) + goto clear_rest_of_line; + else + return; + } + } + + o_cpos = _rl_last_c_pos; + + /* When this function returns, _rl_last_c_pos is correct, and an absolute + cursor position in multibyte mode, but a buffer index when not in a + multibyte locale. */ + _rl_move_cursor_relative (od, old, old_face); + +#if defined (HANDLE_MULTIBYTE) + /* We need to indicate that the cursor position is correct in the presence of + invisible characters in the prompt string. Let's see if setting this when + we make sure we're at the end of the drawn prompt string works. */ + if (current_line == 0 && mb_cur_max > 1 && rl_byte_oriented == 0 && + (_rl_last_c_pos > 0 || o_cpos > 0) && + _rl_last_c_pos == prompt_physical_chars) + cpos_adjusted = 1; +#endif + + /* if (len (new) > len (old)) + lendiff == difference in buffer (bytes) + col_lendiff == difference on screen (columns) + When not using multibyte characters, these are equal */ + lendiff = (nls - nfd) - (ols - ofd); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + int newchars, newwidth, newind; + int oldchars, oldwidth, oldind; + + newchars = nls - new; + oldchars = ols - old; + + /* If we can do it, try to adjust nls and ols so that nls-new will + contain the entire new prompt string. That way we can use + prompt_physical_chars and not have to recompute column widths. + _rl_col_width adds wrap_offset and expects the caller to compensate, + which we do below, so we do the same thing if we don't call + _rl_col_width. + We don't have to compare, since we know the characters are the same. + The check of differing numbers of invisible chars may be extraneous. + XXX - experimental */ + if (current_line == 0 && nfd == new && newchars > prompt_last_invisible && + newchars <= local_prompt_len && + local_prompt_len <= nmax && + current_invis_chars != visible_wrap_offset) + { + while (newchars < nmax && oldchars < omax && newchars < local_prompt_len) + { +#if defined (HANDLE_MULTIBYTE) + newind = _rl_find_next_mbchar (new, newchars, 1, MB_FIND_NONZERO); + oldind = _rl_find_next_mbchar (old, oldchars, 1, MB_FIND_NONZERO); + + nls += newind - newchars; + ols += oldind - oldchars; + + newchars = newind; + oldchars = oldind; +#else + nls++; ols++; + newchars++; oldchars++; +#endif + } + newwidth = (newchars == local_prompt_len) ? prompt_physical_chars + wrap_offset + : _rl_col_width (new, 0, nls - new, 1); + /* if we changed nls and ols, we need to recompute lendiff */ + lendiff = (nls - nfd) - (ols - ofd); + + nlsf = new_face + (nls - new); + olsf = old_face + (ols - old); + } + else + newwidth = _rl_col_width (new, nfd - new, nls - new, 1); + + oldwidth = _rl_col_width (old, ofd - old, ols - old, 1); + + col_lendiff = newwidth - oldwidth; + } + else + col_lendiff = lendiff; + + /* col_lendiff uses _rl_col_width(), which doesn't know about whether or not + the multibyte characters it counts are invisible, so unless we're printing + the entire prompt string (in which case we can use prompt_physical_chars) + the count is short by the number of bytes in the invisible multibyte + characters - the number of multibyte characters. + + We don't have a good way to solve this without moving to something like + a bitmap that indicates which characters are visible and which are + invisible. We fix it up (imperfectly) in the caller and by trying to use + the entire prompt string wherever we can. */ + + /* 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 && current_invis_chars != visible_wrap_offset) + { + 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; + } + } + + /* We use temp as a count of the number of bytes from the first difference + to the end of the new line. col_temp is the corresponding number of + screen columns. A `dumb' update moves to the spot of first difference + and writes TEMP bytes. */ + /* Insert (diff (len (old), len (new)) ch. */ + temp = ne - nfd; + if (mb_cur_max > 1 && rl_byte_oriented == 0) + col_temp = _rl_col_width (new, nfd - new, ne - new, 1); + else + col_temp = temp; + + /* how many bytes from the new line buffer to write to the display */ + bytes_to_insert = nls - nfd; + + /* col_lendiff > 0 if we are adding characters to the line */ + 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; + + /* If col_lendiff is > 0, implying that the new string takes up more + screen real estate than the old, but lendiff is < 0, meaning that it + takes fewer bytes, we need to just output the characters starting + from the first difference. These will overwrite what is on the + display, so there's no reason to do a smart update. This can really + only happen in a multibyte environment. */ + if (lendiff < 0) + { + puts_face (nfd, nfdf, temp); + _rl_last_c_pos += col_temp; + /* If nfd begins before any invisible characters in the prompt, + adjust _rl_last_c_pos to account for wrap_offset and set + cpos_adjusted to let the caller know. */ + if (current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */ + return; + } + /* Sometimes it is cheaper to print the characters rather than + 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. */ + else 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 + _rl_term_IC or _rl_term_ic will screw up the screen because of the + invisible characters. We need to just draw them. */ + /* The same thing happens if we're trying to draw before the last + invisible character in the prompt string or we're increasing the + number of invisible characters in the line and we're not drawing + the entire prompt string. */ + if (*ols && ((_rl_horizontal_scroll_mode && + _rl_last_c_pos == 0 && + lendiff > prompt_visible_length && + current_invis_chars > 0) == 0) && + (((mb_cur_max > 1 && rl_byte_oriented == 0) && + current_line == 0 && wrap_offset && + ((nfd - new) <= prompt_last_invisible) && + (col_lendiff < prompt_visible_length)) == 0) && + (visible_wrap_offset >= current_invis_chars)) + { + open_some_spaces (col_lendiff); + puts_face (nfd, nfdf, bytes_to_insert); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); + else + _rl_last_c_pos += bytes_to_insert; + } + else if ((mb_cur_max == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0) + { + /* At the end of a line the characters do not have to + be "inserted". They can just be placed on the screen. */ + puts_face (nfd, nfdf, temp); + _rl_last_c_pos += col_temp; + return; + } + else /* just write from first difference to end of new line */ + { + puts_face (nfd, nfdf, temp); + _rl_last_c_pos += col_temp; + /* If nfd begins before the last invisible character in the + prompt, adjust _rl_last_c_pos to account for wrap_offset + and set cpos_adjusted to let the caller know. */ + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */ + return; + } + + if (bytes_to_insert > lendiff) + { + /* If nfd begins before the last invisible character in the + prompt, adjust _rl_last_c_pos to account for wrap_offset + and set cpos_adjusted to let the caller know. */ + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */ + } + } + else + { + /* cannot insert chars, write to EOL */ + puts_face (nfd, nfdf, temp); + _rl_last_c_pos += col_temp; + /* If we're in a multibyte locale and were before the last invisible + char in the current line (which implies we just output some invisible + characters) we need to adjust _rl_last_c_pos, since it represents + a physical character position. */ + /* The current_line*rl_screenwidth+prompt_invis_chars_first_line is a + crude attempt to compute how far into the new line buffer we are. + It doesn't work well in the face of multibyte characters and needs + to be rethought. XXX */ + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && + current_line == prompt_last_screen_line && wrap_offset && + displaying_prompt_first_line && + wrap_offset != prompt_invis_chars_first_line && + ((nfd-new) < (prompt_last_invisible-(current_line*_rl_screenwidth+prompt_invis_chars_first_line)))) + ADJUST_CPOS (wrap_offset - prompt_invis_chars_first_line); + + /* What happens if wrap_offset == prompt_invis_chars_first_line + and we are drawing the first line (current_line == 0), or if we + are drawing the first line and changing the number of invisible + characters in the line? If we're starting to draw before the last + invisible character in the prompt, we need to adjust by + _rl_last_c_pos -= prompt_invis_chars_first_line. This can happen + when we finish reading a digit argument (with the "(arg: N)" + prompt) and are switching back to displaying a line with a prompt + containing invisible characters, since we have to redraw the + entire prompt string. */ + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && + current_line == 0 && wrap_offset && + displaying_prompt_first_line && + wrap_offset == prompt_invis_chars_first_line && + visible_wrap_offset != current_invis_chars && + visible_wrap_offset != prompt_invis_chars_first_line && + ((nfd-new) < prompt_last_invisible)) + ADJUST_CPOS (prompt_invis_chars_first_line); + } + } + else /* Delete characters from line. */ + { + /* If possible and inexpensive to use terminal deletion, then do so. */ + 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 && + displaying_prompt_first_line && + -lendiff == visible_wrap_offset) + col_lendiff = 0; + + /* If we have moved lmargin and we're shrinking the line, we've + already moved the cursor to the first character of the new line, + so deleting -col_lendiff characters will mess up the cursor + position calculation */ + if (_rl_horizontal_scroll_mode && displaying_prompt_first_line == 0 && + col_lendiff && _rl_last_c_pos < -col_lendiff) + col_lendiff = 0; + + if (col_lendiff) + delete_chars (-col_lendiff); /* delete (diff) characters */ + + /* Copy (new) chars to screen from first diff to last match, + overwriting what is there. */ + if (bytes_to_insert > 0) + { + /* If nfd begins at the prompt, or before the invisible + characters in the prompt, we need to adjust _rl_last_c_pos + in a multibyte locale to account for the wrap offset and + set cpos_adjusted accordingly. */ + puts_face (nfd, nfdf, bytes_to_insert); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + /* This still doesn't take into account whether or not the + characters that this counts are invisible. */ + _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); + if (current_line == 0 && wrap_offset && + displaying_prompt_first_line && + prompt_invis_chars_first_line && + _rl_last_c_pos >= prompt_invis_chars_first_line && + ((nfd - new) <= prompt_last_invisible)) + ADJUST_CPOS (prompt_invis_chars_first_line); + +#if 1 +#ifdef HANDLE_MULTIBYTE + /* If we write a non-space into the last screen column, + remove the note that we added a space to compensate for + a multibyte double-width character that didn't fit, since + it's only valid for what was previously there. */ + /* XXX - watch this */ + if (_rl_last_c_pos == _rl_screenwidth && + line_state_invisible->wrapped_line[current_line+1] && + nfd[bytes_to_insert-1] != ' ') + line_state_invisible->wrapped_line[current_line+1] = 0; +#endif +#endif + } + else + _rl_last_c_pos += bytes_to_insert; + + /* XXX - we only want to do this if we are at the end of the line + so we move there with _rl_move_cursor_relative */ + if (_rl_horizontal_scroll_mode && ((oe-old) > (ne-new))) + { + _rl_move_cursor_relative (ne-new, new, new_face); + goto clear_rest_of_line; + } + } + } + /* Otherwise, print over the existing material. */ + else + { + if (temp > 0) + { + /* If nfd begins at the prompt, or before the invisible + characters in the prompt, we need to adjust _rl_last_c_pos + in a multibyte locale to account for the wrap offset and + set cpos_adjusted accordingly. */ + puts_face (nfd, nfdf, temp); + _rl_last_c_pos += col_temp; /* XXX */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + if (current_line == 0 && wrap_offset && + displaying_prompt_first_line && + _rl_last_c_pos > wrap_offset && + ((nfd - new) <= prompt_last_invisible)) + ADJUST_CPOS (wrap_offset); /* XXX - prompt_invis_chars_first_line? */ + } + } +clear_rest_of_line: + lendiff = (oe - old) - (ne - new); + if (mb_cur_max > 1 && rl_byte_oriented == 0) + col_lendiff = _rl_col_width (old, 0, oe - old, 1) - _rl_col_width (new, 0, ne - new, 1); + else + col_lendiff = lendiff; + + /* If we've already printed over the entire width of the screen, + including the old material, then col_lendiff doesn't matter and + space_to_eol will insert too many spaces. XXX - maybe we should + adjust col_lendiff based on the difference between _rl_last_c_pos + and _rl_screenwidth */ + if (col_lendiff && ((mb_cur_max == 1 || rl_byte_oriented) || (_rl_last_c_pos < _rl_screenwidth))) + { + if (_rl_term_autowrap && current_line < inv_botlin) + space_to_eol (col_lendiff); + else + _rl_clear_to_eol (col_lendiff); + } + } + } +} + +/* Tell the update routines that we have moved onto a new (empty) line. */ +int +rl_on_new_line (void) +{ + if (visible_line) + visible_line[0] = '\0'; + + _rl_last_c_pos = _rl_last_v_pos = 0; + _rl_vis_botlin = last_lmargin = 0; + if (vis_lbreaks) + vis_lbreaks[0] = vis_lbreaks[1] = 0; + visible_wrap_offset = 0; + return 0; +} + +/* Clear all screen lines occupied by the current readline line buffer + (visible line) */ +int +rl_clear_visible_line (void) +{ + int curr_line; + + /* Make sure we move to column 0 so we clear the entire line */ + _rl_cr (); + _rl_last_c_pos = 0; + + /* Move to the last screen line of the current visible line */ + _rl_move_vert (_rl_vis_botlin); + + /* And erase screen lines going up to line 0 (first visible line) */ + for (curr_line = _rl_last_v_pos; curr_line >= 0; curr_line--) + { + _rl_move_vert (curr_line); + _rl_clear_to_eol (_rl_screenwidth); + _rl_cr (); /* in case we use space_to_eol() */ + } + + return 0; +} + +/* Tell the update routines that we have moved onto a new line with the + prompt already displayed. Code originally from the version of readline + distributed with CLISP. rl_expand_prompt must have already been called + (explicitly or implicitly). This still doesn't work exactly right; it + should use expand_prompt() */ +int +rl_on_new_line_with_prompt (void) +{ + int prompt_size, i, l, real_screenwidth, newlines; + char *prompt_last_line, *lprompt; + + /* Initialize visible_line and invisible_line to ensure that they can hold + the already-displayed prompt. */ + prompt_size = strlen (rl_prompt) + 1; + init_line_structures (prompt_size); + + /* Make sure the line structures hold the already-displayed prompt for + redisplay. */ + lprompt = local_prompt ? local_prompt : rl_prompt; + strcpy (visible_line, lprompt); + strcpy (invisible_line, lprompt); + + /* If the prompt contains newlines, take the last tail. */ + prompt_last_line = strrchr (rl_prompt, '\n'); + if (!prompt_last_line) + prompt_last_line = rl_prompt; + + l = strlen (prompt_last_line); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + _rl_last_c_pos = _rl_col_width (prompt_last_line, 0, l, 1); /* XXX */ + 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 + one less, see terminal.c. */ + real_screenwidth = _rl_screenwidth + (_rl_term_autowrap ? 0 : 1); + _rl_last_v_pos = l / real_screenwidth; + /* If the prompt length is a multiple of real_screenwidth, we don't know + whether the cursor is at the end of the last line, or already at the + beginning of the next line. Output a newline just to be safe. */ + if (l > 0 && (l % real_screenwidth) == 0) + _rl_output_some_chars ("\n", 1); + last_lmargin = 0; + + newlines = 0; i = 0; + while (i <= l) + { + _rl_vis_botlin = newlines; + vis_lbreaks[newlines++] = i; + i += real_screenwidth; + } + vis_lbreaks[newlines] = l; + visible_wrap_offset = 0; + + rl_display_prompt = rl_prompt; /* XXX - make sure it's set */ + + return 0; +} + +/* Actually update the display, period. */ +int +rl_forced_update_display (void) +{ + register char *temp; + + if (visible_line) + { + temp = visible_line; + while (*temp) + *temp++ = '\0'; + } + rl_on_new_line (); + forced_display++; + (*rl_redisplay_function) (); + return 0; +} + +/* Redraw only the last line of a multi-line prompt. */ +void +rl_redraw_prompt_last_line (void) +{ + char *t; + + t = strrchr (rl_display_prompt, '\n'); + if (t) + redraw_prompt (++t); + else + rl_forced_update_display (); +} + +/* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices. + (Well, when we don't have multibyte characters, _rl_last_c_pos is a + buffer index.) + DATA is the contents of the screen line of interest; i.e., where + the movement is being done. + DATA is always the visible line or the invisible line */ +static void +_rl_move_cursor_relative (int new, const char *data, const char *dataf) +{ + register int i; + int woff; /* number of invisible chars on current line */ + int cpos, dpos; /* current and desired cursor positions */ + int adjust; + int in_invisline; + int mb_cur_max = MB_CUR_MAX; + + woff = WRAP_OFFSET (_rl_last_v_pos, wrap_offset); + cpos = _rl_last_c_pos; + + if (cpos == 0 && cpos == new) + 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 and must be + calculated. We need to account for invisible characters in this line, + as long as we are past them and they are counted by _rl_col_width. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + adjust = 1; + /* Try to short-circuit common cases and eliminate a bunch of multibyte + character function calls. */ + /* 1. prompt string */ + if (new == local_prompt_len && memcmp (data, local_prompt, new) == 0) + { + dpos = prompt_physical_chars; + cpos_adjusted = 1; + adjust = 0; + } + /* 2. prompt_string + line contents */ + else if (new > local_prompt_len && local_prompt && memcmp (data, local_prompt, local_prompt_len) == 0) + { + dpos = prompt_physical_chars + _rl_col_width (data, local_prompt_len, new, 1); + cpos_adjusted = 1; + adjust = 0; + } + else + dpos = _rl_col_width (data, 0, new, 1); + + if (displaying_prompt_first_line == 0) + adjust = 0; + + /* yet another special case: printing the last line of a prompt with + multibyte characters and invisible characters whose printable length + exceeds the screen width with the last invisible character + (prompt_last_invisible) in the last line. IN_INVISLINE is the + offset of DATA in invisible_line */ + in_invisline = 0; + if (data > invisible_line && data < invisible_line+inv_lbreaks[_rl_inv_botlin+1]) + in_invisline = data - invisible_line; + + /* Use NEW when comparing against the last invisible character in the + prompt string, since they're both buffer indices and DPOS is a + desired display position. */ + /* NEW is relative to the current displayed line, while + PROMPT_LAST_INVISIBLE is relative to the entire (wrapped) line. + Need a way to reconcile these two variables by turning NEW into a + buffer position relative to the start of the line */ + if (adjust && ((new > prompt_last_invisible) || /* XXX - don't use woff here */ + (new+in_invisline > prompt_last_invisible) || /* invisible line */ + (prompt_physical_chars >= _rl_screenwidth && /* visible line */ + _rl_last_v_pos == prompt_last_screen_line && + wrap_offset >= woff && dpos >= woff && + new > (prompt_last_invisible-(vis_lbreaks[_rl_last_v_pos])-wrap_offset)))) + /* XXX last comparison might need to be >= */ + { + dpos -= woff; + /* Since this will be assigned to _rl_last_c_pos at the end (more + precisely, _rl_last_c_pos == dpos when this function returns), + let the caller know. */ + cpos_adjusted = 1; + } + } + else +#endif + dpos = new; + + /* If we don't have to do anything, then return. */ + if (cpos == dpos) + return; + + /* It may be faster to output a CR, and then move forwards instead + of moving backwards. */ + /* i == current physical cursor position. */ +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1 && rl_byte_oriented == 0) + i = _rl_last_c_pos; + else +#endif + i = _rl_last_c_pos - woff; + if (dpos == 0 || CR_FASTER (dpos, _rl_last_c_pos) || + (_rl_term_autowrap && i == _rl_screenwidth)) + { + _rl_cr (); + cpos = _rl_last_c_pos = 0; + } + + if (cpos < dpos) + { + /* Move the cursor forward. We do it by printing the command + to move the cursor forward if there is one, else print that + portion of the output buffer again. Which is cheaper? */ + + /* The above comment is left here for posterity. It is faster + to print one character (non-control) than to print a control + sequence telling the terminal to move forward one character. + That kind of control is for people who don't know what the + data is underneath the cursor. */ + + /* However, we need a handle on where the current display position is + in the buffer for the immediately preceding comment to be true. + In multibyte locales, we don't currently have that info available. + Without it, we don't know where the data we have to display begins + in the buffer and we have to go back to the beginning of the screen + line. In this case, we can use the terminal sequence to move forward + if it's available. */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + { + if (_rl_term_forward_char) + { + for (i = cpos; i < dpos; i++) + tputs (_rl_term_forward_char, 1, _rl_output_character_function); + } + else + { + _rl_cr (); + puts_face (data, dataf, new); + } + } + else + puts_face (data + cpos, dataf + cpos, new - cpos); + } + +#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. */ +#endif + else if (cpos > dpos) + _rl_backspace (cpos - dpos); + + _rl_last_c_pos = dpos; +} + +/* PWP: move the cursor up or down. */ +void +_rl_move_vert (int to) +{ + register int delta, i; + + if (_rl_last_v_pos == to || to > _rl_screenheight) + return; + + if ((delta = to - _rl_last_v_pos) > 0) + { + for (i = 0; i < delta; i++) + putc ('\n', rl_outstream); + _rl_cr (); + _rl_last_c_pos = 0; + } + else + { /* delta < 0 */ +#ifdef __DJGPP__ + int row, col; + + fflush (rl_outstream); + ScreenGetCursor (&row, &col); + ScreenSetCursor (row + delta, col); + i = -delta; +#else + if (_rl_term_up && *_rl_term_up) + for (i = 0; i < -delta; i++) + tputs (_rl_term_up, 1, _rl_output_character_function); +#endif /* !__DJGPP__ */ + } + + _rl_last_v_pos = to; /* Now TO is here */ +} + +/* Physically print C on rl_outstream. This is for functions which know + how to optimize the display. Return the number of characters output. */ +int +rl_show_char (int c) +{ + int n = 1; + if (META_CHAR (c) && (_rl_output_meta_chars == 0)) + { + fprintf (rl_outstream, "M-"); + n += 2; + c = UNMETA (c); + } + +#if defined (DISPLAY_TABS) + if ((CTRL_CHAR (c) && c != '\t') || c == RUBOUT) +#else + if (CTRL_CHAR (c) || c == RUBOUT) +#endif /* !DISPLAY_TABS */ + { + fprintf (rl_outstream, "C-"); + n += 2; + c = CTRL_CHAR (c) ? UNCTRL (c) : '?'; + } + + putc (c, rl_outstream); + fflush (rl_outstream); + return n; +} + +int +rl_character_len (int c, int pos) +{ + unsigned char uc; + + uc = (unsigned char)c; + + if (META_CHAR (uc)) + return ((_rl_output_meta_chars == 0) ? 4 : 1); + + if (uc == '\t') + { +#if defined (DISPLAY_TABS) + return (((pos | 7) + 1) - pos); +#else + return (2); +#endif /* !DISPLAY_TABS */ + } + + if (CTRL_CHAR (c) || c == RUBOUT) + return (2); + + return ((ISPRINT (uc)) ? 1 : 2); +} +/* How to print things in the "echo-area". The prompt is treated as a + mini-modeline. */ +static int msg_saved_prompt = 0; + +#if defined (USE_VARARGS) +int +#if defined (PREFER_STDARG) +rl_message (const char *format, ...) +#else +rl_message (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif +#if defined (HAVE_VSNPRINTF) + int bneed; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + if (msg_buf == 0) + msg_buf = xmalloc (msg_bufsiz = 128); + +#if defined (HAVE_VSNPRINTF) + bneed = vsnprintf (msg_buf, msg_bufsiz, format, args); + if (bneed >= msg_bufsiz - 1) + { + msg_bufsiz = bneed + 1; + msg_buf = xrealloc (msg_buf, msg_bufsiz); + va_end (args); + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + vsnprintf (msg_buf, msg_bufsiz - 1, format, args); + } +#else + vsprintf (msg_buf, format, args); + msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */ +#endif + va_end (args); + + if (saved_local_prompt == 0) + { + rl_save_prompt (); + msg_saved_prompt = 1; + } + else if (local_prompt != saved_local_prompt) + { + FREE (local_prompt); + FREE (local_prompt_prefix); + local_prompt = (char *)NULL; + } + rl_display_prompt = msg_buf; + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + (*rl_redisplay_function) (); + + return 0; +} +#else /* !USE_VARARGS */ +int +rl_message (format, arg1, arg2) + char *format; +{ + if (msg_buf == 0) + msg_buf = xmalloc (msg_bufsiz = 128); + + sprintf (msg_buf, format, arg1, arg2); + msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */ + + rl_display_prompt = msg_buf; + if (saved_local_prompt == 0) + { + rl_save_prompt (); + msg_saved_prompt = 1; + } + else if (local_prompt != saved_local_prompt) + { + FREE (local_prompt); + FREE (local_prompt_prefix); + local_prompt = (char *)NULL; + } + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + (*rl_redisplay_function) (); + + return 0; +} +#endif /* !USE_VARARGS */ + +/* How to clear things from the "echo-area". */ +int +rl_clear_message (void) +{ + rl_display_prompt = rl_prompt; + if (msg_saved_prompt) + { + rl_restore_prompt (); + msg_saved_prompt = 0; + } + (*rl_redisplay_function) (); + return 0; +} + +int +rl_reset_line_state (void) +{ + rl_on_new_line (); + + rl_display_prompt = rl_prompt ? rl_prompt : ""; + forced_display = 1; + return 0; +} + +/* Save all of the variables associated with the prompt and its display. Most + of the complexity is dealing with the invisible characters in the prompt + string and where they are. There are enough of these that I should consider + a struct. */ +void +rl_save_prompt (void) +{ + saved_local_prompt = local_prompt; + saved_local_prefix = local_prompt_prefix; + saved_prefix_length = prompt_prefix_length; + saved_local_length = local_prompt_len; + saved_last_invisible = prompt_last_invisible; + saved_visible_length = prompt_visible_length; + saved_invis_chars_first_line = prompt_invis_chars_first_line; + saved_physical_chars = prompt_physical_chars; + saved_local_prompt_newlines = local_prompt_newlines; + + local_prompt = local_prompt_prefix = (char *)0; + local_prompt_len = 0; + local_prompt_newlines = (int *)0; + + prompt_last_invisible = prompt_visible_length = prompt_prefix_length = 0; + prompt_invis_chars_first_line = prompt_physical_chars = 0; +} + +void +rl_restore_prompt (void) +{ + FREE (local_prompt); + FREE (local_prompt_prefix); + FREE (local_prompt_newlines); + + local_prompt = saved_local_prompt; + local_prompt_prefix = saved_local_prefix; + local_prompt_len = saved_local_length; + local_prompt_newlines = saved_local_prompt_newlines; + + prompt_prefix_length = saved_prefix_length; + prompt_last_invisible = saved_last_invisible; + prompt_visible_length = saved_visible_length; + prompt_invis_chars_first_line = saved_invis_chars_first_line; + prompt_physical_chars = saved_physical_chars; + + /* can test saved_local_prompt to see if prompt info has been saved. */ + saved_local_prompt = saved_local_prefix = (char *)0; + saved_local_length = 0; + saved_last_invisible = saved_visible_length = saved_prefix_length = 0; + saved_invis_chars_first_line = saved_physical_chars = 0; + saved_local_prompt_newlines = 0; +} + +char * +_rl_make_prompt_for_search (int pchar) +{ + int len; + char *pmt, *p; + + rl_save_prompt (); + + /* We've saved the prompt, and can do anything with the various prompt + strings we need before they're restored. We want the unexpanded + portion of the prompt string after any final newline. */ + p = rl_prompt ? strrchr (rl_prompt, '\n') : 0; + if (p == 0) + { + len = (rl_prompt && *rl_prompt) ? strlen (rl_prompt) : 0; + pmt = (char *)xmalloc (len + 2); + if (len) + strcpy (pmt, rl_prompt); + pmt[len] = pchar; + pmt[len+1] = '\0'; + } + else + { + p++; + len = strlen (p); + pmt = (char *)xmalloc (len + 2); + if (len) + strcpy (pmt, p); + pmt[len] = pchar; + pmt[len+1] = '\0'; + } + + /* will be overwritten by expand_prompt, called from rl_message */ + prompt_physical_chars = saved_physical_chars + 1; + return pmt; +} + +/* Quick redisplay hack when erasing characters at the end of the line. */ +void +_rl_erase_at_end_of_line (int l) +{ + register int i; + + _rl_backspace (l); + for (i = 0; i < l; i++) + putc (' ', rl_outstream); + _rl_backspace (l); + for (i = 0; i < l; i++) + visible_line[--_rl_last_c_pos] = '\0'; + rl_display_fixed++; +} + +/* Clear to the end of the line. COUNT is the minimum + number of character spaces to clear, but we use a terminal escape + sequence if available. */ +void +_rl_clear_to_eol (int count) +{ +#ifndef __MSDOS__ + if (_rl_term_clreol) + tputs (_rl_term_clreol, 1, _rl_output_character_function); + else +#endif + if (count) + space_to_eol (count); +} + +/* Clear to the end of the line using spaces. COUNT is the minimum + number of character spaces to clear, */ +static void +space_to_eol (int count) +{ + register int i; + + for (i = 0; i < count; i++) + putc (' ', rl_outstream); + + _rl_last_c_pos += count; +} + +void +_rl_clear_screen (int clrscr) +{ +#if defined (__DJGPP__) + ScreenClear (); + ScreenSetCursor (0, 0); +#else + if (_rl_term_clrpag) + { + tputs (_rl_term_clrpag, 1, _rl_output_character_function); + if (clrscr && _rl_term_clrscroll) + tputs (_rl_term_clrscroll, 1, _rl_output_character_function); + } + else + rl_crlf (); +#endif /* __DJGPP__ */ +} + +/* Insert COUNT characters from STRING to the output stream at column COL. */ +static void +insert_some_chars (char *string, int count, int col) +{ + open_some_spaces (col); + _rl_output_some_chars (string, count); +} + +/* Insert COL spaces, keeping the cursor at the same position. We follow the + ncurses documentation and use either im/ei with explicit spaces, or IC/ic + by itself. We assume there will either be ei or we don't need to use it. */ +static void +open_some_spaces (int col) +{ +#if !defined (__MSDOS__) && (!defined (__MINGW32__) || defined (NCURSES_VERSION)) + char *buffer; + register int i; + + /* If IC is defined, then we do not have to "enter" insert mode. */ + if (_rl_term_IC) + { + buffer = tgoto (_rl_term_IC, 0, col); + tputs (buffer, 1, _rl_output_character_function); + } + else if (_rl_term_im && *_rl_term_im) + { + tputs (_rl_term_im, 1, _rl_output_character_function); + /* just output the desired number of spaces */ + for (i = col; i--; ) + _rl_output_character_function (' '); + /* If there is a string to turn off insert mode, use it now. */ + if (_rl_term_ei && *_rl_term_ei) + tputs (_rl_term_ei, 1, _rl_output_character_function); + /* and move back the right number of spaces */ + _rl_backspace (col); + } + else if (_rl_term_ic && *_rl_term_ic) + { + /* If there is a special command for inserting characters, then + use that first to open up the space. */ + for (i = col; i--; ) + tputs (_rl_term_ic, 1, _rl_output_character_function); + } +#endif /* !__MSDOS__ && (!__MINGW32__ || NCURSES_VERSION)*/ +} + +/* Delete COUNT characters from the display line. */ +static void +delete_chars (int count) +{ + if (count > _rl_screenwidth) /* XXX */ + return; + +#if !defined (__MSDOS__) && (!defined (__MINGW32__) || defined (NCURSES_VERSION)) + if (_rl_term_DC && *_rl_term_DC) + { + char *buffer; + buffer = tgoto (_rl_term_DC, count, count); + tputs (buffer, count, _rl_output_character_function); + } + else + { + if (_rl_term_dc && *_rl_term_dc) + while (count--) + tputs (_rl_term_dc, 1, _rl_output_character_function); + } +#endif /* !__MSDOS__ && (!__MINGW32__ || NCURSES_VERSION)*/ +} + +void +_rl_update_final (void) +{ + int full_lines, woff, botline_length; + + if (line_structures_initialized == 0) + return; + + full_lines = 0; + /* If the cursor is the only thing on an otherwise-blank last line, + compensate so we don't print an extra CRLF. */ + if (_rl_vis_botlin && _rl_last_c_pos == 0 && + visible_line[vis_lbreaks[_rl_vis_botlin]] == 0) + { + _rl_vis_botlin--; + full_lines = 1; + } + _rl_move_vert (_rl_vis_botlin); + woff = W_OFFSET(_rl_vis_botlin, wrap_offset); + botline_length = VIS_LLEN(_rl_vis_botlin) - woff; + /* If we've wrapped lines, remove the final xterm line-wrap flag. */ + if (full_lines && _rl_term_autowrap && botline_length == _rl_screenwidth) + { + char *last_line, *last_face; + + /* LAST_LINE includes invisible characters, so if you want to get the + last character of the first line, you have to take WOFF into account. + This needs to be done for both calls to _rl_move_cursor_relative, + which takes a buffer position as the first argument, and any direct + subscripts of LAST_LINE. */ + last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; /* = VIS_CHARS(_rl_vis_botlin); */ + last_face = &vis_face[vis_lbreaks[_rl_vis_botlin]]; /* = VIS_CHARS(_rl_vis_botlin); */ + cpos_buffer_position = -1; /* don't know where we are in buffer */ + _rl_move_cursor_relative (_rl_screenwidth - 1 + woff, last_line, last_face); /* XXX */ + _rl_clear_to_eol (0); + puts_face (&last_line[_rl_screenwidth - 1 + woff], + &last_face[_rl_screenwidth - 1 + woff], 1); + } + _rl_vis_botlin = 0; + if (botline_length > 0 || _rl_last_c_pos > 0) + rl_crlf (); + fflush (rl_outstream); + rl_display_fixed++; +} + +/* Move to the start of the current line. */ +static void +cr (void) +{ + _rl_cr (); + _rl_last_c_pos = 0; +} + +/* Redraw the last line of a multi-line prompt that may possibly contain + terminal escape sequences. Called with the cursor at column 0 of the + line to draw the prompt on. */ +static void +redraw_prompt (char *t) +{ + char *oldp; + + oldp = rl_display_prompt; + rl_save_prompt (); + + rl_display_prompt = t; + local_prompt = expand_prompt (t, PMT_MULTILINE, + &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + + rl_forced_update_display (); + + rl_display_prompt = oldp; + rl_restore_prompt(); +} + +/* Redisplay the current line after a SIGWINCH is received. */ +void +_rl_redisplay_after_sigwinch (void) +{ + char *t; + + /* Clear the last line (assuming that the screen size change will result in + either more or fewer characters on that line only) and put the cursor at + column 0. Make sure the right thing happens if we have wrapped to a new + screen line. */ + if (_rl_term_cr) + { + rl_clear_visible_line (); + if (_rl_last_v_pos > 0) + _rl_move_vert (0); + } + else + rl_crlf (); + + if (_rl_screenwidth < prompt_visible_length) + _rl_reset_prompt (); /* update local_prompt_newlines array */ + + /* Redraw only the last line of a multi-line prompt. */ + t = strrchr (rl_display_prompt, '\n'); + if (t) + redraw_prompt (++t); + else + rl_forced_update_display (); +} + +void +_rl_clean_up_for_exit (void) +{ + if (_rl_echoing_p) + { + if (_rl_vis_botlin > 0) /* minor optimization plus bug fix */ + _rl_move_vert (_rl_vis_botlin); + _rl_vis_botlin = 0; + fflush (rl_outstream); + rl_restart_output (1, 0); + } +} + +void +_rl_erase_entire_line (void) +{ + cr (); + _rl_clear_to_eol (0); + cr (); + fflush (rl_outstream); +} + +void +_rl_ttyflush (void) +{ + fflush (rl_outstream); +} + +/* return the `current display line' of the cursor -- the number of lines to + move up to get to the first screen line of the current readline line. */ +int +_rl_current_display_line (void) +{ + int ret, nleft; + + /* Find out whether or not there might be invisible characters in the + editing buffer. */ + if (rl_display_prompt == rl_prompt) + nleft = _rl_last_c_pos - _rl_screenwidth - rl_visible_prompt_length; + else + nleft = _rl_last_c_pos - _rl_screenwidth; + + if (nleft > 0) + ret = 1 + nleft / _rl_screenwidth; + else + ret = 0; + + return ret; +} + +void +_rl_refresh_line (void) +{ + rl_clear_visible_line (); + rl_redraw_prompt_last_line (); + rl_keep_mark_active (); +} + +#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 (const char *str, int start, int end, int flags) +{ + WCHAR_T wc; + mbstate_t ps; + int tmp, point, width, max; + + if (end <= start) + return 0; + if (MB_CUR_MAX == 1 || rl_byte_oriented) + /* this can happen in some cases where it's inconvenient to check */ + return (end - start); + + memset (&ps, 0, sizeof (mbstate_t)); + + point = 0; + max = end; + + /* Try to short-circuit common cases. The adjustment to remove wrap_offset + is done by the caller. */ + /* 1. prompt string */ + if (flags && start == 0 && end == local_prompt_len && memcmp (str, local_prompt, local_prompt_len) == 0) + return (prompt_physical_chars + wrap_offset); + /* 2. prompt string + line contents */ + else if (flags && start == 0 && local_prompt_len > 0 && end > local_prompt_len && local_prompt && memcmp (str, local_prompt, local_prompt_len) == 0) + { + tmp = prompt_physical_chars + wrap_offset; + /* XXX - try to call ourselves recursively with non-prompt portion */ + tmp += _rl_col_width (str, local_prompt_len, end, flags); + return (tmp); + } + + while (point < start) + { + if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point])) + { + memset (&ps, 0, sizeof (mbstate_t)); + tmp = 1; + } + else + tmp = mbrlen (str + point, max, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* 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 (MB_NULLWCH (tmp)) + 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) + { + if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point])) + { + tmp = 1; + wc = (WCHAR_T) str[point]; + } + else + tmp = MBRTOWC (&wc, str + point, max, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* 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 (MB_NULLWCH (tmp)) + break; /* Found '\0' */ + else + { + point += tmp; + max -= tmp; + tmp = WCWIDTH(wc); + width += (tmp >= 0) ? tmp : 1; + } + } + + width += point - end; + + return width; +} +#endif /* HANDLE_MULTIBYTE */ diff --git a/lib/readline/doc/Makefile b/lib/readline/doc/Makefile new file mode 100644 index 0000000..2ad833a --- /dev/null +++ b/lib/readline/doc/Makefile @@ -0,0 +1,159 @@ +# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# + +topdir = . +srcdir = . +VPATH = . + +prefix = /usr/local +infodir = ${prefix}/info + +mandir = ${prefix}/man +manpfx = man + +man1ext = 1 +man1dir = $(mandir)/$(manpfx)$(man1ext) +man3ext = 3 +man3dir = $(mandir)/$(manpfx)$(man3ext) + +SHELL = /bin/sh +RM = rm -f + +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 + +BUILD_DIR = . +TEXINPUTDIR = $(srcdir) + +MAKEINFO = LANGUAGE= makeinfo +TEXI2DVI = $(srcdir)/texi2dvi +TEXI2HTML = $(srcdir)/texi2html +QUIETPS = #set this to -q to shut up dvips +PSDPI = 300 # I don't have any 600-dpi printers +DVIPS = dvips -D ${PSDPI} $(QUIETPS) -o $@ # tricky +DVIPDF = dvipdfm -o $@ -p ${PAPERSIZE} +PSPDF = gs -sPAPERSIZE=${PAPERSIZE} -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -sOutputFile=$@ + +RLSRC = $(srcdir)/rlman.texi $(srcdir)/rluser.texi \ + $(srcdir)/rltech.texi $(srcdir)/version.texi \ + $(srcdir)/rluserman.texi +HISTSRC = $(srcdir)/history.texi $(srcdir)/hsuser.texi \ + $(srcdir)/hstech.texi $(srcdir)/version.texi + +# This should be a program that converts troff to an ascii-readable format +NROFF = groff -Tascii + +# This should be a program that converts troff to postscript +GROFF = groff + +DVIOBJ = readline.dvi history.dvi rluserman.dvi +INFOOBJ = readline.info history.info rluserman.info +PSOBJ = readline.ps history.ps rluserman.ps +HTMLOBJ = readline.html history.html rluserman.html +PDFOBJ = readline.pdf history.pdf rluserman.pdf + +INTERMEDIATE_OBJ = rlman.dvi + +CREATED_DOCS = $(DVIOBJ) $(INFOOBJ) $(PSOBJ) $(HTMLOBJ) $(PDFOBJ) + +.SUFFIXES: .ps .txt .dvi .html .pdf + +.ps.pdf: + $(RM) $@ + -${PSPDF} $< + +.dvi.pdf: + $(RM) $@ + -${DVIPDF} $< + +all: info dvi html ps +nodvi: info html +pdf: $(PDFOBJ) + +readline.dvi: $(RLSRC) + TEXINPUTS=.:$(TEXINPUTDIR):$$TEXINPUTS $(TEXI2DVI) $(srcdir)/rlman.texi + mv rlman.dvi readline.dvi + +readline.info: $(RLSRC) + $(MAKEINFO) --no-split -I $(TEXINPUTDIR) -o $@ $(srcdir)/rlman.texi + +rluserman.dvi: $(RLSRC) + TEXINPUTS=.:$(TEXINPUTDIR):$$TEXINPUTS $(TEXI2DVI) $(srcdir)/rluserman.texi + +rluserman.info: $(RLSRC) + $(MAKEINFO) --no-split -I $(TEXINPUTDIR) -o $@ $(srcdir)/rluserman.texi + +history.dvi: ${HISTSRC} + TEXINPUTS=.:$(TEXINPUTDIR):$$TEXINPUTS $(TEXI2DVI) $(srcdir)/history.texi + +history.info: ${HISTSRC} + $(MAKEINFO) --no-split -I $(TEXINPUTDIR) -o $@ $(srcdir)/history.texi + +readline.ps: readline.dvi + $(RM) $@ + $(DVIPS) readline.dvi + +rluserman.ps: rluserman.dvi + $(RM) $@ + $(DVIPS) rluserman.dvi + +history.ps: history.dvi + $(RM) $@ + $(DVIPS) history.dvi + +readline.html: ${RLSRC} + $(TEXI2HTML) -menu -monolithic -I $(TEXINPUTDIR) $(srcdir)/rlman.texi + sed -e 's:rlman.html:readline.html:' rlman.html > readline.html + $(RM) rlman.html + +rluserman.html: ${RLSRC} + $(TEXI2HTML) -menu -monolithic -I $(TEXINPUTDIR) $(srcdir)/rluserman.texi + +history.html: ${HISTSRC} + $(TEXI2HTML) -menu -monolithic -I $(TEXINPUTDIR) $(srcdir)/history.texi + +info: $(INFOOBJ) +dvi: $(DVIOBJ) +ps: $(PSOBJ) +html: $(HTMLOBJ) + +readline.pdf: readline.dvi +history.pdf: history.dvi +rluserman.pdf: rluserman.dvi + +clean: + $(RM) *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.cps *.pgs \ + *.fns *.kys *.tps *.vrs *.bt *.bts *.o core *.core + +distclean: clean + $(RM) $(CREATED_DOCS) + $(RM) $(INTERMEDIATE_OBJ) + +mostlyclean: clean + +maintainer-clean: clean + $(RM) $(CREATED_DOCS) + $(RM) $(INTERMEDIATE_OBJ) + $(RM) Makefile + +install: + @echo "This documentation should not be installed." + +uninstall: diff --git a/lib/readline/doc/fdl.texi b/lib/readline/doc/fdl.texi new file mode 100644 index 0000000..8805f1a --- /dev/null +++ b/lib/readline/doc/fdl.texi @@ -0,0 +1,506 @@ +@c The GNU Free Documentation License. +@center Version 1.3, 3 November 2008 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. +@uref{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The ``publisher'' means any person or entity that distributes copies +of the Document to the public. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +@item +RELICENSING + +``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the +site means any set of copyrightable works thus published on the MMC +site. + +``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +``Incorporate'' means to publish or republish a Document, in whole or +in part, as part of another Document. + +An MMC is ``eligible for relicensing'' if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole +or in part into the MMC, (1) had no cover texts or invariant sections, +and (2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/lib/readline/doc/history.texi b/lib/readline/doc/history.texi new file mode 100644 index 0000000..721118b --- /dev/null +++ b/lib/readline/doc/history.texi @@ -0,0 +1,85 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header (This is for running Texinfo on a region.) +@setfilename history.info +@settitle GNU History Library +@include version.texi + +@c %**end of header (This is for running Texinfo on a region.) + +@copying +This document describes the GNU History library +(version @value{VERSION}, @value{UPDATED}), +a programming tool that provides a consistent user interface for +recalling lines of previously typed input. + +Copyright @copyright{} 1988--2022 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the section entitled +``GNU Free Documentation License''. + +@end quotation +@end copying + +@dircategory Libraries +@direntry +* History: (history). The GNU history library API. +@end direntry + +@titlepage +@title GNU History Library +@subtitle Edition @value{EDITION}, for @code{History Library} Version @value{VERSION}. +@subtitle @value{UPDATED-MONTH} +@author Chet Ramey, Case Western Reserve University +@author Brian Fox, Free Software Foundation + +@page + +@vskip 0pt plus 1filll +@insertcopying + +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU History Library + +This document describes the GNU History library, a programming tool that +provides a consistent user interface for recalling lines of previously +typed input. + +@menu +* Using History Interactively:: GNU History User's Manual. +* Programming with GNU History:: GNU History Programmer's Manual. +* GNU Free Documentation License:: License for copying this manual. +* Concept Index:: Index of concepts described in this manual. +* Function and Variable Index:: Index of externally visible functions + and variables. +@end menu +@end ifnottex + +@syncodeindex fn vr + +@include hsuser.texi +@include hstech.texi + +@node GNU Free Documentation License +@appendix GNU Free Documentation License + +@include fdl.texi + +@node Concept Index +@appendix Concept Index +@printindex cp + +@node Function and Variable Index +@appendix Function and Variable Index +@printindex vr + +@bye diff --git a/lib/readline/doc/hstech.texi b/lib/readline/doc/hstech.texi new file mode 100644 index 0000000..da6417b --- /dev/null +++ b/lib/readline/doc/hstech.texi @@ -0,0 +1,602 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988-2022 Free Software Foundation, Inc. +Authored by Brian Fox and Chet Ramey. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Programming with GNU History +@chapter Programming with GNU History + +This chapter describes how to interface programs that you write +with the @sc{gnu} History Library. +It should be considered a technical guide. +For information on the interactive use of @sc{gnu} History, @pxref{Using +History Interactively}. + +@menu +* Introduction to History:: What is the GNU History library for? +* History Storage:: How information is stored. +* History Functions:: Functions that you can use. +* History Variables:: Variables that control behaviour. +* History Programming Example:: Example of using the GNU History Library. +@end menu + +@node Introduction to History +@section Introduction to History + +Many programs read input from the user a line at a time. The @sc{gnu} +History library is able to keep track of those lines, associate arbitrary +data with each line, and utilize information from previous lines in +composing new ones. + +A programmer using the History library has available functions +for remembering lines on a history list, associating arbitrary data +with a line, removing lines from the list, searching through the list +for a line containing an arbitrary text string, and referencing any line +in the list directly. In addition, a history @dfn{expansion} function +is available which provides for a consistent user interface across +different programs. + +The user using programs written with the History library has the +benefit of a consistent user interface with a set of well-known +commands for manipulating the text of previous lines and using that text +in new commands. The basic history manipulation commands are similar to +the history substitution provided by @code{csh}. + +The programmer can also use the Readline library, which +includes some history manipulation by default, and has the added +advantage of command line editing. + +Before declaring any functions using any functionality the History +library provides in other code, an application writer should include +the file @code{<readline/history.h>} in any file that uses the +History library's features. It supplies extern declarations for all +of the library's public functions and variables, and declares all of +the public data structures. + +@node History Storage +@section History Storage + +The history list is an array of history entries. A history entry is +declared as follows: + +@example +typedef void *histdata_t; + +typedef struct _hist_entry @{ + char *line; + char *timestamp; + histdata_t data; +@} HIST_ENTRY; +@end example + +The history list itself might therefore be declared as + +@example +HIST_ENTRY **the_history_list; +@end example + +The state of the History library is encapsulated into a single structure: + +@example +/* + * A structure used to pass around the current state of the history. + */ +typedef struct _hist_state @{ + HIST_ENTRY **entries; /* Pointer to the entries themselves. */ + int offset; /* The location pointer within this array. */ + int length; /* Number of elements within this array. */ + int size; /* Number of slots allocated to this array. */ + int flags; +@} HISTORY_STATE; +@end example + +If the flags member includes @code{HS_STIFLED}, the history has been +stifled. + +@node History Functions +@section History Functions + +This section describes the calling sequence for the various functions +exported by the @sc{gnu} History library. + +@menu +* Initializing History and State Management:: Functions to call when you + want to use history in a + program. +* History List Management:: Functions used to manage the list + of history entries. +* Information About the History List:: Functions returning information about + the history list. +* Moving Around the History List:: Functions used to change the position + in the history list. +* Searching the History List:: Functions to search the history list + for entries containing a string. +* Managing the History File:: Functions that read and write a file + containing the history list. +* History Expansion:: Functions to perform csh-like history + expansion. +@end menu + +@node Initializing History and State Management +@subsection Initializing History and State Management + +This section describes functions used to initialize and manage +the state of the History library when you want to use the history +functions in your program. + +@deftypefun void using_history (void) +Begin a session in which the history functions might be used. This +initializes the interactive variables. +@end deftypefun + +@deftypefun {HISTORY_STATE *} history_get_history_state (void) +Return a structure describing the current state of the input history. +@end deftypefun + +@deftypefun void history_set_history_state (HISTORY_STATE *state) +Set the state of the history list according to @var{state}. +@end deftypefun + +@node History List Management +@subsection History List Management + +These functions manage individual entries on the history list, or set +parameters managing the list itself. + +@deftypefun void add_history (const char *string) +Place @var{string} at the end of the history list. The associated data +field (if any) is set to @code{NULL}. +If the maximum number of history entries has been set using +@code{stifle_history()}, and the new number of history entries would exceed +that maximum, the oldest history entry is removed. +@end deftypefun + +@deftypefun void add_history_time (const char *string) +Change the time stamp associated with the most recent history entry to +@var{string}. +@end deftypefun + +@deftypefun {HIST_ENTRY *} remove_history (int which) +Remove history entry at offset @var{which} from the history. The +removed element is returned so you can free the line, data, +and containing structure. +@end deftypefun + +@deftypefun {histdata_t} free_history_entry (HIST_ENTRY *histent) +Free the history entry @var{histent} and any history library private +data associated with it. Returns the application-specific data +so the caller can dispose of it. +@end deftypefun + +@deftypefun {HIST_ENTRY *} replace_history_entry (int which, const char *line, histdata_t data) +Make the history entry at offset @var{which} have @var{line} and @var{data}. +This returns the old entry so the caller can dispose of any +application-specific data. In the case +of an invalid @var{which}, a @code{NULL} pointer is returned. +@end deftypefun + +@deftypefun void clear_history (void) +Clear the history list by deleting all the entries. +@end deftypefun + +@deftypefun void stifle_history (int max) +Stifle the history list, remembering only the last @var{max} entries. +The history list will contain only @var{max} entries at a time. +@end deftypefun + +@deftypefun int unstifle_history (void) +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 + +@deftypefun int history_is_stifled (void) +Returns non-zero if the history is stifled, zero if it is not. +@end deftypefun + +@node Information About the History List +@subsection Information About the History List + +These functions return information about the entire history list or +individual list entries. + +@deftypefun {HIST_ENTRY **} history_list (void) +Return a @code{NULL} terminated array of @code{HIST_ENTRY *} which is the +current input history. Element 0 of this list is the beginning of time. +If there is no history, return @code{NULL}. +@end deftypefun + +@deftypefun int where_history (void) +Returns the offset of the current history element. +@end deftypefun + +@deftypefun {HIST_ENTRY *} current_history (void) +Return the history entry at the current position, as determined by +@code{where_history()}. If there is no entry there, return a @code{NULL} +pointer. +@end deftypefun + +@deftypefun {HIST_ENTRY *} history_get (int offset) +Return the history entry at position @var{offset}. +The range of valid +values of @var{offset} starts at @code{history_base} and ends at +@var{history_length} - 1 (@pxref{History Variables}). +If there is no entry there, or if @var{offset} is outside the valid +range, return a @code{NULL} pointer. +@end deftypefun + +@deftypefun time_t history_get_time (HIST_ENTRY *entry) +Return the time stamp associated with the history entry @var{entry}. +If the timestamp is missing or invalid, return 0. +@end deftypefun + +@deftypefun int history_total_bytes (void) +Return the number of bytes that the primary history entries are using. +This function returns the sum of the lengths of all the lines in the +history. +@end deftypefun + +@node Moving Around the History List +@subsection Moving Around the History List + +These functions allow the current index into the history list to be +set or changed. + +@deftypefun int history_set_pos (int pos) +Set the current history offset to @var{pos}, an absolute index +into the list. +Returns 1 on success, 0 if @var{pos} is less than zero or greater +than the number of history entries. +@end deftypefun + +@deftypefun {HIST_ENTRY *} previous_history (void) +Back up the current history offset to the previous history entry, and +return a pointer to that entry. If there is no previous entry, return +a @code{NULL} pointer. +@end deftypefun + +@deftypefun {HIST_ENTRY *} next_history (void) +If the current history offset refers to a valid history entry, +increment the current history offset. +If the possibly-incremented history offset refers to a valid history +entry, return a pointer to that entry; +otherwise, return a @code{BNULL} pointer. +@end deftypefun + +@node Searching the History List +@subsection Searching the History List +@cindex History Searching + +These functions allow searching of the history list for entries containing +a specific string. Searching may be performed both forward and backward +from the current history position. The search may be @dfn{anchored}, +meaning that the string must match at the beginning of the history entry. +@cindex anchored search + +@deftypefun int history_search (const char *string, int direction) +Search the history for @var{string}, starting at the current history offset. +If @var{direction} is less than 0, then the search is through +previous entries, otherwise through subsequent entries. +If @var{string} is found, then +the current history index is set to that history entry, and the value +returned is the offset in the line of the entry where +@var{string} was found. Otherwise, nothing is changed, and a -1 is +returned. +@end deftypefun + +@deftypefun int history_search_prefix (const char *string, int direction) +Search the history for @var{string}, starting at the current history +offset. The search is anchored: matching lines must begin with +@var{string}. If @var{direction} is less than 0, then the search is +through previous entries, otherwise through subsequent entries. +If @var{string} is found, then the +current history index is set to that entry, and the return value is 0. +Otherwise, nothing is changed, and a -1 is returned. +@end deftypefun + +@deftypefun int history_search_pos (const char *string, int direction, int pos) +Search for @var{string} in the history list, starting at @var{pos}, an +absolute index into the list. If @var{direction} is negative, the search +proceeds backward from @var{pos}, otherwise forward. Returns the absolute +index of the history element where @var{string} was found, or -1 otherwise. +@end deftypefun + +@node Managing the History File +@subsection Managing the History File + +The History library can read the history from and write it to a file. +This section documents the functions for managing a history file. + +@deftypefun int read_history (const char *filename) +Add the contents of @var{filename} to the history list, a line at a time. +If @var{filename} is @code{NULL}, then read from @file{~/.history}. +Returns 0 if successful, or @code{errno} if not. +@end deftypefun + +@deftypefun int read_history_range (const char *filename, int from, int to) +Read a range of lines from @var{filename}, adding them to the history list. +Start reading at line @var{from} and end at @var{to}. +If @var{from} is zero, start at the beginning. If @var{to} is less than +@var{from}, then read until the end of the file. If @var{filename} is +@code{NULL}, then read from @file{~/.history}. Returns 0 if successful, +or @code{errno} if not. +@end deftypefun + +@deftypefun int write_history (const char *filename) +Write the current history to @var{filename}, overwriting @var{filename} +if necessary. +If @var{filename} is @code{NULL}, then write the history list to +@file{~/.history}. +Returns 0 on success, or @code{errno} on a read or write error. +@end deftypefun + +@deftypefun int append_history (int nelements, const char *filename) +Append the last @var{nelements} of the history list to @var{filename}. +If @var{filename} is @code{NULL}, then append to @file{~/.history}. +Returns 0 on success, or @code{errno} on a read or write error. +@end deftypefun + +@deftypefun int history_truncate_file (const char *filename, int nlines) +Truncate the history file @var{filename}, leaving only the last +@var{nlines} lines. +If @var{filename} is @code{NULL}, then @file{~/.history} is truncated. +Returns 0 on success, or @code{errno} on failure. +@end deftypefun + +@node History Expansion +@subsection History Expansion + +These functions implement history expansion. + +@deftypefun int history_expand (char *string, char **output) +Expand @var{string}, placing the result into @var{output}, a pointer +to a string (@pxref{History Interaction}). Returns: +@table @code +@item 0 +If no expansions took place (or, if the only change in +the text was the removal of escape characters preceding the history expansion +character); +@item 1 +if expansions did take place; +@item -1 +if there was an error in expansion; +@item 2 +if the returned line should be displayed, but not executed, +as with the @code{:p} modifier (@pxref{Modifiers}). +@end table + +If an error occurred in expansion, then @var{output} contains a descriptive +error message. +@end deftypefun + +@deftypefun {char *} get_history_event (const char *string, int *cindex, int qchar) +Returns the text of the history event beginning at @var{string} + +@var{*cindex}. @var{*cindex} is modified to point to after the event +specifier. At function entry, @var{cindex} points to the index into +@var{string} where the history event specification begins. @var{qchar} +is a character that is allowed to end the event specification in addition +to the ``normal'' terminating characters. +@end deftypefun + +@deftypefun {char **} history_tokenize (const char *string) +Return an array of tokens parsed out of @var{string}, much as the +shell might. The tokens are split on the characters in the +@var{history_word_delimiters} variable, +and shell quoting conventions are obeyed as described below. +@end deftypefun + +@deftypefun {char *} history_arg_extract (int first, int last, const char *string) +Extract a string segment consisting of the @var{first} through @var{last} +arguments present in @var{string}. Arguments are split using +@code{history_tokenize}. +@end deftypefun + +@node History Variables +@section History Variables + +This section describes the externally-visible variables exported by +the @sc{gnu} History Library. + +@deftypevar int history_base +The logical offset of the first entry in the history list. +@end deftypevar + +@deftypevar int history_length +The number of entries currently stored in the history list. +@end deftypevar + +@deftypevar int history_max_entries +The maximum number of history entries. This must be changed using +@code{stifle_history()}. +@end deftypevar + +@deftypevar int history_write_timestamps +If non-zero, timestamps are written to the history file, so they can be +preserved between sessions. The default value is 0, meaning that +timestamps are not saved. + +The current timestamp format uses the value of @var{history_comment_char} +to delimit timestamp entries in the history file. If that variable does +not have a value (the default), timestamps will not be written. +@end deftypevar + +@deftypevar char history_expansion_char +The character that introduces a history event. The default is @samp{!}. +Setting this to 0 inhibits history expansion. +@end deftypevar + +@deftypevar char history_subst_char +The character that invokes word substitution if found at the start of +a line. The default is @samp{^}. +@end deftypevar + +@deftypevar char history_comment_char +During tokenization, if this character is seen as the first character +of a word, then it and all subsequent characters up to a newline are +ignored, suppressing history expansion for the remainder of the line. +This is disabled by default. +@end deftypevar + +@deftypevar {char *} history_word_delimiters +The characters that separate tokens for @code{history_tokenize()}. +The default value is @code{" \t\n()<>;&|"}. +@end deftypevar + +@deftypevar {char *} history_search_delimiter_chars +The list of additional characters which can delimit a history search +string, in addition to space, TAB, @samp{:} and @samp{?} in the case of +a substring search. The default is empty. +@end deftypevar + +@deftypevar {char *} history_no_expand_chars +The list of characters which inhibit history expansion if found immediately +following @var{history_expansion_char}. The default is space, tab, newline, +carriage return, and @samp{=}. +@end deftypevar + +@deftypevar int history_quotes_inhibit_expansion +If non-zero, the history expansion code implements shell-like quoting: +single-quoted words are not scanned for the history expansion +character or the history comment character, and double-quoted words may +have history expansion performed, since single quotes are not special +within double quotes. +The default value is 0. +@end deftypevar + +@deftypevar int history_quoting_state +An application may set this variable to indicate that the current line +being expanded is subject to existing quoting. If set to @samp{'}, the +history expansion function will assume that the line is single-quoted and +inhibit expansion until it reads an unquoted closing single quote; if set +to @samp{"}, history expansion will assume the line is double quoted until +it reads an unquoted closing double quote. If set to zero, the default, +the history expansion function will assume the line is not quoted and +treat quote characters within the line as described above. +This is only effective if @var{history_quotes_inhibit_expansion} is set. +@end deftypevar + +@deftypevar {rl_linebuf_func_t *} history_inhibit_expansion_function +This should be set to the address of a function that takes two arguments: +a @code{char *} (@var{string}) +and an @code{int} index into that string (@var{i}). +It should return a non-zero value if the history expansion starting at +@var{string[i]} should not be performed; zero if the expansion should +be done. +It is intended for use by applications like Bash that use the history +expansion character for additional purposes. +By default, this variable is set to @code{NULL}. +@end deftypevar + +@node History Programming Example +@section History Programming Example + +The following program demonstrates simple use of the @sc{gnu} History Library. + +@smallexample +#include <stdio.h> +#include <readline/history.h> + +main (argc, argv) + int argc; + char **argv; +@{ + char line[1024], *t; + int len, done = 0; + + line[0] = 0; + + using_history (); + while (!done) + @{ + printf ("history$ "); + fflush (stdout); + t = fgets (line, sizeof (line) - 1, stdin); + if (t && *t) + @{ + len = strlen (t); + if (t[len - 1] == '\n') + t[len - 1] = '\0'; + @} + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + @{ + char *expansion; + int result; + + result = history_expand (line, &expansion); + if (result) + fprintf (stderr, "%s\n", expansion); + + if (result < 0 || result == 2) + @{ + free (expansion); + continue; + @} + + add_history (expansion); + strncpy (line, expansion, sizeof (line) - 1); + free (expansion); + @} + + if (strcmp (line, "quit") == 0) + done = 1; + else if (strcmp (line, "save") == 0) + write_history ("history_file"); + else if (strcmp (line, "read") == 0) + read_history ("history_file"); + else if (strcmp (line, "list") == 0) + @{ + register HIST_ENTRY **the_list; + register int i; + + the_list = history_list (); + if (the_list) + for (i = 0; the_list[i]; i++) + printf ("%d: %s\n", i + history_base, the_list[i]->line); + @} + else if (strncmp (line, "delete", 6) == 0) + @{ + int which; + if ((sscanf (line + 6, "%d", &which)) == 1) + @{ + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + @{ + free (entry->line); + free (entry); + @} + @} + else + @{ + fprintf (stderr, "non-numeric arg given to `delete'\n"); + @} + @} + @} +@} +@end smallexample diff --git a/lib/readline/doc/hsuser.texi b/lib/readline/doc/hsuser.texi new file mode 100644 index 0000000..9081baf --- /dev/null +++ b/lib/readline/doc/hsuser.texi @@ -0,0 +1,533 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988--2022 Free Software Foundation, Inc. +Authored by Brian Fox and Chet Ramey. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Using History Interactively +@chapter Using History Interactively + +@ifclear BashFeatures +@defcodeindex bt +@end ifclear + +@ifset BashFeatures +This chapter describes how to use the @sc{gnu} History Library +interactively, from a user's standpoint. +It should be considered a user's guide. +For information on using the @sc{gnu} History Library in other programs, +see the @sc{gnu} Readline Library Manual. +@end ifset +@ifclear BashFeatures +This chapter describes how to use the @sc{gnu} History Library interactively, +from a user's standpoint. It should be considered a user's guide. For +information on using the @sc{gnu} History Library in your own programs, +@pxref{Programming with GNU History}. +@end ifclear + +@ifset BashFeatures +@menu +* Bash History Facilities:: How Bash lets you manipulate your command + history. +* Bash History Builtins:: The Bash builtin commands that manipulate + the command history. +* History Interaction:: What it feels like using History as a user. +@end menu +@end ifset +@ifclear BashFeatures +@menu +* History Interaction:: What it feels like using History as a user. +@end menu +@end ifclear + +@ifset BashFeatures +@node Bash History Facilities +@section Bash History Facilities +@cindex command history +@cindex history list + +When the @option{-o history} option to the @code{set} builtin +is enabled (@pxref{The Set Builtin}), +the shell provides access to the @dfn{command history}, +the list of commands previously typed. +The value of the @env{HISTSIZE} shell variable is used as the +number of commands to save in a history list. +The text of the last @env{$HISTSIZE} +commands (default 500) is saved. +The shell stores each command in the history list prior to +parameter and variable expansion +but after history expansion is performed, subject to the +values of the shell variables +@env{HISTIGNORE} and @env{HISTCONTROL}. + +When the shell starts up, the history is initialized from the +file named by the @env{HISTFILE} variable (default @file{~/.bash_history}). +The file named by the value of @env{HISTFILE} is truncated, if +necessary, to contain no more than the number of lines specified by +the value of the @env{HISTFILESIZE} variable. +When a shell with history enabled exits, the last +@env{$HISTSIZE} lines are copied from the history list to the file +named by @env{$HISTFILE}. +If the @code{histappend} shell option is set (@pxref{Bash Builtins}), +the lines are appended to the history file, +otherwise the history file is overwritten. +If @env{HISTFILE} +is unset, or if the history file is unwritable, the history is not saved. +After saving the history, the history file is truncated +to contain no more than @env{$HISTFILESIZE} lines. +If @env{HISTFILESIZE} is unset, or set to null, a non-numeric value, or +a numeric value less than zero, the history file is not truncated. + +If the @env{HISTTIMEFORMAT} is set, the time stamp information +associated with each history entry is written to the history file, +marked with the history comment character. +When the history file is read, lines beginning with the history +comment character followed immediately by a digit are interpreted +as timestamps for the following history entry. + +The builtin command @code{fc} may be used to list or edit and re-execute +a portion of the history list. +The @code{history} builtin may be used to display or modify the history +list and manipulate the history file. +When using command-line editing, search commands +are available in each editing mode that provide access to the +history list (@pxref{Commands For History}). + +The shell allows control over which commands are saved on the history +list. The @env{HISTCONTROL} and @env{HISTIGNORE} +variables may be set to cause the shell to save only a subset of the +commands entered. +The @code{cmdhist} +shell option, if enabled, causes the shell to attempt to save each +line of a multi-line command in the same history entry, adding +semicolons where necessary to preserve syntactic correctness. +The @code{lithist} +shell option causes the shell to save the command with embedded newlines +instead of semicolons. +The @code{shopt} builtin is used to set these options. +@xref{The Shopt Builtin}, for a description of @code{shopt}. + +@node Bash History Builtins +@section Bash History Builtins +@cindex history builtins + +Bash provides two builtin commands which manipulate the +history list and history file. + +@table @code + +@item fc +@btindex fc +@example +@code{fc [-e @var{ename}] [-lnr] [@var{first}] [@var{last}]} +@code{fc -s [@var{pat}=@var{rep}] [@var{command}]} +@end example + +The first form selects a range of commands from @var{first} to +@var{last} from the history list and displays or edits and re-executes +them. +Both @var{first} and +@var{last} may be specified as a string (to locate the most recent +command beginning with that string) or as a number (an index into the +history list, where a negative number is used as an offset from the +current command number). + +When listing, a @var{first} or @var{last} of 0 is equivalent to -1 +and -0 is equivalent to the current command (usually the @code{fc} +command); +otherwise 0 is equivalent to -1 and -0 is invalid. + +If @var{last} is not specified, it is set to +@var{first}. If @var{first} is not specified, it is set to the previous +command for editing and @minus{}16 for listing. If the @option{-l} flag is +given, the commands are listed on standard output. The @option{-n} flag +suppresses the command numbers when listing. The @option{-r} flag +reverses the order of the listing. Otherwise, the editor given by +@var{ename} is invoked on a file containing those commands. If +@var{ename} is not given, the value of the following variable expansion +is used: @code{$@{FCEDIT:-$@{EDITOR:-vi@}@}}. This says to use the +value of the @env{FCEDIT} variable if set, or the value of the +@env{EDITOR} variable if that is set, or @code{vi} if neither is set. +When editing is complete, the edited commands are echoed and executed. + +In the second form, @var{command} is re-executed after each instance +of @var{pat} in the selected command is replaced by @var{rep}. +@var{command} is interpreted the same as @var{first} above. + +A useful alias to use with the @code{fc} command is @code{r='fc -s'}, so +that typing @samp{r cc} runs the last command beginning with @code{cc} +and typing @samp{r} re-executes the last command (@pxref{Aliases}). + +@item history +@btindex history +@example +history [@var{n}] +history -c +history -d @var{offset} +history -d @var{start}-@var{end} +history [-anrw] [@var{filename}] +history -ps @var{arg} +@end example + +With no options, display the history list with line numbers. +Lines prefixed with a @samp{*} have been modified. +An argument of @var{n} lists only the last @var{n} lines. +If the shell variable @env{HISTTIMEFORMAT} is set and not null, +it is used as a format string for @var{strftime} to display +the time stamp associated with each displayed history entry. +No intervening blank is printed between the formatted time stamp +and the history line. + +Options, if supplied, have the following meanings: + +@table @code +@item -c +Clear the history list. This may be combined +with the other options to replace the history list completely. + +@item -d @var{offset} +Delete the history entry at position @var{offset}. +If @var{offset} is positive, it should be specified as it appears when +the history is displayed. +If @var{offset} is negative, it is interpreted as relative to one greater +than the last history position, so negative indices count back from the +end of the history, and an index of @samp{-1} refers to the current +@code{history -d} command. + +@item -d @var{start}-@var{end} +Delete the range of history entries between positions @var{start} and +@var{end}, inclusive. +Positive and negative values for @var{start} and @var{end} +are interpreted as described above. + +@item -a +Append the new history lines to the history file. +These are history lines entered since the beginning of the current +Bash session, but not already appended to the history file. + +@item -n +Append the history lines not already read from the history file +to the current history list. These are lines appended to the history +file since the beginning of the current Bash session. + +@item -r +Read the history file and append its contents to +the history list. + +@item -w +Write out the current history list to the history file. + +@item -p +Perform history substitution on the @var{arg}s and display the result +on the standard output, without storing the results in the history list. + +@item -s +The @var{arg}s are added to the end of +the history list as a single entry. + +@end table + +If a @var{filename} argument is supplied +when any of the @option{-w}, @option{-r}, @option{-a}, or @option{-n} options +is used, Bash uses @var{filename} as the history file. +If not, then the value of the @env{HISTFILE} variable is used. + +The return value is 0 unless an invalid option is encountered, an +error occurs while reading or writing the history file, an invalid +@var{offset} or range is supplied as an argument to @option{-d}, or the +history expansion supplied as an argument to @option{-p} fails. + +@end table +@end ifset + +@node History Interaction +@section History Expansion +@cindex history expansion + +The History library provides a history expansion feature that is similar +to the history expansion provided by @code{csh}. This section +describes the syntax used to manipulate the history information. + +History expansions introduce words from the history list into +the input stream, making it easy to repeat commands, insert the +arguments to a previous command into the current input line, or +fix errors in previous commands quickly. + +@ifset BashFeatures +History expansion is performed immediately after a complete line +is read, before the shell breaks it into words, and is performed +on each line individually. Bash attempts to inform the history +expansion functions about quoting still in effect from previous lines. +@end ifset + +History expansion takes place in two parts. The first is to determine +which line from the history list should be used during substitution. +The second is to select portions of that line for inclusion into the +current one. The line selected from the history is called the +@dfn{event}, and the portions of that line that are acted upon are +called @dfn{words}. Various @dfn{modifiers} are available to manipulate +the selected words. The line is broken into words in the same fashion +that Bash does, so that several words +surrounded by quotes are considered one word. +History expansions are introduced by the appearance of the +history expansion character, which is @samp{!} by default. + +History expansion implements shell-like quoting conventions: +a backslash can be used to remove the special handling for the next character; +single quotes enclose verbatim sequences of characters, and can be used to +inhibit history expansion; +and characters enclosed within double quotes may be subject to history +expansion, since backslash can escape the history expansion character, +but single quotes may not, since they are not treated specially within +double quotes. + +@ifset BashFeatures +When using the shell, only @samp{\} and @samp{'} may be used to escape the +history expansion character, but the history expansion character is +also treated as quoted if it immediately precedes the closing double quote +in a double-quoted string. +@end ifset + +@ifset BashFeatures +Several shell options settable with the @code{shopt} +builtin (@pxref{The Shopt Builtin}) may be used to tailor +the behavior of history expansion. If the +@code{histverify} shell option is enabled, and Readline +is being used, history substitutions are not immediately passed to +the shell parser. +Instead, the expanded line is reloaded into the Readline +editing buffer for further modification. +If Readline is being used, and the @code{histreedit} +shell option is enabled, a failed history expansion will be +reloaded into the Readline editing buffer for correction. +The @option{-p} option to the @code{history} builtin command +may be used to see what a history expansion will do before using it. +The @option{-s} option to the @code{history} builtin may be used to +add commands to the end of the history list without actually executing +them, so that they are available for subsequent recall. +This is most useful in conjunction with Readline. + +The shell allows control of the various characters used by the +history expansion mechanism with the @code{histchars} variable, +as explained above (@pxref{Bash Variables}). The shell uses +the history comment character to mark history timestamps when +writing the history file. +@end ifset + +@menu +* Event Designators:: How to specify which history line to use. +* Word Designators:: Specifying which words are of interest. +* Modifiers:: Modifying the results of substitution. +@end menu + +@node Event Designators +@subsection Event Designators +@cindex event designators + +An event designator is a reference to a command line entry in the +history list. +Unless the reference is absolute, events are relative to the current +position in the history list. +@cindex history events + +@table @asis + +@item @code{!} +@ifset BashFeatures +Start a history substitution, except when followed by a space, tab, +the end of the line, @samp{=} or @samp{(} (when the +@code{extglob} shell option is enabled using the @code{shopt} builtin). +@end ifset +@ifclear BashFeatures +Start a history substitution, except when followed by a space, tab, +the end of the line, or @samp{=}. +@end ifclear + +@item @code{!@var{n}} +Refer to command line @var{n}. + +@item @code{!-@var{n}} +Refer to the command @var{n} lines back. + +@item @code{!!} +Refer to the previous command. This is a synonym for @samp{!-1}. + +@item @code{!@var{string}} +Refer to the most recent command +preceding the current position in the history list +starting with @var{string}. + +@item @code{!?@var{string}[?]} +Refer to the most recent command +preceding the current position in the history list +containing @var{string}. +The trailing +@samp{?} may be omitted if the @var{string} is followed immediately by +a newline. +If @var{string} is missing, the string from the most recent search is used; +it is an error if there is no previous search string. + +@item @code{^@var{string1}^@var{string2}^} +Quick Substitution. Repeat the last command, replacing @var{string1} +with @var{string2}. Equivalent to +@code{!!:s^@var{string1}^@var{string2}^}. + +@item @code{!#} +The entire command line typed so far. + +@end table + +@node Word Designators +@subsection Word Designators + +Word designators are used to select desired words from the event. +A @samp{:} separates the event specification from the word designator. It +may be omitted if the word designator begins with a @samp{^}, @samp{$}, +@samp{*}, @samp{-}, or @samp{%}. Words are numbered from the beginning +of the line, with the first word being denoted by 0 (zero). Words are +inserted into the current line separated by single spaces. + +@need 0.75 +For example, + +@table @code +@item !! +designates the preceding command. When you type this, the preceding +command is repeated in toto. + +@item !!:$ +designates the last argument of the preceding command. This may be +shortened to @code{!$}. + +@item !fi:2 +designates the second argument of the most recent command starting with +the letters @code{fi}. +@end table + +@need 0.75 +Here are the word designators: + +@table @code + +@item 0 (zero) +The @code{0}th word. For many applications, this is the command word. + +@item @var{n} +The @var{n}th word. + +@item ^ +The first argument; that is, word 1. + +@item $ +The last argument. + +@item % +The first word matched by the most recent @samp{?@var{string}?} search, +if the search string begins with a character that is part of a word. + +@item @var{x}-@var{y} +A range of words; @samp{-@var{y}} abbreviates @samp{0-@var{y}}. + +@item * +All of the words, except the @code{0}th. This is a synonym for @samp{1-$}. +It is not an error to use @samp{*} if there is just one word in the event; +the empty string is returned in that case. + +@item @var{x}* +Abbreviates @samp{@var{x}-$} + +@item @var{x}- +Abbreviates @samp{@var{x}-$} like @samp{@var{x}*}, but omits the last word. +If @samp{x} is missing, it defaults to 0. + +@end table + +If a word designator is supplied without an event specification, the +previous command is used as the event. + +@node Modifiers +@subsection Modifiers + +After the optional word designator, you can add a sequence of one or more +of the following modifiers, each preceded by a @samp{:}. +These modify, or edit, the word or words selected from the history event. + +@table @code + +@item h +Remove a trailing pathname component, leaving only the head. + +@item t +Remove all leading pathname components, leaving the tail. + +@item r +Remove a trailing suffix of the form @samp{.@var{suffix}}, leaving +the basename. + +@item e +Remove all but the trailing suffix. + +@item p +Print the new command but do not execute it. + +@ifset BashFeatures +@item q +Quote the substituted words, escaping further substitutions. + +@item x +Quote the substituted words as with @samp{q}, +but break into words at spaces, tabs, and newlines. +The @samp{q} and @samp{x} modifiers are mutually exclusive; the last one +supplied is used. +@end ifset + +@item s/@var{old}/@var{new}/ +Substitute @var{new} for the first occurrence of @var{old} in the +event line. +Any character may be used as the delimiter in place of @samp{/}. +The delimiter may be quoted in @var{old} and @var{new} +with a single backslash. If @samp{&} appears in @var{new}, +it is replaced by @var{old}. A single backslash will quote +the @samp{&}. +If @var{old} is null, it is set to the last @var{old} +substituted, or, if no previous history substitutions took place, +the last @var{string} +in a !?@var{string}@code{[?]} +search. +If @var{new} is null, each matching @var{old} is deleted. +The final delimiter is optional if it is the last +character on the input line. + +@item & +Repeat the previous substitution. + +@item g +@itemx a +Cause changes to be applied over the entire event line. Used in +conjunction with @samp{s}, as in @code{gs/@var{old}/@var{new}/}, +or with @samp{&}. + +@item G +Apply the following @samp{s} or @samp{&} modifier once to each word +in the event. + +@end table diff --git a/lib/readline/doc/rlman.texi b/lib/readline/doc/rlman.texi new file mode 100644 index 0000000..d046b87 --- /dev/null +++ b/lib/readline/doc/rlman.texi @@ -0,0 +1,84 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename readline.info +@settitle GNU Readline Library +@include version.texi + +@comment %**end of header (This is for running Texinfo on a region.) +@synindex vr fn + +@copying +This manual describes the GNU Readline Library +(version @value{VERSION}, @value{UPDATED}), a library which aids in the +consistency of user interface across discrete programs which provide +a command line interface. + +Copyright @copyright{} 1988--2022 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the section entitled +``GNU Free Documentation License''. + +@end quotation +@end copying + +@dircategory Libraries +@direntry +* Readline: (readline). The GNU readline library API. +@end direntry + +@titlepage +@title GNU Readline Library +@subtitle Edition @value{EDITION}, for @code{Readline Library} Version @value{VERSION}. +@subtitle @value{UPDATED-MONTH} +@author Chet Ramey, Case Western Reserve University +@author Brian Fox, Free Software Foundation + +@page +@vskip 0pt plus 1filll +@insertcopying + +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU Readline Library + +This document describes the GNU Readline Library, a utility which aids +in the consistency of user interface across discrete programs which +provide a command line interface. +The Readline home page is @url{http://www.gnu.org/software/readline/}. + +@menu +* Command Line Editing:: GNU Readline User's Manual. +* Programming with GNU Readline:: GNU Readline Programmer's Manual. +* GNU Free Documentation License:: License for copying this manual. +* Concept Index:: Index of concepts described in this manual. +* Function and Variable Index:: Index of externally visible functions + and variables. +@end menu +@end ifnottex + +@include rluser.texi +@include rltech.texi + +@node GNU Free Documentation License +@appendix GNU Free Documentation License + +@include fdl.texi + +@node Concept Index +@unnumbered Concept Index +@printindex cp + +@node Function and Variable Index +@unnumbered Function and Variable Index +@printindex fn + +@bye diff --git a/lib/readline/doc/rltech.texi b/lib/readline/doc/rltech.texi new file mode 100644 index 0000000..a93f77d --- /dev/null +++ b/lib/readline/doc/rltech.texi @@ -0,0 +1,2815 @@ +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rltech.info +@comment %**end of header (This is for running Texinfo on a region.) + +@ifinfo +This document describes the GNU Readline Library, a utility for aiding +in the consistency of user interface across discrete programs that need +to provide a command line interface. + +Copyright (C) 1988--2022 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +pare preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end ifinfo + +@node Programming with GNU Readline +@chapter Programming with GNU Readline + +This chapter describes the interface between the @sc{gnu} Readline Library and +other programs. If you are a programmer, and you wish to include the +features found in @sc{gnu} Readline +such as completion, line editing, and interactive history manipulation +in your own programs, this section is for you. + +@menu +* Basic Behavior:: Using the default behavior of Readline. +* Custom Functions:: Adding your own functions to Readline. +* Readline Variables:: Variables accessible to custom + functions. +* Readline Convenience Functions:: Functions which Readline supplies to + aid in writing your own custom + functions. +* Readline Signal Handling:: How Readline behaves when it receives signals. +* Custom Completers:: Supplanting or supplementing Readline's + completion functions. +@end menu + +@node Basic Behavior +@section Basic Behavior + +Many programs provide a command line interface, such as @code{mail}, +@code{ftp}, and @code{sh}. For such programs, the default behaviour of +Readline is sufficient. This section describes how to use Readline in +the simplest way possible, perhaps to replace calls in your code to +@code{gets()} or @code{fgets()}. + +@findex readline +@cindex readline, function + +The function @code{readline()} prints a prompt @var{prompt} +and then reads and returns a single line of text from the user. +If @var{prompt} is @code{NULL} or the empty string, no prompt is displayed. +The line @code{readline} returns is allocated with @code{malloc()}; +the caller should @code{free()} the line when it has finished with it. +The declaration for @code{readline} in ANSI C is + +@example +@code{char *readline (const char *@var{prompt});} +@end example + +@noindent +So, one might say +@example +@code{char *line = readline ("Enter a line: ");} +@end example +@noindent +in order to read a line of text from the user. +The line returned has the final newline removed, so only the +text remains. + +If @code{readline} encounters an @code{EOF} while reading the line, and the +line is empty at that point, then @code{(char *)NULL} is returned. +Otherwise, the line is ended just as if a newline had been typed. + +Readline performs some expansion on the @var{prompt} before it is +displayed on the screen. See the description of @code{rl_expand_prompt} +(@pxref{Redisplay}) for additional details, especially if @var{prompt} +will contain characters that do not consume physical screen space when +displayed. + +If you want the user to be able to get at the line later, (with +@key{C-p} for example), you must call @code{add_history()} to save the +line away in a @dfn{history} list of such lines. + +@example +@code{add_history (line)}; +@end example + +@noindent +For full details on the GNU History Library, see the associated manual. + +It is preferable to avoid saving empty lines on the history list, since +users rarely have a burning need to reuse a blank line. Here is +a function which usefully replaces the standard @code{gets()} library +function, and has the advantage of no static buffer to overflow: + +@example +/* 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. */ +char * +rl_gets () +@{ + /* If the buffer has already been allocated, + return the memory to the free pool. */ + if (line_read) + @{ + free (line_read); + line_read = (char *)NULL; + @} + + /* Get a line from the user. */ + line_read = readline (""); + + /* If the line has any text in it, + save it on the history. */ + if (line_read && *line_read) + add_history (line_read); + + return (line_read); +@} +@end example + +This function gives the user the default behaviour of @key{TAB} +completion: completion on file names. If you do not want Readline to +complete on filenames, you can change the binding of the @key{TAB} key +with @code{rl_bind_key()}. + +@example +@code{int rl_bind_key (int @var{key}, rl_command_func_t *@var{function});} +@end example + +@code{rl_bind_key()} takes two arguments: @var{key} is the character that +you want to bind, and @var{function} is the address of the function to +call when @var{key} is pressed. Binding @key{TAB} to @code{rl_insert()} +makes @key{TAB} insert itself. +@code{rl_bind_key()} returns non-zero if @var{key} is not a valid +ASCII character code (between 0 and 255). + +Thus, to disable the default @key{TAB} behavior, the following suffices: +@example +@code{rl_bind_key ('\t', rl_insert);} +@end example + +This code should be executed once at the start of your program; you +might write a function called @code{initialize_readline()} which +performs this and other desired initializations, such as installing +custom completers (@pxref{Custom Completers}). + +@node Custom Functions +@section Custom Functions + +Readline provides many functions for manipulating the text of +the line, but it isn't possible to anticipate the needs of all +programs. This section describes the various functions and variables +defined within the Readline library which allow a user program to add +customized functionality to Readline. + +Before declaring any functions that customize Readline's behavior, or +using any functionality Readline provides in other code, an +application writer should include the file @code{<readline/readline.h>} +in any file that uses Readline's features. Since some of the definitions +in @code{readline.h} use the @code{stdio} library, the file +@code{<stdio.h>} should be included before @code{readline.h}. + +@code{readline.h} defines a C preprocessor variable that should +be treated as an integer, @code{RL_READLINE_VERSION}, which may +be used to conditionally compile application code depending on +the installed Readline version. The value is a hexadecimal +encoding of the major and minor version numbers of the library, +of the form 0x@var{MMmm}. @var{MM} is the two-digit major +version number; @var{mm} is the two-digit minor version number. +For Readline 4.2, for example, the value of +@code{RL_READLINE_VERSION} would be @code{0x0402}. + +@menu +* Readline Typedefs:: C declarations to make code readable. +* Function Writing:: Variables and calling conventions. +@end menu + +@node Readline Typedefs +@subsection Readline Typedefs + +For readability, we declare a number of new object types, all pointers +to functions. + +The reason for declaring these new types is to make it easier to write +code describing pointers to C functions with appropriately prototyped +arguments and return values. + +For instance, say we want to declare a variable @var{func} as a pointer +to a function which takes two @code{int} arguments and returns an +@code{int} (this is the type of all of the Readline bindable functions). +Instead of the classic C declaration + +@code{int (*func)();} + +@noindent +or the ANSI-C style declaration + +@code{int (*func)(int, int);} + +@noindent +we may write + +@code{rl_command_func_t *func;} + +The full list of function pointer types available is + +@table @code +@item typedef int rl_command_func_t (int, int); + +@item typedef char *rl_compentry_func_t (const char *, int); + +@item typedef char **rl_completion_func_t (const char *, int, int); + +@item typedef char *rl_quote_func_t (char *, int, char *); + +@item typedef char *rl_dequote_func_t (char *, int); + +@item typedef int rl_compignore_func_t (char **); + +@item typedef void rl_compdisp_func_t (char **, int, int); + +@item typedef int rl_hook_func_t (void); + +@item typedef int rl_getc_func_t (FILE *); + +@item typedef int rl_linebuf_func_t (char *, int); + +@item typedef int rl_intfunc_t (int); +@item #define rl_ivoidfunc_t rl_hook_func_t +@item typedef int rl_icpfunc_t (char *); +@item typedef int rl_icppfunc_t (char **); + +@item typedef void rl_voidfunc_t (void); +@item typedef void rl_vintfunc_t (int); +@item typedef void rl_vcpfunc_t (char *); +@item typedef void rl_vcppfunc_t (char **); + +@end table + +@node Function Writing +@subsection Writing a New Function + +In order to write new functions for Readline, you need to know the +calling conventions for keyboard-invoked functions, and the names of the +variables that describe the current state of the line read so far. + +The calling sequence for a command @code{foo} looks like + +@example +@code{int foo (int count, int key)} +@end example + +@noindent +where @var{count} is the numeric argument (or 1 if defaulted) and +@var{key} is the key that invoked this function. + +It is completely up to the function as to what should be done with the +numeric argument. Some functions use it as a repeat count, some +as a flag, and others to choose alternate behavior (refreshing the current +line as opposed to refreshing the screen, for example). Some choose to +ignore it. In general, if a +function uses the numeric argument as a repeat count, it should be able +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 value greater than zero if some error occurs. +This is the convention obeyed by all of the builtin Readline bindable +command functions. + +@node Readline Variables +@section Readline Variables + +These variables are available to function writers. + +@deftypevar {char *} rl_line_buffer +This is the line gathered so far. You are welcome to modify the +contents of the line, but see @ref{Allowing Undoing}. The +function @code{rl_extend_line_buffer} is available to increase +the memory allocated to @code{rl_line_buffer}. +@end deftypevar + +@deftypevar int rl_point +The offset of the current cursor position in @code{rl_line_buffer} +(the @emph{point}). +@end deftypevar + +@deftypevar int rl_end +The number of characters present in @code{rl_line_buffer}. When +@code{rl_point} is at the end of the line, @code{rl_point} and +@code{rl_end} are equal. +@end deftypevar + +@deftypevar int rl_mark +The @var{mark} (saved position) in the current line. If set, the mark +and point define a @emph{region}. +@end deftypevar + +@deftypevar int rl_done +Setting this to a non-zero value causes Readline to return the current +line immediately. +Readline will set this variable when it has read a key sequence bound +to @code{accept-line} and is about to return the line to the caller. +@end deftypevar + +@deftypevar int rl_eof_found +Readline will set this variable when it has read an EOF character (e.g., the +stty @samp{EOF} character) on an empty line or encountered a read error and +is about to return a NULL line to the caller. +@end deftypevar + +@deftypevar int rl_num_chars_to_read +Setting this to a positive value before calling @code{readline()} causes +Readline to return after accepting that many characters, rather +than reading up to a character bound to @code{accept-line}. +@end deftypevar + +@deftypevar int rl_pending_input +Setting this to a value makes it the next keystroke read. This is a +way to stuff a single character into the input stream. +@end deftypevar + +@deftypevar int rl_dispatching +Set to a non-zero value if a function is being called from a key binding; +zero otherwise. Application functions can test this to discover whether +they were called directly or by Readline's dispatching mechanism. +@end deftypevar + +@deftypevar int rl_erase_empty_line +Setting this to a non-zero value causes Readline to completely erase +the current line, including any prompt, any time a newline is typed as +the only character on an otherwise-empty line. The cursor is moved to +the beginning of the newly-blank line. +@end deftypevar + +@deftypevar {char *} rl_prompt +The prompt Readline uses. This is set from the argument to +@code{readline()}, and should not be assigned to directly. +The @code{rl_set_prompt()} function (@pxref{Redisplay}) may +be used to modify the prompt string after calling @code{readline()}. +@end deftypevar + +@deftypevar {char *} rl_display_prompt +The string displayed as the prompt. This is usually identical to +@var{rl_prompt}, but may be changed temporarily by functions that +use the prompt string as a message area, such as incremental search. +@end deftypevar + +@deftypevar int rl_already_prompted +If an application wishes to display the prompt itself, rather than have +Readline do it the first time @code{readline()} is called, it should set +this variable to a non-zero value after displaying the prompt. +The prompt must also be passed as the argument to @code{readline()} so +the redisplay functions can update the display properly. +The calling application is responsible for managing the value; Readline +never sets it. +@end deftypevar + +@deftypevar {const char *} rl_library_version +The version number of this revision of the library. +@end deftypevar + +@deftypevar int rl_readline_version +An integer encoding the current version of the library. The encoding is +of the form 0x@var{MMmm}, where @var{MM} is the two-digit major version +number, and @var{mm} is the two-digit minor version number. +For example, for Readline-4.2, @code{rl_readline_version} would have the +value 0x0402. +@end deftypevar + +@deftypevar {int} rl_gnu_readline_p +Always set to 1, denoting that this is @sc{gnu} Readline rather than some +emulation. +@end deftypevar + +@deftypevar {const char *} rl_terminal_name +The terminal type, used for initialization. If not set by the application, +Readline sets this to the value of the @env{TERM} environment variable +the first time it is called. +@end deftypevar + +@deftypevar {const char *} rl_readline_name +This variable is set to a unique name by each application using Readline. +The value allows conditional parsing of the inputrc file +(@pxref{Conditional Init Constructs}). +@end deftypevar + +@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 int rl_prefer_env_winsize +If non-zero, Readline gives values found in the @env{LINES} and +@env{COLUMNS} environment variables greater precedence than values fetched +from the kernel when computing the screen dimensions. +@end deftypevar + +@deftypevar {rl_command_func_t *} rl_last_func +The address of the last command function Readline executed. May be used to +test whether or not a function is being executed twice in succession, for +example. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_startup_hook +If non-zero, this is the address of a function to call just +before @code{readline} prints the first prompt. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_pre_input_hook +If non-zero, this is the address of a function to call after +the first prompt has been printed and just before @code{readline} +starts reading input characters. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_event_hook +If non-zero, this is the address of a function to call periodically +when Readline is waiting for terminal input. +By default, this will be called at most ten times a second if there +is no keyboard input. +@end deftypevar + +@deftypevar {rl_getc_func_t *} rl_getc_function +If non-zero, Readline will call indirectly through this pointer +to get a character from the input stream. By default, it is set to +@code{rl_getc}, the default Readline character input function +(@pxref{Character Input}). +In general, an application that sets @var{rl_getc_function} should consider +setting @var{rl_input_available_hook} as well. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_signal_event_hook +If non-zero, this is the address of a function to call if a read system +call is interrupted when Readline is reading terminal input. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_timeout_event_hook +If non-zero, this is the address of a function to call if Readline times +out while reading input. +@end deftypevar + +@deftypevar {rl_hook_func_t *} rl_input_available_hook +If non-zero, Readline will use this function's return value when it needs +to determine whether or not there is available input on the current input +source. +The default hook checks @code{rl_instream}; if an application is using a +different input source, it should set the hook appropriately. +Readline queries for available input when implementing intra-key-sequence +timeouts during input and incremental searches. +This may use an application-specific timeout before returning a value; +Readline uses the value passed to @code{rl_set_keyboard_input_timeout()} +or the value of the user-settable @var{keyseq-timeout} variable. +This is designed for use by applications using Readline's callback interface +(@pxref{Alternate Interface}), which may not use the traditional +@code{read(2)} and file descriptor interface, or other applications using +a different input mechanism. +If an application uses an input mechanism or hook that can potentially exceed +the value of @var{keyseq-timeout}, it should increase the timeout or set +this hook appropriately even when not using the callback interface. +In general, an application that sets @var{rl_getc_function} should consider +setting @var{rl_input_available_hook} as well. +@end deftypevar + +@deftypevar {rl_voidfunc_t *} rl_redisplay_function +If non-zero, Readline will call indirectly through this pointer +to update the display with the current contents of the editing buffer. +By default, it is set to @code{rl_redisplay}, the default Readline +redisplay function (@pxref{Redisplay}). +@end deftypevar + +@deftypevar {rl_vintfunc_t *} rl_prep_term_function +If non-zero, Readline will call indirectly through this pointer +to initialize the terminal. The function takes a single argument, an +@code{int} flag that says whether or not to use eight-bit characters. +By default, this is set to @code{rl_prep_terminal} +(@pxref{Terminal Management}). +@end deftypevar + +@deftypevar {rl_voidfunc_t *} rl_deprep_term_function +If non-zero, Readline will call indirectly through this pointer +to reset the terminal. This function should undo the effects of +@code{rl_prep_term_function}. +By default, this is set to @code{rl_deprep_terminal} +(@pxref{Terminal Management}). +@end deftypevar + +@deftypevar {Keymap} rl_executing_keymap +This variable is set to the keymap (@pxref{Keymaps}) in which the +currently executing Readline function was found. +@end deftypevar + +@deftypevar {Keymap} rl_binding_keymap +This variable is set to the keymap (@pxref{Keymaps}) in which the +last key binding occurred. +@end deftypevar + +@deftypevar {char *} rl_executing_macro +This variable is set to the text of any currently-executing macro. +@end deftypevar + +@deftypevar int rl_executing_key +The key that caused the dispatch to the currently-executing Readline function. +@end deftypevar + +@deftypevar {char *} rl_executing_keyseq +The full key sequence that caused the dispatch to the currently-executing +Readline function. +@end deftypevar + +@deftypevar int rl_key_sequence_length +The number of characters in @var{rl_executing_keyseq}. +@end deftypevar + +@deftypevar {int} rl_readline_state +A variable with bit values that encapsulate the current Readline state. +A bit is set with the @code{RL_SETSTATE} macro, and unset with the +@code{RL_UNSETSTATE} macro. Use the @code{RL_ISSTATE} macro to test +whether a particular state bit is set. Current state bits include: + +@table @code +@item RL_STATE_NONE +Readline has not yet been called, nor has it begun to initialize. +@item RL_STATE_INITIALIZING +Readline is initializing its internal data structures. +@item RL_STATE_INITIALIZED +Readline has completed its initialization. +@item RL_STATE_TERMPREPPED +Readline has modified the terminal modes to do its own input and redisplay. +@item RL_STATE_READCMD +Readline is reading a command from the keyboard. +@item RL_STATE_METANEXT +Readline is reading more input after reading the meta-prefix character. +@item RL_STATE_DISPATCHING +Readline is dispatching to a command. +@item RL_STATE_MOREINPUT +Readline is reading more input while executing an editing command. +@item RL_STATE_ISEARCH +Readline is performing an incremental history search. +@item RL_STATE_NSEARCH +Readline is performing a non-incremental history search. +@item RL_STATE_SEARCH +Readline is searching backward or forward through the history for a string. +@item RL_STATE_NUMERICARG +Readline is reading a numeric argument. +@item RL_STATE_MACROINPUT +Readline is currently getting its input from a previously-defined keyboard +macro. +@item RL_STATE_MACRODEF +Readline is currently reading characters defining a keyboard macro. +@item RL_STATE_OVERWRITE +Readline is in overwrite mode. +@item RL_STATE_COMPLETING +Readline is performing word completion. +@item RL_STATE_SIGHANDLER +Readline is currently executing the readline signal handler. +@item RL_STATE_UNDOING +Readline is performing an undo. +@item RL_STATE_INPUTPENDING +Readline has input pending due to a call to @code{rl_execute_next()}. +@item RL_STATE_TTYCSAVED +Readline has saved the values of the terminal's special characters. +@item RL_STATE_CALLBACK +Readline is currently using the alternate (callback) interface +(@pxref{Alternate Interface}). +@item RL_STATE_VIMOTION +Readline is reading the argument to a vi-mode "motion" command. +@item RL_STATE_MULTIKEY +Readline is reading a multiple-keystroke command. +@item RL_STATE_VICMDONCE +Readline has entered vi command (movement) mode at least one time during +the current call to @code{readline()}. +@item RL_STATE_DONE +Readline has read a key sequence bound to @code{accept-line} +and is about to return the line to the caller. +@item RL_STATE_TIMEOUT +Readline has timed out (it did not receive a line or specified number of +characters before the timeout duration specified by @code{rl_set_timeout} +elapsed) and is returning that status to the caller. +@item RL_STATE_EOF +Readline has read an EOF character (e.g., the stty @samp{EOF} character) +or encountered a read error and is about to return a NULL line to the caller. +@end table + +@end deftypevar + +@deftypevar {int} rl_explicit_arg +Set to a non-zero value if an explicit numeric argument was specified by +the user. Only valid in a bindable command function. +@end deftypevar + +@deftypevar {int} rl_numeric_arg +Set to the value of any numeric argument explicitly specified by the user +before executing the current Readline function. Only valid in a bindable +command function. +@end deftypevar + +@deftypevar {int} rl_editing_mode +Set to a value denoting Readline's current editing mode. A value of +@var{1} means Readline is currently in emacs mode; @var{0} +means that vi mode is active. +@end deftypevar + + +@node Readline Convenience Functions +@section Readline Convenience Functions + +@menu +* Function Naming:: How to give a function you write a name. +* Keymaps:: Making keymaps. +* Binding Keys:: Changing Keymaps. +* Associating Function Names and Bindings:: Translate function names to + key sequences. +* Allowing Undoing:: How to make your functions undoable. +* Redisplay:: Functions to control line display. +* Modifying Text:: Functions to modify @code{rl_line_buffer}. +* Character Input:: Functions to read keyboard input. +* Terminal Management:: Functions to manage terminal settings. +* Utility Functions:: Generally useful functions and hooks. +* Miscellaneous Functions:: Functions that don't fall into any category. +* Alternate Interface:: Using Readline in a `callback' fashion. +* A Readline Example:: An example Readline function. +* Alternate Interface Example:: An example program using the alternate interface. +@end menu + +@node Function Naming +@subsection Naming a Function + +The user can dynamically change the bindings of keys while using +Readline. This is done by representing the function with a descriptive +name. The user is able to type the descriptive name when referring to +the function. Thus, in an init file, one might find + +@example +Meta-Rubout: backward-kill-word +@end example + +This binds the keystroke @key{Meta-Rubout} to the function +@emph{descriptively} named @code{backward-kill-word}. You, as the +programmer, should bind the functions you write to descriptive names as +well. Readline provides a function for doing that: + +@deftypefun int rl_add_defun (const char *name, rl_command_func_t *function, int key) +Add @var{name} to the list of named functions. Make @var{function} be +the function that gets called. If @var{key} is not -1, then bind it to +@var{function} using @code{rl_bind_key()}. +@end deftypefun + +Using this function alone is sufficient for most applications. +It is the recommended way to add a few functions to the default +functions that Readline has built in. +If you need to do something other than adding a function to Readline, +you may need to use the underlying functions described below. + +@node Keymaps +@subsection Selecting a Keymap + +Key bindings take place on a @dfn{keymap}. The keymap is the +association between the keys that the user types and the functions that +get run. You can make your own keymaps, copy existing keymaps, and tell +Readline which keymap to use. + +@deftypefun Keymap rl_make_bare_keymap (void) +Returns a new, empty keymap. The space for the keymap is allocated with +@code{malloc()}; the caller should free it by calling +@code{rl_free_keymap()} when done. +@end deftypefun + +@deftypefun Keymap rl_copy_keymap (Keymap map) +Return a new keymap which is a copy of @var{map}. +@end deftypefun + +@deftypefun Keymap rl_make_keymap (void) +Return a new keymap with the printing characters bound to rl_insert, +the lowercase Meta characters bound to run their equivalents, and +the Meta digits bound to produce numeric arguments. +@end deftypefun + +@deftypefun void rl_discard_keymap (Keymap keymap) +Free the storage associated with the data in @var{keymap}. +The caller should free @var{keymap}. +@end deftypefun + +@deftypefun void rl_free_keymap (Keymap keymap) +Free all storage associated with @var{keymap}. This calls +@code{rl_discard_keymap} to free subordindate keymaps and macros. +@end deftypefun + +@deftypefun int rl_empty_keymap (Keymap keymap) +Return non-zero if there are no keys bound to functions in @var{keymap}; +zero if there are any keys bound. +@end deftypefun + +Readline has several internal keymaps. These functions allow you to +change which keymap is active. + +@deftypefun Keymap rl_get_keymap (void) +Returns the currently active keymap. +@end deftypefun + +@deftypefun void rl_set_keymap (Keymap keymap) +Makes @var{keymap} the currently active keymap. +@end deftypefun + +@deftypefun Keymap rl_get_keymap_by_name (const char *name) +Return the keymap matching @var{name}. @var{name} is one which would +be supplied in a @code{set keymap} inputrc line (@pxref{Readline Init File}). +@end deftypefun + +@deftypefun {char *} rl_get_keymap_name (Keymap keymap) +Return the name matching @var{keymap}. @var{name} is one which would +be supplied in a @code{set keymap} inputrc line (@pxref{Readline Init File}). +@end deftypefun + +@deftypefun int rl_set_keymap_name (const char *name, Keymap keymap) +Set the name of @var{keymap}. This name will then be "registered" and +available for use in a @code{set keymap} inputrc directive +@pxref{Readline Init File}). +The @var{name} may not be one of Readline's builtin keymap names; +you may not add a different name for one of Readline's builtin keymaps. +You may replace the name associated with a given keymap by calling this +function more than once with the same @var{keymap} argument. +You may associate a registered @var{name} with a new keymap by calling this +function more than once with the same @var{name} argument. +There is no way to remove a named keymap once the name has been +registered. +Readline will make a copy of @var{name}. +The return value is greater than zero unless @var{name} is one of +Readline's builtin keymap names or @var{keymap} is one of Readline's +builtin keymaps. +@end deftypefun + +@node Binding Keys +@subsection Binding Keys + +Key sequences are associate with functions through the keymap. +Readline has several internal keymaps: @code{emacs_standard_keymap}, +@code{emacs_meta_keymap}, @code{emacs_ctlx_keymap}, +@code{vi_movement_keymap}, and @code{vi_insertion_keymap}. +@code{emacs_standard_keymap} is the default, and the examples in +this manual assume that. + +Since @code{readline()} installs a set of default key bindings the first +time it is called, there is always the danger that a custom binding +installed before the first call to @code{readline()} will be overridden. +An alternate mechanism is to install custom key bindings in an +initialization function assigned to the @code{rl_startup_hook} variable +(@pxref{Readline Variables}). + +These functions manage key bindings. + +@deftypefun int rl_bind_key (int key, rl_command_func_t *function) +Binds @var{key} to @var{function} in the currently active keymap. +Returns non-zero in the case of an invalid @var{key}. +@end deftypefun + +@deftypefun int rl_bind_key_in_map (int key, rl_command_func_t *function, Keymap map) +Bind @var{key} to @var{function} in @var{map}. +Returns non-zero in the case of an invalid @var{key}. +@end deftypefun + +@deftypefun int rl_bind_key_if_unbound (int key, rl_command_func_t *function) +Binds @var{key} to @var{function} if it is not already bound in the +currently active keymap. +Returns non-zero in the case of an invalid @var{key} or if @var{key} is +already bound. +@end deftypefun + +@deftypefun int rl_bind_key_if_unbound_in_map (int key, rl_command_func_t *function, Keymap map) +Binds @var{key} to @var{function} if it is not already bound in @var{map}. +Returns non-zero in the case of an invalid @var{key} or if @var{key} is +already bound. +@end deftypefun + +@deftypefun int rl_unbind_key (int key) +Bind @var{key} to the null function in the currently active keymap. +Returns non-zero in case of error. +@end deftypefun + +@deftypefun int rl_unbind_key_in_map (int key, Keymap map) +Bind @var{key} to the null function in @var{map}. +Returns non-zero in case of error. +@end deftypefun + +@deftypefun int rl_unbind_function_in_map (rl_command_func_t *function, Keymap map) +Unbind all keys that execute @var{function} in @var{map}. +@end deftypefun + +@deftypefun int rl_unbind_command_in_map (const char *command, Keymap map) +Unbind all keys that are bound to @var{command} in @var{map}. +@end deftypefun + +@deftypefun int rl_bind_keyseq (const char *keyseq, rl_command_func_t *function) +Bind the key sequence represented by the string @var{keyseq} to the function +@var{function}, beginning in the current keymap. +This makes new keymaps as necessary. +The return value is non-zero if @var{keyseq} is invalid. +@end deftypefun + +@deftypefun int rl_bind_keyseq_in_map (const char *keyseq, rl_command_func_t *function, Keymap map) +Bind the key sequence represented by the string @var{keyseq} to the function +@var{function}. This makes new keymaps as necessary. +Initial bindings are performed in @var{map}. +The return value is non-zero if @var{keyseq} is invalid. +@end deftypefun + +@deftypefun int rl_set_key (const char *keyseq, rl_command_func_t *function, Keymap map) +Equivalent to @code{rl_bind_keyseq_in_map}. +@end deftypefun + +@deftypefun int rl_bind_keyseq_if_unbound (const char *keyseq, rl_command_func_t *function) +Binds @var{keyseq} to @var{function} if it is not already bound in the +currently active keymap. +Returns non-zero in the case of an invalid @var{keyseq} or if @var{keyseq} is +already bound. +@end deftypefun + +@deftypefun int rl_bind_keyseq_if_unbound_in_map (const char *keyseq, rl_command_func_t *function, Keymap map) +Binds @var{keyseq} to @var{function} if it is not already bound in @var{map}. +Returns non-zero in the case of an invalid @var{keyseq} or if @var{keyseq} is +already bound. +@end deftypefun + +@deftypefun int rl_generic_bind (int type, const char *keyseq, char *data, Keymap map) +Bind the key sequence represented by the string @var{keyseq} to the arbitrary +pointer @var{data}. @var{type} says what kind of data is pointed to by +@var{data}; this can be a function (@code{ISFUNC}), a macro +(@code{ISMACR}), or a keymap (@code{ISKMAP}). This makes new keymaps as +necessary. The initial keymap in which to do bindings is @var{map}. +@end deftypefun + +@deftypefun int rl_parse_and_bind (char *line) +Parse @var{line} as if it had been read from the @code{inputrc} file and +perform any key bindings and variable assignments found +(@pxref{Readline Init File}). +@end deftypefun + +@deftypefun int rl_read_init_file (const char *filename) +Read keybindings and variable assignments from @var{filename} +(@pxref{Readline Init File}). +@end deftypefun + +@node Associating Function Names and Bindings +@subsection Associating Function Names and Bindings + +These functions allow you to find out what keys invoke named functions +and the functions invoked by a particular key sequence. You may also +associate a new function name with an arbitrary function. + +@deftypefun {rl_command_func_t *} rl_named_function (const char *name) +Return the function with name @var{name}. +@end deftypefun + +@deftypefun {rl_command_func_t *} rl_function_of_keyseq (const char *keyseq, Keymap map, int *type) +Return the function invoked by @var{keyseq} in keymap @var{map}. +If @var{map} is @code{NULL}, the current keymap is used. If @var{type} is +not @code{NULL}, the type of the object is returned in the @code{int} variable +it points to (one of @code{ISFUNC}, @code{ISKMAP}, or @code{ISMACR}). +It takes a "translated" key sequence and should not be used if the key sequence +can include NUL. +@end deftypefun + +@deftypefun {rl_command_func_t *} rl_function_of_keyseq_len (const char *keyseq, size_t len, Keymap map, int *type) +Return the function invoked by @var{keyseq} of length @var{len} +in keymap @var{map}. Equivalent to @code{rl_function_of_keyseq} with the +addition of the @var{len} parameter. +It takes a "translated" key sequence and should be used if the key sequence +can include NUL. +@end deftypefun + +@deftypefun {int} rl_trim_arg_from_keyseq (const char *keyseq, size_t len, Keymap map) +If there is a numeric argument at the beginning of @var{keyseq}, possibly +including digits, return the index of the first character in @var{keyseq} +following the numeric argument. +This can be used to skip over the numeric argument (which is available as +@code{rl_numeric_arg} while traversing the key sequence that invoked the +current command. +@end deftypefun + +@deftypefun {char **} rl_invoking_keyseqs (rl_command_func_t *function) +Return an array of strings representing the key sequences used to +invoke @var{function} in the current keymap. +@end deftypefun + +@deftypefun {char **} rl_invoking_keyseqs_in_map (rl_command_func_t *function, Keymap map) +Return an array of strings representing the key sequences used to +invoke @var{function} in the keymap @var{map}. +@end deftypefun + +@deftypefun void rl_function_dumper (int readable) +Print the Readline function names and the key sequences currently +bound to them to @code{rl_outstream}. If @var{readable} is non-zero, +the list is formatted in such a way that it can be made part of an +@code{inputrc} file and re-read. +@end deftypefun + +@deftypefun void rl_list_funmap_names (void) +Print the names of all bindable Readline functions to @code{rl_outstream}. +@end deftypefun + +@deftypefun {const char **} rl_funmap_names (void) +Return a NULL terminated array of known function names. The array is +sorted. The array itself is allocated, but not the strings inside. You +should free the array, but not the pointers, using @code{free} or +@code{rl_free} when you are done. +@end deftypefun + +@deftypefun int rl_add_funmap_entry (const char *name, rl_command_func_t *function) +Add @var{name} to the list of bindable Readline command names, and make +@var{function} the function to be called when @var{name} is invoked. +@end deftypefun + +@node Allowing Undoing +@subsection Allowing Undoing + +Supporting the undo command is a painless thing, and makes your +functions much more useful. It is certainly easy to try +something if you know you can undo it. + +If your function simply inserts text once, or deletes text once, and +uses @code{rl_insert_text()} or @code{rl_delete_text()} to do it, then +undoing is already done for you automatically. + +If you do multiple insertions or multiple deletions, or any combination +of these operations, you should group them together into one operation. +This is done with @code{rl_begin_undo_group()} and +@code{rl_end_undo_group()}. + +The types of events that can be undone are: + +@smallexample +enum undo_code @{ UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END @}; +@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 +tells what to undo, not how to undo it. @code{UNDO_BEGIN} and +@code{UNDO_END} are tags added by @code{rl_begin_undo_group()} and +@code{rl_end_undo_group()}. + +@deftypefun int rl_begin_undo_group (void) +Begins saving undo information in a group construct. The undo +information usually comes from calls to @code{rl_insert_text()} and +@code{rl_delete_text()}, but could be the result of calls to +@code{rl_add_undo()}. +@end deftypefun + +@deftypefun int rl_end_undo_group (void) +Closes the current undo group started with @code{rl_begin_undo_group +()}. There should be one call to @code{rl_end_undo_group()} +for each call to @code{rl_begin_undo_group()}. +@end deftypefun + +@deftypefun void rl_add_undo (enum undo_code what, int start, int end, char *text) +Remember how to undo an event (according to @var{what}). The affected +text runs from @var{start} to @var{end}, and encompasses @var{text}. +@end deftypefun + +@deftypefun void rl_free_undo_list (void) +Free the existing undo list. +@end deftypefun + +@deftypefun int rl_do_undo (void) +Undo the first thing on the undo list. Returns @code{0} if there was +nothing to undo, non-zero if something was undone. +@end deftypefun + +Finally, if you neither insert nor delete text, but directly modify the +existing text (e.g., change its case), call @code{rl_modifying()} +once, just before you modify the text. You must supply the indices of +the text range that you are going to modify. + +@deftypefun int rl_modifying (int start, int end) +Tell Readline to save the text between @var{start} and @var{end} as a +single undo unit. It is assumed that you will subsequently modify +that text. +@end deftypefun + +@node Redisplay +@subsection Redisplay + +@deftypefun void rl_redisplay (void) +Change what's displayed on the screen to reflect the current contents +of @code{rl_line_buffer}. +@end deftypefun + +@deftypefun int rl_forced_update_display (void) +Force the line to be updated and redisplayed, whether or not +Readline thinks the screen display is correct. +@end deftypefun + +@deftypefun int rl_on_new_line (void) +Tell the update functions that we have moved onto a new (empty) line, +usually after outputting a newline. +@end deftypefun + +@deftypefun int rl_on_new_line_with_prompt (void) +Tell the update functions that we have moved onto a new line, with +@var{rl_prompt} already displayed. +This could be used by applications that want to output the prompt string +themselves, but still need Readline to know the prompt string length for +redisplay. +It should be used after setting @var{rl_already_prompted}. +@end deftypefun + +@deftypefun int rl_clear_visible_line (void) +Clear the screen lines corresponding to the current line's contents. +@end deftypefun + +@deftypefun int rl_reset_line_state (void) +Reset the display state to a clean state and redisplay the current line +starting on a new line. +@end deftypefun + +@deftypefun int rl_crlf (void) +Move the cursor to the start of the next screen line. +@end deftypefun + +@deftypefun int rl_show_char (int c) +Display character @var{c} on @code{rl_outstream}. +If Readline has not been set to display meta characters directly, this +will convert meta characters to a meta-prefixed key sequence. +This is intended for use by applications which wish to do their own +redisplay. +@end deftypefun + +@deftypefun int rl_message (const char *, @dots{}) +The arguments are a format string as would be supplied to @code{printf}, +possibly containing conversion specifications such as @samp{%d}, and +any additional arguments necessary to satisfy the conversion specifications. +The resulting string is displayed in the @dfn{echo area}. The echo area +is also used to display numeric arguments and search strings. +You should call @code{rl_save_prompt} to save the prompt information +before calling this function. +@end deftypefun + +@deftypefun int rl_clear_message (void) +Clear the message in the echo area. If the prompt was saved with a call to +@code{rl_save_prompt} before the last call to @code{rl_message}, +call @code{rl_restore_prompt} before calling this function. +@end deftypefun + +@deftypefun void rl_save_prompt (void) +Save the local Readline prompt display state in preparation for +displaying a new message in the message area with @code{rl_message()}. +@end deftypefun + +@deftypefun void rl_restore_prompt (void) +Restore the local Readline prompt display state saved by the most +recent call to @code{rl_save_prompt}. +if @code{rl_save_prompt} was called to save the prompt before a call +to @code{rl_message}, this function should be called before the +corresponding call to @code{rl_clear_message}. +@end deftypefun + +@deftypefun int rl_expand_prompt (char *prompt) +Expand any special character sequences in @var{prompt} and set up the +local Readline prompt redisplay variables. +This function is called by @code{readline()}. It may also be called to +expand the primary prompt if the @code{rl_on_new_line_with_prompt()} +function or @code{rl_already_prompted} variable is used. +It returns the number of visible characters on the last line of the +(possibly multi-line) prompt. +Applications may indicate that the prompt contains characters that take +up no physical screen space when displayed by bracketing a sequence of +such characters with the special markers @code{RL_PROMPT_START_IGNORE} +and @code{RL_PROMPT_END_IGNORE} (declared in @file{readline.h} as +@samp{\001} and @samp{\002}, respectively). +This may be used to embed terminal-specific escape sequences in prompts. +@end deftypefun + +@deftypefun int rl_set_prompt (const char *prompt) +Make Readline use @var{prompt} for subsequent redisplay. This calls +@code{rl_expand_prompt()} to expand the prompt and sets @code{rl_prompt} +to the result. +@end deftypefun + +@node Modifying Text +@subsection Modifying Text + +@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) +Return a copy of the text between @var{start} and @var{end} in +the current line. +@end deftypefun + +@deftypefun int rl_kill_text (int start, int end) +Copy the text between @var{start} and @var{end} in the current line +to the kill ring, appending or prepending to the last kill if the +last command was a kill command. The text is deleted. +If @var{start} is less than @var{end}, +the text is appended, otherwise prepended. If the last command was +not a kill, a new kill ring slot is used. +@end deftypefun + +@deftypefun int rl_push_macro_input (char *macro) +Cause @var{macro} to be inserted into the line, as if it had been invoked +by a key bound to a macro. Not especially useful; use +@code{rl_insert_text()} instead. +@end deftypefun + +@node Character Input +@subsection Character Input + +@deftypefun int rl_read_key (void) +Return the next character available from Readline's current input stream. +This handles input inserted into +the input stream via @var{rl_pending_input} (@pxref{Readline Variables}) +and @code{rl_stuff_char()}, macros, and characters read from the keyboard. +While waiting for input, this function will call any function assigned to +the @code{rl_event_hook} variable. +@end deftypefun + +@deftypefun int rl_getc (FILE *stream) +Return the next character available from @var{stream}, which is assumed to +be the keyboard. +@end deftypefun + +@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()}. 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) +Make @var{c} be the next command to be executed when @code{rl_read_key()} +is called. This sets @var{rl_pending_input}. +@end deftypefun + +@deftypefun int rl_clear_pending_input (void) +Unset @var{rl_pending_input}, effectively negating the effect of any +previous call to @code{rl_execute_next()}. This works only if the +pending input has not already been read with @code{rl_read_key()}. +@end deftypefun + +@deftypefun int rl_set_keyboard_input_timeout (int u) +While waiting for keyboard input in @code{rl_read_key()}, Readline will +wait for @var{u} microseconds for input before calling any function +assigned to @code{rl_event_hook}. @var{u} must be greater than or equal +to zero (a zero-length timeout is equivalent to a poll). +The default waiting period is one-tenth of a second. +Returns the old timeout value. +@end deftypefun + +@deftypefun int rl_set_timeout (unsigned int secs, unsigned int usecs) +Set a timeout for subsequent calls to @code{readline()}. If Readline does +not read a complete line, or the number of characters specified by +@code{rl_num_chars_to_read}, before the duration specified by @var{secs} +(in seconds) and @var{usecs} (microseconds), it returns and sets +@code{RL_STATE_TIMEOUT} in @code{rl_readline_state}. +Passing 0 for @code{secs} and @code{usecs} cancels any previously set +timeout; the convenience macro @code{rl_clear_timeout()} is shorthand +for this. +Returns 0 if the timeout is set successfully. +@end deftypefun + +@deftypefun int rl_timeout_remaining (unsigned int *secs, unsigned int *usecs) +Return the number of seconds and microseconds remaining in the current +timeout duration in @var{*secs} and @var{*usecs}, respectively. +Both @var{*secs} and @var{*usecs} must be non-NULL to return any values. +The return value is -1 on error or when there is no timeout set, +0 when the timeout has expired (leaving @var{*secs} and @var{*usecs} +unchanged), +and 1 if the timeout has not expired. +If either of @var{secs} and @var{usecs} is @code{NULL}, +the return value indicates whether the timeout has expired. +@end deftypefun + +@node Terminal Management +@subsection Terminal Management + +@deftypefun void rl_prep_terminal (int meta_flag) +Modify the terminal settings for Readline's use, so @code{readline()} +can read a single character at a time from the keyboard. +The @var{meta_flag} argument should be non-zero if Readline should +read eight-bit input. +@end deftypefun + +@deftypefun void rl_deprep_terminal (void) +Undo the effects of @code{rl_prep_terminal()}, leaving the terminal in +the state in which it was before the most recent call to +@code{rl_prep_terminal()}. +@end deftypefun + +@deftypefun void rl_tty_set_default_bindings (Keymap kmap) +Read the operating system's terminal editing characters (as would be +displayed by @code{stty}) to their Readline equivalents. +The bindings are performed in @var{kmap}. +@end deftypefun + +@deftypefun void rl_tty_unset_default_bindings (Keymap kmap) +Reset the bindings manipulated by @code{rl_tty_set_default_bindings} so +that the terminal editing characters are bound to @code{rl_insert}. +The bindings are performed in @var{kmap}. +@end deftypefun + +@deftypefun int rl_tty_set_echoing (int value) +Set Readline's idea of whether or not it is echoing output to its output +stream (@var{rl_outstream}). If @var{value} is 0, Readline does not display +output to @var{rl_outstream}; any other value enables output. The initial +value is set when Readline initializes the terminal settings. +This function returns the previous value. +@end deftypefun + +@deftypefun int rl_reset_terminal (const char *terminal_name) +Reinitialize Readline's idea of the terminal settings using +@var{terminal_name} as the terminal type (e.g., @code{vt100}). +If @var{terminal_name} is @code{NULL}, the value of the @code{TERM} +environment variable is used. +@end deftypefun + +@node Utility Functions +@subsection Utility Functions + +@deftypefun int rl_save_state (struct readline_state *sp) +Save a snapshot of Readline's internal state to @var{sp}. +The contents of the @var{readline_state} structure are documented +in @file{readline.h}. +The caller is responsible for allocating the structure. +@end deftypefun + +@deftypefun int rl_restore_state (struct readline_state *sp) +Restore Readline's internal state to that stored in @var{sp}, which must +have been saved by a call to @code{rl_save_state}. +The contents of the @var{readline_state} structure are documented +in @file{readline.h}. +The caller is responsible for freeing the structure. +@end deftypefun + +@deftypefun void rl_free (void *mem) +Deallocate the memory pointed to by @var{mem}. @var{mem} must have been +allocated by @code{malloc}. +@end deftypefun + +@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 void 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. +@end deftypefun + +@deftypefun int rl_initialize (void) +Initialize or re-initialize Readline's internal state. +It's not strictly necessary to call this; @code{readline()} calls it before +reading any input. +@end deftypefun + +@deftypefun int rl_ding (void) +Ring the terminal bell, obeying the setting of @code{bell-style}. +@end deftypefun + +@deftypefun int rl_alphabetic (int c) +Return 1 if @var{c} is an alphabetic character. +@end deftypefun + +@deftypefun void rl_display_match_list (char **matches, int len, int max) +A convenience function for displaying a list of strings in +columnar format on Readline's output stream. @code{matches} is the list +of strings, in argv format, such as a list of completion matches. +@code{len} is the number of strings in @code{matches}, and @code{max} +is the length of the longest string in @code{matches}. This function uses +the setting of @code{print-completions-horizontally} to select how the +matches are displayed (@pxref{Readline Init File Syntax}). +When displaying completions, this function sets the number of columns used +for display to the value of @code{completion-display-width}, the value of +the environment variable @env{COLUMNS}, or the screen width, in that order. +@end deftypefun + +The following are implemented as macros, defined in @code{chardefs.h}. +Applications should refrain from using them. + +@deftypefun int _rl_uppercase_p (int c) +Return 1 if @var{c} is an uppercase alphabetic character. +@end deftypefun + +@deftypefun int _rl_lowercase_p (int c) +Return 1 if @var{c} is a lowercase alphabetic character. +@end deftypefun + +@deftypefun int _rl_digit_p (int c) +Return 1 if @var{c} is a numeric character. +@end deftypefun + +@deftypefun int _rl_to_upper (int c) +If @var{c} is a lowercase alphabetic character, return the corresponding +uppercase character. +@end deftypefun + +@deftypefun int _rl_to_lower (int c) +If @var{c} is an uppercase alphabetic character, return the corresponding +lowercase character. +@end deftypefun + +@deftypefun int _rl_digit_value (int c) +If @var{c} is a number, return the value it represents. +@end deftypefun + +@node Miscellaneous Functions +@subsection Miscellaneous Functions + +@deftypefun int rl_macro_bind (const char *keyseq, const char *macro, Keymap map) +Bind the key sequence @var{keyseq} to invoke the macro @var{macro}. +The binding is performed in @var{map}. When @var{keyseq} is invoked, the +@var{macro} will be inserted into the line. This function is deprecated; +use @code{rl_generic_bind()} instead. +@end deftypefun + +@deftypefun void rl_macro_dumper (int readable) +Print the key sequences bound to macros and their values, using +the current keymap, to @code{rl_outstream}. +If @var{readable} is non-zero, the list is formatted in such a way +that it can be made part of an @code{inputrc} file and re-read. +@end deftypefun + +@deftypefun int rl_variable_bind (const char *variable, const char *value) +Make the Readline variable @var{variable} have @var{value}. +This behaves as if the Readline command +@samp{set @var{variable} @var{value}} had been executed in an @code{inputrc} +file (@pxref{Readline Init File Syntax}). +@end deftypefun + +@deftypefun {char *} rl_variable_value (const char *variable) +Return a string representing the value of the Readline variable @var{variable}. +For boolean variables, this string is either @samp{on} or @samp{off}. +@end deftypefun + +@deftypefun void rl_variable_dumper (int readable) +Print the Readline variable names and their current values +to @code{rl_outstream}. +If @var{readable} is non-zero, the list is formatted in such a way +that it can be made part of an @code{inputrc} file and re-read. +@end deftypefun + +@deftypefun int rl_set_paren_blink_timeout (int u) +Set the time interval (in microseconds) that Readline waits when showing +a balancing character when @code{blink-matching-paren} has been enabled. +@end deftypefun + +@deftypefun {char *} rl_get_termcap (const char *cap) +Retrieve the string value of the termcap capability @var{cap}. +Readline fetches the termcap entry for the current terminal name and +uses those capabilities to move around the screen line and perform other +terminal-specific operations, like erasing a line. Readline does not +use all of a terminal's capabilities, and this function will return +values for only those capabilities Readline uses. +@end deftypefun + +@deftypefun {void} rl_clear_history (void) +Clear the history list by deleting all of the entries, in the same manner +as the History library's @code{clear_history()} function. +This differs from @code{clear_history} because it frees private data +Readline saves in the history list. +@end deftypefun + +@deftypefun {void} rl_activate_mark (void) +Enable an @emph{active} mark. +When this is enabled, the text between point and mark (the @var{region}) is +displayed in the terminal's standout mode (a @var{face}). +This is called by various Readline functions that set the mark and insert +text, and is available for applications to call. +@end deftypefun + +@deftypefun {void} rl_deactivate_mark (void) +Turn off the active mark. +@end deftypefun + +@deftypefun {void} rl_keep_mark_active (void) +Indicate that the mark should remain active when the current Readline +function completes and after redisplay occurs. +In most cases, the mark remains active for only the duration of a single +bindable Readline function. +@end deftypefun + +@deftypefun {int} rl_mark_active_p (void) +Return a non-zero value if the mark is currently active; zero otherwise. +@end deftypefun + +@node Alternate Interface +@subsection Alternate Interface + +An alternate interface is available to plain @code{readline()}. Some +applications need to interleave keyboard I/O with file, device, or +window system I/O, typically by using a main loop to @code{select()} +on various file descriptors. To accommodate this need, Readline can +also be invoked as a `callback' function from an event loop. There +are functions available to make this easy. + +@deftypefun void rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *lhandler) +Set up the terminal for Readline I/O and display the initial +expanded value of @var{prompt}. Save the value of @var{lhandler} to +use as a handler function to call when a complete line of input has been +entered. +The handler function receives the text of the line as an argument. +As with @code{readline()}, the handler function should @code{free} the +line when it it finished with it. +@end deftypefun + +@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 installed 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, +and the line handler remains installed, +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_sigcleanup (void) +Clean up any internal state the callback interface uses to maintain state +between calls to rl_callback_read_char (e.g., the state of any active +incremental searches). This is intended to be used by applications that +wish to perform their own signal handling; Readline's internal signal handler +calls this when appropriate. +@end deftypefun + +@deftypefun void rl_callback_handler_remove (void) +Restore the terminal to its initial state and remove the line handler. +You may call this function 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 +@subsection A Readline Example + +Here is a function which changes lowercase characters to their uppercase +equivalents, and uppercase characters to lowercase. If +this function was bound to @samp{M-c}, then typing @samp{M-c} would +change the case of the character under point. Typing @samp{M-1 0 M-c} +would change the case of the following 10 characters, leaving the cursor on +the last character changed. + +@example +/* Invert the case of the COUNT following characters. */ +int +invert_case_line (count, key) + int count, key; +@{ + register int start, end, i; + + start = rl_point; + + if (rl_point >= rl_end) + return (0); + + if (count < 0) + @{ + direction = -1; + count = -count; + @} + else + direction = 1; + + /* Find the end of the range to modify. */ + end = start + (count * direction); + + /* Force it to be within range. */ + if (end > rl_end) + end = rl_end; + else if (end < 0) + end = 0; + + if (start == end) + return (0); + + if (start > end) + @{ + int temp = start; + start = end; + end = temp; + @} + + /* 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++) + @{ + if (_rl_uppercase_p (rl_line_buffer[i])) + rl_line_buffer[i] = _rl_to_lower (rl_line_buffer[i]); + else if (_rl_lowercase_p (rl_line_buffer[i])) + rl_line_buffer[i] = _rl_to_upper (rl_line_buffer[i]); + @} + /* Move point to on top of the last character changed. */ + rl_point = (direction == 1) ? end - 1 : start; + return (0); +@} +@end example + +@node Alternate Interface Example +@subsection Alternate Interface Example + +Here is a complete program that illustrates Readline's alternate interface. +It reads lines from the terminal and displays them, providing the +standard history and TAB completion functions. +It understands the EOF character or "exit" to exit the program. + +@example +/* Standard include files. stdio.h is required. */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.h> + +/* Used for select(2) */ +#include <sys/types.h> +#include <sys/select.h> + +#include <signal.h> + +#include <stdio.h> + +/* Standard readline include files. */ +#include <readline/readline.h> +#include <readline/history.h> + +static void cb_linehandler (char *); +static void sighandler (int); + +int running; +int sigwinch_received; +const char *prompt = "rltest$ "; + +/* Handle SIGWINCH and window size changes when readline is not active and + reading a character. */ +static void +sighandler (int sig) +@{ + sigwinch_received = 1; +@} + +/* Callback function called for each line when accept-line executed, EOF + seen, or EOF character read. This sets a flag and returns; it could + also call exit(3). */ +static void +cb_linehandler (char *line) +@{ + /* Can use ^D (stty eof) or `exit' to exit. */ + if (line == NULL || strcmp (line, "exit") == 0) + @{ + if (line == 0) + printf ("\n"); + printf ("exit\n"); + /* This function needs to be called to reset the terminal settings, + and calling it from the line handler keeps one extra prompt from + being displayed. */ + rl_callback_handler_remove (); + + running = 0; + @} + else + @{ + if (*line) + add_history (line); + printf ("input line: %s\n", line); + free (line); + @} +@} + +int +main (int c, char **v) +@{ + fd_set fds; + int r; + + /* Set the default locale values according to environment variables. */ + setlocale (LC_ALL, ""); + + /* Handle window size changes when readline is not active and reading + characters. */ + signal (SIGWINCH, sighandler); + + /* Install the line handler. */ + rl_callback_handler_install (prompt, cb_linehandler); + + /* Enter a simple event loop. This waits until something is available + to read on readline's input stream (defaults to standard input) and + calls the builtin character read callback to read it. It does not + have to modify the user's terminal settings. */ + running = 1; + while (running) + @{ + FD_ZERO (&fds); + FD_SET (fileno (rl_instream), &fds); + + r = select (FD_SETSIZE, &fds, NULL, NULL, NULL); + if (r < 0 && errno != EINTR) + @{ + perror ("rltest: select"); + rl_callback_handler_remove (); + break; + @} + if (sigwinch_received) + @{ + rl_resize_terminal (); + sigwinch_received = 0; + @} + if (r < 0) + continue; + + if (FD_ISSET (fileno (rl_instream), &fds)) + rl_callback_read_char (); + @} + + printf ("rltest: Event loop has exited\n"); + return 0; +@} +@end example + +@node Readline Signal Handling +@section Readline Signal Handling + +Signals are asynchronous events sent to a process by the Unix kernel, +sometimes on behalf of another process. They are intended to indicate +exceptional events, like a user pressing the terminal's interrupt key, +or a network connection being broken. There is a class of signals that can +be sent to the process currently reading input from the keyboard. Since +Readline changes the terminal attributes when it is called, it needs to +perform special processing when such a signal is received in order to +restore the terminal to a sane state, or provide application writers with +functions to do so manually. + +Readline contains an internal signal handler that is installed for a +number of signals (@code{SIGINT}, @code{SIGQUIT}, @code{SIGTERM}, +@code{SIGHUP}, +@code{SIGALRM}, @code{SIGTSTP}, @code{SIGTTIN}, and @code{SIGTTOU}). +When one of these signals is received, the signal handler +will reset the terminal attributes to those that were in effect before +@code{readline()} was called, reset the signal handling to what it was +before @code{readline()} was called, and resend the signal to the calling +application. +If and when the calling application's signal handler returns, Readline +will reinitialize the terminal and continue to accept input. +When a @code{SIGINT} is received, the Readline signal handler performs +some additional work, which will cause any partially-entered line to be +aborted (see the description of @code{rl_free_line_state()} below). + +There is an additional Readline signal handler, for @code{SIGWINCH}, which +the kernel sends to a process whenever the terminal's size changes (for +example, if a user resizes an @code{xterm}). The Readline @code{SIGWINCH} +handler updates Readline's internal screen size information, and then calls +any @code{SIGWINCH} signal handler the calling application has installed. +Readline calls the application's @code{SIGWINCH} signal handler without +resetting the terminal to its original state. If the application's signal +handler does more than update its idea of the terminal size and return (for +example, a @code{longjmp} back to a main processing loop), it @emph{must} +call @code{rl_cleanup_after_signal()} (described below), to restore the +terminal state. + +When an application is using the callback interface +(@pxref{Alternate Interface}), Readline installs signal handlers only for +the duration of the call to @code{rl_callback_read_char}. Applications +using the callback interface should be prepared to clean up Readline's +state if they wish to handle the signal before the line handler completes +and restores the terminal state. + +If an application using the callback interface wishes to have Readline +install its signal handlers at the time the application calls +@code{rl_callback_handler_install} and remove them only when a complete +line of input has been read, it should set the +@code{rl_persistent_signal_handlers} variable to a non-zero value. +This allows an application to defer all of the handling of the signals +Readline catches to Readline. +Applications should use this variable with care; it can result in Readline +catching signals and not acting on them (or allowing the application to react +to them) until the application calls @code{rl_callback_read_char}. This +can result in an application becoming less responsive to keyboard signals +like SIGINT. +If an application does not want or need to perform any signal handling, or +does not need to do any processing between calls to @code{rl_callback_read_char}, +setting this variable may be desirable. + +Readline provides two variables that allow application writers to +control whether or not it will catch certain signals and act on them +when they are received. It is important that applications change the +values of these variables only when calling @code{readline()}, not in +a signal handler, so Readline's internal signal state is not corrupted. + +@deftypevar int rl_catch_signals +If this variable is non-zero, Readline will install signal handlers for +@code{SIGINT}, @code{SIGQUIT}, @code{SIGTERM}, @code{SIGHUP}, @code{SIGALRM}, +@code{SIGTSTP}, @code{SIGTTIN}, and @code{SIGTTOU}. + +The default value of @code{rl_catch_signals} is 1. +@end deftypevar + +@deftypevar int rl_catch_sigwinch +If this variable is set to a non-zero value, +Readline will install a signal handler for @code{SIGWINCH}. + +The default value of @code{rl_catch_sigwinch} is 1. +@end deftypevar + +@deftypevar int rl_persistent_signal_handlers +If an application using the callback interface wishes Readline's signal +handlers to be installed and active during the set of calls to +@code{rl_callback_read_char} that constitutes an entire single line, +it should set this variable to a non-zero value. + +The default value of @code{rl_persistent_signal_handlers} is 0. +@end deftypevar + +@deftypevar int rl_change_environment +If this variable is set to a non-zero value, +and Readline is handling @code{SIGWINCH}, Readline will modify the +@var{LINES} and @var{COLUMNS} environment variables upon receipt of a +@code{SIGWINCH} + +The default value of @code{rl_change_environment} is 1. +@end deftypevar + +If an application does not wish to have Readline catch any signals, or +to handle signals other than those Readline catches (@code{SIGHUP}, +for example), +Readline provides convenience functions to do the necessary terminal +and internal state cleanup upon receipt of a signal. + +@deftypefun int rl_pending_signal (void) +Return the signal number of the most recent signal Readline received but +has not yet handled, or 0 if there is no pending signal. +@end deftypefun + +@deftypefun void rl_cleanup_after_signal (void) +This function will reset the state of the terminal to what it was before +@code{readline()} was called, and remove the Readline signal handlers for +all signals, depending on the values of @code{rl_catch_signals} and +@code{rl_catch_sigwinch}. +@end deftypefun + +@deftypefun void rl_free_line_state (void) +This will free any partial state associated with the current input line +(undo information, any partial history entry, any partially-entered +keyboard macro, and any partially-entered numeric argument). This +should be called before @code{rl_cleanup_after_signal()}. The +Readline signal handler for @code{SIGINT} calls this to abort the +current input line. +@end deftypefun + +@deftypefun void rl_reset_after_signal (void) +This will reinitialize the terminal and reinstall any Readline signal +handlers, depending on the values of @code{rl_catch_signals} and +@code{rl_catch_sigwinch}. +@end deftypefun + +If an application wants to force Readline to handle any signals that +have arrived while it has been executing, @code{rl_check_signals()} +will call Readline's internal signal handler if there are any pending +signals. This is primarily intended for those applications that use +a custom @code{rl_getc_function} (@pxref{Readline Variables}) and wish +to handle signals received while waiting for input. + +@deftypefun void rl_check_signals (void) +If there are any pending signals, call Readline's internal signal handling +functions to process them. @code{rl_pending_signal()} can be used independently +to determine whether or not there are any pending signals. +@end deftypefun + +If an application does not wish Readline to catch @code{SIGWINCH}, it may +call @code{rl_resize_terminal()} or @code{rl_set_screen_size()} to force +Readline to update its idea of the terminal size when it receives +a @code{SIGWINCH}. + +@deftypefun void rl_echo_signal_char (int sig) +If an application wishes to install its own signal handlers, but still +have Readline display characters that generate signals, calling this +function with @var{sig} set to @code{SIGINT}, @code{SIGQUIT}, or +@code{SIGTSTP} will display the character generating that signal. +@end deftypefun + +@deftypefun void rl_resize_terminal (void) +Update Readline's internal screen size by reading values from the kernel. +@end deftypefun + +@deftypefun void rl_set_screen_size (int rows, int cols) +Set Readline's idea of the terminal size to @var{rows} rows and +@var{cols} columns. If either @var{rows} or @var{columns} is less than +or equal to 0, Readline's idea of that terminal dimension is unchanged. +This is intended to tell Readline the physical dimensions of the terminal, +and is used internally to calculate the maximum number of characters that +may appear on a single line and on the screen. +@end deftypefun + +If an application does not want to install a @code{SIGWINCH} handler, but +is still interested in the screen dimensions, it may query Readline's idea +of the screen size. + +@deftypefun void rl_get_screen_size (int *rows, int *cols) +Return Readline's idea of the terminal's size in the +variables pointed to by the arguments. +@end deftypefun + +@deftypefun void rl_reset_screen_size (void) +Cause Readline to reobtain the screen size and recalculate its dimensions. +@end deftypefun + +The following functions install and remove Readline's signal handlers. + +@deftypefun int rl_set_signals (void) +Install Readline's signal handler for @code{SIGINT}, @code{SIGQUIT}, +@code{SIGTERM}, @code{SIGHUP}, @code{SIGALRM}, @code{SIGTSTP}, @code{SIGTTIN}, +@code{SIGTTOU}, and @code{SIGWINCH}, depending on the values of +@code{rl_catch_signals} and @code{rl_catch_sigwinch}. +@end deftypefun + +@deftypefun int rl_clear_signals (void) +Remove all of the Readline signal handlers installed by +@code{rl_set_signals()}. +@end deftypefun + +@node Custom Completers +@section Custom Completers +@cindex application-specific completion functions + +Typically, a program that reads commands from the user has a way of +disambiguating commands and data. If your program is one of these, then +it can provide completion for commands, data, or both. +The following sections describe how your program and Readline +cooperate to provide this service. + +@menu +* How Completing Works:: The logic used to do completion. +* Completion Functions:: Functions provided by Readline. +* Completion Variables:: Variables which control completion. +* A Short Completion Example:: An example of writing completer subroutines. +@end menu + +@node How Completing Works +@subsection How Completing Works + +In order to complete some text, the full list of possible completions +must be available. That is, it is not possible to accurately +expand a partial word without knowing all of the possible words +which make sense in that context. The Readline library provides +the user interface to completion, and two of the most common +completion functions: filename and username. For completing other types +of text, you must write your own completion function. This section +describes exactly what such functions must do, and provides an example. + +There are three major functions used to perform completion: + +@enumerate +@item +The user-interface function @code{rl_complete()}. This function is +called with the same arguments as other bindable Readline functions: +@var{count} and @var{invoking_key}. +It isolates the word to be completed and calls +@code{rl_completion_matches()} to generate a list of possible completions. +It then either lists the possible completions, inserts the possible +completions, or actually performs the +completion, depending on which behavior is desired. + +@item +The internal function @code{rl_completion_matches()} uses an +application-supplied @dfn{generator} function to generate the list of +possible matches, and then returns the array of these matches. +The caller should place the address of its generator function in +@code{rl_completion_entry_function}. + +@item +The generator function is called repeatedly from +@code{rl_completion_matches()}, returning a string each time. The +arguments to the generator function are @var{text} and @var{state}. +@var{text} is the partial word to be completed. @var{state} is zero the +first time the function is called, allowing the generator to perform +any necessary initialization, and a positive non-zero integer for +each subsequent call. The generator function returns +@code{(char *)NULL} to inform @code{rl_completion_matches()} that there are +no more possibilities left. Usually the generator function computes the +list of possible completions when @var{state} is zero, and returns them +one at a time on subsequent calls. Each string the generator function +returns as a match must be allocated with @code{malloc()}; Readline +frees the strings when it has finished with them. +Such a generator function is referred to as an +@dfn{application-specific completion function}. + +@end enumerate + +@deftypefun int rl_complete (int ignore, int invoking_key) +Complete the word at or before point. You have supplied the function +that does the initial simple matching selection algorithm (see +@code{rl_completion_matches()}). The default is to do filename completion. +@end deftypefun + +@deftypevar {rl_compentry_func_t *} rl_completion_entry_function +This is a pointer to the generator function for +@code{rl_completion_matches()}. +If the value of @code{rl_completion_entry_function} is +@code{NULL} then the default filename generator +function, @code{rl_filename_completion_function()}, is used. +An @dfn{application-specific completion function} is a function whose +address is assigned to @code{rl_completion_entry_function} and whose +return values are used to generate possible completions. +@end deftypevar + +@node Completion Functions +@subsection Completion Functions + +Here is the complete list of callable completion functions present in +Readline. + +@deftypefun int rl_complete_internal (int what_to_do) +Complete the word at or before point. @var{what_to_do} says what to do +with the completion. A value of @samp{?} means list the possible +completions. @samp{TAB} means do standard completion. @samp{*} means +insert all of the possible completions. @samp{!} means to display +all of the possible completions, if there is more than one, as well as +performing partial completion. @samp{@@} is similar to @samp{!}, but +possible completions are not listed if the possible completions share +a common prefix. +@end deftypefun + +@deftypefun int rl_complete (int ignore, int invoking_key) +Complete the word at or before point. You have supplied the function +that does the initial simple matching selection algorithm (see +@code{rl_completion_matches()} and @code{rl_completion_entry_function}). +The default is to do filename +completion. This calls @code{rl_complete_internal()} with an +argument depending on @var{invoking_key}. +@end deftypefun + +@deftypefun int rl_possible_completions (int count, int invoking_key) +List the possible completions. See description of @code{rl_complete +()}. This calls @code{rl_complete_internal()} with an argument of +@samp{?}. +@end deftypefun + +@deftypefun int rl_insert_completions (int count, int invoking_key) +Insert the list of possible completions into the line, deleting the +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 appropriate value to pass to @code{rl_complete_internal()} +depending on whether @var{cfunc} was called twice in succession and +the values of the @code{show-all-if-ambiguous} and +@code{show-all-if-unmodified} variables. +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}. +The first entry in the returned array is the substitution for @var{text}. +The remaining entries are the possible completions. The array is +terminated with a @code{NULL} pointer. + +@var{entry_func} is a function of two args, and returns a +@code{char *}. The first argument is @var{text}. The second is a +state argument; it is zero on the first call, and non-zero on subsequent +calls. @var{entry_func} returns a @code{NULL} pointer to the caller +when there are no more matches. +@end deftypefun + +@deftypefun {char *} rl_filename_completion_function (const char *text, int state) +A generator function for filename completion in the general case. +@var{text} is a partial filename. +The Bash source is a useful reference for writing application-specific +completion functions (the Bash completion functions call this and other +Readline functions). +@end deftypefun + +@deftypefun {char *} rl_username_completion_function (const char *text, int state) +A completion generator for usernames. @var{text} contains a partial +username preceded by a random character (usually @samp{~}). As with all +completion generators, @var{state} is zero on the first call and non-zero +for subsequent calls. +@end deftypefun + +@node Completion Variables +@subsection Completion Variables + +@deftypevar {rl_compentry_func_t *} rl_completion_entry_function +A pointer to the generator function for @code{rl_completion_matches()}. +@code{NULL} means to use @code{rl_filename_completion_function()}, +the default filename completer. +@end deftypevar + +@deftypevar {rl_completion_func_t *} rl_attempted_completion_function +A pointer to an alternative function to create matches. +The function is called with @var{text}, @var{start}, and @var{end}. +@var{start} and @var{end} are indices in @code{rl_line_buffer} defining +the boundaries of @var{text}, which is a character string. +If this function exists and returns @code{NULL}, or if this variable is +set to @code{NULL}, then @code{rl_complete()} will call the value of +@code{rl_completion_entry_function} to generate matches, otherwise the +array of strings returned will be used. +If this function sets the @code{rl_attempted_completion_over} +variable to a non-zero value, Readline will not perform its default +completion even if this function returns no matches. +@end deftypevar + +@deftypevar {rl_quote_func_t *} rl_filename_quoting_function +A pointer to a function that will quote a filename in an +application-specific fashion. This is called if filename completion is being +attempted and one of the characters in @code{rl_filename_quote_characters} +appears in a completed filename. The function is called with +@var{text}, @var{match_type}, and @var{quote_pointer}. The @var{text} +is the filename to be quoted. The @var{match_type} is either +@code{SINGLE_MATCH}, if there is only one completion match, or +@code{MULT_MATCH}. Some functions use this to decide whether or not to +insert a closing quote character. The @var{quote_pointer} is a pointer +to any opening quote character the user typed. Some functions choose +to reset this character. +@end deftypevar + +@deftypevar {rl_dequote_func_t *} rl_filename_dequoting_function +A pointer to a function that will remove application-specific quoting +characters from a filename before completion is attempted, so those +characters do not interfere with matching the text against names in +the filesystem. It is called with @var{text}, the text of the word +to be dequoted, and @var{quote_char}, which is the quoting character +that delimits the filename (usually @samp{'} or @samp{"}). If +@var{quote_char} is zero, the filename was not in an embedded string. +@end deftypevar + +@deftypevar {rl_linebuf_func_t *} rl_char_is_quoted_p +A pointer to a function to call that determines whether or not a specific +character in the line buffer is quoted, according to whatever quoting +mechanism the program calling Readline uses. The function is called with +two arguments: @var{text}, the text of the line, and @var{index}, the +index of the character in the line. It is used to decide whether a +character found in @code{rl_completer_word_break_characters} should be +used to break words for the completer. +@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 could be used to expand symbolic links or shell variables in pathnames. +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 used as part of the completion, replacing +the directory portion of the pathname the user typed. +At the least, even if no other expansion is performed, this function should +remove any quote characters from the directory name, because its result will +be passed directly to @code{opendir()}. + +The directory completion hook returns an integer that should be non-zero if +the function modifies its directory argument. +The function should not modify the directory argument if it returns 0. +@end deftypevar + +@deftypevar {rl_icppfunc_t *} rl_directory_rewrite_hook; +If non-zero, this is the address of a function to call when completing +a directory name. This function takes the address of the directory name +to be modified as an argument. Unlike @code{rl_directory_completion_hook}, +it only modifies the directory name used in @code{opendir}, not what is +displayed when the possible completions are printed or inserted. It is +called before rl_directory_completion_hook. +At the least, even if no other expansion is performed, this function should +remove any quote characters from the directory name, because its result will +be passed directly to @code{opendir()}. + +The directory rewrite hook returns an integer that should be non-zero if +the function modifies its directory argument. +The function should not modify the directory argument if it returns 0. +@end deftypevar + +@deftypevar {rl_icppfunc_t *} rl_filename_stat_hook +If non-zero, this is the address of a function for the completer to +call before deciding which character to append to a completed name. +This function modifies its filename name argument, and the modified value +is passed to @code{stat()} to determine the file's type and characteristics. +This function does not need to remove quote characters from the filename. + +The stat hook returns an integer that should be non-zero if +the function modifies its directory argument. +The function should not modify the directory argument if it returns 0. +@end deftypevar + +@deftypevar {rl_dequote_func_t *} rl_filename_rewrite_hook +If non-zero, this is the address of a function called when reading +directory entries from the filesystem for completion and comparing +them to the partial word to be completed. The function should +perform any necessary application or system-specific conversion on +the filename, such as converting between character sets or converting +from a filesystem format to a character input format. +The function takes two arguments: @var{fname}, the filename to be converted, +and @var{fnlen}, its length in bytes. +It must either return its first argument (if no conversion takes place) +or the converted filename in newly-allocated memory. The converted +form is used to compare against the word to be completed, and, if it +matches, is added to the list of matches. Readline will free the +allocated string. +@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. +You may call that function from this hook. +@end deftypevar + +@deftypevar {const char *} rl_basic_word_break_characters +The basic list of characters that signal a break between words for the +completer routine. The default value of this variable is the characters +which break words for completion in Bash: +@code{" \t\n\"\\'`@@$><=;|&@{("}. +@end deftypevar + +@deftypevar {const char *} rl_basic_quote_characters +A list of quote characters which can cause a word break. +@end deftypevar + +@deftypevar {const char *} rl_completer_word_break_characters +The list of characters that signal a break between words for +@code{rl_complete_internal()}. The default list is the value of +@code{rl_basic_word_break_characters}. +@end deftypevar + +@deftypevar {rl_cpvfunc_t *} rl_completion_word_break_hook +If non-zero, this is the address of a function to call when Readline is +deciding where to separate words for word completion. It should return +a character string like @code{rl_completer_word_break_characters} to be +used to perform the current completion. The function may choose to set +@code{rl_completer_word_break_characters} itself. If the function +returns @code{NULL}, @code{rl_completer_word_break_characters} is used. +@end deftypevar + +@deftypevar {const char *} rl_completer_quote_characters +A list of characters which can be used to quote a substring of the line. +Completion occurs on the entire substring, and within the substring +@code{rl_completer_word_break_characters} are treated as any other character, +unless they also appear within this list. +@end deftypevar + +@deftypevar {const char *} rl_filename_quote_characters +A list of characters that cause a filename to be quoted by the completer +when they appear in a completed filename. The default is the null string. +@end deftypevar + +@deftypevar {const char *} rl_special_prefixes +The list of characters that are word break characters, but should be +left in @var{text} when it is passed to the completion function. +Programs can use this to help determine what kind of completing to do. +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, Readline asks the user for +confirmation before displaying them. +The default value is 100. A negative value +indicates that Readline should never ask for confirmation. +@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 +default is a space character (@samp{ }). Setting this to the null +character (@samp{\0}) prevents anything being appended automatically. +This can be changed in application-specific completion functions to +provide the ``most sensible word separator character'' according to +an application-specific command line syntax specification. +It is set to the default before any application-specific completion function +is called, and may only be changed within such a function. +@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, and may only be changed within such a function. +@end deftypevar + +@deftypevar int rl_completion_quote_character +When Readline is completing quoted text, as delimited by one of the +characters in @var{rl_completer_quote_characters}, it sets this variable +to the quoting character found. +This is set before any application-specific completion function is called. +@end deftypevar + +@deftypevar int rl_completion_suppress_quote +If non-zero, Readline does not append a matching quote character when +performing completion on a quoted string. +It is set to 0 before any application-specific completion function +is called, and may only be changed within such a function. +@end deftypevar + +@deftypevar int rl_completion_found_quote +When Readline is completing quoted text, it sets this variable +to a non-zero value if the word being completed contains or is delimited +by any quoting characters, including backslashes. +This is set 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-specific 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-specific 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. +@end deftypevar + +@deftypevar int rl_filename_completion_desired +Non-zero means that the results of the matches are to be treated as +filenames. This is @emph{always} zero when completion is attempted, +and can only be changed +within an application-specific completion function. If it is set to a +non-zero value by such a function, directory names have a slash appended +and Readline attempts to quote completed filenames if they contain any +characters in @code{rl_filename_quote_characters} and +@code{rl_filename_quoting_desired} is set to a non-zero value. +@end deftypevar + +@deftypevar int rl_filename_quoting_desired +Non-zero means that the results of the matches are to be quoted using +double quotes (or an application-specific quoting mechanism) if the +completed filename contains any characters in +@code{rl_filename_quote_chars}. This is @emph{always} non-zero +when completion is attempted, and can only be changed within an +application-specific completion function. +The quoting is effected via a call to the function pointed to +by @code{rl_filename_quoting_function}. +@end deftypevar + +@deftypevar int rl_attempted_completion_over +If an application-specific completion function assigned to +@code{rl_attempted_completion_function} sets this variable to a non-zero +value, Readline will not perform its default filename completion even +if the application's completion function returns no matches. +It should be set only by an application's completion function. +@end deftypevar + +@deftypevar int rl_sort_completion_matches +If an application sets this variable to 0, Readline will not sort the +list of completions (which implies that it cannot remove any duplicate +completions). The default value is 1, which means that Readline will +sort the completions and, depending on the value of +@code{rl_ignore_completion_duplicates}, will attempt to remove duplicate +matches. +@end deftypevar + +@deftypevar int rl_completion_type +Set to a character describing the type of completion Readline is currently +attempting; see the description of @code{rl_complete_internal()} +(@pxref{Completion Functions}) for the list of characters. +This is set to the appropriate value before any application-specific +completion function is called, allowing such functions to present +the same interface as @code{rl_complete()}. +@end deftypevar + +@deftypevar int rl_completion_invoking_key +Set to the final character in the key sequence that invoked one of the +completion functions that call @code{rl_complete_internal()}. This is +set to the appropriate value before any application-specific completion +function is called. +@end deftypevar + +@deftypevar int rl_inhibit_completion +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 + +@node A Short Completion Example +@subsection A Short Completion Example + +Here is a small application demonstrating the use of the GNU Readline +library. It is called @code{fileman}, and the source code resides in +@file{examples/fileman.c}. This sample application provides +completion of command names, line editing features, and access to the +history list. + +@page +@smallexample +/* 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. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#ifdef HAVE_SYS_FILE_H +# include <sys/file.h> +#endif +#include <sys/stat.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> +#include <locale.h> + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include <time.h> + +#include <readline/readline.h> +#include <readline/history.h> + +extern char *xmalloc PARAMS((size_t)); + +/* The names of functions that actually do the manipulation. */ +int com_list PARAMS((char *)); +int com_view PARAMS((char *)); +int com_rename PARAMS((char *)); +int com_stat PARAMS((char *)); +int com_pwd PARAMS((char *)); +int com_delete PARAMS((char *)); +int com_help PARAMS((char *)); +int com_cd PARAMS((char *)); +int com_quit PARAMS((char *)); + +/* A structure which contains information on the commands this program + can understand. */ + +typedef struct @{ + char *name; /* User printable name of the function. */ + rl_icpfunc_t *func; /* Function to call to do the job. */ + char *doc; /* Documentation for this function. */ +@} COMMAND; + +COMMAND commands[] = @{ + @{ "cd", com_cd, "Change to directory DIR" @}, + @{ "delete", com_delete, "Delete FILE" @}, + @{ "help", com_help, "Display this text" @}, + @{ "?", com_help, "Synonym for `help'" @}, + @{ "list", com_list, "List files in DIR" @}, + @{ "ls", com_list, "Synonym for `list'" @}, + @{ "pwd", com_pwd, "Print the current working directory" @}, + @{ "quit", com_quit, "Quit using Fileman" @}, + @{ "rename", com_rename, "Rename FILE to NEWNAME" @}, + @{ "stat", com_stat, "Print out statistics on FILE" @}, + @{ "view", com_view, "View the contents of FILE" @}, + @{ (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL @} +@}; + +/* Forward declarations. */ +char *stripwhite (); +COMMAND *find_command (); + +/* The name of this program, as taken from argv[0]. */ +char *progname; + +/* When non-zero, this global means the user is done using this program. */ +int done; + +char * +dupstr (s) + char *s; +@{ + char *r; + + r = xmalloc (strlen (s) + 1); + strcpy (r, s); + return (r); +@} + +main (argc, argv) + int argc; + char **argv; +@{ + char *line, *s; + + setlocale (LC_ALL, ""); + + progname = argv[0]; + + initialize_readline (); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + for ( ; done == 0; ) + @{ + line = readline ("FileMan: "); + + if (!line) + break; + + /* Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. */ + s = stripwhite (line); + + if (*s) + @{ + add_history (s); + execute_line (s); + @} + + free (line); + @} + exit (0); +@} + +/* Execute a command line. */ +int +execute_line (line) + char *line; +@{ + register int i; + COMMAND *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && whitespace (line[i])) + i++; + word = line + i; + + while (line[i] && !whitespace (line[i])) + i++; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) + @{ + fprintf (stderr, "%s: No such command for FileMan.\n", word); + return (-1); + @} + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* Call the function. */ + return ((*(command->func)) (word)); +@} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND * +find_command (name) + char *name; +@{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +@} + +/* Strip whitespace from the start and end of STRING. Return a pointer + into STRING. */ +char * +stripwhite (string) + char *string; +@{ + register char *s, *t; + + for (s = string; whitespace (*s); s++) + ; + + if (*s == 0) + return (s); + + t = s + strlen (s) - 1; + while (t > s && whitespace (*t)) + t--; + *++t = '\0'; + + return s; +@} + +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +char *command_generator PARAMS((const char *, int)); +char **fileman_completion PARAMS((const char *, int, int)); + +/* Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. */ +initialize_readline () +@{ + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "FileMan"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = fileman_completion; +@} + +/* Attempt to complete on the contents of TEXT. START and END bound the + region of rl_line_buffer that contains the word to complete. TEXT is + the word to complete. We can use the entire contents of rl_line_buffer + in case we want to do some simple parsing. Return the array of matches, + or NULL if there aren't any. */ +char ** +fileman_completion (text, start, end) + const char *text; + int start, end; +@{ + char **matches; + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = rl_completion_matches (text, command_generator); + + return (matches); +@} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +command_generator (text, state) + const char *text; + int state; +@{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) + @{ + list_index = 0; + len = strlen (text); + @} + + /* Return the next name which partially matches from the command list. */ + while (name = commands[list_index].name) + @{ + list_index++; + + if (strncmp (name, text, len) == 0) + return (dupstr(name)); + @} + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +@} + +/* **************************************************************** */ +/* */ +/* FileMan Commands */ +/* */ +/* **************************************************************** */ + +/* String to pass to system (). This is for the LIST, VIEW and RENAME + commands. */ +static char syscom[1024]; + +/* List the file(s) named in arg. */ +com_list (arg) + char *arg; +@{ + if (!arg) + arg = ""; + + sprintf (syscom, "ls -FClg %s", arg); + return (system (syscom)); +@} + +com_view (arg) + char *arg; +@{ + if (!valid_argument ("view", arg)) + return 1; + +#if defined (__MSDOS__) + /* more.com doesn't grok slashes in pathnames */ + sprintf (syscom, "less %s", arg); +#else + sprintf (syscom, "more %s", arg); +#endif + return (system (syscom)); +@} + +com_rename (arg) + char *arg; +@{ + too_dangerous ("rename"); + return (1); +@} + +com_stat (arg) + char *arg; +@{ + struct stat finfo; + + if (!valid_argument ("stat", arg)) + return (1); + + if (stat (arg, &finfo) == -1) + @{ + perror (arg); + return (1); + @} + + printf ("Statistics for `%s':\n", arg); + + printf ("%s has %d link%s, and is %d byte%s in length.\n", + arg, + finfo.st_nlink, + (finfo.st_nlink == 1) ? "" : "s", + finfo.st_size, + (finfo.st_size == 1) ? "" : "s"); + printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime)); + printf (" Last access at: %s", ctime (&finfo.st_atime)); + printf (" Last modified at: %s", ctime (&finfo.st_mtime)); + return (0); +@} + +com_delete (arg) + char *arg; +@{ + too_dangerous ("delete"); + return (1); +@} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +com_help (arg) + char *arg; +@{ + register int i; + int printed = 0; + + for (i = 0; commands[i].name; i++) + @{ + if (!*arg || (strcmp (arg, commands[i].name) == 0)) + @{ + printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); + printed++; + @} + @} + + if (!printed) + @{ + printf ("No commands match `%s'. Possibilities are:\n", arg); + + for (i = 0; commands[i].name; i++) + @{ + /* Print in six columns. */ + if (printed == 6) + @{ + printed = 0; + printf ("\n"); + @} + + printf ("%s\t", commands[i].name); + printed++; + @} + + if (printed) + printf ("\n"); + @} + return (0); +@} + +/* Change to the directory ARG. */ +com_cd (arg) + char *arg; +@{ + if (chdir (arg) == -1) + @{ + perror (arg); + return 1; + @} + + com_pwd (""); + return (0); +@} + +/* Print out the current working directory. */ +com_pwd (ignore) + char *ignore; +@{ + char dir[1024], *s; + + s = getcwd (dir, sizeof(dir) - 1); + if (s == 0) + @{ + printf ("Error getting pwd: %s\n", dir); + return 1; + @} + + printf ("Current directory is %s\n", dir); + return 0; +@} + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +com_quit (arg) + char *arg; +@{ + done = 1; + return (0); +@} + +/* Function which tells you that you can't do this. */ +too_dangerous (caller) + char *caller; +@{ + fprintf (stderr, + "%s: Too dangerous for me to distribute. Write it yourself.\n", + caller); +@} + +/* 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; +@{ + if (!arg || !*arg) + @{ + fprintf (stderr, "%s: Argument required.\n", caller); + return (0); + @} + + return (1); +@} +@end smallexample diff --git a/lib/readline/doc/rluser.texi b/lib/readline/doc/rluser.texi new file mode 100644 index 0000000..cbcbb45 --- /dev/null +++ b/lib/readline/doc/rluser.texi @@ -0,0 +1,2488 @@ +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rluser.info +@comment %**end of header (This is for running Texinfo on a region.) + +@ignore +This file documents the end user interface to the GNU command line +editing features. It is to be an appendix to manuals for programs which +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--2022 Free Software Foundation, Inc. + +Authored by Brian Fox and Chet Ramey. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@comment If you are including this manual as an appendix, then set the +@comment variable readline-appendix. + +@ifclear BashFeatures +@defcodeindex bt +@end ifclear + +@node Command Line Editing +@chapter Command Line Editing + +This chapter describes the basic features of the @sc{gnu} +command line editing interface. +@ifset BashFeatures +Command line editing is provided by the Readline library, which is +used by several different programs, including Bash. +Command line editing is enabled by default when using an interactive shell, +unless the @option{--noediting} option is supplied at shell invocation. +Line editing is also used when using the @option{-e} option to the +@code{read} builtin command (@pxref{Bash Builtins}). +By default, the line editing commands are similar to those of Emacs. +A vi-style line editing interface is also available. +Line editing can be enabled at any time using the @option{-o emacs} or +@option{-o vi} options to the @code{set} builtin command +(@pxref{The Set Builtin}), or disabled using the @option{+o emacs} or +@option{+o vi} options to @code{set}. +@end ifset + +@menu +* Introduction and Notation:: Notation used in this text. +* Readline Interaction:: The minimum set of commands for editing a line. +* Readline Init File:: Customizing Readline from a user's view. +* Bindable Readline Commands:: A description of most of the Readline commands + available for binding +* Readline vi Mode:: A short description of how to make Readline + behave like the vi editor. +@ifset BashFeatures +* Programmable Completion:: How to specify the possible completions for + a specific command. +* Programmable Completion Builtins:: Builtin commands to specify how to + complete arguments for a particular command. +* A Programmable Completion Example:: An example shell function for + generating possible completions. +@end ifset +@end menu + +@node Introduction and Notation +@section Introduction to Line Editing + +The following paragraphs describe the notation used to represent +keystrokes. + +The text @kbd{C-k} is read as `Control-K' and describes the character +produced when the @key{k} key is pressed while the Control key +is depressed. + +The text @kbd{M-k} is read as `Meta-K' and describes the character +produced when the Meta key (if you have one) is depressed, and the @key{k} +key is pressed. +The Meta key is labeled @key{ALT} on many keyboards. +On keyboards with two keys labeled @key{ALT} (usually to either side of +the space bar), the @key{ALT} on the left side is generally set to +work as a Meta key. +The @key{ALT} key on the right may also be configured to work as a +Meta key or may be configured as some other modifier, such as a +Compose key for typing accented characters. + +If you do not have a Meta or @key{ALT} key, or another key working as +a Meta key, the identical keystroke can be generated by typing @key{ESC} +@emph{first}, and then typing @key{k}. +Either process is known as @dfn{metafying} the @key{k} key. + +The text @kbd{M-C-k} is read as `Meta-Control-k' and describes the +character produced by @dfn{metafying} @kbd{C-k}. + +In addition, several keys have their own names. Specifically, +@key{DEL}, @key{ESC}, @key{LFD}, @key{SPC}, @key{RET}, and @key{TAB} all +stand for themselves when seen in this text, or in an init file +(@pxref{Readline Init File}). +If your keyboard lacks a @key{LFD} key, typing @key{C-j} will +produce the desired character. +The @key{RET} key may be labeled @key{Return} or @key{Enter} on +some keyboards. + +@node Readline Interaction +@section Readline Interaction +@cindex interaction, readline + +Often during an interactive session you type in a long line of text, +only to notice that the first word on the line is misspelled. The +Readline library gives you a set of commands for manipulating the text +as you type it in, allowing you to just fix your typo, and not forcing +you to retype the majority of the line. Using these editing commands, +you move the cursor to the place that needs correction, and delete or +insert the text of the corrections. Then, when you are satisfied with +the line, you simply press @key{RET}. You do not have to be at the +end of the line to press @key{RET}; the entire line is accepted +regardless of the location of the cursor within the line. + +@menu +* Readline Bare Essentials:: The least you need to know about Readline. +* Readline Movement Commands:: Moving about the input line. +* Readline Killing Commands:: How to delete text, and how to get it back! +* Readline Arguments:: Giving numeric arguments to commands. +* Searching:: Searching through previous lines. +@end menu + +@node Readline Bare Essentials +@subsection Readline Bare Essentials +@cindex notation, readline +@cindex command editing +@cindex editing command lines + +In order to enter characters into the line, simply type them. The typed +character appears where the cursor was, and then the cursor moves one +space to the right. If you mistype a character, you can use your +erase character to back up and delete the mistyped character. + +Sometimes you may mistype a character, and +not notice the error until you have typed several other characters. In +that case, you can type @kbd{C-b} to move the cursor to the left, and then +correct your mistake. Afterwards, you can move the cursor to the right +with @kbd{C-f}. + +When you add text in the middle of a line, you will notice that characters +to the right of the cursor are `pushed over' to make room for the text +that you have inserted. Likewise, when you delete text behind the cursor, +characters to the right of the cursor are `pulled back' to fill in the +blank space created by the removal of the text. A list of the bare +essentials for editing the text of an input line follows. + +@table @asis +@item @kbd{C-b} +Move back one character. +@item @kbd{C-f} +Move forward one character. +@item @key{DEL} or @key{Backspace} +Delete the character to the left of the cursor. +@item @kbd{C-d} +Delete the character underneath the cursor. +@item @w{Printing characters} +Insert the character into the line at the cursor. +@item @kbd{C-_} or @kbd{C-x C-u} +Undo the last editing command. You can undo all the way back to an +empty line. +@end table + +@noindent +(Depending on your configuration, the @key{Backspace} key might be set to +delete the character to the left of the cursor and the @key{DEL} key set +to delete the character underneath the cursor, like @kbd{C-d}, rather +than the character to the left of the cursor.) + +@node Readline Movement Commands +@subsection Readline Movement Commands + + +The above table describes the most basic keystrokes that you need +in order to do editing of the input line. For your convenience, many +other commands have been added in addition to @kbd{C-b}, @kbd{C-f}, +@kbd{C-d}, and @key{DEL}. Here are some commands for moving more rapidly +about the line. + +@table @kbd +@item C-a +Move to the start of the line. +@item C-e +Move to the end of the line. +@item M-f +Move forward a word, where a word is composed of letters and digits. +@item M-b +Move backward a word. +@item C-l +Clear the screen, reprinting the current line at the top. +@end table + +Notice how @kbd{C-f} moves forward a character, while @kbd{M-f} moves +forward a word. It is a loose convention that control keystrokes +operate on characters while meta keystrokes operate on words. + +@node Readline Killing Commands +@subsection Readline Killing Commands + +@cindex killing text +@cindex yanking text + +@dfn{Killing} text means to delete the text from the line, but to save +it away for later use, usually by @dfn{yanking} (re-inserting) +it back into the line. +(`Cut' and `paste' are more recent jargon for `kill' and `yank'.) + +If the description for a command says that it `kills' text, then you can +be sure that you can get the text back in a different (or the same) +place later. + +When you use a kill command, the text is saved in a @dfn{kill-ring}. +Any number of consecutive kills save all of the killed text together, so +that when you yank it back, you get it all. The kill +ring is not line specific; the text that you killed on a previously +typed line is available to be yanked back later, when you are typing +another line. +@cindex kill ring + +Here is the list of commands for killing text. + +@table @kbd +@item C-k +Kill the text from the current cursor position to the end of the line. + +@item M-d +Kill from the cursor to the end of the current word, or, if between +words, to the end of the next word. +Word boundaries are the same as those used by @kbd{M-f}. + +@item M-@key{DEL} +Kill from the cursor to the start of the current word, or, if between +words, to the start of the previous word. +Word boundaries are the same as those used by @kbd{M-b}. + +@item C-w +Kill from the cursor to the previous whitespace. This is different than +@kbd{M-@key{DEL}} because the word boundaries differ. + +@end table + +Here is how to @dfn{yank} the text back into the line. Yanking +means to copy the most-recently-killed text from the kill buffer. + +@table @kbd +@item C-y +Yank the most recently killed text back into the buffer at the cursor. + +@item M-y +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is @kbd{C-y} or @kbd{M-y}. +@end table + +@node Readline Arguments +@subsection Readline Arguments + +You can pass numeric arguments to Readline commands. Sometimes the +argument acts as a repeat count, other times it is the @i{sign} of the +argument that is significant. If you pass a negative argument to a +command which normally acts in a forward direction, that command will +act in a backward direction. For example, to kill text back to the +start of the line, you might type @samp{M-- C-k}. + +The general way to pass numeric arguments to a command is to type meta +digits before the command. If the first `digit' typed is a minus +sign (@samp{-}), then the sign of the argument will be negative. Once +you have typed one meta digit to get the argument started, you can type +the remainder of the digits, and then the command. For example, to give +the @kbd{C-d} command an argument of 10, you could type @samp{M-1 0 C-d}, +which will delete the next ten characters on the input line. + +@node Searching +@subsection Searching for Commands in the History + +Readline provides commands for searching through the command history +@ifset BashFeatures +(@pxref{Bash History Facilities}) +@end ifset +for lines containing a specified string. +There are two search modes: @dfn{incremental} and @dfn{non-incremental}. + +Incremental searches begin before the user has finished typing the +search string. +As each character of the search string is typed, Readline displays +the next entry from the history matching the string typed so far. +An incremental search requires only as many characters as needed to +find the desired history entry. +To search backward in the history for a particular string, type +@kbd{C-r}. Typing @kbd{C-s} searches forward through the history. +The characters present in the value of the @code{isearch-terminators} variable +are used to terminate an incremental search. +If that variable has not been assigned a value, the @key{ESC} and +@kbd{C-J} characters will terminate an incremental search. +@kbd{C-g} will abort an incremental search and restore the original line. +When the search is terminated, the history entry containing the +search string becomes the current line. + +To find other matching entries in the history list, type @kbd{C-r} or +@kbd{C-s} as appropriate. +This will search backward or forward in the history for the next +entry matching the search string typed so far. +Any other key sequence bound to a Readline command will terminate +the search and execute that command. +For instance, a @key{RET} will terminate the search and accept +the line, thereby executing the command from the history list. +A movement command will terminate the search, make the last line found +the current line, and begin editing. + +Readline remembers the last incremental search string. If two +@kbd{C-r}s are typed without any intervening characters defining a new +search string, any remembered search string is used. + +Non-incremental searches read the entire search string before starting +to search for matching history lines. The search string may be +typed by the user or be part of the contents of the current line. + +@node Readline Init File +@section Readline Init File +@cindex initialization file, readline + +Although the Readline library comes with a set of Emacs-like +keybindings installed by default, it is possible to use a different set +of keybindings. +Any user can customize programs that use Readline by putting +commands in an @dfn{inputrc} file, +conventionally in their home directory. +The name of this +@ifset BashFeatures +file is taken from the value of the shell variable @env{INPUTRC}. If +@end ifset +@ifclear BashFeatures +file is taken from the value of the environment variable @env{INPUTRC}. If +@end ifclear +that variable is unset, the default is @file{~/.inputrc}. If that +file does not exist or cannot be read, the ultimate default is +@file{/etc/inputrc}. +@ifset BashFeatures +The @w{@code{bind}} builtin command can also be used to set Readline +keybindings and variables. +@xref{Bash Builtins}. +@end ifset + +When a program which uses the Readline library starts up, the +init file is read, and the key bindings are set. + +In addition, the @code{C-x C-r} command re-reads this init file, thus +incorporating any changes that you might have made to it. + +@menu +* Readline Init File Syntax:: Syntax for the commands in the inputrc file. + +* Conditional Init Constructs:: Conditional key bindings in the inputrc file. + +* Sample Init File:: An example inputrc file. +@end menu + +@node Readline Init File Syntax +@subsection Readline Init File Syntax + +There are only a few basic constructs allowed in the +Readline init file. Blank lines are ignored. +Lines beginning with a @samp{#} are comments. +Lines beginning with a @samp{$} indicate conditional +constructs (@pxref{Conditional Init Constructs}). Other lines +denote variable settings and key bindings. + +@table @asis +@item Variable Settings +You can modify the run-time behavior of Readline by +altering the values of variables in Readline +using the @code{set} command within the init file. +The syntax is simple: + +@example +set @var{variable} @var{value} +@end example + +@noindent +Here, for example, is how to +change from the default Emacs-like key binding to use +@code{vi} line editing commands: + +@example +set editing-mode vi +@end example + +Variable names and values, where appropriate, are recognized without regard +to case. Unrecognized variable names are ignored. + +Boolean variables (those that can be set to on or off) are set to on if +the value is null or empty, @var{on} (case-insensitive), or 1. Any other +value results in the variable being set to off. + +@ifset BashFeatures +The @w{@code{bind -V}} command lists the current Readline variable names +and values. @xref{Bash Builtins}. +@end ifset + +A great deal of run-time behavior is changeable with the following +variables. + +@cindex variables, readline +@table @code + +@item active-region-start-color +@vindex active-region-start-color +A string variable that controls the text color and background when displaying +the text in the active region (see the description of +@code{enable-active-region} below). +This string must not take up any physical character positions on the display, +so it should consist only of terminal escape sequences. +It is output to the terminal before displaying the text in the active region. +This variable is reset to the default value whenever the terminal type changes. +The default value is the string that puts the terminal in standout mode, +as obtained from the terminal's terminfo description. +A sample value might be @samp{\e[01;33m}. + +@item active-region-end-color +@vindex active-region-end-color +A string variable that "undoes" the effects of @code{active-region-start-color} +and restores "normal" terminal display appearance after displaying text +in the active region. +This string must not take up any physical character positions on the display, +so it should consist only of terminal escape sequences. +It is output to the terminal after displaying the text in the active region. +This variable is reset to the default value whenever the terminal type changes. +The default value is the string that restores the terminal from standout mode, +as obtained from the terminal's terminfo description. +A sample value might be @samp{\e[0m}. + +@item bell-style +@vindex bell-style +Controls what happens when Readline wants to ring the terminal bell. +If set to @samp{none}, Readline never rings the bell. If set to +@samp{visible}, Readline uses a visible bell if one is available. +If set to @samp{audible} (the default), Readline attempts to ring +the terminal's bell. + +@item bind-tty-special-chars +@vindex bind-tty-special-chars +If set to @samp{on} (the default), Readline attempts to bind the control +characters treated specially by the kernel's terminal driver to their +Readline equivalents. + +@item blink-matching-paren +@vindex blink-matching-paren +If set to @samp{on}, Readline attempts to briefly move the cursor to an +opening parenthesis when a closing parenthesis is inserted. The default +is @samp{off}. + +@item colored-completion-prefix +@vindex colored-completion-prefix +If set to @samp{on}, when listing completions, Readline displays the +common prefix of the set of possible completions using a different color. +The color definitions are taken from the value of the @env{LS_COLORS} +environment variable. +If there is a color definition in @env{LS_COLORS} for the custom suffix +@samp{readline-colored-completion-prefix}, Readline uses this color for +the common prefix instead of its default. +The default is @samp{off}. + +@item colored-stats +@vindex colored-stats +If set to @samp{on}, Readline displays possible completions using different +colors to indicate their file type. +The color definitions are taken from the value of the @env{LS_COLORS} +environment variable. +The default is @samp{off}. + +@item comment-begin +@vindex comment-begin +The string to insert at the beginning of the line when the +@code{insert-comment} command is executed. The default value +is @code{"#"}. + +@item completion-display-width +@vindex completion-display-width +The number of screen columns used to display possible matches +when performing completion. +The value is ignored if it is less than 0 or greater than the terminal +screen width. +A value of 0 will cause matches to be displayed one per line. +The default value is -1. + +@item completion-ignore-case +@vindex completion-ignore-case +If set to @samp{on}, Readline performs filename matching and completion +in a case-insensitive fashion. +The default value is @samp{off}. + +@item completion-map-case +@vindex completion-map-case +If set to @samp{on}, and @var{completion-ignore-case} is enabled, Readline +treats hyphens (@samp{-}) and underscores (@samp{_}) as equivalent when +performing case-insensitive filename matching and completion. +The default value is @samp{off}. + +@item completion-prefix-display-length +@vindex completion-prefix-display-length +The length in characters of the common prefix of a list of possible +completions that is displayed without modification. When set to a +value greater than zero, common prefixes longer than this value are +replaced with an ellipsis when displaying possible completions. + +@item completion-query-items +@vindex completion-query-items +The number of possible completions that determines when the user is +asked whether the list of possibilities should be displayed. +If the number of possible completions is greater than or equal to this value, +Readline will ask whether or not the user wishes to view them; +otherwise, they are simply listed. +This variable must be set to an integer value greater than or equal to zero. +A zero value means Readline should never ask; negative values are +treated as zero. +The default limit is @code{100}. + +@item convert-meta +@vindex convert-meta +If set to @samp{on}, Readline will convert characters with the +eighth bit set to an @sc{ascii} key sequence by stripping the eighth +bit and prefixing an @key{ESC} character, converting them to a +meta-prefixed key sequence. +The default value is @samp{on}, but +will be set to @samp{off} if the locale is one that contains +eight-bit characters. +This variable is dependent on the @code{LC_CTYPE} locale category, and +may change if the locale is changed. + +@item disable-completion +@vindex disable-completion +If set to @samp{On}, Readline will inhibit word completion. +Completion characters will be inserted into the line as if they had +been mapped to @code{self-insert}. The default is @samp{off}. + +@item echo-control-characters +@vindex echo-control-characters +When set to @samp{on}, on operating systems that indicate they support it, +Readline echoes a character corresponding to a signal generated from the +keyboard. The default is @samp{on}. + +@item editing-mode +@vindex editing-mode +The @code{editing-mode} variable controls which default set of +key bindings is used. By default, Readline starts up in Emacs editing +mode, where the keystrokes are most similar to Emacs. This variable can be +set to either @samp{emacs} or @samp{vi}. + +@item emacs-mode-string +@vindex emacs-mode-string +If the @var{show-mode-in-prompt} variable is enabled, +this string is displayed immediately before the last line of the primary +prompt when emacs editing mode is active. The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available. +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{@@}. + +@item enable-active-region +@vindex enable-active-region +The @dfn{point} is the current cursor position, and @dfn{mark} refers +to a saved cursor position (@pxref{Commands For Moving}). +The text between the point and mark is referred to as the @dfn{region}. +When this variable is set to @samp{On}, Readline allows certain commands +to designate the region as @dfn{active}. +When the region is active, Readline highlights the text in the region using +the value of the @code{active-region-start-color}, which defaults to the +string that enables +the terminal's standout mode. +The active region shows the text inserted by bracketed-paste and any +matching text found by incremental and non-incremental history searches. +The default is @samp{On}. + +@item enable-bracketed-paste +@vindex enable-bracketed-paste +When set to @samp{On}, Readline configures the terminal to insert each +paste into the editing buffer as a single string of characters, instead +of treating each character as if it had been read from the keyboard. +This is called putting the terminal into @dfn{bracketed paste mode}; +it prevents Readline from executing any editing commands bound to key +sequences appearing in the pasted text. +The default is @samp{On}. + +@item enable-keypad +@vindex enable-keypad +When set to @samp{on}, Readline will try to enable the application +keypad when it is called. Some systems need this to enable the +arrow keys. The default is @samp{off}. + +@item enable-meta-key +When set to @samp{on}, Readline will try to enable any meta modifier +key the terminal claims to support when it is called. On many terminals, +the meta key is used to send eight-bit characters. +The default is @samp{on}. + +@item expand-tilde +@vindex expand-tilde +If set to @samp{on}, tilde expansion is performed when Readline +attempts word completion. The default is @samp{off}. + +@item history-preserve-point +@vindex history-preserve-point +If set to @samp{on}, the history code attempts to place the point (the +current cursor position) at the +same location on each history line retrieved with @code{previous-history} +or @code{next-history}. The default is @samp{off}. + +@item history-size +@vindex history-size +Set the maximum number of history entries saved in the history list. +If set to zero, any existing history entries are deleted and no new entries +are saved. +If set to a value less than zero, the number of history entries is not +limited. +By default, the number of history entries is not limited. +If an attempt is made to set @var{history-size} to a non-numeric value, +the maximum number of history entries will be set to 500. + +@item horizontal-scroll-mode +@vindex horizontal-scroll-mode +This variable can be set to either @samp{on} or @samp{off}. Setting it +to @samp{on} means that the text of the lines being edited will scroll +horizontally on a single screen line when they are longer than the width +of the screen, instead of wrapping onto a new screen line. +This variable is automatically set to @samp{on} for terminals of height 1. +By default, this variable is set to @samp{off}. + +@item input-meta +@vindex input-meta +@vindex meta-flag +If set to @samp{on}, Readline will enable eight-bit input (it +will not clear the eighth bit in the characters it reads), +regardless of what the terminal claims it can support. The +default value is @samp{off}, but Readline will set it to @samp{on} if the +locale contains eight-bit characters. +The name @code{meta-flag} is a synonym for this variable. +This variable is dependent on the @code{LC_CTYPE} locale category, and +may change if the locale is changed. + +@item isearch-terminators +@vindex isearch-terminators +The string of characters that should terminate an incremental search without +subsequently executing the character as a command (@pxref{Searching}). +If this variable has not been given a value, the characters @key{ESC} and +@kbd{C-J} will terminate an incremental search. + +@item keymap +@vindex keymap +Sets Readline's idea of the current keymap for key binding commands. +Built-in @code{keymap} names are +@code{emacs}, +@code{emacs-standard}, +@code{emacs-meta}, +@code{emacs-ctlx}, +@code{vi}, +@code{vi-move}, +@code{vi-command}, and +@code{vi-insert}. +@code{vi} is equivalent to @code{vi-command} (@code{vi-move} is also a +synonym); @code{emacs} is equivalent to @code{emacs-standard}. +Applications may add additional names. +The default value is @code{emacs}. +The value of the @code{editing-mode} variable also affects the +default keymap. + +@item keyseq-timeout +Specifies the duration Readline will wait for a character when reading an +ambiguous key sequence (one that can form a complete key sequence using +the input read so far, or can take additional input to complete a longer +key sequence). +If no input is received within the timeout, Readline will use the shorter +but complete key sequence. +Readline uses this value to determine whether or not input is +available on the current input source (@code{rl_instream} by default). +The value is specified in milliseconds, so a value of 1000 means that +Readline will wait one second for additional input. +If this variable is set to a value less than or equal to zero, or to a +non-numeric value, Readline will wait until another key is pressed to +decide which key sequence to complete. +The default value is @code{500}. + +@item mark-directories +If set to @samp{on}, completed directory names have a slash +appended. The default is @samp{on}. + +@item mark-modified-lines +@vindex mark-modified-lines +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 +names begin with a @samp{.} (hidden files) when performing filename +completion. +If set to @samp{off}, the leading @samp{.} must be +supplied by the user in the filename to be completed. +This variable is @samp{on} by default. + +@item menu-complete-display-prefix +@vindex menu-complete-display-prefix +If set to @samp{on}, menu completion displays the common prefix of the +list of possible completions (which may be empty) before cycling through +the list. The default is @samp{off}. + +@item output-meta +@vindex output-meta +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}, but Readline will set it to @samp{on} if the +locale contains eight-bit characters. +This variable is dependent on the @code{LC_CTYPE} locale category, and +may change if the locale is changed. + +@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. +The default is @samp{off}. + +@item revert-all-at-newline +@vindex revert-all-at-newline +If set to @samp{on}, Readline will undo all changes to history lines +before returning when @code{accept-line} is executed. By default, +history lines may be modified and retain individual undo lists across +calls to @code{readline()}. The default is @samp{off}. + +@item show-all-if-ambiguous +@vindex show-all-if-ambiguous +This alters the default behavior of the completion functions. If +set to @samp{on}, +words which have more than one possible completion cause the +matches to be listed immediately instead of ringing the bell. +The default value is @samp{off}. + +@item show-all-if-unmodified +@vindex show-all-if-unmodified +This alters the default behavior of the completion functions in +a fashion similar to @var{show-all-if-ambiguous}. +If set to @samp{on}, +words which have more than one possible completion without any +possible partial completion (the possible completions don't share +a common prefix) cause the matches to be listed immediately instead +of ringing the bell. +The default value is @samp{off}. + +@item show-mode-in-prompt +@vindex show-mode-in-prompt +If set to @samp{on}, add a string to the beginning of the prompt +indicating the editing mode: emacs, vi command, or vi insertion. +The mode strings are user-settable (e.g., @var{emacs-mode-string}). +The default value is @samp{off}. + +@item skip-completed-text +@vindex skip-completed-text +If set to @samp{on}, this alters the default completion behavior when +inserting a single match into the line. It's only active when +performing completion in the middle of a word. If enabled, Readline +does not insert characters from the completion that match characters +after point in the word being completed, so portions of the word +following the cursor are not duplicated. +For instance, if this is enabled, attempting completion when the cursor +is after the @samp{e} in @samp{Makefile} will result in @samp{Makefile} +rather than @samp{Makefilefile}, assuming there is a single possible +completion. +The default value is @samp{off}. + +@item vi-cmd-mode-string +@vindex vi-cmd-mode-string +If the @var{show-mode-in-prompt} variable is enabled, +this string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in command mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available. +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{(cmd)}. + +@item vi-ins-mode-string +@vindex vi-ins-mode-string +If the @var{show-mode-in-prompt} variable is enabled, +this string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in insertion mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available. +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{(ins)}. + +@item visible-stats +@vindex visible-stats +If set to @samp{on}, a character denoting a file's type +is appended to the filename when listing possible +completions. The default is @samp{off}. + +@end table + +@item Key Bindings +The syntax for controlling key bindings in the init file is +simple. First you need to find the name of the command that you +want to change. The following sections contain tables of the command +name, the default keybinding, if any, and a short description of what +the command does. + +Once you know the name of the command, simply place on a line +in the init file the name of the key +you wish to bind the command to, a colon, and then the name of the +command. +There can be no space between the key name and the colon -- that will be +interpreted as part of the key name. +The name of the key can be expressed in different ways, depending on +what you find most comfortable. + +In addition to command names, Readline allows keys to be bound +to a string that is inserted when the key is pressed (a @var{macro}). + +@ifset BashFeatures +The @w{@code{bind -p}} command displays Readline function names and +bindings in a format that can be put directly into an initialization file. +@xref{Bash Builtins}. +@end ifset + +@table @asis +@item @w{@var{keyname}: @var{function-name} or @var{macro}} +@var{keyname} is the name of a key spelled out in English. For example: +@example +Control-u: universal-argument +Meta-Rubout: backward-kill-word +Control-o: "> output" +@end example + +In the example above, @kbd{C-u} is bound to the function +@code{universal-argument}, +@kbd{M-DEL} is bound to the function @code{backward-kill-word}, and +@kbd{C-o} is bound to run the macro +expressed on the right hand side (that is, to insert the text +@samp{> output} into the line). + +A number of symbolic character names are recognized while +processing this key binding syntax: +@var{DEL}, +@var{ESC}, +@var{ESCAPE}, +@var{LFD}, +@var{NEWLINE}, +@var{RET}, +@var{RETURN}, +@var{RUBOUT}, +@var{SPACE}, +@var{SPC}, +and +@var{TAB}. + +@item @w{"@var{keyseq}": @var{function-name} or @var{macro}} +@var{keyseq} differs from @var{keyname} above in that strings +denoting an entire key sequence can be specified, by placing +the key sequence in double quotes. Some @sc{gnu} Emacs style key +escapes can be used, as in the following example, but the +special character names are not recognized. + +@example +"\C-u": universal-argument +"\C-x\C-r": re-read-init-file +"\e[11~": "Function Key 1" +@end example + +In the above example, @kbd{C-u} is again bound to the function +@code{universal-argument} (just as it was in the first example), +@samp{@kbd{C-x} @kbd{C-r}} is bound to the function @code{re-read-init-file}, +and @samp{@key{ESC} @key{[} @key{1} @key{1} @key{~}} is bound to insert +the text @samp{Function Key 1}. + +@end table + +The following @sc{gnu} Emacs style escape sequences are available when +specifying key sequences: + +@table @code +@item @kbd{\C-} +control prefix +@item @kbd{\M-} +meta prefix +@item @kbd{\e} +an escape character +@item @kbd{\\} +backslash +@item @kbd{\"} +@key{"}, a double quotation mark +@item @kbd{\'} +@key{'}, a single quote or apostrophe +@end table + +In addition to the @sc{gnu} Emacs style escape sequences, a second +set of backslash escapes is available: + +@table @code +@item \a +alert (bell) +@item \b +backspace +@item \d +delete +@item \f +form feed +@item \n +newline +@item \r +carriage return +@item \t +horizontal tab +@item \v +vertical tab +@item \@var{nnn} +the eight-bit character whose value is the octal value @var{nnn} +(one to three digits) +@item \x@var{HH} +the eight-bit character whose value is the hexadecimal value @var{HH} +(one or two hex digits) +@end table + +When entering the text of a macro, single or double quotes must +be used to indicate a macro definition. +Unquoted text is assumed to be a function name. +In the macro body, the backslash escapes described above are expanded. +Backslash will quote any other character in the macro text, +including @samp{"} and @samp{'}. +For example, the following binding will make @samp{@kbd{C-x} \} +insert a single @samp{\} into the line: +@example +"\C-x\\": "\\" +@end example + +@end table + +@node Conditional Init Constructs +@subsection Conditional Init Constructs + +Readline implements a facility similar in spirit to the conditional +compilation features of the C preprocessor which allows key +bindings and variable settings to be performed as the result +of tests. There are four parser directives used. + +@table @code +@item $if +The @code{$if} construct allows bindings to be made based on the +editing mode, the terminal being used, or the application using +Readline. The text of the test, after any comparison operator, +extends to the end of the line; +unless otherwise noted, no characters are required to isolate it. + +@table @code +@item mode +The @code{mode=} form of the @code{$if} directive is used to test +whether Readline is in @code{emacs} or @code{vi} mode. +This may be used in conjunction +with the @samp{set keymap} command, for instance, to set bindings in +the @code{emacs-standard} and @code{emacs-ctlx} keymaps only if +Readline is starting out in @code{emacs} mode. + +@item term +The @code{term=} form may be used to include terminal-specific +key bindings, perhaps to bind the key sequences output by the +terminal's function keys. The word on the right side of the +@samp{=} is tested against both the full name of the terminal and +the portion of the terminal name before the first @samp{-}. This +allows @code{sun} to match both @code{sun} and @code{sun-cmd}, +for instance. + +@item version +The @code{version} test may be used to perform comparisons against +specific Readline versions. +The @code{version} expands to the current Readline version. +The set of comparison operators includes +@samp{=} (and @samp{==}), @samp{!=}, @samp{<=}, @samp{>=}, @samp{<}, +and @samp{>}. +The version number supplied on the right side of the operator consists +of a major version number, an optional decimal point, and an optional +minor version (e.g., @samp{7.1}). If the minor version is omitted, it +is assumed to be @samp{0}. +The operator may be separated from the string @code{version} and +from the version number argument by whitespace. +The following example sets a variable if the Readline version being used +is 7.0 or newer: +@example +$if version >= 7.0 +set show-mode-in-prompt on +$endif +@end example + +@item application +The @var{application} construct is used to include +application-specific settings. Each program using the Readline +library sets the @var{application name}, and you can test for +a particular value. +This could be used to bind key sequences to functions useful for +a specific program. For instance, the following command adds a +key sequence that quotes the current or previous word in Bash: +@example +$if Bash +# Quote the current or previous word +"\C-xq": "\eb\"\ef\"" +$endif +@end example + +@item variable +The @var{variable} construct provides simple equality tests for Readline +variables and values. +The permitted comparison operators are @samp{=}, @samp{==}, and @samp{!=}. +The variable name must be separated from the comparison operator by +whitespace; the operator may be separated from the value on the right hand +side by whitespace. +Both string and boolean variables may be tested. Boolean variables must be +tested against the values @var{on} and @var{off}. +The following example is equivalent to the @code{mode=emacs} test described +above: +@example +$if editing-mode == emacs +set show-mode-in-prompt on +$endif +@end example +@end table + +@item $endif +This command, as seen in the previous example, terminates an +@code{$if} command. + +@item $else +Commands in this branch of the @code{$if} directive are executed if +the test fails. + +@item $include +This directive takes a single filename as an argument and reads commands +and bindings from that file. +For example, the following directive reads from @file{/etc/inputrc}: +@example +$include /etc/inputrc +@end example +@end table + +@node Sample Init File +@subsection Sample Init File + +Here is an example of an @var{inputrc} file. This illustrates key +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. +# +# You can re-read the inputrc file with C-x C-r. +# Lines beginning with '#' are comments. +# +# First, include any system-wide bindings and variable +# assignments from /etc/Inputrc +$include /etc/Inputrc + +# +# Set various bindings for emacs mode. + +set editing-mode emacs + +$if mode=emacs + +Meta-Control-h: backward-kill-word Text after the function name is ignored + +# +# Arrow keys in keypad mode +# +#"\M-OD": backward-char +#"\M-OC": forward-char +#"\M-OA": previous-history +#"\M-OB": next-history +# +# Arrow keys in ANSI mode +# +"\M-[D": backward-char +"\M-[C": forward-char +"\M-[A": previous-history +"\M-[B": next-history +# +# Arrow keys in 8 bit keypad mode +# +#"\M-\C-OD": backward-char +#"\M-\C-OC": forward-char +#"\M-\C-OA": previous-history +#"\M-\C-OB": next-history +# +# Arrow keys in 8 bit ANSI mode +# +#"\M-\C-[D": backward-char +#"\M-\C-[C": forward-char +#"\M-\C-[A": previous-history +#"\M-\C-[B": next-history + +C-q: quoted-insert + +$endif + +# An old-style binding. This happens to be the default. +TAB: complete + +# Macros that are convenient for shell interaction +$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 +# and move to just after the open quote +"\C-x\"": "\"\"\C-b" +# insert a backslash (testing backslash escapes +# in sequences and macros) +"\C-x\\": "\\" +# Quote the current or previous word +"\C-xq": "\eb\"\ef\"" +# Add a binding to refresh the line, which is unbound +"\C-xr": redraw-current-line +# Edit variable on current line. +"\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" +$endif + +# use a visible bell if one is available +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 +set convert-meta off + +# display characters with the eighth bit set directly +# rather than as meta-prefixed characters +set output-meta on + +# if there are 150 or more possible completions for a word, +# ask whether or not the user wants to see all of them +set completion-query-items 150 + +# For FTP +$if Ftp +"\C-xg": "get \M-?" +"\C-xt": "put \M-?" +"\M-.": yank-last-arg +$endif +@end example + +@node Bindable Readline Commands +@section Bindable Readline Commands + +@menu +* Commands For Moving:: Moving about the line. +* Commands For History:: Getting at previous lines. +* Commands For Text:: Commands for changing text. +* Commands For Killing:: Commands for killing and yanking. +* Numeric Arguments:: Specifying numeric arguments, repeat counts. +* Commands For Completion:: Getting Readline to do the typing for you. +* Keyboard Macros:: Saving and re-executing typed characters +* Miscellaneous Commands:: Other miscellaneous commands. +@end menu + +This section describes Readline commands that may be bound to key +sequences. +@ifset BashFeatures +You can list your key bindings by executing +@w{@code{bind -P}} or, for a more terse format, suitable for an +@var{inputrc} file, @w{@code{bind -p}}. (@xref{Bash Builtins}.) +@end ifset +Command names without an accompanying key sequence are unbound by default. + +In the following descriptions, @dfn{point} refers to the current cursor +position, and @dfn{mark} refers to a cursor position saved by the +@code{set-mark} command. +The text between the point and mark is referred to as the @dfn{region}. + +@node Commands For Moving +@subsection Commands For Moving +@ftable @code +@item beginning-of-line (C-a) +Move to the start of the current line. + +@item end-of-line (C-e) +Move to the end of the line. + +@item forward-char (C-f) +Move forward a character. + +@item backward-char (C-b) +Move back a character. + +@item forward-word (M-f) +Move forward to the end of the next word. +Words are composed of letters and digits. + +@item backward-word (M-b) +Move back to the start of the current or previous word. +Words are composed of letters and digits. + +@ifset BashFeatures +@item shell-forward-word (M-C-f) +Move forward to the end of the next word. +Words are delimited by non-quoted shell metacharacters. + +@item shell-backward-word (M-C-b) +Move back to the start of the current or previous word. +Words are delimited by non-quoted shell metacharacters. +@end ifset + +@item previous-screen-line () +Attempt to move point to the same physical screen column on the previous +physical screen line. This will not have the desired effect if the current +Readline line does not take up more than one physical line or if point is not +greater than the length of the prompt plus the screen width. + +@item next-screen-line () +Attempt to move point to the same physical screen column on the next +physical screen line. This will not have the desired effect if the current +Readline line does not take up more than one physical line or if the length +of the current Readline line is not greater than the length of the prompt +plus the screen width. + +@item clear-display (M-C-l) +Clear the screen and, if possible, the terminal's scrollback buffer, +then redraw the current line, +leaving the current line at the top of the screen. + +@item clear-screen (C-l) +Clear the screen, +then redraw the current line, +leaving the current line at the top of the screen. + +@item redraw-current-line () +Refresh the current line. By default, this is unbound. + +@end ftable + +@node Commands For History +@subsection Commands For Manipulating The History + +@ftable @code +@item accept-line (Newline or Return) +@ifset BashFeatures +Accept the line regardless of where the cursor is. +If this line is +non-empty, add it to the history list according to the setting of +the @env{HISTCONTROL} and @env{HISTIGNORE} variables. +If this line is a modified history line, then restore the history line +to its original state. +@end ifset +@ifclear BashFeatures +Accept the line regardless of where the cursor is. +If this line is +non-empty, it may be added to the history list for future recall with +@code{add_history()}. +If this line is a modified history line, the history line is restored +to its original state. +@end ifclear + +@item previous-history (C-p) +Move `back' through the history list, fetching the previous command. + +@item next-history (C-n) +Move `forward' through the history list, fetching the next command. + +@item beginning-of-history (M-<) +Move to the first line in the history. + +@item end-of-history (M->) +Move to the end of the input history, i.e., the line currently +being entered. + +@item reverse-search-history (C-r) +Search backward starting at the current line and moving `up' through +the history as necessary. This is an incremental search. +This command sets the region to the matched text and activates the mark. + +@item forward-search-history (C-s) +Search forward starting at the current line and moving `down' through +the history as necessary. This is an incremental search. +This command sets the region to the matched text and activates the mark. + +@item non-incremental-reverse-search-history (M-p) +Search backward starting at the current line and moving `up' +through the history as necessary using a non-incremental search +for a string supplied by the user. +The search string may match anywhere in a history line. + +@item non-incremental-forward-search-history (M-n) +Search forward starting at the current line and moving `down' +through the history as necessary using a non-incremental search +for a string supplied by the user. +The search string may match anywhere in a history line. + +@item history-search-forward () +Search forward through the history for the string of characters +between the start of the current line and the point. +The search string must match at the beginning of a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-search-backward () +Search backward through the history for the string of characters +between the start of the current line and the point. +The search string must match at the beginning of a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-substring-search-forward () +Search forward through the history for the string of characters +between the start of the current line and the point. +The search string may match anywhere in a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-substring-search-backward () +Search backward through the history for the string of characters +between the start of the current line and the point. +The search string may match anywhere in a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item yank-nth-arg (M-C-y) +Insert the first argument to the previous command (usually +the second word on the previous line) at point. +With an argument @var{n}, +insert the @var{n}th word from the previous command (the words +in the previous command begin with word 0). A negative argument +inserts the @var{n}th word from the end of the previous command. +Once the argument @var{n} is computed, the argument is extracted +as if the @samp{!@var{n}} history expansion had been specified. + +@item yank-last-arg (M-. or M-_) +Insert last argument to the previous command (the last word of the +previous history entry). +With a numeric argument, behave exactly like @code{yank-nth-arg}. +Successive calls to @code{yank-last-arg} move back through the history +list, inserting the last word (or the word specified by the argument to +the first call) of each line in turn. +Any numeric argument supplied to these successive calls determines +the direction to move through the history. A negative argument switches +the direction through the history (back or forward). +The history expansion facilities are used to extract the last argument, +as if the @samp{!$} history expansion had been specified. + +@item operate-and-get-next (C-o) +Accept the current line for return to the calling application as if a +newline had been entered, +and fetch the next line relative to the current line from the history +for editing. +A numeric argument, if supplied, specifies the history entry to use instead +of the current line. + +@item fetch-history () +With a numeric argument, fetch that entry from the history list +and make it the current line. +Without an argument, move back to the first entry in the history list. + +@end ftable + +@node Commands For Text +@subsection Commands For Changing Text + +@ftable @code + +@item @i{end-of-file} (usually C-d) +The character indicating end-of-file as set, for example, by +@code{stty}. If this character is read when there are no characters +on the line, and point is at the beginning of the line, Readline +interprets it as the end of input and returns @sc{eof}. + +@item delete-char (C-d) +Delete the character at point. If this function is bound to the +same character as the tty @sc{eof} character, as @kbd{C-d} +commonly is, see above for the effects. + +@item backward-delete-char (Rubout) +Delete the character behind the cursor. A numeric argument means +to kill the characters instead of deleting them. + +@item forward-backward-delete-char () +Delete the character under the cursor, unless the cursor is at the +end of the line, in which case the character behind the cursor is +deleted. By default, this is not bound to a key. + +@item quoted-insert (C-q or C-v) +Add the next character typed to the line verbatim. This is +how to insert key sequences like @kbd{C-q}, for example. + +@ifclear BashFeatures +@item tab-insert (M-@key{TAB}) +Insert a tab character. +@end ifclear + +@item self-insert (a, b, A, 1, !, @dots{}) +Insert yourself. + +@item bracketed-paste-begin () +This function is intended to be bound to the "bracketed paste" escape +sequence sent by some terminals, and such a binding is assigned by default. +It allows Readline to insert the pasted text as a single unit without treating +each character as if it had been read from the keyboard. The characters +are inserted as if each one was bound to @code{self-insert} instead of +executing any editing commands. + +Bracketed paste sets the region (the characters between point and the mark) +to the inserted text. It uses the concept of an @emph{active mark}: when the +mark is active, Readline redisplay uses the terminal's standout mode to +denote the region. + +@item transpose-chars (C-t) +Drag the character before the cursor forward over +the character at the cursor, moving the +cursor forward as well. If the insertion point +is at the end of the line, then this +transposes the last two characters of the line. +Negative arguments have no effect. + +@item transpose-words (M-t) +Drag the word before point past the word after point, +moving point past that word as well. +If the insertion point is at the end of the line, this transposes +the last two words on the line. + +@item upcase-word (M-u) +Uppercase the current (or following) word. With a negative argument, +uppercase the previous word, but do not move the cursor. + +@item downcase-word (M-l) +Lowercase the current (or following) word. With a negative argument, +lowercase the previous word, but do not move the cursor. + +@item capitalize-word (M-c) +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 +@subsection Killing And Yanking + +@ftable @code + +@item kill-line (C-k) +Kill the text from point to the end of the line. +With a negative numeric argument, kill backward from the cursor to the +beginning of the current line. + +@item backward-kill-line (C-x Rubout) +Kill backward from the cursor to the beginning of the current line. +With a negative numeric argument, kill forward from the cursor to the +end of the current line. + +@item unix-line-discard (C-u) +Kill backward from the cursor to the beginning of the current line. + +@item kill-whole-line () +Kill all characters on the current line, no matter where point is. +By default, this is unbound. + +@item kill-word (M-d) +Kill from point to the end of the current word, or if between +words, to the end of the next word. +Word boundaries are the same as @code{forward-word}. + +@item backward-kill-word (M-@key{DEL}) +Kill the word behind point. +Word boundaries are the same as @code{backward-word}. + +@ifset BashFeatures +@item shell-kill-word (M-C-d) +Kill from point to the end of the current word, or if between +words, to the end of the next word. +Word boundaries are the same as @code{shell-forward-word}. + +@item shell-backward-kill-word () +Kill the word behind point. +Word boundaries are the same as @code{shell-backward-word}. +@end ifset + +@item shell-transpose-words (M-C-t) +Drag the word before point past the word after point, +moving point past that word as well. +If the insertion point is at the end of the line, this transposes +the last two words on the line. +Word boundaries are the same as @code{shell-forward-word} and +@code{shell-backward-word}. + +@item unix-word-rubout (C-w) +Kill the word behind point, using white space as a word boundary. +The killed text is saved on the kill-ring. + +@item unix-filename-rubout () +Kill the word behind point, using white space and the slash character +as the word boundaries. +The killed text is saved on the kill-ring. + +@item delete-horizontal-space () +Delete all spaces and tabs around point. By default, this is unbound. + +@item kill-region () +Kill the text in the current region. +By default, this command is unbound. + +@item copy-region-as-kill () +Copy the text in the region to the kill buffer, so it can be yanked +right away. By default, this command is unbound. + +@item copy-backward-word () +Copy the word before point to the kill buffer. +The word boundaries are the same as @code{backward-word}. +By default, this command is unbound. + +@item copy-forward-word () +Copy the word following point to the kill buffer. +The word boundaries are the same as @code{forward-word}. +By default, this command is unbound. + +@item yank (C-y) +Yank the top of the kill ring into the buffer at point. + +@item yank-pop (M-y) +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is @code{yank} or @code{yank-pop}. +@end ftable + +@node Numeric Arguments +@subsection Specifying Numeric Arguments +@ftable @code + +@item digit-argument (@kbd{M-0}, @kbd{M-1}, @dots{} @kbd{M--}) +Add this digit to the argument already accumulating, or start a new +argument. @kbd{M--} starts a negative argument. + +@item universal-argument () +This is another way to specify an argument. +If this command is followed by one or more digits, optionally with a +leading minus sign, those digits define the argument. +If the command is followed by digits, executing @code{universal-argument} +again ends the numeric argument, but is otherwise ignored. +As a special case, if this command is immediately followed by a +character that is neither a digit nor minus sign, the argument count +for the next command is multiplied by four. +The argument count is initially one, so executing this function the +first time makes the argument count four, a second time makes the +argument count sixteen, and so on. +By default, this is not bound to a key. +@end ftable + +@node Commands For Completion +@subsection Letting Readline Type For You + +@ftable @code +@item complete (@key{TAB}) +Attempt to perform completion on the text before point. +The actual completion performed is application-specific. +@ifset BashFeatures +Bash attempts completion treating the text as a variable (if the +text begins with @samp{$}), username (if the text begins with +@samp{~}), hostname (if the text begins with @samp{@@}), or +command (including aliases and functions) in turn. If none +of these produces a match, filename completion is attempted. +@end ifset +@ifclear BashFeatures +The default is filename completion. +@end ifclear + +@item possible-completions (M-?) +List the possible completions of the text before point. +When displaying completions, Readline sets the number of columns used +for display to the value of @code{completion-display-width}, the value of +the environment variable @env{COLUMNS}, or the screen width, in that order. + +@item insert-completions (M-*) +Insert all completions of the text before point that would have +been generated by @code{possible-completions}. + +@item menu-complete () +Similar to @code{complete}, but replaces the word to be completed +with a single match from the list of possible completions. +Repeated execution of @code{menu-complete} steps through the list +of possible completions, inserting each match in turn. +At the end of the list of completions, the bell is rung +(subject to the setting of @code{bell-style}) +and the original text is restored. +An argument of @var{n} moves @var{n} positions forward in the list +of matches; a negative argument may be used to move backward +through the list. +This command is intended to be bound to @key{TAB}, but is unbound +by default. + +@item menu-complete-backward () +Identical to @code{menu-complete}, but moves backward through the list +of possible completions, as if @code{menu-complete} had been given a +negative argument. + +@item delete-char-or-list () +Deletes the character under the cursor if not at the beginning or +end of the line (like @code{delete-char}). +If at the end of the line, behaves identically to +@code{possible-completions}. +This command is unbound by default. + +@ifset BashFeatures +@item complete-filename (M-/) +Attempt filename completion on the text before point. + +@item possible-filename-completions (C-x /) +List the possible completions of the text before point, +treating it as a filename. + +@item complete-username (M-~) +Attempt completion on the text before point, treating +it as a username. + +@item possible-username-completions (C-x ~) +List the possible completions of the text before point, +treating it as a username. + +@item complete-variable (M-$) +Attempt completion on the text before point, treating +it as a shell variable. + +@item possible-variable-completions (C-x $) +List the possible completions of the text before point, +treating it as a shell variable. + +@item complete-hostname (M-@@) +Attempt completion on the text before point, treating +it as a hostname. + +@item possible-hostname-completions (C-x @@) +List the possible completions of the text before point, +treating it as a hostname. + +@item complete-command (M-!) +Attempt completion on the text before point, treating +it as a command name. Command completion attempts to +match the text against aliases, reserved words, shell +functions, shell builtins, and finally executable filenames, +in that order. + +@item possible-command-completions (C-x !) +List the possible completions of the text before point, +treating it as a command name. + +@item dynamic-complete-history (M-@key{TAB}) +Attempt completion on the text before point, comparing +the text against lines from the history list for possible +completion matches. + +@item dabbrev-expand () +Attempt menu completion on the text before point, comparing +the text against lines from the history list for possible +completion matches. + +@item complete-into-braces (M-@{) +Perform filename completion and insert the list of possible completions +enclosed within braces so the list is available to the shell +(@pxref{Brace Expansion}). + +@end ifset +@end ftable + +@node Keyboard Macros +@subsection Keyboard Macros +@ftable @code + +@item start-kbd-macro (C-x () +Begin saving the characters typed into the current keyboard macro. + +@item end-kbd-macro (C-x )) +Stop saving the characters typed into the current keyboard macro +and save the definition. + +@item call-last-kbd-macro (C-x e) +Re-execute the last keyboard macro defined, by making the characters +in the macro appear as if typed at the keyboard. + +@item print-last-kbd-macro () +Print the last keyboard macro defined in a format suitable for the +@var{inputrc} file. + +@end ftable + +@node Miscellaneous Commands +@subsection Some Miscellaneous Commands +@ftable @code + +@item re-read-init-file (C-x C-r) +Read in the contents of the @var{inputrc} file, and incorporate +any bindings or variable assignments found there. + +@item abort (C-g) +Abort the current editing command and +ring the terminal's bell (subject to the setting of +@code{bell-style}). + +@item do-lowercase-version (M-A, M-B, M-@var{x}, @dots{}) +If the metafied character @var{x} is upper case, run the command +that is bound to the corresponding metafied lower case character. +The behavior is undefined if @var{x} is already lower case. + +@item prefix-meta (@key{ESC}) +Metafy the next character typed. This is for keyboards +without a meta key. Typing @samp{@key{ESC} f} is equivalent to typing +@kbd{M-f}. + +@item undo (C-_ or C-x C-u) +Incremental undo, separately remembered for each line. + +@item revert-line (M-r) +Undo all changes made to this line. This is like executing the @code{undo} +command enough times to get back to the beginning. + +@ifset BashFeatures +@item tilde-expand (M-&) +@end ifset +@ifclear BashFeatures +@item tilde-expand (M-~) +@end ifclear +Perform tilde expansion on the current word. + +@item set-mark (C-@@) +Set the mark to the point. If a +numeric argument is supplied, the mark is set to that position. + +@item exchange-point-and-mark (C-x C-x) +Swap the point with the mark. The current cursor position is set to +the saved position, and the old cursor position is saved as the mark. + +@item character-search (C-]) +A character is read and point is moved to the next occurrence of that +character. A negative argument searches for previous occurrences. + +@item character-search-backward (M-C-]) +A character is read and point is moved to the previous occurrence +of that character. A negative argument searches for subsequent +occurrences. + +@item skip-csi-sequence () +Read enough characters to consume a multi-key sequence such as those +defined for keys like Home and End. Such sequences begin with a +Control Sequence Indicator (CSI), usually ESC-[. If this sequence is +bound to "\e[", keys producing such sequences will have no effect +unless explicitly bound to a Readline command, instead of inserting +stray characters into the editing buffer. This is unbound by default, +but usually bound to ESC-[. + +@item insert-comment (M-#) +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 () +Print all of the functions and their key bindings to the +Readline output stream. If a numeric argument is supplied, +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. + +@item dump-variables () +Print all of the settable variables and their values to the +Readline output stream. If a numeric argument is supplied, +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. + +@item dump-macros () +Print all of the Readline key sequences bound to macros and the +strings they output. If a numeric argument is supplied, +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 spell-correct-word (C-x s) +Perform spelling correction on the current word, treating it as a directory +or filename, in the same way as the @code{cdspell} shell option. +Word boundaries are the same as those used by @code{shell-forward-word}. + +@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. + +@item shell-expand-line (M-C-e) +Expand the line as the shell does. +This performs alias and history expansion as well as all of the shell +word expansions (@pxref{Shell Expansions}). + +@item history-expand-line (M-^) +Perform history expansion on the current line. + +@item magic-space () +Perform history expansion on the current line and insert a space +(@pxref{History Interaction}). + +@item alias-expand-line () +Perform alias expansion on the current line (@pxref{Aliases}). + +@item history-and-alias-expand-line () +Perform history and alias expansion on the current line. + +@item insert-last-argument (M-. or M-_) +A synonym for @code{yank-last-arg}. + +@item edit-and-execute-command (C-x C-e) +Invoke an editor on the current command line, and execute the result as shell +commands. +Bash attempts to invoke +@code{$VISUAL}, @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 +@section Readline vi Mode + +While the Readline library does not have a full set of @code{vi} +editing functions, it does contain enough to allow simple editing +of the line. The Readline @code{vi} mode behaves as specified in +the @sc{posix} standard. + +@ifset BashFeatures +In order to switch interactively between @code{emacs} and @code{vi} +editing modes, use the @samp{set -o emacs} and @samp{set -o vi} +commands (@pxref{The Set Builtin}). +@end ifset +@ifclear BashFeatures +In order to switch interactively between @code{emacs} and @code{vi} +editing modes, use the command @kbd{M-C-j} (bound to emacs-editing-mode +when in @code{vi} mode and to vi-editing-mode in @code{emacs} mode). +@end ifclear +The Readline default is @code{emacs} mode. + +When you enter a line in @code{vi} mode, you are already placed in +`insertion' mode, as if you had typed an @samp{i}. Pressing @key{ESC} +switches you into `command' mode, where you can edit the text of the +line with the standard @code{vi} movement keys, move to previous +history lines with @samp{k} and subsequent lines with @samp{j}, and +so forth. + +@ifset BashFeatures +@node Programmable Completion +@section Programmable Completion +@cindex programmable completion + +When word completion is attempted for an argument to a command for +which a completion specification (a @var{compspec}) has been defined +using the @code{complete} builtin (@pxref{Programmable Completion Builtins}), +the programmable completion facilities are invoked. + +First, the command name is identified. +If a compspec has been defined for that command, the +compspec is used to generate the list of possible completions for the word. +If the command word is the empty string (completion attempted at the +beginning of an empty line), any compspec defined with +the @option{-E} option to @code{complete} is used. +If the command word is a full pathname, a compspec for the full +pathname is searched for first. +If no compspec is found for the full pathname, an attempt is made to +find a compspec for the portion following the final slash. +If those searches do not result in a compspec, any compspec defined with +the @option{-D} option to @code{complete} is used as the default. +If there is no default compspec, Bash attempts alias expansion +on the command word as a final resort, and attempts to find a compspec +for the command word from any successful expansion + +Once a compspec has been found, it is used to generate the list of +matching words. +If a compspec is not found, the default Bash completion +described above (@pxref{Commands For Completion}) is performed. + +First, the actions specified by the compspec are used. +Only matches which are prefixed by the word being completed are +returned. +When the @option{-f} or @option{-d} option is used for filename or +directory name completion, the shell variable @env{FIGNORE} is +used to filter the matches. +@xref{Bash Variables}, for a description of @env{FIGNORE}. + +Any completions specified by a filename expansion pattern to the +@option{-G} option are generated next. +The words generated by the pattern need not match the word being completed. +The @env{GLOBIGNORE} shell variable is not used to filter the matches, +but the @env{FIGNORE} shell variable is used. + +Next, the string specified as the argument to the @option{-W} option +is considered. +The string is first split using the characters in the @env{IFS} +special variable as delimiters. +Shell quoting is honored within the string, in order to provide a +mechanism for the words to contain shell metacharacters or characters +in the value of @env{IFS}. +Each word is then expanded using +brace expansion, tilde expansion, parameter and variable expansion, +command substitution, and arithmetic expansion, +as described above (@pxref{Shell Expansions}). +The results are split using the rules described above +(@pxref{Word Splitting}). +The results of the expansion are prefix-matched against the word being +completed, and the matching words become the possible completions. + +After these matches have been generated, any shell function or command +specified with the @option{-F} and @option{-C} options is invoked. +When the command or function is invoked, the @env{COMP_LINE}, +@env{COMP_POINT}, @env{COMP_KEY}, and @env{COMP_TYPE} variables are +assigned values as described above (@pxref{Bash Variables}). +If a shell function is being invoked, the @env{COMP_WORDS} and +@env{COMP_CWORD} variables are also set. +When the function or command is invoked, the first argument ($1) is the +name of the command whose arguments are being completed, the +second argument ($2) is the word being completed, and the third argument +($3) is the word preceding the word being completed on the current command +line. +No filtering of the generated completions against the word being completed +is performed; the function or command has complete freedom in generating +the matches. + +Any function specified with @option{-F} is invoked first. +The function may use any of the shell facilities, including the +@code{compgen} and @code{compopt} builtins described below +(@pxref{Programmable Completion Builtins}), to generate the matches. +It must put the possible completions in the @env{COMPREPLY} array +variable, one per array element. + +Next, any command specified with the @option{-C} option is invoked +in an environment equivalent to command substitution. +It should print a list of completions, one per line, to +the standard output. +Backslash may be used to escape a newline, if necessary. + +After all of the possible completions are generated, any filter +specified with the @option{-X} option is applied to the list. +The filter is a pattern as used for pathname expansion; a @samp{&} +in the pattern is replaced with the text of the word being completed. +A literal @samp{&} may be escaped with a backslash; the backslash +is removed before attempting a match. +Any completion that matches the pattern will be removed from the list. +A leading @samp{!} negates the pattern; in this case any completion +not matching the pattern will be removed. +If the @code{nocasematch} shell option +(see the description of @code{shopt} in @ref{The Shopt Builtin}) +is enabled, the match is performed without regard to the case +of alphabetic characters. + +Finally, any prefix and suffix specified with the @option{-P} and @option{-S} +options are added to each member of the completion list, and the result is +returned to the Readline completion code as the list of possible +completions. + +If the previously-applied actions do not generate any matches, and the +@option{-o dirnames} option was supplied to @code{complete} when the +compspec was defined, directory name completion is attempted. + +If the @option{-o plusdirs} option was supplied to @code{complete} when +the compspec was defined, directory name completion is attempted and any +matches are added to the results of the other actions. + +By default, if a compspec is found, whatever it generates is returned to +the completion code as the full set of possible completions. +The default Bash completions are not attempted, and the Readline default +of filename completion is disabled. +If the @option{-o bashdefault} option was supplied to @code{complete} when +the compspec was defined, the default Bash completions are attempted +if the compspec generates no matches. +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 (and, if attempted, the default Bash completions) +generate 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. + +There is some support for dynamically modifying completions. This is +most useful when used in combination with a default completion specified +with @option{-D}. It's possible for shell functions executed as completion +handlers to indicate that completion should be retried by returning an +exit status of 124. If a shell function returns 124, and changes +the compspec associated with the command on which completion is being +attempted (supplied as the first argument when the function is executed), +programmable completion restarts from the beginning, with an +attempt to find a new compspec for that command. This allows a set of +completions to be built dynamically as completion is attempted, rather than +being loaded all at once. + +For instance, assuming that there is a library of compspecs, each kept in a +file corresponding to the name of the command, the following default +completion function would load completions dynamically: + +@example +_completion_loader() +@{ + . "/etc/bash_completion.d/$1.sh" >/dev/null 2>&1 && return 124 +@} +complete -D -F _completion_loader -o bashdefault -o default +@end example + +@node Programmable Completion Builtins +@section Programmable Completion Builtins +@cindex completion builtins + +Three builtin commands are available to manipulate the programmable completion +facilities: one to specify how the arguments to a particular command are to +be completed, and two to modify the completion as it is happening. + +@table @code +@item compgen +@btindex compgen +@example +@code{compgen [@var{option}] [@var{word}]} +@end example + +Generate possible completion matches for @var{word} according to +the @var{option}s, which may be any option accepted by the +@code{complete} +builtin with the exception of @option{-p} and @option{-r}, and write +the matches to the standard output. +When using the @option{-F} or @option{-C} options, the various shell variables +set by the programmable completion facilities, while available, will not +have useful values. + +The matches will be generated in the same way as if the programmable +completion code had generated them directly from a completion specification +with the same flags. +If @var{word} is specified, only those completions matching @var{word} +will be displayed. + +The return value is true unless an invalid option is supplied, or no +matches were generated. + +@item complete +@btindex complete +@example +@code{complete [-abcdefgjksuv] [-o @var{comp-option}] [-DEI] [-A @var{action}] [-G @var{globpat}] +[-W @var{wordlist}] [-F @var{function}] [-C @var{command}] [-X @var{filterpat}] +[-P @var{prefix}] [-S @var{suffix}] @var{name} [@var{name} @dots{}]} +@code{complete -pr [-DEI] [@var{name} @dots{}]} +@end example + +Specify how arguments to each @var{name} should be completed. +If the @option{-p} option is supplied, or if no options are supplied, existing +completion specifications are printed in a way that allows them to be +reused as input. +The @option{-r} option removes a completion specification for +each @var{name}, or, if no @var{name}s are supplied, all +completion specifications. +The @option{-D} option indicates that other supplied options and actions should +apply to the ``default'' command completion; that is, completion attempted +on a command for which no completion has previously been defined. +The @option{-E} option indicates that other supplied options and actions should +apply to ``empty'' command completion; that is, completion attempted on a +blank line. +The @option{-I} option indicates that other supplied options and actions should +apply to completion on the initial non-assignment word on the line, or after a +command delimiter such as @samp{;} or @samp{|}, which is usually command +name completion. +If multiple options are supplied, the @option{-D} option takes precedence +over @option{-E}, and both take precedence over @option{-I}. +If any of @option{-D}, @option{-E}, or @option{-I} are supplied, any other +@var{name} arguments are ignored; these completions only apply to the case +specified by the option. + +The process of applying these completion specifications when word completion +is attempted is described above (@pxref{Programmable Completion}). + +Other options, if specified, have the following meanings. +The arguments to the @option{-G}, @option{-W}, and @option{-X} options +(and, if necessary, the @option{-P} and @option{-S} options) +should be quoted to protect them from expansion before the +@code{complete} builtin is invoked. + + +@table @code +@item -o @var{comp-option} +The @var{comp-option} controls several aspects of the compspec's behavior +beyond the simple generation of completions. +@var{comp-option} may be one of: + +@table @code + +@item bashdefault +Perform the rest of the default Bash completions if the compspec +generates no matches. + +@item default +Use Readline's default filename completion if the compspec generates +no matches. + +@item dirnames +Perform directory name completion if the compspec generates no matches. + +@item filenames +Tell Readline that the compspec generates filenames, so it can perform any +filename-specific processing (like adding a slash to directory names, +quoting special characters, or suppressing trailing spaces). +This option is intended to be used with shell functions specified +with @option{-F}. + +@item noquote +Tell Readline not to quote the completed words if they are filenames +(quoting filenames is the default). + +@item nosort +Tell Readline not to sort the list of possible completions alphabetically. + +@item nospace +Tell Readline not to append a space (the default) to words completed at +the end of the line. + +@item plusdirs +After any matches defined by the compspec are generated, +directory name completion is attempted and any +matches are added to the results of the other actions. + +@end table + +@item -A @var{action} +The @var{action} may be one of the following to generate a list of possible +completions: + +@table @code +@item alias +Alias names. May also be specified as @option{-a}. + +@item arrayvar +Array variable names. + +@item binding +Readline key binding names (@pxref{Bindable Readline Commands}). + +@item builtin +Names of shell builtin commands. May also be specified as @option{-b}. + +@item command +Command names. May also be specified as @option{-c}. + +@item directory +Directory names. May also be specified as @option{-d}. + +@item disabled +Names of disabled shell builtins. + +@item enabled +Names of enabled shell builtins. + +@item export +Names of exported shell variables. May also be specified as @option{-e}. + +@item file +File names. May also be specified as @option{-f}. + +@item function +Names of shell functions. + +@item group +Group names. May also be specified as @option{-g}. + +@item helptopic +Help topics as accepted by the @code{help} builtin (@pxref{Bash Builtins}). + +@item hostname +Hostnames, as taken from the file specified by the +@env{HOSTFILE} shell variable (@pxref{Bash Variables}). + +@item job +Job names, if job control is active. May also be specified as @option{-j}. + +@item keyword +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}). + +@item shopt +Shell option names as accepted by the @code{shopt} builtin +(@pxref{Bash Builtins}). + +@item signal +Signal names. + +@item stopped +Names of stopped jobs, if job control is active. + +@item user +User names. May also be specified as @option{-u}. + +@item variable +Names of all shell variables. May also be specified as @option{-v}. +@end table + +@item -C @var{command} +@var{command} is executed in a subshell environment, and its output is +used as the possible completions. +Arguments are passed as with the @option{-F} option. + +@item -F @var{function} +The shell function @var{function} is executed in the current shell +environment. +When it is executed, $1 is the name of the command whose arguments are +being completed, $2 is the word being completed, and $3 is the word +preceding the word being completed, as described above +(@pxref{Programmable Completion}). +When it finishes, the possible completions are retrieved from the value +of the @env{COMPREPLY} array variable. + +@item -G @var{globpat} +The filename expansion pattern @var{globpat} is expanded to generate +the possible completions. + +@item -P @var{prefix} +@var{prefix} is added at the beginning of each possible completion +after all other options have been applied. + +@item -S @var{suffix} +@var{suffix} is appended to each possible completion +after all other options have been applied. + +@item -W @var{wordlist} +The @var{wordlist} is split using the characters in the +@env{IFS} special variable as delimiters, and each resultant word +is expanded. +The possible completions are the members of the resultant list which +match the word being completed. + +@item -X @var{filterpat} +@var{filterpat} is a pattern as used for filename expansion. +It is applied to the list of possible completions generated by the +preceding options and arguments, and each completion matching +@var{filterpat} is removed from the list. +A leading @samp{!} in @var{filterpat} negates the pattern; in this +case, any completion not matching @var{filterpat} is removed. +@end table + +The return value is true unless an invalid option is supplied, an option +other than @option{-p} or @option{-r} is supplied without a @var{name} +argument, an attempt is made to remove a completion specification for +a @var{name} for which no specification exists, or +an error occurs adding a completion specification. + +@item compopt +@btindex compopt +@example +@code{compopt} [-o @var{option}] [-DEI] [+o @var{option}] [@var{name}] +@end example +Modify completion options for each @var{name} according to the +@var{option}s, or for the currently-executing completion if no @var{name}s +are supplied. +If no @var{option}s are given, display the completion options for each +@var{name} or the current completion. +The possible values of @var{option} are those valid for the @code{complete} +builtin described above. +The @option{-D} option indicates that other supplied options should +apply to the ``default'' command completion; that is, completion attempted +on a command for which no completion has previously been defined. +The @option{-E} option indicates that other supplied options should +apply to ``empty'' command completion; that is, completion attempted on a +blank line. +The @option{-I} option indicates that other supplied options should +apply to completion on the initial non-assignment word on the line, or after a +command delimiter such as @samp{;} or @samp{|}, which is usually command +name completion. + +If multiple options are supplied, the @option{-D} option takes precedence +over @option{-E}, and both take precedence over @option{-I} + +The return value is true unless an invalid option is supplied, an attempt +is made to modify the options for a @var{name} for which no completion +specification exists, or an output error occurs. + +@end table + +@node A Programmable Completion Example +@section A Programmable Completion Example + +The most common way to obtain additional completion functionality beyond +the default actions @code{complete} and @code{compgen} provide is to use +a shell function and bind it to a particular command using @code{complete -F}. + +The following function provides completions for the @code{cd} builtin. +It is a reasonably good example of what shell functions must do when +used for completion. This function uses the word passed as @code{$2} +to determine the directory name to complete. You can also use the +@code{COMP_WORDS} array variable; the current word is indexed by the +@code{COMP_CWORD} variable. + +The function relies on the @code{complete} and @code{compgen} builtins +to do much of the work, adding only the things that the Bash @code{cd} +does beyond accepting basic directory names: +tilde expansion (@pxref{Tilde Expansion}), +searching directories in @var{$CDPATH}, which is described above +(@pxref{Bourne Shell Builtins}), +and basic support for the @code{cdable_vars} shell option +(@pxref{The Shopt Builtin}). +@code{_comp_cd} modifies the value of @var{IFS} so that it contains only +a newline to accommodate file names containing spaces and tabs -- +@code{compgen} prints the possible completions it generates one per line. + +Possible completions go into the @var{COMPREPLY} array variable, one +completion per array element. The programmable completion system retrieves +the completions from there when the function returns. + +@example +# A completion function for the cd builtin +# based on the cd completion function from the bash_completion package +_comp_cd() +@{ + local IFS=$' \t\n' # normalize IFS + local cur _skipdot _cdpath + local i j k + + # Tilde expansion, which also expands tilde to full pathname + case "$2" in + \~*) eval cur="$2" ;; + *) cur=$2 ;; + esac + + # no cdpath or absolute pathname -- straight directory completion + if [[ -z "$@{CDPATH:-@}" ]] || [[ "$cur" == @@(./*|../*|/*) ]]; then + # compgen prints paths one per line; could also use while loop + IFS=$'\n' + COMPREPLY=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + # CDPATH+directories in the current directory if not in CDPATH + else + IFS=$'\n' + _skipdot=false + # preprocess CDPATH to convert null directory names to . + _cdpath=$@{CDPATH/#:/.:@} + _cdpath=$@{_cdpath//::/:.:@} + _cdpath=$@{_cdpath/%:/:.@} + for i in $@{_cdpath//:/$'\n'@}; do + if [[ $i -ef . ]]; then _skipdot=true; fi + k="$@{#COMPREPLY[@@]@}" + for j in $( compgen -d -- "$i/$cur" ); do + COMPREPLY[k++]=$@{j#$i/@} # cut off directory + done + done + $_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + fi + + # variable names if appropriate shell option set and no completions + if shopt -q cdable_vars && [[ $@{#COMPREPLY[@@]@} -eq 0 ]]; then + COMPREPLY=( $(compgen -v -- "$cur") ) + fi + + return 0 +@} +@end example + +We install the completion function using the @option{-F} option to +@code{complete}: + +@example +# Tell readline to quote appropriate and append slashes to directories; +# use the bash default completion for other arguments +complete -o filenames -o nospace -o bashdefault -F _comp_cd cd +@end example + +@noindent +Since we'd like Bash and Readline to take care of some +of the other details for us, we use several other options to tell Bash +and Readline what to do. The @option{-o filenames} option tells Readline +that the possible completions should be treated as filenames, and quoted +appropriately. That option will also cause Readline to append a slash to +filenames it can determine are directories (which is why we might want to +extend @code{_comp_cd} to append a slash if we're using directories found +via @var{CDPATH}: Readline can't tell those completions are directories). +The @option{-o nospace} option tells Readline to not append a space +character to the directory name, in case we want to append to it. +The @option{-o bashdefault} option brings in the rest of the "Bash default" +completions -- possible completions that Bash adds to the default Readline +set. These include things like command name completion, variable completion +for words beginning with @samp{$} or @samp{$@{}, completions containing pathname +expansion patterns (@pxref{Filename Expansion}), and so on. + +Once installed using @code{complete}, @code{_comp_cd} will be called every +time we attempt word completion for a @code{cd} command. + +Many more examples -- an extensive collection of completions for most of +the common GNU, Unix, and Linux commands -- are available as part of the +bash_completion project. This is installed by default on many GNU/Linux +distributions. Originally written by Ian Macdonald, the project now lives +at @url{https://github.com/scop/bash-completion/}. There are ports for +other systems such as Solaris and Mac OS X. + +An older version of the bash_completion package is distributed with bash +in the @file{examples/complete} subdirectory. + +@end ifset diff --git a/lib/readline/doc/rluserman.texi b/lib/readline/doc/rluserman.texi new file mode 100644 index 0000000..ae9ec6e --- /dev/null +++ b/lib/readline/doc/rluserman.texi @@ -0,0 +1,70 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rluserman.info +@settitle GNU Readline Library +@include version.texi + +@comment %**end of header (This is for running Texinfo on a region.) + +@copying +This manual describes the end user interface of the GNU Readline Library +(version @value{VERSION}, @value{UPDATED}), a library which aids in the +consistency of user interface across discrete programs which provide +a command line interface. + +Copyright @copyright{} 1988--2022 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the section entitled +``GNU Free Documentation License''. + +@end quotation +@end copying + +@dircategory Libraries +@direntry +* RLuserman: (rluserman). The GNU readline library User's Manual. +@end direntry + +@titlepage +@title GNU Readline Library User Interface +@subtitle Edition @value{EDITION}, for @code{Readline Library} Version @value{VERSION}. +@subtitle @value{UPDATED-MONTH} +@author Chet Ramey, Case Western Reserve University +@author Brian Fox, Free Software Foundation + +@page +@vskip 0pt plus 1filll +@insertcopying + +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU Readline Library + +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 which provide a command line interface. +The Readline home page is @url{http://www.gnu.org/software/readline/}. + +@menu +* Command Line Editing:: GNU Readline User's Manual. +* GNU Free Documentation License:: License for copying this manual. +@end menu +@end ifnottex + +@include rluser.texi + +@node GNU Free Documentation License +@appendix GNU Free Documentation License + +@include fdl.texi + +@bye diff --git a/lib/readline/doc/version.texi b/lib/readline/doc/version.texi new file mode 100644 index 0000000..d310273 --- /dev/null +++ b/lib/readline/doc/version.texi @@ -0,0 +1,11 @@ +@ignore +Copyright (C) 1988-2022 Free Software Foundation, Inc. +@end ignore + +@set EDITION 8.2 +@set VERSION 8.2 + +@set UPDATED 19 September 2022 +@set UPDATED-MONTH September 2022 + +@set LASTCHANGE Mon Sep 19 11:15:16 EDT 2022 diff --git a/lib/readline/emacs_keymap.c b/lib/readline/emacs_keymap.c new file mode 100644 index 0000000..02597da --- /dev/null +++ b/lib/readline/emacs_keymap.c @@ -0,0 +1,872 @@ +/* emacs_keymap.c -- the keymap for emacs_mode in readline (). */ + +/* Copyright (C) 1987-2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (BUFSIZ) +#include <stdio.h> +#endif /* !BUFSIZ */ + +#include "readline.h" + +/* An array of function pointers, one for each possible key. + If the type byte is ISKMAP, then the pointer is the address of + a keymap. */ + +KEYMAP_ENTRY_ARRAY emacs_standard_keymap = { + + /* Control keys. */ + { ISFUNC, rl_set_mark }, /* Control-@ */ + { ISFUNC, rl_beg_of_line }, /* Control-a */ + { 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_char }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, rl_operate_and_get_next }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISKMAP, (rl_command_func_t *)emacs_ctlx_keymap }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */ + { ISKMAP, (rl_command_func_t *)emacs_meta_keymap }, /* Control-[ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */ + { ISFUNC, rl_char_search }, /* Control-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout }, /* RUBOUT */ + +#if KEYMAP_SIZE > 128 + /* Pure 8-bit characters (128 - 159). + These might be used in some + character sets. */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + + /* ISO Latin-1 characters (160 - 255) */ + { ISFUNC, rl_insert }, /* No-break space */ + { ISFUNC, rl_insert }, /* Inverted exclamation mark */ + { ISFUNC, rl_insert }, /* Cent sign */ + { ISFUNC, rl_insert }, /* Pound sign */ + { ISFUNC, rl_insert }, /* Currency sign */ + { ISFUNC, rl_insert }, /* Yen sign */ + { ISFUNC, rl_insert }, /* Broken bar */ + { ISFUNC, rl_insert }, /* Section sign */ + { ISFUNC, rl_insert }, /* Diaeresis */ + { ISFUNC, rl_insert }, /* Copyright sign */ + { ISFUNC, rl_insert }, /* Feminine ordinal indicator */ + { ISFUNC, rl_insert }, /* Left pointing double angle quotation mark */ + { ISFUNC, rl_insert }, /* Not sign */ + { ISFUNC, rl_insert }, /* Soft hyphen */ + { ISFUNC, rl_insert }, /* Registered sign */ + { ISFUNC, rl_insert }, /* Macron */ + { ISFUNC, rl_insert }, /* Degree sign */ + { ISFUNC, rl_insert }, /* Plus-minus sign */ + { ISFUNC, rl_insert }, /* Superscript two */ + { ISFUNC, rl_insert }, /* Superscript three */ + { ISFUNC, rl_insert }, /* Acute accent */ + { ISFUNC, rl_insert }, /* Micro sign */ + { ISFUNC, rl_insert }, /* Pilcrow sign */ + { ISFUNC, rl_insert }, /* Middle dot */ + { ISFUNC, rl_insert }, /* Cedilla */ + { ISFUNC, rl_insert }, /* Superscript one */ + { ISFUNC, rl_insert }, /* Masculine ordinal indicator */ + { ISFUNC, rl_insert }, /* Right pointing double angle quotation mark */ + { ISFUNC, rl_insert }, /* Vulgar fraction one quarter */ + { ISFUNC, rl_insert }, /* Vulgar fraction one half */ + { ISFUNC, rl_insert }, /* Vulgar fraction three quarters */ + { ISFUNC, rl_insert }, /* Inverted questionk mark */ + { ISFUNC, rl_insert }, /* Latin capital letter a with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter a with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter a with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter a with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter a with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter a with ring above */ + { ISFUNC, rl_insert }, /* Latin capital letter ae */ + { ISFUNC, rl_insert }, /* Latin capital letter c with cedilla */ + { ISFUNC, rl_insert }, /* Latin capital letter e with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter e with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter e with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter e with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter i with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter i with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter i with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter i with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter eth (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin capital letter n with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter o with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter o with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter o with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter o with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter o with diaeresis */ + { ISFUNC, rl_insert }, /* Multiplication sign */ + { ISFUNC, rl_insert }, /* Latin capital letter o with stroke */ + { ISFUNC, rl_insert }, /* Latin capital letter u with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter u with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter u with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter u with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter Y with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter thorn (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin small letter sharp s (German) */ + { ISFUNC, rl_insert }, /* Latin small letter a with grave */ + { ISFUNC, rl_insert }, /* Latin small letter a with acute */ + { ISFUNC, rl_insert }, /* Latin small letter a with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter a with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter a with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter a with ring above */ + { ISFUNC, rl_insert }, /* Latin small letter ae */ + { ISFUNC, rl_insert }, /* Latin small letter c with cedilla */ + { ISFUNC, rl_insert }, /* Latin small letter e with grave */ + { ISFUNC, rl_insert }, /* Latin small letter e with acute */ + { ISFUNC, rl_insert }, /* Latin small letter e with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter e with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter i with grave */ + { ISFUNC, rl_insert }, /* Latin small letter i with acute */ + { ISFUNC, rl_insert }, /* Latin small letter i with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter i with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter eth (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin small letter n with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter o with grave */ + { ISFUNC, rl_insert }, /* Latin small letter o with acute */ + { ISFUNC, rl_insert }, /* Latin small letter o with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter o with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter o with diaeresis */ + { ISFUNC, rl_insert }, /* Division sign */ + { ISFUNC, rl_insert }, /* Latin small letter o with stroke */ + { ISFUNC, rl_insert }, /* Latin small letter u with grave */ + { ISFUNC, rl_insert }, /* Latin small letter u with acute */ + { ISFUNC, rl_insert }, /* Latin small letter u with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter u with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter y with acute */ + { ISFUNC, rl_insert }, /* Latin small letter thorn (Icelandic) */ + { ISFUNC, rl_insert } /* Latin small letter y with diaeresis */ +#endif /* KEYMAP_SIZE > 128 */ +}; + +KEYMAP_ENTRY_ARRAY emacs_meta_keymap = { + + /* Meta keys. Just like above, but the high bit is set. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-@ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-c */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-d */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-f */ + { ISFUNC, rl_abort }, /* Meta-Control-g */ + { ISFUNC, rl_backward_kill_word }, /* Meta-Control-h */ + { ISFUNC, rl_tab_insert }, /* Meta-Control-i */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-j */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-k */ + { ISFUNC, rl_clear_display }, /* Meta-Control-l */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-m */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-o */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-q */ + { ISFUNC, rl_revert_line }, /* Meta-Control-r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-s */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-t */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-x */ + { ISFUNC, rl_yank_nth_arg }, /* Meta-Control-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-z */ + + { ISFUNC, rl_complete }, /* Meta-Control-[ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-\ */ + { ISFUNC, rl_backward_char_search }, /* Meta-Control-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-^ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_set_mark }, /* Meta-SPACE */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-! */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-" */ + { ISFUNC, rl_insert_comment }, /* Meta-# */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-$ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-% */ + { ISFUNC, rl_tilde_expand }, /* Meta-& */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-' */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-( */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-) */ + { ISFUNC, rl_insert_completions }, /* Meta-* */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-+ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-, */ + { ISFUNC, rl_digit_argument }, /* Meta-- */ + { ISFUNC, rl_yank_last_arg}, /* Meta-. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-/ */ + + /* Regular digits. */ + { ISFUNC, rl_digit_argument }, /* Meta-0 */ + { ISFUNC, rl_digit_argument }, /* Meta-1 */ + { ISFUNC, rl_digit_argument }, /* Meta-2 */ + { ISFUNC, rl_digit_argument }, /* Meta-3 */ + { ISFUNC, rl_digit_argument }, /* Meta-4 */ + { ISFUNC, rl_digit_argument }, /* Meta-5 */ + { ISFUNC, rl_digit_argument }, /* Meta-6 */ + { ISFUNC, rl_digit_argument }, /* Meta-7 */ + { ISFUNC, rl_digit_argument }, /* Meta-8 */ + { ISFUNC, rl_digit_argument }, /* Meta-9 */ + + /* A little more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-: */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-; */ + { ISFUNC, rl_beginning_of_history }, /* Meta-< */ + { ISFUNC, rl_possible_completions }, /* Meta-= */ + { ISFUNC, rl_end_of_history }, /* Meta-> */ + { ISFUNC, rl_possible_completions }, /* Meta-? */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-@ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-A */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-B */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-C */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-D */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-E */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-F */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-G */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-H */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-I */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-J */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-K */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-L */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-M */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-N */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-O */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-P */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Q */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-R */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-S */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-T */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-U */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-V */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-W */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-X */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Y */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Z */ + + /* Some more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-[ */ /* was rl_arrow_keys */ + { ISFUNC, rl_delete_horizontal_space }, /* Meta-\ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-^ */ + { ISFUNC, rl_yank_last_arg }, /* Meta-_ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-` */ + + /* Lowercase alphabet. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-a */ + { ISFUNC, rl_backward_word }, /* Meta-b */ + { ISFUNC, rl_capitalize_word }, /* Meta-c */ + { ISFUNC, rl_kill_word }, /* Meta-d */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-e */ + { ISFUNC, rl_forward_word }, /* Meta-f */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-g */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-h */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-i */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-j */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-k */ + { ISFUNC, rl_downcase_word }, /* Meta-l */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-m */ + { ISFUNC, rl_noninc_forward_search }, /* Meta-n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-o */ /* was rl_arrow_keys */ + { ISFUNC, rl_noninc_reverse_search }, /* Meta-p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-q */ + { ISFUNC, rl_revert_line }, /* Meta-r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-s */ + { ISFUNC, rl_transpose_words }, /* Meta-t */ + { ISFUNC, rl_upcase_word }, /* Meta-u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-x */ + { ISFUNC, rl_yank_pop }, /* Meta-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-z */ + + /* Final punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-{ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-| */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-} */ + { ISFUNC, rl_tilde_expand }, /* Meta-~ */ + { ISFUNC, rl_backward_kill_word }, /* Meta-rubout */ + +#if KEYMAP_SIZE > 128 + /* Undefined keys. */ + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 } +#endif /* KEYMAP_SIZE > 128 */ +}; + +KEYMAP_ENTRY_ARRAY emacs_ctlx_keymap = { + + /* Control keys. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-@ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-d */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-h */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-i */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-j */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-k */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-l */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-m */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-o */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-q */ + { ISFUNC, rl_re_read_init_file }, /* Control-r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-s */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-t */ + { ISFUNC, rl_undo_command }, /* Control-u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-w */ + { ISFUNC, rl_exchange_point_and_mark }, /* Control-x */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-[ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* SPACE */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ! */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* " */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* # */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* $ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* % */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* & */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ' */ + { ISFUNC, rl_start_kbd_macro }, /* ( */ + { ISFUNC, rl_end_kbd_macro }, /* ) */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* * */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* + */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* , */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* - */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* . */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 0 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 1 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 2 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 3 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 4 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 5 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 6 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 7 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 8 */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* : */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ; */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* < */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* = */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* > */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ? */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* [ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* \ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ^ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* _ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* c */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* d */ + { ISFUNC, rl_call_last_kbd_macro }, /* e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* f */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* g */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* h */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* i */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* j */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* k */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* l */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* m */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* o */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* q */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* s */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* t */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* x */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* { */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* | */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* } */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_line }, /* RUBOUT */ + +#if KEYMAP_SIZE > 128 + /* Undefined keys. */ + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 } +#endif /* KEYMAP_SIZE > 128 */ +}; diff --git a/lib/readline/examples/Inputrc b/lib/readline/examples/Inputrc new file mode 100644 index 0000000..a358bc4 --- /dev/null +++ b/lib/readline/examples/Inputrc @@ -0,0 +1,81 @@ +# My ~/.inputrc file is in -*- text -*- for easy editing with Emacs. +# +# Notice the various bindings which are conditionalized depending +# on which program is running, or what terminal is active. +# + +# Copyright (C) 1989-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# + +# In all programs, all terminals, make sure this is bound. +"\C-x\C-r": re-read-init-file + +# Hp terminals (and some others) have ugly default behaviour for C-h. +"\C-h": backward-delete-char +"\e\C-h": backward-kill-word +"\C-xd": dump-functions + +# In xterm windows, make the arrow keys do the right thing. +$if TERM=xterm +"\e[A": previous-history +"\e[B": next-history +"\e[C": forward-char +"\e[D": backward-char + +# alternate arrow key prefix +"\eOA": previous-history +"\eOB": next-history +"\eOC": forward-char +"\eOD": backward-char + +# Under Xterm in Bash, we bind local Function keys to do something useful. +$if Bash +"\e[11~": "Function Key 1" +"\e[12~": "Function Key 2" +"\e[13~": "Function Key 3" +"\e[14~": "Function Key 4" +"\e[15~": "Function Key 5" + +# I know the following escape sequence numbers are 1 greater than +# the function key. Don't ask me why, I didn't design the xterm terminal. +"\e[17~": "Function Key 6" +"\e[18~": "Function Key 7" +"\e[19~": "Function Key 8" +"\e[20~": "Function Key 9" +"\e[21~": "Function Key 10" +$endif +$endif + +# For Bash, all terminals, add some Bash specific hacks. +$if Bash +"\C-xv": show-bash-version +"\C-x\C-e": shell-expand-line + +# Here is one for editing my path. +"\C-xp": "$PATH\C-x\C-e\C-e\"\C-aPATH=\":\C-b" + +# Make C-x r read my mail in emacs. +# "\C-xr": "emacs -f rmail\C-j" +$endif + +# For FTP, different hacks: +$if Ftp +"\C-xg": "get \M-?" +"\C-xt": "put \M-?" +"\M-.": yank-last-arg +$endif + +" ": self-insert diff --git a/lib/readline/examples/Makefile b/lib/readline/examples/Makefile new file mode 100644 index 0000000..2df2240 --- /dev/null +++ b/lib/readline/examples/Makefile @@ -0,0 +1,44 @@ +# This is the Makefile for the examples subdirectory of readline. -*- text -*- +# +# Copyright (C) 1994 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. +# + +EXECUTABLES = fileman rltest rl +CFLAGS = -g -I../.. -I.. -DREADLINE_LIBRARY +LDFLAGS = -g -L.. + +.c.o: + $(CC) $(CFLAGS) -c $< + +all: $(EXECUTABLES) + + +rl: rl.o + $(CC) $(LDFLAGS) -o $@ rl.o -lreadline -ltermcap + +fileman: fileman.o + $(CC) $(LDFLAGS) -o $@ fileman.o -lreadline -ltermcap + +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 diff --git a/lib/readline/examples/excallback.c b/lib/readline/examples/excallback.c new file mode 100644 index 0000000..923c923 --- /dev/null +++ b/lib/readline/examples/excallback.c @@ -0,0 +1,200 @@ +/* +From: Jeff Solomon <jsolomon@stanford.edu> +Date: Fri, 9 Apr 1999 10:13:27 -0700 (PDT) +To: chet@po.cwru.edu +Subject: new readline example +Message-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU> + +Chet, + +I've been using readline 4.0. Specifically, I've been using the perl +version Term::ReadLine::Gnu. It works great. + +Anyway, I've been playing around the alternate interface and I wanted +to contribute a little C program, callback.c, to you that you could +use as an example of the alternate interface in the /examples +directory of the readline distribution. + +My example shows how, using the alternate interface, you can +interactively change the prompt (which is very nice imo). Also, I +point out that you must roll your own terminal setting when using the +alternate interface because readline depreps (using your parlance) the +terminal while in the user callback. I try to demostrate what I mean +with an example. I've included the program below. + +To compile, I just put the program in the examples directory and made +the appropriate changes to the EXECUTABLES and OBJECTS line and added +an additional target 'callback'. + +I compiled on my Sun Solaris2.6 box using Sun's cc. + +Let me know what you think. + +Jeff +*/ +/* +Copyright (C) 1999 Jeff Solomon +*/ + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> + +#include <stdio.h> +#include <termios.h> /* xxx - should make this more general */ + +#include <locale.h> + +#ifdef READLINE_LIBRARY +# include "readline.h" +#else +# include <readline/readline.h> +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +/* This little examples demonstrates the alternate interface to using readline. + * In the alternate interface, the user maintains control over program flow and + * only calls readline when STDIN is readable. Using the alternate interface, + * you can do anything else while still using readline (like talking to a + * network or another program) without blocking. + * + * Specifically, this program highlights two importants features of the + * alternate interface. The first is the ability to interactively change the + * prompt, which can't be done using the regular interface since rl_prompt is + * read-only. + * + * The second feature really highlights a subtle point when using the alternate + * interface. That is, readline will not alter the terminal when inside your + * callback handler. So let's so, your callback executes a user command that + * takes a non-trivial amount of time to complete (seconds). While your + * executing the command, the user continues to type keystrokes and expects them + * to be re-echoed on the new prompt when it returns. Unfortunately, the default + * terminal configuration doesn't do this. After the prompt returns, the user + * must hit one additional keystroke and then will see all of his previous + * keystrokes. To illustrate this, compile and run this program. Type "sleep" at + * the prompt and then type "bar" before the prompt returns (you have 3 + * seconds). Notice how "bar" is re-echoed on the prompt after the prompt + * returns? This is what you expect to happen. Now comment out the 4 lines below + * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do + * the same thing. When the prompt returns, you should not see "bar". Now type + * "f", see how "barf" magically appears? This behavior is un-expected and not + * desired. + */ + +void process_line(char *line); +int change_prompt(void); +char *get_prompt(void); + +int prompt = 1; +char prompt_buf[40], line_buf[256]; +tcflag_t old_lflag; +cc_t old_vtime; +struct termios term; + +int +main() +{ + fd_set fds; + + setlocale (LC_ALL, ""); + + /* Adjust the terminal slightly before the handler is installed. Disable + * canonical mode processing and set the input character time flag to be + * non-blocking. + */ + if( tcgetattr(STDIN_FILENO, &term) < 0 ) { + perror("tcgetattr"); + exit(1); + } + old_lflag = term.c_lflag; + old_vtime = term.c_cc[VTIME]; + term.c_lflag &= ~ICANON; + term.c_cc[VTIME] = 1; + /* COMMENT LINE BELOW - see above */ + if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { + perror("tcsetattr"); + exit(1); + } + + rl_add_defun("change-prompt", change_prompt, CTRL('t')); + rl_callback_handler_install(get_prompt(), process_line); + + while(1) { + FD_ZERO(&fds); + FD_SET(fileno(stdin), &fds); + + if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) { + perror("select"); + exit(1); + } + + if( FD_ISSET(fileno(stdin), &fds) ) { + rl_callback_read_char(); + } + } +} + +void +process_line(char *line) +{ + if( line == NULL ) { + fprintf(stderr, "\n", line); + + /* reset the old terminal setting before exiting */ + term.c_lflag = old_lflag; + term.c_cc[VTIME] = old_vtime; + if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { + perror("tcsetattr"); + exit(1); + } + exit(0); + } + + if( strcmp(line, "sleep") == 0 ) { + sleep(3); + } else { + fprintf(stderr, "|%s|\n", line); + } + + free (line); +} + +int +change_prompt(void) +{ + /* toggle the prompt variable */ + prompt = !prompt; + + /* save away the current contents of the line */ + strcpy(line_buf, rl_line_buffer); + + /* install a new handler which will change the prompt and erase the current line */ + rl_callback_handler_install(get_prompt(), process_line); + + /* insert the old text on the new line */ + rl_insert_text(line_buf); + + /* redraw the current line - this is an undocumented function. It invokes the + * redraw-current-line command. + */ + rl_refresh_line(0, 0); +} + +char * +get_prompt(void) +{ + /* The prompts can even be different lengths! */ + sprintf(prompt_buf, "%s", + prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> "); + return prompt_buf; +} diff --git a/lib/readline/examples/fileman.c b/lib/readline/examples/fileman.c new file mode 100644 index 0000000..2a8b097 --- /dev/null +++ b/lib/readline/examples/fileman.c @@ -0,0 +1,506 @@ +/* fileman.c - file manager example for readline library. */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* 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. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#ifdef HAVE_SYS_FILE_H +# include <sys/file.h> +#endif +#include <sys/stat.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include <time.h> + +#ifdef READLINE_LIBRARY +# include "readline.h" +# include "history.h" +#else +# include <readline/readline.h> +# include <readline/history.h> +#endif + +extern char *xmalloc PARAMS((size_t)); + +void initialize_readline PARAMS((void)); +void too_dangerous PARAMS((char *)); + +int execute_line PARAMS((char *)); +int valid_argument PARAMS((char *, char *)); + +/* The names of functions that actually do the manipulation. */ +int com_list PARAMS((char *)); +int com_view PARAMS((char *)); +int com_rename PARAMS((char *)); +int com_stat PARAMS((char *)); +int com_pwd PARAMS((char *)); +int com_delete PARAMS((char *)); +int com_help PARAMS((char *)); +int com_cd PARAMS((char *)); +int com_quit PARAMS((char *)); + +/* A structure which contains information on the commands this program + can understand. */ + +typedef struct { + char *name; /* User printable name of the function. */ + rl_icpfunc_t *func; /* Function to call to do the job. */ + char *doc; /* Documentation for this function. */ +} COMMAND; + +COMMAND commands[] = { + { "cd", com_cd, "Change to directory DIR" }, + { "delete", com_delete, "Delete FILE" }, + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "list", com_list, "List files in DIR" }, + { "ls", com_list, "Synonym for `list'" }, + { "pwd", com_pwd, "Print the current working directory" }, + { "quit", com_quit, "Quit using Fileman" }, + { "rename", com_rename, "Rename FILE to NEWNAME" }, + { "stat", com_stat, "Print out statistics on FILE" }, + { "view", com_view, "View the contents of FILE" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +/* Forward declarations. */ +char *stripwhite (); +COMMAND *find_command (); + +/* The name of this program, as taken from argv[0]. */ +char *progname; + +/* When non-zero, this global means the user is done using this program. */ +int done; + +char * +dupstr (s) + char *s; +{ + char *r; + + r = xmalloc (strlen (s) + 1); + strcpy (r, s); + return (r); +} + +int +main (argc, argv) + int argc; + char **argv; +{ + char *line, *s; + + progname = argv[0]; + + initialize_readline (); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + for ( ; done == 0; ) + { + line = readline ("FileMan: "); + + if (!line) + break; + + /* Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. */ + s = stripwhite (line); + + if (*s) + { + add_history (s); + execute_line (s); + } + + free (line); + } + exit (0); +} + +/* Execute a command line. */ +int +execute_line (line) + char *line; +{ + register int i; + COMMAND *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && whitespace (line[i])) + i++; + word = line + i; + + while (line[i] && !whitespace (line[i])) + i++; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) + { + fprintf (stderr, "%s: No such command for FileMan.\n", word); + return (-1); + } + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* Call the function. */ + return ((*(command->func)) (word)); +} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND * +find_command (name) + char *name; +{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +} + +/* Strip whitespace from the start and end of STRING. Return a pointer + into STRING. */ +char * +stripwhite (string) + char *string; +{ + register char *s, *t; + + for (s = string; whitespace (*s); s++) + ; + + if (*s == 0) + return (s); + + t = s + strlen (s) - 1; + while (t > s && whitespace (*t)) + t--; + *++t = '\0'; + + return s; +} + +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +char *command_generator PARAMS((const char *, int)); +char **fileman_completion PARAMS((const char *, int, int)); + +/* Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. */ +void +initialize_readline () +{ + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "FileMan"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = fileman_completion; +} + +/* Attempt to complete on the contents of TEXT. START and END bound the + region of rl_line_buffer that contains the word to complete. TEXT is + the word to complete. We can use the entire contents of rl_line_buffer + in case we want to do some simple parsing. Return the array of matches, + or NULL if there aren't any. */ +char ** +fileman_completion (text, start, end) + const char *text; + int start, end; +{ + char **matches; + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = rl_completion_matches (text, command_generator); + + return (matches); +} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +command_generator (text, state) + const char *text; + int state; +{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) + { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the command list. */ + while (name = commands[list_index].name) + { + list_index++; + + if (strncmp (name, text, len) == 0) + return (dupstr(name)); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/* **************************************************************** */ +/* */ +/* FileMan Commands */ +/* */ +/* **************************************************************** */ + +/* String to pass to system (). This is for the LIST, VIEW and RENAME + commands. */ +static char syscom[1024]; + +/* List the file(s) named in arg. */ +int +com_list (arg) + char *arg; +{ + if (!arg) + arg = ""; + + sprintf (syscom, "ls -FClg %s", arg); + return (system (syscom)); +} + +int +com_view (arg) + char *arg; +{ + if (!valid_argument ("view", arg)) + return 1; + +#if defined (__MSDOS__) + /* more.com doesn't grok slashes in pathnames */ + sprintf (syscom, "less %s", arg); +#else + sprintf (syscom, "more %s", arg); +#endif + return (system (syscom)); +} + +int +com_rename (arg) + char *arg; +{ + too_dangerous ("rename"); + return (1); +} + +int +com_stat (arg) + char *arg; +{ + struct stat finfo; + + if (!valid_argument ("stat", arg)) + return (1); + + if (stat (arg, &finfo) == -1) + { + perror (arg); + return (1); + } + + printf ("Statistics for `%s':\n", arg); + + printf ("%s has %d link%s, and is %lu byte%s in length.\n", + arg, + finfo.st_nlink, + (finfo.st_nlink == 1) ? "" : "s", + (unsigned long)finfo.st_size, + (finfo.st_size == 1) ? "" : "s"); + printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime)); + printf (" Last access at: %s", ctime (&finfo.st_atime)); + printf (" Last modified at: %s", ctime (&finfo.st_mtime)); + return (0); +} + +int +com_delete (arg) + char *arg; +{ + too_dangerous ("delete"); + return (1); +} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +int +com_help (arg) + char *arg; +{ + register int i; + int printed = 0; + + for (i = 0; commands[i].name; i++) + { + if (!*arg || (strcmp (arg, commands[i].name) == 0)) + { + printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); + printed++; + } + } + + if (!printed) + { + printf ("No commands match `%s'. Possibilities are:\n", arg); + + for (i = 0; commands[i].name; i++) + { + /* Print in six columns. */ + if (printed == 6) + { + printed = 0; + printf ("\n"); + } + + printf ("%s\t", commands[i].name); + printed++; + } + + if (printed) + printf ("\n"); + } + return (0); +} + +/* Change to the directory ARG. */ +int +com_cd (arg) + char *arg; +{ + if (chdir (arg) == -1) + { + perror (arg); + return 1; + } + + com_pwd (""); + return (0); +} + +/* Print out the current working directory. */ +int +com_pwd (ignore) + char *ignore; +{ + char dir[1024], *s; + + s = getcwd (dir, sizeof(dir) - 1); + if (s == 0) + { + printf ("Error getting pwd: %s\n", dir); + return 1; + } + + printf ("Current directory is %s\n", dir); + return 0; +} + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +int +com_quit (arg) + char *arg; +{ + done = 1; + return (0); +} + +/* Function which tells you that you can't do this. */ +void +too_dangerous (caller) + char *caller; +{ + fprintf (stderr, + "%s: Too dangerous for me to distribute. Write it yourself.\n", + caller); +} + +/* 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; +{ + if (!arg || !*arg) + { + fprintf (stderr, "%s: Argument required.\n", caller); + return (0); + } + + return (1); +} diff --git a/lib/readline/examples/histexamp.c b/lib/readline/examples/histexamp.c new file mode 100644 index 0000000..309d769 --- /dev/null +++ b/lib/readline/examples/histexamp.c @@ -0,0 +1,128 @@ +/* histexamp.c - history library example program. */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> + +#ifdef READLINE_LIBRARY +# include "history.h" +#else +# include <readline/history.h> +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + char line[1024], *t; + int len, done; + + line[0] = 0; + done = 0; + + using_history (); + while (!done) + { + printf ("history$ "); + fflush (stdout); + t = fgets (line, sizeof (line) - 1, stdin); + if (t && *t) + { + len = strlen (t); + if (t[len - 1] == '\n') + t[len - 1] = '\0'; + } + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + { + char *expansion; + int result; + + using_history (); + + result = history_expand (line, &expansion); + if (result) + fprintf (stderr, "%s\n", expansion); + + if (result < 0 || result == 2) + { + free (expansion); + continue; + } + + add_history (expansion); + strncpy (line, expansion, sizeof (line) - 1); + free (expansion); + } + + if (strcmp (line, "quit") == 0) + done = 1; + else if (strcmp (line, "save") == 0) + write_history ("history_file"); + else if (strcmp (line, "read") == 0) + read_history ("history_file"); + else if (strcmp (line, "list") == 0) + { + register HIST_ENTRY **the_list; + register int i; + time_t tt; + char timestr[128]; + + the_list = history_list (); + if (the_list) + for (i = 0; the_list[i]; i++) + { + tt = history_get_time (the_list[i]); + if (tt) + strftime (timestr, sizeof (timestr), "%a %R", localtime(&tt)); + else + strcpy (timestr, "??"); + printf ("%d: %s: %s\n", i + history_base, timestr, the_list[i]->line); + } + } + else if (strncmp (line, "delete", 6) == 0) + { + int which; + if ((sscanf (line + 6, "%d", &which)) == 1) + { + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + { + free (entry->line); + free (entry); + } + } + else + { + fprintf (stderr, "non-numeric arg given to `delete'\n"); + } + } + } +} diff --git a/lib/readline/examples/manexamp.c b/lib/readline/examples/manexamp.c new file mode 100644 index 0000000..351c628 --- /dev/null +++ b/lib/readline/examples/manexamp.c @@ -0,0 +1,111 @@ +/* manexamp.c -- The examples which appear in the documentation are here. */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <readline/readline.h> + +/* **************************************************************** */ +/* */ +/* How to Emulate gets () */ +/* */ +/* **************************************************************** */ + +/* 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. */ +char * +rl_gets () +{ + /* If the buffer has already been allocated, return the memory + to the free pool. */ + if (line_read) + { + free (line_read); + line_read = (char *)NULL; + } + + /* Get a line from the user. */ + line_read = readline (""); + + /* If the line has any text in it, save it on the history. */ + if (line_read && *line_read) + add_history (line_read); + + return (line_read); +} + +/* **************************************************************** */ +/* */ +/* Writing a Function to be Called by Readline. */ +/* */ +/* **************************************************************** */ + +/* Invert the case of the COUNT following characters. */ +invert_case_line (count, key) + int count, key; +{ + register int start, end; + + start = rl_point; + + if (count < 0) + { + direction = -1; + count = -count; + } + else + direction = 1; + + /* Find the end of the range to modify. */ + end = start + (count * direction); + + /* Force it to be within range. */ + if (end > rl_end) + end = rl_end; + else if (end < 0) + end = -1; + + if (start > end) + { + int temp = start; + start = end; + end = temp; + } + + if (start == end) + return; + + /* Tell readline that we are modifying the line, so save the undo + information. */ + rl_modifying (start, end); + + for (; start != end; start += direction) + { + if (_rl_uppercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = _rl_to_lower (rl_line_buffer[start]); + else if (_rl_lowercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = _rl_to_upper (rl_line_buffer[start]); + } + + /* Move point to on top of the last character changed. */ + rl_point = end - direction; +} diff --git a/lib/readline/examples/rl-callbacktest.c b/lib/readline/examples/rl-callbacktest.c new file mode 100644 index 0000000..7febacd --- /dev/null +++ b/lib/readline/examples/rl-callbacktest.c @@ -0,0 +1,115 @@ +/* Standard include files. stdio.h is required. */ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +/* Used for select(2) */ +#include <sys/types.h> +#include <sys/select.h> + +#include <signal.h> + +#include <errno.h> +#include <stdio.h> + +#include <locale.h> + +/* Standard readline include files. */ +#if defined (READLINE_LIBRARY) +# include "readline.h" +# include "history.h" +#else +# include <readline/readline.h> +# include <readline/history.h> +#endif + +extern int errno; + +static void cb_linehandler (char *); +static void signandler (int); + +int running, sigwinch_received; +const char *prompt = "rltest$ "; + +/* Handle SIGWINCH and window size changes when readline is not active and + reading a character. */ +static void +sighandler (int sig) +{ + sigwinch_received = 1; +} + +/* Callback function called for each line when accept-line executed, EOF + seen, or EOF character read. This sets a flag and returns; it could + also call exit(3). */ +static void +cb_linehandler (char *line) +{ + /* Can use ^D (stty eof) or `exit' to exit. */ + if (line == NULL || strcmp (line, "exit") == 0) + { + if (line == 0) + printf ("\n"); + printf ("exit\n"); + /* This function needs to be called to reset the terminal settings, + and calling it from the line handler keeps one extra prompt from + being displayed. */ + rl_callback_handler_remove (); + + running = 0; + } + else + { + if (*line) + add_history (line); + printf ("input line: %s\n", line); + free (line); + } +} + +int +main (int c, char **v) +{ + fd_set fds; + int r; + + setlocale (LC_ALL, ""); + + /* Handle SIGWINCH */ + signal (SIGWINCH, sighandler); + + /* Install the line handler. */ + rl_callback_handler_install (prompt, cb_linehandler); + + /* Enter a simple event loop. This waits until something is available + to read on readline's input stream (defaults to standard input) and + calls the builtin character read callback to read it. It does not + have to modify the user's terminal settings. */ + running = 1; + while (running) + { + FD_ZERO (&fds); + FD_SET (fileno (rl_instream), &fds); + + r = select (FD_SETSIZE, &fds, NULL, NULL, NULL); + if (r < 0 && errno != EINTR) + { + perror ("rltest: select"); + rl_callback_handler_remove (); + break; + } + if (sigwinch_received) + { + rl_resize_terminal (); + sigwinch_received = 0; + } + if (r < 0) + continue; + + if (FD_ISSET (fileno (rl_instream), &fds)) + rl_callback_read_char (); + } + + printf ("rltest: Event loop has exited\n"); + return 0; +} diff --git a/lib/readline/examples/rl.c b/lib/readline/examples/rl.c new file mode 100644 index 0000000..39e5b8e --- /dev/null +++ b/lib/readline/examples/rl.c @@ -0,0 +1,166 @@ +/* + * rl - command-line interface to read a line from the standard input + * (or another fd) using readline. + * + * usage: rl [-p prompt] [-u unit] [-d default] [-n nchars] + */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +extern void exit(); +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#if defined (READLINE_LIBRARY) +# include "posixstat.h" +# include "readline.h" +# include "history.h" +#else +# include <sys/stat.h> +# include <readline/readline.h> +# include <readline/history.h> +#endif + +extern int optind; +extern char *optarg; + +#if !defined (strchr) && !defined (__STDC__) +extern char *strrchr(); +#endif + +static char *progname; +static char *deftext; + +static int +set_deftext () +{ + if (deftext) + { + rl_insert_text (deftext); + deftext = (char *)NULL; + rl_startup_hook = (rl_hook_func_t *)NULL; + } + return 0; +} + +static void +usage() +{ + fprintf (stderr, "%s: usage: %s [-p prompt] [-u unit] [-d default] [-n nchars]\n", + progname, progname); +} + +int +main (argc, argv) + int argc; + char **argv; +{ + char *temp, *prompt; + struct stat sb; + int opt, fd, nch; + FILE *ifp; + + progname = strrchr(argv[0], '/'); + if (progname == 0) + progname = argv[0]; + else + progname++; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + + /* defaults */ + prompt = "readline$ "; + fd = nch = 0; + deftext = (char *)0; + + while ((opt = getopt(argc, argv, "p:u:d:n:")) != EOF) + { + switch (opt) + { + case 'p': + prompt = optarg; + break; + case 'u': + fd = atoi(optarg); + if (fd < 0) + { + fprintf (stderr, "%s: bad file descriptor `%s'\n", progname, optarg); + exit (2); + } + break; + case 'd': + deftext = optarg; + break; + case 'n': + nch = atoi(optarg); + if (nch < 0) + { + fprintf (stderr, "%s: bad value for -n: `%s'\n", progname, optarg); + exit (2); + } + break; + default: + usage (); + exit (2); + } + } + + if (fd != 0) + { + if (fstat (fd, &sb) < 0) + { + fprintf (stderr, "%s: %d: bad file descriptor\n", progname, fd); + exit (1); + } + ifp = fdopen (fd, "r"); + rl_instream = ifp; + } + + if (deftext && *deftext) + rl_startup_hook = set_deftext; + + if (nch > 0) + rl_num_chars_to_read = nch; + + temp = readline (prompt); + + /* Test for EOF. */ + if (temp == 0) + exit (1); + + printf ("%s\n", temp); + exit (0); +} diff --git a/lib/readline/examples/rlcat.c b/lib/readline/examples/rlcat.c new file mode 100644 index 0000000..aabe0ca --- /dev/null +++ b/lib/readline/examples/rlcat.c @@ -0,0 +1,187 @@ +/* + * rlcat - cat(1) using readline + * + * usage: rlcat + */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +extern void exit(); +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#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; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""): +#endif + + 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) == EOF) + 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; +} diff --git a/lib/readline/examples/rltest.c b/lib/readline/examples/rltest.c new file mode 100644 index 0000000..8b7c00c --- /dev/null +++ b/lib/readline/examples/rltest.c @@ -0,0 +1,101 @@ +/* **************************************************************** */ +/* */ +/* Testing Readline */ +/* */ +/* **************************************************************** */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library for + reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +extern void exit(); +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef READLINE_LIBRARY +# include "readline.h" +# include "history.h" +#else +# include <readline/readline.h> +# include <readline/history.h> +#endif + +extern HIST_ENTRY **history_list (); + +int +main () +{ + char *temp, *prompt; + int done; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + + temp = (char *)NULL; + prompt = "readline$ "; + done = 0; + + while (!done) + { + temp = readline (prompt); + + /* Test for EOF. */ + if (!temp) + exit (1); + + /* If there is anything on the line, print it and remember it. */ + if (*temp) + { + fprintf (stderr, "%s\r\n", temp); + add_history (temp); + } + + /* Check for `command' that we handle. */ + if (strcmp (temp, "quit") == 0) + done = 1; + + if (strcmp (temp, "list") == 0) + { + HIST_ENTRY **list; + register int i; + + list = history_list (); + if (list) + { + for (i = 0; list[i]; i++) + fprintf (stderr, "%d: %s\r\n", i, list[i]->line); + } + } + free (temp); + } + exit (0); +} diff --git a/lib/readline/funmap.c b/lib/readline/funmap.c new file mode 100644 index 0000000..8b1cb40 --- /dev/null +++ b/lib/readline/funmap.c @@ -0,0 +1,273 @@ +/* funmap.c -- attach names to functions. */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#if !defined (BUFSIZ) +#include <stdio.h> +#endif /* BUFSIZ */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "rlconf.h" +#include "readline.h" + +#include "xmalloc.h" + +#ifdef __STDC__ +typedef int QSFUNC (const void *, const void *); +#else +typedef int QSFUNC (); +#endif + +extern int _rl_qsort_string_compare (char **, char **); + +FUNMAP **funmap; +static int funmap_size; +static int funmap_entry; + +/* After initializing the function map, this is the index of the first + program specific function. */ +int funmap_program_specific_entry_start; + +static const FUNMAP default_funmap[] = { + { "abort", rl_abort }, + { "accept-line", rl_newline }, + { "arrow-key-prefix", rl_arrow_keys }, + { "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 }, + { "backward-word", rl_backward_word }, + { "beginning-of-history", rl_beginning_of_history }, + { "beginning-of-line", rl_beg_of_line }, + { "bracketed-paste-begin", rl_bracketed_paste_begin }, + { "call-last-kbd-macro", rl_call_last_kbd_macro }, + { "capitalize-word", rl_capitalize_word }, + { "character-search", rl_char_search }, + { "character-search-backward", rl_backward_char_search }, + { "clear-display", rl_clear_display }, + { "clear-screen", rl_clear_screen }, + { "complete", rl_complete }, + { "copy-backward-word", rl_copy_backward_word }, + { "copy-forward-word", rl_copy_forward_word }, + { "copy-region-as-kill", rl_copy_region_to_kill }, + { "delete-char", rl_delete }, + { "delete-char-or-list", rl_delete_or_show_completions }, + { "delete-horizontal-space", rl_delete_horizontal_space }, + { "digit-argument", rl_digit_argument }, + { "do-lowercase-version", rl_do_lowercase_version }, + { "downcase-word", rl_downcase_word }, + { "dump-functions", rl_dump_functions }, + { "dump-macros", rl_dump_macros }, + { "dump-variables", rl_dump_variables }, + { "emacs-editing-mode", rl_emacs_editing_mode }, + { "end-kbd-macro", rl_end_kbd_macro }, + { "end-of-history", rl_end_of_history }, + { "end-of-line", rl_end_of_line }, + { "exchange-point-and-mark", rl_exchange_point_and_mark }, + { "fetch-history", rl_fetch_history }, + { "forward-backward-delete-char", rl_rubout_or_delete }, + { "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 }, + { "history-search-forward", rl_history_search_forward }, + { "history-substring-search-backward", rl_history_substr_search_backward }, + { "history-substring-search-forward", rl_history_substr_search_forward }, + { "insert-comment", rl_insert_comment }, + { "insert-completions", rl_insert_completions }, + { "kill-whole-line", rl_kill_full_line }, + { "kill-line", rl_kill_line }, + { "kill-region", rl_kill_region }, + { "kill-word", rl_kill_word }, + { "menu-complete", rl_menu_complete }, + { "menu-complete-backward", rl_backward_menu_complete }, + { "next-history", rl_get_next_history }, + { "next-screen-line", rl_next_screen_line }, + { "non-incremental-forward-search-history", rl_noninc_forward_search }, + { "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 }, + { "old-menu-complete", rl_old_menu_complete }, + { "operate-and-get-next", rl_operate_and_get_next }, + { "overwrite-mode", rl_overwrite_mode }, +#if defined (_WIN32) + { "paste-from-clipboard", rl_paste_from_clipboard }, +#endif + { "possible-completions", rl_possible_completions }, + { "previous-history", rl_get_previous_history }, + { "previous-screen-line", rl_previous_screen_line }, + { "print-last-kbd-macro", rl_print_last_kbd_macro }, + { "quoted-insert", rl_quoted_insert }, + { "re-read-init-file", rl_re_read_init_file }, + { "redraw-current-line", rl_refresh_line}, + { "reverse-search-history", rl_reverse_search_history }, + { "revert-line", rl_revert_line }, + { "self-insert", rl_insert }, + { "set-mark", rl_set_mark }, + { "skip-csi-sequence", rl_skip_csi_sequence }, + { "start-kbd-macro", rl_start_kbd_macro }, + { "tab-insert", rl_tab_insert }, + { "tilde-expand", rl_tilde_expand }, + { "transpose-chars", rl_transpose_chars }, + { "transpose-words", rl_transpose_words }, + { "tty-status", rl_tty_status }, + { "undo", rl_undo_command }, + { "universal-argument", rl_universal_argument }, + { "unix-filename-rubout", rl_unix_filename_rubout }, + { "unix-line-discard", rl_unix_line_discard }, + { "unix-word-rubout", rl_unix_word_rubout }, + { "upcase-word", rl_upcase_word }, + { "yank", rl_yank }, + { "yank-last-arg", rl_yank_last_arg }, + { "yank-nth-arg", rl_yank_nth_arg }, + { "yank-pop", rl_yank_pop }, + +#if defined (VI_MODE) + { "vi-append-eol", rl_vi_append_eol }, + { "vi-append-mode", rl_vi_append_mode }, + { "vi-arg-digit", rl_vi_arg_digit }, + { "vi-back-to-indent", rl_vi_back_to_indent }, + { "vi-backward-bigword", rl_vi_bWord }, + { "vi-backward-word", rl_vi_bword }, + { "vi-bWord", rl_vi_bWord }, + { "vi-bword", rl_vi_bword }, /* BEWARE: name matching is case insensitive */ + { "vi-change-case", rl_vi_change_case }, + { "vi-change-char", rl_vi_change_char }, + { "vi-change-to", rl_vi_change_to }, + { "vi-char-search", rl_vi_char_search }, + { "vi-column", rl_vi_column }, + { "vi-complete", rl_vi_complete }, + { "vi-delete", rl_vi_delete }, + { "vi-delete-to", rl_vi_delete_to }, + { "vi-eWord", rl_vi_eWord }, + { "vi-editing-mode", rl_vi_editing_mode }, + { "vi-end-bigword", rl_vi_eWord }, + { "vi-end-word", rl_vi_end_word }, + { "vi-eof-maybe", rl_vi_eof_maybe }, + { "vi-eword", rl_vi_eword }, /* BEWARE: name matching is case insensitive */ + { "vi-fWord", rl_vi_fWord }, + { "vi-fetch-history", rl_vi_fetch_history }, + { "vi-first-print", rl_vi_first_print }, + { "vi-forward-bigword", rl_vi_fWord }, + { "vi-forward-word", rl_vi_fword }, + { "vi-fword", rl_vi_fword }, /* BEWARE: name matching is case insensitive */ + { "vi-goto-mark", rl_vi_goto_mark }, + { "vi-insert-beg", rl_vi_insert_beg }, + { "vi-insertion-mode", rl_vi_insert_mode }, + { "vi-match", rl_vi_match }, + { "vi-movement-mode", rl_vi_movement_mode }, + { "vi-next-word", rl_vi_next_word }, + { "vi-overstrike", rl_vi_overstrike }, + { "vi-overstrike-delete", rl_vi_overstrike_delete }, + { "vi-prev-word", rl_vi_prev_word }, + { "vi-put", rl_vi_put }, + { "vi-redo", rl_vi_redo }, + { "vi-replace", rl_vi_replace }, + { "vi-rubout", rl_vi_rubout }, + { "vi-search", rl_vi_search }, + { "vi-search-again", rl_vi_search_again }, + { "vi-set-mark", rl_vi_set_mark }, + { "vi-subst", rl_vi_subst }, + { "vi-tilde-expand", rl_vi_tilde_expand }, + { "vi-undo", rl_vi_undo }, + { "vi-unix-word-rubout", rl_vi_unix_word_rubout }, + { "vi-yank-arg", rl_vi_yank_arg }, + { "vi-yank-pop", rl_vi_yank_pop }, + { "vi-yank-to", rl_vi_yank_to }, +#endif /* VI_MODE */ + + {(char *)NULL, (rl_command_func_t *)NULL } +}; + +int +rl_add_funmap_entry (const char *name, rl_command_func_t *function) +{ + if (funmap_entry + 2 >= funmap_size) + { + funmap_size += 64; + funmap = (FUNMAP **)xrealloc (funmap, funmap_size * sizeof (FUNMAP *)); + } + + funmap[funmap_entry] = (FUNMAP *)xmalloc (sizeof (FUNMAP)); + funmap[funmap_entry]->name = name; + funmap[funmap_entry]->function = function; + + funmap[++funmap_entry] = (FUNMAP *)NULL; + return funmap_entry; +} + +static int funmap_initialized; + +/* Make the funmap contain all of the default entries. */ +void +rl_initialize_funmap (void) +{ + register int i; + + if (funmap_initialized) + return; + + for (i = 0; default_funmap[i].name; i++) + rl_add_funmap_entry (default_funmap[i].name, default_funmap[i].function); + + funmap_initialized = 1; + funmap_program_specific_entry_start = i; +} + +/* Produce a NULL terminated array of known function names. The array + is sorted. The array itself is allocated, but not the strings inside. + You should free () the array when you done, but not the pointers. */ +const char ** +rl_funmap_names (void) +{ + const char **result; + int result_size, result_index; + + /* Make sure that the function map has been initialized. */ + rl_initialize_funmap (); + + for (result_index = result_size = 0, result = (const char **)NULL; funmap[result_index]; result_index++) + { + if (result_index + 2 > result_size) + { + result_size += 20; + result = (const char **)xrealloc (result, result_size * sizeof (char *)); + } + + result[result_index] = funmap[result_index]->name; + result[result_index + 1] = (char *)NULL; + } + + qsort (result, result_index, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare); + return (result); +} diff --git a/lib/readline/histexpand.c b/lib/readline/histexpand.c new file mode 100644 index 0000000..8ab6809 --- /dev/null +++ b/lib/readline/histexpand.c @@ -0,0 +1,1718 @@ +/* histexpand.c -- history expansion. */ + +/* Copyright (C) 1989-2021 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# ifndef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "rlmbutil.h" + +#include "history.h" +#include "histlib.h" +#include "chardefs.h" + +#include "rlshell.h" +#include "xmalloc.h" + +#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" +#define HISTORY_QUOTE_CHARACTERS "\"'`" +#define HISTORY_EVENT_DELIMITERS "^$*%-" + +#define slashify_in_quotes "\\`\"$" + +#define fielddelim(c) (whitespace(c) || (c) == '\n') + +typedef int _hist_search_func_t (const char *, int); + +static char error_pointer; + +static char *subst_lhs; +static char *subst_rhs; +static int subst_lhs_len; +static int subst_rhs_len; + +/* Characters that delimit history event specifications and separate event + specifications from word designators. Static for now */ +static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS; + +static char *get_history_word_specifier (char *, char *, int *); +static int history_tokenize_word (const char *, int); +static char **history_tokenize_internal (const char *, int, int *); +static char *history_substring (const char *, int, int); +static void freewords (char **, int); +static char *history_find_word (char *, int); + +static char *quote_breaks (char *); + +/* Variables exported by this file. */ +/* The character that represents the start of a history expansion + request. This is usually `!'. */ +char history_expansion_char = '!'; + +/* The character that invokes word substitution if found at the start of + a line. This is usually `^'. */ +char history_subst_char = '^'; + +/* During tokenization, if this character is seen as the first character + of a word, then it, and all subsequent characters up to a newline are + ignored. For a Bourne shell, this should be '#'. Bash special cases + the interactive comment character to not be a comment delimiter. */ +char history_comment_char = '\0'; + +/* The list of characters which inhibit the expansion of text if found + immediately following history_expansion_char. */ +char *history_no_expand_chars = " \t\n\r="; + +/* If set to a non-zero value, single quotes inhibit history expansion. + The default is 0. */ +int history_quotes_inhibit_expansion = 0; + +/* Used to split words by history_tokenize_internal. */ +char *history_word_delimiters = HISTORY_WORD_DELIMITERS; + +/* If set, this points to a function that is called to verify that a + particular history expansion should be performed. */ +rl_linebuf_func_t *history_inhibit_expansion_function; + +int history_quoting_state = 0; + +/* **************************************************************** */ +/* */ +/* History Expansion */ +/* */ +/* **************************************************************** */ + +/* Hairy history expansion on text, not tokens. This is of general + use, and thus belongs in this library. */ + +/* The last string searched for by a !?string? search. */ +static char *search_string; +/* The last string matched by a !?string? search. */ +static char *search_match; + +/* Return the event specified at TEXT + OFFSET modifying OFFSET to + point to after the event specifier. Just a pointer to the history + line is returned; NULL is returned in the event of a bad specifier. + You pass STRING with *INDEX equal to the history_expansion_char that + begins this specification. + DELIMITING_QUOTE is a character that is allowed to end the string + specification for what to search for in addition to the normal + characters `:', ` ', `\t', `\n', and sometimes `?'. + So you might call this function like: + line = get_history_event ("!echo:p", &index, 0); */ +char * +get_history_event (const char *string, int *caller_index, int delimiting_quote) +{ + register int i; + register char c; + HIST_ENTRY *entry; + int which, sign, local_index, substring_okay; + _hist_search_func_t *search_func; + char *temp; + + /* The event can be specified in a number of ways. + + !! the previous command + !n command line N + !-n current command-line minus N + !str the most recent command starting with STR + !?str[?] + the most recent command containing STR + + All values N are determined via HISTORY_BASE. */ + + i = *caller_index; + + if (string[i] != history_expansion_char) + return ((char *)NULL); + + /* Move on to the specification. */ + i++; + + sign = 1; + substring_okay = 0; + +#define RETURN_ENTRY(e, w) \ + return ((e = history_get (w)) ? e->line : (char *)NULL) + + /* Handle !! case. */ + if (string[i] == history_expansion_char) + { + i++; + which = history_base + (history_length - 1); + *caller_index = i; + RETURN_ENTRY (entry, which); + } + + /* Hack case of numeric line specification. */ + if (string[i] == '-' && _rl_digit_p (string[i+1])) + { + sign = -1; + i++; + } + + if (_rl_digit_p (string[i])) + { + /* Get the extent of the digits and compute the value. */ + for (which = 0; _rl_digit_p (string[i]); i++) + which = (which * 10) + _rl_digit_value (string[i]); + + *caller_index = i; + + if (sign < 0) + which = (history_length + history_base) - which; + + RETURN_ENTRY (entry, which); + } + + /* This must be something to search for. If the spec begins with + a '?', then the string may be anywhere on the line. Otherwise, + the string must be found at the start of a line. */ + if (string[i] == '?') + { + substring_okay++; + i++; + } + + /* Only a closing `?' or a newline delimit a substring search string. */ + for (local_index = i; c = string[i]; i++) + { +#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 ((char *)string, i, &ps); + if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1) + { + i += v - 1; + continue; + } + } + +#endif /* HANDLE_MULTIBYTE */ + if ((!substring_okay && + (whitespace (c) || c == ':' || + (i > local_index && history_event_delimiter_chars && c == '-') || + (c != '-' && history_event_delimiter_chars && member (c, history_event_delimiter_chars)) || + (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); + if (which) + strncpy (temp, string + local_index, which); + temp[which] = '\0'; + + if (substring_okay && string[i] == '?') + i++; + + *caller_index = i; + +#define FAIL_SEARCH() \ + do { \ + history_offset = history_length; xfree (temp) ; return (char *)NULL; \ + } while (0) + + /* If there is no search string, try to use the previous search string, + if one exists. If not, fail immediately. */ + if (*temp == '\0' && substring_okay) + { + if (search_string) + { + xfree (temp); + temp = savestring (search_string); + } + else + FAIL_SEARCH (); + } + + search_func = substring_okay ? history_search : history_search_prefix; + while (1) + { + local_index = (*search_func) (temp, -1); + + if (local_index < 0) + FAIL_SEARCH (); + + if (local_index == 0 || substring_okay) + { + entry = current_history (); + if (entry == 0) + FAIL_SEARCH (); + history_offset = history_length; + + /* If this was a substring search, then remember the + string that we matched for word substitution. */ + if (substring_okay) + { + FREE (search_string); + search_string = temp; + + FREE (search_match); + search_match = history_find_word (entry->line, local_index); + } + else + xfree (temp); + + return (entry->line); + } + + if (history_offset) + history_offset--; + else + FAIL_SEARCH (); + } +#undef FAIL_SEARCH +#undef RETURN_ENTRY +} + +/* Function for extracting single-quoted strings. Used for inhibiting + history expansion within single quotes. */ + +/* Extract the contents of STRING as if it is enclosed in single quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening single quote; on exit, SINDEX is left pointing + to the closing single quote. FLAGS currently used to allow backslash + to escape a single quote (e.g., for bash $'...'). */ +static void +hist_string_extract_single_quoted (char *string, int *sindex, int flags) +{ + register int i; + + for (i = *sindex; string[i] && string[i] != '\''; i++) + { + if ((flags & 1) && string[i] == '\\' && string[i+1]) + i++; + } + + *sindex = i; +} + +static char * +quote_breaks (char *s) +{ + register char *p, *r; + char *ret; + int len = 3; + + for (p = s; p && *p; p++, len++) + { + if (*p == '\'') + len += 3; + else if (whitespace (*p) || *p == '\n') + len += 2; + } + + r = ret = (char *)xmalloc (len); + *r++ = '\''; + for (p = s; p && *p; ) + { + if (*p == '\'') + { + *r++ = '\''; + *r++ = '\\'; + *r++ = '\''; + *r++ = '\''; + p++; + } + else if (whitespace (*p) || *p == '\n') + { + *r++ = '\''; + *r++ = *p++; + *r++ = '\''; + } + else + *r++ = *p++; + } + *r++ = '\''; + *r = '\0'; + return ret; +} + +static char * +hist_error(char *s, int start, int current, int errtype) +{ + char *temp; + const char *emsg; + int ll, elen; + + ll = current - start; + + switch (errtype) + { + case EVENT_NOT_FOUND: + emsg = "event not found"; + elen = 15; + break; + case BAD_WORD_SPEC: + emsg = "bad word specifier"; + elen = 18; + break; + case SUBST_FAILED: + emsg = "substitution failed"; + elen = 19; + break; + case BAD_MODIFIER: + emsg = "unrecognized history modifier"; + elen = 29; + break; + case NO_PREV_SUBST: + emsg = "no previous substitution"; + elen = 24; + break; + default: + emsg = "unknown expansion error"; + elen = 23; + break; + } + + temp = (char *)xmalloc (ll + elen + 3); + if (s[start]) + strncpy (temp, s + start, ll); + else + ll = 0; + temp[ll] = ':'; + temp[ll + 1] = ' '; + strcpy (temp + ll + 2, emsg); + return (temp); +} + +/* Get a history substitution string from STR starting at *IPTR + and return it. The length is returned in LENPTR. + + A backslash can quote the delimiter. If the string is the + empty string, the previous pattern is used. If there is + no previous pattern for the lhs, the last history search + string is used. + + If IS_RHS is 1, we ignore empty strings and set the pattern + to "" anyway. subst_lhs is not changed if the lhs is empty; + subst_rhs is allowed to be set to the empty string. */ + +static char * +get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr) +{ + register int si, i, j, k; + 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 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) + { + s = (char *)xmalloc (si - i + 1); + for (j = 0, k = i; k < si; j++, k++) + { + /* Remove a backslash quoting the search string delimiter. */ + if (str[k] == '\\' && str[k + 1] == delimiter) + k++; + s[j] = str[k]; + } + s[j] = '\0'; + if (lenptr) + *lenptr = j; + } + + i = si; + if (str[i]) + i++; + *iptr = i; + + return s; +} + +static void +postproc_subst_rhs (void) +{ + char *new; + int i, j, new_size; + + new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); + for (i = j = 0; i < subst_rhs_len; i++) + { + if (subst_rhs[i] == '&') + { + if (j + subst_lhs_len >= new_size) + new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); + strcpy (new + j, subst_lhs); + j += subst_lhs_len; + } + else + { + /* a single backslash protects the `&' from lhs interpolation */ + if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') + i++; + if (j >= new_size) + new = (char *)xrealloc (new, new_size *= 2); + new[j++] = subst_rhs[i]; + } + } + new[j] = '\0'; + xfree (subst_rhs); + subst_rhs = new; + subst_rhs_len = j; +} + +/* Expand the bulk of a history specifier starting at STRING[START]. + Returns 0 if everything is OK, -1 if an error occurred, and 1 + if the `p' modifier was supplied and the caller should just print + the returned string. Returns the new index into string in + *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ +/* need current line for !# */ +static int +history_expand_internal (char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line) +{ + int i, n, starting_index; + int substitute_globally, subst_bywords, 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); + + i = start; + + /* If it is followed by something that starts a word specifier, + then !! is implied as the event specifier. */ + + if (member (string[i + 1], ":$*%^")) + { + char fake_s[3]; + int fake_i = 0; + i++; + fake_s[0] = fake_s[1] = history_expansion_char; + fake_s[2] = '\0'; + event = get_history_event (fake_s, &fake_i, 0); + } + else if (string[i + 1] == '#') + { + i += 2; + event = current_line; + } + else + event = get_history_event (string, &i, qc); + + if (event == 0) + { + *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); + xfree (result); + return (-1); + } + + /* If a word specifier is found, then do what that requires. */ + starting_index = i; + word_spec = get_history_word_specifier (string, event, &i); + + /* There is no such thing as a `malformed word specifier'. However, + it is possible for a specifier that has no match. In that case, + we complain. */ + if (word_spec == (char *)&error_pointer) + { + *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); + xfree (result); + return (-1); + } + + /* If no word specifier, than the thing of interest was the event. */ + temp = word_spec ? savestring (word_spec) : savestring (event); + FREE (word_spec); + + /* Perhaps there are other modifiers involved. Do what they say. */ + want_quotes = substitute_globally = subst_bywords = print_only = 0; + starting_index = i; + + while (string[i] == ':') + { + c = string[i + 1]; + + if (c == 'g' || c == 'a') + { + substitute_globally = 1; + i++; + c = string[i + 1]; + } + else if (c == 'G') + { + subst_bywords = 1; + i++; + c = string[i + 1]; + } + + switch (c) + { + default: + *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); + xfree (result); + xfree (temp); + return -1; + + case 'q': + want_quotes = 'q'; + break; + + case 'x': + want_quotes = 'x'; + break; + + /* :p means make this the last executed line. So we + return an error state after adding this line to the + history. */ + case 'p': + print_only = 1; + break; + + /* :t discards all but the last part of the pathname. */ + case 't': + tstr = strrchr (temp, '/'); + if (tstr) + { + tstr++; + t = savestring (tstr); + xfree (temp); + temp = t; + } + break; + + /* :h discards the last part of a pathname. */ + case 'h': + tstr = strrchr (temp, '/'); + if (tstr) + *tstr = '\0'; + break; + + /* :r discards the suffix. */ + case 'r': + tstr = strrchr (temp, '.'); + if (tstr) + *tstr = '\0'; + break; + + /* :e discards everything but the suffix. */ + case 'e': + tstr = strrchr (temp, '.'); + if (tstr) + { + t = savestring (tstr); + xfree (temp); + temp = t; + } + break; + + /* :s/this/that substitutes `that' for the first + occurrence of `this'. :gs/this/that substitutes `that' + for each occurrence of `this'. :& repeats the last + substitution. :g& repeats the last substitution + globally. */ + + case '&': + case 's': + { + char *new_event; + int delimiter, failed, si, l_temp, ws, we; + + if (c == 's') + { + if (i + 2 < (int)strlen (string)) + { +#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 */ + + i += 3; + + t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len); + /* An empty substitution lhs with no previous substitution + uses the last search string as the lhs. */ + if (t) + { + FREE (subst_lhs); + subst_lhs = t; + } + else if (!subst_lhs) + { + if (search_string && *search_string) + { + subst_lhs = savestring (search_string); + subst_lhs_len = strlen (subst_lhs); + } + else + { + subst_lhs = (char *) NULL; + subst_lhs_len = 0; + } + } + + FREE (subst_rhs); + subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); + + /* If `&' appears in the rhs, it's supposed to be replaced + with the lhs. */ + if (member ('&', subst_rhs)) + postproc_subst_rhs (); + } + else + i += 2; + + /* If there is no lhs, the substitution can't succeed. */ + if (subst_lhs_len == 0) + { + *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); + xfree (result); + xfree (temp); + return -1; + } + + l_temp = strlen (temp); + /* Ignore impossible cases. */ + if (subst_lhs_len > l_temp) + { + *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); + xfree (result); + xfree (temp); + return (-1); + } + + /* Find the first occurrence of THIS in TEMP. */ + /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three + cases to consider: + + 1. substitute_globally == subst_bywords == 0 + 2. substitute_globally == 1 && subst_bywords == 0 + 3. substitute_globally == 0 && subst_bywords == 1 + + In the first case, we substitute for the first occurrence only. + In the second case, we substitute for every occurrence. + In the third case, we tokenize into words and substitute the + first occurrence of each word. */ + + si = we = 0; + for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) + { + /* First skip whitespace and find word boundaries if + we're past the end of the word boundary we found + the last time. */ + if (subst_bywords && si > we) + { + for (; temp[si] && fielddelim (temp[si]); si++) + ; + ws = si; + we = history_tokenize_word (temp, si); + } + + if (STREQN (temp+si, subst_lhs, subst_lhs_len)) + { + int len = subst_rhs_len - subst_lhs_len + l_temp; + new_event = (char *)xmalloc (1 + len); + strncpy (new_event, temp, si); + strncpy (new_event + si, subst_rhs, subst_rhs_len); + strncpy (new_event + si + subst_rhs_len, + temp + si + subst_lhs_len, + l_temp - (si + subst_lhs_len)); + new_event[len] = '\0'; + xfree (temp); + temp = new_event; + + failed = 0; + + if (substitute_globally) + { + /* Reported to fix a bug that causes it to skip every + other match when matching a single character. Was + si += subst_rhs_len previously. */ + si += subst_rhs_len - 1; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + else if (subst_bywords) + { + si = we; + l_temp = strlen (temp); + continue; + } + else + break; + } + } + + if (substitute_globally > 1) + { + substitute_globally = 0; + continue; /* don't want to increment i */ + } + + if (failed == 0) + continue; /* don't want to increment i */ + + *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); + xfree (result); + xfree (temp); + return (-1); + } + } + i += 2; + } + /* Done with modifiers. */ + /* Believe it or not, we have to back the pointer up by one. */ + --i; + + if (want_quotes) + { + char *x; + + if (want_quotes == 'q') + x = sh_single_quote (temp); + else if (want_quotes == 'x') + x = quote_breaks (temp); + else + x = savestring (temp); + + xfree (temp); + temp = x; + } + + n = strlen (temp); + if (n >= result_len) + result = (char *)xrealloc (result, n + 2); + strcpy (result, temp); + xfree (temp); + + *end_index_ptr = i; + *ret_string = result; + return (print_only); +} + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + -1) If there was an error in expansion. + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + 2) If the `p' modifier was given and the caller should print the result + + If an error occurred in expansion, then OUTPUT contains a descriptive + error message. */ + +#define ADD_STRING(s) \ + do \ + { \ + int sl = strlen (s); \ + j += sl; \ + if (j >= result_len) \ + { \ + while (j >= result_len) \ + result_len += 128; \ + result = (char *)xrealloc (result, result_len); \ + } \ + strcpy (result + j - sl, s); \ + } \ + while (0) + +#define ADD_CHAR(c) \ + do \ + { \ + if (j >= result_len - 1) \ + result = (char *)xrealloc (result, result_len += 64); \ + result[j++] = c; \ + result[j] = '\0'; \ + } \ + while (0) + +int +history_expand (char *hstring, char **output) +{ + register int j; + int i, r, l, passc, cc, modified, eindex, only_printing, dquote, squote, flag; + char *string; + + /* The output string, and its length. */ + 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; + + if (output == 0) + return 0; + + /* Setting the history expansion character to 0 inhibits all + history expansion. */ + if (history_expansion_char == 0) + { + *output = savestring (hstring); + return (0); + } + + /* Prepare the buffer for printing error messages. */ + result = (char *)xmalloc (result_len = 256); + result[0] = '\0'; + + only_printing = modified = 0; + l = strlen (hstring); + + /* Grovel the string. Only backslash and single quotes can quote the + history escape character. We also handle arg specifiers. */ + + /* Before we grovel forever, see if the history_expansion_char appears + anywhere within the text. */ + + /* The quick substitution character is a history expansion all right. That + is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, + that is the substitution that we do. */ + if (hstring[0] == history_subst_char) + { + string = (char *)xmalloc (l + 5); + + string[0] = string[1] = history_expansion_char; + string[2] = ':'; + string[3] = 's'; + strcpy (string + 4, hstring); + l += 4; + } + else + { +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + string = hstring; + /* If not quick substitution, still maybe have to do expansion. */ + + /* `!' followed by one of the characters in history_no_expand_chars + is NOT an expansion. */ + dquote = history_quoting_state == '"'; + squote = history_quoting_state == '\''; + + /* If the calling application tells us we are already reading a + single-quoted string, consume the rest of the string right now + and then go on. */ + i = 0; + if (squote && history_quotes_inhibit_expansion) + { + hist_string_extract_single_quoted (string, &i, 0); + squote = 0; + if (string[i]) + i++; + } + + for ( ; 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 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. */ + if (history_comment_char && string[i] == history_comment_char && + dquote == 0 && + (i == 0 || member (string[i - 1], history_word_delimiters))) + { + while (string[i]) + i++; + break; + } + else if (string[i] == history_expansion_char) + { + if (cc == 0 || member (cc, history_no_expand_chars)) + continue; + /* DQUOTE won't be set unless history_quotes_inhibit_expansion + is set. The idea here is to treat double-quoted strings the + same as the word outside double quotes; in effect making the + double quote part of history_no_expand_chars when DQUOTE is + set. */ + else if (dquote && cc == '"') + continue; + /* If the calling application has set + history_inhibit_expansion_function to a function that checks + for special cases that should not be history expanded, + call the function and skip the expansion if it returns a + non-zero value. */ + else if (history_inhibit_expansion_function && + (*history_inhibit_expansion_function) (string, i)) + continue; + else + break; + } + /* Shell-like quoting: allow backslashes to quote double quotes + inside a double-quoted string. */ + else if (dquote && string[i] == '\\' && cc == '"') + i++; + /* More shell-like quoting: if we're paying attention to single + quotes and letting them quote the history expansion character, + then we need to pay attention to double quotes, because single + quotes are not special inside double-quoted strings. */ + else if (history_quotes_inhibit_expansion && string[i] == '"') + { + dquote = 1 - dquote; + } + else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'') + { + /* If this is bash, single quotes inhibit history expansion. */ + flag = (i > 0 && string[i - 1] == '$'); + i++; + hist_string_extract_single_quoted (string, &i, flag); + } + else if (history_quotes_inhibit_expansion && string[i] == '\\') + { + /* If this is bash, allow backslashes to quote single + quotes and the history expansion character. */ + if (cc == '\'' || cc == history_expansion_char) + i++; + } + + } + + if (string[i] != history_expansion_char) + { + xfree (result); + *output = savestring (string); + return (0); + } + } + + /* Extract and perform the substitution. */ + dquote = history_quoting_state == '"'; + squote = history_quoting_state == '\''; + + /* If the calling application tells us we are already reading a + single-quoted string, consume the rest of the string right now + and then go on. */ + i = j = 0; + if (squote && history_quotes_inhibit_expansion) + { + int c; + + hist_string_extract_single_quoted (string, &i, 0); + squote = 0; + for (c = 0; c < i; c++) + ADD_CHAR (string[c]); + if (string[i]) + { + ADD_CHAR (string[i]); + i++; + } + } + + for (passc = 0; i < l; i++) + { + int qc, tchar = string[i]; + + if (passc) + { + passc = 0; + ADD_CHAR (tchar); + 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); + continue; + } + } +#endif /* HANDLE_MULTIBYTE */ + + if (tchar == history_expansion_char) + tchar = -3; + else if (tchar == history_comment_char) + tchar = -2; + + switch (tchar) + { + default: + ADD_CHAR (string[i]); + break; + + case '\\': + passc++; + ADD_CHAR (tchar); + break; + + case '"': + dquote = 1 - dquote; + ADD_CHAR (tchar); + break; + + case '\'': + { + /* If history_quotes_inhibit_expansion is set, single quotes + inhibit history expansion, otherwise they are treated like + double quotes. */ + if (squote) + { + squote = 0; + ADD_CHAR (tchar); + } + else if (dquote == 0 && history_quotes_inhibit_expansion) + { + int quote, slen; + + flag = (i > 0 && string[i - 1] == '$'); + quote = i++; + hist_string_extract_single_quoted (string, &i, flag); + + slen = i - quote + 2; + temp = (char *)xmalloc (slen); + strncpy (temp, string + quote, slen); + temp[slen - 1] = '\0'; + ADD_STRING (temp); + xfree (temp); + } + else if (dquote == 0 && squote == 0 && history_quotes_inhibit_expansion == 0) + { + squote = 1; + ADD_CHAR (string[i]); + } + else + ADD_CHAR (string[i]); + break; + } + + case -2: /* history_comment_char */ + if ((dquote == 0 || history_quotes_inhibit_expansion == 0) && + (i == 0 || member (string[i - 1], history_word_delimiters))) + { + temp = (char *)xmalloc (l - i + 1); + strcpy (temp, string + i); + ADD_STRING (temp); + xfree (temp); + i = l; + } + else + ADD_CHAR (string[i]); + break; + + case -3: /* history_expansion_char */ + cc = string[i + 1]; + + /* If the history_expansion_char is followed by one of the + characters in history_no_expand_chars, then it is not a + candidate for expansion of any kind. */ + if (cc == 0 || member (cc, history_no_expand_chars) || + (dquote && cc == '"')) + { + ADD_CHAR (string[i]); + break; + } + + /* If the application has defined a function to determine whether + or not a history expansion should be performed, call it here. */ + /* We check against what we've expanded so far, with the current + expansion appended, because that seems to be what csh does. We + decide to expand based on what we have to this point, not what + we started with. */ + if (history_inhibit_expansion_function) + { + int save_j, temp; + + save_j = j; + ADD_CHAR (string[i]); + ADD_CHAR (cc); + + temp = (*history_inhibit_expansion_function) (result, save_j); + if (temp) + { + result[--j] = '\0'; /* `unadd' cc, leaving ADD_CHAR(string[i]) */ + break; + } + else + result[j = save_j] = '\0'; + } + +#if defined (NO_BANG_HASH_MODIFIERS) + /* There is something that is listed as a `word specifier' in csh + documentation which means `the expanded text to this point'. + That is not a word specifier, it is an event specifier. If we + don't want to allow modifiers with `!#', just stick the current + output line in again. */ + if (cc == '#') + { + if (result) + { + temp = (char *)xmalloc (1 + strlen (result)); + strcpy (temp, result); + ADD_STRING (temp); + xfree (temp); + } + i++; + break; + } +#endif + qc = squote ? '\'' : (dquote ? '"' : 0); + r = history_expand_internal (string, i, qc, &eindex, &temp, result); + if (r < 0) + { + *output = temp; + xfree (result); + if (string != hstring) + xfree (string); + return -1; + } + else + { + if (temp) + { + modified++; + if (*temp) + ADD_STRING (temp); + xfree (temp); + } + only_printing += r == 1; + i = eindex; + } + break; + } + } + + *output = result; + if (string != hstring) + xfree (string); + + if (only_printing) + { +#if 0 + add_history (result); +#endif + return (2); + } + + return (modified != 0); +} + +/* Return a consed string which is the word specified in SPEC, and found + in FROM. NULL is returned if there is no spec. The address of + ERROR_POINTER is returned if the word specified cannot be found. + CALLER_INDEX is the offset in SPEC to start looking; it is updated + to point to just after the last character parsed. */ +static char * +get_history_word_specifier (char *spec, char *from, int *caller_index) +{ + register int i = *caller_index; + int first, last; + int expecting_word_spec = 0; + char *result; + + /* The range of words to return doesn't exist yet. */ + first = last = 0; + result = (char *)NULL; + + /* If we found a colon, then this *must* be a word specification. If + it isn't, then it is an error. */ + if (spec[i] == ':') + { + i++; + expecting_word_spec++; + } + + /* Handle special cases first. */ + + /* `%' is the word last searched for. */ + if (spec[i] == '%') + { + *caller_index = i + 1; + return (search_match ? savestring (search_match) : savestring ("")); + } + + /* `*' matches all of the arguments, but not the command. */ + if (spec[i] == '*') + { + *caller_index = i + 1; + result = history_arg_extract (1, '$', from); + return (result ? result : savestring ("")); + } + + /* `$' is last arg. */ + if (spec[i] == '$') + { + *caller_index = i + 1; + return (history_arg_extract ('$', '$', from)); + } + + /* Try to get FIRST and LAST figured out. */ + + if (spec[i] == '-') + first = 0; + else if (spec[i] == '^') + { + first = 1; + i++; + } + else if (_rl_digit_p (spec[i]) && expecting_word_spec) + { + for (first = 0; _rl_digit_p (spec[i]); i++) + first = (first * 10) + _rl_digit_value (spec[i]); + } + else + return ((char *)NULL); /* no valid `first' for word specifier */ + + if (spec[i] == '^' || spec[i] == '*') + { + last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ + i++; + } + else if (spec[i] != '-') + last = first; + else + { + i++; + + if (_rl_digit_p (spec[i])) + { + for (last = 0; _rl_digit_p (spec[i]); i++) + last = (last * 10) + _rl_digit_value (spec[i]); + } + else if (spec[i] == '$') + { + i++; + last = '$'; + } + else if (spec[i] == '^') + { + i++; + last = 1; + } +#if 0 + else if (!spec[i] || spec[i] == ':') + /* check against `:' because there could be a modifier separator */ +#else + else + /* csh seems to allow anything to terminate the word spec here, + leaving it as an abbreviation. */ +#endif + last = -1; /* x- abbreviates x-$ omitting word `$' */ + } + + *caller_index = i; + + if (last >= first || last == '$' || last < 0) + result = history_arg_extract (first, last, from); + + return (result ? result : (char *)&error_pointer); +} + +/* Extract the args specified, starting at FIRST, and ending at LAST. + The args are taken from STRING. If either FIRST or LAST is < 0, + then make that arg count from the right (subtract from the number of + tokens, so that FIRST = -1 means the next to last token on the line). + If LAST is `$' the last arg from STRING is used. */ +char * +history_arg_extract (int first, int last, const char *string) +{ + register int i, len; + char *result; + int size, offset; + char **list; + + /* XXX - think about making history_tokenize return a struct array, + each struct in array being a string and a length to avoid the + calls to strlen below. */ + if ((list = history_tokenize (string)) == NULL) + return ((char *)NULL); + + for (len = 0; list[len]; len++) + ; + + if (last < 0) + last = len + last - 1; + + if (first < 0) + first = len + first - 1; + + if (last == '$') + last = len - 1; + + if (first == '$') + first = len - 1; + + last++; + + if (first >= len || last > len || first < 0 || last < 0 || first > last) + result = ((char *)NULL); + else + { + for (size = 0, i = first; i < last; i++) + size += strlen (list[i]) + 1; + result = (char *)xmalloc (size + 1); + result[0] = '\0'; + + for (i = first, offset = 0; i < last; i++) + { + strcpy (result + offset, list[i]); + offset += strlen (list[i]); + if (i + 1 < last) + { + result[offset++] = ' '; + result[offset] = 0; + } + } + } + + for (i = 0; i < len; i++) + xfree (list[i]); + xfree (list); + + return (result); +} + +static int +history_tokenize_word (const char *string, int ind) +{ + register int i, j; + int delimiter, nestdelim, delimopen; + + i = ind; + delimiter = nestdelim = 0; + + if (member (string[i], "()\n")) /* XXX - included \n, but why? been here forever */ + { + i++; + return i; + } + + if (ISDIGIT (string[i])) + { + j = i; + while (string[j] && ISDIGIT (string[j])) + j++; + if (string[j] == 0) + return (j); + if (string[j] == '<' || string[j] == '>') + i = j; /* digit sequence is a file descriptor */ + else + { + i = j; + goto get_word; /* digit sequence is part of a word */ + } + } + + if (member (string[i], "<>;&|")) + { + int peek = string[i + 1]; + + if (peek == string[i]) + { + if (peek == '<' && string[i + 2] == '-') + i++; + else if (peek == '<' && string[i + 2] == '<') + i++; + i += 2; + return i; + } + else if (peek == '&' && (string[i] == '>' || string[i] == '<')) + { + j = i + 2; + while (string[j] && ISDIGIT (string[j])) /* file descriptor */ + j++; + if (string[j] =='-') /* <&[digits]-, >&[digits]- */ + j++; + return j; + } + else if ((peek == '>' && string[i] == '&') || (peek == '|' && string[i] == '>')) + { + i += 2; + return i; + } + /* XXX - process substitution -- separated out for later -- bash-4.2 */ + else if (peek == '(' && (string[i] == '>' || string[i] == '<')) /*)*/ + { + i += 2; + delimopen = '('; + delimiter = ')'; + nestdelim = 1; + goto get_word; + } + + i++; + return i; + } + +get_word: + /* Get word from string + i; */ + + if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i++]; + + for (; string[i]; i++) + { + if (string[i] == '\\' && string[i + 1] == '\n') + { + i++; + continue; + } + + if (string[i] == '\\' && delimiter != '\'' && + (delimiter != '"' || member (string[i], slashify_in_quotes))) + { + i++; + continue; + } + + /* delimiter must be set and set to something other than a quote if + nestdelim is set, so these tests are safe. */ + if (nestdelim && string[i] == delimopen) + { + nestdelim++; + continue; + } + if (nestdelim && string[i] == delimiter) + { + nestdelim--; + if (nestdelim == 0) + delimiter = 0; + continue; + } + + if (delimiter && string[i] == delimiter) + { + delimiter = 0; + continue; + } + + /* Command and process substitution; shell extended globbing patterns */ + if (nestdelim == 0 && delimiter == 0 && member (string[i], "<>$!@?+*") && string[i+1] == '(') /*)*/ + { + i += 2; + delimopen = '('; + delimiter = ')'; + nestdelim = 1; + continue; + } + + if (delimiter == 0 && (member (string[i], history_word_delimiters))) + break; + + if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i]; + } + + return i; +} + +static char * +history_substring (const char *string, int start, int end) +{ + register int len; + register char *result; + + len = end - start; + result = (char *)xmalloc (len + 1); + strncpy (result, string + start, len); + result[len] = '\0'; + return result; +} + +/* Parse STRING into tokens and return an array of strings. If WIND is + not -1 and INDP is not null, we also want the word surrounding index + WIND. The position in the returned array of strings is returned in + *INDP. */ +static char ** +history_tokenize_internal (const char *string, int wind, int *indp) +{ + char **result; + register int i, start, result_index, size; + + /* If we're searching for a string that's not part of a word (e.g., " "), + make sure we set *INDP to a reasonable value. */ + if (indp && wind != -1) + *indp = -1; + + /* Get a token, and stuff it into RESULT. The tokens are split + exactly where the shell would split them. */ + for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) + { + /* Skip leading whitespace. */ + for (; string[i] && fielddelim (string[i]); i++) + ; + if (string[i] == 0 || string[i] == history_comment_char) + return (result); + + start = i; + + i = history_tokenize_word (string, start); + + /* If we have a non-whitespace delimiter character (which would not be + skipped by the loop above), use it and any adjacent delimiters to + make a separate field. Any adjacent white space will be skipped the + next time through the loop. */ + if (i == start && history_word_delimiters) + { + i++; + while (string[i] && member (string[i], history_word_delimiters)) + i++; + } + + /* If we are looking for the word in which the character at a + particular index falls, remember it. */ + if (indp && wind != -1 && wind >= start && wind < i) + *indp = result_index; + + if (result_index + 2 >= size) + result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); + + result[result_index++] = history_substring (string, start, i); + result[result_index] = (char *)NULL; + } + + return (result); +} + +/* Return an array of tokens, much as the shell might. The tokens are + parsed out of STRING. */ +char ** +history_tokenize (const char *string) +{ + return (history_tokenize_internal (string, -1, (int *)NULL)); +} + +/* Free members of WORDS from START to an empty string */ +static void +freewords (char **words, int start) +{ + register int i; + + for (i = start; words[i]; i++) + xfree (words[i]); +} + +/* Find and return the word which contains the character at index IND + in the history line LINE. Used to save the word matched by the + last history !?string? search. */ +static char * +history_find_word (char *line, int ind) +{ + char **words, *s; + int i, wind; + + words = history_tokenize_internal (line, ind, &wind); + if (wind == -1 || words == 0) + { + if (words) + freewords (words, 0); + FREE (words); + return ((char *)NULL); + } + s = words[wind]; + for (i = 0; i < wind; i++) + xfree (words[i]); + freewords (words, wind + 1); + xfree (words); + return s; +} diff --git a/lib/readline/histfile.c b/lib/readline/histfile.c new file mode 100644 index 0000000..3bfec55 --- /dev/null +++ b/lib/readline/histfile.c @@ -0,0 +1,833 @@ +/* histfile.c - functions to manipulate the history file. */ + +/* Copyright (C) 1989-2019 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* The goal is to make the implementation transparent, so that you + don't have to know what data types are used, just what functions + you can call. I think I have done that. */ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# define _XOPEN_SOURCE_EXTENDED 1 +# include <unistd.h> +# include <floss.h> +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#include <sys/types.h> +#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif +#include "posixstat.h" +#include <fcntl.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <ctype.h> + +#if defined (__EMX__) +# undef HAVE_MMAP +#endif + +#ifdef HISTORY_USE_MMAP +# include <sys/mman.h> + +# ifdef MAP_FILE +# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE) +# define MAP_WFLAGS (MAP_FILE|MAP_SHARED) +# else +# define MAP_RFLAGS MAP_PRIVATE +# define MAP_WFLAGS MAP_SHARED +# endif + +# ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +# endif + +#endif /* HISTORY_USE_MMAP */ + +#if defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif + +/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment + on win 95/98/nt), we want to open files with O_BINARY mode so that there + is no \n -> \r\n conversion performed. On other systems, we don't want to + mess around with O_BINARY at all, so we ensure that it's defined to 0. */ +#if defined (__EMX__) || defined (__CYGWIN__) +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#else /* !__EMX__ && !__CYGWIN__ */ +# undef O_BINARY +# define O_BINARY 0 +#endif /* !__EMX__ && !__CYGWIN__ */ + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#include "history.h" +#include "histlib.h" + +#include "rlshell.h" +#include "xmalloc.h" + +#if !defined (PATH_MAX) +# define PATH_MAX 1024 /* default */ +#endif + +/* history file version; currently unused */ +int history_file_version = 1; + +/* If non-zero, we write timestamps to the history file in history_do_write() */ +int history_write_timestamps = 0; + +/* If non-zero, we assume that a history file that starts with a timestamp + uses timestamp-delimited entries and can include multi-line history + entries. Used by read_history_range */ +int history_multiline_entries = 0; + +/* Immediately after a call to read_history() or read_history_range(), this + will return the number of lines just read from the history file in that + call. */ +int history_lines_read_from_file = 0; + +/* Immediately after a call to write_history() or history_do_write(), this + will return the number of lines just written to the history file in that + call. This also works with history_truncate_file. */ +int history_lines_written_to_file = 0; + +/* Does S look like the beginning of a history timestamp entry? Placeholder + for more extensive tests. */ +#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) ) + +static char *history_backupfile (const char *); +static char *history_tempfile (const char *); +static int histfile_backup (const char *, const char *); +static int histfile_restore (const char *, const char *); +static int history_rename (const char *, const char *); + +/* Return the string that should be used in the place of this + filename. This only matters when you don't specify the + filename to read_history (), or write_history (). */ +static char * +history_filename (const char *filename) +{ + char *return_val; + const char *home; + int home_len; + + return_val = filename ? savestring (filename) : (char *)NULL; + + if (return_val) + return (return_val); + + home = sh_get_env_value ("HOME"); +#if defined (_WIN32) + if (home == 0) + home = sh_get_env_value ("APPDATA"); +#endif + + if (home == 0) + return (NULL); + else + home_len = strlen (home); + + return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */ + strcpy (return_val, home); + return_val[home_len] = '/'; +#if defined (__MSDOS__) + strcpy (return_val + home_len + 1, "_history"); +#else + strcpy (return_val + home_len + 1, ".history"); +#endif + + return (return_val); +} + +static char * +history_backupfile (const char *filename) +{ + const char *fn; + char *ret, linkbuf[PATH_MAX+1]; + size_t len; + ssize_t n; + struct stat fs; + + fn = filename; +#if defined (HAVE_READLINK) + /* Follow symlink to avoid backing up symlink itself; call will fail if + not a symlink */ + if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0) + { + linkbuf[n] = '\0'; + fn = linkbuf; + } +#endif + + len = strlen (fn); + ret = xmalloc (len + 2); + strcpy (ret, fn); + ret[len] = '-'; + ret[len+1] = '\0'; + return ret; +} + +static char * +history_tempfile (const char *filename) +{ + const char *fn; + char *ret, linkbuf[PATH_MAX+1]; + size_t len; + ssize_t n; + struct stat fs; + int pid; + + fn = filename; +#if defined (HAVE_READLINK) + /* Follow symlink so tempfile created in the same directory as any symlinked + history file; call will fail if not a symlink */ + if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0) + { + linkbuf[n] = '\0'; + fn = linkbuf; + } +#endif + + len = strlen (fn); + ret = xmalloc (len + 11); + strcpy (ret, fn); + + pid = (int)getpid (); + + /* filename-PID.tmp */ + ret[len] = '-'; + ret[len+1] = (pid / 10000 % 10) + '0'; + ret[len+2] = (pid / 1000 % 10) + '0'; + ret[len+3] = (pid / 100 % 10) + '0'; + ret[len+4] = (pid / 10 % 10) + '0'; + ret[len+5] = (pid % 10) + '0'; + strcpy (ret + len + 6, ".tmp"); + + return ret; +} + +/* Add the contents of FILENAME to the history list, a line at a time. + If FILENAME is NULL, then read from ~/.history. Returns 0 if + successful, or errno if not. */ +int +read_history (const char *filename) +{ + return (read_history_range (filename, 0, -1)); +} + +/* Read a range of lines from FILENAME, adding them to the history list. + Start reading at the FROM'th line and end at the TO'th. If FROM + is zero, start at the beginning. If TO is less than FROM, read + until the end of the file. If FILENAME is NULL, then read from + ~/.history. Returns 0 if successful, or errno if not. */ +int +read_history_range (const char *filename, int from, int to) +{ + register char *line_start, *line_end, *p; + char *input, *buffer, *bufend, *last_ts; + int file, current_line, chars_read, has_timestamps, reset_comment_char; + struct stat finfo; + size_t file_size; +#if defined (EFBIG) + int overflow_errno = EFBIG; +#elif defined (EOVERFLOW) + int overflow_errno = EOVERFLOW; +#else + int overflow_errno = EIO; +#endif + + history_lines_read_from_file = 0; + + buffer = last_ts = (char *)NULL; + input = history_filename (filename); + file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1; + + if ((file < 0) || (fstat (file, &finfo) == -1)) + goto error_and_exit; + + if (S_ISREG (finfo.st_mode) == 0) + { +#ifdef EFTYPE + errno = EFTYPE; +#else + errno = EINVAL; +#endif + goto error_and_exit; + } + + file_size = (size_t)finfo.st_size; + + /* check for overflow on very large files */ + if (file_size != finfo.st_size || file_size + 1 < file_size) + { + errno = overflow_errno; + goto error_and_exit; + } + + if (file_size == 0) + { + xfree (input); + close (file); + return 0; /* don't waste time if we don't have to */ + } + +#ifdef HISTORY_USE_MMAP + /* We map read/write and private so we can change newlines to NULs without + affecting the underlying object. */ + buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0); + if ((void *)buffer == MAP_FAILED) + { + errno = overflow_errno; + goto error_and_exit; + } + chars_read = file_size; +#else + buffer = (char *)malloc (file_size + 1); + if (buffer == 0) + { + errno = overflow_errno; + goto error_and_exit; + } + + chars_read = read (file, buffer, file_size); +#endif + if (chars_read < 0) + { + error_and_exit: + if (errno != 0) + chars_read = errno; + else + chars_read = EIO; + if (file >= 0) + close (file); + + FREE (input); +#ifndef HISTORY_USE_MMAP + FREE (buffer); +#endif + + return (chars_read); + } + + close (file); + + /* Set TO to larger than end of file if negative. */ + if (to < 0) + to = chars_read; + + /* Start at beginning of file, work to end. */ + bufend = buffer + chars_read; + *bufend = '\0'; /* null-terminate buffer for timestamp checks */ + current_line = 0; + + /* Heuristic: the history comment character rarely changes, so assume we + have timestamps if the buffer starts with `#[:digit:]' and temporarily + set history_comment_char so timestamp parsing works right */ + reset_comment_char = 0; + if (history_comment_char == '\0' && buffer[0] == '#' && isdigit ((unsigned char)buffer[1])) + { + history_comment_char = '#'; + reset_comment_char = 1; + } + + has_timestamps = HIST_TIMESTAMP_START (buffer); + history_multiline_entries += has_timestamps && history_write_timestamps; + + /* Skip lines until we are at FROM. */ + if (has_timestamps) + last_ts = buffer; + for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++) + if (*line_end == '\n') + { + p = line_end + 1; + /* If we see something we think is a timestamp, continue with this + line. We should check more extensively here... */ + if (HIST_TIMESTAMP_START(p) == 0) + current_line++; + else + last_ts = p; + line_start = p; + /* If we are at the last line (current_line == from) but we have + timestamps (has_timestamps), then line_start points to the + text of the last command, and we need to skip to its end. */ + if (current_line >= from && has_timestamps) + { + for (line_end = p; line_end < bufend && *line_end != '\n'; line_end++) + ; + line_start = (*line_end == '\n') ? line_end + 1 : line_end; + } + } + + /* If there are lines left to gobble, then gobble them now. */ + for (line_end = line_start; line_end < bufend; line_end++) + if (*line_end == '\n') + { + /* Change to allow Windows-like \r\n end of line delimiter. */ + if (line_end > line_start && line_end[-1] == '\r') + line_end[-1] = '\0'; + else + *line_end = '\0'; + + if (*line_start) + { + if (HIST_TIMESTAMP_START(line_start) == 0) + { + if (last_ts == NULL && history_length > 0 && history_multiline_entries) + _hs_append_history_line (history_length - 1, line_start); + else + add_history (line_start); + if (last_ts) + { + add_history_time (last_ts); + last_ts = NULL; + } + } + else + { + last_ts = line_start; + current_line--; + } + } + + current_line++; + + if (current_line >= to) + break; + + line_start = line_end + 1; + } + + history_lines_read_from_file = current_line; + if (reset_comment_char) + history_comment_char = '\0'; + + FREE (input); +#ifndef HISTORY_USE_MMAP + FREE (buffer); +#else + munmap (buffer, file_size); +#endif + + return (0); +} + +/* We need a special version for WIN32 because Windows rename() refuses to + overwrite an existing file. */ +static int +history_rename (const char *old, const char *new) +{ +#if defined (_WIN32) + return (MoveFileEx (old, new, MOVEFILE_REPLACE_EXISTING) == 0 ? -1 : 0); +#else + return (rename (old, new)); +#endif +} + +/* Save FILENAME to BACK, handling case where FILENAME is a symlink + (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */ +static int +histfile_backup (const char *filename, const char *back) +{ +#if defined (HAVE_READLINK) + char linkbuf[PATH_MAX+1]; + ssize_t n; + + /* Follow to target of symlink to avoid renaming symlink itself */ + if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0) + { + linkbuf[n] = '\0'; + return (history_rename (linkbuf, back)); + } +#endif + return (history_rename (filename, back)); +} + +/* Restore ORIG from BACKUP handling case where ORIG is a symlink + (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */ +static int +histfile_restore (const char *backup, const char *orig) +{ +#if defined (HAVE_READLINK) + char linkbuf[PATH_MAX+1]; + ssize_t n; + + /* Follow to target of symlink to avoid renaming symlink itself */ + if ((n = readlink (orig, linkbuf, sizeof (linkbuf) - 1)) > 0) + { + linkbuf[n] = '\0'; + return (history_rename (backup, linkbuf)); + } +#endif + return (history_rename (backup, orig)); +} + +/* Should we call chown, based on whether finfo and nfinfo describe different + files with different owners? */ + +#define SHOULD_CHOWN(finfo, nfinfo) \ + (finfo.st_uid != nfinfo.st_uid || finfo.st_gid != nfinfo.st_gid) + +/* Truncate the history file FNAME, leaving only LINES trailing lines. + If FNAME is NULL, then use ~/.history. Writes a new file and renames + it to the original name. Returns 0 on success, errno on failure. */ +int +history_truncate_file (const char *fname, int lines) +{ + char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */ + int file, chars_read, rv, orig_lines, exists, r; + struct stat finfo, nfinfo; + size_t file_size; + + history_lines_written_to_file = 0; + + buffer = (char *)NULL; + filename = history_filename (fname); + tempname = 0; + file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1; + rv = exists = 0; + + /* Don't try to truncate non-regular files. */ + if (file == -1 || fstat (file, &finfo) == -1) + { + rv = errno; + if (file != -1) + close (file); + goto truncate_exit; + } + exists = 1; + + nfinfo.st_uid = finfo.st_uid; + nfinfo.st_gid = finfo.st_gid; + + if (S_ISREG (finfo.st_mode) == 0) + { + close (file); +#ifdef EFTYPE + rv = EFTYPE; +#else + rv = EINVAL; +#endif + goto truncate_exit; + } + + file_size = (size_t)finfo.st_size; + + /* check for overflow on very large files */ + if (file_size != finfo.st_size || file_size + 1 < file_size) + { + close (file); +#if defined (EFBIG) + rv = errno = EFBIG; +#elif defined (EOVERFLOW) + rv = errno = EOVERFLOW; +#else + rv = errno = EINVAL; +#endif + goto truncate_exit; + } + + buffer = (char *)malloc (file_size + 1); + if (buffer == 0) + { + rv = errno; + close (file); + goto truncate_exit; + } + + chars_read = read (file, buffer, file_size); + close (file); + + if (chars_read <= 0) + { + rv = (chars_read < 0) ? errno : 0; + goto truncate_exit; + } + + orig_lines = lines; + /* Count backwards from the end of buffer until we have passed + LINES lines. bp1 is set funny initially. But since bp[1] can't + be a comment character (since it's off the end) and *bp can't be + both a newline and the history comment character, it should be OK. */ + for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--) + { + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) + lines--; + bp1 = bp; + } + + /* If this is the first line, then the file contains exactly the + number of lines we want to truncate to, so we don't need to do + anything. It's the first line if we don't find a newline between + the current value of i and 0. Otherwise, write from the start of + this line until the end of the buffer. */ + for ( ; bp > buffer; bp--) + { + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) + { + bp++; + break; + } + bp1 = bp; + } + + /* Write only if there are more lines in the file than we want to + truncate to. */ + if (bp <= buffer) + { + rv = 0; + /* No-op if LINES == 0 at this point */ + history_lines_written_to_file = orig_lines - lines; + goto truncate_exit; + } + + tempname = history_tempfile (filename); + + if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1) + { + if (write (file, bp, chars_read - (bp - buffer)) < 0) + rv = errno; + + if (fstat (file, &nfinfo) < 0 && rv == 0) + rv = errno; + + if (close (file) < 0 && rv == 0) + rv = errno; + } + else + rv = errno; + + truncate_exit: + FREE (buffer); + + history_lines_written_to_file = orig_lines - lines; + + if (rv == 0 && filename && tempname) + rv = histfile_restore (tempname, filename); + + if (rv != 0) + { + rv = errno; + if (tempname) + unlink (tempname); + history_lines_written_to_file = 0; + } + +#if defined (HAVE_CHOWN) + /* Make sure the new filename is owned by the same user as the old. If one + user is running this, it's a no-op. If the shell is running after sudo + with a shared history file, we don't want to leave the history file + owned by root. */ + if (rv == 0 && exists && SHOULD_CHOWN (finfo, nfinfo)) + r = chown (filename, finfo.st_uid, finfo.st_gid); +#endif + + xfree (filename); + FREE (tempname); + + return rv; +} + +/* Workhorse function for writing history. Writes the last NELEMENT entries + from the history list to FILENAME. OVERWRITE is non-zero if you + wish to replace FILENAME with the entries. */ +static int +history_do_write (const char *filename, int nelements, int overwrite) +{ + register int i; + char *output, *tempname, *histname; + int file, mode, rv, exists; + struct stat finfo, nfinfo; +#ifdef HISTORY_USE_MMAP + size_t cursize; + + history_lines_written_to_file = 0; + + 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 + histname = history_filename (filename); + exists = histname ? (stat (histname, &finfo) == 0) : 0; + + tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0; + output = tempname ? tempname : histname; + + file = output ? open (output, mode, 0600) : -1; + rv = 0; + + if (file == -1) + { + rv = errno; + FREE (histname); + FREE (tempname); + return (rv); + } + +#ifdef HISTORY_USE_MMAP + cursize = overwrite ? 0 : lseek (file, 0, SEEK_END); +#endif + + if (nelements > history_length) + nelements = history_length; + + /* Build a buffer of all the lines to write, and write them in one syscall. + Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */ + { + HIST_ENTRY **the_history; /* local */ + register int j; + int buffer_size; + char *buffer; + + the_history = history_list (); + /* Calculate the total number of bytes to write. */ + for (buffer_size = 0, i = history_length - nelements; i < history_length; i++) + { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + buffer_size += strlen (the_history[i]->timestamp) + 1; + buffer_size += strlen (the_history[i]->line) + 1; + } + + /* Allocate the buffer, and fill it. */ +#ifdef HISTORY_USE_MMAP + if (ftruncate (file, buffer_size+cursize) == -1) + goto mmap_error; + buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize); + if ((void *)buffer == MAP_FAILED) + { +mmap_error: + rv = errno; + close (file); + if (tempname) + unlink (tempname); + FREE (histname); + FREE (tempname); + return rv; + } +#else + buffer = (char *)malloc (buffer_size); + if (buffer == 0) + { + rv = errno; + close (file); + if (tempname) + unlink (tempname); + FREE (histname); + FREE (tempname); + return rv; + } +#endif + + for (j = 0, i = history_length - nelements; i < history_length; i++) + { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + { + strcpy (buffer + j, the_history[i]->timestamp); + j += strlen (the_history[i]->timestamp); + buffer[j++] = '\n'; + } + strcpy (buffer + j, the_history[i]->line); + j += strlen (the_history[i]->line); + buffer[j++] = '\n'; + } + +#ifdef HISTORY_USE_MMAP + if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0) + rv = errno; +#else + if (write (file, buffer, buffer_size) < 0) + rv = errno; + xfree (buffer); +#endif + } + + history_lines_written_to_file = nelements; + + if (close (file) < 0 && rv == 0) + rv = errno; + + if (rv == 0 && histname && tempname) + rv = histfile_restore (tempname, histname); + + if (rv != 0) + { + rv = errno; + if (tempname) + unlink (tempname); + history_lines_written_to_file = 0; + } + +#if defined (HAVE_CHOWN) + /* Make sure the new filename is owned by the same user as the old. If one + user is running this, it's a no-op. If the shell is running after sudo + with a shared history file, we don't want to leave the history file + owned by root. */ + if (rv == 0 && exists) + mode = chown (histname, finfo.st_uid, finfo.st_gid); +#endif + + FREE (histname); + FREE (tempname); + + return (rv); +} + +/* Append NELEMENT entries to FILENAME. The entries appended are from + the end of the list minus NELEMENTs up to the end of the list. */ +int +append_history (int nelements, const char *filename) +{ + return (history_do_write (filename, nelements, HISTORY_APPEND)); +} + +/* Overwrite FILENAME with the current history. If FILENAME is NULL, + then write the history list to ~/.history. Values returned + are as in read_history ().*/ +int +write_history (const char *filename) +{ + return (history_do_write (filename, history_length, HISTORY_OVERWRITE)); +} diff --git a/lib/readline/histlib.h b/lib/readline/histlib.h new file mode 100644 index 0000000..29fc4d2 --- /dev/null +++ b/lib/readline/histlib.h @@ -0,0 +1,92 @@ +/* histlib.h -- internal definitions for the history library. */ + +/* Copyright (C) 1989-2009,2021-2022 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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) \ + : ((a)[0] == (b)[0]) && (strncmp ((a), (b), (n)) == 0)) +#endif + +#ifndef savestring +#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) +#endif + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifndef _rl_digit_p +#define _rl_digit_p(c) ((c) >= '0' && (c) <= '9') +#endif + +#ifndef _rl_digit_value +#define _rl_digit_value(c) ((c) - '0') +#endif + +#ifndef member +# if !defined (strchr) && !defined (__STDC__) +extern char *strchr (); +# endif /* !strchr && !__STDC__ */ +#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) +#endif + +#ifndef FREE +# define FREE(x) if (x) free (x) +#endif + +/* Possible history errors passed to hist_error. */ +#define EVENT_NOT_FOUND 0 +#define BAD_WORD_SPEC 1 +#define SUBST_FAILED 2 +#define BAD_MODIFIER 3 +#define NO_PREV_SUBST 4 + +/* Possible definitions for history starting point specification. */ +#define NON_ANCHORED_SEARCH 0 +#define ANCHORED_SEARCH 0x01 +#define PATTERN_SEARCH 0x02 + +/* Possible definitions for what style of writing the history file we want. */ +#define HISTORY_APPEND 0 +#define HISTORY_OVERWRITE 1 + +/* internal extern function declarations used by other parts of the library */ + +/* histsearch.c */ +extern int _hs_history_patsearch (const char *, int, int); + +/* history.c */ +extern void _hs_replace_history_data (int, histdata_t *, histdata_t *); +extern int _hs_at_end_of_history (void); + +/* histfile.c */ +extern void _hs_append_history_line (int, const char *); + +#endif /* !_HISTLIB_H_ */ diff --git a/lib/readline/history.c b/lib/readline/history.c new file mode 100644 index 0000000..81d4c16 --- /dev/null +++ b/lib/readline/history.c @@ -0,0 +1,614 @@ +/* history.c -- standalone history library */ + +/* Copyright (C) 1989-2021 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* The goal is to make the implementation transparent, so that you + don't have to know what data types are used, just what functions + you can call. I think I have done that. */ +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#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 + +#include <errno.h> + +#include "history.h" +#include "histlib.h" + +#include "xmalloc.h" + +#if !defined (errno) +extern int errno; +#endif + +/* How big to make the_history when we first allocate it. */ +#define DEFAULT_HISTORY_INITIAL_SIZE 502 + +#define MAX_HISTORY_INITIAL_SIZE 8192 + +/* The number of slots to increase the_history by. */ +#define DEFAULT_HISTORY_GROW_SIZE 50 + +static char *hist_inittime (void); + +/* **************************************************************** */ +/* */ +/* History Functions */ +/* */ +/* **************************************************************** */ + +/* An array of HIST_ENTRY. This is where we store the history. */ +static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL; + +/* Non-zero means that we have enforced a limit on the amount of + history that we save. */ +static int history_stifled; + +/* The current number of slots allocated to the input_history. */ +static int history_size; + +/* If HISTORY_STIFLED is non-zero, then this is the maximum number of + entries to remember. */ +int history_max_entries; +int max_input_history; /* backwards compatibility */ + +/* The current location of the interactive history pointer. Just makes + life easier for outside callers. */ +int history_offset; + +/* The number of strings currently stored in the history list. */ +int history_length; + +/* The logical `base' of the history array. It defaults to 1. */ +int history_base = 1; + +/* Return the current HISTORY_STATE of the history. */ +HISTORY_STATE * +history_get_history_state (void) +{ + HISTORY_STATE *state; + + state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE)); + state->entries = the_history; + state->offset = history_offset; + state->length = history_length; + state->size = history_size; + state->flags = 0; + if (history_stifled) + state->flags |= HS_STIFLED; + + return (state); +} + +/* Set the state of the current history array to STATE. */ +void +history_set_history_state (HISTORY_STATE *state) +{ + the_history = state->entries; + history_offset = state->offset; + history_length = state->length; + history_size = state->size; + if (state->flags & HS_STIFLED) + history_stifled = 1; +} + +/* Begin a session in which the history functions might be used. This + initializes interactive variables. */ +void +using_history (void) +{ + history_offset = history_length; +} + +/* Return the number of bytes that the primary history entries are using. + This just adds up the lengths of the_history->lines and the associated + timestamps. */ +int +history_total_bytes (void) +{ + register int i, result; + + for (i = result = 0; the_history && the_history[i]; i++) + result += HISTENT_BYTES (the_history[i]); + + return (result); +} + +/* Returns the magic number which says what history element we are + looking at now. In this implementation, it returns history_offset. */ +int +where_history (void) +{ + return (history_offset); +} + +/* Make the current history item be the one at POS, an absolute index. + Returns zero if POS is out of range, else non-zero. */ +int +history_set_pos (int pos) +{ + if (pos > history_length || pos < 0 || !the_history) + return (0); + history_offset = pos; + return (1); +} + +/* Are we currently at the end of the history list? */ +int +_hs_at_end_of_history (void) +{ + return (the_history == 0 || history_offset == history_length); +} + +/* Return the current history array. The caller has to be careful, since this + is the actual array of data, and could be bashed or made corrupt easily. + The array is terminated with a NULL pointer. */ +HIST_ENTRY ** +history_list (void) +{ + return (the_history); +} + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +HIST_ENTRY * +current_history (void) +{ + return ((history_offset == history_length) || the_history == 0) + ? (HIST_ENTRY *)NULL + : the_history[history_offset]; +} + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry then return + a NULL pointer. */ +HIST_ENTRY * +previous_history (void) +{ + return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL; +} + +/* Move history_offset forward to the next history entry, and return + a pointer to that entry. If there is no next entry then return a + NULL pointer. */ +HIST_ENTRY * +next_history (void) +{ + return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset]; +} + +/* Return the history entry which is logically at OFFSET in the history array. + OFFSET is relative to history_base. */ +HIST_ENTRY * +history_get (int offset) +{ + int local_index; + + local_index = offset - history_base; + return (local_index >= history_length || local_index < 0 || the_history == 0) + ? (HIST_ENTRY *)NULL + : the_history[local_index]; +} + +HIST_ENTRY * +alloc_history_entry (char *string, char *ts) +{ + HIST_ENTRY *temp; + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + + temp->line = string ? savestring (string) : string; + temp->data = (char *)NULL; + temp->timestamp = ts; + + return temp; +} + +time_t +history_get_time (HIST_ENTRY *hist) +{ + char *ts; + time_t t; + + if (hist == 0 || hist->timestamp == 0) + return 0; + ts = hist->timestamp; + if (ts[0] != history_comment_char) + return 0; + errno = 0; + t = (time_t) strtol (ts + 1, (char **)NULL, 10); /* XXX - should use strtol() here */ + if (errno == ERANGE) + return (time_t)0; + return t; +} + +static char * +hist_inittime (void) +{ + time_t t; + char ts[64], *ret; + + t = (time_t) time ((time_t *)0); +#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */ + snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t); +#else + sprintf (ts, "X%lu", (unsigned long) t); +#endif + ret = savestring (ts); + ret[0] = history_comment_char; + + return ret; +} + +/* Place STRING at the end of the history list. The data field + is set to NULL. */ +void +add_history (const char *string) +{ + HIST_ENTRY *temp; + int new_length; + + if (history_stifled && (history_length == history_max_entries)) + { + register int i; + + /* If the history is stifled, and history_length is zero, + and it equals history_max_entries, we don't save items. */ + if (history_length == 0) + return; + + /* If there is something in the slot, then remove it. */ + if (the_history[0]) + (void) free_history_entry (the_history[0]); + + /* Copy the rest of the entries, moving down one slot. Copy includes + trailing NULL. */ + memmove (the_history, the_history + 1, history_length * sizeof (HIST_ENTRY *)); + + new_length = history_length; + history_base++; + } + else + { + if (history_size == 0) + { + if (history_stifled && history_max_entries > 0) + history_size = (history_max_entries > MAX_HISTORY_INITIAL_SIZE) + ? MAX_HISTORY_INITIAL_SIZE + : history_max_entries + 2; + else + history_size = DEFAULT_HISTORY_INITIAL_SIZE; + the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *)); + new_length = 1; + } + else + { + if (history_length == (history_size - 1)) + { + history_size += DEFAULT_HISTORY_GROW_SIZE; + the_history = (HIST_ENTRY **) + xrealloc (the_history, history_size * sizeof (HIST_ENTRY *)); + } + new_length = history_length + 1; + } + } + + temp = alloc_history_entry ((char *)string, hist_inittime ()); + + the_history[new_length] = (HIST_ENTRY *)NULL; + the_history[new_length - 1] = temp; + history_length = new_length; +} + +/* Change the time stamp of the most recent history entry to STRING. */ +void +add_history_time (const char *string) +{ + HIST_ENTRY *hs; + + if (string == 0 || history_length < 1) + return; + hs = the_history[history_length - 1]; + FREE (hs->timestamp); + hs->timestamp = savestring (string); +} + +/* Free HIST and return the data so the calling application can free it + if necessary and desired. */ +histdata_t +free_history_entry (HIST_ENTRY *hist) +{ + histdata_t x; + + if (hist == 0) + return ((histdata_t) 0); + FREE (hist->line); + FREE (hist->timestamp); + x = hist->data; + xfree (hist); + return (x); +} + +HIST_ENTRY * +copy_history_entry (HIST_ENTRY *hist) +{ + HIST_ENTRY *ret; + char *ts; + + if (hist == 0) + return hist; + + ret = alloc_history_entry (hist->line, (char *)NULL); + + ts = hist->timestamp ? savestring (hist->timestamp) : hist->timestamp; + ret->timestamp = ts; + + ret->data = hist->data; + + return ret; +} + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +HIST_ENTRY * +replace_history_entry (int which, const char *line, histdata_t data) +{ + HIST_ENTRY *temp, *old_value; + + if (which < 0 || which >= history_length) + return ((HIST_ENTRY *)NULL); + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + old_value = the_history[which]; + + temp->line = savestring (line); + temp->data = data; + temp->timestamp = old_value->timestamp ? savestring (old_value->timestamp) : 0; + the_history[which] = temp; + + return (old_value); +} + +/* Append LINE to the history line at offset WHICH, adding a newline to the + end of the current line first. This can be used to construct multi-line + history entries while reading lines from the history file. */ +void +_hs_append_history_line (int which, const char *line) +{ + HIST_ENTRY *hent; + size_t newlen, curlen, minlen; + char *newline; + + hent = the_history[which]; + curlen = strlen (hent->line); + minlen = curlen + strlen (line) + 2; /* min space needed */ + if (curlen > 256) /* XXX - for now */ + { + newlen = 512; /* now realloc in powers of 2 */ + /* we recalcluate every time; the operations are cheap */ + while (newlen < minlen) + newlen <<= 1; + } + else + newlen = minlen; + /* Assume that realloc returns the same pointer and doesn't try a new + alloc/copy if the new size is the same as the one last passed. */ + newline = realloc (hent->line, newlen); + if (newline) + { + hent->line = newline; + hent->line[curlen++] = '\n'; + strcpy (hent->line + curlen, line); + } +} + +/* Replace the DATA in the specified history entries, replacing OLD with + NEW. WHICH says which one(s) to replace: WHICH == -1 means to replace + all of the history entries where entry->data == OLD; WHICH == -2 means + to replace the `newest' history entry where entry->data == OLD; and + WHICH >= 0 means to replace that particular history entry's data, as + long as it matches OLD. */ +void +_hs_replace_history_data (int which, histdata_t *old, histdata_t *new) +{ + HIST_ENTRY *entry; + register int i, last; + + if (which < -2 || which >= history_length || history_length == 0 || the_history == 0) + return; + + if (which >= 0) + { + entry = the_history[which]; + if (entry && entry->data == old) + entry->data = new; + return; + } + + last = -1; + for (i = 0; i < history_length; i++) + { + entry = the_history[i]; + if (entry == 0) + continue; + if (entry->data == old) + { + last = i; + if (which == -1) + entry->data = new; + } + } + if (which == -2 && last >= 0) + { + entry = the_history[last]; + entry->data = new; /* XXX - we don't check entry->old */ + } +} + +/* Remove history element WHICH from the history. The removed + element is returned to you so you can free the line, data, + and containing structure. */ +HIST_ENTRY * +remove_history (int which) +{ + HIST_ENTRY *return_value; + register int i; +#if 1 + int nentries; + HIST_ENTRY **start, **end; +#endif + + if (which < 0 || which >= history_length || history_length == 0 || the_history == 0) + return ((HIST_ENTRY *)NULL); + + return_value = the_history[which]; + +#if 1 + /* Copy the rest of the entries, moving down one slot. Copy includes + trailing NULL. */ + nentries = history_length - which; + start = the_history + which; + end = start + 1; + memmove (start, end, nentries * sizeof (HIST_ENTRY *)); +#else + for (i = which; i < history_length; i++) + the_history[i] = the_history[i + 1]; +#endif + + history_length--; + + return (return_value); +} + +HIST_ENTRY ** +remove_history_range (int first, int last) +{ + HIST_ENTRY **return_value; + register int i; + int nentries; + HIST_ENTRY **start, **end; + + if (the_history == 0 || history_length == 0) + return ((HIST_ENTRY **)NULL); + if (first < 0 || first >= history_length || last < 0 || last >= history_length) + return ((HIST_ENTRY **)NULL); + if (first > last) + return (HIST_ENTRY **)NULL; + + nentries = last - first + 1; + return_value = (HIST_ENTRY **)malloc ((nentries + 1) * sizeof (HIST_ENTRY *)); + if (return_value == 0) + return return_value; + + /* Return all the deleted entries in a list */ + for (i = first ; i <= last; i++) + return_value[i - first] = the_history[i]; + return_value[i - first] = (HIST_ENTRY *)NULL; + + /* Copy the rest of the entries, moving down NENTRIES slots. Copy includes + trailing NULL. */ + start = the_history + first; + end = the_history + last + 1; + memmove (start, end, (history_length - last) * sizeof (HIST_ENTRY *)); + + history_length -= nentries; + + return (return_value); +} + +/* Stifle the history list, remembering only MAX number of lines. */ +void +stifle_history (int max) +{ + register int i, j; + + if (max < 0) + max = 0; + + if (history_length > max) + { + /* This loses because we cannot free the data. */ + for (i = 0, j = history_length - max; i < j; i++) + free_history_entry (the_history[i]); + + history_base = i; + for (j = 0, i = history_length - max; j < max; i++, j++) + the_history[j] = the_history[i]; + the_history[j] = (HIST_ENTRY *)NULL; + history_length = j; + } + + history_stifled = 1; + max_input_history = history_max_entries = max; +} + +/* 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 (void) +{ + if (history_stifled) + { + history_stifled = 0; + return (history_max_entries); + } + else + return (-history_max_entries); +} + +int +history_is_stifled (void) +{ + return (history_stifled); +} + +void +clear_history (void) +{ + register int i; + + /* This loses because we cannot free the data. */ + for (i = 0; i < history_length; i++) + { + free_history_entry (the_history[i]); + the_history[i] = (HIST_ENTRY *)NULL; + } + + history_offset = history_length = 0; + history_base = 1; /* reset history base to default */ +} diff --git a/lib/readline/history.h b/lib/readline/history.h new file mode 100644 index 0000000..5208f9a --- /dev/null +++ b/lib/readline/history.h @@ -0,0 +1,291 @@ +/* history.h -- the names of functions that you can call in history. */ + +/* Copyright (C) 1989-2022 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _HISTORY_H_ +#define _HISTORY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <time.h> /* XXX - for history timestamp code */ + +#if defined READLINE_LIBRARY +# include "rlstdc.h" +# include "rltypedefs.h" +#else +# include <readline/rlstdc.h> +# include <readline/rltypedefs.h> +#endif + +#ifdef __STDC__ +typedef void *histdata_t; +#else +typedef char *histdata_t; +#endif + +/* Let's not step on anyone else's define for now, since we don't use this yet. */ +#ifndef HS_HISTORY_VERSION +# define HS_HISTORY_VERSION 0x0802 /* History 8.2 */ +#endif + +/* The structure used to store a history entry. */ +typedef struct _hist_entry { + char *line; + char *timestamp; /* char * rather than time_t for read/write */ + histdata_t data; +} HIST_ENTRY; + +/* Size of the history-library-managed space in history entry HS. */ +#define HISTENT_BYTES(hs) (strlen ((hs)->line) + strlen ((hs)->timestamp)) + +/* A structure used to pass the current state of the history stuff around. */ +typedef struct _hist_state { + HIST_ENTRY **entries; /* Pointer to the entries themselves. */ + int offset; /* The location pointer within this array. */ + int length; /* Number of elements within this array. */ + int size; /* Number of slots allocated to this array. */ + int flags; +} HISTORY_STATE; + +/* Flag values for the `flags' member of HISTORY_STATE. */ +#define HS_STIFLED 0x01 + +/* Initialization and state management. */ + +/* Begin a session in which the history functions might be used. This + just initializes the interactive variables. */ +extern void using_history (void); + +/* Return the current HISTORY_STATE of the history. */ +extern HISTORY_STATE *history_get_history_state (void); + +/* Set the state of the current history array to STATE. */ +extern void history_set_history_state (HISTORY_STATE *); + +/* Manage the history list. */ + +/* Place STRING at the end of the history list. + The associated data field (if any) is set to NULL. */ +extern void add_history (const char *); + +/* Change the timestamp associated with the most recent history entry to + STRING. */ +extern void add_history_time (const char *); + +/* Remove an entry from the history list. WHICH is the magic number that + tells us which element to delete. The elements are numbered from 0. */ +extern HIST_ENTRY *remove_history (int); + +/* Remove a set of entries from the history list: FIRST to LAST, inclusive */ +extern HIST_ENTRY **remove_history_range (int, int); + +/* Allocate a history entry consisting of STRING and TIMESTAMP and return + a pointer to it. */ +extern HIST_ENTRY *alloc_history_entry (char *, char *); + +/* Copy the history entry H, but not the (opaque) data pointer */ +extern HIST_ENTRY *copy_history_entry (HIST_ENTRY *); + +/* Free the history entry H and return any application-specific data + associated with it. */ +extern histdata_t free_history_entry (HIST_ENTRY *); + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +extern HIST_ENTRY *replace_history_entry (int, const char *, histdata_t); + +/* Clear the history list and start over. */ +extern void clear_history (void); + +/* Stifle the history list, remembering only MAX number of entries. */ +extern void stifle_history (int); + +/* 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. */ +extern int unstifle_history (void); + +/* Return 1 if the history is stifled, 0 if it is not. */ +extern int history_is_stifled (void); + +/* Information about the history list. */ + +/* Return a NULL terminated array of HIST_ENTRY which is the current input + history. Element 0 of this list is the beginning of time. If there + is no history, return NULL. */ +extern HIST_ENTRY **history_list (void); + +/* Returns the number which says what history element we are now + looking at. */ +extern int where_history (void); + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +extern HIST_ENTRY *current_history (void); + +/* Return the history entry which is logically at OFFSET in the history + array. OFFSET is relative to history_base. */ +extern HIST_ENTRY *history_get (int); + +/* Return the timestamp associated with the HIST_ENTRY * passed as an + argument */ +extern time_t history_get_time (HIST_ENTRY *); + +/* Return the number of bytes that the primary history entries are using. + This just adds up the lengths of the_history->lines. */ +extern int history_total_bytes (void); + +/* Moving around the history list. */ + +/* Set the position in the history list to POS. */ +extern int history_set_pos (int); + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry, return + a NULL pointer. */ +extern HIST_ENTRY *previous_history (void); + +/* Move history_offset forward to the next item in the input_history, + and return the a pointer to that entry. If there is no next entry, + return a NULL pointer. */ +extern HIST_ENTRY *next_history (void); + +/* Searching the history list. */ + +/* Search the history for STRING, starting at history_offset. + If DIRECTION < 0, then the search is through previous entries, + else through subsequent. If the string is found, then + current_history () is the history entry, and the value of this function + is the offset in the line of that history entry that the string was + found in. Otherwise, nothing is changed, and a -1 is returned. */ +extern int history_search (const char *, int); + +/* Search the history for STRING, starting at history_offset. + The search is anchored: matching lines must begin with string. + DIRECTION is as in history_search(). */ +extern int history_search_prefix (const char *, int); + +/* Search for STRING in the history list, starting at POS, an + absolute index into the list. DIR, if negative, says to search + backwards from POS, else forwards. + Returns the absolute index of the history element where STRING + was found, or -1 otherwise. */ +extern int history_search_pos (const char *, int, int); + +/* Managing the history file. */ + +/* Add the contents of FILENAME to the history list, a line at a time. + If FILENAME is NULL, then read from ~/.history. Returns 0 if + successful, or errno if not. */ +extern int read_history (const char *); + +/* Read a range of lines from FILENAME, adding them to the history list. + Start reading at the FROM'th line and end at the TO'th. If FROM + is zero, start at the beginning. If TO is less than FROM, read + until the end of the file. If FILENAME is NULL, then read from + ~/.history. Returns 0 if successful, or errno if not. */ +extern int read_history_range (const char *, int, int); + +/* Write the current history to FILENAME. If FILENAME is NULL, + then write the history list to ~/.history. Values returned + are as in read_history (). */ +extern int write_history (const char *); + +/* Append NELEMENT entries to FILENAME. The entries appended are from + the end of the list minus NELEMENTs up to the end of the list. */ +extern int append_history (int, const char *); + +/* Truncate the history file, leaving only the last NLINES lines. */ +extern int history_truncate_file (const char *, int); + +/* History expansion. */ + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + -1) If there was an error in expansion. + 2) If the returned line should just be printed. + + If an error occurred in expansion, then OUTPUT contains a descriptive + error message. */ +extern int history_expand (char *, char **); + +/* Extract a string segment consisting of the FIRST through LAST + arguments present in STRING. Arguments are broken up as in + the shell. */ +extern char *history_arg_extract (int, int, const char *); + +/* Return the text of the history event beginning at the current + offset into STRING. Pass STRING with *INDEX equal to the + history_expansion_char that begins this specification. + DELIMITING_QUOTE is a character that is allowed to end the string + specification for what to search for in addition to the normal + characters `:', ` ', `\t', `\n', and sometimes `?'. */ +extern char *get_history_event (const char *, int *, int); + +/* Return an array of tokens, much as the shell might. The tokens are + parsed out of STRING. */ +extern char **history_tokenize (const char *); + +/* Exported history variables. */ +extern int history_base; +extern int history_length; +extern int history_max_entries; +extern int history_offset; + +extern int history_lines_read_from_file; +extern int history_lines_written_to_file; + +extern char history_expansion_char; +extern char history_subst_char; +extern char *history_word_delimiters; +extern char history_comment_char; +extern char *history_no_expand_chars; +extern char *history_search_delimiter_chars; + +extern int history_quotes_inhibit_expansion; +extern int history_quoting_state; + +extern int history_write_timestamps; + +/* These two are undocumented; the second is reserved for future use */ +extern int history_multiline_entries; +extern int history_file_version; + +/* Backwards compatibility */ +extern int max_input_history; + +/* If set, this function is called to decide whether or not a particular + history expansion should be treated as a special case for the calling + application and not expanded. */ +extern rl_linebuf_func_t *history_inhibit_expansion_function; + +#ifdef __cplusplus +} +#endif + +#endif /* !_HISTORY_H_ */ diff --git a/lib/readline/histsearch.c b/lib/readline/histsearch.c new file mode 100644 index 0000000..b62c06b --- /dev/null +++ b/lib/readline/histsearch.c @@ -0,0 +1,287 @@ +/* histsearch.c -- searching the history list. */ + +/* Copyright (C) 1989, 1992-2009,2017,2021 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History 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 3 of the License, or + (at your option) any later version. + + History 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 History. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#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_FNMATCH) +# include <fnmatch.h> +#endif + +#include "history.h" +#include "histlib.h" +#include "xmalloc.h" + +/* The list of alternate characters that can delimit a history search + string. */ +char *history_search_delimiter_chars = (char *)NULL; + +static int history_search_internal (const char *, int, int); + +/* Search the history for STRING, starting at history_offset. + If DIRECTION < 0, then the search is through previous entries, else + through subsequent. If ANCHORED is non-zero, the string must + appear at the beginning of a history line, otherwise, the string + may appear anywhere in the line. If the string is found, then + current_history () is the history entry, and the value of this + function is the offset in the line of that history entry that the + string was found in. Otherwise, nothing is changed, and a -1 is + returned. */ + +static int +history_search_internal (const char *string, int direction, int flags) +{ + register int i, reverse; + register char *line; + register int line_index; + int string_len, anchored, patsearch; + HIST_ENTRY **the_history; /* local */ + + i = history_offset; + reverse = (direction < 0); + anchored = (flags & ANCHORED_SEARCH); +#if defined (HAVE_FNMATCH) + patsearch = (flags & PATTERN_SEARCH); +#else + patsearch = 0; +#endif + + /* Take care of trivial cases first. */ + if (string == 0 || *string == '\0') + return (-1); + + if (!history_length || ((i >= history_length) && !reverse)) + return (-1); + + if (reverse && (i >= history_length)) + i = history_length - 1; + +#define NEXT_LINE() do { if (reverse) i--; else i++; } while (0) + + the_history = history_list (); + string_len = strlen (string); + while (1) + { + /* Search each line in the history list for STRING. */ + + /* At limit for direction? */ + if ((reverse && i < 0) || (!reverse && i == history_length)) + return (-1); + + line = the_history[i]->line; + line_index = strlen (line); + + /* If STRING is longer than line, no match. */ + if (patsearch == 0 && (string_len > line_index)) + { + NEXT_LINE (); + continue; + } + + /* Handle anchored searches first. */ + if (anchored == ANCHORED_SEARCH) + { +#if defined (HAVE_FNMATCH) + if (patsearch) + { + if (fnmatch (string, line, 0) == 0) + { + history_offset = i; + return (0); + } + } + else +#endif + if (STREQN (string, line, string_len)) + { + history_offset = i; + return (0); + } + + NEXT_LINE (); + continue; + } + + /* Do substring search. */ + if (reverse) + { + line_index -= (patsearch == 0) ? string_len : 1; + + while (line_index >= 0) + { +#if defined (HAVE_FNMATCH) + if (patsearch) + { + if (fnmatch (string, line + line_index, 0) == 0) + { + history_offset = i; + return (line_index); + } + } + else +#endif + if (STREQN (string, line + line_index, string_len)) + { + history_offset = i; + return (line_index); + } + line_index--; + } + } + else + { + register int limit; + + limit = line_index - string_len + 1; + line_index = 0; + + while (line_index < limit) + { +#if defined (HAVE_FNMATCH) + if (patsearch) + { + if (fnmatch (string, line + line_index, 0) == 0) + { + history_offset = i; + return (line_index); + } + } + else +#endif + if (STREQN (string, line + line_index, string_len)) + { + history_offset = i; + return (line_index); + } + line_index++; + } + } + NEXT_LINE (); + } +} + +int +_hs_history_patsearch (const char *string, int direction, int flags) +{ + char *pat; + size_t len, start; + int ret, unescaped_backslash; + +#if defined (HAVE_FNMATCH) + /* Assume that the string passed does not have a leading `^' and any + anchored search request is captured in FLAGS */ + len = strlen (string); + ret = len - 1; + /* fnmatch is required to reject a pattern that ends with an unescaped + backslash */ + if (unescaped_backslash = (string[ret] == '\\')) + { + while (ret > 0 && string[--ret] == '\\') + unescaped_backslash = 1 - unescaped_backslash; + } + if (unescaped_backslash) + return -1; + pat = (char *)xmalloc (len + 3); + /* If the search string is not anchored, we'll be calling fnmatch (assuming + we have it). Prefix a `*' to the front of the search string so we search + anywhere in the line. */ + if ((flags & ANCHORED_SEARCH) == 0 && string[0] != '*') + { + pat[0] = '*'; + start = 1; + len++; + } + else + { + start = 0; + } + + /* Attempt to reduce the number of searches by tacking a `*' onto the end + of a pattern that doesn't have one. Assume a pattern that ends in a + backslash contains an even number of trailing backslashes; we check + above */ + strcpy (pat + start, string); + if (pat[len - 1] != '*') + { + pat[len] = '*'; /* XXX */ + pat[len+1] = '\0'; + } +#else + pat = string; +#endif + + ret = history_search_internal (pat, direction, flags|PATTERN_SEARCH); + + if (pat != string) + xfree (pat); + return ret; +} + +/* Do a non-anchored search for STRING through the history in DIRECTION. */ +int +history_search (const char *string, int direction) +{ + return (history_search_internal (string, direction, NON_ANCHORED_SEARCH)); +} + +/* Do an anchored search for string through the history in DIRECTION. */ +int +history_search_prefix (const char *string, int direction) +{ + return (history_search_internal (string, direction, ANCHORED_SEARCH)); +} + +/* Search for STRING in the history list. DIR is < 0 for searching + backwards. POS is an absolute index into the history list at + which point to begin searching. */ +int +history_search_pos (const char *string, int dir, int pos) +{ + int ret, old; + + old = where_history (); + history_set_pos (pos); + if (history_search (string, dir) == -1) + { + history_set_pos (old); + return (-1); + } + ret = where_history (); + history_set_pos (old); + return ret; +} diff --git a/lib/readline/input.c b/lib/readline/input.c new file mode 100644 index 0000000..6f038d4 --- /dev/null +++ b/lib/readline/input.c @@ -0,0 +1,1000 @@ +/* input.c -- character input functions for readline. */ + +/* Copyright (C) 1994-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# define _XOPEN_SOURCE_EXTENDED 1 +# define _TANDEM_SOURCE 1 +# include <floss.h> +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <signal.h> + +#include "posixselect.h" +#include "posixtime.h" + +#if defined (FIONREAD_IN_SYS_IOCTL) +# include <sys/ioctl.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +/* Some standard library routines. */ +#include "readline.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +/* What kind of non-blocking I/O do we have? */ +#if !defined (O_NDELAY) && defined (O_NONBLOCK) +# define O_NDELAY O_NONBLOCK /* Posix style */ +#endif + +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) +extern sigset_t _rl_orig_sigset; +#endif + +/* Non-null means it is a pointer to a function to run while waiting for + character input. */ +rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL; + +/* A function to call if a read(2) is interrupted by a signal. */ +rl_hook_func_t *rl_signal_event_hook = (rl_hook_func_t *)NULL; + +/* A function to call when readline times out after a time is specified. */ +rl_hook_func_t *rl_timeout_event_hook = (rl_hook_func_t *)NULL; + +/* A function to replace _rl_input_available for applications using the + callback interface. */ +rl_hook_func_t *rl_input_available_hook = (rl_hook_func_t *)NULL; + +rl_getc_func_t *rl_getc_function = rl_getc; + +static int _keyboard_input_timeout = 100000; /* 0.1 seconds; it's in usec */ + +static int ibuffer_space (void); +static int rl_get_char (int *); +static int rl_gather_tyi (void); + +/* Windows isatty returns true for every character device, including the null + device, so we need to perform additional checks. */ +#if defined (_WIN32) && !defined (__CYGWIN__) +#include <io.h> +#include <conio.h> +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +int +win32_isatty (int fd) +{ + if (_isatty(fd)) + { + HANDLE h; + DWORD ignored; + + if ((h = (HANDLE) _get_osfhandle (fd)) == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return 0; + } + if (GetConsoleMode (h, &ignored) != 0) + return 1; + } + errno = ENOTTY; + return 0; +} + +#define isatty(x) win32_isatty(x) +#endif + +/* Readline timeouts */ + +/* I don't know how to set a timeout for _getch() in MinGW32, so we use + SIGALRM. */ +#if (defined (HAVE_PSELECT) || defined (HAVE_SELECT)) && !defined (__MINGW32__) +# define RL_TIMEOUT_USE_SELECT +#else +# define RL_TIMEOUT_USE_SIGALRM +#endif + +int rl_set_timeout (unsigned int, unsigned int); +int rl_timeout_remaining (unsigned int *, unsigned int *); + +int _rl_timeout_init (void); +int _rl_timeout_sigalrm_handler (void); +int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *); + +static void _rl_timeout_handle (void); +#if defined (RL_TIMEOUT_USE_SIGALRM) +static int set_alarm (unsigned int *, unsigned int *); +static void reset_alarm (void); +#endif + +/* We implement timeouts as a future time using a supplied interval + (timeout_duration) from when the timeout is set (timeout_point). + That allows us to easily determine whether the timeout has occurred + and compute the time remaining until it does. */ +static struct timeval timeout_point; +static struct timeval timeout_duration; + +/* **************************************************************** */ +/* */ +/* Character Input Buffering */ +/* */ +/* **************************************************************** */ + +static int pop_index, push_index; +static unsigned char ibuffer[512]; +static int ibuffer_len = sizeof (ibuffer) - 1; + +#define any_typein (push_index != pop_index) + +int +_rl_any_typein (void) +{ + return any_typein; +} + +int +_rl_pushed_input_available (void) +{ + return (push_index != pop_index); +} + +/* Return the amount of space available in the buffer for stuffing + characters. */ +static int +ibuffer_space (void) +{ + if (pop_index > push_index) + return (pop_index - push_index - 1); + else + return (ibuffer_len - (push_index - pop_index)); +} + +/* Get a key from the buffer of characters to be read. + Return the key in KEY. + Result is non-zero if there was a key, or 0 if there wasn't. */ +static int +rl_get_char (int *key) +{ + if (push_index == pop_index) + return (0); + + *key = ibuffer[pop_index++]; +#if 0 + if (pop_index >= ibuffer_len) +#else + if (pop_index > ibuffer_len) +#endif + pop_index = 0; + + return (1); +} + +/* Stuff KEY into the *front* of the input buffer. + Returns non-zero if successful, zero if there is + no space left in the buffer. */ +int +_rl_unget_char (int key) +{ + if (ibuffer_space ()) + { + pop_index--; + if (pop_index < 0) + pop_index = ibuffer_len; + ibuffer[pop_index] = key; + return (1); + } + return (0); +} + +/* 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 (void) +{ + int tty; + register int tem, result; + int chars_avail, k; + char input; +#if defined(HAVE_SELECT) + fd_set readfds, exceptfds; + struct timeval timeout; +#endif + + chars_avail = 0; + input = 0; + tty = fileno (rl_instream); + +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + FD_ZERO (&readfds); + FD_ZERO (&exceptfds); + FD_SET (tty, &readfds); + FD_SET (tty, &exceptfds); + USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout); +#if defined (RL_TIMEOUT_USE_SELECT) + result = _rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL); +#else + result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout); +#endif + if (result <= 0) + return 0; /* Nothing to read. */ +#endif + + result = -1; + errno = 0; +#if defined (FIONREAD) + result = ioctl (tty, FIONREAD, &chars_avail); + if (result == -1 && errno == EIO) + return -1; + if (result == -1) + chars_avail = 0; +#endif + +#if defined (O_NDELAY) + if (result == -1) + { + tem = fcntl (tty, F_GETFL, 0); + + fcntl (tty, F_SETFL, (tem | O_NDELAY)); + chars_avail = read (tty, &input, 1); + + fcntl (tty, F_SETFL, tem); + if (chars_avail == -1 && errno == EAGAIN) + return 0; + if (chars_avail == -1 && errno == EIO) + return -1; + if (chars_avail == 0) /* EOF */ + { + rl_stuff_char (EOF); + return (0); + } + } +#endif /* O_NDELAY */ + +#if defined (__MINGW32__) + /* Use getch/_kbhit to check for available console input, in the same way + that we read it normally. */ + chars_avail = isatty (tty) ? _kbhit () : 0; + result = 0; +#endif + + /* If there's nothing available, don't waste time trying to read + something. */ + if (chars_avail <= 0) + return 0; + + tem = ibuffer_space (); + + if (chars_avail > tem) + chars_avail = tem; + + /* One cannot read all of the available input. I can only read a single + character at a time, or else programs which require input can be + thwarted. If the buffer is larger than one character, I lose. + Damn! */ + if (tem < ibuffer_len) + chars_avail = 0; + + if (result != -1) + { + while (chars_avail--) + { + RL_CHECK_SIGNALS (); + k = (*rl_getc_function) (rl_instream); + if (rl_stuff_char (k) == 0) + break; /* some problem; no more room */ + if (k == NEWLINE || k == RETURN) + break; + } + } + else + { + if (chars_avail) + rl_stuff_char (input); + } + + return 1; +} + +int +rl_set_keyboard_input_timeout (int u) +{ + int o; + + o = _keyboard_input_timeout; + if (u >= 0) + _keyboard_input_timeout = u; + return (o); +} + +/* Is there input available to be read on the readline input file + descriptor? Only works if the system has select(2) or FIONREAD. + Uses the value of _keyboard_input_timeout as the timeout; if another + readline function wants to specify a timeout and not leave it up to + the user, it should use _rl_input_queued(timeout_value_in_microseconds) + instead. */ +int +_rl_input_available (void) +{ +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + fd_set readfds, exceptfds; + struct timeval timeout; +#endif +#if !defined (HAVE_SELECT) && defined (FIONREAD) + int chars_avail; +#endif + int tty; + + if (rl_input_available_hook) + return (*rl_input_available_hook) (); + + tty = fileno (rl_instream); + +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + FD_ZERO (&readfds); + FD_ZERO (&exceptfds); + FD_SET (tty, &readfds); + FD_SET (tty, &exceptfds); + USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout); +# if defined (RL_TIMEOUT_USE_SELECT) + return (_rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL) > 0); +# else + return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0); +# endif +#else + +#if defined (FIONREAD) + if (ioctl (tty, FIONREAD, &chars_avail) == 0) + return (chars_avail); +#endif + +#endif + +#if defined (__MINGW32__) + if (isatty (tty)) + return (_kbhit ()); +#endif + + return 0; +} + +int +_rl_nchars_available () +{ + int chars_avail, fd, result; + + chars_avail = 0; + +#if defined (FIONREAD) + fd = fileno (rl_instream); + errno = 0; + result = ioctl (fd, FIONREAD, &chars_avail); + if (result == -1 && errno == EIO) + return -1; +#endif + + return chars_avail; +} + +int +_rl_input_queued (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 (int c) +{ + int key, t, i; + char *string; + + i = key = 0; + string = (char *)xmalloc (ibuffer_len + 1); + string[i++] = (char) c; + + while ((t = rl_get_char (&key)) && + _rl_keymap[key].type == ISFUNC && + _rl_keymap[key].function == rl_insert) + string[i++] = key; + + if (t) + _rl_unget_char (key); + + string[i] = '\0'; + rl_insert_text (string); + xfree (string); +} + +/* Add KEY to the buffer of characters to be read. Returns 1 if the + character was stuffed correctly; 0 otherwise. */ +int +rl_stuff_char (int key) +{ + if (ibuffer_space () == 0) + return 0; + + if (key == EOF) + { + key = NEWLINE; + rl_pending_input = EOF; + RL_SETSTATE (RL_STATE_INPUTPENDING); + } + ibuffer[push_index++] = key; +#if 0 + if (push_index >= ibuffer_len) +#else + if (push_index > ibuffer_len) +#endif + push_index = 0; + + return 1; +} + +/* Make C be the next command to be executed. */ +int +rl_execute_next (int c) +{ + rl_pending_input = c; + RL_SETSTATE (RL_STATE_INPUTPENDING); + return 0; +} + +/* Clear any pending input pushed with rl_execute_next() */ +int +rl_clear_pending_input (void) +{ + rl_pending_input = 0; + RL_UNSETSTATE (RL_STATE_INPUTPENDING); + return 0; +} + +/* **************************************************************** */ +/* */ +/* Timeout utility */ +/* */ +/* **************************************************************** */ + +#if defined (RL_TIMEOUT_USE_SIGALRM) +# if defined (HAVE_SETITIMER) + +static int +set_alarm (unsigned int *secs, unsigned int *usecs) +{ + struct itimerval it; + + timerclear (&it.it_interval); + timerset (&it.it_value, *secs, *usecs); + return setitimer (ITIMER_REAL, &it, NULL); +} + +static void +reset_alarm () +{ + struct itimerval it; + + timerclear (&it.it_interval); + timerclear (&it.it_value); + setitimer (ITIMER_REAL, &it, NULL); +} +# else +static int +set_alarm (unsigned int *secs, unsigned int *usecs) +{ + if (*secs == 0 || *usecs >= USEC_PER_SEC / 2) + (*secs)++; + *usecs = 0; + + return alarm (*secs); +} +static void +reset_alarm () +{ + alarm (0); +} +# endif +#endif + +/* Set a timeout which will be used for the next call of `readline + ()'. When (0, 0) are specified the timeout is cleared. */ +int +rl_set_timeout (unsigned int secs, unsigned int usecs) +{ + timeout_duration.tv_sec = secs + usecs / USEC_PER_SEC; + timeout_duration.tv_usec = usecs % USEC_PER_SEC; + + return 0; +} + +/* Start measuring the time. Returns 0 on success. Returns -1 on + error. */ +int +_rl_timeout_init (void) +{ + unsigned int secs, usecs; + + /* Clear the timeout state of the previous edit */ + RL_UNSETSTATE(RL_STATE_TIMEOUT); + timerclear (&timeout_point); + + /* Return 0 when timeout is unset. */ + if (timerisunset (&timeout_duration)) + return 0; + + /* Return -1 on gettimeofday error. */ + if (gettimeofday(&timeout_point, 0) != 0) + { + timerclear (&timeout_point); + return -1; + } + + secs = timeout_duration.tv_sec; + usecs = timeout_duration.tv_usec; + +#if defined (RL_TIMEOUT_USE_SIGALRM) + /* If select(2)/pselect(2) is unavailable, use SIGALRM. */ + if (set_alarm (&secs, &usecs) < 0) + return -1; +#endif + + timeout_point.tv_sec += secs; + timeout_point.tv_usec += usecs; + if (timeout_point.tv_usec >= USEC_PER_SEC) + { + timeout_point.tv_sec++; + timeout_point.tv_usec -= USEC_PER_SEC; + } + + return 0; +} + +/* Get the remaining time until the scheduled timeout. Returns -1 on + error or no timeout set with secs and usecs unchanged. Returns 0 + on an expired timeout with secs and usecs unchanged. Returns 1 + when the timeout has not yet expired. The remaining time is stored + in secs and usecs. When NULL is specified to either of the + arguments, just the expiration is tested. */ +int +rl_timeout_remaining (unsigned int *secs, unsigned int *usecs) +{ + struct timeval current_time; + + /* Return -1 when timeout is unset. */ + if (timerisunset (&timeout_point)) + { + errno = 0; + return -1; + } + + /* Return -1 on error. errno is set by gettimeofday. */ + if (gettimeofday(¤t_time, 0) != 0) + return -1; + + /* Return 0 when timeout has already expired. */ + /* could use timercmp (&timeout_point, ¤t_time, <) here */ + if (current_time.tv_sec > timeout_point.tv_sec || + (current_time.tv_sec == timeout_point.tv_sec && + current_time.tv_usec >= timeout_point.tv_usec)) + return 0; + + if (secs && usecs) + { + *secs = timeout_point.tv_sec - current_time.tv_sec; + *usecs = timeout_point.tv_usec - current_time.tv_usec; + if (timeout_point.tv_usec < current_time.tv_usec) + { + (*secs)--; + *usecs += USEC_PER_SEC; + } + } + + return 1; +} + +/* This should only be called if RL_TIMEOUT_USE_SELECT is defined. */ + +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) +int +_rl_timeout_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout, const sigset_t *sigmask) +{ + int result; +#if defined (HAVE_PSELECT) + struct timespec ts; +#else + sigset_t origmask; + struct timeval tv; +#endif + int tmout_status; + struct timeval tmout; + unsigned int sec, usec; + + /* When the remaining time for rl_timeout is shorter than the + keyboard input timeout, replace `timeout' with the remaining time + for `rl_timeout' and set `tmout_status = 1'. */ + tmout_status = rl_timeout_remaining (&sec, &usec); + tmout.tv_sec = sec; + tmout.tv_usec = usec; + + if (tmout_status == 0) + _rl_timeout_handle (); + else if (tmout_status == 1) + { + if (timeout == NULL || timercmp (&tmout, timeout, <)) + timeout = &tmout; + else + tmout_status = -1; + } + +#if defined (HAVE_PSELECT) + if (timeout) + { + TIMEVAL_TO_TIMESPEC (timeout, &ts); + result = pselect (nfds, readfds, writefds, exceptfds, &ts, sigmask); + } + else + result = pselect (nfds, readfds, writefds, exceptfds, NULL, sigmask); +#else + if (sigmask) + sigprocmask (SIG_SETMASK, sigmask, &origmask); + + if (timeout) + { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_usec; + result = select (nfds, readfds, writefds, exceptfds, &tv); + } + else + result = select (nfds, readfds, writefds, exceptfds, NULL); + + if (sigmask) + sigprocmask (SIG_SETMASK, &origmask, NULL); +#endif + + if (tmout_status == 1 && result == 0) + _rl_timeout_handle (); + + return result; +} +#endif + +static void +_rl_timeout_handle () +{ + if (rl_timeout_event_hook) + (*rl_timeout_event_hook) (); + + RL_SETSTATE(RL_STATE_TIMEOUT); + _rl_abort_internal (); +} + +int +_rl_timeout_handle_sigalrm () +{ +#if defined (RL_TIMEOUT_USE_SIGALRM) + if (timerisunset (&timeout_point)) + return -1; + + /* Reset `timeout_point' to the current time to ensure that later + calls of `rl_timeout_pending ()' return 0 (timeout expired). */ + if (gettimeofday(&timeout_point, 0) != 0) + timerclear (&timeout_point); + + reset_alarm (); + + _rl_timeout_handle (); +#endif + return -1; +} +/* **************************************************************** */ +/* */ +/* Character Input */ +/* */ +/* **************************************************************** */ + +/* Read a key, including pending input. */ +int +rl_read_key (void) +{ + int c, r; + + if (rl_pending_input) + { + c = rl_pending_input; /* XXX - cast to unsigned char if > 0? */ + rl_clear_pending_input (); + } + else + { + /* If input is coming from a macro, then use that. */ + if (c = _rl_next_macro_key ()) + return ((unsigned char)c); + + /* If the user has an event function, then call it periodically. */ + if (rl_event_hook) + { + while (rl_event_hook) + { + if (rl_get_char (&c) != 0) + break; + + if ((r = rl_gather_tyi ()) < 0) /* XXX - EIO */ + { + rl_done = 1; + RL_SETSTATE (RL_STATE_DONE); + return (errno == EIO ? (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF) : '\n'); + } + else if (r > 0) /* read something */ + continue; + + RL_CHECK_SIGNALS (); + if (rl_done) /* XXX - experimental */ + return ('\n'); + (*rl_event_hook) (); + } + } + else + { + if (rl_get_char (&c) == 0) + c = (*rl_getc_function) (rl_instream); +/* fprintf(stderr, "rl_read_key: calling RL_CHECK_SIGNALS: _rl_caught_signal = %d\r\n", _rl_caught_signal); */ + RL_CHECK_SIGNALS (); + } + } + + return (c); +} + +int +rl_getc (FILE *stream) +{ + int result; + unsigned char c; + int fd; +#if defined (HAVE_PSELECT) + sigset_t empty_set; + fd_set readfds; +#endif + + fd = fileno (stream); + while (1) + { + RL_CHECK_SIGNALS (); + + /* We know at this point that _rl_caught_signal == 0 */ + +#if defined (__MINGW32__) + if (isatty (fd) + return (_getch ()); /* "There is no error return." */ +#endif + result = 0; +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + /* At this point, if we have pselect, we're using select/pselect for the + timeouts. We handled MinGW above. */ + FD_ZERO (&readfds); + FD_SET (fd, &readfds); +# if defined (HANDLE_SIGNALS) + result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &_rl_orig_sigset); +# else + sigemptyset (&empty_set); + sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &empty_set); + result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &empty_set); +# endif /* HANDLE_SIGNALS */ + if (result == 0) + _rl_timeout_handle (); /* check the timeout */ +#endif + if (result >= 0) + result = read (fd, &c, sizeof (unsigned char)); + + if (result == sizeof (unsigned char)) + return (c); + + /* If zero characters are returned, then the file that we are + reading from is empty! Return EOF in that case. */ + if (result == 0) + return (EOF); + +#if defined (__BEOS__) + if (errno == EINTR) + continue; +#endif + +#if defined (EWOULDBLOCK) +# define X_EWOULDBLOCK EWOULDBLOCK +#else +# define X_EWOULDBLOCK -99 +#endif + +#if defined (EAGAIN) +# define X_EAGAIN EAGAIN +#else +# define X_EAGAIN -99 +#endif + + if (errno == X_EWOULDBLOCK || errno == X_EAGAIN) + { + if (sh_unset_nodelay_mode (fd) < 0) + return (EOF); + continue; + } + +#undef X_EWOULDBLOCK +#undef X_EAGAIN + +/* fprintf(stderr, "rl_getc: result = %d errno = %d\n", result, errno); */ + +handle_error: + /* If the error that we received was EINTR, then try again, + this is simply an interrupted system call to read (). We allow + the read to be interrupted if we caught SIGHUP, SIGTERM, or any + of the other signals readline treats specially. If the + application sets an event hook, call it for other signals. + Otherwise (not EINTR), some error occurred, also signifying EOF. */ + if (errno != EINTR) + return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); + /* fatal signals of interest */ +#if defined (SIGHUP) + else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM) +#else + else if (_rl_caught_signal == SIGTERM) +#endif + return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); + /* keyboard-generated signals of interest */ +#if defined (SIGQUIT) + else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT) +#else + else if (_rl_caught_signal == SIGINT) +#endif + RL_CHECK_SIGNALS (); +#if defined (SIGTSTP) + else if (_rl_caught_signal == SIGTSTP) + RL_CHECK_SIGNALS (); +#endif + /* non-keyboard-generated signals of interest */ +#if defined (SIGWINCH) + else if (_rl_caught_signal == SIGWINCH) + RL_CHECK_SIGNALS (); +#endif /* SIGWINCH */ +#if defined (SIGALRM) + else if (_rl_caught_signal == SIGALRM +# if defined (SIGVTALRM) + || _rl_caught_signal == SIGVTALRM +# endif + ) + RL_CHECK_SIGNALS (); +#endif /* SIGALRM */ + + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); + } +} + +#if defined (HANDLE_MULTIBYTE) +/* read multibyte char */ +int +_rl_read_mbchar (char *mbchar, int size) +{ + int mb_len, c; + 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)); + + mb_len = 0; + while (mb_len < size) + { + c = (mb_len == 0) ? _rl_bracketed_read_key () : rl_read_key (); + + if (c < 0) + break; + + mbchar[mb_len++] = c; + + 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 == 0) + { + mbchar[0] = '\0'; /* null wide character */ + mb_len = 1; + break; + } + 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 MLEN. 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 (int first, char *mb, int mlen) +{ + int i, c, n; + mbstate_t ps; + + c = first; + memset (mb, 0, mlen); + for (i = 0; c >= 0 && i < mlen; i++) + { + mb[i] = (char)c; + memset (&ps, 0, sizeof (mbstate_t)); + n = _rl_get_char_len (mb, &ps); + if (n == -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 */ diff --git a/lib/readline/isearch.c b/lib/readline/isearch.c new file mode 100644 index 0000000..c2d4d23 --- /dev/null +++ b/lib/readline/isearch.c @@ -0,0 +1,893 @@ +/* isearch.c - incremental searching */ + +/* **************************************************************** */ +/* */ +/* I-Search and Searching */ +/* */ +/* **************************************************************** */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif + +#include "rldefs.h" +#include "rlmbutil.h" + +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +/* Variables exported to other files in the readline library. */ +char *_rl_isearch_terminators = (char *)NULL; + +_rl_search_cxt *_rl_iscxt = 0; + +static int rl_search_history (int, int); + +static _rl_search_cxt *_rl_isearch_init (int); +static void _rl_isearch_fini (_rl_search_cxt *); + +/* Last line found by the current incremental search, so we don't `find' + identical lines many times in a row. Now part of isearch context. */ +/* static char *prev_line_found; */ + +/* Last search string and its length. */ +static char *last_isearch_string; +static int last_isearch_string_len; + +static char * const default_isearch_terminators = "\033\012"; + +_rl_search_cxt * +_rl_scxt_alloc (int type, int flags) +{ + _rl_search_cxt *cxt; + + cxt = (_rl_search_cxt *)xmalloc (sizeof (_rl_search_cxt)); + + cxt->type = type; + cxt->sflags = flags; + + cxt->search_string = 0; + cxt->search_string_size = cxt->search_string_index = 0; + + cxt->lines = 0; + cxt->allocated_line = 0; + cxt->hlen = cxt->hindex = 0; + + cxt->save_point = rl_point; + cxt->save_mark = rl_mark; + cxt->save_line = where_history (); + cxt->last_found_line = cxt->save_line; + cxt->prev_line_found = 0; + + cxt->save_undo_list = 0; + + cxt->keymap = _rl_keymap; + cxt->okeymap = _rl_keymap; + + cxt->history_pos = 0; + cxt->direction = 0; + + cxt->prevc = cxt->lastc = 0; + + cxt->sline = 0; + cxt->sline_len = cxt->sline_index = 0; + + cxt->search_terminators = 0; + + return cxt; +} + +void +_rl_scxt_dispose (_rl_search_cxt *cxt, int flags) +{ + FREE (cxt->search_string); + FREE (cxt->allocated_line); + FREE (cxt->lines); + + xfree (cxt); +} + +/* Search backwards through the history looking for a string which is typed + interactively. Start with the current line. */ +int +rl_reverse_search_history (int sign, int key) +{ + return (rl_search_history (-sign, key)); +} + +/* Search forwards through the history looking for a string which is typed + interactively. Start with the current line. */ +int +rl_forward_search_history (int sign, int key) +{ + return (rl_search_history (sign, key)); +} + +/* Display the current state of the search in the echo-area. + SEARCH_STRING contains the string that is being searched for, + DIRECTION is zero for forward, or non-zero for reverse, + WHERE is the history list number of the current line. If it is + -1, then this line is the starting one. */ +static void +rl_display_search (char *search_string, int flags, int where) +{ + char *message; + int msglen, searchlen; + + searchlen = (search_string && *search_string) ? strlen (search_string) : 0; + + message = (char *)xmalloc (searchlen + 64); + msglen = 0; + +#if defined (NOTDEF) + if (where != -1) + { + sprintf (message, "[%d]", where + history_base); + msglen = strlen (message); + } +#endif /* NOTDEF */ + + message[msglen++] = '('; + + if (flags & SF_FAILED) + { + strcpy (message + msglen, "failed "); + msglen += 7; + } + + if (flags & SF_REVERSE) + { + strcpy (message + msglen, "reverse-"); + msglen += 8; + } + + strcpy (message + msglen, "i-search)`"); + msglen += 10; + + if (search_string && *search_string) + { + strcpy (message + msglen, search_string); + msglen += searchlen; + } + else + _rl_optimize_redisplay (); + + strcpy (message + msglen, "': "); + + rl_message ("%s", message); + xfree (message); +#if 0 + /* rl_message calls this */ + (*rl_redisplay_function) (); +#endif +} + +static _rl_search_cxt * +_rl_isearch_init (int direction) +{ + _rl_search_cxt *cxt; + register int i; + HIST_ENTRY **hlist; + + cxt = _rl_scxt_alloc (RL_SEARCH_ISEARCH, 0); + if (direction < 0) + cxt->sflags |= SF_REVERSE; + + cxt->search_terminators = _rl_isearch_terminators ? _rl_isearch_terminators + : default_isearch_terminators; + + /* Create an array of pointers to the lines that we want to search. */ + hlist = history_list (); + rl_maybe_replace_line (); + i = 0; + if (hlist) + for (i = 0; hlist[i]; i++); + + /* Allocate space for this many lines, +1 for the current input line, + and remember those lines. */ + cxt->lines = (char **)xmalloc ((1 + (cxt->hlen = i)) * sizeof (char *)); + for (i = 0; i < cxt->hlen; i++) + cxt->lines[i] = hlist[i]->line; + + if (_rl_saved_line_for_history) + cxt->lines[i] = _rl_saved_line_for_history->line; + else + { + /* Keep track of this so we can free it. */ + cxt->allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer)); + strcpy (cxt->allocated_line, &rl_line_buffer[0]); + cxt->lines[i] = cxt->allocated_line; + } + + cxt->hlen++; + + /* The line where we start the search. */ + cxt->history_pos = cxt->save_line; + + rl_save_prompt (); + + /* Initialize search parameters. */ + cxt->search_string = (char *)xmalloc (cxt->search_string_size = 128); + cxt->search_string[cxt->search_string_index = 0] = '\0'; + + /* Normalize DIRECTION into 1 or -1. */ + cxt->direction = (direction >= 0) ? 1 : -1; + + cxt->sline = rl_line_buffer; + cxt->sline_len = strlen (cxt->sline); + cxt->sline_index = rl_point; + + _rl_iscxt = cxt; /* save globally */ + + /* experimental right now */ + _rl_init_executing_keyseq (); + + return cxt; +} + +static void +_rl_isearch_fini (_rl_search_cxt *cxt) +{ + /* First put back the original state. */ + rl_replace_line (cxt->lines[cxt->save_line], 0); + + rl_restore_prompt (); + + /* Save the search string for possible later use. */ + FREE (last_isearch_string); + last_isearch_string = cxt->search_string; + last_isearch_string_len = cxt->search_string_index; + cxt->search_string = 0; + cxt->search_string_size = 0; + cxt->search_string_index = 0; + + if (cxt->last_found_line < cxt->save_line) + rl_get_previous_history (cxt->save_line - cxt->last_found_line, 0); + else + rl_get_next_history (cxt->last_found_line - cxt->save_line, 0); + + /* 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 (cxt->sline_index < 0) + { + if (cxt->last_found_line == cxt->save_line) + cxt->sline_index = cxt->save_point; + else + cxt->sline_index = strlen (rl_line_buffer); + rl_mark = cxt->save_mark; + rl_deactivate_mark (); + } + + rl_point = cxt->sline_index; + /* Don't worry about where to put the mark here; rl_get_previous_history + and rl_get_next_history take care of it. + If we want to highlight the search string, this is where to set the + point and mark to do it. */ + _rl_fix_point (0); + rl_deactivate_mark (); + +/* _rl_optimize_redisplay (); */ + rl_clear_message (); +} + +/* XXX - we could use _rl_bracketed_read_mbstring () here. */ +int +_rl_search_getchar (_rl_search_cxt *cxt) +{ + int c; + + /* Read a key and decide how to proceed. */ + RL_SETSTATE(RL_STATE_MOREINPUT); + c = cxt->lastc = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + +#if defined (HANDLE_MULTIBYTE) + /* This ends up with C (and LASTC) being set to the last byte of the + multibyte character. In most cases c == lastc == mb[0] */ + if (c >= 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0) + c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX); +#endif + + RL_CHECK_SIGNALS (); + return c; +} + +#define ENDSRCH_CHAR(c) \ + ((CTRL_CHAR (c) || META_CHAR (c) || (c) == RUBOUT) && ((c) != CTRL ('G'))) + +/* Process just-read character C according to isearch context CXT. Return + -1 if the caller should just free the context and return, 0 if we should + break out of the loop, and 1 if we should continue to read characters. */ +int +_rl_isearch_dispatch (_rl_search_cxt *cxt, int c) +{ + int n, wstart, wlen, limit, cval, incr; + char *paste; + size_t pastelen; + int j; + rl_command_func_t *f; + + f = (rl_command_func_t *)NULL; + + if (c < 0) + { + cxt->sflags |= SF_FAILED; + cxt->history_pos = cxt->last_found_line; + return -1; + } + + _rl_add_executing_keyseq (c); + + /* XXX - experimental code to allow users to bracketed-paste into the search + string even when ESC is one of the isearch-terminators. Not perfect yet. */ + if (_rl_enable_bracketed_paste && c == ESC && strchr (cxt->search_terminators, c) && (n = _rl_nchars_available ()) > (BRACK_PASTE_SLEN-1)) + { + j = _rl_read_bracketed_paste_prefix (c); + if (j == 1) + { + cxt->lastc = -7; /* bracketed paste, see below */ + goto opcode_dispatch; + } + else if (_rl_pushed_input_available ()) /* eat extra char we pushed back */ + c = cxt->lastc = rl_read_key (); + else + c = cxt->lastc; /* last ditch */ + } + + /* If we are moving into a new keymap, modify cxt->keymap and go on. + This can be a problem if c == ESC and we want to terminate the + incremental search, so we check */ + if (c >= 0 && cxt->keymap[c].type == ISKMAP && strchr (cxt->search_terminators, cxt->lastc) == 0) + { + /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued + takes microseconds, so multiply by 1000. If we don't get any + additional input and this keymap shadows another function, process + that key as if it was all we read. */ + if (_rl_keyseq_timeout > 0 && + RL_ISSTATE (RL_STATE_CALLBACK) == 0 && + RL_ISSTATE (RL_STATE_INPUTPENDING) == 0 && + _rl_pushed_input_available () == 0 && + ((Keymap)(cxt->keymap[c].function))[ANYOTHERKEY].function && + _rl_input_queued (_rl_keyseq_timeout*1000) == 0) + goto add_character; + + cxt->okeymap = cxt->keymap; + cxt->keymap = FUNCTION_TO_KEYMAP (cxt->keymap, c); + cxt->sflags |= SF_CHGKMAP; + /* XXX - we should probably save this sequence, so we can do + something useful if this doesn't end up mapping to a command we + interpret here. Right now we just save the most recent character + that caused the index into a new keymap. */ + cxt->prevc = c; +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (cxt->mb[1] == 0) + { + cxt->pmb[0] = c; /* XXX should be == cxt->mb[0] */ + cxt->pmb[1] = '\0'; + } + else + memcpy (cxt->pmb, cxt->mb, sizeof (cxt->pmb)); + } +#endif + return 1; + } + +add_character: + + /* Translate the keys we do something with to opcodes. */ + if (c >= 0 && cxt->keymap[c].type == ISFUNC) + { + /* If we have a multibyte character, see if it's bound to something that + affects the search. */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && cxt->mb[1]) + f = rl_function_of_keyseq (cxt->mb, cxt->keymap, (int *)NULL); + else +#endif + { + f = cxt->keymap[c].function; + if (f == rl_do_lowercase_version) + f = cxt->keymap[_rl_to_lower (c)].function; + } + + if (f == rl_reverse_search_history) + cxt->lastc = (cxt->sflags & SF_REVERSE) ? -1 : -2; + else if (f == rl_forward_search_history) + cxt->lastc = (cxt->sflags & SF_REVERSE) ? -2 : -1; + else if (f == rl_rubout) + cxt->lastc = -3; + else if (c == CTRL ('G') || f == rl_abort) + cxt->lastc = -4; + else if (c == CTRL ('W') || f == rl_unix_word_rubout) /* XXX */ + cxt->lastc = -5; + else if (c == CTRL ('Y') || f == rl_yank) /* XXX */ + cxt->lastc = -6; + else if (f == rl_bracketed_paste_begin) + cxt->lastc = -7; + } + + /* If we changed the keymap earlier while translating a key sequence into + a command, restore it now that we've succeeded. */ + if (cxt->sflags & SF_CHGKMAP) + { + cxt->keymap = cxt->okeymap; + cxt->sflags &= ~SF_CHGKMAP; + /* If we indexed into a new keymap, but didn't map to a command that + affects the search (lastc > 0), and the character that mapped to a + new keymap would have ended the search (ENDSRCH_CHAR(cxt->prevc)), + handle that now as if the previous char would have ended the search + and we would have read the current character. */ + /* XXX - should we check cxt->mb? */ + if (cxt->lastc > 0 && ENDSRCH_CHAR (cxt->prevc)) + { + rl_stuff_char (cxt->lastc); + rl_execute_next (cxt->prevc); + /* XXX - do we insert everything in cxt->pmb? */ + return (0); + } + /* Otherwise, if the current character is mapped to self-insert or + nothing (i.e., not an editing command), and the previous character + was a keymap index, then we need to insert both the previous + character and the current character into the search string. */ + else if (cxt->lastc > 0 && cxt->prevc > 0 && + cxt->keymap[cxt->prevc].type == ISKMAP && + (f == 0 || f == rl_insert)) + { + /* Make lastc be the next character read */ + /* XXX - do we insert everything in cxt->mb? */ + rl_execute_next (cxt->lastc); + /* Dispatch on the previous character (insert into search string) */ + cxt->lastc = cxt->prevc; +#if defined (HANDLE_MULTIBYTE) + /* Have to overwrite cxt->mb here because dispatch uses it below */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (cxt->pmb[1] == 0) + { + cxt->mb[0] = cxt->lastc; /* == cxt->prevc */ + cxt->mb[1] = '\0'; + } + else + memcpy (cxt->mb, cxt->pmb, sizeof (cxt->mb)); + } +#endif + cxt->prevc = 0; + } + else if (cxt->lastc > 0 && cxt->prevc > 0 && f && f != rl_insert) + { + _rl_term_executing_keyseq (); /* should this go in the caller? */ + + _rl_pending_command.map = cxt->keymap; + _rl_pending_command.count = 1; /* XXX */ + _rl_pending_command.key = cxt->lastc; + _rl_pending_command.func = f; + _rl_command_to_execute = &_rl_pending_command; + + return (0); + } + } + + /* 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 (cxt->lastc > 0 && strchr (cxt->search_terminators, cxt->lastc)) + { + /* 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. + XXX - since _rl_input_available depends on the application- + settable keyboard timeout value, this could alternatively + use _rl_input_queued(100000) */ + if (cxt->lastc == ESC && (_rl_pushed_input_available () || _rl_input_available ())) + rl_execute_next (ESC); + return (0); + } + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (cxt->lastc >= 0 && (cxt->mb[0] && cxt->mb[1] == '\0') && ENDSRCH_CHAR (cxt->lastc)) + { + /* This sets rl_pending_input to LASTC; it will be picked up the next + time rl_read_key is called. */ + rl_execute_next (cxt->lastc); + return (0); + } + } + else +#endif + if (cxt->lastc >= 0 && ENDSRCH_CHAR (cxt->lastc)) + { + /* This sets rl_pending_input to LASTC; it will be picked up the next + time rl_read_key is called. */ + rl_execute_next (cxt->lastc); + return (0); + } + + _rl_init_executing_keyseq (); + +opcode_dispatch: + /* Now dispatch on the character. `Opcodes' affect the search string or + state. Other characters are added to the string. */ + switch (cxt->lastc) + { + /* search again */ + case -1: + if (cxt->search_string_index == 0) + { + if (last_isearch_string) + { + cxt->search_string_size = 64 + last_isearch_string_len; + cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); + strcpy (cxt->search_string, last_isearch_string); + cxt->search_string_index = last_isearch_string_len; + rl_display_search (cxt->search_string, cxt->sflags, -1); + break; + } + /* XXX - restore keymap here? */ + return (1); + } + else if ((cxt->sflags & SF_REVERSE) && cxt->sline_index >= 0) + cxt->sline_index--; + else if (cxt->sline_index != cxt->sline_len) + cxt->sline_index++; + else + rl_ding (); + break; + + /* switch directions */ + case -2: + cxt->direction = -cxt->direction; + if (cxt->direction < 0) + cxt->sflags |= SF_REVERSE; + else + cxt->sflags &= ~SF_REVERSE; + break; + + /* 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 (cxt->search_string_index == 0) + rl_ding (); + else if (MB_CUR_MAX == 1 || rl_byte_oriented) + cxt->search_string[--cxt->search_string_index] = '\0'; + else + { + wstart = _rl_find_prev_mbchar (cxt->search_string, cxt->search_string_index, MB_FIND_NONZERO); + if (wstart >= 0) + cxt->search_string[cxt->search_string_index = wstart] = '\0'; + else + cxt->search_string[cxt->search_string_index = 0] = '\0'; + } + + if (cxt->search_string_index == 0) + rl_ding (); + + break; + + case -4: /* C-G, abort */ + rl_replace_line (cxt->lines[cxt->save_line], 0); + rl_point = cxt->save_point; + rl_mark = cxt->save_mark; + rl_deactivate_mark (); + rl_restore_prompt(); + rl_clear_message (); + + _rl_fix_point (1); /* in case save_line and save_point are out of sync */ + return -1; + + case -5: /* C-W */ + /* skip over portion of line we already matched and yank word */ + wstart = rl_point + cxt->search_string_index; + if (wstart >= rl_end) + { + rl_ding (); + break; + } + + /* if not in a word, move to one. */ + cval = _rl_char_value (rl_line_buffer, wstart); + if (_rl_walphabetic (cval) == 0) + { + rl_ding (); + break; + } + n = MB_NEXTCHAR (rl_line_buffer, wstart, 1, MB_FIND_NONZERO);; + while (n < rl_end) + { + cval = _rl_char_value (rl_line_buffer, n); + if (_rl_walphabetic (cval) == 0) + break; + n = MB_NEXTCHAR (rl_line_buffer, n, 1, MB_FIND_NONZERO);; + } + wlen = n - wstart + 1; + if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size) + { + cxt->search_string_size += wlen + 1; + cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); + } + for (; wstart < n; wstart++) + cxt->search_string[cxt->search_string_index++] = rl_line_buffer[wstart]; + cxt->search_string[cxt->search_string_index] = '\0'; + break; + + case -6: /* C-Y */ + /* skip over portion of line we already matched and yank rest */ + wstart = rl_point + cxt->search_string_index; + if (wstart >= rl_end) + { + rl_ding (); + break; + } + n = rl_end - wstart + 1; + if (cxt->search_string_index + n + 1 >= cxt->search_string_size) + { + cxt->search_string_size += n + 1; + cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); + } + for (n = wstart; n < rl_end; n++) + cxt->search_string[cxt->search_string_index++] = rl_line_buffer[n]; + cxt->search_string[cxt->search_string_index] = '\0'; + break; + + case -7: /* bracketed paste */ + paste = _rl_bracketed_text (&pastelen); + if (paste == 0 || *paste == 0) + { + xfree (paste); + break; + } + if (_rl_enable_active_region) + rl_activate_mark (); + if (cxt->search_string_index + pastelen + 1 >= cxt->search_string_size) + { + cxt->search_string_size += pastelen + 2; + cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); + } + memcpy (cxt->search_string + cxt->search_string_index, paste, pastelen); + cxt->search_string_index += pastelen; + cxt->search_string[cxt->search_string_index] = '\0'; + xfree (paste); + break; + + /* Add character to search string and continue search. */ + default: +#if defined (HANDLE_MULTIBYTE) + wlen = (cxt->mb[0] == 0 || cxt->mb[1] == 0) ? 1 : RL_STRLEN (cxt->mb); +#else + wlen = 1; +#endif + if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size) + { + cxt->search_string_size += 128; /* 128 much greater than MB_CUR_MAX */ + cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); + } +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int j; + + if (cxt->mb[0] == 0 || cxt->mb[1] == 0) + cxt->search_string[cxt->search_string_index++] = cxt->mb[0]; + else + for (j = 0; j < wlen; ) + cxt->search_string[cxt->search_string_index++] = cxt->mb[j++]; + } + else +#endif + cxt->search_string[cxt->search_string_index++] = cxt->lastc; /* XXX - was c instead of lastc */ + cxt->search_string[cxt->search_string_index] = '\0'; + break; + } + + for (cxt->sflags &= ~(SF_FOUND|SF_FAILED);; ) + { + if (cxt->search_string_index == 0) + { + cxt->sflags |= SF_FAILED; + break; + } + + limit = cxt->sline_len - cxt->search_string_index + 1; + + /* Search the current line. */ + while ((cxt->sflags & SF_REVERSE) ? (cxt->sline_index >= 0) : (cxt->sline_index < limit)) + { + if (STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index)) + { + cxt->sflags |= SF_FOUND; + break; + } + else + cxt->sline_index += cxt->direction; + + if (cxt->sline_index < 0) + { + cxt->sline_index = 0; + break; + } + } + if (cxt->sflags & SF_FOUND) + break; + + /* Move to the next line, but skip new copies of the line + we just found and lines shorter than the string we're + searching for. */ + do + { + /* Move to the next line. */ + cxt->history_pos += cxt->direction; + + /* At limit for direction? */ + if ((cxt->sflags & SF_REVERSE) ? (cxt->history_pos < 0) : (cxt->history_pos == cxt->hlen)) + { + cxt->sflags |= SF_FAILED; + break; + } + + /* We will need these later. */ + cxt->sline = cxt->lines[cxt->history_pos]; + cxt->sline_len = strlen (cxt->sline); + } + while ((cxt->prev_line_found && STREQ (cxt->prev_line_found, cxt->lines[cxt->history_pos])) || + (cxt->search_string_index > cxt->sline_len)); + + if (cxt->sflags & SF_FAILED) + { + /* XXX - reset sline_index if < 0 */ + if (cxt->sline_index < 0) + cxt->sline_index = 0; + break; + } + + /* Now set up the line for searching... */ + cxt->sline_index = (cxt->sflags & SF_REVERSE) ? cxt->sline_len - cxt->search_string_index : 0; + } + + /* reset the keymaps for the next time through the loop */ + cxt->keymap = cxt->okeymap = _rl_keymap; + + if (cxt->sflags & SF_FAILED) + { + /* We cannot find the search string. Ding the bell. */ + rl_ding (); + cxt->history_pos = cxt->last_found_line; + rl_deactivate_mark (); + rl_display_search (cxt->search_string, cxt->sflags, (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos); + return 1; + } + + /* We have found the search string. Just display it. But don't + actually move there in the history list until the user accepts + the location. */ + if (cxt->sflags & SF_FOUND) + { + cxt->prev_line_found = cxt->lines[cxt->history_pos]; + rl_replace_line (cxt->lines[cxt->history_pos], 0); + if (_rl_enable_active_region) + rl_activate_mark (); + rl_point = cxt->sline_index; + if (rl_mark_active_p () && cxt->search_string_index > 0) + rl_mark = rl_point + cxt->search_string_index; + cxt->last_found_line = cxt->history_pos; + rl_display_search (cxt->search_string, cxt->sflags, (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos); + } + + return 1; +} + +int +_rl_isearch_cleanup (_rl_search_cxt *cxt, int r) +{ + if (r >= 0) + _rl_isearch_fini (cxt); + _rl_scxt_dispose (cxt, 0); + _rl_iscxt = 0; + + RL_UNSETSTATE(RL_STATE_ISEARCH); + + return (r != 0); +} + +/* Search through the history looking for an interactively typed string. + This is analogous to i-search. We start the search in the current line. + DIRECTION is which direction to search; >= 0 means forward, < 0 means + backwards. */ +static int +rl_search_history (int direction, int invoking_key) +{ + _rl_search_cxt *cxt; /* local for now, but saved globally */ + int c, r; + + RL_SETSTATE(RL_STATE_ISEARCH); + cxt = _rl_isearch_init (direction); + + rl_display_search (cxt->search_string, cxt->sflags, -1); + + /* If we are using the callback interface, all we do is set up here and + return. The key is that we leave RL_STATE_ISEARCH set. */ + if (RL_ISSTATE (RL_STATE_CALLBACK)) + return (0); + + r = -1; + for (;;) + { + c = _rl_search_getchar (cxt); + /* We might want to handle EOF here (c == 0) */ + r = _rl_isearch_dispatch (cxt, cxt->lastc); + if (r <= 0) + break; + } + + /* The searching is over. The user may have found the string that she + was looking for, or else she may have exited a failing search. If + LINE_INDEX is -1, then that shows that the string searched for was + not found. We use this to determine where to place rl_point. */ + return (_rl_isearch_cleanup (cxt, r)); +} + +#if defined (READLINE_CALLBACKS) +/* Called from the callback functions when we are ready to read a key. The + callback functions know to call this because RL_ISSTATE(RL_STATE_ISEARCH). + If _rl_isearch_dispatch finishes searching, this function is responsible + for turning off RL_STATE_ISEARCH, which it does using _rl_isearch_cleanup. */ +int +_rl_isearch_callback (_rl_search_cxt *cxt) +{ + int c, r; + + c = _rl_search_getchar (cxt); + /* We might want to handle EOF here */ + r = _rl_isearch_dispatch (cxt, cxt->lastc); + + return (r <= 0) ? _rl_isearch_cleanup (cxt, r) : 0; +} +#endif diff --git a/lib/readline/keymaps.c b/lib/readline/keymaps.c new file mode 100644 index 0000000..4ade30b --- /dev/null +++ b/lib/readline/keymaps.c @@ -0,0 +1,174 @@ +/* keymaps.c -- Functions and keymaps for the GNU Readline library. */ + +/* Copyright (C) 1988,1989-2009,2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <stdio.h> /* for FILE * definition for readline.h */ + +#include "readline.h" +#include "rlconf.h" + +#include "emacs_keymap.c" + +#if defined (VI_MODE) +#include "vi_keymap.c" +#endif + +#include "xmalloc.h" + +/* **************************************************************** */ +/* */ +/* Functions for manipulating Keymaps. */ +/* */ +/* **************************************************************** */ + + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +Keymap +rl_make_bare_keymap (void) +{ + register int i; + Keymap keymap; + + keymap = (Keymap)xmalloc (KEYMAP_SIZE * sizeof (KEYMAP_ENTRY)); + for (i = 0; i < KEYMAP_SIZE; i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = (rl_command_func_t *)NULL; + } + +#if 0 + for (i = 'A'; i < ('Z' + 1); i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = rl_do_lowercase_version; + } +#endif + + return (keymap); +} + +/* A convenience function that returns 1 if there are no keys bound to + functions in KEYMAP */ +int +rl_empty_keymap (Keymap keymap) +{ + int i; + + for (i = 0; i < ANYOTHERKEY; i++) + { + if (keymap[i].type != ISFUNC || keymap[i].function) + return 0; + } + return 1; +} + +/* Return a new keymap which is a copy of MAP. Just copies pointers, does + not copy text of macros or descend into child keymaps. */ +Keymap +rl_copy_keymap (Keymap map) +{ + register int i; + Keymap temp; + + temp = rl_make_bare_keymap (); + for (i = 0; i < KEYMAP_SIZE; i++) + { + temp[i].type = map[i].type; + temp[i].function = map[i].function; + } + return (temp); +} + +/* Return a new keymap with the printing characters bound to rl_insert, + the uppercase Meta characters bound to run their lowercase equivalents, + and the Meta digits bound to produce numeric arguments. */ +Keymap +rl_make_keymap (void) +{ + register int i; + Keymap newmap; + + newmap = rl_make_bare_keymap (); + + /* All ASCII printing characters are self-inserting. */ + for (i = ' '; i < 127; i++) + newmap[i].function = rl_insert; + + newmap[TAB].function = rl_insert; + newmap[RUBOUT].function = rl_rubout; /* RUBOUT == 127 */ + newmap[CTRL('H')].function = rl_rubout; + +#if KEYMAP_SIZE > 128 + /* Printing characters in ISO Latin-1 and some 8-bit character sets. */ + for (i = 128; i < 256; i++) + newmap[i].function = rl_insert; +#endif /* KEYMAP_SIZE > 128 */ + + return (newmap); +} + +/* Free the storage associated with MAP. */ +void +rl_discard_keymap (Keymap map) +{ + int i; + + if (map == 0) + return; + + for (i = 0; i < KEYMAP_SIZE; i++) + { + switch (map[i].type) + { + case ISFUNC: + break; + + case ISKMAP: + rl_discard_keymap ((Keymap)map[i].function); + xfree ((char *)map[i].function); + break; + + case ISMACR: + xfree ((char *)map[i].function); + break; + } + } +} + +/* Convenience function that discards, then frees, MAP. */ +void +rl_free_keymap (Keymap map) +{ + rl_discard_keymap (map); + xfree ((char *)map); +} diff --git a/lib/readline/keymaps.h b/lib/readline/keymaps.h new file mode 100644 index 0000000..2903814 --- /dev/null +++ b/lib/readline/keymaps.h @@ -0,0 +1,100 @@ +/* keymaps.h -- Manipulation of readline keymaps. */ + +/* Copyright (C) 1987, 1989, 1992-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _KEYMAPS_H_ +#define _KEYMAPS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (READLINE_LIBRARY) +# include "rlstdc.h" +# include "chardefs.h" +# include "rltypedefs.h" +#else +# include <readline/rlstdc.h> +# include <readline/chardefs.h> +# include <readline/rltypedefs.h> +#endif + +/* A keymap contains one entry for each key in the ASCII set. + Each entry consists of a type and a pointer. + FUNCTION is the address of a function to run, or the + address of a keymap to indirect through. + TYPE says which kind of thing FUNCTION is. */ +typedef struct _keymap_entry { + char type; + rl_command_func_t *function; +} 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) plus one for subsequence matching. */ +#define KEYMAP_SIZE 257 +#define ANYOTHERKEY KEYMAP_SIZE-1 + +typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; +typedef KEYMAP_ENTRY *Keymap; + +/* The values that TYPE can have in a keymap entry. */ +#define ISFUNC 0 +#define ISKMAP 1 +#define ISMACR 2 + +extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; +extern KEYMAP_ENTRY_ARRAY vi_insertion_keymap, vi_movement_keymap; + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +extern Keymap rl_make_bare_keymap (void); + +/* Return a new keymap which is a copy of MAP. */ +extern Keymap rl_copy_keymap (Keymap); + +/* Return a new keymap with the printing characters bound to rl_insert, + the lowercase Meta characters bound to run their equivalents, and + the Meta digits bound to produce numeric arguments. */ +extern Keymap rl_make_keymap (void); + +/* Free the storage associated with a keymap. */ +extern void rl_discard_keymap (Keymap); + +/* These functions actually appear in bind.c */ + +/* Return the keymap corresponding to a given name. Names look like + `emacs' or `emacs-meta' or `vi-insert'. */ +extern Keymap rl_get_keymap_by_name (const char *); + +/* Return the current keymap. */ +extern Keymap rl_get_keymap (void); + +/* Set the current keymap to MAP. */ +extern void rl_set_keymap (Keymap); + +/* Set the name of MAP to NAME */ +extern int rl_set_keymap_name (const char *, Keymap); + +#ifdef __cplusplus +} +#endif + +#endif /* _KEYMAPS_H_ */ diff --git a/lib/readline/kill.c b/lib/readline/kill.c new file mode 100644 index 0000000..4cf933b --- /dev/null +++ b/lib/readline/kill.c @@ -0,0 +1,900 @@ +/* kill.c -- kill ring management. */ + +/* Copyright (C) 1994-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.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> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +/* **************************************************************** */ +/* */ +/* Killing Mechanism */ +/* */ +/* **************************************************************** */ + +/* What we assume for a max number of kills. */ +#define DEFAULT_MAX_KILLS 10 + +/* The real variable to look at to find out when to flush kills. */ +static int rl_max_kills = DEFAULT_MAX_KILLS; + +/* Where to store killed text. */ +static char **rl_kill_ring = (char **)NULL; + +/* Where we are in the kill ring. */ +static int rl_kill_index; + +/* How many slots we have in the kill ring. */ +static int rl_kill_ring_length; + +static int _rl_copy_to_kill_ring (char *, int); +static int region_kill_internal (int); +static int _rl_copy_word_as_kill (int, int); +static int rl_yank_nth_arg_internal (int, int, int); + +/* How to say that you only want to save a certain amount + of kill material. */ +int +rl_set_retained_kills (int num) +{ + return 0; +} + +/* Add TEXT to the kill ring, allocating a new kill ring slot as necessary. + This uses TEXT directly, so the caller must not free it. If APPEND is + non-zero, and the last command was a kill, the text is appended to the + current kill ring slot, otherwise prepended. */ +static int +_rl_copy_to_kill_ring (char *text, int append) +{ + char *old, *new; + int slot; + + /* First, find the slot to work with. */ + if (_rl_last_command_was_kill == 0 || rl_kill_ring == 0) + { + /* Get a new slot. */ + if (rl_kill_ring == 0) + { + /* If we don't have any defined, then make one. */ + rl_kill_ring = (char **) + xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *)); + rl_kill_ring[slot = 0] = (char *)NULL; + } + else + { + /* We have to add a new slot on the end, unless we have + exceeded the max limit for remembering kills. */ + slot = rl_kill_ring_length; + if (slot == rl_max_kills) + { + register int i; + xfree (rl_kill_ring[0]); + for (i = 0; i < slot; i++) + rl_kill_ring[i] = rl_kill_ring[i + 1]; + } + else + { + slot = rl_kill_ring_length += 1; + rl_kill_ring = (char **)xrealloc (rl_kill_ring, (slot + 1) * sizeof (char *)); + } + rl_kill_ring[--slot] = (char *)NULL; + } + } + else + slot = rl_kill_ring_length - 1; + + /* If the last command was a kill, prepend or append. */ + if (_rl_last_command_was_kill && rl_kill_ring[slot] && rl_editing_mode != vi_mode) + { + old = rl_kill_ring[slot]; + new = (char *)xmalloc (1 + strlen (old) + strlen (text)); + + if (append) + { + strcpy (new, old); + strcat (new, text); + } + else + { + strcpy (new, text); + strcat (new, old); + } + xfree (old); + xfree (text); + rl_kill_ring[slot] = new; + } + else + rl_kill_ring[slot] = text; + + rl_kill_index = slot; + return 0; +} + +/* The way to kill something. This appends or prepends to the last + kill, if the last command was a kill command. if FROM is less + than TO, then the text is appended, otherwise prepended. If the + last command was not a kill command, then a new slot is made for + this kill. */ +int +rl_kill_text (int from, int to) +{ + char *text; + + /* Is there anything to kill? */ + if (from == to) + { + _rl_last_command_was_kill++; + return 0; + } + + text = rl_copy_text (from, to); + + /* Delete the copied text from the line. */ + rl_delete_text (from, to); + + _rl_copy_to_kill_ring (text, from < to); + + _rl_last_command_was_kill++; + return 0; +} + +/* Now REMEMBER! In order to do prepending or appending correctly, kill + commands always make rl_point's original position be the FROM argument, + and rl_point's extent be the TO argument. */ + +/* **************************************************************** */ +/* */ +/* Killing Commands */ +/* */ +/* **************************************************************** */ + +/* Delete the word at point, saving the text in the kill ring. */ +int +rl_kill_word (int count, int key) +{ + 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; +} + +/* Rubout the word before point, placing it on the kill ring. */ +int +rl_backward_kill_word (int count, int key) +{ + int orig_point; + + if (count < 0) + return (rl_kill_word (-count, key)); + else + { + orig_point = rl_point; + rl_backward_word (count, key); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Kill from here to the end of the line. If DIRECTION is negative, kill + back to the line start instead. */ +int +rl_kill_line (int direction, int key) +{ + int orig_point; + + if (direction < 0) + return (rl_backward_kill_line (1, key)); + else + { + orig_point = rl_point; + rl_end_of_line (1, key); + 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; +} + +/* Kill backwards to the start of the line. If DIRECTION is negative, kill + forwards to the line end instead. */ +int +rl_backward_kill_line (int direction, int key) +{ + int orig_point; + + if (direction < 0) + return (rl_kill_line (1, key)); + else + { + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + rl_beg_of_line (1, key); + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + } + return 0; +} + +/* Kill the whole line, no matter where point is. */ +int +rl_kill_full_line (int count, int key) +{ + rl_begin_undo_group (); + rl_point = 0; + rl_kill_text (rl_point, rl_end); + rl_mark = 0; + rl_end_undo_group (); + return 0; +} + +/* The next two functions mimic unix line editing behaviour, except they + save the deleted text on the kill ring. This is safer than not saving + it, and since we have a ring, nobody should get screwed. */ + +/* This does what C-w does in Unix. We can't prevent people from + using behaviour that they expect. */ +int +rl_unix_word_rubout (int count, int key) +{ + int orig_point; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + while (rl_point && whitespace (rl_line_buffer[rl_point - 1])) + rl_point--; + + while (rl_point && (whitespace (rl_line_buffer[rl_point - 1]) == 0)) + rl_point--; /* XXX - multibyte? */ + } + + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + + return 0; +} + +/* This deletes one filename component in a Unix pathname. That is, it + deletes backward to directory separator (`/') or whitespace. */ +int +rl_unix_filename_rubout (int count, int key) +{ + int orig_point, c; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + c = rl_line_buffer[rl_point - 1]; + + /* First move backwards through whitespace */ + while (rl_point && whitespace (c)) + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + + /* Consume one or more slashes. */ + if (c == '/') + { + int i; + + i = rl_point - 1; + while (i > 0 && c == '/') + c = rl_line_buffer[--i]; + if (i == 0 || whitespace (c)) + { + rl_point = i + whitespace (c); + continue; /* slashes only */ + } + c = '/'; + } + + while (rl_point && (whitespace (c) || c == '/')) + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + + while (rl_point && (whitespace (c) == 0) && c != '/') + { + rl_point--; /* XXX - multibyte? */ + c = rl_line_buffer[rl_point - 1]; + } + } + + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + + return 0; +} + +/* Here is C-u doing what Unix does. You don't *have* to use these + key-bindings. We have a choice of killing the entire line, or + killing from where we are to the start of the line. We choose the + latter, because if you are a Unix weenie, then you haven't backspaced + into the line at all, and if you aren't, then you know what you are + doing. */ +int +rl_unix_line_discard (int count, int key) +{ + if (rl_point == 0) + rl_ding (); + else + { + rl_kill_text (rl_point, 0); + rl_point = 0; + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Copy the text in the `region' to the kill ring. If DELETE is non-zero, + delete the text from the line as well. */ +static int +region_kill_internal (int delete) +{ + char *text; + + if (rl_mark != rl_point) + { + 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_fix_point (1); + _rl_last_command_was_kill++; + return 0; +} + +/* Copy the text in the region to the kill ring. */ +int +rl_copy_region_to_kill (int count, int key) +{ + return (region_kill_internal (0)); +} + +/* Kill the text between the point and mark. */ +int +rl_kill_region (int count, int key) +{ + int r, npoint; + + npoint = (rl_point < rl_mark) ? rl_point : rl_mark; + r = region_kill_internal (1); + rl_point = npoint; + _rl_fix_point (1); + return r; +} + +/* Copy COUNT words to the kill ring. DIR says which direction we look + to find the words. */ +static int +_rl_copy_word_as_kill (int count, int dir) +{ + int om, op, r; + + om = rl_mark; + op = rl_point; + + if (dir > 0) + rl_forward_word (count, 0); + else + rl_backward_word (count, 0); + + rl_mark = rl_point; + + if (dir > 0) + rl_backward_word (count, 0); + else + rl_forward_word (count, 0); + + r = region_kill_internal (0); + + rl_mark = om; + rl_point = op; + + return r; +} + +int +rl_copy_forward_word (int count, int key) +{ + if (count < 0) + return (rl_copy_backward_word (-count, key)); + + return (_rl_copy_word_as_kill (count, 1)); +} + +int +rl_copy_backward_word (int count, int key) +{ + if (count < 0) + return (rl_copy_forward_word (-count, key)); + + return (_rl_copy_word_as_kill (count, -1)); +} + +/* Yank back the last killed text. This ignores arguments. */ +int +rl_yank (int count, int key) +{ + if (rl_kill_ring == 0) + { + _rl_abort_internal (); + return 1; + } + + _rl_set_mark_at_pos (rl_point); + rl_insert_text (rl_kill_ring[rl_kill_index]); + return 0; +} + +/* If the last command was yank, or yank_pop, and the text just + before point is identical to the current kill item, then + delete that text from the line, rotate the index down, and + yank back some other text. */ +int +rl_yank_pop (int count, int key) +{ + int l, n; + + if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) || + !rl_kill_ring) + { + _rl_abort_internal (); + return 1; + } + + l = strlen (rl_kill_ring[rl_kill_index]); + n = rl_point - l; + if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l)) + { + rl_delete_text (n, rl_point); + rl_point = n; + rl_kill_index--; + if (rl_kill_index < 0) + rl_kill_index = rl_kill_ring_length - 1; + rl_yank (1, 0); + return 0; + } + else + { + _rl_abort_internal (); + return 1; + } +} + +#if defined (VI_MODE) +int +rl_vi_yank_pop (int count, int key) +{ + int l, n, origpoint; + + if (((rl_last_func != rl_vi_yank_pop) && (rl_last_func != rl_vi_put)) || + !rl_kill_ring) + { + _rl_abort_internal (); + return 1; + } + + l = strlen (rl_kill_ring[rl_kill_index]); +#if 0 /* TAG:readline-8.3 8/29/2022 matteopaolini1995@gmail.com */ + origpoint = rl_point; + n = rl_point - l + 1; +#else + n = rl_point - l; +#endif + if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l)) + { +#if 0 /* TAG:readline-8.3 */ + rl_delete_text (n, n + l); /* remember vi cursor positioning */ + rl_point = origpoint - l; +#else + rl_delete_text (n, rl_point); + rl_point = n; +#endif + rl_kill_index--; + if (rl_kill_index < 0) + rl_kill_index = rl_kill_ring_length - 1; + rl_vi_put (1, 'p'); + return 0; + } + else + { + _rl_abort_internal (); + return 1; + } +} +#endif /* VI_MODE */ + +/* Yank the COUNTh argument from the previous history line, skipping + HISTORY_SKIP lines before looking for the `previous line'. */ +static int +rl_yank_nth_arg_internal (int count, int key, int history_skip) +{ + register HIST_ENTRY *entry; + char *arg; + int i, pos; + + pos = where_history (); + + if (history_skip) + { + for (i = 0; i < history_skip; i++) + entry = previous_history (); + } + + entry = previous_history (); + + history_set_pos (pos); + + if (entry == 0) + { + rl_ding (); + return 1; + } + + arg = history_arg_extract (count, count, entry->line); + if (!arg || !*arg) + { + rl_ding (); + FREE (arg); + return 1; + } + + 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. */ + if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) + { + rl_vi_append_mode (1, key); + rl_insert_text (" "); + } +#endif /* VI_MODE */ + + rl_insert_text (arg); + xfree (arg); + + rl_end_undo_group (); + return 0; +} + +/* Yank the COUNTth argument from the previous history line. */ +int +rl_yank_nth_arg (int count, int key) +{ + return (rl_yank_nth_arg_internal (count, key, 0)); +} + +/* Yank the last argument from the previous history line. This `knows' + how rl_yank_nth_arg treats a count of `$'. With an argument, this + behaves the same as rl_yank_nth_arg. */ +int +rl_yank_last_arg (int count, int key) +{ + static int history_skip = 0; + static int explicit_arg_p = 0; + static int count_passed = 1; + static int direction = 1; + static int undo_needed = 0; + int retval; + + if (rl_last_func != rl_yank_last_arg) + { + history_skip = 0; + explicit_arg_p = rl_explicit_arg; + count_passed = count; + direction = 1; + } + else + { + if (undo_needed) + rl_do_undo (); + if (count < 0) /* XXX - was < 1 */ + direction = -direction; + history_skip += direction; + if (history_skip < 0) + history_skip = 0; + } + + if (explicit_arg_p) + retval = rl_yank_nth_arg_internal (count_passed, key, history_skip); + else + retval = rl_yank_nth_arg_internal ('$', key, history_skip); + + undo_needed = retval == 0; + return retval; +} + +/* Having read the special escape sequence denoting the beginning of a + `bracketed paste' sequence, read the rest of the pasted input until the + closing sequence and return the pasted text. */ +char * +_rl_bracketed_text (size_t *lenp) +{ + int c; + size_t len, cap; + char *buf; + + len = 0; + buf = xmalloc (cap = 64); + buf[0] = '\0'; + + RL_SETSTATE (RL_STATE_MOREINPUT); + while ((c = rl_read_key ()) >= 0) + { + if (RL_ISSTATE (RL_STATE_MACRODEF)) + _rl_add_macro_char (c); + + if (c == '\r') /* XXX */ + c = '\n'; + + if (len == cap) + buf = xrealloc (buf, cap *= 2); + + buf[len++] = c; + if (len >= BRACK_PASTE_SLEN && c == BRACK_PASTE_LAST && + STREQN (buf + len - BRACK_PASTE_SLEN, BRACK_PASTE_SUFF, BRACK_PASTE_SLEN)) + { + len -= BRACK_PASTE_SLEN; + break; + } + } + RL_UNSETSTATE (RL_STATE_MOREINPUT); + + if (c >= 0) + { + if (len == cap) + buf = xrealloc (buf, cap + 1); + buf[len] = '\0'; + } + + if (lenp) + *lenp = len; + return (buf); +} + +/* Having read the special escape sequence denoting the beginning of a + `bracketed paste' sequence, read the rest of the pasted input until the + closing sequence and insert the pasted text as a single unit without + interpretation. Temporarily highlight the inserted text. */ +int +rl_bracketed_paste_begin (int count, int key) +{ + int retval, c; + size_t len, cap; + char *buf; + + buf = _rl_bracketed_text (&len); + rl_mark = rl_point; + retval = rl_insert_text (buf) == len ? 0 : 1; + if (_rl_enable_active_region) + rl_activate_mark (); + + xfree (buf); + return (retval); +} + +int +_rl_read_bracketed_paste_prefix (int c) +{ + char pbuf[BRACK_PASTE_SLEN+1], *pbpref; + int key, ind, j; + + pbpref = BRACK_PASTE_PREF; /* XXX - debugging */ + if (c != pbpref[0]) + return (0); + pbuf[ind = 0] = c; + while (ind < BRACK_PASTE_SLEN-1 && + (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) && + _rl_pushed_input_available () == 0 && + _rl_input_queued (0)) + { + key = rl_read_key (); /* XXX - for now */ + if (key < 0) + break; + pbuf[++ind] = key; + if (pbuf[ind] != pbpref[ind]) + break; + } + + if (ind < BRACK_PASTE_SLEN-1) /* read incomplete sequence */ + { + while (ind >= 0) + _rl_unget_char (pbuf[ind--]); + return (key < 0 ? key : 0); + } + return (key < 0 ? key : 1); +} + +/* Get a character from wherever we read input, handling input in bracketed + paste mode. If we don't have or use bracketed paste mode, this can be + used in place of rl_read_key(). */ +int +_rl_bracketed_read_key () +{ + int c, r; + char *pbuf; + size_t pblen; + + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (c < 0) + return -1; + + /* read pasted data with bracketed-paste mode enabled. */ + if (_rl_enable_bracketed_paste && c == ESC && (r = _rl_read_bracketed_paste_prefix (c)) == 1) + { + pbuf = _rl_bracketed_text (&pblen); + if (pblen == 0) + { + xfree (pbuf); + return 0; /* XXX */ + } + c = (unsigned char)pbuf[0]; + if (pblen > 1) + { + while (--pblen > 0) + _rl_unget_char ((unsigned char)pbuf[pblen]); + } + xfree (pbuf); + } + + return c; +} + +/* Get a character from wherever we read input, handling input in bracketed + paste mode. If we don't have or use bracketed paste mode, this can be + used in place of rl_read_key(). */ +int +_rl_bracketed_read_mbstring (char *mb, int mlen) +{ + int c, r; + + c = _rl_bracketed_read_key (); + if (c < 0) + return -1; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + c = _rl_read_mbstring (c, mb, mlen); + else +#endif + mb[0] = c; + mb[mlen] = '\0'; /* just in case */ + + return c; +} + +/* A special paste command for Windows users. */ +#if defined (_WIN32) +#include <windows.h> + +int +rl_paste_from_clipboard (int count, int key) +{ + char *data, *ptr; + int len; + + if (OpenClipboard (NULL) == 0) + return (0); + + data = (char *)GetClipboardData (CF_TEXT); + if (data) + { + ptr = strchr (data, '\r'); + if (ptr) + { + len = ptr - data; + ptr = (char *)xmalloc (len + 1); + ptr[len] = '\0'; + strncpy (ptr, data, len); + } + else + ptr = data; + _rl_set_mark_at_pos (rl_point); + rl_insert_text (ptr); + if (ptr != data) + xfree (ptr); + CloseClipboard (); + } + return (0); +} +#endif /* _WIN32 */ diff --git a/lib/readline/macro.c b/lib/readline/macro.c new file mode 100644 index 0000000..231a24b --- /dev/null +++ b/lib/readline/macro.c @@ -0,0 +1,334 @@ +/* macro.c -- keyboard macros for readline. */ + +/* Copyright (C) 1994-2009,2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.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> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#define MAX_MACRO_LEVEL 16 + +/* **************************************************************** */ +/* */ +/* Hacking Keyboard Macros */ +/* */ +/* **************************************************************** */ + +/* The currently executing macro string. If this is non-zero, + then it is a malloc ()'ed string where input is coming from. */ +char *rl_executing_macro = (char *)NULL; + +/* The offset in the above string to the next character to be read. */ +static int executing_macro_index; + +/* The current macro string being built. Characters get stuffed + in here by add_macro_char (). */ +static char *current_macro = (char *)NULL; + +/* The size of the buffer allocated to current_macro. */ +static int current_macro_size; + +/* The index at which characters are being added to current_macro. */ +static int current_macro_index; + +/* A structure used to save nested macro strings. + It is a linked list of string/index for each saved macro. */ +struct saved_macro { + struct saved_macro *next; + char *string; + int sindex; +}; + +/* The list of saved macros. */ +static struct saved_macro *macro_list = (struct saved_macro *)NULL; + +static int macro_level = 0; + +/* Set up to read subsequent input from STRING. + STRING is free ()'ed when we are done with it. */ +void +_rl_with_macro_input (char *string) +{ + if (macro_level > MAX_MACRO_LEVEL) + { + _rl_errmsg ("maximum macro execution nesting level exceeded"); + _rl_abort_internal (); + return; + } + +#if 0 + if (rl_executing_macro) /* XXX - later */ +#endif + _rl_push_executing_macro (); + rl_executing_macro = string; + executing_macro_index = 0; + RL_SETSTATE(RL_STATE_MACROINPUT); +} + +/* Return the next character available from a macro, or 0 if + there are no macro characters. */ +int +_rl_next_macro_key (void) +{ + int c; + + if (rl_executing_macro == 0) + return (0); + + if (rl_executing_macro[executing_macro_index] == 0) + { + _rl_pop_executing_macro (); + return (_rl_next_macro_key ()); + } + +#if defined (READLINE_CALLBACKS) + c = rl_executing_macro[executing_macro_index++]; + if (RL_ISSTATE (RL_STATE_CALLBACK) && RL_ISSTATE (RL_STATE_READCMD|RL_STATE_MOREINPUT) && rl_executing_macro[executing_macro_index] == 0) + _rl_pop_executing_macro (); + return c; +#else + /* XXX - consider doing the same as the callback code, just not testing + whether we're running in callback mode */ + return (rl_executing_macro[executing_macro_index++]); +#endif +} + +int +_rl_peek_macro_key (void) +{ + if (rl_executing_macro == 0) + return (0); + if (rl_executing_macro[executing_macro_index] == 0 && (macro_list == 0 || macro_list->string == 0)) + return (0); + if (rl_executing_macro[executing_macro_index] == 0 && macro_list && macro_list->string) + return (macro_list->string[0]); + return (rl_executing_macro[executing_macro_index]); +} + +int +_rl_prev_macro_key (void) +{ + if (rl_executing_macro == 0) + return (0); + + if (executing_macro_index == 0) + return (0); + + executing_macro_index--; + return (rl_executing_macro[executing_macro_index]); +} + +/* Save the currently executing macro on a stack of saved macros. */ +void +_rl_push_executing_macro (void) +{ + struct saved_macro *saver; + + saver = (struct saved_macro *)xmalloc (sizeof (struct saved_macro)); + saver->next = macro_list; + saver->sindex = executing_macro_index; + saver->string = rl_executing_macro; + + macro_list = saver; + + macro_level++; +} + +/* Discard the current macro, replacing it with the one + on the top of the stack of saved macros. */ +void +_rl_pop_executing_macro (void) +{ + struct saved_macro *macro; + + FREE (rl_executing_macro); + rl_executing_macro = (char *)NULL; + executing_macro_index = 0; + + if (macro_list) + { + macro = macro_list; + rl_executing_macro = macro_list->string; + executing_macro_index = macro_list->sindex; + macro_list = macro_list->next; + xfree (macro); + } + + macro_level--; + + if (rl_executing_macro == 0) + RL_UNSETSTATE(RL_STATE_MACROINPUT); +} + +/* Add a character to the macro being built. */ +void +_rl_add_macro_char (int c) +{ + if (current_macro_index + 1 >= current_macro_size) + { + if (current_macro == 0) + current_macro = (char *)xmalloc (current_macro_size = 25); + else + current_macro = (char *)xrealloc (current_macro, current_macro_size += 25); + } + + current_macro[current_macro_index++] = c; + current_macro[current_macro_index] = '\0'; +} + +void +_rl_kill_kbd_macro (void) +{ + if (current_macro) + { + xfree (current_macro); + current_macro = (char *) NULL; + } + current_macro_size = current_macro_index = 0; + + FREE (rl_executing_macro); + rl_executing_macro = (char *) NULL; + executing_macro_index = 0; + + RL_UNSETSTATE(RL_STATE_MACRODEF); +} + +/* Begin defining a keyboard macro. + Keystrokes are recorded as they are executed. + End the definition with rl_end_kbd_macro (). + If a numeric argument was explicitly typed, then append this + definition to the end of the existing macro, and start by + re-executing the existing macro. */ +int +rl_start_kbd_macro (int ignore1, int ignore2) +{ + if (RL_ISSTATE (RL_STATE_MACRODEF)) + { + _rl_abort_internal (); + return 1; + } + + if (rl_explicit_arg) + { + if (current_macro) + _rl_with_macro_input (savestring (current_macro)); + } + else + current_macro_index = 0; + + RL_SETSTATE(RL_STATE_MACRODEF); + return 0; +} + +/* Stop defining a keyboard macro. + A numeric argument says to execute the macro right now, + that many times, counting the definition as the first time. */ +int +rl_end_kbd_macro (int count, int ignore) +{ + if (RL_ISSTATE (RL_STATE_MACRODEF) == 0) + { + _rl_abort_internal (); + return 1; + } + + current_macro_index -= rl_key_sequence_length; + if (current_macro_index < 0) + current_macro_index = 0; + current_macro[current_macro_index] = '\0'; + + RL_UNSETSTATE(RL_STATE_MACRODEF); + + return (rl_call_last_kbd_macro (--count, 0)); +} + +/* Execute the most recently defined keyboard macro. + COUNT says how many times to execute it. */ +int +rl_call_last_kbd_macro (int count, int ignore) +{ + if (current_macro == 0) + _rl_abort_internal (); + + if (RL_ISSTATE (RL_STATE_MACRODEF)) + { + rl_ding (); /* no recursive macros */ + current_macro[--current_macro_index] = '\0'; /* erase this char */ + return 0; + } + + while (count--) + _rl_with_macro_input (savestring (current_macro)); + return 0; +} + +int +rl_print_last_kbd_macro (int count, int ignore) +{ + char *m; + + if (current_macro == 0) + { + rl_ding (); + return 0; + } + m = _rl_untranslate_macro_value (current_macro, 1); + rl_crlf (); + printf ("%s", m); + fflush (stdout); + rl_crlf (); + FREE (m); + rl_forced_update_display (); + rl_display_fixed = 1; + + return 0; +} + +void +rl_push_macro_input (char *macro) +{ + _rl_with_macro_input (macro); +} diff --git a/lib/readline/mbutil.c b/lib/readline/mbutil.c new file mode 100644 index 0000000..47e9100 --- /dev/null +++ b/lib/readline/mbutil.c @@ -0,0 +1,524 @@ +/* mbutil.c -- readline multibyte character utility functions */ + +/* Copyright (C) 2001-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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 + +/* Ditto */ +int _rl_utf8locale = 0; + +/* **************************************************************** */ +/* */ +/* Multibyte Character Utility Functions */ +/* */ +/* **************************************************************** */ + +#if defined(HANDLE_MULTIBYTE) + +/* **************************************************************** */ +/* */ +/* UTF-8 specific Character Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return the length in bytes of the possibly-multibyte character beginning + at S. Encoding is UTF-8. */ +static int +_rl_utf8_mblen (const char *s, size_t n) +{ + unsigned char c, c1, c2, c3; + + if (s == 0) + return (0); /* no shift states */ + if (n <= 0) + return (-1); + + c = (unsigned char)*s; + if (c < 0x80) + return (c != 0); + if (c >= 0xc2) + { + c1 = (unsigned char)s[1]; + if (c < 0xe0) + { + if (n == 1) + return -2; + if (n >= 2 && (c1 ^ 0x80) < 0x40) + return 2; + } + else if (c < 0xf0) + { + if (n == 1) + return -2; + if ((c1 ^ 0x80) < 0x40 + && (c >= 0xe1 || c1 >= 0xa0) + && (c != 0xed || c1 < 0xa0)) + { + if (n == 2) + return -2; + c2 = (unsigned char)s[2]; + if ((c2 ^ 0x80) < 0x40) + return 3; + } + } + else if (c < 0xf4) + { + if (n == 1) + return -2; + if (((c1 ^ 0x80) < 0x40) + && (c >= 0xf1 || c1 >= 0x90) + && (c < 0xf4 || (c == 0xf4 && c1 < 0x90))) + { + if (n == 2) + return -2; + c2 = (unsigned char)s[2]; + if ((c2 ^ 0x80) < 0x40) + { + if (n == 3) + return -2; + c3 = (unsigned char)s[3]; + if ((c3 ^ 0x80) < 0x40) + return 4; + } + } + } + } + /* invalid or incomplete multibyte character */ + return -1; +} + +static int +_rl_find_next_mbchar_internal (char *string, int seed, int count, int find_non_zero) +{ + size_t tmp, len; + mbstate_t ps; + int point; + WCHAR_T wc; + + tmp = 0; + + 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 _rl_adjust_point returns -1, the character or string is invalid. + treat as a byte. */ + if (point == seed - 1) /* invalid */ + return seed + 1; + + /* if this is true, means that seed was not pointing to a byte indicating + the beginning of a multibyte character. Correct the point and consume + one char. */ + if (seed < point) + count--; + + while (count > 0) + { + len = strlen (string + point); + if (len == 0) + break; + if (_rl_utf8locale && UTF8_SINGLEBYTE(string[point])) + { + tmp = 1; + wc = (WCHAR_T) string[point]; + memset(&ps, 0, sizeof(mbstate_t)); + } + else + tmp = MBRTOWC (&wc, string+point, len, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* invalid bytes. assume a byte represents a character */ + point++; + count--; + /* reset states. */ + memset(&ps, 0, sizeof(mbstate_t)); + } + else if (MB_NULLWCH (tmp)) + break; /* found wide '\0' */ + 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 (MB_NULLWCH (tmp) == 0 && MB_INVALIDCH (tmp) == 0 && WCWIDTH (wc) == 0) + { + point += tmp; + tmp = MBRTOWC (&wc, string + point, strlen (string + point), &ps); + } + } + + return point; +} + +static inline int +_rl_test_nonzero (char *string, int ind, int len) +{ + size_t tmp; + WCHAR_T wc; + mbstate_t ps; + + memset (&ps, 0, sizeof (mbstate_t)); + tmp = MBRTOWC (&wc, string + ind, len - ind, &ps); + /* treat invalid multibyte sequences as non-zero-width */ + return (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp) || WCWIDTH (wc) > 0); +} + +/* experimental -- needs to handle zero-width characters better */ +static int +_rl_find_prev_utf8char (char *string, int seed, int find_non_zero) +{ + char *s; + unsigned char b; + int save, prev; + size_t len; + + if (find_non_zero) + len = RL_STRLEN (string); + + prev = seed - 1; + while (prev >= 0) + { + b = (unsigned char)string[prev]; + if (UTF8_SINGLEBYTE (b)) + return (prev); + + save = prev; + + /* Move back until we're not in the middle of a multibyte char */ + if (UTF8_MBCHAR (b)) + { + while (prev > 0 && (b = (unsigned char)string[--prev]) && UTF8_MBCHAR (b)) + ; + } + + if (UTF8_MBFIRSTCHAR (b)) + { + if (find_non_zero) + { + if (_rl_test_nonzero (string, prev, len)) + return (prev); + else /* valid but WCWIDTH (wc) == 0 */ + prev = prev - 1; + } + else + return (prev); + } + else + return (save); /* invalid utf-8 multibyte sequence */ + } + + return ((prev < 0) ? 0 : prev); +} + +/*static*/ int +_rl_find_prev_mbchar_internal (char *string, int seed, int find_non_zero) +{ + mbstate_t ps; + int prev, non_zero_prev, point, length; + size_t tmp; + WCHAR_T wc; + + if (_rl_utf8locale) + return (_rl_find_prev_utf8char (string, seed, find_non_zero)); + + 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) + { + if (_rl_utf8locale && UTF8_SINGLEBYTE(string[point])) + { + tmp = 1; + wc = (WCHAR_T) string[point]; + memset(&ps, 0, sizeof(mbstate_t)); + } + else + tmp = MBRTOWC (&wc, string + point, length - point, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* in this case, bytes are invalid or too short 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)); + + /* Since we're assuming that this byte represents a single + non-zero-width character, don't forget about it. */ + prev = point; + } + else if (MB_NULLWCH (tmp)) + 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 (char *src, mbstate_t *ps) +{ + size_t tmp, l; + int mb_cur_max; + + /* Look at no more than MB_CUR_MAX characters */ + l = (size_t)strlen (src); + if (_rl_utf8locale && l > 0 && UTF8_SINGLEBYTE(*src)) + tmp = (*src != 0) ? 1 : 0; + else + { + mb_cur_max = MB_CUR_MAX; + tmp = mbrlen((const char *)src, (l < mb_cur_max) ? l : mb_cur_max, ps); + } + if (tmp == (size_t)(-2)) + { + /* too short to compose multibyte char */ + if (ps) + memset (ps, 0, sizeof(mbstate_t)); + return -2; + } + else if (tmp == (size_t)(-1)) + { + /* invalid to compose multibyte char */ + /* initialize the conversion state */ + if (ps) + 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 (char *buf1, int pos1, mbstate_t *ps1, char *buf2, int pos2, mbstate_t *ps2) +{ + 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 invalid (point < 0 || more than string length), + it returns -1 */ +int +_rl_adjust_point (char *string, int point, mbstate_t *ps) +{ + size_t tmp; + int length, pos; + + tmp = 0; + pos = 0; + length = strlen(string); + if (point < 0) + return -1; + if (length < point) + return -1; + + while (pos < point) + { + if (_rl_utf8locale && UTF8_SINGLEBYTE(string[pos])) + tmp = 1; + else + tmp = mbrlen (string + pos, length - pos, ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* in this case, bytes are invalid or too short 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 */ + if (ps) + memset (ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tmp)) + pos++; + else + pos += tmp; + } + + return (pos - point); +} + +int +_rl_is_mbchar_matched (char *string, int seed, int 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; +} + +WCHAR_T +_rl_char_value (char *buf, int ind) +{ + size_t tmp; + WCHAR_T wc; + mbstate_t ps; + int l; + + if (MB_LEN_MAX == 1 || rl_byte_oriented) + return ((WCHAR_T) buf[ind]); + if (_rl_utf8locale && UTF8_SINGLEBYTE(buf[ind])) + return ((WCHAR_T) buf[ind]); + l = strlen (buf); + if (ind >= l - 1) + return ((WCHAR_T) buf[ind]); + if (l < ind) /* Sanity check */ + l = strlen (buf+ind); + memset (&ps, 0, sizeof (mbstate_t)); + tmp = MBRTOWC (&wc, buf + ind, l - ind, &ps); + if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp)) + return ((WCHAR_T) buf[ind]); + return wc; +} +#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 (char *string, int seed, int count, int 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 (char *string, int seed, int flags) +{ +#if defined (HANDLE_MULTIBYTE) + return _rl_find_prev_mbchar_internal (string, seed, flags); +#else + return ((seed == 0) ? seed : seed - 1); +#endif +} diff --git a/lib/readline/misc.c b/lib/readline/misc.c new file mode 100644 index 0000000..4bca9c5 --- /dev/null +++ b/lib/readline/misc.c @@ -0,0 +1,781 @@ +/* misc.c -- miscellaneous bindable readline functions. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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 "histlib.h" +#include "rlshell.h" +#include "xmalloc.h" + +static int rl_digit_loop (void); +static void _rl_history_set_point (void); + +/* 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; + +_rl_arg_cxt _rl_argcxt; + +/* 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 */ +/* */ +/* **************************************************************** */ + +int +_rl_arg_overflow (void) +{ + if (rl_numeric_arg > 1000000) + { + _rl_argcxt = 0; + rl_explicit_arg = rl_numeric_arg = 0; + rl_ding (); + rl_restore_prompt (); + rl_clear_message (); + RL_UNSETSTATE(RL_STATE_NUMERICARG); + return 1; + } + return 0; +} + +void +_rl_arg_init (void) +{ + rl_save_prompt (); + _rl_argcxt = 0; + RL_SETSTATE(RL_STATE_NUMERICARG); +} + +int +_rl_arg_getchar (void) +{ + int c; + + rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg); + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + return c; +} + +/* Process C as part of the current numeric argument. Return -1 if the + argument should be aborted, 0 if we should not read any more chars, and + 1 if we should continue to read chars. */ +int +_rl_arg_dispatch (_rl_arg_cxt cxt, int c) +{ + int key, r; + + key = c; + + /* If we see a key bound to `universal-argument' after seeing digits, + it ends the argument but is otherwise ignored. */ + if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument) + { + if ((cxt & NUM_SAWDIGITS) == 0) + { + rl_numeric_arg *= 4; + return 1; + } + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_argcxt |= NUM_READONE; + return 0; /* XXX */ + } + else + { + key = _rl_bracketed_read_key (); + rl_restore_prompt (); + rl_clear_message (); + RL_UNSETSTATE(RL_STATE_NUMERICARG); + if (key < 0) + return -1; + return (_rl_dispatch (key, _rl_keymap)); + } + } + + c = UNMETA (c); + + if (_rl_digit_p (c)) + { + r = _rl_digit_value (c); + rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + r : r; + rl_explicit_arg = 1; + _rl_argcxt |= NUM_SAWDIGITS; + } + else if (c == '-' && rl_explicit_arg == 0) + { + rl_numeric_arg = 1; + _rl_argcxt |= NUM_SAWMINUS; + rl_arg_sign = -1; + } + else + { + /* Make M-- command equivalent to M--1 command. */ + if ((_rl_argcxt & NUM_SAWMINUS) && rl_numeric_arg == 1 && rl_explicit_arg == 0) + rl_explicit_arg = 1; + rl_restore_prompt (); + rl_clear_message (); + RL_UNSETSTATE(RL_STATE_NUMERICARG); + + r = _rl_dispatch (key, _rl_keymap); + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + /* At worst, this will cause an extra redisplay. Otherwise, + we have to wait until the next character comes in. */ + if (rl_done == 0) + (*rl_redisplay_function) (); + r = 0; + } + return r; + } + + return 1; +} + +/* Handle C-u style numeric args, as well as M--, and M-digits. */ +static int +rl_digit_loop (void) +{ + int c, r; + + while (1) + { + if (_rl_arg_overflow ()) + return 1; + + c = _rl_arg_getchar (); + + if (c < 0) + { + _rl_abort_internal (); + return -1; + } + + r = _rl_arg_dispatch (_rl_argcxt, c); + if (r <= 0 || (RL_ISSTATE (RL_STATE_NUMERICARG) == 0)) + break; + } + + return r; +} + +/* Create a default argument. */ +void +_rl_reset_argument (void) +{ + rl_numeric_arg = rl_arg_sign = 1; + rl_explicit_arg = 0; + _rl_argcxt = 0; +} + +/* Start a numeric argument with initial value KEY */ +int +rl_digit_argument (int ignore, int key) +{ + _rl_arg_init (); + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_arg_dispatch (_rl_argcxt, key); + rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg); + return 0; + } + else + { + rl_execute_next (key); + return (rl_digit_loop ()); + } +} + +/* 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 (int count, int key) +{ + _rl_arg_init (); + rl_numeric_arg *= 4; + + return (RL_ISSTATE (RL_STATE_CALLBACK) ? 0 : rl_digit_loop ()); +} + +int +_rl_arg_callback (_rl_arg_cxt cxt) +{ + int c, r; + + c = _rl_arg_getchar (); + if (c < 0) + return (1); /* EOF */ + + if (_rl_argcxt & NUM_READONE) + { + _rl_argcxt &= ~NUM_READONE; + rl_restore_prompt (); + rl_clear_message (); + RL_UNSETSTATE(RL_STATE_NUMERICARG); + rl_execute_next (c); + return 0; + } + + r = _rl_arg_dispatch (cxt, c); + if (r > 0) + rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg); + return (r != 1); +} + +/* What to do when you abort reading an argument. */ +int +rl_discard_argument (void) +{ + rl_ding (); + rl_clear_message (); + _rl_reset_argument (); + + return 0; +} + +/* **************************************************************** */ +/* */ +/* 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 (void) +{ + using_history (); + if (_rl_saved_line_for_history) + _rl_free_saved_history_line (); + _rl_saved_line_for_history = (HIST_ENTRY *)NULL; + _rl_history_search_pos = -99; /* some random invalid history position */ +} + +/* Free the contents (and containing structure) of a HIST_ENTRY. */ +void +_rl_free_history_entry (HIST_ENTRY *entry) +{ + if (entry == 0) + return; + + FREE (entry->line); + FREE (entry->timestamp); + + xfree (entry); +} + +/* Perhaps put back the current line if it has changed. */ +int +rl_maybe_replace_line (void) +{ + 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); + xfree (temp->line); + FREE (temp->timestamp); + xfree (temp); + } + return 0; +} + +/* Restore the _rl_saved_line_for_history if there is one. */ +int +rl_maybe_unsave_line (void) +{ + if (_rl_saved_line_for_history) + { + /* Can't call with `1' because rl_undo_list might point to an undo + list from a history entry, as in rl_replace_from_history() below. */ + rl_replace_line (_rl_saved_line_for_history->line, 0); + rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data; + + /* Doesn't free `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 (void) +{ + 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->timestamp = (char *)NULL; + _rl_saved_line_for_history->data = (char *)rl_undo_list; + } + + return 0; +} + +int +_rl_free_saved_history_line (void) +{ + UNDO_LIST *orig; + + if (_rl_saved_line_for_history) + { + if (rl_undo_list && rl_undo_list == (UNDO_LIST *)_rl_saved_line_for_history->data) + rl_undo_list = 0; + /* Have to free this separately because _rl_free_history entry can't: + it doesn't know whether or not this has application data. Only the + callers that know this is _rl_saved_line_for_history can know that + it's an undo list. */ + if (_rl_saved_line_for_history->data) + _rl_free_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; + } + return 0; +} + +static void +_rl_history_set_point (void) +{ + 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_keymap != vi_insertion_keymap) + 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 (HIST_ENTRY *entry, int flags) +{ + /* Can't call with `1' because rl_undo_list might point to an undo list + from a history entry, just like we're setting up here. */ + 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 +} + +/* Process and free undo lists attached to each history entry prior to the + current entry, inclusive, reverting each line to its saved state. This + is destructive, and state about the current line is lost. This is not + intended to be called while actively editing, and the current line is + not assumed to have been added to the history list. */ +void +_rl_revert_previous_lines (void) +{ + int hpos; + HIST_ENTRY *entry; + UNDO_LIST *ul, *saved_undo_list; + char *lbuf; + + lbuf = savestring (rl_line_buffer); + saved_undo_list = rl_undo_list; + hpos = where_history (); + + entry = (hpos == history_length) ? previous_history () : current_history (); + while (entry) + { + if (ul = (UNDO_LIST *)entry->data) + { + if (ul == saved_undo_list) + saved_undo_list = 0; + /* Set up rl_line_buffer and other variables from history entry */ + rl_replace_from_history (entry, 0); /* entry->line is now current */ + entry->data = 0; /* entry->data is now current undo list */ + /* Undo all changes to this history entry */ + while (rl_undo_list) + rl_do_undo (); + /* And copy the reverted line back to the history entry, preserving + the timestamp. */ + FREE (entry->line); + entry->line = savestring (rl_line_buffer); + } + entry = previous_history (); + } + + /* Restore history state */ + rl_undo_list = saved_undo_list; /* may have been set to null */ + history_set_pos (hpos); + + /* reset the line buffer */ + rl_replace_line (lbuf, 0); + _rl_set_the_line (); + + /* and clean up */ + xfree (lbuf); +} + +/* Revert all lines in the history by making sure we are at the end of the + history before calling _rl_revert_previous_lines() */ +void +_rl_revert_all_lines (void) +{ + int pos; + + pos = where_history (); + using_history (); + _rl_revert_previous_lines (); + history_set_pos (pos); +} + +/* Free the history list, including private readline data and take care + of pointer aliases to history data. Resets rl_undo_list if it points + to an UNDO_LIST * saved as some history entry's data member. This + should not be called while editing is active. */ +void +rl_clear_history (void) +{ + HIST_ENTRY **hlist, *hent; + register int i; + UNDO_LIST *ul, *saved_undo_list; + + saved_undo_list = rl_undo_list; + hlist = history_list (); /* direct pointer, not copy */ + + for (i = 0; i < history_length; i++) + { + hent = hlist[i]; + if (ul = (UNDO_LIST *)hent->data) + { + if (ul == saved_undo_list) + saved_undo_list = 0; + _rl_free_undo_list (ul); + hent->data = 0; + } + _rl_free_history_entry (hent); + } + + history_offset = history_length = 0; + rl_undo_list = saved_undo_list; /* should be NULL */ +} + +/* **************************************************************** */ +/* */ +/* History Commands */ +/* */ +/* **************************************************************** */ + +/* Meta-< goes to the start of the history. */ +int +rl_beginning_of_history (int count, int 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 (int count, int 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 (int count, int 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 (int count, int key) +{ + HIST_ENTRY *old_temp, *temp; + int had_saved_line; + + if (count < 0) + return (rl_get_next_history (-count, key)); + + if (count == 0 || history_list () == 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. */ + had_saved_line = _rl_saved_line_for_history != 0; + 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) + { + if (had_saved_line == 0) + _rl_free_saved_history_line (); + rl_ding (); + } + else + { + rl_replace_from_history (temp, 0); + _rl_history_set_point (); + } + + return 0; +} + +/* With an argument, move back that many history lines, else move to the + beginning of history. */ +int +rl_fetch_history (int count, int c) +{ + int wanted, nhist; + + /* Giving an argument of n means we want the nth command in the history + file. The command number is interpreted the same way that the bash + `history' command does it -- that is, giving an argument count of 450 + to this command would get the command listed as number 450 in the + output of `history'. */ + if (rl_explicit_arg) + { + nhist = history_base + where_history (); + /* Negative arguments count back from the end of the history list. */ + wanted = (count >= 0) ? nhist - count : -count; + + if (wanted <= 0 || wanted >= nhist) + { + /* In vi mode, we don't change the line with an out-of-range + argument, as for the `G' command. */ + if (rl_editing_mode == vi_mode) + rl_ding (); + else + rl_beginning_of_history (0, 0); + } + else + rl_get_previous_history (wanted, c); + } + else + rl_beginning_of_history (count, 0); + + return (0); +} + +/* The equivalent of the Korn shell C-o operate-and-get-next-history-line + editing command. */ + +/* This could stand to be global to the readline library */ +static rl_hook_func_t *_rl_saved_internal_startup_hook = 0; +static int saved_history_logical_offset = -1; + +#define HISTORY_FULL() (history_is_stifled () && history_length >= history_max_entries) + +static int +set_saved_history () +{ + int absolute_offset, count; + + if (saved_history_logical_offset >= 0) + { + absolute_offset = saved_history_logical_offset - history_base; + count = where_history () - absolute_offset; + rl_get_previous_history (count, 0); + } + saved_history_logical_offset = -1; + _rl_internal_startup_hook = _rl_saved_internal_startup_hook; + + return (0); +} + +int +rl_operate_and_get_next (int count, int c) +{ + /* Accept the current line. */ + rl_newline (1, c); + + saved_history_logical_offset = rl_explicit_arg ? count : where_history () + history_base + 1; + + _rl_saved_internal_startup_hook = _rl_internal_startup_hook; + _rl_internal_startup_hook = set_saved_history; + + return 0; +} + +/* **************************************************************** */ +/* */ +/* Editing Modes */ +/* */ +/* **************************************************************** */ +/* How to toggle back and forth between editing modes. */ +int +rl_vi_editing_mode (int count, int 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_insert_mode (1, key); +#endif /* VI_MODE */ + + return 0; +} + +int +rl_emacs_editing_mode (int count, int 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; + + if (_rl_show_mode_in_prompt) + _rl_reset_prompt (); + + return 0; +} + +/* Function for the rest of the library to use to set insert/overwrite mode. */ +void +_rl_set_insert_mode (int im, int 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 (int count, int 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; +} diff --git a/lib/readline/nls.c b/lib/readline/nls.c new file mode 100644 index 0000000..8c027d6 --- /dev/null +++ b/lib/readline/nls.c @@ -0,0 +1,348 @@ +/* nls.c -- skeletal internationalization code. */ + +/* Copyright (C) 1996-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_LOCALE_H) +# include <locale.h> +#endif + +#if defined (HAVE_LANGINFO_CODESET) +# include <langinfo.h> +#endif + +#include <ctype.h> + +#include "rldefs.h" +#include "readline.h" +#include "rlshell.h" +#include "rlprivate.h" +#include "xmalloc.h" + +static int utf8locale (char *); + +#define RL_DEFAULT_LOCALE "C" +static char *_rl_current_locale = 0; + +#if !defined (HAVE_SETLOCALE) +/* A list of legal values for the LANG or LC_CTYPE environment variables. + If a locale name in this list is the value for the LC_ALL, LC_CTYPE, + or LANG environment variable (using the first of those with a value), + readline eight-bit mode is enabled. */ +static char *legal_lang_values[] = +{ + "iso88591", + "iso88592", + "iso88593", + "iso88594", + "iso88595", + "iso88596", + "iso88597", + "iso88598", + "iso88599", + "iso885910", + "koi8r", + "utf8", + 0 +}; + +static char *normalize_codeset (char *); +#endif /* !HAVE_SETLOCALE */ + +static char *find_codeset (char *, size_t *); + +static char *_rl_get_locale_var (const char *); + +static char * +_rl_get_locale_var (const char *v) +{ + char *lspec; + + lspec = sh_get_env_value ("LC_ALL"); + if (lspec == 0 || *lspec == 0) + lspec = sh_get_env_value (v); + if (lspec == 0 || *lspec == 0) + lspec = sh_get_env_value ("LANG"); + + return lspec; +} + +static int +utf8locale (char *lspec) +{ + char *cp; + size_t len; + +#if HAVE_LANGINFO_CODESET + cp = nl_langinfo (CODESET); + return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8")); +#else + cp = find_codeset (lspec, &len); + + if (cp == 0 || len < 4 || len > 5) + return 0; + return ((len == 5) ? strncmp (cp, "UTF-8", len) == 0 : strncmp (cp, "utf8", 4) == 0); +#endif +} + +/* Query the right environment variables and call setlocale() to initialize + the C library locale settings. */ +char * +_rl_init_locale (void) +{ + char *ret, *lspec; + + /* Set the LC_CTYPE locale category from environment variables. */ + lspec = _rl_get_locale_var ("LC_CTYPE"); + /* Since _rl_get_locale_var queries the right environment variables, + we query the current locale settings with setlocale(), and, if + that doesn't return anything, we set lspec to the empty string to + force the subsequent call to setlocale() to define the `native' + environment. */ +#if defined (HAVE_SETLOCALE) + if (lspec == 0 || *lspec == 0) + lspec = setlocale (LC_CTYPE, (char *)NULL); + if (lspec == 0) + lspec = ""; + ret = setlocale (LC_CTYPE, lspec); /* ok, since it does not change locale */ + if (ret == 0 || *ret == 0) + ret = setlocale (LC_CTYPE, (char *)NULL); + if (ret == 0 || *ret == 0) + ret = RL_DEFAULT_LOCALE; +#else + ret = (lspec == 0 || *lspec == 0) ? RL_DEFAULT_LOCALE : lspec; +#endif + + _rl_utf8locale = (ret && *ret) ? utf8locale (ret) : 0; + + _rl_current_locale = savestring (ret); + return ret; +} + +/* If we have setlocale(3), just check the current LC_CTYPE category + value (passed as LOCALESTR), and go into eight-bit mode if it's not "C" + or "POSIX". If FORCE is non-zero, we reset the locale variables to values + appropriate for the C locale if the locale is "C" or "POSIX". FORCE is 0 + when this is called from _rl_init_eightbit, since we're modifying the + default initial values and don't need to change anything else. If we + don't have setlocale(3), we check the codeset portion of LOCALESTR against + a set of known values and go into eight-bit mode if it matches one of those. + Returns 1 if we set eight-bit (multibyte) mode. */ +static int +_rl_set_localevars (char *localestr, int force) +{ +#if defined (HAVE_SETLOCALE) + if (localestr && *localestr && (localestr[0] != 'C' || localestr[1]) && (STREQ (localestr, "POSIX") == 0)) + { + _rl_meta_flag = 1; + _rl_convert_meta_chars_to_ascii = 0; + _rl_output_meta_chars = 1; + return (1); + } + else if (force) + { + /* Default "C" locale settings. */ + _rl_meta_flag = 0; + _rl_convert_meta_chars_to_ascii = 1; + _rl_output_meta_chars = 0; + return (0); + } + else + return (0); + +#else /* !HAVE_SETLOCALE */ + char *t; + int i; + + /* We don't have setlocale. Finesse it. Check the environment for the + appropriate variables and set eight-bit mode if they have the right + values. */ + if (localestr == 0 || (t = normalize_codeset (localestr)) == 0) + return (0); + for (i = 0; t && legal_lang_values[i]; i++) + if (STREQ (t, legal_lang_values[i])) + { + _rl_meta_flag = 1; + _rl_convert_meta_chars_to_ascii = 0; + _rl_output_meta_chars = 1; + break; + } + + if (force && legal_lang_values[i] == 0) /* didn't find it */ + { + /* Default "C" locale settings. */ + _rl_meta_flag = 0; + _rl_convert_meta_chars_to_ascii = 1; + _rl_output_meta_chars = 0; + } + + _rl_utf8locale = *t ? STREQ (t, "utf8") : 0; + + xfree (t); + return (legal_lang_values[i] ? 1 : 0); +#endif /* !HAVE_SETLOCALE */ +} + +/* Check for LC_ALL, LC_CTYPE, and LANG and use the first with a value + to decide the defaults for 8-bit character input and output. Returns + 1 if we set eight-bit mode. */ +int +_rl_init_eightbit (void) +{ + char *t, *ol; + + ol = _rl_current_locale; + t = _rl_init_locale (); /* resets _rl_current_locale, returns static pointer */ + xfree (ol); + + return (_rl_set_localevars (t, 0)); +} + +#if !defined (HAVE_SETLOCALE) +static char * +normalize_codeset (char *codeset) +{ + size_t namelen, i; + int len, all_digits; + char *wp, *retval; + + codeset = find_codeset (codeset, &namelen); + + if (codeset == 0) + return (codeset); + + all_digits = 1; + for (len = 0, i = 0; i < namelen; i++) + { + if (ISALNUM ((unsigned char)codeset[i])) + { + len++; + all_digits &= _rl_digit_p (codeset[i]); + } + } + + retval = (char *)malloc ((all_digits ? 3 : 0) + len + 1); + if (retval == 0) + return ((char *)0); + + wp = retval; + /* Add `iso' to beginning of an all-digit codeset */ + if (all_digits) + { + *wp++ = 'i'; + *wp++ = 's'; + *wp++ = 'o'; + } + + for (i = 0; i < namelen; i++) + if (ISALPHA ((unsigned char)codeset[i])) + *wp++ = _rl_to_lower (codeset[i]); + else if (_rl_digit_p (codeset[i])) + *wp++ = codeset[i]; + *wp = '\0'; + + return retval; +} +#endif /* !HAVE_SETLOCALE */ + +/* Isolate codeset portion of locale specification. */ +static char * +find_codeset (char *name, size_t *lenp) +{ + char *cp, *language, *result; + + cp = language = name; + result = (char *)0; + + while (*cp && *cp != '_' && *cp != '@' && *cp != '+' && *cp != ',') + cp++; + + /* This does not make sense: language has to be specified. As + an exception we allow the variable to contain only the codeset + name. Perhaps there are funny codeset names. */ + if (language == cp) + { + *lenp = strlen (language); + result = language; + } + else + { + /* Next is the territory. */ + if (*cp == '_') + do + ++cp; + while (*cp && *cp != '.' && *cp != '@' && *cp != '+' && *cp != ',' && *cp != '_'); + + /* Now, finally, is the codeset. */ + result = cp; + if (*cp == '.') + do + ++cp; + while (*cp && *cp != '@'); + + if (cp - result > 2) + { + result++; + *lenp = cp - result; + } + else + { + *lenp = strlen (language); + result = language; + } + } + + return result; +} + +void +_rl_reset_locale (void) +{ + char *ol, *nl; + + /* This should not be NULL; _rl_init_eightbit sets it on the first call to + readline() or rl_initialize(). */ + ol = _rl_current_locale; + nl = _rl_init_locale (); /* resets _rl_current_locale */ + + if ((ol == 0 && nl) || (ol && nl && (STREQ (ol, nl) == 0))) + (void)_rl_set_localevars (nl, 1); + + xfree (ol); +} diff --git a/lib/readline/parens.c b/lib/readline/parens.c new file mode 100644 index 0000000..57ce704 --- /dev/null +++ b/lib/readline/parens.c @@ -0,0 +1,184 @@ +/* parens.c -- implementation of matching parentheses feature. */ + +/* Copyright (C) 1987, 1989, 1992-2015, 2017, 2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# include <floss.h> +#endif + +#include "rlconf.h" + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "posixselect.h" + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +#include "readline.h" +#include "rlprivate.h" + +static int find_matching_open (char *, int, int); + +/* Non-zero means try to blink the matching open parenthesis when the + close parenthesis is inserted. */ +int rl_blink_matching_paren = 0; + +static int _paren_blink_usec = 500000; + +/* Change emacs_standard_keymap to have bindings for paren matching when + ON_OR_OFF is 1, change them back to self_insert when ON_OR_OFF == 0. */ +void +_rl_enable_paren_matching (int on_or_off) +{ + if (on_or_off) + { + /* ([{ */ + rl_bind_key_in_map (')', rl_insert_close, emacs_standard_keymap); + rl_bind_key_in_map (']', rl_insert_close, emacs_standard_keymap); + rl_bind_key_in_map ('}', rl_insert_close, emacs_standard_keymap); + +#if defined (VI_MODE) + /* ([{ */ + rl_bind_key_in_map (')', rl_insert_close, vi_insertion_keymap); + rl_bind_key_in_map (']', rl_insert_close, vi_insertion_keymap); + rl_bind_key_in_map ('}', rl_insert_close, vi_insertion_keymap); +#endif + } + else + { + /* ([{ */ + rl_bind_key_in_map (')', rl_insert, emacs_standard_keymap); + rl_bind_key_in_map (']', rl_insert, emacs_standard_keymap); + rl_bind_key_in_map ('}', rl_insert, emacs_standard_keymap); + +#if defined (VI_MODE) + /* ([{ */ + rl_bind_key_in_map (')', rl_insert, vi_insertion_keymap); + rl_bind_key_in_map (']', rl_insert, vi_insertion_keymap); + rl_bind_key_in_map ('}', rl_insert, vi_insertion_keymap); +#endif + } +} + +int +rl_set_paren_blink_timeout (int u) +{ + int o; + + o = _paren_blink_usec; + if (u > 0) + _paren_blink_usec = u; + return (o); +} + +int +rl_insert_close (int count, int invoking_key) +{ + if (rl_explicit_arg || !rl_blink_matching_paren) + _rl_insert_char (count, invoking_key); + else + { +#if defined (HAVE_SELECT) + int orig_point, match_point, ready; + struct timeval timer; + fd_set readfds; + + _rl_insert_char (1, invoking_key); + (*rl_redisplay_function) (); + match_point = + find_matching_open (rl_line_buffer, rl_point - 2, invoking_key); + + /* Emacs might message or ring the bell here, but I don't. */ + if (match_point < 0) + return 1; + + FD_ZERO (&readfds); + FD_SET (fileno (rl_instream), &readfds); + USEC_TO_TIMEVAL (_paren_blink_usec, timer); + + orig_point = rl_point; + rl_point = match_point; + (*rl_redisplay_function) (); +# if defined (RL_TIMEOUT_USE_SELECT) + ready = _rl_timeout_select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer, NULL); +# else + ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); +# endif + rl_point = orig_point; +#else /* !HAVE_SELECT */ + _rl_insert_char (count, invoking_key); +#endif /* !HAVE_SELECT */ + } + return 0; +} + +static int +find_matching_open (char *string, int from, int closer) +{ + register int i; + int opener, level, delimiter; + + switch (closer) + { + case ']': opener = '['; break; + case '}': opener = '{'; break; + case ')': opener = '('; break; + default: + return (-1); + } + + level = 1; /* The closer passed in counts as 1. */ + delimiter = 0; /* Delimited state unknown. */ + + for (i = from; i > -1; i--) + { + if (delimiter && (string[i] == delimiter)) + delimiter = 0; + else if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, string[i])) + delimiter = string[i]; + else if (!delimiter && (string[i] == closer)) + level++; + else if (!delimiter && (string[i] == opener)) + level--; + + if (!level) + break; + } + return (i); +} diff --git a/lib/readline/parse-colors.c b/lib/readline/parse-colors.c new file mode 100644 index 0000000..05ec9bc --- /dev/null +++ b/lib/readline/parse-colors.c @@ -0,0 +1,440 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2017 + 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +// strdup() / strcpy() +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +// abort() +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "rldefs.h" // STREQ, savestring +#include "readline.h" +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +#include "colors.h" +#include "parse-colors.h" + +#if defined (COLOR_SUPPORT) + +static bool get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count); + +struct bin_str _rl_color_indicator[] = + { + { LEN_STR_PAIR ("\033[") }, // lc: Left of color sequence + { LEN_STR_PAIR ("m") }, // rc: Right of color sequence + { 0, NULL }, // ec: End color (replaces lc+no+rc) + { LEN_STR_PAIR ("0") }, // rs: Reset to ordinary colors + { 0, NULL }, // no: Normal + { 0, NULL }, // fi: File: default + { LEN_STR_PAIR ("01;34") }, // di: Directory: bright blue + { LEN_STR_PAIR ("01;36") }, // ln: Symlink: bright cyan + { LEN_STR_PAIR ("33") }, // pi: Pipe: yellow/brown + { LEN_STR_PAIR ("01;35") }, // so: Socket: bright magenta + { LEN_STR_PAIR ("01;33") }, // bd: Block device: bright yellow + { LEN_STR_PAIR ("01;33") }, // cd: Char device: bright yellow + { 0, NULL }, // mi: Missing file: undefined + { 0, NULL }, // or: Orphaned symlink: undefined + { LEN_STR_PAIR ("01;32") }, // ex: Executable: bright green + { LEN_STR_PAIR ("01;35") }, // do: Door: bright magenta + { LEN_STR_PAIR ("37;41") }, // su: setuid: white on red + { LEN_STR_PAIR ("30;43") }, // sg: setgid: black on yellow + { LEN_STR_PAIR ("37;44") }, // st: sticky: black on blue + { LEN_STR_PAIR ("34;42") }, // ow: other-writable: blue on green + { LEN_STR_PAIR ("30;42") }, // tw: ow w/ sticky: black on green + { LEN_STR_PAIR ("30;41") }, // ca: black on red + { 0, NULL }, // mh: disabled by default + { LEN_STR_PAIR ("\033[K") }, // cl: clear to end of line + }; + +/* Parse a string as part of the LS_COLORS variable; this may involve + decoding all kinds of escape characters. If equals_end is set an + unescaped equal sign ends the string, otherwise only a : or \0 + does. Set *OUTPUT_COUNT to the number of bytes output. Return + true if successful. + + The resulting string is *not* null-terminated, but may contain + embedded nulls. + + Note that both dest and src are char **; on return they point to + the first free byte after the array and the character that ended + the input string, respectively. */ + +static bool +get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count) { + char num; /* For numerical codes */ + size_t count; /* Something to count with */ + enum { + ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR + } state; + const char *p; + char *q; + + p = *src; /* We don't want to double-indirect */ + q = *dest; /* the whole darn time. */ + + count = 0; /* No characters counted in yet. */ + num = 0; + + state = ST_GND; /* Start in ground state. */ + while (state < ST_END) + { + switch (state) + { + case ST_GND: /* Ground state (no escapes) */ + switch (*p) + { + case ':': + case '\0': + state = ST_END; /* End of string */ + break; + case '\\': + state = ST_BACKSLASH; /* Backslash scape sequence */ + ++p; + break; + case '^': + state = ST_CARET; /* Caret escape */ + ++p; + break; + case '=': + if (equals_end) + { + state = ST_END; /* End */ + break; + } + /* else fall through */ + default: + *(q++) = *(p++); + ++count; + break; + } + break; + + case ST_BACKSLASH: /* Backslash escaped character */ + switch (*p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + state = ST_OCTAL; /* Octal sequence */ + num = *p - '0'; + break; + case 'x': + case 'X': + state = ST_HEX; /* Hex sequence */ + num = 0; + break; + case 'a': /* Bell */ + num = '\a'; + break; + case 'b': /* Backspace */ + num = '\b'; + break; + case 'e': /* Escape */ + num = 27; + break; + case 'f': /* Form feed */ + num = '\f'; + break; + case 'n': /* Newline */ + num = '\n'; + break; + case 'r': /* Carriage return */ + num = '\r'; + break; + case 't': /* Tab */ + num = '\t'; + break; + case 'v': /* Vtab */ + num = '\v'; + break; + case '?': /* Delete */ + num = 127; + break; + case '_': /* Space */ + num = ' '; + break; + case '\0': /* End of string */ + state = ST_ERROR; /* Error! */ + break; + default: /* Escaped character like \ ^ : = */ + num = *p; + break; + } + if (state == ST_BACKSLASH) + { + *(q++) = num; + ++count; + state = ST_GND; + } + ++p; + break; + + case ST_OCTAL: /* Octal sequence */ + if (*p < '0' || *p > '7') + { + *(q++) = num; + ++count; + state = ST_GND; + } + else + num = (num << 3) + (*(p++) - '0'); + break; + + case ST_HEX: /* Hex sequence */ + switch (*p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + num = (num << 4) + (*(p++) - '0'); + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + num = (num << 4) + (*(p++) - 'a') + 10; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + num = (num << 4) + (*(p++) - 'A') + 10; + break; + default: + *(q++) = num; + ++count; + state = ST_GND; + break; + } + break; + + case ST_CARET: /* Caret escape */ + state = ST_GND; /* Should be the next state... */ + if (*p >= '@' && *p <= '~') + { + *(q++) = *(p++) & 037; + ++count; + } + else if (*p == '?') + { + *(q++) = 127; + ++count; + } + else + state = ST_ERROR; + break; + + default: + /* should we ? */ + /* abort (); no, we should not */ + state = ST_ERROR; + break; + } + } + + *dest = q; + *src = p; + *output_count = count; + + return state != ST_ERROR; +} +#endif /* COLOR_SUPPORT */ + +void _rl_parse_colors(void) +{ +#if defined (COLOR_SUPPORT) + const char *p; /* Pointer to character being parsed */ + char *buf; /* color_buf buffer pointer */ + int state; /* State of parser */ + int ind_no; /* Indicator number */ + char label[3]; /* Indicator label */ + COLOR_EXT_TYPE *ext; /* Extension we are working on */ + + p = sh_get_env_value ("LS_COLORS"); + if (p == 0 || *p == '\0') + { + _rl_color_ext_list = NULL; + return; + } + + ext = NULL; + strcpy (label, "??"); + + /* This is an overly conservative estimate, but any possible + LS_COLORS string will *not* generate a color_buf longer than + itself, so it is a safe way of allocating a buffer in + advance. */ + buf = color_buf = savestring (p); + + state = 1; + while (state > 0) + { + switch (state) + { + case 1: /* First label character */ + switch (*p) + { + case ':': + ++p; + break; + + case '*': + /* Allocate new extension block and add to head of + linked list (this way a later definition will + override an earlier one, which can be useful for + having terminal-specific defs override global). */ + + ext = (COLOR_EXT_TYPE *)xmalloc (sizeof *ext); + ext->next = _rl_color_ext_list; + _rl_color_ext_list = ext; + + ++p; + ext->ext.string = buf; + + state = (get_funky_string (&buf, &p, true, &ext->ext.len) + ? 4 : -1); + break; + + case '\0': + state = 0; /* Done! */ + break; + + default: /* Assume it is file type label */ + label[0] = *(p++); + state = 2; + break; + } + break; + + case 2: /* Second label character */ + if (*p) + { + label[1] = *(p++); + state = 3; + } + else + state = -1; /* Error */ + break; + + case 3: /* Equal sign after indicator label */ + state = -1; /* Assume failure... */ + if (*(p++) == '=')/* It *should* be... */ + { + for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) + { + if (STREQ (label, indicator_name[ind_no])) + { + _rl_color_indicator[ind_no].string = buf; + state = (get_funky_string (&buf, &p, false, + &_rl_color_indicator[ind_no].len) + ? 1 : -1); + break; + } + } + if (state == -1) + { + _rl_errmsg ("LS_COLORS: unrecognized prefix: %s", label); + /* recover from an unrecognized prefix */ + while (p && *p && *p != ':') + p++; + if (p && *p == ':') + state = 1; + else if (p && *p == 0) + state = 0; + } + } + break; + + case 4: /* Equal sign after *.ext */ + if (*(p++) == '=') + { + ext->seq.string = buf; + state = (get_funky_string (&buf, &p, false, &ext->seq.len) + ? 1 : -1); + } + else + state = -1; + /* XXX - recover here as with an unrecognized prefix? */ + if (state == -1 && ext->ext.string) + _rl_errmsg ("LS_COLORS: syntax error: %s", ext->ext.string); + break; + } + } + + if (state < 0) + { + COLOR_EXT_TYPE *e; + COLOR_EXT_TYPE *e2; + + _rl_errmsg ("unparsable value for LS_COLORS environment variable"); + free (color_buf); + for (e = _rl_color_ext_list; e != NULL; /* empty */) + { + e2 = e; + e = e->next; + free (e2); + } + _rl_color_ext_list = NULL; + _rl_colored_stats = 0; /* can't have colored stats without colors */ + } +#else /* !COLOR_SUPPORT */ + ; +#endif /* !COLOR_SUPPORT */ +} diff --git a/lib/readline/parse-colors.h b/lib/readline/parse-colors.h new file mode 100644 index 0000000..aef86f7 --- /dev/null +++ b/lib/readline/parse-colors.h @@ -0,0 +1,46 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#ifndef _PARSE_COLORS_H_ +#define _PARSE_COLORS_H_ + +#include "readline.h" + +#define LEN_STR_PAIR(s) sizeof (s) - 1, s + +void _rl_parse_colors (void); + +static const char *const indicator_name[]= + { + "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so", + "bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st", + "ow", "tw", "ca", "mh", "cl", NULL + }; + +/* Buffer for color sequences */ +static char *color_buf; + +#endif /* !_PARSE_COLORS_H_ */ diff --git a/lib/readline/posixdir.h b/lib/readline/posixdir.h new file mode 100644 index 0000000..b737bd7 --- /dev/null +++ b/lib/readline/posixdir.h @@ -0,0 +1,71 @@ +/* posixdir.h -- Posix directory reading includes and defines. */ + +/* Copyright (C) 1987,1991,2012,2019,2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* This file should be included instead of <dirent.h> or <sys/dir.h>. */ + +#if !defined (_POSIXDIR_H_) +#define _POSIXDIR_H_ + +#if defined (HAVE_DIRENT_H) +# include <dirent.h> +# if defined (HAVE_STRUCT_DIRENT_D_NAMLEN) +# define D_NAMLEN(d) ((d)->d_namlen) +# else +# define D_NAMLEN(d) (strlen ((d)->d_name)) +# endif /* !HAVE_STRUCT_DIRENT_D_NAMLEN */ +#else +# if defined (HAVE_SYS_NDIR_H) +# include <sys/ndir.h> +# endif +# if defined (HAVE_SYS_DIR_H) +# include <sys/dir.h> +# endif +# if defined (HAVE_NDIR_H) +# include <ndir.h> +# endif +# if !defined (dirent) +# define dirent direct +# endif /* !dirent */ +# define D_NAMLEN(d) ((d)->d_namlen) +#endif /* !HAVE_DIRENT_H */ + +/* The bash code fairly consistently uses d_fileno; make sure it's available */ +#if defined (HAVE_STRUCT_DIRENT_D_INO) && !defined (HAVE_STRUCT_DIRENT_D_FILENO) +# define d_fileno d_ino +#endif + +/* Posix does not require that the d_ino field be present, and some + systems do not provide it. */ +#if !defined (HAVE_STRUCT_DIRENT_D_INO) || defined (BROKEN_DIRENT_D_INO) +# define REAL_DIR_ENTRY(dp) 1 +#else +# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) +#endif /* _POSIX_SOURCE */ + +#if defined (HAVE_STRUCT_DIRENT_D_INO) && !defined (BROKEN_DIRENT_D_INO) +# define D_INO_AVAILABLE +#endif + +/* Signal the rest of the code that it can safely use dirent.d_fileno */ +#if defined (D_INO_AVAILABLE) || defined (HAVE_STRUCT_DIRENT_D_FILENO) +# define D_FILENO_AVAILABLE 1 +#endif + +#endif /* !_POSIXDIR_H_ */ diff --git a/lib/readline/posixjmp.h b/lib/readline/posixjmp.h new file mode 100644 index 0000000..9c7e99e --- /dev/null +++ b/lib/readline/posixjmp.h @@ -0,0 +1,46 @@ +/* posixjmp.h -- wrapper for setjmp.h with changes for POSIX systems. */ + +/* Copyright (C) 1987,1991-2015 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _POSIXJMP_H_ +#define _POSIXJMP_H_ + +#include <setjmp.h> + +/* This *must* be included *after* config.h */ + +#if defined (HAVE_POSIX_SIGSETJMP) +# define procenv_t sigjmp_buf + +# define setjmp_nosigs(x) sigsetjmp((x), 0) +# define setjmp_sigs(x) sigsetjmp((x), 1) + +# define _rl_longjmp(x, n) siglongjmp((x), (n)) +# define sh_longjmp(x, n) siglongjmp((x), (n)) +#else +# define procenv_t jmp_buf + +# define setjmp_nosigs setjmp +# define setjmp_sigs setjmp + +# define _rl_longjmp(x, n) longjmp((x), (n)) +# define sh_longjmp(x, n) longjmp((x), (n)) +#endif + +#endif /* _POSIXJMP_H_ */ diff --git a/lib/readline/posixselect.h b/lib/readline/posixselect.h new file mode 100644 index 0000000..da6a1ac --- /dev/null +++ b/lib/readline/posixselect.h @@ -0,0 +1,47 @@ +/* posixselect.h -- wrapper for select(2) includes and definitions */ + +/* Copyright (C) 2009 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _POSIXSELECT_H_ +#define _POSIXSELECT_H_ + +#if defined (FD_SET) && !defined (HAVE_SELECT) +# define HAVE_SELECT 1 +#endif + +#if defined (HAVE_SELECT) +# if !defined (HAVE_SYS_SELECT_H) || !defined (M_UNIX) +# include <sys/time.h> +# endif +#endif /* HAVE_SELECT */ +#if defined (HAVE_SYS_SELECT_H) +# include <sys/select.h> +#endif + +#ifndef USEC_PER_SEC +# define USEC_PER_SEC 1000000 +#endif + +#define USEC_TO_TIMEVAL(us, tv) \ +do { \ + (tv).tv_sec = (us) / USEC_PER_SEC; \ + (tv).tv_usec = (us) % USEC_PER_SEC; \ +} while (0) + +#endif /* _POSIXSELECT_H_ */ diff --git a/lib/readline/posixstat.h b/lib/readline/posixstat.h new file mode 100644 index 0000000..b607786 --- /dev/null +++ b/lib/readline/posixstat.h @@ -0,0 +1,162 @@ +/* posixstat.h -- Posix stat(2) definitions for systems that + don't have them. */ + +/* Copyright (C) 1987-2019 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* This file should be included instead of <sys/stat.h>. + It relies on the local sys/stat.h to work though. */ +#if !defined (_POSIXSTAT_H_) +#define _POSIXSTAT_H_ + +#include <sys/stat.h> + +#if defined (STAT_MACROS_BROKEN) +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISREG +# undef S_ISLNK +#endif /* STAT_MACROS_BROKEN */ + +/* These are guaranteed to work only on isc386 */ +#if !defined (S_IFDIR) && !defined (S_ISDIR) +# define S_IFDIR 0040000 +#endif /* !S_IFDIR && !S_ISDIR */ +#if !defined (S_IFMT) +# define S_IFMT 0170000 +#endif /* !S_IFMT */ + +/* Posix 1003.1 5.6.1.1 <sys/stat.h> file types */ + +/* Some Posix-wannabe systems define _S_IF* macros instead of S_IF*, but + do not provide the S_IS* macros that Posix requires. */ + +#if defined (_S_IFMT) && !defined (S_IFMT) +#define S_IFMT _S_IFMT +#endif +#if defined (_S_IFIFO) && !defined (S_IFIFO) +#define S_IFIFO _S_IFIFO +#endif +#if defined (_S_IFCHR) && !defined (S_IFCHR) +#define S_IFCHR _S_IFCHR +#endif +#if defined (_S_IFDIR) && !defined (S_IFDIR) +#define S_IFDIR _S_IFDIR +#endif +#if defined (_S_IFBLK) && !defined (S_IFBLK) +#define S_IFBLK _S_IFBLK +#endif +#if defined (_S_IFREG) && !defined (S_IFREG) +#define S_IFREG _S_IFREG +#endif +#if defined (_S_IFLNK) && !defined (S_IFLNK) +#define S_IFLNK _S_IFLNK +#endif +#if defined (_S_IFSOCK) && !defined (S_IFSOCK) +#define S_IFSOCK _S_IFSOCK +#endif + +/* Test for each symbol individually and define the ones necessary (some + systems claiming Posix compatibility define some but not all). */ + +#if defined (S_IFBLK) && !defined (S_ISBLK) +#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) /* block device */ +#endif + +#if defined (S_IFCHR) && !defined (S_ISCHR) +#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) /* character device */ +#endif + +#if defined (S_IFDIR) && !defined (S_ISDIR) +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) /* directory */ +#endif + +#if defined (S_IFREG) && !defined (S_ISREG) +#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) /* file */ +#endif + +#if defined (S_IFIFO) && !defined (S_ISFIFO) +#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) /* fifo - named pipe */ +#endif + +#if defined (S_IFLNK) && !defined (S_ISLNK) +#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) /* symbolic link */ +#endif + +#if defined (S_IFSOCK) && !defined (S_ISSOCK) +#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) /* socket */ +#endif + +/* + * POSIX 1003.1 5.6.1.2 <sys/stat.h> File Modes + */ + +#if !defined (S_IRWXU) +# if !defined (S_IREAD) +# define S_IREAD 00400 +# define S_IWRITE 00200 +# define S_IEXEC 00100 +# endif /* S_IREAD */ + +# if !defined (S_IRUSR) +# define S_IRUSR S_IREAD /* read, owner */ +# define S_IWUSR S_IWRITE /* write, owner */ +# define S_IXUSR S_IEXEC /* execute, owner */ + +# define S_IRGRP (S_IREAD >> 3) /* read, group */ +# define S_IWGRP (S_IWRITE >> 3) /* write, group */ +# define S_IXGRP (S_IEXEC >> 3) /* execute, group */ + +# define S_IROTH (S_IREAD >> 6) /* read, other */ +# define S_IWOTH (S_IWRITE >> 6) /* write, other */ +# define S_IXOTH (S_IEXEC >> 6) /* execute, other */ +# endif /* !S_IRUSR */ + +# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) +#else /* !S_IRWXU */ + /* S_IRWXU is defined, but "group" and "other" bits might not be + (happens in certain versions of MinGW). */ +# if !defined (S_IRGRP) +# define S_IRGRP (S_IREAD >> 3) /* read, group */ +# define S_IWGRP (S_IWRITE >> 3) /* write, group */ +# define S_IXGRP (S_IEXEC >> 3) /* execute, group */ +# endif /* !S_IRGRP */ + +# if !defined (S_IROTH) +# define S_IROTH (S_IREAD >> 6) /* read, other */ +# define S_IWOTH (S_IWRITE >> 6) /* write, other */ +# define S_IXOTH (S_IEXEC >> 6) /* execute, other */ +# endif /* !S_IROTH */ +# if !defined (S_IRWXG) +# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +# endif +# if !defined (S_IRWXO) +# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) +# endif +#endif /* !S_IRWXU */ + +/* These are non-standard, but are used in builtins.c$symbolic_umask() */ +#define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH) +#define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH) +#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) + +#endif /* _POSIXSTAT_H_ */ diff --git a/lib/readline/posixtime.h b/lib/readline/posixtime.h new file mode 100644 index 0000000..e70ebec --- /dev/null +++ b/lib/readline/posixtime.h @@ -0,0 +1,84 @@ +/* posixtime.h -- wrapper for time.h, sys/times.h mess. */ + +/* Copyright (C) 1999-2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _POSIXTIME_H_ +#define _POSIXTIME_H_ + +/* include this after config.h */ +/* Some systems require this, mostly for the definition of `struct timezone'. + For example, Dynix/ptx has that definition in <time.h> rather than + sys/time.h */ +#if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +#endif +#include <time.h> + +#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK) +# if !defined (CLK_TCK) +# if defined (HZ) +# define CLK_TCK HZ +# else +# define CLK_TCK 60 /* 60HZ */ +# endif +# endif /* !CLK_TCK */ +#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */ + +#if !HAVE_TIMEVAL +struct timeval +{ + time_t tv_sec; + long int tv_usec; +}; +#endif + +#if !HAVE_GETTIMEOFDAY +extern int gettimeofday PARAMS((struct timeval *, void *)); +#endif + +/* These exist on BSD systems, at least. */ +#if !defined (timerclear) +# define timerclear(tvp) do { (tvp)->tv_sec = 0; (tvp)->tv_usec = 0; } while (0) +#endif +#if !defined (timerisset) +# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif +#if !defined (timercmp) +# define timercmp(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec CMP (b)->tv_usec) \ + : ((a)->tv_sec CMP (b)->tv_sec)) +#endif + +/* These are non-standard. */ +#if !defined (timerisunset) +# define timerisunset(tvp) ((tvp)->tv_sec == 0 && (tvp)->tv_usec == 0) +#endif +#if !defined (timerset) +# define timerset(tvp, s, u) do { tvp->tv_sec = s; tvp->tv_usec = u; } while (0) +#endif + +#ifndef TIMEVAL_TO_TIMESPEC +# define TIMEVAL_TO_TIMESPEC(tv, ts) \ + do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ + } while (0) +#endif + +#endif /* _POSIXTIME_H_ */ diff --git a/lib/readline/readline.c b/lib/readline/readline.c new file mode 100644 index 0000000..9d42a8d --- /dev/null +++ b/lib/readline/readline.c @@ -0,0 +1,1575 @@ +/* readline.c -- a general facility for reading lines of input + with emacs style editing and completion. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include "posixstat.h" +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_LOCALE_H) +# include <locale.h> +#endif + +#include <stdio.h> +#include "posixjmp.h" +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +#if defined (__EMX__) +# define INCL_DOSPROCESS +# include <os2.h> +#endif /* __EMX__ */ + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +#if defined (COLOR_SUPPORT) +# include "parse-colors.h" +#endif + +#ifndef RL_LIBRARY_VERSION +# define RL_LIBRARY_VERSION "8.1" +#endif + +#ifndef RL_READLINE_VERSION +# define RL_READLINE_VERSION 0x0801 +#endif + +/* Forward declarations used in this file. */ +static char *readline_internal (void); +static void readline_initialize_everything (void); + +static void run_startup_hooks (void); + +static void bind_arrow_keys_internal (Keymap); +static void bind_arrow_keys (void); + +static void bind_bracketed_paste_prefix (void); + +static void readline_default_bindings (void); +static void reset_default_bindings (void); + +static int _rl_subseq_result (int, Keymap, int, int); +static int _rl_subseq_getchar (int); + +/* **************************************************************** */ +/* */ +/* Line editing input utility */ +/* */ +/* **************************************************************** */ + +const char *rl_library_version = RL_LIBRARY_VERSION; + +int rl_readline_version = RL_READLINE_VERSION; + +/* True if this is `real' readline as opposed to some stub substitute. */ +int rl_gnu_readline_p = 1; + +/* A pointer to the keymap that is currently in use. + By default, it is the standard emacs keymap. */ +Keymap _rl_keymap = emacs_standard_keymap; + +/* The current style of editing. */ +int rl_editing_mode = emacs_mode; + +/* The current insert mode: input (the default) or overwrite */ +int rl_insert_mode = RL_IM_DEFAULT; + +/* Non-zero if we called this function from _rl_dispatch(). It's present + so functions can find out whether they were called from a key binding + or directly from an application. */ +int rl_dispatching; + +/* Non-zero if the previous command was a kill command. */ +int _rl_last_command_was_kill = 0; + +/* The current value of the numeric argument specified by the user. */ +int rl_numeric_arg = 1; + +/* Non-zero if an argument was typed. */ +int rl_explicit_arg = 0; + +/* Temporary value used while generating the argument. */ +int rl_arg_sign = 1; + +/* Non-zero means we have been called at least once before. */ +static int rl_initialized; + +#if 0 +/* If non-zero, this program is running in an EMACS buffer. */ +static int running_in_emacs; +#endif + +/* Flags word encapsulating the current readline state. */ +unsigned long rl_readline_state = RL_STATE_NONE; + +/* The current offset in the current input line. */ +int rl_point; + +/* Mark in the current input line. */ +int rl_mark; + +/* Length of the current input line. */ +int rl_end; + +/* Make this non-zero to return the current input_line. */ +int rl_done; + +/* If non-zero when readline_internal returns, it means we found EOF */ +int rl_eof_found = 0; + +/* The last function executed by readline. */ +rl_command_func_t *rl_last_func = (rl_command_func_t *)NULL; + +/* Top level environment for readline_internal (). */ +procenv_t _rl_top_level; + +/* The streams we interact with. */ +FILE *_rl_in_stream, *_rl_out_stream; + +/* The names of the streams that we do input and output to. */ +FILE *rl_instream = (FILE *)NULL; +FILE *rl_outstream = (FILE *)NULL; + +/* Non-zero means echo characters as they are read. Defaults to no echo; + set to 1 if there is a controlling terminal, we can get its attributes, + and the attributes include `echo'. Look at rltty.c:prepare_terminal_settings + for the code that sets it. */ +int _rl_echoing_p = 0; + +/* Current prompt. */ +char *rl_prompt = (char *)NULL; +int rl_visible_prompt_length = 0; + +/* Set to non-zero by calling application if it has already printed rl_prompt + and does not want readline to do it the first time. */ +int rl_already_prompted = 0; + +/* The number of characters read in order to type this complete command. */ +int rl_key_sequence_length = 0; + +/* If non-zero, then this is the address of a function to call just + before readline_internal_setup () prints the first prompt. */ +rl_hook_func_t *rl_startup_hook = (rl_hook_func_t *)NULL; + +/* Any readline function can set this and have it run just before the user's + rl_startup_hook. */ +rl_hook_func_t *_rl_internal_startup_hook = (rl_hook_func_t *)NULL; + +/* If non-zero, this is the address of a function to call just before + readline_internal_setup () returns and readline_internal starts + reading input characters. */ +rl_hook_func_t *rl_pre_input_hook = (rl_hook_func_t *)NULL; + +/* What we use internally. You should always refer to RL_LINE_BUFFER. */ +static char *the_line; + +/* The character that can generate an EOF. Really read from + the terminal driver... just defaulted here. */ +int _rl_eof_char = CTRL ('D'); + +/* Non-zero makes this the next keystroke to read. */ +int rl_pending_input = 0; + +/* Pointer to a useful terminal name. */ +const char *rl_terminal_name = (const char *)NULL; + +/* Non-zero means to always use horizontal scrolling in line display. */ +int _rl_horizontal_scroll_mode = 0; + +/* Non-zero means to display an asterisk at the starts of history lines + which have been modified. */ +int _rl_mark_modified_lines = 0; + +/* The style of `bell' notification preferred. This can be set to NO_BELL, + AUDIBLE_BELL, or VISIBLE_BELL. */ +int _rl_bell_preference = AUDIBLE_BELL; + +/* String inserted into the line by rl_insert_comment (). */ +char *_rl_comment_begin; + +/* Keymap holding the function currently being executed. */ +Keymap rl_executing_keymap; + +/* The function currently being executed. */ +rl_command_func_t *_rl_executing_func; + +/* Keymap we're currently using to dispatch. */ +Keymap _rl_dispatching_keymap; + +/* Non-zero means to erase entire line, including prompt, on empty input lines. */ +int rl_erase_empty_line = 0; + +/* Non-zero means to read only this many characters rather than up to a + character bound to accept-line. */ +int rl_num_chars_to_read = 0; + +/* Line buffer and maintenance. */ +char *rl_line_buffer = (char *)NULL; +int rl_line_buffer_len = 0; + +/* Key sequence `contexts' */ +_rl_keyseq_cxt *_rl_kscxt = 0; + +int rl_executing_key; +char *rl_executing_keyseq = 0; +int _rl_executing_keyseq_size = 0; + +struct _rl_cmd _rl_pending_command; +struct _rl_cmd *_rl_command_to_execute = (struct _rl_cmd *)NULL; + +/* Timeout (specified in milliseconds) when reading characters making up an + ambiguous multiple-key sequence */ +int _rl_keyseq_timeout = 500; + +#define RESIZE_KEYSEQ_BUFFER() \ + do \ + { \ + if (rl_key_sequence_length + 2 >= _rl_executing_keyseq_size) \ + { \ + _rl_executing_keyseq_size += 16; \ + rl_executing_keyseq = xrealloc (rl_executing_keyseq, _rl_executing_keyseq_size); \ + } \ + } \ + while (0); + +/* Forward declarations used by the display, termcap, and history code. */ + +/* **************************************************************** */ +/* */ +/* `Forward' declarations */ +/* */ +/* **************************************************************** */ + +/* Non-zero means do not parse any lines other than comments and + parser directives. */ +unsigned char _rl_parsing_conditionalized_out = 0; + +/* Non-zero means to convert characters with the meta bit set to + escape-prefixed characters so we can indirect through + emacs_meta_keymap or vi_escape_keymap. */ +int _rl_convert_meta_chars_to_ascii = 1; + +/* Non-zero means to output characters with the meta bit set directly + rather than as a meta-prefixed escape sequence. */ +int _rl_output_meta_chars = 0; + +/* Non-zero means to look at the termios special characters and bind + them to equivalent readline functions at startup. */ +int _rl_bind_stty_chars = 1; + +/* Non-zero means to go through the history list at every newline (or + whenever rl_done is set and readline returns) and revert each line to + its initial state. */ +int _rl_revert_all_at_newline = 0; + +/* Non-zero means to honor the termios ECHOCTL bit and echo control + characters corresponding to keyboard-generated signals. */ +int _rl_echo_control_chars = 1; + +/* Non-zero means to prefix the displayed prompt with a character indicating + the editing mode: @ for emacs, : for vi-command, + for vi-insert. */ +int _rl_show_mode_in_prompt = 0; + +/* Non-zero means to attempt to put the terminal in `bracketed paste mode', + where it will prefix pasted text with an escape sequence and send + another to mark the end of the paste. */ +int _rl_enable_bracketed_paste = BRACKETED_PASTE_DEFAULT; +int _rl_enable_active_region = BRACKETED_PASTE_DEFAULT; + +/* **************************************************************** */ +/* */ +/* Top Level Functions */ +/* */ +/* **************************************************************** */ + +/* Non-zero means treat 0200 bit in terminal input as Meta bit. */ +int _rl_meta_flag = 0; /* Forward declaration */ + +/* Set up the prompt and expand it. Called from readline() and + rl_callback_handler_install (). */ +int +rl_set_prompt (const char *prompt) +{ + FREE (rl_prompt); + rl_prompt = prompt ? savestring (prompt) : (char *)NULL; + rl_display_prompt = rl_prompt ? rl_prompt : ""; + + rl_visible_prompt_length = rl_expand_prompt (rl_prompt); + return 0; +} + +/* Read a line of input. Prompt with PROMPT. An empty PROMPT means + none. A return value of NULL means that EOF was encountered. */ +char * +readline (const char *prompt) +{ + char *value; +#if 0 + int in_callback; +#endif + + /* If we are at EOF return a NULL string. */ + if (rl_pending_input == EOF) + { + rl_clear_pending_input (); + return ((char *)NULL); + } + +#if 0 + /* If readline() is called after installing a callback handler, temporarily + turn off the callback state to avoid ensuing messiness. Patch supplied + by the gdb folks. XXX -- disabled. This can be fooled and readline + left in a strange state by a poorly-timed longjmp. */ + if (in_callback = RL_ISSTATE (RL_STATE_CALLBACK)) + RL_UNSETSTATE (RL_STATE_CALLBACK); +#endif + + rl_set_prompt (prompt); + + rl_initialize (); + if (rl_prep_term_function) + (*rl_prep_term_function) (_rl_meta_flag); + +#if defined (HANDLE_SIGNALS) + rl_set_signals (); +#endif + + value = readline_internal (); + if (rl_deprep_term_function) + (*rl_deprep_term_function) (); + +#if defined (HANDLE_SIGNALS) + rl_clear_signals (); +#endif + +#if 0 + if (in_callback) + RL_SETSTATE (RL_STATE_CALLBACK); +#endif + +#if HAVE_DECL_AUDIT_USER_TTY && defined (HAVE_LIBAUDIT_H) && defined (ENABLE_TTY_AUDIT_SUPPORT) + if (value) + _rl_audit_tty (value); +#endif + + return (value); +} + +static void +run_startup_hooks (void) +{ + if (rl_startup_hook) + (*rl_startup_hook) (); + + if (_rl_internal_startup_hook) + (*_rl_internal_startup_hook) (); +} + +#if defined (READLINE_CALLBACKS) +# define STATIC_CALLBACK +#else +# define STATIC_CALLBACK static +#endif + +STATIC_CALLBACK void +readline_internal_setup (void) +{ + char *nprompt; + + _rl_in_stream = rl_instream; + _rl_out_stream = rl_outstream; + + /* Enable the meta key only for the duration of readline(), if this + terminal has one and the terminal has been initialized */ + if (_rl_enable_meta & RL_ISSTATE (RL_STATE_TERMPREPPED)) + _rl_enable_meta_key (); + + run_startup_hooks (); + + rl_deactivate_mark (); + +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + rl_vi_insertion_mode (1, 'i'); /* don't want to reset last */ + else +#endif /* VI_MODE */ + if (_rl_show_mode_in_prompt) + _rl_reset_prompt (); + + /* If we're not echoing, we still want to at least print a prompt, because + rl_redisplay will not do it for us. If the calling application has a + custom redisplay function, though, let that function handle it. */ + if (_rl_echoing_p == 0 && rl_redisplay_function == rl_redisplay) + { + if (rl_prompt && rl_already_prompted == 0) + { + nprompt = _rl_strip_prompt (rl_prompt); + fprintf (_rl_out_stream, "%s", nprompt); + fflush (_rl_out_stream); + xfree (nprompt); + } + } + else + { + if (rl_prompt && rl_already_prompted) + rl_on_new_line_with_prompt (); + else + rl_on_new_line (); + (*rl_redisplay_function) (); + } + + if (rl_pre_input_hook) + (*rl_pre_input_hook) (); + + RL_CHECK_SIGNALS (); +} + +STATIC_CALLBACK char * +readline_internal_teardown (int eof) +{ + char *temp; + HIST_ENTRY *entry; + + RL_CHECK_SIGNALS (); + + if (eof) + RL_SETSTATE (RL_STATE_EOF); /* XXX */ + + /* Restore the original of this history line, iff the line that we + are editing was originally in the history, AND the line has changed. */ + entry = current_history (); + + /* We don't want to do this if we executed functions that call + history_set_pos to set the history offset to the line containing the + non-incremental search string. */ + if (entry && rl_undo_list) + { + temp = savestring (the_line); + rl_revert_line (1, 0); + entry = replace_history_entry (where_history (), the_line, (histdata_t)NULL); + _rl_free_history_entry (entry); + + strcpy (the_line, temp); + xfree (temp); + } + + if (_rl_revert_all_at_newline) + _rl_revert_all_lines (); + + /* At any rate, it is highly likely that this line has an undo list. Get + rid of it now. */ + if (rl_undo_list) + rl_free_undo_list (); + + /* Disable the meta key, if this terminal has one and we were told to use it. + The check whether or not we sent the enable string is in + _rl_disable_meta_key(); the flag is set in _rl_enable_meta_key */ + _rl_disable_meta_key (); + + /* Restore normal cursor, if available. */ + _rl_set_insert_mode (RL_IM_INSERT, 0); + + return (eof ? (char *)NULL : savestring (the_line)); +} + +void +_rl_internal_char_cleanup (void) +{ + if (_rl_keep_mark_active) + _rl_keep_mark_active = 0; + else if (rl_mark_active_p ()) + rl_deactivate_mark (); + +#if defined (VI_MODE) + /* In vi mode, when you exit insert mode, the cursor moves back + over the previous character. We explicitly check for that here. */ + if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) + rl_vi_check (); +#endif /* VI_MODE */ + + if (rl_num_chars_to_read && rl_end >= rl_num_chars_to_read) + { + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + rl_newline (1, '\n'); + } + + if (rl_done == 0) + { + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + } + + /* If the application writer has told us to erase the entire line if + the only character typed was something bound to rl_newline, do so. */ + if (rl_erase_empty_line && rl_done && rl_last_func == rl_newline && + rl_point == 0 && rl_end == 0) + _rl_erase_entire_line (); +} + +STATIC_CALLBACK int +#if defined (READLINE_CALLBACKS) +readline_internal_char (void) +#else +readline_internal_charloop (void) +#endif +{ + static int lastc, eof_found; + int c, code, lk, r; + + lastc = EOF; + +#if !defined (READLINE_CALLBACKS) + eof_found = 0; + while (rl_done == 0) + { +#endif + lk = _rl_last_command_was_kill; + +#if defined (HAVE_POSIX_SIGSETJMP) + code = sigsetjmp (_rl_top_level, 0); +#else + code = setjmp (_rl_top_level); +#endif + + if (code) + { + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + + /* If we longjmped because of a timeout, handle it here. */ + if (RL_ISSTATE (RL_STATE_TIMEOUT)) + { + RL_SETSTATE (RL_STATE_DONE); + rl_done = 1; + return 1; + } + + /* If we get here, we're not being called from something dispatched + from _rl_callback_read_char(), which sets up its own value of + _rl_top_level (saving and restoring the old, of course), so + we can just return here. */ + if (RL_ISSTATE (RL_STATE_CALLBACK)) + return (0); + } + + if (rl_pending_input == 0) + { + /* Then initialize the argument and number of keys read. */ + _rl_reset_argument (); + rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; + } + + RL_SETSTATE(RL_STATE_READCMD); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_READCMD); + + /* look at input.c:rl_getc() for the circumstances under which this will + be returned; punt immediately on read error without converting it to + a newline; assume that rl_read_key has already called the signal + handler. */ + if (c == READERR) + { +#if defined (READLINE_CALLBACKS) + RL_SETSTATE(RL_STATE_DONE); + return (rl_done = 1); +#else + RL_SETSTATE(RL_STATE_EOF); + eof_found = 1; + break; +#endif + } + + /* EOF typed to a non-blank line is ^D the first time, EOF the second + time in a row. This won't return any partial line read from the tty. + If we want to change this, to force any existing line to be returned + when read(2) reads EOF, for example, this is the place to change. */ + if (c == EOF && rl_end) + { + if (RL_SIG_RECEIVED ()) + { + RL_CHECK_SIGNALS (); + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); /* XXX */ + } + + /* XXX - reading two consecutive EOFs returns EOF */ + if (RL_ISSTATE (RL_STATE_TERMPREPPED)) + { + if (lastc == _rl_eof_char || lastc == EOF) + rl_end = 0; + else + c = _rl_eof_char; + } + else + c = NEWLINE; + } + + /* The character _rl_eof_char typed to blank line, and not as the + previous character is interpreted as EOF. This doesn't work when + READLINE_CALLBACKS is defined, so hitting a series of ^Ds will + erase all the chars on the line and then return EOF. */ + if (((c == _rl_eof_char && lastc != c) || c == EOF) && rl_end == 0) + { +#if defined (READLINE_CALLBACKS) + RL_SETSTATE(RL_STATE_DONE); + return (rl_done = 1); +#else + RL_SETSTATE(RL_STATE_EOF); + eof_found = 1; + break; +#endif + } + + lastc = c; + r = _rl_dispatch ((unsigned char)c, _rl_keymap); + RL_CHECK_SIGNALS (); + + if (_rl_command_to_execute) + { + (*rl_redisplay_function) (); + + rl_executing_keymap = _rl_command_to_execute->map; + rl_executing_key = _rl_command_to_execute->key; + + _rl_executing_func = _rl_command_to_execute->func; + + rl_dispatching = 1; + RL_SETSTATE(RL_STATE_DISPATCHING); + r = (*(_rl_command_to_execute->func)) (_rl_command_to_execute->count, _rl_command_to_execute->key); + _rl_command_to_execute = 0; + RL_UNSETSTATE(RL_STATE_DISPATCHING); + rl_dispatching = 0; + + RL_CHECK_SIGNALS (); + } + + /* If there was no change in _rl_last_command_was_kill, then no kill + has taken place. Note that if input is pending we are reading + a prefix command, so nothing has changed yet. */ + if (rl_pending_input == 0 && lk == _rl_last_command_was_kill) + _rl_last_command_was_kill = 0; + + _rl_internal_char_cleanup (); + +#if defined (READLINE_CALLBACKS) + return 0; +#else + } + + return (eof_found); +#endif +} + +#if defined (READLINE_CALLBACKS) +static int +readline_internal_charloop (void) +{ + int eof = 1; + + while (rl_done == 0) + eof = readline_internal_char (); + return (eof); +} +#endif /* READLINE_CALLBACKS */ + +/* Read a line of input from the global rl_instream, doing output on + the global rl_outstream. + If rl_prompt is non-null, then that is our prompt. */ +static char * +readline_internal (void) +{ + readline_internal_setup (); + rl_eof_found = readline_internal_charloop (); + return (readline_internal_teardown (rl_eof_found)); +} + +void +_rl_init_line_state (void) +{ + rl_point = rl_end = rl_mark = 0; + the_line = rl_line_buffer; + the_line[0] = 0; +} + +void +_rl_set_the_line (void) +{ + the_line = rl_line_buffer; +} + +#if defined (READLINE_CALLBACKS) +_rl_keyseq_cxt * +_rl_keyseq_cxt_alloc (void) +{ + _rl_keyseq_cxt *cxt; + + cxt = (_rl_keyseq_cxt *)xmalloc (sizeof (_rl_keyseq_cxt)); + + cxt->flags = cxt->subseq_arg = cxt->subseq_retval = 0; + + cxt->okey = 0; + cxt->ocxt = _rl_kscxt; + cxt->childval = 42; /* sentinel value */ + + return cxt; +} + +void +_rl_keyseq_cxt_dispose (_rl_keyseq_cxt *cxt) +{ + xfree (cxt); +} + +void +_rl_keyseq_chain_dispose (void) +{ + _rl_keyseq_cxt *cxt; + + while (_rl_kscxt) + { + cxt = _rl_kscxt; + _rl_kscxt = _rl_kscxt->ocxt; + _rl_keyseq_cxt_dispose (cxt); + } +} +#endif + +static int +_rl_subseq_getchar (int key) +{ + int k; + + if (key == ESC) + RL_SETSTATE(RL_STATE_METANEXT); + RL_SETSTATE(RL_STATE_MOREINPUT); + k = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + if (key == ESC) + RL_UNSETSTATE(RL_STATE_METANEXT); + + return k; +} + +#if defined (READLINE_CALLBACKS) +int +_rl_dispatch_callback (_rl_keyseq_cxt *cxt) +{ + int nkey, r; + + /* For now */ + /* The first time this context is used, we want to read input and dispatch + on it. When traversing the chain of contexts back `up', we want to use + the value from the next context down. We're simulating recursion using + a chain of contexts. */ + if ((cxt->flags & KSEQ_DISPATCHED) == 0) + { + nkey = _rl_subseq_getchar (cxt->okey); + if (nkey < 0) + { + _rl_abort_internal (); + return -1; + } + r = _rl_dispatch_subseq (nkey, cxt->dmap, cxt->subseq_arg); + cxt->flags |= KSEQ_DISPATCHED; + } + else + r = cxt->childval; + + /* For now */ + if (r != -3) /* don't do this if we indicate there will be other matches */ + r = _rl_subseq_result (r, cxt->oldmap, cxt->okey, (cxt->flags & KSEQ_SUBSEQ)); + + RL_CHECK_SIGNALS (); + /* We only treat values < 0 specially to simulate recursion. */ + if (r >= 0 || (r == -1 && (cxt->flags & KSEQ_SUBSEQ) == 0)) /* success! or failure! */ + { + _rl_keyseq_chain_dispose (); + RL_UNSETSTATE (RL_STATE_MULTIKEY); + return r; + } + + if (r != -3) /* magic value that says we added to the chain */ + _rl_kscxt = cxt->ocxt; + if (_rl_kscxt) + _rl_kscxt->childval = r; + if (r != -3) + _rl_keyseq_cxt_dispose (cxt); + + return r; +} +#endif /* READLINE_CALLBACKS */ + +/* Do the command associated with KEY in MAP. + If the associated command is really a keymap, then read + another key, and dispatch into that map. */ +int +_rl_dispatch (register int key, Keymap map) +{ + _rl_dispatching_keymap = map; + return _rl_dispatch_subseq (key, map, 0); +} + +int +_rl_dispatch_subseq (register int key, Keymap map, int got_subseq) +{ + int r, newkey; + char *macro; + rl_command_func_t *func; +#if defined (READLINE_CALLBACKS) + _rl_keyseq_cxt *cxt; +#endif + + if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) + { + if (map[ESC].type == ISKMAP) + { + if (RL_ISSTATE (RL_STATE_MACRODEF)) + _rl_add_macro_char (ESC); + RESIZE_KEYSEQ_BUFFER (); + rl_executing_keyseq[rl_key_sequence_length++] = ESC; + map = FUNCTION_TO_KEYMAP (map, ESC); + key = UNMETA (key); + return (_rl_dispatch (key, map)); + } + else + rl_ding (); + return 0; + } + + if (RL_ISSTATE (RL_STATE_MACRODEF)) + _rl_add_macro_char (key); + + r = 0; + switch (map[key].type) + { + case ISFUNC: + func = map[key].function; + if (func) + { + /* Special case rl_do_lowercase_version (). */ + if (func == rl_do_lowercase_version) + /* Should we do anything special if key == ANYOTHERKEY? */ + return (_rl_dispatch (_rl_to_lower ((unsigned char)key), map)); + + rl_executing_keymap = map; + rl_executing_key = key; + + _rl_executing_func = func; + + RESIZE_KEYSEQ_BUFFER(); + rl_executing_keyseq[rl_key_sequence_length++] = key; + rl_executing_keyseq[rl_key_sequence_length] = '\0'; + + rl_dispatching = 1; + RL_SETSTATE(RL_STATE_DISPATCHING); + r = (*func) (rl_numeric_arg * rl_arg_sign, key); + RL_UNSETSTATE(RL_STATE_DISPATCHING); + rl_dispatching = 0; + + /* If we have input pending, then the last command was a prefix + command. Don't change the state of rl_last_func. Otherwise, + remember the last command executed in this variable. */ +#if defined (VI_MODE) + if (rl_pending_input == 0 && map[key].function != rl_digit_argument && map[key].function != rl_vi_arg_digit) +#else + if (rl_pending_input == 0 && map[key].function != rl_digit_argument) +#endif + rl_last_func = map[key].function; + + RL_CHECK_SIGNALS (); + } + else if (map[ANYOTHERKEY].function) + { + /* OK, there's no function bound in this map, but there is a + shadow function that was overridden when the current keymap + was created. Return -2 to note that. */ + if (RL_ISSTATE (RL_STATE_MACROINPUT)) + _rl_prev_macro_key (); + else + _rl_unget_char (key); + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; + return -2; + } + else if (got_subseq) + { + /* Return -1 to note that we're in a subsequence, but we don't + have a matching key, nor was one overridden. This means + we need to back up the recursion chain and find the last + subsequence that is bound to a function. */ + if (RL_ISSTATE (RL_STATE_MACROINPUT)) + _rl_prev_macro_key (); + else + _rl_unget_char (key); + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; + return -1; + } + else + { +#if defined (READLINE_CALLBACKS) + RL_UNSETSTATE (RL_STATE_MULTIKEY); + _rl_keyseq_chain_dispose (); +#endif + _rl_abort_internal (); + return -1; + } + break; + + case ISKMAP: + if (map[key].function != 0) + { +#if defined (VI_MODE) + /* The only way this test will be true is if a subsequence has been + bound starting with ESC, generally the arrow keys. What we do is + check whether there's input in the queue, which there generally + will be if an arrow key has been pressed, and, if there's not, + just dispatch to (what we assume is) rl_vi_movement_mode right + away. This is essentially an input test with a zero timeout (by + default) or a timeout determined by the value of `keyseq-timeout' */ + /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued + takes microseconds, so multiply by 1000 */ + if (rl_editing_mode == vi_mode && key == ESC && map == vi_insertion_keymap && + (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) && + _rl_pushed_input_available () == 0 && + _rl_input_queued ((_rl_keyseq_timeout > 0) ? _rl_keyseq_timeout*1000 : 0) == 0) + return (_rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key))); + /* This is a very specific test. It can possibly be generalized in + the future, but for now it handles a specific case of ESC being + the last character in a keyboard macro. */ + if (rl_editing_mode == vi_mode && key == ESC && map == vi_insertion_keymap && + (RL_ISSTATE (RL_STATE_INPUTPENDING) == 0) && + (RL_ISSTATE (RL_STATE_MACROINPUT) && _rl_peek_macro_key () == 0) && + _rl_pushed_input_available () == 0 && + _rl_input_queued ((_rl_keyseq_timeout > 0) ? _rl_keyseq_timeout*1000 : 0) == 0) + return (_rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key))); +#endif + + RESIZE_KEYSEQ_BUFFER (); + rl_executing_keyseq[rl_key_sequence_length++] = key; + _rl_dispatching_keymap = FUNCTION_TO_KEYMAP (map, key); + + /* Allocate new context here. Use linked contexts (linked through + cxt->ocxt) to simulate recursion */ +#if defined (READLINE_CALLBACKS) +# if defined (VI_MODE) + /* If we're redoing a vi mode command and we know there is a shadowed + function corresponding to this key, just call it -- all the redoable + vi mode commands already have all the input they need, and rl_vi_redo + assumes that one call to rl_dispatch is sufficient to complete the + command. */ + if (_rl_vi_redoing && RL_ISSTATE (RL_STATE_CALLBACK) && + map[ANYOTHERKEY].function != 0) + return (_rl_subseq_result (-2, map, key, got_subseq)); +# endif + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + /* Return 0 only the first time, to indicate success to + _rl_callback_read_char. The rest of the time, we're called + from _rl_dispatch_callback, so we return -3 to indicate + special handling is necessary. */ + r = RL_ISSTATE (RL_STATE_MULTIKEY) ? -3 : 0; + cxt = _rl_keyseq_cxt_alloc (); + + if (got_subseq) + cxt->flags |= KSEQ_SUBSEQ; + cxt->okey = key; + cxt->oldmap = map; + cxt->dmap = _rl_dispatching_keymap; + cxt->subseq_arg = got_subseq || cxt->dmap[ANYOTHERKEY].function; + + RL_SETSTATE (RL_STATE_MULTIKEY); + _rl_kscxt = cxt; + + return r; /* don't indicate immediate success */ + } +#endif + + /* Tentative inter-character timeout for potential multi-key + sequences? If no input within timeout, abort sequence and + act as if we got non-matching input. */ + /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued + takes microseconds, so multiply by 1000 */ + if (_rl_keyseq_timeout > 0 && + (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) && + _rl_pushed_input_available () == 0 && + _rl_dispatching_keymap[ANYOTHERKEY].function && + _rl_input_queued (_rl_keyseq_timeout*1000) == 0) + { + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; + return (_rl_subseq_result (-2, map, key, got_subseq)); + } + + newkey = _rl_subseq_getchar (key); + if (newkey < 0) + { + _rl_abort_internal (); + return -1; + } + + r = _rl_dispatch_subseq (newkey, _rl_dispatching_keymap, got_subseq || map[ANYOTHERKEY].function); + return _rl_subseq_result (r, map, key, got_subseq); + } + else + { + _rl_abort_internal (); /* XXX */ + return -1; + } + break; + + case ISMACR: + if (map[key].function != 0) + { + rl_executing_keyseq[rl_key_sequence_length] = '\0'; + macro = savestring ((char *)map[key].function); + _rl_with_macro_input (macro); + return 0; + } + break; + } + +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap && + key != ANYOTHERKEY && + _rl_dispatching_keymap == vi_movement_keymap && + _rl_vi_textmod_command (key)) + _rl_vi_set_last (key, rl_numeric_arg, rl_arg_sign); +#endif + + return (r); +} + +static int +_rl_subseq_result (int r, Keymap map, int key, int got_subseq) +{ + Keymap m; + int type, nt; + rl_command_func_t *func, *nf; + + if (r == -2) + /* We didn't match anything, and the keymap we're indexed into + shadowed a function previously bound to that prefix. Call + the function. The recursive call to _rl_dispatch_subseq has + already taken care of pushing any necessary input back onto + the input queue with _rl_unget_char. */ + { + m = _rl_dispatching_keymap; + type = m[ANYOTHERKEY].type; + func = m[ANYOTHERKEY].function; + if (type == ISFUNC && func == rl_do_lowercase_version) + r = _rl_dispatch (_rl_to_lower ((unsigned char)key), map); + else if (type == ISFUNC) + { + /* If we shadowed a function, whatever it is, we somehow need a + keymap with map[key].func == shadowed-function. + Let's use this one. Then we can dispatch using the original + key, since there are commands (e.g., in vi mode) for which it + matters. */ + nt = m[key].type; + nf = m[key].function; + + m[key].type = type; + m[key].function = func; + /* Don't change _rl_dispatching_keymap, set it here */ + _rl_dispatching_keymap = map; /* previous map */ + r = _rl_dispatch_subseq (key, m, 0); + m[key].type = nt; + m[key].function = nf; + } + else + /* We probably shadowed a keymap, so keep going. */ + r = _rl_dispatch (ANYOTHERKEY, m); + } + else if (r < 0 && map[ANYOTHERKEY].function) + { + /* We didn't match (r is probably -1), so return something to + tell the caller that it should try ANYOTHERKEY for an + overridden function. */ + if (RL_ISSTATE (RL_STATE_MACROINPUT)) + _rl_prev_macro_key (); + else + _rl_unget_char (key); + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; + _rl_dispatching_keymap = map; + return -2; + } + else if (r < 0 && got_subseq) /* XXX */ + { + /* OK, back up the chain. */ + if (RL_ISSTATE (RL_STATE_MACROINPUT)) + _rl_prev_macro_key (); + else + _rl_unget_char (key); + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; + _rl_dispatching_keymap = map; + return -1; + } + + return r; +} + +/* **************************************************************** */ +/* */ +/* Initializations */ +/* */ +/* **************************************************************** */ + +/* Initialize readline (and terminal if not already). */ +int +rl_initialize (void) +{ + /* Initialize the timeout first to get the precise start time. */ + _rl_timeout_init (); + + /* If we have never been called before, initialize the + terminal and data structures. */ + if (rl_initialized == 0) + { + RL_SETSTATE(RL_STATE_INITIALIZING); + readline_initialize_everything (); + RL_UNSETSTATE(RL_STATE_INITIALIZING); + rl_initialized++; + RL_SETSTATE(RL_STATE_INITIALIZED); + } + else + _rl_reset_locale (); /* check current locale and set locale variables */ + + /* Initialize the current line information. */ + _rl_init_line_state (); + + /* We aren't done yet. We haven't even gotten started yet! */ + rl_done = 0; + RL_UNSETSTATE(RL_STATE_DONE|RL_STATE_TIMEOUT|RL_STATE_EOF); + + /* Tell the history routines what is going on. */ + _rl_start_using_history (); + + /* Make the display buffer match the state of the line. */ + rl_reset_line_state (); + + /* No such function typed yet. */ + rl_last_func = (rl_command_func_t *)NULL; + + /* Parsing of key-bindings begins in an enabled state. */ + _rl_parsing_conditionalized_out = 0; + +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + _rl_vi_initialize_line (); +#endif + + /* Each line starts in insert mode (the default). */ + _rl_set_insert_mode (RL_IM_DEFAULT, 1); + + return 0; +} + +#if 0 +#if defined (__EMX__) +static void +_emx_build_environ (void) +{ + TIB *tibp; + PIB *pibp; + char *t, **tp; + int c; + + DosGetInfoBlocks (&tibp, &pibp); + t = pibp->pib_pchenv; + for (c = 1; *t; c++) + t += strlen (t) + 1; + tp = environ = (char **)xmalloc ((c + 1) * sizeof (char *)); + t = pibp->pib_pchenv; + while (*t) + { + *tp++ = t; + t += strlen (t) + 1; + } + *tp = 0; +} +#endif /* __EMX__ */ +#endif + +/* Initialize the entire state of the world. */ +static void +readline_initialize_everything (void) +{ +#if 0 +#if defined (__EMX__) + if (environ == 0) + _emx_build_environ (); +#endif +#endif + +#if 0 + /* Find out if we are running in Emacs -- UNUSED. */ + running_in_emacs = sh_get_env_value ("EMACS") != (char *)0; +#endif + + /* Set up input and output if they are not already set up. */ + if (!rl_instream) + rl_instream = stdin; + + if (!rl_outstream) + rl_outstream = stdout; + + /* Bind _rl_in_stream and _rl_out_stream immediately. These values + may change, but they may also be used before readline_internal () + is called. */ + _rl_in_stream = rl_instream; + _rl_out_stream = rl_outstream; + + /* Allocate data structures. */ + if (rl_line_buffer == 0) + rl_line_buffer = (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE); + + /* Initialize the terminal interface. */ + if (rl_terminal_name == 0) + rl_terminal_name = sh_get_env_value ("TERM"); + _rl_init_terminal_io (rl_terminal_name); + + /* Bind tty characters to readline functions. */ + readline_default_bindings (); + + /* Initialize the function names. */ + rl_initialize_funmap (); + + /* Decide whether we should automatically go into eight-bit mode. */ + _rl_init_eightbit (); + + /* Read in the init file. */ + rl_read_init_file ((char *)NULL); + + /* XXX */ + if (_rl_horizontal_scroll_mode && _rl_term_autowrap) + { + _rl_screenwidth--; + _rl_screenchars -= _rl_screenheight; + } + + /* Override the effect of any `set keymap' assignments in the + inputrc file. */ + rl_set_keymap_from_edit_mode (); + + /* Try to bind a common arrow key prefix, if not already bound. */ + bind_arrow_keys (); + + /* Bind the bracketed paste prefix assuming that the user will enable + it on terminals that support it. */ + bind_bracketed_paste_prefix (); + + /* If the completion parser's default word break characters haven't + been set yet, then do so now. */ + if (rl_completer_word_break_characters == 0) + rl_completer_word_break_characters = rl_basic_word_break_characters; + +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats || _rl_colored_completion_prefix) + _rl_parse_colors (); +#endif + + rl_executing_keyseq = malloc (_rl_executing_keyseq_size = 16); + if (rl_executing_keyseq) + rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; +} + +/* If this system allows us to look at the values of the regular + input editing characters, then bind them to their readline + equivalents, iff the characters are not bound to keymaps. */ +static void +readline_default_bindings (void) +{ + if (_rl_bind_stty_chars) + rl_tty_set_default_bindings (_rl_keymap); +} + +/* Reset the default bindings for the terminal special characters we're + interested in back to rl_insert and read the new ones. */ +static void +reset_default_bindings (void) +{ + if (_rl_bind_stty_chars) + { + rl_tty_unset_default_bindings (_rl_keymap); + rl_tty_set_default_bindings (_rl_keymap); + } +} + +/* Bind some common arrow key sequences in MAP. */ +static void +bind_arrow_keys_internal (Keymap map) +{ + Keymap xkeymap; + + xkeymap = _rl_keymap; + _rl_keymap = map; + +#if defined (__MSDOS__) + rl_bind_keyseq_if_unbound ("\033[0A", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033[0B", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033[0C", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033[0D", rl_get_next_history); +#endif + + rl_bind_keyseq_if_unbound ("\033[A", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033[B", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\033[C", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033[D", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033[H", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\033[F", rl_end_of_line); + + rl_bind_keyseq_if_unbound ("\033OA", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033OB", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\033OC", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033OD", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033OH", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\033OF", rl_end_of_line); + + /* Key bindings for control-arrow keys */ + rl_bind_keyseq_if_unbound ("\033[1;5C", rl_forward_word); + rl_bind_keyseq_if_unbound ("\033[1;5D", rl_backward_word); + rl_bind_keyseq_if_unbound ("\033[3;5~", rl_kill_word); + + /* Key bindings for alt-arrow keys */ + rl_bind_keyseq_if_unbound ("\033[1;3C", rl_forward_word); + rl_bind_keyseq_if_unbound ("\033[1;3D", rl_backward_word); + +#if defined (__MINGW32__) + rl_bind_keyseq_if_unbound ("\340H", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\340P", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\340M", rl_forward_char); + rl_bind_keyseq_if_unbound ("\340K", rl_backward_char); + rl_bind_keyseq_if_unbound ("\340G", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\340O", rl_end_of_line); + rl_bind_keyseq_if_unbound ("\340S", rl_delete); + rl_bind_keyseq_if_unbound ("\340R", rl_overwrite_mode); + + /* These may or may not work because of the embedded NUL. */ + rl_bind_keyseq_if_unbound ("\\000H", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\\000P", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\\000M", rl_forward_char); + rl_bind_keyseq_if_unbound ("\\000K", rl_backward_char); + rl_bind_keyseq_if_unbound ("\\000G", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\\000O", rl_end_of_line); + rl_bind_keyseq_if_unbound ("\\000S", rl_delete); + rl_bind_keyseq_if_unbound ("\\000R", rl_overwrite_mode); +#endif + + _rl_keymap = xkeymap; +} + +/* Try and bind the common arrow key prefixes after giving termcap and + the inputrc file a chance to bind them and create `real' keymaps + for the arrow key prefix. */ +static void +bind_arrow_keys (void) +{ + bind_arrow_keys_internal (emacs_standard_keymap); + +#if defined (VI_MODE) + bind_arrow_keys_internal (vi_movement_keymap); + /* Unbind vi_movement_keymap[ESC] to allow users to repeatedly hit ESC + in vi command mode while still allowing the arrow keys to work. */ + if (vi_movement_keymap[ESC].type == ISKMAP) + rl_bind_keyseq_in_map ("\033", (rl_command_func_t *)NULL, vi_movement_keymap); + bind_arrow_keys_internal (vi_insertion_keymap); +#endif +} + +static void +bind_bracketed_paste_prefix (void) +{ + Keymap xkeymap; + + xkeymap = _rl_keymap; + + _rl_keymap = emacs_standard_keymap; + rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin); + +#if defined (VI_MODE) + _rl_keymap = vi_insertion_keymap; + rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin); + /* XXX - is there a reason to do this in the vi command keymap? */ +#endif + + _rl_keymap = xkeymap; +} + +/* **************************************************************** */ +/* */ +/* Saving and Restoring Readline's state */ +/* */ +/* **************************************************************** */ + +int +rl_save_state (struct readline_state *sp) +{ + if (sp == 0) + return -1; + + sp->point = rl_point; + sp->end = rl_end; + sp->mark = rl_mark; + sp->buffer = rl_line_buffer; + sp->buflen = rl_line_buffer_len; + sp->ul = rl_undo_list; + sp->prompt = rl_prompt; + + sp->rlstate = rl_readline_state; + sp->done = rl_done; + sp->kmap = _rl_keymap; + + sp->lastfunc = rl_last_func; + sp->insmode = rl_insert_mode; + sp->edmode = rl_editing_mode; + sp->kseq = rl_executing_keyseq; + sp->kseqlen = rl_key_sequence_length; + sp->inf = rl_instream; + sp->outf = rl_outstream; + sp->pendingin = rl_pending_input; + sp->macro = rl_executing_macro; + + sp->catchsigs = rl_catch_signals; + sp->catchsigwinch = rl_catch_sigwinch; + + sp->entryfunc = rl_completion_entry_function; + sp->menuentryfunc = rl_menu_completion_entry_function; + sp->ignorefunc = rl_ignore_some_completions_function; + sp->attemptfunc = rl_attempted_completion_function; + sp->wordbreakchars = rl_completer_word_break_characters; + + return (0); +} + +int +rl_restore_state (struct readline_state *sp) +{ + if (sp == 0) + return -1; + + rl_point = sp->point; + rl_end = sp->end; + rl_mark = sp->mark; + the_line = rl_line_buffer = sp->buffer; + rl_line_buffer_len = sp->buflen; + rl_undo_list = sp->ul; + rl_prompt = sp->prompt; + + rl_readline_state = sp->rlstate; + rl_done = sp->done; + _rl_keymap = sp->kmap; + + rl_last_func = sp->lastfunc; + rl_insert_mode = sp->insmode; + rl_editing_mode = sp->edmode; + rl_executing_keyseq = sp->kseq; + rl_key_sequence_length = sp->kseqlen; + rl_instream = sp->inf; + rl_outstream = sp->outf; + rl_pending_input = sp->pendingin; + rl_executing_macro = sp->macro; + + rl_catch_signals = sp->catchsigs; + rl_catch_sigwinch = sp->catchsigwinch; + + rl_completion_entry_function = sp->entryfunc; + rl_menu_completion_entry_function = sp->menuentryfunc; + rl_ignore_some_completions_function = sp->ignorefunc; + rl_attempted_completion_function = sp->attemptfunc; + rl_completer_word_break_characters = sp->wordbreakchars; + + rl_deactivate_mark (); + + return (0); +} + +/* Functions to manage the string that is the current key sequence. */ + +void +_rl_init_executing_keyseq (void) +{ + rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; +} + +void +_rl_term_executing_keyseq (void) +{ + rl_executing_keyseq[rl_key_sequence_length] = '\0'; +} + +void +_rl_end_executing_keyseq (void) +{ + if (rl_key_sequence_length > 0) + rl_executing_keyseq[--rl_key_sequence_length] = '\0'; +} + +void +_rl_add_executing_keyseq (int key) +{ + RESIZE_KEYSEQ_BUFFER (); + rl_executing_keyseq[rl_key_sequence_length++] = key; +} + +/* `delete' the last character added to the executing key sequence. Use this + before calling rl_execute_next to avoid keys being added twice. */ +void +_rl_del_executing_keyseq (void) +{ + if (rl_key_sequence_length > 0) + rl_key_sequence_length--; +} diff --git a/lib/readline/readline.h b/lib/readline/readline.h new file mode 100644 index 0000000..cac269f --- /dev/null +++ b/lib/readline/readline.h @@ -0,0 +1,986 @@ +/* Readline.h -- the names of functions callable from within readline. */ + +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_READLINE_H_) +#define _READLINE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#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 0x0802 /* Readline 8.2 */ +#define RL_VERSION_MAJOR 8 +#define RL_VERSION_MINOR 2 + +/* Readline data structures. */ + +/* Maintaining the state of undo. We remember individual deletes and inserts + on a chain of things to do. */ + +/* The actions that undo knows how to undo. Notice that UNDO_DELETE means + to insert some text, and UNDO_INSERT means to delete some text. I.e., + the code tells undo what to undo, not how to undo it. */ +enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END }; + +/* What an element of THE_UNDO_LIST looks like. */ +typedef struct undo_list { + struct undo_list *next; + int start, end; /* Where the change took place. */ + char *text; /* The text to insert, if undoing a delete. */ + enum undo_code what; /* Delete, Insert, Begin, End. */ +} UNDO_LIST; + +/* The current undo list for RL_LINE_BUFFER. */ +extern UNDO_LIST *rl_undo_list; + +/* The data structure for mapping textual names to code addresses. */ +typedef struct _funmap { + const char *name; + rl_command_func_t *function; +} FUNMAP; + +extern FUNMAP **funmap; + +/* **************************************************************** */ +/* */ +/* Functions available to bind to key sequences */ +/* */ +/* **************************************************************** */ + +/* Bindable commands for numeric arguments. */ +extern int rl_digit_argument (int, int); +extern int rl_universal_argument (int, int); + +/* Bindable commands for moving the cursor. */ +extern int rl_forward_byte (int, int); +extern int rl_forward_char (int, int); +extern int rl_forward (int, int); +extern int rl_backward_byte (int, int); +extern int rl_backward_char (int, int); +extern int rl_backward (int, int); +extern int rl_beg_of_line (int, int); +extern int rl_end_of_line (int, int); +extern int rl_forward_word (int, int); +extern int rl_backward_word (int, int); +extern int rl_refresh_line (int, int); +extern int rl_clear_screen (int, int); +extern int rl_clear_display (int, int); +extern int rl_skip_csi_sequence (int, int); +extern int rl_arrow_keys (int, int); + +extern int rl_previous_screen_line (int, int); +extern int rl_next_screen_line (int, int); + +/* Bindable commands for inserting and deleting text. */ +extern int rl_insert (int, int); +extern int rl_quoted_insert (int, int); +extern int rl_tab_insert (int, int); +extern int rl_newline (int, int); +extern int rl_do_lowercase_version (int, int); +extern int rl_rubout (int, int); +extern int rl_delete (int, int); +extern int rl_rubout_or_delete (int, int); +extern int rl_delete_horizontal_space (int, int); +extern int rl_delete_or_show_completions (int, int); +extern int rl_insert_comment (int, int); + +/* Bindable commands for changing case. */ +extern int rl_upcase_word (int, int); +extern int rl_downcase_word (int, int); +extern int rl_capitalize_word (int, int); + +/* Bindable commands for transposing characters and words. */ +extern int rl_transpose_words (int, int); +extern int rl_transpose_chars (int, int); + +/* Bindable commands for searching within a line. */ +extern int rl_char_search (int, int); +extern int rl_backward_char_search (int, int); + +/* Bindable commands for readline's interface to the command history. */ +extern int rl_beginning_of_history (int, int); +extern int rl_end_of_history (int, int); +extern int rl_get_next_history (int, int); +extern int rl_get_previous_history (int, int); +extern int rl_operate_and_get_next (int, int); +extern int rl_fetch_history (int, int); + +/* Bindable commands for managing the mark and region. */ +extern int rl_set_mark (int, int); +extern int rl_exchange_point_and_mark (int, int); + +/* Bindable commands to set the editing mode (emacs or vi). */ +extern int rl_vi_editing_mode (int, int); +extern int rl_emacs_editing_mode (int, int); + +/* Bindable commands to change the insert mode (insert or overwrite) */ +extern int rl_overwrite_mode (int, int); + +/* Bindable commands for managing key bindings. */ +extern int rl_re_read_init_file (int, int); +extern int rl_dump_functions (int, int); +extern int rl_dump_macros (int, int); +extern int rl_dump_variables (int, int); + +/* Bindable commands for word completion. */ +extern int rl_complete (int, int); +extern int rl_possible_completions (int, int); +extern int rl_insert_completions (int, int); +extern int rl_old_menu_complete (int, int); +extern int rl_menu_complete (int, int); +extern int rl_backward_menu_complete (int, int); + +/* Bindable commands for killing and yanking text, and managing the kill ring. */ +extern int rl_kill_word (int, int); +extern int rl_backward_kill_word (int, int); +extern int rl_kill_line (int, int); +extern int rl_backward_kill_line (int, int); +extern int rl_kill_full_line (int, int); +extern int rl_unix_word_rubout (int, int); +extern int rl_unix_filename_rubout (int, int); +extern int rl_unix_line_discard (int, int); +extern int rl_copy_region_to_kill (int, int); +extern int rl_kill_region (int, int); +extern int rl_copy_forward_word (int, int); +extern int rl_copy_backward_word (int, int); +extern int rl_yank (int, int); +extern int rl_yank_pop (int, int); +extern int rl_yank_nth_arg (int, int); +extern int rl_yank_last_arg (int, int); +extern int rl_bracketed_paste_begin (int, int); +/* Not available unless _WIN32 is defined. */ +#if defined (_WIN32) +extern int rl_paste_from_clipboard (int, int); +#endif + +/* Bindable commands for incremental searching. */ +extern int rl_reverse_search_history (int, int); +extern int rl_forward_search_history (int, int); + +/* Bindable keyboard macro commands. */ +extern int rl_start_kbd_macro (int, int); +extern int rl_end_kbd_macro (int, int); +extern int rl_call_last_kbd_macro (int, int); +extern int rl_print_last_kbd_macro (int, int); + +/* Bindable undo commands. */ +extern int rl_revert_line (int, int); +extern int rl_undo_command (int, int); + +/* Bindable tilde expansion commands. */ +extern int rl_tilde_expand (int, int); + +/* Bindable terminal control commands. */ +extern int rl_restart_output (int, int); +extern int rl_stop_output (int, int); + +/* Miscellaneous bindable commands. */ +extern int rl_abort (int, int); +extern int rl_tty_status (int, int); + +/* Bindable commands for incremental and non-incremental history searching. */ +extern int rl_history_search_forward (int, int); +extern int rl_history_search_backward (int, int); +extern int rl_history_substr_search_forward (int, int); +extern int rl_history_substr_search_backward (int, int); +extern int rl_noninc_forward_search (int, int); +extern int rl_noninc_reverse_search (int, int); +extern int rl_noninc_forward_search_again (int, int); +extern int rl_noninc_reverse_search_again (int, int); + +/* Bindable command used when inserting a matching close character. */ +extern int rl_insert_close (int, int); + +/* Not available unless READLINE_CALLBACKS is defined. */ +extern void rl_callback_handler_install (const char *, rl_vcpfunc_t *); +extern void rl_callback_read_char (void); +extern void rl_callback_handler_remove (void); +extern void rl_callback_sigcleanup (void); + +/* Things for vi mode. Not available unless readline is compiled -DVI_MODE. */ +/* VI-mode bindable commands. */ +extern int rl_vi_redo (int, int); +extern int rl_vi_undo (int, int); +extern int rl_vi_yank_arg (int, int); +extern int rl_vi_fetch_history (int, int); +extern int rl_vi_search_again (int, int); +extern int rl_vi_search (int, int); +extern int rl_vi_complete (int, int); +extern int rl_vi_tilde_expand (int, int); +extern int rl_vi_prev_word (int, int); +extern int rl_vi_next_word (int, int); +extern int rl_vi_end_word (int, int); +extern int rl_vi_insert_beg (int, int); +extern int rl_vi_append_mode (int, int); +extern int rl_vi_append_eol (int, int); +extern int rl_vi_eof_maybe (int, int); +extern int rl_vi_insertion_mode (int, int); +extern int rl_vi_insert_mode (int, int); +extern int rl_vi_movement_mode (int, int); +extern int rl_vi_arg_digit (int, int); +extern int rl_vi_change_case (int, int); +extern int rl_vi_put (int, int); +extern int rl_vi_column (int, int); +extern int rl_vi_delete_to (int, int); +extern int rl_vi_change_to (int, int); +extern int rl_vi_yank_to (int, int); +extern int rl_vi_yank_pop (int, int); +extern int rl_vi_rubout (int, int); +extern int rl_vi_delete (int, int); +extern int rl_vi_back_to_indent (int, int); +extern int rl_vi_unix_word_rubout (int, int); +extern int rl_vi_first_print (int, int); +extern int rl_vi_char_search (int, int); +extern int rl_vi_match (int, int); +extern int rl_vi_change_char (int, int); +extern int rl_vi_subst (int, int); +extern int rl_vi_overstrike (int, int); +extern int rl_vi_overstrike_delete (int, int); +extern int rl_vi_replace (int, int); +extern int rl_vi_set_mark (int, int); +extern int rl_vi_goto_mark (int, int); + +/* VI-mode utility functions. */ +extern int rl_vi_check (void); +extern int rl_vi_domove (int, int *); +extern int rl_vi_bracktype (int); + +extern void rl_vi_start_inserting (int, int, int); + +/* VI-mode pseudo-bindable commands, used as utility functions. */ +extern int rl_vi_fWord (int, int); +extern int rl_vi_bWord (int, int); +extern int rl_vi_eWord (int, int); +extern int rl_vi_fword (int, int); +extern int rl_vi_bword (int, int); +extern int rl_vi_eword (int, int); + +/* **************************************************************** */ +/* */ +/* Well Published Functions */ +/* */ +/* **************************************************************** */ + +/* Readline functions. */ +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */ +extern char *readline (const char *); + +extern int rl_set_prompt (const char *); +extern int rl_expand_prompt (char *); + +extern int rl_initialize (void); + +/* Undocumented; unused by readline */ +extern int rl_discard_argument (void); + +/* Utility functions to bind keys to readline commands. */ +extern int rl_add_defun (const char *, rl_command_func_t *, int); +extern int rl_bind_key (int, rl_command_func_t *); +extern int rl_bind_key_in_map (int, rl_command_func_t *, Keymap); +extern int rl_unbind_key (int); +extern int rl_unbind_key_in_map (int, Keymap); +extern int rl_bind_key_if_unbound (int, rl_command_func_t *); +extern int rl_bind_key_if_unbound_in_map (int, rl_command_func_t *, Keymap); +extern int rl_unbind_function_in_map (rl_command_func_t *, Keymap); +extern int rl_unbind_command_in_map (const char *, Keymap); +extern int rl_bind_keyseq (const char *, rl_command_func_t *); +extern int rl_bind_keyseq_in_map (const char *, rl_command_func_t *, Keymap); +extern int rl_bind_keyseq_if_unbound (const char *, rl_command_func_t *); +extern int rl_bind_keyseq_if_unbound_in_map (const char *, rl_command_func_t *, Keymap); +extern int rl_generic_bind (int, const char *, char *, Keymap); + +extern char *rl_variable_value (const char *); +extern int rl_variable_bind (const char *, const char *); + +/* Backwards compatibility, use rl_bind_keyseq_in_map instead. */ +extern int rl_set_key (const char *, rl_command_func_t *, Keymap); + +/* Backwards compatibility, use rl_generic_bind instead. */ +extern int rl_macro_bind (const char *, const char *, Keymap); + +/* Undocumented in the texinfo manual; not really useful to programs. */ +extern int rl_translate_keyseq (const char *, char *, int *); +extern char *rl_untranslate_keyseq (int); + +extern rl_command_func_t *rl_named_function (const char *); +extern rl_command_func_t *rl_function_of_keyseq (const char *, Keymap, int *); +extern rl_command_func_t *rl_function_of_keyseq_len (const char *, size_t, Keymap, int *); +extern int rl_trim_arg_from_keyseq (const char *, size_t, Keymap); + +extern void rl_list_funmap_names (void); +extern char **rl_invoking_keyseqs_in_map (rl_command_func_t *, Keymap); +extern char **rl_invoking_keyseqs (rl_command_func_t *); + +extern void rl_function_dumper (int); +extern void rl_macro_dumper (int); +extern void rl_variable_dumper (int); + +extern int rl_read_init_file (const char *); +extern int rl_parse_and_bind (char *); + +/* Functions for manipulating keymaps. */ +extern Keymap rl_make_bare_keymap (void); +extern int rl_empty_keymap (Keymap); +extern Keymap rl_copy_keymap (Keymap); +extern Keymap rl_make_keymap (void); +extern void rl_discard_keymap (Keymap); +extern void rl_free_keymap (Keymap); + +extern Keymap rl_get_keymap_by_name (const char *); +extern char *rl_get_keymap_name (Keymap); +extern void rl_set_keymap (Keymap); +extern Keymap rl_get_keymap (void); + +extern int rl_set_keymap_name (const char *, Keymap); + +/* Undocumented; used internally only. */ +extern void rl_set_keymap_from_edit_mode (void); +extern char *rl_get_keymap_name_from_edit_mode (void); + +/* Functions for manipulating the funmap, which maps command names to functions. */ +extern int rl_add_funmap_entry (const char *, rl_command_func_t *); +extern const char **rl_funmap_names (void); +/* Undocumented, only used internally -- there is only one funmap, and this + function may be called only once. */ +extern void rl_initialize_funmap (void); + +/* Utility functions for managing keyboard macros. */ +extern void rl_push_macro_input (char *); + +/* Functions for undoing, from undo.c */ +extern void rl_add_undo (enum undo_code, int, int, char *); +extern void rl_free_undo_list (void); +extern int rl_do_undo (void); +extern int rl_begin_undo_group (void); +extern int rl_end_undo_group (void); +extern int rl_modifying (int, int); + +/* Functions for redisplay. */ +extern void rl_redisplay (void); +extern int rl_on_new_line (void); +extern int rl_on_new_line_with_prompt (void); +extern int rl_forced_update_display (void); +extern int rl_clear_visible_line (void); +extern int rl_clear_message (void); +extern int rl_reset_line_state (void); +extern int rl_crlf (void); + +/* Functions to manage the mark and region, especially the notion of an + active mark and an active region. */ +extern void rl_keep_mark_active (void); + +extern void rl_activate_mark (void); +extern void rl_deactivate_mark (void); +extern int rl_mark_active_p (void); + +#if defined (USE_VARARGS) && defined (PREFER_STDARG) +extern int rl_message (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +#else +extern int rl_message (); +#endif + +extern int rl_show_char (int); + +/* Undocumented in texinfo manual. */ +extern int rl_character_len (int, int); +extern void rl_redraw_prompt_last_line (void); + +/* Save and restore internal prompt redisplay information. */ +extern void rl_save_prompt (void); +extern void rl_restore_prompt (void); + +/* Modifying text. */ +extern void rl_replace_line (const char *, int); +extern int rl_insert_text (const char *); +extern int rl_delete_text (int, int); +extern int rl_kill_text (int, int); +extern char *rl_copy_text (int, int); + +/* Terminal and tty mode management. */ +extern void rl_prep_terminal (int); +extern void rl_deprep_terminal (void); +extern void rl_tty_set_default_bindings (Keymap); +extern void rl_tty_unset_default_bindings (Keymap); + +extern int rl_tty_set_echoing (int); +extern int rl_reset_terminal (const char *); +extern void rl_resize_terminal (void); +extern void rl_set_screen_size (int, int); +extern void rl_get_screen_size (int *, int *); +extern void rl_reset_screen_size (void); + +extern char *rl_get_termcap (const char *); + +/* Functions for character input. */ +extern int rl_stuff_char (int); +extern int rl_execute_next (int); +extern int rl_clear_pending_input (void); +extern int rl_read_key (void); +extern int rl_getc (FILE *); +extern int rl_set_keyboard_input_timeout (int); + +/* Functions to set and reset timeouts. */ +extern int rl_set_timeout (unsigned int, unsigned int); +extern int rl_timeout_remaining (unsigned int *, unsigned int *); + +#undef rl_clear_timeout +#define rl_clear_timeout() rl_set_timeout (0, 0) + +/* `Public' utility functions . */ +extern void rl_extend_line_buffer (int); +extern int rl_ding (void); +extern int rl_alphabetic (int); +extern void rl_free (void *); + +/* Readline signal handling, from signals.c */ +extern int rl_set_signals (void); +extern int rl_clear_signals (void); +extern void rl_cleanup_after_signal (void); +extern void rl_reset_after_signal (void); +extern void rl_free_line_state (void); + +extern int rl_pending_signal (void); +extern void rl_check_signals (void); + +extern void rl_echo_signal_char (int); + +extern int rl_set_paren_blink_timeout (int); + +/* History management functions. */ + +extern void rl_clear_history (void); + +/* Undocumented. */ +extern int rl_maybe_save_line (void); +extern int rl_maybe_unsave_line (void); +extern int rl_maybe_replace_line (void); + +/* Completion functions. */ +extern int rl_complete_internal (int); +extern void rl_display_match_list (char **, int, int); + +extern char **rl_completion_matches (const char *, rl_compentry_func_t *); +extern char *rl_username_completion_function (const char *, int); +extern char *rl_filename_completion_function (const char *, int); + +extern int rl_completion_mode (rl_command_func_t *); + +#if 0 +/* Backwards compatibility (compat.c). These will go away sometime. */ +extern void free_undo_list (void); +extern int maybe_save_line (void); +extern int maybe_unsave_line (void); +extern int maybe_replace_line (void); + +extern int ding (void); +extern int alphabetic (int); +extern int crlf (void); + +extern char **completion_matches (char *, rl_compentry_func_t *); +extern char *username_completion_function (const char *, int); +extern char *filename_completion_function (const char *, int); +#endif + +/* **************************************************************** */ +/* */ +/* Well Published Variables */ +/* */ +/* **************************************************************** */ + +/* The version of this incarnation of the readline library. */ +extern const char *rl_library_version; /* e.g., "4.2" */ +extern int rl_readline_version; /* e.g., 0x0402 */ + +/* True if this is real GNU readline. */ +extern int rl_gnu_readline_p; + +/* Flags word encapsulating the current readline state. */ +extern unsigned long rl_readline_state; + +/* Says which editing mode readline is currently using. 1 means emacs mode; + 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; + +/* The prompt readline uses. This is set from the argument to + readline (), and should not be assigned to directly. */ +extern char *rl_prompt; + +/* The prompt string that is actually displayed by rl_redisplay. Public so + applications can more easily supply their own redisplay functions. */ +extern char *rl_display_prompt; + +/* The line buffer that is in use. */ +extern char *rl_line_buffer; + +/* The location of point, and end. */ +extern int rl_point; +extern int rl_end; + +/* The mark, or saved cursor position. */ +extern int rl_mark; + +/* Flag to indicate that readline has finished with the current input + line and should return it. */ +extern int rl_done; + +/* Flag to indicate that readline has read an EOF character or read has + returned 0 or error, and is returning a NULL line as a result. */ +extern int rl_eof_found; + +/* If set to a character value, that will be the next keystroke read. */ +extern int rl_pending_input; + +/* Non-zero if we called this function from _rl_dispatch(). It's present + so functions can find out whether they were called from a key binding + or directly from an application. */ +extern int rl_dispatching; + +/* Non-zero if the user typed a numeric argument before executing the + current function. */ +extern int rl_explicit_arg; + +/* The current value of the numeric argument specified by the user. */ +extern int rl_numeric_arg; + +/* The address of the last command function Readline executed. */ +extern rl_command_func_t *rl_last_func; + +/* The name of the terminal to use. */ +extern const char *rl_terminal_name; + +/* The input and output streams. */ +extern FILE *rl_instream; +extern FILE *rl_outstream; + +/* If non-zero, Readline gives values of LINES and COLUMNS from the environment + greater precedence than values fetched from the kernel when computing the + screen dimensions. */ +extern int rl_prefer_env_winsize; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +extern rl_hook_func_t *rl_startup_hook; + +/* If non-zero, this is the address of a function to call just before + readline_internal_setup () returns and readline_internal starts + reading input characters. */ +extern rl_hook_func_t *rl_pre_input_hook; + +/* The address of a function to call periodically while Readline is + awaiting character input, or NULL, for no event handling. */ +extern rl_hook_func_t *rl_event_hook; + +/* The address of a function to call if a read is interrupted by a signal. */ +extern rl_hook_func_t *rl_signal_event_hook; + +extern rl_hook_func_t *rl_timeout_event_hook; + +/* The address of a function to call if Readline needs to know whether or not + there is data available from the current input source. */ +extern rl_hook_func_t *rl_input_available_hook; + +/* The address of the function to call to fetch a character from the current + Readline input stream */ +extern rl_getc_func_t *rl_getc_function; + +extern rl_voidfunc_t *rl_redisplay_function; + +extern rl_vintfunc_t *rl_prep_term_function; +extern rl_voidfunc_t *rl_deprep_term_function; + +/* Dispatch variables. */ +extern Keymap rl_executing_keymap; +extern Keymap rl_binding_keymap; + +extern int rl_executing_key; +extern char *rl_executing_keyseq; +extern int rl_key_sequence_length; + +/* Display variables. */ +/* If non-zero, readline will erase the entire line, including any prompt, + if the only thing typed on an otherwise-blank line is something bound to + rl_newline. */ +extern int rl_erase_empty_line; + +/* If non-zero, the application has already printed the prompt (rl_prompt) + before calling readline, so readline should not output it the first time + redisplay is done. */ +extern int rl_already_prompted; + +/* A non-zero value means to read only this many characters rather than + up to a character bound to accept-line. */ +extern int rl_num_chars_to_read; + +/* The text of a currently-executing keyboard macro. */ +extern char *rl_executing_macro; + +/* Variables to control readline signal handling. */ +/* If non-zero, readline will install its own signal handlers for + SIGINT, SIGTERM, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */ +extern int rl_catch_signals; + +/* If non-zero, readline will install a signal handler for SIGWINCH + that also attempts to call any calling application's SIGWINCH signal + handler. Note that the terminal is not cleaned up before the + application's signal handler is called; use rl_cleanup_after_signal() + to do that. */ +extern int rl_catch_sigwinch; + +/* If non-zero, the readline SIGWINCH handler will modify LINES and + COLUMNS in the environment. */ +extern int rl_change_environment; + +/* Completion variables. */ +/* Pointer to the generator function for completion_matches (). + NULL means to use rl_filename_completion_function (), the default + filename completer. */ +extern rl_compentry_func_t *rl_completion_entry_function; + +/* Optional generator for menu completion. Default is + rl_completion_entry_function (rl_filename_completion_function). */ +extern rl_compentry_func_t *rl_menu_completion_entry_function; + +/* If rl_ignore_some_completions_function is non-NULL it is the address + of a function to call after all of the possible matches have been + generated, but before the actual completion is done to the input line. + The function is called with one argument; a NULL terminated array + of (char *). If your function removes any of the elements, they + must be free()'ed. */ +extern rl_compignore_func_t *rl_ignore_some_completions_function; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +extern rl_completion_func_t *rl_attempted_completion_function; + +/* The basic list of characters that signal a break between words for the + completer routine. The initial contents of this variable is what + breaks words in the shell, i.e. "n\"\\'`@$>". */ +extern const char *rl_basic_word_break_characters; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +extern const char *rl_completer_word_break_characters; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +extern rl_cpvfunc_t *rl_completion_word_break_hook; + +/* List of characters which can be used to quote a substring of the line. + Completion occurs on the entire substring, and within the substring + rl_completer_word_break_characters are treated as any other character, + unless they also appear within this list. */ +extern const char *rl_completer_quote_characters; + +/* List of quote characters which cause a word break. */ +extern const char *rl_basic_quote_characters; + +/* List of characters that need to be quoted in filenames by the completer. */ +extern const char *rl_filename_quote_characters; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +extern const char *rl_special_prefixes; + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. It + changes what is displayed when the possible completions are printed + or inserted. The directory completion hook should perform + any necessary dequoting. This function should return 1 if it modifies + the directory name pointer passed as an argument. If the directory + completion hook returns 0, it should not modify the directory name + pointer passed as an argument. */ +extern rl_icppfunc_t *rl_directory_completion_hook; + +/* If non-zero, this is the address of a function to call when completing + a directory name. This function takes the address of the directory name + to be modified as an argument. Unlike rl_directory_completion_hook, it + only modifies the directory name used in opendir(2), not what is displayed + when the possible completions are printed or inserted. If set, it takes + precedence over rl_directory_completion_hook. The directory rewrite + hook should perform any necessary dequoting. This function has the same + return value properties as the directory_completion_hook. + + I'm not happy with how this works yet, so it's undocumented. I'm trying + it in bash to see how well it goes. */ +extern rl_icppfunc_t *rl_directory_rewrite_hook; + +/* If non-zero, this is the address of a function for the completer to call + before deciding which character to append to a completed name. It should + modify the directory name passed as an argument if appropriate, and return + non-zero if it modifies the name. This should not worry about dequoting + the filename; that has already happened by the time it gets here. */ +extern rl_icppfunc_t *rl_filename_stat_hook; + +/* If non-zero, this is the address of a function to call when reading + directory entries from the filesystem for completion and comparing + them to the partial word to be completed. The function should + either return its first argument (if no conversion takes place) or + newly-allocated memory. This can, for instance, convert filenames + between character sets for comparison against what's typed at the + keyboard. The returned value is what is added to the list of + matches. The second argument is the length of the filename to be + converted. */ +extern rl_dequote_func_t *rl_filename_rewrite_hook; + +/* Backwards compatibility with previous versions of readline. */ +#define rl_symbolic_link_hook rl_directory_completion_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 instead of actually doing the display. + It takes three arguments: (char **matches, int num_matches, int max_length) + where MATCHES is the array of strings that matched, NUM_MATCHES is the + number of strings in that array, and MAX_LENGTH is the length of the + longest string in that array. */ +extern rl_compdisp_func_t *rl_completion_display_matches_hook; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +extern int rl_filename_completion_desired; + +/* Non-zero means that the results of the matches are to be quoted using + double quotes (or an application-specific quoting mechanism) if the + filename contains any characters in rl_word_break_chars. This is + ALWAYS non-zero on entry, and can only be changed within a completion + entry finder function. */ +extern int rl_filename_quoting_desired; + +/* Set to a function to quote a filename in an application-specific fashion. + Called with the text to quote, the type of match found (single or multiple) + and a pointer to the quoting character to be used, which the function can + reset if desired. */ +extern rl_quote_func_t *rl_filename_quoting_function; + +/* Function to call to remove quoting characters from a filename. Called + before completion is attempted, so the embedded quotes do not interfere + with matching names in the file system. */ +extern rl_dequote_func_t *rl_filename_dequoting_function; + +/* Function to call to decide whether or not a word break character is + quoted. If a character is quoted, it does not break words for the + completer. */ +extern rl_linebuf_func_t *rl_char_is_quoted_p; + +/* Non-zero means to suppress normal filename completion after the + user-specified completion function has been called. */ +extern int rl_attempted_completion_over; + +/* Set to a character describing the type of completion being attempted by + rl_complete_internal; available for use by application completion + functions. */ +extern int rl_completion_type; + +/* Set to the last key used to invoke one of the completion functions */ +extern int rl_completion_invoking_key; + +/* 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; + +/* Character appended to completed words when at the end of the line. The + 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; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +extern int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +extern int rl_completion_found_quote; + +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +extern int rl_completion_suppress_quote; + +/* If non-zero, readline will sort the completion matches. On by default. */ +extern int rl_sort_completion_matches; + +/* 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; + +/* Applications can set this to non-zero to have readline's signal handlers + installed during the entire duration of reading a complete line, as in + readline-6.2. This should be used with care, because it can result in + readline receiving signals and not handling them until it's called again + via rl_callback_read_char, thereby stealing them from the application. + By default, signal handlers are only active while readline is active. */ +extern int rl_persistent_signal_handlers; + +/* Input error; can be returned by (*rl_getc_function) if readline is reading + a top-level command (RL_ISSTATE (RL_STATE_READCMD)). */ +#define READERR (-2) + +/* Definitions available for use by readline clients. */ +#define RL_PROMPT_START_IGNORE '\001' +#define RL_PROMPT_END_IGNORE '\002' + +/* Possible values for do_replace argument to rl_filename_quoting_function, + called by rl_complete_internal. */ +#define NO_MATCH 0 +#define SINGLE_MATCH 1 +#define MULT_MATCH 2 + +/* Possible state values for rl_readline_state */ +#define RL_STATE_NONE 0x000000 /* no state; before first call */ + +#define RL_STATE_INITIALIZING 0x0000001 /* initializing */ +#define RL_STATE_INITIALIZED 0x0000002 /* initialization done */ +#define RL_STATE_TERMPREPPED 0x0000004 /* terminal is prepped */ +#define RL_STATE_READCMD 0x0000008 /* reading a command key */ +#define RL_STATE_METANEXT 0x0000010 /* reading input after ESC */ +#define RL_STATE_DISPATCHING 0x0000020 /* dispatching to a command */ +#define RL_STATE_MOREINPUT 0x0000040 /* reading more input in a command function */ +#define RL_STATE_ISEARCH 0x0000080 /* doing incremental search */ +#define RL_STATE_NSEARCH 0x0000100 /* doing non-inc search */ +#define RL_STATE_SEARCH 0x0000200 /* doing a history search */ +#define RL_STATE_NUMERICARG 0x0000400 /* reading numeric argument */ +#define RL_STATE_MACROINPUT 0x0000800 /* getting input from a macro */ +#define RL_STATE_MACRODEF 0x0001000 /* defining keyboard macro */ +#define RL_STATE_OVERWRITE 0x0002000 /* overwrite mode */ +#define RL_STATE_COMPLETING 0x0004000 /* doing completion */ +#define RL_STATE_SIGHANDLER 0x0008000 /* in readline sighandler */ +#define RL_STATE_UNDOING 0x0010000 /* doing an undo */ +#define RL_STATE_INPUTPENDING 0x0020000 /* rl_execute_next called */ +#define RL_STATE_TTYCSAVED 0x0040000 /* tty special chars saved */ +#define RL_STATE_CALLBACK 0x0080000 /* using the callback interface */ +#define RL_STATE_VIMOTION 0x0100000 /* reading vi motion arg */ +#define RL_STATE_MULTIKEY 0x0200000 /* reading multiple-key command */ +#define RL_STATE_VICMDONCE 0x0400000 /* entered vi command mode at least once */ +#define RL_STATE_CHARSEARCH 0x0800000 /* vi mode char search */ +#define RL_STATE_REDISPLAYING 0x1000000 /* updating terminal display */ + +#define RL_STATE_DONE 0x2000000 /* done; accepted line */ +#define RL_STATE_TIMEOUT 0x4000000 /* done; timed out */ +#define RL_STATE_EOF 0x8000000 /* done; got eof on read */ + +#define RL_SETSTATE(x) (rl_readline_state |= (x)) +#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; + int buflen; + char *buffer; + UNDO_LIST *ul; + char *prompt; + + /* global state */ + int rlstate; + int done; + Keymap kmap; + + /* input state */ + rl_command_func_t *lastfunc; + int insmode; + int edmode; + char *kseq; + int kseqlen; + + int pendingin; + FILE *inf; + FILE *outf; + char *macro; + + /* signal state */ + int catchsigs; + int catchsigwinch; + + /* search state */ + + /* completion state */ + rl_compentry_func_t *entryfunc; + rl_compentry_func_t *menuentryfunc; + rl_compignore_func_t *ignorefunc; + rl_completion_func_t *attemptfunc; + const char *wordbreakchars; + + /* options state */ + + /* hook state */ + + /* reserved for future expansion, so the struct size doesn't change */ + char reserved[64]; +}; + +extern int rl_save_state (struct readline_state *); +extern int rl_restore_state (struct readline_state *); + +#ifdef __cplusplus +} +#endif + +#endif /* _READLINE_H_ */ diff --git a/lib/readline/rlconf.h b/lib/readline/rlconf.h new file mode 100644 index 0000000..b6d6a2f --- /dev/null +++ b/lib/readline/rlconf.h @@ -0,0 +1,79 @@ +/* rlconf.h -- readline configuration definitions */ + +/* Copyright (C) 1992-2015 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLCONF_H_) +#define _RLCONF_H_ + +/* Define this if you want the vi-mode editing available. */ +#define VI_MODE + +/* Define this to get an indication of file type when listing completions. */ +#define VISIBLE_STATS + +/* Define this to get support for colors when listing completions and in + other places. */ +#define COLOR_SUPPORT + +/* This definition is needed by readline.c, rltty.c, and signals.c. */ +/* If on, then readline handles signals in a way that doesn't suck. */ +#define HANDLE_SIGNALS + +/* Ugly but working hack for binding prefix meta. */ +#define PREFIX_META_HACK + +/* The next-to-last-ditch effort file name for a user-specific init file. */ +#define DEFAULT_INPUTRC "~/.inputrc" + +/* The ultimate last-ditch filename for an init file -- system-wide. */ +#define SYS_INPUTRC "/etc/inputrc" + +/* If defined, expand tabs to spaces. */ +#define DISPLAY_TABS + +/* If defined, use the terminal escape sequence to move the cursor forward + over a character when updating the line rather than rewriting it. */ +/* #define HACK_TERMCAP_MOTION */ + +/* The string inserted by the `insert comment' command. */ +#define RL_COMMENT_BEGIN_DEFAULT "#" + +/* Define this if you want code that allows readline to be used in an + X `callback' style. */ +#define READLINE_CALLBACKS + +/* Define this if you want the cursor to indicate insert or overwrite mode. */ +/* #define CURSOR_MODE */ + +/* Define this if you want to enable code that talks to the Linux kernel + tty auditing system. */ +/* #define ENABLE_TTY_AUDIT_SUPPORT */ + +/* Defaults for the various editing mode indicators, inserted at the beginning + of the last (maybe only) line of the prompt if show-mode-in-prompt is on */ +#define RL_EMACS_MODESTR_DEFAULT "@" +#define RL_EMACS_MODESTR_DEFLEN 1 + +#define RL_VI_INS_MODESTR_DEFAULT "(ins)" +#define RL_VI_INS_MODESTR_DEFLEN 5 +#define RL_VI_CMD_MODESTR_DEFAULT "(cmd)" +#define RL_VI_CMD_MODESTR_DEFLEN 5 + +#endif /* _RLCONF_H_ */ diff --git a/lib/readline/rldefs.h b/lib/readline/rldefs.h new file mode 100644 index 0000000..98577a6 --- /dev/null +++ b/lib/readline/rldefs.h @@ -0,0 +1,166 @@ +/* rldefs.h -- an attempt to isolate some of the system-specific defines + for readline. This should be included after any files that define + system-specific constants like _POSIX_VERSION or USG. */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLDEFS_H_) +#define _RLDEFS_H_ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "rlstdc.h" + +#if defined (STRCOLL_BROKEN) +# undef HAVE_STRCOLL +#endif + +#if defined (_POSIX_VERSION) && !defined (TERMIOS_MISSING) +# define TERMIOS_TTY_DRIVER +#else +# if defined (HAVE_TERMIO_H) +# define TERMIO_TTY_DRIVER +# else +# if !defined (__MINGW32__) +# define NEW_TTY_DRIVER +# else +# define NO_TTY_DRIVER +# endif +# endif +#endif + +/* Posix macro to check file in statbuf for directory-ness. + This requires that <sys/stat.h> be included before this test. */ +#if defined (S_IFDIR) && !defined (S_ISDIR) +# define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +/* Decide which flavor of the header file describing the C library + string functions to include and include it. */ + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# if defined (PREFER_VARARGS) +# include <varargs.h> +# endif +#endif + +#if defined (HAVE_STRCASECMP) +#define _rl_stricmp strcasecmp +#define _rl_strnicmp strncasecmp +#else +extern int _rl_stricmp (const char *, const char *); +extern int _rl_strnicmp (const char *, const char *, int); +#endif + +#if defined (HAVE_STRPBRK) && !defined (HAVE_MULTIBYTE) +# define _rl_strpbrk(a,b) strpbrk((a),(b)) +#else +extern char *_rl_strpbrk (const char *, const char *); +#endif + +#if !defined (emacs_mode) +# define no_mode -1 +# define vi_mode 0 +# 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 + to functions and pointers to structs are different sizes). + This is not what is wanted. */ +#if defined (CRAY) +# define FUNCTION_TO_KEYMAP(map, key) (Keymap)((int)map[key].function) +# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)((int)(data)) +#else +# define FUNCTION_TO_KEYMAP(map, key) (Keymap)(map[key].function) +# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)(data) +#endif + +#ifndef savestring +#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x)) +#endif + +/* Possible values for _rl_bell_preference. */ +#define NO_BELL 0 +#define AUDIBLE_BELL 1 +#define VISIBLE_BELL 2 + +/* Definitions used when searching the line for characters. */ +/* NOTE: it is necessary that opposite directions are inverses */ +#define FTO 1 /* forward to */ +#define BTO -1 /* backward to */ +#define FFIND 2 /* forward find */ +#define BFIND -2 /* backward find */ + +/* 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 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 + +#if !defined (STREQ) +#define STREQ(a, b) (((a)[0] == (b)[0]) && (strcmp ((a), (b)) == 0)) +#define STREQN(a, b, n) (((n) == 0) ? (1) \ + : ((a)[0] == (b)[0]) && (strncmp ((a), (b), (n)) == 0)) +#endif + +#if !defined (RL_STRLEN) +# define RL_STRLEN(s) (((s) && (s)[0]) ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0) +#endif + +#if !defined (FREE) +# 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" + +#endif /* !_RLDEFS_H_ */ diff --git a/lib/readline/rlmbutil.h b/lib/readline/rlmbutil.h new file mode 100644 index 0000000..d906057 --- /dev/null +++ b/lib/readline/rlmbutil.h @@ -0,0 +1,225 @@ +/* rlmbutil.h -- utility functions for multibyte characters. */ + +/* Copyright (C) 2001-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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 amendment 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) && defined (HAVE_LOCALE_H) +# include <wchar.h> +# include <wctype.h> +# if defined (HAVE_ISWCTYPE) && \ + defined (HAVE_ISWLOWER) && \ + defined (HAVE_ISWUPPER) && \ + defined (HAVE_MBSRTOWCS) && \ + defined (HAVE_MBRTOWC) && \ + defined (HAVE_MBRLEN) && \ + defined (HAVE_TOWLOWER) && \ + defined (HAVE_TOWUPPER) && \ + defined (HAVE_WCHAR_T) && \ + defined (HAVE_WCWIDTH) + /* system is supposed to support XPG5 */ +# define HANDLE_MULTIBYTE 1 +# endif +#endif + +/* If we don't want multibyte chars even on a system that supports them, let + the configuring user turn multibyte support off. */ +#if defined (NO_MULTIBYTE_SUPPORT) +# undef HANDLE_MULTIBYTE +#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 */ +/************************************************/ + +/* + * wchar_t doesn't work for 32-bit values on Windows using MSVC + */ +#ifdef WCHAR_T_BROKEN +# define WCHAR_T char32_t +# define MBRTOWC mbrtoc32 +# define WCRTOMB c32rtomb +#else /* normal systems */ +# define WCHAR_T wchar_t +# define MBRTOWC mbrtowc +# define WCRTOMB wcrtomb +#endif + +/* + * 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 (char *, int, int); +extern int _rl_find_next_mbchar (char *, int, int, int); + +#ifdef HANDLE_MULTIBYTE + +extern int _rl_compare_chars (char *, int, mbstate_t *, char *, int, mbstate_t *); +extern int _rl_get_char_len (char *, mbstate_t *); +extern int _rl_adjust_point (char *, int, mbstate_t *); + +extern int _rl_read_mbchar (char *, int); +extern int _rl_read_mbstring (int, char *, int); + +extern int _rl_is_mbchar_matched (char *, int, int, char *, int); + +extern WCHAR_T _rl_char_value (char *, int); +extern int _rl_walphabetic (WCHAR_T); + +#define _rl_to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc)) +#define _rl_to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc)) + +#define MB_NEXTCHAR(b,s,c,f) \ + ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \ + ? _rl_find_next_mbchar ((b), (s), (c), (f)) \ + : ((s) + (c))) +#define MB_PREVCHAR(b,s,f) \ + ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \ + ? _rl_find_prev_mbchar ((b), (s), (f)) \ + : ((s) - 1)) + +#define MB_INVALIDCH(x) ((x) == (size_t)-1 || (x) == (size_t)-2) +#define MB_NULLWCH(x) ((x) == 0) + +/* Try and shortcut the printable ascii characters to cut down the number of + calls to a libc wcwidth() */ +static inline int +_rl_wcwidth (WCHAR_T wc) +{ + switch (wc) + { + case ' ': case '!': case '"': case '#': case '%': + case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case '-': case '.': case '/': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case ':': case ';': case '<': case '=': case '>': + case '?': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case '[': case '\\': case ']': case '^': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': case '{': case '|': case '}': case '~': + return 1; + default: + return wcwidth (wc); + } +} + +/* Unicode combining characters range from U+0300 to U+036F */ +#define UNICODE_COMBINING_CHAR(x) ((x) >= 768 && (x) <= 879) + +#if defined (WCWIDTH_BROKEN) +# define WCWIDTH(wc) ((_rl_utf8locale && UNICODE_COMBINING_CHAR(wc)) ? 0 : _rl_wcwidth(wc)) +#else +# define WCWIDTH(wc) _rl_wcwidth(wc) +#endif + +#if defined (WCWIDTH_BROKEN) +# define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0 && iswcntrl(x) == 0) +#else +# define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0) +#endif + +#define UTF8_SINGLEBYTE(c) (((c) & 0x80) == 0) +#define UTF8_MBFIRSTCHAR(c) (((c) & 0xc0) == 0xc0) +#define UTF8_MBCHAR(c) (((c) & 0xc0) == 0x80) + +#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)) + +#define _rl_char_value(buf,ind) ((buf)[(ind)]) + +#define _rl_walphabetic(c) (rl_alphabetic (c)) + +#define _rl_to_wupper(c) (_rl_to_upper (c)) +#define _rl_to_wlower(c) (_rl_to_lower (c)) + +#define MB_NEXTCHAR(b,s,c,f) ((s) + (c)) +#define MB_PREVCHAR(b,s,f) ((s) - 1) + +#define MB_INVALIDCH(x) (0) +#define MB_NULLWCH(x) (0) + +#define UTF8_SINGLEBYTE(c) (1) + +#if !defined (HAVE_WCHAR_T) && !defined (wchar_t) +# define wchar_t int +#endif + +#endif /* !HANDLE_MULTIBYTE */ + +extern int rl_byte_oriented; + +#endif /* _RL_MBUTIL_H_ */ diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h new file mode 100644 index 0000000..d87d07a --- /dev/null +++ b/lib/readline/rlprivate.h @@ -0,0 +1,633 @@ +/* rlprivate.h -- functions and variables global to the readline library, + but not intended for use by applications. */ + +/* Copyright (C) 1999-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RL_PRIVATE_H_) +#define _RL_PRIVATE_H_ + +#include "rlconf.h" /* for VISIBLE_STATS */ +#include "rlstdc.h" +#include "posixjmp.h" /* defines procenv_t */ +#include "rlmbutil.h" /* for HANDLE_MULTIBYTE */ + +/************************************************************************* + * * + * Convenience definitions * + * * + *************************************************************************/ + +#define EMACS_MODE() (rl_editing_mode == emacs_mode) +#define VI_COMMAND_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) +#define VI_INSERT_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_insertion_keymap) + +#define RL_CHECK_SIGNALS() \ + do { \ + if (_rl_caught_signal) _rl_signal_handler (_rl_caught_signal); \ + } while (0) + +#define RL_SIG_RECEIVED() (_rl_caught_signal != 0) +#define RL_SIGINT_RECEIVED() (_rl_caught_signal == SIGINT) +#define RL_SIGWINCH_RECEIVED() (_rl_caught_signal == SIGWINCH) + +#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay) +#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc) + +/************************************************************************* + * * + * Global structs undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ +/* search types */ +#define RL_SEARCH_ISEARCH 0x01 /* incremental search */ +#define RL_SEARCH_NSEARCH 0x02 /* non-incremental search */ +#define RL_SEARCH_CSEARCH 0x04 /* intra-line char search */ + +/* search flags */ +#define SF_REVERSE 0x01 +#define SF_FOUND 0x02 +#define SF_FAILED 0x04 +#define SF_CHGKMAP 0x08 +#define SF_PATTERN 0x10 +#define SF_NOCASE 0x20 /* unused so far */ + +typedef struct __rl_search_context +{ + int type; + int sflags; + + char *search_string; + int search_string_index; + int search_string_size; + + char **lines; + char *allocated_line; + int hlen; + int hindex; + + int save_point; + int save_mark; + int save_line; + int last_found_line; + char *prev_line_found; + + UNDO_LIST *save_undo_list; + + Keymap keymap; /* used when dispatching commands in search string */ + Keymap okeymap; /* original keymap */ + + int history_pos; + int direction; + + int prevc; + int lastc; +#if defined (HANDLE_MULTIBYTE) + char mb[MB_LEN_MAX]; + char pmb[MB_LEN_MAX]; +#endif + + char *sline; + int sline_len; + int sline_index; + + char *search_terminators; +} _rl_search_cxt; + +struct _rl_cmd { + Keymap map; + int count; + int key; + rl_command_func_t *func; +}; +extern struct _rl_cmd _rl_pending_command; +extern struct _rl_cmd *_rl_command_to_execute; + +/* Callback data for reading numeric arguments */ +#define NUM_SAWMINUS 0x01 +#define NUM_SAWDIGITS 0x02 +#define NUM_READONE 0x04 + +typedef int _rl_arg_cxt; + +/* A context for reading key sequences longer than a single character when + using the callback interface. */ +#define KSEQ_DISPATCHED 0x01 +#define KSEQ_SUBSEQ 0x02 +#define KSEQ_RECURSIVE 0x04 + +typedef struct __rl_keyseq_context +{ + int flags; + int subseq_arg; + int subseq_retval; /* XXX */ + int okey; + + Keymap dmap; + Keymap oldmap; + + struct __rl_keyseq_context *ocxt; + int childval; +} _rl_keyseq_cxt; + +/* vi-mode commands that use result of motion command to define boundaries */ +#define VIM_DELETE 0x01 +#define VIM_CHANGE 0x02 +#define VIM_YANK 0x04 + +/* various states for vi-mode commands that use motion commands. reflects + RL_READLINE_STATE */ +#define VMSTATE_READ 0x01 +#define VMSTATE_NUMARG 0x02 + +typedef struct __rl_vimotion_context +{ + int op; + int state; + int flags; /* reserved */ + _rl_arg_cxt ncxt; + int numeric_arg; + int start, end; /* rl_point, rl_end */ + int key, motion; /* initial key, motion command */ +} _rl_vimotion_cxt; + +/* fill in more as needed */ +/* `Generic' callback data and functions */ +typedef struct __rl_callback_generic_arg +{ + int count; + int i1, i2; + /* add here as needed */ +} _rl_callback_generic_arg; + +typedef int _rl_callback_func_t (_rl_callback_generic_arg *); + +typedef void _rl_sigcleanup_func_t (int, void *); + +/************************************************************************* + * * + * Global functions undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ + +/************************************************************************* + * * + * Global variables undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ + +/* complete.c */ +extern int rl_complete_with_tilde_expansion; +#if defined (VISIBLE_STATS) +extern int rl_visible_stats; +#endif /* VISIBLE_STATS */ +#if defined (COLOR_SUPPORT) +extern int _rl_colored_stats; +extern int _rl_colored_completion_prefix; +#endif + +/* readline.c */ +extern int rl_line_buffer_len; +extern int rl_arg_sign; +extern int rl_visible_prompt_length; +extern int rl_byte_oriented; + +/* display.c */ +extern int rl_display_fixed; + +/* parens.c */ +extern int rl_blink_matching_paren; + +/************************************************************************* + * * + * Global functions and variables unused and undocumented * + * * + *************************************************************************/ + +/* kill.c */ +extern int rl_set_retained_kills (int); + +/* terminal.c */ +extern void _rl_set_screen_size (int, int); + +/* undo.c */ +extern int _rl_fix_last_undo_of_type (int, int, int); + +/* util.c */ +extern char *_rl_savestring (const char *); + +/************************************************************************* + * * + * Functions and variables private to the readline library * + * * + *************************************************************************/ + +/* NOTE: Functions and variables prefixed with `_rl_' are + pseudo-global: they are global so they can be shared + between files in the readline library, but are not intended + to be visible to readline callers. */ + +/************************************************************************* + * Undocumented private functions * + *************************************************************************/ + +#if defined(READLINE_CALLBACKS) + +/* readline.c */ +extern void readline_internal_setup (void); +extern char *readline_internal_teardown (int); +extern int readline_internal_char (void); + +extern _rl_keyseq_cxt *_rl_keyseq_cxt_alloc (void); +extern void _rl_keyseq_cxt_dispose (_rl_keyseq_cxt *); +extern void _rl_keyseq_chain_dispose (void); + +extern int _rl_dispatch_callback (_rl_keyseq_cxt *); + +/* callback.c */ +extern _rl_callback_generic_arg *_rl_callback_data_alloc (int); +extern void _rl_callback_data_dispose (_rl_callback_generic_arg *); + +#endif /* READLINE_CALLBACKS */ + +/* bind.c */ +extern char *_rl_untranslate_macro_value (char *, int); + +/* complete.c */ +extern void _rl_reset_completion_state (void); +extern char _rl_find_completion_word (int *, int *); +extern void _rl_free_match_list (char **); + +/* display.c */ +extern char *_rl_strip_prompt (char *); +extern void _rl_reset_prompt (void); +extern void _rl_move_vert (int); +extern void _rl_save_prompt (void); +extern void _rl_restore_prompt (void); +extern char *_rl_make_prompt_for_search (int); +extern void _rl_erase_at_end_of_line (int); +extern void _rl_clear_to_eol (int); +extern void _rl_clear_screen (int); +extern void _rl_update_final (void); +extern void _rl_optimize_redisplay (void); +extern void _rl_redisplay_after_sigwinch (void); +extern void _rl_clean_up_for_exit (void); +extern void _rl_erase_entire_line (void); +extern int _rl_current_display_line (void); +extern void _rl_refresh_line (void); + +/* input.c */ +extern int _rl_any_typein (void); +extern int _rl_input_available (void); +extern int _rl_nchars_available (void); +extern int _rl_input_queued (int); +extern void _rl_insert_typein (int); +extern int _rl_unget_char (int); +extern int _rl_pushed_input_available (void); + +extern int _rl_timeout_init (void); +extern int _rl_timeout_handle_sigalrm (void); +#if defined (_POSIXSELECT_H_) +/* use as a sentinel for fd_set, struct timeval, and sigset_t definitions */ +extern int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *); +#endif + +/* isearch.c */ +extern _rl_search_cxt *_rl_scxt_alloc (int, int); +extern void _rl_scxt_dispose (_rl_search_cxt *, int); + +extern int _rl_isearch_dispatch (_rl_search_cxt *, int); +extern int _rl_isearch_callback (_rl_search_cxt *); +extern int _rl_isearch_cleanup (_rl_search_cxt *, int); + +extern int _rl_search_getchar (_rl_search_cxt *); + +/* kill.c */ +#ifndef BRACKETED_PASTE_DEFAULT +# define BRACKETED_PASTE_DEFAULT 1 /* XXX - for now */ +#endif + +#define BRACK_PASTE_PREF "\033[200~" +#define BRACK_PASTE_SUFF "\033[201~" + +#define BRACK_PASTE_LAST '~' +#define BRACK_PASTE_SLEN 6 + +#define BRACK_PASTE_INIT "\033[?2004h" +#define BRACK_PASTE_FINI "\033[?2004l\r" + +extern int _rl_read_bracketed_paste_prefix (int); +extern char *_rl_bracketed_text (size_t *); +extern int _rl_bracketed_read_key (void); +extern int _rl_bracketed_read_mbstring (char *, int); + +/* macro.c */ +extern void _rl_with_macro_input (char *); +extern int _rl_peek_macro_key (void); +extern int _rl_next_macro_key (void); +extern int _rl_prev_macro_key (void); +extern void _rl_push_executing_macro (void); +extern void _rl_pop_executing_macro (void); +extern void _rl_add_macro_char (int); +extern void _rl_kill_kbd_macro (void); + +/* misc.c */ +extern int _rl_arg_overflow (void); +extern void _rl_arg_init (void); +extern int _rl_arg_getchar (void); +extern int _rl_arg_callback (_rl_arg_cxt); +extern void _rl_reset_argument (void); + +extern void _rl_start_using_history (void); +extern int _rl_free_saved_history_line (void); +extern void _rl_set_insert_mode (int, int); + +extern void _rl_revert_previous_lines (void); +extern void _rl_revert_all_lines (void); + +/* nls.c */ +extern char *_rl_init_locale (void); +extern int _rl_init_eightbit (void); +extern void _rl_reset_locale (void); + +/* parens.c */ +extern void _rl_enable_paren_matching (int); + +/* readline.c */ +extern void _rl_init_line_state (void); +extern void _rl_set_the_line (void); +extern int _rl_dispatch (int, Keymap); +extern int _rl_dispatch_subseq (int, Keymap, int); +extern void _rl_internal_char_cleanup (void); + +extern void _rl_init_executing_keyseq (void); +extern void _rl_term_executing_keyseq (void); +extern void _rl_end_executing_keyseq (void); +extern void _rl_add_executing_keyseq (int); +extern void _rl_del_executing_keyseq (void); + +extern rl_command_func_t *_rl_executing_func; + +/* rltty.c */ +extern int _rl_disable_tty_signals (void); +extern int _rl_restore_tty_signals (void); + +/* search.c */ +extern int _rl_nsearch_callback (_rl_search_cxt *); +extern int _rl_nsearch_cleanup (_rl_search_cxt *, int); + +/* signals.c */ +extern void _rl_signal_handler (int); + +extern void _rl_block_sigint (void); +extern void _rl_release_sigint (void); +extern void _rl_block_sigwinch (void); +extern void _rl_release_sigwinch (void); + +/* terminal.c */ +extern void _rl_get_screen_size (int, int); +extern void _rl_sigwinch_resize_terminal (void); +extern int _rl_init_terminal_io (const char *); +#ifdef _MINIX +extern void _rl_output_character_function (int); +#else +extern int _rl_output_character_function (int); +#endif +extern void _rl_cr (void); +extern void _rl_output_some_chars (const char *, int); +extern int _rl_backspace (int); +extern void _rl_enable_meta_key (void); +extern void _rl_disable_meta_key (void); +extern void _rl_control_keypad (int); +extern void _rl_set_cursor (int, int); +extern void _rl_standout_on (void); +extern void _rl_standout_off (void); +extern int _rl_reset_region_color (int, const char *); +extern void _rl_region_color_on (void); +extern void _rl_region_color_off (void); + +/* text.c */ +extern void _rl_fix_point (int); +extern void _rl_fix_mark (void); +extern int _rl_replace_text (const char *, int, int); +extern int _rl_forward_char_internal (int); +extern int _rl_backward_char_internal (int); +extern int _rl_insert_char (int, int); +extern int _rl_overwrite_char (int, int); +extern int _rl_overwrite_rubout (int, int); +extern int _rl_rubout_char (int, int); +#if defined (HANDLE_MULTIBYTE) +extern int _rl_char_search_internal (int, int, char *, int); +#else +extern int _rl_char_search_internal (int, int, int); +#endif +extern int _rl_set_mark_at_pos (int); + +/* undo.c */ +extern UNDO_LIST *_rl_copy_undo_entry (UNDO_LIST *); +extern UNDO_LIST *_rl_copy_undo_list (UNDO_LIST *); +extern void _rl_free_undo_list (UNDO_LIST *); + +/* util.c */ +#if defined (USE_VARARGS) && defined (PREFER_STDARG) +extern void _rl_ttymsg (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +extern void _rl_errmsg (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +extern void _rl_trace (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +#else +extern void _rl_ttymsg (); +extern void _rl_errmsg (); +extern void _rl_trace (); +#endif +extern void _rl_audit_tty (char *); + +extern int _rl_tropen (void); + +extern int _rl_abort_internal (void); +extern int _rl_null_function (int, int); +extern char *_rl_strindex (const char *, const char *); +extern int _rl_qsort_string_compare (char **, char **); +extern int (_rl_uppercase_p) (int); +extern int (_rl_lowercase_p) (int); +extern int (_rl_pure_alphabetic) (int); +extern int (_rl_digit_p) (int); +extern int (_rl_to_lower) (int); +extern int (_rl_to_upper) (int); +extern int (_rl_digit_value) (int); + +/* vi_mode.c */ +extern void _rl_vi_initialize_line (void); +extern void _rl_vi_reset_last (void); +extern void _rl_vi_set_last (int, int, int); +extern int _rl_vi_textmod_command (int); +extern int _rl_vi_motion_command (int); +extern void _rl_vi_done_inserting (void); +extern int _rl_vi_domove_callback (_rl_vimotion_cxt *); +extern int _rl_vi_domove_motion_cleanup (int, _rl_vimotion_cxt *); + +/* Use HS_HISTORY_VERSION as the sentinel to see if we've included history.h + and so can use HIST_ENTRY */ +#if defined (HS_HISTORY_VERSION) +extern void _rl_free_history_entry (HIST_ENTRY *); +#endif + +/************************************************************************* + * Undocumented private variables * + *************************************************************************/ + +/* bind.c */ +extern const char * const _rl_possible_control_prefixes[]; +extern const char * const _rl_possible_meta_prefixes[]; + +/* callback.c */ +extern _rl_callback_func_t *_rl_callback_func; +extern _rl_callback_generic_arg *_rl_callback_data; + +/* complete.c */ +extern int _rl_complete_show_all; +extern int _rl_complete_show_unmodified; +extern int _rl_complete_mark_directories; +extern int _rl_complete_mark_symlink_dirs; +extern int _rl_completion_prefix_display_length; +extern int _rl_completion_columns; +extern int _rl_print_completions_horizontally; +extern int _rl_completion_case_fold; +extern int _rl_completion_case_map; +extern int _rl_match_hidden_files; +extern int _rl_page_completions; +extern int _rl_skip_completed_text; +extern int _rl_menu_complete_prefix_first; + +/* display.c */ +extern int _rl_vis_botlin; +extern int _rl_last_c_pos; +extern int _rl_suppress_redisplay; +extern int _rl_want_redisplay; + +extern char *_rl_emacs_mode_str; +extern int _rl_emacs_modestr_len; +extern char *_rl_vi_ins_mode_str; +extern int _rl_vi_ins_modestr_len; +extern char *_rl_vi_cmd_mode_str; +extern int _rl_vi_cmd_modestr_len; + +/* isearch.c */ +extern char *_rl_isearch_terminators; + +extern _rl_search_cxt *_rl_iscxt; + +/* macro.c */ +extern char *_rl_executing_macro; + +/* misc.c */ +extern int _rl_history_preserve_point; +extern int _rl_history_saved_point; + +extern _rl_arg_cxt _rl_argcxt; + +/* nls.c */ +extern int _rl_utf8locale; + +/* readline.c */ +extern int _rl_echoing_p; +extern int _rl_horizontal_scroll_mode; +extern int _rl_mark_modified_lines; +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_bind_stty_chars; +extern int _rl_revert_all_at_newline; +extern int _rl_echo_control_chars; +extern int _rl_show_mode_in_prompt; +extern int _rl_enable_bracketed_paste; +extern int _rl_enable_active_region; +extern char *_rl_active_region_start_color; +extern char *_rl_active_region_end_color; +extern char *_rl_comment_begin; +extern unsigned char _rl_parsing_conditionalized_out; +extern Keymap _rl_keymap; +extern FILE *_rl_in_stream; +extern FILE *_rl_out_stream; +extern int _rl_last_command_was_kill; +extern int _rl_eof_char; +extern procenv_t _rl_top_level; +extern _rl_keyseq_cxt *_rl_kscxt; +extern int _rl_keyseq_timeout; + +extern int _rl_executing_keyseq_size; + +extern rl_hook_func_t *_rl_internal_startup_hook; + +/* search.c */ +extern _rl_search_cxt *_rl_nscxt; +extern int _rl_history_search_pos; + +/* signals.c */ +extern int volatile _rl_caught_signal; + +extern _rl_sigcleanup_func_t *_rl_sigcleanup; +extern void *_rl_sigcleanarg; + +extern int _rl_echoctl; + +extern int _rl_intr_char; +extern int _rl_quit_char; +extern int _rl_susp_char; + +/* terminal.c */ +extern int _rl_enable_keypad; +extern int _rl_enable_meta; +extern char *_rl_term_clreol; +extern char *_rl_term_clrpag; +extern char *_rl_term_clrscroll; +extern char *_rl_term_im; +extern char *_rl_term_ic; +extern char *_rl_term_ei; +extern char *_rl_term_DC; +extern char *_rl_term_up; +extern char *_rl_term_dc; +extern char *_rl_term_cr; +extern char *_rl_term_IC; +extern char *_rl_term_forward_char; +extern int _rl_screenheight; +extern int _rl_screenwidth; +extern int _rl_screenchars; +extern int _rl_terminal_can_insert; +extern int _rl_term_autowrap; + +/* text.c */ +extern int _rl_optimize_typeahead; +extern int _rl_keep_mark_active; + +/* undo.c */ +extern int _rl_doing_an_undo; +extern int _rl_undo_group_level; + +/* vi_mode.c */ +extern int _rl_vi_last_command; +extern int _rl_vi_redoing; +extern _rl_vimotion_cxt *_rl_vimvcxt; + +/* Use HS_HISTORY_VERSION as the sentinel to see if we've included history.h + and so can use HIST_ENTRY */ +#if defined (HS_HISTORY_VERSION) +extern HIST_ENTRY *_rl_saved_line_for_history; +#endif + +#endif /* _RL_PRIVATE_H_ */ diff --git a/lib/readline/rlshell.h b/lib/readline/rlshell.h new file mode 100644 index 0000000..6055b14 --- /dev/null +++ b/lib/readline/rlshell.h @@ -0,0 +1,33 @@ +/* rlshell.h -- utility functions normally provided by bash. */ + +/* Copyright (C) 1999-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RL_SHELL_H_) +#define _RL_SHELL_H_ + +#include "rlstdc.h" + +extern char *sh_single_quote (char *); +extern void sh_set_lines_and_columns (int, int); +extern char *sh_get_env_value (const char *); +extern char *sh_get_home_dir (void); +extern int sh_unset_nodelay_mode (int); + +#endif /* _RL_SHELL_H_ */ diff --git a/lib/readline/rlstdc.h b/lib/readline/rlstdc.h new file mode 100644 index 0000000..2aaa30b --- /dev/null +++ b/lib/readline/rlstdc.h @@ -0,0 +1,57 @@ +/* stdc.h -- macros to make source compile on both ANSI C and K&R C compilers. */ + +/* Copyright (C) 1993-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RL_STDC_H_) +#define _RL_STDC_H_ + +/* Adapted from BSD /usr/include/sys/cdefs.h. */ + +/* A function can be defined using prototypes and compile on both ANSI C + and traditional C compilers with something like this: + extern char *func PARAMS((char *, char *, int)); */ + +#if !defined (PARAMS) +# if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus) +# define PARAMS(protos) protos +# else +# define PARAMS(protos) () +# endif +#endif + +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) +# define __attribute__(x) +# endif +#endif + +/* Moved from config.h.in because readline.h:rl_message depends on these + defines. */ +#if defined (__STDC__) && defined (HAVE_STDARG_H) +# define PREFER_STDARG +# define USE_VARARGS +#else +# if defined (HAVE_VARARGS_H) +# define PREFER_VARARGS +# define USE_VARARGS +# endif +#endif + +#endif /* !_RL_STDC_H_ */ diff --git a/lib/readline/rltty.c b/lib/readline/rltty.c new file mode 100644 index 0000000..882a3d4 --- /dev/null +++ b/lib/readline/rltty.c @@ -0,0 +1,995 @@ +/* rltty.c -- functions to prepare and restore the terminal for readline's + use. */ + +/* Copyright (C) 1992-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "rldefs.h" + +#include "rltty.h" +#if defined (HAVE_SYS_IOCTL_H) +# include <sys/ioctl.h> /* include for declaration of ioctl */ +#endif + +#include "readline.h" +#include "rlprivate.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal; +rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal; + +static void set_winsize (int); + +/* **************************************************************** */ +/* */ +/* Saving and Restoring the TTY */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that the terminal is in a prepped state. There are several + flags that are OR'd in to denote whether or not we have sent various + init strings to the terminal. */ +#define TPX_PREPPED 0x01 +#define TPX_BRACKPASTE 0x02 +#define TPX_METAKEY 0x04 + +static int terminal_prepped; + +static _RL_TTY_CHARS _rl_tty_chars, _rl_last_tty_chars; + +/* If non-zero, means that this process has called tcflow(fd, TCOOFF) + and output is suspended. */ +#if defined (__ksr1__) +static int ksrflow; +#endif + +/* Dummy call to force a backgrounded readline to stop before it tries + to get the tty settings. */ +static void +set_winsize (tty) + int tty; +{ +#if defined (TIOCGWINSZ) + struct winsize w; + + if (ioctl (tty, TIOCGWINSZ, &w) == 0) + (void) ioctl (tty, TIOCSWINSZ, &w); +#endif /* TIOCGWINSZ */ +} + +#if defined (NO_TTY_DRIVER) +/* Nothing */ +#elif defined (NEW_TTY_DRIVER) + +/* Values for the `flags' field of a struct bsdtty. This tells which + elements of the struct bsdtty have been fetched from the system and + are valid. */ +#define SGTTY_SET 0x01 +#define LFLAG_SET 0x02 +#define TCHARS_SET 0x04 +#define LTCHARS_SET 0x08 + +struct bsdtty { + struct sgttyb sgttyb; /* Basic BSD tty driver information. */ + int lflag; /* Local mode flags, like LPASS8. */ +#if defined (TIOCGETC) + struct tchars tchars; /* Terminal special characters, including ^S and ^Q. */ +#endif +#if defined (TIOCGLTC) + struct ltchars ltchars; /* 4.2 BSD editing characters */ +#endif + int flags; /* Bitmap saying which parts of the struct are valid. */ +}; + +#define TIOTYPE struct bsdtty + +static TIOTYPE otio; + +static void save_tty_chars (TIOTYPE *); +static int _get_tty_settings (int, TIOTYPE *); +static int get_tty_settings (int, TIOTYPE *); +static int _set_tty_settings (int, TIOTYPE *); +static int set_tty_settings (int, TIOTYPE *); + +static void prepare_terminal_settings (int, TIOTYPE, TIOTYPE *); + +static void set_special_char (Keymap, TIOTYPE *, int, rl_command_func_t *); + +static void +save_tty_chars (TIOTYPE *tiop) +{ + _rl_last_tty_chars = _rl_tty_chars; + + if (tiop->flags & SGTTY_SET) + { + _rl_tty_chars.t_erase = tiop->sgttyb.sg_erase; + _rl_tty_chars.t_kill = tiop->sgttyb.sg_kill; + } + + if (tiop->flags & TCHARS_SET) + { + _rl_intr_char = _rl_tty_chars.t_intr = tiop->tchars.t_intrc; + _rl_quit_char = _rl_tty_chars.t_quit = tiop->tchars.t_quitc; + + _rl_tty_chars.t_start = tiop->tchars.t_startc; + _rl_tty_chars.t_stop = tiop->tchars.t_stopc; + _rl_tty_chars.t_eof = tiop->tchars.t_eofc; + _rl_tty_chars.t_eol = '\n'; + _rl_tty_chars.t_eol2 = tiop->tchars.t_brkc; + } + + if (tiop->flags & LTCHARS_SET) + { + _rl_susp_char = _rl_tty_chars.t_susp = tiop->ltchars.t_suspc; + + _rl_tty_chars.t_dsusp = tiop->ltchars.t_dsuspc; + _rl_tty_chars.t_reprint = tiop->ltchars.t_rprntc; + _rl_tty_chars.t_flush = tiop->ltchars.t_flushc; + _rl_tty_chars.t_werase = tiop->ltchars.t_werasc; + _rl_tty_chars.t_lnext = tiop->ltchars.t_lnextc; + } + + _rl_tty_chars.t_status = -1; +} + +static int +get_tty_settings (int tty, TIOTYPE *tiop) +{ + set_winsize (tty); + + tiop->flags = tiop->lflag = 0; + + errno = 0; + if (ioctl (tty, TIOCGETP, &(tiop->sgttyb)) < 0) + return -1; + tiop->flags |= SGTTY_SET; + +#if defined (TIOCLGET) + if (ioctl (tty, TIOCLGET, &(tiop->lflag)) == 0) + tiop->flags |= LFLAG_SET; +#endif + +#if defined (TIOCGETC) + if (ioctl (tty, TIOCGETC, &(tiop->tchars)) == 0) + tiop->flags |= TCHARS_SET; +#endif + +#if defined (TIOCGLTC) + if (ioctl (tty, TIOCGLTC, &(tiop->ltchars)) == 0) + tiop->flags |= LTCHARS_SET; +#endif + + return 0; +} + +static int +set_tty_settings (int tty, TIOTYPE *tiop) +{ + if (tiop->flags & SGTTY_SET) + { + ioctl (tty, TIOCSETN, &(tiop->sgttyb)); + tiop->flags &= ~SGTTY_SET; + } + _rl_echoing_p = 1; + +#if defined (TIOCLSET) + if (tiop->flags & LFLAG_SET) + { + ioctl (tty, TIOCLSET, &(tiop->lflag)); + tiop->flags &= ~LFLAG_SET; + } +#endif + +#if defined (TIOCSETC) + if (tiop->flags & TCHARS_SET) + { + ioctl (tty, TIOCSETC, &(tiop->tchars)); + tiop->flags &= ~TCHARS_SET; + } +#endif + +#if defined (TIOCSLTC) + if (tiop->flags & LTCHARS_SET) + { + ioctl (tty, TIOCSLTC, &(tiop->ltchars)); + tiop->flags &= ~LTCHARS_SET; + } +#endif + + return 0; +} + +static void +prepare_terminal_settings (int meta_flag, TIOTYPE oldtio, TIOTYPE *tiop) +{ + _rl_echoing_p = (oldtio.sgttyb.sg_flags & ECHO); + _rl_echoctl = (oldtio.sgttyb.sg_flags & ECHOCTL); + + /* Copy the original settings to the structure we're going to use for + our settings. */ + tiop->sgttyb = oldtio.sgttyb; + tiop->lflag = oldtio.lflag; +#if defined (TIOCGETC) + tiop->tchars = oldtio.tchars; +#endif +#if defined (TIOCGLTC) + tiop->ltchars = oldtio.ltchars; +#endif + tiop->flags = oldtio.flags; + + /* First, the basic settings to put us into character-at-a-time, no-echo + input mode. */ + tiop->sgttyb.sg_flags &= ~(ECHO | CRMOD); + tiop->sgttyb.sg_flags |= CBREAK; + + /* If this terminal doesn't care how the 8th bit is used, then we can + use it for the meta-key. If only one of even or odd parity is + specified, then the terminal is using parity, and we cannot. */ +#if !defined (ANYP) +# define ANYP (EVENP | ODDP) +#endif + if (((oldtio.sgttyb.sg_flags & ANYP) == ANYP) || + ((oldtio.sgttyb.sg_flags & ANYP) == 0)) + { + tiop->sgttyb.sg_flags |= ANYP; + + /* Hack on local mode flags if we can. */ +#if defined (TIOCLGET) +# if defined (LPASS8) + tiop->lflag |= LPASS8; +# endif /* LPASS8 */ +#endif /* TIOCLGET */ + } + +#if defined (TIOCGETC) +# if defined (USE_XON_XOFF) + /* Get rid of terminal output start and stop characters. */ + tiop->tchars.t_stopc = -1; /* C-s */ + tiop->tchars.t_startc = -1; /* C-q */ + + /* If there is an XON character, bind it to restart the output. */ + if (oldtio.tchars.t_startc != -1) + rl_bind_key (oldtio.tchars.t_startc, rl_restart_output); +# endif /* USE_XON_XOFF */ + + /* If there is an EOF char, bind _rl_eof_char to it. */ + if (oldtio.tchars.t_eofc != -1) + _rl_eof_char = oldtio.tchars.t_eofc; + +# if defined (NO_KILL_INTR) + /* Get rid of terminal-generated SIGQUIT and SIGINT. */ + tiop->tchars.t_quitc = -1; /* C-\ */ + tiop->tchars.t_intrc = -1; /* C-c */ +# endif /* NO_KILL_INTR */ +#endif /* TIOCGETC */ + +#if defined (TIOCGLTC) + /* Make the interrupt keys go away. Just enough to make people happy. */ + tiop->ltchars.t_dsuspc = -1; /* C-y */ + tiop->ltchars.t_lnextc = -1; /* C-v */ +#endif /* TIOCGLTC */ +} + +#else /* !defined (NEW_TTY_DRIVER) */ + +#if !defined (VMIN) +# define VMIN VEOF +#endif + +#if !defined (VTIME) +# define VTIME VEOL +#endif + +#if defined (TERMIOS_TTY_DRIVER) +# define TIOTYPE struct termios +# define DRAIN_OUTPUT(fd) tcdrain (fd) +# define GETATTR(tty, tiop) (tcgetattr (tty, tiop)) +# ifdef M_UNIX +# define SETATTR(tty, tiop) (tcsetattr (tty, TCSANOW, tiop)) +# else +# define SETATTR(tty, tiop) (tcsetattr (tty, TCSADRAIN, tiop)) +# endif /* !M_UNIX */ +#else +# define TIOTYPE struct termio +# define DRAIN_OUTPUT(fd) +# define GETATTR(tty, tiop) (ioctl (tty, TCGETA, tiop)) +# define SETATTR(tty, tiop) (ioctl (tty, TCSETAW, tiop)) +#endif /* !TERMIOS_TTY_DRIVER */ + +static TIOTYPE otio; + +static void save_tty_chars (TIOTYPE *); +static int _get_tty_settings (int, TIOTYPE *); +static int get_tty_settings (int, TIOTYPE *); +static int _set_tty_settings (int, TIOTYPE *); +static int set_tty_settings (int, TIOTYPE *); + +static void prepare_terminal_settings (int, TIOTYPE, TIOTYPE *); + +static void set_special_char (Keymap, TIOTYPE *, int, rl_command_func_t *); +static void _rl_bind_tty_special_chars (Keymap, TIOTYPE); + +#if defined (FLUSHO) +# define OUTPUT_BEING_FLUSHED(tp) (tp->c_lflag & FLUSHO) +#else +# define OUTPUT_BEING_FLUSHED(tp) 0 +#endif + +static void +save_tty_chars (TIOTYPE *tiop) +{ + _rl_last_tty_chars = _rl_tty_chars; + + _rl_tty_chars.t_eof = tiop->c_cc[VEOF]; + _rl_tty_chars.t_eol = tiop->c_cc[VEOL]; +#ifdef VEOL2 + _rl_tty_chars.t_eol2 = tiop->c_cc[VEOL2]; +#endif + _rl_tty_chars.t_erase = tiop->c_cc[VERASE]; +#ifdef VWERASE + _rl_tty_chars.t_werase = tiop->c_cc[VWERASE]; +#endif + _rl_tty_chars.t_kill = tiop->c_cc[VKILL]; +#ifdef VREPRINT + _rl_tty_chars.t_reprint = tiop->c_cc[VREPRINT]; +#endif + _rl_intr_char = _rl_tty_chars.t_intr = tiop->c_cc[VINTR]; + _rl_quit_char = _rl_tty_chars.t_quit = tiop->c_cc[VQUIT]; +#ifdef VSUSP + _rl_susp_char = _rl_tty_chars.t_susp = tiop->c_cc[VSUSP]; +#endif +#ifdef VDSUSP + _rl_tty_chars.t_dsusp = tiop->c_cc[VDSUSP]; +#endif +#ifdef VSTART + _rl_tty_chars.t_start = tiop->c_cc[VSTART]; +#endif +#ifdef VSTOP + _rl_tty_chars.t_stop = tiop->c_cc[VSTOP]; +#endif +#ifdef VLNEXT + _rl_tty_chars.t_lnext = tiop->c_cc[VLNEXT]; +#endif +#ifdef VDISCARD + _rl_tty_chars.t_flush = tiop->c_cc[VDISCARD]; +#endif +#ifdef VSTATUS + _rl_tty_chars.t_status = tiop->c_cc[VSTATUS]; +#endif +} + +#if defined (_AIX) || defined (_AIX41) +/* Currently this is only used on AIX */ +static void +rltty_warning (char *msg) +{ + _rl_errmsg ("warning: %s", msg); +} +#endif + +#if defined (_AIX) +void +setopost (TIOTYPE *tp) +{ + if ((tp->c_oflag & OPOST) == 0) + { + _rl_errmsg ("warning: turning on OPOST for terminal\r"); + tp->c_oflag |= OPOST|ONLCR; + } +} +#endif + +static int +_get_tty_settings (int tty, TIOTYPE *tiop) +{ + int ioctl_ret; + + while (1) + { + ioctl_ret = GETATTR (tty, tiop); + if (ioctl_ret < 0) + { + if (errno != EINTR) + return -1; + else + continue; + } + if (OUTPUT_BEING_FLUSHED (tiop)) + { +#if defined (FLUSHO) + _rl_errmsg ("warning: turning off output flushing"); + tiop->c_lflag &= ~FLUSHO; + break; +#else + continue; +#endif + } + break; + } + + return 0; +} + +static int +get_tty_settings (int tty, TIOTYPE *tiop) +{ + set_winsize (tty); + + errno = 0; + if (_get_tty_settings (tty, tiop) < 0) + return -1; + +#if defined (_AIX) + setopost(tiop); +#endif + + return 0; +} + +static int +_set_tty_settings (int tty, TIOTYPE *tiop) +{ + while (SETATTR (tty, tiop) < 0) + { + if (errno != EINTR) + return -1; + errno = 0; + } + return 0; +} + +static int +set_tty_settings (int tty, TIOTYPE *tiop) +{ + if (_set_tty_settings (tty, tiop) < 0) + return -1; + +#if 0 + +#if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + if (ksrflow) + { + ksrflow = 0; + tcflow (tty, TCOON); + } +# else /* !ksr1 */ + tcflow (tty, TCOON); /* Simulate a ^Q. */ +# endif /* !ksr1 */ +#else + ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ +#endif /* !TERMIOS_TTY_DRIVER */ + +#endif /* 0 */ + + return 0; +} + +static void +prepare_terminal_settings (int meta_flag, TIOTYPE oldtio, TIOTYPE *tiop) +{ + int sc; + Keymap kmap; + + _rl_echoing_p = (oldtio.c_lflag & ECHO); +#if defined (ECHOCTL) + _rl_echoctl = (oldtio.c_lflag & ECHOCTL); +#endif + + tiop->c_lflag &= ~(ICANON | ECHO); + + if ((unsigned char) oldtio.c_cc[VEOF] != (unsigned char) _POSIX_VDISABLE) + _rl_eof_char = oldtio.c_cc[VEOF]; + +#if defined (USE_XON_XOFF) +#if defined (IXANY) + tiop->c_iflag &= ~(IXON | IXANY); +#else + /* `strict' Posix systems do not define IXANY. */ + tiop->c_iflag &= ~IXON; +#endif /* IXANY */ +#endif /* USE_XON_XOFF */ + + /* Only turn this off if we are using all 8 bits. */ + if (((tiop->c_cflag & CSIZE) == CS8) || meta_flag) + tiop->c_iflag &= ~(ISTRIP | INPCK); + + /* Make sure we differentiate between CR and NL on input. */ + tiop->c_iflag &= ~(ICRNL | INLCR); + +#if !defined (HANDLE_SIGNALS) + tiop->c_lflag &= ~ISIG; +#else + tiop->c_lflag |= ISIG; +#endif + + tiop->c_cc[VMIN] = 1; + tiop->c_cc[VTIME] = 0; + +#if defined (FLUSHO) + if (OUTPUT_BEING_FLUSHED (tiop)) + { + tiop->c_lflag &= ~FLUSHO; + oldtio.c_lflag &= ~FLUSHO; + } +#endif + + /* Turn off characters that we need on Posix systems with job control, + just to be sure. This includes ^Y and ^V. This should not really + be necessary. */ +#if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_VDISABLE) + +#if defined (VLNEXT) + tiop->c_cc[VLNEXT] = _POSIX_VDISABLE; +#endif + +#if defined (VDSUSP) + tiop->c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif + + /* Conditionally disable some other tty special characters if there is a + key binding for them in the current keymap. Readline ordinarily doesn't + bind these characters, but an application or user might. */ +#if defined (VI_MODE) + kmap = (rl_editing_mode == vi_mode) ? vi_insertion_keymap : _rl_keymap; +#else + kmap = _rl_keymap; +#endif +#if defined (VDISCARD) + sc = tiop->c_cc[VDISCARD]; + if (sc != _POSIX_VDISABLE && kmap[(unsigned char)sc].type == ISFUNC) + tiop->c_cc[VDISCARD] = _POSIX_VDISABLE; +#endif /* VDISCARD */ + +#endif /* TERMIOS_TTY_DRIVER && _POSIX_VDISABLE */ +} +#endif /* !NEW_TTY_DRIVER */ + +/* Put the terminal in CBREAK mode so that we can detect key presses. */ +#if defined (NO_TTY_DRIVER) +void +rl_prep_terminal (int meta_flag) +{ + _rl_echoing_p = 1; +} + +void +rl_deprep_terminal (void) +{ +} + +#else /* ! NO_TTY_DRIVER */ +void +rl_prep_terminal (int meta_flag) +{ + int tty, nprep; + TIOTYPE tio; + + if (terminal_prepped) + return; + + /* Try to keep this function from being INTerrupted. */ + _rl_block_sigint (); + + tty = rl_instream ? fileno (rl_instream) : fileno (stdin); + + if (get_tty_settings (tty, &tio) < 0) + { +#if defined (ENOTSUP) + /* MacOS X and Linux, at least, lie about the value of errno if + tcgetattr fails. */ + if (errno == ENOTTY || errno == EINVAL || errno == ENOTSUP) +#else + if (errno == ENOTTY || errno == EINVAL) +#endif + _rl_echoing_p = 1; /* XXX */ + + _rl_release_sigint (); + return; + } + + otio = tio; + + if (_rl_bind_stty_chars) + { +#if defined (VI_MODE) + /* If editing in vi mode, make sure we restore the bindings in the + insertion keymap no matter what keymap we ended up in. */ + if (rl_editing_mode == vi_mode) + rl_tty_unset_default_bindings (vi_insertion_keymap); + else +#endif + rl_tty_unset_default_bindings (_rl_keymap); + } + save_tty_chars (&otio); + RL_SETSTATE(RL_STATE_TTYCSAVED); + if (_rl_bind_stty_chars) + { +#if defined (VI_MODE) + /* If editing in vi mode, make sure we set the bindings in the + insertion keymap no matter what keymap we ended up in. */ + if (rl_editing_mode == vi_mode) + _rl_bind_tty_special_chars (vi_insertion_keymap, tio); + else +#endif + _rl_bind_tty_special_chars (_rl_keymap, tio); + } + + prepare_terminal_settings (meta_flag, otio, &tio); + + if (set_tty_settings (tty, &tio) < 0) + { + _rl_release_sigint (); + return; + } + + if (_rl_enable_keypad) + _rl_control_keypad (1); + + nprep = TPX_PREPPED; + + if (_rl_enable_bracketed_paste) + { + fprintf (rl_outstream, BRACK_PASTE_INIT); + nprep |= TPX_BRACKPASTE; + } + + fflush (rl_outstream); + terminal_prepped = nprep; + RL_SETSTATE(RL_STATE_TERMPREPPED); + + _rl_release_sigint (); +} + +/* Restore the terminal's normal settings and modes. */ +void +rl_deprep_terminal (void) +{ + int tty; + + if (terminal_prepped == 0) + return; + + /* Try to keep this function from being interrupted. */ + _rl_block_sigint (); + + tty = rl_instream ? fileno (rl_instream) : fileno (stdin); + + if (terminal_prepped & TPX_BRACKPASTE) + { + fprintf (rl_outstream, BRACK_PASTE_FINI); + /* Since the last character in BRACK_PASTE_FINI is \r */ + _rl_last_c_pos = 0; + if (rl_eof_found && (RL_ISSTATE (RL_STATE_TIMEOUT) == 0)) + fprintf (rl_outstream, "\n"); + else if (_rl_echoing_p == 0) + fprintf (rl_outstream, "\n"); + } + + if (_rl_enable_keypad) + _rl_control_keypad (0); + + fflush (rl_outstream); + + if (set_tty_settings (tty, &otio) < 0) + { + _rl_release_sigint (); + return; + } + + terminal_prepped = 0; + RL_UNSETSTATE(RL_STATE_TERMPREPPED); + + _rl_release_sigint (); +} +#endif /* !NO_TTY_DRIVER */ + +/* Set readline's idea of whether or not it is echoing output to the terminal, + returning the old value. */ +int +rl_tty_set_echoing (int u) +{ + int o; + + o = _rl_echoing_p; + _rl_echoing_p = u; + return o; +} + +/* **************************************************************** */ +/* */ +/* Bogus Flow Control */ +/* */ +/* **************************************************************** */ + +int +rl_restart_output (int count, int key) +{ +#if defined (__MINGW32__) + return 0; +#else /* !__MING32__ */ + + int fildes = fileno (rl_outstream); +#if defined (TIOCSTART) +#if defined (apollo) + ioctl (&fildes, TIOCSTART, 0); +#else + ioctl (fildes, TIOCSTART, 0); +#endif /* apollo */ + +#else /* !TIOCSTART */ +# if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + if (ksrflow) + { + ksrflow = 0; + tcflow (fildes, TCOON); + } +# else /* !ksr1 */ + tcflow (fildes, TCOON); /* Simulate a ^Q. */ +# endif /* !ksr1 */ +# else /* !TERMIOS_TTY_DRIVER */ +# if defined (TCXONC) + ioctl (fildes, TCXONC, TCOON); +# endif /* TCXONC */ +# endif /* !TERMIOS_TTY_DRIVER */ +#endif /* !TIOCSTART */ + + return 0; +#endif /* !__MINGW32__ */ +} + +int +rl_stop_output (int count, int key) +{ +#if defined (__MINGW32__) + return 0; +#else + + int fildes = fileno (rl_instream); + +#if defined (TIOCSTOP) +# if defined (apollo) + ioctl (&fildes, TIOCSTOP, 0); +# else + ioctl (fildes, TIOCSTOP, 0); +# endif /* apollo */ +#else /* !TIOCSTOP */ +# if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + ksrflow = 1; +# endif /* ksr1 */ + tcflow (fildes, TCOOFF); +# else +# if defined (TCXONC) + ioctl (fildes, TCXONC, TCOON); +# endif /* TCXONC */ +# endif /* !TERMIOS_TTY_DRIVER */ +#endif /* !TIOCSTOP */ + + return 0; +#endif /* !__MINGW32__ */ +} + +/* **************************************************************** */ +/* */ +/* Default Key Bindings */ +/* */ +/* **************************************************************** */ + +#if !defined (NO_TTY_DRIVER) +#define SET_SPECIAL(sc, func) set_special_char(kmap, &ttybuff, sc, func) +#endif + +#if defined (NO_TTY_DRIVER) + +#define SET_SPECIAL(sc, func) +#define RESET_SPECIAL(c) + +#elif defined (NEW_TTY_DRIVER) +static void +set_special_char (Keymap kmap, TIOTYPE *tiop, int sc, rl_command_func_t *func) +{ + if (sc != -1 && kmap[(unsigned char)sc].type == ISFUNC) + kmap[(unsigned char)sc].function = func; +} + +#define RESET_SPECIAL(c) \ + if (c != -1 && kmap[(unsigned char)c].type == ISFUNC) \ + kmap[(unsigned char)c].function = rl_insert; + +static void +_rl_bind_tty_special_chars (Keymap kmap, TIOTYPE ttybuff) +{ + if (ttybuff.flags & SGTTY_SET) + { + SET_SPECIAL (ttybuff.sgttyb.sg_erase, rl_rubout); + SET_SPECIAL (ttybuff.sgttyb.sg_kill, rl_unix_line_discard); + } + +# if defined (TIOCGLTC) + if (ttybuff.flags & LTCHARS_SET) + { + SET_SPECIAL (ttybuff.ltchars.t_werasc, rl_unix_word_rubout); + SET_SPECIAL (ttybuff.ltchars.t_lnextc, rl_quoted_insert); + } +# endif /* TIOCGLTC */ +} + +#else /* !NEW_TTY_DRIVER */ +static void +set_special_char (Keymap kmap, TIOTYPE *tiop, int sc, rl_command_func_t *func) +{ + unsigned char uc; + + uc = tiop->c_cc[sc]; + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) + kmap[uc].function = func; +} + +/* used later */ +#define RESET_SPECIAL(uc) \ + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) \ + kmap[uc].function = rl_insert; + +static void +_rl_bind_tty_special_chars (Keymap kmap, TIOTYPE ttybuff) +{ + SET_SPECIAL (VERASE, rl_rubout); + SET_SPECIAL (VKILL, rl_unix_line_discard); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + SET_SPECIAL (VLNEXT, rl_quoted_insert); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) +# if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + SET_SPECIAL (VWERASE, rl_vi_unix_word_rubout); + else +# endif + SET_SPECIAL (VWERASE, rl_unix_word_rubout); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + +#endif /* !NEW_TTY_DRIVER */ + +/* Set the system's default editing characters to their readline equivalents + in KMAP. Should be static, now that we have rl_tty_set_default_bindings. */ +void +rltty_set_default_bindings (Keymap kmap) +{ +#if !defined (NO_TTY_DRIVER) + TIOTYPE ttybuff; + int tty; + + tty = fileno (rl_instream); + + if (get_tty_settings (tty, &ttybuff) == 0) + _rl_bind_tty_special_chars (kmap, ttybuff); +#endif +} + +/* New public way to set the system default editing chars to their readline + equivalents. */ +void +rl_tty_set_default_bindings (Keymap kmap) +{ + rltty_set_default_bindings (kmap); +} + +/* Rebind all of the tty special chars that readline worries about back + to self-insert. Call this before saving the current terminal special + chars with save_tty_chars(). This only works on POSIX termios or termio + systems. */ +void +rl_tty_unset_default_bindings (Keymap kmap) +{ + /* Don't bother before we've saved the tty special chars at least once. */ + if (RL_ISSTATE(RL_STATE_TTYCSAVED) == 0) + return; + + RESET_SPECIAL (_rl_tty_chars.t_erase); + RESET_SPECIAL (_rl_tty_chars.t_kill); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_lnext); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_werase); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + +#if defined (HANDLE_SIGNALS) + +#if defined (NEW_TTY_DRIVER) || defined (NO_TTY_DRIVER) +int +_rl_disable_tty_signals (void) +{ + return 0; +} + +int +_rl_restore_tty_signals (void) +{ + return 0; +} +#else + +static TIOTYPE sigstty, nosigstty; +static int tty_sigs_disabled = 0; + +int +_rl_disable_tty_signals (void) +{ + if (tty_sigs_disabled) + return 0; + + if (_get_tty_settings (fileno (rl_instream), &sigstty) < 0) + return -1; + + nosigstty = sigstty; + + nosigstty.c_lflag &= ~ISIG; + nosigstty.c_iflag &= ~IXON; + + if (_set_tty_settings (fileno (rl_instream), &nosigstty) < 0) + return (_set_tty_settings (fileno (rl_instream), &sigstty)); + + tty_sigs_disabled = 1; + return 0; +} + +int +_rl_restore_tty_signals (void) +{ + int r; + + if (tty_sigs_disabled == 0) + return 0; + + r = _set_tty_settings (fileno (rl_instream), &sigstty); + + if (r == 0) + tty_sigs_disabled = 0; + + return r; +} +#endif /* !NEW_TTY_DRIVER */ + +#endif /* HANDLE_SIGNALS */ diff --git a/lib/readline/rltty.h b/lib/readline/rltty.h new file mode 100644 index 0000000..5bcc946 --- /dev/null +++ b/lib/readline/rltty.h @@ -0,0 +1,80 @@ +/* rltty.h - tty driver-related definitions used by some library files. */ + +/* Copyright (C) 1995-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLTTY_H_) +#define _RLTTY_H_ + +/* Posix systems use termios and the Posix signal functions. */ +#if defined (TERMIOS_TTY_DRIVER) +# include <termios.h> +#endif /* TERMIOS_TTY_DRIVER */ + +/* System V machines use termio. */ +#if defined (TERMIO_TTY_DRIVER) +# include <termio.h> +# if !defined (TCOON) +# define TCOON 1 +# endif +#endif /* TERMIO_TTY_DRIVER */ + +/* Other (BSD) machines use sgtty. */ +#if defined (NEW_TTY_DRIVER) +# include <sgtty.h> +#endif + +#include "rlwinsize.h" + +/* Define _POSIX_VDISABLE if we are not using the `new' tty driver and + it is not already defined. It is used both to determine if a + special character is disabled and to disable certain special + characters. Posix systems should set to 0, USG systems to -1. */ +#if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE) +# if defined (_SVR4_VDISABLE) +# define _POSIX_VDISABLE _SVR4_VDISABLE +# else +# if defined (_POSIX_VERSION) +# define _POSIX_VDISABLE 0 +# else /* !_POSIX_VERSION */ +# define _POSIX_VDISABLE -1 +# endif /* !_POSIX_VERSION */ +# endif /* !_SVR4_DISABLE */ +#endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */ + +typedef struct _rl_tty_chars { + unsigned char t_eof; + unsigned char t_eol; + unsigned char t_eol2; + unsigned char t_erase; + unsigned char t_werase; + unsigned char t_kill; + unsigned char t_reprint; + unsigned char t_intr; + unsigned char t_quit; + unsigned char t_susp; + unsigned char t_dsusp; + unsigned char t_start; + unsigned char t_stop; + unsigned char t_lnext; + unsigned char t_flush; + unsigned char t_status; +} _RL_TTY_CHARS; + +#endif /* _RLTTY_H_ */ diff --git a/lib/readline/rltypedefs.h b/lib/readline/rltypedefs.h new file mode 100644 index 0000000..1636549 --- /dev/null +++ b/lib/readline/rltypedefs.h @@ -0,0 +1,100 @@ +/* rltypedefs.h -- Type declarations for readline functions. */ + +/* Copyright (C) 2000-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _RL_TYPEDEFS_H_ +#define _RL_TYPEDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Old-style, attempt to mark as deprecated in some way people will notice. */ + +#if !defined (_FUNCTION_DEF) +# define _FUNCTION_DEF + +#if defined(__GNUC__) || defined(__clang__) +typedef int Function () __attribute__((deprecated)); +typedef void VFunction () __attribute__((deprecated)); +typedef char *CPFunction () __attribute__((deprecated)); +typedef char **CPPFunction () __attribute__((deprecated)); +#else +typedef int Function (); +typedef void VFunction (); +typedef char *CPFunction (); +typedef char **CPPFunction (); +#endif + +#endif /* _FUNCTION_DEF */ + +/* New style. */ + +#if !defined (_RL_FUNCTION_TYPEDEF) +# define _RL_FUNCTION_TYPEDEF + +/* Bindable functions */ +typedef int rl_command_func_t (int, int); + +/* Typedefs for the completion system */ +typedef char *rl_compentry_func_t (const char *, int); +typedef char **rl_completion_func_t (const char *, int, int); + +typedef char *rl_quote_func_t (char *, int, char *); +typedef char *rl_dequote_func_t (char *, int); + +typedef int rl_compignore_func_t (char **); + +typedef void rl_compdisp_func_t (char **, int, int); + +/* Type for input and pre-read hook functions like rl_event_hook */ +typedef int rl_hook_func_t (void); + +/* Input function type */ +typedef int rl_getc_func_t (FILE *); + +/* Generic function that takes a character buffer (which could be the readline + line buffer) and an index into it (which could be rl_point) and returns + an int. */ +typedef int rl_linebuf_func_t (char *, int); + +/* `Generic' function pointer typedefs */ +typedef int rl_intfunc_t (int); +#define rl_ivoidfunc_t rl_hook_func_t +typedef int rl_icpfunc_t (char *); +typedef int rl_icppfunc_t (char **); + +typedef void rl_voidfunc_t (void); +typedef void rl_vintfunc_t (int); +typedef void rl_vcpfunc_t (char *); +typedef void rl_vcppfunc_t (char **); + +typedef char *rl_cpvfunc_t (void); +typedef char *rl_cpifunc_t (int); +typedef char *rl_cpcpfunc_t (char *); +typedef char *rl_cpcppfunc_t (char **); + +#endif /* _RL_FUNCTION_TYPEDEF */ + +#ifdef __cplusplus +} +#endif + +#endif /* _RL_TYPEDEFS_H_ */ diff --git a/lib/readline/rlwinsize.h b/lib/readline/rlwinsize.h new file mode 100644 index 0000000..d198fcf --- /dev/null +++ b/lib/readline/rlwinsize.h @@ -0,0 +1,58 @@ +/* rlwinsize.h -- an attempt to isolate some of the system-specific defines + for `struct winsize' and TIOCGWINSZ. */ + +/* Copyright (C) 1997-2009 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLWINSIZE_H_) +#define _RLWINSIZE_H_ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +/* Try to find the definitions of `struct winsize' and TIOGCWINSZ */ + +#if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ) +# include <sys/ioctl.h> +#endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */ + +#if defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL) +# include <termios.h> +#endif /* STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */ + +/* Not in either of the standard places, look around. */ +#if !defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL) +# if defined (HAVE_SYS_STREAM_H) +# include <sys/stream.h> +# endif /* HAVE_SYS_STREAM_H */ +# if defined (HAVE_SYS_PTEM_H) /* SVR4.2, at least, has it here */ +# include <sys/ptem.h> +# define _IO_PTEM_H /* work around SVR4.2 1.1.4 bug */ +# endif /* HAVE_SYS_PTEM_H */ +# if defined (HAVE_SYS_PTE_H) /* ??? */ +# include <sys/pte.h> +# endif /* HAVE_SYS_PTE_H */ +#endif /* !STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */ + +#if defined (M_UNIX) && !defined (_SCO_DS) && !defined (tcflow) +# define tcflow(fd, action) ioctl(fd, TCXONC, action) +#endif + +#endif /* _RL_WINSIZE_H */ diff --git a/lib/readline/savestring.c b/lib/readline/savestring.c new file mode 100644 index 0000000..f4bb6aa --- /dev/null +++ b/lib/readline/savestring.c @@ -0,0 +1,40 @@ +/* savestring.c - function version of savestring for backwards compatibility */ + +/* Copyright (C) 1998,2003,2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#include <config.h> +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#include "xmalloc.h" + +/* Backwards compatibility, now that savestring has been removed from + all `public' readline header files. */ +char * +savestring (const char *s) +{ + char *ret; + + ret = (char *)xmalloc (strlen (s) + 1); + strcpy (ret, s); + return ret; +} diff --git a/lib/readline/search.c b/lib/readline/search.c new file mode 100644 index 0000000..eea2301 --- /dev/null +++ b/lib/readline/search.c @@ -0,0 +1,707 @@ +/* search.c - code for non-incremental searching in emacs and vi modes. */ + +/* Copyright (C) 1992-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif + +#include "rldefs.h" +#include "rlmbutil.h" + +#include "readline.h" +#include "history.h" +#include "histlib.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#ifdef abs +# undef abs +#endif +#define abs(x) (((x) >= 0) ? (x) : -(x)) + +_rl_search_cxt *_rl_nscxt = 0; + +static char *noninc_search_string = (char *) NULL; +static int noninc_history_pos; + +static char *prev_line_found = (char *) NULL; + +static int _rl_history_search_len; +/*static*/ int _rl_history_search_pos; +static int _rl_history_search_flags; + +static char *history_search_string; +static int history_string_size; + +static void make_history_line_current (HIST_ENTRY *); +static int noninc_search_from_pos (char *, int, int, int, int *); +static int noninc_dosearch (char *, int, int); +static int noninc_search (int, int); +static int rl_history_search_internal (int, int); +static void rl_history_search_reinit (int); + +static _rl_search_cxt *_rl_nsearch_init (int, int); +static void _rl_nsearch_abort (_rl_search_cxt *); +static int _rl_nsearch_dispatch (_rl_search_cxt *, int); + +/* Make the data from the history entry ENTRY be the contents of the + current line. This doesn't do anything with rl_point; the caller + must set it. */ +static void +make_history_line_current (HIST_ENTRY *entry) +{ + UNDO_LIST *xlist; + + xlist = _rl_saved_line_for_history ? (UNDO_LIST *)_rl_saved_line_for_history->data : 0; + /* At this point, rl_undo_list points to a private search string list. */ + if (rl_undo_list && rl_undo_list != (UNDO_LIST *)entry->data && rl_undo_list != xlist) + rl_free_undo_list (); + + /* Now we create a new undo list with a single insert for this text. + WE DON'T CHANGE THE ORIGINAL HISTORY ENTRY UNDO LIST */ + _rl_replace_text (entry->line, 0, rl_end); + _rl_fix_point (1); +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + /* POSIX.2 says that the `U' command doesn't affect the copy of any + command lines to the edit line. We're going to implement that by + making the undo list start after the matching line is copied to the + current editing buffer. */ + rl_free_undo_list (); +#endif + + /* This will need to free the saved undo list associated with the original + (pre-search) line buffer. + XXX - look at _rl_free_saved_history_line and consider calling it if + rl_undo_list != xlist (or calling rl_free_undo list directly on + _rl_saved_line_for_history->data) */ + if (_rl_saved_line_for_history) + _rl_free_history_entry (_rl_saved_line_for_history); + _rl_saved_line_for_history = (HIST_ENTRY *)NULL; +} + +/* Search the history list for STRING starting at absolute history position + POS. If STRING begins with `^', the search must match STRING at the + beginning of a history line, otherwise a full substring match is performed + for STRING. DIR < 0 means to search backwards through the history list, + DIR >= 0 means to search forward. */ +static int +noninc_search_from_pos (char *string, int pos, int dir, int flags, int *ncp) +{ + int ret, old, sflags; + char *s; + + if (pos < 0) + return -1; + + old = where_history (); + if (history_set_pos (pos) == 0) + return -1; + + RL_SETSTATE(RL_STATE_SEARCH); + /* These functions return the match offset in the line; history_offset gives + the matching line in the history list */ + if (flags & SF_PATTERN) + { + s = string; + sflags = 0; /* Non-anchored search */ + if (*s == '^') + { + sflags |= ANCHORED_SEARCH; + s++; + } + ret = _hs_history_patsearch (s, dir, sflags); + } + else if (*string == '^') + ret = history_search_prefix (string + 1, dir); + else + ret = history_search (string, dir); + RL_UNSETSTATE(RL_STATE_SEARCH); + + if (ncp) + *ncp = ret; /* caller will catch -1 to indicate no-op */ + + if (ret != -1) + ret = where_history (); + + history_set_pos (old); + return (ret); +} + +/* Search for a line in the history containing STRING. If DIR is < 0, the + search is backwards through previous entries, else through subsequent + entries. Returns 1 if the search was successful, 0 otherwise. */ +static int +noninc_dosearch (char *string, int dir, int flags) +{ + int oldpos, pos, ind; + HIST_ENTRY *entry; + + if (string == 0 || *string == '\0' || noninc_history_pos < 0) + { + rl_ding (); + return 0; + } + + pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir, flags, &ind); + if (pos == -1) + { + /* Search failed, current history position unchanged. */ + rl_maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + rl_ding (); + return 0; + } + + noninc_history_pos = pos; + + oldpos = where_history (); + history_set_pos (noninc_history_pos); + entry = current_history (); /* will never be NULL after successful search */ + +#if defined (VI_MODE) + if (rl_editing_mode != vi_mode) +#endif + history_set_pos (oldpos); + + make_history_line_current (entry); + + if (_rl_enable_active_region && ((flags & SF_PATTERN) == 0) && ind > 0 && ind < rl_end) + { + rl_point = ind; + rl_mark = ind + strlen (string); + if (rl_mark > rl_end) + rl_mark = rl_end; /* can't happen? */ + rl_activate_mark (); + } + else + { + rl_point = 0; + rl_mark = rl_end; + } + + rl_clear_message (); + return 1; +} + +static _rl_search_cxt * +_rl_nsearch_init (int dir, int pchar) +{ + _rl_search_cxt *cxt; + char *p; + + cxt = _rl_scxt_alloc (RL_SEARCH_NSEARCH, 0); + if (dir < 0) + cxt->sflags |= SF_REVERSE; /* not strictly needed */ +#if defined (VI_MODE) + if (VI_COMMAND_MODE() && (pchar == '?' || pchar == '/')) + cxt->sflags |= SF_PATTERN; +#endif + + cxt->direction = dir; + cxt->history_pos = cxt->save_line; + + rl_maybe_save_line (); + + /* Clear the undo list, since reading the search string should create its + own undo list, and the whole list will end up being freed when we + finish reading the search string. */ + rl_undo_list = 0; + + /* Use the line buffer to read the search string. */ + rl_line_buffer[0] = 0; + rl_end = rl_point = 0; + + p = _rl_make_prompt_for_search (pchar ? pchar : ':'); + rl_message ("%s", p); + xfree (p); + + RL_SETSTATE(RL_STATE_NSEARCH); + + _rl_nscxt = cxt; + + return cxt; +} + +int +_rl_nsearch_cleanup (_rl_search_cxt *cxt, int r) +{ + _rl_scxt_dispose (cxt, 0); + _rl_nscxt = 0; + + RL_UNSETSTATE(RL_STATE_NSEARCH); + + return (r != 1); +} + +static void +_rl_nsearch_abort (_rl_search_cxt *cxt) +{ + rl_maybe_unsave_line (); + rl_point = cxt->save_point; + rl_mark = cxt->save_mark; + rl_restore_prompt (); + rl_clear_message (); + _rl_fix_point (1); + + RL_UNSETSTATE (RL_STATE_NSEARCH); +} + +/* Process just-read character C according to search context CXT. Return -1 + if the caller should abort the search, 0 if we should break out of the + loop, and 1 if we should continue to read characters. */ +static int +_rl_nsearch_dispatch (_rl_search_cxt *cxt, int c) +{ + int n; + + if (c < 0) + c = CTRL ('C'); + + switch (c) + { + case CTRL('W'): + rl_unix_word_rubout (1, c); + break; + + case CTRL('U'): + rl_unix_line_discard (1, c); + break; + + case RETURN: + case NEWLINE: + return 0; + + case CTRL('H'): + case RUBOUT: + if (rl_point == 0) + { + _rl_nsearch_abort (cxt); + return -1; + } + _rl_rubout_char (1, c); + break; + + case CTRL('C'): + case CTRL('G'): + rl_ding (); + _rl_nsearch_abort (cxt); + return -1; + + case ESC: + /* XXX - experimental code to allow users to bracketed-paste into the + search string. Similar code is in isearch.c:_rl_isearch_dispatch(). + The difference here is that the bracketed paste sometimes doesn't + paste everything, so checking for the prefix and the suffix in the + input queue doesn't work well. We just have to check to see if the + number of chars in the input queue is enough for the bracketed paste + prefix and hope for the best. */ + if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1))) + { + if (_rl_read_bracketed_paste_prefix (c) == 1) + rl_bracketed_paste_begin (1, c); + else + { + c = rl_read_key (); /* get the ESC that got pushed back */ + _rl_insert_char (1, c); + } + } + else + _rl_insert_char (1, c); + break; + + default: +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_insert_text (cxt->mb); + else +#endif + _rl_insert_char (1, c); + break; + } + + (*rl_redisplay_function) (); + rl_deactivate_mark (); + return 1; +} + +/* Perform one search according to CXT, using NONINC_SEARCH_STRING. Return + -1 if the search should be aborted, any other value means to clean up + using _rl_nsearch_cleanup (). Returns 1 if the search was successful, + 0 otherwise. */ +static int +_rl_nsearch_dosearch (_rl_search_cxt *cxt) +{ + rl_mark = cxt->save_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. */ + if (rl_point == 0) + { + if (noninc_search_string == 0) + { + rl_ding (); + rl_restore_prompt (); + RL_UNSETSTATE (RL_STATE_NSEARCH); + return -1; + } + } + else + { + /* We want to start the search from the current history position. */ + noninc_history_pos = cxt->save_line; + FREE (noninc_search_string); + noninc_search_string = savestring (rl_line_buffer); + + /* If we don't want the subsequent undo list generated by the search + matching a history line to include the contents of the search string, + we need to clear rl_line_buffer here. For now, we just clear the + undo list generated by reading the search string. (If the search + fails, the old undo list will be restored by rl_maybe_unsave_line.) */ + rl_free_undo_list (); + } + + rl_restore_prompt (); + return (noninc_dosearch (noninc_search_string, cxt->direction, cxt->sflags&SF_PATTERN)); +} + +/* Search non-interactively through the history list. DIR < 0 means to + search backwards through the history of previous commands; otherwise + the search is for commands subsequent to the current position in the + history list. PCHAR is the character to use for prompting when reading + the search string; if not specified (0), it defaults to `:'. */ +static int +noninc_search (int dir, int pchar) +{ + _rl_search_cxt *cxt; + int c, r; + + cxt = _rl_nsearch_init (dir, pchar); + + if (RL_ISSTATE (RL_STATE_CALLBACK)) + return (0); + + /* Read the search string. */ + r = 0; + while (1) + { + c = _rl_search_getchar (cxt); + + if (c < 0) + { + _rl_nsearch_abort (cxt); + return 1; + } + + if (c == 0) + break; + + r = _rl_nsearch_dispatch (cxt, c); + if (r < 0) + return 1; + else if (r == 0) + break; + } + + r = _rl_nsearch_dosearch (cxt); + return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1)); +} + +/* Search forward through the history list for a string. If the vi-mode + code calls this, KEY will be `?'. */ +int +rl_noninc_forward_search (int count, int key) +{ + return noninc_search (1, (key == '?') ? '?' : 0); +} + +/* Reverse search the history list for a string. If the vi-mode code + calls this, KEY will be `/'. */ +int +rl_noninc_reverse_search (int count, int key) +{ + return noninc_search (-1, (key == '/') ? '/' : 0); +} + +/* Search forward through the history list for the last string searched + for. If there is no saved search string, abort. If the vi-mode code + calls this, KEY will be `N'. */ +int +rl_noninc_forward_search_again (int count, int key) +{ + int r; + + if (!noninc_search_string) + { + rl_ding (); + return (1); + } +#if defined (VI_MODE) + if (VI_COMMAND_MODE() && key == 'N') + r = noninc_dosearch (noninc_search_string, 1, SF_PATTERN); + else +#endif + r = noninc_dosearch (noninc_search_string, 1, 0); + return (r != 1); +} + +/* Reverse search in the history list for the last string searched + for. If there is no saved search string, abort. If the vi-mode code + calls this, KEY will be `n'. */ +int +rl_noninc_reverse_search_again (int count, int key) +{ + int r; + + if (!noninc_search_string) + { + rl_ding (); + return (1); + } +#if defined (VI_MODE) + if (VI_COMMAND_MODE() && key == 'n') + r = noninc_dosearch (noninc_search_string, -1, SF_PATTERN); + else +#endif + r = noninc_dosearch (noninc_search_string, -1, 0); + return (r != 1); +} + +#if defined (READLINE_CALLBACKS) +int +_rl_nsearch_callback (_rl_search_cxt *cxt) +{ + int c, r; + + c = _rl_search_getchar (cxt); + if (c <= 0) + { + if (c < 0) + _rl_nsearch_abort (cxt); + return 1; + } + r = _rl_nsearch_dispatch (cxt, c); + if (r != 0) + return 1; + + r = _rl_nsearch_dosearch (cxt); + return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1)); +} +#endif + +static int +rl_history_search_internal (int count, int dir) +{ + HIST_ENTRY *temp; + int ret, oldpos, newcol; + int had_saved_line; + char *t; + + had_saved_line = _rl_saved_line_for_history != 0; + rl_maybe_save_line (); + temp = (HIST_ENTRY *)NULL; + + /* Search COUNT times through the history for a line matching + history_search_string. If history_search_string[0] == '^', the + line must match from the start; otherwise any substring can match. + When this loop finishes, TEMP, if non-null, is the history line to + copy into the line buffer. */ + while (count) + { + RL_CHECK_SIGNALS (); + ret = noninc_search_from_pos (history_search_string, _rl_history_search_pos + dir, dir, 0, &newcol); + if (ret == -1) + break; + + /* Get the history entry we found. */ + _rl_history_search_pos = ret; + oldpos = where_history (); + history_set_pos (_rl_history_search_pos); + temp = current_history (); /* will never be NULL after successful search */ + history_set_pos (oldpos); + + /* Don't find multiple instances of the same line. */ + if (prev_line_found && STREQ (prev_line_found, temp->line)) + continue; + prev_line_found = temp->line; + count--; + } + + /* If we didn't find anything at all, return. */ + if (temp == 0) + { + /* XXX - check had_saved_line here? */ + rl_maybe_unsave_line (); + rl_ding (); + /* If you don't want the saved history line (last match) to show up + in the line buffer after the search fails, change the #if 0 to + #if 1 */ +#if 0 + if (rl_point > _rl_history_search_len) + { + 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; + } + + /* Copy the line we found into the current line buffer. */ + make_history_line_current (temp); + + /* decide where to put rl_point -- need to change this for pattern search */ + if (_rl_history_search_flags & ANCHORED_SEARCH) + rl_point = _rl_history_search_len; /* easy case */ + else + { +#if 0 + t = strstr (rl_line_buffer, history_search_string); /* XXX */ + rl_point = t ? (int)(t - rl_line_buffer) + _rl_history_search_len : rl_end; +#else + rl_point = (newcol >= 0) ? newcol : rl_end; +#endif + } + rl_mark = rl_end; + + return 0; +} + +static void +rl_history_search_reinit (int flags) +{ + int sind; + + _rl_history_search_pos = where_history (); + _rl_history_search_len = rl_point; + _rl_history_search_flags = flags; + + prev_line_found = (char *)NULL; + if (rl_point) + { + /* Allocate enough space for anchored and non-anchored searches */ + if (_rl_history_search_len >= history_string_size - 2) + { + history_string_size = _rl_history_search_len + 2; + history_search_string = (char *)xrealloc (history_search_string, history_string_size); + } + sind = 0; + if (flags & ANCHORED_SEARCH) + history_search_string[sind++] = '^'; + strncpy (history_search_string + sind, rl_line_buffer, rl_point); + history_search_string[rl_point + sind] = '\0'; + } + _rl_free_saved_history_line (); /* XXX rl_undo_list? */ +} + +/* Search forward in the history for the string of characters + from the start of the line to rl_point. This is a non-incremental + search. The search is anchored to the beginning of the history line. */ +int +rl_history_search_forward (int count, int ignore) +{ + if (count == 0) + return (0); + + if (rl_last_func != rl_history_search_forward && + rl_last_func != rl_history_search_backward) + rl_history_search_reinit (ANCHORED_SEARCH); + + if (_rl_history_search_len == 0) + return (rl_get_next_history (count, ignore)); + return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1)); +} + +/* Search backward through the history for the string of characters + from the start of the line to rl_point. This is a non-incremental + search. */ +int +rl_history_search_backward (int count, int ignore) +{ + if (count == 0) + return (0); + + if (rl_last_func != rl_history_search_forward && + rl_last_func != rl_history_search_backward) + rl_history_search_reinit (ANCHORED_SEARCH); + + if (_rl_history_search_len == 0) + return (rl_get_previous_history (count, ignore)); + return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1)); +} + +/* Search forward in the history for the string of characters + from the start of the line to rl_point. This is a non-incremental + search. The search succeeds if the search string is present anywhere + in the history line. */ +int +rl_history_substr_search_forward (int count, int ignore) +{ + if (count == 0) + return (0); + + if (rl_last_func != rl_history_substr_search_forward && + rl_last_func != rl_history_substr_search_backward) + rl_history_search_reinit (NON_ANCHORED_SEARCH); + + if (_rl_history_search_len == 0) + return (rl_get_next_history (count, ignore)); + return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1)); +} + +/* Search backward through the history for the string of characters + from the start of the line to rl_point. This is a non-incremental + search. */ +int +rl_history_substr_search_backward (int count, int ignore) +{ + if (count == 0) + return (0); + + if (rl_last_func != rl_history_substr_search_forward && + rl_last_func != rl_history_substr_search_backward) + rl_history_search_reinit (NON_ANCHORED_SEARCH); + + if (_rl_history_search_len == 0) + return (rl_get_previous_history (count, ignore)); + return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1)); +} diff --git a/lib/readline/shell.c b/lib/readline/shell.c new file mode 100644 index 0000000..36c9148 --- /dev/null +++ b/lib/readline/shell.c @@ -0,0 +1,214 @@ +/* shell.c -- readline utility functions that are normally provided by + bash when readline is linked as part of the shell. */ + +/* Copyright (C) 1997-2009,2017,2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_STRING_H) +# include <string.h> +#else +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#if defined (HAVE_FCNTL_H) +#include <fcntl.h> +#endif +#if defined (HAVE_PWD_H) +#include <pwd.h> +#endif + +#include <stdio.h> + +#include "rlstdc.h" +#include "rlshell.h" +#include "rldefs.h" + +#include "xmalloc.h" + +#if defined (HAVE_GETPWUID) && !defined (HAVE_GETPW_DECLS) +extern struct passwd *getpwuid (uid_t); +#endif /* HAVE_GETPWUID && !HAVE_GETPW_DECLS */ + +#ifndef NULL +# define NULL 0 +#endif + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +/* Nonzero if the integer type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* 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; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \ + + 1 + TYPE_SIGNED (t)) + +/* All of these functions are resolved from bash if we are linking readline + as part of bash. */ + +/* Does shell-like quoting using single quotes. */ +char * +sh_single_quote (char *string) +{ + register int c; + char *result, *r, *s; + + result = (char *)xmalloc (3 + (4 * strlen (string))); + r = result; + *r++ = '\''; + + for (s = string; s && (c = *s); s++) + { + *r++ = c; + + if (c == '\'') + { + *r++ = '\\'; /* insert escaped single quote */ + *r++ = '\''; + *r++ = '\''; /* start new quoted string */ + } + } + + *r++ = '\''; + *r = '\0'; + + return (result); +} + +/* Set the environment variables LINES and COLUMNS to lines and cols, + respectively. */ +static char setenv_buf[INT_STRLEN_BOUND (int) + 1]; +static char putenv_buf1[INT_STRLEN_BOUND (int) + 6 + 1]; /* sizeof("LINES=") == 6 */ +static char putenv_buf2[INT_STRLEN_BOUND (int) + 8 + 1]; /* sizeof("COLUMNS=") == 8 */ + +void +sh_set_lines_and_columns (int lines, int cols) +{ +#if defined (HAVE_SETENV) + sprintf (setenv_buf, "%d", lines); + setenv ("LINES", setenv_buf, 1); + + sprintf (setenv_buf, "%d", cols); + setenv ("COLUMNS", setenv_buf, 1); +#else /* !HAVE_SETENV */ +# if defined (HAVE_PUTENV) + sprintf (putenv_buf1, "LINES=%d", lines); + putenv (putenv_buf1); + + sprintf (putenv_buf2, "COLUMNS=%d", cols); + putenv (putenv_buf2); +# endif /* HAVE_PUTENV */ +#endif /* !HAVE_SETENV */ +} + +char * +sh_get_env_value (const char *varname) +{ + return ((char *)getenv (varname)); +} + +char * +sh_get_home_dir (void) +{ + static char *home_dir = (char *)NULL; + struct passwd *entry; + + if (home_dir) + return (home_dir); + + home_dir = (char *)NULL; +#if defined (HAVE_GETPWUID) +# if defined (__TANDEM) + entry = getpwnam (getlogin ()); +# else + entry = getpwuid (getuid ()); +# endif + if (entry) + home_dir = savestring (entry->pw_dir); +#endif + +#if defined (HAVE_GETPWENT) + endpwent (); /* some systems need this */ +#endif + + return (home_dir); +} + +#if !defined (O_NDELAY) +# if defined (FNDELAY) +# define O_NDELAY FNDELAY +# endif +#endif + +int +sh_unset_nodelay_mode (int fd) +{ +#if defined (HAVE_FCNTL) + int flags, bflags; + + if ((flags = fcntl (fd, F_GETFL, 0)) < 0) + return -1; + + bflags = 0; + +#ifdef O_NONBLOCK + bflags |= O_NONBLOCK; +#endif + +#ifdef O_NDELAY + bflags |= O_NDELAY; +#endif + + if (flags & bflags) + { + flags &= ~bflags; + return (fcntl (fd, F_SETFL, flags)); + } +#endif + + return 0; +} diff --git a/lib/readline/signals.c b/lib/readline/signals.c new file mode 100644 index 0000000..9df365e --- /dev/null +++ b/lib/readline/signals.c @@ -0,0 +1,754 @@ +/* signals.c -- signal handling support for readline. */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> /* Just for NULL. Yuck. */ +#include <sys/types.h> +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +#if defined (GWINSZ_IN_SYS_IOCTL) +# include <sys/ioctl.h> +#endif /* GWINSZ_IN_SYS_IOCTL */ + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" + +#if defined (HANDLE_SIGNALS) + +#define SIGHANDLER_RETURN return + +/* This typedef is equivalent to the one for Function; it allows us + to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */ +typedef void SigHandler (int); + +#if defined (HAVE_POSIX_SIGNALS) +typedef struct sigaction sighandler_cxt; +# define rl_sigaction(s, nh, oh) sigaction(s, nh, oh) +#else +typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt; +# define sigemptyset(m) +#endif /* !HAVE_POSIX_SIGNALS */ + +#ifndef SA_RESTART +# define SA_RESTART 0 +#endif + +static SigHandler *rl_set_sighandler (int, SigHandler *, sighandler_cxt *); +static void rl_maybe_set_sighandler (int, SigHandler *, sighandler_cxt *); +static void rl_maybe_restore_sighandler (int, sighandler_cxt *); + +static void rl_signal_handler (int); +static void _rl_handle_signal (int); + +/* Exported variables for use by applications. */ + +/* If non-zero, readline will install its own signal handlers for + SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */ +int rl_catch_signals = 1; + +/* If non-zero, readline will install a signal handler for SIGWINCH. */ +#ifdef SIGWINCH +int rl_catch_sigwinch = 1; +#else +int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */ +#endif + +/* Private variables. */ +int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including <signal.h> everywhere */ + +/* If non-zero, print characters corresponding to received signals as long as + the user has indicated his desire to do so (_rl_echo_control_chars). */ +int _rl_echoctl = 0; + +int _rl_intr_char = 0; +int _rl_quit_char = 0; +int _rl_susp_char = 0; + +static int signals_set_flag; +static int sigwinch_set_flag; + +#if defined (HAVE_POSIX_SIGNALS) +sigset_t _rl_orig_sigset; +#endif /* !HAVE_POSIX_SIGNALS */ + +/* **************************************************************** */ +/* */ +/* Signal Handling */ +/* */ +/* **************************************************************** */ + +static sighandler_cxt old_int, old_term, old_hup, old_alrm, old_quit; +#if defined (SIGTSTP) +static sighandler_cxt old_tstp, old_ttou, old_ttin; +#endif +#if defined (SIGWINCH) +static sighandler_cxt old_winch; +#endif + +_rl_sigcleanup_func_t *_rl_sigcleanup; +void *_rl_sigcleanarg; + +/* Readline signal handler functions. */ + +/* Called from RL_CHECK_SIGNALS() macro to run signal handling code. */ +void +_rl_signal_handler (int sig) +{ + _rl_caught_signal = 0; /* XXX */ + +#if defined (SIGWINCH) + if (sig == SIGWINCH) + { + RL_SETSTATE(RL_STATE_SIGHANDLER); + + rl_resize_terminal (); + /* XXX - experimental for now */ + /* Call a signal hook because though we called the original signal handler + in rl_sigwinch_handler below, we will not resend the signal to + ourselves. */ + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); + + RL_UNSETSTATE(RL_STATE_SIGHANDLER); + } + else +#endif + _rl_handle_signal (sig); + + SIGHANDLER_RETURN; +} + +static void +rl_signal_handler (int sig) +{ + _rl_caught_signal = sig; + SIGHANDLER_RETURN; +} + +/* This is called to handle a signal when it is safe to do so (out of the + signal handler execution path). Called by _rl_signal_handler for all the + signals readline catches except SIGWINCH. */ +static void +_rl_handle_signal (int sig) +{ + int block_sig; + +#if defined (HAVE_POSIX_SIGNALS) + sigset_t set, oset; +#else /* !HAVE_POSIX_SIGNALS */ +# if defined (HAVE_BSD_SIGNALS) + long omask; +# else /* !HAVE_BSD_SIGNALS */ + sighandler_cxt dummy_cxt; /* needed for rl_set_sighandler call */ +# endif /* !HAVE_BSD_SIGNALS */ +#endif /* !HAVE_POSIX_SIGNALS */ + + RL_SETSTATE(RL_STATE_SIGHANDLER); + +#if !defined (HAVE_BSD_SIGNALS) && !defined (HAVE_POSIX_SIGNALS) + /* Since the signal will not be blocked while we are in the signal + handler, ignore it until rl_clear_signals resets the catcher. */ +# if defined (SIGALRM) + if (sig == SIGINT || sig == SIGALRM) +# else + if (sig == SIGINT) +# endif + rl_set_sighandler (sig, SIG_IGN, &dummy_cxt); +#endif /* !HAVE_BSD_SIGNALS && !HAVE_POSIX_SIGNALS */ + + /* If there's a sig cleanup function registered, call it and `deregister' + the cleanup function to avoid multiple calls */ + if (_rl_sigcleanup) + { + (*_rl_sigcleanup) (sig, _rl_sigcleanarg); + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + } + +#if defined (HAVE_POSIX_SIGNALS) + /* Get the current set of blocked signals. If we want to block a signal for + the duration of the cleanup functions, make sure to add it to SET and + set block_sig = 1 (see the SIGHUP case below). */ + block_sig = 0; /* sentinel to block signals with sigprocmask */ + sigemptyset (&set); + sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &set); +#endif + + switch (sig) + { + case SIGINT: + /* We will end up blocking SIGTTOU while we are resetting the tty, so + watch out for this if it causes problems. We could prevent this by + setting block_sig to 1 without modifying SET. */ + _rl_reset_completion_state (); + rl_free_line_state (); +#if defined (READLINE_CALLBACKS) + rl_callback_sigcleanup (); +#endif + + /* FALLTHROUGH */ + +#if defined (SIGTSTP) + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: +# if defined (HAVE_POSIX_SIGNALS) + /* Block SIGTTOU so we can restore the terminal settings to something + sane without stopping on SIGTTOU if we have been placed into the + background. Even trying to get the current terminal pgrp with + tcgetpgrp() will generate SIGTTOU, so we don't bother. We still do + this even if we've been stopped on SIGTTOU, since we handle signals + when we have returned from the signal handler and the signal is no + longer blocked. */ + if (block_sig == 0) + { + sigaddset (&set, SIGTTOU); + block_sig = 1; + } +# endif +#endif /* SIGTSTP */ + /* Any signals that should be blocked during cleanup should go here. */ +#if defined (SIGHUP) + case SIGHUP: +# if defined (_AIX) + if (block_sig == 0) + { + sigaddset (&set, sig); + block_sig = 1; + } +# endif // _AIX +#endif + /* Signals that don't require blocking during cleanup should go here. */ + case SIGTERM: +#if defined (SIGALRM) + case SIGALRM: + if (sig == SIGALRM) + _rl_timeout_handle_sigalrm (); +#endif +#if defined (SIGQUIT) + case SIGQUIT: +#endif + +#if defined (HAVE_POSIX_SIGNALS) + if (block_sig) + sigprocmask (SIG_BLOCK, &set, &oset); +#endif + + rl_echo_signal_char (sig); + rl_cleanup_after_signal (); + + /* At this point, the application's signal handler, if any, is the + current handler. */ + +#if defined (HAVE_POSIX_SIGNALS) + /* Unblock any signal(s) blocked above */ + if (block_sig) + sigprocmask (SIG_UNBLOCK, &oset, (sigset_t *)NULL); +#endif + + /* We don't have to bother unblocking the signal because we are not + running in a signal handler context. */ + +#if defined (__EMX__) + signal (sig, SIG_ACK); +#endif + +#if defined (HAVE_KILL) + kill (getpid (), sig); +#else + raise (sig); /* assume we have raise */ +#endif + + /* We don't need to modify the signal mask now that this is not run in + a signal handler context. */ + + rl_reset_after_signal (); + } + + RL_UNSETSTATE(RL_STATE_SIGHANDLER); + SIGHANDLER_RETURN; +} + +#if defined (SIGWINCH) +static void +rl_sigwinch_handler (int sig) +{ + SigHandler *oh; + +#if defined (MUST_REINSTALL_SIGHANDLERS) + sighandler_cxt dummy_winch; + + /* We don't want to change old_winch -- it holds the state of SIGWINCH + disposition set by the calling application. We need this state + because we call the application's SIGWINCH handler after updating + our own idea of the screen size. */ + rl_set_sighandler (SIGWINCH, rl_sigwinch_handler, &dummy_winch); +#endif + + RL_SETSTATE(RL_STATE_SIGHANDLER); + _rl_caught_signal = sig; + + /* If another sigwinch handler has been installed, call it. */ + oh = (SigHandler *)old_winch.sa_handler; + if (oh && oh != (SigHandler *)SIG_IGN && oh != (SigHandler *)SIG_DFL) + (*oh) (sig); + + RL_UNSETSTATE(RL_STATE_SIGHANDLER); + SIGHANDLER_RETURN; +} +#endif /* SIGWINCH */ + +/* Functions to manage signal handling. */ + +#if !defined (HAVE_POSIX_SIGNALS) +static int +rl_sigaction (int sig, sighandler_cxt *nh, sighandler_cxt *oh) +{ + oh->sa_handler = signal (sig, nh->sa_handler); + return 0; +} +#endif /* !HAVE_POSIX_SIGNALS */ + +/* Set up a readline-specific signal handler, saving the old signal + information in OHANDLER. Return the old signal handler, like + signal(). */ +static SigHandler * +rl_set_sighandler (int sig, SigHandler *handler, sighandler_cxt *ohandler) +{ + sighandler_cxt old_handler; +#if defined (HAVE_POSIX_SIGNALS) + struct sigaction act; + + act.sa_handler = handler; +# if defined (SIGWINCH) + act.sa_flags = (sig == SIGWINCH) ? SA_RESTART : 0; +# else + act.sa_flags = 0; +# endif /* SIGWINCH */ + sigemptyset (&act.sa_mask); + sigemptyset (&ohandler->sa_mask); + sigaction (sig, &act, &old_handler); +#else + old_handler.sa_handler = (SigHandler *)signal (sig, handler); +#endif /* !HAVE_POSIX_SIGNALS */ + + /* XXX -- assume we have memcpy */ + /* If rl_set_signals is called twice in a row, don't set the old handler to + rl_signal_handler, because that would cause infinite recursion. */ + if (handler != rl_signal_handler || old_handler.sa_handler != rl_signal_handler) + memcpy (ohandler, &old_handler, sizeof (sighandler_cxt)); + + return (ohandler->sa_handler); +} + +/* Set disposition of SIG to HANDLER, returning old state in OHANDLER. Don't + change disposition if OHANDLER indicates the signal was ignored. */ +static void +rl_maybe_set_sighandler (int sig, SigHandler *handler, sighandler_cxt *ohandler) +{ + sighandler_cxt dummy; + SigHandler *oh; + + sigemptyset (&dummy.sa_mask); + dummy.sa_flags = 0; + oh = rl_set_sighandler (sig, handler, ohandler); + if (oh == (SigHandler *)SIG_IGN) + rl_sigaction (sig, ohandler, &dummy); +} + +/* Set the disposition of SIG to HANDLER, if HANDLER->sa_handler indicates the + signal was not being ignored. MUST only be called for signals whose + disposition was changed using rl_maybe_set_sighandler or for which the + SIG_IGN check was performed inline (e.g., SIGALRM below). */ +static void +rl_maybe_restore_sighandler (int sig, sighandler_cxt *handler) +{ + sighandler_cxt dummy; + + sigemptyset (&dummy.sa_mask); + dummy.sa_flags = 0; + if (handler->sa_handler != SIG_IGN) + rl_sigaction (sig, handler, &dummy); +} + +int +rl_set_signals (void) +{ + sighandler_cxt dummy; + SigHandler *oh; +#if defined (HAVE_POSIX_SIGNALS) + static int sigmask_set = 0; + static sigset_t bset, oset; +#endif + +#if defined (HAVE_POSIX_SIGNALS) + if (rl_catch_signals && sigmask_set == 0) + { + sigemptyset (&bset); + + sigaddset (&bset, SIGINT); + sigaddset (&bset, SIGTERM); +#if defined (SIGHUP) + sigaddset (&bset, SIGHUP); +#endif +#if defined (SIGQUIT) + sigaddset (&bset, SIGQUIT); +#endif +#if defined (SIGALRM) + sigaddset (&bset, SIGALRM); +#endif +#if defined (SIGTSTP) + sigaddset (&bset, SIGTSTP); +#endif +#if defined (SIGTTIN) + sigaddset (&bset, SIGTTIN); +#endif +#if defined (SIGTTOU) + sigaddset (&bset, SIGTTOU); +#endif + sigmask_set = 1; + } +#endif /* HAVE_POSIX_SIGNALS */ + + if (rl_catch_signals && signals_set_flag == 0) + { +#if defined (HAVE_POSIX_SIGNALS) + sigemptyset (&_rl_orig_sigset); + sigprocmask (SIG_BLOCK, &bset, &_rl_orig_sigset); +#endif + + rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int); + rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term); +#if defined (SIGHUP) + rl_maybe_set_sighandler (SIGHUP, rl_signal_handler, &old_hup); +#endif +#if defined (SIGQUIT) + rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit); +#endif + +#if defined (SIGALRM) + oh = rl_set_sighandler (SIGALRM, rl_signal_handler, &old_alrm); + if (oh == (SigHandler *)SIG_IGN) + rl_sigaction (SIGALRM, &old_alrm, &dummy); +#if defined (HAVE_POSIX_SIGNALS) && defined (SA_RESTART) + /* If the application using readline has already installed a signal + handler with SA_RESTART, SIGALRM will cause reads to be restarted + automatically, so readline should just get out of the way. Since + we tested for SIG_IGN above, we can just test for SIG_DFL here. */ + if (oh != (SigHandler *)SIG_DFL && (old_alrm.sa_flags & SA_RESTART)) + rl_sigaction (SIGALRM, &old_alrm, &dummy); +#endif /* HAVE_POSIX_SIGNALS */ +#endif /* SIGALRM */ + +#if defined (SIGTSTP) + rl_maybe_set_sighandler (SIGTSTP, rl_signal_handler, &old_tstp); +#endif /* SIGTSTP */ + +#if defined (SIGTTOU) + rl_maybe_set_sighandler (SIGTTOU, rl_signal_handler, &old_ttou); +#endif /* SIGTTOU */ + +#if defined (SIGTTIN) + rl_maybe_set_sighandler (SIGTTIN, rl_signal_handler, &old_ttin); +#endif /* SIGTTIN */ + + signals_set_flag = 1; + +#if defined (HAVE_POSIX_SIGNALS) + sigprocmask (SIG_SETMASK, &_rl_orig_sigset, (sigset_t *)NULL); +#endif + } + else if (rl_catch_signals == 0) + { +#if defined (HAVE_POSIX_SIGNALS) + sigemptyset (&_rl_orig_sigset); + sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &_rl_orig_sigset); +#endif + } + +#if defined (SIGWINCH) + if (rl_catch_sigwinch && sigwinch_set_flag == 0) + { + rl_maybe_set_sighandler (SIGWINCH, rl_sigwinch_handler, &old_winch); + sigwinch_set_flag = 1; + } +#endif /* SIGWINCH */ + + return 0; +} + +int +rl_clear_signals (void) +{ + sighandler_cxt dummy; + + if (rl_catch_signals && signals_set_flag == 1) + { + /* Since rl_maybe_set_sighandler doesn't override a SIG_IGN handler, + we should in theory not have to restore a handler where + old_xxx.sa_handler == SIG_IGN. That's what rl_maybe_restore_sighandler + does. Fewer system calls should reduce readline's per-line + overhead */ + rl_maybe_restore_sighandler (SIGINT, &old_int); + rl_maybe_restore_sighandler (SIGTERM, &old_term); +#if defined (SIGHUP) + rl_maybe_restore_sighandler (SIGHUP, &old_hup); +#endif +#if defined (SIGQUIT) + rl_maybe_restore_sighandler (SIGQUIT, &old_quit); +#endif +#if defined (SIGALRM) + rl_maybe_restore_sighandler (SIGALRM, &old_alrm); +#endif + +#if defined (SIGTSTP) + rl_maybe_restore_sighandler (SIGTSTP, &old_tstp); +#endif /* SIGTSTP */ + +#if defined (SIGTTOU) + rl_maybe_restore_sighandler (SIGTTOU, &old_ttou); +#endif /* SIGTTOU */ + +#if defined (SIGTTIN) + rl_maybe_restore_sighandler (SIGTTIN, &old_ttin); +#endif /* SIGTTIN */ + + signals_set_flag = 0; + } + +#if defined (SIGWINCH) + if (rl_catch_sigwinch && sigwinch_set_flag == 1) + { + sigemptyset (&dummy.sa_mask); + rl_sigaction (SIGWINCH, &old_winch, &dummy); + sigwinch_set_flag = 0; + } +#endif + + return 0; +} + +/* Clean up the terminal and readline state after catching a signal, before + resending it to the calling application. */ +void +rl_cleanup_after_signal (void) +{ + _rl_clean_up_for_exit (); + if (rl_deprep_term_function) + (*rl_deprep_term_function) (); + rl_clear_pending_input (); + rl_clear_signals (); +} + +/* Reset the terminal and readline state after a signal handler returns. */ +void +rl_reset_after_signal (void) +{ + if (rl_prep_term_function) + (*rl_prep_term_function) (_rl_meta_flag); + rl_set_signals (); +} + +/* Free up the readline variable line state for the current line (undo list, + any partial history entry, any keyboard macros in progress, and any + numeric arguments in process) after catching a signal, before calling + rl_cleanup_after_signal(). */ +void +rl_free_line_state (void) +{ + register HIST_ENTRY *entry; + + rl_free_undo_list (); + + entry = current_history (); + if (entry) + entry->data = (char *)NULL; + + _rl_kill_kbd_macro (); + rl_clear_message (); + _rl_reset_argument (); +} + +int +rl_pending_signal (void) +{ + return (_rl_caught_signal); +} + +void +rl_check_signals (void) +{ + RL_CHECK_SIGNALS (); +} +#endif /* HANDLE_SIGNALS */ + +/* **************************************************************** */ +/* */ +/* SIGINT Management */ +/* */ +/* **************************************************************** */ + +#if defined (HAVE_POSIX_SIGNALS) +static sigset_t sigint_set, sigint_oset; +static sigset_t sigwinch_set, sigwinch_oset; +#else /* !HAVE_POSIX_SIGNALS */ +# if defined (HAVE_BSD_SIGNALS) +static int sigint_oldmask; +static int sigwinch_oldmask; +# endif /* HAVE_BSD_SIGNALS */ +#endif /* !HAVE_POSIX_SIGNALS */ + +static int sigint_blocked; +static int sigwinch_blocked; + +/* Cause SIGINT to not be delivered until the corresponding call to + release_sigint(). */ +void +_rl_block_sigint (void) +{ + if (sigint_blocked) + return; + + sigint_blocked = 1; +} + +/* Allow SIGINT to be delivered. */ +void +_rl_release_sigint (void) +{ + if (sigint_blocked == 0) + return; + + sigint_blocked = 0; + RL_CHECK_SIGNALS (); +} + +/* Cause SIGWINCH to not be delivered until the corresponding call to + release_sigwinch(). */ +void +_rl_block_sigwinch (void) +{ + if (sigwinch_blocked) + return; + +#if defined (SIGWINCH) + +#if defined (HAVE_POSIX_SIGNALS) + sigemptyset (&sigwinch_set); + sigemptyset (&sigwinch_oset); + sigaddset (&sigwinch_set, SIGWINCH); + sigprocmask (SIG_BLOCK, &sigwinch_set, &sigwinch_oset); +#else /* !HAVE_POSIX_SIGNALS */ +# if defined (HAVE_BSD_SIGNALS) + sigwinch_oldmask = sigblock (sigmask (SIGWINCH)); +# else /* !HAVE_BSD_SIGNALS */ +# if defined (HAVE_USG_SIGHOLD) + sighold (SIGWINCH); +# endif /* HAVE_USG_SIGHOLD */ +# endif /* !HAVE_BSD_SIGNALS */ +#endif /* !HAVE_POSIX_SIGNALS */ + +#endif /* SIGWINCH */ + + sigwinch_blocked = 1; +} + +/* Allow SIGWINCH to be delivered. */ +void +_rl_release_sigwinch (void) +{ + if (sigwinch_blocked == 0) + return; + +#if defined (SIGWINCH) + +#if defined (HAVE_POSIX_SIGNALS) + sigprocmask (SIG_SETMASK, &sigwinch_oset, (sigset_t *)NULL); +#else +# if defined (HAVE_BSD_SIGNALS) + sigsetmask (sigwinch_oldmask); +# else /* !HAVE_BSD_SIGNALS */ +# if defined (HAVE_USG_SIGHOLD) + sigrelse (SIGWINCH); +# endif /* HAVE_USG_SIGHOLD */ +# endif /* !HAVE_BSD_SIGNALS */ +#endif /* !HAVE_POSIX_SIGNALS */ + +#endif /* SIGWINCH */ + + sigwinch_blocked = 0; +} + +/* **************************************************************** */ +/* */ +/* Echoing special control characters */ +/* */ +/* **************************************************************** */ +void +rl_echo_signal_char (int sig) +{ + char cstr[3]; + int cslen, c; + + if (_rl_echoctl == 0 || _rl_echo_control_chars == 0) + return; + + switch (sig) + { + case SIGINT: c = _rl_intr_char; break; +#if defined (SIGQUIT) + case SIGQUIT: c = _rl_quit_char; break; +#endif +#if defined (SIGTSTP) + case SIGTSTP: c = _rl_susp_char; break; +#endif + default: return; + } + + if (CTRL_CHAR (c) || c == RUBOUT) + { + cstr[0] = '^'; + cstr[1] = CTRL_CHAR (c) ? UNCTRL (c) : '?'; + cstr[cslen = 2] = '\0'; + } + else + { + cstr[0] = c; + cstr[cslen = 1] = '\0'; + } + + _rl_output_some_chars (cstr, cslen); +} diff --git a/lib/readline/tcap.h b/lib/readline/tcap.h new file mode 100644 index 0000000..859e6ee --- /dev/null +++ b/lib/readline/tcap.h @@ -0,0 +1,60 @@ +/* tcap.h -- termcap library functions and variables. */ + +/* Copyright (C) 1996-2015 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLTCAP_H_) +#define _RLTCAP_H_ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#if defined (HAVE_TERMCAP_H) +# if defined (__linux__) && !defined (SPEED_T_IN_SYS_TYPES) +# include "rltty.h" +# endif +# include <termcap.h> +#elif defined (HAVE_NCURSES_TERMCAP_H) +# include <ncurses/termcap.h> +#else + +/* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC. + Unfortunately, PC is a global variable used by the termcap library. */ +#ifdef PC +# undef PC +#endif + +extern char PC; +extern char *UP, *BC; + +extern short ospeed; + +extern int tgetent (); +extern int tgetflag (); +extern int tgetnum (); +extern char *tgetstr (); + +extern int tputs (); + +extern char *tgoto (); + +#endif /* HAVE_TERMCAP_H */ + +#endif /* !_RLTCAP_H_ */ diff --git a/lib/readline/terminal.c b/lib/readline/terminal.c new file mode 100644 index 0000000..9997161 --- /dev/null +++ b/lib/readline/terminal.c @@ -0,0 +1,939 @@ +/* terminal.c -- controlling the terminal with termcap. */ + +/* Copyright (C) 1996-2022 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include "posixstat.h" +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_LOCALE_H) +# include <locale.h> +#endif + +#include <stdio.h> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +#ifdef __MSDOS__ +# include <pc.h> +#endif + +#include "rltty.h" +#if defined (HAVE_SYS_IOCTL_H) +# include <sys/ioctl.h> /* include for declaration of ioctl */ +#endif +#include "tcap.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +#if defined (__MINGW32__) +# include <windows.h> +# include <wincon.h> + +static void _win_get_screensize (int *, int *); +#endif + +#if defined (__EMX__) +static void _emx_get_screensize (int *, int *); +#endif + +/* If the calling application sets this to a non-zero value, readline will + use the $LINES and $COLUMNS environment variables to set its idea of the + window size before interrogating the kernel. */ +int rl_prefer_env_winsize = 0; + +/* If this is non-zero, readline will set LINES and COLUMNS in the + environment when it handles SIGWINCH. */ +int rl_change_environment = 1; + +/* **************************************************************** */ +/* */ +/* Terminal and Termcap */ +/* */ +/* **************************************************************** */ + +#ifndef __MSDOS__ +static char *term_buffer = (char *)NULL; +static char *term_string_buffer = (char *)NULL; +#endif + +static int tcap_initialized; + +#if !defined (__linux__) && !defined (NCURSES_VERSION) +# if defined (__EMX__) || defined (NEED_EXTERN_PC) +extern +# endif /* __EMX__ || NEED_EXTERN_PC */ +char PC, *BC, *UP; +#endif /* !__linux__ && !NCURSES_VERSION */ + +/* Some strings to control terminal actions. These are output by tputs (). */ +char *_rl_term_clreol; +char *_rl_term_clrpag; +char *_rl_term_clrscroll; +char *_rl_term_cr; +char *_rl_term_backspace; +char *_rl_term_goto; +char *_rl_term_pc; + +/* Non-zero if we determine that the terminal can do character insertion. */ +int _rl_terminal_can_insert = 0; + +/* How to insert characters. */ +char *_rl_term_im; +char *_rl_term_ei; +char *_rl_term_ic; +char *_rl_term_ip; +char *_rl_term_IC; + +/* How to delete characters. */ +char *_rl_term_dc; +char *_rl_term_DC; + +/* How to move forward a char, non-destructively */ +char *_rl_term_forward_char; + +/* How to go up a line. */ +char *_rl_term_up; + +/* A visible bell; char if the terminal can be made to flash the screen. */ +static char *_rl_visible_bell; + +/* Non-zero means the terminal can auto-wrap lines. */ +int _rl_term_autowrap = -1; + +/* Non-zero means that this terminal has a meta key. */ +static int term_has_meta; + +/* The sequences to write to turn on and off the meta key, if this + terminal has one. */ +static char *_rl_term_mm; +static char *_rl_term_mo; + +/* The sequences to enter and exit standout mode. */ +static char *_rl_term_so; +static char *_rl_term_se; + +/* The key sequences output by the arrow keys, if this terminal has any. */ +static char *_rl_term_ku; +static char *_rl_term_kd; +static char *_rl_term_kr; +static char *_rl_term_kl; + +/* How to initialize and reset the arrow keys, if this terminal has any. */ +static char *_rl_term_ks; +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 */ + +/* Delete key */ +static char *_rl_term_kD; + +/* Insert key */ +static char *_rl_term_kI; + +/* Page up and page down keys */ +static char *_rl_term_kP; +static char *_rl_term_kN; + +/* Cursor control */ +static char *_rl_term_vs; /* very visible */ +static char *_rl_term_ve; /* normal */ + +/* User-settable color sequences to begin and end the active region. Defaults + are rl_term_so and rl_term_se on non-dumb terminals. */ +char *_rl_active_region_start_color = NULL; +char *_rl_active_region_end_color = NULL; + +/* It's not clear how HPUX is so broken here. */ +#ifdef TGETENT_BROKEN +# define TGETENT_SUCCESS 0 +#else +# define TGETENT_SUCCESS 1 +#endif +#ifdef TGETFLAG_BROKEN +# define TGETFLAG_SUCCESS 0 +#else +# define TGETFLAG_SUCCESS 1 +#endif +#define TGETFLAG(cap) (tgetflag (cap) == TGETFLAG_SUCCESS) + +static void bind_termcap_arrow_keys (Keymap); + +/* Variables that hold the screen dimensions, used by the display code. */ +int _rl_screenwidth, _rl_screenheight, _rl_screenchars; + +/* Non-zero means the user wants to enable the keypad. */ +int _rl_enable_keypad; + +/* Non-zero means the user wants to enable a meta key. */ +int _rl_enable_meta = 1; + +#if defined (__EMX__) +static void +_emx_get_screensize (int *swp, int *shp) +{ + int sz[2]; + + _scrsize (sz); + + if (swp) + *swp = sz[0]; + if (shp) + *shp = sz[1]; +} +#endif + +#if defined (__MINGW32__) +static void +_win_get_screensize (int *swp, int *shp) +{ + HANDLE hConOut; + CONSOLE_SCREEN_BUFFER_INFO scr; + + hConOut = GetStdHandle (STD_OUTPUT_HANDLE); + if (hConOut != INVALID_HANDLE_VALUE) + { + if (GetConsoleScreenBufferInfo (hConOut, &scr)) + { + *swp = scr.dwSize.X; + *shp = scr.srWindow.Bottom - scr.srWindow.Top + 1; + } + } +} +#endif + +/* Get readline's idea of the screen size. TTY is a file descriptor open + to the terminal. If IGNORE_ENV is true, we do not pay attention to the + values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being + non-null serve to check whether or not we have initialized termcap. */ +void +_rl_get_screen_size (int tty, int ignore_env) +{ + char *ss; +#if defined (TIOCGWINSZ) + struct winsize window_size; +#endif /* TIOCGWINSZ */ + int wr, wc; + + wr = wc = -1; +#if defined (TIOCGWINSZ) + if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) + { + wc = (int) window_size.ws_col; + wr = (int) window_size.ws_row; + } +#endif /* TIOCGWINSZ */ + +#if defined (__EMX__) + _emx_get_screensize (&wc, &wr); +#elif defined (__MINGW32__) + _win_get_screensize (&wc, &wr); +#endif + + if (ignore_env || rl_prefer_env_winsize == 0) + { + _rl_screenwidth = wc; + _rl_screenheight = wr; + } + else + _rl_screenwidth = _rl_screenheight = -1; + + /* Environment variable COLUMNS overrides setting of "co" if IGNORE_ENV + is unset. If we prefer the environment, check it first before + assigning the value returned by the kernel. */ + if (_rl_screenwidth <= 0) + { + if (ignore_env == 0 && (ss = sh_get_env_value ("COLUMNS"))) + _rl_screenwidth = atoi (ss); + + if (_rl_screenwidth <= 0) + _rl_screenwidth = wc; + +#if defined (__DJGPP__) + if (_rl_screenwidth <= 0) + _rl_screenwidth = ScreenCols (); +#else + if (_rl_screenwidth <= 0 && term_string_buffer) + _rl_screenwidth = tgetnum ("co"); +#endif + } + + /* Environment variable LINES overrides setting of "li" if IGNORE_ENV + is unset. */ + if (_rl_screenheight <= 0) + { + if (ignore_env == 0 && (ss = sh_get_env_value ("LINES"))) + _rl_screenheight = atoi (ss); + + if (_rl_screenheight <= 0) + _rl_screenheight = wr; + +#if defined (__DJGPP__) + if (_rl_screenheight <= 0) + _rl_screenheight = ScreenRows (); +#else + if (_rl_screenheight <= 0 && term_string_buffer) + _rl_screenheight = tgetnum ("li"); +#endif + } + + /* If all else fails, default to 80x24 terminal. */ + if (_rl_screenwidth <= 1) + _rl_screenwidth = 80; + + if (_rl_screenheight <= 0) + _rl_screenheight = 24; + + /* If we're being compiled as part of bash, set the environment + variables $LINES and $COLUMNS to new values. Otherwise, just + do a pair of putenv () or setenv () calls. */ + if (rl_change_environment) + sh_set_lines_and_columns (_rl_screenheight, _rl_screenwidth); + + if (_rl_term_autowrap == 0) + _rl_screenwidth--; + + _rl_screenchars = _rl_screenwidth * _rl_screenheight; +} + +void +_rl_set_screen_size (int rows, int cols) +{ + if (_rl_term_autowrap == -1) + _rl_init_terminal_io (rl_terminal_name); + + if (rows > 0) + _rl_screenheight = rows; + if (cols > 0) + { + _rl_screenwidth = cols; + if (_rl_term_autowrap == 0) + _rl_screenwidth--; + } + + if (rows > 0 || cols > 0) + _rl_screenchars = _rl_screenwidth * _rl_screenheight; +} + +void +rl_set_screen_size (int rows, int cols) +{ + _rl_set_screen_size (rows, cols); +} + +void +rl_get_screen_size (int *rows, int *cols) +{ + if (rows) + *rows = _rl_screenheight; + if (cols) + *cols = _rl_screenwidth; +} + +void +rl_reset_screen_size (void) +{ + _rl_get_screen_size (fileno (rl_instream), 0); +} + +void +_rl_sigwinch_resize_terminal (void) +{ + _rl_get_screen_size (fileno (rl_instream), 1); +} + +void +rl_resize_terminal (void) +{ + int width, height; + + width = _rl_screenwidth; + height = _rl_screenheight; + _rl_get_screen_size (fileno (rl_instream), 1); + if (_rl_echoing_p && (width != _rl_screenwidth || height != _rl_screenheight)) + { + if (CUSTOM_REDISPLAY_FUNC ()) + rl_forced_update_display (); + else if (RL_ISSTATE(RL_STATE_REDISPLAYING) == 0) + _rl_redisplay_after_sigwinch (); + } +} + +struct _tc_string { + const char * const tc_var; + char **tc_value; +}; + +/* This should be kept sorted, just in case we decide to change the + search algorithm to something smarter. */ +static const struct _tc_string tc_strings[] = +{ + { "@7", &_rl_term_at7 }, + { "DC", &_rl_term_DC }, + { "E3", &_rl_term_clrscroll }, + { "IC", &_rl_term_IC }, + { "ce", &_rl_term_clreol }, + { "cl", &_rl_term_clrpag }, + { "cr", &_rl_term_cr }, + { "dc", &_rl_term_dc }, + { "ei", &_rl_term_ei }, + { "ic", &_rl_term_ic }, + { "im", &_rl_term_im }, + { "kD", &_rl_term_kD }, /* delete */ + { "kH", &_rl_term_kH }, /* home down ?? */ + { "kI", &_rl_term_kI }, /* insert */ + { "kN", &_rl_term_kN }, /* page down */ + { "kP", &_rl_term_kP }, /* page up */ + { "kd", &_rl_term_kd }, + { "ke", &_rl_term_ke }, /* end keypad mode */ + { "kh", &_rl_term_kh }, /* home */ + { "kl", &_rl_term_kl }, + { "kr", &_rl_term_kr }, + { "ks", &_rl_term_ks }, /* start keypad mode */ + { "ku", &_rl_term_ku }, + { "le", &_rl_term_backspace }, + { "mm", &_rl_term_mm }, + { "mo", &_rl_term_mo }, + { "nd", &_rl_term_forward_char }, + { "pc", &_rl_term_pc }, + { "se", &_rl_term_se }, + { "so", &_rl_term_so }, + { "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)) + +/* Read the desired terminal capability strings into BP. The capabilities + are described in the TC_STRINGS table. */ +static void +get_term_capabilities (char **bp) +{ +#if !defined (__DJGPP__) /* XXX - doesn't DJGPP have a termcap library? */ + register int i; + + for (i = 0; i < NUM_TC_STRINGS; i++) + *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp); +#endif + tcap_initialized = 1; +} + +int +_rl_init_terminal_io (const char *terminal_name) +{ + const char *term; + char *buffer; + int tty, tgetent_ret, dumbterm, reset_region_colors; + + term = terminal_name ? terminal_name : sh_get_env_value ("TERM"); + _rl_term_clrpag = _rl_term_cr = _rl_term_clreol = _rl_term_clrscroll = (char *)NULL; + tty = rl_instream ? fileno (rl_instream) : 0; + + if (term == 0) + term = "dumb"; + + dumbterm = STREQ (term, "dumb"); + + reset_region_colors = 1; + +#ifdef __MSDOS__ + _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_mm = _rl_term_mo = (char *)NULL; + _rl_terminal_can_insert = term_has_meta = _rl_term_autowrap = 0; + _rl_term_cr = "\r"; + _rl_term_backspace = (char *)NULL; + _rl_term_goto = _rl_term_pc = _rl_term_ip = (char *)NULL; + _rl_term_ks = _rl_term_ke =_rl_term_vs = _rl_term_ve = (char *)NULL; + _rl_term_kh = _rl_term_kH = _rl_term_at7 = _rl_term_kI = (char *)NULL; + _rl_term_kN = _rl_term_kP = (char *)NULL; + _rl_term_so = _rl_term_se = (char *)NULL; +#if defined(HACK_TERMCAP_MOTION) + _rl_term_forward_char = (char *)NULL; +#endif + + _rl_get_screen_size (tty, 0); +#else /* !__MSDOS__ */ + /* I've separated this out for later work on not calling tgetent at all + if the calling application has supplied a custom redisplay function, + (and possibly if the application has supplied a custom input function). */ + if (CUSTOM_REDISPLAY_FUNC()) + { + tgetent_ret = -1; + } + else + { + if (term_string_buffer == 0) + term_string_buffer = (char *)xmalloc(2032); + + if (term_buffer == 0) + term_buffer = (char *)xmalloc(4080); + + buffer = term_string_buffer; + + tgetent_ret = tgetent (term_buffer, term); + } + + if (tgetent_ret != TGETENT_SUCCESS) + { + FREE (term_string_buffer); + FREE (term_buffer); + buffer = term_buffer = term_string_buffer = (char *)NULL; + + _rl_term_autowrap = 0; /* used by _rl_get_screen_size */ + + /* Allow calling application to set default height and width, using + rl_set_screen_size */ + if (_rl_screenwidth <= 0 || _rl_screenheight <= 0) + { +#if defined (__EMX__) + _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight); + _rl_screenwidth--; +#else /* !__EMX__ */ + _rl_get_screen_size (tty, 0); +#endif /* !__EMX__ */ + } + + /* Defaults. */ + if (_rl_screenwidth <= 0 || _rl_screenheight <= 0) + { + _rl_screenwidth = 79; + _rl_screenheight = 24; + } + + /* Everything below here is used by the redisplay code (tputs). */ + _rl_screenchars = _rl_screenwidth * _rl_screenheight; + _rl_term_cr = "\r"; + _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 = _rl_term_kD = (char *)NULL; + _rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL; + _rl_term_kN = _rl_term_kP = (char *)NULL; + _rl_term_mm = _rl_term_mo = (char *)NULL; + _rl_term_ve = _rl_term_vs = (char *)NULL; + _rl_term_forward_char = (char *)NULL; + _rl_term_so = _rl_term_se = (char *)NULL; + _rl_terminal_can_insert = term_has_meta = 0; + + /* Assume generic unknown terminal can't handle the enable/disable + escape sequences */ + _rl_enable_bracketed_paste = 0; + + /* No terminal so/se capabilities. */ + _rl_enable_active_region = 0; + _rl_reset_region_color (0, NULL); + _rl_reset_region_color (1, NULL); + + /* Reasonable defaults for tgoto(). Readline currently only uses + tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we + change that later... */ + PC = '\0'; + BC = _rl_term_backspace = "\b"; + UP = _rl_term_up; + + return 0; + } + + get_term_capabilities (&buffer); + + /* Set up the variables that the termcap library expects the application + to provide. */ + PC = _rl_term_pc ? *_rl_term_pc : 0; + BC = _rl_term_backspace; + UP = _rl_term_up; + + if (_rl_term_cr == 0) + _rl_term_cr = "\r"; + + _rl_term_autowrap = TGETFLAG ("am") && TGETFLAG ("xn"); + + /* Allow calling application to set default height and width, using + rl_set_screen_size */ + if (_rl_screenwidth <= 0 || _rl_screenheight <= 0) + _rl_get_screen_size (tty, 0); + + /* "An application program can assume that the terminal can do + character insertion if *any one of* the capabilities `IC', + `im', `ic' or `ip' is provided." But we can't do anything if + only `ip' is provided, so... */ + _rl_terminal_can_insert = (_rl_term_IC || _rl_term_im || _rl_term_ic); + + /* Check to see if this terminal has a meta key and clear the capability + variables if there is none. */ + term_has_meta = TGETFLAG ("km"); + if (term_has_meta == 0) + _rl_term_mm = _rl_term_mo = (char *)NULL; +#endif /* !__MSDOS__ */ + + /* Attempt to find and bind the arrow keys. Do not override already + bound keys in an overzealous attempt, however. */ + + bind_termcap_arrow_keys (emacs_standard_keymap); + +#if defined (VI_MODE) + bind_termcap_arrow_keys (vi_movement_keymap); + bind_termcap_arrow_keys (vi_insertion_keymap); +#endif /* VI_MODE */ + + /* There's no way to determine whether or not a given terminal supports + bracketed paste mode, so we assume a terminal named "dumb" does not. */ + if (dumbterm) + _rl_enable_bracketed_paste = _rl_enable_active_region = 0; + + if (reset_region_colors) + { + _rl_reset_region_color (0, _rl_term_so); + _rl_reset_region_color (1, _rl_term_se); + } + + return 0; +} + +/* Bind the arrow key sequences from the termcap description in MAP. */ +static void +bind_termcap_arrow_keys (Keymap map) +{ + Keymap xkeymap; + + xkeymap = _rl_keymap; + _rl_keymap = map; + + rl_bind_keyseq_if_unbound (_rl_term_ku, rl_get_previous_history); + rl_bind_keyseq_if_unbound (_rl_term_kd, rl_get_next_history); + rl_bind_keyseq_if_unbound (_rl_term_kr, rl_forward_char); + rl_bind_keyseq_if_unbound (_rl_term_kl, rl_backward_char); + + rl_bind_keyseq_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */ + rl_bind_keyseq_if_unbound (_rl_term_at7, rl_end_of_line); /* End */ + + rl_bind_keyseq_if_unbound (_rl_term_kD, rl_delete); + rl_bind_keyseq_if_unbound (_rl_term_kI, rl_overwrite_mode); /* Insert */ + + rl_bind_keyseq_if_unbound (_rl_term_kN, rl_history_search_forward); /* Page Down */ + rl_bind_keyseq_if_unbound (_rl_term_kP, rl_history_search_backward); /* Page Up */ + + _rl_keymap = xkeymap; +} + +char * +rl_get_termcap (const char *cap) +{ + register int i; + + if (tcap_initialized == 0) + return ((char *)NULL); + for (i = 0; i < NUM_TC_STRINGS; i++) + { + if (tc_strings[i].tc_var[0] == cap[0] && strcmp (tc_strings[i].tc_var, cap) == 0) + return *(tc_strings[i].tc_value); + } + return ((char *)NULL); +} + +/* Re-initialize the terminal considering that the TERM/TERMCAP variable + has changed. */ +int +rl_reset_terminal (const char *terminal_name) +{ + _rl_screenwidth = _rl_screenheight = 0; + _rl_init_terminal_io (terminal_name); + return 0; +} + +/* A function for the use of tputs () */ +#ifdef _MINIX +void +_rl_output_character_function (int c) +{ + putc (c, _rl_out_stream); +} +#else /* !_MINIX */ +int +_rl_output_character_function (int c) +{ + return putc (c, _rl_out_stream); +} +#endif /* !_MINIX */ + +/* Write COUNT characters from STRING to the output stream. */ +void +_rl_output_some_chars (const char *string, int count) +{ + fwrite (string, 1, count, _rl_out_stream); +} + +/* Move the cursor back. */ +int +_rl_backspace (int count) +{ + register int i; + +#ifndef __MSDOS__ + if (_rl_term_backspace) + for (i = 0; i < count; i++) + tputs (_rl_term_backspace, 1, _rl_output_character_function); + else +#endif + for (i = 0; i < count; i++) + putc ('\b', _rl_out_stream); + return 0; +} + +/* Move to the start of the next line. */ +int +rl_crlf (void) +{ +#if defined (NEW_TTY_DRIVER) || defined (__MINT__) + if (_rl_term_cr) + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif /* NEW_TTY_DRIVER || __MINT__ */ + putc ('\n', _rl_out_stream); + return 0; +} + +void +_rl_cr (void) +{ +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif +} + +/* Ring the terminal bell. */ +int +rl_ding (void) +{ + if (_rl_echoing_p) + { + switch (_rl_bell_preference) + { + case NO_BELL: + default: + break; + case VISIBLE_BELL: + if (_rl_visible_bell) + { +#ifdef __DJGPP__ + ScreenVisualBell (); +#else + tputs (_rl_visible_bell, 1, _rl_output_character_function); +#endif + break; + } + /* FALLTHROUGH */ + case AUDIBLE_BELL: + fprintf (stderr, "\007"); + fflush (stderr); + break; + } + return (0); + } + return (-1); +} + +/* **************************************************************** */ +/* */ +/* Entering and leaving terminal standout mode */ +/* */ +/* **************************************************************** */ + +void +_rl_standout_on (void) +{ +#ifndef __MSDOS__ + if (_rl_term_so && _rl_term_se) + tputs (_rl_term_so, 1, _rl_output_character_function); +#endif +} + +void +_rl_standout_off (void) +{ +#ifndef __MSDOS__ + if (_rl_term_so && _rl_term_se) + tputs (_rl_term_se, 1, _rl_output_character_function); +#endif +} + +/* **************************************************************** */ +/* */ +/* Controlling color for a portion of the line */ +/* */ +/* **************************************************************** */ + +/* Reset the region color variables to VALUE depending on WHICH (0 == start, + 1 == end). This is where all the memory allocation for the color variable + strings is performed. We might want to pass a flag saying whether or not + to translate VALUE like a key sequence, but it doesn't really matter. */ +int +_rl_reset_region_color (int which, const char *value) +{ + int len; + + if (which == 0) + { + xfree (_rl_active_region_start_color); + if (value && *value) + { + _rl_active_region_start_color = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_active_region_start_color, &len); + _rl_active_region_start_color[len] = '\0'; + } + else + _rl_active_region_start_color = NULL; + } + else + { + xfree (_rl_active_region_end_color); + if (value && *value) + { + _rl_active_region_end_color = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_active_region_end_color, &len); + _rl_active_region_end_color[len] = '\0'; + } + else + _rl_active_region_end_color = NULL; + } + + return 0; +} + +void +_rl_region_color_on (void) +{ +#ifndef __MSDOS__ + if (_rl_active_region_start_color && _rl_active_region_end_color) + tputs (_rl_active_region_start_color, 1, _rl_output_character_function); +#endif +} + +void +_rl_region_color_off (void) +{ +#ifndef __MSDOS__ + if (_rl_active_region_start_color && _rl_active_region_end_color) + tputs (_rl_active_region_end_color, 1, _rl_output_character_function); +#endif +} + +/* **************************************************************** */ +/* */ +/* Controlling the Meta Key and Keypad */ +/* */ +/* **************************************************************** */ + +static int enabled_meta = 0; /* flag indicating we enabled meta mode */ + +void +_rl_enable_meta_key (void) +{ +#if !defined (__DJGPP__) + if (term_has_meta && _rl_term_mm) + { + tputs (_rl_term_mm, 1, _rl_output_character_function); + enabled_meta = 1; + } +#endif +} + +void +_rl_disable_meta_key (void) +{ +#if !defined (__DJGPP__) + if (term_has_meta && _rl_term_mo && enabled_meta) + { + tputs (_rl_term_mo, 1, _rl_output_character_function); + enabled_meta = 0; + } +#endif +} + +void +_rl_control_keypad (int on) +{ +#if !defined (__DJGPP__) + if (on && _rl_term_ks) + tputs (_rl_term_ks, 1, _rl_output_character_function); + else if (!on && _rl_term_ke) + 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 (int im, int force) +{ +#ifndef __MSDOS__ + 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); + } + } +#endif +} diff --git a/lib/readline/text.c b/lib/readline/text.c new file mode 100644 index 0000000..91c3f33 --- /dev/null +++ b/lib/readline/text.c @@ -0,0 +1,1888 @@ +/* text.c -- text handling commands for readline. */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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" + +#if defined (__EMX__) +# define INCL_DOSPROCESS +# include <os2.h> +#endif /* __EMX__ */ + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +/* Forward declarations. */ +static int rl_change_case (int, int); +static int _rl_char_search (int, int, int); + +#if defined (READLINE_CALLBACKS) +static int _rl_insert_next_callback (_rl_callback_generic_arg *); +static int _rl_char_search_callback (_rl_callback_generic_arg *); +#endif + +/* The largest chunk of text that can be inserted in one call to + rl_insert_text. Text blocks larger than this are divided. */ +#define TEXT_COUNT_MAX 1024 + +int _rl_optimize_typeahead = 1; /* rl_insert tries to read typeahead */ + +/* **************************************************************** */ +/* */ +/* Insert and Delete */ +/* */ +/* **************************************************************** */ + +/* Insert a string of text into the line at point. This is the only + way that you should do insertion. _rl_insert_char () calls this + function. Returns the number of characters inserted. */ +int +rl_insert_text (const char *string) +{ + register int i, l; + + l = (string && *string) ? strlen (string) : 0; + if (l == 0) + return 0; + + if (rl_end + l >= rl_line_buffer_len) + rl_extend_line_buffer (rl_end + l); + + for (i = rl_end; i >= rl_point; i--) + rl_line_buffer[i + l] = rl_line_buffer[i]; + + strncpy (rl_line_buffer + rl_point, string, l); + + /* Remember how to undo this if we aren't undoing something. */ + if (_rl_doing_an_undo == 0) + { + /* If possible and desirable, concatenate the undos. */ + if ((l == 1) && + rl_undo_list && + (rl_undo_list->what == UNDO_INSERT) && + (rl_undo_list->end == rl_point) && + (rl_undo_list->end - rl_undo_list->start < 20)) + rl_undo_list->end++; + else + rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL); + } + rl_point += l; + rl_end += l; + rl_line_buffer[rl_end] = '\0'; + return l; +} + +/* Delete the string between FROM and TO. FROM is inclusive, TO is not. + Returns the number of characters deleted. */ +int +rl_delete_text (int from, int to) +{ + register char *text; + register int diff, i; + + /* Fix it if the caller is confused. */ + if (from > to) + SWAP (from, to); + + /* fix boundaries */ + if (to > rl_end) + { + to = rl_end; + if (from > to) + from = to; + } + if (from < 0) + from = 0; + + text = rl_copy_text (from, to); + + /* Some versions of strncpy() can't handle overlapping arguments. */ + diff = to - from; + for (i = from; i < rl_end - diff; i++) + rl_line_buffer[i] = rl_line_buffer[i + diff]; + + /* Remember how to undo this delete. */ + if (_rl_doing_an_undo == 0) + rl_add_undo (UNDO_DELETE, from, to, text); + else + xfree (text); + + rl_end -= diff; + rl_line_buffer[rl_end] = '\0'; + _rl_fix_mark (); + return (diff); +} + +/* Fix up point so that it is within the line boundaries after killing + text. If FIX_MARK_TOO is non-zero, the mark is forced within line + boundaries also. */ + +#define _RL_FIX_POINT(x) \ + do { \ + if (x > rl_end) \ + x = rl_end; \ + else if (x < 0) \ + x = 0; \ + } while (0) + +void +_rl_fix_point (int fix_mark_too) +{ + _RL_FIX_POINT (rl_point); + if (fix_mark_too) + _RL_FIX_POINT (rl_mark); +} + +void +_rl_fix_mark (void) +{ + _RL_FIX_POINT (rl_mark); +} +#undef _RL_FIX_POINT + +/* Replace the contents of the line buffer between START and END with + TEXT. The operation is undoable. To replace the entire line in an + undoable mode, use _rl_replace_text(text, 0, rl_end); */ +int +_rl_replace_text (const char *text, int start, int end) +{ + int n; + + n = 0; + rl_begin_undo_group (); + if (start <= end) + rl_delete_text (start, end + 1); + rl_point = start; + if (*text) + n = rl_insert_text (text); + rl_end_undo_group (); + + return n; +} + +/* Replace the current line buffer contents with TEXT. If CLEAR_UNDO is + non-zero, we free the current undo list. */ +void +rl_replace_line (const char *text, int clear_undo) +{ + int len; + + len = strlen (text); + if (len >= rl_line_buffer_len) + rl_extend_line_buffer (len); + strcpy (rl_line_buffer, text); + rl_end = len; + + if (clear_undo) + rl_free_undo_list (); + + _rl_fix_point (1); +} + +/* **************************************************************** */ +/* */ +/* Readline character functions */ +/* */ +/* **************************************************************** */ + +/* This is not a gap editor, just a stupid line input routine. No hair + is involved in writing any of the functions, and none should be. */ + +/* Note that: + + rl_end is the place in the string that we would place '\0'; + i.e., it is always safe to place '\0' there. + + rl_point is the place in the string where the cursor is. Sometimes + this is the same as rl_end. + + Any command that is called interactively receives two arguments. + The first is a count: the numeric arg passed to this command. + The second is the key which invoked this command. +*/ + +/* **************************************************************** */ +/* */ +/* Movement Commands */ +/* */ +/* **************************************************************** */ + +/* Note that if you `optimize' the display for these functions, you cannot + use said functions in other functions which do not do optimizing display. + I.e., you will have to update the data base for rl_redisplay, and you + might as well let rl_redisplay do that job. */ + +/* Move forward COUNT bytes. */ +int +rl_forward_byte (int count, int key) +{ + if (count < 0) + return (rl_backward_byte (-count, key)); + + if (count > 0) + { + int end, lend; + + end = rl_point + count; +#if defined (VI_MODE) + lend = rl_end > 0 ? rl_end - (VI_COMMAND_MODE()) : rl_end; +#else + lend = rl_end; +#endif + + if (end > lend) + { + rl_point = lend; + rl_ding (); + } + else + rl_point = end; + } + + if (rl_end < 0) + rl_end = 0; + + return 0; +} + +int +_rl_forward_char_internal (int count) +{ + int point; + +#if defined (HANDLE_MULTIBYTE) + point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO); + +#if defined (VI_MODE) + if (point >= rl_end && VI_COMMAND_MODE()) + point = _rl_find_prev_mbchar (rl_line_buffer, rl_end, MB_FIND_NONZERO); +#endif + + if (rl_end < 0) + rl_end = 0; +#else + point = rl_point + count; +#endif + + if (point > rl_end) + point = rl_end; + return (point); +} + +int +_rl_backward_char_internal (int count) +{ + int point; + + point = rl_point; +#if defined (HANDLE_MULTIBYTE) + if (count > 0) + { + while (count > 0 && point > 0) + { + point = _rl_find_prev_mbchar (rl_line_buffer, point, MB_FIND_NONZERO); + count--; + } + if (count > 0) + return 0; /* XXX - rl_ding() here? */ + } +#else + if (count > 0) + point -= count; +#endif + + if (point < 0) + point = 0; + return (point); +} + +#if defined (HANDLE_MULTIBYTE) +/* Move forward COUNT characters. */ +int +rl_forward_char (int count, int key) +{ + int point; + + if (MB_CUR_MAX == 1 || rl_byte_oriented) + return (rl_forward_byte (count, key)); + + if (count < 0) + return (rl_backward_char (-count, key)); + + if (count > 0) + { + if (rl_point == rl_end && EMACS_MODE()) + { + rl_ding (); + return 0; + } + + point = _rl_forward_char_internal (count); + + if (rl_point == point) + rl_ding (); + + rl_point = point; + } + + return 0; +} +#else /* !HANDLE_MULTIBYTE */ +int +rl_forward_char (int count, int key) +{ + return (rl_forward_byte (count, key)); +} +#endif /* !HANDLE_MULTIBYTE */ + +/* Backwards compatibility. */ +int +rl_forward (int count, int key) +{ + return (rl_forward_char (count, key)); +} + +/* Move backward COUNT bytes. */ +int +rl_backward_byte (int count, int key) +{ + if (count < 0) + return (rl_forward_byte (-count, key)); + + if (count > 0) + { + if (rl_point < count) + { + rl_point = 0; + rl_ding (); + } + else + rl_point -= count; + } + + if (rl_point < 0) + rl_point = 0; + + return 0; +} + +#if defined (HANDLE_MULTIBYTE) +/* Move backward COUNT characters. */ +int +rl_backward_char (int count, int key) +{ + int point; + + if (MB_CUR_MAX == 1 || rl_byte_oriented) + return (rl_backward_byte (count, key)); + + if (count < 0) + return (rl_forward_char (-count, key)); + + if (count > 0) + { + point = rl_point; + + while (count > 0 && point > 0) + { + point = _rl_find_prev_mbchar (rl_line_buffer, point, MB_FIND_NONZERO); + count--; + } + if (count > 0) + { + rl_point = 0; + rl_ding (); + } + else + rl_point = point; + } + + return 0; +} +#else +int +rl_backward_char (int count, int key) +{ + return (rl_backward_byte (count, key)); +} +#endif + +/* Backwards compatibility. */ +int +rl_backward (int count, int key) +{ + return (rl_backward_char (count, key)); +} + +/* Move to the beginning of the line. */ +int +rl_beg_of_line (int count, int key) +{ + rl_point = 0; + return 0; +} + +/* Move to the end of the line. */ +int +rl_end_of_line (int count, int key) +{ + rl_point = rl_end; + return 0; +} + +/* Move forward a word. We do what Emacs does. Handles multibyte chars. */ +int +rl_forward_word (int count, int key) +{ + int c; + + if (count < 0) + return (rl_backward_word (-count, key)); + + while (count) + { + if (rl_point > rl_end) + rl_point = rl_end; + if (rl_point == rl_end) + return 0; + + /* If we are not in a word, move forward until we are in one. + Then, move forward until we hit a non-alphabetic character. */ + c = _rl_char_value (rl_line_buffer, rl_point); + + if (_rl_walphabetic (c) == 0) + { + rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + while (rl_point < rl_end) + { + c = _rl_char_value (rl_line_buffer, rl_point); + if (_rl_walphabetic (c)) + break; + rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + } + } + + if (rl_point > rl_end) + rl_point = rl_end; + if (rl_point == rl_end) + return 0; + + rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + while (rl_point < rl_end) + { + c = _rl_char_value (rl_line_buffer, rl_point); + if (_rl_walphabetic (c) == 0) + break; + rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + } + + --count; + } + + return 0; +} + +/* Move backward a word. We do what Emacs does. Handles multibyte chars. */ +int +rl_backward_word (int count, int key) +{ + int c, p; + + if (count < 0) + return (rl_forward_word (-count, key)); + + while (count) + { + if (rl_point == 0) + return 0; + + /* Like rl_forward_word (), except that we look at the characters + just before point. */ + + p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO); + c = _rl_char_value (rl_line_buffer, p); + + if (_rl_walphabetic (c) == 0) + { + rl_point = p; + while (rl_point > 0) + { + p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO); + c = _rl_char_value (rl_line_buffer, p); + if (_rl_walphabetic (c)) + break; + rl_point = p; + } + } + + while (rl_point) + { + p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO); + c = _rl_char_value (rl_line_buffer, p); + if (_rl_walphabetic (c) == 0) + break; + else + rl_point = p; + } + + --count; + } + + return 0; +} + +/* Clear the current line. Numeric argument to C-l does this. */ +int +rl_refresh_line (int ignore1, int ignore2) +{ + _rl_refresh_line (); + rl_display_fixed = 1; + return 0; +} + +/* C-l typed to a line without quoting clears the screen, and then reprints + the prompt and the current input line. Given a numeric arg, redraw only + the current line. */ +int +rl_clear_screen (int count, int key) +{ + if (rl_explicit_arg) + { + rl_refresh_line (count, key); + return 0; + } + + _rl_clear_screen (0); /* calls termcap function to clear screen */ + rl_keep_mark_active (); + rl_forced_update_display (); + rl_display_fixed = 1; + + return 0; +} + +int +rl_clear_display (int count, int key) +{ + _rl_clear_screen (1); /* calls termcap function to clear screen and scrollback buffer */ + rl_forced_update_display (); + rl_display_fixed = 1; + + return 0; +} + +int +rl_previous_screen_line (int count, int key) +{ + int c; + + c = _rl_term_autowrap ? _rl_screenwidth : (_rl_screenwidth + 1); + return (rl_backward_char (c, key)); +} + +int +rl_next_screen_line (int count, int key) +{ + int c; + + c = _rl_term_autowrap ? _rl_screenwidth : (_rl_screenwidth + 1); + return (rl_forward_char (c, key)); +} + +int +rl_skip_csi_sequence (int count, int key) +{ + int ch; + + RL_SETSTATE (RL_STATE_MOREINPUT); + do + ch = rl_read_key (); + while (ch >= 0x20 && ch < 0x40); + RL_UNSETSTATE (RL_STATE_MOREINPUT); + + return (ch < 0); +} + +int +rl_arrow_keys (int count, int key) +{ + int ch; + + RL_SETSTATE(RL_STATE_MOREINPUT); + ch = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + if (ch < 0) + return (1); + + switch (_rl_to_upper (ch)) + { + case 'A': + rl_get_previous_history (count, ch); + break; + + case 'B': + rl_get_next_history (count, ch); + break; + + case 'C': + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_forward_char (count, ch); + else + rl_forward_byte (count, ch); + break; + + case 'D': + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_backward_char (count, ch); + else + rl_backward_byte (count, ch); + break; + + default: + rl_ding (); + } + + return 0; +} + +/* **************************************************************** */ +/* */ +/* Text commands */ +/* */ +/* **************************************************************** */ + +#ifdef HANDLE_MULTIBYTE +static char pending_bytes[MB_LEN_MAX]; +static int pending_bytes_length = 0; +static mbstate_t ps = {0}; +#endif + +/* Insert the character C at the current location, moving point forward. + If C introduces a multibyte sequence, we read the whole sequence and + then insert the multibyte char into the line buffer. */ +int +_rl_insert_char (int count, int c) +{ + register int i; + char *string; +#ifdef HANDLE_MULTIBYTE + int string_size; + char incoming[MB_LEN_MAX + 1]; + int incoming_length = 0; + mbstate_t ps_back; + static int stored_count = 0; +#endif + + if (count <= 0) + return 0; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX == 1 || rl_byte_oriented) + { + incoming[0] = c; + incoming[1] = '\0'; + incoming_length = 1; + } + else if (_rl_utf8locale && (c & 0x80) == 0) + { + incoming[0] = c; + incoming[1] = '\0'; + incoming_length = 1; + } + else + { + WCHAR_T wc; + size_t ret; + + if (stored_count <= 0) + stored_count = count; + else + count = stored_count; + + ps_back = ps; + pending_bytes[pending_bytes_length++] = c; + ret = MBRTOWC (&wc, pending_bytes, pending_bytes_length, &ps); + + if (ret == (size_t)-2) + { + /* Bytes too short to compose character, try to wait for next byte. + Restore the state of the byte sequence, because in this case the + effect of mbstate is undefined. */ + ps = ps_back; + return 1; + } + else if (ret == (size_t)-1) + { + /* Invalid byte sequence for the current locale. Treat first byte + as a single character. */ + incoming[0] = pending_bytes[0]; + incoming[1] = '\0'; + incoming_length = 1; + pending_bytes_length--; + memmove (pending_bytes, pending_bytes + 1, pending_bytes_length); + /* 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 (ret == (size_t)0) + { + incoming[0] = '\0'; + incoming_length = 0; + pending_bytes_length--; + /* 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 (ret == 1) + { + incoming[0] = pending_bytes[0]; + incoming[incoming_length = 1] = '\0'; + pending_bytes_length = 0; + } + else + { + /* We successfully read a single multibyte character. */ + memcpy (incoming, pending_bytes, pending_bytes_length); + incoming[pending_bytes_length] = '\0'; + incoming_length = pending_bytes_length; + pending_bytes_length = 0; + } + } +#endif /* HANDLE_MULTIBYTE */ + + /* If we can optimize, then do it. But don't let people crash + readline because of extra large arguments. */ + if (count > 1 && count <= TEXT_COUNT_MAX) + { +#if defined (HANDLE_MULTIBYTE) + string_size = count * incoming_length; + string = (char *)xmalloc (1 + string_size); + + i = 0; + while (i < string_size) + { + if (incoming_length == 1) + string[i++] = *incoming; + else + { + strncpy (string + i, incoming, incoming_length); + i += incoming_length; + } + } + incoming_length = 0; + stored_count = 0; +#else /* !HANDLE_MULTIBYTE */ + string = (char *)xmalloc (1 + count); + + for (i = 0; i < count; i++) + string[i] = c; +#endif /* !HANDLE_MULTIBYTE */ + + string[i] = '\0'; + rl_insert_text (string); + xfree (string); + + return 0; + } + + if (count > TEXT_COUNT_MAX) + { + int decreaser; +#if defined (HANDLE_MULTIBYTE) + string_size = incoming_length * TEXT_COUNT_MAX; + string = (char *)xmalloc (1 + string_size); + + i = 0; + while (i < string_size) + { + if (incoming_length == 1) + string[i++] = *incoming; + else + { + strncpy (string + i, incoming, incoming_length); + i += incoming_length; + } + } + + while (count) + { + decreaser = (count > TEXT_COUNT_MAX) ? TEXT_COUNT_MAX : count; + string[decreaser*incoming_length] = '\0'; + rl_insert_text (string); + count -= decreaser; + } + + xfree (string); + incoming_length = 0; + stored_count = 0; +#else /* !HANDLE_MULTIBYTE */ + char str[TEXT_COUNT_MAX+1]; + + for (i = 0; i < TEXT_COUNT_MAX; i++) + str[i] = c; + + while (count) + { + decreaser = (count > TEXT_COUNT_MAX ? TEXT_COUNT_MAX : count); + str[decreaser] = '\0'; + rl_insert_text (str); + count -= decreaser; + } +#endif /* !HANDLE_MULTIBYTE */ + + return 0; + } + + if (MB_CUR_MAX == 1 || rl_byte_oriented) + { + /* We are inserting a single character. + If there is pending input, then make a string of all of the + pending characters that are bound to rl_insert, and insert + them all. Don't do this if we're current reading input from + a macro. */ + if ((RL_ISSTATE (RL_STATE_MACROINPUT) == 0) && _rl_pushed_input_available ()) + _rl_insert_typein (c); + else + { + /* Inserting a single character. */ + char str[2]; + + str[1] = '\0'; + str[0] = c; + rl_insert_text (str); + } + } +#if defined (HANDLE_MULTIBYTE) + else + { + rl_insert_text (incoming); + stored_count = 0; + } +#endif + + return 0; +} + +/* Overwrite the character at point (or next COUNT characters) with C. + If C introduces a multibyte character sequence, read the entire sequence + before starting the overwrite loop. */ +int +_rl_overwrite_char (int count, int c) +{ + int i; +#if defined (HANDLE_MULTIBYTE) + char mbkey[MB_LEN_MAX]; + int k; + + /* Read an entire multibyte character sequence to insert COUNT times. */ + k = 1; + if (count > 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0) + k = _rl_read_mbstring (c, mbkey, MB_LEN_MAX); + if (k < 0) + return 1; +#endif + + rl_begin_undo_group (); + + for (i = 0; i < count; i++) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_insert_text (mbkey); + else +#endif + _rl_insert_char (1, c); + + if (rl_point < rl_end) + rl_delete (1, c); + } + + rl_end_undo_group (); + + return 0; +} + +int +rl_insert (int count, int c) +{ + int r, n, x; + + r = (rl_insert_mode == RL_IM_INSERT) ? _rl_insert_char (count, c) : _rl_overwrite_char (count, c); + + /* XXX -- attempt to batch-insert pending input that maps to self-insert */ + x = 0; + n = (unsigned short)-2; + while (_rl_optimize_typeahead && + rl_num_chars_to_read == 0 && + (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) && + _rl_pushed_input_available () == 0 && + _rl_input_queued (0) && + (n = rl_read_key ()) > 0 && + _rl_keymap[(unsigned char)n].type == ISFUNC && + _rl_keymap[(unsigned char)n].function == rl_insert) + { + r = (rl_insert_mode == RL_IM_INSERT) ? _rl_insert_char (1, n) : _rl_overwrite_char (1, n); + /* _rl_insert_char keeps its own set of pending characters to compose a + complete multibyte character, and only returns 1 if it sees a character + that's part of a multibyte character but too short to complete one. We + can try to read another character in the hopes that we will get the + next one or just punt. Right now we try to read another character. + We don't want to call rl_insert_next if _rl_insert_char has already + stored the character in the pending_bytes array because that will + result in doubled input. */ + n = (unsigned short)-2; + x++; /* count of bytes of typeahead read, currently unused */ + if (r == 1) /* read partial multibyte character */ + continue; + if (rl_done || r != 0) + break; + } + + if (n != (unsigned short)-2) /* -2 = sentinel value for having inserted N */ + { + /* setting rl_pending_input inhibits setting rl_last_func so we do it + ourselves here */ + rl_last_func = rl_insert; + _rl_reset_argument (); + rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; + r = rl_execute_next (n); + } + + return r; +} + +/* Insert the next typed character verbatim. */ +static int +_rl_insert_next (int count) +{ + int c; + + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (c < 0) + return 1; + + if (RL_ISSTATE (RL_STATE_MACRODEF)) + _rl_add_macro_char (c); + +#if defined (HANDLE_SIGNALS) + if (RL_ISSTATE (RL_STATE_CALLBACK) == 0) + _rl_restore_tty_signals (); +#endif + + return (_rl_insert_char (count, c)); +} + +#if defined (READLINE_CALLBACKS) +static int +_rl_insert_next_callback (_rl_callback_generic_arg *data) +{ + int count, r; + + count = data->count; + r = 0; + + if (count < 0) + { + data->count++; + r = _rl_insert_next (1); + _rl_want_redisplay = 1; + /* If we should keep going, leave the callback function installed */ + if (data->count < 0 && r == 0) + return r; + count = 0; /* data->count == 0 || r != 0; force break below */ + } + + /* Deregister function, let rl_callback_read_char deallocate data */ + _rl_callback_func = 0; + _rl_want_redisplay = 1; + + if (count == 0) + return r; + + return _rl_insert_next (count); +} +#endif + +int +rl_quoted_insert (int count, int key) +{ + /* Let's see...should the callback interface futz with signal handling? */ +#if defined (HANDLE_SIGNALS) + if (RL_ISSTATE (RL_STATE_CALLBACK) == 0) + _rl_disable_tty_signals (); +#endif + +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = _rl_callback_data_alloc (count); + _rl_callback_func = _rl_insert_next_callback; + return (0); + } +#endif + + /* A negative count means to quote the next -COUNT characters. */ + if (count < 0) + { + int r; + + do + r = _rl_insert_next (1); + while (r == 0 && ++count < 0); + return r; + } + + return _rl_insert_next (count); +} + +/* Insert a tab character. */ +int +rl_tab_insert (int count, int key) +{ + return (_rl_insert_char (count, '\t')); +} + +/* What to do when a NEWLINE is pressed. We accept the whole line. + KEY is the key that invoked this command. I guess it could have + meaning in the future. */ +int +rl_newline (int count, int key) +{ + if (rl_mark_active_p ()) + { + rl_deactivate_mark (); + (*rl_redisplay_function) (); + _rl_want_redisplay = 0; + } + + rl_done = 1; + + if (_rl_history_preserve_point) + _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point; + + RL_SETSTATE(RL_STATE_DONE); + +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + { + _rl_vi_done_inserting (); + if (_rl_vi_textmod_command (_rl_vi_last_command) == 0) /* XXX */ + _rl_vi_reset_last (); + } +#endif /* VI_MODE */ + + /* If we've been asked to erase empty lines, suppress the final update, + since _rl_update_final calls rl_crlf(). */ + if (rl_erase_empty_line && rl_point == 0 && rl_end == 0) + return 0; + + if (_rl_echoing_p) + _rl_update_final (); + return 0; +} + +/* What to do for some uppercase characters, like meta characters, + and some characters appearing in emacs_ctlx_keymap. This function + is just a stub, you bind keys to it and the code in _rl_dispatch () + is special cased. */ +int +rl_do_lowercase_version (int ignore1, int ignore2) +{ + return 99999; /* prevent from being combined with _rl_null_function */ +} + +/* This is different from what vi does, so the code's not shared. Emacs + rubout in overwrite mode has one oddity: it replaces a control + character that's displayed as two characters (^X) with two spaces. */ +int +_rl_overwrite_rubout (int count, int key) +{ + int opoint; + int i, l; + + if (rl_point == 0) + { + rl_ding (); + return 1; + } + + opoint = rl_point; + + /* L == number of spaces to insert */ + for (i = l = 0; i < count; i++) + { + rl_backward_char (1, key); + l += rl_character_len (rl_line_buffer[rl_point], rl_point); /* not exactly right */ + } + + rl_begin_undo_group (); + + if (count > 1 || rl_explicit_arg) + rl_kill_text (opoint, rl_point); + else + rl_delete_text (opoint, rl_point); + + /* Emacs puts point at the beginning of the sequence of spaces. */ + if (rl_point < rl_end) + { + opoint = rl_point; + _rl_insert_char (l, ' '); + rl_point = opoint; + } + + rl_end_undo_group (); + + return 0; +} + +/* Rubout the character behind point. */ +int +rl_rubout (int count, int key) +{ + if (count < 0) + return (rl_delete (-count, key)); + + if (!rl_point) + { + rl_ding (); + return 1; + } + + if (rl_insert_mode == RL_IM_OVERWRITE) + return (_rl_overwrite_rubout (count, key)); + + return (_rl_rubout_char (count, key)); +} + +int +_rl_rubout_char (int count, int key) +{ + int orig_point; + unsigned char c; + + /* Duplicated code because this is called from other parts of the library. */ + if (count < 0) + return (rl_delete (-count, key)); + + if (rl_point == 0) + { + rl_ding (); + return 1; + } + + orig_point = rl_point; + if (count > 1 || rl_explicit_arg) + { + rl_backward_char (count, key); + rl_kill_text (orig_point, rl_point); + } + else if (MB_CUR_MAX == 1 || rl_byte_oriented) + { + c = rl_line_buffer[--rl_point]; + rl_delete_text (rl_point, orig_point); + /* The erase-at-end-of-line hack is of questionable merit now. */ + if (rl_point == rl_end && ISPRINT ((unsigned char)c) && _rl_last_c_pos) + { + int l; + l = rl_character_len (c, rl_point); + _rl_erase_at_end_of_line (l); + } + } + else + { + rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO); + rl_delete_text (rl_point, orig_point); + } + + return 0; +} + +/* Delete the character under the cursor. Given a numeric argument, + kill that many characters instead. */ +int +rl_delete (int count, int key) +{ + int xpoint; + + if (count < 0) + return (_rl_rubout_char (-count, key)); + + if (rl_point == rl_end) + { + rl_ding (); + return 1; + } + + if (count > 1 || rl_explicit_arg) + { + xpoint = rl_point; + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_forward_char (count, key); + else + rl_forward_byte (count, key); + + rl_kill_text (xpoint, rl_point); + rl_point = xpoint; + } + else + { + xpoint = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + rl_delete_text (rl_point, xpoint); + } + return 0; +} + +/* Delete the character under the cursor, unless the insertion + point is at the end of the line, in which case the character + behind the cursor is deleted. COUNT is obeyed and may be used + to delete forward or backward that many characters. */ +int +rl_rubout_or_delete (int count, int key) +{ + if (rl_end != 0 && rl_point == rl_end) + return (_rl_rubout_char (count, key)); + else + return (rl_delete (count, key)); +} + +/* Delete all spaces and tabs around point. */ +int +rl_delete_horizontal_space (int count, int ignore) +{ + int start; + + while (rl_point && whitespace (rl_line_buffer[rl_point - 1])) + rl_point--; + + start = rl_point; + + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + rl_point++; + + if (start != rl_point) + { + rl_delete_text (start, rl_point); + rl_point = start; + } + + if (rl_point < 0) + rl_point = 0; + + return 0; +} + +/* Like the tcsh editing function delete-char-or-list. The eof character + is caught before this is invoked, so this really does the same thing as + delete-char-or-list-or-eof, as long as it's bound to the eof character. */ +int +rl_delete_or_show_completions (int count, int key) +{ + if (rl_end != 0 && rl_point == rl_end) + return (rl_possible_completions (count, key)); + else + return (rl_delete (count, key)); +} + +#ifndef RL_COMMENT_BEGIN_DEFAULT +#define RL_COMMENT_BEGIN_DEFAULT "#" +#endif + +/* Turn the current line into a comment in shell history. + A K*rn shell style function. */ +int +rl_insert_comment (int count, int key) +{ + char *rl_comment_text; + int rl_comment_len; + + rl_beg_of_line (1, key); + rl_comment_text = _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT; + + if (rl_explicit_arg == 0) + rl_insert_text (rl_comment_text); + else + { + rl_comment_len = strlen (rl_comment_text); + if (STREQN (rl_comment_text, rl_line_buffer, rl_comment_len)) + rl_delete_text (rl_point, rl_point + rl_comment_len); + else + rl_insert_text (rl_comment_text); + } + + (*rl_redisplay_function) (); + rl_newline (1, '\n'); + + return (0); +} + +/* **************************************************************** */ +/* */ +/* Changing Case */ +/* */ +/* **************************************************************** */ + +/* The three kinds of things that we know how to do. */ +#define UpCase 1 +#define DownCase 2 +#define CapCase 3 + +/* Uppercase the word at point. */ +int +rl_upcase_word (int count, int key) +{ + return (rl_change_case (count, UpCase)); +} + +/* Lowercase the word at point. */ +int +rl_downcase_word (int count, int key) +{ + return (rl_change_case (count, DownCase)); +} + +/* Upcase the first letter, downcase the rest. */ +int +rl_capitalize_word (int count, int key) +{ + return (rl_change_case (count, CapCase)); +} + +/* The meaty function. + Change the case of COUNT words, performing OP on them. + OP is one of UpCase, DownCase, or CapCase. + If a negative argument is given, leave point where it started, + otherwise, leave it where it moves to. */ +static int +rl_change_case (int count, int op) +{ + int start, next, end; + int inword, nc, nop; + WCHAR_T c; +#if defined (HANDLE_MULTIBYTE) + WCHAR_T wc, nwc; + char mb[MB_LEN_MAX+1]; + int mlen; + size_t m; + mbstate_t mps; +#endif + + start = rl_point; + rl_forward_word (count, 0); + end = rl_point; + + if (op != UpCase && op != DownCase && op != CapCase) + { + rl_ding (); + return 1; + } + + if (count < 0) + SWAP (start, end); + +#if defined (HANDLE_MULTIBYTE) + memset (&mps, 0, sizeof (mbstate_t)); +#endif + + /* We are going to modify some text, so let's prepare to undo it. */ + rl_modifying (start, end); + + inword = 0; + while (start < end) + { + c = _rl_char_value (rl_line_buffer, start); + /* This assumes that the upper and lower case versions are the same width. */ + next = MB_NEXTCHAR (rl_line_buffer, start, 1, MB_FIND_NONZERO); + + if (_rl_walphabetic (c) == 0) + { + inword = 0; + start = next; + continue; + } + + if (op == CapCase) + { + nop = inword ? DownCase : UpCase; + inword = 1; + } + else + nop = op; + /* Can't check isascii here; some languages (e.g, Turkish) have + multibyte upper and lower case equivalents of single-byte ascii + characters */ + if (MB_CUR_MAX == 1 || rl_byte_oriented) + { + nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c); + rl_line_buffer[start] = nc; + } +#if defined (HANDLE_MULTIBYTE) + else + { + m = MBRTOWC (&wc, rl_line_buffer + start, end - start, &mps); + if (MB_INVALIDCH (m)) + wc = (WCHAR_T)rl_line_buffer[start]; + else if (MB_NULLWCH (m)) + wc = L'\0'; + nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc); + if (nwc != wc) /* just skip unchanged characters */ + { + char *s, *e; + mbstate_t ts; + + memset (&ts, 0, sizeof (mbstate_t)); + mlen = WCRTOMB (mb, nwc, &ts); + if (mlen < 0) + { + nwc = wc; + memset (&ts, 0, sizeof (mbstate_t)); + mlen = WCRTOMB (mb, nwc, &ts); + if (mlen < 0) /* should not happen */ + strncpy (mb, rl_line_buffer + start, mlen = m); + } + if (mlen > 0) + mb[mlen] = '\0'; + /* what to do if m != mlen? adjust below */ + /* m == length of old char, mlen == length of new char */ + s = rl_line_buffer + start; + e = rl_line_buffer + rl_end; + if (m == mlen) + memcpy (s, mb, mlen); + else if (m > mlen) + { + memcpy (s, mb, mlen); + memmove (s + mlen, s + m, (e - s) - m); + next -= m - mlen; /* next char changes */ + end -= m - mlen; /* end of word changes */ + rl_end -= m - mlen; /* end of line changes */ + rl_line_buffer[rl_end] = 0; + } + else if (m < mlen) + { + rl_extend_line_buffer (rl_end + mlen + (e - s) - m + 2); + s = rl_line_buffer + start; /* have to redo this */ + e = rl_line_buffer + rl_end; + memmove (s + mlen, s + m, (e - s) - m); + memcpy (s, mb, mlen); + next += mlen - m; /* next char changes */ + end += mlen - m; /* end of word changes */ + rl_end += mlen - m; /* end of line changes */ + rl_line_buffer[rl_end] = 0; + } + } + } +#endif + + start = next; + } + + rl_point = end; + return 0; +} + +/* **************************************************************** */ +/* */ +/* Transposition */ +/* */ +/* **************************************************************** */ + +/* Transpose the words at point. If point is at the end of the line, + transpose the two words before point. */ +int +rl_transpose_words (int count, int key) +{ + char *word1, *word2; + int w1_beg, w1_end, w2_beg, w2_end; + int orig_point, orig_end; + + orig_point = rl_point; + orig_end = rl_end; + + if (!count) + return 0; + + /* Find the two words. */ + rl_forward_word (count, key); + w2_end = rl_point; + rl_backward_word (1, key); + w2_beg = rl_point; + rl_backward_word (count, key); + w1_beg = rl_point; + rl_forward_word (1, key); + w1_end = rl_point; + + /* Do some check to make sure that there really are two words. */ + if ((w1_beg == w2_beg) || (w2_beg < w1_end)) + { + rl_ding (); + rl_point = orig_point; + return 1; + } + + /* Get the text of the words. */ + word1 = rl_copy_text (w1_beg, w1_end); + word2 = rl_copy_text (w2_beg, w2_end); + + /* We are about to do many insertions and deletions. Remember them + as one operation. */ + rl_begin_undo_group (); + + /* Do the stuff at word2 first, so that we don't have to worry + about word1 moving. */ + rl_point = w2_beg; + rl_delete_text (w2_beg, w2_end); + rl_insert_text (word1); + + rl_point = w1_beg; + rl_delete_text (w1_beg, w1_end); + rl_insert_text (word2); + + /* This is exactly correct since the text before this point has not + changed in length. */ + rl_point = w2_end; + rl_end = orig_end; /* just make sure */ + + /* I think that does it. */ + rl_end_undo_group (); + xfree (word1); + xfree (word2); + + return 0; +} + +/* Transpose the characters at point. If point is at the end of the line, + then transpose the characters before point. */ +int +rl_transpose_chars (int count, int key) +{ +#if defined (HANDLE_MULTIBYTE) + char *dummy; + int i; +#else + char dummy[2]; +#endif + int char_length, prev_point; + + if (count == 0) + return 0; + + if (!rl_point || rl_end < 2) + { + rl_ding (); + return 1; + } + + rl_begin_undo_group (); + + if (rl_point == rl_end) + { + rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO); + count = 1; + } + + prev_point = rl_point; + rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO); + +#if defined (HANDLE_MULTIBYTE) + char_length = prev_point - rl_point; + dummy = (char *)xmalloc (char_length + 1); + for (i = 0; i < char_length; i++) + dummy[i] = rl_line_buffer[rl_point + i]; + dummy[i] = '\0'; +#else + dummy[0] = rl_line_buffer[rl_point]; + dummy[char_length = 1] = '\0'; +#endif + + rl_delete_text (rl_point, rl_point + char_length); + + rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO); + + _rl_fix_point (0); + rl_insert_text (dummy); + rl_end_undo_group (); + +#if defined (HANDLE_MULTIBYTE) + xfree (dummy); +#endif + + return 0; +} + +/* **************************************************************** */ +/* */ +/* Character Searching */ +/* */ +/* **************************************************************** */ + +int +#if defined (HANDLE_MULTIBYTE) +_rl_char_search_internal (int count, int dir, char *smbchar, int len) +#else +_rl_char_search_internal (int count, int dir, int schar) +#endif +{ + int pos, inc; +#if defined (HANDLE_MULTIBYTE) + int prepos; +#endif + + if (dir == 0) + return 1; + + pos = rl_point; + inc = (dir < 0) ? -1 : 1; + while (count) + { + if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= rl_end)) + { + rl_ding (); + return 1; + } + +#if defined (HANDLE_MULTIBYTE) + pos = (inc > 0) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY) + : _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY); +#else + pos += inc; +#endif + do + { +#if defined (HANDLE_MULTIBYTE) + if (_rl_is_mbchar_matched (rl_line_buffer, pos, rl_end, smbchar, len)) +#else + if (rl_line_buffer[pos] == schar) +#endif + { + count--; + if (dir < 0) + rl_point = (dir == BTO) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY) + : pos; + else + rl_point = (dir == FTO) ? _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY) + : pos; + break; + } +#if defined (HANDLE_MULTIBYTE) + prepos = pos; +#endif + } +#if defined (HANDLE_MULTIBYTE) + while ((dir < 0) ? (pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)) != prepos + : (pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos); +#else + while ((dir < 0) ? pos-- : ++pos < rl_end); +#endif + } + return (0); +} + +/* Search COUNT times for a character read from the current input stream. + FDIR is the direction to search if COUNT is non-negative; otherwise + the search goes in BDIR. So much is dependent on HANDLE_MULTIBYTE + that there are two separate versions of this function. */ +#if defined (HANDLE_MULTIBYTE) +static int +_rl_char_search (int count, int fdir, int bdir) +{ + char mbchar[MB_LEN_MAX]; + int mb_len; + + mb_len = _rl_read_mbchar (mbchar, MB_LEN_MAX); + + if (mb_len <= 0) + return 1; + + if (count < 0) + return (_rl_char_search_internal (-count, bdir, mbchar, mb_len)); + else + return (_rl_char_search_internal (count, fdir, mbchar, mb_len)); +} +#else /* !HANDLE_MULTIBYTE */ +static int +_rl_char_search (int count, int fdir, int bdir) +{ + int c; + + c = _rl_bracketed_read_key (); + if (c < 0) + return 1; + + if (count < 0) + return (_rl_char_search_internal (-count, bdir, c)); + else + return (_rl_char_search_internal (count, fdir, c)); +} +#endif /* !HANDLE_MULTIBYTE */ + +#if defined (READLINE_CALLBACKS) +static int +_rl_char_search_callback (data) + _rl_callback_generic_arg *data; +{ + _rl_callback_func = 0; + _rl_want_redisplay = 1; + + return (_rl_char_search (data->count, data->i1, data->i2)); +} +#endif + +int +rl_char_search (int count, int key) +{ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = _rl_callback_data_alloc (count); + _rl_callback_data->i1 = FFIND; + _rl_callback_data->i2 = BFIND; + _rl_callback_func = _rl_char_search_callback; + return (0); + } +#endif + + return (_rl_char_search (count, FFIND, BFIND)); +} + +int +rl_backward_char_search (int count, int key) +{ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = _rl_callback_data_alloc (count); + _rl_callback_data->i1 = BFIND; + _rl_callback_data->i2 = FFIND; + _rl_callback_func = _rl_char_search_callback; + return (0); + } +#endif + + return (_rl_char_search (count, BFIND, FFIND)); +} + +/* **************************************************************** */ +/* */ +/* The Mark and the Region. */ +/* */ +/* **************************************************************** */ + +/* Set the mark at POSITION. */ +int +_rl_set_mark_at_pos (int position) +{ + if (position < 0 || position > rl_end) + return 1; + + rl_mark = position; + return 0; +} + +/* A bindable command to set the mark. */ +int +rl_set_mark (int count, int key) +{ + return (_rl_set_mark_at_pos (rl_explicit_arg ? count : rl_point)); +} + +/* Exchange the position of mark and point. */ +int +rl_exchange_point_and_mark (int count, int key) +{ + if (rl_mark > rl_end) + rl_mark = -1; + + if (rl_mark < 0) + { + rl_ding (); + rl_mark = 0; /* like _RL_FIX_POINT */ + return 1; + } + else + { + SWAP (rl_point, rl_mark); + rl_activate_mark (); + } + + return 0; +} + +/* Active mark support */ + +/* Is the region active? */ +static int mark_active = 0; + +/* Does the current command want the mark to remain active when it completes? */ +int _rl_keep_mark_active; + +void +rl_keep_mark_active (void) +{ + _rl_keep_mark_active++; +} + +void +rl_activate_mark (void) +{ + mark_active = 1; + rl_keep_mark_active (); +} + +void +rl_deactivate_mark (void) +{ + mark_active = 0; +} + +int +rl_mark_active_p (void) +{ + return (mark_active); +} diff --git a/lib/readline/tilde.c b/lib/readline/tilde.c new file mode 100644 index 0000000..d678a31 --- /dev/null +++ b/lib/readline/tilde.c @@ -0,0 +1,493 @@ +/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ + +/* Copyright (C) 1988-2020 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#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 /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <sys/types.h> +#if defined (HAVE_PWD_H) +#include <pwd.h> +#endif + +#include "tilde.h" + +#if defined (TEST) || defined (STATIC_MALLOC) +static void *xmalloc (), *xrealloc (); +#else +# include "xmalloc.h" +#endif /* TEST || STATIC_MALLOC */ + +#if !defined (HAVE_GETPW_DECLS) +# if defined (HAVE_GETPWUID) +extern struct passwd *getpwuid (uid_t); +# endif +# if defined (HAVE_GETPWNAM) +extern struct passwd *getpwnam (const char *); +# endif +#endif /* !HAVE_GETPW_DECLS */ + +#if !defined (savestring) +#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x)) +#endif /* !savestring */ + +#if !defined (NULL) +# if defined (__STDC__) +# define NULL ((void *) 0) +# else +# define NULL 0x0 +# endif /* !__STDC__ */ +#endif /* !NULL */ + +/* If being compiled as part of bash, these will be satisfied from + variables.o. If being compiled as part of readline, they will + be satisfied from shell.o. */ +extern char *sh_get_home_dir (void); +extern char *sh_get_env_value (const char *); + +/* The default value of tilde_additional_prefixes. This is set to + whitespace preceding a tilde so that simple programs which do not + perform any word separation get desired behaviour. */ +static const char *default_prefixes[] = + { " ~", "\t~", (const char *)NULL }; + +/* The default value of tilde_additional_suffixes. This is set to + whitespace or newline so that simple programs which do not + perform any word separation get desired behaviour. */ +static const char *default_suffixes[] = + { " ", "\n", (const char *)NULL }; + +/* If non-null, this contains the address of a function that the application + wants called before trying the standard tilde expansions. The function + is called with the text sans tilde, and returns a malloc()'ed string + which is the expansion, or a NULL pointer if the expansion fails. */ +tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +char **tilde_additional_prefixes = (char **)default_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +char **tilde_additional_suffixes = (char **)default_suffixes; + +static int tilde_find_prefix (const char *, int *); +static int tilde_find_suffix (const char *); +static char *isolate_tilde_prefix (const char *, int *); +static char *glue_prefix_and_suffix (char *, const char *, int); + +/* Find the start of a tilde expansion in STRING, and return the index of + the tilde which starts the expansion. Place the length of the text + which identified this tilde starter in LEN, excluding the tilde itself. */ +static int +tilde_find_prefix (const char *string, int *len) +{ + register int i, j, string_len; + register char **prefixes; + + prefixes = tilde_additional_prefixes; + + string_len = strlen (string); + *len = 0; + + if (*string == '\0' || *string == '~') + return (0); + + if (prefixes) + { + for (i = 0; i < string_len; i++) + { + for (j = 0; prefixes[j]; j++) + { + if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) + { + *len = strlen (prefixes[j]) - 1; + return (i + *len); + } + } + } + } + return (string_len); +} + +/* Find the end of a tilde expansion in STRING, and return the index of + the character which ends the tilde definition. */ +static int +tilde_find_suffix (const char *string) +{ + register int i, j, string_len; + register char **suffixes; + + suffixes = tilde_additional_suffixes; + string_len = strlen (string); + + for (i = 0; i < string_len; i++) + { +#if defined (__MSDOS__) + if (string[i] == '/' || string[i] == '\\' /* || !string[i] */) +#else + if (string[i] == '/' /* || !string[i] */) +#endif + break; + + for (j = 0; suffixes && suffixes[j]; j++) + { + if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) + return (i); + } + } + return (i); +} + +/* Return a new string which is the result of tilde expanding STRING. */ +char * +tilde_expand (const char *string) +{ + char *result; + int result_size, result_index; + + result_index = result_size = 0; + if (result = strchr (string, '~')) + result = (char *)xmalloc (result_size = (strlen (string) + 16)); + else + result = (char *)xmalloc (result_size = (strlen (string) + 1)); + + /* Scan through STRING expanding tildes as we come to them. */ + while (1) + { + register int start, end; + char *tilde_word, *expansion; + int len; + + /* Make START point to the tilde which starts the expansion. */ + start = tilde_find_prefix (string, &len); + + /* Copy the skipped text into the result. */ + if ((result_index + start + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); + + strncpy (result + result_index, string, start); + result_index += start; + + /* Advance STRING to the starting tilde. */ + string += start; + + /* Make END be the index of one after the last character of the + username. */ + end = tilde_find_suffix (string); + + /* If both START and END are zero, we are all done. */ + if (!start && !end) + break; + + /* Expand the entire tilde word, and copy it into RESULT. */ + tilde_word = (char *)xmalloc (1 + end); + strncpy (tilde_word, string, end); + tilde_word[end] = '\0'; + string += end; + + expansion = tilde_expand_word (tilde_word); + + if (expansion == 0) + expansion = tilde_word; + else + xfree (tilde_word); + + len = strlen (expansion); +#ifdef __CYGWIN__ + /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when + $HOME for `user' is /. On cygwin, // denotes a network drive. */ + if (len > 1 || *expansion != '/' || *string != '/') +#endif + { + if ((result_index + len + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); + + strcpy (result + result_index, expansion); + result_index += len; + } + xfree (expansion); + } + + result[result_index] = '\0'; + + return (result); +} + +/* Take FNAME and return the tilde prefix we want expanded. If LENP is + non-null, the index of the end of the prefix into FNAME is returned in + the location it points to. */ +static char * +isolate_tilde_prefix (const char *fname, int *lenp) +{ + char *ret; + int i; + + ret = (char *)xmalloc (strlen (fname)); +#if defined (__MSDOS__) + for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++) +#else + for (i = 1; fname[i] && fname[i] != '/'; i++) +#endif + ret[i - 1] = fname[i]; + ret[i - 1] = '\0'; + if (lenp) + *lenp = i; + return ret; +} + +#if 0 +/* Public function to scan a string (FNAME) beginning with a tilde and find + the portion of the string that should be passed to the tilde expansion + function. Right now, it just calls tilde_find_suffix and allocates new + memory, but it can be expanded to do different things later. */ +char * +tilde_find_word (const char *fname, int flags, int *lenp) +{ + int x; + char *r; + + x = tilde_find_suffix (fname); + if (x == 0) + { + r = savestring (fname); + if (lenp) + *lenp = 0; + } + else + { + r = (char *)xmalloc (1 + x); + strncpy (r, fname, x); + r[x] = '\0'; + if (lenp) + *lenp = x; + } + + return r; +} +#endif + +/* Return a string that is PREFIX concatenated with SUFFIX starting at + SUFFIND. */ +static char * +glue_prefix_and_suffix (char *prefix, const char *suffix, int suffind) +{ + char *ret; + int plen, slen; + + plen = (prefix && *prefix) ? strlen (prefix) : 0; + slen = strlen (suffix + suffind); + ret = (char *)xmalloc (plen + slen + 1); + if (plen) + strcpy (ret, prefix); + strcpy (ret + plen, suffix + suffind); + return ret; +} + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. + This always returns a newly-allocated string, never static storage. */ +char * +tilde_expand_word (const char *filename) +{ + char *dirname, *expansion, *username; + int user_len; + struct passwd *user_entry; + + if (filename == 0) + return ((char *)NULL); + + if (*filename != '~') + return (savestring (filename)); + + /* A leading `~/' or a bare `~' is *always* translated to the value of + $HOME or the home directory of the current user, regardless of any + preexpansion hook. */ + if (filename[1] == '\0' || filename[1] == '/') + { + /* Prefix $HOME to the rest of the string. */ + expansion = sh_get_env_value ("HOME"); +#if defined (_WIN32) + if (expansion == 0) + expansion = sh_get_env_value ("APPDATA"); +#endif + + /* If there is no HOME variable, look up the directory in + the password database. */ + if (expansion == 0) + expansion = sh_get_home_dir (); + + return (glue_prefix_and_suffix (expansion, filename, 1)); + } + + username = isolate_tilde_prefix (filename, &user_len); + + if (tilde_expansion_preexpansion_hook) + { + expansion = (*tilde_expansion_preexpansion_hook) (username); + if (expansion) + { + dirname = glue_prefix_and_suffix (expansion, filename, user_len); + xfree (username); + xfree (expansion); + return (dirname); + } + } + + /* No preexpansion hook, or the preexpansion hook failed. Look in the + password database. */ + dirname = (char *)NULL; +#if defined (HAVE_GETPWNAM) + user_entry = getpwnam (username); +#else + user_entry = 0; +#endif + if (user_entry == 0) + { + /* If the calling program has a special syntax for expanding tildes, + and we couldn't find a standard expansion, then let them try. */ + if (tilde_expansion_failure_hook) + { + expansion = (*tilde_expansion_failure_hook) (username); + if (expansion) + { + dirname = glue_prefix_and_suffix (expansion, filename, user_len); + xfree (expansion); + } + } + /* If we don't have a failure hook, or if the failure hook did not + expand the tilde, return a copy of what we were passed. */ + if (dirname == 0) + dirname = savestring (filename); + } +#if defined (HAVE_GETPWENT) + else + dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); +#endif + + xfree (username); +#if defined (HAVE_GETPWENT) + endpwent (); +#endif + return (dirname); +} + + +#if defined (TEST) +#undef NULL +#include <stdio.h> + +main (int argc, char **argv) +{ + char *result, line[512]; + int done = 0; + + while (!done) + { + printf ("~expand: "); + fflush (stdout); + + if (!gets (line)) + strcpy (line, "done"); + + if ((strcmp (line, "done") == 0) || + (strcmp (line, "quit") == 0) || + (strcmp (line, "exit") == 0)) + { + done = 1; + break; + } + + result = tilde_expand (line); + printf (" --> %s\n", result); + free (result); + } + exit (0); +} + +static void memory_error_and_abort (void); + +static void * +xmalloc (size_t bytes) +{ + void *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void * +xrealloc (void *pointer, int bytes) +{ + void *temp; + + if (!pointer) + temp = malloc (bytes); + else + temp = realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort (void) +{ + fprintf (stderr, "readline: out of virtual memory\n"); + abort (); +} + +/* + * Local variables: + * compile-command: "gcc -g -DTEST -o tilde tilde.c" + * end: + */ +#endif /* TEST */ diff --git a/lib/readline/tilde.h b/lib/readline/tilde.h new file mode 100644 index 0000000..bc8022a --- /dev/null +++ b/lib/readline/tilde.h @@ -0,0 +1,68 @@ +/* tilde.h: Externally available variables and function in libtilde.a. */ + +/* Copyright (C) 1992-2009,2021 Free Software Foundation, Inc. + + This file contains the Readline Library (Readline), a set of + routines for providing Emacs style line input to programs that ask + for it. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_TILDE_H_) +# define _TILDE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char *tilde_hook_func_t (char *); + +/* If non-null, this contains the address of a function that the application + wants called before trying the standard tilde expansions. The function + is called with the text sans tilde, and returns a malloc()'ed string + which is the expansion, or a NULL pointer if the expansion fails. */ +extern tilde_hook_func_t *tilde_expansion_preexpansion_hook; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern tilde_hook_func_t *tilde_expansion_failure_hook; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +extern char **tilde_additional_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +extern char **tilde_additional_suffixes; + +/* Return a new string which is the result of tilde expanding STRING. */ +extern char *tilde_expand (const char *); + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +extern char *tilde_expand_word (const char *); + +/* Find the portion of the string beginning with ~ that should be expanded. */ +extern char *tilde_find_word (const char *, int, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _TILDE_H_ */ diff --git a/lib/readline/undo.c b/lib/readline/undo.c new file mode 100644 index 0000000..e4c457d --- /dev/null +++ b/lib/readline/undo.c @@ -0,0 +1,365 @@ +/* undo.c - manage list of changes to lines, offering opportunity to undo them */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.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> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#include "histlib.h" + +/* Non-zero tells rl_delete_text and rl_insert_text to not add to + the undo list. */ +int _rl_doing_an_undo = 0; + +/* How many unclosed undo groups we currently have. */ +int _rl_undo_group_level = 0; + +/* The current undo list for THE_LINE. */ +UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL; + +/* **************************************************************** */ +/* */ +/* Undo, and Undoing */ +/* */ +/* **************************************************************** */ + +static UNDO_LIST * +alloc_undo_entry (enum undo_code what, int start, int end, char *text) +{ + UNDO_LIST *temp; + + temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST)); + temp->what = what; + temp->start = start; + temp->end = end; + temp->text = text; + + temp->next = (UNDO_LIST *)NULL; + return temp; +} + +/* Remember how to undo something. Concatenate some undos if that + seems right. */ +void +rl_add_undo (enum undo_code what, int start, int end, char *text) +{ + UNDO_LIST *temp; + + temp = alloc_undo_entry (what, start, end, text); + temp->next = rl_undo_list; + rl_undo_list = temp; +} + +/* Free an UNDO_LIST */ +void +_rl_free_undo_list (UNDO_LIST *ul) +{ + UNDO_LIST *release; + + while (ul) + { + release = ul; + ul = ul->next; + + if (release->what == UNDO_DELETE) + xfree (release->text); + + xfree (release); + } +} + +/* Free the existing undo list. */ +void +rl_free_undo_list (void) +{ + UNDO_LIST *release, *orig_list; + + orig_list = rl_undo_list; + _rl_free_undo_list (rl_undo_list); + rl_undo_list = (UNDO_LIST *)NULL; + _hs_replace_history_data (-1, (histdata_t *)orig_list, (histdata_t *)NULL); +} + +UNDO_LIST * +_rl_copy_undo_entry (UNDO_LIST *entry) +{ + UNDO_LIST *new; + + new = alloc_undo_entry (entry->what, entry->start, entry->end, (char *)NULL); + new->text = entry->text ? savestring (entry->text) : 0; + return new; +} + +UNDO_LIST * +_rl_copy_undo_list (UNDO_LIST *head) +{ + UNDO_LIST *list, *new, *roving, *c; + + if (head == 0) + return head; + + list = head; + new = 0; + while (list) + { + c = _rl_copy_undo_entry (list); + if (new == 0) + roving = new = c; + else + { + roving->next = c; + roving = roving->next; + } + list = list->next; + } + + roving->next = 0; + return new; +} + +/* Undo the next thing in the list. Return 0 if there + is nothing to undo, or non-zero if there was. */ +int +rl_do_undo (void) +{ + UNDO_LIST *release, *search; + int waiting_for_begin, start, end; + HIST_ENTRY *cur, *temp; + +#define TRANS(i) ((i) == -1 ? rl_point : ((i) == -2 ? rl_end : (i))) + + start = end = waiting_for_begin = 0; + do + { + if (rl_undo_list == 0) + return (0); + + _rl_doing_an_undo = 1; + RL_SETSTATE(RL_STATE_UNDOING); + + /* To better support vi-mode, a start or end value of -1 means + rl_point, and a value of -2 means rl_end. */ + if (rl_undo_list->what == UNDO_DELETE || rl_undo_list->what == UNDO_INSERT) + { + start = TRANS (rl_undo_list->start); + end = TRANS (rl_undo_list->end); + } + + switch (rl_undo_list->what) + { + /* Undoing deletes means inserting some text. */ + case UNDO_DELETE: + rl_point = start; + _rl_fix_point (1); + rl_insert_text (rl_undo_list->text); + xfree (rl_undo_list->text); + break; + + /* Undoing inserts means deleting some text. */ + case UNDO_INSERT: + rl_delete_text (start, end); + rl_point = start; + _rl_fix_point (1); + break; + + /* Undoing an END means undoing everything 'til we get to a BEGIN. */ + case UNDO_END: + waiting_for_begin++; + break; + + /* Undoing a BEGIN means that we are done with this group. */ + case UNDO_BEGIN: + if (waiting_for_begin) + waiting_for_begin--; + else + rl_ding (); + break; + } + + _rl_doing_an_undo = 0; + RL_UNSETSTATE(RL_STATE_UNDOING); + + release = rl_undo_list; + rl_undo_list = rl_undo_list->next; + release->next = 0; /* XXX */ + + /* If we are editing a history entry, make sure the change is replicated + in the history entry's line */ + cur = current_history (); + if (cur && cur->data && (UNDO_LIST *)cur->data == release) + { + temp = replace_history_entry (where_history (), rl_line_buffer, (histdata_t)rl_undo_list); + xfree (temp->line); + FREE (temp->timestamp); + xfree (temp); + } + + /* Make sure there aren't any history entries with that undo list */ + _hs_replace_history_data (-1, (histdata_t *)release, (histdata_t *)rl_undo_list); + + /* And make sure this list isn't anywhere in the saved line for history */ + if (_rl_saved_line_for_history && _rl_saved_line_for_history->data) + { + /* Brute force; no finesse here */ + search = (UNDO_LIST *)_rl_saved_line_for_history->data; + if (search == release) + _rl_saved_line_for_history->data = rl_undo_list; + else + { + while (search->next) + { + if (search->next == release) + { + search->next = rl_undo_list; + break; + } + search = search->next; + } + } + } + + xfree (release); + } + while (waiting_for_begin); + + return (1); +} +#undef TRANS + +int +_rl_fix_last_undo_of_type (int type, int start, int end) +{ + UNDO_LIST *rl; + + for (rl = rl_undo_list; rl; rl = rl->next) + { + if (rl->what == type) + { + rl->start = start; + rl->end = end; + return 0; + } + } + return 1; +} + +/* Begin a group. Subsequent undos are undone as an atomic operation. */ +int +rl_begin_undo_group (void) +{ + rl_add_undo (UNDO_BEGIN, 0, 0, 0); + _rl_undo_group_level++; + return 0; +} + +/* End an undo group started with rl_begin_undo_group (). */ +int +rl_end_undo_group (void) +{ + rl_add_undo (UNDO_END, 0, 0, 0); + _rl_undo_group_level--; + return 0; +} + +/* Save an undo entry for the text from START to END. */ +int +rl_modifying (int start, int end) +{ + if (start > end) + { + SWAP (start, end); + } + + if (start != end) + { + char *temp = rl_copy_text (start, end); + rl_begin_undo_group (); + rl_add_undo (UNDO_DELETE, start, end, temp); + rl_add_undo (UNDO_INSERT, start, end, (char *)NULL); + rl_end_undo_group (); + } + return 0; +} + +/* Revert the current line to its previous state. */ +int +rl_revert_line (int count, int key) +{ + if (rl_undo_list == 0) + rl_ding (); + else + { + while (rl_undo_list) + rl_do_undo (); +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode) + rl_point = rl_mark = 0; /* rl_end should be set correctly */ +#endif + } + + return 0; +} + +/* Do some undoing of things that were done. */ +int +rl_undo_command (int count, int key) +{ + if (count < 0) + return 0; /* Nothing to do. */ + + while (count) + { + if (rl_do_undo ()) + count--; + else + { + rl_ding (); + break; + } + } + return 0; +} diff --git a/lib/readline/util.c b/lib/readline/util.c new file mode 100644 index 0000000..2e986db --- /dev/null +++ b/lib/readline/util.c @@ -0,0 +1,577 @@ +/* util.c -- readline utility functions */ + +/* Copyright (C) 1987-2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#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" +#include "rlshell.h" + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return 0 if C is not a member of the class of characters that belong + in words, or 1 if it is. */ + +int _rl_allow_pathname_alphabetic_chars = 0; +static const char * const pathname_alphabetic_chars = "/-_=~.#$"; + +int +rl_alphabetic (int c) +{ + if (_rl_alphabetic_p (c)) + return (1); + + return (_rl_allow_pathname_alphabetic_chars && + strchr (pathname_alphabetic_chars, c) != NULL); +} + +#if defined (HANDLE_MULTIBYTE) +int +_rl_walphabetic (WCHAR_T wc) +{ + int c; + + if (iswalnum (wc)) + return (1); + + c = wc & 0177; + return (_rl_allow_pathname_alphabetic_chars && + strchr (pathname_alphabetic_chars, c) != NULL); +} +#endif + +/* How to abort things. */ +int +_rl_abort_internal (void) +{ + if (RL_ISSTATE (RL_STATE_TIMEOUT) == 0) + rl_ding (); /* Don't ring the bell on a timeout */ + rl_clear_message (); + _rl_reset_argument (); + rl_clear_pending_input (); + rl_deactivate_mark (); + + while (rl_executing_macro) + _rl_pop_executing_macro (); + _rl_kill_kbd_macro (); + + RL_UNSETSTATE (RL_STATE_MULTIKEY); /* XXX */ + + rl_last_func = (rl_command_func_t *)NULL; + + _rl_longjmp (_rl_top_level, 1); + return (0); +} + +int +rl_abort (int count, int key) +{ + return (_rl_abort_internal ()); +} + +int +_rl_null_function (int count, int key) +{ + return 0; +} + +int +rl_tty_status (int count, int key) +{ +#if defined (TIOCSTAT) + ioctl (1, TIOCSTAT, (char *)0); + rl_refresh_line (count, key); +#else + rl_ding (); +#endif + return 0; +} + +/* Return a copy of the string between FROM and TO. + FROM is inclusive, TO is not. */ +char * +rl_copy_text (int from, int to) +{ + register int length; + char *copy; + + /* Fix it if the caller is confused. */ + if (from > to) + SWAP (from, to); + + length = to - from; + copy = (char *)xmalloc (1 + length); + strncpy (copy, rl_line_buffer + from, length); + copy[length] = '\0'; + return (copy); +} + +/* Increase the size of RL_LINE_BUFFER until it has enough space to hold + LEN characters. */ +void +rl_extend_line_buffer (int len) +{ + while (len >= rl_line_buffer_len) + { + rl_line_buffer_len += DEFAULT_BUFFER_SIZE; + rl_line_buffer = (char *)xrealloc (rl_line_buffer, rl_line_buffer_len); + } + + _rl_set_the_line (); +} + + +/* A function for simple tilde expansion. */ +int +rl_tilde_expand (int ignore, int key) +{ + register int start, end; + char *homedir, *temp; + int len; + + end = rl_point; + start = end - 1; + + if (rl_point == rl_end && rl_line_buffer[rl_point] == '~') + { + homedir = tilde_expand ("~"); + _rl_replace_text (homedir, start, end); + xfree (homedir); + return (0); + } + else if (start >= 0 && rl_line_buffer[start] != '~') + { + for (; start >= 0 && !whitespace (rl_line_buffer[start]); start--) + ; + start++; + } + else if (start < 0) + start = 0; + + end = start; + do + end++; + while (whitespace (rl_line_buffer[end]) == 0 && end < rl_end); + + if (whitespace (rl_line_buffer[end]) || end >= rl_end) + end--; + + /* If the first character of the current word is a tilde, perform + tilde expansion and insert the result. If not a tilde, do + nothing. */ + if (rl_line_buffer[start] == '~') + { + len = end - start + 1; + temp = (char *)xmalloc (len + 1); + strncpy (temp, rl_line_buffer + start, len); + temp[len] = '\0'; + homedir = tilde_expand (temp); + xfree (temp); + + _rl_replace_text (homedir, start, end); + xfree (homedir); + } + + return (0); +} + +#if defined (USE_VARARGS) +void +#if defined (PREFER_STDARG) +_rl_ttymsg (const char *format, ...) +#else +_rl_ttymsg (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + fprintf (stderr, "readline: "); + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + + va_end (args); + + rl_forced_update_display (); +} + +void +#if defined (PREFER_STDARG) +_rl_errmsg (const char *format, ...) +#else +_rl_errmsg (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + fprintf (stderr, "readline: "); + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + + va_end (args); +} + +#else /* !USE_VARARGS */ +void +_rl_ttymsg (format, arg1, arg2) + char *format; +{ + fprintf (stderr, "readline: "); + fprintf (stderr, format, arg1, arg2); + fprintf (stderr, "\n"); + + rl_forced_update_display (); +} + +void +_rl_errmsg (format, arg1, arg2) + char *format; +{ + fprintf (stderr, "readline: "); + fprintf (stderr, format, arg1, arg2); + fprintf (stderr, "\n"); +} +#endif /* !USE_VARARGS */ + +/* **************************************************************** */ +/* */ +/* String Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Determine if s2 occurs in s1. If so, return a pointer to the + match in s1. The compare is case insensitive. */ +char * +_rl_strindex (const char *s1, const char *s2) +{ + register int i, l, len; + + for (i = 0, l = strlen (s2), len = strlen (s1); (len - i) >= l; i++) + if (_rl_strnicmp (s1 + i, s2, l) == 0) + return ((char *) (s1 + i)); + return ((char *)NULL); +} + +#ifndef HAVE_STRPBRK +/* Find the first occurrence in STRING1 of any character from STRING2. + Return a pointer to the character in STRING1. */ +char * +_rl_strpbrk (const char *string1, const char *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++) + { + for (scan = string2; *scan; scan++) + { + 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) + string1 += v - 1; /* -1 to account for auto-increment in loop */ + } +#endif + } + return ((char *)NULL); +} +#endif + +#if !defined (HAVE_STRCASECMP) +/* Compare at most COUNT characters from string1 to string2. Case + doesn't matter (strncasecmp). */ +int +_rl_strnicmp (const char *string1, const char *string2, int count) +{ + register const char *s1; + register const char *s2; + register int d; + + if (count <= 0 || (string1 == string2)) + return 0; + + s1 = string1; + s2 = string2; + do + { + d = _rl_to_lower (*s1) - _rl_to_lower (*s2); /* XXX - cast to unsigned char? */ + if (d != 0) + return d; + if (*s1++ == '\0') + break; + s2++; + } + while (--count != 0); + + return (0); +} + +/* strcmp (), but caseless (strcasecmp). */ +int +_rl_stricmp (const char *string1, const char *string2) +{ + register const char *s1; + register const char *s2; + register int d; + + s1 = string1; + s2 = string2; + + if (s1 == s2) + return 0; + + while ((d = _rl_to_lower (*s1) - _rl_to_lower (*s2)) == 0) + { + if (*s1++ == '\0') + return 0; + s2++; + } + + return (d); +} +#endif /* !HAVE_STRCASECMP */ + +/* Stupid comparison routine for qsort () ing strings. */ +int +_rl_qsort_string_compare (char **s1, char **s2) +{ +#if defined (HAVE_STRCOLL) + return (strcoll (*s1, *s2)); +#else + int result; + + result = **s1 - **s2; + if (result == 0) + result = strcmp (*s1, *s2); + + return result; +#endif +} + +/* Function equivalents for the macros defined in chardefs.h. */ +#define FUNCTION_FOR_MACRO(f) int (f) (int c) { return f (c); } + +FUNCTION_FOR_MACRO (_rl_digit_p) +FUNCTION_FOR_MACRO (_rl_digit_value) +FUNCTION_FOR_MACRO (_rl_lowercase_p) +FUNCTION_FOR_MACRO (_rl_pure_alphabetic) +FUNCTION_FOR_MACRO (_rl_to_lower) +FUNCTION_FOR_MACRO (_rl_to_upper) +FUNCTION_FOR_MACRO (_rl_uppercase_p) + +/* A convenience function, to force memory deallocation to be performed + by readline. DLLs on Windows apparently require this. */ +void +rl_free (void *mem) +{ + if (mem) + free (mem); +} + +/* Backwards compatibility, now that savestring has been removed from + all `public' readline header files. */ +#undef _rl_savestring +char * +_rl_savestring (const char *s) +{ + return (strcpy ((char *)xmalloc (1 + (int)strlen (s)), (s))); +} + +#if defined (DEBUG) +#if defined (USE_VARARGS) +static FILE *_rl_tracefp; + +void +#if defined (PREFER_STDARG) +_rl_trace (const char *format, ...) +#else +_rl_trace (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + if (_rl_tracefp == 0) + _rl_tropen (); + vfprintf (_rl_tracefp, format, args); + fprintf (_rl_tracefp, "\n"); + fflush (_rl_tracefp); + + va_end (args); +} + +int +_rl_tropen (void) +{ + char fnbuf[128], *x; + + if (_rl_tracefp) + fclose (_rl_tracefp); +#if defined (_WIN32) && !defined (__CYGWIN__) + x = sh_get_env_value ("TEMP"); + if (x == 0) + x = "."; +#else + x = "/var/tmp"; +#endif + snprintf (fnbuf, sizeof (fnbuf), "%s/rltrace.%ld", x, (long)getpid()); + unlink(fnbuf); + _rl_tracefp = fopen (fnbuf, "w+"); + return _rl_tracefp != 0; +} + +int +_rl_trclose (void) +{ + int r; + + r = fclose (_rl_tracefp); + _rl_tracefp = 0; + return r; +} + +void +_rl_settracefp (FILE *fp) +{ + _rl_tracefp = fp; +} +#endif +#endif /* DEBUG */ + + +#if HAVE_DECL_AUDIT_USER_TTY && defined (HAVE_LIBAUDIT_H) && defined (ENABLE_TTY_AUDIT_SUPPORT) +#include <sys/socket.h> +#include <libaudit.h> +#include <linux/audit.h> +#include <linux/netlink.h> + +/* Report STRING to the audit system. */ +void +_rl_audit_tty (char *string) +{ + struct audit_message req; + struct sockaddr_nl addr; + size_t size; + int fd; + + fd = socket (PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); + if (fd < 0) + return; + size = strlen (string) + 1; + + if (NLMSG_SPACE (size) > MAX_AUDIT_MESSAGE_LENGTH) + return; + + memset (&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_SPACE (size); + req.nlh.nlmsg_type = AUDIT_USER_TTY; + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_seq = 0; + if (size && string) + memcpy (NLMSG_DATA(&req.nlh), string, size); + memset (&addr, 0, sizeof(addr)); + + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + sendto (fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)); + close (fd); +} +#endif diff --git a/lib/readline/vi_keymap.c b/lib/readline/vi_keymap.c new file mode 100644 index 0000000..045258b --- /dev/null +++ b/lib/readline/vi_keymap.c @@ -0,0 +1,875 @@ +/* vi_keymap.c -- the keymap for vi_mode in readline (). */ + +/* Copyright (C) 1987-2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (BUFSIZ) +#include <stdio.h> +#endif /* !BUFSIZ */ + +#include "readline.h" + +#if 0 +extern KEYMAP_ENTRY_ARRAY vi_escape_keymap; +#endif + +/* The keymap arrays for handling vi mode. */ +KEYMAP_ENTRY_ARRAY vi_movement_keymap = { + /* The regular control keys come first. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-@ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_emacs_editing_mode }, /* Control-e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { 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 */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_vi_unix_word_rubout }, /* Control-w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */ + + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-[ */ /* vi_escape_keymap */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */ + { ISFUNC, rl_vi_undo }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_forward_char }, /* SPACE */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ! */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* " */ + { ISFUNC, rl_insert_comment }, /* # */ + { ISFUNC, rl_end_of_line }, /* $ */ + { ISFUNC, rl_vi_match }, /* % */ + { ISFUNC, rl_vi_tilde_expand }, /* & */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ' */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ( */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ) */ + { ISFUNC, rl_vi_complete }, /* * */ + { ISFUNC, rl_get_next_history}, /* + */ + { ISFUNC, rl_vi_char_search }, /* , */ + { ISFUNC, rl_get_previous_history }, /* - */ + { ISFUNC, rl_vi_redo }, /* . */ + { ISFUNC, rl_vi_search }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_beg_of_line }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* : */ + { ISFUNC, rl_vi_char_search }, /* ; */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* < */ + { ISFUNC, rl_vi_complete }, /* = */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* > */ + { ISFUNC, rl_vi_search }, /* ? */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_vi_append_eol }, /* A */ + { ISFUNC, rl_vi_prev_word}, /* B */ + { ISFUNC, rl_vi_change_to }, /* C */ + { ISFUNC, rl_vi_delete_to }, /* D */ + { ISFUNC, rl_vi_end_word }, /* E */ + { ISFUNC, rl_vi_char_search }, /* F */ + { ISFUNC, rl_vi_fetch_history }, /* G */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* H */ + { ISFUNC, rl_vi_insert_beg }, /* I */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* J */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* K */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* L */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* M */ + { ISFUNC, rl_vi_search_again }, /* N */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* O */ + { ISFUNC, rl_vi_put }, /* P */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Q */ + { ISFUNC, rl_vi_replace }, /* R */ + { ISFUNC, rl_vi_subst }, /* S */ + { ISFUNC, rl_vi_char_search }, /* T */ + { ISFUNC, rl_revert_line }, /* U */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* V */ + { ISFUNC, rl_vi_next_word }, /* W */ + { ISFUNC, rl_vi_rubout }, /* X */ + { ISFUNC, rl_vi_yank_to }, /* Y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* [ */ + { ISFUNC, rl_vi_complete }, /* \ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ] */ + { ISFUNC, rl_vi_first_print }, /* ^ */ + { ISFUNC, rl_vi_yank_arg }, /* _ */ + { ISFUNC, rl_vi_goto_mark }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_vi_append_mode }, /* a */ + { ISFUNC, rl_vi_prev_word }, /* b */ + { ISFUNC, rl_vi_change_to }, /* c */ + { ISFUNC, rl_vi_delete_to }, /* d */ + { ISFUNC, rl_vi_end_word }, /* e */ + { ISFUNC, rl_vi_char_search }, /* f */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* g */ + { ISFUNC, rl_backward_char }, /* h */ + { ISFUNC, rl_vi_insert_mode }, /* i */ + { ISFUNC, rl_get_next_history }, /* j */ + { ISFUNC, rl_get_previous_history }, /* k */ + { ISFUNC, rl_forward_char }, /* l */ + { ISFUNC, rl_vi_set_mark }, /* m */ + { ISFUNC, rl_vi_search_again }, /* n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* o */ + { ISFUNC, rl_vi_put }, /* p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* q */ + { ISFUNC, rl_vi_change_char }, /* r */ + { ISFUNC, rl_vi_subst }, /* s */ + { ISFUNC, rl_vi_char_search }, /* t */ + { ISFUNC, rl_vi_undo }, /* u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* v */ + { ISFUNC, rl_vi_next_word }, /* w */ + { ISFUNC, rl_vi_delete }, /* x */ + { ISFUNC, rl_vi_yank_to }, /* y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* { */ + { ISFUNC, rl_vi_column }, /* | */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* } */ + { ISFUNC, rl_vi_change_case }, /* ~ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* RUBOUT */ + +#if KEYMAP_SIZE > 128 + /* Undefined keys. */ + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 } +#endif /* KEYMAP_SIZE > 128 */ +}; + +KEYMAP_ENTRY_ARRAY vi_insertion_keymap = { + /* The regular control keys come first. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-@ */ + { ISFUNC, rl_insert }, /* Control-a */ + { ISFUNC, rl_insert }, /* Control-b */ + { ISFUNC, rl_insert }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_insert }, /* Control-e */ + { ISFUNC, rl_insert }, /* Control-f */ + { ISFUNC, rl_insert }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_insert }, /* Control-k */ + { ISFUNC, rl_insert }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_menu_complete}, /* Control-n */ + { ISFUNC, rl_insert }, /* Control-o */ + { ISFUNC, rl_backward_menu_complete }, /* Control-p */ + { ISFUNC, rl_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_vi_unix_word_rubout }, /* Control-w */ + { ISFUNC, rl_insert }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, rl_insert }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, rl_insert }, /* Control-\ */ + { ISFUNC, rl_insert }, /* Control-] */ + { ISFUNC, rl_insert }, /* Control-^ */ + { ISFUNC, rl_vi_undo }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout }, /* RUBOUT */ + +#if KEYMAP_SIZE > 128 + /* Pure 8-bit characters (128 - 159). + These might be used in some + character sets. */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* ? */ + + /* ISO Latin-1 characters (160 - 255) */ + { ISFUNC, rl_insert }, /* No-break space */ + { ISFUNC, rl_insert }, /* Inverted exclamation mark */ + { ISFUNC, rl_insert }, /* Cent sign */ + { ISFUNC, rl_insert }, /* Pound sign */ + { ISFUNC, rl_insert }, /* Currency sign */ + { ISFUNC, rl_insert }, /* Yen sign */ + { ISFUNC, rl_insert }, /* Broken bar */ + { ISFUNC, rl_insert }, /* Section sign */ + { ISFUNC, rl_insert }, /* Diaeresis */ + { ISFUNC, rl_insert }, /* Copyright sign */ + { ISFUNC, rl_insert }, /* Feminine ordinal indicator */ + { ISFUNC, rl_insert }, /* Left pointing double angle quotation mark */ + { ISFUNC, rl_insert }, /* Not sign */ + { ISFUNC, rl_insert }, /* Soft hyphen */ + { ISFUNC, rl_insert }, /* Registered sign */ + { ISFUNC, rl_insert }, /* Macron */ + { ISFUNC, rl_insert }, /* Degree sign */ + { ISFUNC, rl_insert }, /* Plus-minus sign */ + { ISFUNC, rl_insert }, /* Superscript two */ + { ISFUNC, rl_insert }, /* Superscript three */ + { ISFUNC, rl_insert }, /* Acute accent */ + { ISFUNC, rl_insert }, /* Micro sign */ + { ISFUNC, rl_insert }, /* Pilcrow sign */ + { ISFUNC, rl_insert }, /* Middle dot */ + { ISFUNC, rl_insert }, /* Cedilla */ + { ISFUNC, rl_insert }, /* Superscript one */ + { ISFUNC, rl_insert }, /* Masculine ordinal indicator */ + { ISFUNC, rl_insert }, /* Right pointing double angle quotation mark */ + { ISFUNC, rl_insert }, /* Vulgar fraction one quarter */ + { ISFUNC, rl_insert }, /* Vulgar fraction one half */ + { ISFUNC, rl_insert }, /* Vulgar fraction three quarters */ + { ISFUNC, rl_insert }, /* Inverted questionk mark */ + { ISFUNC, rl_insert }, /* Latin capital letter a with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter a with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter a with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter a with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter a with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter a with ring above */ + { ISFUNC, rl_insert }, /* Latin capital letter ae */ + { ISFUNC, rl_insert }, /* Latin capital letter c with cedilla */ + { ISFUNC, rl_insert }, /* Latin capital letter e with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter e with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter e with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter e with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter i with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter i with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter i with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter i with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter eth (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin capital letter n with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter o with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter o with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter o with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter o with tilde */ + { ISFUNC, rl_insert }, /* Latin capital letter o with diaeresis */ + { ISFUNC, rl_insert }, /* Multiplication sign */ + { ISFUNC, rl_insert }, /* Latin capital letter o with stroke */ + { ISFUNC, rl_insert }, /* Latin capital letter u with grave */ + { ISFUNC, rl_insert }, /* Latin capital letter u with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter u with circumflex */ + { ISFUNC, rl_insert }, /* Latin capital letter u with diaeresis */ + { ISFUNC, rl_insert }, /* Latin capital letter Y with acute */ + { ISFUNC, rl_insert }, /* Latin capital letter thorn (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin small letter sharp s (German) */ + { ISFUNC, rl_insert }, /* Latin small letter a with grave */ + { ISFUNC, rl_insert }, /* Latin small letter a with acute */ + { ISFUNC, rl_insert }, /* Latin small letter a with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter a with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter a with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter a with ring above */ + { ISFUNC, rl_insert }, /* Latin small letter ae */ + { ISFUNC, rl_insert }, /* Latin small letter c with cedilla */ + { ISFUNC, rl_insert }, /* Latin small letter e with grave */ + { ISFUNC, rl_insert }, /* Latin small letter e with acute */ + { ISFUNC, rl_insert }, /* Latin small letter e with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter e with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter i with grave */ + { ISFUNC, rl_insert }, /* Latin small letter i with acute */ + { ISFUNC, rl_insert }, /* Latin small letter i with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter i with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter eth (Icelandic) */ + { ISFUNC, rl_insert }, /* Latin small letter n with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter o with grave */ + { ISFUNC, rl_insert }, /* Latin small letter o with acute */ + { ISFUNC, rl_insert }, /* Latin small letter o with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter o with tilde */ + { ISFUNC, rl_insert }, /* Latin small letter o with diaeresis */ + { ISFUNC, rl_insert }, /* Division sign */ + { ISFUNC, rl_insert }, /* Latin small letter o with stroke */ + { ISFUNC, rl_insert }, /* Latin small letter u with grave */ + { ISFUNC, rl_insert }, /* Latin small letter u with acute */ + { ISFUNC, rl_insert }, /* Latin small letter u with circumflex */ + { ISFUNC, rl_insert }, /* Latin small letter u with diaeresis */ + { ISFUNC, rl_insert }, /* Latin small letter y with acute */ + { ISFUNC, rl_insert }, /* Latin small letter thorn (Icelandic) */ + { ISFUNC, rl_insert } /* Latin small letter y with diaeresis */ +#endif /* KEYMAP_SIZE > 128 */ +}; + +/* Unused for the time being. */ +#if 0 +KEYMAP_ENTRY_ARRAY vi_escape_keymap = { + /* The regular control keys come first. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-@ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-c */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-d */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-f */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-g */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-h */ + { ISFUNC, rl_tab_insert}, /* Control-i */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-l */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-m */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-n */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-o */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-q */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-s */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-t */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-x */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-\ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-^ */ + { ISFUNC, rl_vi_undo }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* SPACE */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ! */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* " */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* # */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* $ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* % */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* & */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ' */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ( */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ) */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* * */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* + */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* , */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* - */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* . */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_vi_arg_digit }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* : */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ; */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* < */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* = */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* > */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ? */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_arrow_keys }, /* [ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* \ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ] */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ^ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* _ */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* a */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* b */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* c */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* d */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* e */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* f */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* g */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* h */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* i */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* j */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* k */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* l */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* m */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* n */ + { ISFUNC, rl_arrow_keys }, /* o */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* p */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* q */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* r */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* s */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* t */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* u */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* v */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* w */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* x */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* y */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* { */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* | */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* } */ + { ISFUNC, (rl_command_func_t *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_word }, /* RUBOUT */ + +#if KEYMAP_SIZE > 128 + /* Undefined keys. */ + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 }, + { ISFUNC, (rl_command_func_t *)0x0 } +#endif /* KEYMAP_SIZE > 128 */ +}; +#endif diff --git a/lib/readline/vi_mode.c b/lib/readline/vi_mode.c new file mode 100644 index 0000000..3a033ba --- /dev/null +++ b/lib/readline/vi_mode.c @@ -0,0 +1,2415 @@ +/* vi_mode.c -- A vi emulation mode for Bash. + Derived from code written by Jeff Sparkes (jsparkes@bnr.ca). */ + +/* Copyright (C) 1987-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +/* **************************************************************** */ +/* */ +/* VI Emulation Mode */ +/* */ +/* **************************************************************** */ +#include "rlconf.h" + +#if defined (VI_MODE) + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +/* Some standard library routines. */ +#include "rldefs.h" +#include "rlmbutil.h" + +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#ifndef member +#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0) +#endif + +/* Increment START to the next character in RL_LINE_BUFFER, handling multibyte chars */ +#if defined (HANDLE_MULTIBYTE) +#define INCREMENT_POS(start) \ + do { \ + if (MB_CUR_MAX == 1 || rl_byte_oriented) \ + start++; \ + else \ + start = _rl_find_next_mbchar (rl_line_buffer, start, 1, MB_FIND_ANY); \ + } while (0) +#else /* !HANDLE_MULTIBYTE */ +#define INCREMENT_POS(start) (start)++ +#endif /* !HANDLE_MULTIBYTE */ + +/* This is global so other parts of the code can check whether the last + command was a text modification command. */ +int _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */ + +_rl_vimotion_cxt *_rl_vimvcxt = 0; + +/* Non-zero indicates we are redoing a vi-mode command with `.' */ +int _rl_vi_redoing; + +/* Non-zero means enter insertion mode. */ +static int _rl_vi_doing_insert; + +/* Command keys which do movement for xxx_to commands. */ +static const char * const vi_motion = " hl^$0ftFT;,%wbeWBE|`"; + +/* Keymap used for vi replace characters. Created dynamically since + rarely used. */ +static Keymap vi_replace_map; + +/* The number of characters inserted in the last replace operation. */ +static int vi_replace_count; + +/* If non-zero, we have text inserted after a c[motion] command that put + us implicitly into insert mode. Some people want this text to be + attached to the command so that it is `redoable' with `.'. */ +static int vi_continued_command; +static char *vi_insert_buffer; +static int vi_insert_buffer_size; + +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]; +static int _rl_vi_last_search_mblen; +#else +static int _rl_vi_last_search_char; +#endif +static char _rl_vi_last_replacement[MB_LEN_MAX+1]; /* reserve for trailing NULL */ + +static int _rl_vi_last_key_before_insert; + +/* Text modification commands. These are the `redoable' commands. */ +static const char * const vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~"; + +/* Arrays for the saved marks. */ +static int vi_mark_chars['z' - 'a' + 1]; + +static void _rl_vi_replace_insert (int); +static void _rl_vi_save_replace (void); +static void _rl_vi_stuff_insert (int); +static void _rl_vi_save_insert (UNDO_LIST *); + +static void vi_save_insert_buffer (int, int); + +static inline void _rl_vi_backup (void); + +static int _rl_vi_arg_dispatch (int); +static int rl_digit_loop1 (void); + +static int _rl_vi_set_mark (void); +static int _rl_vi_goto_mark (void); + +static inline int _rl_vi_advance_point (void); +static inline int _rl_vi_backup_point (void); + +static void _rl_vi_append_forward (int); + +static int _rl_vi_callback_getchar (char *, int); + +#if defined (READLINE_CALLBACKS) +static int _rl_vi_callback_set_mark (_rl_callback_generic_arg *); +static int _rl_vi_callback_goto_mark (_rl_callback_generic_arg *); +static int _rl_vi_callback_change_char (_rl_callback_generic_arg *); +static int _rl_vi_callback_char_search (_rl_callback_generic_arg *); +#endif + +static int rl_domove_read_callback (_rl_vimotion_cxt *); +static int rl_domove_motion_callback (_rl_vimotion_cxt *); +static int rl_vi_domove_getchar (_rl_vimotion_cxt *); + +static int vi_change_dispatch (_rl_vimotion_cxt *); +static int vi_delete_dispatch (_rl_vimotion_cxt *); +static int vi_yank_dispatch (_rl_vimotion_cxt *); + +static int vidomove_dispatch (_rl_vimotion_cxt *); + +void +_rl_vi_initialize_line (void) +{ + register int i, n; + + n = sizeof (vi_mark_chars) / sizeof (vi_mark_chars[0]); + for (i = 0; i < n; i++) + vi_mark_chars[i] = -1; + + RL_UNSETSTATE(RL_STATE_VICMDONCE); +} + +void +_rl_vi_reset_last (void) +{ + _rl_vi_last_command = 'i'; + _rl_vi_last_repeat = 1; + _rl_vi_last_arg_sign = 1; + _rl_vi_last_motion = 0; +} + +void +_rl_vi_set_last (int key, int repeat, int sign) +{ + _rl_vi_last_command = key; + _rl_vi_last_repeat = repeat; + _rl_vi_last_arg_sign = sign; +} + +/* A convenience function that calls _rl_vi_set_last to save the last command + information and enters insertion mode. */ +void +rl_vi_start_inserting (int key, int repeat, int sign) +{ + _rl_vi_set_last (key, repeat, sign); + rl_begin_undo_group (); /* ensure inserts aren't concatenated */ + rl_vi_insertion_mode (1, key); +} + +/* Is the command C a VI mode text modification command? */ +int +_rl_vi_textmod_command (int c) +{ + return (member (c, vi_textmod)); +} + +int +_rl_vi_motion_command (int c) +{ + return (member (c, vi_motion)); +} + +static void +_rl_vi_replace_insert (int count) +{ + int nchars; + + nchars = strlen (vi_insert_buffer); + + rl_begin_undo_group (); + while (count--) + /* nchars-1 to compensate for _rl_replace_text using `end+1' in call + to rl_delete_text */ + _rl_replace_text (vi_insert_buffer, rl_point, rl_point+nchars-1); + rl_end_undo_group (); +} + +static void +_rl_vi_stuff_insert (int count) +{ + rl_begin_undo_group (); + while (count--) + rl_insert_text (vi_insert_buffer); + rl_end_undo_group (); +} + +/* Bound to `.'. Called from command mode, so we know that we have to + redo a text modification command. The default for _rl_vi_last_command + puts you back into insert mode. */ +int +rl_vi_redo (int count, int c) +{ + int r; + + if (rl_explicit_arg == 0) + { + rl_numeric_arg = _rl_vi_last_repeat; + rl_arg_sign = _rl_vi_last_arg_sign; + } + + r = 0; + _rl_vi_redoing = 1; + /* If we're redoing an insert with `i', stuff in the inserted text + and do not go into insertion mode. */ + if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer) + { + _rl_vi_stuff_insert (count); + /* And back up point over the last character inserted. */ + if (rl_point > 0) + _rl_vi_backup (); + } + else if (_rl_vi_last_command == 'R' && vi_insert_buffer && *vi_insert_buffer) + { + _rl_vi_replace_insert (count); + /* And back up point over the last character inserted. */ + if (rl_point > 0) + _rl_vi_backup (); + } + /* Ditto for redoing an insert with `I', but move to the beginning of the + line like the `I' command does. */ + else if (_rl_vi_last_command == 'I' && vi_insert_buffer && *vi_insert_buffer) + { + rl_beg_of_line (1, 'I'); + _rl_vi_stuff_insert (count); + if (rl_point > 0) + _rl_vi_backup (); + } + /* Ditto for redoing an insert with `a', but move forward a character first + like the `a' command does. */ + else if (_rl_vi_last_command == 'a' && vi_insert_buffer && *vi_insert_buffer) + { + _rl_vi_append_forward ('a'); + _rl_vi_stuff_insert (count); + if (rl_point > 0) + _rl_vi_backup (); + } + /* Ditto for redoing an insert with `A', but move to the end of the line + like the `A' command does. */ + else if (_rl_vi_last_command == 'A' && vi_insert_buffer && *vi_insert_buffer) + { + rl_end_of_line (1, 'A'); + _rl_vi_stuff_insert (count); + if (rl_point > 0) + _rl_vi_backup (); + } + else if (_rl_vi_last_command == '.' && _rl_keymap == vi_movement_keymap) + { + rl_ding (); + r = 0; + } + else + r = _rl_dispatch (_rl_vi_last_command, _rl_keymap); + + _rl_vi_redoing = 0; + + return (r); +} + +/* A placeholder for further expansion. */ +int +rl_vi_undo (int count, int key) +{ + return (rl_undo_command (count, key)); +} + +/* Yank the nth arg from the previous line into this line at point. */ +int +rl_vi_yank_arg (int count, int key) +{ + /* Readline thinks that the first word on a line is the 0th, while vi + thinks the first word on a line is the 1st. Compensate. */ + if (rl_explicit_arg) + rl_yank_nth_arg (count - 1, key); + else + rl_yank_nth_arg ('$', key); + + return (0); +} + +/* With an argument, move back that many history lines, else move to the + beginning of history. */ +int +rl_vi_fetch_history (int count, int c) +{ + return (rl_fetch_history (count, c)); +} + +/* Search again for the last thing searched for. */ +int +rl_vi_search_again (int count, int key) +{ + switch (key) + { + case 'n': + rl_noninc_reverse_search_again (count, key); + break; + + case 'N': + rl_noninc_forward_search_again (count, key); + break; + } + return (0); +} + +/* Do a vi style search. */ +int +rl_vi_search (int count, int key) +{ + switch (key) + { + case '?': + _rl_free_saved_history_line (); + rl_noninc_forward_search (count, key); + break; + + case '/': + _rl_free_saved_history_line (); + rl_noninc_reverse_search (count, key); + break; + + default: + rl_ding (); + break; + } + return (0); +} + +/* Completion, from vi's point of view. */ +int +rl_vi_complete (int ignore, int key) +{ + if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point]))) + { + if (!whitespace (rl_line_buffer[rl_point + 1])) + rl_vi_end_word (1, 'E'); + _rl_vi_advance_point (); + } + + if (key == '*') + rl_complete_internal ('*'); /* Expansion and replacement. */ + else if (key == '=') + rl_complete_internal ('?'); /* List possible completions. */ + else if (key == '\\') + rl_complete_internal (TAB); /* Standard Readline completion. */ + else + rl_complete (0, key); + + if (key == '*' || key == '\\') + rl_vi_start_inserting (key, 1, rl_arg_sign); + + return (0); +} + +/* Tilde expansion for vi mode. */ +int +rl_vi_tilde_expand (int ignore, int key) +{ + rl_tilde_expand (0, key); + rl_vi_start_inserting (key, 1, rl_arg_sign); + return (0); +} + +/* Previous word in vi mode. */ +int +rl_vi_prev_word (int count, int key) +{ + if (count < 0) + return (rl_vi_next_word (-count, key)); + + if (rl_point == 0) + { + rl_ding (); + return (0); + } + + if (_rl_uppercase_p (key)) + rl_vi_bWord (count, key); + else + rl_vi_bword (count, key); + + return (0); +} + +/* Next word in vi mode. */ +int +rl_vi_next_word (int count, int key) +{ + if (count < 0) + return (rl_vi_prev_word (-count, key)); + + if (rl_point >= (rl_end - 1)) + { + rl_ding (); + return (0); + } + + if (_rl_uppercase_p (key)) + rl_vi_fWord (count, key); + else + rl_vi_fword (count, key); + return (0); +} + +static inline int +_rl_vi_advance_point (void) +{ + int point; + + point = rl_point; + if (rl_point < rl_end) +#if defined (HANDLE_MULTIBYTE) + { + if (MB_CUR_MAX == 1 || rl_byte_oriented) + rl_point++; + else + { + point = rl_point; + rl_point = _rl_forward_char_internal (1); + if (point == rl_point || rl_point > rl_end) + rl_point = rl_end; + } + } +#else + rl_point++; +#endif + + return point; +} + +/* Move the cursor back one character. */ +static inline void +_rl_vi_backup (void) +{ + 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--; +} + +/* Move the point back one character, returning the starting value and not + doing anything at the beginning of the line */ +static inline int +_rl_vi_backup_point (void) +{ + int point; + + point = rl_point; + if (rl_point > 0) +#if defined (HANDLE_MULTIBYTE) + { + if (MB_CUR_MAX == 1 || rl_byte_oriented) + rl_point--; + else + { + point = rl_point; + rl_point = _rl_backward_char_internal (1); + if (rl_point < 0) + rl_point = 0; /* XXX - not really necessary */ + } + } +#else + rl_point--; +#endif + return point; +} + +/* Move to the end of the ?next? word. */ +int +rl_vi_end_word (int count, int key) +{ + if (count < 0) + { + rl_ding (); + return 1; + } + + if (_rl_uppercase_p (key)) + rl_vi_eWord (count, key); + else + rl_vi_eword (count, key); + return (0); +} + +/* Move forward a word the way that 'W' does. */ +int +rl_vi_fWord (int count, int ignore) +{ + while (count-- && rl_point < (rl_end - 1)) + { + /* Skip until whitespace. */ + while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) + _rl_vi_advance_point (); + + /* Now skip whitespace. */ + while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) + _rl_vi_advance_point (); + } + return (0); +} + +int +rl_vi_bWord (int count, int ignore) +{ + while (count-- && rl_point > 0) + { + /* If we are at the start of a word, move back to whitespace so + we will go back to the start of the previous word. */ + if (!whitespace (rl_line_buffer[rl_point]) && + whitespace (rl_line_buffer[rl_point - 1])) + rl_point--; + + while (rl_point > 0 && whitespace (rl_line_buffer[rl_point])) + _rl_vi_backup_point (); + + if (rl_point > 0) + { + do + _rl_vi_backup_point (); + while (rl_point > 0 && !whitespace (rl_line_buffer[rl_point])); + if (rl_point > 0) /* hit whitespace */ + rl_point++; + + if (rl_point < 0) + rl_point = 0; + } + } + return (0); +} + +int +rl_vi_eWord (int count, int ignore) +{ + int opoint; + + while (count-- && rl_point < (rl_end - 1)) + { + if (whitespace (rl_line_buffer[rl_point]) == 0) + _rl_vi_advance_point (); + + /* Move to the next non-whitespace character (to the start of the + next word). */ + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + _rl_vi_advance_point (); + + if (rl_point && rl_point < rl_end) + { + opoint = rl_point; + + /* Skip whitespace. */ + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + opoint = _rl_vi_advance_point (); /* XXX - why? */ + + /* Skip until whitespace. */ + while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point])) + opoint = _rl_vi_advance_point (); + + /* Move back to the last character of the word. */ + rl_point = opoint; + } + } + return (0); +} + +int +rl_vi_fword (int count, int ignore) +{ + int opoint; + + while (count-- && rl_point < (rl_end - 1)) + { + /* Move to white space (really non-identifer). */ + if (_rl_isident (rl_line_buffer[rl_point])) + { + while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end) + _rl_vi_advance_point (); + } + else /* if (!whitespace (rl_line_buffer[rl_point])) */ + { + while (!_rl_isident (rl_line_buffer[rl_point]) && + !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) + _rl_vi_advance_point (); + } + + opoint = rl_point; + + /* Move past whitespace. */ + while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) + opoint = _rl_vi_advance_point (); + } + return (0); +} + +int +rl_vi_bword (int count, int ignore) +{ + int opoint; + + while (count-- && rl_point > 0) + { + int prev_is_ident, cur_is_ident; + + /* If we are at the start of a word, move back to whitespace + so we will go back to the start of the previous word. */ + if (!whitespace (rl_line_buffer[rl_point]) && + whitespace (rl_line_buffer[rl_point - 1])) + if (--rl_point == 0) + break; + + /* If this character and the previous character are `opposite', move + back so we don't get messed up by the rl_point++ down there in + the while loop. Without this code, words like `l;' screw up the + function. */ + cur_is_ident = _rl_isident (rl_line_buffer[rl_point]); + opoint = _rl_vi_backup_point (); + prev_is_ident = _rl_isident (rl_line_buffer[rl_point]); + if ((cur_is_ident && !prev_is_ident) || (!cur_is_ident && prev_is_ident)) + ; /* leave point alone, we backed it up one character */ + else + rl_point = opoint; + + while (rl_point > 0 && whitespace (rl_line_buffer[rl_point])) + _rl_vi_backup_point (); + + if (rl_point > 0) + { + opoint = rl_point; + if (_rl_isident (rl_line_buffer[rl_point])) + do + opoint = _rl_vi_backup_point (); + while (rl_point > 0 && _rl_isident (rl_line_buffer[rl_point])); + else + do + opoint = _rl_vi_backup_point (); + while (rl_point > 0 && !_rl_isident (rl_line_buffer[rl_point]) && + !whitespace (rl_line_buffer[rl_point])); + + if (rl_point > 0) + rl_point = opoint; + + if (rl_point < 0) + rl_point = 0; + } + } + return (0); +} + +int +rl_vi_eword (int count, int ignore) +{ + int opoint; + + while (count-- && rl_point < (rl_end - 1)) + { + if (whitespace (rl_line_buffer[rl_point]) == 0) + _rl_vi_advance_point (); + + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + _rl_vi_advance_point (); + + opoint = rl_point; + if (rl_point < rl_end) + { + if (_rl_isident (rl_line_buffer[rl_point])) + do + { + opoint = _rl_vi_advance_point (); + } + while (rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point])); + else + do + { + opoint = _rl_vi_advance_point (); + } + while (rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point]) + && !whitespace (rl_line_buffer[rl_point])); + } + rl_point = opoint; + } + return (0); +} + +int +rl_vi_insert_beg (int count, int key) +{ + rl_beg_of_line (1, key); + rl_vi_insert_mode (1, key); + return (0); +} + +static void +_rl_vi_append_forward (int key) +{ + _rl_vi_advance_point (); +} + +int +rl_vi_append_mode (int count, int key) +{ + _rl_vi_append_forward (key); + rl_vi_start_inserting (key, 1, rl_arg_sign); + return (0); +} + +int +rl_vi_append_eol (int count, int key) +{ + rl_end_of_line (1, key); + rl_vi_append_mode (1, key); + return (0); +} + +/* What to do in the case of C-d. */ +int +rl_vi_eof_maybe (int count, int c) +{ + return (rl_newline (1, '\n')); +} + +/* Insertion mode stuff. */ + +/* Switching from one mode to the other really just involves + switching keymaps. */ +int +rl_vi_insertion_mode (int count, int key) +{ + _rl_keymap = vi_insertion_keymap; + _rl_vi_last_key_before_insert = key; + if (_rl_show_mode_in_prompt) + _rl_reset_prompt (); + return (0); +} + +int +rl_vi_insert_mode (int count, int key) +{ + rl_vi_start_inserting (key, 1, rl_arg_sign); + return (0); +} + +static void +vi_save_insert_buffer (int start, int len) +{ + /* Same code as _rl_vi_save_insert below */ + if (len >= vi_insert_buffer_size) + { + vi_insert_buffer_size += (len + 32) - (len % 32); + vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size); + } + strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1); + vi_insert_buffer[len-1] = '\0'; +} + +static void +_rl_vi_save_replace (void) +{ + int len, start, end; + UNDO_LIST *up; + + up = rl_undo_list; + if (up == 0 || up->what != UNDO_END || vi_replace_count <= 0) + { + if (vi_insert_buffer_size >= 1) + vi_insert_buffer[0] = '\0'; + return; + } + /* Let's try it the quick and easy way for now. This should essentially + accommodate every UNDO_INSERT and save the inserted text to + vi_insert_buffer */ + end = rl_point; + start = end - vi_replace_count + 1; + len = vi_replace_count + 1; + + if (start < 0) + { + len = end + 1; + start = 0; + } + + vi_save_insert_buffer (start, len); +} + +static void +_rl_vi_save_insert (UNDO_LIST *up) +{ + int len, start, end; + + if (up == 0 || up->what != UNDO_INSERT) + { + if (vi_insert_buffer_size >= 1) + vi_insert_buffer[0] = '\0'; + return; + } + + start = up->start; + end = up->end; + len = end - start + 1; + + vi_save_insert_buffer (start, len); +} + +void +_rl_vi_done_inserting (void) +{ + if (_rl_vi_doing_insert) + { + /* The `c', `s', `S', and `R' commands set this. */ + rl_end_undo_group (); /* for the group in rl_vi_start_inserting */ + /* Now, the text between rl_undo_list->next->start and + rl_undo_list->next->end is what was inserted while in insert + mode. It gets copied to VI_INSERT_BUFFER because it depends + on absolute indices into the line which may change (though they + probably will not). */ + _rl_vi_doing_insert = 0; + if (_rl_vi_last_key_before_insert == 'R') + _rl_vi_save_replace (); /* Half the battle */ + else + _rl_vi_save_insert (rl_undo_list->next); + /* sanity check, should always be >= 1 here */ + if (_rl_undo_group_level > 0) + rl_end_undo_group (); /* for the group in the command (change or replace) */ + } + else + { + if (rl_undo_list && (_rl_vi_last_key_before_insert == 'i' || + _rl_vi_last_key_before_insert == 'a' || + _rl_vi_last_key_before_insert == 'I' || + _rl_vi_last_key_before_insert == 'A')) + _rl_vi_save_insert (rl_undo_list); + /* XXX - Other keys probably need to be checked. */ + else if (_rl_vi_last_key_before_insert == 'C') + rl_end_undo_group (); + } + + /* Sanity check, make sure all the undo groups are closed before we leave + insert mode */ + while (_rl_undo_group_level > 0) + rl_end_undo_group (); +} + +int +rl_vi_movement_mode (int count, int key) +{ + if (rl_point > 0) + rl_backward_char (1, key); + + _rl_keymap = vi_movement_keymap; + _rl_vi_done_inserting (); + + /* This is how POSIX.2 says `U' should behave -- everything up until the + first time you go into command mode should not be undone. */ + if (RL_ISSTATE (RL_STATE_VICMDONCE) == 0) + rl_free_undo_list (); + + if (_rl_show_mode_in_prompt) + _rl_reset_prompt (); + + RL_SETSTATE (RL_STATE_VICMDONCE); + return (0); +} + +int +rl_vi_arg_digit (int count, int c) +{ + if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg) + return (rl_beg_of_line (1, c)); + else + 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 (int count) +{ + WCHAR_T wc; + char mb[MB_LEN_MAX+1]; + int mlen, p; + size_t m; + 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) + { + m = MBRTOWC (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps); + if (MB_INVALIDCH (m)) + wc = (WCHAR_T)rl_line_buffer[rl_point]; + else if (MB_NULLWCH (m)) + wc = L'\0'; + 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) + { + p = rl_point; + mlen = WCRTOMB (mb, wc, &ps); + if (mlen >= 0) + mb[mlen] = '\0'; + rl_begin_undo_group (); + rl_vi_delete (1, 0); + if (rl_point < p) /* Did we retreat at EOL? */ + _rl_vi_advance_point (); + 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 (int count, int ignore) +{ + int c, p; + + /* Don't try this on an empty line. */ + if (rl_point >= rl_end) + return (0); + + c = 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])) + c = _rl_to_lower (rl_line_buffer[rl_point]); + else if (_rl_lowercase_p (rl_line_buffer[rl_point])) + c = _rl_to_upper (rl_line_buffer[rl_point]); + else + { + /* Just skip over characters neither upper nor lower case. */ + rl_forward_char (1, c); + continue; + } + + /* Vi is kind of strange here. */ + if (c) + { + p = rl_point; + rl_begin_undo_group (); + rl_vi_delete (1, c); + if (rl_point < p) /* Did we retreat at EOL? */ + rl_point++; + _rl_insert_char (1, c); + rl_end_undo_group (); + rl_vi_check (); + } + else + rl_forward_char (1, c); + } + return (0); +} + +int +rl_vi_put (int count, int key) +{ + if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end)) + rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); + + while (count--) + rl_yank (1, key); + + rl_backward_char (1, key); + return (0); +} + +/* Move the cursor back one character if you're at the end of the line */ +int +rl_vi_check (void) +{ + if (rl_point && rl_point == rl_end) + _rl_vi_backup (); + return (0); +} + +/* Move to the character position specified by COUNT */ +int +rl_vi_column (int count, int key) +{ + if (count > rl_end) + rl_end_of_line (1, key); + else + { + rl_point = 0; + rl_point = _rl_forward_char_internal (count - 1); + } + return (0); +} + +/* Process C as part of the current numeric argument. Return -1 if the + argument should be aborted, 0 if we should not read any more chars, and + 1 if we should continue to read chars. */ +static int +_rl_vi_arg_dispatch (int c) +{ + int key; + + key = c; + if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument) + { + rl_numeric_arg *= 4; + return 1; + } + + c = UNMETA (c); + + if (_rl_digit_p (c)) + { + if (rl_explicit_arg) + rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c); + else + rl_numeric_arg = _rl_digit_value (c); + rl_explicit_arg = 1; + return 1; /* keep going */ + } + else + { + rl_clear_message (); + rl_stuff_char (key); + return 0; /* done */ + } +} + +/* A simplified loop for vi. Don't dispatch key at end. + Don't recognize minus sign? + Should this do rl_save_prompt/rl_restore_prompt? */ +static int +rl_digit_loop1 (void) +{ + int c, r; + + while (1) + { + if (_rl_arg_overflow ()) + return 1; + + c = _rl_arg_getchar (); + + r = _rl_vi_arg_dispatch (c); + if (r <= 0) + break; + } + + RL_UNSETSTATE(RL_STATE_NUMERICARG); + return (0); +} + +/* This set of functions is basically to handle the commands that take a + motion argument while in callback mode: read the command, read the motion + command modifier, find the extent of the text to affect, and dispatch the + command for execution. */ +static void +_rl_mvcxt_init (_rl_vimotion_cxt *m, int op, int key) +{ + m->op = op; + m->state = m->flags = 0; + m->ncxt = 0; + m->numeric_arg = -1; + m->start = rl_point; + m->end = rl_end; + m->key = key; + m->motion = -1; +} + +static _rl_vimotion_cxt * +_rl_mvcxt_alloc (int op, int key) +{ + _rl_vimotion_cxt *m; + + m = xmalloc (sizeof (_rl_vimotion_cxt)); + _rl_mvcxt_init (m, op, key); + return m; +} + +static void +_rl_mvcxt_dispose (_rl_vimotion_cxt *m) +{ + xfree (m); +} + +static int +rl_domove_motion_callback (_rl_vimotion_cxt *m) +{ + int c; + + _rl_vi_last_motion = c = m->motion; + + /* Append a blank character temporarily so that the motion routines + work right at the end of the line. Original value of rl_end is saved + as m->end. */ + rl_extend_line_buffer (rl_end + 1); + rl_line_buffer[rl_end++] = ' '; + rl_line_buffer[rl_end] = '\0'; + + _rl_dispatch (c, _rl_keymap); + +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + /* Messy case where char search can be vi motion command; see rest of + details in callback.c. vi_char_search and callback_char_search just + set and unset the CHARSEARCH state. This is where any vi motion + command that needs to set its own state should be handled, with any + corresponding code to manage that state in callback.c */ + if (RL_ISSTATE (RL_STATE_CHARSEARCH)) + return 0; + else + return (_rl_vi_domove_motion_cleanup (c, m)); + } +#endif + + return (_rl_vi_domove_motion_cleanup (c, m)); +} + +int +_rl_vi_domove_motion_cleanup (int c, _rl_vimotion_cxt *m) +{ + int r; + + /* Remove the blank that we added in rl_domove_motion_callback. */ + rl_end = m->end; + rl_line_buffer[rl_end] = '\0'; + _rl_fix_point (0); + + /* No change in position means the command failed. */ + if (rl_mark == rl_point) + { + /* 'c' and 'C' enter insert mode after the delete even if the motion + didn't delete anything, as long as the motion command is valid. */ + if (_rl_to_upper (m->key) == 'C' && _rl_vi_motion_command (c)) + return (vidomove_dispatch (m)); + RL_UNSETSTATE (RL_STATE_VIMOTION); + return (-1); + } + + /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next + word. If we are not at the end of the line, and we are on a + non-whitespace character, move back one (presumably to whitespace). */ + if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark && + !whitespace (rl_line_buffer[rl_point])) + rl_point--; /* XXX */ + + /* If cw or cW, back up to the end of a word, so the behaviour of ce + or cE is the actual result. Brute-force, no subtlety. */ + if (m->key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W')) + { + /* Don't move farther back than where we started. */ + while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point])) + rl_point--; + + /* Posix.2 says that if cw or cW moves the cursor towards the end of + the line, the character under the cursor should be deleted. */ + if (rl_point == rl_mark) + _rl_vi_advance_point (); + else + { + /* Move past the end of the word so that the kill doesn't + remove the last letter of the previous word. Only do this + if we are not at the end of the line. */ + if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point])) + _rl_vi_advance_point (); + } + } + + if (rl_mark < rl_point) + SWAP (rl_point, rl_mark); + +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + (*rl_redisplay_function)(); /* make sure motion is displayed */ +#endif + + r = vidomove_dispatch (m); + + return (r); +} + +#define RL_VIMOVENUMARG() (RL_ISSTATE (RL_STATE_VIMOTION) && RL_ISSTATE (RL_STATE_NUMERICARG)) + +static int +rl_domove_read_callback (_rl_vimotion_cxt *m) +{ + int c, save; + + c = m->motion; + + if (member (c, vi_motion)) + { +#if defined (READLINE_CALLBACKS) + /* If we just read a vi-mode motion command numeric argument, turn off + the `reading numeric arg' state */ + if (RL_ISSTATE (RL_STATE_CALLBACK) && RL_VIMOVENUMARG()) + RL_UNSETSTATE (RL_STATE_NUMERICARG); +#endif + /* Should do everything, including turning off RL_STATE_VIMOTION */ + return (rl_domove_motion_callback (m)); + } + else if (m->key == c && (m->key == 'd' || m->key == 'y' || m->key == 'c')) + { + rl_mark = rl_end; + rl_beg_of_line (1, c); + _rl_vi_last_motion = c; + RL_UNSETSTATE (RL_STATE_VIMOTION); + return (vidomove_dispatch (m)); + } +#if defined (READLINE_CALLBACKS) + /* XXX - these need to handle rl_universal_argument bindings */ + /* Reading vi motion char continuing numeric argument */ + else if (_rl_digit_p (c) && RL_ISSTATE (RL_STATE_CALLBACK) && RL_VIMOVENUMARG()) + { + return (_rl_vi_arg_dispatch (c)); + } + /* Readine vi motion char starting numeric argument */ + else if (_rl_digit_p (c) && RL_ISSTATE (RL_STATE_CALLBACK) && RL_ISSTATE (RL_STATE_VIMOTION) && (RL_ISSTATE (RL_STATE_NUMERICARG) == 0)) + { + RL_SETSTATE (RL_STATE_NUMERICARG); + return (_rl_vi_arg_dispatch (c)); + } +#endif + else if (_rl_digit_p (c)) + { + /* This code path taken when not in callback mode */ + save = rl_numeric_arg; + rl_numeric_arg = _rl_digit_value (c); + rl_explicit_arg = 1; + RL_SETSTATE (RL_STATE_NUMERICARG); + rl_digit_loop1 (); + rl_numeric_arg *= save; + c = rl_vi_domove_getchar (m); + if (c < 0) + { + m->motion = 0; + return -1; + } + m->motion = c; + return (rl_domove_motion_callback (m)); + } + else + { + RL_UNSETSTATE (RL_STATE_VIMOTION); + RL_UNSETSTATE (RL_STATE_NUMERICARG); + return (1); + } +} + +static int +rl_vi_domove_getchar (_rl_vimotion_cxt *m) +{ + return (_rl_bracketed_read_key ()); +} + +#if defined (READLINE_CALLBACKS) +int +_rl_vi_domove_callback (_rl_vimotion_cxt *m) +{ + int c, r; + + m->motion = c = rl_vi_domove_getchar (m); + if (c < 0) + return 1; /* EOF */ + r = rl_domove_read_callback (m); + + return ((r == 0) ? r : 1); /* normalize return values */ +} +#endif + +/* This code path is taken when not in callback mode. */ +int +rl_vi_domove (int x, int *ignore) +{ + int r; + _rl_vimotion_cxt *m; + + m = _rl_vimvcxt; + *ignore = m->motion = rl_vi_domove_getchar (m); + + if (m->motion < 0) + { + m->motion = 0; + return -1; + } + + return (rl_domove_read_callback (m)); +} + +static int +vi_delete_dispatch (_rl_vimotion_cxt *m) +{ + /* These are the motion commands that do not require adjusting the + mark. */ + if (((strchr (" l|h^0bBFT`", m->motion) == 0) && (rl_point >= m->start)) && + (rl_mark < rl_end)) + INCREMENT_POS (rl_mark); + + rl_kill_text (rl_point, rl_mark); + return (0); +} + +int +rl_vi_delete_to (int count, int key) +{ + int c, r; + _rl_vimotion_cxt *savecxt; + + savecxt = 0; + if (_rl_vi_redoing) + { + savecxt = _rl_vimvcxt; + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_DELETE, key); + } + else if (_rl_vimvcxt) + _rl_mvcxt_init (_rl_vimvcxt, VIM_DELETE, key); + else + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_DELETE, key); + + _rl_vimvcxt->start = rl_point; + + rl_mark = rl_point; + if (_rl_uppercase_p (key)) + { + _rl_vimvcxt->motion = '$'; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing && _rl_vi_last_motion != 'd') /* `dd' is special */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing) /* handle redoing `dd' here */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + rl_mark = rl_end; + rl_beg_of_line (1, key); + RL_UNSETSTATE (RL_STATE_VIMOTION); + r = vidomove_dispatch (_rl_vimvcxt); + } +#if defined (READLINE_CALLBACKS) + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + RL_SETSTATE (RL_STATE_VIMOTION); + return (0); + } +#endif + else + r = rl_vi_domove (key, &c); + + if (r < 0) + { + rl_ding (); + r = -1; + } + + _rl_mvcxt_dispose (_rl_vimvcxt); + _rl_vimvcxt = savecxt; + + return r; +} + +static int +vi_change_dispatch (_rl_vimotion_cxt *m) +{ + /* These are the motion commands that do not require adjusting the + mark. c[wW] are handled by special-case code in rl_vi_domove(), + and already leave the mark at the correct location. */ + if (((strchr (" l|hwW^0bBFT`", m->motion) == 0) && (rl_point >= m->start)) && + (rl_mark < rl_end)) + INCREMENT_POS (rl_mark); + + /* The cursor never moves with c[wW]. */ + if ((_rl_to_upper (m->motion) == 'W') && rl_point < m->start) + rl_point = m->start; + + if (_rl_vi_redoing) + { + if (vi_insert_buffer && *vi_insert_buffer) + rl_begin_undo_group (); + rl_delete_text (rl_point, rl_mark); + if (vi_insert_buffer && *vi_insert_buffer) + { + rl_insert_text (vi_insert_buffer); + rl_end_undo_group (); + } + } + else + { + rl_begin_undo_group (); /* to make the `u' command work */ + rl_kill_text (rl_point, rl_mark); + /* `C' does not save the text inserted for undoing or redoing. */ + if (_rl_uppercase_p (m->key) == 0) + _rl_vi_doing_insert = 1; + /* XXX -- TODO -- use m->numericarg? */ + rl_vi_start_inserting (m->key, rl_numeric_arg, rl_arg_sign); + } + + return (0); +} + +int +rl_vi_change_to (int count, int key) +{ + int c, r; + _rl_vimotion_cxt *savecxt; + + savecxt = 0; + if (_rl_vi_redoing) + { + savecxt = _rl_vimvcxt; + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_CHANGE, key); + } + else if (_rl_vimvcxt) + _rl_mvcxt_init (_rl_vimvcxt, VIM_CHANGE, key); + else + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_CHANGE, key); + _rl_vimvcxt->start = rl_point; + + rl_mark = rl_point; + if (_rl_uppercase_p (key)) + { + _rl_vimvcxt->motion = '$'; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing && _rl_vi_last_motion != 'c') /* `cc' is special */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing) /* handle redoing `cc' here */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + rl_mark = rl_end; + rl_beg_of_line (1, key); + RL_UNSETSTATE (RL_STATE_VIMOTION); + r = vidomove_dispatch (_rl_vimvcxt); + } +#if defined (READLINE_CALLBACKS) + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + RL_SETSTATE (RL_STATE_VIMOTION); + return (0); + } +#endif + else + r = rl_vi_domove (key, &c); + + if (r < 0) + { + rl_ding (); + r = -1; /* normalize return value */ + } + + _rl_mvcxt_dispose (_rl_vimvcxt); + _rl_vimvcxt = savecxt; + + return r; +} + +static int +vi_yank_dispatch (_rl_vimotion_cxt *m) +{ + /* These are the motion commands that do not require adjusting the + mark. */ + if (((strchr (" l|h^0%bBFT`", m->motion) == 0) && (rl_point >= m->start)) && + (rl_mark < rl_end)) + INCREMENT_POS (rl_mark); + + rl_begin_undo_group (); + rl_kill_text (rl_point, rl_mark); + rl_end_undo_group (); + rl_do_undo (); + rl_point = m->start; + + _rl_fix_point (1); + + return (0); +} + +int +rl_vi_yank_to (int count, int key) +{ + int c, r; + _rl_vimotion_cxt *savecxt; + + savecxt = 0; + if (_rl_vi_redoing) + { + savecxt = _rl_vimvcxt; + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_YANK, key); + } + else if (_rl_vimvcxt) + _rl_mvcxt_init (_rl_vimvcxt, VIM_YANK, key); + else + _rl_vimvcxt = _rl_mvcxt_alloc (VIM_YANK, key); + _rl_vimvcxt->start = rl_point; + + rl_mark = rl_point; + if (_rl_uppercase_p (key)) + { + _rl_vimvcxt->motion = '$'; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing && _rl_vi_last_motion != 'y') /* `yy' is special */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + r = rl_domove_motion_callback (_rl_vimvcxt); + } + else if (_rl_vi_redoing) /* handle redoing `yy' here */ + { + _rl_vimvcxt->motion = _rl_vi_last_motion; + rl_mark = rl_end; + rl_beg_of_line (1, key); + RL_UNSETSTATE (RL_STATE_VIMOTION); + r = vidomove_dispatch (_rl_vimvcxt); + } +#if defined (READLINE_CALLBACKS) + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + RL_SETSTATE (RL_STATE_VIMOTION); + return (0); + } +#endif + else + r = rl_vi_domove (key, &c); + + if (r < 0) + { + rl_ding (); + r = -1; + } + + _rl_mvcxt_dispose (_rl_vimvcxt); + _rl_vimvcxt = savecxt; + + return r; +} + +static int +vidomove_dispatch (_rl_vimotion_cxt *m) +{ + int r; + + switch (m->op) + { + case VIM_DELETE: + r = vi_delete_dispatch (m); + break; + case VIM_CHANGE: + r = vi_change_dispatch (m); + break; + case VIM_YANK: + r = vi_yank_dispatch (m); + break; + default: + _rl_errmsg ("vidomove_dispatch: unknown operator %d", m->op); + r = 1; + break; + } + + RL_UNSETSTATE (RL_STATE_VIMOTION); + return r; +} + +int +rl_vi_rubout (int count, int key) +{ + int opoint; + + if (count < 0) + return (rl_vi_delete (-count, key)); + + if (rl_point == 0) + { + rl_ding (); + return 1; + } + + opoint = rl_point; + if (count > 1 && MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_backward_char (count, key); + else 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 -= count; + + if (rl_point < 0) + rl_point = 0; + + rl_kill_text (rl_point, opoint); + + return (0); +} + +int +rl_vi_delete (int count, int key) +{ + int end; + + if (count < 0) + return (rl_vi_rubout (-count, key)); + + if (rl_end == 0) + { + rl_ding (); + return 1; + } + + 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; + + rl_kill_text (rl_point, end); + + if (rl_point > 0 && rl_point == rl_end) + rl_backward_char (1, key); + + return (0); +} + +/* This does what Posix specifies vi-mode C-w to do: using whitespace and + punctuation characters as the word boundaries. */ + +#define vi_unix_word_boundary(c) (whitespace(c) || ispunct(c)) + +int +rl_vi_unix_word_rubout (int count, int key) +{ + int orig_point; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + /* This isn't quite what ksh93 does but it seems to match what the + Posix description of sh specifies, with a few accommodations + for sequences of whitespace characters between words and at + the end of the line. */ + + /* Skip over whitespace at the end of the line as a special case */ + if (rl_point > 0 && (rl_line_buffer[rl_point] == 0) && + whitespace (rl_line_buffer[rl_point - 1])) + while (--rl_point > 0 && whitespace (rl_line_buffer[rl_point])) + ; + + /* If we're at the start of a word, move back to word boundary so we + move back to the `preceding' word */ + if (rl_point > 0 && (vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0) && + vi_unix_word_boundary (rl_line_buffer[rl_point - 1])) + rl_point--; + + /* If we are at a word boundary (whitespace/punct), move backward + past a sequence of word boundary characters. If we are at the + end of a word (non-word boundary), move back to a word boundary */ + if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point])) + while (rl_point && vi_unix_word_boundary (rl_line_buffer[rl_point - 1])) + rl_point--; + else if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0) + while (rl_point > 0 && (vi_unix_word_boundary (rl_line_buffer[rl_point - 1]) == 0)) + _rl_vi_backup_point (); + } + + rl_kill_text (orig_point, rl_point); + } + + return 0; +} + + +int +rl_vi_back_to_indent (int count, int key) +{ + rl_beg_of_line (1, key); + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + rl_point++; + return (0); +} + +int +rl_vi_first_print (int count, int key) +{ + return (rl_vi_back_to_indent (1, key)); +} + +static int _rl_cs_dir, _rl_cs_orig_dir; + +#if defined (READLINE_CALLBACKS) +static int +_rl_vi_callback_char_search (_rl_callback_generic_arg *data) +{ + int c; +#if defined (HANDLE_MULTIBYTE) + c = _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX); +#else + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); +#endif + + if (c <= 0) + { + RL_UNSETSTATE (RL_STATE_CHARSEARCH); + return -1; + } + +#if !defined (HANDLE_MULTIBYTE) + _rl_vi_last_search_char = c; +#endif + + _rl_callback_func = 0; + _rl_want_redisplay = 1; + RL_UNSETSTATE (RL_STATE_CHARSEARCH); + +#if defined (HANDLE_MULTIBYTE) + return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen)); +#else + return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_char)); +#endif +} +#endif + +int +rl_vi_char_search (int count, int key) +{ + int c; +#if defined (HANDLE_MULTIBYTE) + static char *target; + static int tlen; +#else + static char target; +#endif + + if (key == ';' || key == ',') + { + if (_rl_cs_orig_dir == 0) + return 1; +#if defined (HANDLE_MULTIBYTE) + if (_rl_vi_last_search_mblen == 0) + return 1; +#else + if (_rl_vi_last_search_char == 0) + return 1; +#endif + _rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir; + } + else + { + switch (key) + { + case 't': + _rl_cs_orig_dir = _rl_cs_dir = FTO; + break; + + case 'T': + _rl_cs_orig_dir = _rl_cs_dir = BTO; + break; + + case 'f': + _rl_cs_orig_dir = _rl_cs_dir = FFIND; + break; + + case 'F': + _rl_cs_orig_dir = _rl_cs_dir = BFIND; + break; + } + + if (_rl_vi_redoing) + { + /* set target and tlen below */ + } +#if defined (READLINE_CALLBACKS) + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = _rl_callback_data_alloc (count); + _rl_callback_data->i1 = _rl_cs_dir; + _rl_callback_data->i2 = key; + _rl_callback_func = _rl_vi_callback_char_search; + RL_SETSTATE (RL_STATE_CHARSEARCH); + return (0); + } +#endif + else + { +#if defined (HANDLE_MULTIBYTE) + c = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX); + if (c <= 0) + return -1; + _rl_vi_last_search_mblen = c; +#else + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + if (c < 0) + return -1; + _rl_vi_last_search_char = c; +#endif + } + } + +#if defined (HANDLE_MULTIBYTE) + target = _rl_vi_last_search_mbchar; + tlen = _rl_vi_last_search_mblen; +#else + target = _rl_vi_last_search_char; +#endif + +#if defined (HANDLE_MULTIBYTE) + return (_rl_char_search_internal (count, _rl_cs_dir, target, tlen)); +#else + return (_rl_char_search_internal (count, _rl_cs_dir, target)); +#endif +} + +/* Match brackets */ +int +rl_vi_match (int ignore, int key) +{ + int count = 1, brack, pos, tmp, pre; + + pos = rl_point; + if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0) + { + 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) + { + rl_point = pos; + rl_ding (); + return 1; + } + } + + pos = rl_point; + + if (brack < 0) + { + while (count) + { + 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) + count--; + else if (b == brack) + count++; + } + else + { + rl_ding (); + return 1; + } + } + } + else + { /* brack > 0 */ + while (count) + { + 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) + count--; + else if (b == brack) + count++; + } + else + { + rl_ding (); + return 1; + } + } + } + rl_point = pos; + return (0); +} + +int +rl_vi_bracktype (int c) +{ + switch (c) + { + case '(': return 1; + case ')': return -1; + case '[': return 2; + case ']': return -2; + case '{': return 3; + case '}': return -3; + default: return 0; + } +} + +static int +_rl_vi_change_char (int count, int c, char *mb) +{ + int p; + + if (c == '\033' || c == CTRL ('C')) + return -1; + + rl_begin_undo_group (); + while (count-- && rl_point < rl_end) + { + p = rl_point; + rl_vi_delete (1, c); + if (rl_point < p) /* Did we retreat at EOL? */ + _rl_vi_append_forward (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); + } + + /* The cursor shall be left on the last character changed. */ + rl_backward_char (1, c); + + rl_end_undo_group (); + + return (0); +} + +static int +_rl_vi_callback_getchar (char *mb, int mlen) +{ + return (_rl_bracketed_read_mbstring (mb, mlen)); +} + +#if defined (READLINE_CALLBACKS) +static int +_rl_vi_callback_change_char (_rl_callback_generic_arg *data) +{ + int c; + char mb[MB_LEN_MAX+1]; + + c = _rl_vi_callback_getchar (mb, MB_LEN_MAX); + if (c < 0) + return -1; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + strncpy (_rl_vi_last_replacement, mb, MB_LEN_MAX); + else +#endif + _rl_vi_last_replacement[0] = c; + _rl_vi_last_replacement[MB_LEN_MAX] = '\0'; /* XXX */ + + _rl_callback_func = 0; + _rl_want_redisplay = 1; + + return (_rl_vi_change_char (data->count, c, mb)); +} +#endif + +int +rl_vi_change_char (int count, int key) +{ + int c; + char mb[MB_LEN_MAX+1]; + + if (_rl_vi_redoing) + { + strncpy (mb, _rl_vi_last_replacement, MB_LEN_MAX); + c = (unsigned char)_rl_vi_last_replacement[0]; /* XXX */ + mb[MB_LEN_MAX] = '\0'; + } +#if defined (READLINE_CALLBACKS) + else if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = _rl_callback_data_alloc (count); + _rl_callback_func = _rl_vi_callback_change_char; + return (0); + } +#endif + else + { + c = _rl_vi_callback_getchar (mb, MB_LEN_MAX); + if (c < 0) + return -1; +#ifdef HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + strncpy (_rl_vi_last_replacement, mb, MB_LEN_MAX); + else +#endif + _rl_vi_last_replacement[0] = c; + _rl_vi_last_replacement[MB_LEN_MAX] = '\0'; /* just in case */ + } + + if (c < 0) + return -1; + + return (_rl_vi_change_char (count, c, mb)); +} + +int +rl_vi_subst (int count, int key) +{ + /* If we are redoing, rl_vi_change_to will stuff the last motion char */ + if (_rl_vi_redoing == 0) + rl_stuff_char ((key == 'S') ? 'c' : 'l'); /* `S' == `cc', `s' == `cl' */ + + return (rl_vi_change_to (count, 'c')); +} + +int +rl_vi_overstrike (int count, int key) +{ + if (_rl_vi_doing_insert == 0) + { + _rl_vi_doing_insert = 1; + rl_begin_undo_group (); + } + + if (count > 0) + { + if (_rl_overwrite_char (count, key) != 0) + return (1); + vi_replace_count += count; + } + + return (0); +} + +int +rl_vi_overstrike_delete (int count, int key) +{ + int i, s; + + for (i = 0; i < count; i++) + { + if (vi_replace_count == 0) + { + rl_ding (); + break; + } + s = rl_point; + + if (rl_do_undo ()) + vi_replace_count--; /* XXX */ + + if (rl_point == s) + rl_backward_char (1, key); + } + + if (vi_replace_count == 0 && _rl_vi_doing_insert) + { + rl_end_undo_group (); + rl_do_undo (); + _rl_vi_doing_insert = 0; + } + return (0); +} + +static int +rl_vi_overstrike_kill_line (int count, int key) +{ + int r, end; + + end = rl_end; + r = rl_unix_line_discard (count, key); + vi_replace_count -= end - rl_end; + return r; +} + +static int +rl_vi_overstrike_kill_word (int count, int key) +{ + int r, end; + + end = rl_end; + r = rl_vi_unix_word_rubout (count, key); + vi_replace_count -= end - rl_end; + return r; +} + +static int +rl_vi_overstrike_yank (int count, int key) +{ + int r, end; + + end = rl_end; + r = rl_yank (count, key); + vi_replace_count += rl_end - end; + return r; +} + +/* Read bracketed paste mode pasted text and insert it in overwrite mode */ +static int +rl_vi_overstrike_bracketed_paste (int count, int key) +{ + int r; + char *pbuf; + size_t pblen; + + pbuf = _rl_bracketed_text (&pblen); + if (pblen == 0) + { + xfree (pbuf); + return 0; + } + r = pblen; + while (--r >= 0) + _rl_unget_char ((unsigned char)pbuf[r]); + xfree (pbuf); + + while (_rl_pushed_input_available ()) + { + key = rl_read_key (); + r = rl_vi_overstrike (1, key); + } + + return r; +} + +int +rl_vi_replace (int count, int key) +{ + int i; + + vi_replace_count = 0; + + if (vi_replace_map == 0) + { + vi_replace_map = rl_make_bare_keymap (); + + for (i = 0; i < ' '; i++) + if (vi_insertion_keymap[i].type == ISFUNC) + vi_replace_map[i].function = vi_insertion_keymap[i].function; + + for (i = ' '; i < KEYMAP_SIZE; i++) + vi_replace_map[i].function = rl_vi_overstrike; + + vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete; + + /* Make sure these are what we want. */ + vi_replace_map[ESC].function = rl_vi_movement_mode; + vi_replace_map[RETURN].function = rl_newline; + vi_replace_map[NEWLINE].function = rl_newline; + + /* If the normal vi insertion keymap has ^H bound to erase, do the + same here. Probably should remove the assignment to RUBOUT up + there, but I don't think it will make a difference in real life. */ + if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC && + vi_insertion_keymap[CTRL ('H')].function == rl_rubout) + vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete; + + /* Same for ^U and unix-line-discard. */ + if (vi_insertion_keymap[CTRL ('U')].type == ISFUNC && + vi_insertion_keymap[CTRL ('U')].function == rl_unix_line_discard) + vi_replace_map[CTRL ('U')].function = rl_vi_overstrike_kill_line; + + /* And for ^W and unix-word-rubout. */ + if (vi_insertion_keymap[CTRL ('W')].type == ISFUNC && + vi_insertion_keymap[CTRL ('W')].function == rl_vi_unix_word_rubout) + vi_replace_map[CTRL ('W')].function = rl_vi_overstrike_kill_word; + + /* And finally for ^Y and yank. */ + if (vi_insertion_keymap[CTRL ('Y')].type == ISFUNC && + vi_insertion_keymap[CTRL ('Y')].function == rl_yank) + vi_replace_map[CTRL ('Y')].function = rl_vi_overstrike_yank; + + /* Make sure this is the value we need. */ + vi_replace_map[ANYOTHERKEY].type = ISFUNC; + vi_replace_map[ANYOTHERKEY].function = (rl_command_func_t *)NULL; + } + + rl_vi_start_inserting (key, 1, rl_arg_sign); + + _rl_vi_last_key_before_insert = 'R'; /* in case someone rebinds it */ + _rl_keymap = vi_replace_map; + + if (_rl_enable_bracketed_paste) + rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_vi_overstrike_bracketed_paste); + + return (0); +} + +#if 0 +/* Try to complete the word we are standing on or the word that ends with + the previous character. A space matches everything. Word delimiters are + space and ;. */ +int +rl_vi_possible_completions (void) +{ + int save_pos = rl_point; + + if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';') + { + while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' && + rl_line_buffer[rl_point] != ';') + _rl_vi_advance_point (); + } + else if (rl_line_buffer[rl_point - 1] == ';') + { + rl_ding (); + return (0); + } + + rl_possible_completions (); + rl_point = save_pos; + + return (0); +} +#endif + +/* Functions to save and restore marks. */ +static int +_rl_vi_set_mark (void) +{ + int ch; + + RL_SETSTATE(RL_STATE_MOREINPUT); + ch = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (ch < 0 || ch < 'a' || ch > 'z') /* make test against 0 explicit */ + { + rl_ding (); + return 1; + } + ch -= 'a'; + vi_mark_chars[ch] = rl_point; + return 0; +} + +#if defined (READLINE_CALLBACKS) +static int +_rl_vi_callback_set_mark (_rl_callback_generic_arg *data) +{ + _rl_callback_func = 0; + _rl_want_redisplay = 1; + + return (_rl_vi_set_mark ()); +} +#endif + +int +rl_vi_set_mark (int count, int key) +{ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = 0; + _rl_callback_func = _rl_vi_callback_set_mark; + return (0); + } +#endif + + return (_rl_vi_set_mark ()); +} + +static int +_rl_vi_goto_mark (void) +{ + int ch; + + RL_SETSTATE(RL_STATE_MOREINPUT); + ch = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (ch == '`') + { + rl_point = rl_mark; + _rl_fix_point (1); + return 0; + } + else if (ch < 0 || ch < 'a' || ch > 'z') /* make test against 0 explicit */ + { + rl_ding (); + return 1; + } + + ch -= 'a'; + if (vi_mark_chars[ch] == -1) + { + rl_ding (); + return 1; + } + rl_point = vi_mark_chars[ch]; + _rl_fix_point (1); + return 0; +} + +#if defined (READLINE_CALLBACKS) +static int +_rl_vi_callback_goto_mark (_rl_callback_generic_arg *data) +{ + _rl_callback_func = 0; + _rl_want_redisplay = 1; + + return (_rl_vi_goto_mark ()); +} +#endif + +int +rl_vi_goto_mark (int count, int key) +{ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + { + _rl_callback_data = 0; + _rl_callback_func = _rl_vi_callback_goto_mark; + return (0); + } +#endif + + return (_rl_vi_goto_mark ()); +} +#endif /* VI_MODE */ diff --git a/lib/readline/xfree.c b/lib/readline/xfree.c new file mode 100644 index 0000000..c199b29 --- /dev/null +++ b/lib/readline/xfree.c @@ -0,0 +1,49 @@ +/* xfree.c -- safe version of free that ignores attempts to free NUL */ + +/* Copyright (C) 1991-2010,2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "xmalloc.h" + +/* **************************************************************** */ +/* */ +/* Memory Deallocation. */ +/* */ +/* **************************************************************** */ + +/* Use this as the function to call when adding unwind protects so we + don't need to know what free() returns. */ +void +xfree (PTR_T string) +{ + if (string) + free (string); +} diff --git a/lib/readline/xmalloc.c b/lib/readline/xmalloc.c new file mode 100644 index 0000000..5d01d75 --- /dev/null +++ b/lib/readline/xmalloc.c @@ -0,0 +1,75 @@ +/* xmalloc.c -- safe versions of malloc and realloc */ + +/* Copyright (C) 1991-2017 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "xmalloc.h" + +/* **************************************************************** */ +/* */ +/* Memory Allocation and Deallocation. */ +/* */ +/* **************************************************************** */ + +static void +memory_error_and_abort (char *fname) +{ + fprintf (stderr, "%s: out of virtual memory\n", fname); + exit (2); +} + +/* Return a pointer to free()able block of memory large enough + to hold BYTES number of bytes. If the memory cannot be allocated, + print an error message and abort. */ +PTR_T +xmalloc (size_t bytes) +{ + PTR_T temp; + + temp = malloc (bytes); + if (temp == 0) + memory_error_and_abort ("xmalloc"); + return (temp); +} + +PTR_T +xrealloc (PTR_T pointer, size_t bytes) +{ + PTR_T temp; + + temp = pointer ? realloc (pointer, bytes) : malloc (bytes); + + if (temp == 0) + memory_error_and_abort ("xrealloc"); + return (temp); +} diff --git a/lib/readline/xmalloc.h b/lib/readline/xmalloc.h new file mode 100644 index 0000000..0fb9df9 --- /dev/null +++ b/lib/readline/xmalloc.h @@ -0,0 +1,45 @@ +/* xmalloc.h -- memory allocation that aborts on errors. */ + +/* Copyright (C) 1999-2009,2010-2021 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_XMALLOC_H_) +#define _XMALLOC_H_ + +#if defined (READLINE_LIBRARY) +# include "rlstdc.h" +#else +# include <readline/rlstdc.h> +#endif + +#ifndef PTR_T + +#ifdef __STDC__ +# define PTR_T void * +#else +# define PTR_T char * +#endif + +#endif /* !PTR_T */ + +extern PTR_T xmalloc (size_t); +extern PTR_T xrealloc (void *, size_t); +extern void xfree (void *); + +#endif /* _XMALLOC_H_ */ diff --git a/lib/sh/Makefile.in b/lib/sh/Makefile.in new file mode 100644 index 0000000..8c42c73 --- /dev/null +++ b/lib/sh/Makefile.in @@ -0,0 +1,657 @@ +# +# Makefile for the Bash library +# +# +# Copyright (C) 1998-2022 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ + +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +LIBBUILD = ${BUILD_DIR}/lib + +BASHINCDIR = ${topdir}/include + +INTL_LIBSRC = ${topdir}/lib/intl +INTL_BUILDDIR = ${LIBBUILD}/intl +INTL_INC = @INTL_INC@ +LIBINTL_H = @LIBINTL_H@ + +datarootdir = @datarootdir@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ ${DEBUG} +STYLE_CFLAGS = @STYLE_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@ + +PROFILE_FLAGS = @PROFILE_FLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +INCLUDES = -I. -I../.. -I$(topdir) -I$(topdir)/lib -I$(BASHINCDIR) -I$(srcdir) $(INTL_INC) + +CCFLAGS = ${ADDON_CFLAGS} ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) \ + $(LOCAL_CFLAGS) $(STYLE_CFLAGS) $(CFLAGS) $(CPPFLAGS) + +GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \ + -Wcast-align -Wstrict-prototypes -Wconversion \ + -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic + +.c.o: + $(CC) -c $(CCFLAGS) $< + +# The name of the library target. +LIBRARY_NAME = libsh.a + +# The C code source files for this library. +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 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 strcasestr.c snprintf.c mailstat.c \ + fmtulong.c fmtullong.c fmtumax.c shmatch.c strnlen.c \ + strtoll.c strtoull.c strtoimax.c strtoumax.c memset.c strstr.c \ + mktime.c strftime.c mbschr.c zcatfd.c zmapfd.c winsize.c eaccess.c \ + wcsdup.c fpurge.c zgetline.c mbscmp.c uconvert.c ufuncs.c \ + casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \ + strchrnul.c unicode.c wcswidth.c wcsnwidth.c shmbchar.c strdup.c \ + strvis.c utf8.c random.c gettimeofday.c timers.c + +# The header files for this library. +HSOURCES = + +# The object files contained in $(LIBRARY_NAME) +LIBOBJS = @LIBOBJS@ +OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \ + itos.o zread.o zwrite.o shtty.o shmatch.o eaccess.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 snprintf.o mailstat.o fmtulong.o \ + fmtullong.o fmtumax.o zcatfd.o zmapfd.o winsize.o wcsdup.o \ + fpurge.o zgetline.o mbscmp.o uconvert.o ufuncs.o casemod.o \ + input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o strvis.o \ + utf8.o random.o gettimeofday.o timers.o wcsnwidth.o ${LIBOBJS} + +SUPPORT = Makefile + +all: $(LIBRARY_NAME) + +$(LIBRARY_NAME): $(OBJECTS) + $(RM) $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +force: + +# The rule for 'includes' is written funny so that the if statement +# always returns TRUE unless there really was an error installing the +# include files. +install: + +clean: + $(RM) $(OBJECTS) $(LIBRARY_NAME) + +realclean distclean maintainer-clean: clean + $(RM) Makefile + +mostlyclean: clean + +# Dependencies + +${BUILD_DIR}/version.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile + -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} version.h ) + +${BUILD_DIR}/pathnames.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile + -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} pathnames.h ) + +# rules for losing makes, like SunOS +casemod.o: casemod.c +clktck.o: clktck.c +clock.o: clock.c +eaccess.o: eaccess.c +dprintf.o: dprintf.c +fmtullong.o: fmtullong.c +fmtulong.o: fmtulong.c +fmtumax.o: fmtumax.c +fnxform.o: fnxform.c +fpurge.o: fpurge.c +getcwd.o: getcwd.c +getenv.o: getenv.c +gettimeofday.o: gettimeofday.c +inet_aton.o: inet_aton.c +input_avail.o: input_avail.c +itos.o: itos.c +mailstat.o: mailstat.c +makepath.o: makepath.c +mbscasecmp.o: mbscasecmp.c +mbschr.o: mbschr.c +mbscmp.o: mbscmp.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 +pathphys.o: pathphys.c +random.o: random.c +rename.o: rename.c +setlinebuf.o: setlinebuf.c +shmatch.o: shmatch.c +shmbchar.o: shmbchar.c +shquote.o: shquote.c +shtty.o: shtty.c +snprintf.o: snprintf.c +spell.o: spell.c +strcasecmp.o: strcasecmp.c +strchrnul.o: strchrnul.c +strerror.o: strerror.c +strftime.o: strftime.c +strcasestr.o: strcasestr.c +stringlist.o: stringlist.c +stringvec.o: stringvec.c +strnlen.o: strnlen.c +strpbrk.o: strpbrk.c +strtod.o: strtod.c +strtoimax.o: strtoimax.c +strtol.o: strtol.c +strtoll.o: strtoll.c +strtoul.o: strtoul.c +strtoull.o: strtoull.c +strtoumax.o: strtoumax.c +strtrans.o: strtrans.c +strvis.o: strvis.c +timers.o: timers.c +times.o: times.c +timeval.o: timeval.c +tmpfile.o: tmpfile.c +uconvert.o: uconvert.c +ufuncs.o: ufuncs.c +unicode.o: unicode.c +utf8.o: utf8.c +vprint.o: vprint.c +wcsdup.o: wcsdup.c +wcsnwidth.o: wcsnwidth.c +wcswidth.o: wcswidth.c +winsize.o: winsize.c +zcatfd.o: zcatfd.c +zmapfd.o: zmapfd.c +zgetline.o: zgetline.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 + +# all files in the library depend on config.h +casemod.o: ${BUILD_DIR}/config.h +clktck.o: ${BUILD_DIR}/config.h +clock.o: ${BUILD_DIR}/config.h +eaccess.o: ${BUILD_DIR}/config.h +dprintf.o: ${BUILD_DIR}/config.h +fmtullong.o: ${BUILD_DIR}/config.h +fmtulong.o: ${BUILD_DIR}/config.h +fmtumax.o: ${BUILD_DIR}/config.h +fnxform.o: ${BUILD_DIR}/config.h +fpurge.o: ${BUILD_DIR}/config.h +getcwd.o: ${BUILD_DIR}/config.h +getenv.o: ${BUILD_DIR}/config.h +gettimeofday.o: ${BUILD_DIR}/config.h +inet_aton.o: ${BUILD_DIR}/config.h +input_avail.o: ${BUILD_DIR}/config.h +itos.o: ${BUILD_DIR}/config.h +mailstat.o: ${BUILD_DIR}/config.h +makepath.o: ${BUILD_DIR}/config.h +mbscasecmp.o: ${BUILD_DIR}/config.h +mbschr.o: ${BUILD_DIR}/config.h +mbscmp.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 +pathphys.o: ${BUILD_DIR}/config.h +random.o: ${BUILD_DIR}/config.h +rename.o: ${BUILD_DIR}/config.h +setlinebuf.o: ${BUILD_DIR}/config.h +shmatch.o: ${BUILD_DIR}/config.h +shmbchar.o: ${BUILD_DIR}/config.h +shquote.o: ${BUILD_DIR}/config.h +shtty.o: ${BUILD_DIR}/config.h +snprintf.o: ${BUILD_DIR}/config.h +spell.o: ${BUILD_DIR}/config.h +strcasecmp.o: ${BUILD_DIR}/config.h +strchrnul.o: ${BUILD_DIR}/config.h +strerror.o: ${BUILD_DIR}/config.h +strftime.o: ${BUILD_DIR}/config.h +strcasestr.o: ${BUILD_DIR}/config.h +stringlist.o: ${BUILD_DIR}/config.h +stringvec.o: ${BUILD_DIR}/config.h +strnlen.o: ${BUILD_DIR}/config.h +strpbrk.o: ${BUILD_DIR}/config.h +strtod.o: ${BUILD_DIR}/config.h +strtoimax.o: ${BUILD_DIR}/config.h +strtol.o: ${BUILD_DIR}/config.h +strtoll.o: ${BUILD_DIR}/config.h +strtoul.o: ${BUILD_DIR}/config.h +strtoull.o: ${BUILD_DIR}/config.h +strtoumax.o: ${BUILD_DIR}/config.h +strtrans.o: ${BUILD_DIR}/config.h +strvis.o: ${BUILD_DIR}/config.h +timers.o: ${BUILD_DIR}/config.h +times.o: ${BUILD_DIR}/config.h +timeval.o: ${BUILD_DIR}/config.h +tmpfile.o: ${BUILD_DIR}/config.h ${topdir}/config-top.h +uconvert.o: ${BUILD_DIR}/config.h +ufuncs.o: ${BUILD_DIR}/config.h +unicode.o: ${BUILD_DIR}/config.h +utf8.o: ${BUILD_DIR}/config.h +vprint.o: ${BUILD_DIR}/config.h +wcsdup.o: ${BUILD_DIR}/config.h +wcsnwidth.o: ${BUILD_DIR}/config.h +wcswidth.o: ${BUILD_DIR}/config.h +winsize.o: ${BUILD_DIR}/config.h +zcatfd.o: ${BUILD_DIR}/config.h +zgetline.o: ${BUILD_DIR}/config.h +zmapfd.o: ${BUILD_DIR}/config.h +zread.o: ${BUILD_DIR}/config.h +zwrite.o: ${BUILD_DIR}/config.h + +clktck.o: ${topdir}/bashtypes.h + +getcwd.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h +getcwd.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/posixdir.h +getcwd.o: ${BASHINCDIR}/memalloc.h ${BASHINCDIR}/ansi_stdlib.h + +getenv.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +getenv.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +getenv.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +getenv.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +getenv.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +getenv.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +getenv.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +getenv.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#getenv.o: ${BUILD_DIR}/version.h + +inet_aton.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +inet_aton.o: ${BASHINCDIR}/stdc.h + +itos.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +itos.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +itos.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +itos.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +itos.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +itos.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +itos.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +itos.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#itos.o: ${BUILD_DIR}/version.h + +makepath.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +makepath.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +makepath.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +makepath.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +makepath.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +makepath.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +makepath.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +makepath.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#makepath.o: ${BUILD_DIR}/version.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 +netopen.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +netopen.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +netopen.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +netopen.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +netopen.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +netopen.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h +#netopen.o: ${BUILD_DIR}/version.h + +oslib.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h +oslib.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +oslib.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +oslib.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +oslib.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +oslib.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +oslib.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +oslib.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +oslib.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h +oslib.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +#oslib.o: ${BUILD_DIR}/version.h + +pathcanon.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h +pathcanon.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +pathcanon.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +pathcanon.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +pathcanon.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +pathcanon.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +pathcanon.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +pathcanon.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +pathcanon.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h +pathcanon.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +#pathcanon.o: ${BUILD_DIR}/version.h + +pathphys.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/maxpath.h +pathphys.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +pathphys.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +pathphys.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +pathphys.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +pathphys.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +pathphys.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +pathphys.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +pathphys.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/filecntl.h +pathphys.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +#pathphys.o: ${BUILD_DIR}/version.h + +random.o: ${topdir}/bashtypes.h ${BASHINCDIR}/stdc.h +random.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +random.o: ${BASHINCDIR}/filecntl.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 + +eaccess.o: ${topdir}/bashtypes.h +eaccess.o: ${BASHINCDIR}/posixstat.h +eaccess.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +eaccess.o: ${BASHINCDIR}/filecntl.h +eaccess.o: ${BASHINCDIR}/stdc.h +eaccess.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +eaccess.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +eaccess.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +eaccess.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +eaccess.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +eaccess.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +eaccess.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#eaccess.o: ${BUILD_DIR}/version.h + +shmatch.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h +shmatch.o: ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h +shmatch.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +shmatch.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +shmatch.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +shmatch.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +shmatch.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +shmatch.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +shmatch.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h + +shquote.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h +shquote.o: ${BASHINCDIR}/ansi_stdlib.h ${topdir}/xmalloc.h +shquote.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h + +shtty.o: ${BASHINCDIR}/shtty.h +shtty.o: ${BASHINCDIR}/stdc.h + +snprintf.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h ${topdir}/xmalloc.h +snprintf.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +snprintf.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h +snprintf.o: ${BASHINCDIR}/typemax.h + +spell.o: ${topdir}/bashtypes.h +spell.o: ${BASHINCDIR}/posixstat.h ${BASHINCDIR}/posixdir.h +spell.o: ${BASHINCDIR}/ansi_stdlib.h + +strcasecmp.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h +strcasecmp.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h + +strerror.o: ${topdir}/bashtypes.h +strerror.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +strerror.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +strerror.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +strerror.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +strerror.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +strerror.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +strerror.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#strerror.o: ${BUILD_DIR}/version.h + +strcasestr.o: ${BASHINCDIR}/stdc.h ${topdir}/bashansi.h +strcasestr.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h + +stringlist.o: ${topdir}/bashansi.h +stringlist.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +stringlist.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +stringlist.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +stringlist.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +stringlist.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +stringlist.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +stringlist.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#stringlist.o: ${BUILD_DIR}/version.h + +stringvec.o: ${topdir}/bashansi.h ${BASHINCDIR}/chartypes.h +stringvec.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +stringvec.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +stringvec.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +stringvec.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +stringvec.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +stringvec.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +stringvec.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +#stringvec.o: ${BUILD_DIR}/version.h + +strnlen.o: ${BASHINCDIR}/stdc.h + +strpbrk.o: ${BASHINCDIR}/stdc.h + +strtod.o: ${topdir}/bashansi.h +strtod.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h + +strtoimax.o: ${BASHINCDIR}/stdc.h + +strtol.o: ${topdir}/bashansi.h +strtol.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strtol.o: ${BASHINCDIR}/typemax.h + +strtoll.o: ${topdir}/bashansi.h +strtoll.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strtoll.o: ${BASHINCDIR}/typemax.h + +strtoul.o: ${topdir}/bashansi.h +strtoul.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strtoul.o: ${BASHINCDIR}/typemax.h + +strtoull.o: ${topdir}/bashansi.h +strtoull.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strtoull.o: ${BASHINCDIR}/typemax.h + +strtoumax.o: ${BASHINCDIR}/stdc.h + +strtrans.o: ${topdir}/bashansi.h +strtrans.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strtrans.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +strtrans.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +strtrans.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +strtrans.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +strtrans.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +strtrans.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +strtrans.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h +strtrans.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h +#strtrans.o: ${BUILD_DIR}/version.h + +strvis.o: ${topdir}/bashansi.h +strvis.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h +strvis.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h +strvis.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h + +times.o: ${BASHINCDIR}/systimes.h +times.o: ${BASHINCDIR}/posixtime.h + +timeval.o: ${BASHINCDIR}/posixtime.h +gettimeofday.o: ${BASHINCDIR}/posixtime.h + +tmpfile.o: ${topdir}/bashtypes.h +tmpfile.o: ${BASHINCDIR}/chartypes.h +tmpfile.o: ${BASHINCDIR}/posixstat.h +tmpfile.o: ${BASHINCDIR}/filecntl.h +tmpfile.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +tmpfile.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +tmpfile.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +tmpfile.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +tmpfile.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +tmpfile.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +tmpfile.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h + +uconvert.o: ${topdir}/bashtypes.h +uconvert.o: ${BASHINCDIR}/chartypes.h +uconvert.o: ${topdir}/shell.h ${topdir}/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +uconvert.o: ${topdir}/command.h ${BASHINCDIR}/stdc.h ${topdir}/error.h +uconvert.o: ${topdir}/general.h ${topdir}/bashtypes.h ${topdir}/variables.h ${topdir}/conftypes.h +uconvert.o: ${topdir}/array.h ${topdir}/hashlib.h ${topdir}/quit.h +uconvert.o: ${topdir}/unwind_prot.h ${topdir}/dispose_cmd.h +uconvert.o: ${topdir}/make_cmd.h ${topdir}/subst.h ${topdir}/sig.h +uconvert.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h + +ufuncs.o: ${topdir}/bashtypes.h + +clock.o: ${BASHINCDIR}/posixtime.h + +mailstat.o: ${topdir}/bashansi.h +mailstat.o: ${topdir}/bashtypes.h +mailstat.o: ${BASHINCDIR}/ansi_stdlib.h +mailstat.o: ${BASHINCDIR}/posixstat.h +mailstat.o: ${BASHINCDIR}/posixdir.h +mailstat.o: ${BASHINCDIR}/maxpath.h + +fmtulong.o: ${topdir}/bashansi.h +fmtulong.o: ${BASHINCDIR}/ansi_stdlib.h +fmtulong.o: ${BASHINCDIR}/chartypes.h +fmtulong.o: ${BASHINCDIR}/stdc.h +fmtulong.o: ${BASHINCDIR}/typemax.h +fmtulong.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h + +fmtullong.o: ${topdir}/bashansi.h +fmtullong.o: ${BASHINCDIR}/ansi_stdlib.h +fmtullong.o: ${BASHINCDIR}/chartypes.h +fmtullong.o: ${BASHINCDIR}/stdc.h +fmtullong.o: ${BASHINCDIR}/typemax.h +fmtullong.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.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 +fmtumax.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h + +wcsdup.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +wcsdup.o: ${BASHINCDIR}/stdc.h +wcsdup.o: ${topdir}/xmalloc.h + +wcsnwidth.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +wcsnwidth.o: ${BASHINCDIR}/stdc.h + +wcswidth.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +wcswidth.o: ${BASHINCDIR}/stdc.h + +mbschr.o: ${topdir}/bashansi.h +mbschr.o: ${BASHINCDIR}/ansi_stdlib.h +mbschr.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h + +zgetline.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +zgetline.o: ${BASHINCDIR}/stdc.h +zgetline.o: ${topdir}/xmalloc.h +zgetline.o: ${topdir}/bashtypes.h + +mbscasecmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +mbscasecmp.o: ${BASHINCDIR}/stdc.h +mbscasecmp.o: ${topdir}/xmalloc.h + +mbscmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +mbscmp.o: ${BASHINCDIR}/stdc.h +mbscmp.o: ${topdir}/xmalloc.h + +casemod.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +casemod.o: ${BASHINCDIR}/stdc.h +casemod.o: ${topdir}/xmalloc.h +casemod.o: ${topdir}/bashtypes.h +casemod.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h +casemod.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h + +dprintf.o: ${BASHINCDIR}/stdc.h + +input_avail.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +input_avail.o: ${BASHINCDIR}/stdc.h +input_avail.o: ${topdir}/xmalloc.h ${BASHINCDIR}/posixselect.h + +mktime.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +mktime.o: ${BASHINCDIR}/stdc.h + +fnxform.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +fnxform.o: ${BASHINCDIR}/stdc.h +fnxform.o: ${topdir}/bashtypes.h +fnxform.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h + +shmbchar.o: ${BASHINCDIR}/shmbchar.h +shmbchar.o: ${BASHINCDIR}/shmbutil.h + +timers.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +timers.o: ${BASHINCDIR}/stdc.h +timers.o: ${topdir}/xmalloc.h ${topdir}/sig.h +timers.o: ${BASHINCDIR}/posixtime.h ${BASHINCDIR}/stat-time.h +timers.o: ${BASHINCDIR}/posixselect.h +timers.o: ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +timers.o: ${BASHINCDIR}/timer.h + +unicode.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +unicode.o: ${BASHINCDIR}/stdc.h +unicode.o: ${topdir}/xmalloc.h + +utf8.o: ${topdir}/bashansi.h +utf8.o: ${BASHINCDIR}/ansi_stdlib.h +utf8.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h + +winsize.o: ${BASHINCDIR}/stdc.h +winsize.o: ${topdir}/xmalloc.h +winsize.o: ${topdir}/bashtypes.h + +zmapfd.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +zmapfd.o: ${BASHINCDIR}/stdc.h +zmapfd.o: ${topdir}/command.h +zmapfd.o: ${topdir}/general.h +zmapfd.o: ${topdir}/bashtypes.h ${BASHINCDIR}/chartypes.h ${topdir}/xmalloc.h diff --git a/lib/sh/casemod.c b/lib/sh/casemod.c new file mode 100644 index 0000000..0403f91 --- /dev/null +++ b/lib/sh/casemod.c @@ -0,0 +1,271 @@ +/* casemod.c -- functions to change case of strings */ + +/* Copyright (C) 2008-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include <stdc.h> + +#include <bashansi.h> +#include <bashintl.h> +#include <bashtypes.h> + +#include <stdio.h> +#include <ctype.h> +#include <xmalloc.h> + +#include <shmbchar.h> +#include <shmbutil.h> +#include <chartypes.h> +#include <typemax.h> + +#include <glob/strmatch.h> + +#define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc)) +#define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc)) + +#if !defined (HANDLE_MULTIBYTE) +# define cval(s, i, l) ((s)[(i)]) +# define iswalnum(c) (isalnum(c)) +# define TOGGLE(x) (ISUPPER (x) ? tolower ((unsigned char)x) : (TOUPPER (x))) +#else +# define TOGGLE(x) (iswupper (x) ? towlower (x) : (_to_wupper(x))) +#endif + +/* These must agree with the defines in externs.h */ +#define CASE_NOOP 0x0000 +#define CASE_LOWER 0x0001 +#define CASE_UPPER 0x0002 +#define CASE_CAPITALIZE 0x0004 +#define CASE_UNCAP 0x0008 +#define CASE_TOGGLE 0x0010 +#define CASE_TOGGLEALL 0x0020 +#define CASE_UPFIRST 0x0040 +#define CASE_LOWFIRST 0x0080 + +#define CASE_USEWORDS 0x1000 /* modify behavior to act on words in passed string */ + +extern char *substring PARAMS((char *, int, int)); + +#ifndef UCHAR_MAX +# define UCHAR_MAX TYPE_MAXIMUM(unsigned char) +#endif + +#if defined (HANDLE_MULTIBYTE) +static wchar_t +cval (s, i, l) + char *s; + int i, l; +{ + size_t tmp; + wchar_t wc; + mbstate_t mps; + + if (MB_CUR_MAX == 1 || is_basic (s[i])) + return ((wchar_t)s[i]); + if (i >= (l - 1)) + return ((wchar_t)s[i]); + memset (&mps, 0, sizeof (mbstate_t)); + tmp = mbrtowc (&wc, s + i, l - i, &mps); + if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp)) + return ((wchar_t)s[i]); + return wc; +} +#endif + +/* Modify the case of characters in STRING matching PAT based on the value of + FLAGS. If PAT is null, modify the case of each character */ +char * +sh_modcase (string, pat, flags) + const char *string; + char *pat; + int flags; +{ + int start, next, end, retind; + int inword, c, nc, nop, match, usewords; + char *ret, *s; + wchar_t wc; + int mb_cur_max; +#if defined (HANDLE_MULTIBYTE) + wchar_t nwc; + char mb[MB_LEN_MAX+1]; + int mlen; + size_t m; + mbstate_t state; +#endif + + if (string == 0 || *string == 0) + { + ret = (char *)xmalloc (1); + ret[0] = '\0'; + return ret; + } + +#if defined (HANDLE_MULTIBYTE) + memset (&state, 0, sizeof (mbstate_t)); +#endif + + start = 0; + end = strlen (string); + mb_cur_max = MB_CUR_MAX; + + ret = (char *)xmalloc (2*end + 1); + retind = 0; + + /* See if we are supposed to split on alphanumerics and operate on each word */ + usewords = (flags & CASE_USEWORDS); + flags &= ~CASE_USEWORDS; + + inword = 0; + while (start < end) + { + wc = cval ((char *)string, start, end); + + if (iswalnum (wc) == 0) + inword = 0; + + if (pat) + { + next = start; + ADVANCE_CHAR (string, end, next); + s = substring ((char *)string, start, next); + match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH; + free (s); + if (match == 0) + { + /* copy unmatched portion */ + memcpy (ret + retind, string + start, next - start); + retind += next - start; + start = next; + inword = 1; + continue; + } + } + + /* XXX - for now, the toggling operators work on the individual + words in the string, breaking on alphanumerics. Should I + leave the capitalization operators to do that also? */ + if (flags == CASE_CAPITALIZE) + { + if (usewords) + nop = inword ? CASE_LOWER : CASE_UPPER; + else + nop = (start > 0) ? CASE_LOWER : CASE_UPPER; + inword = 1; + } + else if (flags == CASE_UNCAP) + { + if (usewords) + nop = inword ? CASE_UPPER : CASE_LOWER; + else + nop = (start > 0) ? CASE_UPPER : CASE_LOWER; + inword = 1; + } + else if (flags == CASE_UPFIRST) + { + if (usewords) + nop = inword ? CASE_NOOP : CASE_UPPER; + else + nop = (start > 0) ? CASE_NOOP : CASE_UPPER; + inword = 1; + } + else if (flags == CASE_LOWFIRST) + { + if (usewords) + nop = inword ? CASE_NOOP : CASE_LOWER; + else + nop = (start > 0) ? CASE_NOOP : CASE_LOWER; + inword = 1; + } + else if (flags == CASE_TOGGLE) + { + nop = inword ? CASE_NOOP : CASE_TOGGLE; + inword = 1; + } + else + nop = flags; + + /* Can't short-circuit, some locales have multibyte upper and lower + case equivalents of single-byte ascii characters (e.g., Turkish) */ + if (mb_cur_max == 1) + { +singlebyte: + switch (nop) + { + default: + case CASE_NOOP: nc = wc; break; + case CASE_UPPER: nc = TOUPPER (wc); break; + case CASE_LOWER: nc = TOLOWER (wc); break; + case CASE_TOGGLEALL: + case CASE_TOGGLE: nc = TOGGLE (wc); break; + } + ret[retind++] = nc; + } +#if defined (HANDLE_MULTIBYTE) + else + { + m = mbrtowc (&wc, string + start, end - start, &state); + /* Have to go through wide case conversion even for single-byte + chars, to accommodate single-byte characters where the + corresponding upper or lower case equivalent is multibyte. */ + if (MB_INVALIDCH (m)) + { + wc = (unsigned char)string[start]; + goto singlebyte; + } + else if (MB_NULLWCH (m)) + wc = L'\0'; + switch (nop) + { + default: + case CASE_NOOP: nwc = wc; break; + case CASE_UPPER: nwc = _to_wupper (wc); break; + case CASE_LOWER: nwc = _to_wlower (wc); break; + case CASE_TOGGLEALL: + case CASE_TOGGLE: nwc = TOGGLE (wc); break; + } + + /* We don't have to convert `wide' characters that are in the + unsigned char range back to single-byte `multibyte' characters. */ + if ((int)nwc <= UCHAR_MAX && is_basic ((int)nwc)) + ret[retind++] = nwc; + else + { + mlen = wcrtomb (mb, nwc, &state); + if (mlen > 0) + mb[mlen] = '\0'; + /* Don't assume the same width */ + strncpy (ret + retind, mb, mlen); + retind += mlen; + } + } +#endif + + ADVANCE_CHAR (string, end, start); + } + + ret[retind] = '\0'; + return ret; +} diff --git a/lib/sh/clktck.c b/lib/sh/clktck.c new file mode 100644 index 0000000..8b9b5b3 --- /dev/null +++ b/lib/sh/clktck.c @@ -0,0 +1,61 @@ +/* clktck.c - get the value of CLK_TCK. */ + +/* Copyright (C) 1997 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif + +#if defined (HAVE_UNISTD_H) +# 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) +# define CLK_TCK HZ +# else +# define CLK_TCK 60 +# endif +# endif /* !CLK_TCK */ +#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */ + +long +get_clk_tck () +{ + static long retval = 0; + + if (retval != 0) + return (retval); + +#if defined (HAVE_SYSCONF) && defined (_SC_CLK_TCK) + retval = sysconf (_SC_CLK_TCK); +#else /* !SYSCONF || !_SC_CLK_TCK */ + retval = CLK_TCK; +#endif /* !SYSCONF || !_SC_CLK_TCK */ + + return (retval); +} diff --git a/lib/sh/clock.c b/lib/sh/clock.c new file mode 100644 index 0000000..c6c52bf --- /dev/null +++ b/lib/sh/clock.c @@ -0,0 +1,87 @@ +/* clock.c - operations on struct tms and clock_t's */ + +/* Copyright (C) 1999 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_TIMES) + +#include <sys/types.h> +#include <posixtime.h> + +#if defined (HAVE_SYS_TIMES_H) +# include <sys/times.h> +#endif + +#include <stdio.h> +#include <stdc.h> + +#include <bashintl.h> + +#ifndef locale_decpoint +extern int locale_decpoint PARAMS((void)); +#endif + +extern long get_clk_tck PARAMS((void)); + +void +clock_t_to_secs (t, sp, sfp) + clock_t t; + time_t *sp; + int *sfp; +{ + static long clk_tck = -1; + + if (clk_tck == -1) + clk_tck = get_clk_tck (); + + *sfp = t % clk_tck; + *sfp = (*sfp * 1000) / clk_tck; + + *sp = t / clk_tck; + + /* Sanity check */ + if (*sfp >= 1000) + { + *sp += 1; + *sfp -= 1000; + } +} + +/* Print the time defined by a clock_t (returned by the `times' and `time' + system calls) in a standard way to stdio stream FP. This is scaled in + terms of the value of CLK_TCK, which is what is returned by the + `times' call. */ +void +print_clock_t (fp, t) + FILE *fp; + clock_t t; +{ + time_t timestamp; + long minutes; + int seconds, seconds_fraction; + + clock_t_to_secs (t, ×tamp, &seconds_fraction); + + minutes = timestamp / 60; + seconds = timestamp % 60; + + fprintf (fp, "%ldm%d%c%03ds", minutes, seconds, locale_decpoint(), seconds_fraction); +} +#endif /* HAVE_TIMES */ diff --git a/lib/sh/dprintf.c b/lib/sh/dprintf.c new file mode 100644 index 0000000..b3b5d64 --- /dev/null +++ b/lib/sh/dprintf.c @@ -0,0 +1,70 @@ +/* dprintf -- printf to a file descriptor */ + +/* Copyright (C) 2008-2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdc.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include <stdio.h> + +int +#if defined (PREFER_STDARG) +dprintf(int fd, const char *format, ...) +#else +dprintf(fd, format, va_alist) + int fd; + const char *format; + va_dcl +#endif +{ + FILE *fp; + int fd2, rc, r2; + va_list args; + + if ((fd2 = dup(fd)) < 0) + return -1; + fp = fdopen (fd2, "w"); + if (fp == 0) + { + close (fd2); + return -1; + } + + SH_VA_START (args, format); + rc = vfprintf (fp, format, args); + fflush (fp); + va_end (args); + + r2 = fclose (fp); /* check here */ + + return rc; +} diff --git a/lib/sh/eaccess.c b/lib/sh/eaccess.c new file mode 100644 index 0000000..c3043ec --- /dev/null +++ b/lib/sh/eaccess.c @@ -0,0 +1,244 @@ +/* eaccess.c - eaccess replacement for the shell, plus other access functions. */ + +/* Copyright (C) 2006-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* !_POSIX_VERSION */ +#include "posixstat.h" +#include "filecntl.h" + +#include "shell.h" + +#if !defined (R_OK) +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#define F_OK 0 +#endif /* R_OK */ + +static int path_is_devfd PARAMS((const char *)); +static int sh_stataccess PARAMS((const char *, int)); +#if HAVE_DECL_SETREGID +static int sh_euidaccess PARAMS((const char *, int)); +#endif + +static int +path_is_devfd (path) + const char *path; +{ + if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) + return 1; + else if (STREQN (path, "/dev/std", 8)) + { + if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err")) + return 1; + else + return 0; + } + else + return 0; +} + +/* A wrapper for stat () which disallows pathnames that are empty strings + and handles /dev/fd emulation on systems that don't have it. */ +int +sh_stat (path, finfo) + const char *path; + struct stat *finfo; +{ + static char *pbuf = 0; + + if (*path == '\0') + { + errno = ENOENT; + return (-1); + } + if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) + { + /* If stating /dev/fd/n doesn't produce the same results as fstat of + FD N, then define DEV_FD_STAT_BROKEN */ +#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN) + intmax_t fd; + int r; + + if (legal_number (path + 8, &fd) && fd == (int)fd) + { + r = fstat ((int)fd, finfo); + if (r == 0 || errno != EBADF) + return (r); + } + errno = ENOENT; + return (-1); +#else + /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a + trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx. + On most systems, with the notable exception of linux, this is + effectively a no-op. */ + pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8)); + strcpy (pbuf, DEV_FD_PREFIX); + strcat (pbuf, path + 8); + return (stat (pbuf, finfo)); +#endif /* !HAVE_DEV_FD */ + } +#if !defined (HAVE_DEV_STDIN) + else if (STREQN (path, "/dev/std", 8)) + { + if (STREQ (path+8, "in")) + return (fstat (0, finfo)); + else if (STREQ (path+8, "out")) + return (fstat (1, finfo)); + else if (STREQ (path+8, "err")) + return (fstat (2, finfo)); + else + return (stat (path, finfo)); + } +#endif /* !HAVE_DEV_STDIN */ + return (stat (path, finfo)); +} + +/* Do the same thing access(2) does, but use the effective uid and gid, + and don't make the mistake of telling root that any file is + executable. This version uses stat(2). */ +static int +sh_stataccess (path, mode) + const char *path; + int mode; +{ + struct stat st; + + if (sh_stat (path, &st) < 0) + return (-1); + + if (current_user.euid == 0) + { + /* Root can read or write any file. */ + if ((mode & X_OK) == 0) + return (0); + + /* Root can execute any file that has any one of the execute + bits set. */ + if (st.st_mode & S_IXUGO) + return (0); + } + + if (st.st_uid == current_user.euid) /* owner */ + mode <<= 6; + else if (group_member (st.st_gid)) + mode <<= 3; + + if (st.st_mode & mode) + return (0); + + errno = EACCES; + return (-1); +} + +#if HAVE_DECL_SETREGID +/* Version to call when uid != euid or gid != egid. We temporarily swap + the effective and real uid and gid as appropriate. */ +static int +sh_euidaccess (path, mode) + const char *path; + int mode; +{ + int r, e; + + if (current_user.uid != current_user.euid) + setreuid (current_user.euid, current_user.uid); + if (current_user.gid != current_user.egid) + setregid (current_user.egid, current_user.gid); + + r = access (path, mode); + e = errno; + + if (current_user.uid != current_user.euid) + setreuid (current_user.uid, current_user.euid); + if (current_user.gid != current_user.egid) + setregid (current_user.gid, current_user.egid); + + errno = e; + return r; +} +#endif + +int +sh_eaccess (path, mode) + const char *path; + int mode; +{ + int ret; + + if (path_is_devfd (path)) + return (sh_stataccess (path, mode)); + +#if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS) +# if defined (HAVE_FACCESSAT) && defined (AT_EACCESS) + ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS); +# else /* HAVE_EACCESS */ /* FreeBSD */ + ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */ +# endif /* HAVE_EACCESS */ +# if defined (__FreeBSD__) || defined (SOLARIS) || defined (_AIX) + if (ret == 0 && current_user.euid == 0 && mode == X_OK) + return (sh_stataccess (path, mode)); +# endif /* __FreeBSD__ || SOLARIS || _AIX */ + return ret; +#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */ + return access (path, mode|EFF_ONLY_OK); +#else + if (mode == F_OK) + return (sh_stataccess (path, mode)); + +# if HAVE_DECL_SETREGID + if (current_user.uid != current_user.euid || current_user.gid != current_user.egid) + return (sh_euidaccess (path, mode)); +# endif + + if (current_user.uid == current_user.euid && current_user.gid == current_user.egid) + { + ret = access (path, mode); +#if defined (__FreeBSD__) || defined (SOLARIS) + if (ret == 0 && current_user.euid == 0 && mode == X_OK) + return (sh_stataccess (path, mode)); +#endif + return ret; + } + + return (sh_stataccess (path, mode)); +#endif +} diff --git a/lib/sh/fmtullong.c b/lib/sh/fmtullong.c new file mode 100644 index 0000000..46eaf50 --- /dev/null +++ b/lib/sh/fmtullong.c @@ -0,0 +1,31 @@ +/* fmtullong.c - convert `long long int' to string */ + +/* Copyright (C) 2001-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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifdef HAVE_LONG_LONG_INT + +#define LONG long long +#define UNSIGNED_LONG unsigned long long +#define fmtulong fmtullong + +#include "fmtulong.c" + +#endif diff --git a/lib/sh/fmtulong.c b/lib/sh/fmtulong.c new file mode 100644 index 0000000..0ccc22b --- /dev/null +++ b/lib/sh/fmtulong.c @@ -0,0 +1,191 @@ +/* fmtulong.c -- Convert unsigned long int to string. */ + +/* Copyright (C) 1998-2011 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#include <bashansi.h> +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#include <chartypes.h> +#include <errno.h> + +#include <bashintl.h> + +#include "stdc.h" + +#include <typemax.h> + +#ifndef errno +extern int errno; +#endif + +#define x_digs "0123456789abcdef" +#define X_digs "0123456789ABCDEF" + +/* XXX -- assumes uppercase letters, lowercase letters, and digits are + contiguous */ +#define FMTCHAR(x) \ + ((x) < 10) ? (x) + '0' \ + : (((x) < 36) ? (x) - 10 + 'a' \ + : (((x) < 62) ? (x) - 36 + 'A' \ + : (((x) == 62) ? '@' : '_'))) + +#ifndef FL_PREFIX +# define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */ +# define FL_ADDBASE 0x02 /* add base# prefix to converted value */ +# define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */ +# define FL_UNSIGNED 0x08 /* don't add any sign */ +#endif + +#ifndef LONG +# define LONG long +# define UNSIGNED_LONG unsigned long +#endif + +/* `unsigned long' (or unsigned long long) to string conversion for a given + base. The caller passes the output buffer and the size. This should + check for buffer underflow, but currently does not. */ +char * +fmtulong (ui, base, buf, len, flags) + UNSIGNED_LONG ui; + int base; + char *buf; + size_t len; + int flags; +{ + char *p; + int sign; + LONG si; + + if (base == 0) + base = 10; + + if (base < 2 || base > 64) + { +#if 1 + /* XXX - truncation possible with long translation */ + strncpy (buf, _("invalid base"), len - 1); + buf[len-1] = '\0'; + errno = EINVAL; + return (p = buf); +#else + base = 10; +#endif + } + + sign = 0; + if ((flags & FL_UNSIGNED) == 0 && (LONG)ui < 0) + { + ui = -ui; + sign = '-'; + } + + p = buf + len - 2; + p[1] = '\0'; + + /* handle common cases explicitly */ + switch (base) + { + case 10: + if (ui < 10) + { + *p-- = TOCHAR (ui); + break; + } + /* Favor signed arithmetic over unsigned arithmetic; it is faster on + many machines. */ + if ((LONG)ui < 0) + { + *p-- = TOCHAR (ui % 10); + si = ui / 10; + } + else + si = ui; + do + *p-- = TOCHAR (si % 10); + while (si /= 10); + break; + + case 8: + do + *p-- = TOCHAR (ui & 7); + while (ui >>= 3); + break; + + case 16: + do + *p-- = (flags & FL_HEXUPPER) ? X_digs[ui & 15] : x_digs[ui & 15]; + while (ui >>= 4); + break; + + case 2: + do + *p-- = TOCHAR (ui & 1); + while (ui >>= 1); + break; + + default: + do + *p-- = FMTCHAR (ui % base); + while (ui /= base); + break; + } + + if ((flags & FL_PREFIX) && (base == 8 || base == 16)) + { + if (base == 16) + { + *p-- = (flags & FL_HEXUPPER) ? 'X' : 'x'; + *p-- = '0'; + } + else if (p[1] != '0') + *p-- = '0'; + } + else if ((flags & FL_ADDBASE) && base != 10) + { + *p-- = '#'; + *p-- = TOCHAR (base % 10); + if (base > 10) + *p-- = TOCHAR (base / 10); + } + + if (sign) + *p-- = '-'; + + return (p + 1); +} diff --git a/lib/sh/fmtumax.c b/lib/sh/fmtumax.c new file mode 100644 index 0000000..f2786b5 --- /dev/null +++ b/lib/sh/fmtumax.c @@ -0,0 +1,27 @@ +/* fmtumax.c -- Convert uintmax_t to string. */ + +/* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#define LONG intmax_t +#define UNSIGNED_LONG uintmax_t +#define fmtulong fmtumax + +#include "fmtulong.c" diff --git a/lib/sh/fnxform.c b/lib/sh/fnxform.c new file mode 100644 index 0000000..35d7e73 --- /dev/null +++ b/lib/sh/fnxform.c @@ -0,0 +1,199 @@ +/* fnxform - use iconv(3) to transform strings to and from "filename" format */ + +/* Copyright (C) 2009-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "bashansi.h" +#include <stdio.h> +#include "bashtypes.h" + +#include "stdc.h" +#include "bashintl.h" +#include <xmalloc.h> + +#if defined (HAVE_ICONV) +# include <iconv.h> +#endif + +#if defined (HAVE_LOCALE_CHARSET) +extern const char *locale_charset PARAMS((void)); +#else +extern char *get_locale_var PARAMS((char *)); +#endif + +#if defined (HAVE_ICONV) +static iconv_t conv_fromfs = (iconv_t)-1; +static iconv_t conv_tofs = (iconv_t)-1; + +#define OUTLEN_MAX 4096 + +static char *outbuf = 0; +static size_t outlen = 0; + +static char *curencoding PARAMS((void)); +static void init_tofs PARAMS((void)); +static void init_fromfs PARAMS((void)); + +static char * +curencoding () +{ + char *loc; +#if defined (HAVE_LOCALE_CHARSET) + loc = (char *)locale_charset (); + return loc; +#else + char *dot, *mod; + + loc = get_locale_var ("LC_CTYPE"); + if (loc == 0 || *loc == 0) + return ""; + dot = strchr (loc, '.'); + if (dot == 0) + return loc; + mod = strchr (dot, '@'); + if (mod) + *mod = '\0'; + return ++dot; +#endif +} + +static void +init_tofs () +{ + char *cur; + + cur = curencoding (); + conv_tofs = iconv_open ("UTF-8-MAC", cur); +} + +static void +init_fromfs () +{ + char *cur; + + cur = curencoding (); + conv_fromfs = iconv_open (cur, "UTF-8-MAC"); +} + +char * +fnx_tofs (string, len) + char *string; + size_t len; +{ +#ifdef MACOSX + ICONV_CONST char *inbuf; + char *tempbuf; + size_t templen; + + if (conv_tofs == (iconv_t)-1) + init_tofs (); + if (conv_tofs == (iconv_t)-1) + return string; + + /* Free and reallocate outbuf if it's *too* big */ + if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8) + { + free (outbuf); + outbuf = 0; + outlen = 0; + } + + inbuf = string; + if (outbuf == 0 || outlen < len + 8) + { + outlen = len + 8; + outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1); + } + tempbuf = outbuf; + templen = outlen; + + iconv (conv_tofs, NULL, NULL, NULL, NULL); + + if (iconv (conv_tofs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1) + return string; + + *tempbuf = '\0'; + return outbuf; +#else + return string; +#endif +} + +char * +fnx_fromfs (string, len) + char *string; + size_t len; +{ +#ifdef MACOSX + ICONV_CONST char *inbuf; + char *tempbuf; + size_t templen; + + if (conv_fromfs == (iconv_t)-1) + init_fromfs (); + if (conv_fromfs == (iconv_t)-1) + return string; + + /* Free and reallocate outbuf if it's *too* big */ + if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8) + { + free (outbuf); + outbuf = 0; + outlen = 0; + } + + inbuf = string; + if (outbuf == 0 || outlen < (len + 8)) + { + outlen = len + 8; + outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1); + } + tempbuf = outbuf; + templen = outlen; + + iconv (conv_fromfs, NULL, NULL, NULL, NULL); + + if (iconv (conv_fromfs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1) + return string; + + *tempbuf = '\0'; + return outbuf; +#else + return string; +#endif +} + +#else +char * +fnx_tofs (string) + char *string; +{ + return string; +} + +char * +fnx_fromfs (string) + char *string; +{ + return string; +} +#endif diff --git a/lib/sh/fpurge.c b/lib/sh/fpurge.c new file mode 100644 index 0000000..8cd4e36 --- /dev/null +++ b/lib/sh/fpurge.c @@ -0,0 +1,232 @@ +/* fpurge - Flushing buffers of a FILE stream. */ + +/* Copyright (C) 2007-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "stdc.h" + +#include <stdio.h> + +/* Specification. Same as in ../../externs.h. */ +#define NEED_FPURGE_DECL +#if HAVE_FPURGE +# define fpurge _bash_fpurge +#endif +extern int fpurge PARAMS((FILE *stream)); + +#if HAVE___FPURGE /* glibc >= 2.2, Haiku, Solaris >= 7 */ +# include <stdio_ext.h> +#endif +#include <stdlib.h> + +/* Inline contents of gnulib:stdio-impl.h */ + +/* Many stdio implementations have the same logic and therefore can share + the same implementation of stdio extension API, except that some fields + have different naming conventions, or their access requires some casts. */ + +/* BSD stdio derived implementations. */ + +#if defined __NetBSD__ /* NetBSD */ +/* Get __NetBSD_Version__. */ +# include <sys/param.h> +#endif + +#if defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */ + +# if defined __DragonFly__ /* DragonFly */ + /* See <http://www.dragonflybsd.org/cvsweb/src/lib/libc/stdio/priv_stdio.h?rev=HEAD&content-type=text/x-cvsweb-markup>. */ +# define fp_ ((struct { struct __FILE_public pub; \ + struct { unsigned char *_base; int _size; } _bf; \ + void *cookie; \ + void *_close; \ + void *_read; \ + void *_seek; \ + void *_write; \ + struct { unsigned char *_base; int _size; } _ub; \ + int _ur; \ + unsigned char _ubuf[3]; \ + unsigned char _nbuf[1]; \ + struct { unsigned char *_base; int _size; } _lb; \ + int _blksize; \ + fpos_t _offset; \ + /* More fields, not relevant here. */ \ + } *) fp) + /* See <http://www.dragonflybsd.org/cvsweb/src/include/stdio.h?rev=HEAD&content-type=text/x-cvsweb-markup>. */ +# define _p pub._p +# define _flags pub._flags +# define _r pub._r +# define _w pub._w +# else +# define fp_ fp +# endif + +# if (defined __NetBSD__ && __NetBSD_Version__ >= 105270000) || defined __OpenBSD__ /* NetBSD >= 1.5ZA, OpenBSD */ + /* See <http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdio/fileext.h?rev=HEAD&content-type=text/x-cvsweb-markup> + and <http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/fileext.h?rev=HEAD&content-type=text/x-cvsweb-markup> */ + struct __sfileext + { + struct __sbuf _ub; /* ungetc buffer */ + /* More fields, not relevant here. */ + }; +# define fp_ub ((struct __sfileext *) fp->_ext._base)->_ub +# else /* FreeBSD, NetBSD <= 1.5Z, DragonFly, MacOS X, Cygwin */ +# define fp_ub fp_->_ub +# endif + +# define HASUB(fp) (fp_ub._base != NULL) + +#endif + +/* SystemV derived implementations. */ + +#if defined _IOERR + +# if defined __sun && defined _LP64 /* Solaris/{SPARC,AMD64} 64-bit */ +# define fp_ ((struct { unsigned char *_ptr; \ + unsigned char *_base; \ + unsigned char *_end; \ + long _cnt; \ + int _file; \ + unsigned int _flag; \ + } *) fp) +# else +# define fp_ fp +# endif + +# if defined _SCO_DS /* OpenServer */ +# define _cnt __cnt +# define _ptr __ptr +# define _base __base +# define _flag __flag +# endif + +#endif + +int +fpurge (FILE *fp) +{ +#if HAVE___FPURGE /* glibc >= 2.2, Haiku, Solaris >= 7 */ + + __fpurge (fp); + /* The __fpurge function does not have a return value. */ + return 0; + +#elif HAVE_FPURGE /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin 1.7 */ + + /* Call the system's fpurge function. */ +# undef fpurge +# if !HAVE_DECL_FPURGE + extern int fpurge (FILE *); +# endif + int result = fpurge (fp); +# if defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */ + if (result == 0) + /* Correct the invariants that fpurge broke. + <stdio.h> on BSD systems says: + "The following always hold: if _flags & __SRD, _w is 0." + If this invariant is not fulfilled and the stream is read-write but + currently reading, subsequent putc or fputc calls will write directly + into the buffer, although they shouldn't be allowed to. */ + if ((fp_->_flags & __SRD) != 0) + fp_->_w = 0; +# endif + return result; + +#else + + /* Most systems provide FILE as a struct and the necessary bitmask in + <stdio.h>, because they need it for implementing getc() and putc() as + fast macros. */ +# if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ + fp->_IO_read_end = fp->_IO_read_ptr; + fp->_IO_write_ptr = fp->_IO_write_base; + /* Avoid memory leak when there is an active ungetc buffer. */ + if (fp->_IO_save_base != NULL) + { + free (fp->_IO_save_base); + fp->_IO_save_base = NULL; + } + return 0; +# elif defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, MacOS X, Cygwin */ + fp_->_p = fp_->_bf._base; + fp_->_r = 0; + fp_->_w = ((fp_->_flags & (__SLBF | __SNBF | __SRD)) == 0 /* fully buffered and not currently reading? */ + ? fp_->_bf._size + : 0); + /* Avoid memory leak when there is an active ungetc buffer. */ + if (fp_ub._base != NULL) + { + if (fp_ub._base != fp_->_ubuf) + free (fp_ub._base); + fp_ub._base = NULL; + } + return 0; +# elif defined __EMX__ /* emx+gcc */ + fp->_ptr = fp->_buffer; + fp->_rcount = 0; + fp->_wcount = 0; + fp->_ungetc_count = 0; + return 0; +# elif defined _IOERR || defined __TANDEM /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, mingw */ + fp->_ptr = fp->_base; + if (fp->_ptr != NULL) + fp->_cnt = 0; + return 0; +# elif defined __UCLIBC__ /* uClibc */ +# ifdef __STDIO_BUFFERS + if (fp->__modeflags & __FLAG_WRITING) + fp->__bufpos = fp->__bufstart; + else if (fp->__modeflags & (__FLAG_READONLY | __FLAG_READING)) + fp->__bufpos = fp->__bufread; +# endif + return 0; +# elif defined __QNX__ /* QNX */ + fp->_Rback = fp->_Back + sizeof (fp->_Back); + fp->_Rsave = NULL; + if (fp->_Mode & 0x2000 /* _MWRITE */) + /* fp->_Buf <= fp->_Next <= fp->_Wend */ + fp->_Next = fp->_Buf; + else + /* fp->_Buf <= fp->_Next <= fp->_Rend */ + fp->_Rend = fp->_Next; + return 0; +# elif defined __MINT__ /* Atari FreeMiNT */ + if (fp->__pushed_back) + { + fp->__bufp = fp->__pushback_bufp; + fp->__pushed_back = 0; + } + /* Preserve the current file position. */ + if (fp->__target != -1) + fp->__target += fp->__bufp - fp->__buffer; + fp->__bufp = fp->__buffer; + /* Nothing in the buffer, next getc is nontrivial. */ + fp->__get_limit = fp->__bufp; + /* Nothing in the buffer, next putc is nontrivial. */ + fp->__put_limit = fp->__buffer; + return 0; +# else +# warning "Please port gnulib fpurge.c to your platform! Look at the definitions of fflush, setvbuf and ungetc on your system, then report this to bug-gnulib." + return 0; +# endif + +#endif +} diff --git a/lib/sh/getcwd.c b/lib/sh/getcwd.c new file mode 100644 index 0000000..d7bd241 --- /dev/null +++ b/lib/sh/getcwd.c @@ -0,0 +1,356 @@ +/* getcwd.c -- get pathname of current directory */ + +/* Copyright (C) 1991 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_GETCWD) + +#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) + #pragma alloca +#endif /* _AIX && RISC6000 && !__GNUC__ */ + +#if defined (__QNX__) +# undef HAVE_LSTAT +#endif + +#include <bashtypes.h> +#include <errno.h> + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <posixdir.h> +#include <posixstat.h> +#include <maxpath.h> +#include <memalloc.h> + +#include <bashansi.h> + +#if !defined (D_FILENO_AVAILABLE) +# include "command.h" +# include "general.h" +# include "externs.h" +#endif + +#include <xmalloc.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if !defined (HAVE_LSTAT) +# define lstat stat +#endif + +#if !defined (NULL) +# define NULL 0 +#endif + +/* If the d_fileno member of a struct dirent doesn't return anything useful, + we need to check inode number equivalence the hard way. Return 1 if + the inode corresponding to PATH/DIR is identical to THISINO. */ +#if !defined (D_FILENO_AVAILABLE) +static int +_path_checkino (dotp, name, thisino) + char *dotp; + char *name; + ino_t thisino; +{ + char *fullpath; + int r, e; + struct stat st; + + e = errno; + fullpath = sh_makepath (dotp, name, MP_RMDOT); + if (stat (fullpath, &st) < 0) + { + errno = e; + return 0; + } + free (fullpath); + errno = e; + return (st.st_ino == thisino); +} +#endif + +/* Get the pathname of the current working directory, + and put it in SIZE bytes of BUF. Returns NULL if the + directory couldn't be determined or SIZE was too small. + If successful, returns BUF. In GNU, if BUF is NULL, + an array is allocated with `malloc'; the array is SIZE + bytes long, unless SIZE <= 0, in which case it is as + big as necessary. */ +#if defined (__STDC__) +char * +getcwd (char *buf, size_t size) +#else /* !__STDC__ */ +char * +getcwd (buf, size) + char *buf; + size_t size; +#endif /* !__STDC__ */ +{ + static const char dots[] + = "../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../.."; + const char *dotp, *dotlist; + size_t dotsize; + dev_t rootdev, thisdev; + ino_t rootino, thisino; + char path[PATH_MAX + 1]; + register char *pathp; + char *pathbuf; + size_t pathsize; + struct stat st; + int saved_errno; + + if (buf != NULL && size == 0) + { + errno = EINVAL; + return ((char *)NULL); + } + + pathsize = sizeof (path); + pathp = &path[pathsize]; + *--pathp = '\0'; + pathbuf = path; + + if (stat (".", &st) < 0) + return ((char *)NULL); + thisdev = st.st_dev; + thisino = st.st_ino; + + if (stat ("/", &st) < 0) + return ((char *)NULL); + rootdev = st.st_dev; + rootino = st.st_ino; + + saved_errno = 0; + + dotsize = sizeof (dots) - 1; + dotp = &dots[sizeof (dots)]; + dotlist = dots; + while (!(thisdev == rootdev && thisino == rootino)) + { + register DIR *dirstream; + register struct dirent *d; + dev_t dotdev; + ino_t dotino; + char mount_point; + int namlen; + + /* Look at the parent directory. */ + if (dotp == dotlist) + { + /* My, what a deep directory tree you have, Grandma. */ + char *new; + if (dotlist == dots) + { + new = (char *)malloc (dotsize * 2 + 1); + if (new == NULL) + goto lose; + memcpy (new, dots, dotsize); + } + else + { + new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1); + if (new == NULL) + goto lose; + } + memcpy (&new[dotsize], new, dotsize); + dotp = &new[dotsize]; + dotsize *= 2; + new[dotsize] = '\0'; + dotlist = new; + } + + dotp -= 3; + + /* Figure out if this directory is a mount point. */ + if (stat (dotp, &st) < 0) + goto lose; + dotdev = st.st_dev; + dotino = st.st_ino; + mount_point = dotdev != thisdev; + + /* Search for the last directory. */ + dirstream = opendir (dotp); + if (dirstream == NULL) + goto lose; + while ((d = readdir (dirstream)) != NULL) + { + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; +#if defined (D_FILENO_AVAILABLE) + if (mount_point || d->d_fileno == thisino) +#else + if (mount_point || _path_checkino (dotp, d->d_name, thisino)) +#endif + { + char *name; + + namlen = D_NAMLEN(d); + name = (char *) + alloca (dotlist + dotsize - dotp + 1 + namlen + 1); + memcpy (name, dotp, dotlist + dotsize - dotp); + name[dotlist + dotsize - dotp] = '/'; + memcpy (&name[dotlist + dotsize - dotp + 1], + d->d_name, namlen + 1); + if (lstat (name, &st) < 0) + { +#if 0 + int save = errno; + (void) closedir (dirstream); + errno = save; + goto lose; +#else + saved_errno = errno; +#endif + } + if (st.st_dev == thisdev && st.st_ino == thisino) + break; + } + } + if (d == NULL) + { +#if 0 + int save = errno; +#else + int save = errno ? errno : saved_errno; +#endif + (void) closedir (dirstream); + errno = save; + goto lose; + } + else + { + size_t space; + + while ((space = pathp - pathbuf) <= namlen) + { + char *new; + + if (pathbuf == path) + { + new = (char *)malloc (pathsize * 2); + if (!new) + goto lose; + } + else + { + new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2)); + if (!new) + goto lose; + pathp = new + space; + } + (void) memcpy (new + pathsize + space, pathp, pathsize - space); + pathp = new + pathsize + space; + pathbuf = new; + pathsize *= 2; + } + + pathp -= namlen; + (void) memcpy (pathp, d->d_name, namlen); + *--pathp = '/'; + (void) closedir (dirstream); + } + + thisdev = dotdev; + thisino = dotino; + } + + if (pathp == &path[sizeof(path) - 1]) + *--pathp = '/'; + + if (dotlist != dots) + free ((PTR_T) dotlist); + + { + size_t len = pathbuf + pathsize - pathp; + if (buf == NULL && size <= 0) + size = len; + + if ((size_t) size < len) + { + errno = ERANGE; + goto lose2; + } + if (buf == NULL) + { + buf = (char *) malloc (size); + if (buf == NULL) + goto lose2; + } + + (void) memcpy((PTR_T) buf, (PTR_T) pathp, len); + } + + if (pathbuf != path) + free (pathbuf); + + return (buf); + + lose: + if ((dotlist != dots) && dotlist) + { + int e = errno; + free ((PTR_T) dotlist); + errno = e; + } + + lose2: + if ((pathbuf != path) && pathbuf) + { + int e = errno; + free ((PTR_T) pathbuf); + errno = e; + } + return ((char *)NULL); +} + +#if defined (TEST) +# include <stdio.h> +main (argc, argv) + int argc; + char **argv; +{ + char b[PATH_MAX]; + + if (getcwd(b, sizeof(b))) + { + printf ("%s\n", b); + exit (0); + } + else + { + perror ("cwd: getcwd"); + exit (1); + } +} +#endif /* TEST */ +#endif /* !HAVE_GETCWD */ diff --git a/lib/sh/getenv.c b/lib/sh/getenv.c new file mode 100644 index 0000000..1e682ae --- /dev/null +++ b/lib/sh/getenv.c @@ -0,0 +1,233 @@ +/* getenv.c - get environment variable value from the shell's variable + list. */ + +/* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (CAN_REDEFINE_GETENV) + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#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 + routines to get the changed values of exported variables. */ + +/* The NeXT C library has getenv () defined and used in the same file. + This screws our scheme. However, Bash will run on the NeXT using + the C library getenv (), since right now the only environment variable + that we care about is HOME, and that is already defined. */ +static char *last_tempenv_value = (char *)NULL; + +char * +getenv (name) + const char *name; +{ + 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; + return (last_tempenv_value); + } + else if (shell_variables) + { + var = find_variable ((char *)name); + if (var && exported_p (var)) + return (value_cell (var)); + } + else if (environ) + { + register int i, len; + + /* In some cases, s5r3 invokes getenv() before main(); BSD systems + using gprof also exhibit this behavior. This means that + shell_variables will be 0 when this is invoked. We look up the + variable in the real environment in that case. */ + + for (i = 0, len = strlen (name); environ[i]; i++) + { + if ((STREQN (environ[i], name, len)) && (environ[i][len] == '=')) + return (environ[i] + len + 1); + } + } + + return ((char *)NULL); +} + +/* Some versions of Unix use _getenv instead. */ +char * +_getenv (name) + const char *name; +{ + 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, 0); + 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, 0); + 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 = (char *)value; /* some compilers need explicit cast */ + /* XXX - should we worry about readonly here? */ + if (rewrite == 0) + var = find_variable (name); + + if (var == 0) + var = bind_variable (name, v, 0); + + 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 */ diff --git a/lib/sh/gettimeofday.c b/lib/sh/gettimeofday.c new file mode 100644 index 0000000..b654c15 --- /dev/null +++ b/lib/sh/gettimeofday.c @@ -0,0 +1,35 @@ +/* gettimeofday.c - gettimeofday replacement using time() */ + +/* Copyright (C) 2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#if !defined (HAVE_GETTIMEOFDAY) + +#include "posixtime.h" + +/* A version of gettimeofday that just sets tv_sec from time(3) */ +int +gettimeofday (struct timeval *tv, void *tz) +{ + tv->tv_sec = (time_t) time ((time_t *)0); + tv->tv_usec = 0; + return 0; +} +#endif diff --git a/lib/sh/inet_aton.c b/lib/sh/inet_aton.c new file mode 100644 index 0000000..e377178 --- /dev/null +++ b/lib/sh/inet_aton.c @@ -0,0 +1,214 @@ +/* inet_aton - convert string to numeric IP address */ + +/* Snagged from GNU C library, version 2.0.3. */ + +/* + * ++Copyright++ 1983, 1990, 1993 + * - + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; +static char rcsid[] = "$Id: inet_addr.c,v 1.5 1996/08/14 03:48:37 drepper Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <config.h> + +#if !defined (HAVE_INET_ATON) && defined (HAVE_NETWORK) && defined (HAVE_NETINET_IN_H) && defined (HAVE_ARPA_INET_H) + +#include <sys/types.h> +#if defined (HAVE_SYS_PARAM_H) +#include <sys/param.h> +#endif +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <bashansi.h> +#include <ctype.h> +#include <stdc.h> + +#ifndef INADDR_NONE +# define INADDR_NONE 0xffffffff +#endif + +/* these are compatibility routines, not needed on recent BSD releases */ + +#if 0 +/* Not used, not needed. */ +/* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +u_long +inet_addr(cp) + register const char *cp; +{ + struct in_addr val; + + if (inet_aton(cp, &val)) + return (val.s_addr); + return (INADDR_NONE); +} +#endif + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(cp, addr) + register const char *cp; + struct in_addr *addr; +{ + register u_bits32_t val; + register int base, n; + register unsigned char c; + u_int parts[4]; + register u_int *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ +#if 0 + if (!isdigit(c)) +#else + if (c != '0' && c != '1' && c != '2' && c != '3' && c != '4' && + c != '5' && c != '6' && c != '7' && c != '8' && c != '9') +#endif + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + for (;;) { + if (isascii(c) && isdigit(c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) | + (c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii(c) || !isspace(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +#endif /* !HAVE_INET_ATON */ diff --git a/lib/sh/input_avail.c b/lib/sh/input_avail.c new file mode 100644 index 0000000..2ac4461 --- /dev/null +++ b/lib/sh/input_avail.c @@ -0,0 +1,165 @@ +/* input_avail.c -- check whether or not data is available for reading on a + specified file descriptor. */ + +/* Copyright (C) 2008,2009-2019 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (__TANDEM) +# include <floss.h> +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_PSELECT) +# include <signal.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "bashansi.h" + +#include "posixselect.h" + +#if defined (FIONREAD_IN_SYS_IOCTL) +# include <sys/ioctl.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if !defined (O_NDELAY) && defined (O_NONBLOCK) +# define O_NDELAY O_NONBLOCK /* Posix style */ +#endif + +/* Return >= 1 if select/FIONREAD indicates data available for reading on + file descriptor FD; 0 if no data available. Return -1 on error. */ +int +input_avail (fd) + int fd; +{ + int result, chars_avail; +#if defined(HAVE_SELECT) + fd_set readfds, exceptfds; + struct timeval timeout; +#endif + + if (fd < 0) + return -1; + + chars_avail = 0; + +#if defined (HAVE_SELECT) + FD_ZERO (&readfds); + FD_ZERO (&exceptfds); + FD_SET (fd, &readfds); + FD_SET (fd, &exceptfds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + result = select (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout); + return ((result <= 0) ? 0 : 1); +#endif + +#if defined (FIONREAD) + errno = 0; + result = ioctl (fd, FIONREAD, &chars_avail); + if (result == -1 && errno == EIO) + return -1; + return (chars_avail); +#endif + + return 0; +} + +/* Wait until NCHARS are available for reading on file descriptor FD. + This can wait indefinitely. Return -1 on error. */ +int +nchars_avail (fd, nchars) + int fd; + int nchars; +{ + int result, chars_avail; +#if defined(HAVE_SELECT) + fd_set readfds, exceptfds; +#endif +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + sigset_t set, oset; +#endif + + if (fd < 0 || nchars < 0) + return -1; + if (nchars == 0) + return (input_avail (fd)); + + chars_avail = 0; + +#if defined (HAVE_SELECT) + FD_ZERO (&readfds); + FD_ZERO (&exceptfds); + FD_SET (fd, &readfds); + FD_SET (fd, &exceptfds); +#endif +#if defined (HAVE_SELECT) || defined (HAVE_PSELECT) + sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &set); +# ifdef SIGCHLD + sigaddset (&set, SIGCHLD); +# endif + sigemptyset (&oset); +#endif + + while (1) + { + result = 0; +#if defined (HAVE_PSELECT) + /* XXX - use pselect(2) to block SIGCHLD atomically */ + result = pselect (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, (struct timespec *)NULL, &set); +#elif defined (HAVE_SELECT) + sigprocmask (SIG_BLOCK, &set, &oset); + result = select (fd + 1, &readfds, (fd_set *)NULL, &exceptfds, (struct timeval *)NULL); + sigprocmask (SIG_BLOCK, &oset, (sigset_t *)NULL); +#endif + if (result < 0) + return -1; + +#if defined (FIONREAD) + errno = 0; + result = ioctl (fd, FIONREAD, &chars_avail); + if (result == -1 && errno == EIO) + return -1; + if (chars_avail >= nchars) + break; +#else + break; +#endif + } + + return 0; +} diff --git a/lib/sh/itos.c b/lib/sh/itos.c new file mode 100644 index 0000000..cd36ef3 --- /dev/null +++ b/lib/sh/itos.c @@ -0,0 +1,84 @@ +/* itos.c -- Convert integer to string. */ + +/* Copyright (C) 1998-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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <bashansi.h> +#include "shell.h" + +char * +inttostr (i, buf, len) + intmax_t i; + char *buf; + size_t len; +{ + return (fmtumax (i, 10, buf, len, 0)); +} + +/* Integer to string conversion. This conses the string; the + caller should free it. */ +char * +itos (i) + intmax_t i; +{ + char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1]; + + p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0); + return (savestring (p)); +} + +/* Integer to string conversion. This conses the string using strdup; + caller should free it and be prepared to deal with NULL return. */ +char * +mitos (i) + intmax_t i; +{ + char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1]; + + p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0); + return (strdup (p)); +} + +char * +uinttostr (i, buf, len) + uintmax_t i; + char *buf; + size_t len; +{ + return (fmtumax (i, 10, buf, len, FL_UNSIGNED)); +} + +/* Integer to string conversion. This conses the string; the + caller should free it. */ +char * +uitos (i) + uintmax_t i; +{ + char *p, lbuf[INT_STRLEN_BOUND(uintmax_t) + 1]; + + p = fmtumax (i, 10, lbuf, sizeof(lbuf), FL_UNSIGNED); + return (savestring (p)); +} diff --git a/lib/sh/mailstat.c b/lib/sh/mailstat.c new file mode 100644 index 0000000..bd5c25f --- /dev/null +++ b/lib/sh/mailstat.c @@ -0,0 +1,159 @@ +/* mailstat.c -- stat a mailbox file, handling maildir-type mail directories */ + +/* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <stdio.h> +#include <errno.h> + +#include <bashtypes.h> +#include <posixstat.h> +#include <posixdir.h> +#include <bashansi.h> + +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif + +#include <maxpath.h> + +/* + * Stat a file. If it's a maildir, check all messages + * in the maildir and present the grand total as a file. + * The fields in the 'struct stat' are from the mail directory. + * The following fields are emulated: + * + * st_nlink always 1, unless st_blocks is not present, in which case it's + * the total number of messages + * st_size total number of bytes in all files + * st_blocks total number of messages, if present in struct stat + * st_atime access time of newest file in maildir + * st_mtime modify time of newest file in maildir + * st_mode S_IFDIR changed to S_IFREG + * + * This is good enough for most mail-checking applications. + */ + +int +mailstat(path, st) + const char *path; + struct stat *st; +{ + static struct stat st_new_last, st_ret_last; + struct stat st_ret, st_tmp; + DIR *dd; + struct dirent *fn; + char dir[PATH_MAX * 2], file[PATH_MAX * 2 + 1]; + int i, l; + time_t atime, mtime; + + atime = mtime = 0; + + /* First see if it's a directory. */ + if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0) + return i; + + if (strlen(path) > sizeof(dir) - 5) + { +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = EINVAL; +#endif + return -1; + } + + st_ret = *st; + st_ret.st_nlink = 1; + st_ret.st_size = 0; +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + st_ret.st_blocks = 0; +#else + st_ret.st_nlink = 0; +#endif + st_ret.st_mode &= ~S_IFDIR; + st_ret.st_mode |= S_IFREG; + + /* See if cur/ is present */ + sprintf(dir, "%s/cur", path); + if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) + return 0; + st_ret.st_atime = st_tmp.st_atime; + + /* See if tmp/ is present */ + sprintf(dir, "%s/tmp", path); + if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) + return 0; + st_ret.st_mtime = st_tmp.st_mtime; + + /* And new/ */ + sprintf(dir, "%s/new", path); + if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) + return 0; + st_ret.st_mtime = st_tmp.st_mtime; + + /* Optimization - if new/ didn't change, nothing else did. */ + if (st_tmp.st_dev == st_new_last.st_dev && + st_tmp.st_ino == st_new_last.st_ino && + st_tmp.st_atime == st_new_last.st_atime && + st_tmp.st_mtime == st_new_last.st_mtime) + { + *st = st_ret_last; + return 0; + } + st_new_last = st_tmp; + + /* Loop over new/ and cur/ */ + for (i = 0; i < 2; i++) + { + sprintf(dir, "%s/%s", path, i ? "cur" : "new"); + sprintf(file, "%s/", dir); + l = strlen(file); + if ((dd = opendir(dir)) == NULL) + return 0; + while ((fn = readdir(dd)) != NULL) + { + if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file)) + continue; + strcpy(file + l, fn->d_name); + if (stat(file, &st_tmp) != 0) + continue; + st_ret.st_size += st_tmp.st_size; +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + st_ret.st_blocks++; +#else + st_ret.st_nlink++; +#endif + if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime) + atime = st_tmp.st_atime; + if (st_tmp.st_mtime > mtime) + mtime = st_tmp.st_mtime; + } + closedir(dd); + } + +/* if (atime) */ /* Set atime even if cur/ is empty */ + st_ret.st_atime = atime; + if (mtime) + st_ret.st_mtime = mtime; + + *st = st_ret_last = st_ret; + return 0; +} diff --git a/lib/sh/makepath.c b/lib/sh/makepath.c new file mode 100644 index 0000000..ab46c96 --- /dev/null +++ b/lib/sh/makepath.c @@ -0,0 +1,128 @@ +/* makepath.c - glue PATH and DIR together into a full pathname. */ + +/* Copyright (C) 1987-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <bashansi.h> +#include "shell.h" + +#include <tilde/tilde.h> + +#ifndef NULL +# define NULL 0 +#endif + +/* MAKE SURE THESE AGREE WITH ../../externs.h. */ + +#ifndef MP_DOTILDE +# define MP_DOTILDE 0x01 +# define MP_DOCWD 0x02 +# define MP_RMDOT 0x04 +# define MP_IGNDOT 0x08 +#endif + +extern char *get_working_directory PARAMS((char *)); + +static char *nullpath = ""; + +/* Take PATH, an element from, e.g., $CDPATH, and DIR, a directory name, + and paste them together into PATH/DIR. Tilde expansion is performed on + PATH if (flags & MP_DOTILDE) is non-zero. If PATH is NULL or the empty + string, it is converted to the current directory. A full pathname is + used if (flags & MP_DOCWD) is non-zero, otherwise `./' is used. If + (flags & MP_RMDOT) is non-zero, any `./' is removed from the beginning + of DIR. If (flags & MP_IGNDOT) is non-zero, a PATH that is "." or "./" + is ignored. */ + +#define MAKEDOT() \ + do { \ + xpath = (char *)xmalloc (2); \ + xpath[0] = '.'; \ + xpath[1] = '\0'; \ + pathlen = 1; \ + } while (0) + +char * +sh_makepath (path, dir, flags) + const char *path, *dir; + int flags; +{ + int dirlen, pathlen; + char *ret, *xpath, *xdir, *r, *s; + + if (path == 0 || *path == '\0') + { + if (flags & MP_DOCWD) + { + xpath = get_working_directory ("sh_makepath"); + if (xpath == 0) + { + ret = get_string_value ("PWD"); + if (ret) + xpath = savestring (ret); + } + if (xpath == 0) + MAKEDOT(); + else + pathlen = strlen (xpath); + } + else + MAKEDOT(); + } + else if ((flags & MP_IGNDOT) && path[0] == '.' && (path[1] == '\0' || + (path[1] == '/' && path[2] == '\0'))) + { + xpath = nullpath; + pathlen = 0; + } + else + { + xpath = ((flags & MP_DOTILDE) && *path == '~') ? bash_tilde_expand (path, 0) : (char *)path; + pathlen = strlen (xpath); + } + + xdir = (char *)dir; + dirlen = strlen (xdir); + if ((flags & MP_RMDOT) && dir[0] == '.' && dir[1] == '/') + { + xdir += 2; + dirlen -= 2; + } + + r = ret = (char *)xmalloc (2 + dirlen + pathlen); + s = xpath; + while (*s) + *r++ = *s++; + if (s > xpath && s[-1] != '/') + *r++ = '/'; + s = xdir; + while (*r++ = *s++) + ; + if (xpath != path && xpath != nullpath) + free (xpath); + return (ret); +} diff --git a/lib/sh/mbscasecmp.c b/lib/sh/mbscasecmp.c new file mode 100644 index 0000000..0ab9560 --- /dev/null +++ b/lib/sh/mbscasecmp.c @@ -0,0 +1,79 @@ +/* mbscasecmp - case-insensitive multibyte string comparison. */ + +/* Copyright (C) 2009-2015 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_MBSCASECMP) && defined (HANDLE_MULTIBYTE) + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#include <wchar.h> +#include <wctype.h> + +/* Compare MBS1 and MBS2 without regard to case. */ +int +mbscasecmp (mbs1, mbs2) + const char *mbs1; + const char *mbs2; +{ + int len1, len2, mb_cur_max; + wchar_t c1, c2, l1, l2; + + len1 = len2 = 0; + /* Reset multibyte characters to their initial state. */ + (void) mblen ((char *) NULL, 0); + + mb_cur_max = MB_CUR_MAX; + do + { + len1 = mbtowc (&c1, mbs1, mb_cur_max); + len2 = mbtowc (&c2, mbs2, mb_cur_max); + + if (len1 == 0) + return len2 == 0 ? 0 : -1; + else if (len2 == 0) + return 1; + else if (len1 > 0 && len2 < 0) + return -1; + else if (len1 < 0 && len2 > 0) + return 1; + else if (len1 < 0 && len2 < 0) + { + len1 = strlen (mbs1); + len2 = strlen (mbs2); + return (len1 == len2 ? memcmp (mbs1, mbs2, len1) + : ((len1 < len2) ? (memcmp (mbs1, mbs2, len1) > 0 ? 1 : -1) + : (memcmp (mbs1, mbs2, len2) >= 0 ? 1 : -1))); + } + + l1 = towlower (c1); + l2 = towlower (c2); + + mbs1 += len1; + mbs2 += len2; + } + while (l1 == l2); + + return l1 - l2; +} + +#endif diff --git a/lib/sh/mbschr.c b/lib/sh/mbschr.c new file mode 100644 index 0000000..639962d --- /dev/null +++ b/lib/sh/mbschr.c @@ -0,0 +1,91 @@ +/* mbschr.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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include "bashansi.h" +#include "shmbutil.h" + +extern int locale_mb_cur_max; +extern int locale_utf8locale; + +#undef mbschr + +extern char *utf8_mbschr (const char *, int); /* XXX */ + +/* 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) +mbschr (const char *s, int c) +#else +mbschr (s, c) + const char *s; + int c; +#endif +{ +#if HANDLE_MULTIBYTE + char *pos; + mbstate_t state; + size_t strlength, mblength; + + if (locale_utf8locale && c < 0x80) + return (utf8_mbschr (s, c)); /* XXX */ + + /* 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' && locale_mb_cur_max > 1) + { + pos = (char *)s; + memset (&state, '\0', sizeof(mbstate_t)); + strlength = strlen (s); + + while (strlength > 0) + { + if (is_basic (*pos)) + mblength = 1; + else + { + mblength = mbrlen (pos, strlength, &state); + if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0) + mblength = 1; + } + + if (mblength == 1 && c == (unsigned char)*pos) + return pos; + + strlength -= mblength; + pos += mblength; + } + + return ((char *)NULL); + } + else +#endif + return (strchr (s, c)); +} diff --git a/lib/sh/mbscmp.c b/lib/sh/mbscmp.c new file mode 100644 index 0000000..c7c8443 --- /dev/null +++ b/lib/sh/mbscmp.c @@ -0,0 +1,77 @@ +/* mbscmp - multibyte string comparison. */ + +/* Copyright (C) 1995-2018 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_MBSCMP) && defined (HANDLE_MULTIBYTE) + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +extern int locale_utf8locale; + +extern int utf8_mbscmp (const char *, const char *); + +/* Compare MBS1 and MBS2. */ +int +mbscmp (mbs1, mbs2) + const char *mbs1; + const char *mbs2; +{ + int len1, len2, mb_cur_max; + wchar_t c1, c2; + + len1 = len2 = 0; + /* Reset multibyte characters to their initial state. */ + (void) mblen ((char *) NULL, 0); + + mb_cur_max = MB_CUR_MAX; + do + { + len1 = mbtowc (&c1, mbs1, mb_cur_max); + len2 = mbtowc (&c2, mbs2, mb_cur_max); + + if (len1 == 0) + return len2 == 0 ? 0 : -1; + else if (len2 == 0) + return 1; + else if (len1 > 0 && len2 < 0) + return -1; + else if (len1 < 0 && len2 > 0) + return 1; + else if (len1 < 0 && len2 < 0) + { + len1 = strlen (mbs1); + len2 = strlen (mbs2); + return (len1 == len2 ? memcmp (mbs1, mbs2, len1) + : ((len1 < len2) ? (memcmp (mbs1, mbs2, len1) > 0 ? 1 : -1) + : (memcmp (mbs1, mbs2, len2) >= 0 ? 1 : -1))); + } + + mbs1 += len1; + mbs2 += len2; + } + while (c1 == c2); + + return c1 - c2; +} + +#endif diff --git a/lib/sh/memset.c b/lib/sh/memset.c new file mode 100644 index 0000000..4ebc418 --- /dev/null +++ b/lib/sh/memset.c @@ -0,0 +1,29 @@ +/* memset.c -- set an area of memory to a given value */ + +/* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +char * +memset (char *str, int c, unsigned int len) +{ + register char *st = str; + + while (len-- > 0) + *st++ = c; + return str; +} diff --git a/lib/sh/mktime.c b/lib/sh/mktime.c new file mode 100644 index 0000000..9ee675b --- /dev/null +++ b/lib/sh/mktime.c @@ -0,0 +1,438 @@ +/* mktime - convert struct tm to a time_t value */ + +/* Copyright (C) 1993-2020 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + Contributed by Paul Eggert (eggert@twinsun.com). + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ +/* 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. */ +#endif +#include <time.h> + +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "bashansi.h" + +#if DEBUG_MKTIME +#include <stdio.h> +/* Make it work even if the system's libc has its own mktime routine. */ +#define mktime my_mktime +#endif /* DEBUG_MKTIME */ + +#ifndef PARAMS +#if defined (__GNUC__) || (defined (__STDC__) && __STDC__) +#define PARAMS(args) args +#else +#define PARAMS(args) () +#endif /* GCC. */ +#endif /* Not PARAMS. */ + +#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 + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : ~ TYPE_MAXIMUM (t))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) + +#ifndef TIME_T_MIN +# define TIME_T_MIN TYPE_MINIMUM (time_t) +#endif +#ifndef TIME_T_MAX +# define TIME_T_MAX TYPE_MAXIMUM (time_t) +#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 PARAMS ((int, int, int, int, int, const struct tm *)); +time_t __mktime_internal PARAMS ((struct tm *, + struct tm *(*) (const time_t *, struct tm *), + time_t *)); + + +static struct tm *my_localtime_r PARAMS ((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) PARAMS ((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_MKTIME + +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_MKTIME */ + +/* +Local Variables: +compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime" +End: +*/ diff --git a/lib/sh/netconn.c b/lib/sh/netconn.c new file mode 100644 index 0000000..e20f104 --- /dev/null +++ b/lib/sh/netconn.c @@ -0,0 +1,82 @@ +/* netconn.c -- is a particular file descriptor a network connection?. */ + +/* Copyright (C) 2002-2005 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) +# 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_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2) && !defined (__BEOS__) + int rv; + socklen_t l; + struct sockaddr sa; + + l = sizeof(sa); + rv = getpeername(fd, &sa, &l); + /* Posix.2 says getpeername can return these errors. */ + return ((rv < 0 && (errno == ENOTSOCK || errno == ENOTCONN || errno == EINVAL || errno == EBADF)) ? 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__ */ +} diff --git a/lib/sh/netopen.c b/lib/sh/netopen.c new file mode 100644 index 0000000..ee0baf6 --- /dev/null +++ b/lib/sh/netopen.c @@ -0,0 +1,351 @@ +/* + * netopen.c -- functions to make tcp/udp connections + * + * Chet Ramey + * chet@ins.CWRU.Edu + */ + +/* Copyright (C) 1987-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_NETWORK) + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <sys/types.h> + +#if defined (HAVE_SYS_SOCKET_H) +# include <sys/socket.h> +#endif + +#if defined (HAVE_NETINET_IN_H) +# include <netinet/in.h> +#endif + +#if defined (HAVE_NETDB_H) +# include <netdb.h> +#endif + +#if defined (HAVE_ARPA_INET_H) +# include <arpa/inet.h> +#endif + +#include <bashansi.h> +#include <bashintl.h> + +#include <errno.h> + +#include <shell.h> +#include <xmalloc.h> + +#ifndef errno +extern int errno; +#endif + +#if !defined (HAVE_INET_ATON) +extern int inet_aton PARAMS((const char *, struct in_addr *)); +#endif + +#ifndef HAVE_GETADDRINFO +static int _getaddr PARAMS((char *, struct in_addr *)); +static int _getserv PARAMS((char *, int, unsigned short *)); +static int _netopen4 PARAMS((char *, char *, int)); +#else /* HAVE_GETADDRINFO */ +static int _netopen6 PARAMS((char *, char *, int)); +#endif + +static int _netopen PARAMS((char *, char *, int)); + +#ifndef HAVE_GETADDRINFO +/* Stuff the internet address corresponding to HOST into AP, in network + byte order. Return 1 on success, 0 on failure. */ + +static int +_getaddr (host, ap) + char *host; + struct in_addr *ap; +{ + struct hostent *h; + int r; + + r = 0; + if (host[0] >= '0' && host[0] <= '9') + { + /* If the first character is a digit, guess that it's an + Internet address and return immediately if inet_aton succeeds. */ + r = inet_aton (host, ap); + if (r) + return r; + } +#if !defined (HAVE_GETHOSTBYNAME) + return 0; +#else + h = gethostbyname (host); + if (h && h->h_addr) + { + bcopy(h->h_addr, (char *)ap, h->h_length); + return 1; + } +#endif + return 0; + +} + +/* Return 1 if SERV is a valid port number and stuff the converted value into + PP in network byte order. */ +static int +_getserv (serv, proto, pp) + char *serv; + int proto; + unsigned short *pp; +{ + intmax_t l; + unsigned short s; + + if (legal_number (serv, &l)) + { + s = (unsigned short)(l & 0xFFFF); + if (s != l) + return (0); + s = htons (s); + if (pp) + *pp = s; + return 1; + } + else +#if defined (HAVE_GETSERVBYNAME) + { + struct servent *se; + + se = getservbyname (serv, (proto == 't') ? "tcp" : "udp"); + if (se == 0) + return 0; + if (pp) + *pp = se->s_port; /* ports returned in network byte order */ + return 1; + } +#else /* !HAVE_GETSERVBYNAME */ + return 0; +#endif /* !HAVE_GETSERVBYNAME */ +} + +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses the + * traditional BSD mechanisms. Returns the connected socket or -1 on error. + */ +static int +_netopen4(host, serv, typ) + char *host, *serv; + int typ; +{ + struct in_addr ina; + struct sockaddr_in sin; + unsigned short p; + int s, e; + + if (_getaddr(host, &ina) == 0) + { + internal_error (_("%s: host unknown"), host); + errno = EINVAL; + return -1; + } + + if (_getserv(serv, typ, &p) == 0) + { + internal_error(_("%s: invalid service"), serv); + errno = EINVAL; + return -1; + } + + memset ((char *)&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = p; + sin.sin_addr = ina; + + s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0); + if (s < 0) + { + sys_error ("socket"); + return (-1); + } + + if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) + { + e = errno; + sys_error("connect"); + close(s); + errno = e; + return (-1); + } + + return(s); +} +#endif /* ! HAVE_GETADDRINFO */ + +#ifdef HAVE_GETADDRINFO +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) + * which provides support for IPv6. Returns the connected socket or -1 + * on error. + */ +static int +_netopen6 (host, serv, typ) + char *host, *serv; + int typ; +{ + int s, e; + struct addrinfo hints, *res, *res0; + int gerr; + + 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; +#else + hints.ai_family = PF_UNSPEC; +#endif + hints.ai_socktype = (typ == 't') ? SOCK_STREAM : SOCK_DGRAM; + + gerr = getaddrinfo (host, serv, &hints, &res0); + if (gerr) + { + if (gerr == EAI_SERVICE) + internal_error ("%s: %s", serv, gai_strerror (gerr)); + else + internal_error ("%s: %s", host, gai_strerror (gerr)); + errno = EINVAL; + return -1; + } + + for (res = res0; res; res = res->ai_next) + { + if ((s = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) + { + if (res->ai_next) + continue; + sys_error ("socket"); + freeaddrinfo (res0); + return -1; + } + if (connect (s, res->ai_addr, res->ai_addrlen) < 0) + { + if (res->ai_next) + { + close (s); + continue; + } + e = errno; + sys_error ("connect"); + close (s); + freeaddrinfo (res0); + errno = e; + return -1; + } + freeaddrinfo (res0); + break; + } + return s; +} +#endif /* HAVE_GETADDRINFO */ + +/* + * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) + * if available, falling back to the traditional BSD mechanisms otherwise. + * Returns the connected socket or -1 on error. + */ +static int +_netopen(host, serv, typ) + char *host, *serv; + int typ; +{ +#ifdef HAVE_GETADDRINFO + return (_netopen6 (host, serv, typ)); +#else + return (_netopen4 (host, serv, typ)); +#endif +} + +/* + * Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to + * host `host' on port `port' and return the connected socket. + */ +int +netopen (path) + char *path; +{ + char *np, *s, *t; + int fd; + + np = (char *)xmalloc (strlen (path) + 1); + strcpy (np, path); + + s = np + 9; + t = strchr (s, '/'); + if (t == 0) + { + internal_error (_("%s: bad network path specification"), path); + free (np); + return -1; + } + *t++ = '\0'; + fd = _netopen (s, t, path[5]); + free (np); + + return fd; +} + +#if 0 +/* + * Open a TCP connection to host `host' on the port defined for service + * `serv' and return the connected socket. + */ +int +tcpopen (host, serv) + char *host, *serv; +{ + return (_netopen (host, serv, 't')); +} + +/* + * Open a UDP connection to host `host' on the port defined for service + * `serv' and return the connected socket. + */ +int +udpopen (host, serv) + char *host, *serv; +{ + return _netopen (host, serv, 'u'); +} +#endif + +#else /* !HAVE_NETWORK */ + +int +netopen (path) + char *path; +{ + internal_error (_("network operations not supported")); + return -1; +} + +#endif /* !HAVE_NETWORK */ diff --git a/lib/sh/oslib.c b/lib/sh/oslib.c new file mode 100644 index 0000000..edc5c6f --- /dev/null +++ b/lib/sh/oslib.c @@ -0,0 +1,301 @@ +/* oslib.c - functions present only in some unix versions. */ + +/* Copyright (C) 1995,2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#include <posixstat.h> +#include <filecntl.h> +#include <bashansi.h> + +#if !defined (HAVE_KILLPG) +# include <signal.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <chartypes.h> + +#include <shell.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* Make the functions strchr and strrchr if they do not exist. */ +#if !defined (HAVE_STRCHR) +char * +strchr (string, c) + char *string; + int c; +{ + register char *s; + + for (s = string; s && *s; s++) + if (*s == c) + return (s); + + return ((char *) NULL); +} + +char * +strrchr (string, c) + char *string; + int c; +{ + register char *s, *t; + + for (s = string, t = (char *)NULL; s && *s; s++) + if (*s == c) + t = s; + return (t); +} +#endif /* !HAVE_STRCHR */ + +#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN) +/* Replacement for dup2 (), for those systems which either don't have it, + or supply one with broken behaviour. */ +int +dup2 (fd1, fd2) + int fd1, fd2; +{ + int saved_errno, r; + + /* If FD1 is not a valid file descriptor, then return immediately with + an error. */ + if (fcntl (fd1, F_GETFL, 0) == -1) + return (-1); + + if (fd2 < 0 || fd2 >= getdtablesize ()) + { + errno = EBADF; + return (-1); + } + + if (fd1 == fd2) + return (0); + + saved_errno = errno; + + (void) close (fd2); + r = fcntl (fd1, F_DUPFD, fd2); + + if (r >= 0) + errno = saved_errno; + else + if (errno == EINVAL) + errno = EBADF; + + /* Force the new file descriptor to remain open across exec () calls. */ + SET_OPEN_ON_EXEC (fd2); + return (r); +} +#endif /* !HAVE_DUP2 */ + +/* + * Return the total number of available file descriptors. + * + * On some systems, like 4.2BSD and its descendants, there is a system call + * that returns the size of the descriptor table: getdtablesize(). There are + * lots of ways to emulate this on non-BSD systems. + * + * On System V.3, this can be obtained via a call to ulimit: + * return (ulimit(4, 0L)); + * + * On other System V systems, NOFILE is defined in /usr/include/sys/param.h + * (this is what we assume below), so we can simply use it: + * return (NOFILE); + * + * On POSIX systems, there are specific functions for retrieving various + * configuration parameters: + * return (sysconf(_SC_OPEN_MAX)); + * + */ + +#if !defined (HAVE_GETDTABLESIZE) +int +getdtablesize () +{ +# if defined (_POSIX_VERSION) && defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX) + return (sysconf(_SC_OPEN_MAX)); /* Posix systems use sysconf */ +# else /* ! (_POSIX_VERSION && HAVE_SYSCONF && _SC_OPEN_MAX) */ +# if defined (ULIMIT_MAXFDS) + return (ulimit (4, 0L)); /* System V.3 systems use ulimit(4, 0L) */ +# else /* !ULIMIT_MAXFDS */ +# if defined (NOFILE) /* Other systems use NOFILE */ + return (NOFILE); +# else /* !NOFILE */ + return (20); /* XXX - traditional value is 20 */ +# endif /* !NOFILE */ +# endif /* !ULIMIT_MAXFDS */ +# endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */ +} +#endif /* !HAVE_GETDTABLESIZE */ + +#if !defined (HAVE_BCOPY) +# if defined (bcopy) +# undef bcopy +# endif +void +bcopy (s,d,n) + void *d, *s; + size_t n; +{ + FASTCOPY (s, d, n); +} +#endif /* !HAVE_BCOPY */ + +#if !defined (HAVE_BZERO) +# if defined (bzero) +# undef bzero +# endif +void +bzero (s, n) + void *s; + size_t n; +{ + register int i; + register char *r; + + for (i = 0, r = s; i < n; i++) + *r++ = '\0'; +} +#endif + +#if !defined (HAVE_GETHOSTNAME) +# if defined (HAVE_UNAME) +# include <sys/utsname.h> +int +gethostname (name, namelen) + char *name; + size_t namelen; +{ + int i; + struct utsname ut; + + --namelen; + + uname (&ut); + i = strlen (ut.nodename) + 1; + strncpy (name, ut.nodename, i < namelen ? i : namelen); + name[namelen] = '\0'; + return (0); +} +# else /* !HAVE_UNAME */ +int +gethostname (name, namelen) + char *name; + size_t namelen; +{ + strncpy (name, "unknown", namelen); + name[namelen] = '\0'; + return 0; +} +# endif /* !HAVE_UNAME */ +#endif /* !HAVE_GETHOSTNAME */ + +#if !defined (HAVE_KILLPG) +int +killpg (pgrp, sig) + pid_t pgrp; + int sig; +{ + return (kill (-pgrp, sig)); +} +#endif /* !HAVE_KILLPG */ + +#if !defined (HAVE_MKFIFO) && defined (PROCESS_SUBSTITUTION) +int +mkfifo (path, mode) + char *path; + mode_t mode; +{ +#if defined (S_IFIFO) + return (mknod (path, (mode | S_IFIFO), 0)); +#else /* !S_IFIFO */ + return (-1); +#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); +} diff --git a/lib/sh/pathcanon.c b/lib/sh/pathcanon.c new file mode 100644 index 0000000..7d0df9f --- /dev/null +++ b/lib/sh/pathcanon.c @@ -0,0 +1,234 @@ +/* pathcanon.c -- canonicalize and manipulate pathnames. */ + +/* Copyright (C) 2000 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif +#include <posixstat.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <filecntl.h> +#include <bashansi.h> +#include <stdio.h> +#include <chartypes.h> +#include <errno.h> + +#include "shell.h" + +#if !defined (errno) +extern int errno; +#endif + +#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) + char *path; +{ + int l; + struct stat sb; + + /* This should leave errno set to the correct value. */ + errno = 0; + l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode); +#if defined (__CYGWIN__) + if (l == 0) + l = _is_cygdrive (path); +#endif + return l; +} + +/* Canonicalize PATH, and return a new path. The new path differs from PATH + in that: + Multiple `/'s are collapsed to a single `/'. + Leading `./'s and trailing `/.'s are removed. + Trailing `/'s are removed. + Non-leading `../'s and trailing `..'s are handled by removing + portions of the path. */ + +/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */ + +#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/') + +char * +sh_canonpath (path, flags) + char *path; + int flags; +{ + char stub_char; + char *result, *p, *q, *base, *dotdot; + int rooted, double_slash_path; + + /* The result cannot be larger than the input PATH. */ + result = (flags & PATH_NOALLOC) ? path : savestring (path); + + /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any + leading `x:' (dos drive name). */ + if (rooted = ROOTEDPATH(path)) + { + stub_char = DIRSEP; +#if defined (__CYGWIN__) + base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1; +#else + base = result + 1; +#endif + double_slash_path = DOUBLE_SLASH (path); + base += double_slash_path; + } + else + { + stub_char = '.'; +#if defined (__CYGWIN__) + base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result; +#else + base = result; +#endif + double_slash_path = 0; + } + + /* + * invariants: + * base points to the portion of the path we want to modify + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + p = q = dotdot = base; + + while (*p) + { + if (ISDIRSEP(p[0])) /* null element */ + p++; + else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */ + p += 1; /* don't count the separator in case it is nul */ + else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */ + { + p += 2; /* skip `..' */ + if (q > dotdot) /* can backtrack */ + { + if (flags & PATH_CHECKDOTDOT) + { + char c; + + /* Make sure what we have so far corresponds to a valid + path before we chop some of it off. */ + c = *q; + *q = '\0'; + if (_path_isdir (result) == 0) + { + if ((flags & PATH_NOALLOC) == 0) + free (result); + return ((char *)NULL); + } + *q = c; + } + + while (--q > dotdot && ISDIRSEP(*q) == 0) + ; + } + else if (rooted == 0) + { + /* /.. is / but ./../ is .. */ + if (q != base) + *q++ = DIRSEP; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + } + else /* real path element */ + { + /* add separator if not at start of work portion of result */ + if (q != base) + *q++ = DIRSEP; + while (*p && (ISDIRSEP(*p) == 0)) + *q++ = *p++; + /* Check here for a valid directory with _path_isdir. */ + if (flags & PATH_CHECKEXISTS) + { + char c; + + /* Make sure what we have so far corresponds to a valid + path before we chop some of it off. */ + c = *q; + *q = '\0'; + if (_path_isdir (result) == 0) + { + if ((flags & PATH_NOALLOC) == 0) + free (result); + return ((char *)NULL); + } + *q = c; + } + } + } + + /* Empty string is really ``.'' or `/', depending on what we started with. */ + if (q == result) + *q++ = stub_char; + *q = '\0'; + + /* If the result starts with `//', but the original path does not, we + can turn the // into /. Because of how we set `base', this should never + be true, but it's a sanity check. */ + if (DOUBLE_SLASH(result) && double_slash_path == 0) + { + if (result[2] == '\0') /* short-circuit for bare `//' */ + result[1] = '\0'; + else + memmove (result, result + 1, strlen (result + 1) + 1); + } + + return (result); +} diff --git a/lib/sh/pathphys.c b/lib/sh/pathphys.c new file mode 100644 index 0000000..95b72f1 --- /dev/null +++ b/lib/sh/pathphys.c @@ -0,0 +1,296 @@ +/* pathphys.c -- return pathname with all symlinks expanded. */ + +/* Copyright (C) 2000-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif +#include <posixstat.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <filecntl.h> +#include <bashansi.h> +#include <stdio.h> +#include <chartypes.h> +#include <errno.h> + +#include "shell.h" + +#if !defined (MAXSYMLINKS) +# define MAXSYMLINKS 32 +#endif + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern char *get_working_directory PARAMS((char *)); + +static int +_path_readlink (path, buf, bufsiz) + char *path; + char *buf; + int bufsiz; +{ +#ifdef HAVE_READLINK + return readlink (path, buf, bufsiz); +#else + errno = EINVAL; + return -1; +#endif +} + +/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */ + +#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/') + +/* + * Return PATH with all symlinks expanded in newly-allocated memory. + * This always gets an absolute pathname. + */ + +char * +sh_physpath (path, flags) + char *path; + int flags; +{ + char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1]; + char *result, *p, *q, *qsave, *qbase, *workpath; + int double_slash_path, linklen, nlink; + + linklen = strlen (path); + +#if 0 + /* First sanity check -- punt immediately if the name is too long. */ + if (linklen >= PATH_MAX) + return (savestring (path)); +#endif + + nlink = 0; + q = result = (char *)xmalloc (PATH_MAX + 1); + + /* Even if we get something longer than PATH_MAX, we might be able to + shorten it, so we try. */ + if (linklen >= PATH_MAX) + workpath = savestring (path); + else + { + workpath = (char *)xmalloc (PATH_MAX + 1); + strcpy (workpath, path); + } + + /* This always gets an absolute pathname. */ + + /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any + leading `x:' (dos drive name). */ +#if defined (__CYGWIN__) + qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1; +#else + qbase = workpath + 1; +#endif + double_slash_path = DOUBLE_SLASH (workpath); + qbase += double_slash_path; + + for (p = workpath; p < qbase; ) + *q++ = *p++; + qbase = q; + + /* + * invariants: + * qbase points to the portion of the result path we want to modify + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * + * XXX -- need to fix error checking for too-long pathnames + */ + + while (*p) + { + if (ISDIRSEP(p[0])) /* null element */ + p++; + else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */ + p += 1; /* don't count the separator in case it is nul */ + else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */ + { + p += 2; /* skip `..' */ + if (q > qbase) + { + while (--q > qbase && ISDIRSEP(*q) == 0) + ; + } + } + else /* real path element */ + { + /* add separator if not at start of work portion of result */ + qsave = q; + if (q != qbase) + *q++ = DIRSEP; + while (*p && (ISDIRSEP(*p) == 0)) + { + if (q - result >= PATH_MAX) + { +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = EINVAL; +#endif + goto error; + } + + *q++ = *p++; + } + + *q = '\0'; + + linklen = _path_readlink (result, linkbuf, PATH_MAX); + if (linklen < 0) /* if errno == EINVAL, it's not a symlink */ + { + if (errno != EINVAL) + goto error; + continue; + } + + /* It's a symlink, and the value is in LINKBUF. */ + nlink++; + if (nlink > MAXSYMLINKS) + { +#ifdef ELOOP + errno = ELOOP; +#else + errno = EINVAL; +#endif +error: + free (result); + free (workpath); + return ((char *)NULL); + } + + linkbuf[linklen] = '\0'; + + /* If the new path length would overrun PATH_MAX, punt now. */ + if ((strlen (p) + linklen + 2) >= PATH_MAX) + { +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = EINVAL; +#endif + goto error; + } + + /* Form the new pathname by copying the link value to a temporary + buffer and appending the rest of `workpath'. Reset p to point + to the start of the rest of the path. If the link value is an + absolute pathname, reset p, q, and qbase. If not, reset p + and q. */ + strcpy (tbuf, linkbuf); + tbuf[linklen] = '/'; + strcpy (tbuf + linklen, p); + strcpy (workpath, tbuf); + + if (ABSPATH(linkbuf)) + { + q = result; + /* Duplicating some code here... */ +#if defined (__CYGWIN__) + qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1; +#else + qbase = workpath + 1; +#endif + double_slash_path = DOUBLE_SLASH (workpath); + qbase += double_slash_path; + + for (p = workpath; p < qbase; ) + *q++ = *p++; + qbase = q; + } + else + { + p = workpath; + q = qsave; + } + } + } + + *q = '\0'; + free (workpath); + + /* If the result starts with `//', but the original path does not, we + can turn the // into /. Because of how we set `qbase', this should never + be true, but it's a sanity check. */ + if (DOUBLE_SLASH(result) && double_slash_path == 0) + { + if (result[2] == '\0') /* short-circuit for bare `//' */ + result[1] = '\0'; + else + memmove (result, result + 1, strlen (result + 1) + 1); + } + + return (result); +} + +char * +sh_realpath (pathname, resolved) + const char *pathname; + char *resolved; +{ + char *tdir, *wd; + + if (pathname == 0 || *pathname == '\0') + { + errno = (pathname == 0) ? EINVAL : ENOENT; + return ((char *)NULL); + } + + if (ABSPATH (pathname) == 0) + { + wd = get_working_directory ("sh_realpath"); + if (wd == 0) + return ((char *)NULL); + tdir = sh_makepath (wd, (char *)pathname, 0); + free (wd); + } + else + tdir = savestring (pathname); + + wd = sh_physpath (tdir, 0); + free (tdir); + + if (resolved == 0) + return (wd); + + if (wd) + { + strncpy (resolved, wd, PATH_MAX - 1); + resolved[PATH_MAX - 1] = '\0'; + free (wd); + return resolved; + } + else + { + resolved[0] = '\0'; + return wd; + } +} diff --git a/lib/sh/random.c b/lib/sh/random.c new file mode 100644 index 0000000..1eaa71a --- /dev/null +++ b/lib/sh/random.c @@ -0,0 +1,240 @@ +/* random.c -- Functions for managing 16-bit and 32-bit random numbers. */ + +/* Copyright (C) 2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" + +#if defined (HAVE_SYS_RANDOM_H) +# include <sys/random.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "filecntl.h" + +#include <stdio.h> +#include "bashansi.h" + +#include "shell.h" + +extern time_t shell_start_time; + +extern int last_random_value; + +static u_bits32_t intrand32 PARAMS((u_bits32_t)); +static u_bits32_t genseed PARAMS((void)); + +static u_bits32_t brand32 PARAMS((void)); +static void sbrand32 PARAMS((u_bits32_t)); +static void perturb_rand32 PARAMS((void)); + +/* The random number seed. You can change this by setting RANDOM. */ +static u_bits32_t rseed = 1; + +/* Returns a 32-bit pseudo-random number. */ +static u_bits32_t +intrand32 (last) + u_bits32_t last; +{ + /* Minimal Standard generator from + "Random number generators: good ones are hard to find", + Park and Miller, Communications of the ACM, vol. 31, no. 10, + October 1988, p. 1195. Filtered through FreeBSD. + + x(n+1) = 16807 * x(n) mod (m). + + We split up the calculations to avoid overflow. + + h = last / q; l = x - h * q; t = a * l - h * r + m = 2147483647, a = 16807, q = 127773, r = 2836 + + There are lots of other combinations of constants to use; look at + https://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html#Other-random-number-generators */ + + bits32_t h, l, t; + u_bits32_t ret; + + /* Can't seed with 0. */ + ret = (last == 0) ? 123459876 : last; + h = ret / 127773; + l = ret - (127773 * h); + t = 16807 * l - 2836 * h; + ret = (t < 0) ? t + 0x7fffffff : t; + + return (ret); +} + +static u_bits32_t +genseed () +{ + struct timeval tv; + u_bits32_t iv; + + gettimeofday (&tv, NULL); + iv = (u_bits32_t)seedrand; /* let the compiler truncate */ + iv = tv.tv_sec ^ tv.tv_usec ^ getpid () ^ getppid () ^ current_user.uid ^ iv; + return (iv); +} + +#define BASH_RAND_MAX 32767 /* 0x7fff - 16 bits */ + +/* Returns a pseudo-random number between 0 and 32767. */ +int +brand () +{ + unsigned int ret; + + rseed = intrand32 (rseed); + if (shell_compatibility_level > 50) + ret = (rseed >> 16) ^ (rseed & 65535); + else + ret = rseed; + return (ret & BASH_RAND_MAX); +} + +/* Set the random number generator seed to SEED. */ +void +sbrand (seed) + unsigned long seed; +{ + rseed = seed; + last_random_value = 0; +} + +void +seedrand () +{ + u_bits32_t iv; + + iv = genseed (); + sbrand (iv); +} + +static u_bits32_t rseed32 = 1073741823; +static int last_rand32; + +static int urandfd = -1; + +#define BASH_RAND32_MAX 0x7fffffff /* 32 bits */ + +/* Returns a 32-bit pseudo-random number between 0 and 4294967295. */ +static u_bits32_t +brand32 () +{ + u_bits32_t ret; + + rseed32 = intrand32 (rseed32); + return (rseed32 & BASH_RAND32_MAX); +} + +static void +sbrand32 (seed) + u_bits32_t seed; +{ + last_rand32 = rseed32 = seed; +} + +void +seedrand32 () +{ + u_bits32_t iv; + + iv = genseed (); + sbrand32 (iv); +} + +static void +perturb_rand32 () +{ + rseed32 ^= genseed (); +} + +/* Force another attempt to open /dev/urandom on the next call to get_urandom32 */ +void +urandom_close () +{ + if (urandfd >= 0) + close (urandfd); + urandfd = -1; +} + +#if !defined (HAVE_GETRANDOM) +/* Imperfect emulation of getrandom(2). */ +#ifndef GRND_NONBLOCK +# define GRND_NONBLOCK 1 +# define GRND_RANDOM 2 +#endif + +static ssize_t +getrandom (buf, len, flags) + void *buf; + size_t len; + unsigned int flags; +{ + int oflags; + ssize_t r; + static int urand_unavail = 0; + +#if HAVE_GETENTROPY + r = getentropy (buf, len); + return (r == 0) ? len : -1; +#endif + + if (urandfd == -1 && urand_unavail == 0) + { + oflags = O_RDONLY; + if (flags & GRND_NONBLOCK) + oflags |= O_NONBLOCK; + urandfd = open ("/dev/urandom", oflags, 0); + if (urandfd >= 0) + SET_CLOSE_ON_EXEC (urandfd); + else + { + urand_unavail = 1; + return -1; + } + } + if (urandfd >= 0 && (r = read (urandfd, buf, len)) == len) + return (r); + return -1; +} +#endif + +u_bits32_t +get_urandom32 () +{ + u_bits32_t ret; + + if (getrandom ((void *)&ret, sizeof (ret), GRND_NONBLOCK) == sizeof (ret)) + return (last_rand32 = ret); + +#if defined (HAVE_ARC4RANDOM) + ret = arc4random (); +#else + if (subshell_environment) + perturb_rand32 (); + do + ret = brand32 (); + while (ret == last_rand32); +#endif + return (last_rand32 = ret); +} diff --git a/lib/sh/rename.c b/lib/sh/rename.c new file mode 100644 index 0000000..e410b5e --- /dev/null +++ b/lib/sh/rename.c @@ -0,0 +1,76 @@ +/* + * rename - rename a file + */ + +/* Copyright (C) 1999 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#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; +{ + 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); + + if (unlink (from) < 0 && errno != ENOENT) + { + int e = errno; + unlink (to); + errno = e; + return (-1); + } + + return (0); +} +#endif /* !HAVE_RENAME */ diff --git a/lib/sh/setlinebuf.c b/lib/sh/setlinebuf.c new file mode 100644 index 0000000..dd76e9f --- /dev/null +++ b/lib/sh/setlinebuf.c @@ -0,0 +1,66 @@ +/* setlinebuf.c - line-buffer a stdio stream. */ + +/* Copyright (C) 1997,2022 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <stdio.h> + +#include <xmalloc.h> + +#if defined (USING_BASH_MALLOC) +# define LBUF_BUFSIZE 2016 +#else +# define LBUF_BUFSIZE BUFSIZ +#endif + +static char *stdoutbuf = 0; +static char *stderrbuf = 0; + +/* Cause STREAM to buffer lines as opposed to characters or blocks. */ +int +sh_setlinebuf (stream) + FILE *stream; +{ +#if !defined (HAVE_SETLINEBUF) && !defined (HAVE_SETVBUF) + return (0); +#endif + +#if defined (HAVE_SETVBUF) + char *local_linebuf; + +#if defined (USING_BASH_MALLOC) + if (stream == stdout && stdoutbuf == 0) + local_linebuf = stdoutbuf = (char *)xmalloc (LBUF_BUFSIZE); + else if (stream == stderr && stderrbuf == 0) + local_linebuf = stderrbuf = (char *)xmalloc (LBUF_BUFSIZE); + else + local_linebuf = (char *)NULL; /* let stdio handle it */ +#else + local_linebuf = (char *)NULL; +#endif + + return (setvbuf (stream, local_linebuf, _IOLBF, LBUF_BUFSIZE)); +#else /* !HAVE_SETVBUF */ + + setlinebuf (stream); + return (0); + +#endif /* !HAVE_SETVBUF */ +} diff --git a/lib/sh/shmatch.c b/lib/sh/shmatch.c new file mode 100644 index 0000000..a717d45 --- /dev/null +++ b/lib/sh/shmatch.c @@ -0,0 +1,132 @@ +/* + * shmatch.c -- shell interface to posix regular expression matching. + */ + +/* Copyright (C) 2003-2022 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined (HAVE_POSIX_REGEXP) + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <regex.h> + +#include "shell.h" +#include "variables.h" +#include "externs.h" + +extern int glob_ignore_case, match_ignore_case; + +#if defined (ARRAY_VARS) +extern SHELL_VAR *builtin_find_indexed_array (char *, int); +#endif + +int +sh_regmatch (string, pattern, flags) + const char *string; + const char *pattern; + int flags; +{ + regex_t regex = { 0 }; + regmatch_t *matches; + int rflags; +#if defined (ARRAY_VARS) + SHELL_VAR *rematch; + ARRAY *amatch; + int subexp_ind; + char *subexp_str; + int subexp_len; +#endif + int result; + +#if defined (ARRAY_VARS) + rematch = (SHELL_VAR *)NULL; +#endif + + rflags = REG_EXTENDED; + if (match_ignore_case) + rflags |= REG_ICASE; +#if !defined (ARRAY_VARS) + rflags |= REG_NOSUB; +#endif + + if (regcomp (®ex, pattern, rflags)) + return 2; /* flag for printing a warning here. */ + +#if defined (ARRAY_VARS) + matches = (regmatch_t *)malloc (sizeof (regmatch_t) * (regex.re_nsub + 1)); +#else + matches = NULL; +#endif + + /* man regexec: NULL PMATCH ignored if NMATCH == 0 */ + if (regexec (®ex, string, matches ? regex.re_nsub + 1 : 0, matches, 0)) + result = EXECUTION_FAILURE; + else + result = EXECUTION_SUCCESS; /* match */ + +#if defined (ARRAY_VARS) + subexp_len = strlen (string) + 10; + subexp_str = malloc (subexp_len + 1); + + /* Store the parenthesized subexpressions in the array BASH_REMATCH. + Element 0 is the portion that matched the entire regexp. Element 1 + is the part that matched the first subexpression, and so on. */ +#if 1 + unbind_global_variable_noref ("BASH_REMATCH"); + rematch = make_new_array_variable ("BASH_REMATCH"); +#else + /* TAG:bash-5.3 */ + rematch = builtin_find_indexed_array ("BASH_REMATCH", 1); +#endif + amatch = rematch ? array_cell (rematch) : (ARRAY *)0; + + if (matches && amatch && (flags & SHMAT_SUBEXP) && result == EXECUTION_SUCCESS && subexp_str) + { + for (subexp_ind = 0; subexp_ind <= regex.re_nsub; subexp_ind++) + { + memset (subexp_str, 0, subexp_len); + strncpy (subexp_str, string + matches[subexp_ind].rm_so, + matches[subexp_ind].rm_eo - matches[subexp_ind].rm_so); + array_insert (amatch, subexp_ind, subexp_str); + } + } + +#if 0 + VSETATTR (rematch, att_readonly); +#endif + + free (subexp_str); + free (matches); +#endif /* ARRAY_VARS */ + + regfree (®ex); + + return result; +} + +#endif /* HAVE_POSIX_REGEXP */ diff --git a/lib/sh/shmbchar.c b/lib/sh/shmbchar.c new file mode 100644 index 0000000..f2f2582 --- /dev/null +++ b/lib/sh/shmbchar.c @@ -0,0 +1,137 @@ +/* Copyright (C) 2001, 2006, 2009, 2010, 2012, 2015-2018 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + + +#include <config.h> + +#if defined (HANDLE_MULTIBYTE) +#include <stdlib.h> +#include <limits.h> + +#include <errno.h> + +#include <shmbutil.h> +#include <shmbchar.h> + +#ifndef errno +extern int errno; +#endif + +#if IS_BASIC_ASCII + +/* Bit table of characters in the ISO C "basic character set". */ +const unsigned int is_basic_table [UCHAR_MAX / 32 + 1] = +{ + 0x00001a00, /* '\t' '\v' '\f' */ + 0xffffffef, /* ' '...'#' '%'...'?' */ + 0xfffffffe, /* 'A'...'Z' '[' '\\' ']' '^' '_' */ + 0x7ffffffe /* 'a'...'z' '{' '|' '}' '~' */ + /* The remaining bits are 0. */ +}; + +#endif /* IS_BASIC_ASCII */ + +extern int locale_utf8locale; + +extern char *utf8_mbsmbchar (const char *); +extern int utf8_mblen (const char *, size_t); + +/* Count the number of characters in S, counting multi-byte characters as a + single character. */ +size_t +mbstrlen (s) + const char *s; +{ + size_t clen, nc; + mbstate_t mbs = { 0 }, mbsbak = { 0 }; + int f, mb_cur_max; + + nc = 0; + mb_cur_max = MB_CUR_MAX; + while (*s && (clen = (f = is_basic (*s)) ? 1 : mbrlen(s, mb_cur_max, &mbs)) != 0) + { + if (MB_INVALIDCH(clen)) + { + clen = 1; /* assume single byte */ + mbs = mbsbak; + } + + if (f == 0) + mbsbak = mbs; + + s += clen; + nc++; + } + return nc; +} + +/* Return pointer to first multibyte char in S, or NULL if none. */ +/* XXX - if we know that the locale is UTF-8, we can just check whether or + not any byte has the eighth bit turned on */ +char * +mbsmbchar (s) + const char *s; +{ + char *t; + size_t clen; + mbstate_t mbs = { 0 }; + int mb_cur_max; + + if (locale_utf8locale) + return (utf8_mbsmbchar (s)); /* XXX */ + + mb_cur_max = MB_CUR_MAX; + for (t = (char *)s; *t; t++) + { + if (is_basic (*t)) + continue; + + if (locale_utf8locale) /* not used if above code active */ + clen = utf8_mblen (t, mb_cur_max); + else + clen = mbrlen (t, mb_cur_max, &mbs); + + if (clen == 0) + return 0; + if (MB_INVALIDCH(clen)) + continue; + + if (clen > 1) + return t; + } + return 0; +} + +int +sh_mbsnlen(src, srclen, maxlen) + const char *src; + size_t srclen; + int maxlen; +{ + int count; + int sind; + DECLARE_MBSTATE; + + for (sind = count = 0; src[sind]; ) + { + count++; /* number of multibyte characters */ + ADVANCE_CHAR (src, srclen, sind); + if (sind > maxlen) + break; + } + + return count; +} +#endif diff --git a/lib/sh/shquote.c b/lib/sh/shquote.c new file mode 100644 index 0000000..26fe018 --- /dev/null +++ b/lib/sh/shquote.c @@ -0,0 +1,432 @@ +/* shquote - functions to quote and dequote strings */ + +/* Copyright (C) 1999-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include <stdc.h> + +#include "syntax.h" +#include <xmalloc.h> + +#include "shmbchar.h" +#include "shmbutil.h" + +extern char *ansic_quote PARAMS((char *, int, int *)); +extern int ansic_shouldquote PARAMS((const char *)); + +/* Default set of characters that should be backslash-quoted in strings */ +static const char bstab[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, /* TAB, NL */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 1, 1, 1, 0, 1, 0, 1, 1, /* SPACE, !, DQUOTE, DOL, AMP, SQUOTE */ + 1, 1, 1, 0, 1, 0, 0, 0, /* LPAR, RPAR, STAR, COMMA */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 1, 1, /* SEMI, LESSTHAN, GREATERTHAN, QUEST */ + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, /* LBRACK, BS, RBRACK, CARAT */ + + 1, 0, 0, 0, 0, 0, 0, 0, /* BACKQ */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, /* LBRACE, BAR, RBRACE */ + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + +/* **************************************************************** */ +/* */ +/* Functions for quoting strings to be re-read as input */ +/* */ +/* **************************************************************** */ + +/* Return a new string which is the single-quoted version of STRING. + Used by alias and trap, among others. */ +char * +sh_single_quote (string) + const char *string; +{ + register int c; + char *result, *r; + const char *s; + + result = (char *)xmalloc (3 + (4 * strlen (string))); + r = result; + + if (string[0] == '\'' && string[1] == 0) + { + *r++ = '\\'; + *r++ = '\''; + *r++ = 0; + return result; + } + + *r++ = '\''; + + for (s = string; s && (c = *s); s++) + { + *r++ = c; + + if (c == '\'') + { + *r++ = '\\'; /* insert escaped single quote */ + *r++ = '\''; + *r++ = '\''; /* start new quoted string */ + } + } + + *r++ = '\''; + *r = '\0'; + + return (result); +} + +/* Quote STRING using double quotes. Return a new string. */ +char * +sh_double_quote (string) + const char *string; +{ + register unsigned char c; + int mb_cur_max; + char *result, *r; + size_t slen; + const char *s, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + mb_cur_max = MB_CUR_MAX; + + result = (char *)xmalloc (3 + (2 * strlen (string))); + r = result; + *r++ = '"'; + + for (s = string; s && (c = *s); s++) + { + /* Backslash-newline disappears within double quotes, so don't add one. */ + if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n') + *r++ = '\\'; + +#if defined (HANDLE_MULTIBYTE) + if ((locale_utf8locale && (c & 0x80)) || + (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0)) + { + COPY_CHAR_P (r, s, send); + s--; /* compensate for auto-increment in loop above */ + continue; + } +#endif + + /* Assume that the string will not be further expanded, so no need to + add CTLESC to protect CTLESC or CTLNUL. */ + *r++ = c; + } + + *r++ = '"'; + *r = '\0'; + + return (result); +} + +/* Turn S into a simple double-quoted string. If FLAGS is non-zero, quote + double quote characters in S with backslashes. */ +char * +sh_mkdoublequoted (s, slen, flags) + const char *s; + int slen, flags; +{ + char *r, *ret; + const char *send; + int rlen, mb_cur_max; + DECLARE_MBSTATE; + + send = s + slen; + mb_cur_max = flags ? MB_CUR_MAX : 1; + rlen = (flags == 0) ? slen + 3 : (2 * slen) + 1; + ret = r = (char *)xmalloc (rlen); + + *r++ = '"'; + while (*s) + { + if (flags && *s == '"') + *r++ = '\\'; + +#if defined (HANDLE_MULTIBYTE) + if (flags && ((locale_utf8locale && (*s & 0x80)) || + (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (*s) == 0))) + { + COPY_CHAR_P (r, s, send); + continue; + } +#endif + *r++ = *s++; + } + *r++ = '"'; + *r = '\0'; + + return ret; +} + +/* Remove backslashes that are quoting characters that are special between + double quotes. Return a new string. XXX - should this handle CTLESC + and CTLNUL? */ +char * +sh_un_double_quote (string) + char *string; +{ + register int c, pass_next; + char *result, *r, *s; + + r = result = (char *)xmalloc (strlen (string) + 1); + + for (pass_next = 0, s = string; s && (c = *s); s++) + { + if (pass_next) + { + *r++ = c; + pass_next = 0; + continue; + } + if (c == '\\' && (sh_syntaxtab[(unsigned char) s[1]] & CBSDQUOTE)) + { + pass_next = 1; + continue; + } + *r++ = c; + } + + *r = '\0'; + return result; +} + +/* Quote special characters in STRING using backslashes. Return a new + string. NOTE: if the string is to be further expanded, we need a + way to protect the CTLESC and CTLNUL characters. As I write this, + the current callers will never cause the string to be expanded without + going through the shell parser, which will protect the internal + quoting characters. TABLE, if set, points to a map of the ascii code + set with char needing to be backslash-quoted if table[char]==1. FLAGS, + if 1, causes tildes to be quoted as well. If FLAGS&2, backslash-quote + other shell blank characters. */ + +char * +sh_backslash_quote (string, table, flags) + char *string; + char *table; + int flags; +{ + int c, mb_cur_max; + size_t slen; + char *result, *r, *s, *backslash_table, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + result = (char *)xmalloc (2 * slen + 1); + + backslash_table = table ? table : (char *)bstab; + mb_cur_max = MB_CUR_MAX; + + for (r = result, s = string; s && (c = *s); s++) + { +#if defined (HANDLE_MULTIBYTE) + /* XXX - isascii, even if is_basic(c) == 0 - works in most cases. */ + if (c >= 0 && c <= 127 && backslash_table[(unsigned char)c] == 1) + { + *r++ = '\\'; + *r++ = c; + continue; + } + if ((locale_utf8locale && (c & 0x80)) || + (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0)) + { + COPY_CHAR_P (r, s, send); + s--; /* compensate for auto-increment in loop above */ + continue; + } +#endif + if (backslash_table[(unsigned char)c] == 1) + *r++ = '\\'; + else if (c == '#' && s == string) /* comment char */ + *r++ = '\\'; + else if ((flags&1) && c == '~' && (s == string || s[-1] == ':' || s[-1] == '=')) + /* Tildes are special at the start of a word or after a `:' or `=' + (technically unquoted, but it doesn't make a difference in practice) */ + *r++ = '\\'; + else if ((flags&2) && shellblank((unsigned char)c)) + *r++ = '\\'; + *r++ = c; + } + + *r = '\0'; + return (result); +} + +#if defined (PROMPT_STRING_DECODE) || defined (TRANSLATABLE_STRINGS) +/* Quote characters that get special treatment when in double quotes in STRING + using backslashes. FLAGS is reserved for future use. Return a new string. */ +char * +sh_backslash_quote_for_double_quotes (string, flags) + char *string; + int flags; +{ + unsigned char c; + char *result, *r, *s, *send; + size_t slen; + int mb_cur_max; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + mb_cur_max = MB_CUR_MAX; + result = (char *)xmalloc (2 * slen + 1); + + for (r = result, s = string; s && (c = *s); s++) + { + /* Backslash-newline disappears within double quotes, so don't add one. */ + if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n') + *r++ = '\\'; + /* I should probably use the CSPECL flag for these in sh_syntaxtab[] */ + else if (c == CTLESC || c == CTLNUL) + *r++ = CTLESC; /* could be '\\'? */ + +#if defined (HANDLE_MULTIBYTE) + if ((locale_utf8locale && (c & 0x80)) || + (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0)) + { + COPY_CHAR_P (r, s, send); + s--; /* compensate for auto-increment in loop above */ + continue; + } +#endif + + *r++ = c; + } + + *r = '\0'; + return (result); +} +#endif /* PROMPT_STRING_DECODE */ + +char * +sh_quote_reusable (s, flags) + char *s; + int flags; +{ + char *ret; + + if (s == 0) + return s; + else if (*s == 0) + { + ret = (char *)xmalloc (3); + ret[0] = ret[1] = '\''; + ret[2] = '\0'; + } + else if (ansic_shouldquote (s)) + ret = ansic_quote (s, 0, (int *)0); + else if (flags) + ret = sh_backslash_quote (s, 0, 1); + else + ret = sh_single_quote (s); + + return ret; +} + +int +sh_contains_shell_metas (string) + const char *string; +{ + const char *s; + + for (s = string; s && *s; s++) + { + switch (*s) + { + case ' ': case '\t': case '\n': /* IFS white space */ + case '\'': case '"': case '\\': /* quoting chars */ + case '|': case '&': case ';': /* shell metacharacters */ + case '(': case ')': case '<': case '>': + case '!': case '{': case '}': /* reserved words */ + case '*': case '[': case '?': case ']': /* globbing chars */ + case '^': + case '$': case '`': /* expansion chars */ + return (1); + case '~': /* tilde expansion */ + if (s == string || s[-1] == '=' || s[-1] == ':') + return (1); + break; + case '#': + if (s == string) /* comment char */ + return (1); + /* FALLTHROUGH */ + default: + break; + } + } + + return (0); +} + +int +sh_contains_quotes (string) + const char *string; +{ + const char *s; + + for (s = string; s && *s; s++) + { + if (*s == '\'' || *s == '"' || *s == '\\') + return 1; + } + return 0; +} diff --git a/lib/sh/shtty.c b/lib/sh/shtty.c new file mode 100644 index 0000000..0433f5e --- /dev/null +++ b/lib/sh/shtty.c @@ -0,0 +1,330 @@ +/* + * shtty.c -- abstract interface to the terminal, focusing on capabilities. + */ + +/* Copyright (C) 1999 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <shtty.h> + +static TTYSTRUCT ttin, ttout; +static int ttsaved = 0; + +int +ttgetattr(fd, ttp) +int fd; +TTYSTRUCT *ttp; +{ +#ifdef TERMIOS_TTY_DRIVER + return tcgetattr(fd, ttp); +#else +# ifdef TERMIO_TTY_DRIVER + return ioctl(fd, TCGETA, ttp); +# else + return ioctl(fd, TIOCGETP, ttp); +# endif +#endif +} + +int +ttsetattr(fd, ttp) +int fd; +TTYSTRUCT *ttp; +{ +#ifdef TERMIOS_TTY_DRIVER + return tcsetattr(fd, TCSADRAIN, ttp); +#else +# ifdef TERMIO_TTY_DRIVER + return ioctl(fd, TCSETAW, ttp); +# else + return ioctl(fd, TIOCSETN, ttp); +# endif +#endif +} + +void +ttsave() +{ + if (ttsaved) + return; + ttgetattr (0, &ttin); + ttgetattr (1, &ttout); + ttsaved = 1; +} + +void +ttrestore() +{ + if (ttsaved == 0) + return; + ttsetattr (0, &ttin); + ttsetattr (1, &ttout); + ttsaved = 0; +} + +/* Retrieve the internally-saved attributes associated with tty fd FD. */ +TTYSTRUCT * +ttattr (fd) + int fd; +{ + if (ttsaved == 0) + return ((TTYSTRUCT *)0); + if (fd == 0) + return &ttin; + else if (fd == 1) + return &ttout; + else + return ((TTYSTRUCT *)0); +} + +/* + * Change attributes in ttp so that when it is installed using + * ttsetattr, the terminal will be in one-char-at-a-time mode. + */ +int +tt_setonechar(ttp) + TTYSTRUCT *ttp; +{ +#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) + + /* XXX - might not want this -- it disables erase and kill processing. */ + ttp->c_lflag &= ~ICANON; + + ttp->c_lflag |= ISIG; +# ifdef IEXTEN + ttp->c_lflag |= IEXTEN; +# endif + + ttp->c_iflag |= ICRNL; /* make sure we get CR->NL on input */ + ttp->c_iflag &= ~INLCR; /* but no NL->CR */ + +# ifdef OPOST + ttp->c_oflag |= OPOST; +# endif +# ifdef ONLCR + ttp->c_oflag |= ONLCR; +# endif +# ifdef OCRNL + ttp->c_oflag &= ~OCRNL; +# endif +# ifdef ONOCR + ttp->c_oflag &= ~ONOCR; +# endif +# ifdef ONLRET + ttp->c_oflag &= ~ONLRET; +# endif + + ttp->c_cc[VMIN] = 1; + ttp->c_cc[VTIME] = 0; + +#else + + ttp->sg_flags |= CBREAK; + +#endif + + return 0; +} + +/* Set the tty associated with FD and TTP into one-character-at-a-time mode */ +int +ttfd_onechar (fd, ttp) + int fd; + TTYSTRUCT *ttp; +{ + if (tt_setonechar(ttp) < 0) + return -1; + return (ttsetattr (fd, ttp)); +} + +/* Set the terminal into one-character-at-a-time mode */ +int +ttonechar () +{ + TTYSTRUCT tt; + + if (ttsaved == 0) + return -1; + tt = ttin; + return (ttfd_onechar (0, &tt)); +} + +/* + * Change attributes in ttp so that when it is installed using + * ttsetattr, the terminal will be in no-echo mode. + */ +int +tt_setnoecho(ttp) + TTYSTRUCT *ttp; +{ +#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) + ttp->c_lflag &= ~(ECHO|ECHOK|ECHONL); +#else + ttp->sg_flags &= ~ECHO; +#endif + + return 0; +} + +/* Set the tty associated with FD and TTP into no-echo mode */ +int +ttfd_noecho (fd, ttp) + int fd; + TTYSTRUCT *ttp; +{ + if (tt_setnoecho (ttp) < 0) + return -1; + return (ttsetattr (fd, ttp)); +} + +/* Set the terminal into no-echo mode */ +int +ttnoecho () +{ + TTYSTRUCT tt; + + if (ttsaved == 0) + return -1; + tt = ttin; + return (ttfd_noecho (0, &tt)); +} + +/* + * Change attributes in ttp so that when it is installed using + * ttsetattr, the terminal will be in eight-bit mode (pass8). + */ +int +tt_seteightbit (ttp) + TTYSTRUCT *ttp; +{ +#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) + ttp->c_iflag &= ~ISTRIP; + ttp->c_cflag |= CS8; + ttp->c_cflag &= ~PARENB; +#else + ttp->sg_flags |= ANYP; +#endif + + return 0; +} + +/* Set the tty associated with FD and TTP into eight-bit mode */ +int +ttfd_eightbit (fd, ttp) + int fd; + TTYSTRUCT *ttp; +{ + if (tt_seteightbit (ttp) < 0) + return -1; + return (ttsetattr (fd, ttp)); +} + +/* Set the terminal into eight-bit mode */ +int +tteightbit () +{ + TTYSTRUCT tt; + + if (ttsaved == 0) + return -1; + tt = ttin; + return (ttfd_eightbit (0, &tt)); +} + +/* + * Change attributes in ttp so that when it is installed using + * ttsetattr, the terminal will be in non-canonical input mode. + */ +int +tt_setnocanon (ttp) + TTYSTRUCT *ttp; +{ +#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) + ttp->c_lflag &= ~ICANON; +#endif + + return 0; +} + +/* Set the tty associated with FD and TTP into non-canonical mode */ +int +ttfd_nocanon (fd, ttp) + int fd; + TTYSTRUCT *ttp; +{ + if (tt_setnocanon (ttp) < 0) + return -1; + return (ttsetattr (fd, ttp)); +} + +/* Set the terminal into non-canonical mode */ +int +ttnocanon () +{ + TTYSTRUCT tt; + + if (ttsaved == 0) + return -1; + tt = ttin; + return (ttfd_nocanon (0, &tt)); +} + +/* + * Change attributes in ttp so that when it is installed using + * ttsetattr, the terminal will be in cbreak, no-echo mode. + */ +int +tt_setcbreak(ttp) + TTYSTRUCT *ttp; +{ + if (tt_setonechar (ttp) < 0) + return -1; + return (tt_setnoecho (ttp)); +} + +/* Set the tty associated with FD and TTP into cbreak (no-echo, + one-character-at-a-time) mode */ +int +ttfd_cbreak (fd, ttp) + int fd; + TTYSTRUCT *ttp; +{ + if (tt_setcbreak (ttp) < 0) + return -1; + return (ttsetattr (fd, ttp)); +} + +/* Set the terminal into cbreak (no-echo, one-character-at-a-time) mode */ +int +ttcbreak () +{ + TTYSTRUCT tt; + + if (ttsaved == 0) + return -1; + tt = ttin; + return (ttfd_cbreak (0, &tt)); +} diff --git a/lib/sh/snprintf.c b/lib/sh/snprintf.c new file mode 100644 index 0000000..1f0f4c7 --- /dev/null +++ b/lib/sh/snprintf.c @@ -0,0 +1,2221 @@ +/* snprintf - formatted output to strings, with bounds checking and allocation */ + +/* + build a test version with + gcc -g -DDRIVER -I../.. -I../../include -o test-snprintf snprintf.c fmtu*long.o +*/ + +/* + Unix snprintf implementation. + derived from inetutils/libinetutils/snprintf.c Version 1.1 + + Copyright (C) 2001-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. + + Original (pre-bash) Revision History: + + 1.1: + * added changes from Miles Bader + * corrected a bug with %f + * added support for %#g + * added more comments :-) + 1.0: + * supporting must ANSI syntaxic_sugars + 0.0: + * support %s %c %d + + THANKS(for the patches and ideas): + Miles Bader + Cyrille Rustom + Jacek Slabocewiz + Mike Parker(mouse) + +*/ + +/* + * Currently doesn't handle (and bash/readline doesn't use): + * * *M$ width, precision specifications + * * %N$ numbered argument conversions + * * support for `F' is imperfect with ldfallback(), since underlying + * printf may not handle it -- should ideally have another autoconf test + */ + +#define FLOATING_POINT + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* GCC 4.2 on Snow Leopard doesn't like the snprintf prototype */ +#if defined(DEBUG) && !defined (MACOSX) +# undef HAVE_SNPRINTF +# undef HAVE_ASPRINTF + +# define HAVE_SNPRINTF 0 +# define HAVE_ASPRINTF 0 +#endif + +#if defined(DRIVER) && !defined(HAVE_CONFIG_H) +#define HAVE_LONG_LONG_INT +#define HAVE_LONG_DOUBLE +#ifdef __linux__ +#define HAVE_PRINTF_A_FORMAT +#endif +#define HAVE_ISINF_IN_LIBC +#define HAVE_ISNAN_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 + +#if !HAVE_SNPRINTF || !HAVE_ASPRINTF + +#include <bashtypes.h> + +#if defined(PREFER_STDARG) +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif +#include <bashansi.h> +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif +#include <chartypes.h> + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#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" +#else +# define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */ +# define FL_ADDBASE 0x02 /* add base# prefix to converted value */ +# define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */ +# define FL_UNSIGNED 0x08 /* don't add any sign */ +extern char *fmtulong PARAMS((unsigned long int, int, char *, size_t, int)); +extern char *fmtullong PARAMS((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; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#ifndef INT_STRLEN_BOUND +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \ + + 1 + TYPE_SIGNED (t)) +#endif + +/* conversion flags */ +#define PF_ALTFORM 0x00001 /* # */ +#define PF_HEXPREFIX 0x00002 /* 0[Xx] */ +#define PF_LADJUST 0x00004 /* - */ +#define PF_ZEROPAD 0x00008 /* 0 */ +#define PF_PLUS 0x00010 /* + */ +#define PF_SPACE 0x00020 /* ' ' */ +#define PF_THOUSANDS 0x00040 /* ' */ + +#define PF_DOT 0x00080 /* `.precision' */ +#define PF_STAR_P 0x00100 /* `*' after precision */ +#define PF_STAR_W 0x00200 /* `*' before or without precision */ + +/* length modifiers */ +#define PF_SIGNEDCHAR 0x00400 /* hh */ +#define PF_SHORTINT 0x00800 /* h */ +#define PF_LONGINT 0x01000 /* l */ +#define PF_LONGLONG 0x02000 /* ll */ +#define PF_LONGDBL 0x04000 /* L */ +#define PF_INTMAX_T 0x08000 /* j */ +#define PF_SIZE_T 0x10000 /* z */ +#define PF_PTRDIFF_T 0x20000 /* t */ + +#define PF_ALLOCBUF 0x40000 /* for asprintf, vasprintf */ + +#define PFM_SN 0x01 /* snprintf, vsnprintf */ +#define PFM_AS 0x02 /* asprintf, vasprintf */ + +#define ASBUFSIZE 128 + +#define x_digs "0123456789abcdef" +#define X_digs "0123456789ABCDEF" + +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 + * manipulate the Real numbers without having + * to resort to mathematical function(it + * would require to link with -lm) and not + * going down to the bit pattern(not portable) + * + * so a number, a real is: + + real = integral + fraction + + integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0 + fraction = b(1)*10^-1 + b(2)*10^-2 + ... + + where: + 0 <= a(i) => 9 + 0 <= b(i) => 9 + + from then it was simple math + */ + +/* + * size of the buffer for the integral part + * and the fraction part + */ +#define MAX_INT 99 + 1 /* 1 for the null */ +#define MAX_FRACT 307 + 1 + +/* + * These functions use static buffers to store the results, + * and so are not reentrant + */ +#define itoa(n) fmtulong(n, 10, intbuf, sizeof(intbuf), 0); +#define dtoa(n, p, f) numtoa(n, 10, p, f) + +#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) \ + ? GETARG (long) \ + : (((p)->flags & PF_SHORTINT) ? (long)(short)GETARG (int) \ + : (long)GETARG (int))) + +#define GETUNSIGNED(p) \ + (((p)->flags & PF_LONGINT) \ + ? 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) GETARG (long double) +#endif +#define GETDOUBLE(p) GETARG (double) + +#define SET_SIZE_FLAGS(p, type) \ + if (sizeof (type) > sizeof (int)) \ + (p)->flags |= PF_LONGINT; \ + if (sizeof (type) > sizeof (long)) \ + (p)->flags |= PF_LONGLONG; + +/* this struct holds everything we need */ +struct DATA +{ + int length; + char *base; /* needed for [v]asprintf */ + char *holder; + int counter; + const char *pf; + +/* FLAGS */ + int flags; + int justify; + int width, precision; + char pad; +}; + +/* the floating point stuff */ +#ifdef FLOATING_POINT +static double pow_10 PARAMS((int)); +static int log_10 PARAMS((double)); +static double integral PARAMS((double, double *)); +static char *numtoa PARAMS((double, int, int, char **)); +#endif + +static void init_data PARAMS((struct DATA *, char *, size_t, const char *, int)); +static void init_conv_flag PARAMS((struct DATA *)); + +/* for the format */ +#ifdef FLOATING_POINT +static void floating PARAMS((struct DATA *, double)); +static void exponent PARAMS((struct DATA *, double)); +#endif +static void number PARAMS((struct DATA *, unsigned long, int)); +#ifdef HAVE_LONG_LONG_INT +static void lnumber PARAMS((struct DATA *, unsigned long long, int)); +#endif +static void pointer PARAMS((struct DATA *, unsigned long)); +static void strings PARAMS((struct DATA *, char *)); + +#ifdef FLOATING_POINT +# define FALLBACK_FMTSIZE 32 +# define FALLBACK_BASE 4096 +# define LFALLBACK_BASE 5120 +# ifdef HAVE_LONG_DOUBLE +static void ldfallback PARAMS((struct DATA *, const char *, const char *, long double)); +# endif +static void dfallback PARAMS((struct DATA *, const char *, const char *, double)); +#endif + +static char *groupnum PARAMS((char *)); + +#if defined (HAVE_LONG_DOUBLE) +# define LONGDOUBLE long double +#else +# define LONGDOUBLE double +#endif + +#ifndef isnan + static inline int isnan_f (float x) { return x != x; } + static inline int isnan_d (double x) { return x != x; } + static inline int isnan_ld (LONGDOUBLE x) { return x != x; } + # define isnan(x) \ + (sizeof (x) == sizeof (LONGDOUBLE) ? isnan_ld (x) \ + : sizeof (x) == sizeof (double) ? isnan_d (x) \ + : isnan_f (x)) +#endif + +#ifndef isinf + static inline int isinf_f (float x) { return !isnan (x) && isnan (x - x); } + static inline int isinf_d (double x) { return !isnan (x) && isnan (x - x); } + static inline int isinf_ld (LONGDOUBLE x) { return !isnan (x) && isnan (x - x); } + # define isinf(x) \ + (sizeof (x) == sizeof (LONGDOUBLE) ? isinf_ld (x) \ + : sizeof (x) == sizeof (double) ? isinf_d (x) \ + : isinf_f (x)) +#endif + +#ifdef DRIVER +static void memory_error_and_abort (); +static void *xmalloc PARAMS((size_t)); +static void *xrealloc PARAMS((void *, size_t)); +static void xfree PARAMS((void *)); +#else +# include <xmalloc.h> +#endif + +/* those are defines specific to snprintf to hopefully + * make the code clearer :-) + */ +#define RIGHT 1 +#define LEFT 0 +#define NOT_FOUND -1 +#define FOUND 1 +#define MAX_FIELD 15 + +/* round off to the precision */ +#define ROUND(d, p) \ + (d < 0.) ? \ + d - pow_10(-(p)->precision) * 0.5 : \ + d + pow_10(-(p)->precision) * 0.5 + +/* set default precision */ +#define DEF_PREC(p) \ + if ((p)->precision == NOT_FOUND) \ + (p)->precision = 6 + +/* put a char. increment the number of chars written even if we've exceeded + the vsnprintf/snprintf buffer size (for the return value) */ +#define PUT_CHAR(c, p) \ + do \ + { \ + if (((p)->flags & PF_ALLOCBUF) && ((p)->counter >= (p)->length - 1)) \ + { \ + (p)->length += ASBUFSIZE; \ + (p)->base = (char *)xrealloc((p)->base, (p)->length); \ + (p)->holder = (p)->base + (p)->counter; /* in case reallocated */ \ + } \ + if ((p)->counter < (p)->length) \ + *(p)->holder++ = (c); \ + (p)->counter++; \ + } \ + 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 (((p)->flags & PF_PLUS) && (d) > zero) \ + PUT_CHAR('+', p) + +#define PUT_SPACE(d, p, zero) \ + if (((p)->flags & PF_SPACE) && (d) > zero) \ + PUT_CHAR(' ', p) + +/* pad right */ +#define PAD_RIGHT(p) \ + if ((p)->width > 0 && (p)->justify != LEFT) \ + for (; (p)->width > 0; (p)->width--) \ + PUT_CHAR((p)->pad, p) + +/* pad left */ +#define PAD_LEFT(p) \ + if ((p)->width > 0 && (p)->justify == LEFT) \ + for (; (p)->width > 0; (p)->width--) \ + PUT_CHAR((p)->pad, p) + +/* pad with zeros from decimal precision */ +#define PAD_ZERO(p) \ + if ((p)->precision > 0) \ + for (; (p)->precision > 0; (p)->precision--) \ + PUT_CHAR('0', p) + +/* if width and prec. in the args */ +#define STAR_ARGS(p) \ + do { \ + if ((p)->flags & PF_STAR_W) \ + { \ + (p)->width = GETARG (int); \ + if ((p)->width < 0) \ + { \ + (p)->flags |= PF_LADJUST; \ + (p)->justify = LEFT; \ + (p)->width = -(p)->width; \ + } \ + } \ + if ((p)->flags & PF_STAR_P) \ + { \ + (p)->precision = GETARG (int); \ + if ((p)->precision < 0) \ + { \ + (p)->flags &= ~PF_STAR_P; \ + (p)->precision = NOT_FOUND; \ + } \ + } \ + } while (0) + +#if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV) +# 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 +/* + * Find the nth power of 10 + */ +static double +pow_10(n) + int n; +{ + double P; + + /* handle common cases with fast switch statement. */ + switch (n) + { + case -3: return .001; + case -2: return .01; + case -1: return .1; + case 0: return 1.; + case 1: return 10.; + case 2: return 100.; + case 3: return 1000.; + } + + if (n < 0) + { + P = .0001; + for (n += 4; n < 0; n++) + P /= 10.; + } + else + { + P = 10000.; + for (n -= 4; n > 0; n--) + P *= 10.; + } + + return P; +} + +/* + * Find the integral part of the log in base 10 + * Note: this not a real log10() + I just need and approximation(integerpart) of x in: + 10^x ~= r + * log_10(200) = 2; + * log_10(250) = 2; + * + * NOTE: do not call this with r == 0 -- an infinite loop results. + */ +static int +log_10(r) + double r; +{ + int i = 0; + double result = 1.; + + if (r < 0.) + r = -r; + + if (r < 1.) + { + while (result >= r) + { + result /= 10.; + i++; + } + return (-i); + } + else + { + while (result <= r) + { + result *= 10.; + i++; + } + return (i - 1); + } +} + +/* + * This function return the fraction part of a double + * and set in ip the integral part. + * In many ways it resemble the modf() found on most Un*x + */ +static double +integral(real, ip) + double real; + double *ip; +{ + int j; + double i, s, p; + double real_integral = 0.; + + /* take care of the obvious */ + /* equal to zero ? */ + if (real == 0.) + { + *ip = 0.; + return (0.); + } + + /* negative number ? */ + if (real < 0.) + real = -real; + + /* a fraction ? */ + if ( real < 1.) + { + *ip = 0.; + return real; + } + + /* the real work :-) */ + for (j = log_10(real); j >= 0; j--) + { + p = pow_10(j); + s = (real - real_integral)/p; + i = 0.; + while (i + 1. <= s) + i++; + real_integral += i*p; + } + *ip = real_integral; + return (real - real_integral); +} + +#define PRECISION 1.e-6 +/* + * return an ascii representation of the integral part of the number + * and set fract to be an ascii representation of the fraction part + * the container for the fraction and the integral part or statically + * declare with fix size + */ +static char * +numtoa(number, base, precision, fract) + double number; + int base, precision; + char **fract; +{ + register int i, j; + double ip, fp; /* integer and fraction part */ + double fraction; + int digits, sign; + static char integral_part[MAX_INT]; + static char fraction_part[MAX_FRACT]; + int ch; + + /* taking care of the obvious case: 0.0 */ + if (number == 0.) + { + integral_part[0] = '0'; + integral_part[1] = '\0'; + /* The fractional part has to take the precision into account */ + for (ch = 0; ch < precision-1; ch++) + fraction_part[ch] = '0'; + fraction_part[ch] = '0'; + fraction_part[ch+1] = '\0'; + if (fract) + *fract = fraction_part; + return integral_part; + } + + /* -0 is tricky */ + sign = (number == -0.) ? '-' : ((number < 0.) ? '-' : '+'); + digits = MAX_INT - 1; + + /* for negative numbers */ + if (sign == '-') + { + number = -number; + digits--; /* sign consume one digit */ + } + + fraction = integral(number, &ip); + number = ip; + + /* do the integral part */ + if (ip == 0.) + { + integral_part[0] = '0'; + i = 1; + } + else + { + for ( i = 0; i < digits && number != 0.; ++i) + { + number /= base; + fp = integral(number, &ip); + ch = (int)((fp + PRECISION)*base); /* force to round */ + integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10; + if (! ISXDIGIT((unsigned char)integral_part[i])) + break; /* bail out overflow !! */ + number = ip; + } + } + + /* Oh No !! out of bound, ho well fill it up ! */ + if (number != 0.) + for (i = 0; i < digits; ++i) + integral_part[i] = '9'; + + /* put the sign ? */ + if (sign == '-') + integral_part[i++] = '-'; + + integral_part[i] = '\0'; + + /* reverse every thing */ + for ( i--, j = 0; j < i; j++, i--) + SWAP_INT(integral_part[i], integral_part[j]); + + /* the fractional part */ + for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--) + { + fraction_part[i] = (int)((fp + PRECISION)*10. + '0'); + if (! DIGIT(fraction_part[i])) /* underflow ? */ + break; + fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.); + } + fraction_part[i] = '\0'; + + if (fract != (char **)0) + *fract = fraction_part; + + return integral_part; +} +#endif + +/* for %d and friends, it puts in holder + * the representation with the right padding + */ +static void +number(p, d, base) + struct DATA *p; + unsigned long d; + int base; +{ + char *tmp, *t; + long sd; + int flags; + + /* An explicit precision turns off the zero-padding flag and sets the + pad character back to space. */ + if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) + { + p->flags &= ~PF_ZEROPAD; + p->pad = ' '; + } + + sd = d; /* signed for ' ' padding in base 10 */ + flags = 0; + flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0; + if (*p->pf == 'X') + 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; + } + + /* need to add one for any `+', but we only add one in base 10 */ + p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS)); + PAD_RIGHT(p); + + if ((p->flags & PF_DOT) && p->precision > 0) + { + p->precision -= strlen(tmp); + PAD_ZERO(p); + } + + switch (base) + { + case 10: + PUT_PLUS(sd, p, 0); + PUT_SPACE(sd, p, 0); + break; + case 8: + if (p->flags & PF_ALTFORM) + PUT_CHAR('0', p); + break; + case 16: + if (p->flags & PF_ALTFORM) + { + PUT_CHAR('0', p); + PUT_CHAR(*p->pf, p); + } + break; + } + + while (*tmp) + { + PUT_CHAR(*tmp, p); + tmp++; + } + + PAD_LEFT(p); + FREE (t); +} + +#ifdef HAVE_LONG_LONG_INT +/* + * identical to number() but works for `long long' + */ +static void +lnumber(p, d, base) + struct DATA *p; + unsigned long long d; + int base; +{ + char *tmp, *t; + long long sd; + int flags; + + /* An explicit precision turns off the zero-padding flag and sets the + pad character back to space. */ + if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) + { + p->flags &= ~PF_ZEROPAD; + p->pad = ' '; + } + + sd = d; /* signed for ' ' padding in base 10 */ + flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0; + if (*p->pf == 'X') + 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; + } + + /* need to add one for any `+', but we only add one in base 10 */ + p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS)); + PAD_RIGHT(p); + + if ((p->flags & PF_DOT) && p->precision > 0) + { + p->precision -= strlen(tmp); + PAD_ZERO(p); + } + + switch (base) + { + case 10: + PUT_PLUS(sd, p, 0); + PUT_SPACE(sd, p, 0); + break; + case 8: + if (p->flags & PF_ALTFORM) + PUT_CHAR('0', p); + break; + case 16: + if (p->flags & PF_ALTFORM) + { + PUT_CHAR('0', p); + PUT_CHAR(*p->pf, p); + } + break; + } + + while (*tmp) + { + PUT_CHAR(*tmp, p); + tmp++; + } + + PAD_LEFT(p); + FREE (t); +} +#endif + +static void +pointer(p, d) + struct DATA *p; + unsigned long d; +{ + char *tmp; + + tmp = fmtulong(d, 16, intbuf, sizeof(intbuf), 0); + p->width -= strlen(tmp); + PAD_RIGHT(p); + + /* prefix '0x' for pointers */ + PUT_CHAR('0', p); + PUT_CHAR('x', p); + + while (*tmp) + { + PUT_CHAR(*tmp, p); + tmp++; + } + + PAD_LEFT(p); +} + +/* %s strings */ +static void +strings(p, tmp) + struct DATA *p; + char *tmp; +{ + size_t len; + + len = strlen(tmp); + if (p->precision != NOT_FOUND) /* the smallest number */ + 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 + +/* 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, *t; + int i; + + if (d != 0 && (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; + + 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'; + if (tmp2[0] == '\0') + p->precision = 0; + } + + /* calculate the padding. 1 for the dot */ + p->width = p->width - + /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */ +#if 0 + ((d > 0. && p->justify == RIGHT) ? 1:0) - +#else + ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) - +#endif + ((p->flags & PF_SPACE) ? 1:0) - + strlen(tmp) - p->precision - + ((p->precision != 0 || (p->flags & PF_ALTFORM)) ? 1 : 0); /* radix char */ + + if (p->pad == ' ') + { + PAD_RIGHT(p); + PUT_PLUS(d, p, 0.); + } + else + { + if (*tmp == '-') + PUT_CHAR(*tmp++, p); + PUT_PLUS(d, p, 0.); + PAD_RIGHT(p); + } + PUT_SPACE(d, p, 0.); + + while (*tmp) + { + PUT_CHAR(*tmp, p); /* the integral */ + tmp++; + } + FREE (t); + + if (p->precision != 0 || (p->flags & PF_ALTFORM)) + PUT_CHAR(decpoint, p); /* put the '.' */ + + for (; *tmp2; tmp2++) + PUT_CHAR(*tmp2, p); /* the fraction */ + + PAD_LEFT(p); +} + +/* %e %E %g %G exponent representation */ +static void +exponent(p, d) + struct DATA *p; + double d; +{ + char *tmp, *tmp2; + int j, i; + + if (d != 0 && (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))) + return; /* already printed nan or inf */ + + GETLOCALEDATA(decpoint, thoussep, grouping); + DEF_PREC(p); + if (d == 0.) + j = 0; + else + { + 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' (but no `.' if precision == 0 */ + /* calculate how much padding need */ + p->width = p->width - + /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */ +#if 0 + ((d > 0. && p->justify == RIGHT) ? 1:0) - +#else + ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) - +#endif + (p->precision != 0 || (p->flags & PF_ALTFORM)) - + ((p->flags & PF_SPACE) ? 1:0) - p->precision - 5; + + if (p->pad == ' ') + { + PAD_RIGHT(p); + PUT_PLUS(d, p, 0.); + } + else + { + if (*tmp == '-') + PUT_CHAR(*tmp++, p); + PUT_PLUS(d, p, 0.); + PAD_RIGHT(p); + } + PUT_SPACE(d, p, 0.); + + while (*tmp) + { + PUT_CHAR(*tmp, p); + tmp++; + } + + 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'; + + for (; *tmp2; tmp2++) + PUT_CHAR(*tmp2, p); /* the fraction */ + + /* the exponent put the 'e|E' */ + if (*p->pf == 'g' || *p->pf == 'e') + PUT_CHAR('e', p); + else + PUT_CHAR('E', p); + + /* the sign of the exp */ + if (j >= 0) + PUT_CHAR('+', p); + else + { + PUT_CHAR('-', p); + j = -j; + } + + tmp = itoa(j); + /* pad out to at least two spaces. pad with `0' if the exponent is a + single digit. */ + if (j <= 9) + PUT_CHAR('0', p); + + /* the exponent */ + while (*tmp) + { + PUT_CHAR(*tmp, p); + tmp++; + } + + PAD_LEFT(p); +} +#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) + struct DATA *p; +{ + p->flags &= PF_ALLOCBUF; /* preserve PF_ALLOCBUF flag */ + p->precision = p->width = NOT_FOUND; + p->justify = NOT_FOUND; + p->pad = ' '; +} + +static void +init_data (p, string, length, format, mode) + struct DATA *p; + char *string; + size_t length; + const char *format; + int mode; +{ + p->length = length - 1; /* leave room for '\0' */ + p->holder = p->base = string; + p->pf = format; + p->counter = 0; + p->flags = (mode == PFM_AS) ? PF_ALLOCBUF : 0; +} + +static int +#if defined (__STDC__) +vsnprintf_internal(struct DATA *data, char *string, size_t length, const char *format, va_list args) +#else +vsnprintf_internal(data, string, length, format, args) + struct DATA *data; + char *string; + size_t length; + const char *format; + va_list args; +#endif +{ + double d; /* temporary holder */ +#ifdef HAVE_LONG_DOUBLE + long double ld; /* for later */ +#endif + unsigned long ul; +#ifdef HAVE_UNSIGNED_LONG_LONG_INT + unsigned long long ull; +#endif + int state, i, c, n; + char *s; +#if HANDLE_MULTIBYTE + wchar_t *ws; + wint_t wc; +#endif + const char *convstart; + int negprec; + + /* 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 < 0) + return -1; + + if (format == 0) + return 0; + + /* Reset these for each call because the locale might have changed. */ + decpoint = thoussep = 0; + grouping = 0; + + negprec = 0; + for (; c = *(data->pf); data->pf++) + { + if (c != '%') + { + PUT_CHAR (c, data); + continue; + } + + convstart = data->pf; + init_conv_flag (data); /* initialise format flags */ + + state = 1; + for (state = 1; state && *data->pf; ) + { + c = *(++data->pf); + /* fmtend = data->pf */ +#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE) + if (data->flags & PF_LONGDBL) + { + switch (c) + { + case 'f': case 'F': + case 'e': case 'E': + case 'g': case 'G': +# ifdef HAVE_PRINTF_A_FORMAT + case 'a': case 'A': +# endif + STAR_ARGS (data); + ld = GETLDOUBLE (data); + ldfallback (data, convstart, data->pf, ld); + goto conv_break; + } + } +#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */ + + switch (c) + { + /* Parse format flags */ + case '\0': /* a NULL here ? ? bail out */ + *data->holder = '\0'; + return data->counter; + break; + case '#': + data->flags |= PF_ALTFORM; + continue; + case '*': + if (data->flags & PF_DOT) + data->flags |= PF_STAR_P; + else + data->flags |= PF_STAR_W; + continue; + case '-': + if ((data->flags & PF_DOT) == 0) + { + data->flags |= PF_LADJUST; + data->justify = LEFT; + } + else + negprec = 1; + continue; + case ' ': + if ((data->flags & PF_PLUS) == 0) + data->flags |= PF_SPACE; + continue; + case '+': + if ((data->flags & PF_DOT) == 0) + { + data->flags |= PF_PLUS; + if ((data->flags & PF_LADJUST) == 0) + data->justify = RIGHT; + } + continue; + case '\'': + data->flags |= PF_THOUSANDS; + continue; + + case '0': + /* If we're not specifying precision (in which case we've seen + a `.') and we're not performing left-adjustment (in which + case the `0' is ignored), a `0' is taken as the zero-padding + flag. */ + if ((data->flags & (PF_DOT|PF_LADJUST)) == 0) + { + data->flags |= PF_ZEROPAD; + data->pad = '0'; + continue; + } + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + n = 0; + do + { + n = n * 10 + TODIGIT(c); + c = *(++data->pf); + } + while (DIGIT(c)); + data->pf--; /* went too far */ + if (n < 0) + n = 0; + if (data->flags & PF_DOT) + data->precision = negprec ? NOT_FOUND : n; + else + data->width = n; + continue; + + /* optional precision */ + case '.': + data->flags |= PF_DOT; + data->precision = 0; + continue; + + /* length modifiers */ + case 'h': + data->flags |= (data->flags & PF_SHORTINT) ? PF_SIGNEDCHAR : PF_SHORTINT; + continue; + case 'l': + data->flags |= (data->flags & PF_LONGINT) ? PF_LONGLONG : PF_LONGINT; + continue; + case 'L': + data->flags |= PF_LONGDBL; + continue; + case 'q': + data->flags |= PF_LONGLONG; + continue; + case 'j': + data->flags |= PF_INTMAX_T; + SET_SIZE_FLAGS(data, intmax_t); + continue; + case 'z': + data->flags |= PF_SIZE_T; + SET_SIZE_FLAGS(data, size_t); + continue; + case 't': + data->flags |= PF_PTRDIFF_T; + SET_SIZE_FLAGS(data, ptrdiff_t); + continue; + + /* Conversion specifiers */ +#ifdef FLOATING_POINT + case 'f': /* float, double */ + case 'F': + STAR_ARGS(data); + d = GETDOUBLE(data); + floating(data, d); +conv_break: + state = 0; + break; + case 'g': + case 'G': + STAR_ARGS(data); + DEF_PREC(data); + d = GETDOUBLE(data); + i = (d != 0.) ? log_10(d) : -1; + /* + * for '%g|%G' ANSI: use f if exponent + * is in the range or [-4,p] exclusively + * else use %e|%E + */ + if (-4 < i && i < data->precision) + { + /* reset precision */ + data->precision -= i + 1; + floating(data, d); + } + else + { + /* reduce precision by 1 because of leading digit before + decimal point in e format, unless specified as 0. */ + if (data->precision > 0) + data->precision--; + exponent(data, d); + } + state = 0; + break; + case 'e': + case 'E': /* Exponent double */ + STAR_ARGS(data); + d = GETDOUBLE(data); + exponent(data, d); + state = 0; + break; +# ifdef HAVE_PRINTF_A_FORMAT + case 'a': + case 'A': + STAR_ARGS(data); + d = GETDOUBLE(data); + dfallback(data, convstart, data->pf, d); + state = 0; + break; +# endif /* HAVE_PRINTF_A_FORMAT */ +#endif /* FLOATING_POINT */ + case 'U': + data->flags |= PF_LONGINT; + /* FALLTHROUGH */ + case 'u': + STAR_ARGS(data); +#ifdef HAVE_LONG_LONG_INT + if (data->flags & PF_LONGLONG) + { + ull = GETARG (unsigned long long); + lnumber(data, ull, 10); + } + else +#endif + { + ul = GETUNSIGNED(data); + number(data, ul, 10); + } + state = 0; + break; + case 'D': + data->flags |= PF_LONGINT; + /* FALLTHROUGH */ + case 'd': /* decimal */ + case 'i': + STAR_ARGS(data); +#ifdef HAVE_LONG_LONG_INT + if (data->flags & PF_LONGLONG) + { + ull = GETARG (long long); + lnumber(data, ull, 10); + } + else +#endif + { + ul = GETSIGNED(data); + number(data, ul, 10); + } + state = 0; + break; + case 'o': /* octal */ + STAR_ARGS(data); +#ifdef HAVE_LONG_LONG_INT + if (data->flags & PF_LONGLONG) + { + ull = GETARG (unsigned long long); + lnumber(data, ull, 8); + } + else +#endif + { + ul = GETUNSIGNED(data); + number(data, ul, 8); + } + state = 0; + break; + case 'x': + case 'X': /* hexadecimal */ + STAR_ARGS(data); +#ifdef HAVE_LONG_LONG_INT + if (data->flags & PF_LONGLONG) + { + ull = GETARG (unsigned long long); + lnumber(data, ull, 16); + } + else +#endif + { + ul = GETUNSIGNED(data); + number(data, ul, 16); + } + state = 0; + break; + case 'p': + STAR_ARGS(data); + 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 */ + 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); +#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_INT + if (data->flags & PF_LONGLONG) + *(GETARG (long long *)) = data->counter; + else +#endif + if (data->flags & PF_LONGINT) + *(GETARG (long *)) = data->counter; + else if (data->flags & PF_SHORTINT) + *(GETARG (short *)) = data->counter; + else + *(GETARG (int *)) = data->counter; + state = 0; + break; + case '%': /* nothing just % */ + PUT_CHAR('%', data); + state = 0; + break; + default: + /* is this an error ? maybe bail out */ + state = 0; + break; + } /* end switch */ + } /* end of `%' for loop */ + } /* end of format string for loop */ + + if (data->length >= 0) + *data->holder = '\0'; /* the end ye ! */ + + return data->counter; +} + +#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE) +/* + * Printing floating point numbers accurately is an art. I'm not good + * at it. Fall back to sprintf for long double formats. + */ +static void +ldfallback (data, fs, fe, ld) + struct DATA *data; + const char *fs, *fe; + long double ld; +{ + register char *x; + char fmtbuf[FALLBACK_FMTSIZE], *obuf; + int fl; + + fl = LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2; + obuf = (char *)xmalloc (fl); + fl = fe - fs + 1; + strncpy (fmtbuf, fs, fl); + fmtbuf[fl] = '\0'; + + if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P)) + sprintf (obuf, fmtbuf, data->width, data->precision, ld); + else if (data->flags & PF_STAR_W) + sprintf (obuf, fmtbuf, data->width, ld); + else if (data->flags & PF_STAR_P) + sprintf (obuf, fmtbuf, data->precision, ld); + else + sprintf (obuf, fmtbuf, ld); + + for (x = obuf; *x; x++) + PUT_CHAR (*x, data); + xfree (obuf); +} +#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */ + +#ifdef FLOATING_POINT +/* Used for %a, %A if the libc printf supports them. */ +static void +dfallback (data, fs, fe, d) + struct DATA *data; + const char *fs, *fe; + double d; +{ + register char *x; + char fmtbuf[FALLBACK_FMTSIZE], obuf[FALLBACK_BASE]; + int fl; + + fl = fe - fs + 1; + strncpy (fmtbuf, fs, fl); + fmtbuf[fl] = '\0'; + + if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P)) + sprintf (obuf, fmtbuf, data->width, data->precision, d); + else if (data->flags & PF_STAR_W) + sprintf (obuf, fmtbuf, data->width, d); + else if (data->flags & PF_STAR_P) + sprintf (obuf, fmtbuf, data->precision, d); + else + sprintf (obuf, fmtbuf, d); + + for (x = obuf; *x; x++) + PUT_CHAR (*x, data); +} +#endif /* FLOATING_POINT */ + +#if !HAVE_SNPRINTF + +int +#if defined (__STDC__) +vsnprintf(char *string, size_t length, const char *format, va_list args) +#else +vsnprintf(string, length, format, args) + char *string; + size_t length; + const char *format; + va_list args; +#endif +{ + 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)); +} + +int +#if defined(PREFER_STDARG) +snprintf(char *string, size_t length, const char * format, ...) +#else +snprintf(string, length, format, va_alist) + char *string; + size_t length; + const char *format; + va_dcl +#endif +{ + struct DATA data; + int rval; + va_list args; + + 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); + + va_end(args); + + return rval; +} + +#endif /* HAVE_SNPRINTF */ + +#if !HAVE_ASPRINTF + +int +#if defined (__STDC__) +vasprintf(char **stringp, const char *format, va_list args) +#else +vasprintf(stringp, format, args) + char **stringp; + const char *format; + va_list args; +#endif +{ + struct DATA data; + char *string; + int r; + + string = (char *)xmalloc(ASBUFSIZE); + init_data (&data, string, ASBUFSIZE, format, PFM_AS); + r = vsnprintf_internal(&data, string, ASBUFSIZE, format, args); + *stringp = data.base; /* not string in case reallocated */ + return r; +} + +int +#if defined(PREFER_STDARG) +asprintf(char **stringp, const char * format, ...) +#else +asprintf(stringp, format, va_alist) + char **stringp; + const char *format; + va_dcl +#endif +{ + int rval; + va_list args; + + SH_VA_START(args, format); + + rval = vasprintf (stringp, format, args); + + va_end(args); + + return rval; +} + +#endif /* !HAVE_ASPRINTF */ + +#endif /* !HAVE_SNPRINTF || !HAVE_ASPRINTF */ + +#ifdef DRIVER + +static void +memory_error_and_abort () +{ + write (2, "out of virtual memory\n", 22); + abort (); +} + +static void * +xmalloc(bytes) + size_t bytes; +{ + void *ret; + + ret = malloc(bytes); + if (ret == 0) + memory_error_and_abort (); + return ret; +} + +static void * +xrealloc (pointer, bytes) + void *pointer; + size_t bytes; +{ + void *ret; + + ret = pointer ? realloc(pointer, bytes) : malloc(bytes); + if (ret == 0) + memory_error_and_abort (); + return ret; +} + +static void +xfree(x) + void *x; +{ + if (x) + free (x); +} + +/* set of small tests for snprintf() */ +main() +{ + char holder[100]; + 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"); + printf("printf() format\n"); + printf("snprintf() format\n\n"); +*/ +/* Checking the field widths */ + + printf("/%%ld %%ld/, 336, 336\n"); + snprintf(holder, sizeof holder, "/%ld %ld/\n", 336, 336); + asprintf(&h, "/%ld %ld/\n", 336, 336); + printf("/%ld %ld/\n", 336, 336); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%d/, 336\n"); + snprintf(holder, sizeof holder, "/%d/\n", 336); + asprintf(&h, "/%d/\n", 336); + printf("/%d/\n", 336); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%2d/, 336\n"); + snprintf(holder, sizeof holder, "/%2d/\n", 336); + asprintf(&h, "/%2d/\n", 336); + printf("/%2d/\n", 336); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%10d/, 336\n"); + snprintf(holder, sizeof holder, "/%10d/\n", 336); + asprintf(&h, "/%10d/\n", 336); + printf("/%10d/\n", 336); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%-10d/, 336\n"); + snprintf(holder, sizeof holder, "/%-10d/\n", 336); + asprintf(&h, "/%-10d/\n", 336); + printf("/%-10d/\n", 336); + printf("%s", holder); + printf("%s\n", h); + + +/* floating points */ + + printf("/%%f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%f/\n", 1234.56); + asprintf(&h, "/%f/\n", 1234.56); + printf("/%f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%e/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%e/\n", 1234.56); + asprintf(&h, "/%e/\n", 1234.56); + printf("/%e/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%4.2f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56); + asprintf(&h, "/%4.2f/\n", 1234.56); + printf("/%4.2f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%3.1f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56); + asprintf(&h, "/%3.1f/\n", 1234.56); + printf("/%3.1f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%10.3f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56); + asprintf(&h, "/%10.3f/\n", 1234.56); + printf("/%10.3f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%10.3e/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56); + asprintf(&h, "/%10.3e/\n", 1234.56); + printf("/%10.3e/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%+4.2f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56); + asprintf(&h, "/%+4.2f/\n", 1234.56); + printf("/%+4.2f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%010.2f/, 1234.56\n"); + snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56); + asprintf(&h, "/%010.2f/\n", 1234.56); + printf("/%010.2f/\n", 1234.56); + printf("%s", holder); + printf("%s\n", h); + +#define BLURB "Outstanding acting !" +/* strings precisions */ + + printf("/%%2s/, \"%s\"\n", BLURB); + snprintf(holder, sizeof holder, "/%2s/\n", BLURB); + asprintf(&h, "/%2s/\n", BLURB); + printf("/%2s/\n", BLURB); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%22s/ %s\n", BLURB); + snprintf(holder, sizeof holder, "/%22s/\n", BLURB); + asprintf(&h, "/%22s/\n", BLURB); + printf("/%22s/\n", BLURB); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%22.5s/ %s\n", BLURB); + snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB); + asprintf(&h, "/%22.5s/\n", BLURB); + printf("/%22.5s/\n", BLURB); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%-22.5s/ %s\n", BLURB); + snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB); + asprintf(&h, "/%-22.5s/\n", BLURB); + printf("/%-22.5s/\n", BLURB); + printf("%s", holder); + printf("%s\n", h); + +/* see some flags */ + + printf("%%x %%X %%#x, 31, 31, 31\n"); + snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31); + asprintf(&h, "%x %X %#x\n", 31, 31, 31); + printf("%x %X %#x\n", 31, 31, 31); + printf("%s", holder); + printf("%s\n", h); + + printf("**%%d**%% d**%% d**, 42, 42, -42\n"); + snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42); + asprintf(&h, "**%d**% d**% d**\n", 42, 42, -42); + printf("**%d**% d**% d**\n", 42, 42, -42); + printf("%s", holder); + printf("%s\n", h); + +/* other flags */ + + printf("/%%g/, 31.4\n"); + snprintf(holder, sizeof holder, "/%g/\n", 31.4); + asprintf(&h, "/%g/\n", 31.4); + printf("/%g/\n", 31.4); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%.6g/, 31.4\n"); + snprintf(holder, sizeof holder, "/%.6g/\n", 31.4); + asprintf(&h, "/%.6g/\n", 31.4); + printf("/%.6g/\n", 31.4); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%.1G/, 31.4\n"); + snprintf(holder, sizeof holder, "/%.1G/\n", 31.4); + asprintf(&h, "/%.1G/\n", 31.4); + printf("/%.1G/\n", 31.4); + printf("%s", holder); + printf("%s\n", h); + + printf("/%%.1G/, 3100000000.4\n"); + snprintf(holder, sizeof holder, "/%.1G/\n", 3100000000.4); + asprintf(&h, "/%.1G/\n", 3100000000.4); + printf("/%.1G/\n", 3100000000.4); + printf("%s", holder); + printf("%s\n", h); + + printf("abc%%n\n"); + printf("abc%n", &i); printf("%d\n", i); + snprintf(holder, sizeof holder, "abc%n", &i); + printf("%s", holder); printf("%d\n\n", i); + asprintf(&h, "abc%n", &i); + printf("%s", h); printf("%d\n\n", i); + + printf("%%*.*s --> 10.10\n"); + snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB); + asprintf(&h, "%*.*s\n", 10, 10, BLURB); + printf("%*.*s\n", 10, 10, BLURB); + printf("%s", holder); + printf("%s\n", h); + + printf("%%%%%%%%\n"); + snprintf(holder, sizeof holder, "%%%%\n"); + asprintf(&h, "%%%%\n"); + printf("%%%%\n"); + printf("%s", holder); + printf("%s\n", h); + +#define BIG "Hello this is a too big string for the buffer" +/* printf("A buffer to small of 10, trying to put this:\n");*/ + printf("<%%>, %s\n", BIG); + i = snprintf(holder, 10, "%s\n", BIG); + i = asprintf(&h, "%s", BIG); + printf("<%s>\n", BIG); + printf("<%s>\n", holder); + printf("<%s>\n\n", h); + + printf ("<%%p> vsnprintf\n"); + i = snprintf(holder, 100, "%p", vsnprintf); + i = asprintf(&h, "%p", vsnprintf); + printf("<%p>\n", vsnprintf); + printf("<%s>\n", holder); + printf("<%s>\n\n", h); + + printf ("<%%lu> LONG_MAX+1\n"); + i = snprintf(holder, 100, "%lu", (unsigned long)(LONG_MAX)+1); + i = asprintf(&h, "%lu", (unsigned long)(LONG_MAX)+1); + printf("<%lu>\n", (unsigned long)(LONG_MAX)+1); + printf("<%s>\n", holder); + printf("<%s>\n\n", h); + +#ifdef HAVE_LONG_LONG_INT + printf ("<%%llu> LLONG_MAX+1\n"); + i = snprintf(holder, 100, "%llu", (unsigned long long)(LLONG_MAX)+1); + i = asprintf(&h, "%llu", (unsigned long long)(LLONG_MAX)+1); + printf("<%llu>\n", (unsigned long long)(LLONG_MAX)+1); + printf("<%s>\n", holder); + printf("<%s>\n\n", h); +#endif + +#ifdef HAVE_LONG_DOUBLE + printf ("<%%6.2LE> 42.42\n"); + i = snprintf(holder, 100, "%6.2LE", (long double)42.42); + i = asprintf(&h, "%6.2LE", (long double)42.42); + printf ("<%6.2LE>\n", (long double)42.42); + printf ("<%s>\n", holder); + printf ("<%s>\n\n", h); +#endif + +#ifdef HAVE_PRINTF_A_FORMAT + printf ("<%%6.2A> 42.42\n"); + i = snprintf(holder, 100, "%6.2A", 42.42); + i = asprintf(&h, "%6.2A", 42.42); + printf ("<%6.2A>\n", 42.42); + printf ("<%s>\n", holder); + printf ("<%s>\n\n", h); + + printf ("<%%6.2LA> 42.42\n"); + i = snprintf(holder, 100, "%6.2LA", (long double)42.42); + i = asprintf(&h, "%6.2LA", (long double)42.42); + printf ("<%6.2LA>\n", (long double)42.42); + printf ("<%s>\n", holder); + printf ("<%s>\n\n", h); +#endif + + printf ("<%%.10240f> DBL_MAX\n"); + si = snprintf(holder, 100, "%.10240f", DBL_MAX); + ai = asprintf(&h, "%.10240f", DBL_MAX); + printf ("<%.10240f>\n", DBL_MAX); + printf ("<%d> <%s>\n", si, holder); + printf ("<%d> <%s>\n\n", ai, h); + + printf ("<%%.10240Lf> LDBL_MAX\n"); + si = snprintf(holder, 100, "%.10240Lf", (long double)LDBL_MAX); + ai = asprintf(&h, "%.10240Lf", (long double)LDBL_MAX); + printf ("<%.10240Lf>\n", (long double)LDBL_MAX); + 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 diff --git a/lib/sh/spell.c b/lib/sh/spell.c new file mode 100644 index 0000000..cdf465b --- /dev/null +++ b/lib/sh/spell.c @@ -0,0 +1,212 @@ +/* spell.c -- spelling correction for pathnames. */ + +/* Copyright (C) 2000-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <bashtypes.h> +#include <posixdir.h> +#include <posixstat.h> +#if defined (HAVE_SYS_PARAM_H) +#include <sys/param.h> +#endif + +#include <stdio.h> + +#include <bashansi.h> +#include <maxpath.h> +#include <stdc.h> + +static int mindist PARAMS((char *, char *, char *)); +static int spdist PARAMS((char *, char *)); + +/* + * `spname' and its helpers are inspired by the code in "The UNIX + * Programming Environment", Kernighan & Pike, Prentice-Hall 1984, + * pages 209 - 213. + */ + +/* + * `spname' -- return a correctly spelled filename + * + * int spname(char * oldname, char * newname) + * Returns: -1 if no reasonable match found + * 0 if exact match found + * 1 if corrected + * Stores corrected name in `newname'. + */ +int +spname(oldname, newname) + char *oldname; + char *newname; +{ + char *op, *np, *p; + char guess[PATH_MAX + 1], best[PATH_MAX + 1]; + + op = oldname; + np = newname; + for (;;) + { + while (*op == '/') /* Skip slashes */ + *np++ = *op++; + *np = '\0'; + + if (*op == '\0') /* Exact or corrected */ + { + /* `.' is rarely the right thing. */ + if (oldname[1] == '\0' && newname[1] == '\0' && + oldname[0] != '.' && newname[0] == '.') + return -1; + return strcmp(oldname, newname) != 0; + } + + /* Copy next component into guess */ + for (p = guess; *op != '/' && *op != '\0'; op++) + if (p < guess + PATH_MAX) + *p++ = *op; + *p = '\0'; + + if (mindist(newname, guess, best) >= 3) + return -1; /* Hopeless */ + + /* + * Add to end of newname + */ + for (p = best; *np = *p++; np++) + ; + } +} + +/* + * Search directory for a guess + */ +static int +mindist(dir, guess, best) + char *dir; + char *guess; + char *best; +{ + DIR *fd; + struct dirent *dp; + int dist, x; + + dist = 3; /* Worst distance */ + if (*dir == '\0') + dir = "."; + + if ((fd = opendir(dir)) == NULL) + return dist; + + while ((dp = readdir(fd)) != NULL) + { + /* + * Look for a better guess. If the new guess is as + * good as the current one, we take it. This way, + * any single character match will be a better match + * than ".". + */ + x = spdist(dp->d_name, guess); + if (x <= dist && x != 3) + { + strcpy(best, dp->d_name); + dist = x; + if (dist == 0) /* Exact match */ + break; + } + } + (void)closedir(fd); + + /* Don't return `.' */ + if (best[0] == '.' && best[1] == '\0') + dist = 3; + return dist; +} + +/* + * `spdist' -- return the "distance" between two names. + * + * int spname(char * oldname, char * newname) + * Returns: 0 if strings are identical + * 1 if two characters are transposed + * 2 if one character is wrong, added or deleted + * 3 otherwise + */ +static int +spdist(cur, new) + char *cur, *new; +{ + while (*cur == *new) + { + if (*cur == '\0') + return 0; /* Exact match */ + cur++; + new++; + } + + if (*cur) + { + if (*new) + { + if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0) + return 1; /* Transposition */ + + if (strcmp (cur + 1, new + 1) == 0) + return 2; /* One character mismatch */ + } + + if (strcmp(&cur[1], &new[0]) == 0) + return 2; /* Extra character */ + } + + if (*new && strcmp(cur, new + 1) == 0) + return 2; /* Missing character */ + + return 3; +} + +char * +dirspell (dirname) + char *dirname; +{ + int n; + char *guess; + + n = (strlen (dirname) * 3 + 1) / 2 + 1; + guess = (char *)malloc (n); + if (guess == 0) + return 0; + + switch (spname (dirname, guess)) + { + case -1: + default: + free (guess); + return (char *)NULL; + case 0: + case 1: + return guess; + } +} diff --git a/lib/sh/strcasecmp.c b/lib/sh/strcasecmp.c new file mode 100644 index 0000000..70d0551 --- /dev/null +++ b/lib/sh/strcasecmp.c @@ -0,0 +1,84 @@ +/* strcasecmp.c - functions for case-insensitive string comparison. */ + +/* Copyright (C) 1995 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_STRCASECMP) + +#include <stdc.h> +#include <bashansi.h> +#include <chartypes.h> + +/* Compare at most COUNT characters from string1 to string2. Case + doesn't matter. */ +int +strncasecmp (string1, string2, count) + const char *string1; + const char *string2; + size_t count; +{ + register const char *s1; + register const char *s2; + register int r; + + if (count <= 0 || (string1 == string2)) + return 0; + + s1 = string1; + s2 = string2; + do + { + if ((r = TOLOWER ((unsigned char) *s1) - TOLOWER ((unsigned char) *s2)) != 0) + return r; + if (*s1++ == '\0') + break; + s2++; + } + while (--count != 0); + + return (0); +} + +/* strcmp (), but caseless. */ +int +strcasecmp (string1, string2) + const char *string1; + const char *string2; +{ + register const char *s1; + register const char *s2; + register int r; + + s1 = string1; + s2 = string2; + + if (s1 == s2) + return (0); + + while ((r = TOLOWER ((unsigned char)*s1) - TOLOWER ((unsigned char)*s2)) == 0) + { + if (*s1++ == '\0') + return 0; + s2++; + } + + return (r); +} +#endif /* !HAVE_STRCASECMP */ diff --git a/lib/sh/strcasestr.c b/lib/sh/strcasestr.c new file mode 100644 index 0000000..c819b3e --- /dev/null +++ b/lib/sh/strcasestr.c @@ -0,0 +1,46 @@ +/* strcasestr.c - Find if one string appears as a substring of another string, + without regard to case. */ + +/* Copyright (C) 2000 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashansi.h> +#include <chartypes.h> + +#include <stdc.h> + +/* Determine if s2 occurs in s1. If so, return a pointer to the + match in s1. The compare is case insensitive. This is a + case-insensitive strstr(3). */ +char * +strcasestr (s1, s2) + const char *s1; + const char *s2; +{ + register int i, l, len, c; + + c = TOLOWER ((unsigned char)s2[0]); + len = strlen (s1); + l = strlen (s2); + for (i = 0; (len - i) >= l; i++) + if ((TOLOWER ((unsigned char)s1[i]) == c) && (strncasecmp (s1 + i, s2, l) == 0)) + return ((char *)s1 + i); + return ((char *)0); +} diff --git a/lib/sh/strchrnul.c b/lib/sh/strchrnul.c new file mode 100644 index 0000000..00cb88c --- /dev/null +++ b/lib/sh/strchrnul.c @@ -0,0 +1,35 @@ +/* Searching in a string. + Copyright (C) 2012 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <stdio.h> + +/* Specification. */ +#include <string.h> + +/* Find the first occurrence of C in S or the final NUL byte. */ +char * +strchrnul (s, c_in) + const char *s; + int c_in; +{ + char c; + register char *s1; + + for (c = c_in, s1 = (char *)s; s1 && *s1 && *s1 != c; s1++) + ; + return (s1); +} diff --git a/lib/sh/strdup.c b/lib/sh/strdup.c new file mode 100644 index 0000000..3d35f7c --- /dev/null +++ b/lib/sh/strdup.c @@ -0,0 +1,41 @@ +/* strdup - return a copy of a string in newly-allocated memory. */ + +/* Copyright (C) 2013 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +/* Get specification. */ +#include <string.h> +#include <stdlib.h> + +/* Duplicate S, returning an identical malloc'd string. */ +char * +strdup (s) + const char *s; +{ + size_t len; + void *new; + + len = strlen (s) + 1; + if ((new = malloc (len)) == NULL) + return NULL; + + memcpy (new, s, len); + return ((char *)new); +} diff --git a/lib/sh/strerror.c b/lib/sh/strerror.c new file mode 100644 index 0000000..bf63926 --- /dev/null +++ b/lib/sh/strerror.c @@ -0,0 +1,74 @@ +/* strerror.c - string corresponding to a particular value of errno. */ + +/* Copyright (C) 1995 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_STRERROR) + +#include <bashtypes.h> +#if defined (HAVE_SYS_PARAM_H) +# include <sys/param.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include <shell.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* Return a string corresponding to the error number E. From + the ANSI C spec. */ +#if defined (strerror) +# undef strerror +#endif + +static char *errbase = "Unknown system error "; + +char * +strerror (e) + int e; +{ + static char emsg[40]; +#if defined (HAVE_SYS_ERRLIST) + extern int sys_nerr; + extern char *sys_errlist[]; + + if (e > 0 && e < sys_nerr) + return (sys_errlist[e]); + else +#endif /* HAVE_SYS_ERRLIST */ + { + char *z; + + z = itos (e); + strcpy (emsg, errbase); + strcat (emsg, z); + free (z); + return (&emsg[0]); + } +} +#endif /* HAVE_STRERROR */ diff --git a/lib/sh/strftime.c b/lib/sh/strftime.c new file mode 100644 index 0000000..60bee79 --- /dev/null +++ b/lib/sh/strftime.c @@ -0,0 +1,1012 @@ +/* strftime - formatted time and date to a string */ +/* + * 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. + * + * 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 + * Updated December, 2001 + * Updated January, 2011 + * Updated April, 2012 + * + * 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 + * Fixes from Tanaka Akira <akr@m17n.org> + * December 2001 + */ +#include <config.h> + +#include <sys/types.h> + +#include <stdio.h> +#include <ctype.h> +#include <posixtime.h> +#include <errno.h> + +#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 */ +#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */ +#define POSIX_2008 1 /* flag and fw for C, F, G, Y formats */ + +#undef strchr /* avoid AIX weirdness */ + +#if !defined (errno) +extern int errno; +#endif + +#if defined (SHELL) +extern char *get_string_value (const char *); +#endif + +extern void tzset(void); +static int weeknumber(const struct tm *timeptr, int firstweekday); +static int iso8601wknum(const struct tm *timeptr); + +#ifndef inline +#ifdef __GNUC__ +#define inline __inline__ +#else +#define inline /**/ +#endif +#endif + +#define range(low, item, hi) max(low, min(item, hi)) + +/* Whew! This stuff is a mess. */ +#if !defined(OS2) && !defined(MSDOS) && !defined(__CYGWIN__) && defined(HAVE_TZNAME) +extern char *tzname[2]; +extern int daylight; +#if defined(SOLARIS) || defined(mips) || defined (M_UNIX) +extern long int timezone, altzone; +#else +# if defined (HPUX) || defined(__hpux) +extern long int timezone; +# else +# if !defined(__CYGWIN__) +extern int timezone, altzone; +# endif +# endif +#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); +} + +#ifdef POSIX_2008 +/* iso_8601_2000_year --- format a year per ISO 8601:2000 as in 1003.1 */ + +static void +iso_8601_2000_year(char *buf, int year, size_t fw) +{ + int extra; + char sign = '\0'; + + if (year >= -9999 && year <= 9999) { + sprintf(buf, "%0*d", (int) fw, year); + return; + } + + /* now things get weird */ + if (year > 9999) { + sign = '+'; + } else { + sign = '-'; + year = -year; + } + + extra = year / 10000; + year %= 10000; + sprintf(buf, "%c_%04d_%d", sign, extra, year); +} +#endif /* POSIX_2008 */ + +/* 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, oerrno; + long 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 +#ifndef __CYGWIN__ + extern char *timezone(); + struct timeval tv; + struct timezone zone; +#endif /* __CYGWIN__ */ +#endif /* HAVE_TZNAME */ +#endif /* HAVE_TM_NAME */ +#endif /* HAVE_TM_ZONE */ +#ifdef POSIX_2008 + int pad; + size_t fw; + char flag; +#endif /* POSIX_2008 */ + + /* 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", }; + + oerrno = errno; + + 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; + } +#ifdef POSIX_2008 + pad = '\0'; + fw = 0; + flag = '\0'; + switch (*++format) { + case '+': + flag = '+'; + /* fall through */ + case '0': + pad = '0'; + format++; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + + default: + format--; + goto again; + } + for (; isdigit(*format); format++) { + fw = fw * 10 + (*format - '0'); + } + format--; +#endif /* POSIX_2008 */ + + 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': +#ifdef POSIX_2008 + if (pad != '\0' && fw > 0) { + size_t min_fw = (flag ? 3 : 2); + + fw = max(fw, min_fw); + sprintf(tbuf, flag + ? "%+0*ld" + : "%0*ld", (int) fw, + (timeptr->tm_year + 1900L) / 100); + } else +#endif /* POSIX_2008 */ + century: + sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 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 */ + { +#ifdef POSIX_2008 + /* + * Field width for %F is for the whole thing. + * It must be at least 10. + */ + char m_d[10]; + strftime(m_d, sizeof m_d, "-%m-%d", timeptr); + size_t min_fw = 10; + + if (pad != '\0' && fw > 0) { + fw = max(fw, min_fw); + } else { + fw = min_fw; + } + + fw -= 6; /* -XX-XX at end are invariant */ + + iso_8601_2000_year(tbuf, timeptr->tm_year + 1900, fw); + strcat(tbuf, m_d); +#else + strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr); +#endif /* POSIX_2008 */ + } + 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 = 1900L + timeptr->tm_year + 1; + else if (timeptr->tm_mon == 0 && w >= 52) + y = 1900L + timeptr->tm_year - 1; + else + y = 1900L + timeptr->tm_year; + + if (*format == 'G') { +#ifdef POSIX_2008 + if (pad != '\0' && fw > 0) { + size_t min_fw = 4; + + fw = max(fw, min_fw); + sprintf(tbuf, flag + ? "%+0*ld" + : "%0*ld", (int) fw, + y); + } else +#endif /* POSIX_2008 */ + sprintf(tbuf, "%ld", y); + } + else + sprintf(tbuf, "%02ld", 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) + 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) */ + + 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 */ +#ifdef POSIX_2008 + if (pad != '\0' && fw > 0) { + size_t min_fw = 4; + + fw = max(fw, min_fw); + sprintf(tbuf, flag + ? "%+0*ld" + : "%0*ld", (int) fw, + 1900L + timeptr->tm_year); + } else +#endif /* POSIX_2008 */ + sprintf(tbuf, "%ld", 1900L + 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 */ + if (timeptr->tm_isdst < 0) + break; +#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. + */ +# if defined(__hpux) || defined (HPUX) || defined(__CYGWIN__) + off = -timezone / 60; +# else + /* ADR: 4 August 2001, fixed this per gazelle@interaccess.com */ + off = -(daylight ? altzone : timezone) / 60; +# endif +#else /* !HAVE_TZNAME */ + gettimeofday(& tv, & zone); + 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, "%02ld%02ld", off/60, off%60); + break; + + case 'Z': /* time zone name or abbreviation */ +#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-%4ld", + range(1, timeptr->tm_mday, 31), + months_a[range(0, timeptr->tm_mon, 11)], + timeptr->tm_year + 1900L); + 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'; + if (s == start) + errno = oerrno; + return (s - start); + } else + return 0; +} + +/* isleap --- is a year a leap year? */ + +static int +isleap(long 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 + 1900L); + 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) Emperor/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) Emperor/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 */ diff --git a/lib/sh/stringlist.c b/lib/sh/stringlist.c new file mode 100644 index 0000000..3f28b63 --- /dev/null +++ b/lib/sh/stringlist.c @@ -0,0 +1,297 @@ +/* stringlist.c - functions to handle a generic `list of strings' structure */ + +/* Copyright (C) 2000-2019 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <bashansi.h> + +#include "shell.h" + +#ifdef STRDUP +# undef STRDUP +#endif +#define STRDUP(x) ((x) ? savestring (x) : (char *)NULL) + +/* Allocate a new STRINGLIST, with room for N strings. */ + +STRINGLIST * +strlist_create (n) + int n; +{ + STRINGLIST *ret; + register int i; + + ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST)); + if (n) + { + ret->list = strvec_create (n+1); + ret->list_size = n; + for (i = 0; i < n; i++) + ret->list[i] = (char *)NULL; + } + else + { + ret->list = (char **)NULL; + ret->list_size = 0; + } + ret->list_len = 0; + return ret; +} + +STRINGLIST * +strlist_resize (sl, n) + STRINGLIST *sl; + int n; +{ + register int i; + + if (sl == 0) + return (sl = strlist_create (n)); + + if (n > sl->list_size) + { + 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 +strlist_dispose (sl) + STRINGLIST *sl; +{ + if (sl == 0) + return; + if (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 * +strlist_copy (sl) + STRINGLIST *sl; +{ + STRINGLIST *new; + register int i; + + if (sl == 0) + return ((STRINGLIST *)0); + 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++) + new->list[i] = STRDUP (sl->list[i]); + } + new->list_size = sl->list_size; + new->list_len = sl->list_len; + /* just being careful */ + if (new->list) + new->list[new->list_len] = (char *)NULL; + return new; +} + +/* Return a new STRINGLIST with everything from M1 and M2. */ + +STRINGLIST * +strlist_merge (m1, m2) + STRINGLIST *m1, *m2; +{ + STRINGLIST *sl; + int i, n, l1, l2; + + l1 = m1 ? m1->list_len : 0; + l2 = m2 ? m2->list_len : 0; + + 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++) + sl->list[n] = STRDUP (m2->list[i]); + sl->list_len = n; + sl->list[n] = (char *)NULL; + return (sl); +} + +/* Make STRINGLIST M1 contain everything in M1 and M2. */ +STRINGLIST * +strlist_append (m1, m2) + STRINGLIST *m1, *m2; +{ + register int i, n, len1, len2; + + if (m1 == 0) + return (m2 ? strlist_copy (m2) : (STRINGLIST *)0); + + len1 = m1->list_len; + len2 = m2 ? m2->list_len : 0; + + if (len2) + { + 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; + m1->list_len = n; + } + + return m1; +} + +STRINGLIST * +strlist_prefix_suffix (sl, prefix, suffix) + STRINGLIST *sl; + char *prefix, *suffix; +{ + int plen, slen, tlen, llen, i; + char *t; + + if (sl == 0 || sl->list == 0 || sl->list_len == 0) + return sl; + + plen = STRLEN (prefix); + slen = STRLEN (suffix); + + if (plen == 0 && slen == 0) + return (sl); + + for (i = 0; i < sl->list_len; i++) + { + llen = STRLEN (sl->list[i]); + tlen = plen + llen + slen + 1; + t = (char *)xmalloc (tlen + 1); + if (plen) + strcpy (t, prefix); + strcpy (t + plen, sl->list[i]); + if (slen) + strcpy (t + plen + llen, suffix); + free (sl->list[i]); + sl->list[i] = t; + } + + return (sl); +} + +void +strlist_print (sl, prefix) + STRINGLIST *sl; + char *prefix; +{ + register int i; + + if (sl == 0) + return; + for (i = 0; i < sl->list_len; i++) + printf ("%s%s\n", prefix ? prefix : "", sl->list[i]); +} + +void +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; + strvec_sort (sl->list, 0); +} + +STRINGLIST * +strlist_from_word_list (list, alloc, starting_index, ip) + WORD_LIST *list; + int alloc, starting_index, *ip; +{ + STRINGLIST *ret; + int slen, len; + + if (list == 0) + { + if (ip) + *ip = 0; + return ((STRINGLIST *)0); + } + slen = list_length (list); + ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST)); + ret->list = strvec_from_word_list (list, alloc, starting_index, &len); + ret->list_size = slen + starting_index; + ret->list_len = len; + if (ip) + *ip = len; + return ret; +} + +WORD_LIST * +strlist_to_word_list (sl, alloc, starting_index) + STRINGLIST *sl; + int alloc, starting_index; +{ + WORD_LIST *list; + + if (sl == 0 || sl->list == 0) + return ((WORD_LIST *)NULL); + + list = strvec_to_word_list (sl->list, alloc, starting_index); + return list; +} diff --git a/lib/sh/stringvec.c b/lib/sh/stringvec.c new file mode 100644 index 0000000..8600042 --- /dev/null +++ b/lib/sh/stringvec.c @@ -0,0 +1,272 @@ +/* stringvec.c - functions for managing arrays of strings. */ + +/* Copyright (C) 2000-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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <bashansi.h> +#include <stdio.h> +#include <chartypes.h> + +#include "shell.h" + +/* Allocate an array of strings with room for N members. */ +char ** +strvec_create (n) + int n; +{ + return ((char **)xmalloc ((n) * sizeof (char *))); +} + +/* Allocate an array of strings with room for N members. */ +char ** +strvec_mcreate (n) + int n; +{ + return ((char **)malloc ((n) * sizeof (char *))); +} + +char ** +strvec_resize (array, nsize) + char **array; + int nsize; +{ + return ((char **)xrealloc (array, nsize * sizeof (char *))); +} + +char ** +strvec_mresize (array, nsize) + char **array; + int nsize; +{ + return ((char **)realloc (array, nsize * sizeof (char *))); +} + +/* Return the length of ARRAY, a NULL terminated array of char *. */ +int +strvec_len (array) + char **array; +{ + register int i; + + for (i = 0; array[i]; i++); + return (i); +} + +/* Free the contents of ARRAY, a NULL terminated array of char *. */ +void +strvec_flush (array) + char **array; +{ + register int i; + + if (array == 0) + return; + + for (i = 0; array[i]; i++) + free (array[i]); +} + +void +strvec_dispose (array) + char **array; +{ + if (array == 0) + return; + + 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; +} + +/* 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); +} + +/* Allocate and return a new copy of ARRAY and its contents. */ +char ** +strvec_copy (array) + char **array; +{ + register int i; + int len; + char **ret; + + len = strvec_len (array); + + ret = (char **)xmalloc ((len + 1) * sizeof (char *)); + for (i = 0; array[i]; i++) + ret[i] = savestring (array[i]); + ret[i] = (char *)NULL; + + return (ret); +} + +/* Comparison routine for use by qsort that conforms to the new Posix + requirements (http://austingroupbugs.net/view.php?id=1070). + + Perform a bytewise comparison if *S1 and *S2 collate equally. */ +int +strvec_posixcmp (s1, s2) + register char **s1, **s2; +{ + int result; + +#if defined (HAVE_STRCOLL) + result = strcoll (*s1, *s2); + if (result != 0) + return result; +#endif + + if ((result = **s1 - **s2) == 0) + result = strcmp (*s1, *s2); + + return (result); +} + +/* Comparison routine for use with qsort() on arrays of strings. Uses + strcoll(3) if available, otherwise it uses strcmp(3). */ +int +strvec_strcmp (s1, s2) + register char **s1, **s2; +{ +#if defined (HAVE_STRCOLL) + return (strcoll (*s1, *s2)); +#else /* !HAVE_STRCOLL */ + int result; + + if ((result = **s1 - **s2) == 0) + result = strcmp (*s1, *s2); + + return (result); +#endif /* !HAVE_STRCOLL */ +} + +/* Sort ARRAY, a null terminated array of pointers to strings. */ +void +strvec_sort (array, posix) + char **array; + int posix; +{ + if (posix) + qsort (array, strvec_len (array), sizeof (char *), (QSFUNC *)strvec_posixcmp); + else + 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 *)); +} diff --git a/lib/sh/strnlen.c b/lib/sh/strnlen.c new file mode 100644 index 0000000..10414d3 --- /dev/null +++ b/lib/sh/strnlen.c @@ -0,0 +1,49 @@ +/* strnlen - return length of passed string, with length limit */ + +/* Copyright (C) 2004 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined (HAVE_STRNLEN) + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdc.h> + +/* Find the length of S, but scan at most MAXLEN characters. If no '\0' + terminator is found within the first MAXLEN characters, return MAXLEN. */ +size_t +strnlen (s, maxlen) + register const char *s; + size_t maxlen; +{ + register const char *e; + size_t n; + + for (e = s, n = 0; *e && n < maxlen; e++, n++) + ; + return n; +} +#endif diff --git a/lib/sh/strpbrk.c b/lib/sh/strpbrk.c new file mode 100644 index 0000000..8cce830 --- /dev/null +++ b/lib/sh/strpbrk.c @@ -0,0 +1,49 @@ +/* strpbrk.c - locate multiple characters in a string */ + +/* Copyright (C) 1991, 1994 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined (HAVE_STRPBRK) + +#include <stdc.h> + +/* Find the first occurrence in S of any character in ACCEPT. */ +char * +strpbrk (s, accept) + register const char *s; + register const char *accept; +{ + while (*s != '\0') + { + const char *a = accept; + while (*a != '\0') + if (*a++ == *s) + return (char *) s; + ++s; + } + + return 0; +} +#endif diff --git a/lib/sh/strstr.c b/lib/sh/strstr.c new file mode 100644 index 0000000..c43b05e --- /dev/null +++ b/lib/sh/strstr.c @@ -0,0 +1,125 @@ +/* strstr - find a substring within a string */ + +/* Copyright (C) 1994, 1999 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * My personal strstr() implementation that beats most other algorithms. + * Until someone tells me otherwise, I assume that this is the + * fastest implementation of strstr() in C. + * I deliberately chose not to comment it. You should have at least + * as much fun trying to understand it, as I had to write it :-). + * + * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined _LIBC || defined HAVE_STRING_H +# include <string.h> +#endif +#include <sys/types.h> + +typedef unsigned chartype; + +#undef strstr + +char * +strstr (const char *phaystack, const char *pneedle) +{ + register const unsigned char *haystack, *needle; + register chartype b, c; + + haystack = (const unsigned char *) phaystack; + needle = (const unsigned char *) pneedle; + + b = *needle; + if (b != '\0') + { + haystack--; /* possible ANSI violation */ + do + { + c = *++haystack; + if (c == '\0') + goto ret0; + } + while (c != b); + + c = *++needle; + if (c == '\0') + goto foundneedle; + ++needle; + goto jin; + + for (;;) + { + register chartype a; + register const unsigned char *rhaystack, *rneedle; + + do + { + a = *++haystack; + if (a == '\0') + goto ret0; + if (a == b) + break; + a = *++haystack; + if (a == '\0') + goto ret0; +shloop:; } + while (a != b); + +jin: a = *++haystack; + if (a == '\0') + goto ret0; + + if (a != c) + goto shloop; + + rhaystack = haystack-- + 1; + rneedle = needle; + a = *rneedle; + + if (*rhaystack == a) + do + { + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = *++needle; + if (*rhaystack != a) + break; + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = *++needle; + } + while (*rhaystack == a); + + needle = rneedle; /* took the register-poor approach */ + + if (a == '\0') + break; + } + } +foundneedle: + return (char*) haystack; +ret0: + return 0; +} diff --git a/lib/sh/strtod.c b/lib/sh/strtod.c new file mode 100644 index 0000000..55e1154 --- /dev/null +++ b/lib/sh/strtod.c @@ -0,0 +1,207 @@ +/* strtod.c - convert string to double-precision floating-point value. */ + +/* Copyright (C) 1991, 1992 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef HAVE_STRTOD + +#include <errno.h> +#ifndef errno +extern int errno; +#endif + +#include <chartypes.h> +#include <math.h> + +#if HAVE_FLOAT_H +# include <float.h> +#else +# define DBL_MAX 1.7976931348623159e+308 +# define DBL_MIN 2.2250738585072010e-308 +#endif + +#include <bashansi.h> + +#ifndef NULL +# define NULL 0 +#endif + +#ifndef HUGE_VAL +# define HUGE_VAL HUGE +#endif + +#ifndef locale_decpoint +extern int locale_decpoint PARAMS((void)); +#endif + +/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the + character after the last one used in the number is put in *ENDPTR. */ +double +strtod (nptr, endptr) + const char *nptr; + char **endptr; +{ + register const char *s; + short sign; + + /* The number so far. */ + double num; + + int radixchar; + int got_dot; /* Found a decimal point. */ + int got_digit; /* Seen any digits. */ + + /* The exponent of the number. */ + long int exponent; + + if (nptr == NULL) + { + errno = EINVAL; + goto noconv; + } + + s = nptr; + + /* Eat whitespace. */ + while (ISSPACE ((unsigned char)*s)) + ++s; + + /* Get the sign. */ + sign = *s == '-' ? -1 : 1; + if (*s == '-' || *s == '+') + ++s; + + radixchar = locale_decpoint (); + num = 0.0; + got_dot = 0; + got_digit = 0; + exponent = 0; + for (;; ++s) + { + if (DIGIT (*s)) + { + got_digit = 1; + + /* Make sure that multiplication by 10 will not overflow. */ + if (num > DBL_MAX * 0.1) + /* The value of the digit doesn't matter, since we have already + gotten as many digits as can be represented in a `double'. + This doesn't necessarily mean the result will overflow. + The exponent may reduce it to within range. + + We just need to record that there was another + digit so that we can multiply by 10 later. */ + ++exponent; + else + num = (num * 10.0) + (*s - '0'); + + /* Keep track of the number of digits after the decimal point. + If we just divided by 10 here, we would lose precision. */ + if (got_dot) + --exponent; + } + else if (!got_dot && *s == radixchar) + /* Record that we have found the decimal point. */ + got_dot = 1; + else + /* Any other character terminates the number. */ + break; + } + + if (!got_digit) + goto noconv; + + if (TOLOWER ((unsigned char)*s) == 'e') + { + /* Get the exponent specified after the `e' or `E'. */ + int save = errno; + char *end; + long int exp; + + errno = 0; + ++s; + exp = strtol (s, &end, 10); + if (errno == ERANGE) + { + /* The exponent overflowed a `long int'. It is probably a safe + assumption that an exponent that cannot be represented by + a `long int' exceeds the limits of a `double'. */ + if (endptr != NULL) + *endptr = end; + if (exp < 0) + goto underflow; + else + goto overflow; + } + else if (end == s) + /* There was no exponent. Reset END to point to + the 'e' or 'E', so *ENDPTR will be set there. */ + end = (char *) s - 1; + errno = save; + s = end; + exponent += exp; + } + + if (endptr != NULL) + *endptr = (char *) s; + + if (num == 0.0) + return 0.0; + + /* Multiply NUM by 10 to the EXPONENT power, + checking for overflow and underflow. */ + + if (exponent < 0) + { + if (num < DBL_MIN * pow (10.0, (double) -exponent)) + goto underflow; + } + else if (exponent > 0) + { + if (num > DBL_MAX * pow (10.0, (double) -exponent)) + goto overflow; + } + + num *= pow (10.0, (double) exponent); + + return num * sign; + +overflow: + /* Return an overflow error. */ + errno = ERANGE; + return HUGE_VAL * sign; + +underflow: + /* Return an underflow error. */ + if (endptr != NULL) + *endptr = (char *) nptr; + errno = ERANGE; + return 0.0; + +noconv: + /* There was no number. */ + if (endptr != NULL) + *endptr = (char *) nptr; + return 0.0; +} + +#endif /* !HAVE_STRTOD */ diff --git a/lib/sh/strtoimax.c b/lib/sh/strtoimax.c new file mode 100644 index 0000000..584fa0b --- /dev/null +++ b/lib/sh/strtoimax.c @@ -0,0 +1,113 @@ +/* strtoimax - convert string representation of a number into an intmax_t value. */ + +/* Copyright 1999-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Written by Paul Eggert. Modified by Chet Ramey for Bash. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#if HAVE_STDINT_H +# include <stdint.h> +#endif + +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include <stdc.h> + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +#ifndef HAVE_DECL_STRTOL +"this configure-time declaration test was not run" +#endif +#if !HAVE_DECL_STRTOL +extern long strtol PARAMS((const char *, char **, int)); +#endif + +#ifndef HAVE_DECL_STRTOLL +"this configure-time declaration test was not run" +#endif +#if !HAVE_DECL_STRTOLL && HAVE_LONG_LONG_INT +extern long long strtoll PARAMS((const char *, char **, int)); +#endif + +#ifdef strtoimax +#undef strtoimax +#endif + +intmax_t +strtoimax (ptr, endptr, base) + const char *ptr; + char **endptr; + int base; +{ +#if HAVE_LONG_LONG_INT + verify(size_is_that_of_long_or_long_long, + (sizeof (intmax_t) == sizeof (long) || + sizeof (intmax_t) == sizeof (long long))); + + if (sizeof (intmax_t) != sizeof (long)) + return (strtoll (ptr, endptr, base)); +#else + verify (size_is_that_of_long, sizeof (intmax_t) == sizeof (long)); +#endif + + return (strtol (ptr, endptr, base)); +} + +#ifdef TESTING +# include <stdio.h> +int +main () +{ + char *p, *endptr; + intmax_t x; +#if HAVE_LONG_LONG_INT + long long y; +#endif + long z; + + printf ("sizeof intmax_t: %d\n", sizeof (intmax_t)); + +#if HAVE_LONG_LONG_INT + printf ("sizeof long long: %d\n", sizeof (long long)); +#endif + printf ("sizeof long: %d\n", sizeof (long)); + + x = strtoimax("42", &endptr, 10); +#if HAVE_LONG_LONG_INT + y = strtoll("42", &endptr, 10); +#else + y = -1; +#endif + z = strtol("42", &endptr, 10); + + printf ("%lld %lld %ld\n", x, y, z); + + exit (0); +} +#endif diff --git a/lib/sh/strtol.c b/lib/sh/strtol.c new file mode 100644 index 0000000..c839a97 --- /dev/null +++ b/lib/sh/strtol.c @@ -0,0 +1,259 @@ +/* strtol - convert string representation of a number into a long integer value. */ + +/* Copyright (C) 1991,92,94,95,96,97,98,99,2000,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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !HAVE_STRTOL + +#include <chartypes.h> +#include <errno.h> + +#ifndef errno +extern int errno; +#endif + +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <typemax.h> + +#include <stdc.h> +#include <bashansi.h> + +#ifndef NULL +# define NULL 0 +#endif + +/* Nonzero if we are defining `strtoul' or `strtoull', operating on + unsigned integers. */ +#ifndef UNSIGNED +# define UNSIGNED 0 +# define INT LONG int +#else +# define INT unsigned LONG int +#endif + +#if UNSIGNED +# ifdef QUAD +# define strtol strtoull +# else +# define strtol strtoul +# endif +#else +# ifdef QUAD +# define strtol strtoll +# endif +#endif + +/* If QUAD is defined, we are defining `strtoll' or `strtoull', + operating on `long long ints. */ + +#ifdef QUAD +# define LONG long long +# define STRTOL_LONG_MIN LLONG_MIN +# define STRTOL_LONG_MAX LLONG_MAX +# define STRTOL_ULONG_MAX ULLONG_MAX +#else /* !QUAD */ +# define LONG long +# define STRTOL_LONG_MIN LONG_MIN +# define STRTOL_LONG_MAX LONG_MAX +# define STRTOL_ULONG_MAX ULONG_MAX +#endif + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is no longer reset to 10; EINVAL is returned. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ + +INT +strtol (nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + int negative; + register unsigned LONG int cutoff; + register unsigned int cutlim; + register unsigned LONG int i; + register const char *s; + register unsigned char c; + const char *save, *end; + int overflow; + + if (base < 0 || base == 1 || base > 36) + { + __set_errno (EINVAL); + return 0; + } + + save = s = nptr; + + /* Skip white space. */ + while (ISSPACE ((unsigned char)*s)) + ++s; + if (*s == '\0') + goto noconv; + + /* Check for a sign. */ + if (*s == '-' || *s == '+') + { + negative = (*s == '-'); + ++s; + } + else + negative = 0; + + /* Recognize number prefix and if BASE is zero, figure it out ourselves. */ + if (*s == '0') + { + if ((base == 0 || base == 16) && TOUPPER ((unsigned char) s[1]) == 'X') + { + s += 2; + base = 16; + } + else if (base == 0) + base = 8; + } + else if (base == 0) + base = 10; + + /* Save the pointer so we can check later if anything happened. */ + save = s; + + end = NULL; + + cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base; + cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base; + + overflow = 0; + i = 0; + c = *s; + if (sizeof (long int) != sizeof (LONG int)) + { + unsigned long int j = 0; + unsigned long int jmax = ULONG_MAX / base; + + for (;c != '\0'; c = *++s) + { + if (s == end) + break; + if (DIGIT (c)) + c -= '0'; + else if (ISALPHA (c)) + c = TOUPPER (c) - 'A' + 10; + else + break; + + if ((int) c >= base) + break; + /* Note that we never can have an overflow. */ + else if (j >= jmax) + { + /* We have an overflow. Now use the long representation. */ + i = (unsigned LONG int) j; + goto use_long; + } + else + j = j * (unsigned long int) base + c; + } + + i = (unsigned LONG int) j; + } + else + for (;c != '\0'; c = *++s) + { + if (s == end) + break; + if (DIGIT (c)) + c -= '0'; + else if (ISALPHA (c)) + c = TOUPPER (c) - 'A' + 10; + else + break; + if ((int) c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + use_long: + i *= (unsigned LONG int) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (char *) s; + +#if !UNSIGNED + /* Check for a value that is within the range of + `unsigned LONG int', but outside the range of `LONG int'. */ + if (overflow == 0 + && i > (negative + ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1 + : (unsigned LONG int) STRTOL_LONG_MAX)) + overflow = 1; +#endif + + if (overflow) + { + __set_errno (ERANGE); +#if UNSIGNED + return STRTOL_ULONG_MAX; +#else + return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return negative ? -i : i; + +noconv: + /* We must handle a special case here: the base is 0 or 16 and the + first two characters are '0' and 'x', but the rest are no + hexadecimal digits. This is no error case. We return 0 and + ENDPTR points to the `x`. */ + if (endptr != NULL) + { + if (save - nptr >= 2 && TOUPPER ((unsigned char) save[-1]) == 'X' && save[-2] == '0') + *endptr = (char *) &save[-1]; + else + /* There was no number to convert. */ + *endptr = (char *) nptr; + } + + return 0L; +} + +#endif /* !HAVE_STRTOL */ diff --git a/lib/sh/strtoll.c b/lib/sh/strtoll.c new file mode 100644 index 0000000..f90300b --- /dev/null +++ b/lib/sh/strtoll.c @@ -0,0 +1,30 @@ +/* strtoll - convert string representation of a number into a long long value. */ + +/* Copyright (C) 1997 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_LONG_LONG_INT) && !HAVE_STRTOLL + +#define QUAD 1 +#undef HAVE_STRTOL + +#include "strtol.c" + +#endif /* HAVE_LONG_LONG_INT && !HAVE_STRTOLL */ diff --git a/lib/sh/strtoul.c b/lib/sh/strtoul.c new file mode 100644 index 0000000..cbaa484 --- /dev/null +++ b/lib/sh/strtoul.c @@ -0,0 +1,30 @@ +/* strtoul - convert string representation of a number into an unsigned long value. */ + +/* Copyright (C) 1997 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifndef HAVE_STRTOUL + +#define UNSIGNED 1 +#undef HAVE_STRTOL + +#include <strtol.c> + +#endif /* !HAVE_STRTOUL */ diff --git a/lib/sh/strtoull.c b/lib/sh/strtoull.c new file mode 100644 index 0000000..fe6592e --- /dev/null +++ b/lib/sh/strtoull.c @@ -0,0 +1,31 @@ +/* strtoull - convert string representation of a number into an unsigned long long value. */ + +/* Copyright (C) 1997 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNSIGNED_LONG_LONG_INT) && !HAVE_STRTOULL + +#define QUAD 1 +#define UNSIGNED 1 +#undef HAVE_STRTOL + +#include "strtol.c" + +#endif /* HAVE_UNSIGNED_LONG_LONG_INT && !HAVE_STRTOULL */ diff --git a/lib/sh/strtoumax.c b/lib/sh/strtoumax.c new file mode 100644 index 0000000..2e26efc --- /dev/null +++ b/lib/sh/strtoumax.c @@ -0,0 +1,113 @@ +/* strtoumax - convert string representation of a number into an uintmax_t value. */ + +/* Copyright 1999-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Written by Paul Eggert. Modified by Chet Ramey for Bash. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#if HAVE_STDINT_H +# include <stdint.h> +#endif + +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include <stdc.h> + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +#ifndef HAVE_DECL_STRTOUL +"this configure-time declaration test was not run" +#endif +#if !HAVE_DECL_STRTOUL +extern unsigned long strtoul PARAMS((const char *, char **, int)); +#endif + +#ifndef HAVE_DECL_STRTOULL +"this configure-time declaration test was not run" +#endif +#if !HAVE_DECL_STRTOULL && HAVE_UNSIGNED_LONG_LONG_INT +extern unsigned long long strtoull PARAMS((const char *, char **, int)); +#endif + +#ifdef strtoumax +#undef strtoumax +#endif + +uintmax_t +strtoumax (ptr, endptr, base) + const char *ptr; + char **endptr; + int base; +{ +#if HAVE_UNSIGNED_LONG_LONG_INT + verify (size_is_that_of_unsigned_long_or_unsigned_long_long, + (sizeof (uintmax_t) == sizeof (unsigned long) || + sizeof (uintmax_t) == sizeof (unsigned long long))); + + if (sizeof (uintmax_t) != sizeof (unsigned long)) + return (strtoull (ptr, endptr, base)); +#else + verify (size_is_that_of_unsigned_long, sizeof (uintmax_t) == sizeof (unsigned long)); +#endif + + return (strtoul (ptr, endptr, base)); +} + +#ifdef TESTING +# include <stdio.h> +int +main () +{ + char *p, *endptr; + uintmax_t x; +#if HAVE_UNSIGNED_LONG_LONG_INT + unsigned long long y; +#endif + unsigned long z; + + printf ("sizeof uintmax_t: %d\n", sizeof (uintmax_t)); + +#if HAVE_UNSIGNED_LONG_LONG_INT + printf ("sizeof unsigned long long: %d\n", sizeof (unsigned long long)); +#endif + printf ("sizeof unsigned long: %d\n", sizeof (unsigned long)); + + x = strtoumax("42", &endptr, 10); +#if HAVE_LONG_LONG_INT + y = strtoull("42", &endptr, 10); +#else + y = 0; +#endif + z = strtoul("42", &endptr, 10); + + printf ("%llu %llu %lu\n", x, y, z); + + exit (0); +} +#endif diff --git a/lib/sh/strtrans.c b/lib/sh/strtrans.c new file mode 100644 index 0000000..b2b1acc --- /dev/null +++ b/lib/sh/strtrans.c @@ -0,0 +1,400 @@ +/* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */ + +/* Copyright (C) 2000-2015 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <bashansi.h> +#include <stdio.h> +#include <chartypes.h> + +#include "shell.h" + +#include "shmbchar.h" +#include "shmbutil.h" + +#ifdef ESC +#undef ESC +#endif +#define ESC '\033' /* ASCII */ + +/* Convert STRING by expanding the escape sequences specified by the + ANSI C standard. If SAWC is non-null, recognize `\c' and use that + as a string terminator. If we see \c, set *SAWC to 1 before + returning. LEN is the length of STRING. If (FLAGS&1) is non-zero, + that we're translating a string for `echo -e', and therefore should not + treat a single quote as a character that may be escaped with a backslash. + If (FLAGS&2) is non-zero, we're expanding for the parser and want to + quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want + to remove the backslash before any unrecognized escape sequence. */ +char * +ansicstr (string, len, flags, sawc, rlen) + char *string; + int len, flags, *sawc, *rlen; +{ + int c, temp; + char *ret, *r, *s; + unsigned long v; + size_t clen; + int b, mb_cur_max; +#if defined (HANDLE_MULTIBYTE) + wchar_t wc; +#endif + + if (string == 0 || *string == '\0') + return ((char *)NULL); + + mb_cur_max = MB_CUR_MAX; +#if defined (HANDLE_MULTIBYTE) + temp = 4*len + 4; + if (temp < 12) + temp = 12; /* ensure enough for eventual u32cesc */ + ret = (char *)xmalloc (temp); +#else + ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */ +#endif + for (r = ret, s = string; s && *s; ) + { + c = *s++; + if (c != '\\' || *s == '\0') + { + clen = 1; +#if defined (HANDLE_MULTIBYTE) + if ((locale_utf8locale && (c & 0x80)) || + (locale_utf8locale == 0 && mb_cur_max > 0 && is_basic (c) == 0)) + { + clen = mbrtowc (&wc, s - 1, mb_cur_max, 0); + if (MB_INVALIDCH (clen)) + clen = 1; + } +#endif + *r++ = c; + for (--clen; clen > 0; clen--) + *r++ = *s++; + } + else + { + switch (c = *s++) + { +#if defined (__STDC__) + case 'a': c = '\a'; break; + case 'v': c = '\v'; break; +#else + case 'a': c = (int) 0x07; break; + case 'v': c = (int) 0x0B; break; +#endif + case 'b': c = '\b'; break; + case 'e': case 'E': /* ESC -- non-ANSI */ + c = ESC; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': +#if 1 + if (flags & 1) + { + *r++ = '\\'; + break; + } + /*FALLTHROUGH*/ +#endif + case '0': + /* If (FLAGS & 1), we're translating a string for echo -e (or + the equivalent xpg_echo option), so we obey the SUSv3/ + POSIX-2001 requirement and accept 0-3 octal digits after + a leading `0'. */ + temp = 2 + ((flags & 1) && (c == '0')); + for (c -= '0'; ISOCTAL (*s) && temp--; s++) + c = (c * 8) + OCTVALUE (*s); + c &= 0xFF; + break; + case 'x': /* Hex digit -- non-ANSI */ + if ((flags & 2) && *s == '{') + { + flags |= 16; /* internal flag value */ + s++; + } + /* Consume at least two hex characters */ + for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++) + c = (c * 16) + HEXVALUE (*s); + /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars + until a non-xdigit or `}', so potentially more than two + chars are consumed. */ + if (flags & 16) + { + for ( ; ISXDIGIT ((unsigned char)*s); s++) + c = (c * 16) + HEXVALUE (*s); + flags &= ~16; + if (*s == '}') + s++; + } + /* \x followed by non-hex digits is passed through unchanged */ + else if (temp == 2) + { + *r++ = '\\'; + c = 'x'; + } + c &= 0xFF; + break; +#if defined (HANDLE_MULTIBYTE) + case 'u': + case 'U': + temp = (c == 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */ + for (v = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++) + v = (v * 16) + HEXVALUE (*s); + if (temp == ((c == 'u') ? 4 : 8)) + { + *r++ = '\\'; /* c remains unchanged */ + break; + } + else if (v <= 0x7f) /* <= 0x7f translates directly */ + { + c = v; + break; + } + else + { + temp = u32cconv (v, r); + r += temp; + continue; + } +#endif + case '\\': + break; + case '\'': case '"': case '?': + if (flags & 1) + *r++ = '\\'; + break; + case 'c': + if (sawc) + { + *sawc = 1; + *r = '\0'; + if (rlen) + *rlen = r - ret; + return ret; + } + else if ((flags & 1) == 0 && *s == 0) + ; /* pass \c through */ + else if ((flags & 1) == 0 && (c = *s)) + { + s++; + if ((flags & 2) && c == '\\' && c == *s) + s++; /* Posix requires $'\c\\' do backslash escaping */ + c = TOCTRL(c); + break; + } + /*FALLTHROUGH*/ + default: + if ((flags & 4) == 0) + *r++ = '\\'; + break; + } + if ((flags & 2) && (c == CTLESC || c == CTLNUL)) + *r++ = CTLESC; + *r++ = c; + } + } + *r = '\0'; + if (rlen) + *rlen = r - ret; + return ret; +} + +/* Take a string STR, possibly containing non-printing characters, and turn it + into a $'...' ANSI-C style quoted string. Returns a new string. */ +char * +ansic_quote (str, flags, rlen) + char *str; + int flags, *rlen; +{ + char *r, *ret, *s; + int l, rsize; + unsigned char c; + size_t clen; + int b; +#if defined (HANDLE_MULTIBYTE) + wchar_t wc; +#endif + + if (str == 0 || *str == 0) + return ((char *)0); + + l = strlen (str); + rsize = 4 * l + 4; + r = ret = (char *)xmalloc (rsize); + + *r++ = '$'; + *r++ = '\''; + + for (s = str; c = *s; s++) + { + b = l = 1; /* 1 == add backslash; 0 == no backslash */ + clen = 1; + + switch (c) + { + case ESC: c = 'E'; break; +#ifdef __STDC__ + case '\a': c = 'a'; break; + case '\v': c = 'v'; break; +#else + case 0x07: c = 'a'; break; + case 0x0b: c = 'v'; break; +#endif + + case '\b': c = 'b'; break; + case '\f': c = 'f'; break; + case '\n': c = 'n'; break; + case '\r': c = 'r'; break; + case '\t': c = 't'; break; + case '\\': + case '\'': + break; + default: +#if defined (HANDLE_MULTIBYTE) + b = is_basic (c); + /* XXX - clen comparison to 0 is dicey */ + if ((b == 0 && ((clen = mbrtowc (&wc, s, MB_CUR_MAX, 0)) < 0 || MB_INVALIDCH (clen) || iswprint (wc) == 0)) || + (b == 1 && ISPRINT (c) == 0)) +#else + if (ISPRINT (c) == 0) +#endif + { + *r++ = '\\'; + *r++ = TOCHAR ((c >> 6) & 07); + *r++ = TOCHAR ((c >> 3) & 07); + *r++ = TOCHAR (c & 07); + continue; + } + l = 0; + break; + } + if (b == 0 && clen == 0) + break; + + if (l) + *r++ = '\\'; + + if (clen == 1) + *r++ = c; + else + { + for (b = 0; b < (int)clen; b++) + *r++ = (unsigned char)s[b]; + s += clen - 1; /* -1 because of the increment above */ + } + } + + *r++ = '\''; + *r = '\0'; + if (rlen) + *rlen = r - ret; + return ret; +} + +#if defined (HANDLE_MULTIBYTE) +int +ansic_wshouldquote (string) + const char *string; +{ + const wchar_t *wcs; + wchar_t wcc; + wchar_t *wcstr = NULL; + size_t slen; + + slen = mbstowcs (wcstr, string, 0); + + if (slen == (size_t)-1) + return 1; + + wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1)); + mbstowcs (wcstr, string, slen + 1); + + for (wcs = wcstr; wcc = *wcs; wcs++) + if (iswprint(wcc) == 0) + { + free (wcstr); + return 1; + } + + free (wcstr); + return 0; +} +#endif + +/* return 1 if we need to quote with $'...' because of non-printing chars. */ +int +ansic_shouldquote (string) + const char *string; +{ + const char *s; + unsigned char c; + + if (string == 0) + return 0; + + for (s = string; c = *s; s++) + { +#if defined (HANDLE_MULTIBYTE) + if (is_basic (c) == 0) + return (ansic_wshouldquote (s)); +#endif + if (ISPRINT (c) == 0) + return 1; + } + + return 0; +} + +/* $'...' ANSI-C expand the portion of STRING between START and END and + return the result. The result cannot be longer than the input string. */ +char * +ansiexpand (string, start, end, lenp) + char *string; + int start, end, *lenp; +{ + char *temp, *t; + int len, tlen; + + temp = (char *)xmalloc (end - start + 1); + for (tlen = 0, len = start; len < end; ) + temp[tlen++] = string[len++]; + temp[tlen] = '\0'; + + if (*temp) + { + t = ansicstr (temp, tlen, 2, (int *)NULL, lenp); + free (temp); + return (t); + } + else + { + if (lenp) + *lenp = 0; + return (temp); + } +} diff --git a/lib/sh/strvis.c b/lib/sh/strvis.c new file mode 100644 index 0000000..7a11d57 --- /dev/null +++ b/lib/sh/strvis.c @@ -0,0 +1,154 @@ +/* strvis.c - make unsafe graphical characters in a string visible. */ + +/* Copyright (C) 2022 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* This is a stripped-down version suitable for the shell's use. */ +#include <config.h> + +#include <unistd.h> + +#include "bashansi.h" +#include <stdio.h> + +#include "chartypes.h" +#include "bashintl.h" +#include "shmbutil.h" + +#define SAFECHAR(c) ((c) == ' ' || (c) == '\t') + +#ifndef RUBOUT +#define RUBOUT 0x7f +#endif + +#ifndef CTRL_CHAR +#define CTRL_CHAR(c) ((c) < 0x20) +#endif + +#ifndef META_CHAR +#define META_CHAR(c) ((c) > 0x7f && (c) <= UCHAR_MAX) +#endif + +#ifndef UNCTRL +#define UNCTRL(c) (TOUPPER ((c) | 0x40)) +#endif + +#ifndef UNMETA +#define UNMETA(c) ((c) & 0x7f) +#endif + +int +sh_charvis (s, sindp, slen, ret, rindp) + const char *s; + size_t *sindp; + size_t slen; + char *ret; + size_t *rindp; +{ + unsigned char c; + size_t si, ri; + const char *send; + DECLARE_MBSTATE; + + si = *sindp; + ri = *rindp; + c = s[*sindp]; + +#if defined (HANDLE_MULTIBYTE) + send = (locale_mb_cur_max > 1) ? s + slen : 0; +#else + send = 0; +#endif + + if (SAFECHAR (c)) + { + ret[ri++] = c; + si++; + } + else if (c == RUBOUT) + { + ret[ri++] = '^'; + ret[ri++] = '?'; + si++; + } + else if (CTRL_CHAR (c)) + { + ret[ri++] = '^'; + ret[ri++] = UNCTRL (c); + si++; + } +#if defined (HANDLE_MULTIBYTE) + else if (locale_utf8locale && (c & 0x80)) + COPY_CHAR_I (ret, ri, s, send, si); + else if (locale_mb_cur_max > 1 && is_basic (c) == 0) + COPY_CHAR_I (ret, ri, s, send, si); +#endif + else if (META_CHAR (c)) + { + ret[ri++] = 'M'; + ret[ri++] = '-'; + ret[ri++] = UNMETA (c); + si++; + } + else + ret[ri++] = s[si++]; + + *sindp = si; + *rindp = ri; + + return si; +} + +/* Return a new string with `unsafe' non-graphical characters in S rendered + in a visible way. */ +char * +sh_strvis (string) + const char *string; +{ + size_t slen, sind; + char *ret; + size_t retind, retsize; + unsigned char c; + DECLARE_MBSTATE; + + if (string == 0) + return 0; + if (*string == '\0') + { + if ((ret = (char *)malloc (1)) == 0) + return 0; + ret[0] = '\0'; + return ret; + } + + slen = strlen (string); + retsize = 3 * slen + 1; + + ret = (char *)malloc (retsize); + if (ret == 0) + return 0; + + retind = 0; + sind = 0; + + while (string[sind]) + sind = sh_charvis (string, &sind, slen, ret, &retind); + + ret[retind] = '\0'; + return ret; +} diff --git a/lib/sh/timers.c b/lib/sh/timers.c new file mode 100644 index 0000000..69b754c --- /dev/null +++ b/lib/sh/timers.c @@ -0,0 +1,262 @@ +/* timers - functions to manage shell timers */ + +/* Copyright (C) 2021 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include "posixtime.h" + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#if defined (HAVE_SELECT) +# include "posixselect.h" +# include "stat-time.h" +#endif + +#include "sig.h" +#include "bashjmp.h" +#include "xmalloc.h" + +#include "timer.h" + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#ifndef FREE +#define FREE(s) do { if (s) free (s); } while (0) +#endif + +extern unsigned int falarm (unsigned int, unsigned int); + +static void shtimer_zero (sh_timer *); + +static void +shtimer_zero (sh_timer *t) +{ + t->tmout.tv_sec = 0; + t->tmout.tv_usec = 0; + + t->fd = -1; + t->flags = t->alrmflag = 0; + + t->alrm_handler = t->old_handler = 0; + + memset (t->jmpenv, '\0', sizeof (t->jmpenv)); + + t->tm_handler = 0; + t->data = 0; +} + +sh_timer * +shtimer_alloc (void) +{ + sh_timer *t; + + t = (sh_timer *)xmalloc (sizeof (sh_timer)); + shtimer_zero (t); + return t; +} + +void +shtimer_flush (sh_timer *t) +{ + /* The caller can manage t->data arbitrarily as long as it frees and sets + t->data to 0 before calling this function. Otherwise, we do what we can + to avoid memleaks. */ + FREE (t->data); + shtimer_zero (t); +} + +void +shtimer_dispose (sh_timer *t) +{ + free (t); +} + +/* We keep the timer as an offset into the future from the time it's set. */ +void +shtimer_set (sh_timer *t, time_t sec, long usec) +{ + struct timeval now; + + if (t->flags & SHTIMER_ALARM) + { + t->alrmflag = 0; /* just paranoia */ + t->old_handler = set_signal_handler (SIGALRM, t->alrm_handler); + t->flags |= SHTIMER_SIGSET; + falarm (t->tmout.tv_sec = sec, t->tmout.tv_usec = usec); + t->flags |= SHTIMER_ALRMSET; + return; + } + + if (gettimeofday (&now, 0) < 0) + timerclear (&now); + + t->tmout.tv_sec = now.tv_sec + sec; + t->tmout.tv_usec = now.tv_usec + usec; + if (t->tmout.tv_usec > USEC_PER_SEC) + { + t->tmout.tv_sec++; + t->tmout.tv_usec -= USEC_PER_SEC; + } +} + +void +shtimer_unset (sh_timer *t) +{ + t->tmout.tv_sec = 0; + t->tmout.tv_usec = 0; + + if (t->flags & SHTIMER_ALARM) + { + t->alrmflag = 0; + if (t->flags & SHTIMER_ALRMSET) + falarm (0, 0); + if (t->old_handler && (t->flags & SHTIMER_SIGSET)) + { + set_signal_handler (SIGALRM, t->old_handler); + t->flags &= ~SHTIMER_SIGSET; + t->old_handler = 0; + } + } +} + +void +shtimer_cleanup (sh_timer *t) +{ + shtimer_unset (t); +} + +void +shtimer_clear (sh_timer *t) +{ + shtimer_unset (t); + shtimer_dispose (t); +} + +int +shtimer_chktimeout (sh_timer *t) +{ + struct timeval now; + int r; + + /* Use the flag to avoid returning sigalrm_seen here */ + if (t->flags & SHTIMER_ALARM) + return t->alrmflag; + + /* Could check a flag for this */ + if (t->tmout.tv_sec == 0 && t->tmout.tv_usec == 0) + return 0; + + if (gettimeofday (&now, 0) < 0) + return 0; + r = ((now.tv_sec > t->tmout.tv_sec) || + (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec)); + + return r; +} + +#if defined (HAVE_SELECT) || defined (HAVE_PSELECT) +int +shtimer_select (sh_timer *t) +{ + int r, nfd; + sigset_t blocked_sigs, prevmask; + struct timeval now, tv; + fd_set readfds; +#if defined (HAVE_PSELECT) + struct timespec ts; +#endif + + /* We don't want a SIGCHLD to interrupt this */ + sigemptyset (&blocked_sigs); +# if defined (SIGCHLD) + sigaddset (&blocked_sigs, SIGCHLD); +# endif + + if (gettimeofday (&now, 0) < 0) + { + if (t->flags & SHTIMER_LONGJMP) + sh_longjmp (t->jmpenv, 1); + else + return -1; + } + + /* If the timer has already expired, return immediately */ + if ((now.tv_sec > t->tmout.tv_sec) || + (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec)) + { + if (t->flags & SHTIMER_LONGJMP) + sh_longjmp (t->jmpenv, 1); + else if (t->tm_handler) + return ((*t->tm_handler) (t)); + else + return 0; + } + + /* compute timeout */ + tv.tv_sec = t->tmout.tv_sec - now.tv_sec; + tv.tv_usec = t->tmout.tv_usec - now.tv_usec; + if (tv.tv_usec < 0) + { + tv.tv_sec--; + tv.tv_usec += USEC_PER_SEC; + } + +#if defined (HAVE_PSELECT) + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#else + sigemptyset (&prevmask); +#endif /* !HAVE_PSELECT */ + + nfd = (t->fd >= 0) ? t->fd + 1 : 0; + FD_ZERO (&readfds); + if (t->fd >= 0) + FD_SET (t->fd, &readfds); + +#if defined (HAVE_PSELECT) + r = pselect(nfd, &readfds, (fd_set *)0, (fd_set *)0, &ts, &blocked_sigs); +#else + sigprocmask (SIG_SETMASK, &blocked_sigs, &prevmask); + r = select(nfd, &readfds, (fd_set *)0, (fd_set *)0, &tv); + sigprocmask (SIG_SETMASK, &prevmask, NULL); +#endif + + if (r < 0) + return r; /* caller will handle */ + else if (r == 0 && (t->flags & SHTIMER_LONGJMP)) + sh_longjmp (t->jmpenv, 1); + else if (r == 0 && t->tm_handler) + return ((*t->tm_handler) (t)); + else + return r; +} +#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */ + +int +shtimer_alrm (sh_timer *t) +{ + return 0; +} diff --git a/lib/sh/times.c b/lib/sh/times.c new file mode 100644 index 0000000..2423078 --- /dev/null +++ b/lib/sh/times.c @@ -0,0 +1,77 @@ +/* times.c - times(3) library function */ + +/* Copyright (C) 1999-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_TIMES) + +#include <sys/types.h> +#include <posixtime.h> +#include <systimes.h> + +#if defined (HAVE_SYS_RESOURCE_H) && defined (HAVE_GETRUSAGE) +# include <sys/resource.h> +#endif /* HAVE_SYS_RESOURCE_H && HAVE_GETRUSAGE */ + +extern long get_clk_tck PARAMS((void)); + +#define CONVTCK(r) (r.tv_sec * clk_tck + r.tv_usec / (1000000 / clk_tck)) + +clock_t +times(tms) + struct tms *tms; +{ + clock_t rv; + static long clk_tck = -1; + +#if defined (HAVE_GETRUSAGE) + struct timeval tv; + struct rusage ru; + + if (clk_tck == -1) + clk_tck = get_clk_tck(); + + if (getrusage(RUSAGE_SELF, &ru) < 0) + return ((clock_t)-1); + tms->tms_utime = CONVTCK(ru.ru_utime); + tms->tms_stime = CONVTCK(ru.ru_stime); + + if (getrusage(RUSAGE_CHILDREN, &ru) < 0) + return ((clock_t)-1); + tms->tms_cutime = CONVTCK(ru.ru_utime); + tms->tms_cstime = CONVTCK(ru.ru_stime); + + if (gettimeofday(&tv, NULL) < 0) + return ((clock_t)-1); + rv = (clock_t)(CONVTCK(tv)); +#else /* !HAVE_GETRUSAGE */ + if (clk_tck == -1) + clk_tck = get_clk_tck(); + + /* We can't do anything. */ + tms->tms_utime = tms->tms_stime = (clock_t)0; + tms->tms_cutime = tms->tms_cstime = (clock_t)0; + + rv = (clock_t)time((time_t *)0) * clk_tck; +# endif /* HAVE_GETRUSAGE */ + + return rv; +} +#endif /* !HAVE_TIMES */ diff --git a/lib/sh/timeval.c b/lib/sh/timeval.c new file mode 100644 index 0000000..f2ca762 --- /dev/null +++ b/lib/sh/timeval.c @@ -0,0 +1,179 @@ +/* timeval.c - functions to perform operations on struct timevals */ + +/* Copyright (C) 1999 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_TIMEVAL) + +#include <sys/types.h> +#include <posixtime.h> + +#include <bashintl.h> +#include <stdc.h> + +#ifndef locale_decpoint +extern int locale_decpoint PARAMS((void)); +#endif + +#include <stdio.h> + +struct timeval * +difftimeval (d, t1, t2) + struct timeval *d, *t1, *t2; +{ + d->tv_sec = t2->tv_sec - t1->tv_sec; + d->tv_usec = t2->tv_usec - t1->tv_usec; + if (d->tv_usec < 0) + { + d->tv_usec += 1000000; + d->tv_sec -= 1; + if (d->tv_sec < 0) /* ??? -- BSD/OS does this */ + { + d->tv_sec = 0; + d->tv_usec = 0; + } + } + return d; +} + +struct timeval * +addtimeval (d, t1, t2) + struct timeval *d, *t1, *t2; +{ + d->tv_sec = t1->tv_sec + t2->tv_sec; + d->tv_usec = t1->tv_usec + t2->tv_usec; + if (d->tv_usec >= 1000000) + { + d->tv_usec -= 1000000; + d->tv_sec += 1; + } + return d; +} + +struct timeval * +multimeval (d, m) + struct timeval *d; + int m; +{ + time_t t; + + t = d->tv_usec * m; + d->tv_sec = d->tv_sec * m + t / 1000000; + d->tv_usec = t % 1000000; + return d; +} + +struct timeval * +divtimeval (d, m) + struct timeval *d; + int m; +{ + time_t t; + + t = d->tv_sec; + d->tv_sec = t / m; + d->tv_usec = (d->tv_usec + 1000000 * (t % m)) / m; + return d; +} + +/* Do "cpu = ((user + sys) * 10000) / real;" with timevals. + Barely-tested code from Deven T. Corzine <deven@ties.org>. */ +int +timeval_to_cpu (rt, ut, st) + struct timeval *rt, *ut, *st; /* real, user, sys */ +{ + struct timeval t1, t2; + register int i; + + addtimeval (&t1, ut, st); + t2.tv_sec = rt->tv_sec; + t2.tv_usec = rt->tv_usec; + + for (i = 0; i < 6; i++) + { + if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999)) + break; + t1.tv_sec *= 10; + t1.tv_sec += t1.tv_usec / 100000; + t1.tv_usec *= 10; + t1.tv_usec %= 1000000; + t2.tv_sec *= 10; + t2.tv_sec += t2.tv_usec / 100000; + t2.tv_usec *= 10; + t2.tv_usec %= 1000000; + } + for (i = 0; i < 4; i++) + { + if (t1.tv_sec < 100000000) + t1.tv_sec *= 10; + else + t2.tv_sec /= 10; + } + + return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec); +} + +/* Convert a pointer to a struct timeval to seconds and thousandths of a + second, returning the values in *SP and *SFP, respectively. This does + rounding on the fractional part, not just truncation to three places. */ +void +timeval_to_secs (tvp, sp, sfp) + struct timeval *tvp; + time_t *sp; + int *sfp; +{ + int rest; + + *sp = tvp->tv_sec; + + *sfp = tvp->tv_usec % 1000000; /* pretty much a no-op */ + rest = *sfp % 1000; + *sfp = (*sfp * 1000) / 1000000; + if (rest >= 500) + *sfp += 1; + + /* Sanity check */ + if (*sfp >= 1000) + { + *sp += 1; + *sfp -= 1000; + } +} + +/* Print the contents of a struct timeval * in a standard way to stdio + stream FP. */ +void +print_timeval (fp, tvp) + FILE *fp; + struct timeval *tvp; +{ + time_t timestamp; + long minutes; + int seconds, seconds_fraction; + + timeval_to_secs (tvp, ×tamp, &seconds_fraction); + + minutes = timestamp / 60; + seconds = timestamp % 60; + + fprintf (fp, "%ldm%d%c%03ds", minutes, seconds, locale_decpoint (), seconds_fraction); +} + +#endif /* HAVE_TIMEVAL */ diff --git a/lib/sh/tmpfile.c b/lib/sh/tmpfile.c new file mode 100644 index 0000000..ef8b067 --- /dev/null +++ b/lib/sh/tmpfile.c @@ -0,0 +1,311 @@ +/* + * tmpfile.c - functions to create and safely open temp files for the shell. + */ + +/* Copyright (C) 2000-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <bashtypes.h> +#include <posixstat.h> +#include <posixtime.h> +#include <filecntl.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <bashansi.h> + +#include <stdio.h> +#include <errno.h> + +#include <shell.h> + +#ifndef errno +extern int errno; +#endif + +#define BASEOPENFLAGS (O_CREAT | O_TRUNC | O_EXCL | O_BINARY) + +#define DEFAULT_TMPDIR "." /* bogus default, should be changed */ +#define DEFAULT_NAMEROOT "shtmp" + +/* Use ANSI-C rand() interface if random(3) is not available */ +#if !HAVE_RANDOM +#define random() rand() +#endif + +extern pid_t dollar_dollar_pid; + +static char *get_sys_tmpdir PARAMS((void)); +static char *get_tmpdir PARAMS((int)); + +static char *sys_tmpdir = (char *)NULL; +static int ntmpfiles; +static int tmpnamelen = -1; +static unsigned long filenum = 1L; + +static char * +get_sys_tmpdir () +{ + if (sys_tmpdir) + return sys_tmpdir; + +#ifdef P_tmpdir + sys_tmpdir = P_tmpdir; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; +#endif + + sys_tmpdir = "/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = "/var/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = "/usr/tmp"; + if (file_iswdir (sys_tmpdir)) + return sys_tmpdir; + + sys_tmpdir = DEFAULT_TMPDIR; + + return sys_tmpdir; +} + +static char * +get_tmpdir (flags) + int flags; +{ + char *tdir; + + tdir = (flags & MT_USETMPDIR) ? get_string_value ("TMPDIR") : (char *)NULL; + if (tdir && (file_iswdir (tdir) == 0 || strlen (tdir) > PATH_MAX)) + tdir = 0; + + if (tdir == 0) + tdir = get_sys_tmpdir (); + +#if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX) + if (tmpnamelen == -1) + tmpnamelen = pathconf (tdir, _PC_NAME_MAX); +#else + tmpnamelen = 0; +#endif + + return tdir; +} + +static void +sh_seedrand () +{ +#if HAVE_RANDOM + int d; + static int seeded = 0; + if (seeded == 0) + { + struct timeval tv; + + gettimeofday (&tv, NULL); + srandom (tv.tv_sec ^ tv.tv_usec ^ (getpid () << 16) ^ (uintptr_t)&d); + seeded = 1; + } +#endif +} + +char * +sh_mktmpname (nameroot, flags) + char *nameroot; + int flags; +{ + char *filename, *tdir, *lroot; + struct stat sb; + int r, tdlen; + static int seeded = 0; + + filename = (char *)xmalloc (PATH_MAX + 1); + tdir = get_tmpdir (flags); + tdlen = strlen (tdir); + + lroot = nameroot ? nameroot : DEFAULT_NAMEROOT; + if (nameroot == 0) + flags &= ~MT_TEMPLATE; + + if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX) + flags &= ~MT_TEMPLATE; + +#ifdef USE_MKTEMP + if (flags & MT_TEMPLATE) + strcpy (filename, nameroot); + else + sprintf (filename, "%s/%s.XXXXXX", tdir, lroot); + if (mktemp (filename) == 0) + { + free (filename); + filename = NULL; + } +#else /* !USE_MKTEMP */ + sh_seedrand (); + while (1) + { + filenum = (filenum << 1) ^ + (unsigned long) time ((time_t *)0) ^ + (unsigned long) dollar_dollar_pid ^ + (unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++); + sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum); + if (tmpnamelen > 0 && tmpnamelen < 32) + filename[tdlen + 1 + tmpnamelen] = '\0'; +# ifdef HAVE_LSTAT + r = lstat (filename, &sb); +# else + r = stat (filename, &sb); +# endif + if (r < 0 && errno == ENOENT) + break; + } +#endif /* !USE_MKTEMP */ + + return filename; +} + +int +sh_mktmpfd (nameroot, flags, namep) + char *nameroot; + int flags; + char **namep; +{ + char *filename, *tdir, *lroot; + int fd, tdlen; + + filename = (char *)xmalloc (PATH_MAX + 1); + tdir = get_tmpdir (flags); + tdlen = strlen (tdir); + + lroot = nameroot ? nameroot : DEFAULT_NAMEROOT; + if (nameroot == 0) + flags &= ~MT_TEMPLATE; + + if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX) + flags &= ~MT_TEMPLATE; + +#ifdef USE_MKSTEMP + if (flags & MT_TEMPLATE) + strcpy (filename, nameroot); + else + sprintf (filename, "%s/%s.XXXXXX", tdir, lroot); + fd = mkstemp (filename); + if (fd < 0 || namep == 0) + { + free (filename); + filename = NULL; + } + if (namep) + *namep = filename; + return fd; +#else /* !USE_MKSTEMP */ + sh_seedrand (); + do + { + filenum = (filenum << 1) ^ + (unsigned long) time ((time_t *)0) ^ + (unsigned long) dollar_dollar_pid ^ + (unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++); + sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum); + if (tmpnamelen > 0 && tmpnamelen < 32) + filename[tdlen + 1 + tmpnamelen] = '\0'; + fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600); + } + while (fd < 0 && errno == EEXIST); + + if (namep) + *namep = filename; + else + free (filename); + + return fd; +#endif /* !USE_MKSTEMP */ +} + +FILE * +sh_mktmpfp (nameroot, flags, namep) + char *nameroot; + int flags; + char **namep; +{ + int fd; + FILE *fp; + + fd = sh_mktmpfd (nameroot, flags, namep); + if (fd < 0) + return ((FILE *)NULL); + fp = fdopen (fd, (flags & MT_READWRITE) ? "w+" : "w"); + if (fp == 0) + close (fd); + return fp; +} + +char * +sh_mktmpdir (nameroot, flags) + char *nameroot; + int flags; +{ + char *filename, *tdir, *lroot, *dirname; + int fd, tdlen; + +#ifdef USE_MKDTEMP + filename = (char *)xmalloc (PATH_MAX + 1); + tdir = get_tmpdir (flags); + tdlen = strlen (tdir); + + lroot = nameroot ? nameroot : DEFAULT_NAMEROOT; + if (nameroot == 0) + flags &= ~MT_TEMPLATE; + + if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX) + flags &= ~MT_TEMPLATE; + + if (flags & MT_TEMPLATE) + strcpy (filename, nameroot); + else + sprintf (filename, "%s/%s.XXXXXX", tdir, lroot); + dirname = mkdtemp (filename); + if (dirname == 0) + { + free (filename); + filename = NULL; + } + return dirname; +#else /* !USE_MKDTEMP */ + filename = (char *)NULL; + do + { + filename = sh_mktmpname (nameroot, flags); + fd = mkdir (filename, 0700); + if (fd == 0) + break; + free (filename); + filename = (char *)NULL; + } + while (fd < 0 && errno == EEXIST); + + return (filename); +#endif /* !USE_MKDTEMP */ +} diff --git a/lib/sh/uconvert.c b/lib/sh/uconvert.c new file mode 100644 index 0000000..457552e --- /dev/null +++ b/lib/sh/uconvert.c @@ -0,0 +1,124 @@ +/* uconvert - convert string representations of decimal numbers into whole + number/fractional value pairs. */ + +/* Copyright (C) 2008,2009,2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" + +#include "posixtime.h" + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <stdio.h> +#include "chartypes.h" + +#include "shell.h" +#include "builtins.h" + +#define DECIMAL '.' /* XXX - should use locale */ + +#define RETURN(x) \ +do { \ + if (ip) *ip = ipart * mult; \ + if (up) *up = upart; \ + if (ep) *ep = p; \ + return (x); \ +} while (0) + +/* + * An incredibly simplistic floating point converter. + */ +static int multiplier[7] = { 1, 100000, 10000, 1000, 100, 10, 1 }; + +/* Take a decimal number int-part[.[micro-part]] and convert it to the whole + and fractional portions. The fractional portion is returned in + millionths (micro); callers are responsible for multiplying appropriately. + EP, if non-null, gets the address of the character where conversion stops. + Return 1 if value converted; 0 if invalid integer for either whole or + fractional parts. */ +int +uconvert(s, ip, up, ep) + char *s; + long *ip, *up; + char **ep; +{ + int n, mult; + long ipart, upart; + char *p; + + ipart = upart = 0; + mult = 1; + + if (s && (*s == '-' || *s == '+')) + { + mult = (*s == '-') ? -1 : 1; + p = s + 1; + } + else + p = s; + + for ( ; p && *p; p++) + { + if (*p == DECIMAL) /* decimal point */ + break; + if (DIGIT(*p) == 0) + RETURN(0); + ipart = (ipart * 10) + (*p - '0'); + } + + if (p == 0 || *p == 0) /* callers ensure p can never be 0; this is to shut up clang */ + RETURN(1); + + if (*p == DECIMAL) + p++; + + /* Look for up to six digits past a decimal point. */ + for (n = 0; n < 6 && p[n]; n++) + { + if (DIGIT(p[n]) == 0) + { + if (ep) + { + upart *= multiplier[n]; + p += n; /* To set EP */ + } + RETURN(0); + } + upart = (upart * 10) + (p[n] - '0'); + } + + /* Now convert to millionths */ + upart *= multiplier[n]; + + if (n == 6 && p[6] >= '5' && p[6] <= '9') + upart++; /* round up 1 */ + + if (ep) + { + p += n; + while (DIGIT(*p)) + p++; + } + + RETURN(1); +} diff --git a/lib/sh/ufuncs.c b/lib/sh/ufuncs.c new file mode 100644 index 0000000..4dc4853 --- /dev/null +++ b/lib/sh/ufuncs.c @@ -0,0 +1,140 @@ +/* ufuncs - sleep and alarm functions that understand fractional values */ + +/* Copyright (C) 2008,2009-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" + +#include "posixtime.h" + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if defined (HAVE_SELECT) +# include "posixselect.h" +# include "quit.h" +# include "trap.h" +# include "stat-time.h" +#endif + +/* A version of `alarm' using setitimer if it's available. */ + +#if defined (HAVE_SETITIMER) +unsigned int +falarm(secs, usecs) + unsigned int secs, usecs; +{ + struct itimerval it, oit; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + + it.it_value.tv_sec = secs; + it.it_value.tv_usec = usecs; + + if (setitimer(ITIMER_REAL, &it, &oit) < 0) + return (-1); /* XXX will be converted to unsigned */ + + /* Backwards compatibility with alarm(3) */ + if (oit.it_value.tv_usec) + oit.it_value.tv_sec++; + return (oit.it_value.tv_sec); +} +#else +int +falarm (secs, usecs) + unsigned int secs, usecs; +{ + if (secs == 0 && usecs == 0) + return (alarm (0)); + + if (secs == 0 || usecs >= 500000) + { + secs++; + usecs = 0; + } + return (alarm (secs)); +} +#endif /* !HAVE_SETITIMER */ + +/* A version of sleep using fractional seconds and select. I'd like to use + `usleep', but it's already taken */ + +#if defined (HAVE_TIMEVAL) && (defined (HAVE_SELECT) || defined (HAVE_PSELECT)) +int +fsleep(sec, usec) + unsigned int sec, usec; +{ + int e, r; + sigset_t blocked_sigs, prevmask; +#if defined (HAVE_PSELECT) + struct timespec ts; +#else + struct timeval tv; +#endif + + sigemptyset (&blocked_sigs); +# if defined (SIGCHLD) + sigaddset (&blocked_sigs, SIGCHLD); +# endif + +#if defined (HAVE_PSELECT) + ts.tv_sec = sec; + ts.tv_nsec = usec * 1000; +#else + sigemptyset (&prevmask); + tv.tv_sec = sec; + tv.tv_usec = usec; +#endif /* !HAVE_PSELECT */ + + do + { +#if defined (HAVE_PSELECT) + r = pselect(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &ts, &blocked_sigs); +#else + sigprocmask (SIG_SETMASK, &blocked_sigs, &prevmask); + r = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv); + sigprocmask (SIG_SETMASK, &prevmask, NULL); +#endif + e = errno; + if (r < 0 && errno == EINTR) + return -1; /* caller will handle */ + errno = e; + } + while (r < 0 && errno == EINTR); + + return r; +} +#else /* !HAVE_TIMEVAL || !HAVE_SELECT */ +int +fsleep(sec, usec) + long sec, usec; +{ + if (usec >= 500000) /* round */ + sec++; + return (sleep(sec)); +} +#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */ diff --git a/lib/sh/unicode.c b/lib/sh/unicode.c new file mode 100644 index 0000000..d781353 --- /dev/null +++ b/lib/sh/unicode.c @@ -0,0 +1,339 @@ +/* unicode.c - functions to convert unicode characters */ + +/* Copyright (C) 2010-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HANDLE_MULTIBYTE) + +#include <stdc.h> +#include <wchar.h> +#include <bashansi.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdio.h> +#include <limits.h> + +#if HAVE_ICONV +# include <iconv.h> +#endif + +#include <xmalloc.h> + +#ifndef USHORT_MAX +# ifdef USHRT_MAX +# define USHORT_MAX USHRT_MAX +# else +# define USHORT_MAX ((unsigned short) ~(unsigned short)0) +# endif +#endif + +#if !defined (STREQ) +# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0) +#endif /* !STREQ */ + +#if defined (HAVE_LOCALE_CHARSET) +extern const char *locale_charset PARAMS((void)); +#else +extern char *get_locale_var PARAMS((char *)); +#endif + +extern int locale_utf8locale; + +static int u32init = 0; +static int utf8locale = 0; +#if defined (HAVE_ICONV) +static iconv_t localconv; +#endif + +#ifndef HAVE_LOCALE_CHARSET +static char charsetbuf[40]; + +static char * +stub_charset () +{ + char *locale, *s, *t; + + locale = get_locale_var ("LC_CTYPE"); + if (locale == 0 || *locale == 0) + { + strcpy (charsetbuf, "ASCII"); + return charsetbuf; + } + s = strrchr (locale, '.'); + if (s) + { + strncpy (charsetbuf, s+1, sizeof (charsetbuf) - 1); + charsetbuf[sizeof (charsetbuf) - 1] = '\0'; + t = strchr (charsetbuf, '@'); + if (t) + *t = 0; + return charsetbuf; + } + strncpy (charsetbuf, locale, sizeof (charsetbuf) - 1); + charsetbuf[sizeof (charsetbuf) - 1] = '\0'; + return charsetbuf; +} +#endif + +void +u32reset () +{ +#if defined (HAVE_ICONV) + if (u32init && localconv != (iconv_t)-1) + { + iconv_close (localconv); + localconv = (iconv_t)-1; + } +#endif + u32init = 0; + utf8locale = 0; +} + +/* u32toascii ? */ +int +u32tochar (x, s) + unsigned long x; + char *s; +{ + int l; + + l = (x <= UCHAR_MAX) ? 1 : ((x <= USHORT_MAX) ? 2 : 4); + + if (x <= UCHAR_MAX) + s[0] = x & 0xFF; + else if (x <= USHORT_MAX) /* assume unsigned short = 16 bits */ + { + s[0] = (x >> 8) & 0xFF; + s[1] = x & 0xFF; + } + else + { + s[0] = (x >> 24) & 0xFF; + s[1] = (x >> 16) & 0xFF; + s[2] = (x >> 8) & 0xFF; + s[3] = x & 0xFF; + } + s[l] = '\0'; + return l; +} + +int +u32tocesc (wc, s) + u_bits32_t wc; + char *s; +{ + int l; + + if (wc < 0x10000) + l = sprintf (s, "\\u%04X", wc); + else + l = sprintf (s, "\\U%08X", wc); + return l; +} + +/* Convert unsigned 32-bit int to utf-8 character string */ +int +u32toutf8 (wc, s) + u_bits32_t wc; + char *s; +{ + int l; + + if (wc < 0x0080) + { + s[0] = (char)wc; + l = 1; + } + else if (wc < 0x0800) + { + s[0] = (wc >> 6) | 0xc0; + s[1] = (wc & 0x3f) | 0x80; + l = 2; + } + else if (wc < 0x10000) + { + /* Technically, we could return 0 here if 0xd800 <= wc <= 0x0dfff */ + s[0] = (wc >> 12) | 0xe0; + s[1] = ((wc >> 6) & 0x3f) | 0x80; + s[2] = (wc & 0x3f) | 0x80; + l = 3; + } + else if (wc < 0x200000) + { + s[0] = (wc >> 18) | 0xf0; + s[1] = ((wc >> 12) & 0x3f) | 0x80; + s[2] = ((wc >> 6) & 0x3f) | 0x80; + s[3] = (wc & 0x3f) | 0x80; + l = 4; + } + /* Strictly speaking, UTF-8 doesn't have characters longer than 4 bytes */ + else if (wc < 0x04000000) + { + s[0] = (wc >> 24) | 0xf8; + s[1] = ((wc >> 18) & 0x3f) | 0x80; + s[2] = ((wc >> 12) & 0x3f) | 0x80; + s[3] = ((wc >> 6) & 0x3f) | 0x80; + s[4] = (wc & 0x3f) | 0x80; + l = 5; + } + else if (wc < 0x080000000) + { + s[0] = (wc >> 30) | 0xfc; + s[1] = ((wc >> 24) & 0x3f) | 0x80; + s[2] = ((wc >> 18) & 0x3f) | 0x80; + s[3] = ((wc >> 12) & 0x3f) | 0x80; + s[4] = ((wc >> 6) & 0x3f) | 0x80; + s[5] = (wc & 0x3f) | 0x80; + l = 6; + } + else + l = 0; + + s[l] = '\0'; + return l; +} + +/* Convert a 32-bit unsigned int (unicode) to a UTF-16 string. Rarely used, + only if sizeof(wchar_t) == 2. */ +int +u32toutf16 (c, s) + u_bits32_t c; + wchar_t *s; +{ + int l; + + l = 0; + if (c < 0x0d800 || (c >= 0x0e000 && c <= 0x0ffff)) + { + s[0] = (wchar_t) (c & 0xFFFF); + l = 1; + } + else if (c >= 0x10000 && c <= 0x010ffff) + { + c -= 0x010000; + s[0] = (wchar_t)((c >> 10) + 0xd800); + s[1] = (wchar_t)((c & 0x3ff) + 0xdc00); + l = 2; + } + s[l] = 0; + return l; +} + +/* convert a single unicode-32 character into a multibyte string and put the + result in S, which must be large enough (at least max(10,MB_LEN_MAX) bytes) */ +int +u32cconv (c, s) + unsigned long c; + char *s; +{ + wchar_t wc; + wchar_t ws[3]; + int n; +#if HAVE_ICONV + const char *charset; + char obuf[25], *optr; + size_t obytesleft; + const char *iptr; + size_t sn; +#endif + +#if __STDC_ISO_10646__ + wc = c; + if (sizeof (wchar_t) == 4 && c <= 0x7fffffff) + n = wctomb (s, wc); + else if (sizeof (wchar_t) == 2 && c <= 0x10ffff && u32toutf16 (c, ws)) + n = wcstombs (s, ws, MB_LEN_MAX); + else + n = -1; + if (n != -1) + return n; +#endif + +#if HAVE_ICONV + /* this is mostly from coreutils-8.5/lib/unicodeio.c */ + if (u32init == 0) + { + utf8locale = locale_utf8locale; + localconv = (iconv_t)-1; + if (utf8locale == 0) + { +#if HAVE_LOCALE_CHARSET + charset = locale_charset (); +#elif HAVE_NL_LANGINFO + charset = nl_langinfo (CODESET); +#else + charset = stub_charset (); +#endif + localconv = iconv_open (charset, "UTF-8"); + if (localconv == (iconv_t)-1) + /* We assume ASCII when presented with an unknown encoding. */ + localconv = iconv_open ("ASCII", "UTF-8"); + } + u32init = 1; + } + + /* NL_LANGINFO and locale_charset used when setting locale_utf8locale */ + + /* If we have a UTF-8 locale, convert to UTF-8 and return converted value. */ + n = u32toutf8 (c, s); + if (utf8locale) + return n; + + /* If the conversion is not supported, even the ASCII requested above, we + bail now. Currently we return the UTF-8 conversion. We could return + u32tocesc(). */ + if (localconv == (iconv_t)-1) + return n; + + optr = obuf; + obytesleft = sizeof (obuf); + iptr = s; + sn = n; + + iconv (localconv, NULL, NULL, NULL, NULL); + + if (iconv (localconv, (ICONV_CONST char **)&iptr, &sn, &optr, &obytesleft) == (size_t)-1) + { + /* You get ISO C99 escape sequences if iconv fails */ + n = u32tocesc (c, s); + return n; + } + + *optr = '\0'; + + /* number of chars to be copied is optr - obuf if we want to do bounds + checking */ + strcpy (s, obuf); + return (optr - obuf); +#endif /* HAVE_ICONV */ + + if (locale_utf8locale) + n = u32toutf8 (c, s); + else + n = u32tocesc (c, s); /* fallback is ISO C99 escape sequences */ + return n; +} +#else +void +u32reset () +{ +} +#endif /* HANDLE_MULTIBYTE */ diff --git a/lib/sh/utf8.c b/lib/sh/utf8.c new file mode 100644 index 0000000..fed2522 --- /dev/null +++ b/lib/sh/utf8.c @@ -0,0 +1,196 @@ +/* utf8.c - UTF-8 character handling functions */ + +/* Copyright (C) 2018 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include "bashansi.h" +#include "shmbutil.h" + +extern int locale_mb_cur_max; +extern int locale_utf8locale; + +#if defined (HANDLE_MULTIBYTE) + +char * +utf8_mbschr (s, c) + const char *s; + int c; +{ + return strchr (s, c); /* for now */ +} + +int +utf8_mbscmp (s1, s2) + const char *s1, *s2; +{ + /* Use the fact that the UTF-8 encoding preserves lexicographic order. */ + return strcmp (s1, s2); +} + +char * +utf8_mbsmbchar (str) + const char *str; +{ + register char *s; + + for (s = (char *)str; *s; s++) + if ((*s & 0xc0) == 0x80) + return s; + return (0); +} + +int +utf8_mbsnlen(src, srclen, maxlen) + const char *src; + size_t srclen; + int maxlen; +{ + register int sind, count; + + for (sind = count = 0; src[sind] && sind <= maxlen; sind++) + { + if ((src[sind] & 0xc0) != 0x80) + count++; + } + return (count); +} + +/* Adapted from GNU gnulib. Handles UTF-8 characters up to 4 bytes long */ +int +utf8_mblen (s, n) + const char *s; + size_t n; +{ + unsigned char c, c1, c2, c3; + + if (s == 0) + return (0); /* no shift states */ + if (n <= 0) + return (-1); + + c = (unsigned char)*s; + if (c < 0x80) + return (c != 0); + if (c >= 0xc2) + { + c1 = (unsigned char)s[1]; + if (c < 0xe0) + { + if (n == 1) + return -2; + + /* + * c c1 + * + * U+0080..U+07FF C2..DF 80..BF + */ + + if (n >= 2 && (c1 ^ 0x80) < 0x40) /* 0x80..0xbf */ + return 2; + } + else if (c < 0xf0) + { + if (n == 1) + return -2; + + /* + * c c1 c2 + * + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+CFFF E1..EC 80..BF 80..BF + * U+D000..U+D7FF ED 80..9F 80..BF + * U+E000..U+FFFF EE..EF 80..BF 80..BF + */ + + if ((c1 ^ 0x80) < 0x40 + && (c >= 0xe1 || c1 >= 0xa0) + && (c != 0xed || c1 < 0xa0)) + { + if (n == 2) + return -2; /* incomplete */ + + c2 = (unsigned char)s[2]; + if ((c2 ^ 0x80) < 0x40) + return 3; + } + } + else if (c <= 0xf4) + { + if (n == 1) + return -2; + + /* + * c c1 c2 c3 + * + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + */ + if (((c1 ^ 0x80) < 0x40) + && (c >= 0xf1 || c1 >= 0x90) + && (c < 0xf4 || (c == 0xf4 && c1 < 0x90))) + { + if (n == 2) + return -2; /* incomplete */ + + c2 = (unsigned char)s[2]; + if ((c2 ^ 0x80) < 0x40) + { + if (n == 3) + return -2; + + c3 = (unsigned char)s[3]; + if ((c3 ^ 0x80) < 0x40) + return 4; + } + } + } + } + /* invalid or incomplete multibyte character */ + return -1; +} + +/* We can optimize this if we know the locale is UTF-8, but needs to handle + malformed byte sequences. */ +size_t +utf8_mbstrlen(s) + const char *s; +{ + size_t clen, nc; + int mb_cur_max; + + nc = 0; + mb_cur_max = MB_CUR_MAX; + while (*s && (clen = (size_t)utf8_mblen(s, mb_cur_max)) != 0) + { + if (MB_INVALIDCH(clen)) + clen = 1; /* assume single byte */ + + s += clen; + nc++; + } + return nc; +} + +#endif diff --git a/lib/sh/vprint.c b/lib/sh/vprint.c new file mode 100644 index 0000000..567fba3 --- /dev/null +++ b/lib/sh/vprint.c @@ -0,0 +1,85 @@ +/* vprint.c -- v[fs]printf() for 4.[23] BSD systems. */ + +/* Copyright (C) 1987,1989 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (USE_VFPRINTF_EMULATION) + +#include <stdio.h> + +#if !defined (NULL) +# if defined (__STDC__) +# define NULL ((void *)0) +# else +# define NULL 0x0 +# endif /* __STDC__ */ +#endif /* !NULL */ + +/* + * Beware! Don't trust the value returned by either of these functions; it + * seems that pre-4.3-tahoe implementations of _doprnt () return the first + * argument, i.e. a char *. + */ +#include <varargs.h> + +int +vfprintf (iop, fmt, ap) + FILE *iop; + char *fmt; + va_list ap; +{ + int len; + char localbuf[BUFSIZ]; + + if (iop->_flag & _IONBF) + { + iop->_flag &= ~_IONBF; + iop->_ptr = iop->_base = localbuf; + len = _doprnt (fmt, ap, iop); + (void) fflush (iop); + iop->_flag |= _IONBF; + iop->_base = NULL; + iop->_bufsiz = 0; + iop->_cnt = 0; + } + else + len = _doprnt (fmt, ap, iop); + return (ferror (iop) ? EOF : len); +} + +/* + * Ditto for vsprintf + */ +int +vsprintf (str, fmt, ap) + char *str, *fmt; + va_list ap; +{ + FILE f; + int len; + + f._flag = _IOWRT|_IOSTRG; + f._ptr = str; + f._cnt = 32767; + len = _doprnt (fmt, ap, &f); + *f._ptr = 0; + return (len); +} +#endif /* USE_VFPRINTF_EMULATION */ diff --git a/lib/sh/wcsdup.c b/lib/sh/wcsdup.c new file mode 100644 index 0000000..62a3c86 --- /dev/null +++ b/lib/sh/wcsdup.c @@ -0,0 +1,44 @@ +/* wcsdup.c - duplicate wide character string */ + +/* Copyright (C) 2006 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE) + +#include <stdc.h> +#include <wchar.h> +#include <bashansi.h> +#include <xmalloc.h> + +wchar_t * +wcsdup (ws) + const wchar_t *ws; +{ + wchar_t *ret; + size_t len; + + len = wcslen (ws); + ret = xmalloc ((len + 1) * sizeof (wchar_t)); + if (ret == 0) + return ret; + + return (wcscpy (ret, ws)); +} +#endif /* !HAVE_WCSDUP && HANDLE_MULTIBYTE */ diff --git a/lib/sh/wcsnwidth.c b/lib/sh/wcsnwidth.c new file mode 100644 index 0000000..9c7e7cc --- /dev/null +++ b/lib/sh/wcsnwidth.c @@ -0,0 +1,56 @@ +/* wcsnwidth.c - compute display width of wide character string, up to max + specified width, return length. */ + +/* Copyright (C) 2012 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HANDLE_MULTIBYTE) + +#include <stdc.h> +#include <wchar.h> +#include <bashansi.h> + +/* Return the number of wide characters that will be displayed from wide string + PWCS. If the display width exceeds MAX, return the number of wide chars + from PWCS required to display MAX characters on the screen. */ +int +wcsnwidth(pwcs, n, max) + const wchar_t *pwcs; + size_t n, max; +{ + wchar_t wc, *ws; + int len, l; + + len = 0; + ws = (wchar_t *)pwcs; + while (n-- > 0 && (wc = *ws++) != L'\0') + { + l = wcwidth (wc); + if (l < 0) + return (-1); + else if (l == max - len) + return (ws - pwcs); + else if (l > max - len) + return (--ws - pwcs); + len += l; + } + return (ws - pwcs); +} +#endif diff --git a/lib/sh/wcswidth.c b/lib/sh/wcswidth.c new file mode 100644 index 0000000..1a30d9f --- /dev/null +++ b/lib/sh/wcswidth.c @@ -0,0 +1,46 @@ +/* wcswidth.c - compute display width of wide character string */ + +/* Copyright (C) 2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HANDLE_MULTIBYTE) && !defined (HAVE_WCSWIDTH) + +#include <stdc.h> +#include <wchar.h> +#include <bashansi.h> + +int +wcswidth(pwcs, n) + const wchar_t *pwcs; + size_t n; +{ + wchar_t wc; + int len, l; + + len = 0; + while (n-- > 0 && (wc = *pwcs++) != L'\0') + { + if ((l = wcwidth(wc)) < 0) + return (-1); + len += l; + } + return (len); +} +#endif diff --git a/lib/sh/winsize.c b/lib/sh/winsize.c new file mode 100644 index 0000000..846fcd5 --- /dev/null +++ b/lib/sh/winsize.c @@ -0,0 +1,104 @@ +/* winsize.c - handle window size changes and information. */ + +/* Copyright (C) 2005-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <stdc.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <sys/ioctl.h> + +/* Try to find the definitions of `struct winsize' and TIOGCWINSZ */ + +#if 0 +#if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ) +# include <sys/ioctl.h> +#endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */ +#endif + +#if defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL) +# include <termios.h> +#endif /* STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */ + +/* Not in either of the standard places, look around. */ +#if !defined (STRUCT_WINSIZE_IN_TERMIOS) && !defined (STRUCT_WINSIZE_IN_SYS_IOCTL) +# if defined (HAVE_SYS_STREAM_H) +# include <sys/stream.h> +# endif /* HAVE_SYS_STREAM_H */ +# if defined (HAVE_SYS_PTEM_H) /* SVR4.2, at least, has it here */ +# include <sys/ptem.h> +# define _IO_PTEM_H /* work around SVR4.2 1.1.4 bug */ +# endif /* HAVE_SYS_PTEM_H */ +# if defined (HAVE_SYS_PTE_H) /* ??? */ +# include <sys/pte.h> +# endif /* HAVE_SYS_PTE_H */ +#endif /* !STRUCT_WINSIZE_IN_TERMIOS && !STRUCT_WINSIZE_IN_SYS_IOCTL */ + +#include <stdio.h> + +/* Return the fd from which we are actually getting input. */ +#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr) + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern int shell_tty; + +#if defined (READLINE) +/* Let's not call readline, forcing readline to initialize the termcap/terminfo + variables it needs, unless we have to. */ +extern int interactive_shell; +extern int no_line_editing; +extern int bash_readline_initialized; +extern void rl_set_screen_size PARAMS((int, int)); +#endif +extern void sh_set_lines_and_columns PARAMS((int, int)); + +void +get_new_window_size (from_sig, rp, cp) + int from_sig; + int *rp, *cp; +{ +#if defined (TIOCGWINSZ) + struct winsize win; + int tty; + + tty = input_tty (); + if (tty >= 0 && (ioctl (tty, TIOCGWINSZ, &win) == 0) && + win.ws_row > 0 && win.ws_col > 0) + { + sh_set_lines_and_columns (win.ws_row, win.ws_col); +#if defined (READLINE) + if ((interactive_shell && no_line_editing == 0) || bash_readline_initialized) + rl_set_screen_size (win.ws_row, win.ws_col); +#endif + if (rp) + *rp = win.ws_row; + if (cp) + *cp = win.ws_col; + } +#endif +} diff --git a/lib/sh/zcatfd.c b/lib/sh/zcatfd.c new file mode 100644 index 0000000..aa8199f --- /dev/null +++ b/lib/sh/zcatfd.c @@ -0,0 +1,74 @@ +/* zcatfd - copy contents of file descriptor to another */ + +/* Copyright (C) 2002-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <errno.h> + +#include <stdc.h> + +#if !defined (errno) +extern int errno; +#endif + +#ifndef ZBUFSIZ +# define ZBUFSIZ 4096 +#endif + +extern ssize_t zread PARAMS((int, char *, size_t)); +extern int zwrite PARAMS((int, char *, ssize_t)); + +/* Dump contents of file descriptor FD to OFD. FN is the filename for + error messages (not used right now). */ +int +zcatfd (fd, ofd, fn) + int fd, ofd; + char *fn; +{ + ssize_t nr; + int rval; + char lbuf[ZBUFSIZ]; + + rval = 0; + while (1) + { + nr = zread (fd, lbuf, sizeof (lbuf)); + if (nr == 0) + break; + else if (nr < 0) + { + rval = -1; + break; + } + else if (zwrite (ofd, lbuf, nr) < 0) + { + rval = -1; + break; + } + } + + return rval; +} diff --git a/lib/sh/zgetline.c b/lib/sh/zgetline.c new file mode 100644 index 0000000..5e1ef72 --- /dev/null +++ b/lib/sh/zgetline.c @@ -0,0 +1,126 @@ +/* zgetline - read a line of input from a specified file descriptor and return + a pointer to a newly-allocated buffer containing the data. */ + +/* Copyright (C) 2008-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <errno.h> +#include "xmalloc.h" + +#if !defined (errno) +extern int errno; +#endif + +extern ssize_t zread PARAMS((int, char *, size_t)); +extern ssize_t zreadc PARAMS((int, char *)); +extern ssize_t zreadintr PARAMS((int, char *, size_t)); +extern ssize_t zreadcintr PARAMS((int, char *)); + +typedef ssize_t breadfunc_t PARAMS((int, char *, size_t)); +typedef ssize_t creadfunc_t PARAMS((int, char *)); + +/* Initial memory allocation for automatic growing buffer in zreadlinec */ +#define GET_LINE_INITIAL_ALLOCATION 16 + +/* Derived from GNU libc's getline. + The behavior is almost the same as getline. See man getline. + The differences are + (1) using file descriptor instead of FILE *; + (2) the order of arguments: the file descriptor comes first; + (3) the addition of a fourth argument, DELIM; sets the delimiter to + be something other than newline if desired. If setting DELIM, + the next argument should be 1; and + (4) the addition of a fifth argument, UNBUFFERED_READ; this argument + controls whether get_line uses buffering or not to get a byte data + from FD. get_line uses zreadc if UNBUFFERED_READ is zero; and + uses zread if UNBUFFERED_READ is non-zero. + + Returns number of bytes read or -1 on error. */ + +ssize_t +zgetline (fd, lineptr, n, delim, unbuffered_read) + int fd; + char **lineptr; + size_t *n; + int delim; + int unbuffered_read; +{ + int retval; + size_t nr; + char *line, c; + + if (lineptr == 0 || n == 0 || (*lineptr == 0 && *n != 0)) + return -1; + + nr = 0; + line = *lineptr; + + while (1) + { + retval = unbuffered_read ? zread (fd, &c, 1) : zreadc(fd, &c); + + if (retval <= 0) + { + if (line && nr > 0) + line[nr] = '\0'; + break; + } + + if (nr + 2 >= *n) + { + size_t new_size; + + new_size = (*n == 0) ? GET_LINE_INITIAL_ALLOCATION : *n * 2; + line = (*n >= new_size) ? NULL : xrealloc (*lineptr, new_size); + + if (line) + { + *lineptr = line; + *n = new_size; + } + else + { + if (*n > 0) + { + (*lineptr)[*n - 1] = '\0'; + nr = *n - 2; + } + break; + } + } + + line[nr] = c; + nr++; + + if (c == delim) + { + line[nr] = '\0'; + break; + } + } + + return nr - 1; +} diff --git a/lib/sh/zmapfd.c b/lib/sh/zmapfd.c new file mode 100644 index 0000000..9ff50a4 --- /dev/null +++ b/lib/sh/zmapfd.c @@ -0,0 +1,93 @@ +/* zmapfd - read contents of file descriptor into a newly-allocated buffer */ + +/* Copyright (C) 2006-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <errno.h> + +#include "bashansi.h" +#include "command.h" +#include "general.h" + +#if !defined (errno) +extern int errno; +#endif + +#ifndef ZBUFSIZ +# define ZBUFSIZ 4096 +#endif + +extern ssize_t zread PARAMS((int, char *, size_t)); + +/* Dump contents of file descriptor FD to *OSTR. FN is the filename for + error messages (not used right now). */ +int +zmapfd (fd, ostr, fn) + int fd; + char **ostr; + char *fn; +{ + ssize_t nr; + int rval; + char lbuf[ZBUFSIZ]; + char *result; + size_t rsize, rind; + + rval = 0; + result = (char *)xmalloc (rsize = ZBUFSIZ); + rind = 0; + + while (1) + { + nr = zread (fd, lbuf, sizeof (lbuf)); + if (nr == 0) + { + rval = rind; + break; + } + else if (nr < 0) + { + free (result); + if (ostr) + *ostr = (char *)NULL; + return -1; + } + + RESIZE_MALLOCED_BUFFER (result, rind, nr, rsize, ZBUFSIZ); + memcpy (result+rind, lbuf, nr); + rind += nr; + } + + RESIZE_MALLOCED_BUFFER (result, rind, 1, rsize, 128); + result[rind] = '\0'; + + if (ostr) + *ostr = result; + else + free (result); + + return rval; +} diff --git a/lib/sh/zread.c b/lib/sh/zread.c new file mode 100644 index 0000000..dafb7f6 --- /dev/null +++ b/lib/sh/zread.c @@ -0,0 +1,228 @@ +/* zread - read data from file descriptor into buffer with retries */ + +/* Copyright (C) 1999-2020 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <signal.h> +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif + +#ifndef SEEK_CUR +# define SEEK_CUR 1 +#endif + +#ifndef ZBUFSIZ +# define ZBUFSIZ 4096 +#endif + +extern int executing_builtin; + +extern void check_signals_and_traps (void); +extern void check_signals (void); +extern int signal_is_trapped (int); +extern int read_builtin_timeout (int); + +/* Read LEN bytes from FD into BUF. Retry the read on EINTR. Any other + error causes the loop to break. */ +ssize_t +zread (fd, buf, len) + int fd; + char *buf; + size_t len; +{ + ssize_t r; + + check_signals (); /* check for signals before a blocking read */ + /* should generalize into a mechanism where different parts of the shell can + `register' timeouts and have them checked here. */ + while (((r = read_builtin_timeout (fd)) < 0 || (r = read (fd, buf, len)) < 0) && + errno == EINTR) + { + int t; + t = errno; + /* XXX - bash-5.0 */ + /* We check executing_builtin and run traps here for backwards compatibility */ + if (executing_builtin) + check_signals_and_traps (); /* XXX - should it be check_signals()? */ + else + check_signals (); + errno = t; + } + + return r; +} + +/* Read LEN bytes from FD into BUF. Retry the read on EINTR, up to three + interrupts. Any other error causes the loop to break. */ + +#ifdef NUM_INTR +# undef NUM_INTR +#endif +#define NUM_INTR 3 + +ssize_t +zreadretry (fd, buf, len) + int fd; + char *buf; + size_t len; +{ + ssize_t r; + int nintr; + + for (nintr = 0; ; ) + { + r = read (fd, buf, len); + if (r >= 0) + return r; + if (r == -1 && errno == EINTR) + { + if (++nintr >= NUM_INTR) + return -1; + continue; + } + return r; + } +} + +/* Call read(2) and allow it to be interrupted. Just a stub for now. */ +ssize_t +zreadintr (fd, buf, len) + int fd; + char *buf; + size_t len; +{ + check_signals (); + return (read (fd, buf, len)); +} + +/* Read one character from FD and return it in CP. Return values are as + in read(2). This does some local buffering to avoid many one-character + calls to read(2), like those the `read' builtin performs. */ + +static char lbuf[ZBUFSIZ]; +static size_t lind, lused; + +ssize_t +zreadc (fd, cp) + int fd; + char *cp; +{ + ssize_t nr; + + if (lind == lused || lused == 0) + { + nr = zread (fd, lbuf, sizeof (lbuf)); + lind = 0; + if (nr <= 0) + { + lused = 0; + return nr; + } + lused = nr; + } + if (cp) + *cp = lbuf[lind++]; + return 1; +} + +/* Don't mix calls to zreadc and zreadcintr in the same function, since they + use the same local buffer. */ +ssize_t +zreadcintr (fd, cp) + int fd; + char *cp; +{ + ssize_t nr; + + if (lind == lused || lused == 0) + { + nr = zreadintr (fd, lbuf, sizeof (lbuf)); + lind = 0; + if (nr <= 0) + { + lused = 0; + return nr; + } + lused = nr; + } + if (cp) + *cp = lbuf[lind++]; + return 1; +} + +/* Like zreadc, but read a specified number of characters at a time. Used + for `read -N'. */ +ssize_t +zreadn (fd, cp, len) + int fd; + char *cp; + size_t len; +{ + ssize_t nr; + + if (lind == lused || lused == 0) + { + if (len > sizeof (lbuf)) + len = sizeof (lbuf); + nr = zread (fd, lbuf, len); + lind = 0; + if (nr <= 0) + { + lused = 0; + return nr; + } + lused = nr; + } + if (cp) + *cp = lbuf[lind++]; + return 1; +} + +void +zreset () +{ + lind = lused = 0; +} + +/* Sync the seek pointer for FD so that the kernel's idea of the last char + read is the last char returned by zreadc. */ +void +zsyncfd (fd) + int fd; +{ + off_t off, r; + + off = lused - lind; + r = 0; + if (off > 0) + r = lseek (fd, -off, SEEK_CUR); + + if (r != -1) + lused = lind = 0; +} diff --git a/lib/sh/zwrite.c b/lib/sh/zwrite.c new file mode 100644 index 0000000..3240f4f --- /dev/null +++ b/lib/sh/zwrite.c @@ -0,0 +1,64 @@ +/* zwrite - write contents of buffer to file descriptor, retrying on error */ + +/* Copyright (C) 1999-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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif + +/* Write NB bytes from BUF to file descriptor FD, retrying the write if + it is interrupted. We retry three times if we get a zero-length + write. Any other signal causes this function to return prematurely. */ +int +zwrite (fd, buf, nb) + int fd; + char *buf; + size_t nb; +{ + int n, i, nt; + + for (n = nb, nt = 0;;) + { + i = write (fd, buf, n); + if (i > 0) + { + n -= i; + if (n <= 0) + return nb; + buf += i; + } + else if (i == 0) + { + if (++nt > 3) + return (nb - n); + } + else if (errno != EINTR) + return -1; + } +} diff --git a/lib/termcap/Makefile.in b/lib/termcap/Makefile.in new file mode 100644 index 0000000..66dd7c1 --- /dev/null +++ b/lib/termcap/Makefile.in @@ -0,0 +1,90 @@ +## -*- text -*- #################################################### +# # +# Makefile for termcap replacement libbrary. # +# # +#################################################################### + +# Copyright (C) 1996-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +libdir = @libdir@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ + +DEFS = @DEFS@ + +INCLUDES = -I. -I../.. -I$(topdir) -I$(topdir)/lib -I$(srcdir) + +CCFLAGS = $(CFLAGS) $(DEFS) $(CPPFLAGS) ${INCLUDES} + +# 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. +.c.o: + $(CC) -c $(CCFLAGS) $< + +SOURCES = termcap.c tparam.c +OBJECTS = termcap.o tparam.o + +DOCUMENTATION = termcap.texinfo + +THINGS_TO_TAR = $(SOURCES) $(DOCUMENTATION) + +########################################################################## + +all: libtermcap.a + +libtermcap.a: $(OBJECTS) + $(RM) -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +install: + +clean: + $(RM) *.o *.a *.log *.cp *.tp *.vr *.fn *.aux *.pg *.toc + +mostlyclean: clean + +distclean maintainer-clean: clean + $(RM) Makefile + +$(DESTDIR)$(libdir)/libtermcap.a: libtermcap.a + ${INSTALL_DATA} -c -m 644 libtermcap.a $@ + -test -n "$(RANLIB)" && $(RANLIB) -t $@ + +termcap.o: $(BUILD_DIR)/config.h +tparam.o: $(BUILD_DIR)/config.h +version.o: $(BUILD_DIR)/config.h diff --git a/lib/termcap/ltcap.h b/lib/termcap/ltcap.h new file mode 100644 index 0000000..a97f0d8 --- /dev/null +++ b/lib/termcap/ltcap.h @@ -0,0 +1,30 @@ +/* ltcap.h - Local declarations for termcap library. */ + +/* Copyright (C) 1999-2009 Free Software Foundation, Inc. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LTCAP_H_ +#define _LTCAP_H_ 1 + +#if !defined (__APPLE__) +# define __private_extern__ +#endif + +#ifndef MAX_TGETENT_BUFSIZ +# define MAX_TGETENT_BUFSIZ 2048 +#endif + +#endif /* _LTCAP_H_ */ diff --git a/lib/termcap/termcap.c b/lib/termcap/termcap.c new file mode 100644 index 0000000..87fae05 --- /dev/null +++ b/lib/termcap/termcap.c @@ -0,0 +1,820 @@ +/* termcap.c - Work-alike for termcap, plus extra features. */ + +/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Emacs config.h may rename various library functions such as malloc. */ +#ifdef HAVE_CONFIG_H + +#include <config.h> + +/* Get the O_* definitions for open et al. */ +#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif + +#include <fcntl.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +extern char *getenv (); +extern char *malloc (); +extern char *realloc (); +#endif + +#if defined (HAVE_STRING_H) +#include <string.h> +#endif + +#if !defined (HAVE_BCOPY) && (defined (HAVE_STRING_H) || defined (STDC_HEADERS)) +# define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#else /* not HAVE_CONFIG_H */ + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#else +char *getenv (); +char *malloc (); +char *realloc (); +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +/* Do this after the include, in case string.h prototypes bcopy. */ +#if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy) +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef _POSIX_VERSION +#include <fcntl.h> +#endif + +#endif /* not HAVE_CONFIG_H */ + +#ifndef NULL +#define NULL (char *) 0 +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +/* BUFSIZE is the initial size allocated for the buffer + for reading the termcap file. + It is not a limit. + Make it large normally for speed. + Make it variable when debugging, so can exercise + increasing the space dynamically. */ + +#ifndef BUFSIZE +#ifdef DEBUG +#define BUFSIZE bufsize + +int bufsize = 128; +#else +#define BUFSIZE 2048 +#endif +#endif + +#include "ltcap.h" + +#ifndef TERMCAP_FILE +#define TERMCAP_FILE "/etc/termcap" +#endif + +#ifndef emacs +static void +memory_out () +{ + write (2, "virtual memory exhausted\n", 25); + exit (1); +} + +static char * +xmalloc (size) + unsigned size; +{ + register char *tem = malloc (size); + + if (!tem) + memory_out (); + return tem; +} + +static char * +xrealloc (ptr, size) + char *ptr; + unsigned size; +{ + register char *tem = realloc (ptr, size); + + if (!tem) + memory_out (); + return tem; +} +#endif /* not emacs */ + +/* Looking up capabilities in the entry already found. */ + +/* The pointer to the data made by tgetent is left here + for tgetnum, tgetflag and tgetstr to find. */ +static char *term_entry; + +static char *tgetst1 (); + +/* Search entry BP for capability CAP. + Return a pointer to the capability (in BP) if found, + 0 if not found. */ + +static char * +find_capability (bp, cap) + register char *bp, *cap; +{ + for (; *bp; bp++) + if (bp[0] == ':' + && bp[1] == cap[0] + && bp[2] == cap[1]) + return &bp[4]; + return NULL; +} + +__private_extern__ +int +tgetnum (cap) + char *cap; +{ + register char *ptr = find_capability (term_entry, cap); + if (!ptr || ptr[-1] != '#') + return -1; + return atoi (ptr); +} + +__private_extern__ +int +tgetflag (cap) + char *cap; +{ + register char *ptr = find_capability (term_entry, cap); + return ptr && ptr[-1] == ':'; +} + +/* Look up a string-valued capability CAP. + If AREA is non-null, it points to a pointer to a block in which + to store the string. That pointer is advanced over the space used. + If AREA is null, space is allocated with `malloc'. */ + +__private_extern__ +char * +tgetstr (cap, area) + char *cap; + char **area; +{ + register char *ptr = find_capability (term_entry, cap); + if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~')) + return NULL; + return tgetst1 (ptr, area); +} + +/* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted, + gives meaning of character following \, or a space if no special meaning. + Eight characters per line within the string. */ + +static char esctab[] + = " \007\010 \033\014 \ + \012 \ + \015 \011 \013 \ + "; + +/* PTR points to a string value inside a termcap entry. + Copy that value, processing \ and ^ abbreviations, + into the block that *AREA points to, + or to newly allocated storage if AREA is NULL. + Return the address to which we copied the value, + or NULL if PTR is NULL. */ + +static char * +tgetst1 (ptr, area) + char *ptr; + char **area; +{ + register char *p, *r; + register int c; + register int size; + char *ret; + register int c1; + + if (!ptr) + return NULL; + + /* `ret' gets address of where to store the string. */ + if (!area) + { + /* Compute size of block needed (may overestimate). */ + p = ptr; + while ((c = *p++) && c != ':' && c != '\n') + ; + ret = (char *) xmalloc (p - ptr + 1); + } + else + ret = *area; + + /* Copy the string value, stopping at null or colon. + Also process ^ and \ abbreviations. */ + p = ptr; + r = ret; + while ((c = *p++) && c != ':' && c != '\n') + { + if (c == '^') + { + c = *p++; + if (c == '?') + c = 0177; + else + c &= 037; + } + else if (c == '\\') + { + c = *p++; + if (c >= '0' && c <= '7') + { + c -= '0'; + size = 0; + + while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7') + { + c *= 8; + c += c1 - '0'; + p++; + } + } + else if (c >= 0100 && c < 0200) + { + c1 = esctab[(c & ~040) - 0100]; + if (c1 != ' ') + c = c1; + } + } + *r++ = c; + } + *r = '\0'; + /* Update *AREA. */ + if (area) + *area = r + 1; + return ret; +} + +/* Outputting a string with padding. */ + +short ospeed; +/* If OSPEED is 0, we use this as the actual baud rate. */ +int tputs_baud_rate; +__private_extern__ char PC = '\0'; + +/* Actual baud rate if positive; + - baud rate / 100 if negative. */ + +static int speeds[] = + { +#ifdef VMS + 0, 50, 75, 110, 134, 150, -3, -6, -12, -18, + -20, -24, -36, -48, -72, -96, -192 +#else /* not VMS */ + 0, 50, 75, 110, 135, 150, -2, -3, -6, -12, + -18, -24, -48, -96, -192, -288, -384, -576, -1152 +#endif /* not VMS */ + }; + +__private_extern__ +int +tputs (str, nlines, outfun) + register char *str; + int nlines; + register int (*outfun) (); +{ + register int padcount = 0; + register int speed; + +#ifdef emacs + extern baud_rate; + speed = baud_rate; + /* For quite high speeds, convert to the smaller + units to avoid overflow. */ + if (speed > 10000) + speed = - speed / 100; +#else + if (ospeed == 0) + speed = tputs_baud_rate; + else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0])) + speed = speeds[ospeed]; + else + speed = 0; +#endif + + if (!str) + return -1; + + while (*str >= '0' && *str <= '9') + { + padcount += *str++ - '0'; + padcount *= 10; + } + if (*str == '.') + { + str++; + padcount += *str++ - '0'; + } + if (*str == '*') + { + str++; + padcount *= nlines; + } + while (*str) + (*outfun) (*str++); + + /* PADCOUNT is now in units of tenths of msec. + SPEED is measured in characters per 10 seconds + or in characters per .1 seconds (if negative). + We use the smaller units for larger speeds to avoid overflow. */ + padcount *= speed; + padcount += 500; + padcount /= 1000; + if (speed < 0) + padcount = -padcount; + else + { + padcount += 50; + padcount /= 100; + } + + while (padcount-- > 0) + (*outfun) (PC); + + return 0; +} + +/* Finding the termcap entry in the termcap data base. */ + +struct buffer + { + char *beg; + int size; + char *ptr; + int ateof; + int full; + }; + +/* Forward declarations of static functions. */ + +static int scan_file (); +static char *gobble_line (); +static int compare_contin (); +static int name_match (); + +#ifdef VMS + +#include <rmsdef.h> +#include <fab.h> +#include <nam.h> + +static int +valid_filename_p (fn) + char *fn; +{ + struct FAB fab = cc$rms_fab; + struct NAM nam = cc$rms_nam; + char esa[NAM$C_MAXRSS]; + + fab.fab$l_fna = fn; + fab.fab$b_fns = strlen(fn); + fab.fab$l_nam = &nam; + fab.fab$l_fop = FAB$M_NAM; + + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + + return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL; +} + +#else /* !VMS */ + +#ifdef MSDOS /* MW, May 1993 */ +static int +valid_filename_p (fn) + char *fn; +{ + return *fn == '\\' || *fn == '/' || + (*fn >= 'A' && *fn <= 'z' && fn[1] == ':'); +} +#else +#define valid_filename_p(fn) (*(fn) == '/') +#endif + +#endif /* !VMS */ + +/* Find the termcap entry data for terminal type NAME + and store it in the block that BP points to. + Record its address for future use. + + If BP is null, space is dynamically allocated. + + Return -1 if there is some difficulty accessing the data base + of terminal types, + 0 if the data base is accessible but the type NAME is not defined + in it, and some other value otherwise. */ + +__private_extern__ +int +tgetent (bp, name) + char *bp, *name; +{ + register char *termcap_name; + register int fd; + struct buffer buf; + register char *bp1; + char *bp2; + char *term; + int malloc_size = 0; + register int c; + char *tcenv; /* TERMCAP value, if it contains :tc=. */ + char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ + int filep; + +#ifdef INTERNAL_TERMINAL + /* For the internal terminal we don't want to read any termcap file, + so fake it. */ + if (!strcmp (name, "internal")) + { + term = INTERNAL_TERMINAL; + if (!bp) + { + malloc_size = 1 + strlen (term); + bp = (char *) xmalloc (malloc_size); + } + strcpy (bp, term); + goto ret; + } +#endif /* INTERNAL_TERMINAL */ + + /* For compatibility with programs like `less' that want to + put data in the termcap buffer themselves as a fallback. */ + if (bp) + term_entry = bp; + + termcap_name = getenv ("TERMCAP"); + if (termcap_name && *termcap_name == '\0') + termcap_name = NULL; +#if 0 +#if defined (MSDOS) && !defined (TEST) + if (termcap_name && (*termcap_name == '\\' + || *termcap_name == '/' + || termcap_name[1] == ':')) + dostounix_filename(termcap_name); +#endif +#endif + + filep = termcap_name && valid_filename_p (termcap_name); + + /* If termcap_name is non-null and starts with / (in the un*x case, that is), + it is a file name to use instead of /etc/termcap. + If it is non-null and does not start with /, + it is the entry itself, but only if + the name the caller requested matches the TERM variable. */ + + if (termcap_name && !filep && !strcmp (name, getenv ("TERM"))) + { + indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0); + if (!indirect) + { + if (!bp) + bp = termcap_name; + else + strcpy (bp, termcap_name); + goto ret; + } + else + { /* It has tc=. Need to read /etc/termcap. */ + tcenv = termcap_name; + termcap_name = NULL; + } + } + + if (!termcap_name || !filep) + termcap_name = TERMCAP_FILE; + + /* Here we know we must search a file and termcap_name has its name. */ + +#ifdef MSDOS + fd = open (termcap_name, O_RDONLY|O_TEXT, 0); +#else + fd = open (termcap_name, O_RDONLY, 0); +#endif + if (fd < 0) + return -1; + + buf.size = BUFSIZE; + /* Add 1 to size to ensure room for terminating null. */ + buf.beg = (char *) xmalloc (buf.size + 1); + term = indirect ? indirect : name; + + if (!bp) + { + malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; + bp = (char *) xmalloc (malloc_size); + } + bp1 = bp; + + if (indirect) + /* Copy the data from the environment variable. */ + { + strcpy (bp, tcenv); + bp1 += strlen (tcenv); + } + + while (term) + { + /* Scan the file, reading it via buf, till find start of main entry. */ + if (scan_file (term, fd, &buf) == 0) + { + close (fd); + free (buf.beg); + if (malloc_size) + free (bp); + return 0; + } + + /* Free old `term' if appropriate. */ + if (term != name) + free (term); + + /* If BP is malloc'd by us, make sure it is big enough. */ + if (malloc_size) + { + malloc_size = bp1 - bp + buf.size; + termcap_name = (char *) xrealloc (bp, malloc_size); + bp1 += termcap_name - bp; + bp = termcap_name; + } + + bp2 = bp1; + + /* Copy the line of the entry from buf into bp. */ + termcap_name = buf.ptr; + while ((*bp1++ = c = *termcap_name++) && c != '\n') + /* Drop out any \ newline sequence. */ + if (c == '\\' && *termcap_name == '\n') + { + bp1--; + termcap_name++; + } + *bp1 = '\0'; + + /* Does this entry refer to another terminal type's entry? + If something is found, copy it into heap and null-terminate it. */ + term = tgetst1 (find_capability (bp2, "tc"), (char **) 0); + } + + close (fd); + free (buf.beg); + + if (malloc_size) + bp = (char *) xrealloc (bp, bp1 - bp + 1); + + ret: + term_entry = bp; + return 1; +} + +/* Given file open on FD and buffer BUFP, + scan the file from the beginning until a line is found + that starts the entry for terminal type STR. + Return 1 if successful, with that line in BUFP, + or 0 if no entry is found in the file. */ + +static int +scan_file (str, fd, bufp) + char *str; + int fd; + register struct buffer *bufp; +{ + register char *end; + + bufp->ptr = bufp->beg; + bufp->full = 0; + bufp->ateof = 0; + *bufp->ptr = '\0'; + + lseek (fd, 0L, 0); + + while (!bufp->ateof) + { + /* Read a line into the buffer. */ + end = NULL; + do + { + /* if it is continued, append another line to it, + until a non-continued line ends. */ + end = gobble_line (fd, bufp, end); + } + while (!bufp->ateof && end[-2] == '\\'); + + if (*bufp->ptr != '#' + && name_match (bufp->ptr, str)) + return 1; + + /* Discard the line just processed. */ + bufp->ptr = end; + } + return 0; +} + +/* Return nonzero if NAME is one of the names specified + by termcap entry LINE. */ + +static int +name_match (line, name) + char *line, *name; +{ + register char *tem; + + if (!compare_contin (line, name)) + return 1; + /* This line starts an entry. Is it the right one? */ + for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++) + if (*tem == '|' && !compare_contin (tem + 1, name)) + return 1; + + return 0; +} + +static int +compare_contin (str1, str2) + register char *str1, *str2; +{ + register int c1, c2; + while (1) + { + c1 = *str1++; + c2 = *str2++; + while (c1 == '\\' && *str1 == '\n') + { + str1++; + while ((c1 = *str1++) == ' ' || c1 == '\t'); + } + if (c2 == '\0') + { + /* End of type being looked up. */ + if (c1 == '|' || c1 == ':') + /* If end of name in data base, we win. */ + return 0; + else + return 1; + } + else if (c1 != c2) + return 1; + } +} + +/* Make sure that the buffer <- BUFP contains a full line + of the file open on FD, starting at the place BUFP->ptr + points to. Can read more of the file, discard stuff before + BUFP->ptr, or make the buffer bigger. + + Return the pointer to after the newline ending the line, + or to the end of the file, if there is no newline to end it. + + Can also merge on continuation lines. If APPEND_END is + non-null, it points past the newline of a line that is + continued; we add another line onto it and regard the whole + thing as one line. The caller decides when a line is continued. */ + +static char * +gobble_line (fd, bufp, append_end) + int fd; + register struct buffer *bufp; + char *append_end; +{ + register char *end; + register int nread; + register char *buf = bufp->beg; + register char *tem; + + if (!append_end) + append_end = bufp->ptr; + + while (1) + { + end = append_end; + while (*end && *end != '\n') end++; + if (*end) + break; + if (bufp->ateof) + return buf + bufp->full; + if (bufp->ptr == buf) + { + if (bufp->full == bufp->size) + { + bufp->size *= 2; + /* Add 1 to size to ensure room for terminating null. */ + tem = (char *) xrealloc (buf, bufp->size + 1); + bufp->ptr = (bufp->ptr - buf) + tem; + append_end = (append_end - buf) + tem; + bufp->beg = buf = tem; + } + } + else + { + append_end -= bufp->ptr - buf; + bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf); + bufp->ptr = buf; + } + if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full))) + bufp->ateof = 1; + bufp->full += nread; + buf[bufp->full] = '\0'; + } + return end + 1; +} + +#ifdef TEST + +#ifdef NULL +#undef NULL +#endif + +#include <stdio.h> + +main (argc, argv) + int argc; + char **argv; +{ + char *term; + char *buf; + + term = argv[1]; + printf ("TERM: %s\n", term); + + buf = (char *) tgetent (0, term); + if ((int) buf <= 0) + { + printf ("No entry.\n"); + return 0; + } + + printf ("Entry: %s\n", buf); + + tprint ("cm"); + tprint ("AL"); + + printf ("co: %d\n", tgetnum ("co")); + printf ("am: %d\n", tgetflag ("am")); +} + +tprint (cap) + char *cap; +{ + char *x = tgetstr (cap, 0); + register char *y; + + printf ("%s: ", cap); + if (x) + { + for (y = x; *y; y++) + if (*y <= ' ' || *y == 0177) + printf ("\\%0o", *y); + else + putchar (*y); + free (x); + } + else + printf ("none"); + putchar ('\n'); +} + +#endif /* TEST */ diff --git a/lib/termcap/termcap.h b/lib/termcap/termcap.h new file mode 100644 index 0000000..b0e3061 --- /dev/null +++ b/lib/termcap/termcap.h @@ -0,0 +1,63 @@ +/* termcap.h - public declarations for termcap library. */ + +/* Copyright (C) 1991, 1992, 1995, 2001, 2005, 2006, 2008,2009 Free Software Foundation, Inc. + + 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _TERMCAP_H +#define _TERMCAP_H 1 + +#if __STDC__ + +extern int tgetent (char *buffer, const char *termtype); + +extern int tgetnum (const char *name); +extern int tgetflag (const char *name); +extern char *tgetstr (const char *name, char **area); + +extern char PC; +extern short ospeed; +extern int tputs (const char *string, int nlines, int (*outfun) (int)); + +extern char *tparam (const char *ctlstring, char *buffer, int size, ...); + +extern char *UP; +extern char *BC; + +extern char *tgoto (const char *cstring, int hpos, int vpos); + +#else /* not __STDC__ */ + +extern int tgetent (); + +extern int tgetnum (); +extern int tgetflag (); +extern char *tgetstr (); + +extern char PC; +extern short ospeed; + +extern void tputs (); + +extern char *tparam (); + +extern char *UP; +extern char *BC; + +extern char *tgoto (); + +#endif /* not __STDC__ */ + +#endif /* not _TERMCAP_H */ diff --git a/lib/termcap/tparam.c b/lib/termcap/tparam.c new file mode 100644 index 0000000..c4bff08 --- /dev/null +++ b/lib/termcap/tparam.c @@ -0,0 +1,345 @@ +/* tparam.c - merge parameters into a termcap entry string. */ + +/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Emacs config.h may rename various library functions such as malloc. */ +#ifdef HAVE_CONFIG_H +#include <config.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +extern char *getenv (); +extern char *malloc (); +extern char *realloc (); +#endif + +#if defined (HAVE_STRING_H) +#include <string.h> +#endif + +#if !defined (HAVE_BCOPY) && (defined (HAVE_STRING_H) || defined (STDC_HEADERS)) +# define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#else /* not HAVE_CONFIG_H */ + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#ifdef STDC_HEADERS +#include <stdlib.h> +#include <string.h> +#else +char *malloc (); +char *realloc (); +#endif + +#endif /* not HAVE_CONFIG_H */ + +#include "ltcap.h" + +#ifndef NULL +#define NULL (char *) 0 +#endif + +#ifndef emacs +static void +memory_out () +{ + write (2, "virtual memory exhausted\n", 25); + exit (1); +} + +static char * +xmalloc (size) + unsigned size; +{ + register char *tem = malloc (size); + + if (!tem) + memory_out (); + return tem; +} + +static char * +xrealloc (ptr, size) + char *ptr; + unsigned size; +{ + register char *tem = realloc (ptr, size); + + if (!tem) + memory_out (); + return tem; +} +#endif /* not emacs */ + +/* Assuming STRING is the value of a termcap string entry + containing `%' constructs to expand parameters, + merge in parameter values and store result in block OUTSTRING points to. + LEN is the length of OUTSTRING. If more space is needed, + a block is allocated with `malloc'. + + The value returned is the address of the resulting string. + This may be OUTSTRING or may be the address of a block got with `malloc'. + In the latter case, the caller must free the block. + + The fourth and following args to tparam serve as the parameter values. */ + +static char *tparam1 (); + +/* VARARGS 2 */ +char * +tparam (string, outstring, len, arg0, arg1, arg2, arg3) + char *string; + char *outstring; + int len; + int arg0, arg1, arg2, arg3; +{ + int arg[4]; + + arg[0] = arg0; + arg[1] = arg1; + arg[2] = arg2; + arg[3] = arg3; + return tparam1 (string, outstring, len, NULL, NULL, arg); +} + +__private_extern__ char *BC; +__private_extern__ char *UP; + +static char tgoto_buf[50]; + +__private_extern__ +char * +tgoto (cm, hpos, vpos) + char *cm; + int hpos, vpos; +{ + int args[2]; + if (!cm) + return NULL; + args[0] = vpos; + args[1] = hpos; + return tparam1 (cm, tgoto_buf, 50, UP, BC, args); +} + +static char * +tparam1 (string, outstring, len, up, left, argp) + char *string; + char *outstring; + int len; + char *up, *left; + register int *argp; +{ + register int c; + register char *p = string; + register char *op = outstring; + char *outend; + int outlen = 0; + + register int tem; + int *old_argp = argp; + int doleft = 0; + int doup = 0; + + outend = outstring + len; + + while (1) + { + /* If the buffer might be too short, make it bigger. */ + if (op + 5 >= outend) + { + register char *new; + if (outlen == 0) + { + outlen = len + 40; + new = (char *) xmalloc (outlen); + outend += 40; + bcopy (outstring, new, op - outstring); + } + else + { + outend += outlen; + outlen *= 2; + new = (char *) xrealloc (outstring, outlen); + } + op += new - outstring; + outend += new - outstring; + outstring = new; + } + c = *p++; + if (!c) + break; + if (c == '%') + { + c = *p++; + tem = *argp; + switch (c) + { + case 'd': /* %d means output in decimal. */ + if (tem < 10) + goto onedigit; + if (tem < 100) + goto twodigit; + case '3': /* %3 means output in decimal, 3 digits. */ + if (tem > 999) + { + *op++ = tem / 1000 + '0'; + tem %= 1000; + } + *op++ = tem / 100 + '0'; + case '2': /* %2 means output in decimal, 2 digits. */ + twodigit: + tem %= 100; + *op++ = tem / 10 + '0'; + onedigit: + *op++ = tem % 10 + '0'; + argp++; + break; + + case 'C': + /* For c-100: print quotient of value by 96, if nonzero, + then do like %+. */ + if (tem >= 96) + { + *op++ = tem / 96; + tem %= 96; + } + case '+': /* %+x means add character code of char x. */ + tem += *p++; + case '.': /* %. means output as character. */ + if (left) + { + /* If want to forbid output of 0 and \n and \t, + and this is one of them, increment it. */ + while (tem == 0 || tem == '\n' || tem == '\t') + { + tem++; + if (argp == old_argp) + doup++, outend -= strlen (up); + else + doleft++, outend -= strlen (left); + } + } + *op++ = tem ? tem : 0200; + case 'f': /* %f means discard next arg. */ + argp++; + break; + + case 'b': /* %b means back up one arg (and re-use it). */ + argp--; + break; + + case 'r': /* %r means interchange following two args. */ + argp[0] = argp[1]; + argp[1] = tem; + old_argp++; + break; + + case '>': /* %>xy means if arg is > char code of x, */ + if (argp[0] > *p++) /* then add char code of y to the arg, */ + argp[0] += *p; /* and in any case don't output. */ + p++; /* Leave the arg to be output later. */ + break; + + case 'a': /* %a means arithmetic. */ + /* Next character says what operation. + Add or subtract either a constant or some other arg. */ + /* First following character is + to add or - to subtract + or = to assign. */ + /* Next following char is 'p' and an arg spec + (0100 plus position of that arg relative to this one) + or 'c' and a constant stored in a character. */ + tem = p[2] & 0177; + if (p[1] == 'p') + tem = argp[tem - 0100]; + if (p[0] == '-') + argp[0] -= tem; + else if (p[0] == '+') + argp[0] += tem; + else if (p[0] == '*') + argp[0] *= tem; + else if (p[0] == '/') + argp[0] /= tem; + else + argp[0] = tem; + + p += 3; + break; + + case 'i': /* %i means add one to arg, */ + argp[0] ++; /* and leave it to be output later. */ + argp[1] ++; /* Increment the following arg, too! */ + break; + + case '%': /* %% means output %; no arg. */ + goto ordinary; + + case 'n': /* %n means xor each of next two args with 140. */ + argp[0] ^= 0140; + argp[1] ^= 0140; + break; + + case 'm': /* %m means xor each of next two args with 177. */ + argp[0] ^= 0177; + argp[1] ^= 0177; + break; + + case 'B': /* %B means express arg as BCD char code. */ + argp[0] += 6 * (tem / 10); + break; + + case 'D': /* %D means weird Delta Data transformation. */ + argp[0] -= 2 * (tem % 16); + break; + } + } + else + /* Ordinary character in the argument string. */ + ordinary: + *op++ = c; + } + *op = 0; + while (doup-- > 0) + strcat (op, up); + while (doleft-- > 0) + strcat (op, left); + return outstring; +} + +#ifdef DEBUG + +main (argc, argv) + int argc; + char **argv; +{ + char buf[50]; + int args[3]; + args[0] = atoi (argv[2]); + args[1] = atoi (argv[3]); + args[2] = atoi (argv[4]); + tparam1 (argv[1], buf, "LEFT", "UP", args); + printf ("%s\n", buf); + return 0; +} + +#endif /* DEBUG */ diff --git a/lib/termcap/version.c b/lib/termcap/version.c new file mode 100644 index 0000000..cad57be --- /dev/null +++ b/lib/termcap/version.c @@ -0,0 +1,22 @@ +/* version.c - termcap library version information. */ + +/* Copyright (C) 1985-2009 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Make the library identifiable with the RCS ident command. */ +static char *termcap_version_string = "\n$Version: GNU termcap 1.3 $\n"; diff --git a/lib/tilde/Makefile.in b/lib/tilde/Makefile.in new file mode 100644 index 0000000..e61c671 --- /dev/null +++ b/lib/tilde/Makefile.in @@ -0,0 +1,127 @@ +## -*- text -*- #################################################### +# # +# Makefile for the GNU Tilde Library. # +# # +#################################################################### + +# Copyright (C) 1996-2021 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. + +srcdir = @srcdir@ +VPATH = @srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +PROFILE_FLAGS = @PROFILE_FLAGS@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +STYLE_CFLAGS = @STYLE_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +BASHINCDIR = ${topdir}/include + +INCLUDES = -I. -I../.. -I$(topdir) -I${BASHINCDIR} -I$(topdir)/lib + +CCFLAGS = ${ASAN_CFLAGS} $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) \ + ${INCLUDES} $(STYLE_CFLAGS) $(LOCAL_CFLAGS) $(CFLAGS) + +.c.o: + $(CC) -c $(CCFLAGS) $< + +# The name of the library target. +LIBRARY_NAME = libtilde.a + +# The C code source files for this library. +CSOURCES = $(srcdir)/tilde.c + +# The header files for this library. +HSOURCES = $(srcdir)/tilde.h + +OBJECTS = tilde.o + +# The texinfo files which document this library. +DOCSOURCE = doc/tilde.texi +DOCOBJECT = doc/tilde.dvi +DOCSUPPORT = doc/Makefile +DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT) + +SUPPORT = Makefile ChangeLog $(DOCSUPPORT) + +SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE) + +THINGS_TO_TAR = $(SOURCES) $(SUPPORT) + +###################################################################### + +all: $(LIBRARY_NAME) + +$(LIBRARY_NAME): $(OBJECTS) + $(RM) -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +documentation: force + -(cd doc; $(MAKE) $(MFLAGS)) + +force: + +# The rule for 'includes' is written funny so that the if statement +# always returns TRUE unless there really was an error installing the +# include files. +install: + $(INSTALL_DATA) -c -m 644 $(LIBRARY_NAME) $(libdir)/$(LIBRARY_NAME) + -test -n "$(RANLIB)" && $(RANLIB) -t $(libdir)/$(LIBRARY_NAME) + +clean: + $(RM) -f $(OBJECTS) $(LIBRARY_NAME) + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +realclean distclean maintainer-clean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + $(RM) -f Makefile + +mostlyclean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +###################################################################### +# # +# Dependencies for the object files which make up this library. # +# # +###################################################################### + +tilde.o: tilde.h $(BASHINCDIR)/ansi_stdlib.h +tilde.o: $(BUILD_DIR)/config.h + +# Rules for deficient makes, like SunOS and Solaris +tilde.o: tilde.c diff --git a/lib/tilde/README b/lib/tilde/README new file mode 100644 index 0000000..a8772f3 --- /dev/null +++ b/lib/tilde/README @@ -0,0 +1,5 @@ +If you're building this separately from bash or the readline library, add +$(srcdir)/shell.c to the CSOURCES variable and shell.o to the OBJECTS +variable in Makefile.in. (Not that this is very useful without readline +or bash.) + diff --git a/lib/tilde/shell.c b/lib/tilde/shell.c new file mode 100644 index 0000000..9805a92 --- /dev/null +++ b/lib/tilde/shell.c @@ -0,0 +1,79 @@ +/* shell.c -- tilde utility functions that are normally provided by + bash when readline is linked as part of the shell. */ + +/* Copyright (C) 1998-2017 Free Software Foundation, Inc. + + This file is part of the GNU Tilde Library. + + The GNU Tilde 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 3 of the License, or + (at your option) any later version. + + The GNU Tilde 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. + + You should have received a copy of the GNU General Public License + along with the GNU Tilde Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# 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_STRING_H) +# include <string.h> +#else +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#include <pwd.h> + +#if !defined (HAVE_GETPW_DECLS) +extern struct passwd *getpwuid (); +#endif /* !HAVE_GETPW_DECLS */ + +char * +get_env_value (char *varname) +{ + return ((char *)getenv (varname)); +} + +/* If we're not using $HOME, assume that the passwd file information won't + change while this shell instance is running. */ +char * +get_home_dir (void) +{ + static char *home_dir = (char *)NULL; + struct passwd *entry; + + if (home_dir) + return (home_dir); + +#if defined (HAVE_GETPWUID) + entry = getpwuid (getuid ()); + if (entry) + home_dir = savestring (entry->pw_dir); +#endif + +#if defined (HAVE_GETPWENT) + endpwent (); /* some systems need this */ +#endif + + return (home_dir); +} diff --git a/lib/tilde/tilde.c b/lib/tilde/tilde.c new file mode 100644 index 0000000..d678a31 --- /dev/null +++ b/lib/tilde/tilde.c @@ -0,0 +1,493 @@ +/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ + +/* Copyright (C) 1988-2020 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#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 /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <sys/types.h> +#if defined (HAVE_PWD_H) +#include <pwd.h> +#endif + +#include "tilde.h" + +#if defined (TEST) || defined (STATIC_MALLOC) +static void *xmalloc (), *xrealloc (); +#else +# include "xmalloc.h" +#endif /* TEST || STATIC_MALLOC */ + +#if !defined (HAVE_GETPW_DECLS) +# if defined (HAVE_GETPWUID) +extern struct passwd *getpwuid (uid_t); +# endif +# if defined (HAVE_GETPWNAM) +extern struct passwd *getpwnam (const char *); +# endif +#endif /* !HAVE_GETPW_DECLS */ + +#if !defined (savestring) +#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x)) +#endif /* !savestring */ + +#if !defined (NULL) +# if defined (__STDC__) +# define NULL ((void *) 0) +# else +# define NULL 0x0 +# endif /* !__STDC__ */ +#endif /* !NULL */ + +/* If being compiled as part of bash, these will be satisfied from + variables.o. If being compiled as part of readline, they will + be satisfied from shell.o. */ +extern char *sh_get_home_dir (void); +extern char *sh_get_env_value (const char *); + +/* The default value of tilde_additional_prefixes. This is set to + whitespace preceding a tilde so that simple programs which do not + perform any word separation get desired behaviour. */ +static const char *default_prefixes[] = + { " ~", "\t~", (const char *)NULL }; + +/* The default value of tilde_additional_suffixes. This is set to + whitespace or newline so that simple programs which do not + perform any word separation get desired behaviour. */ +static const char *default_suffixes[] = + { " ", "\n", (const char *)NULL }; + +/* If non-null, this contains the address of a function that the application + wants called before trying the standard tilde expansions. The function + is called with the text sans tilde, and returns a malloc()'ed string + which is the expansion, or a NULL pointer if the expansion fails. */ +tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +char **tilde_additional_prefixes = (char **)default_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +char **tilde_additional_suffixes = (char **)default_suffixes; + +static int tilde_find_prefix (const char *, int *); +static int tilde_find_suffix (const char *); +static char *isolate_tilde_prefix (const char *, int *); +static char *glue_prefix_and_suffix (char *, const char *, int); + +/* Find the start of a tilde expansion in STRING, and return the index of + the tilde which starts the expansion. Place the length of the text + which identified this tilde starter in LEN, excluding the tilde itself. */ +static int +tilde_find_prefix (const char *string, int *len) +{ + register int i, j, string_len; + register char **prefixes; + + prefixes = tilde_additional_prefixes; + + string_len = strlen (string); + *len = 0; + + if (*string == '\0' || *string == '~') + return (0); + + if (prefixes) + { + for (i = 0; i < string_len; i++) + { + for (j = 0; prefixes[j]; j++) + { + if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) + { + *len = strlen (prefixes[j]) - 1; + return (i + *len); + } + } + } + } + return (string_len); +} + +/* Find the end of a tilde expansion in STRING, and return the index of + the character which ends the tilde definition. */ +static int +tilde_find_suffix (const char *string) +{ + register int i, j, string_len; + register char **suffixes; + + suffixes = tilde_additional_suffixes; + string_len = strlen (string); + + for (i = 0; i < string_len; i++) + { +#if defined (__MSDOS__) + if (string[i] == '/' || string[i] == '\\' /* || !string[i] */) +#else + if (string[i] == '/' /* || !string[i] */) +#endif + break; + + for (j = 0; suffixes && suffixes[j]; j++) + { + if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) + return (i); + } + } + return (i); +} + +/* Return a new string which is the result of tilde expanding STRING. */ +char * +tilde_expand (const char *string) +{ + char *result; + int result_size, result_index; + + result_index = result_size = 0; + if (result = strchr (string, '~')) + result = (char *)xmalloc (result_size = (strlen (string) + 16)); + else + result = (char *)xmalloc (result_size = (strlen (string) + 1)); + + /* Scan through STRING expanding tildes as we come to them. */ + while (1) + { + register int start, end; + char *tilde_word, *expansion; + int len; + + /* Make START point to the tilde which starts the expansion. */ + start = tilde_find_prefix (string, &len); + + /* Copy the skipped text into the result. */ + if ((result_index + start + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); + + strncpy (result + result_index, string, start); + result_index += start; + + /* Advance STRING to the starting tilde. */ + string += start; + + /* Make END be the index of one after the last character of the + username. */ + end = tilde_find_suffix (string); + + /* If both START and END are zero, we are all done. */ + if (!start && !end) + break; + + /* Expand the entire tilde word, and copy it into RESULT. */ + tilde_word = (char *)xmalloc (1 + end); + strncpy (tilde_word, string, end); + tilde_word[end] = '\0'; + string += end; + + expansion = tilde_expand_word (tilde_word); + + if (expansion == 0) + expansion = tilde_word; + else + xfree (tilde_word); + + len = strlen (expansion); +#ifdef __CYGWIN__ + /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when + $HOME for `user' is /. On cygwin, // denotes a network drive. */ + if (len > 1 || *expansion != '/' || *string != '/') +#endif + { + if ((result_index + len + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); + + strcpy (result + result_index, expansion); + result_index += len; + } + xfree (expansion); + } + + result[result_index] = '\0'; + + return (result); +} + +/* Take FNAME and return the tilde prefix we want expanded. If LENP is + non-null, the index of the end of the prefix into FNAME is returned in + the location it points to. */ +static char * +isolate_tilde_prefix (const char *fname, int *lenp) +{ + char *ret; + int i; + + ret = (char *)xmalloc (strlen (fname)); +#if defined (__MSDOS__) + for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++) +#else + for (i = 1; fname[i] && fname[i] != '/'; i++) +#endif + ret[i - 1] = fname[i]; + ret[i - 1] = '\0'; + if (lenp) + *lenp = i; + return ret; +} + +#if 0 +/* Public function to scan a string (FNAME) beginning with a tilde and find + the portion of the string that should be passed to the tilde expansion + function. Right now, it just calls tilde_find_suffix and allocates new + memory, but it can be expanded to do different things later. */ +char * +tilde_find_word (const char *fname, int flags, int *lenp) +{ + int x; + char *r; + + x = tilde_find_suffix (fname); + if (x == 0) + { + r = savestring (fname); + if (lenp) + *lenp = 0; + } + else + { + r = (char *)xmalloc (1 + x); + strncpy (r, fname, x); + r[x] = '\0'; + if (lenp) + *lenp = x; + } + + return r; +} +#endif + +/* Return a string that is PREFIX concatenated with SUFFIX starting at + SUFFIND. */ +static char * +glue_prefix_and_suffix (char *prefix, const char *suffix, int suffind) +{ + char *ret; + int plen, slen; + + plen = (prefix && *prefix) ? strlen (prefix) : 0; + slen = strlen (suffix + suffind); + ret = (char *)xmalloc (plen + slen + 1); + if (plen) + strcpy (ret, prefix); + strcpy (ret + plen, suffix + suffind); + return ret; +} + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. + This always returns a newly-allocated string, never static storage. */ +char * +tilde_expand_word (const char *filename) +{ + char *dirname, *expansion, *username; + int user_len; + struct passwd *user_entry; + + if (filename == 0) + return ((char *)NULL); + + if (*filename != '~') + return (savestring (filename)); + + /* A leading `~/' or a bare `~' is *always* translated to the value of + $HOME or the home directory of the current user, regardless of any + preexpansion hook. */ + if (filename[1] == '\0' || filename[1] == '/') + { + /* Prefix $HOME to the rest of the string. */ + expansion = sh_get_env_value ("HOME"); +#if defined (_WIN32) + if (expansion == 0) + expansion = sh_get_env_value ("APPDATA"); +#endif + + /* If there is no HOME variable, look up the directory in + the password database. */ + if (expansion == 0) + expansion = sh_get_home_dir (); + + return (glue_prefix_and_suffix (expansion, filename, 1)); + } + + username = isolate_tilde_prefix (filename, &user_len); + + if (tilde_expansion_preexpansion_hook) + { + expansion = (*tilde_expansion_preexpansion_hook) (username); + if (expansion) + { + dirname = glue_prefix_and_suffix (expansion, filename, user_len); + xfree (username); + xfree (expansion); + return (dirname); + } + } + + /* No preexpansion hook, or the preexpansion hook failed. Look in the + password database. */ + dirname = (char *)NULL; +#if defined (HAVE_GETPWNAM) + user_entry = getpwnam (username); +#else + user_entry = 0; +#endif + if (user_entry == 0) + { + /* If the calling program has a special syntax for expanding tildes, + and we couldn't find a standard expansion, then let them try. */ + if (tilde_expansion_failure_hook) + { + expansion = (*tilde_expansion_failure_hook) (username); + if (expansion) + { + dirname = glue_prefix_and_suffix (expansion, filename, user_len); + xfree (expansion); + } + } + /* If we don't have a failure hook, or if the failure hook did not + expand the tilde, return a copy of what we were passed. */ + if (dirname == 0) + dirname = savestring (filename); + } +#if defined (HAVE_GETPWENT) + else + dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); +#endif + + xfree (username); +#if defined (HAVE_GETPWENT) + endpwent (); +#endif + return (dirname); +} + + +#if defined (TEST) +#undef NULL +#include <stdio.h> + +main (int argc, char **argv) +{ + char *result, line[512]; + int done = 0; + + while (!done) + { + printf ("~expand: "); + fflush (stdout); + + if (!gets (line)) + strcpy (line, "done"); + + if ((strcmp (line, "done") == 0) || + (strcmp (line, "quit") == 0) || + (strcmp (line, "exit") == 0)) + { + done = 1; + break; + } + + result = tilde_expand (line); + printf (" --> %s\n", result); + free (result); + } + exit (0); +} + +static void memory_error_and_abort (void); + +static void * +xmalloc (size_t bytes) +{ + void *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void * +xrealloc (void *pointer, int bytes) +{ + void *temp; + + if (!pointer) + temp = malloc (bytes); + else + temp = realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort (void) +{ + fprintf (stderr, "readline: out of virtual memory\n"); + abort (); +} + +/* + * Local variables: + * compile-command: "gcc -g -DTEST -o tilde tilde.c" + * end: + */ +#endif /* TEST */ diff --git a/lib/tilde/tilde.h b/lib/tilde/tilde.h new file mode 100644 index 0000000..bc8022a --- /dev/null +++ b/lib/tilde/tilde.h @@ -0,0 +1,68 @@ +/* tilde.h: Externally available variables and function in libtilde.a. */ + +/* Copyright (C) 1992-2009,2021 Free Software Foundation, Inc. + + This file contains the Readline Library (Readline), a set of + routines for providing Emacs style line input to programs that ask + for it. + + Readline 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 3 of the License, or + (at your option) any later version. + + Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_TILDE_H_) +# define _TILDE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char *tilde_hook_func_t (char *); + +/* If non-null, this contains the address of a function that the application + wants called before trying the standard tilde expansions. The function + is called with the text sans tilde, and returns a malloc()'ed string + which is the expansion, or a NULL pointer if the expansion fails. */ +extern tilde_hook_func_t *tilde_expansion_preexpansion_hook; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern tilde_hook_func_t *tilde_expansion_failure_hook; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +extern char **tilde_additional_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +extern char **tilde_additional_suffixes; + +/* Return a new string which is the result of tilde expanding STRING. */ +extern char *tilde_expand (const char *); + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +extern char *tilde_expand_word (const char *); + +/* Find the portion of the string beginning with ~ that should be expanded. */ +extern char *tilde_find_word (const char *, int, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _TILDE_H_ */ |