version 1.11, 2000/09/04 22:06:31 |
version 1.22, 2002/04/09 01:57:34 |
|
|
* POSSIBILITY OF SUCH DAMAGE. |
* POSSIBILITY OF SUCH DAMAGE. |
*/ |
*/ |
|
|
#include <sys/cdefs.h> |
#include "config.h" |
#if !defined(lint) && !defined(SCCSID) |
#if !defined(lint) && !defined(SCCSID) |
__RCSID("$NetBSD$"); |
__RCSID("$NetBSD$"); |
#endif /* not lint && not SCCSID */ |
#endif /* not lint && not SCCSID */ |
Line 52 __RCSID("$NetBSD$"); |
|
Line 52 __RCSID("$NetBSD$"); |
|
#include <unistd.h> |
#include <unistd.h> |
#include <limits.h> |
#include <limits.h> |
#include "histedit.h" |
#include "histedit.h" |
#include "readline.h" |
#include "readline/readline.h" |
#include "sys.h" |
|
#include "el.h" |
#include "el.h" |
|
#include "fcns.h" /* for EL_NUM_FCNS */ |
|
|
/* for rl_complete() */ |
/* for rl_complete() */ |
#define TAB '\r' |
#define TAB '\r' |
Line 65 __RCSID("$NetBSD$"); |
|
Line 65 __RCSID("$NetBSD$"); |
|
/* readline compatibility stuff - look at readline sources/documentation */ |
/* readline compatibility stuff - look at readline sources/documentation */ |
/* to see what these variables mean */ |
/* to see what these variables mean */ |
const char *rl_library_version = "EditLine wrapper"; |
const char *rl_library_version = "EditLine wrapper"; |
char *rl_readline_name = ""; |
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_instream = NULL; |
FILE *rl_outstream = NULL; |
FILE *rl_outstream = NULL; |
int rl_point = 0; |
int rl_point = 0; |
Line 77 int history_length = 0; |
|
Line 81 int history_length = 0; |
|
int max_input_history = 0; |
int max_input_history = 0; |
char history_expansion_char = '!'; |
char history_expansion_char = '!'; |
char history_subst_char = '^'; |
char history_subst_char = '^'; |
char *history_no_expand_chars = " \t\n=("; |
char *history_no_expand_chars = expand_chars; |
Function *history_inhibit_expansion_function = NULL; |
Function *history_inhibit_expansion_function = NULL; |
|
|
int rl_inhibit_completion = 0; |
int rl_inhibit_completion = 0; |
int rl_attempted_completion_over = 0; |
int rl_attempted_completion_over = 0; |
char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; |
char *rl_basic_word_break_characters = break_chars; |
char *rl_completer_word_break_characters = NULL; |
char *rl_completer_word_break_characters = NULL; |
char *rl_completer_quote_characters = NULL; |
char *rl_completer_quote_characters = NULL; |
CPFunction *rl_completion_entry_function = NULL; |
CPFunction *rl_completion_entry_function = NULL; |
CPPFunction *rl_attempted_completion_function = NULL; |
CPPFunction *rl_attempted_completion_function = NULL; |
|
|
/* used for readline emulation */ |
/* |
|
* 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 = (char *)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 */ |
|
|
|
/* if not zero, non-unique completions always show list of possible matches */ |
|
static int _rl_complete_show_all = 0; |
|
|
static History *h = NULL; |
static History *h = NULL; |
static EditLine *e = NULL; |
static EditLine *e = NULL; |
|
static int el_rl_complete_cmdnum = 0; |
|
|
/* internal functions */ |
/* internal functions */ |
static unsigned char _el_rl_complete(EditLine *, int); |
static unsigned char _el_rl_complete(EditLine *, int); |
Line 101 static int _history_expand_command(con |
|
Line 136 static int _history_expand_command(con |
|
static char *_rl_compat_sub(const char *, const char *, |
static char *_rl_compat_sub(const char *, const char *, |
const char *, int); |
const char *, int); |
static int rl_complete_internal(int); |
static int rl_complete_internal(int); |
|
static int _rl_qsort_string_compare(const void *, const void *); |
|
|
/* |
/* |
* needed for prompt switching in readline() |
* needed for prompt switching in readline() |
Line 112 static char *el_rl_prompt = NULL; |
|
Line 148 static char *el_rl_prompt = NULL; |
|
static char * |
static char * |
_get_prompt(EditLine *el) |
_get_prompt(EditLine *el) |
{ |
{ |
|
|
return (el_rl_prompt); |
return (el_rl_prompt); |
} |
} |
|
|
Line 148 rl_initialize(void) |
|
Line 183 rl_initialize(void) |
|
{ |
{ |
HistEvent ev; |
HistEvent ev; |
const LineInfo *li; |
const LineInfo *li; |
|
int i; |
|
int editmode = 1; |
|
struct termios t; |
|
|
if (e != NULL) |
if (e != NULL) |
el_end(e); |
el_end(e); |
Line 158 rl_initialize(void) |
|
Line 196 rl_initialize(void) |
|
rl_instream = stdin; |
rl_instream = stdin; |
if (!rl_outstream) |
if (!rl_outstream) |
rl_outstream = stdout; |
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); |
e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr); |
|
|
|
if (!editmode) |
|
el_set(e, EL_EDITMODE, 0); |
|
|
h = history_init(); |
h = history_init(); |
if (!e || !h) |
if (!e || !h) |
return (-1); |
return (-1); |
Line 178 rl_initialize(void) |
|
Line 226 rl_initialize(void) |
|
/* so this can be overriden */ |
/* so this can be overriden */ |
el_set(e, EL_EDITOR, "emacs"); |
el_set(e, EL_EDITOR, "emacs"); |
|
|
/* for word completition - this has to go AFTER rebinding keys */ |
/* |
/* to emacs-style */ |
* Word completition - this has to go AFTER rebinding keys |
|
* to emacs-style. |
|
*/ |
el_set(e, EL_ADDFN, "rl_complete", |
el_set(e, EL_ADDFN, "rl_complete", |
"ReadLine compatible completition function", |
"ReadLine compatible completition function", |
_el_rl_complete); |
_el_rl_complete); |
el_set(e, EL_BIND, "^I", "rl_complete", NULL); |
el_set(e, EL_BIND, "^I", "rl_complete", NULL); |
|
|
|
/* |
|
* Find out where the rl_complete function was added; this is |
|
* used later to detect that lastcmd was also rl_complete. |
|
*/ |
|
for(i=EL_NUM_FCNS; i < e->el_map.nfunc; i++) { |
|
if (e->el_map.func[i] == _el_rl_complete) { |
|
el_rl_complete_cmdnum = i; |
|
break; |
|
} |
|
} |
|
|
/* read settings from configuration file */ |
/* read settings from configuration file */ |
el_source(e, NULL); |
el_source(e, NULL); |
|
|
/* some readline apps do use this */ |
/* |
|
* Unfortunately, some applications really do use rl_point |
|
* and rl_line_buffer directly. |
|
*/ |
li = el_line(e); |
li = el_line(e); |
/* LINTED const cast */ |
/* a cheesy way to get rid of const cast. */ |
rl_line_buffer = (char *) li->buffer; |
rl_line_buffer = memchr(li->buffer, *li->buffer, 1); |
rl_point = rl_end = 0; |
rl_point = rl_end = 0; |
|
|
return (0); |
return (0); |
Line 208 readline(const char *prompt) |
|
Line 272 readline(const char *prompt) |
|
HistEvent ev; |
HistEvent ev; |
int count; |
int count; |
const char *ret; |
const char *ret; |
|
char *buf; |
|
|
if (e == NULL || h == NULL) |
if (e == NULL || h == NULL) |
rl_initialize(); |
rl_initialize(); |
Line 223 readline(const char *prompt) |
|
Line 288 readline(const char *prompt) |
|
ret = el_gets(e, &count); |
ret = el_gets(e, &count); |
|
|
if (ret && count > 0) { |
if (ret && count > 0) { |
char *foo; |
|
int lastidx; |
int lastidx; |
|
|
foo = strdup(ret); |
buf = strdup(ret); |
lastidx = count - 1; |
lastidx = count - 1; |
if (foo[lastidx] == '\n') |
if (buf[lastidx] == '\n') |
foo[lastidx] = '\0'; |
buf[lastidx] = '\0'; |
|
|
ret = foo; |
|
} else |
} else |
ret = NULL; |
buf = NULL; |
|
|
history(h, &ev, H_GETSIZE); |
history(h, &ev, H_GETSIZE); |
history_length = ev.num; |
history_length = ev.num; |
|
|
/* LINTED const cast */ |
return buf; |
return (char *) ret; |
|
} |
} |
|
|
/* |
/* |
Line 253 readline(const char *prompt) |
|
Line 314 readline(const char *prompt) |
|
void |
void |
using_history(void) |
using_history(void) |
{ |
{ |
|
|
if (h == NULL || e == NULL) |
if (h == NULL || e == NULL) |
rl_initialize(); |
rl_initialize(); |
} |
} |
Line 717 history_tokenize(const char *str) |
|
Line 777 history_tokenize(const char *str) |
|
start = i; |
start = i; |
for (; str[i]; i++) { |
for (; str[i]; i++) { |
if (str[i] == '\\') { |
if (str[i] == '\\') { |
if (str[i] != '\0') |
if (str[i+1] != '\0') |
i++; |
i++; |
} else if (str[i] == delim) |
} else if (str[i] == delim) |
delim = '\0'; |
delim = '\0'; |
Line 1163 filename_completion_function(const char |
|
Line 1223 filename_completion_function(const char |
|
temp = tilde_expand(dirname); |
temp = tilde_expand(dirname); |
dirname = realloc(dirname, strlen(temp) + 1); |
dirname = realloc(dirname, strlen(temp) + 1); |
(void) strcpy(dirname, temp); /* safe */ |
(void) strcpy(dirname, temp); /* safe */ |
free(temp); /* no more needed */ |
free(temp); /* no longer needed */ |
} |
} |
/* will be used in cycle */ |
/* will be used in cycle */ |
filename_len = strlen(filename); |
filename_len = strlen(filename); |
Line 1251 username_completion_function(const char |
|
Line 1311 username_completion_function(const char |
|
static unsigned char |
static unsigned char |
_el_rl_complete(EditLine *el, int ch) |
_el_rl_complete(EditLine *el, int ch) |
{ |
{ |
|
|
return (unsigned char) rl_complete(0, ch); |
return (unsigned char) rl_complete(0, ch); |
} |
} |
|
|
|
|
completion_matches(const char *text, CPFunction *genfunc) |
completion_matches(const char *text, CPFunction *genfunc) |
{ |
{ |
char **match_list = NULL, *retstr, *prevstr; |
char **match_list = NULL, *retstr, *prevstr; |
size_t math_list_len, max_equal, which, i; |
size_t match_list_len, max_equal, which, i; |
int matches; |
int matches; |
|
|
if (h == NULL || e == NULL) |
if (h == NULL || e == NULL) |
rl_initialize(); |
rl_initialize(); |
|
|
matches = 0; |
matches = 0; |
math_list_len = 1; |
match_list_len = 1; |
while ((retstr = (*genfunc) (text, matches)) != NULL) { |
while ((retstr = (*genfunc) (text, matches)) != NULL) { |
if (matches + 1 >= math_list_len) { |
if (matches + 1 >= match_list_len) { |
math_list_len <<= 1; |
match_list_len <<= 1; |
match_list = realloc(match_list, |
match_list = realloc(match_list, |
math_list_len * sizeof(char *)); |
match_list_len * sizeof(char *)); |
} |
} |
match_list[++matches] = retstr; |
match_list[++matches] = retstr; |
} |
} |
Line 1287 completion_matches(const char *text, CPF |
|
Line 1346 completion_matches(const char *text, CPF |
|
which = 2; |
which = 2; |
prevstr = match_list[1]; |
prevstr = match_list[1]; |
max_equal = strlen(prevstr); |
max_equal = strlen(prevstr); |
for (; which < matches; which++) { |
for (; which <= matches; which++) { |
for (i = 0; i < max_equal && |
for (i = 0; i < max_equal && |
prevstr[i] == match_list[which][i]; i++) |
prevstr[i] == match_list[which][i]; i++) |
continue; |
continue; |
Line 1300 completion_matches(const char *text, CPF |
|
Line 1359 completion_matches(const char *text, CPF |
|
match_list[0] = retstr; |
match_list[0] = retstr; |
|
|
/* add NULL as last pointer to the array */ |
/* add NULL as last pointer to the array */ |
if (matches + 1 >= math_list_len) |
if (matches + 1 >= match_list_len) |
match_list = realloc(match_list, |
match_list = realloc(match_list, |
(math_list_len + 1) * sizeof(char *)); |
(match_list_len + 1) * sizeof(char *)); |
match_list[matches + 1] = (char *) NULL; |
match_list[matches + 1] = (char *) NULL; |
|
|
return (match_list); |
return (match_list); |
} |
} |
|
|
|
/* |
|
* Sort function for qsort(). Just wrapper around strcasecmp(). |
|
*/ |
|
static int |
|
_rl_qsort_string_compare(i1, i2) |
|
const void *i1, *i2; |
|
{ |
|
const char *s1 = ((const char * const *)i1)[0]; |
|
const char *s2 = ((const char * const *)i2)[0]; |
|
|
|
return strcasecmp(s1, s2); |
|
} |
|
|
/* |
/* |
* called by rl_complete() |
* 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 (matches, len, max) |
|
char **matches; |
|
int len, max; |
|
{ |
|
int i, idx, limit, count; |
|
int screenwidth = e->el_term.t_size.h; |
|
|
|
/* |
|
* Find out how many entries can be put on one line, count |
|
* with two spaces between strings. |
|
*/ |
|
limit = screenwidth / (max + 2); |
|
if (limit == 0) |
|
limit = 1; |
|
|
|
/* how many lines of output */ |
|
count = len / limit; |
|
if (count * limit < len) |
|
count++; |
|
|
|
/* Sort the items if they are not already sorted. */ |
|
qsort(&matches[1], (size_t)(len - 1), sizeof(char *), |
|
_rl_qsort_string_compare); |
|
|
|
idx = 1; |
|
for(; count > 0; count--) { |
|
for(i=0; i < limit && matches[idx]; i++, idx++) |
|
fprintf(e->el_outfile, "%-*s ", max, matches[idx]); |
|
fprintf(e->el_outfile, "\n"); |
|
} |
|
} |
|
|
|
/* |
|
* Complete the word at or before point, called by rl_complete() |
|
* '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. |
|
* |
|
* Note: '*' support is not implemented |
*/ |
*/ |
/* ARGSUSED */ |
|
static int |
static int |
rl_complete_internal(int what_to_do) |
rl_complete_internal(int what_to_do) |
{ |
{ |
CPFunction *complet_func; |
CPFunction *complet_func; |
const LineInfo *li; |
const LineInfo *li; |
char *temp, *temp2, **arr; |
char *temp, **matches; |
|
const char *ctemp; |
size_t len; |
size_t len; |
|
|
|
rl_completion_type = what_to_do; |
|
|
if (h == NULL || e == NULL) |
if (h == NULL || e == NULL) |
rl_initialize(); |
rl_initialize(); |
|
|
Line 1328 rl_complete_internal(int what_to_do) |
|
Line 1447 rl_complete_internal(int what_to_do) |
|
if (!complet_func) |
if (!complet_func) |
complet_func = filename_completion_function; |
complet_func = filename_completion_function; |
|
|
|
/* We now look backwards for the start of a filename/variable word */ |
li = el_line(e); |
li = el_line(e); |
/* LINTED const cast */ |
ctemp = (const char *) li->cursor; |
temp = (char *) li->cursor; |
while (ctemp > li->buffer |
while (temp > li->buffer && |
&& !strchr(rl_basic_word_break_characters, ctemp[-1]) |
!strchr(rl_basic_word_break_characters, *(temp - 1))) |
&& (!rl_special_prefixes |
temp--; |
|| !strchr(rl_special_prefixes, ctemp[-1]) ) ) |
|
ctemp--; |
len = li->cursor - temp; |
|
temp2 = alloca(len + 1); |
len = li->cursor - ctemp; |
(void) strncpy(temp2, temp, len); |
temp = alloca(len + 1); |
temp = temp2; |
(void) strncpy(temp, ctemp, len); |
temp[len] = '\0'; |
temp[len] = '\0'; |
|
|
/* these can be used by function called in completion_matches() */ |
/* these can be used by function called in completion_matches() */ |
Line 1347 rl_complete_internal(int what_to_do) |
|
Line 1467 rl_complete_internal(int what_to_do) |
|
rl_end = li->lastchar - li->buffer; |
rl_end = li->lastchar - li->buffer; |
|
|
if (!rl_attempted_completion_function) |
if (!rl_attempted_completion_function) |
arr = completion_matches(temp, complet_func); |
matches = completion_matches(temp, complet_func); |
else { |
else { |
int end = li->cursor - li->buffer; |
int end = li->cursor - li->buffer; |
arr = (*rl_attempted_completion_function) (temp, (int) |
matches = (*rl_attempted_completion_function) (temp, (int) |
(end - len), end); |
(end - len), end); |
} |
} |
|
|
if (arr) { |
if (matches) { |
int i; |
int i, retval = CC_REFRESH; |
|
int matches_num, maxlen, match_len, match_display=1; |
|
|
|
/* |
|
* Only replace the completed string with common part of |
|
* possible matches if there is possible completion. |
|
*/ |
|
if (matches[0][0] != '\0') { |
|
el_deletestr(e, (int) len); |
|
el_insertstr(e, matches[0]); |
|
} |
|
|
|
if (what_to_do == '?') |
|
goto display_matches; |
|
|
|
if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { |
|
/* |
|
* We found exact match. Add a space after |
|
* it, unless we do filename completition and the |
|
* object is a directory. |
|
*/ |
|
size_t alen = strlen(matches[0]); |
|
if ((complet_func != filename_completion_function |
|
|| (alen > 0 && (matches[0])[alen - 1] != '/')) |
|
&& rl_completion_append_character) { |
|
char buf[2]; |
|
buf[0] = rl_completion_append_character; |
|
buf[1] = '\0'; |
|
el_insertstr(e, buf); |
|
} |
|
} else if (what_to_do == '!') { |
|
display_matches: |
|
/* |
|
* More than one match and requested to list possible |
|
* matches. |
|
*/ |
|
|
|
for(i=1, maxlen=0; matches[i]; i++) { |
|
match_len = strlen(matches[i]); |
|
if (match_len > maxlen) |
|
maxlen = match_len; |
|
} |
|
matches_num = i - 1; |
|
|
|
/* newline to get on next line from command line */ |
|
fprintf(e->el_outfile, "\n"); |
|
|
|
/* |
|
* If there are too many items, ask user for display |
|
* confirmation. |
|
*/ |
|
if (matches_num > rl_completion_query_items) { |
|
fprintf(e->el_outfile, |
|
"Display all %d possibilities? (y or n) ", |
|
matches_num); |
|
fflush(e->el_outfile); |
|
if (getc(stdin) != 'y') |
|
match_display = 0; |
|
fprintf(e->el_outfile, "\n"); |
|
} |
|
|
el_deletestr(e, (int) len); |
if (match_display) |
el_insertstr(e, arr[0]); |
rl_display_match_list(matches, matches_num, |
if (strcmp(arr[0], arr[1]) == 0) { |
maxlen); |
/* lcd is valid object, so add a space to mark it */ |
retval = CC_REDISPLAY; |
/* in case of filename completition, add a space */ |
} else if (matches[0][0]) { |
/* only if object found is not directory */ |
/* |
size_t alen = strlen(arr[0]); |
* There was some common match, but the name was |
if (complet_func != filename_completion_function |
* not complete enough. Next tab will print possible |
|| (alen > 0 && (arr[0])[alen - 1] != '/')) |
* completions. |
el_insertstr(e, " "); |
*/ |
} else |
el_beep(e); |
|
} else { |
/* lcd is not a valid object - further specification */ |
/* lcd is not a valid object - further specification */ |
/* is needed */ |
/* is needed */ |
el_beep(e); |
el_beep(e); |
|
retval = CC_NORM; |
|
} |
|
|
/* free elements of array and the array itself */ |
/* free elements of array and the array itself */ |
for (i = 0; arr[i]; i++) |
for (i = 0; matches[i]; i++) |
free(arr[i]); |
free(matches[i]); |
free(arr), arr = NULL; |
free(matches), matches = NULL; |
|
|
return (CC_REFRESH); |
return (retval); |
} |
} |
return (CC_NORM); |
return (CC_NORM); |
} |
} |
Line 1395 rl_complete(int ignore, int invoking_key |
|
Line 1577 rl_complete(int ignore, int invoking_key |
|
if (rl_inhibit_completion) { |
if (rl_inhibit_completion) { |
rl_insert(ignore, invoking_key); |
rl_insert(ignore, invoking_key); |
return (CC_REFRESH); |
return (CC_REFRESH); |
} else |
} else if (e->el_state.lastcmd == el_rl_complete_cmdnum) |
return (rl_complete_internal(invoking_key)); |
return rl_complete_internal('?'); |
|
else if (_rl_complete_show_all) |
|
return rl_complete_internal('!'); |
|
else |
|
return (rl_complete_internal(TAB)); |
} |
} |
|
|
|
|