📄 readline.c
字号:
/* $NetBSD: readline.c,v 1.21 2002/03/18 16:20:36 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE 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.21 2002/03/18 16:20:36 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>#ifdef SOLARIS#include <alloca.h>#endif#include "histedit.h"#include "readline/readline.h"#include "el.h"#include "fcns.h" /* for EL_NUM_FCNS *//* 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";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;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;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;CPFunction *rl_completion_entry_function = NULL;CPPFunction *rl_attempted_completion_function = 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 = (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 EditLine *e = NULL;static int el_rl_complete_cmdnum = 0;/* internal functions */static unsigned char _el_rl_complete(EditLine *, int);static char *_get_prompt(EditLine *);static HIST_ENTRY *_move_history(int);static int _history_search_gen(const char *, int, int);static int _history_expand_command(const char *, size_t, char **);static char *_rl_compat_sub(const char *, const char *, const char *, int);static int rl_complete_internal(int);static int _rl_qsort_string_compare(const void *, const void *);/* * needed for prompt switching in readline() */static char *el_rl_prompt = NULL;/* ARGSUSED */static char *_get_prompt(EditLine *el){ return (el_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 (HIST_ENTRY *) NULL; rl_he.line = ev.str; rl_he.data = ""; return (&rl_he);}/* * READLINE compatibility stuff *//* * initialize rl compat stuff */intrl_initialize(void){ HistEvent ev; const LineInfo *li; int i; 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); /* for proper prompt printing in readline() */ el_rl_prompt = strdup(""); el_set(e, EL_PROMPT, _get_prompt); el_set(e, EL_SIGNAL, 1); /* set default mode to "emacs"-style and read setting afterwards */ /* so this can be overriden */ el_set(e, EL_EDITOR, "emacs"); /* * Word completition - this has to go AFTER rebinding keys * to emacs-style. */ el_set(e, EL_ADDFN, "rl_complete", "ReadLine compatible completition function", _el_rl_complete); 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 */ el_source(e, NULL); /* * Unfortunately, some applications really do use rl_point * and rl_line_buffer directly. */ li = el_line(e); /* a cheesy way to get rid of const cast. */ rl_line_buffer = memchr(li->buffer, *li->buffer, 1); rl_point = rl_end = 0; return (0);}/* * read one line from input stream and return it, chomping * trailing newline (if there is any) */char *readline(const char *prompt){ HistEvent ev; int count; const char *ret; char *buf; if (e == NULL || h == NULL) rl_initialize(); /* update prompt accordingly to what has been passed */ if (!prompt) prompt = ""; if (strcmp(el_rl_prompt, prompt) != 0) { free(el_rl_prompt); el_rl_prompt = strdup(prompt); } /* get one line from input stream */ ret = el_gets(e, &count); if (ret && count > 0) { int lastidx; buf = strdup(ret); 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 */voidusing_history(void){ if (h == NULL || e == NULL) rl_initialize();}/* * substitute ``what'' with ``with'', returning resulting string; if * globally == 1, substitutes all occurences of what, otherwise only the * first one */static char *_rl_compat_sub(const char *str, const char *what, const char *with, int globally){ char *result; const char *temp, *new; int len, with_len, what_len, add; size_t size, i; result = malloc((size = 16)); temp = str; with_len = strlen(with); what_len = strlen(what); len = 0; do { new = strstr(temp, what); if (new) { i = new - temp; add = i + with_len; if (i + add + 1 >= size) { size += add + 1; result = realloc(result, size); } (void) strncpy(&result[len], temp, i); len += i; (void) strcpy(&result[len], with); /* safe */ len += with_len; temp = new + what_len; } else { add = strlen(temp); if (len + add + 1 >= size) { size += add + 1; result = realloc(result, size); } (void) strcpy(&result[len], temp); /* safe */ len += add; temp = NULL; } } while (temp && globally); result[len] = '\0'; return (result);}/* * 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 * word designator ``%'' isn't supported (yet ?) * * 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 cmdlen, char **result){ char **arr, *tempcmd, *line, *search = NULL, *cmd; const char *event_data = NULL; static char *from = NULL, *to = NULL; int start = -1, end = -1, max, i, idx; int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, g_on = 0; int event_num = 0, retval; size_t cmdsize; *result = NULL; cmd = alloca(cmdlen + 1); (void) strncpy(cmd, command, cmdlen); cmd[cmdlen] = 0; idx = 1; /* find out which event to take */ if (cmd[idx] == history_expansion_char) { event_num = history_length; idx++; } else { int off, num; size_t len; off = idx; while (cmd[off] && !strchr(":^$*-%", cmd[off])) off++; num = atoi(&cmd[idx]); if (num != 0) { event_num = num; if (num < 0) event_num += history_length + 1; } else { int prefix = 1, curr_num; HistEvent ev; len = off - idx; if (cmd[idx] == '?') { idx++, len--; if (cmd[off - 1] == '?') len--; else if (cmd[off] != '\n' && cmd[off] != '\0') return (-1); prefix = 0; } search = alloca(len + 1); (void) strncpy(search, &cmd[idx], len); search[len] = '\0'; if (history(h, &ev, H_CURR) != 0) return (-1); curr_num = ev.num; if (prefix) retval = history_search_prefix(search, -1); else retval = history_search(search, -1); if (retval == -1) { fprintf(rl_outstream, "%s: Event not found\n", search); return (-1); } if (history(h, &ev, H_CURR) != 0) return (-1); event_data = ev.str; /* roll back to original position */ history(h, &ev, H_NEXT_EVENT, curr_num); } idx = off; } if (!event_data && event_num >= 0) { HIST_ENTRY *rl_he; rl_he = history_get(event_num); if (!rl_he) return (0); event_data = rl_he->line; } else return (-1); if (cmd[idx] != ':') return (-1); cmd += idx + 1; /* recognize cmd */ if (*cmd == '^') start = end = 1, cmd++; else if (*cmd == '$') start = end = -1, cmd++; else if (*cmd == '*') start = 1, end = -1, cmd++; else if (isdigit((unsigned char) *cmd)) { const char *temp; int shifted = 0; start = atoi(cmd); temp = cmd; for (; isdigit((unsigned char) *cmd); cmd++); if (temp != cmd) shifted = 1; if (shifted && *cmd == '-') { if (!isdigit((unsigned char) *(cmd + 1))) end = -2; else { end = atoi(cmd + 1); for (; isdigit((unsigned char) *cmd); cmd++); } } else if (shifted && *cmd == '*') end = -1, cmd++; else if (shifted) end = start; } if (*cmd == ':') cmd++; line = strdup(event_data); for (; *cmd; cmd++) { if (*cmd == ':') continue; else if (*cmd == 'h') h_on = 1 | g_on, g_on = 0; else if (*cmd == 't') t_on = 1 | g_on, g_on = 0; else if (*cmd == 'r') r_on = 1 | g_on, g_on = 0; else if (*cmd == 'e') e_on = 1 | g_on, g_on = 0; else if (*cmd == 'p') p_on = 1 | g_on, g_on = 0; else if (*cmd == 'g') g_on = 2; else if (*cmd == 's' || *cmd == '&') { char *what, *with, delim; int len, from_len; size_t size; if (*cmd == '&' && (from == NULL || to == NULL)) continue; else if (*cmd == 's') { delim = *(++cmd), cmd++; size = 16; what = realloc(from, size); len = 0; for (; *cmd && *cmd != delim; cmd++) { if (*cmd == '\\' && *(cmd + 1) == delim) cmd++; if (len >= size) what = realloc(what, (size <<= 1)); what[len++] = *cmd; } what[len] = '\0'; from = what; if (*what == '\0') { free(what); if (search) from = strdup(search); else { from = NULL; return (-1); } } cmd++; /* shift after delim */ if (!*cmd)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -