OpenCores
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, &ltch);
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&gt;&gt;&gt;&gt; ");
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 */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.