Removing elvis, importing nvi, ctags, updating libedit. Change-Id: I881eb04d2dc64cf112facd992de1114e1a59107f
		
			
				
	
	
		
			2275 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2275 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*	$NetBSD: readline.c,v 1.106 2012/10/12 23:35:02 christos Exp $	*/
 | 
						|
 | 
						|
/*-
 | 
						|
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * This code is derived from software contributed to The NetBSD Foundation
 | 
						|
 * by Jaromir Dolecek.
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#if !defined(lint) && !defined(SCCSID)
 | 
						|
__RCSID("$NetBSD: readline.c,v 1.106 2012/10/12 23:35:02 christos Exp $");
 | 
						|
#endif /* not lint && not SCCSID */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <string.h>
 | 
						|
#include <pwd.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <setjmp.h>
 | 
						|
#include <vis.h>
 | 
						|
 | 
						|
#include "readline/readline.h"
 | 
						|
#include "el.h"
 | 
						|
#include "fcns.h"		/* for EL_NUM_FCNS */
 | 
						|
#include "histedit.h"
 | 
						|
#include "filecomplete.h"
 | 
						|
 | 
						|
void rl_prep_terminal(int);
 | 
						|
void rl_deprep_terminal(void);
 | 
						|
 | 
						|
/* for rl_complete() */
 | 
						|
#define TAB		'\r'
 | 
						|
 | 
						|
/* see comment at the #ifdef for sense of this */
 | 
						|
/* #define GDB_411_HACK */
 | 
						|
 | 
						|
/* readline compatibility stuff - look at readline sources/documentation */
 | 
						|
/* to see what these variables mean */
 | 
						|
const char *rl_library_version = "EditLine wrapper";
 | 
						|
int rl_readline_version = RL_READLINE_VERSION;
 | 
						|
static char empty[] = { '\0' };
 | 
						|
static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' };
 | 
						|
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$',
 | 
						|
    '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
 | 
						|
char *rl_readline_name = empty;
 | 
						|
FILE *rl_instream = NULL;
 | 
						|
FILE *rl_outstream = NULL;
 | 
						|
int rl_point = 0;
 | 
						|
int rl_end = 0;
 | 
						|
char *rl_line_buffer = NULL;
 | 
						|
VCPFunction *rl_linefunc = NULL;
 | 
						|
int rl_done = 0;
 | 
						|
VFunction *rl_event_hook = NULL;
 | 
						|
KEYMAP_ENTRY_ARRAY emacs_standard_keymap,
 | 
						|
    emacs_meta_keymap,
 | 
						|
    emacs_ctlx_keymap;
 | 
						|
 | 
						|
int history_base = 1;		/* probably never subject to change */
 | 
						|
int history_length = 0;
 | 
						|
int max_input_history = 0;
 | 
						|
char history_expansion_char = '!';
 | 
						|
char history_subst_char = '^';
 | 
						|
char *history_no_expand_chars = expand_chars;
 | 
						|
Function *history_inhibit_expansion_function = NULL;
 | 
						|
char *history_arg_extract(int start, int end, const char *str);
 | 
						|
 | 
						|
int rl_inhibit_completion = 0;
 | 
						|
int rl_attempted_completion_over = 0;
 | 
						|
char *rl_basic_word_break_characters = break_chars;
 | 
						|
char *rl_completer_word_break_characters = NULL;
 | 
						|
char *rl_completer_quote_characters = NULL;
 | 
						|
Function *rl_completion_entry_function = NULL;
 | 
						|
char *(*rl_completion_word_break_hook)(void) = NULL;
 | 
						|
CPPFunction *rl_attempted_completion_function = NULL;
 | 
						|
Function *rl_pre_input_hook = NULL;
 | 
						|
Function *rl_startup1_hook = NULL;
 | 
						|
int (*rl_getc_function)(FILE *) = NULL;
 | 
						|
char *rl_terminal_name = NULL;
 | 
						|
int rl_already_prompted = 0;
 | 
						|
int rl_filename_completion_desired = 0;
 | 
						|
int rl_ignore_completion_duplicates = 0;
 | 
						|
int rl_catch_signals = 1;
 | 
						|
int readline_echoing_p = 1;
 | 
						|
int _rl_print_completions_horizontally = 0;
 | 
						|
VFunction *rl_redisplay_function = NULL;
 | 
						|
Function *rl_startup_hook = NULL;
 | 
						|
VFunction *rl_completion_display_matches_hook = NULL;
 | 
						|
VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal;
 | 
						|
VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal;
 | 
						|
KEYMAP_ENTRY_ARRAY emacs_meta_keymap;
 | 
						|
 | 
						|
/*
 | 
						|
 * The current prompt string.
 | 
						|
 */
 | 
						|
char *rl_prompt = NULL;
 | 
						|
/*
 | 
						|
 * This is set to character indicating type of completion being done by
 | 
						|
 * rl_complete_internal(); this is available for application completion
 | 
						|
 * functions.
 | 
						|
 */
 | 
						|
int rl_completion_type = 0;
 | 
						|
 | 
						|
/*
 | 
						|
 * If more than this number of items results from query for possible
 | 
						|
 * completions, we ask user if they are sure to really display the list.
 | 
						|
 */
 | 
						|
int rl_completion_query_items = 100;
 | 
						|
 | 
						|
/*
 | 
						|
 * List of characters which are word break characters, but should be left
 | 
						|
 * in the parsed text when it is passed to the completion function.
 | 
						|
 * Shell uses this to help determine what kind of completing to do.
 | 
						|
 */
 | 
						|
char *rl_special_prefixes = NULL;
 | 
						|
 | 
						|
/*
 | 
						|
 * This is the character appended to the completed words if at the end of
 | 
						|
 * the line. Default is ' ' (a space).
 | 
						|
 */
 | 
						|
int rl_completion_append_character = ' ';
 | 
						|
 | 
						|
/* stuff below is used internally by libedit for readline emulation */
 | 
						|
 | 
						|
static History *h = NULL;
 | 
						|
static EditLine *e = NULL;
 | 
						|
static Function *map[256];
 | 
						|
static jmp_buf topbuf;
 | 
						|
 | 
						|
/* internal functions */
 | 
						|
static unsigned char	 _el_rl_complete(EditLine *, int);
 | 
						|
static unsigned char	 _el_rl_tstp(EditLine *, int);
 | 
						|
static char		*_get_prompt(EditLine *);
 | 
						|
static int		 _getc_function(EditLine *, char *);
 | 
						|
static HIST_ENTRY	*_move_history(int);
 | 
						|
static int		 _history_expand_command(const char *, size_t, size_t,
 | 
						|
    char **);
 | 
						|
static char		*_rl_compat_sub(const char *, const char *,
 | 
						|
    const char *, int);
 | 
						|
static int		 _rl_event_read_char(EditLine *, char *);
 | 
						|
static void		 _rl_update_pos(void);
 | 
						|
 | 
						|
 | 
						|
/* ARGSUSED */
 | 
						|
static char *
 | 
						|
_get_prompt(EditLine *el __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	rl_already_prompted = 1;
 | 
						|
	return rl_prompt;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * generic function for moving around history
 | 
						|
 */
 | 
						|
static HIST_ENTRY *
 | 
						|
_move_history(int op)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	static HIST_ENTRY rl_he;
 | 
						|
 | 
						|
	if (history(h, &ev, op) != 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	rl_he.line = ev.str;
 | 
						|
	rl_he.data = NULL;
 | 
						|
 | 
						|
	return &rl_he;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * read one key from user defined input function
 | 
						|
 */
 | 
						|
static int
 | 
						|
/*ARGSUSED*/
 | 
						|
_getc_function(EditLine *el __attribute__((__unused__)), char *c)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	i = (*rl_getc_function)(NULL);
 | 
						|
	if (i == -1)
 | 
						|
		return 0;
 | 
						|
	*c = (char)i;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
_resize_fun(EditLine *el, void *a)
 | 
						|
{
 | 
						|
	const LineInfo *li;
 | 
						|
	char **ap = a;
 | 
						|
 | 
						|
	li = el_line(el);
 | 
						|
	/* a cheesy way to get rid of const cast. */
 | 
						|
	*ap = memchr(li->buffer, *li->buffer, (size_t)1);
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
_default_history_file(void)
 | 
						|
{
 | 
						|
	struct passwd *p;
 | 
						|
	static char path[PATH_MAX];
 | 
						|
 | 
						|
	if (*path)
 | 
						|
		return path;
 | 
						|
	if ((p = getpwuid(getuid())) == NULL)
 | 
						|
		return NULL;
 | 
						|
	(void)snprintf(path, sizeof(path), "%s/.history", p->pw_dir);
 | 
						|
	return path;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * READLINE compatibility stuff
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Set the prompt
 | 
						|
 */
 | 
						|
int
 | 
						|
rl_set_prompt(const char *prompt)
 | 
						|
{
 | 
						|
	char *p;
 | 
						|
 | 
						|
	if (!prompt)
 | 
						|
		prompt = "";
 | 
						|
	if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) 
 | 
						|
		return 0;
 | 
						|
	if (rl_prompt)
 | 
						|
		el_free(rl_prompt);
 | 
						|
	rl_prompt = strdup(prompt);
 | 
						|
	if (rl_prompt == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL)
 | 
						|
		*p = RL_PROMPT_START_IGNORE;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * initialize rl compat stuff
 | 
						|
 */
 | 
						|
int
 | 
						|
rl_initialize(void)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int editmode = 1;
 | 
						|
	struct termios t;
 | 
						|
 | 
						|
	if (e != NULL)
 | 
						|
		el_end(e);
 | 
						|
	if (h != NULL)
 | 
						|
		history_end(h);
 | 
						|
 | 
						|
	if (!rl_instream)
 | 
						|
		rl_instream = stdin;
 | 
						|
	if (!rl_outstream)
 | 
						|
		rl_outstream = stdout;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * See if we don't really want to run the editor
 | 
						|
	 */
 | 
						|
	if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0)
 | 
						|
		editmode = 0;
 | 
						|
 | 
						|
	e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr);
 | 
						|
 | 
						|
	if (!editmode)
 | 
						|
		el_set(e, EL_EDITMODE, 0);
 | 
						|
 | 
						|
	h = history_init();
 | 
						|
	if (!e || !h)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	history(h, &ev, H_SETSIZE, INT_MAX);	/* unlimited */
 | 
						|
	history_length = 0;
 | 
						|
	max_input_history = INT_MAX;
 | 
						|
	el_set(e, EL_HIST, history, h);
 | 
						|
 | 
						|
	/* Setup resize function */
 | 
						|
	el_set(e, EL_RESIZE, _resize_fun, &rl_line_buffer);
 | 
						|
 | 
						|
	/* setup getc function if valid */
 | 
						|
	if (rl_getc_function)
 | 
						|
		el_set(e, EL_GETCFN, _getc_function);
 | 
						|
 | 
						|
	/* for proper prompt printing in readline() */
 | 
						|
	if (rl_set_prompt("") == -1) {
 | 
						|
		history_end(h);
 | 
						|
		el_end(e);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	el_set(e, EL_PROMPT, _get_prompt, RL_PROMPT_START_IGNORE);
 | 
						|
	el_set(e, EL_SIGNAL, rl_catch_signals);
 | 
						|
 | 
						|
	/* set default mode to "emacs"-style and read setting afterwards */
 | 
						|
	/* so this can be overriden */
 | 
						|
	el_set(e, EL_EDITOR, "emacs");
 | 
						|
	if (rl_terminal_name != NULL)
 | 
						|
		el_set(e, EL_TERMINAL, rl_terminal_name);
 | 
						|
	else
 | 
						|
		el_get(e, EL_TERMINAL, &rl_terminal_name);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Word completion - this has to go AFTER rebinding keys
 | 
						|
	 * to emacs-style.
 | 
						|
	 */
 | 
						|
	el_set(e, EL_ADDFN, "rl_complete",
 | 
						|
	    "ReadLine compatible completion function",
 | 
						|
	    _el_rl_complete);
 | 
						|
	el_set(e, EL_BIND, "^I", "rl_complete", NULL);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Send TSTP when ^Z is pressed.
 | 
						|
	 */
 | 
						|
	el_set(e, EL_ADDFN, "rl_tstp",
 | 
						|
	    "ReadLine compatible suspend function",
 | 
						|
	    _el_rl_tstp);
 | 
						|
	el_set(e, EL_BIND, "^Z", "rl_tstp", NULL);
 | 
						|
		
 | 
						|
	/* read settings from configuration file */
 | 
						|
	el_source(e, NULL);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Unfortunately, some applications really do use rl_point
 | 
						|
	 * and rl_line_buffer directly.
 | 
						|
	 */
 | 
						|
	_resize_fun(e, &rl_line_buffer);
 | 
						|
	_rl_update_pos();
 | 
						|
 | 
						|
	if (rl_startup_hook)
 | 
						|
		(*rl_startup_hook)(NULL, 0);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * read one line from input stream and return it, chomping
 | 
						|
 * trailing newline (if there is any)
 | 
						|
 */
 | 
						|
char *
 | 
						|
readline(const char *p)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	const char * volatile prompt = p;
 | 
						|
	int count;
 | 
						|
	const char *ret;
 | 
						|
	char *buf;
 | 
						|
	static int used_event_hook;
 | 
						|
 | 
						|
	if (e == NULL || h == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	rl_done = 0;
 | 
						|
 | 
						|
	(void)setjmp(topbuf);
 | 
						|
 | 
						|
	/* update prompt accordingly to what has been passed */
 | 
						|
	if (rl_set_prompt(prompt) == -1)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (rl_pre_input_hook)
 | 
						|
		(*rl_pre_input_hook)(NULL, 0);
 | 
						|
 | 
						|
	if (rl_event_hook && !(e->el_flags&NO_TTY)) {
 | 
						|
		el_set(e, EL_GETCFN, _rl_event_read_char);
 | 
						|
		used_event_hook = 1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!rl_event_hook && used_event_hook) {
 | 
						|
		el_set(e, EL_GETCFN, EL_BUILTIN_GETCFN);
 | 
						|
		used_event_hook = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	rl_already_prompted = 0;
 | 
						|
 | 
						|
	/* get one line from input stream */
 | 
						|
	ret = el_gets(e, &count);
 | 
						|
 | 
						|
	if (ret && count > 0) {
 | 
						|
		int lastidx;
 | 
						|
 | 
						|
		buf = strdup(ret);
 | 
						|
		if (buf == NULL)
 | 
						|
			return NULL;
 | 
						|
		lastidx = count - 1;
 | 
						|
		if (buf[lastidx] == '\n')
 | 
						|
			buf[lastidx] = '\0';
 | 
						|
	} else
 | 
						|
		buf = NULL;
 | 
						|
 | 
						|
	history(h, &ev, H_GETSIZE);
 | 
						|
	history_length = ev.num;
 | 
						|
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * history functions
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * is normally called before application starts to use
 | 
						|
 * history expansion functions
 | 
						|
 */
 | 
						|
void
 | 
						|
using_history(void)
 | 
						|
{
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * substitute ``what'' with ``with'', returning resulting string; if
 | 
						|
 * globally == 1, substitutes all occurrences of what, otherwise only the
 | 
						|
 * first one
 | 
						|
 */
 | 
						|
static char *
 | 
						|
_rl_compat_sub(const char *str, const char *what, const char *with,
 | 
						|
    int globally)
 | 
						|
{
 | 
						|
	const	char	*s;
 | 
						|
	char	*r, *result;
 | 
						|
	size_t	len, with_len, what_len;
 | 
						|
 | 
						|
	len = strlen(str);
 | 
						|
	with_len = strlen(with);
 | 
						|
	what_len = strlen(what);
 | 
						|
 | 
						|
	/* calculate length we need for result */
 | 
						|
	s = str;
 | 
						|
	while (*s) {
 | 
						|
		if (*s == *what && !strncmp(s, what, what_len)) {
 | 
						|
			len += with_len - what_len;
 | 
						|
			if (!globally)
 | 
						|
				break;
 | 
						|
			s += what_len;
 | 
						|
		} else
 | 
						|
			s++;
 | 
						|
	}
 | 
						|
	r = result = el_malloc((len + 1) * sizeof(*r));
 | 
						|
	if (result == NULL)
 | 
						|
		return NULL;
 | 
						|
	s = str;
 | 
						|
	while (*s) {
 | 
						|
		if (*s == *what && !strncmp(s, what, what_len)) {
 | 
						|
			(void)strncpy(r, with, with_len);
 | 
						|
			r += with_len;
 | 
						|
			s += what_len;
 | 
						|
			if (!globally) {
 | 
						|
				(void)strcpy(r, s);
 | 
						|
				return result;
 | 
						|
			}
 | 
						|
		} else
 | 
						|
			*r++ = *s++;
 | 
						|
	}
 | 
						|
	*r = '\0';
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
static	char	*last_search_pat;	/* last !?pat[?] search pattern */
 | 
						|
static	char	*last_search_match;	/* last !?pat[?] that matched */
 | 
						|
 | 
						|
const char *
 | 
						|
get_history_event(const char *cmd, int *cindex, int qchar)
 | 
						|
{
 | 
						|
	int idx, sign, sub, num, begin, ret;
 | 
						|
	size_t len;
 | 
						|
	char	*pat;
 | 
						|
	const char *rptr;
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	idx = *cindex;
 | 
						|
	if (cmd[idx++] != history_expansion_char)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	/* find out which event to take */
 | 
						|
	if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') {
 | 
						|
		if (history(h, &ev, H_FIRST) != 0)
 | 
						|
			return NULL;
 | 
						|
		*cindex = cmd[idx]? (idx + 1):idx;
 | 
						|
		return ev.str;
 | 
						|
	}
 | 
						|
	sign = 0;
 | 
						|
	if (cmd[idx] == '-') {
 | 
						|
		sign = 1;
 | 
						|
		idx++;
 | 
						|
	}
 | 
						|
 | 
						|
	if ('0' <= cmd[idx] && cmd[idx] <= '9') {
 | 
						|
		HIST_ENTRY *rl_he;
 | 
						|
 | 
						|
		num = 0;
 | 
						|
		while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') {
 | 
						|
			num = num * 10 + cmd[idx] - '0';
 | 
						|
			idx++;
 | 
						|
		}
 | 
						|
		if (sign)
 | 
						|
			num = history_length - num + 1;
 | 
						|
 | 
						|
		if (!(rl_he = history_get(num)))
 | 
						|
			return NULL;
 | 
						|
 | 
						|
		*cindex = idx;
 | 
						|
		return rl_he->line;
 | 
						|
	}
 | 
						|
	sub = 0;
 | 
						|
	if (cmd[idx] == '?') {
 | 
						|
		sub = 1;
 | 
						|
		idx++;
 | 
						|
	}
 | 
						|
	begin = idx;
 | 
						|
	while (cmd[idx]) {
 | 
						|
		if (cmd[idx] == '\n')
 | 
						|
			break;
 | 
						|
		if (sub && cmd[idx] == '?')
 | 
						|
			break;
 | 
						|
		if (!sub && (cmd[idx] == ':' || cmd[idx] == ' '
 | 
						|
				    || cmd[idx] == '\t' || cmd[idx] == qchar))
 | 
						|
			break;
 | 
						|
		idx++;
 | 
						|
	}
 | 
						|
	len = (size_t)idx - (size_t)begin;
 | 
						|
	if (sub && cmd[idx] == '?')
 | 
						|
		idx++;
 | 
						|
	if (sub && len == 0 && last_search_pat && *last_search_pat)
 | 
						|
		pat = last_search_pat;
 | 
						|
	else if (len == 0)
 | 
						|
		return NULL;
 | 
						|
	else {
 | 
						|
		if ((pat = el_malloc((len + 1) * sizeof(*pat))) == NULL)
 | 
						|
			return NULL;
 | 
						|
		(void)strncpy(pat, cmd + begin, len);
 | 
						|
		pat[len] = '\0';
 | 
						|
	}
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0) {
 | 
						|
		if (pat != last_search_pat)
 | 
						|
			el_free(pat);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	num = ev.num;
 | 
						|
 | 
						|
	if (sub) {
 | 
						|
		if (pat != last_search_pat) {
 | 
						|
			if (last_search_pat)
 | 
						|
				el_free(last_search_pat);
 | 
						|
			last_search_pat = pat;
 | 
						|
		}
 | 
						|
		ret = history_search(pat, -1);
 | 
						|
	} else
 | 
						|
		ret = history_search_prefix(pat, -1);
 | 
						|
 | 
						|
	if (ret == -1) {
 | 
						|
		/* restore to end of list on failed search */
 | 
						|
		history(h, &ev, H_FIRST);
 | 
						|
		(void)fprintf(rl_outstream, "%s: Event not found\n", pat);
 | 
						|
		if (pat != last_search_pat)
 | 
						|
			el_free(pat);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (sub && len) {
 | 
						|
		if (last_search_match && last_search_match != pat)
 | 
						|
			el_free(last_search_match);
 | 
						|
		last_search_match = pat;
 | 
						|
	}
 | 
						|
 | 
						|
	if (pat != last_search_pat)
 | 
						|
		el_free(pat);
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return NULL;
 | 
						|
	*cindex = idx;
 | 
						|
	rptr = ev.str;
 | 
						|
 | 
						|
	/* roll back to original position */
 | 
						|
	(void)history(h, &ev, H_SET, num);
 | 
						|
 | 
						|
	return rptr;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * the real function doing history expansion - takes as argument command
 | 
						|
 * to do and data upon which the command should be executed
 | 
						|
 * does expansion the way I've understood readline documentation
 | 
						|
 *
 | 
						|
 * returns 0 if data was not modified, 1 if it was and 2 if the string
 | 
						|
 * should be only printed and not executed; in case of error,
 | 
						|
 * returns -1 and *result points to NULL
 | 
						|
 * it's callers responsibility to free() string returned in *result
 | 
						|
 */
 | 
						|
static int
 | 
						|
_history_expand_command(const char *command, size_t offs, size_t cmdlen,
 | 
						|
    char **result)
 | 
						|
{
 | 
						|
	char *tmp, *search = NULL, *aptr;
 | 
						|
	const char *ptr, *cmd;
 | 
						|
	static char *from = NULL, *to = NULL;
 | 
						|
	int start, end, idx, has_mods = 0;
 | 
						|
	int p_on = 0, g_on = 0;
 | 
						|
 | 
						|
	*result = NULL;
 | 
						|
	aptr = NULL;
 | 
						|
	ptr = NULL;
 | 
						|
 | 
						|
	/* First get event specifier */
 | 
						|
	idx = 0;
 | 
						|
 | 
						|
	if (strchr(":^*$", command[offs + 1])) {
 | 
						|
		char str[4];
 | 
						|
		/*
 | 
						|
		* "!:" is shorthand for "!!:".
 | 
						|
		* "!^", "!*" and "!$" are shorthand for
 | 
						|
		* "!!:^", "!!:*" and "!!:$" respectively.
 | 
						|
		*/
 | 
						|
		str[0] = str[1] = '!';
 | 
						|
		str[2] = '0';
 | 
						|
		ptr = get_history_event(str, &idx, 0);
 | 
						|
		idx = (command[offs + 1] == ':')? 1:0;
 | 
						|
		has_mods = 1;
 | 
						|
	} else {
 | 
						|
		if (command[offs + 1] == '#') {
 | 
						|
			/* use command so far */
 | 
						|
			if ((aptr = el_malloc((offs + 1) * sizeof(*aptr)))
 | 
						|
			    == NULL)
 | 
						|
				return -1;
 | 
						|
			(void)strncpy(aptr, command, offs);
 | 
						|
			aptr[offs] = '\0';
 | 
						|
			idx = 1;
 | 
						|
		} else {
 | 
						|
			int	qchar;
 | 
						|
 | 
						|
			qchar = (offs > 0 && command[offs - 1] == '"')? '"':0;
 | 
						|
			ptr = get_history_event(command + offs, &idx, qchar);
 | 
						|
		}
 | 
						|
		has_mods = command[offs + (size_t)idx] == ':';
 | 
						|
	}
 | 
						|
 | 
						|
	if (ptr == NULL && aptr == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (!has_mods) {
 | 
						|
		*result = strdup(aptr ? aptr : ptr);
 | 
						|
		if (aptr)
 | 
						|
			el_free(aptr);
 | 
						|
		if (*result == NULL)
 | 
						|
			return -1;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	cmd = command + offs + idx + 1;
 | 
						|
 | 
						|
	/* Now parse any word designators */
 | 
						|
 | 
						|
	if (*cmd == '%')	/* last word matched by ?pat? */
 | 
						|
		tmp = strdup(last_search_match? last_search_match:"");
 | 
						|
	else if (strchr("^*$-0123456789", *cmd)) {
 | 
						|
		start = end = -1;
 | 
						|
		if (*cmd == '^')
 | 
						|
			start = end = 1, cmd++;
 | 
						|
		else if (*cmd == '$')
 | 
						|
			start = -1, cmd++;
 | 
						|
		else if (*cmd == '*')
 | 
						|
			start = 1, cmd++;
 | 
						|
	       else if (*cmd == '-' || isdigit((unsigned char) *cmd)) {
 | 
						|
			start = 0;
 | 
						|
			while (*cmd && '0' <= *cmd && *cmd <= '9')
 | 
						|
				start = start * 10 + *cmd++ - '0';
 | 
						|
 | 
						|
			if (*cmd == '-') {
 | 
						|
				if (isdigit((unsigned char) cmd[1])) {
 | 
						|
					cmd++;
 | 
						|
					end = 0;
 | 
						|
					while (*cmd && '0' <= *cmd && *cmd <= '9')
 | 
						|
						end = end * 10 + *cmd++ - '0';
 | 
						|
				} else if (cmd[1] == '$') {
 | 
						|
					cmd += 2;
 | 
						|
					end = -1;
 | 
						|
				} else {
 | 
						|
					cmd++;
 | 
						|
					end = -2;
 | 
						|
				}
 | 
						|
			} else if (*cmd == '*')
 | 
						|
				end = -1, cmd++;
 | 
						|
			else
 | 
						|
				end = start;
 | 
						|
		}
 | 
						|
		tmp = history_arg_extract(start, end, aptr? aptr:ptr);
 | 
						|
		if (tmp == NULL) {
 | 
						|
			(void)fprintf(rl_outstream, "%s: Bad word specifier",
 | 
						|
			    command + offs + idx);
 | 
						|
			if (aptr)
 | 
						|
				el_free(aptr);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	} else
 | 
						|
		tmp = strdup(aptr? aptr:ptr);
 | 
						|
 | 
						|
	if (aptr)
 | 
						|
		el_free(aptr);
 | 
						|
 | 
						|
	if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) {
 | 
						|
		*result = tmp;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	for (; *cmd; cmd++) {
 | 
						|
		if (*cmd == ':')
 | 
						|
			continue;
 | 
						|
		else if (*cmd == 'h') {		/* remove trailing path */
 | 
						|
			if ((aptr = strrchr(tmp, '/')) != NULL)
 | 
						|
				*aptr = '\0';
 | 
						|
		} else if (*cmd == 't') {	/* remove leading path */
 | 
						|
			if ((aptr = strrchr(tmp, '/')) != NULL) {
 | 
						|
				aptr = strdup(aptr + 1);
 | 
						|
				el_free(tmp);
 | 
						|
				tmp = aptr;
 | 
						|
			}
 | 
						|
		} else if (*cmd == 'r') {	/* remove trailing suffix */
 | 
						|
			if ((aptr = strrchr(tmp, '.')) != NULL)
 | 
						|
				*aptr = '\0';
 | 
						|
		} else if (*cmd == 'e') {	/* remove all but suffix */
 | 
						|
			if ((aptr = strrchr(tmp, '.')) != NULL) {
 | 
						|
				aptr = strdup(aptr);
 | 
						|
				el_free(tmp);
 | 
						|
				tmp = aptr;
 | 
						|
			}
 | 
						|
		} else if (*cmd == 'p')		/* print only */
 | 
						|
			p_on = 1;
 | 
						|
		else if (*cmd == 'g')
 | 
						|
			g_on = 2;
 | 
						|
		else if (*cmd == 's' || *cmd == '&') {
 | 
						|
			char *what, *with, delim;
 | 
						|
			size_t len, from_len;
 | 
						|
			size_t size;
 | 
						|
 | 
						|
			if (*cmd == '&' && (from == NULL || to == NULL))
 | 
						|
				continue;
 | 
						|
			else if (*cmd == 's') {
 | 
						|
				delim = *(++cmd), cmd++;
 | 
						|
				size = 16;
 | 
						|
				what = el_realloc(from, size * sizeof(*what));
 | 
						|
				if (what == NULL) {
 | 
						|
					el_free(from);
 | 
						|
					el_free(tmp);
 | 
						|
					return 0;
 | 
						|
				}
 | 
						|
				len = 0;
 | 
						|
				for (; *cmd && *cmd != delim; cmd++) {
 | 
						|
					if (*cmd == '\\' && cmd[1] == delim)
 | 
						|
						cmd++;
 | 
						|
					if (len >= size) {
 | 
						|
						char *nwhat;
 | 
						|
						nwhat = el_realloc(what,
 | 
						|
						    (size <<= 1) *
 | 
						|
						    sizeof(*nwhat));
 | 
						|
						if (nwhat == NULL) {
 | 
						|
							el_free(what);
 | 
						|
							el_free(tmp);
 | 
						|
							return 0;
 | 
						|
						}
 | 
						|
						what = nwhat;
 | 
						|
					}
 | 
						|
					what[len++] = *cmd;
 | 
						|
				}
 | 
						|
				what[len] = '\0';
 | 
						|
				from = what;
 | 
						|
				if (*what == '\0') {
 | 
						|
					el_free(what);
 | 
						|
					if (search) {
 | 
						|
						from = strdup(search);
 | 
						|
						if (from == NULL) {
 | 
						|
							el_free(tmp);
 | 
						|
							return 0;
 | 
						|
						}
 | 
						|
					} else {
 | 
						|
						from = NULL;
 | 
						|
						el_free(tmp);
 | 
						|
						return -1;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				cmd++;	/* shift after delim */
 | 
						|
				if (!*cmd)
 | 
						|
					continue;
 | 
						|
 | 
						|
				size = 16;
 | 
						|
				with = el_realloc(to, size * sizeof(*with));
 | 
						|
				if (with == NULL) {
 | 
						|
					el_free(to);
 | 
						|
					el_free(tmp);
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
				len = 0;
 | 
						|
				from_len = strlen(from);
 | 
						|
				for (; *cmd && *cmd != delim; cmd++) {
 | 
						|
					if (len + from_len + 1 >= size) {
 | 
						|
						char *nwith;
 | 
						|
						size += from_len + 1;
 | 
						|
						nwith = el_realloc(with,
 | 
						|
						    size * sizeof(*nwith));
 | 
						|
						if (nwith == NULL) {
 | 
						|
							el_free(with);
 | 
						|
							el_free(tmp);
 | 
						|
							return -1;
 | 
						|
						}
 | 
						|
						with = nwith;
 | 
						|
					}
 | 
						|
					if (*cmd == '&') {
 | 
						|
						/* safe */
 | 
						|
						(void)strcpy(&with[len], from);
 | 
						|
						len += from_len;
 | 
						|
						continue;
 | 
						|
					}
 | 
						|
					if (*cmd == '\\'
 | 
						|
					    && (*(cmd + 1) == delim
 | 
						|
						|| *(cmd + 1) == '&'))
 | 
						|
						cmd++;
 | 
						|
					with[len++] = *cmd;
 | 
						|
				}
 | 
						|
				with[len] = '\0';
 | 
						|
				to = with;
 | 
						|
			}
 | 
						|
 | 
						|
			aptr = _rl_compat_sub(tmp, from, to, g_on);
 | 
						|
			if (aptr) {
 | 
						|
				el_free(tmp);
 | 
						|
				tmp = aptr;
 | 
						|
			}
 | 
						|
			g_on = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*result = tmp;
 | 
						|
	return p_on? 2:1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * csh-style history expansion
 | 
						|
 */
 | 
						|
int
 | 
						|
history_expand(char *str, char **output)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	size_t idx, i, size;
 | 
						|
	char *tmp, *result;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if (history_expansion_char == 0) {
 | 
						|
		*output = strdup(str);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	*output = NULL;
 | 
						|
	if (str[0] == history_subst_char) {
 | 
						|
		/* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
 | 
						|
		*output = el_malloc((strlen(str) + 4 + 1) * sizeof(**output));
 | 
						|
		if (*output == NULL)
 | 
						|
			return 0;
 | 
						|
		(*output)[0] = (*output)[1] = history_expansion_char;
 | 
						|
		(*output)[2] = ':';
 | 
						|
		(*output)[3] = 's';
 | 
						|
		(void)strcpy((*output) + 4, str);
 | 
						|
		str = *output;
 | 
						|
	} else {
 | 
						|
		*output = strdup(str);
 | 
						|
		if (*output == NULL)
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
 | 
						|
#define ADD_STRING(what, len, fr)					\
 | 
						|
	{								\
 | 
						|
		if (idx + len + 1 > size) {				\
 | 
						|
			char *nresult = el_realloc(result,		\
 | 
						|
			    (size += len + 1) * sizeof(*nresult));	\
 | 
						|
			if (nresult == NULL) {				\
 | 
						|
				el_free(*output);			\
 | 
						|
				if (/*CONSTCOND*/fr)			\
 | 
						|
					el_free(tmp);			\
 | 
						|
				return 0;				\
 | 
						|
			}						\
 | 
						|
			result = nresult;				\
 | 
						|
		}							\
 | 
						|
		(void)strncpy(&result[idx], what, len);			\
 | 
						|
		idx += len;						\
 | 
						|
		result[idx] = '\0';					\
 | 
						|
	}
 | 
						|
 | 
						|
	result = NULL;
 | 
						|
	size = idx = 0;
 | 
						|
	tmp = NULL;
 | 
						|
	for (i = 0; str[i];) {
 | 
						|
		int qchar, loop_again;
 | 
						|
		size_t len, start, j;
 | 
						|
 | 
						|
		qchar = 0;
 | 
						|
		loop_again = 1;
 | 
						|
		start = j = i;
 | 
						|
loop:
 | 
						|
		for (; str[j]; j++) {
 | 
						|
			if (str[j] == '\\' &&
 | 
						|
			    str[j + 1] == history_expansion_char) {
 | 
						|
				(void)strcpy(&str[j], &str[j + 1]);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			if (!loop_again) {
 | 
						|
				if (isspace((unsigned char) str[j])
 | 
						|
				    || str[j] == qchar)
 | 
						|
					break;
 | 
						|
			}
 | 
						|
			if (str[j] == history_expansion_char
 | 
						|
			    && !strchr(history_no_expand_chars, str[j + 1])
 | 
						|
			    && (!history_inhibit_expansion_function ||
 | 
						|
			    (*history_inhibit_expansion_function)(str,
 | 
						|
			    (int)j) == 0))
 | 
						|
				break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (str[j] && loop_again) {
 | 
						|
			i = j;
 | 
						|
			qchar = (j > 0 && str[j - 1] == '"' )? '"':0;
 | 
						|
			j++;
 | 
						|
			if (str[j] == history_expansion_char)
 | 
						|
				j++;
 | 
						|
			loop_again = 0;
 | 
						|
			goto loop;
 | 
						|
		}
 | 
						|
		len = i - start;
 | 
						|
		ADD_STRING(&str[start], len, 0);
 | 
						|
 | 
						|
		if (str[i] == '\0' || str[i] != history_expansion_char) {
 | 
						|
			len = j - i;
 | 
						|
			ADD_STRING(&str[i], len, 0);
 | 
						|
			if (start == 0)
 | 
						|
				ret = 0;
 | 
						|
			else
 | 
						|
				ret = 1;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		ret = _history_expand_command (str, i, (j - i), &tmp);
 | 
						|
		if (ret > 0 && tmp) {
 | 
						|
			len = strlen(tmp);
 | 
						|
			ADD_STRING(tmp, len, 1);
 | 
						|
		}
 | 
						|
		if (tmp) {
 | 
						|
			el_free(tmp);
 | 
						|
			tmp = NULL;
 | 
						|
		}
 | 
						|
		i = j;
 | 
						|
	}
 | 
						|
 | 
						|
	/* ret is 2 for "print only" option */
 | 
						|
	if (ret == 2) {
 | 
						|
		add_history(result);
 | 
						|
#ifdef GDB_411_HACK
 | 
						|
		/* gdb 4.11 has been shipped with readline, where */
 | 
						|
		/* history_expand() returned -1 when the line	  */
 | 
						|
		/* should not be executed; in readline 2.1+	  */
 | 
						|
		/* it should return 2 in such a case		  */
 | 
						|
		ret = -1;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	el_free(*output);
 | 
						|
	*output = result;
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
* Return a string consisting of arguments of "str" from "start" to "end".
 | 
						|
*/
 | 
						|
char *
 | 
						|
history_arg_extract(int start, int end, const char *str)
 | 
						|
{
 | 
						|
	size_t  i, len, max;
 | 
						|
	char	**arr, *result = NULL;
 | 
						|
 | 
						|
	arr = history_tokenize(str);
 | 
						|
	if (!arr)
 | 
						|
		return NULL;
 | 
						|
	if (arr && *arr == NULL)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	for (max = 0; arr[max]; max++)
 | 
						|
		continue;
 | 
						|
	max--;
 | 
						|
 | 
						|
	if (start == '$')
 | 
						|
		start = (int)max;
 | 
						|
	if (end == '$')
 | 
						|
		end = (int)max;
 | 
						|
	if (end < 0)
 | 
						|
		end = (int)max + end + 1;
 | 
						|
	if (start < 0)
 | 
						|
		start = end;
 | 
						|
 | 
						|
	if (start < 0 || end < 0 || (size_t)start > max ||
 | 
						|
	    (size_t)end > max || start > end)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	for (i = (size_t)start, len = 0; i <= (size_t)end; i++)
 | 
						|
		len += strlen(arr[i]) + 1;
 | 
						|
	len++;
 | 
						|
	result = el_malloc(len * sizeof(*result));
 | 
						|
	if (result == NULL)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	for (i = (size_t)start, len = 0; i <= (size_t)end; i++) {
 | 
						|
		(void)strcpy(result + len, arr[i]);
 | 
						|
		len += strlen(arr[i]);
 | 
						|
		if (i < (size_t)end)
 | 
						|
			result[len++] = ' ';
 | 
						|
	}
 | 
						|
	result[len] = '\0';
 | 
						|
 | 
						|
out:
 | 
						|
	for (i = 0; arr[i]; i++)
 | 
						|
		el_free(arr[i]);
 | 
						|
	el_free(arr);
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Parse the string into individual tokens,
 | 
						|
 * similar to how shell would do it.
 | 
						|
 */
 | 
						|
char **
 | 
						|
history_tokenize(const char *str)
 | 
						|
{
 | 
						|
	int size = 1, idx = 0, i, start;
 | 
						|
	size_t len;
 | 
						|
	char **result = NULL, *temp, delim = '\0';
 | 
						|
 | 
						|
	for (i = 0; str[i];) {
 | 
						|
		while (isspace((unsigned char) str[i]))
 | 
						|
			i++;
 | 
						|
		start = i;
 | 
						|
		for (; str[i];) {
 | 
						|
			if (str[i] == '\\') {
 | 
						|
				if (str[i+1] != '\0')
 | 
						|
					i++;
 | 
						|
			} else if (str[i] == delim)
 | 
						|
				delim = '\0';
 | 
						|
			else if (!delim &&
 | 
						|
				    (isspace((unsigned char) str[i]) ||
 | 
						|
				strchr("()<>;&|$", str[i])))
 | 
						|
				break;
 | 
						|
			else if (!delim && strchr("'`\"", str[i]))
 | 
						|
				delim = str[i];
 | 
						|
			if (str[i])
 | 
						|
				i++;
 | 
						|
		}
 | 
						|
 | 
						|
		if (idx + 2 >= size) {
 | 
						|
			char **nresult;
 | 
						|
			size <<= 1;
 | 
						|
			nresult = el_realloc(result, (size_t)size * sizeof(*nresult));
 | 
						|
			if (nresult == NULL) {
 | 
						|
				el_free(result);
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
			result = nresult;
 | 
						|
		}
 | 
						|
		len = (size_t)i - (size_t)start;
 | 
						|
		temp = el_malloc((size_t)(len + 1) * sizeof(*temp));
 | 
						|
		if (temp == NULL) {
 | 
						|
			for (i = 0; i < idx; i++)
 | 
						|
				el_free(result[i]);
 | 
						|
			el_free(result);
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		(void)strncpy(temp, &str[start], len);
 | 
						|
		temp[len] = '\0';
 | 
						|
		result[idx++] = temp;
 | 
						|
		result[idx] = NULL;
 | 
						|
		if (str[i])
 | 
						|
			i++;
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * limit size of history record to ``max'' events
 | 
						|
 */
 | 
						|
void
 | 
						|
stifle_history(int max)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if (history(h, &ev, H_SETSIZE, max) == 0)
 | 
						|
		max_input_history = max;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * "unlimit" size of history - set the limit to maximum allowed int value
 | 
						|
 */
 | 
						|
int
 | 
						|
unstifle_history(void)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int omax;
 | 
						|
 | 
						|
	history(h, &ev, H_SETSIZE, INT_MAX);
 | 
						|
	omax = max_input_history;
 | 
						|
	max_input_history = INT_MAX;
 | 
						|
	return omax;		/* some value _must_ be returned */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
history_is_stifled(void)
 | 
						|
{
 | 
						|
 | 
						|
	/* cannot return true answer */
 | 
						|
	return max_input_history != INT_MAX;
 | 
						|
}
 | 
						|
 | 
						|
static const char _history_tmp_template[] = "/tmp/.historyXXXXXX";
 | 
						|
 | 
						|
int
 | 
						|
history_truncate_file (const char *filename, int nlines)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	FILE *fp, *tp;
 | 
						|
	char template[sizeof(_history_tmp_template)];
 | 
						|
	char buf[4096];
 | 
						|
	int fd;
 | 
						|
	char *cp;
 | 
						|
	off_t off;
 | 
						|
	int count = 0;
 | 
						|
	ssize_t left = 0;
 | 
						|
 | 
						|
	if (filename == NULL && (filename = _default_history_file()) == NULL)
 | 
						|
		return errno;
 | 
						|
	if ((fp = fopen(filename, "r+")) == NULL)
 | 
						|
		return errno;
 | 
						|
	strcpy(template, _history_tmp_template);
 | 
						|
	if ((fd = mkstemp(template)) == -1) {
 | 
						|
		ret = errno;
 | 
						|
		goto out1;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((tp = fdopen(fd, "r+")) == NULL) {
 | 
						|
		close(fd);
 | 
						|
		ret = errno;
 | 
						|
		goto out2;
 | 
						|
	}
 | 
						|
 | 
						|
	for(;;) {
 | 
						|
		if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) {
 | 
						|
			if (ferror(fp)) {
 | 
						|
				ret = errno;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) ==
 | 
						|
			    (off_t)-1) {
 | 
						|
				ret = errno;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp);
 | 
						|
			if (ferror(fp)) {
 | 
						|
				ret = errno;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if (left == 0) {
 | 
						|
				count--;
 | 
						|
				left = sizeof(buf);
 | 
						|
			} else if (fwrite(buf, (size_t)left, (size_t)1, tp)
 | 
						|
			    != 1) {
 | 
						|
				ret = errno;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			fflush(tp);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) {
 | 
						|
			ret = errno;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		count++;
 | 
						|
	}
 | 
						|
	if (ret)
 | 
						|
		goto out3;
 | 
						|
	cp = buf + left - 1;
 | 
						|
	if(*cp != '\n')
 | 
						|
		cp++;
 | 
						|
	for(;;) {
 | 
						|
		while (--cp >= buf) {
 | 
						|
			if (*cp == '\n') {
 | 
						|
				if (--nlines == 0) {
 | 
						|
					if (++cp >= buf + sizeof(buf)) {
 | 
						|
						count++;
 | 
						|
						cp = buf;
 | 
						|
					}
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (nlines <= 0 || count == 0)
 | 
						|
			break;
 | 
						|
		count--;
 | 
						|
		if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) {
 | 
						|
			ret = errno;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) {
 | 
						|
			if (ferror(tp)) {
 | 
						|
				ret = errno;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			ret = EAGAIN;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		cp = buf + sizeof(buf);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret || nlines > 0)
 | 
						|
		goto out3;
 | 
						|
 | 
						|
	if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) {
 | 
						|
		ret = errno;
 | 
						|
		goto out3;
 | 
						|
	}
 | 
						|
 | 
						|
	if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) ==
 | 
						|
	    (off_t)-1) {
 | 
						|
		ret = errno;
 | 
						|
		goto out3;
 | 
						|
	}
 | 
						|
 | 
						|
	for(;;) {
 | 
						|
		if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) {
 | 
						|
			if (ferror(fp))
 | 
						|
				ret = errno;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) {
 | 
						|
			ret = errno;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	fflush(fp);
 | 
						|
	if((off = ftello(fp)) > 0)
 | 
						|
		(void)ftruncate(fileno(fp), off);
 | 
						|
out3:
 | 
						|
	fclose(tp);
 | 
						|
out2:
 | 
						|
	unlink(template);
 | 
						|
out1:
 | 
						|
	fclose(fp);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * read history from a file given
 | 
						|
 */
 | 
						|
int
 | 
						|
read_history(const char *filename)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
	if (filename == NULL && (filename = _default_history_file()) == NULL)
 | 
						|
		return errno;
 | 
						|
	return history(h, &ev, H_LOAD, filename) == -1 ?
 | 
						|
	    (errno ? errno : EINVAL) : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * write history to a file given
 | 
						|
 */
 | 
						|
int
 | 
						|
write_history(const char *filename)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
	if (filename == NULL && (filename = _default_history_file()) == NULL)
 | 
						|
		return errno;
 | 
						|
	return history(h, &ev, H_SAVE, filename) == -1 ?
 | 
						|
	    (errno ? errno : EINVAL) : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns history ``num''th event
 | 
						|
 *
 | 
						|
 * returned pointer points to static variable
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
history_get(int num)
 | 
						|
{
 | 
						|
	static HIST_ENTRY she;
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	/* save current position */
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return NULL;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	/* start from the oldest */
 | 
						|
	if (history(h, &ev, H_LAST) != 0)
 | 
						|
		return NULL;	/* error */
 | 
						|
 | 
						|
	/* look forwards for event matching specified offset */
 | 
						|
	if (history(h, &ev, H_NEXT_EVDATA, num, &she.data))
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	she.line = ev.str;
 | 
						|
 | 
						|
	/* restore pointer to where it was */
 | 
						|
	(void)history(h, &ev, H_SET, curr_num);
 | 
						|
 | 
						|
	return &she;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * add the line to history table
 | 
						|
 */
 | 
						|
int
 | 
						|
add_history(const char *line)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	if (line == NULL)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	(void)history(h, &ev, H_ENTER, line);
 | 
						|
	if (history(h, &ev, H_GETSIZE) == 0)
 | 
						|
		history_length = ev.num;
 | 
						|
 | 
						|
	return !(history_length > 0); /* return 0 if all is okay */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * remove the specified entry from the history list and return it.
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
remove_history(int num)
 | 
						|
{
 | 
						|
	HIST_ENTRY *he;
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if ((he = el_malloc(sizeof(*he))) == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (history(h, &ev, H_DELDATA, num, &he->data) != 0) {
 | 
						|
		el_free(he);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	he->line = ev.str;
 | 
						|
	if (history(h, &ev, H_GETSIZE) == 0)
 | 
						|
		history_length = ev.num;
 | 
						|
 | 
						|
	return he;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * replace the line and data of the num-th entry
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
replace_history_entry(int num, const char *line, histdata_t data)
 | 
						|
{
 | 
						|
	HIST_ENTRY *he;
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	/* save current position */
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return NULL;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	/* start from the oldest */
 | 
						|
	if (history(h, &ev, H_LAST) != 0)
 | 
						|
		return NULL;	/* error */
 | 
						|
 | 
						|
	if ((he = el_malloc(sizeof(*he))) == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	/* look forwards for event matching specified offset */
 | 
						|
	if (history(h, &ev, H_NEXT_EVDATA, num, &he->data))
 | 
						|
		goto out;
 | 
						|
 | 
						|
	he->line = strdup(ev.str);
 | 
						|
	if (he->line == NULL)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	if (history(h, &ev, H_REPLACE, line, data))
 | 
						|
		goto out;
 | 
						|
 | 
						|
	/* restore pointer to where it was */
 | 
						|
	if (history(h, &ev, H_SET, curr_num))
 | 
						|
		goto out;
 | 
						|
 | 
						|
	return he;
 | 
						|
out:
 | 
						|
	el_free(he);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * clear the history list - delete all entries
 | 
						|
 */
 | 
						|
void
 | 
						|
clear_history(void)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	(void)history(h, &ev, H_CLEAR);
 | 
						|
	history_length = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns offset of the current history event
 | 
						|
 */
 | 
						|
int
 | 
						|
where_history(void)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num, off;
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return 0;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	(void)history(h, &ev, H_FIRST);
 | 
						|
	off = 1;
 | 
						|
	while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
 | 
						|
		off++;
 | 
						|
 | 
						|
	return off;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns current history event or NULL if there is no such event
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
current_history(void)
 | 
						|
{
 | 
						|
 | 
						|
	return _move_history(H_CURR);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns total number of bytes history events' data are using
 | 
						|
 */
 | 
						|
int
 | 
						|
history_total_bytes(void)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num;
 | 
						|
	size_t size;
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return -1;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	(void)history(h, &ev, H_FIRST);
 | 
						|
	size = 0;
 | 
						|
	do
 | 
						|
		size += strlen(ev.str) * sizeof(*ev.str);
 | 
						|
	while (history(h, &ev, H_NEXT) == 0);
 | 
						|
 | 
						|
	/* get to the same position as before */
 | 
						|
	history(h, &ev, H_PREV_EVENT, curr_num);
 | 
						|
 | 
						|
	return (int)size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * sets the position in the history list to ``pos''
 | 
						|
 */
 | 
						|
int
 | 
						|
history_set_pos(int pos)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num;
 | 
						|
 | 
						|
	if (pos >= history_length || pos < 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	(void)history(h, &ev, H_CURR);
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * use H_DELDATA to set to nth history (without delete) by passing
 | 
						|
	 * (void **)-1
 | 
						|
	 */
 | 
						|
	if (history(h, &ev, H_DELDATA, pos, (void **)-1)) {
 | 
						|
		(void)history(h, &ev, H_SET, curr_num);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns previous event in history and shifts pointer accordingly
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
previous_history(void)
 | 
						|
{
 | 
						|
 | 
						|
	return _move_history(H_PREV);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * returns next event in history and shifts pointer accordingly
 | 
						|
 */
 | 
						|
HIST_ENTRY *
 | 
						|
next_history(void)
 | 
						|
{
 | 
						|
 | 
						|
	return _move_history(H_NEXT);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * searches for first history event containing the str
 | 
						|
 */
 | 
						|
int
 | 
						|
history_search(const char *str, int direction)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	const char *strp;
 | 
						|
	int curr_num;
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return -1;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if ((strp = strstr(ev.str, str)) != NULL)
 | 
						|
			return (int)(strp - ev.str);
 | 
						|
		if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	(void)history(h, &ev, H_SET, curr_num);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * searches for first history event beginning with str
 | 
						|
 */
 | 
						|
int
 | 
						|
history_search_prefix(const char *str, int direction)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
 | 
						|
	return (history(h, &ev, direction < 0 ?
 | 
						|
	    H_PREV_STR : H_NEXT_STR, str));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * search for event in history containing str, starting at offset
 | 
						|
 * abs(pos); continue backward, if pos<0, forward otherwise
 | 
						|
 */
 | 
						|
/* ARGSUSED */
 | 
						|
int
 | 
						|
history_search_pos(const char *str,
 | 
						|
		   int direction __attribute__((__unused__)), int pos)
 | 
						|
{
 | 
						|
	HistEvent ev;
 | 
						|
	int curr_num, off;
 | 
						|
 | 
						|
	off = (pos > 0) ? pos : -pos;
 | 
						|
	pos = (pos > 0) ? 1 : -1;
 | 
						|
 | 
						|
	if (history(h, &ev, H_CURR) != 0)
 | 
						|
		return -1;
 | 
						|
	curr_num = ev.num;
 | 
						|
 | 
						|
	if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if (strstr(ev.str, str))
 | 
						|
			return off;
 | 
						|
		if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	/* set "current" pointer back to previous state */
 | 
						|
	(void)history(h, &ev,
 | 
						|
	    pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/********************************/
 | 
						|
/* completion functions */
 | 
						|
 | 
						|
char *
 | 
						|
tilde_expand(char *name)
 | 
						|
{
 | 
						|
	return fn_tilde_expand(name);
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
filename_completion_function(const char *name, int state)
 | 
						|
{
 | 
						|
	return fn_filename_completion_function(name, state);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * a completion generator for usernames; returns _first_ username
 | 
						|
 * which starts with supplied text
 | 
						|
 * text contains a partial username preceded by random character
 | 
						|
 * (usually '~'); state resets search from start (??? should we do that anyway)
 | 
						|
 * it's callers responsibility to free returned value
 | 
						|
 */
 | 
						|
char *
 | 
						|
username_completion_function(const char *text, int state)
 | 
						|
{
 | 
						|
#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
 | 
						|
	struct passwd pwres;
 | 
						|
	char pwbuf[1024];
 | 
						|
#endif
 | 
						|
	struct passwd *pass = NULL;
 | 
						|
 | 
						|
	if (text[0] == '\0')
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (*text == '~')
 | 
						|
		text++;
 | 
						|
 | 
						|
	if (state == 0)
 | 
						|
		setpwent();
 | 
						|
 | 
						|
	while (
 | 
						|
#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
 | 
						|
	    getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pass) == 0 && pass != NULL
 | 
						|
#else
 | 
						|
	    (pass = getpwent()) != NULL
 | 
						|
#endif
 | 
						|
	    && text[0] == pass->pw_name[0]
 | 
						|
	    && strcmp(text, pass->pw_name) == 0)
 | 
						|
		continue;
 | 
						|
 | 
						|
	if (pass == NULL) {
 | 
						|
		endpwent();
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	return strdup(pass->pw_name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * el-compatible wrapper to send TSTP on ^Z
 | 
						|
 */
 | 
						|
/* ARGSUSED */
 | 
						|
static unsigned char
 | 
						|
_el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	(void)kill(0, SIGTSTP);
 | 
						|
	return CC_NORM;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Display list of strings in columnar format on readline's output stream.
 | 
						|
 * 'matches' is list of strings, 'len' is number of strings in 'matches',
 | 
						|
 * 'max' is maximum length of string in 'matches'.
 | 
						|
 */
 | 
						|
void
 | 
						|
rl_display_match_list(char **matches, int len, int max)
 | 
						|
{
 | 
						|
 | 
						|
	fn_display_match_list(e, matches, (size_t)len, (size_t)max);
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
/*ARGSUSED*/
 | 
						|
_rl_completion_append_character_function(const char *dummy
 | 
						|
    __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	static char buf[2];
 | 
						|
	buf[0] = (char)rl_completion_append_character;
 | 
						|
	buf[1] = '\0';
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * complete word at current point
 | 
						|
 */
 | 
						|
/* ARGSUSED */
 | 
						|
int
 | 
						|
rl_complete(int ignore __attribute__((__unused__)), int invoking_key)
 | 
						|
{
 | 
						|
#ifdef WIDECHAR
 | 
						|
	static ct_buffer_t wbreak_conv, sprefix_conv;
 | 
						|
#endif
 | 
						|
	char *breakchars;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if (rl_inhibit_completion) {
 | 
						|
		char arr[2];
 | 
						|
		arr[0] = (char)invoking_key;
 | 
						|
		arr[1] = '\0';
 | 
						|
		el_insertstr(e, arr);
 | 
						|
		return CC_REFRESH;
 | 
						|
	}
 | 
						|
 | 
						|
	if (rl_completion_word_break_hook != NULL)
 | 
						|
		breakchars = (*rl_completion_word_break_hook)();
 | 
						|
	else
 | 
						|
		breakchars = rl_basic_word_break_characters;
 | 
						|
 | 
						|
	/* Just look at how many global variables modify this operation! */
 | 
						|
	return fn_complete(e,
 | 
						|
	    (CPFunction *)rl_completion_entry_function,
 | 
						|
	    rl_attempted_completion_function,
 | 
						|
	    ct_decode_string(rl_basic_word_break_characters, &wbreak_conv),
 | 
						|
	    ct_decode_string(breakchars, &sprefix_conv),
 | 
						|
	    _rl_completion_append_character_function,
 | 
						|
	    (size_t)rl_completion_query_items,
 | 
						|
	    &rl_completion_type, &rl_attempted_completion_over,
 | 
						|
	    &rl_point, &rl_end);
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ARGSUSED */
 | 
						|
static unsigned char
 | 
						|
_el_rl_complete(EditLine *el __attribute__((__unused__)), int ch)
 | 
						|
{
 | 
						|
	return (unsigned char)rl_complete(0, ch);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * misc other functions
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * bind key c to readline-type function func
 | 
						|
 */
 | 
						|
int
 | 
						|
rl_bind_key(int c, rl_command_func_t *func)
 | 
						|
{
 | 
						|
	int retval = -1;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if (func == rl_insert) {
 | 
						|
		/* XXX notice there is no range checking of ``c'' */
 | 
						|
		e->el_map.key[c] = ED_INSERT;
 | 
						|
		retval = 0;
 | 
						|
	}
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * read one key from input - handles chars pushed back
 | 
						|
 * to input stream also
 | 
						|
 */
 | 
						|
int
 | 
						|
rl_read_key(void)
 | 
						|
{
 | 
						|
	char fooarr[2 * sizeof(int)];
 | 
						|
 | 
						|
	if (e == NULL || h == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	return el_getc(e, fooarr);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * reset the terminal
 | 
						|
 */
 | 
						|
/* ARGSUSED */
 | 
						|
void
 | 
						|
rl_reset_terminal(const char *p __attribute__((__unused__)))
 | 
						|
{
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
	el_reset(e);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * insert character ``c'' back into input stream, ``count'' times
 | 
						|
 */
 | 
						|
int
 | 
						|
rl_insert(int count, int c)
 | 
						|
{
 | 
						|
	char arr[2];
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	/* XXX - int -> char conversion can lose on multichars */
 | 
						|
	arr[0] = (char)c;
 | 
						|
	arr[1] = '\0';
 | 
						|
 | 
						|
	for (; count > 0; count--)
 | 
						|
		el_push(e, arr);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_insert_text(const char *text)
 | 
						|
{
 | 
						|
	if (!text || *text == 0)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (h == NULL || e == NULL)
 | 
						|
		rl_initialize();
 | 
						|
 | 
						|
	if (el_insertstr(e, text) < 0)
 | 
						|
		return 0;
 | 
						|
	return (int)strlen(text);
 | 
						|
}
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
int
 | 
						|
rl_newline(int count __attribute__((__unused__)),
 | 
						|
    int c __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Readline-4.0 appears to ignore the args.
 | 
						|
	 */
 | 
						|
	return rl_insert(1, '\n');
 | 
						|
}
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
static unsigned char
 | 
						|
rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c)
 | 
						|
{
 | 
						|
	if (map[c] == NULL)
 | 
						|
	    return CC_ERROR;
 | 
						|
 | 
						|
	_rl_update_pos();
 | 
						|
 | 
						|
	(*map[c])(NULL, c);
 | 
						|
 | 
						|
	/* If rl_done was set by the above call, deal with it here */
 | 
						|
	if (rl_done)
 | 
						|
		return CC_EOF;
 | 
						|
 | 
						|
	return CC_NORM;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_add_defun(const char *name, Function *fun, int c)
 | 
						|
{
 | 
						|
	char dest[8];
 | 
						|
	if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0)
 | 
						|
		return -1;
 | 
						|
	map[(unsigned char)c] = fun;
 | 
						|
	el_set(e, EL_ADDFN, name, name, rl_bind_wrapper);
 | 
						|
	vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0);
 | 
						|
	el_set(e, EL_BIND, dest, name, NULL);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_callback_read_char(void)
 | 
						|
{
 | 
						|
	int count = 0, done = 0;
 | 
						|
	const char *buf = el_gets(e, &count);
 | 
						|
	char *wbuf;
 | 
						|
 | 
						|
	if (buf == NULL || count-- <= 0)
 | 
						|
		return;
 | 
						|
	if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF])
 | 
						|
		done = 1;
 | 
						|
	if (buf[count] == '\n' || buf[count] == '\r')
 | 
						|
		done = 2;
 | 
						|
 | 
						|
	if (done && rl_linefunc != NULL) {
 | 
						|
		el_set(e, EL_UNBUFFERED, 0);
 | 
						|
		if (done == 2) {
 | 
						|
		    if ((wbuf = strdup(buf)) != NULL)
 | 
						|
			wbuf[count] = '\0';
 | 
						|
		} else
 | 
						|
			wbuf = NULL;
 | 
						|
		(*(void (*)(const char *))rl_linefunc)(wbuf);
 | 
						|
		//el_set(e, EL_UNBUFFERED, 1);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void 
 | 
						|
rl_callback_handler_install(const char *prompt, VCPFunction *linefunc)
 | 
						|
{
 | 
						|
	if (e == NULL) {
 | 
						|
		rl_initialize();
 | 
						|
	}
 | 
						|
	(void)rl_set_prompt(prompt);
 | 
						|
	rl_linefunc = linefunc;
 | 
						|
	el_set(e, EL_UNBUFFERED, 1);
 | 
						|
}   
 | 
						|
 | 
						|
void 
 | 
						|
rl_callback_handler_remove(void)
 | 
						|
{
 | 
						|
	el_set(e, EL_UNBUFFERED, 0);
 | 
						|
	rl_linefunc = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_redisplay(void)
 | 
						|
{
 | 
						|
	char a[2];
 | 
						|
	a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT];
 | 
						|
	a[1] = '\0';
 | 
						|
	el_push(e, a);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_get_previous_history(int count, int key)
 | 
						|
{
 | 
						|
	char a[2];
 | 
						|
	a[0] = (char)key;
 | 
						|
	a[1] = '\0';
 | 
						|
	while (count--)
 | 
						|
		el_push(e, a);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
/*ARGSUSED*/
 | 
						|
rl_prep_terminal(int meta_flag __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	el_set(e, EL_PREP_TERM, 1);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_deprep_terminal(void)
 | 
						|
{
 | 
						|
	el_set(e, EL_PREP_TERM, 0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_read_init_file(const char *s)
 | 
						|
{
 | 
						|
	return el_source(e, s);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_parse_and_bind(const char *line)
 | 
						|
{
 | 
						|
	const char **argv;
 | 
						|
	int argc;
 | 
						|
	Tokenizer *tok;
 | 
						|
 | 
						|
	tok = tok_init(NULL);
 | 
						|
	tok_str(tok, line, &argc, &argv);
 | 
						|
	argc = el_parse(e, argc, argv);
 | 
						|
	tok_end(tok);
 | 
						|
	return argc ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_variable_bind(const char *var, const char *value)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * The proper return value is undocument, but this is what the
 | 
						|
	 * readline source seems to do.
 | 
						|
	 */
 | 
						|
	return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_stuff_char(int c)
 | 
						|
{
 | 
						|
	char buf[2];
 | 
						|
 | 
						|
	buf[0] = (char)c;
 | 
						|
	buf[1] = '\0';
 | 
						|
	el_insertstr(e, buf);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
_rl_event_read_char(EditLine *el, char *cp)
 | 
						|
{
 | 
						|
	int	n;
 | 
						|
	ssize_t num_read = 0;
 | 
						|
 | 
						|
	*cp = '\0';
 | 
						|
	while (rl_event_hook) {
 | 
						|
 | 
						|
		(*rl_event_hook)();
 | 
						|
 | 
						|
#if defined(FIONREAD)
 | 
						|
		if (ioctl(el->el_infd, FIONREAD, &n) < 0)
 | 
						|
			return -1;
 | 
						|
		if (n)
 | 
						|
			num_read = read(el->el_infd, cp, (size_t)1);
 | 
						|
		else
 | 
						|
			num_read = 0;
 | 
						|
#elif defined(F_SETFL) && defined(O_NDELAY)
 | 
						|
		if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0)
 | 
						|
			return -1;
 | 
						|
		if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0)
 | 
						|
			return -1;
 | 
						|
		num_read = read(el->el_infd, cp, 1);
 | 
						|
		if (fcntl(el->el_infd, F_SETFL, n))
 | 
						|
			return -1;
 | 
						|
#else
 | 
						|
		/* not non-blocking, but what you gonna do? */
 | 
						|
		num_read = read(el->el_infd, cp, 1);
 | 
						|
		return -1;
 | 
						|
#endif
 | 
						|
 | 
						|
		if (num_read < 0 && errno == EAGAIN)
 | 
						|
			continue;
 | 
						|
		if (num_read == 0)
 | 
						|
			continue;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (!rl_event_hook)
 | 
						|
		el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN);
 | 
						|
	return (int)num_read;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
_rl_update_pos(void)
 | 
						|
{
 | 
						|
	const LineInfo *li = el_line(e);
 | 
						|
 | 
						|
	rl_point = (int)(li->cursor - li->buffer);
 | 
						|
	rl_end = (int)(li->lastchar - li->buffer);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_get_screen_size(int *rows, int *cols)
 | 
						|
{
 | 
						|
	if (rows)
 | 
						|
		el_get(e, EL_GETTC, "li", rows, NULL);
 | 
						|
	if (cols)
 | 
						|
		el_get(e, EL_GETTC, "co", cols, NULL);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_set_screen_size(int rows, int cols)
 | 
						|
{
 | 
						|
	char buf[64];
 | 
						|
	(void)snprintf(buf, sizeof(buf), "%d", rows);
 | 
						|
	el_set(e, EL_SETTC, "li", buf, NULL);
 | 
						|
	(void)snprintf(buf, sizeof(buf), "%d", cols);
 | 
						|
	el_set(e, EL_SETTC, "co", buf, NULL);
 | 
						|
}
 | 
						|
 | 
						|
char **
 | 
						|
rl_completion_matches(const char *str, rl_compentry_func_t *fun)
 | 
						|
{
 | 
						|
	size_t len, max, i, j, min;
 | 
						|
	char **list, *match, *a, *b;
 | 
						|
 | 
						|
	len = 1;
 | 
						|
	max = 10;
 | 
						|
	if ((list = el_malloc(max * sizeof(*list))) == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	while ((match = (*fun)(str, (int)(len - 1))) != NULL) {
 | 
						|
		list[len++] = match;
 | 
						|
		if (len == max) {
 | 
						|
			char **nl;
 | 
						|
			max += 10;
 | 
						|
			if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL)
 | 
						|
				goto out;
 | 
						|
			list = nl;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (len == 1)
 | 
						|
		goto out;
 | 
						|
	list[len] = NULL;
 | 
						|
	if (len == 2) {
 | 
						|
		if ((list[0] = strdup(list[1])) == NULL)
 | 
						|
			goto out;
 | 
						|
		return list;
 | 
						|
	}
 | 
						|
	qsort(&list[1], len - 1, sizeof(*list),
 | 
						|
	    (int (*)(const void *, const void *)) strcmp);
 | 
						|
	min = SIZE_T_MAX;
 | 
						|
	for (i = 1, a = list[i]; i < len - 1; i++, a = b) {
 | 
						|
		b = list[i + 1];
 | 
						|
		for (j = 0; a[j] && a[j] == b[j]; j++)
 | 
						|
			continue;
 | 
						|
		if (min > j)
 | 
						|
			min = j;
 | 
						|
	}
 | 
						|
	if (min == 0 && *str) {
 | 
						|
		if ((list[0] = strdup(str)) == NULL)
 | 
						|
			goto out;
 | 
						|
	} else {
 | 
						|
		if ((list[0] = el_malloc((min + 1) * sizeof(*list[0]))) == NULL)
 | 
						|
			goto out;
 | 
						|
		(void)memcpy(list[0], list[1], min);
 | 
						|
		list[0][min] = '\0';
 | 
						|
	}
 | 
						|
	return list;
 | 
						|
		
 | 
						|
out:
 | 
						|
	el_free(list);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
rl_filename_completion_function (const char *text, int state)
 | 
						|
{
 | 
						|
	return fn_filename_completion_function(text, state);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_forced_update_display(void)
 | 
						|
{
 | 
						|
	el_set(e, EL_REFRESH);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_rl_abort_internal(void)
 | 
						|
{
 | 
						|
	el_beep(e);
 | 
						|
	longjmp(topbuf, 1);
 | 
						|
	/*NOTREACHED*/
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_rl_qsort_string_compare(char **s1, char **s2)
 | 
						|
{
 | 
						|
	return strcoll(*s1, *s2);
 | 
						|
}
 | 
						|
 | 
						|
HISTORY_STATE *
 | 
						|
history_get_history_state(void)
 | 
						|
{
 | 
						|
	HISTORY_STATE *hs;
 | 
						|
 | 
						|
	if ((hs = el_malloc(sizeof(*hs))) == NULL)
 | 
						|
		return NULL;
 | 
						|
	hs->length = history_length;
 | 
						|
	return hs;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
/*ARGSUSED*/
 | 
						|
rl_kill_text(int from __attribute__((__unused__)),
 | 
						|
    int to __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
Keymap
 | 
						|
rl_make_bare_keymap(void)
 | 
						|
{
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
Keymap
 | 
						|
rl_get_keymap(void)
 | 
						|
{
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
/*ARGSUSED*/
 | 
						|
rl_set_keymap(Keymap k __attribute__((__unused__)))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
/*ARGSUSED*/
 | 
						|
rl_generic_bind(int type __attribute__((__unused__)),
 | 
						|
    const char * keyseq __attribute__((__unused__)),
 | 
						|
    const char * data __attribute__((__unused__)),
 | 
						|
    Keymap k __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
/*ARGSUSED*/
 | 
						|
rl_bind_key_in_map(int key __attribute__((__unused__)),
 | 
						|
    rl_command_func_t *fun __attribute__((__unused__)),
 | 
						|
    Keymap k __attribute__((__unused__)))
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* unsupported, but needed by python */
 | 
						|
void
 | 
						|
rl_cleanup_after_signal(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rl_on_new_line(void)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rl_free_line_state(void)
 | 
						|
{
 | 
						|
}
 |