?? cmdedit.c
字號:
/* vi: set sw=4 ts=4: *//* * Termios command line History and Editting. * * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. * Written by: Vladimir Oleynik <dzo@simtreas.ru> * * Used ideas: * Adam Rogoyski <rogoyski@cs.utexas.edu> * Dave Cinege <dcinege@psychosis.com> * Jakub Jelinek (c) 1995 * Erik Andersen <andersee@debian.org> (Majorly adjusted for busybox) * * This code is 'as is' with no warranty. * * *//* Usage and Known bugs: Terminal key codes are not extensive, and more will probably need to be added. This version was created on Debian GNU/Linux 2.x. Delete, Backspace, Home, End, and the arrow keys were tested to work in an Xterm and console. Ctrl-A also works as Home. Ctrl-E also works as End. Small bugs (simple effect): - not true viewing if terminal size (x*y symbols) less size (prompt + editor`s line + 2 symbols) - not true viewing if length prompt less terminal width */#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/ioctl.h>#include <ctype.h>#include <signal.h>#include <limits.h>#include "busybox.h"#ifdef BB_LOCALE_SUPPORT#define Isprint(c) isprint((c))#else#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )#endif#ifndef TEST#define D(x)#else#define BB_FEATURE_COMMAND_EDITING#define BB_FEATURE_COMMAND_TAB_COMPLETION#define BB_FEATURE_COMMAND_USERNAME_COMPLETION#define BB_FEATURE_NONPRINTABLE_INVERSE_PUT#define BB_FEATURE_CLEAN_UP#define D(x) x#endif /* TEST */#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION#include <dirent.h>#include <sys/stat.h>#endif#ifdef BB_FEATURE_COMMAND_EDITING#ifndef BB_FEATURE_COMMAND_TAB_COMPLETION#undef BB_FEATURE_COMMAND_USERNAME_COMPLETION#endif#if defined(BB_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(BB_FEATURE_SH_FANCY_PROMPT)#define BB_FEATURE_GETUSERNAME_AND_HOMEDIR#endif#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR# ifndef TEST# include "pwd_grp/pwd.h"# else# include <pwd.h># endif /* TEST */#endif /* advanced FEATURES */struct history { char *s; struct history *p; struct history *n;};/* Maximum length of the linked list for the command line history */static const int MAX_HISTORY = 15;/* First element in command line list */static struct history *his_front = NULL;/* Last element in command line list */static struct history *his_end = NULL;#include <termios.h>#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)#define getTermSettings(fd,argp) tcgetattr(fd, argp);/* Current termio and the previous termio before starting sh */static struct termios initial_settings, new_settings;staticvolatile int cmdedit_termw = 80; /* actual terminal width */static int history_counter = 0; /* Number of commands in history list */staticvolatile int handlers_sets = 0; /* Set next bites: */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_y; /* pseudoreal y terminal position */static int cmdedit_prmt_len; /* lenght prompt without colores string */static int cursor; /* required global for signal handler */static int len; /* --- "" - - "" - -"- --""-- --""--- */static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */static#ifndef BB_FEATURE_SH_FANCY_PROMPT const#endifchar *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIRstatic char *user_buf = "";static char *home_pwd_buf = "";static int my_euid;#endif#ifdef BB_FEATURE_SH_FANCY_PROMPTstatic char *hostname_buf = "";static int num_ok_lines = 1;#endif#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIRstatic int my_euid;#endifstatic int my_uid;static int my_gid;#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION *//* It seems that libc5 doesn't know what a sighandler_t is... */#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)typedef void (*sighandler_t) (int);#endifstatic void cmdedit_setwidth(int w, int redraw_flg);static void win_changed(int nsig){ struct winsize win = { 0, 0, 0, 0 }; static sighandler_t previous_SIGWINCH_handler; /* for reset */ /* emulate || signal call */ if (nsig == -SIGWINCH || nsig == SIGWINCH) { ioctl(0, TIOCGWINSZ, &win); if (win.ws_col > 0) { cmdedit_setwidth(win.ws_col, nsig == SIGWINCH); } } /* Unix not all standart in recall signal */ if (nsig == -SIGWINCH) /* save previous handler */ previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); else if (nsig == SIGWINCH) /* signaled called handler */ signal(SIGWINCH, win_changed); /* set for next call */ else /* nsig == 0 */ /* set previous handler */ signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */}static void cmdedit_reset_term(void){ if ((handlers_sets & SET_RESET_TERM) != 0) {/* sparc and other have broken termios support: use old termio handling. */ setTermSettings(fileno(stdin), (void *) &initial_settings); handlers_sets &= ~SET_RESET_TERM; } if ((handlers_sets & SET_WCHG_HANDLERS) != 0) { /* reset SIGWINCH handler to previous (default) */ win_changed(0); handlers_sets &= ~SET_WCHG_HANDLERS; } fflush(stdout);#if 0//#ifdef BB_FEATURE_CLEAN_UP if (his_front) { struct history *n; while (his_front != his_end) { n = his_front->n; free(his_front->s); free(his_front); his_front = n; } }#endif}/* special for recount position for scroll and remove terminal margin effect */static void cmdedit_set_out_char(int next_char){ int c = (int)((unsigned char) command_ps[cursor]); if (c == 0) c = ' '; /* destroy end char? */#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT if (!Isprint(c)) { /* Inverse put non-printable characters */ if (c >= 128) c -= 128; if (c < ' ') c += '@'; if (c == 127) c = '?'; printf("\033[7m%c\033[0m", c); } else#endif putchar(c); if (++cmdedit_x >= cmdedit_termw) { /* terminal is scrolled down */ cmdedit_y++; cmdedit_x = 0; if (!next_char) next_char = ' '; /* destroy "(auto)margin" */ putchar(next_char); putchar('\b'); } cursor++;}/* Move to end line. Bonus: rewrite line from cursor */static void input_end(void){ while (cursor < len) cmdedit_set_out_char(0);}/* Go to the next line */static void goto_new_line(void){ input_end(); if (cmdedit_x) putchar('\n');}static inline void out1str(const char *s){ fputs(s, stdout);}static inline void beep(void){ putchar('\007');}/* Move back one charactor *//* special for slow terminal */static void input_backward(int num){ if (num > cursor) num = cursor; cursor -= num; /* new cursor (in command, not terminal) */ if (cmdedit_x >= num) { /* no to up line */ cmdedit_x -= num; if (num < 4) while (num-- > 0) putchar('\b'); else printf("\033[%dD", num); } else { int count_y; if (cmdedit_x) { putchar('\r'); /* back to first terminal pos. */ num -= cmdedit_x; /* set previous backward */ } count_y = 1 + num / cmdedit_termw; printf("\033[%dA", count_y); cmdedit_y -= count_y; /* require forward after uping */ cmdedit_x = cmdedit_termw * count_y - num; printf("\033[%dC", cmdedit_x); /* set term cursor */ }}static void put_prompt(void){ out1str(cmdedit_prompt); cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ cursor = 0; cmdedit_y = 0; /* new quasireal y */}#ifndef BB_FEATURE_SH_FANCY_PROMPTstatic void parse_prompt(const char *prmt_ptr){ cmdedit_prompt = prmt_ptr; cmdedit_prmt_len = strlen(prmt_ptr); put_prompt();}#elsestatic void parse_prompt(const char *prmt_ptr){ int prmt_len = 0; int sub_len = 0; char flg_not_length = '['; char *prmt_mem_ptr = xcalloc(1, 1); char *pwd_buf = xgetcwd(0); char buf2[PATH_MAX + 1]; char buf[2]; char c; char *pbuf; if (!pwd_buf) { pwd_buf=(char *)unknown; } while (*prmt_ptr) { pbuf = buf; pbuf[1] = 0; c = *prmt_ptr++; if (c == '\\') { const char *cp = prmt_ptr; int l; c = process_escape_sequence(&prmt_ptr); if(prmt_ptr==cp) { if (*cp == 0) break; c = *prmt_ptr++; switch (c) {#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR case 'u': pbuf = user_buf; break;#endif case 'h': pbuf = hostname_buf; if (*pbuf == 0) { pbuf = xcalloc(256, 1); if (gethostname(pbuf, 255) < 0) { strcpy(pbuf, "?"); } else { char *s = strchr(pbuf, '.'); if (s) *s = 0; } hostname_buf = pbuf; } break; case '$': c = my_euid == 0 ? '#' : '$'; break;#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR case 'w': pbuf = pwd_buf; l = strlen(home_pwd_buf); if (home_pwd_buf[0] != 0 && strncmp(home_pwd_buf, pbuf, l) == 0 && (pbuf[l]=='/' || pbuf[l]=='\0') && strlen(pwd_buf+l)<PATH_MAX) { pbuf = buf2; *pbuf = '~'; strcpy(pbuf+1, pwd_buf+l); } break;#endif case 'W': pbuf = pwd_buf; cp = strrchr(pbuf,'/'); if ( (cp != NULL) && (cp != pbuf) ) pbuf += (cp-pbuf)+1; break; case '!': snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines); break; case 'e': case 'E': /* \e \E = \033 */ c = '\033'; break; case 'x': case 'X': for (l = 0; l < 3;) { int h; buf2[l++] = *prmt_ptr; buf2[l] = 0; h = strtol(buf2, &pbuf, 16); if (h > UCHAR_MAX || (pbuf - buf2) < l) { l--; break; } prmt_ptr++; } buf2[l] = 0; c = (char)strtol(buf2, 0, 16); if(c==0) c = '?'; pbuf = buf; break; case '[': case ']': if (c == flg_not_length) { flg_not_length = flg_not_length == '[' ? ']' : '['; continue; } break; } } } if(pbuf == buf) *pbuf = c; prmt_len += strlen(pbuf); prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); if (flg_not_length == ']') sub_len++; } if(pwd_buf!=(char *)unknown) free(pwd_buf); cmdedit_prompt = prmt_mem_ptr; cmdedit_prmt_len = prmt_len - sub_len; put_prompt();}#endif/* draw promt, editor line, and clear tail */static void redraw(int y, int back_cursor){ if (y > 0) /* up to start y */ printf("\033[%dA", y); putchar('\r'); put_prompt(); input_end(); /* rewrite */ printf("\033[J"); /* destroy tail after cursor */ input_backward(back_cursor);}/* Delete the char in front of the cursor */static void input_delete(void){ int j = cursor; if (j == len) return; strcpy(command_ps + j, command_ps + j + 1); len--; input_end(); /* rewtite new line */ cmdedit_set_out_char(0); /* destroy end char */ input_backward(cursor - j); /* back to old pos cursor */}/* Delete the char in back of the cursor */static void input_backspace(void){ if (cursor > 0) { input_backward(1); input_delete(); }}/* Move forward one charactor */static void input_forward(void){ if (cursor < len) cmdedit_set_out_char(command_ps[cursor + 1]);}static void cmdedit_setwidth(int w, int redraw_flg){ cmdedit_termw = cmdedit_prmt_len + 2; if (w <= cmdedit_termw) { cmdedit_termw = cmdedit_termw % w; } if (w > cmdedit_termw) { cmdedit_termw = w; if (redraw_flg) { /* new y for current cursor */ int new_y = (cursor + cmdedit_prmt_len) / w; /* redraw */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -