URL
https://opencores.org/ocsvn/eco32/eco32/trunk
Subversion Repositories eco32
Compare Revisions
- This comparison shows the changes necessary to convert path
/eco32/tags/eco32-0.23/sim/getline
- from Rev 8 to Rev 157
- ↔ Reverse comparison
Rev 8 → Rev 157
/testgl.c
0,0 → 1,17
#include <stdio.h> |
#include "getline.h" |
|
main() |
/* |
* just echo user input lines, letting user edit them and move through |
* history list |
*/ |
{ |
char *p; |
|
do { |
p = gl_getline("PROMPT>>>> "); |
gl_histadd(p); |
fputs(p, stdout); |
} while (*p != 0); |
} |
/getline.c
0,0 → 1,1125
#ifndef lint |
static char rcsid[] = |
"$Id: getline.c,v 3.11 1993/12/02 15:54:31 thewalt Exp thewalt $"; |
static char *copyright = "Copyright (C) 1991, 1992, 1993, Chris Thewalt"; |
#endif |
|
/* |
* 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 |
*/ |
|
#include "getline.h" |
static int gl_tab(); /* forward reference needed for gl_tab_hook */ |
|
/******************** imported interface *********************************/ |
|
#include <string.h> |
#include <ctype.h> |
#include <errno.h> |
#include <signal.h> |
|
extern int isatty(); |
extern void *malloc(); |
extern void free(); |
|
/********************* exported interface ********************************/ |
|
char *gl_getline(); /* read a line of input */ |
void gl_setwidth(); /* specify width of screen */ |
void gl_histadd(); /* adds entries to hist */ |
void gl_strwidth(); /* to bind gl_strlen */ |
|
int (*gl_in_hook)() = 0; |
int (*gl_out_hook)() = 0; |
int (*gl_tab_hook)() = gl_tab; |
|
/******************** internal interface *********************************/ |
|
#define BUF_SIZE 1024 |
|
static int gl_init_done = -1; /* terminal mode flag */ |
static int gl_termw = 80; /* actual terminal width */ |
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, gl_cnt = 0; /* position and size of input */ |
static char gl_buf[BUF_SIZE]; /* input buffer */ |
static char gl_killbuf[BUF_SIZE]=""; /* killed text */ |
static 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 void gl_init(); /* prepare to edit a line */ |
static void gl_cleanup(); /* to undo gl_init */ |
static void gl_char_init(); /* get ready for no echo input */ |
static void gl_char_cleanup(); /* undo gl_char_init */ |
static size_t (*gl_strlen)() = (size_t(*)())strlen; |
/* returns printable prompt width */ |
|
static void gl_addchar(); /* install specified char */ |
static void gl_del(); /* del, either left (-1) or cur (0) */ |
static void gl_error(); /* write error msg and die */ |
static void gl_fixup(); /* fixup state variables and screen */ |
static int gl_getc(); /* read one char from terminal */ |
static void gl_kill(); /* delete to EOL */ |
static void gl_newline(); /* handle \n or \r */ |
static void gl_putc(); /* write one char to terminal */ |
static void gl_puts(); /* write a line to terminal */ |
static void gl_redraw(); /* issue \n and redraw all */ |
static void gl_transpose(); /* transpose two chars */ |
static void gl_yank(); /* yank killed text */ |
static void gl_word(); /* move a word */ |
|
static void hist_init(); /* initializes hist pointers */ |
static char *hist_next(); /* return ptr to next item */ |
static char *hist_prev(); /* return ptr to prev item */ |
static char *hist_save(); /* makes copy of a string, without NL */ |
|
static void search_addchar(); /* increment search string */ |
static void search_term(); /* reset with current contents */ |
static void search_back(); /* look back for current string */ |
static void search_forw(); /* look forw for current string */ |
|
/************************ nonportable part *********************************/ |
|
extern int write(); |
extern void exit(); |
|
#ifdef unix |
#ifndef __unix__ |
#define __unix__ |
#endif /* not __unix__ */ |
#endif /* unix */ |
|
#ifdef _IBMR2 |
#ifndef __unix__ |
#define __unix__ |
#endif |
#endif |
|
#ifdef __GO32__ |
#include <pc.h> |
#undef MSDOS |
#undef __unix__ |
#endif |
|
#ifdef MSDOS |
#include <bios.h> |
#endif |
|
#ifdef __unix__ |
#ifndef __convexc__ |
extern int read(); |
extern int kill(); |
extern int ioctl(); |
#endif /* not __convexc__ */ |
#ifdef POSIX /* use POSIX interface */ |
#include <termios.h> |
struct termios new_termios, old_termios; |
#else /* not POSIX */ |
#include <sys/ioctl.h> |
#ifdef M_XENIX /* does not really use bsd terminal interface */ |
#undef TIOCSETN |
#endif /* M_XENIX */ |
#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 /* POSIX */ |
#endif /* __unix__ */ |
|
#ifdef vms |
#include <descrip.h> |
#include <ttdef.h> |
#include <iodef.h> |
#include unixio |
|
static int setbuff[2]; /* buffer to set terminal attributes */ |
static short chan = -1; /* channel to terminal */ |
struct dsc$descriptor_s descrip; /* VMS descriptor */ |
#endif |
|
static void |
gl_char_init() /* turn off input echo */ |
{ |
#ifdef __unix__ |
#ifdef POSIX |
tcgetattr(0, &old_termios); |
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); |
#else /* not POSIX */ |
#ifdef TIOCSETN /* BSD */ |
ioctl(0, TIOCGETC, &tch); |
ioctl(0, TIOCGLTC, <ch); |
gl_intrc = tch.t_intrc; |
gl_quitc = tch.t_quitc; |
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 */ |
ioctl(0, TCGETA, &old_termio); |
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 /* TIOCSETN */ |
#endif /* POSIX */ |
#endif /* __unix__ */ |
|
#ifdef vms |
descrip.dsc$w_length = strlen("tt:"); |
descrip.dsc$b_dtype = DSC$K_DTYPE_T; |
descrip.dsc$b_class = DSC$K_CLASS_S; |
descrip.dsc$a_pointer = "tt:"; |
(void)sys$assign(&descrip,&chan,0,0); |
(void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0); |
setbuff[1] |= TT$M_NOECHO; |
(void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); |
#endif /* vms */ |
} |
|
static void |
gl_char_cleanup() /* undo effects of gl_char_init */ |
{ |
#ifdef __unix__ |
#ifdef POSIX |
tcsetattr(0, TCSANOW, &old_termios); |
#else /* not POSIX */ |
#ifdef TIOCSETN /* BSD */ |
ioctl(0, TIOCSETN, &old_tty); |
#else /* SYSV */ |
ioctl(0, TCSETA, &old_termio); |
#endif /* TIOCSETN */ |
#endif /* POSIX */ |
#endif /* __unix__ */ |
|
#ifdef vms |
setbuff[1] &= ~TT$M_NOECHO; |
(void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); |
sys$dassgn(chan); |
chan = -1; |
#endif |
} |
|
#if MSDOS || __EMX__ || __GO32__ |
int pc_keymap(c) |
int c; |
{ |
switch (c) { |
case 72: c = 16; /* up -> ^P */ |
break; |
case 80: c = 14; /* down -> ^N */ |
break; |
case 75: c = 2; /* left -> ^B */ |
break; |
case 77: c = 6; /* right -> ^F */ |
break; |
default: c = 0; /* make it garbage */ |
} |
return c; |
} |
#endif /* MSDOS || __EMX__ || __GO32__ */ |
|
static int |
gl_getc() |
/* get a character without echoing it to screen */ |
{ |
int c; |
#ifdef __unix__ |
char ch; |
#endif |
|
#ifdef __unix__ |
while ((c = read(0, &ch, 1)) == -1) { |
if (errno != EINTR) |
break; |
} |
c = (ch <= 0)? -1 : ch; |
#endif /* __unix__ */ |
#ifdef MSDOS |
c = _bios_keybrd(_NKEYBRD_READ); |
#endif /* MSDOS */ |
#ifdef __GO32__ |
c = getkey () ; |
if (c > 255) c = pc_keymap(c & 0377); |
#endif /* __GO32__ */ |
#ifdef __TURBOC__ |
while(!bioskey(1)) |
; |
c = bioskey(0); |
#endif |
#if MSDOS || __TURBOC__ |
if ((c & 0377) == 224) { |
c = pc_keymap((c >> 8) & 0377); |
} else { |
c &= 0377; |
} |
#endif /* MSDOS || __TURBOC__ */ |
#ifdef __EMX__ |
c = _read_kbd(0, 1, 0); |
if (c == 224 || c == 0) { |
c = pc_keymap(_read_kbd(0, 1, 0)); |
} else { |
c &= 0377; |
} |
#endif |
#ifdef vms |
if(chan < 0) { |
c='\0'; |
} |
(void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0); |
c &= 0177; /* get a char */ |
#endif |
return c; |
} |
|
static void |
gl_putc(c) |
int c; |
{ |
char ch = 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(buf) |
char *buf; |
{ |
int len; |
|
if (buf) { |
len = strlen(buf); |
write(1, buf, len); |
} |
} |
|
static void |
gl_error(buf) |
char *buf; |
{ |
int len = strlen(buf); |
|
gl_cleanup(); |
write(2, buf, len); |
exit(1); |
} |
|
static void |
gl_init() |
/* set up variables and terminal */ |
{ |
if (gl_init_done < 0) { /* -1 only on startup */ |
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() |
/* undo effects of gl_init, as necessary */ |
{ |
if (gl_init_done > 0) |
gl_char_cleanup(); |
gl_init_done = 0; |
} |
|
void |
gl_setwidth(w) |
int w; |
{ |
if (w > 20) { |
gl_termw = w; |
gl_scroll = w / 3; |
} else { |
gl_error("\n*** Error: minimum screen width is 21\n"); |
} |
} |
|
char * |
gl_getline(prompt) |
char *prompt; |
{ |
int c, loc, tmp; |
|
#ifdef __unix__ |
int sig; |
#endif |
|
gl_init(); |
gl_prompt = (prompt)? prompt : ""; |
gl_buf[0] = 0; |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, -2, BUF_SIZE); |
while ((c = gl_getc()) >= 0) { |
gl_extent = 0; /* reset to full extent */ |
if (isprint(c)) { |
if (gl_search_mode) |
search_addchar(c); |
else |
gl_addchar(c); |
} else { |
if (gl_search_mode) { |
if (c == '\033' || c == '\016' || c == '\020') { |
search_term(); |
c = 0; /* ignore the character */ |
} else if (c == '\010' || c == '\177') { |
search_addchar(-1); /* unwind search string */ |
c = 0; |
} else if (c != '\022' && c != '\023') { |
search_term(); /* terminate and handle char */ |
} |
} |
switch (c) { |
case '\n': case '\r': /* newline */ |
gl_newline(); |
gl_cleanup(); |
return gl_buf; |
/*NOTREACHED*/ |
break; |
case '\001': gl_fixup(gl_prompt, -1, 0); /* ^A */ |
break; |
case '\002': gl_fixup(gl_prompt, -1, gl_pos-1); /* ^B */ |
break; |
case '\004': /* ^D */ |
if (gl_cnt == 0) { |
gl_buf[0] = 0; |
gl_cleanup(); |
gl_putc('\n'); |
return gl_buf; |
} else { |
gl_del(0); |
} |
break; |
case '\005': gl_fixup(gl_prompt, -1, gl_cnt); /* ^E */ |
break; |
case '\006': gl_fixup(gl_prompt, -1, gl_pos+1); /* ^F */ |
break; |
case '\010': case '\177': gl_del(-1); /* ^H and DEL */ |
break; |
case '\t': /* TAB */ |
if (gl_tab_hook) { |
tmp = gl_pos; |
loc = gl_tab_hook(gl_buf, gl_strlen(gl_prompt), &tmp); |
if (loc >= 0 || tmp != gl_pos) |
gl_fixup(gl_prompt, loc, tmp); |
} |
break; |
case '\013': gl_kill(gl_pos); /* ^K */ |
break; |
case '\014': gl_redraw(); /* ^L */ |
break; |
case '\016': /* ^N */ |
strcpy(gl_buf, hist_next()); |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, 0, BUF_SIZE); |
break; |
case '\017': gl_overwrite = !gl_overwrite; /* ^O */ |
break; |
case '\020': /* ^P */ |
strcpy(gl_buf, hist_prev()); |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, 0, BUF_SIZE); |
break; |
case '\022': search_back(1); /* ^R */ |
break; |
case '\023': search_forw(1); /* ^S */ |
break; |
case '\024': gl_transpose(); /* ^T */ |
break; |
case '\025': gl_kill(0); /* ^U */ |
break; |
case '\031': gl_yank(); /* ^Y */ |
break; |
case '\033': /* ansi arrow keys */ |
c = gl_getc(); |
if (c == '[') { |
switch(c = gl_getc()) { |
case 'A': /* up */ |
strcpy(gl_buf, hist_prev()); |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, 0, BUF_SIZE); |
break; |
case 'B': /* down */ |
strcpy(gl_buf, hist_next()); |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, 0, BUF_SIZE); |
break; |
case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */ |
break; |
case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */ |
break; |
default: gl_putc('\007'); /* who knows */ |
break; |
} |
} else if (c == 'f' || c == 'F') { |
gl_word(1); |
} else if (c == 'b' || c == 'B') { |
gl_word(-1); |
} else |
gl_putc('\007'); |
break; |
default: /* check for a terminal signal */ |
#ifdef __unix__ |
if (c > 0) { /* ignore 0 (reset above) */ |
sig = 0; |
#ifdef SIGINT |
if (c == gl_intrc) |
sig = SIGINT; |
#endif |
#ifdef SIGQUIT |
if (c == gl_quitc) |
sig = SIGQUIT; |
#endif |
#ifdef SIGTSTP |
if (c == gl_suspc || c == gl_dsuspc) |
sig = SIGTSTP; |
#endif |
if (sig != 0) { |
gl_cleanup(); |
kill(0, sig); |
gl_init(); |
gl_redraw(); |
c = 0; |
} |
} |
#endif /* __unix__ */ |
if (c > 0) |
gl_putc('\007'); |
break; |
} |
} |
} |
gl_cleanup(); |
gl_buf[0] = 0; |
return gl_buf; |
} |
|
static void |
gl_addchar(c) |
int c; |
/* adds the character c to the input buffer at current location */ |
{ |
int i; |
|
if (gl_cnt >= BUF_SIZE - 1) |
gl_error("\n*** Error: getline(): input buffer overflow\n"); |
if (gl_overwrite == 0 || gl_pos == gl_cnt) { |
for (i=gl_cnt; i >= gl_pos; i--) |
gl_buf[i+1] = gl_buf[i]; |
gl_buf[gl_pos] = c; |
gl_fixup(gl_prompt, gl_pos, gl_pos+1); |
} else { |
gl_buf[gl_pos] = c; |
gl_extent = 1; |
gl_fixup(gl_prompt, gl_pos, gl_pos+1); |
} |
} |
|
static void |
gl_yank() |
/* adds the kill buffer to the input buffer at current location */ |
{ |
int i, len; |
|
len = strlen(gl_killbuf); |
if (len > 0) { |
if (gl_overwrite == 0) { |
if (gl_cnt + len >= BUF_SIZE - 1) |
gl_error("\n*** Error: getline(): input buffer overflow\n"); |
for (i=gl_cnt; i >= gl_pos; i--) |
gl_buf[i+len] = gl_buf[i]; |
for (i=0; i < len; i++) |
gl_buf[gl_pos+i] = gl_killbuf[i]; |
gl_fixup(gl_prompt, gl_pos, gl_pos+len); |
} else { |
if (gl_pos + len > gl_cnt) { |
if (gl_pos + len >= BUF_SIZE - 1) |
gl_error("\n*** Error: getline(): input buffer overflow\n"); |
gl_buf[gl_pos + len] = 0; |
} |
for (i=0; i < len; i++) |
gl_buf[gl_pos+i] = gl_killbuf[i]; |
gl_extent = len; |
gl_fixup(gl_prompt, gl_pos, gl_pos+len); |
} |
} else |
gl_putc('\007'); |
} |
|
static void |
gl_transpose() |
/* switch character under cursor and to left of cursor */ |
{ |
int c; |
|
if (gl_pos > 0 && gl_cnt > gl_pos) { |
c = gl_buf[gl_pos-1]; |
gl_buf[gl_pos-1] = gl_buf[gl_pos]; |
gl_buf[gl_pos] = c; |
gl_extent = 2; |
gl_fixup(gl_prompt, gl_pos-1, gl_pos); |
} else |
gl_putc('\007'); |
} |
|
static void |
gl_newline() |
/* |
* Cleans up entire line before returning to caller. A \n is appended. |
* If line longer than screen, we redraw starting at beginning |
*/ |
{ |
int change = gl_cnt; |
int len = gl_cnt; |
int loc = gl_width - 5; /* shifts line back to start position */ |
|
if (gl_cnt >= BUF_SIZE - 1) |
gl_error("\n*** Error: getline(): input buffer overflow\n"); |
if (gl_out_hook) { |
change = gl_out_hook(gl_buf); |
len = strlen(gl_buf); |
} |
if (loc > len) |
loc = len; |
gl_fixup(gl_prompt, change, loc); /* must do this before appending \n */ |
gl_buf[len] = '\n'; |
gl_buf[len+1] = '\0'; |
gl_putc('\n'); |
} |
|
static void |
gl_del(loc) |
int loc; |
/* |
* Delete a character. The loc variable can be: |
* -1 : delete character to left of cursor |
* 0 : delete character under cursor |
*/ |
{ |
int i; |
|
if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) { |
for (i=gl_pos+loc; i < gl_cnt; i++) |
gl_buf[i] = gl_buf[i+1]; |
gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc); |
} else |
gl_putc('\007'); |
} |
|
static void |
gl_kill(pos) |
int pos; |
/* delete from pos to the end of line */ |
{ |
if (pos < gl_cnt) { |
strcpy(gl_killbuf, gl_buf + pos); |
gl_buf[pos] = '\0'; |
gl_fixup(gl_prompt, pos, pos); |
} else |
gl_putc('\007'); |
} |
|
static void |
gl_word(direction) |
int direction; |
/* move forward or backword one word */ |
{ |
int pos = gl_pos; |
|
if (direction > 0) { /* forward */ |
while (!isspace(gl_buf[pos]) && pos < gl_cnt) |
pos++; |
while (isspace(gl_buf[pos]) && pos < gl_cnt) |
pos++; |
} else { /* backword */ |
if (pos > 0) |
pos--; |
while (isspace(gl_buf[pos]) && pos > 0) |
pos--; |
while (!isspace(gl_buf[pos]) && pos > 0) |
pos--; |
if (pos < gl_cnt && isspace(gl_buf[pos])) /* move onto word */ |
pos++; |
} |
gl_fixup(gl_prompt, -1, pos); |
} |
|
static void |
gl_redraw() |
/* emit a newline, reset and redraw prompt and current input line */ |
{ |
if (gl_init_done > 0) { |
gl_putc('\n'); |
gl_fixup(gl_prompt, -2, gl_pos); |
} |
} |
|
static void |
gl_fixup(prompt, change, cursor) |
char *prompt; |
int change, cursor; |
/* |
* This function is used both for redrawing when input changes or for |
* moving within the input line. The parameters are: |
* prompt: compared to last_prompt[] for changes; |
* change : the index of the start of changes in the input buffer, |
* with -1 indicating no changes, -2 indicating we're on |
* a new line, redraw everything. |
* cursor : the desired location of the cursor after the call. |
* A value of BUF_SIZE can be used to indicate the cursor should |
* move just past the end of the input line. |
*/ |
{ |
static int gl_shift; /* index of first on screen character */ |
static int off_right; /* true if more text right of screen */ |
static int off_left; /* true if more text left of screen */ |
static char last_prompt[80] = ""; |
int left = 0, right = -1; /* bounds for redraw */ |
int pad; /* how much to erase at end of line */ |
int backup; /* how far to backup before fixing */ |
int new_shift; /* value of shift based on cursor */ |
int extra; /* adjusts when shift (scroll) happens */ |
int i; |
int new_right = -1; /* alternate right bound, using gl_extent */ |
int l1, l2; |
|
if (change == -2) { /* reset */ |
gl_pos = gl_cnt = gl_shift = off_right = off_left = 0; |
gl_putc('\r'); |
gl_puts(prompt); |
strcpy(last_prompt, prompt); |
change = 0; |
gl_width = gl_termw - gl_strlen(prompt); |
} else if (strcmp(prompt, last_prompt) != 0) { |
l1 = gl_strlen(last_prompt); |
l2 = gl_strlen(prompt); |
gl_cnt = gl_cnt + l1 - l2; |
strcpy(last_prompt, prompt); |
gl_putc('\r'); |
gl_puts(prompt); |
gl_pos = gl_shift; |
gl_width = gl_termw - l2; |
change = 0; |
} |
pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift; /* old length */ |
backup = gl_pos - gl_shift; |
if (change >= 0) { |
gl_cnt = strlen(gl_buf); |
if (change > gl_cnt) |
change = gl_cnt; |
} |
if (cursor > gl_cnt) { |
if (cursor != BUF_SIZE) /* BUF_SIZE means end of line */ |
gl_putc('\007'); |
cursor = gl_cnt; |
} |
if (cursor < 0) { |
gl_putc('\007'); |
cursor = 0; |
} |
if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2)) |
extra = 2; /* shift the scrolling boundary */ |
else |
extra = 0; |
new_shift = cursor + extra + gl_scroll - gl_width; |
if (new_shift > 0) { |
new_shift /= gl_scroll; |
new_shift *= gl_scroll; |
} else |
new_shift = 0; |
if (new_shift != gl_shift) { /* scroll occurs */ |
gl_shift = new_shift; |
off_left = (gl_shift)? 1 : 0; |
off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; |
left = gl_shift; |
new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; |
} else if (change >= 0) { /* no scroll, but text changed */ |
if (change < gl_shift + off_left) { |
left = gl_shift; |
} else { |
left = change; |
backup = gl_pos - change; |
} |
off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; |
right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; |
new_right = (gl_extent && (right > left + gl_extent))? |
left + gl_extent : right; |
} |
pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift; |
pad = (pad < 0)? 0 : pad; |
if (left <= right) { /* clean up screen */ |
for (i=0; i < backup; i++) |
gl_putc('\b'); |
if (left == gl_shift && off_left) { |
gl_putc('$'); |
left++; |
} |
for (i=left; i < new_right; i++) |
gl_putc(gl_buf[i]); |
gl_pos = new_right; |
if (off_right && new_right == right) { |
gl_putc('$'); |
gl_pos++; |
} else { |
for (i=0; i < pad; i++) /* erase remains of prev line */ |
gl_putc(' '); |
gl_pos += pad; |
} |
} |
i = gl_pos - cursor; /* move to final cursor location */ |
if (i > 0) { |
while (i--) |
gl_putc('\b'); |
} else { |
for (i=gl_pos; i < cursor; i++) |
gl_putc(gl_buf[i]); |
} |
gl_pos = cursor; |
} |
|
static int |
gl_tab(buf, offset, loc) |
char *buf; |
int offset; |
int *loc; |
/* default tab handler, acts like tabstops every 8 cols */ |
{ |
int i, count, len; |
|
len = strlen(buf); |
count = 8 - (offset + *loc) % 8; |
for (i=len; i >= *loc; i--) |
buf[i+count] = buf[i]; |
for (i=0; i < count; i++) |
buf[*loc+i] = ' '; |
i = *loc; |
*loc = i + count; |
return i; |
} |
|
/******************* strlen stuff **************************************/ |
|
void gl_strwidth(func) |
size_t (*func)(); |
{ |
if (func != 0) { |
gl_strlen = func; |
} |
} |
|
/******************* History stuff **************************************/ |
|
#ifndef HIST_SIZE |
#define HIST_SIZE 100 |
#endif |
|
static int hist_pos = 0, hist_last = 0; |
static char *hist_buf[HIST_SIZE]; |
|
static void |
hist_init() |
{ |
int i; |
|
hist_buf[0] = ""; |
for (i=1; i < HIST_SIZE; i++) |
hist_buf[i] = (char *)0; |
} |
|
void |
gl_histadd(buf) |
char *buf; |
{ |
static char *prev = 0; |
char *p = buf; |
int len; |
|
/* in case we call gl_histadd() before we call gl_getline() */ |
if (gl_init_done < 0) { /* -1 only on startup */ |
hist_init(); |
gl_init_done = 0; |
} |
while (*p == ' ' || *p == '\t' || *p == '\n') |
p++; |
if (*p) { |
len = strlen(buf); |
if (strchr(p, '\n')) /* previously line already has NL stripped */ |
len--; |
if (prev == 0 || strlen(prev) != len || |
strncmp(prev, buf, len) != 0) { |
hist_buf[hist_last] = hist_save(buf); |
prev = hist_buf[hist_last]; |
hist_last = (hist_last + 1) % HIST_SIZE; |
if (hist_buf[hist_last] && *hist_buf[hist_last]) { |
free(hist_buf[hist_last]); |
} |
hist_buf[hist_last] = ""; |
} |
} |
hist_pos = hist_last; |
} |
|
static char * |
hist_prev() |
/* loads previous hist entry into input buffer, sticks on first */ |
{ |
char *p = 0; |
int next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE; |
|
if (hist_buf[hist_pos] != 0 && next != hist_last) { |
hist_pos = next; |
p = hist_buf[hist_pos]; |
} |
if (p == 0) { |
p = ""; |
gl_putc('\007'); |
} |
return p; |
} |
|
static char * |
hist_next() |
/* loads next hist entry into input buffer, clears on last */ |
{ |
char *p = 0; |
|
if (hist_pos != hist_last) { |
hist_pos = (hist_pos+1) % HIST_SIZE; |
p = hist_buf[hist_pos]; |
} |
if (p == 0) { |
p = ""; |
gl_putc('\007'); |
} |
return p; |
} |
|
static char * |
hist_save(p) |
char *p; |
/* makes a copy of the string */ |
{ |
char *s = 0; |
int len = strlen(p); |
char *nl = strchr(p, '\n'); |
|
if (nl) { |
if ((s = malloc(len)) != 0) { |
strncpy(s, p, len-1); |
s[len-1] = 0; |
} |
} else { |
if ((s = malloc(len+1)) != 0) { |
strcpy(s, p); |
} |
} |
if (s == 0) |
gl_error("\n*** Error: hist_save() failed on malloc\n"); |
return s; |
} |
|
/******************* Search stuff **************************************/ |
|
static char search_prompt[101]; /* prompt includes search string */ |
static char search_string[100]; |
static int search_pos = 0; /* current location in search_string */ |
static int search_forw_flg = 0; /* search direction flag */ |
static int search_last = 0; /* last match found */ |
|
static void |
search_update(c) |
int c; |
{ |
if (c == 0) { |
search_pos = 0; |
search_string[0] = 0; |
search_prompt[0] = '?'; |
search_prompt[1] = ' '; |
search_prompt[2] = 0; |
} else if (c > 0) { |
search_string[search_pos] = c; |
search_string[search_pos+1] = 0; |
search_prompt[search_pos] = c; |
search_prompt[search_pos+1] = '?'; |
search_prompt[search_pos+2] = ' '; |
search_prompt[search_pos+3] = 0; |
search_pos++; |
} else { |
if (search_pos > 0) { |
search_pos--; |
search_string[search_pos] = 0; |
search_prompt[search_pos] = '?'; |
search_prompt[search_pos+1] = ' '; |
search_prompt[search_pos+2] = 0; |
} else { |
gl_putc('\007'); |
hist_pos = hist_last; |
} |
} |
} |
|
static void |
search_addchar(c) |
int c; |
{ |
char *loc; |
|
search_update(c); |
if (c < 0) { |
if (search_pos > 0) { |
hist_pos = search_last; |
} else { |
gl_buf[0] = 0; |
hist_pos = hist_last; |
} |
strcpy(gl_buf, hist_buf[hist_pos]); |
} |
if ((loc = strstr(gl_buf, search_string)) != 0) { |
gl_fixup(search_prompt, 0, loc - gl_buf); |
} else if (search_pos > 0) { |
if (search_forw_flg) { |
search_forw(0); |
} else { |
search_back(0); |
} |
} else { |
gl_fixup(search_prompt, 0, 0); |
} |
} |
|
static void |
search_term() |
{ |
gl_search_mode = 0; |
if (gl_buf[0] == 0) /* not found, reset hist list */ |
hist_pos = hist_last; |
if (gl_in_hook) |
gl_in_hook(gl_buf); |
gl_fixup(gl_prompt, 0, gl_pos); |
} |
|
static void |
search_back(new_search) |
int new_search; |
{ |
int found = 0; |
char *p, *loc; |
|
search_forw_flg = 0; |
if (gl_search_mode == 0) { |
search_last = hist_pos = hist_last; |
search_update(0); |
gl_search_mode = 1; |
gl_buf[0] = 0; |
gl_fixup(search_prompt, 0, 0); |
} else if (search_pos > 0) { |
while (!found) { |
p = hist_prev(); |
if (*p == 0) { /* not found, done looking */ |
gl_buf[0] = 0; |
gl_fixup(search_prompt, 0, 0); |
found = 1; |
} else if ((loc = strstr(p, search_string)) != 0) { |
strcpy(gl_buf, p); |
gl_fixup(search_prompt, 0, loc - p); |
if (new_search) |
search_last = hist_pos; |
found = 1; |
} |
} |
} else { |
gl_putc('\007'); |
} |
} |
|
static void |
search_forw(new_search) |
int new_search; |
{ |
int found = 0; |
char *p, *loc; |
|
search_forw_flg = 1; |
if (gl_search_mode == 0) { |
search_last = hist_pos = hist_last; |
search_update(0); |
gl_search_mode = 1; |
gl_buf[0] = 0; |
gl_fixup(search_prompt, 0, 0); |
} else if (search_pos > 0) { |
while (!found) { |
p = hist_next(); |
if (*p == 0) { /* not found, done looking */ |
gl_buf[0] = 0; |
gl_fixup(search_prompt, 0, 0); |
found = 1; |
} else if ((loc = strstr(p, search_string)) != 0) { |
strcpy(gl_buf, p); |
gl_fixup(search_prompt, 0, loc - p); |
if (new_search) |
search_last = hist_pos; |
found = 1; |
} |
} |
} else { |
gl_putc('\007'); |
} |
} |
/CHANGES
0,0 → 1,59
Changes by HG |
* renamed getline to gl_getline |
|
Changes from last patch (v38i004 in comp.sources.misc) |
* added djgpp support on PCs |
* cleanup up __unix__ ifdefs |
* added __STDC__ prototypes in header file |
* change makefile to build an archive and testgl |
* added retry on interrupted read()s |
* fixed GO32 keymapping to handles arrow keys properly |
|
Changes from last release (v37i050 in comp.sources.misc) |
* Added support for AIX, XENIX, TurboC, gcc (EMX) under OS/2 |
* Added ^U (kill line) functionality |
* Added ESC-B/ESC-F backward/forward one word functionality |
* Made it possible to preload history with gl_histadd() before calling |
getline() |
|
Changes from last release (v28i056 in comp.sources.misc) |
|
* type-ahead saved in BSD mode (was OK in SYSV and POSIX) |
* fixed POSIX mode bug and enabled termios use if POSIX defined. |
* allow caller to supply a prompt width calculation function so that the |
caller can embed escape sequences into the prompt (see gl_strwidth in |
the man page). |
* added a getline.h header file for inclusion into the caller. |
* man page added, thanks to DaviD W. Sanderson (dws@cs.wisc.edu) |
|
|
Changes from previous release (v25i056 and patch v26i092) |
|
* The user no longer calls gl_init() and gl_cleanup(), getline() sets |
required terminal modes on entry and resets before returning. This |
was necessary to capture changes in terminal modes that the main |
program might be making. |
* Getline() now looks to see which characters are bound to signal |
generation, and when these characters are seen getline() resets |
terminal modes before passing on the signal. If the signal handler |
returns to getline(), the screen is automatically updated and editing |
can continue. |
* The toggle key for overwrite mode has been moved from ^G to ^O |
* All code is now classic rather than ANSI C, so any compiler should |
be able to handle it. |
* ^Y now yanks back previously kill'ed (^K) text starting at the |
current location. |
* ^R/^S begin reverse and forward incremental searches through the |
history list. |
* The programmer must add buffers onto the history list by calling |
gl_addhist(char *buffer). This function makes copies of the buffer |
and adds them to the history list if the buffer is not a blank line |
and if the buffer is different than the last item saved (so the |
program need not check for these conditions) |
* The main program can specify the screen width to use with a call to |
gl_setwidth(int width) |
* Getline now insists that both the input and output are connected to |
a terminal. If this is not the case, an error message is written and |
the program is terminated. The main program should check for this |
case and use buffered IO (stdio) for non-interactive sessions. |
|
/getline.3
0,0 → 1,376
.\" Note that in silly ol' [nt]roff, even trailing white space is |
.\" significant. I went through and eliminated it. |
.\" I adopted a convention of using a bold 'getline' when referring to |
.\" the whole package, but an italic 'getline' when referring to the |
.\" specific function. |
.\" Note that in [nt]roff that "-" is a hyphen, while "\-" is a dash. |
.\" I adjusted some source text lines to keep them short (I keep line |
.\" numbers turned on in vi, so I only have 72 cols w/o wrapping). |
.\" It's too bad that getline() doesn't realloc() the buffer as |
.\" necessary. Then it could have an unlimited input line length. |
.\" Note that .RI et al are limited in how many args they can take. |
.\" I corrected gl_addhist to gl_histadd, which is what is actually |
.\" used! Perhaps it really should be gl_addhist, to preserve the |
.\" gl_<verb><object> pattern in the other function names. |
.\" I tried to rephrase certain sections to avoid the passive voice. |
.\" I find the active voice much easier to understand, since I can tell |
.\" more easily what acts on what. |
.TH GETLINE 3 |
.SH NAME |
getline \- command-line editing library with history |
.SH SYNOPSIS |
.RI "char *gl_getline(char *" prompt ); |
.PP |
.RI "void gl_histadd(char *" line ); |
.br |
.RI "void gl_setwidth(int " width ); |
.br |
.RI "void gl_strwidth(int " (*width_func)() ); |
.PP |
.RI "extern int (*gl_in_hook)(char *" buf ); |
.br |
.RI "extern int (*gl_out_hook)(char *" buf ); |
.br |
.RI "extern int (*gl_tab_hook)(char *" buf , |
.RI "int " prompt_width ", int *" cursor_loc ); |
.SH DESCRIPTION |
The |
.B getline |
package is a set of library routines that implement |
an editable command-line history. |
.PP |
.B "Programming Interface" |
.br |
.I getline |
returns a pointer to a line of text read from the user, |
prompting the user with the specified |
.IR prompt . |
The pointer refers to a static buffer allocated by the |
.B getline |
package. |
Clients may assume that the pointer |
.I getline |
returns is always the same, and is never NULL. |
The buffer |
.I getline |
returns to the caller contains the terminating newline character, |
except on end of file, |
in which case the first character in the buffer is 0 |
.RB ( NUL ). |
File descriptors 0 and 1 must be connected to the terminal |
(not redirected), |
so the caller should check for this condition (using |
.IR isatty (\|)) |
and call stdio routines if the session is not interactive. |
.PP |
.I gl_histadd |
adds the given |
.I line |
to the |
.B getline |
history list if the |
.I line |
is not empty and if it is different from the last line |
in the history list |
(so the caller need not check for these conditions). |
.I gl_histadd |
makes its own copies of all the lines it adds to the history list. |
This is so the caller can reuse the buffer it supplies to |
.IR gl_histadd . |
.PP |
.I gl_setwidth |
specifies the terminal |
.I width |
to use for horizontal scrolling. |
The default value is 80 columns, |
and it is important to properly specify the |
.I width |
or lines may wrap inadvertently. |
.PP |
.I gl_strwidth |
allows the application program to supply a prompt string width calculation |
function that returns the number of screen positions used by the argument |
string. |
By default strlen is used, but if the prompt contains escape sequences the user |
can bind a function that returns the actual number of screen postitions |
used by the argument string, not including the escape characters. |
.PP |
In addition to the function call interface, |
.B getline |
has three externally accessible function pointers |
that act as hooks if bound to user-defined functions. |
.B getline |
supplies each of the functions with a pointer to the current buffer |
as the first argument, |
and expects the return value to be the index |
of the first change the function made in the buffer |
(or \-1 if the function did not alter the buffer). |
After the functions return, |
.B getline |
updates the screen as necessary. |
.\"------- |
.\" DWS comment -- |
.\"------- |
Note that the functions may not alter the size of the buffer. |
Indeed, they do not even know how large the buffer is! |
.PP |
.I getline |
calls |
.I gl_in_hook |
(initially NULL) |
each time it loads a new buffer. |
More precisely, this is |
.TP |
\(bu |
at the first call to |
.I getline |
(with an empty buffer) |
.TP |
\(bu |
each time the user enters a new buffer from the history list (with |
.B ^P |
or |
.BR ^N ) |
.TP |
\(bu |
when the user accepts an incremental search string |
(when the user terminates the search). |
.PP |
.I getline |
calls |
.I gl_out_hook |
(initially NULL) |
when a line has been completed by the user entering a |
.B NEWLINE |
.RB (\| ^J \|) |
or |
.B RETURN |
.RB (\| ^M \|). |
The buffer |
.I gl_out_hook |
sees does not yet have the newline appended, and |
.I gl_out_hook |
should not add a newline. |
.PP |
.I getline |
calls |
.I gl_tab_hook |
whenever the user types a |
.BR TAB . |
In addition to the buffer, |
.I getline |
supplies the current |
.I prompt_width |
(presumably needed for tabbing calculations) and |
.IR cursor_loc , |
a pointer to the cursor location. |
.RI ( *cursor_loc |
\(eq 0 corresponds to the first character in the buffer) |
.I *cursor_loc |
tells |
.I gl_tab_hook |
where the |
.B TAB |
was typed. |
Note that when it redraws the screen, |
.I getline |
will honor any change |
.I gl_tab_hook |
may make to |
.IR *cursor_loc . |
.\"------- |
.\" DWS comment -- |
.\"------- |
Note also that |
.I prompt_width |
may not correspond to the actual width of |
.I prompt |
on the screen if |
.I prompt |
contains escape sequences. |
.I gl_tab_hook |
is initially bound to the |
.B getline |
internal static function |
.IR gl_tab , |
which acts like a normal |
.B TAB |
key by inserting spaces. |
.PP |
.B "User Interface" |
.br |
.\"------- |
.\" I adapted the prologue to this section from the ksh man page (dws). |
.\"------- |
To edit, the user moves the cursor to the point needing correction and |
then inserts or deletes characters or words as needed. |
All the editing commands are control characters, |
which typed by holding the |
CTRL key down while typing another character. |
Control characters are indicated below as the caret |
.RB (\| ^ \|) |
followed by another character, |
such as |
.BR ^A . |
.PP |
All edit commands operate from any place on the line, |
not just at the beginning. |
.PP |
These are the |
.I getline |
key bindings. |
.\"------- |
.\" Tt - max width of tag |
.\" Tw - max width of tag + spacing to the paragraph |
.\" Tp - special .TP, with the best indent for the editing command |
.\" descriptions. |
.\" The first version of Tp prints the tags left-justified. |
.\" The second version of Tp prints the tags as follows: |
.\" If one argument is given, it is printed right-justified. |
.\" If two arguments are given, the first is printed left-justified |
.\" and the second is printed right-justified. |
.\"------- |
.nr Tt \w'BACKSPACE' |
.nr Tw \w'BACKSPACE\0\0\0' |
.\" .de Tp |
.\" .TP \n(Twu |
.\" \fB\\$1\fR |
.\" .. |
.de Tp |
.TP \n(Twu |
.if \\n(.$=1 \h@\n(Ttu-\w'\fB\\$1\fR'u@\fB\\$1\fR |
.if \\n(.$=2 \fB\\$1\fR\h@\n(Ttu-\w'\fB\\$1\\$2\fR'u@\fB\\$2\fR |
.. |
.PP |
.\"------- |
.\" Set interparagraph spacing to zero so binding descriptions are |
.\" kept together. |
.\"------- |
.PD 0 |
.Tp "^A" |
Move cursor to beginning of line. |
.Tp "^B" |
Move cursor left (back) 1 column. |
.Tp ESC-B |
Move cursor back one word. |
.Tp "^D" |
Delete the character under the cursor. |
.Tp "^E" |
Move cursor to end of line. |
.Tp "^F" |
Move cursor right (forward) 1 column. |
.Tp ESC-F |
Move cursor forward one word. |
.Tp "^H" |
Delete the character left of the cursor.@ |
.Tp "^I" |
Jump to next tab stop (may be redefined by the program). |
.Tp "^J" |
Return the current line. |
.Tp "^K" |
Kill from cursor to the end of the line (see |
.BR "^Y" \|). |
.Tp "^L" |
Redisplay current line. |
.Tp "^M" |
Return the current line. |
.Tp "^N" |
Fetches next line from the history list. |
.Tp "^O" |
Toggle overwrite/insert mode, initially in insert mode. |
.Tp "^P" |
Fetches previous line from the history list. |
.Tp "^R" |
Begin a reverse incremental search through history list. |
Each printing character typed adds to the search substring |
(initially empty), and |
.B getline |
finds and displays the first matching location. |
Typing |
.B ^R |
again marks the current starting location and begins a new |
search for the current substring. |
Typing |
.B ^H |
or |
.B DEL |
deletes the last character from the search string, |
and |
.B getline |
restarts the search from the last starting location. |
Repeated |
.B ^H |
or |
.B DEL |
characters therefore appear to unwind the search to the match nearest |
the point where the user last typed |
.B ^R |
or |
.BR ^S . |
Typing |
.B ^H |
or |
.B DEL |
until the search string is empty causes |
.B getline |
to reset the start of the search to the beginning of the history list. |
Typing |
.B ESC |
or any other editing character accepts the current match |
and terminates the search. |
.Tp "^S" |
Begin a forward incremental search through the history list. |
The behavior is like that of |
.B ^R |
but in the opposite direction through the history list. |
.Tp "^T" |
Transpose current and previous character. |
.Tp "^U" |
Kill the entire line (see |
.BR "^Y" \|). |
.Tp "^Y" |
Yank previously killed text back at current location. |
.Tp BACKSPACE |
Delete the character left of the cursor. |
.Tp DEL |
Delete the character left of the cursor. |
.Tp RETURN |
Return the current line. |
.Tp TAB |
Jump to next tab stop (may be redefined by the program). |
.\"------- |
.\" Restore default interparagraph spacing. |
.\"------- |
.PD |
.PP |
.B getline |
recognizes DOS and ANSI arrow keys. |
They cause the following actions: |
.B up |
is the same as |
.BR ^P , |
.B down |
is the same as |
.BR ^N , |
.B left |
is the same as |
.BR ^P , |
and |
.B right |
is the same as |
.BR ^F . |
.SH AUTHORS |
.PP |
Program by |
Christopher R. Thewalt (thewalt\|@ce.berkeley.edu) |
.PP |
Original man page by |
DaviD W. Sanderson (dws\|@cs.wisc.edu) |
and Christopher R. Thewalt |
.SH COPYRIGHT |
\& |
.br |
.if n (C) |
.if t \s+8\v'+2p'\fB\(co\fR\v'-2p'\s0 |
\s+2Copyright 1992,1993 by Christopher R. Thewalt and DaviD W. Sanderson\s0 |
(but freely redistributable) |
/index.html
0,0 → 1,210
<HTML><HEAD> |
<TITLE>Index of /usr/skunk/src/Tools/getline</TITLE> |
</HEAD><BODY> |
<H1>Index of /usr/skunk/src/Tools/getline</H1> |
<PRE><IMG SRC="/icons/blank.xbm" ALT=" "> Name Last modified Size Description |
<HR> |
<IMG SRC="/icons/back.xbm" ALT="[DIR]"> <A HREF="/usr/skunk/src/Tools/">Parent Directory</A> 01-Aug-95 06:43 - |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="CHANGES">CHANGES</A> 03-May-95 10:08 3k |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="Makefile">Makefile</A> 21-Jun-95 05:42 1k |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="getline.3">getline.3</A> 03-May-95 10:08 9k |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="getline.c">getline.c</A> 03-May-95 10:08 28k |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="getline.h">getline.h</A> 03-May-95 10:08 1k |
<IMG SRC="/icons/unknown.xbm" ALT="[ ]"> <A HREF="testgl.c">testgl.c</A> 03-May-95 10:08 1k |
</PRE><HR> |
<PRE> |
*************************** Motivation ********************************** |
|
Many interactive programs read input line by line, but would like to |
provide line editing and history functionality to the end-user that |
runs the program. |
|
The input-edit package provides that functionality. As far as the |
programmer is concerned, the program only asks for the next line |
of input. However, until the user presses the RETURN key they can use |
emacs-style line editing commands and can traverse the history of lines |
previously typed. |
|
Other packages, such as GNU's readline, have greater capability but are |
also substantially larger. Input-edit is small, since it uses neither |
stdio nor any termcap features, and is also quite portable. It only uses |
\b to backspace and \007 to ring the bell on errors. Since it cannot |
edit multiple lines it scrolls long lines left and right on the same line. |
|
Input edit uses classic (not ANSI) C, and should run on any Unix |
system (BSD, SYSV or POSIX), PC's under DOS with MSC, TurboC or djgpp, |
PC's under OS/2 with gcc (EMX), or Vax/VMS. Porting the package to new |
systems basicaly requires code to read a character when it is typed without |
echoing it, everything else should be OK. |
|
I have run the package on: |
|
DECstation 5000, Ultrix 4.3 with cc 2.1 and gcc 2.3.3 |
Sun Sparc 2, SunOS 4.1.1, with cc |
SGI Iris, IRIX System V.3, with cc |
PC using DOS with MSC |
|
The description below is broken into two parts, the end-user (editing) |
interface and the programmer interface. Send bug reports, fixes and |
enhancements to: |
|
Chris Thewalt (thewalt@ce.berkeley.edu) |
5/3/93 |
|
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 |
|
PS: I don't have, and don't want to add, a vi mode, sorry. |
|
************************** End-User Interface *************************** |
|
Entering printable keys generally inserts new text into the buffer (unless |
in overwrite mode, see below). Other special keys can be used to modify |
the text in the buffer. In the description of the keys below, ^n means |
Control-n, or holding the CONTROL key down while pressing "n". Errors |
will ring the terminal bell. |
|
^A/^E : Move cursor to beginning/end of the line. |
^F/^B : Move cursor forward/backward one character. |
ESC-F : Move cursor forward one word. |
ESC-B : Move cursor backward one word. |
^D : Delete the character under the cursor. |
^H, DEL : Delete the character to the left of the cursor. |
^K : Kill from the cursor to the end of line. |
^L : Redraw current line. |
^O : Toggle overwrite/insert mode. Initially in insert mode. Text |
added in overwrite mode (including yanks) overwrite |
existing text, while insert mode does not overwrite. |
^P/^N : Move to previous/next item on history list. |
^R/^S : Perform incremental reverse/forward search for string on |
the history list. Typing normal characters adds to the current |
search string and searches for a match. Typing ^R/^S marks |
the start of a new search, and moves on to the next match. |
Typing ^H or DEL deletes the last character from the search |
string, and searches from the starting location of the last search. |
Therefore, repeated DEL's appear to unwind to the match nearest |
the point at which the last ^R or ^S was typed. If DEL is |
repeated until the search string is empty the search location |
begins from the start of the history list. Typing ESC or |
any other editing character accepts the current match and |
loads it into the buffer, terminating the search. |
^T : Toggle the characters under and to the left of the cursor. |
^U : Deletes the entire line |
^Y : Yank previously killed text back at current location. Note that |
this will overwrite or insert, depending on the current mode. |
TAB : By default adds spaces to buffer to get to next TAB stop |
(just after every 8th column), although this may be rebound by the |
programmer, as described below. |
NL, CR : returns current buffer to the program. |
|
DOS and ANSI terminal arrow key sequences are recognized, and act like: |
|
up : same as ^P |
down : same as ^N |
left : same as ^B |
right : same as ^F |
|
************************** Programmer Interface *************************** |
|
The programmer accesses input-edit through these functions, and optionally |
through three additional function pointer hooks. The four functions are: |
|
char *gl_getline(char *prompt) |
|
Prints the prompt and allows the user to edit the current line. A |
pointer to the line is returned when the user finishes by |
typing a newline or a return. Unlike GNU readline, the returned |
pointer points to a static buffer, so it should not be free'd, and |
the buffer contains the newline character. The user enters an |
end-of-file by typing ^D on an empty line, in which case the |
first character of the returned buffer is '\0'. Getline never |
returns a NULL pointer. The getline functions sets terminal modes |
needed to make it work, and resets them before returning to the |
caller. The getline function also looks for characters that would |
generate a signal, and resets the terminal modes before raising the |
signal condition. If the signal handler returns to getline, |
the screen is automatically redrawn and editing can continue. |
Getline now requires both the input and output stream be connected |
to the terminal (not redirected) so the main program should check |
to make sure this is true. If input or output have been redirected |
the main program should use buffered IO (stdio) rather than |
the slow 1 character read()s that getline uses. |
|
void gl_setwidth(int width) |
|
Set the width of the terminal to the specified width. The default |
width is 80 characters, so this function need only be called if the |
width of the terminal is not 80. Since horizontal scrolling is |
controlled by this parameter it is important to get it right. |
|
void gl_histadd(char *buf) |
|
The gl_histadd function checks to see if the buf is not empty or |
whitespace, and also checks to make sure it is different than |
the last saved buffer to avoid repeats on the history list. |
If the buf is a new non-blank string a copy is made and saved on |
the history list, so the caller can re-use the specified buf. |
|
void gl_strwidth(size_t (*func)()) |
The gl_strwidth function allows the caller to supply a pointer to |
a prompt width calculation function (strlen by default). This |
allows the caller to embed escape sequences in the prompt and then |
tell getline how many screen spaces the prompt will take up. |
|
The main loop in testgl.c, included in this directory, shows how the |
input-edit package can be used: |
|
extern char *getline(); |
extern void gl_histadd(); |
main() |
{ |
char *p; |
do { |
p = getline("PROMPT>>>> "); |
gl_histadd(p); |
fputs(p, stdout); |
} while (*p != 0); |
} |
|
In order to allow the main program to have additional access to the buffer, |
to implement things such as completion or auto-indent modes, three |
function pointers can be bound to user functions to modify the buffer as |
described below. By default gl_in_hook and gl_out_hook are set to NULL, |
and gl_tab_hook is bound to a function that inserts spaces until the next |
logical tab stop is reached. The user can reassign any of these pointers |
to other functions. Each of the functions bound to these hooks receives |
the current buffer as the first argument, and must return the location of |
the leftmost change made in the buffer. If the buffer isn't modified the |
functions should return -1. When the hook function returns the screen is |
updated to reflect any changes made by the user function. |
|
int (*gl_in_hook)(char *buf) |
|
If gl_in_hook is non-NULL the function is called each time a new |
buffer is loaded. It is called when getline is entered, with an |
empty buffer, it is called each time a new buffer is loaded from |
the history with ^P or ^N, and it is called when an incremental |
search string is accepted (when the search is terminated). The |
buffer can be modified and will be redrawn upon return to getline(). |
|
int (*gl_out_hook)(char *buf) |
|
If gl_out_hook is non-NULL it is called when a line has been |
completed by the user entering a newline or return. The buffer |
handed to the hook does not yet have the newline appended. If the |
buffer is modified the screen is redrawn before getline returns the |
buffer to the caller. |
|
int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc) |
|
If gl_tab_hook is non-NULL, it is called whenever a tab is typed. |
In addition to receiving the buffer, the current prompt width is |
given (needed to do tabbing right) and a pointer to the cursor |
offset is given, where a 0 offset means the first character in the |
line. Not only does the cursor_loc tell the programmer where the |
TAB was received, but it can be reset so that the cursor will end |
up at the specified location after the screen is redrawn. |
</PRE> |
</BODY></HTML> |
/Makefile
0,0 → 1,26
# |
# Makefile for the getline library |
# |
|
CC = gcc -m32 |
CFLAGS = -O -DPOSIX |
LDFLAGS = -L. |
LDLIBS = -lgetline |
|
.PHONY: all install clean |
|
all: libgetline.a testgl |
|
install: libgetline.a testgl |
|
testgl: libgetline.a testgl.o |
$(CC) $(LDFLAGS) $(CFLAGS) -o testgl testgl.o $(LDLIBS) |
|
libgetline.a: getline.o |
ar cr libgetline.a getline.o |
|
%.o: %.c |
$(CC) $(CFLAGS) -o $@ -c $< |
|
clean: |
rm -f *~ *.o *.a testgl |
/README
0,0 → 1,194
|
*************************** Motivation ********************************** |
|
Many interactive programs read input line by line, but would like to |
provide line editing and history functionality to the end-user that |
runs the program. |
|
The input-edit package provides that functionality. As far as the |
programmer is concerned, the program only asks for the next line |
of input. However, until the user presses the RETURN key they can use |
emacs-style line editing commands and can traverse the history of lines |
previously typed. |
|
Other packages, such as GNU's readline, have greater capability but are |
also substantially larger. Input-edit is small, since it uses neither |
stdio nor any termcap features, and is also quite portable. It only uses |
\b to backspace and \007 to ring the bell on errors. Since it cannot |
edit multiple lines it scrolls long lines left and right on the same line. |
|
Input edit uses classic (not ANSI) C, and should run on any Unix |
system (BSD, SYSV or POSIX), PC's under DOS with MSC, TurboC or djgpp, |
PC's under OS/2 with gcc (EMX), or Vax/VMS. Porting the package to new |
systems basicaly requires code to read a character when it is typed without |
echoing it, everything else should be OK. |
|
I have run the package on: |
|
DECstation 5000, Ultrix 4.3 with cc 2.1 and gcc 2.3.3 |
Sun Sparc 2, SunOS 4.1.1, with cc |
SGI Iris, IRIX System V.3, with cc |
PC using DOS with MSC |
|
The description below is broken into two parts, the end-user (editing) |
interface and the programmer interface. Send bug reports, fixes and |
enhancements to: |
|
Chris Thewalt (thewalt@ce.berkeley.edu) |
5/3/93 |
|
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 |
|
PS: I don't have, and don't want to add, a vi mode, sorry. |
|
************************** End-User Interface *************************** |
|
Entering printable keys generally inserts new text into the buffer (unless |
in overwrite mode, see below). Other special keys can be used to modify |
the text in the buffer. In the description of the keys below, ^n means |
Control-n, or holding the CONTROL key down while pressing "n". Errors |
will ring the terminal bell. |
|
^A/^E : Move cursor to beginning/end of the line. |
^F/^B : Move cursor forward/backward one character. |
ESC-F : Move cursor forward one word. |
ESC-B : Move cursor backward one word. |
^D : Delete the character under the cursor. |
^H, DEL : Delete the character to the left of the cursor. |
^K : Kill from the cursor to the end of line. |
^L : Redraw current line. |
^O : Toggle overwrite/insert mode. Initially in insert mode. Text |
added in overwrite mode (including yanks) overwrite |
existing text, while insert mode does not overwrite. |
^P/^N : Move to previous/next item on history list. |
^R/^S : Perform incremental reverse/forward search for string on |
the history list. Typing normal characters adds to the current |
search string and searches for a match. Typing ^R/^S marks |
the start of a new search, and moves on to the next match. |
Typing ^H or DEL deletes the last character from the search |
string, and searches from the starting location of the last search. |
Therefore, repeated DEL's appear to unwind to the match nearest |
the point at which the last ^R or ^S was typed. If DEL is |
repeated until the search string is empty the search location |
begins from the start of the history list. Typing ESC or |
any other editing character accepts the current match and |
loads it into the buffer, terminating the search. |
^T : Toggle the characters under and to the left of the cursor. |
^U : Deletes the entire line |
^Y : Yank previously killed text back at current location. Note that |
this will overwrite or insert, depending on the current mode. |
TAB : By default adds spaces to buffer to get to next TAB stop |
(just after every 8th column), although this may be rebound by the |
programmer, as described below. |
NL, CR : returns current buffer to the program. |
|
DOS and ANSI terminal arrow key sequences are recognized, and act like: |
|
up : same as ^P |
down : same as ^N |
left : same as ^B |
right : same as ^F |
|
************************** Programmer Interface *************************** |
|
The programmer accesses input-edit through these functions, and optionally |
through three additional function pointer hooks. The four functions are: |
|
char *gl_getline(char *prompt) |
|
Prints the prompt and allows the user to edit the current line. A |
pointer to the line is returned when the user finishes by |
typing a newline or a return. Unlike GNU readline, the returned |
pointer points to a static buffer, so it should not be free'd, and |
the buffer contains the newline character. The user enters an |
end-of-file by typing ^D on an empty line, in which case the |
first character of the returned buffer is '\0'. Getline never |
returns a NULL pointer. The getline functions sets terminal modes |
needed to make it work, and resets them before returning to the |
caller. The getline function also looks for characters that would |
generate a signal, and resets the terminal modes before raising the |
signal condition. If the signal handler returns to getline, |
the screen is automatically redrawn and editing can continue. |
Getline now requires both the input and output stream be connected |
to the terminal (not redirected) so the main program should check |
to make sure this is true. If input or output have been redirected |
the main program should use buffered IO (stdio) rather than |
the slow 1 character read()s that getline uses. |
|
void gl_setwidth(int width) |
|
Set the width of the terminal to the specified width. The default |
width is 80 characters, so this function need only be called if the |
width of the terminal is not 80. Since horizontal scrolling is |
controlled by this parameter it is important to get it right. |
|
void gl_histadd(char *buf) |
|
The gl_histadd function checks to see if the buf is not empty or |
whitespace, and also checks to make sure it is different than |
the last saved buffer to avoid repeats on the history list. |
If the buf is a new non-blank string a copy is made and saved on |
the history list, so the caller can re-use the specified buf. |
|
void gl_strwidth(size_t (*func)()) |
The gl_strwidth function allows the caller to supply a pointer to |
a prompt width calculation function (strlen by default). This |
allows the caller to embed escape sequences in the prompt and then |
tell getline how many screen spaces the prompt will take up. |
|
The main loop in testgl.c, included in this directory, shows how the |
input-edit package can be used: |
|
extern char *gl_getline(); |
extern void gl_histadd(); |
main() |
{ |
char *p; |
do { |
p = gl_getline("PROMPT>>>> "); |
gl_histadd(p); |
fputs(p, stdout); |
} while (*p != 0); |
} |
|
In order to allow the main program to have additional access to the buffer, |
to implement things such as completion or auto-indent modes, three |
function pointers can be bound to user functions to modify the buffer as |
described below. By default gl_in_hook and gl_out_hook are set to NULL, |
and gl_tab_hook is bound to a function that inserts spaces until the next |
logical tab stop is reached. The user can reassign any of these pointers |
to other functions. Each of the functions bound to these hooks receives |
the current buffer as the first argument, and must return the location of |
the leftmost change made in the buffer. If the buffer isn't modified the |
functions should return -1. When the hook function returns the screen is |
updated to reflect any changes made by the user function. |
|
int (*gl_in_hook)(char *buf) |
|
If gl_in_hook is non-NULL the function is called each time a new |
buffer is loaded. It is called when getline is entered, with an |
empty buffer, it is called each time a new buffer is loaded from |
the history with ^P or ^N, and it is called when an incremental |
search string is accepted (when the search is terminated). The |
buffer can be modified and will be redrawn upon return to getline(). |
|
int (*gl_out_hook)(char *buf) |
|
If gl_out_hook is non-NULL it is called when a line has been |
completed by the user entering a newline or return. The buffer |
handed to the hook does not yet have the newline appended. If the |
buffer is modified the screen is redrawn before getline returns the |
buffer to the caller. |
|
int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc) |
|
If gl_tab_hook is non-NULL, it is called whenever a tab is typed. |
In addition to receiving the buffer, the current prompt width is |
given (needed to do tabbing right) and a pointer to the cursor |
offset is given, where a 0 offset means the first character in the |
line. Not only does the cursor_loc tell the programmer where the |
TAB was received, but it can be reset so that the cursor will end |
up at the specified location after the screen is redrawn. |
/getline.h
0,0 → 1,35
#ifndef GETLINE_H |
#define GETLINE_H |
|
/* unix systems can #define POSIX to use termios, otherwise |
* the bsd or sysv interface will be used |
*/ |
|
#if __STDC__ > 0 |
#include <stddef.h> |
|
typedef size_t (*gl_strwidth_proc)(char *); |
|
char *gl_getline(char *); /* read a line of input */ |
void gl_setwidth(int); /* specify width of screen */ |
void gl_histadd(char *); /* adds entries to hist */ |
void gl_strwidth(gl_strwidth_proc); /* to bind gl_strlen */ |
|
extern int (*gl_in_hook)(char *); |
extern int (*gl_out_hook)(char *); |
extern int (*gl_tab_hook)(char *, int, int *); |
|
#else /* not __STDC__ */ |
|
char *gl_getline(); |
void gl_setwidth(); |
void gl_histadd(); |
void gl_strwidth(); |
|
extern int (*gl_in_hook)(); |
extern int (*gl_out_hook)(); |
extern int (*gl_tab_hook)(); |
|
#endif /* __STDC__ */ |
|
#endif /* GETLINE_H */ |