Line discipline part 3 of 4
Dave Shepperd
shepperd at dms.UUCP
Fri Nov 10 06:37:55 AEST 1989
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by shepperd on Wed Nov 8 20:49:53 PST 1989
# Contents: cledaa space.c.awk space.c.rawk
echo x - cledaa
sed 's/^@//' > "cledaa" <<'@//E*O*F cledaa//'
/*
cled.c
A simple line editor intended for command line editing on Xenix with
a ANSI type terminal using the line discipline mode of terminal operation.
Authors:
Dave Shepperd (dms). Atari Games, corp. (shepperd at dms.UUCP):
hacked lvr's editor to bits to make it re-entrant
and added the line discipline and device interfaces
for SCO Xenix/386.
Lyle Rains (lvr). Atari Games, corp. (rains at dms.UUCP):
wrote the editor in 1983 or so for a RT-11
system intended to be included as a library
module.
Copyright 1989 Atari Games, Corp. Reproduction, adaptation, distribution,
performance or display of this computer program or the associated documentation
is not prohibited provided this copyright notice accompanies said reproduction,
adaptation, distribution, performance or display.
Edit history:
891101_dms 1.7 Fixed numerous problems with cledsetup and the ioctl
interface.
891029_dms 1.6 Added support for a bunch more function keys.
891026_dms 1.5 Ported to SCO UNIX. Changed delete word right/left
function to not delete ws and behave the same both
forward and backward. Increased the number of tty
buffs to 24 and led bufs to 32.
890907_dms 1.4 Added CLEKEY_HOME and CLEKEY_END (\033[H and \033[E
respectively).
890630_dms 1.3 Outputting a message during l_close sometimes caused
the process to hang which would then have to be killed
to unhang it (l_close is called during logout). I changed
the message output sequence during l_close to wait 1
second then flush the input and output before
returning control.
890614_dms 1.2 Made several major mods which include adding #defines
to enable/disable multi-led_buf mode and removing
all dependencies on sptalloc/sptfree if a #define so
indicates. The dependencies (_SPTALLOC and MULTI_LB)
also will shrink the sizes of the led_buf and tty_buf
structs. This is mainly so it'll have a hope in hell
of working on a 286 system. In non-multi lb mode, all
dependencies on specific processes are removed which
means prompts may screw up and there'll be only one
history buffer per terminal. Added banner messages
when cled is turned on and off.
890605_dms 1.1 With a little help from a new document from SCO, I
found out how to pass control to shl. It also pointed
out an error I had in the calling of l_ioctl. I wasn't
passing enough arguments. This could have caused some
of the trouble I'd been having. Allowed all chars to
be escaped by the superquote char (including INTR,
et al).
890604_dms 1.1 Some process somewhere at sometime is clobbering the
tty struct AFTER cleread has already gone to sleep.
The iflag and lflag fields get set to 0 but the most
important "bug" is that the line field gets changed to 0
which prevents cleinput from being called at keyboard input,
so it never wakes up from the sleep (unless the process is
killed). Not being able to find where this is happening,
I punted and put a timeout in the read loop. If it ever
times out and the line field is not set to "us", then it
prints a message on the user's terminal as well as the
console and "fixes" the tty struct to what it was when the
read was started. This problem rarely happens, but I've
noticed it occuring the most frequently after exiting telnet.
Added code to allow a program to alter the key bindings.
Added version number and line # and ver # reporting at init.
890525_dms 0.0 The reading of "owed" data wasn't handled properly. It should
have returned from the read after delivering any owed chars
rather than dropping through to a normal read. This caused
a second newline to be entered for the program to see the first.
890517_dms 0.0 Fixed a potential timing problem with terminal
writes intermingled with terminal reads. Fixed a
problem in broadcast message handling (didn't
properly handle multiple long messages).
890516_dms 0.0 Changed all char's and char *'s to unsigned
890515_dms 0.0 Didn't check for no tty_buffers in clewrite before
calling l_write which panic'd sometimes and other times
just clobbered the tty struct (I think). It's
fixed.
890511_dms 0.0 Made all the edit functions "bindable" to any of the
control, keypad and cursor control keys.
890501_dms 0.0 First unleashed upon the unsuspecting (but greatful) users.
*/
#define ctlA 001 /* control characters */
#define ctlB 002
#define ctlE 005
#define ctlF 006
#define BEL 007
#define BKSP 010
#define TAB 011
#define LF 012
#define ctlK 013
#define FF 014
#define CR 015
#define ctlP 020
#define XON 021
#define ctlR 022
#define XOFF 023
#define ctlW 027
#define ESC 033
#define SPACE 040
#define RUB 0177
#define NORMAL 0x000
#define ESCAPE -0x100
#define KEYPAD -0x200
#define CURSKEY -0x300
#define NCC_SPC -0x400
#define ESCCHAR -0x500
#define FUNCKEY -0x600
#define FLAG unsigned char
#define YES 1 /* some flag states */
#define NO 0
#define DEL 1
#define NODEL 0
#ifndef MB_DEBUG
#define MB_DEBUG 0 /* set true for multi-ledbuf debug code */
#endif
#ifndef M_KERNEL
#include <stdio.h>
#ifdef M_XENIX
#include <malloc.h>
#endif
#define cle_puts(str,tp) fputs(str,stderr)
#define cle_putc(chr,tp) fputc(chr,stderr)
#define cle_getc(tp) fgetc(stdin)
#define passc(c) fputc(chr,stderr)
#else
static int cle_putc(),cle_getc(),cle_puts();
#endif
#include <sys/types.h>
#if defined(M_UNIX) && defined(NULL)
#undef NULL
#endif
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/tty.h>
#if defined(M_UNIX)
#include <sys/termio.h>
#include <sys/immu.h>
#include <sys/region.h>
#endif
#include <sys/conf.h>
#include <sys/errno.h>
#ifdef M_KERNEL
#ifdef M_I386
# include <sys/page.h>
# include <sys/seg.h>
#endif
#include <sys/proc.h>
#include <sys/var.h>
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/user.h>
#if defined(M_UNIX) && defined(ftop)
#undef ftop
#endif
#include <sys/mmu.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#if defined(M_UNIX)
#include <sys/cmn_err.h>
#endif
#endif
#define min(a,b) ((a) < (b) ? (a) : (b))
#include "cled_structs.h"
#define Oldend(lb) (lb->key-1)
/*
* The old/key commands buffer is arranged as follows:
|<----------------- HISTBUFSIZ = n ------------------->|
| |
*********************************************************
[ old comm buffer ][ key defn buffer ]
*********************************************************
^ ^^
\_old oldend_/ \_key
* oldend/key varies with how much stuff is in the key defn buffer.
* The old commands buffer gets whatever space is left.
* oldndx starts at 0 (an always empty line with length 1).
* For the latest version, the key defn buffer is always only 1 byte long.
*
* The new command buffer is arranged with a bubble at the cursor for
* insertion of new text:
|<---------------------- maxlin ----------------------->|
| |
*********************************************************
[ front ] (bubble) [ back ]
*********************************************************
^ ^ ^ ^
\_buf \_lcurs=NULL rcurs_/ bufend=NULL_/
*
* The text from buf to lcurs is what is displayed left of the cursor.
* The text from rcurs to bufend is what is displayed right of the cursor.
* Normally, lcurs points to the NULL at the end of the string. bufend
* doesn't move and always points to a NULL at the end of the buffer.
* When the buffer fills up (lcurs = rcurs), no more data is accepted
* and a BELL code is sent to the terminal.
*
* The behavior of the terminal echo under this line discipline is
* different than what you might be used to in the sense that characters
* are only echoed if there is a read pending on the device (ala VMS, MS-DOS
* and other O/S's). Characters input are held in a typeahead buffer (t_rawq).
* When a read to the port is executed, the characters are removed from the
* typeahead buffer and processed by the parser. The parser determines what
* (if any) characters should be echoed for each character input. This also
* means that if an ioctl function is performed that turns off echo after
* characters have been placed in the typeahead buffer, but before a read
* is issued, those characters may not be echoed at all by the driver
* (they will, however, be delivered to the process that issues the first
* read which may choose to echo them itself).
*
*/
static unsigned char our_lineno; /* our lineno */
/************************************************************************
* The following n routines comprise the editor proper. The process *
* context in all cases is task time. None of these routines are *
* (nor should they ever be) called at interrupt time. *
************************************************************************/
/***********************************************************************
* This routine is a dummy routine that simply marks the start of cled
* in the map file (for trouble shooting)
*/
void cled_beginmark()
{
return;
}
/***********************************************************************
* Put a string of text into the command buffer, echoing them as they
* are inserted.
*/
static int putt(cp, cnt, lb)
register char *cp;
int cnt;
struct led_buf *lb;
/*
* At entry:
* cp - ptr to string of chars to be copied
* cnt - count of chars to copy
* lb - ptr to led_buf into which to place the text
* At exit:
* chars moved into lb->buf[lb->lcurs].
* lb->lcurs adjusted as required
* lb->rcurs adjusted if overstrike mode
* lb->flags may be set
* chars echoed as required
*/
{
register unsigned char **rc, **lc;
unsigned short iflg;
struct tty *typ;
rc = &lb->rcurs; /* point to right side of hole */
lc = &lb->lcurs; /* point to left side of hole */
typ = lb->ttybf->ttyp;
iflg = typ->t_iflag;
while ((*lc < *rc - 1) && cnt--) { /* while chars and no overlap */
unsigned char c;
c = *cp;
if ((iflg&ISTRIP) != 0) c &= 0x7F;
if ((iflg&IUCLC) != 0 && (c >= 'A') && (c <= 'Z')) c += 'a'-'A';
lb->flags |= LD_DIRTY; /* buffer has been modified */
**lc = echo((*cp++ = c),lb); /* stuff the char and echo it */
*lc += 1; /* move left pointer */
if ((lb->flags&LD_INSERT) == 0 && **rc) { /* move right ptr if overstrike... */
*rc += 1 ; /* ...mode and not already on null */
}
}
**lc = 0; /* make sure left side is pointing to 0 */
if (cnt >= 0) cle_putc(BEL,typ);
pback(lb,NO);
}
/*************************************************************************
* Find pointer to n'th string in a history or key define buffer.
*/
static unsigned char *ptr(start,ndx)
unsigned char *start;
int ndx;
/*
* At entry:
* start - ptr into buffer where search is to begin
* ndx - the number of the string to find
* At exit:
* returns ptr to first char of requested string if present
* else it returns ptr to null string at end of buffer.
*/
{
while (ndx-- && *start) start += *start;
return (start);
}
/*************************************************************************
* Find the pointer to matching string in a history or key define buffer.
*/
static unsigned char *match_ptr(src,patt,len,indx,exact)
unsigned char *src,*patt;
int exact,len,*indx;
/*
* At entry:
* src - ptr to place in buffer to begin the search.
* patt - ptr to string to match against
* len - len of string pointed to by patt
* indx - ptr to int into which the number of the found string in the
* buffer is placed.
* exact - if YES, the lengths must match as well as the pattern.
* if NO, only 'len' chars are compared.
* At exit:
* returns ptr to first char in found string or ptr to null string
* at end of buffer if no match.
* *indx is updated if the string is found otherwise it is not changed.
*/
{
int nxtndx;
nxtndx = *indx;
src = ptr(src,nxtndx);
while(*src) {
if (*src-1 == len || (exact == NO && *src-1 > len)) {
int result,tlen;
unsigned char *a,*b;
a = src+1;
b = patt;
tlen = len;
result = 0;
while(tlen--) if ((result = *a++ - *b++) != 0) break;
if (result == 0) break;
}
src += *src;
++nxtndx;
}
if (*src == 0) {
src = (unsigned char *)0;
} else {
*indx = nxtndx;
}
return src;
}
/****************************************************************************
* Place n'th string from history or key definition buffer into command buff
*/
static int getstr(src,ndx,lb)
register unsigned char *src;
int ndx;
struct led_buf *lb;
/*
* At entry:
* src - ptr to place in history or key def buffer to begin search
* ndx - number of string to pick up
* lb - ptr to led_buf into which to place the found string
* At exit:
* requested string (or null string) is moved to command buf replacing
* whatever might be there already.
* A number of members of the lb struct will have been changed.
*/
{
register unsigned cnt;
src = ptr(src,ndx);
if (cnt = (unsigned)*src++) putt(src,--cnt,lb);
}
/****************************************************************************
* Move the cursor n places to the left.
*/
static int curs_l(cnt,delete,lb)
int cnt;
FLAG delete;
struct led_buf *lb;
/*
* At entry:
* cnt - number of column positions to move left
* delete - if 1, delete the characters under the cursor as it is moved
* if 0, don't delete the chars under the cursor
* lb - ptr to led_buf which holds the cursor info
* At exit:
* cursor is moved left appropriately unless it is already at the left
* margin in which case nothing will happen. Characters may be echoed
* or the right half of the line may be redrawn if deleting.
* A number of members of the lb struct will have been changed.
*/
{
register unsigned char *rc, *lc;
register FLAG atab;
atab = NO;
rc = lb->rcurs;
lc = lb->lcurs;
while ((lc > lb->buf) && cnt--) {
if ((*--rc = *--lc) == TAB) atab |= YES;
lb->c_posn--;
cle_putc(BKSP,lb->ttybf->ttyp);
}
*(lb->lcurs = lc) = 0;
if (atab) pfront(lb,NO);
if (delete) {
lb->flags |= LD_DIRTY;
pback(lb,YES);
} else {
lb->rcurs = rc;
if (atab) pback(lb,NO);
}
}
/****************************************************************************
* Move the cursor n places to the right.
*/
static int curs_r(cnt,delete,lb)
int cnt;
FLAG delete;
struct led_buf *lb;
/*
* At entry:
* cnt - number of column positions to move right
* delete - if 1, delete the characters under the cursor as it is moved
* if 0, don't delete the chars under the cursor
* lb - ptr to led_buf which holds the cursor info
* At exit:
* cursor is moved right appropriately unless it is already at the right
* margin in which case nothing will happen. Characters may be echoed
* or the right half of the line may be redrawn if deleting.
* A number of members of the lb struct will have been changed.
*/
{
register unsigned char *rc, *lc;
rc = lb->rcurs;
lc = lb->lcurs;
while (*rc && cnt--) {
if (delete) rc++;
else *lc++ = echo(*rc++,lb);
}
lb->rcurs = rc;
if (delete) {
lb->flags |= LD_DIRTY;
pback(lb,YES);
} else *(lb->lcurs = lc) = 0;
}
/************************************************************************
* Test a char for being alpha-numeric.
*/
static int isaln(chr)
register unsigned char chr;
/*
* At entry:
* chr - chr to test
* At exit:
* returns YES if chr is alpha-numeric or '_'
*/
{
return (
((chr >= 'A') && (chr <= 'Z')) ||
((chr >= '0') && (chr <= '9')) ||
((chr >= 'a') && (chr <= 'z')) ||
chr == '_'
);
}
/************************************************************************
* Test a char for being white-space.
*/
static int isws(chr)
register unsigned char chr;
/*
* At entry:
* chr - chr to test
* At exit:
* returns YES if chr is white-space
*/
{
return (
chr == ' ' ||
chr == '\t' ||
chr == '\n'
);
}
/************************************************************************
* Test a char for being delimiter.
*/
static int isdelim(chr)
register unsigned char chr;
/*
* At entry:
* chr - chr to test
* At exit:
* returns YES if chr is delimiter
*/
{
return (!isaln(chr) && !isws(chr));
}
/***********************************************************************
* Skip to end of "word" on the left and optionally 'eat' it.
*/
static int skipwl(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf
* At exit:
* cursor moved. "word" skipped
*/
{
register unsigned char *cp;
cp = lb->lcurs;
while (cp > lb->buf && isws(*(cp-1))) --cp;
while (cp > lb->buf && isaln(*(cp-1))) --cp;
while (cp > lb->buf && isdelim(*(cp-1))) --cp;
curs_l(lb->lcurs - cp, NO,lb);
}
/***********************************************************************
* Skip to end of "word" on the right and optionally 'eat' it.
*/
static int skipwr(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf
* At exit:
* cursor moved. "word" either skipped or eaten
*/
{
register unsigned char *cp;
cp = lb->rcurs;
while (*cp && isdelim(*cp)) ++cp;
while (*cp && isaln(*cp)) ++cp;
while (*cp && isws(*cp)) ++cp;
curs_r(cp - lb->rcurs, NO,lb);
}
/************************************************************************
* Delete 'word' to left of cursor
*/
static int d_lwrd(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing cmd_buf
* At exit:
* 'word' to left of cursor is deleted unless cursor is already at left
* margin in which case nothing happens.
* Several members of the lb struct are updated.
*/
{
register unsigned char *cp;
cp = lb->lcurs;
while (cp > lb->buf && isws(*(cp-1))) --cp;
curs_l(lb->lcurs - cp, NO,lb);
while (cp > lb->buf && isaln(*(cp-1))) --cp;
while (cp > lb->buf && isdelim(*(cp-1))) --cp;
curs_l(lb->lcurs - cp, YES,lb);
}
/************************************************************************
* Delete 'word' to right of cursor
*/
static int d_rwrd(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing cmd_buf
* At exit:
* 'word' to right of cursor is deleted unless cursor is already at right
* margin in which case nothing happens.
* Several members of the lb struct are updated.
*/
{
register unsigned char *cp;
cp = lb->rcurs;
while (*cp && isws(*cp)) ++cp;
curs_r(cp - lb->rcurs, NO,lb);
while (*cp && isdelim(*cp)) ++cp;
while (*cp && isaln(*cp)) ++cp;
curs_r(cp - lb->rcurs, YES,lb);
}
/************************************************************************
* Skip 'word' according to current direction bit
*/
static int skipw(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing cmd_buf
* At exit:
* 'word' to left of cursor is skipped unless cursor is already at right
* margin in which case nothing happens.
* Several members of the lb struct are updated.
*/
{
if (lb->flags&LD_BACKUP) skipwl(lb);
else skipwr(lb);
}
/************************************************************************
* Skip to one end of the line or the other depending on direction bit
*/
static int skiptoe(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing cmd_buf
* At exit:
* cursor is positioned at either end of the line depending on direction bit
* Several members of the lb struct may be updated.
*/
{
if (lb->flags&LD_BACKUP) bol(lb);
else eol(lb);
}
/****************************************************************************
* Copy current contents of command buf to key def buffer
*/
static int copycom(cp, len, lb)
register unsigned char *cp;
register len;
struct led_buf *lb;
/*
* At entry:
* cp - ptr to place in key def buffer to deposit cmd buf
* len - number of chars to move
* lb - ptr to led_buf containing the cmd buf
* At exit:
* command string is moved to requested spot in buffer
* Strings in the history buffer may be deleted if there's overlap.
* A number of members of the lb struct will have been changed.
*/
{
register unsigned char *cp2;
if (len > 255) return; /* don't do it if command too big */
cp2 = lb->buf; /* ptr to source */
*cp++ = len; /* dst gets length (note this is type char!) */
while (--len) *cp++ = *cp2++; /* move the text */
/* eliminate oldlines if overwritten */
for (cp = lb->old; *cp && ((cp + *cp) <= Oldend(lb)); cp += *cp);
*cp = 0;
return;
}
/************************************************************************
* Complete the read (insert a newline in command buffer)
*/
static int newline(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing cmd_buf
* At exit:
* lb struct is cleaned up and a LD_DONE is set in flags to indicate the
* read is complete. The contents of the command line is inserted in
* the history buffer. If it matches a line already in the history
* buffer, the dup is removed and the new one is inserted at the front.
* Several members of the lb struct are updated.
*/
{
register unsigned char *end;
register unsigned char *cp;
int comlen;
eol(lb); /* move bubble to end of line */
comlen = (lb->lcurs - lb->buf) + 1; /* length of current command */
if ((comlen > 1) && (comlen < ((Oldend(lb) - lb->old) - 1))) {
/*
* If dirty = NO, then we used an old line without modification
* so we'll squeeze it out of the old buffer to eliminate the
* repetition, and recopy it into the front of the old buffer.
* This allows us to hold older commands longer. If dirty = YES,
* then we'll look for a matching command in the buffer and, if
* one is found, move it to the head of the buffer. If not, then
* we just insert the new string at the head of the buffer.
*/
if ((lb->flags&LD_DIRTY) != 0) {
lb->oldndx = 0;
end = match_ptr(lb->old,lb->buf,comlen-1,&lb->oldndx,YES);
if (end == 0) end = lb->key - comlen;
lb->oldmatlen = comlen;
} else {
end = ptr(lb->old,lb->oldndx);
}
for (cp = end + comlen; end > (lb->old + 1); *--cp = *--end);
copycom(lb->old + 1, comlen, lb); /* put it in the old com buffer */
}
lb->flags |= LD_DONE;
return;
}
/************************************************************************
* Echo a char to the terminal
*/
static int echo(chr,lb)
register unsigned char chr;
struct led_buf *lb;
/*
* At entry:
* chr - char to be echoed
* lb - ptr to led_buf containing cmd_buf
* At exit:
* chr is written to the output que (t_outq) and may be enclosed with
* inverse video escape sequences if the char is non-printable. Tabs
* are expaned to an appropriate number of spaces.
* Several members in the lb struct will have been changed.
*/
{
unsigned char **cp;
struct tty *typ;
struct tty_buf *tbp;
tbp = lb->ttybf;
cp = tbp->ansi;
typ = tbp->ttyp;
if (chr < ' ' || chr == 0177) {
if (chr == 0) return 0;
if (chr == TAB) {
lb->c_posn += ((chr = (8 - (lb->c_posn & 7))) - 1);
while (chr--) cle_putc(' ',typ);
chr = TAB;
} else {
if (chr == 0177) chr = '?'-'@'; /* convert DEL to inverse ? */
cle_puts(cp[ANSI_SETINV],typ); /* inverse video for VT100 */
cle_putc(chr + '@',typ);
cle_puts(cp[ANSI_SETNORM],typ);
}
} else {
cle_putc(chr,typ);
}
lb->c_posn++;
return (chr);
}
/************************************************************************
* Print the prompt string to the terminal
*/
static int pprompt(lb,clr_flg)
struct led_buf *lb;
int clr_flg;
/*
* At entry:
* lb - ptr to led_buf containing prompt string
* clr_flg - YES if to pre-clear the line before printing
* At exit:
* prompt string is written to the output que (t_outq). Some chars may be
* converted to displayable ones.
*/
{
register unsigned char *cp;
if (clr_flg) {
cle_puts(lb->ttybf->ansi[ANSI_CLRLINE],lb->ttybf->ttyp);
} else {
cle_putc('\r',lb->ttybf->ttyp); /* else start with cr */
}
for (cp = lb->prmpt; echo(*cp++,lb););
}
/************************************************************************
* Print the text to the left of the cursor
*/
static int pfront(lb,clr_flg)
struct led_buf *lb;
int clr_flg;
/*
* At entry:
* lb - ptr to led_buf containing prompt string
* clr_flg - YES if to pre-clear the line before printing
* At exit:
* The prompt string and the text from lb->buf[0] through
* lb->buf[lb->lcurs] is put in the output que (t_outq). Some characters
* may have bee converted to displayable ones. The message completes
* with an escape sequence to turn on the cursor.
*/
{
register unsigned char *cp;
lb->c_posn = 0;
pprompt(lb,clr_flg);
for (cp = lb->buf; echo(*cp++,lb); );
return;
}
/************************************************************************
* Print the text from the cursor to the end of line
*/
static pback(lb,deol)
struct led_buf *lb;
int deol;
/*
* At entry:
* lb - ptr to led_buf containing prompt string
* deol - if 1, follows message with an escape sequence to delete to
* end of line.
* if 0, don't delete to end of line
* At exit:
* String from lb->buf[lb->rcurs] to end of buffer is sent to the
* output que (t_outq).
*/
{
register unsigned char *cp;
register tmp1,tmp2;
unsigned char **ansi;
struct tty *typ;
struct tty_buf *tbp;
tbp = lb->ttybf;
typ = tbp->ttyp;
ansi = tbp->ansi;
if (*lb->rcurs == 0) {
if (deol) cle_puts(ansi[ANSI_CLREOL],typ); /* deol */
return;
}
if (ansi[ANSI_SAVE] != 0 && *ansi[ANSI_SAVE] != '\0') {
cle_puts(lb->ttybf->ansi[ANSI_SAVE],typ); /* save cursor posn */
}
tmp1 = lb->c_posn;
for (cp = lb->rcurs; echo(*cp++,lb); );
tmp2 = lb->end_posn; /* old end_posn */
lb->end_posn = lb->c_posn; /* new end_posn */
lb->c_posn = tmp1; /* restore c_posn */
if (ansi[ANSI_SAVE] == 0 || *ansi[ANSI_SAVE] == '\0') {
for (tmp1 = tmp2 - lb->end_posn; tmp1--; cle_putc(' ',typ));/* shorter - clear end */
for (tmp1 = tmp2 - lb->c_posn; tmp1--; cle_putc(BKSP,typ)); /* restore cursor */
} else {
if (deol) cle_puts(ansi[ANSI_CLREOL],typ); /* deol */
cle_puts(ansi[ANSI_RESTORE],typ); /* restore_cursor */
}
return;
}
/**************************************************************************
* Send a message to the terminal
*/
static int msg(str,lb)
unsigned char *str;
struct led_buf *lb;
/*
* At entry:
* str - pointer to null terminated string containing message
* lb - ptr to led_buf to which to send the message
* At exit:
* message is placed in output que (t_outq) wrapped with the
* necessary escape sequences to make the message appear on the line
* above the current line. Command line is refreshed.
*/
{
long oldq;
struct tty_buf *tbp;
struct tty *typ;
tbp = lb->ttybf;
typ = tbp->ttyp;
oldq = lb->flags;
cle_puts(tbp->ansi[ANSI_UP],typ); /* up one line */
cle_puts(tbp->ansi[ANSI_CLRLINE],typ); /* clear line */
cle_puts(str,typ); /* user's message */
cle_putc(LF,typ); /* generic down-arrow */
reprint(lb); /* refresh */
return;
}
#if 0 /* no key defs anymore. maybe later */
static unsigned char kp_keys[] = "PQnpqrstuvwxy"; /* PF1, PF2, kp., kp0-kp9 */
/*****************************************************************************
* Execute a defined key
*/
static int dokey(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf containing key definitions
* At exit:
* if currently defining a key, the contents of the command buf is
* placed in the key definition buffer (if there's room). If not
* defining a key, the string assigned to key requested by lb->c is
* inserted at the current cursor position.
* Several members of the lb struct will have been changed.
*/
{
register unsigned char *end;
register unsigned char *cp;
register int slide;
int len;
for(len=0;len<sizeof(kp_keys)-1;++len) if (lb->c == kp_keys[len]) break;
if (len >= sizeof(kp_keys)-1) {
msg("Unknown keypad key",lb);
return;
}
lb->indx = len;
if (lb->defkey) {
eol(lb);
len = (lb->lcurs - lb->buf) + 1; /* length of current command */
end = ptr(lb->key,lb->indx);
slide = len - *end; /* length diff old, new keydef */
if (slide > ((Oldend(lb) - lb->old) - 1)) { /* not enough room! */
msg("no room for key definition");
cle_putc(BEL,lb->ttybf->ttyp);
return;
}
lb->key -= slide;
if (slide < 0) { /* old key len was longer */
/* slide other keys forward by the difference */
cp = end - slide;
for (slide = cp - lb->key; slide--; *--cp = *--end);
} else if (slide > 0) { /* old key len was shorter */
/* slide other keys back to make room */
cp = lb->key + slide; /* old key posn */
slide = end - cp; /* length of stuff to be slid */
for (end = lb->key; slide--; *end++ = *cp++);
}
cp = ptr(lb->key,lb->indx);
copycom(cp, len, lb);
d_bol(lb);
reprint(lb);
lb->defkey = 0;
} else {
getstr(lb->key, lb->indx, lb);
}
}
#endif
/*************************************************************************
* The following n routines are executed as a result of input of the *
* appropriate key. The process context in these cases remain task time. *
* They should never be called at interrupt time. *
*************************************************************************/
static int set_defkey(lb) /* begin a define key sequence */
struct led_buf *lb;
{
lb->defkey = 2;
}
static int set_esc(lb) /* char is esc, next char decides what to do */
struct led_buf *lb;
{
lb->defkey <<= 1;
}
static int set_keypad(lb) /* chars are escO, next char is Keypad code */
struct led_buf *lb;
{
lb->defkey <<= 1;
}
static int c_r(lb) /* right arrow, move cursor non-destructively */
struct led_buf *lb;
{
curs_r(1, NODEL, lb);
}
static int c_l(lb) /* left arrow, move cursor non-destructively */
struct led_buf *lb;
{
curs_l(1, NODEL,lb);
}
static int d_eol(lb) /* PF4, delete from cursor to end of line */
struct led_buf *lb;
{
lb->rcurs = lb->bufend; /* eat all the chars on the left side */
*lb->rcurs = 0; /* put null at end of the buffer */
cle_puts(lb->ttybf->ansi[ANSI_CLREOL],lb->ttybf->ttyp); /* tell terminal to erase to eol */
}
static int d_bol(lb) /* NAK, delete from cursor to beginning of line */
struct led_buf *lb;
{
curs_l(lb->maxlin,DEL,lb);
}
static int d_rchr(lb) /* delete char under cursor (right of cursor) */
struct led_buf *lb;
{
curs_r(1, DEL, lb);
}
static int d_lchr(lb) /* delete char to left of cursor */
struct led_buf *lb;
{
curs_l(1, DEL, lb);
}
static int eol(lb) /* go to end of line */
struct led_buf *lb;
{
curs_r(lb->maxlin,NODEL,lb);
}
static int bol(lb) /* go to beginning of line */
struct led_buf *lb;
{
curs_l(lb->maxlin,NODEL,lb);
}
static int tog_insrt(lb) /* toggle insert/overstrike mode */
struct led_buf *lb;
{
lb->flags ^= LD_INSERT;
}
static int reprint(lb) /* refresh the input line */
struct led_buf *lb;
{
pfront(lb,NO);
pback(lb,YES);
}
static int getold_prev(lb) /* up arrow, recall next line in history */
struct led_buf *lb;
{
register *xptr;
xptr = &lb->oldndx;
pprompt(lb,YES);
lb->rcurs = lb->bufend;
lb->lcurs = lb->buf;
lb->c_posn = 0; /* set the column position */
lb->oldmatlen = 0; /* recall this way eats pattern */
if (*ptr(lb->old,*xptr)) getstr(lb->old, ++(*xptr), lb);
lb->flags &= ~LD_DIRTY;
}
static int getold_next(lb) /* down arrow, recall next line in history */
struct led_buf *lb;
{
register *xptr;
xptr = &lb->oldndx;
pprompt(lb,YES);
lb->rcurs = lb->bufend;
lb->lcurs = lb->buf;
lb->c_posn = 0; /* set the column position */
lb->oldmatlen = 0; /* recall this way eats pattern */
if (*xptr) getstr(lb->old, --(*xptr), lb);
lb->flags &= ~LD_DIRTY;
}
static int getold_str(lb) /* recall next matching line in history */
struct led_buf *lb;
{
int comlen;
unsigned char *new;
eol(lb); /* move bubble to end of line */
comlen = (lb->lcurs - lb->buf); /* length of current command */
if (comlen == 0) return;
if ((lb->flags&LD_DIRTY) != 0 || lb->oldmatlen == 0) {
lb->oldmatlen = comlen;
lb->oldndx = 0; /* start looking from beginning */
} else {
if (*ptr(lb->old,lb->oldndx)) {
++lb->oldndx; /* start looking at one beyond that */
} else {
cle_putc(BEL,lb->ttybf->ttyp);
return; /* no more */
}
}
new = match_ptr(lb->old,lb->buf,lb->oldmatlen,&lb->oldndx,NO);
if (new == 0) {
cle_putc(BEL,lb->ttybf->ttyp); /* beep 'em cuz there ain't anymore */
return;
}
pprompt(lb,YES);
lb->rcurs = lb->bufend;
lb->lcurs = lb->buf;
lb->c_posn = 0; /* set the column position */
if (comlen = *new++) putt(new,--comlen,lb);
lb->flags &= ~LD_DIRTY;
}
static int superquote(lb)
struct led_buf *lb;
{
putt("?",1,lb); /* insert a qm */
c_l(lb); /* backup 1 spot */
lb->state = ESCCHAR; /* set the new state */
return;
}
static int setcol_width(siz,tbp)
int siz;
struct tty_buf *tbp;
{
if (siz == 0) { /* set to 80 columns */
cle_puts(tbp->ansi[ANSI_80COL],tbp->ttyp); /* make it 80 */
tbp->flags &= ~TB_132;
} else {
cle_puts(tbp->ansi[ANSI_132COL],tbp->ttyp); /* make it 132 */
tbp->flags |= TB_132;
}
return;
}
static int t132_mode(lb)
struct led_buf *lb;
{
struct tty_buf *tbp;
tbp = lb->ttybf;
if ((tbp->flags&TB_132) != 0) { /* if already at 132 mode */
setcol_width(0,tbp); /* set it to 80 cols */
} else {
setcol_width(1,tbp); /* set it to 132 cols */
}
reprint(lb);
}
static int nop(lb)
struct led_buf *lb;
{
return;
}
static int advance(lb)
struct led_buf *lb;
{
lb->flags &= ~LD_BACKUP;
}
static int backup(lb)
struct led_buf *lb;
{
lb->flags |= LD_BACKUP;
}
static int skipc(lb)
struct led_buf *lb;
{
if (lb->flags&LD_BACKUP) {
c_l(lb); /* backup, cursor left */
} else {
c_r(lb); /* advance, cursor right */
}
}
static int skipl(lb)
struct led_buf *lb;
{
if (lb->flags&LD_BACKUP) {
getold_prev(lb); /* backup, cursor up */
} else {
getold_next(lb); /* advance, cursor down */
}
}
static int ring_bell(lb)
struct led_buf *lb;
{
cle_putc(BEL,lb->ttybf->ttyp);
}
static int dochr();
/* DON'T CHANGE THE ORDER OF THE ENTRIES IN THE FOLLLOWING 3 ARRAYS */
static int (*edit_functions[])() = {
dochr, /* insert a char */
tog_insrt, /* toggle insert/overstrike mode */
bol, /* goto bol */
eol, /* goto eol */
d_lwrd, /* delete "word" to the left */
d_rwrd, /* delete "word" to the right */
d_bol, /* delete to beginning of line */
d_eol, /* delete to end of line */
c_l, /* cursor left */
c_r, /* cursor right */
d_lchr, /* delete character on left */
d_rchr, /* delete character on right */
reprint, /* refresh the line */
getold_prev, /* get previous */
getold_next, /* get next */
getold_str, /* get matching string */
newline, /* end of input */
superquote, /* escape next char */
t132_mode, /* toggle 80/132 mode */
nop, /* do nothing */
advance, /* clear backup bit */
backup, /* set backup bit */
skipw, /* skip word (uses direction bit) */
skipc, /* skip char (uses direction bit) */
skipl, /* skip line (uses direction bit) */
ring_bell, /* echo a bell */
skipwl, /* skip word right */
skipwr, /* skip word left */
skiptoe, /* skip to end of line per direction */
nop, /* actually purge (trapped at interrupt time) */
0 /* trailer */
};
static unsigned char fkey_to_clekey[] = { /* input <esc>[n~ where n is: */
0, /* 0 is unassigned */
CLEKEY_DEL, /* 1 is (or can be) delete key */
CLEKEY_INSERT, /* 2 is insert */
CLEKEY_HOME, /* 3 is home */
CLEKEY_END, /* 4 is end */
CLEKEY_PGUP, /* 5 is page-up */
CLEKEY_PGDN, /* 6 is page-down */
0,0,0,0, /* 7-10 unassigned */
CLEKEY_F1, /* 11 */
CLEKEY_F2,
CLEKEY_F3,
CLEKEY_F4,
CLEKEY_F5,
0, /* 16 unassigned */
CLEKEY_F6,
CLEKEY_F7,
CLEKEY_F8,
CLEKEY_F9,
CLEKEY_F10,
0, /* 22 unassigned */
CLEKEY_F11,
CLEKEY_F12,
CLEKEY_F13,
CLEKEY_F14,
0, /* 27 unassigned */
CLEKEY_F15,
CLEKEY_F16,
0, /* 30 unassigned */
CLEKEY_F17,
CLEKEY_F18,
CLEKEY_F19,
CLEKEY_F20,
0 /* 35 unassigned */
};
unsigned char ckey_to_clekey[] = {
CLEKEY_UP, /* A (all prefixed with <esc>[ )*/
CLEKEY_DOWN, /* B */
CLEKEY_RIGHT, /* C */
CLEKEY_LEFT, /* D */
CLEKEY_END, /* E */
CLEKEY_PGUP, /* F */
CLEKEY_PGDN, /* G */
CLEKEY_HOME, /* H */
CLEKEY_INSERT /* I */
};
static unsigned char hex[] = "0123456789ABCDEF";
static unsigned char *itoa(val,ptr,radix)
unsigned int val;
unsigned char *ptr;
int radix;
{
unsigned int quo,rem;
rem = val % radix;
quo = val / radix;
if (quo != 0) ptr = itoa(quo,ptr,radix);
*ptr++ = hex[rem];
return ptr;
}
/****************************************************************************
* parse_it is the entry point for the editor. It is called for each character
* input from the keyboard. The process context is task time. It must never
* be called from an interrupt routine.
*/
static int parse_it(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf for this process. lb->c has character to process.
* At exit:
* the command line is edited.
*/
{
register short token;
unsigned int fndx;
if (lb->state == NORMAL) { /* if ordinary character */
if (lb->c < SPACE) { /* if char is control */
if (lb->c == ESC) { /* escape is special case */
lb->state = ESCAPE; /* signal other stuff coming later */
return;
}
fndx = lb->ttybf->keymap[lb->c];
(*edit_functions[fndx])(lb); /* do the edit function */
return;
}
putt(&lb->c,1,lb); /* ordinary character */
return;
}
if (lb->state == ESCAPE) { /* we're in an escape sequence */
if (lb->c == '[') { /* if cursor sequence */
lb->state = CURSKEY; /* next one is cursor key */
return;
}
if (lb->c == 'O') { /* if keypad sequence */
lb->state = KEYPAD; /* next one is keypad key */
return;
}
/* else bad escape sequence */
putt("\033",1,lb); /* stuff in an escape */
lb->state = NORMAL; /* back to normal */
parse_it(lb); /* do it again */
return;
}
if (lb->state == CURSKEY) { /* if cursor sequence */
if (lb->c >= '0' && lb->c <= '9') { /* <esc>[n~ key */
lb->keynum = lb->c - '0'; /* compute keynum */
lb->state = FUNCKEY; /* and we're doing function keys */
return; /* wait for rest of escape sequence */
}
if (lb->c == '=') { /* <esc>[=x where a <= x <= l */
lb->keynum = '=';
lb->state = FUNCKEY;
return;
}
fndx = lb->c - 'A'; /* compute index */
if (fndx >= sizeof(ckey_to_clekey)) { /* if out of range */
putt("\033[",2,lb); /* bad esc sequence, stuff <esc>[ */
lb->state = NORMAL; /* back to normal */
parse_it(lb); /* do it again */
return;
}
fndx = ckey_to_clekey[fndx];
(*edit_functions[lb->ttybf->keymap[fndx]])(lb); /* do the function */
lb->state = NORMAL; /* back to normal */
return;
}
if (lb->state == KEYPAD) { /* if keypad sequence */
fndx = -1; /* assume its nfg */
if (lb->c == 'M') fndx = CLEKEY_ENTER;
else if (lb->c >= 'P' && lb->c <= 'S') fndx = lb->c-'P'+CLEKEY_PF1;
else if (lb->c >= 'l' && lb->c <= 'n') fndx = lb->c-'l'+CLEKEY_KPCOMMA;
else if (lb->c >= 'p' && lb->c <= 'y') fndx = lb->c-'p'+CLEKEY_KP0;
if (fndx < 0) {
putt("\033O",2,lb); /* bad escape sequence */
lb->state = NORMAL; /* back to normal */
parse_it(lb); /* reparse */
return;
}
(*edit_functions[lb->ttybf->keymap[fndx]])(lb);
lb->state = NORMAL; /* back to normal */
return;
}
if (lb->state == FUNCKEY) { /* doing an F key */
char str[6];
if (lb->c >= '0' && lb->c <= '9') { /* eat all the numbers */
lb->keynum = lb->keynum * 10 + (lb->c - '0');
return;
} else if (lb->c == '~') { /* end of sequence */
if (lb->keynum < sizeof(fkey_to_clekey) &&
(fndx = fkey_to_clekey[lb->keynum]) != 0 &&
lb->ttybf->keymap[fndx] != CLEFUN_CHAR) {
(*edit_functions[lb->ttybf->keymap[fndx]])(lb);
lb->state = NORMAL; /* back to normal */
return;
}
} else if (lb->keynum == '=') {
if (lb->c >= 'a' && lb->c <= 'l') {
fndx = lb->c - 'a' + CLEKEY_F1;
if (lb->ttybf->keymap[fndx] != CLEFUN_CHAR) {
(*edit_functions[lb->ttybf->keymap[fndx]])(lb);
lb->state = NORMAL; /* back to normal */
return;
}
}
putt("\033[=",3,lb);
lb->state = NORMAL;
parse_it(lb);
return;
}
putt("\033[",2,lb); /* bad esc sequence, stuff <esc>[ */
putt(str,itoa(lb->keynum,str,10)-str,lb);
lb->state = NORMAL;
parse_it(lb); /* reparse it */
return;
}
if (lb->state == NCC_SPC+VEOL) newline(lb);
else if (lb->state == NCC_SPC+VERASE) d_lchr(lb);
else if (lb->state == NCC_SPC+VKILL) d_bol(lb);
else if (lb->state == ESCCHAR) {
d_rchr(lb); /* eat what's under the cursor */
if (lb->c == 0) {
msg("NUL's cannot be inserted in the command line",lb);
} else {
putt(&lb->c,1,lb); /* insert the char as is */
}
} else msg("Unknown key and/or key state",lb);
lb->state = NORMAL;
return;
}
/************************************************************************
* process character (or sequence) that didn't match anything in the list
* of special characters.
*/
static int dochr(lb)
struct led_buf *lb;
/*
* At entry:
* lb - ptr to led_buf. lb->c has char to be processed
* At exit:
* command line edited.
*/
{
switch (lb->state) {
case ESCAPE: putt("\033",1,lb); break;
case KEYPAD: putt("\033O",2,lb); break;
case CURSKEY: putt("\033[",2,lb); break;
default:
if (lb->c == 0) {
msg("NUL's cannot be inserted in the command line",lb);
} else {
putt(&lb->c,1,lb);
}
return;
}
lb->state = NORMAL;
parse_it(lb); /* again because previous escape seq bad */
}
/*****************************************************************************
* The following n routines are specific to the line discipline and are what *
* are called by the kernel. Process context can be both task time and *
* interrupt time and is so indicated at the entry point of each routine. *
*****************************************************************************/
/*****************************************************************************
* The pre-processor variable M_KERNEL is defined when compiling for line
* discipline mode. If it is not defined, then the program will be made in
* standalone mode which is useful for debugging the edit only portion of the
* program.
*****************************************************************************/
#ifndef M_KERNEL
struct user {
unsigned char *u_base;
int u_count;
int u_error;
struct proc *u_procp;
} u; /* allocate a dummy u struct if s.a. mode */
#endif
static struct led_buf cle_buffers[MAX_LEDBUFS],
*ldb_free; /* head of free list que */
static struct tty_buf cle_ttybuf[MAX_TTYBUFS],
*tty_free, /* head of free list */
*tty_used; /* head of used que */
#if MB_DEBUG && MULTI_LB
static int dump_ttybuf(tbp,msg)
struct tty_buf *tbp;
unsigned char *msg;
{
struct tty_buf *nxt;
if (tbp == 0) {
printf("%s: tbp = 0\n",msg);
return;
}
nxt = tbp;
do {
printf("%s: tbp = %X, next = %X, last = %X, tp = %X\n\tlbtop = %X, flags = %X\n",
msg,nxt,nxt->next,nxt->last,nxt->ttyp,nxt->lbtop,nxt->flags);
nxt = nxt->next;
} while (nxt != tbp);
return;
@//E*O*F cledaa//
chmod u=rw,g=r,o=r cledaa
echo x - space.c.awk
sed 's/^@//' > "space.c.awk" <<'@//E*O*F space.c.awk//'
{
if ($3 == "linesw[]" && i++ == 0) {
j = 1
printf("/*\t_cled_ ------------ */\n");
printf("extern int\t\tcleopen(),\t/* _cled_ */\n");
printf("\t\tcleclose(),\t/* _cled_ */\n");
printf("\t\tcleread(),\t/* _cled_ */\n");
printf("\t\tclewrite(),\t/* _cled_ */\n");
printf("\t\tcleioctl(),\t/* _cled_ */\n");
printf("\t\tcleinput(),\t/* _cled_ */\n");
printf("\t\tcleoutput();\t/* _cled_ */\n");
} else if ($1 == "};" && j == 1) {
j = 2;
printf("\t\tcleopen,\t/* _cled_ */\n");
printf("\t\tcleclose,\t/* _cled_ */\n");
printf("\t\tcleread,\t/* _cled_ */\n");
printf("\t\tclewrite,\t/* _cled_ */\n");
printf("\t\tcleioctl,\t/* _cled_ */\n");
printf("\t\tcleinput,\t/* _cled_ */\n");
printf("\t\tcleoutput,\t/* _cled_ */\n");
printf("\t\tnulldev,\t/* _cled_ */\n");
print;
if (getline > 0) {
s = index($2,"=")+1;
if (s > 1) {
e = index($2,";");
if (e > 1) {
n = substr($2,s,e-s)+1;
printf("int linecnt = %d ;\t/* _cled_ */\n",n);
getline;
j = 3;
}
}
}
}
print;
}
END {
if (j != 3) exit 1;
}
@//E*O*F space.c.awk//
chmod u=rw,g=r,o=r space.c.awk
echo x - space.c.rawk
sed 's/^@//' > "space.c.rawk" <<'@//E*O*F space.c.rawk//'
{
if (match($0,"_cled_") == 0) {
print;
} else {
if (j == 0) j = 1;
if ($2 == "linecnt") {
printf("int linecnt=%d;\n",$4-1);
j = 2;
}
}
}
END {
if (j != 2) exit 1;
}
@//E*O*F space.c.rawk//
chmod u=rw,g=r,o=r space.c.rawk
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
1500 6740 44778 cledaa
42 127 1088 space.c.awk
15 39 190 space.c.rawk
1557 6906 46056 total
!!!
wc cledaa space.c.awk space.c.rawk | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
--
Dave Shepperd. shepperd at dms.UUCP or motcsd!dms!shepperd
Atari Games Corporation, 675 Sycamore Drive, Milpitas CA 95035.
Nobody knows what I'm saying. I don't even know what I'm saying.
More information about the Comp.unix.xenix
mailing list