home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
gnuplot32x.tar.Z
/
gnuplot32x.tar
/
os9gnuplot3
/
getline.c
next >
Wrap
Text File
|
1992-06-05
|
19KB
|
673 lines
#ifndef lint
static char rcsid[] =
"$Id: getline.c,v 1.8 1991/11/09 20:27:48 thewalt Exp thewalt $";
#endif
/*
* Fairly portable (ANSI C), emacs style line editing input package.
* This package uses \b to move, and \007 to ring the bell.
* It uses a fixed screen width, as initialized in the gl_init() call,
* and does not draw in the last location to avoid line wraps.
* The only non-portable part is how to turn off character echoing.
* This code works for *NIX of BSD or SYSV flavor, as well as MSDOS (MSC6.0).
* No TERMCAP features are used, so long lines are scrolled on one line
* rather than extending over several lines. The function getline
* returns a pointer to a static buffer area which holds the input string,
* including the newline. On EOF the first character is set equal to '\0'.
* The caller supplies a desired prompt, as shown in the prototype:
*
* char *getline(char *prompt)
*
* Getline is different from GNU readline in that:
* - getline has much more limited editing capabilities, but it
* is also much smaller and doesn't need termcap.
* - you don't free the buffer when done, since it is static
* (make a copy yourself if you want one)
* - the newline is appended to the buffer
* - you don't add lines to history, it is done automatically.
*
* The function gl_init(int screen_width) should be called before
* calling getline(char *prompt), and gl_cleanup(void) should be
* called before before exiting. The function gl_redraw(void) may also
* be called to redraw the screen (as is done when ^L or ^R are read).
*
* The editing keys are:
* ^A, ^E - move to beginnning or end of line, respectively.
* ^F, ^B - nondestructive move forward or back one location, respectively.
* ^D - delete the character currently under the cursor, or
* send EOF if no characters in the buffer.
* ^H, DEL - delete character left of the cursor.
* ^K - delete from cursor to end of line.
* ^P, ^N - move through history, previous and next, respectively.
* ^L, ^R - redraw the current line.
* TAB - call user defined function if bound, or insert spaces
* to get to next TAB stop (just past every 8th column).
* NL, CR - places line on history list if nonblank, calls output
* cleanup function if bound, appends newline and returns
* to the caller.
*
* In addition, the caller can modify the buffer in certain ways, which
* may be useful for things like auto-indent modes. There are three
* function pointers which can be bound to user functions.
* Each of these functions must return the index of the first location
* at which the buffer was modified, or -1 if the buffer wasn't modified.
* Each of the functions receive the current input buffer as the first
* argument. The screen is automatically cleaned up if the buffer is changed.
* The user is responsible for not writing beyond the end of the static
* buffer. The function pointer prototypes are:
*
* int (*gl_in_hook)(char *buf)
* - called on entry to getline, and each time a new history
* string is loaded, from a ^P or ^N. Initally NULL.
* int (*gl_out_hook)(char *buf)
* - called when a \n or \r is read, before appending \n and
* returning to caller. Initally NULL.
* int (*gl_tab_hook)(char *buf, int offset, int *loc)
* - called whenever a TAB is read. The second argument is
* the current line offset due to the width of the prompt.
* The third argument is a pointer to the variable holding the
* current location in the buffer. The location may be reset
* by the user to move the cursor when the call returns.
* Initially a built in tabbing function is bound.
*
* Please send bug reports, fixes and enhancements to Chris Thewalt,
* thewalt@ce.berkeley.edu
*/
/* Changed for general use on OS-9 68K systems by Norbert Nicolay
* nicolay@snert.ikp.uni-koeln.de
*
* Changed for use in gnuplot 3 for OS-9 systems by Dietmar Budelsky
* budelsky@snert.ikp.uni-koeln.de
*/
static char *copyright = "Copyright (C) 1991, Chris Thewalt";
/*
* Copyright (C) 1991 by Chris Thewalt
*
* 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.
*/
#include <stdio.h>
/* There is no stdlib.h on standard OSK machines */
#ifndef OSK
#include <stdlib.h>
#endif
#include <string.h>
#include <ctype.h>
extern int isatty();
#define BUF_SIZE 1024
#define SCROLL 30
static int gl_init_done = 0; /* -1 is terminal, 1 is batch */
static int gl_screen = 80; /* screen width */
static int gl_width = 0; /* net size available for input */
static int gl_pos, gl_cnt = 0; /* position and size of input */
static char gl_buf[BUF_SIZE]; /* input buffer */
static char *gl_prompt; /* to save the prompt string */
void gl_init(int);
void gl_cleanup(void); /* to undo gl_init */
void gl_redraw(void); /* issue \n and redraw all */
static void gl_char_init(void); /* get ready for no echo input */
static void gl_char_cleanup(void); /* undo gl_char_init */
static int gl_getchar(void); /* read one char from stdin */
static void gl_addchar(int); /* install specified char */
static void gl_newline(void); /* handle \n or \r */
static void gl_fixup(int, int); /* fixup state variables and screen */
static void gl_del(int); /* del, either left (-1) or cur (0) */
static void gl_kill(void); /* delete to EOL */
static int gl_tab(char *, int, int *); /* default TAB handler */
static void hist_add(void); /* adds nonblank entries to hist */
static void hist_init(void); /* initializes hist pointers */
static void hist_next(void); /* copies next entry to input buf */
static void hist_prev(void); /* copies prev entry to input buf */
static char *hist_save(char *); /* makes copy of a string */
int (*gl_in_hook)(char *) = 0;
int (*gl_out_hook)(char *) = 0;
int (*gl_tab_hook)(char *, int, int *) = gl_tab;
/************************ nonportable part *********************************/
#ifdef MSDOS
#include <bios.h>
#endif
#ifdef unix
#include <sys/ioctl.h>
#ifndef TIOCGETP
#include <termio.h>
struct termio tty, old_tty;
#else
#include <sgtty.h>
struct sgttyb tty, old_tty;
#endif /* TIOCGETP */
extern int ioctl();
#endif /* unix */
/* neccesary for OSK defs, changes by N. Nicolay */
#ifdef OSK
#include <sgstat.h>
/* changes by D. Budelsky for gnuplot on OSK */
#ifdef READLINE
#include "OS9comp.h"
#define stdout stderr
#endif
#define STDIN 0
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
static struct _sgs old_settings; /* old terminal settings */
static struct _sgs new_settings; /* new terminal settings */
static int old_stdio;
static int old_stdout;
static int initialized = FALSE; /* flag wether terminal is initial. */
#endif
static void
gl_char_init()
/* turn off input echo */
{
#ifdef unix
#ifdef TIOCGETP
ioctl(0, TIOCGETP, &old_tty);
tty = old_tty;
tty.sg_flags |= CBREAK;
tty.sg_flags &= ~ECHO;
ioctl(0, TIOCSETP, &tty);
#else
ioctl(0, TCGETA, &old_tty);
tty = old_tty;
tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
tty.c_cc[VMIN] = 1;
ioctl(0, TCSETA, &tty);
#endif
#endif /* unix */
/* by N. Nicolay */
#ifdef OSK
if (!initialized) {
int count, i, dummy;
count = _gs_rdy (STDIN);
for (i = 0; i < count; i++) read (STDIN, &dummy, 1);
_gs_opt (STDIN, &old_settings);
_gs_opt (STDIN, &new_settings);
new_settings._sgs_echo = (char) 0; /* switch off terminal echo */
new_settings._sgs_eofch = (char) 255; /* inhibit <ESC> = eof */
new_settings._sgs_kbich = (char) 255; /* inhibit ^C */
new_settings._sgs_kbach = (char) 255; /* inhibit ^E */
_ss_opt (STDIN, &new_settings);
#if 0
old_stdio = stdin->_flag; /* turn off buffering of stdin, stdout */
old_stdout = stdout->_flag;
stdin->_flag |= _UNBUF;
stdout->_flag |= _UNBUF;
#endif
count = _gs_rdy (STDIN);
for (i = 0; i < count; i++) read (STDIN, &dummy, 1);
initialized = TRUE;
}
#endif
}
static void
gl_char_cleanup()
/* undo effects of gl_char_init, as necessary */
{
#ifdef unix
#ifdef TIOCSETP
ioctl(0, TIOCSETP, &old_tty);
#else
ioctl(0, TCSETA, &old_tty);
#endif
#endif /* unix */
/* by N. Nicolay */
#ifdef OSK
if (initialized) {
_ss_opt (STDIN, &old_settings);
/*
stdin->_flag = old_stdio;
stdout->_flag = old_stdout;
*/
initialized = FALSE;
}
#endif
}
static int
gl_getchar()
/* get a character without echoing it to screen */
{
int c;
#ifdef unix
c = fgetc(stdin);
#endif
#ifdef OSK
c = getchar();
#endif
#ifdef MSDOS
c = _bios_keybrd(_NKEYBRD_READ) & 0177; /* only using 7 bit ASCII */
#endif
return c;
}
/******************** fairly portable part *********************************/
void
gl_init(int scrn_wdth)
/* set up variables and terminal */
{
gl_screen = scrn_wdth;
if (gl_init_done == 0) {
hist_init();
if (isatty(0) && isatty(1)) {
/* setbuf instead of setvbuf */
#ifndef OSK
setvbuf(stdin, (char *)0, _IONBF, 0);
setvbuf(stdout, (char *)0, _IONBF, 0);
#else
setbuf(stdin, (char *)0);
setbuf(stdout, (char *)0);
#endif
gl_char_init();
gl_init_done = -1; /* -1 means terminal */
} else {
gl_init_done = 1; /* 1 means batch */
}
}
gl_pos = gl_cnt = 0;
}
void
gl_cleanup()
/* undo effects of gl_init, as necessary */
{
if (gl_init_done == -1)
gl_char_cleanup();
gl_init_done = 0;
}
char *
getline(char *prompt)
{
int c, loc, tmp;
if (!gl_init_done)
gl_init(80);
gl_buf[0] = 0; /* used as end of input indicator */
if (gl_init_done == 1) { /* no input editing, and no prompt output */
fgets(gl_buf, BUF_SIZE, stdin);
return gl_buf;
}
gl_fixup(-1, 0); /* this resets gl_fixup */
gl_width = gl_screen - strlen(prompt);
if (prompt == NULL)
prompt = "";
gl_prompt = prompt;
gl_pos = gl_cnt = 0;
fputs(prompt, stdout);
if (gl_in_hook) {
loc = gl_in_hook(gl_buf);
if (loc >= 0)
gl_fixup(0, BUF_SIZE);
}
while ((c = gl_getchar()) != EOF) {
if (isprint(c)) {
gl_addchar(c);
} else {
switch (c) {
case '\n':
/* on OSK \r == \n */
#ifndef OSK
case '\r': /* newline */
#endif
gl_newline();
return gl_buf;
break;
case '\001': gl_fixup(-1, 0); /* ^A */
break;
case '\002': gl_fixup(-1, gl_pos-1); /* ^B */
break;
case '\004': /* ^D */
if (gl_cnt == 0) {
gl_buf[0] = 0;
gl_cleanup();
fputc('\n', stdout);
return gl_buf;
} else {
gl_del(0);
}
break;
case '\005': gl_fixup(-1, gl_cnt); /* ^E */
break;
case '\006': gl_fixup(-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, strlen(gl_prompt), &tmp);
if (loc >= 0 || tmp != gl_pos)
gl_fixup(loc, tmp);
}
break;
case '\013': gl_kill(); /* ^K */
break;
case '\014': case '\022': gl_redraw(); /* ^L and ^R */
break;
case '\016': hist_next(); /* ^N */
break;
case '\020': hist_prev(); /* ^P */
break;
default:
fputc('\007', stdout);
break;
}
}
}
gl_cleanup();
return gl_buf;
}
static void
gl_addchar(int c)
/* adds the character c to the input buffer at current location */
{
int i;
if (gl_cnt >= BUF_SIZE - 1) {
fprintf(stderr, "getline: input buffer overflow\n");
exit(1);
}
for (i=gl_cnt; i >= gl_pos; i--)
gl_buf[i+1] = gl_buf[i];
gl_buf[gl_pos] = c;
gl_fixup(gl_pos, gl_pos+1);
}
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) {
fprintf(stderr, "getline: input buffer overflow\n");
exit(1);
}
hist_add(); /* only adds if nonblank */
if (gl_out_hook) {
change = gl_out_hook(gl_buf);
len = strlen(gl_buf);
}
if (loc > len)
loc = len;
gl_fixup(change, loc); /* must do this before appending \n */
gl_buf[len] = '\n';
gl_buf[len+1] = '\0';
fputc('\n', stdout);
}
static void
gl_del(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_pos+loc, gl_pos+loc);
} else
fputc('\007', stdout);
}
static void
gl_kill()
/* delete from current position to the end of line */
{
if (gl_pos < gl_cnt) {
gl_buf[gl_pos] = '\0';
gl_fixup(gl_pos, gl_pos);
} else
fputc('\007', stdout);
}
void
gl_redraw()
/* emit a newline, reset and redraw prompt and current input line */
{
if (gl_init_done == -1) {
fputc('\n', stdout);
fputs(gl_prompt, stdout);
gl_pos = 0;
gl_fixup(0, BUF_SIZE);
}
}
static void
gl_fixup(int change, int cursor)
/*
* This function is used both for redrawing when input changes or for
* moving within the input line. The parameters are:
* change : the index of the start of changes in the input buffer,
* with -1 indicating no changes.
* 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 */
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;
if (change == -1 && cursor == 0 && gl_buf[0] == 0) { /* reset */
gl_shift = off_right = off_left = 0;
return;
}
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 */
fputc('\007', stdout);
cursor = gl_cnt;
}
if (cursor < 0) {
fputc('\007', stdout);
cursor = 0;
}
if (off_right || off_left && cursor < gl_shift + gl_width - SCROLL / 2)
extra = 2; /* shift the scrolling boundary */
else
extra = 0;
new_shift = cursor + extra + SCROLL - gl_width;
if (new_shift > 0) {
new_shift /= SCROLL;
new_shift *= 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;
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;
}
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++)
fputc('\b', stdout);
if (left == gl_shift && off_left) {
fputc('$', stdout);
left++;
}
for (i=left; i < right; i++)
fputc(gl_buf[i], stdout);
if (off_right) {
fputc('$', stdout);
gl_pos = right + 1;
} else {
for (i=0; i < pad; i++) /* erase remains of prev line */
fputc(' ', stdout);
gl_pos = right + pad;
}
}
i = gl_pos - cursor; /* move to final cursor location */
if (i > 0) {
while (i--)
fputc('\b', stdout);
} else {
for (i=gl_pos; i < cursor; i++)
fputc(gl_buf[i], stdout);
}
gl_pos = cursor;
}
static int
gl_tab(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;
}
/******************* History stuff **************************************/
#ifndef HIST_SIZE
#define HIST_SIZE 100
#endif
int hist_pos, hist_last;
char *hist_buf[HIST_SIZE];
static void
hist_init()
{
int i;
for (i=0; i < HIST_SIZE; i++)
hist_buf[i] = (char *)NULL;
}
static void
hist_add()
{
char *p = gl_buf;
while (*p == ' ' || *p == '\t') /* only save nonblank line */
p++;
if (*p) {
hist_buf[hist_last] = hist_save(gl_buf);
hist_last = (hist_last + 1) % HIST_SIZE;
if (hist_buf[hist_last]) { /* erase next location */
free(hist_buf[hist_last]);
hist_buf[hist_last] = NULL;
}
}
hist_pos = hist_last;
}
static void
hist_prev()
/* loads previous hist entry into input buffer, sticks on first */
{
int next;
next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
if (next != hist_last) {
if (hist_buf[next]) {
hist_pos = next;
strcpy(gl_buf, hist_buf[hist_pos]);
} else
fputc('\007', stdout);
} else
fputc('\007', stdout);
if (gl_in_hook)
gl_in_hook(gl_buf);
gl_fixup(0, BUF_SIZE);
}
static void
hist_next()
/* loads next hist entry into input buffer, clears on last */
{
if (hist_pos != hist_last) {
hist_pos = (hist_pos + 1) % HIST_SIZE;
if (hist_buf[hist_pos]) {
strcpy(gl_buf, hist_buf[hist_pos]);
} else {
gl_buf[0] = 0;
}
} else
fputc('\007', stdout);
if (gl_in_hook)
gl_in_hook(gl_buf);
gl_fixup(0, BUF_SIZE);
}
static char *
hist_save(char *p)
/* makes a copy of the string */
{
char *s = NULL;
/* malloc not defined in stdlib.h on OSK */
#ifdef OSK
if (p && ((s = (char *)malloc(strlen(p)+1)) != NULL)) {
#else /*def OSK*/
if (p && ((s = malloc(strlen(p)+1)) != NULL)) {
#endif
strcpy(s, p);
}
return s;
}