cmdedit: more optimizations

This commit is contained in:
Denis Vlasenko 2007-01-21 19:19:46 +00:00
parent 47bdb3ac48
commit 5592fac308

View File

@ -119,14 +119,6 @@ static struct termios initial_settings, new_settings;
static static
volatile unsigned cmdedit_termw = 80; /* actual terminal width */ volatile unsigned cmdedit_termw = 80; /* actual terminal width */
static
volatile int handlers_sets = 0; /* Set next bits: */
enum {
SET_ATEXIT = 1, /* when atexit() has been called
and get euid,uid,gid to fast compare */
SET_WCHG_HANDLERS = 2, /* winchg signal handler */
SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
};
static int cmdedit_x; /* real x terminal position */ static int cmdedit_x; /* real x terminal position */
@ -148,10 +140,6 @@ static char *user_buf = "";
static char *home_pwd_buf = ""; static char *home_pwd_buf = "";
#endif #endif
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR || ENABLE_FEATURE_COMMAND_TAB_COMPLETION
static int my_euid;
#endif
#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
static int my_uid; static int my_uid;
static int my_gid; static int my_gid;
@ -339,7 +327,6 @@ static void input_backspace(void)
} }
} }
/* Move forward one character */ /* Move forward one character */
static void input_forward(void) static void input_forward(void)
{ {
@ -347,10 +334,21 @@ static void input_forward(void)
cmdedit_set_out_char(command_ps[cursor + 1]); cmdedit_set_out_char(command_ps[cursor + 1]);
} }
#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
static char **matches; static char **matches;
static int num_matches; static unsigned num_matches;
static void free_tab_completion_data(void)
{
if (matches) {
while (num_matches)
free(matches[--num_matches]);
free(matches);
matches = NULL;
}
}
static void add_match(char *matched) static void add_match(char *matched)
{ {
@ -363,7 +361,6 @@ static void add_match(char *matched)
} }
#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
static void username_tab_completion(char *ud, char *with_shash_flg) static void username_tab_completion(char *ud, char *with_shash_flg)
{ {
struct passwd *entry; struct passwd *entry;
@ -504,7 +501,6 @@ static void exe_n_cwd_tab_completion(char *command, int type)
} }
for (i = 0; i < npaths; i++) { for (i = 0; i < npaths; i++) {
dir = opendir(paths[i]); dir = opendir(paths[i]);
if (!dir) /* Don't print an error */ if (!dir) /* Don't print an error */
continue; continue;
@ -559,7 +555,6 @@ static void exe_n_cwd_tab_completion(char *command, int type)
} }
} }
#define QUOT (UCHAR_MAX+1) #define QUOT (UCHAR_MAX+1)
#define collapse_pos(is, in) { \ #define collapse_pos(is, in) { \
@ -580,8 +575,8 @@ static int find_match(char *matchBuf, int *len_with_quotes)
if (int_buf[i] == 0) { if (int_buf[i] == 0) {
pos_buf[i] = -1; /* indicator end line */ pos_buf[i] = -1; /* indicator end line */
break; break;
} else }
pos_buf[i] = i; pos_buf[i] = i;
} }
/* mask \+symbol and convert '\t' to ' ' */ /* mask \+symbol and convert '\t' to ' ' */
@ -731,9 +726,9 @@ static int find_match(char *matchBuf, int *len_with_quotes)
} }
/* /*
display by column original ideas from ls applet, * display by column (original idea from ls applet,
very optimize by my :) * very optimized by me :)
*/ */
static void showfiles(void) static void showfiles(void)
{ {
int ncols, row; int ncols, row;
@ -770,11 +765,6 @@ static void showfiles(void)
} }
} }
static int match_compare(const void *a, const void *b)
{
return strcmp(*(char**)a, *(char**)b);
}
static char *add_quote_for_spec_chars(char *found) static char *add_quote_for_spec_chars(char *found)
{ {
int l = 0; int l = 0;
@ -789,18 +779,14 @@ static char *add_quote_for_spec_chars(char *found)
return s; return s;
} }
static int match_compare(const void *a, const void *b)
{
return strcmp(*(char**)a, *(char**)b);
}
/* Do TAB completion */
static void input_tab(int *lastWasTab) static void input_tab(int *lastWasTab)
{ {
/* Do TAB completion */
if (lastWasTab == 0) { /* free all memory */
if (matches) {
while (num_matches > 0)
free(matches[--num_matches]);
free(matches);
matches = (char **) NULL;
}
return;
}
if (!*lastWasTab) { if (!*lastWasTab) {
char *tmp, *tmp1; char *tmp, *tmp1;
int len_found; int len_found;
@ -813,12 +799,12 @@ static void input_tab(int *lastWasTab)
/* Make a local copy of the string -- up /* Make a local copy of the string -- up
* to the position of the cursor */ * to the position of the cursor */
tmp = strncpy(matchBuf, command_ps, cursor); tmp = strncpy(matchBuf, command_ps, cursor);
tmp[cursor] = 0; tmp[cursor] = '\0';
find_type = find_match(matchBuf, &recalc_pos); find_type = find_match(matchBuf, &recalc_pos);
/* Free up any memory already allocated */ /* Free up any memory already allocated */
input_tab(0); free_tab_completion_data();
#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
/* If the word starts with `~' and there is no slash in the word, /* If the word starts with `~' and there is no slash in the word,
@ -829,24 +815,24 @@ static void input_tab(int *lastWasTab)
if (!matches) if (!matches)
#endif #endif
/* Try to match any executable in our path and everything /* Try to match any executable in our path and everything
* in the current working directory that matches. */ * in the current working directory */
exe_n_cwd_tab_completion(matchBuf, find_type); exe_n_cwd_tab_completion(matchBuf, find_type);
/* Sort, then remove any duplicates found */ /* Sort, then remove any duplicates found */
if (matches) { if (matches) {
int i, n = 0; int i, n = 0;
qsort(matches, num_matches, sizeof(char*), match_compare); qsort(matches, num_matches, sizeof(char*), match_compare);
for (i = 0; i < num_matches - 1; ++i) { for (i = 0; i < num_matches - 1; ++i) {
if (matches[i] && matches[i+1]) { if (matches[i] && matches[i+1]) { /* paranoia */
if (strcmp(matches[i], matches[i+1]) == 0) { if (strcmp(matches[i], matches[i+1]) == 0) {
free(matches[i]); free(matches[i]);
matches[i] = 0; matches[i] = NULL; /* paranoia */
} else { } else {
matches[n++] = matches[i]; matches[n++] = matches[i];
} }
} }
} }
matches[n++] = matches[num_matches-1]; matches[n] = matches[i];
num_matches = n; num_matches = n + 1;
} }
/* Did we find exactly one match? */ /* Did we find exactly one match? */
if (!matches || num_matches > 1) { if (!matches || num_matches > 1) {
@ -858,10 +844,10 @@ static void input_tab(int *lastWasTab)
for (tmp = tmp1; *tmp; tmp++) for (tmp = tmp1; *tmp; tmp++)
for (len_found = 1; len_found < num_matches; len_found++) for (len_found = 1; len_found < num_matches; len_found++)
if (matches[len_found][(tmp - tmp1)] != *tmp) { if (matches[len_found][(tmp - tmp1)] != *tmp) {
*tmp = 0; *tmp = '\0';
break; break;
} }
if (*tmp1 == 0) { /* have unique */ if (*tmp1 == '\0') { /* have unique */
free(tmp1); free(tmp1);
return; return;
} }
@ -881,7 +867,6 @@ static void input_tab(int *lastWasTab)
len_found = strlen(tmp); len_found = strlen(tmp);
/* have space to placed match? */ /* have space to placed match? */
if ((len_found - strlen(matchBuf) + len) < BUFSIZ) { if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
/* before word for match */ /* before word for match */
command_ps[cursor - recalc_pos] = 0; command_ps[cursor - recalc_pos] = 0;
/* save tail line */ /* save tail line */
@ -914,6 +899,7 @@ static void input_tab(int *lastWasTab)
} }
} }
} }
#endif /* FEATURE_COMMAND_TAB_COMPLETION */ #endif /* FEATURE_COMMAND_TAB_COMPLETION */
@ -927,7 +913,7 @@ static int cur_history;
static void get_previous_history(void) static void get_previous_history(void)
{ {
if (command_ps[0] != 0 || history[cur_history] == 0) { if (command_ps[0] != '\0' || history[cur_history] == NULL) {
free(history[cur_history]); free(history[cur_history]);
history[cur_history] = xstrdup(command_ps); history[cur_history] = xstrdup(command_ps);
} }
@ -1126,81 +1112,6 @@ enum { vi_mode = 0 };
* cmdedit_read_input and its helpers * cmdedit_read_input and its helpers
*/ */
#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
#define getTermSettings(fd, argp) tcgetattr(fd, argp);
static sighandler_t previous_SIGWINCH_handler;
static void cmdedit_reset_term(void)
{
if (handlers_sets & SET_RESET_TERM) {
setTermSettings(STDIN_FILENO, (void *) &initial_settings);
handlers_sets &= ~SET_RESET_TERM;
}
if (handlers_sets & SET_WCHG_HANDLERS) {
/* restore SIGWINCH handler */
signal(SIGWINCH, previous_SIGWINCH_handler);
handlers_sets &= ~SET_WCHG_HANDLERS;
}
fflush(stdout);
}
static void cmdedit_setwidth(unsigned w, int redraw_flg)
{
cmdedit_termw = w;
if (redraw_flg) {
/* new y for current cursor */
int new_y = (cursor + cmdedit_prmt_len) / w;
/* redraw */
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
fflush(stdout);
}
}
static void win_changed(int nsig)
{
int width;
get_terminal_width_height(0, &width, NULL);
cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
if (nsig == SIGWINCH)
signal(SIGWINCH, win_changed); /* rearm ourself */
}
static void cmdedit_init(void)
{
cmdedit_prmt_len = 0;
if (!(handlers_sets & SET_WCHG_HANDLERS)) {
previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
win_changed(0); /* do initial resizing */
handlers_sets |= SET_WCHG_HANDLERS;
}
if (!(handlers_sets & SET_ATEXIT)) {
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
struct passwd *entry;
my_euid = geteuid();
entry = getpwuid(my_euid);
if (entry) {
user_buf = xstrdup(entry->pw_name);
home_pwd_buf = xstrdup(entry->pw_dir);
}
#endif
#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
#if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
my_euid = geteuid();
#endif
my_uid = getuid();
my_gid = getgid();
#endif /* FEATURE_COMMAND_TAB_COMPLETION */
handlers_sets |= SET_ATEXIT;
// Crap. We should be able to do it without atexit.
atexit(cmdedit_reset_term); /* be sure to do this only once */
}
}
#if !ENABLE_FEATURE_SH_FANCY_PROMPT #if !ENABLE_FEATURE_SH_FANCY_PROMPT
static void parse_prompt(const char *prmt_ptr) static void parse_prompt(const char *prmt_ptr)
{ {
@ -1259,7 +1170,7 @@ static void parse_prompt(const char *prmt_ptr)
} }
break; break;
case '$': case '$':
c = (my_euid == 0 ? '#' : '$'); c = (geteuid() == 0 ? '#' : '$');
break; break;
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
case 'w': case 'w':
@ -1283,7 +1194,8 @@ static void parse_prompt(const char *prmt_ptr)
pbuf += (cp-pbuf) + 1; pbuf += (cp-pbuf) + 1;
break; break;
case '!': case '!':
snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines); pbuf = buf2;
snprintf(buf2, sizeof(buf2), "%d", num_ok_lines);
break; break;
case 'e': case 'E': /* \e \E = \033 */ case 'e': case 'E': /* \e \E = \033 */
c = '\033'; c = '\033';
@ -1330,11 +1242,71 @@ static void parse_prompt(const char *prmt_ptr)
} }
#endif #endif
#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
#define getTermSettings(fd, argp) tcgetattr(fd, argp);
static sighandler_t previous_SIGWINCH_handler;
static void cmdedit_reset_term(void)
{
setTermSettings(STDIN_FILENO, (void *) &initial_settings);
/* restore SIGWINCH handler */
signal(SIGWINCH, previous_SIGWINCH_handler);
fflush(stdout);
}
static void cmdedit_setwidth(unsigned w, int redraw_flg)
{
cmdedit_termw = w;
if (redraw_flg) {
/* new y for current cursor */
int new_y = (cursor + cmdedit_prmt_len) / w;
/* redraw */
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
fflush(stdout);
}
}
static void win_changed(int nsig)
{
int width;
get_terminal_width_height(0, &width, NULL);
cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
if (nsig == SIGWINCH)
signal(SIGWINCH, win_changed); /* rearm ourself */
}
static void cmdedit_init(void)
{
cmdedit_prmt_len = 0;
previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
win_changed(0); /* do initial resizing */
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
{
struct passwd *entry;
entry = getpwuid(geteuid());
if (entry) {
user_buf = xstrdup(entry->pw_name);
home_pwd_buf = xstrdup(entry->pw_dir);
}
}
#endif
#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
my_uid = getuid();
my_gid = getgid();
#endif
// Crap. We should be able to do it without atexit.
atexit(cmdedit_reset_term); /* be sure to do this only once */
}
/* /*
* the emacs and vi modes share much of the code in the big * The emacs and vi modes share much of the code in the big
* command loop. commands entered when in vi's command mode (aka * command loop. Commands entered when in vi's command mode (aka
* "escape mode") get an extra bit added to distinguish them -- * "escape mode") get an extra bit added to distinguish them --
* this keeps them from being self-inserted. this clutters the * this keeps them from being self-inserted. This clutters the
* big switch a bit, but keeps all the code in one place. * big switch a bit, but keeps all the code in one place.
*/ */
@ -1379,7 +1351,6 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
#endif #endif
new_settings.c_cc[VINTR] = _POSIX_VDISABLE; new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
setTermSettings(0, (void *) &new_settings); setTermSettings(0, (void *) &new_settings);
handlers_sets |= SET_RESET_TERM;
/* Now initialize things */ /* Now initialize things */
cmdedit_init(); cmdedit_init();
@ -1387,11 +1358,12 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
parse_prompt(prompt); parse_prompt(prompt);
while (1) { while (1) {
fflush(stdout); /* buffered out to fast */ fflush(stdout);
if (safe_read(0, &c, 1) < 1) if (safe_read(0, &c, 1) < 1) {
/* if we can't read input then exit */ /* if we can't read input then exit */
goto prepare_to_die; goto prepare_to_die;
}
ic = c; ic = c;
@ -1789,23 +1761,22 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
lastWasTab = FALSE; lastWasTab = FALSE;
} }
setTermSettings(0, (void *) &initial_settings);
handlers_sets &= ~SET_RESET_TERM;
#if MAX_HISTORY > 0 #if MAX_HISTORY > 0
/* Handle command history log */ /* Handle command history log */
/* cleanup may be saved current command line */ /* cleanup may be saved current command line */
if (len > 0) { /* no put empty line */ if (len > 0) {
int i = n_history; int i = n_history;
free(history[MAX_HISTORY]); free(history[MAX_HISTORY]);
history[MAX_HISTORY] = 0; history[MAX_HISTORY] = NULL;
/* After max history, remove the oldest command */ /* After max history, remove the oldest command */
if (i >= MAX_HISTORY) { if (i >= MAX_HISTORY) {
free(history[0]); free(history[0]);
for (i = 0; i < MAX_HISTORY-1; i++) for (i = 0; i < MAX_HISTORY-1; i++)
history[i] = history[i+1]; history[i] = history[i+1];
} }
// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
// (i.e. do not save dups?)
history[i++] = xstrdup(command); history[i++] = xstrdup(command);
cur_history = i; cur_history = i;
n_history = i; n_history = i;
@ -1822,12 +1793,13 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
} }
#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION
input_tab(0); free_tab_completion_data();
#endif #endif
#if ENABLE_FEATURE_SH_FANCY_PROMPT #if ENABLE_FEATURE_SH_FANCY_PROMPT
free(cmdedit_prompt); free(cmdedit_prompt);
#endif #endif
/* restore initial_settings and SIGWINCH handler */
cmdedit_reset_term(); cmdedit_reset_term();
return len; return len;
} }