📄 getline.c
字号:
/* Based on: "$Id: getline.c 15091 2005-05-07 21:24:31Z sedwards $"; */
static const char copyright[] = "getline: Copyright (C) 1991, 1992, 1993, Chris Thewalt";
/*
* Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted, provided
* that the above copyright notices appear in all copies and that both the
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
* Thanks to the following people who have provided enhancements and fixes:
* Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
* DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
* Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
*/
/*
* Note: This version has been updated by Mike Gleason <mgleason@ncftp.com>
*/
#if defined(WIN32) || defined(_WINDOWS)
# include <windows.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <errno.h>
# include <conio.h>
# include <io.h>
# include <fcntl.h>
# define strcasecmp stricmp
# define strncasecmp strnicmp
# define sleep(a) Sleep(a * 1000)
# ifndef S_ISREG
# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
# endif
# ifndef open
# define open _open
# define write _write
# define read _read
# define close _close
# define lseek _lseek
# define stat _stat
# define lstat _stat
# define fstat _fstat
# define dup _dup
# define utime _utime
# define utimbuf _utimbuf
# endif
# ifndef unlink
# define unlink remove
# endif
# define NO_SIGNALS 1
# define LOCAL_PATH_DELIM '\\'
# define LOCAL_PATH_DELIM_STR "\\"
# define LOCAL_PATH_ALTDELIM '/'
# define IsLocalPathDelim(c) ((c == LOCAL_PATH_DELIM) || (c == LOCAL_PATH_ALTDELIM))
# define UNC_PATH_PREFIX "\\\\"
# define IsUNCPrefixed(s) (IsLocalPathDelim(s[0]) && IsLocalPathDelim(s[1]))
# define __windows__ 1
#else
# ifndef __unix__
# define __unix__ 1
# endif
# if defined(AIX) || defined(_AIX)
# define _ALL_SOURCE 1
# endif
# if defined(HAVE_CONFIG_H)
# include <config.h>
# else
# /* guess */
# define HAVE_TERMIOS_H 1
# define HAVE_UNISTD_H 1
# endif
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# include <sys/types.h>
# include <sys/time.h>
# include <sys/stat.h>
# ifdef CAN_USE_SYS_SELECT_H
# include <sys/select.h>
# endif
# include <fcntl.h>
# include <errno.h>
# include <dirent.h>
# include <pwd.h>
# ifdef HAVE_TERMIOS_H /* use HAVE_TERMIOS_H interface */
# include <termios.h>
struct termios new_termios, old_termios;
# else /* not HAVE_TERMIOS_H */
# include <sys/ioctl.h>
# ifdef TIOCSETN /* use BSD interface */
# include <sgtty.h>
struct sgttyb new_tty, old_tty;
struct tchars tch;
struct ltchars ltch;
# else /* use SYSV interface */
# include <termio.h>
struct termio new_termio, old_termio;
# endif /* TIOCSETN */
# endif /* HAVE_TERMIOS_H */
# define LOCAL_PATH_DELIM '/'
# define LOCAL_PATH_DELIM_STR "/"
# define _StrFindLocalPathDelim(a) strchr(a, LOCAL_PATH_DELIM)
# define _StrRFindLocalPathDelim(a) strrchr(a, LOCAL_PATH_DELIM)
# define IsLocalPathDelim(c) (c == LOCAL_PATH_DELIM)
#endif
/********************* C library headers ********************************/
#include <stdio.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#define _getline_c_ 1
#include "getline.h"
static int gl_tab(char *buf, int offset, int *loc, size_t bufsize);
/******************** external interface *********************************/
gl_in_hook_proc gl_in_hook = 0;
gl_out_hook_proc gl_out_hook = 0;
gl_tab_hook_proc gl_tab_hook = gl_tab;
gl_strlen_proc gl_strlen = (gl_strlen_proc) strlen;
gl_tab_completion_proc gl_completion_proc = 0;
int gl_filename_quoting_desired = -1; /* default to unspecified */
const char *gl_filename_quote_characters = " \t*?<>|;&()[]$`";
int gl_ellipses_during_completion = 1;
int gl_completion_exact_match_extra_char;
char gl_buf[GL_BUF_SIZE]; /* input buffer */
/******************** internal interface *********************************/
static int gl_init_done = -1; /* terminal mode flag */
static int gl_termw = 80; /* actual terminal width */
static int gl_termh = 24; /* actual terminal height */
static int gl_scroll = 27; /* width of EOL scrolling region */
static int gl_width = 0; /* net size available for input */
static int gl_extent = 0; /* how far to redraw, 0 means all */
static int gl_overwrite = 0; /* overwrite mode */
static int gl_pos = 0, gl_cnt = 0; /* position and size of input */
static char gl_killbuf[GL_BUF_SIZE]=""; /* killed text */
static const char *gl_prompt; /* to save the prompt string */
static char gl_intrc = 0; /* keyboard SIGINT char */
static char gl_quitc = 0; /* keyboard SIGQUIT char */
static char gl_suspc = 0; /* keyboard SIGTSTP char */
static char gl_dsuspc = 0; /* delayed SIGTSTP char */
static int gl_search_mode = 0; /* search mode flag */
static char **gl_matchlist = 0;
static char *gl_home_dir = NULL;
static int gl_vi_preferred = -1;
static int gl_vi_mode = 0;
static int gl_result = GL_OK;
static void gl_init(void); /* prepare to edit a line */
static void gl_cleanup(void); /* to undo gl_init */
static void gl_char_init(void); /* get ready for no echo input */
static void gl_char_cleanup(void); /* undo gl_char_init */
/* returns printable prompt width */
static void gl_addchar(int c); /* install specified char */
static void gl_del(int loc, int); /* del, either left (-1) or cur (0) */
static void gl_error(const char *const buf); /* write error msg and die */
static void gl_fixup(const char *prompt, int change, int cursor); /* fixup state variables and screen */
static int gl_getc(void); /* read one char from terminal */
static int gl_getcx(int); /* read one char from terminal, if available before timeout */
static void gl_kill(int pos); /* delete to EOL */
static void gl_newline(void); /* handle \n or \r */
static void gl_putc(int c); /* write one char to terminal */
static void gl_puts(const char *const buf); /* write a line to terminal */
static void gl_redraw(void); /* issue \n and redraw all */
static void gl_transpose(void); /* transpose two chars */
static void gl_yank(void); /* yank killed text */
static void gl_word(int direction); /* move a word */
static void gl_killword(int direction);
static void hist_init(void); /* initializes hist pointers */
static char *hist_next(void); /* return ptr to next item */
static char *hist_prev(void); /* return ptr to prev item */
static char *hist_save(char *p); /* makes copy of a string, without NL */
static void search_addchar(int c); /* increment search string */
static void search_term(void); /* reset with current contents */
static void search_back(int new_search); /* look back for current string */
static void search_forw(int new_search); /* look forw for current string */
static void gl_beep(void); /* try to play a system beep sound */
static int gl_do_tab_completion(char *buf, int *loc, size_t bufsize, int tabtab);
/************************ nonportable part *********************************/
#ifdef MSDOS
#include <bios.h>
#endif
static void
gl_char_init(void) /* turn off input echo */
{
#ifdef __unix__
# ifdef HAVE_TERMIOS_H /* Use POSIX */
if (tcgetattr(0, &old_termios) == 0) {
gl_intrc = old_termios.c_cc[VINTR];
gl_quitc = old_termios.c_cc[VQUIT];
# ifdef VSUSP
gl_suspc = old_termios.c_cc[VSUSP];
# endif
# ifdef VDSUSP
gl_dsuspc = old_termios.c_cc[VDSUSP];
# endif
}
new_termios = old_termios;
new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
new_termios.c_iflag |= (IGNBRK|IGNPAR);
new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_termios);
# elif defined(TIOCSETN) /* BSD */
if (ioctl(0, TIOCGETC, &tch) == 0) {
gl_intrc = tch.t_intrc;
gl_quitc = tch.t_quitc;
}
ioctl(0, TIOCGLTC, <ch);
gl_suspc = ltch.t_suspc;
gl_dsuspc = ltch.t_dsuspc;
ioctl(0, TIOCGETP, &old_tty);
new_tty = old_tty;
new_tty.sg_flags |= RAW;
new_tty.sg_flags &= ~ECHO;
ioctl(0, TIOCSETN, &new_tty);
# else /* SYSV */
if (ioctl(0, TCGETA, &old_termio) == 0) {
gl_intrc = old_termio.c_cc[VINTR];
gl_quitc = old_termio.c_cc[VQUIT];
}
new_termio = old_termio;
new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
new_termio.c_iflag |= (IGNBRK|IGNPAR);
new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
new_termio.c_cc[VMIN] = 1;
new_termio.c_cc[VTIME] = 0;
ioctl(0, TCSETA, &new_termio);
# endif
#endif /* __unix__ */
}
static void
gl_char_cleanup(void) /* undo effects of gl_char_init */
{
#ifdef __unix__
# ifdef HAVE_TERMIOS_H
tcsetattr(0, TCSANOW, &old_termios);
# elif defined(TIOCSETN) /* BSD */
ioctl(0, TIOCSETN, &old_tty);
# else /* SYSV */
ioctl(0, TCSETA, &old_termio);
# endif
#endif /* __unix__ */
}
int
gl_get_result(void)
{
return (gl_result);
} /* gl_get_result */
#if defined(MSDOS) || defined(__windows__)
#define K_UP 0x48
#define K_DOWN 0x50
#define K_LEFT 0x4B
#define K_RIGHT 0x4D
#define K_DELETE 0x53
#define K_INSERT 0x52
#define K_HOME 0x47
#define K_END 0x4F
#define K_PGUP 0x49
#define K_PGDN 0x51
int pc_keymap(int c)
{
switch (c) {
case K_UP:
case K_PGUP:
c = 16; /* up -> ^P */
break;
case K_DOWN:
case K_PGDN:
c = 14; /* down -> ^N */
break;
case K_LEFT:
c = 2; /* left -> ^B */
break;
case K_RIGHT:
c = 6; /* right -> ^F */
break;
case K_END:
c = 5; /* end -> ^E */
break;
case K_HOME:
c = 1; /* home -> ^A */
break;
case K_INSERT:
c = 15; /* insert -> ^O */
break;
case K_DELETE:
c = 4; /* del -> ^D */
break;
default:
c = 0; /* make it garbage */
}
return c;
}
#endif /* defined(MSDOS) || defined(__windows__) */
static int
gl_getc(void)
/* get a character without echoing it to screen */
{
int c;
#ifdef __unix__
char ch;
#endif
#ifdef __unix__
ch = '\0';
while ((c = (int) read(0, &ch, 1)) == -1) {
if (errno != EINTR)
break;
}
if (c != (-1))
c = (int) ch;
#endif /* __unix__ */
#ifdef MSDOS
c = _bios_keybrd(_NKEYBRD_READ);
if ((c & 0377) == 224) {
c = pc_keymap((c >> 8) & 0377);
} else {
c &= 0377;
}
#endif /* MSDOS */
#ifdef __windows__
c = (int) _getch();
if ((c == 0) || (c == 0xE0)) {
/* Read key code */
c = (int) _getch();
c = pc_keymap(c);
} else if (c == '\r') {
/* Note: we only get \r from the console,
* and not a matching \n.
*/
c = '\n';
}
#endif
return c;
}
#ifdef __unix__
static int
gl_getcx(int tlen)
/* Get a character without echoing it to screen, timing out
* after tlen tenths of a second.
*/
{
int c, result;
char ch;
fd_set ss;
struct timeval tv;
for (errno = 0;;) {
FD_ZERO(&ss);
FD_SET(0, &ss); /* set STDIN_FILENO */
tv.tv_sec = tlen / 10;
tv.tv_usec = (tlen % 10) * 100000L;
result = select(1, &ss, NULL, NULL, &tv);
if (result == 1) {
/* ready */
break;
} else if (result == 0) {
errno = ETIMEDOUT;
return (-2);
} else if (errno != EINTR) {
return (-1);
}
}
for (errno = 0;;) {
c = (int) read(0, &ch, 1);
if (c == 1)
return ((int) ch);
if (errno != EINTR)
break;
}
return (-1);
} /* gl_getcx */
#endif /* __unix__ */
#ifdef __windows__
static int
gl_getcx(int tlen)
{
int i, c;
c = (-2);
tlen -= 2; /* Adjust for 200ms overhead */
if (tlen < 1)
tlen = 1;
for (i=0; i<tlen; i++) {
if (_kbhit()) {
c = (int) _getch();
if ((c == 0) || (c == 0xE0)) {
/* Read key code */
c = (int) _getch();
c = pc_keymap(c);
break;
}
}
(void) SleepEx((DWORD) (tlen * 100), FALSE);
}
return (c);
} /* gl_getcx */
#endif /* __windows__ */
static void
gl_putc(int c)
{
char ch = (char) (unsigned char) c;
write(1, &ch, 1);
if (ch == '\n') {
ch = '\r';
write(1, &ch, 1); /* RAW mode needs '\r', does not hurt */
}
}
/******************** fairly portable part *********************************/
static void
gl_puts(const char *const buf)
{
int len;
if (buf) {
len = (int) strlen(buf);
write(1, buf, len);
}
}
static void
gl_error(const char *const buf)
{
int len = (int) strlen(buf);
gl_cleanup();
write(2, buf, len);
exit(1);
}
static void
gl_init(void)
/* set up variables and terminal */
{
const char *cp;
int w;
if (gl_init_done < 0) { /* -1 only on startup */
cp = (const char *) getenv("COLUMNS");
if (cp != NULL) {
w = atoi(cp);
if (w > 20)
gl_setwidth(w);
}
cp = (const char *) getenv("ROWS");
if (cp != NULL) {
w = atoi(cp);
if (w > 10)
gl_setheight(w);
}
hist_init();
}
if (isatty(0) == 0 || isatty(1) == 0)
gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
gl_char_init();
gl_init_done = 1;
}
static void
gl_cleanup(void)
/* undo effects of gl_init, as necessary */
{
if (gl_init_done > 0)
gl_char_cleanup();
gl_init_done = 0;
#ifdef __windows__
Sleep(40);
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
#endif
}
static void
gl_check_inputrc_for_vi(void)
{
FILE *fp;
char path[256];
/* If the user has a ~/.inputrc file,
* check it to see if it has a line like
* "set editing-mode vi". If it does,
* we know that the user wants vi
* emulation rather than emacs. If the
* file doesn't exist, it's no big
* deal since we can also check the
* $EDITOR environment variable.
*/
gl_set_home_dir(NULL);
if (gl_home_dir == NULL)
return;
#ifdef HAVE_SNPRINTF
snprintf(path, sizeof(path), "%s/%s", gl_home_dir, ".inputrc");
#else
if (sizeof(path) >= (strlen(gl_home_dir) + strlen("/.inputrc")))
return;
sprintf(path, "%s%s", gl_home_dir, "/.inputrc");
#endif
fp = fopen(
path,
#if defined(__windows__) || defined(MSDOS)
"rt"
#else
"r"
#endif
);
if (fp == NULL)
return;
while (fgets(path, sizeof(path) - 1, fp) != NULL) {
if ((strstr(path, "editing-mode") != NULL) && (strstr(path, "vi") != NULL)) {
gl_vi_preferred = 1;
break;
}
}
(void) fclose(fp);
} /* gl_check_inputrc_for_vi */
void
gl_setwidth(int w)
{
if (w > 250)
w = 250;
if (w > 20) {
gl_termw = w;
gl_scroll = w / 3;
} else {
gl_error("\n*** Error: minimum screen width is 21\n");
}
} /* gl_setwidth */
void
gl_setheight(int w)
{
if (w > 10) {
gl_termh = w;
} else {
gl_error("\n*** Error: minimum screen height is 10\n");
}
} /* gl_setheight */
char *
getline(char *prompt)
{
int c, loc, tmp, lastch;
int vi_count, count;
int vi_delete;
char vi_countbuf[32];
char *cp;
#ifdef __unix__
int sig;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -