[comp.unix.i386] CLED 2.5 SystemV/386 part 03/03

pcg@cs.aber.ac.uk (Piercarlo Grandi) (08/28/90)

--------------------------cut here--------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  cled.c.1
# Wrapped by sw@aware on Tue Aug 28 13:36:12 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cled.c.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cled.c.1'\"
else
echo shar: Extracting \"'cled.c.1'\" \(44477 characters\)
sed "s/^X//" >'cled.c.1' <<'END_OF_FILE'
X/*
X    $Id: cled.c,v 2.5 90/08/26 11:32:08 sw Exp $
X
X    A simple line editor intended for command line editing on Xenix
X    with a TCAP type terminal using the line discipline mode of
X    terminal operation.
X
X    Authors:
X
X    Piercarlo Grandi (piercarl). Aberystwyth. (pcg@cs.aber.ac.uk):
X
X	substantially cleaned up the code and made it more modular
X	and readable. More configurable, with an EMACS key bindings
X	options for non DEC VT compatible terminals. Able to work
X	on dumb terminals if necessary. Corrected a few bugs.
X
X    Dave Shepperd (dms). Atari Games, corp. (shepperd@dms.UUCP):
X
X	hacked lvr's editor to bits to make it re-entrant and added
X	the line discipline and device interfaces for SCO Xenix/386.
X
X    Lyle Rains (lvr). Atari Games, corp. (rains@dms.UUCP):
X
X	wrote the editor in 1983 or so for a RT-11 system intended to be
X	included as a library module.
X
X    Copyright 1989,1990 Piercarlo Grandi (parts).
X    Copyright 1989 Atari Games, Corp. (most).
X
X    Reproduction, adaptation, distribution, performance or display of
X    this computer program or the associated documentation is not
X    prohibited provided this copyright notice accompanies said
X    reproduction, adaptation, distribution, performance or display.
X*/
X
X#include <sys/types.h>
X#include <sys/tty.h>
X#include <sys/termio.h>
X
X#include "patchlevel.h"
X#include "sys/cledio.h"
X#include "sys/cled.h"
X
X#include <sys/param.h>
X#include <sys/sysmacros.h>
X#include <sys/conf.h>
X#include <sys/errno.h>
X
X#ifdef M_KERNEL
X#   undef INKERNEL
X#   define INKERNEL	1
X#endif
X
X#ifdef INKERNEL
X#   undef M_KERNEL
X#   define M_KERNEL	1
X#endif
X
X#if INKERNEL
X#   if M_UNIX
X#	include <sys/immu.h>
X#	include <sys/region.h>
X#   else
X#	include <sys/mmu.h>
X#   endif
X#   if M_I386
X#	include <sys/page.h>
X#	include <sys/seg.h>
X#   endif
X#   include <sys/proc.h>
X#   include <sys/var.h>
X#   include <sys/dir.h>
X#   include <sys/signal.h>
X#   include <sys/user.h>
X#   if M_UNIX && defined(ftop)
X#	undef ftop
X#   endif
X#   include <sys/ioctl.h>
X#   include <sys/file.h>
X#   if M_UNIX
X#	include <sys/cmn_err.h>
X#   endif
X#endif
X
Xextern int		cle_ttys;
Xextern int		cle_leds;
X
Xextern struct led_buf	cle_buffers[];
Xextern struct tty_buf	cle_ttybuf[];
X
X/*
X    Control characters
X*/
X
X#define BEL		007
X#define ESC		033
X#define RUB		0177
X
X/*
X    Parser states
X*/
X
X#define NORMAL		0x0000
X#define ESCAPE		0x0001
X#define ANSIKEY		0x0002
X#define QUOTE		0x0003
X#define NCC_SPC(c)	(0x0100+(c))
X
X/*
X    Boolean type and values
X*/
X
X#define FLAG		unchar
X 
X#define YES		1
X#define NO		0
X#define DEL		1
X#define NODEL		0
X
X#if INKERNEL
X#   define cle_putc(c,tp)	putc((c),&(tp)->t_outq)
X#   define cle_getc(tp)		getc(&(tp)->t_rawq)
X#else
X#   include <stdio.h>
X#   include <malloc.h>
X#   define cle_puts(str,tp)	if (str) fputs((str),stderr)
X#   define cle_putc(chr,tp)	fputc((chr),stderr)
X#   define cle_getc(tp)		fgetc(stdin)
X#endif
X
X#define MIN(a,b)	((a) < (b) ? (a) : (b))
X#define history_end(lb)	((lb)->historyend-1)
X
Xstatic unchar		our_lineno;
X
X/*
X    This routine is a dummy routine that simply marks the start of cled in
X    the map file (for trouble shooting)
X*/
X
Xvoid			cled_begin(){}
X
X/*
X    The following n routines comprise the editor proper. The process
X    context in all cases is task time. None of these routines are (nor
X    should they ever be) called at interrupt time.
X*/
X
Xstatic void		pfront();
Xstatic void		pback();
Xstatic void		bol();
Xstatic void		eol();
X
Xstatic void		reprint();
Xstatic void		ring_bell();
Xstatic void		kick_out();
X
Xstatic unchar		echo();
X
X/*
X    Put a string of text into the command buffer, echoing them as they are
X    inserted.
X
X    ENTRY: cp - ptr to string of chars to be copied cnt - count of chars to
X    copy lb - ptr to led_buf into which to place the text
X
X    EXIT: chars moved into lb->line[lb->lcurs]. lb->lcurs adjusted as
X    required lb->rcurs adjusted if overstrike mode lb->flags may be set
X    chars echoed as required
X*/
X
Xstatic void             putt(cp,cnt,lb)
X    register unchar         *cp;
X    int                     cnt;
X    struct led_buf          *lb;
X{
X    register unchar         **rc,**lc;
X    int			    gap;
X    int			    last;
X    unsigned		    iflg;
X    struct tty              *typ;
X
X    rc = &lb->rcurs;
X    lc = &lb->lcurs;
X
X    typ = lb->tbp->ttyp;
X    iflg = typ->t_iflag;
X
X    /*
X	We want the line not to grow beyond cols, including the prompt and
X	an empty position at the end of the line to avoid problems with
X	automargin terminals.
X    */
X
X    last = lb->tbp->cols - lb->promptlen - 1;
X    gap = (MAXLINE-lb->tbp->cols + 1) + lb->promptlen + 1;
X
X    while (cnt--)
X    {
X	register unchar		    ch = *cp;
X
X	if (iflg&ISTRIP) ch &= 0x7F;
X
X	if (ch != '\0')
X	{
X	    if ((iflg&IUCLC) && ch >= 'A' && ch <= 'Z')
X		ch += 'a' - 'A';
X
X	    if ((*rc - *lc) <= gap || (ch = echo(ch,lb,last)) == '\0')
X		break;
X
X	    *(*lc)++ = *cp++ = ch;
X	    if (!(lb->flags&LD_INSERT) && *(*rc) != '\0')
X		(*rc)++;
X
X	    lb->flags |= LD_DIRTY;
X	}
X    }
X
X    *(*lc) = '\0';
X
X    if (cnt >= 0)
X	ring_bell(lb);
X
X    pback(lb,NO);
X}
X
X/*
X    Find pointer to n'th string in a history or key define buffer.
X
X    ENTRY: start - ptr into buffer where search is to begin ndx - the
X    number of the string to find
X
X    EXIT: returns ptr to first char of requested string if present else it
X    returns ptr to null string at end of buffer.
X*/
X
Xstatic unchar           *ptr(start,ndx)
X    register unchar	    *start;
X    register int	    ndx;
X{
X    while (ndx-- && *start)
X	start += *start;
X
X    return start;
X}
X
X/*
X    Find the pointer to matching string in a history or key define buffer.
X
X    ENTRY: src - ptr to place in buffer to begin the search.  patt - ptr
X    to string to match against len - len of string pointed to by patt indx
X    - ptr to int into which the number of the found string in the buffer
X    is placed.  exact - if YES, the lengths must match as well as the
X    pattern. if NO, only 'len' chars are compared.
X
X    EXIT: returns ptr to first char in found string or ptr to null string
X    at end of buffer if no match. *indx is updated if the string is found
X    otherwise it is not changed.
X*/
X
Xstatic unchar           *match_ptr(src,patt,len,indx,exact)
X    unchar                  *src,*patt;
X    int                     exact,len,*indx;
X{
X    int                     nxtndx;
X
X    for
X    (
X	nxtndx = *indx, src = ptr(src,nxtndx);
X	*src != '\0';
X	nxtndx++, src += *src
X    )
X	if ((*src - 1) == len || (exact == NO && (*src - 1) > len))
X	{
X	    int                     result;
X	    register int	    tlen;
X	    register unchar	    *a,*b;
X
X	    result = 0;
X
X	    for (a = src+1, b = patt, tlen = len; tlen != 0; --tlen)
X		if ((result = *a++ - *b++) != 0)
X		    break;
X
X	    if (result == 0)
X		break;
X	}
X
X    if (*src == '\0')
X	    src = (unchar *) '\0';
X    else    *indx = nxtndx;
X
X    return src;
X}
X
X/*
X    Place n'th string from history or key definition buffer into command buff
X
X    ENTRY: src - ptr to place in history or key def buffer to begin search
X    ndx - number of string to pick up lb - ptr to led_buf into which to
X    place the found string
X
X    EXIT: requested string (or null string) is moved to command buf
X    replacing whatever might be there already. A number of members of the
X    lb struct will have been changed.
X*/
X
Xstatic void             getstr(src,ndx,lb)
X    register unchar         *src;
X    int                     ndx;
X    struct led_buf          *lb;
X{
X    register unsigned       cnt;
X
X    src = ptr(src,ndx);
X    if (cnt = (unsigned) *src++)
X	putt(src,--cnt,lb);
X}
X
X/*
X    Move the cursor n places to the left.
X
X    ENTRY: cnt - number of column positions to move left delete - if 1,
X    delete the characters under the cursor as it is moved if 0, don't
X    delete the chars under the cursor lb - ptr to led_buf which holds the
X    cursor info
X
X    EXIT: cursor is moved left appropriately unless it is already at the
X    left margin in which case nothing will happen. Characters may be
X    echoed or the right half of the line may be redrawn if deleting. A
X    number of members of the lb struct will have been changed.
X*/
X
Xstatic void             curs_l(cnt,delete,lb)
X    int                     cnt;
X    FLAG                    delete;
X    struct led_buf          *lb;
X{
X    register unchar         *rc,*lc;
X    register FLAG           atab;
X    register struct tty	    *typ;
X
X    atab = NO;
X
X    rc = lb->rcurs;
X    lc = lb->lcurs;
X    typ = lb->tbp->ttyp;
X
X    while ((lc > lb->line) && cnt--)
X    {
X	if ((*--rc = *--lc) == '\t')
X	    atab |= YES;
X
X	lb->current--;
X	if (!atab)
X	    cle_putc('\b',typ);
X    }
X    *(lb->lcurs = lc) = '\0';
X
X    if (atab)
X	pfront(lb,NO);
X
X    if (delete)
X    {
X	lb->flags |= LD_DIRTY;
X	pback(lb,YES);
X    }
X    else
X    {
X	lb->rcurs = rc;
X	if (atab)
X	    pback(lb,NO);
X    }
X}
X
X/*
X    Move the cursor n places to the right.
X
X    ENTRY: cnt - number of column positions to move right delete - if 1,
X    delete the characters under the cursor as it is moved if 0, don't
X    delete the chars under the cursor lb - ptr to led_buf which holds the
X    cursor info
X
X    EXIT: cursor is moved right appropriately unless it is already at the
X    right margin in which case nothing will happen. Characters may be
X    echoed or the right half of the line may be redrawn if deleting. A
X    number of members of the lb struct will have been changed.
X*/
X
Xstatic void             curs_r(cnt,delete,lb)
X    int                     cnt;
X    FLAG                    delete;
X    struct led_buf          *lb;
X{
X    register unchar         *rc,*lc;
X
X    rc = lb->rcurs;
X    lc = lb->lcurs;
X
X    if (!delete)
X    {
X	while (*rc && cnt--) *lc++ = echo(*rc++,lb,MAXLINE);
X	lb->rcurs = rc;
X	*(lb->lcurs = lc) = '\0';
X    }
X    else
X    {
X	while (*rc && cnt--) rc++;
X	lb->rcurs = rc;
X	lb->flags |= LD_DIRTY;
X	pback(lb,YES);
X    }
X}
X
X/*
X    Test a char for being alpha-numeric.
X
X    ENTRY: chr - chr to test
X
X    EXIT: returns YES if chr is alpha-numeric or '_'
X*/
X
Xstatic int              isaln(chr)
X    register unchar         chr;
X{
X    return (
X	(chr >= 'a' && chr <= 'z')
X	|| (chr >= 'A' && chr <= 'Z')
X	|| (chr >= '0' && chr <= '9')
X	|| chr == '_'
X    );
X}
X
X/*
X    Test a char for being white-space.
X
X    ENTRY: chr - chr to test
X
X    EXIT: returns YES if chr is white-space
X*/
X
Xstatic int              isws(chr)
X    register unchar         chr;
X
X{
X    return (chr == ' ' || chr == '\t' || chr == '\n');
X}
X
X/*
X    Test a char for being delimiter.
X
X    ENTRY: chr - chr to test
X
X    EXIT: returns YES if chr is delimiter
X*/
X
Xstatic int              isdelim(chr)
X    register unchar         chr;
X{
X    return (!isaln(chr) && !isws(chr));
X}
X
X/*
X    Skip to end of "word" on the left and optionally 'eat' it.
X
X    ENTRY: lb - ptr to led_buf
X
X    EXIT: cursor moved. "word" skipped
X*/
X
Xstatic void             skipwl(lb)
X    struct led_buf          *lb;
X{
X    register unchar         *cp;
X
X    cp = lb->lcurs;
X
X    while (cp > lb->line && isws(*(cp-1)))	--cp;
X    while (cp > lb->line && isaln(*(cp-1)))	--cp;
X    while (cp > lb->line && isdelim(*(cp-1)))	--cp;
X
X    curs_l(lb->lcurs - cp,NO,lb);
X}
X
X/*
X    Skip to end of "word" on the right and optionally 'eat' it.
X
X    ENTRY: lb - ptr to led_buf
X
X    EXIT: cursor moved. "word" either skipped or eaten
X*/
X
Xstatic void             skipwr(lb)
X    struct led_buf          *lb;
X{
X    register unchar         *cp;
X
X    cp = lb->rcurs;
X
X    while (*cp && isdelim(*cp))		++cp;
X    while (*cp && isaln(*cp))		++cp;
X    while (*cp && isws(*cp))		++cp;
X
X    curs_r(cp - lb->rcurs,NO,lb);
X}
X
X/*
X    Delete 'word' to left of cursor
X
X    ENTRY: lb - ptr to led_buf containing cmd_buf
X
X    EXIT: 'word' to left of cursor is deleted unless cursor is already at
X    left margin in which case nothing happens. Several members of the lb
X    struct are updated.
X*/
X
Xstatic void		d_lwrd(lb)
X    struct led_buf          *lb;
X{
X    register unchar         *cp;
X
X    cp = lb->lcurs;
X
X    while (cp > lb->line && isws(*(cp - 1)))	--cp;
X    curs_l(lb->lcurs - cp,NO,lb);
X
X    while (cp > lb->line && isaln(*(cp - 1)))	--cp;
X    while (cp > lb->line && isdelim(*(cp - 1)))	--cp;
X    curs_l(lb->lcurs - cp,YES,lb);
X}
X
X/*
X    Delete 'word' to right of cursor
X
X    ENTRY: lb - ptr to led_buf containing cmd_buf
X
X    EXIT: 'word' to right of cursor is deleted unless cursor is already at
X    right margin in which case nothing happens. Several members of the lb
X    struct are updated.
X*/
X
Xstatic void              d_rwrd(lb)
X    struct led_buf          *lb;
X{
X    register unchar         *cp;
X
X    cp = lb->rcurs;
X
X    while (*cp && isws(*cp))	    ++cp;
X    curs_r(cp - lb->rcurs,NO,lb);
X
X    while (*cp && isdelim(*cp))	    ++cp;
X    while (*cp && isaln(*cp))	    ++cp;
X    curs_r(cp - lb->rcurs,YES,lb);
X}
X
X/*
X    Copy current contents of command buf to key def buffer
X
X    ENTRY: cp - ptr to place in key def buffer to deposit cmd buf len -
X    number of chars to move lb - ptr to led_buf containing the cmd buf At
X    exit: command string is moved to requested spot in buffer Strings in
X    the history buffer may be deleted if there's overlap. A number of
X    members of the lb struct will have been changed.
X*/
X
Xstatic void             copycom(cp,len,lb)
X    register unchar         *cp;
X    register                len;
X    struct led_buf          *lb;
X{
X    register unchar         *cp2;
X
X    if (len > (TTYHOG-3))
X	return;
X
X    cp2 = lb->line;
X    *cp++ = len;
X    while (--len)
X	*cp++ = *cp2++;
X
X    /* eliminate oldlines if overwritten */
X    for (cp = lb->history; *cp && (cp + *cp) <= history_end(lb); cp += *cp);
X
X    *cp = '\0';
X}
X
X/*
X    Complete the read (insert a newline in command buffer)
X
X    ENTRY: lb - ptr to led_buf containing cmd_buf
X
X    EXIT: lb struct is cleaned up and a LD_DONE is set in flags to
X    indicate the read is complete.  The contents of the command line is
X    inserted in the history buffer. If it matches a line already in the
X    history buffer, the dup is removed and the new one is inserted at the
X    front. Several members of the lb struct are updated.
X*/
X
Xstatic void             newline(lb)
X    struct led_buf          *lb;
X{
X    register unchar         *end;
X    register unchar         *cp;
X    int                     linelen;
X
X    eol(lb);
X    linelen = (lb->lcurs - lb->line) + 1;
X
X    if (linelen > 1 && linelen < ((history_end(lb) - lb->history) - 1))
X    {
X	/*
X	   If dirty = NO, then we used an old line without modification so
X	   we'll squeeze it out of the old buffer to eliminate the
X	   repetition, and recopy it into the front of the old buffer. This
X	   allows us to hold older commands longer. If dirty = YES, then
X	   we'll look for a matching command in the buffer and, if one is
X	   found, move it to the head of the buffer. If not, then we just
X	   insert the new string at the head of the buffer.
X	*/
X
X	if (!(lb->flags&LD_DIRTY))
X	    end = ptr(lb->history,lb->lastline);
X	else
X	{
X	    lb->lastline = 0;
X	    end = match_ptr(lb->history,lb->line,linelen - 1,&lb->lastline,YES);
X	    if (end == 0)
X		end = lb->historyend - linelen;
X	    lb->matchlen = linelen;
X	}
X
X	for (cp = end + linelen; end > (lb->history + 1); *--cp = *--end);
X	copycom(lb->history + 1,linelen,lb);
X    }
X
X    lb->flags |= LD_DONE;
X}
X
X#if INKERNEL
X
X/*
X    Kick start the output. This routine is called whenever there is data in
X    the t_outq buffer to make sure that it gets sent to the terminal.
X
X    ENTRY: Process context: Task or Interrupt. May be called from either.
X    tp - ptr to tty struct
X
X    EXIT: If data present in t_outq and output not already running, then a
X    call to the terminal driver processor routine (t_proc) with the output
X    option selected is made which will cause data to be moved from the
X    t_outq clist to the t_obuf output array for subsequent output to the
X    hardware.
X*/
X
Xstatic void             kick_out(tp)
X    struct tty              *tp;
X{
X    int                     ospl;
X
X    ospl = spl6();
X    {
X	if (tp->t_outq.c_cc != 0
X	    && (!(tp->t_state & (BUSY|TIMEOUT|TTSTOP))
X		|| tp->t_tbuf.c_count == 0))
X	    {
X		splx(ospl);
X		(*tp->t_proc) (tp,T_OUTPUT);
X	    }
X    }
X    splx(ospl);
X}
X
X/*
X    Put a null terminated string of characters in the output que for
X    subsequent output to the terminal.
X
X    ENTRY: Process context: Task or Interrupt. s - ptr to null terminated
X    string to output tp - ptr to tty struct
X
X    EXIT: characters are placed in the t_outq clist. If there's no room
X    for the whole message, that that won't fit is discarded with no error.
X*/
X
Xstatic void             cle_puts(s,tp)
X    unchar                  *s;
X    struct tty              *tp;
X{
X    register unchar         *s1 = s;
X    struct clist            dumcl,*clp;
X    struct cblock           *srcp;
X    int                     ospl,cnt;
X
X    if (s1 == 0 || *s1 == 0)
X	return;
X
X    while (*s1++);
X    cnt = s1-1 - s;
X
X    dumcl.c_cc = 0;
X    dumcl.c_cf = dumcl.c_cl = 0;
X    clp = &tp->t_outq;
X
X    ospl = spl6();
X    {
X	putcbp(&dumcl,s,cnt);
X	while ((srcp = getcb(&dumcl)) != 0)
X	    putcb(srcp,clp);
X    }
X    splx(ospl);
X}
X#endif
X
X/*
X    Echo a char to the terminal
X
X    ENTRY: chr - char to be echoed lb - ptr to led_buf containing cmd_buf.
X
X    EXIT: chr is written to the output que (t_outq) and may be enclosed
X    with inverse video escape sequences if the char is non-printable. Tabs
X    are expaned to an appropriate number of spaces. Several members in the
X    lb struct will have been changed.
X*/
X
Xstatic unchar		echo(chr,lb,last)
X    register unchar         chr;
X    register struct led_buf *lb;
X    int			    last;
X{
X    struct tty_buf          *tbp;
X    struct tty              *typ;
X
X    tbp = lb->tbp;
X    typ = tbp->ttyp;
X
X    if (lb->current >= last)
X	return 0;
X
X    if (CLEKEY_CHAR(chr))
X	lb->current += 1,cle_putc(chr,typ);
X    else if (chr == '\t')
X    {
X	chr = 8 - ((lb->current+lb->promptlen) & 7);
X	if ((lb->current += chr) >= last)
X	{
X	    lb->current -= chr;
X	    return '\0';
X	}
X
X	/*
X	    Here we must use spaces and not tabs because we may
X	    want to overwrite.
X	*/
X	while (chr--) cle_putc(' ',typ);
X
X	chr = '\t';
X    }
X    else
X    {
X	unchar                  **tcap = (unchar **) tbp->tcap;
X
X	lb->current += 1;
X
X	if (chr == RUB) chr = '?' - '@';
X	cle_puts(tcap[TCAP_SETINV],typ);
X	cle_putc(chr + '@',typ);
X	cle_puts(tcap[TCAP_SETNORM],typ);
X    }
X
X    return chr;
X}
X
X/*
X    Print the prompt string to the terminal
X
X    ENTRY: lb - ptr to led_buf containing prompt string clr_flg - YES if to
X    pre-clear the line before printing
X
X    EXIT: prompt string is written to the output que (t_outq). Some chars
X    may be converted to displayable ones.
X*/
X
Xstatic void		pprompt(lb,clr_flg)
X    register struct led_buf *lb;
X    int                     clr_flg;
X{
X    register unchar         *cp;
X    register struct tty_buf *tbp = lb->tbp;
X
X    cle_putc('\r',tbp->ttyp);
X
X#if 0
X    if (lb->promptlen) cle_puts(lb->prompt,tbp->ttyp);
X#else
X    for (cp = lb->prompt; *cp && echo(*cp++,lb,tbp->cols-1););
X#endif
X
X    if (clr_flg)
X	cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp);
X}
X
X/*
X    Print the text to the left of the cursor
X
X    ENTRY: lb - ptr to led_buf containing prompt string clr_flg - YES if to
X    pre-clear the line before printing
X
X    EXIT: The prompt string and the text from lb->line[0] through
X    lb->line[lb->lcurs] is put in the output que (t_outq). Some characters
X    may have bee converted to displayable ones. The message completes with
X    an escape sequence to turn on the cursor.
X*/
X
Xstatic void             pfront(lb,clr_flg)
X    register struct led_buf *lb;
X    int                     clr_flg;
X{
X    register unchar         *cp;
X
X    lb->current = 0;
X    pprompt(lb,clr_flg);
X    for (cp = lb->line; *cp && echo(*cp++,lb,MAXLINE););
X}
X
X/*
X    Print the text from the cursor to the end of line
X
X    ENTRY: lb - ptr to led_buf containing prompt string deol - if 1, follows
X    message with an escape sequence to delete to end of line. if 0, don't
X    delete to end of line
X
X    EXIT: String from lb->line[lb->rcurs] to end of buffer is sent to the
X    output que (t_outq).
X*/
X
Xstatic void             pback(lb,deol)
X    struct led_buf          *lb;
X    int                     deol;
X{
X    register unchar         *cp;
X    register int	    tmp1,tmp2;
X    unchar                  **tcap;
X    struct tty              *typ;
X    struct tty_buf          *tbp;
X    int			    save;
X
X    tbp = lb->tbp;
X    typ = tbp->ttyp;
X    tcap = (unchar **) tbp->tcap;
X
X    if (*lb->rcurs == 0)
X    {
X	if (deol)
X	    cle_puts(tcap[TCAP_CLREOL],typ);
X	return;
X    }
X
X    save = tcap[TCAP_SAVE] && *tcap[TCAP_SAVE];
X
X    if (save)
X	cle_puts(tcap[TCAP_SAVE],typ);
X
X    tmp1 = lb->current;
X    for (cp = lb->rcurs; *cp && echo(*cp,lb,MAXLINE); cp++);
X    tmp2 = lb->lastchar;
X    lb->lastchar = lb->current;
X    lb->current = tmp1;
X
X    if (save)
X    {
X	if (deol)
X	    cle_puts(tcap[TCAP_CLREOL],typ);
X	cle_puts(tcap[TCAP_RESTORE],typ);
X    }
X    else
X    {
X	for (tmp1 = tmp2 - lb->lastchar; tmp1--; cle_putc(' ',typ));
X	for (tmp1 = tmp2 - lb->current; tmp1--; cle_putc('\b',typ));
X    }
X}
X
X/*
X    Send a message to the terminal
X
X    ENTRY: str - pointer to null terminated string containing message lb -
X    ptr to led_buf to which to send the message
X
X    EXIT: message is placed in output que (t_outq) wrapped with the
X    necessary escape sequences to make the message appear on the line
X    above the current line. Command line is refreshed.
X*/
X
Xstatic void             msg(str,lb)
X    unchar                  *str;
X    struct led_buf          *lb;
X{
X    long                    oldq;
X    register struct tty_buf *tbp;
X    register struct tty	    *typ;
X
X    tbp = lb->tbp;
X    typ = tbp->ttyp;
X    oldq = lb->flags;
X
X    cle_putc('\r',typ);
X    cle_puts(tbp->tcap[TCAP_CLREOL],typ);
X    cle_puts(tbp->tcap[TCAP_SETINV],typ);
X    cle_puts(str,typ);
X    cle_puts(tbp->tcap[TCAP_SETNORM],typ);
X    cle_putc('\n',typ);
X    reprint(lb);
X}
X
X/*
X    right arrow, move cursor non-destructively
X*/
Xstatic void             c_r(lb)
X    struct led_buf          *lb;
X{
X    curs_r(1,NODEL,lb);
X}
X
X/*
X    left arrow, move cursor non-destructively
X*/
Xstatic void             c_l(lb)
X    struct led_buf          *lb;
X{
X    curs_l(1,NODEL,lb);
X}
X
Xstatic void             d_eol(lb)
X    struct led_buf          *lb;
X{
X    register struct tty_buf *tbp = lb->tbp;
X
X    lb->rcurs = lb->lineend;
X    *lb->rcurs = '\0';
X    cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp);
X}
X
Xstatic void             d_bol(lb)
X    struct led_buf          *lb;
X{
X    curs_l(MAXLINE,DEL,lb);
X}
X
Xstatic void             d_rchr(lb)
X    struct led_buf          *lb;
X{
X    curs_r(1,DEL,lb);
X}
X
Xstatic void              d_lchr(lb)
X    struct led_buf          *lb;
X{
X    curs_l(1,DEL,lb);
X}
X
Xstatic void              eol(lb)
X    struct led_buf          *lb;
X{
X    curs_r(MAXLINE,NODEL,lb);
X}
X
Xstatic void              bol(lb)
X    struct led_buf          *lb;
X{
X    curs_l(MAXLINE,NODEL,lb);
X}
X
Xstatic void             tog_insrt(lb)
X    struct led_buf          *lb;
X{
X    lb->flags ^= LD_INSERT;
X}
X
Xstatic void             reprint(lb)
X    struct led_buf          *lb;
X{
X    pfront(lb,NO);
X    pback(lb,YES);
X}
X
Xstatic void             getold_prev(lb)
X    register struct led_buf *lb;
X{
X    register int	    *xptr;
X    register struct tty_buf *tbp = lb->tbp;
X
X    xptr = &lb->lastline;
X    pprompt(lb,NO);
X
X    lb->lcurs = lb->line;
X    lb->rcurs = lb->lineend;
X    lb->current = 0;
X
X    /* recall this way eats pattern */
X    lb->matchlen = 0;
X
X    if (*ptr(lb->history,*xptr))
X	getstr(lb->history,++(*xptr),lb);
X    cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp);
X
X    lb->flags &=~ (LD_DIRTY);
X}
X
Xstatic void             getold_next(lb)
X    register struct led_buf *lb;
X{
X    register int	    *xptr;
X    register struct tty_buf *tbp = lb->tbp;
X
X    xptr = &lb->lastline;
X    pprompt(lb,NO);
X
X    lb->lcurs = lb->line;
X    lb->rcurs = lb->lineend;
X    lb->current = 0;
X
X    /* recall this way eats pattern */
X    lb->matchlen = 0;
X    if (*xptr)
X	getstr(lb->history,--(*xptr),lb);
X    cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp);
X
X    lb->flags &=~ (LD_DIRTY);
X}
X
Xstatic void             getold_str(lb)
X    register struct led_buf *lb;
X{
X    register int	    linelen;
X    unchar		    *new;
X    register struct tty_buf *tbp = lb->tbp;
X
X    eol(lb);
X
X    linelen = (lb->lcurs - lb->line);
X    if (linelen == 0)
X	return;
X
X    if ((lb->flags&LD_DIRTY) || lb->matchlen == 0)
X    {
X	lb->matchlen = linelen;
X	lb->lastline = 0;
X    }
X    else
X    {
X	if (*ptr(lb->history,lb->lastline))
X	    lb->lastline++;
X	else
X	{
X	    ring_bell(lb);
X	    return;
X	}
X    }
X
X    new = match_ptr(lb->history,lb->line,lb->matchlen,&lb->lastline,NO);
X
X    if (new == 0)
X    {
X	ring_bell(lb);
X	return;
X    }
X
X    pprompt(lb,NO);
X
X    lb->lcurs = lb->line;
X    lb->rcurs = lb->lineend;
X    lb->current = 0;
X
X    if (linelen = *new++)
X	putt(new,--linelen,lb);
X    cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp);
X
X    lb->flags &=~ (LD_DIRTY);
X}
X
Xstatic void		meta(lb)
X    struct led_buf	    *lb;
X{
X    lb->state = ESCAPE;
X}
X
Xstatic void		ansi(lb)
X    struct led_buf	    *lb;
X{
X    lb->state = ANSIKEY;
X}
X
X/*
X    Hardwired ANSI key map for the IBM PC.
X*/
X
Xstatic int		ansikey(ch)
X    register char	    ch;
X{
X    switch (ch)
X    {
X    case '@':	return CLEFUN_INSERT;
X    case 'A':	return CLEFUN_PREVIOUS;
X    case 'B':	return CLEFUN_NEXT;
X    case 'C':	return CLEFUN_CURSR;
X    case 'D':	return CLEFUN_CURSL;
X    case 'H':	return CLEFUN_GOTOBOL;
X    case 'U':	return CLEFUN_SKIPWR;
X    case 'V':	return CLEFUN_SKIPWL;
X    case 'Y':	return CLEFUN_GOTOEOL;
X    default:	return CLEFUN_BELL;
X    }
X}
X
Xstatic void             superquote(lb)
X    struct led_buf          *lb;
X{
X    putt("^",1,lb); c_l(lb);
X    lb->state = QUOTE;
X}
X
Xstatic void             self_insert(lb)
X    struct led_buf          *lb;
X{
X    putt(&lb->c,1,lb);
X}
X
Xstatic void             nop(lb)
X    struct led_buf          *lb;
X{
X}
X
Xstatic void             ring_bell(lb)
X    struct led_buf          *lb;
X{
X    register struct tty_buf *tbp = lb->tbp;
X
X    cle_puts(tbp->tcap[TCAP_FLASH],tbp->ttyp);
X}
X
X/* DON'T CHANGE THE ORDER OF THE ENTRIES IN THE FOLLOWING 3 ARRAYS */
X
Xstatic void             (*edit_functions[]) () =
X{
X    self_insert,		/* insert a char			*/
X    tog_insrt,			/* toggle insert/overstrike mode	*/
X    bol,			/* goto bol				*/
X    eol,			/* goto eol				*/
X    d_lwrd,			/* delete "word" to the left		*/
X    d_rwrd,			/* delete "word" to the right		*/
X    d_bol,			/* delete to beginning of line		*/
X    d_eol,			/* delete to end of line		*/
X    c_l,			/* cursor left				*/
X    c_r,			/* cursor right				*/
X    d_lchr,			/* delete character on left		*/
X    d_rchr,			/* delete character on right		*/
X    reprint,			/* refresh the line			*/
X    getold_prev,		/* get previous				*/
X    getold_next,		/* get next				*/
X    getold_str,			/* get matching string			*/
X    newline,			/* end of input				*/
X    superquote,			/* escape next char			*/
X    nop,			/* do nothing				*/
X    ring_bell,			/* echo a bell				*/
X    skipwl,			/* skip word right			*/
X    skipwr,			/* skip word left			*/
X    nop,			/* actually purge (trapped at interrupt time) */
X    meta,			/* meta/ESC key				*/
X    ansi,			/* ANSI cursors, ESC [			*/
X    0				/* trailer				*/
X};
X
Xstatic unchar           hex[] = "0123456789ABCDEF";
X
Xstatic unchar           *itoa(val,ptr,radix)
X    register unsigned int   val;
X    unchar                  *ptr;
X    register int	    radix;
X{
X    unsigned		    quo;
X    register unsigned	    rem;
X
X    rem = val % radix;
X    quo = val / radix;
X    if (quo != 0)
X	ptr = itoa(quo,ptr,radix);
X    *ptr++ = hex[rem];
X    return ptr;
X}
X
X/*
X    parse_it is the entry point for the editor. It is called for each
X    character input from the keyboard. The process context is task time. It
X    must never be called from an interrupt routine.
X
X    ENTRY: lb - ptr to led_buf for this process. lb->c has character to
X    process.
X
X    EXIT: the command line is edited.
X*/
X
Xstatic void             parse_it(lb)
X    register struct led_buf *lb;
X{
X    int			    s = lb->state;
X    register unchar	    c = lb->c;
X    register unchar	    *kmap = lb->tbp->keymap;
X
X    lb->state = NORMAL; /* next default state ... */
X
X    switch (s)
X    {
X    case NORMAL:
X	if (CLEKEY_CHAR(c))
X		putt(&lb->c,1,lb);
X	else	(*edit_functions[kmap[CLEKEY_CMD(c)]]) (lb);
X	break;
X
X    case ESCAPE:
X	if (CLEKEY_CHAR(c))
X		(*edit_functions[kmap[c]]) (lb);
X	else    ring_bell(lb);
X	break;
X
X    case ANSIKEY:
X	(*edit_functions[ansikey(c)]) (lb);
X	break;
X
X    case QUOTE:
X	d_rchr(lb);
X	if (c != '\0')
X		putt(&lb->c,1,lb);
X	else    msg("NULs cannot be inserted",lb);
X	break;
X
X    default:
X
X	switch (s)
X	{
X	case NCC_SPC(VEOL):	newline(lb);	break;
X	case NCC_SPC(VERASE):	d_lchr(lb);	break;
X	case NCC_SPC(VKILL):	d_bol(lb);	break;
X
X	default:
X	    msg("Unknown key and/or key state",lb);
X	}
X    }
X}
X
X/*
X    The following n routines are specific to the line discipline and are what
X    are called by the kernel. Process context can be both task time and
X    interrupt time and is so indicated at the entry point of each routine.
X
X    The pre-processor variable INKERNEL is defined when compiling for line
X    discipline mode. If it is not defined, then the program will be made in
X    standalone mode which is useful for debugging the edit only portion of the
X    program.
X*/
X
X/*
X    allocate a dummy u struct if s.a. mode
X*/
X#if !INKERNEL
Xstruct user
X{
X    unchar                  *u_base;
X    int                     u_count;
X    int                     u_error;
X    struct proc             *u_procp;
X}                       u;
X#endif
X
X#if MULTILB
Xstatic struct led_buf	*ldb_free;
X#endif
X
Xstatic struct tty_buf	*tty_free;
Xstatic struct tty_buf	*tty_used;
X
X#if DEBUG
Xstatic VOID             dump_ttybuf(tbp,msg)
X    struct tty_buf          *tbp;
X    unchar                  *msg;
X{
X    register struct tty_buf *nxt;
X
X    if (tbp == 0)
X    {
X	printf("%s: tbp = 0\n",msg);
X	return;
X    }
X
X    nxt = tbp;
X
X    do
X    {
X	printf("%s: tbp = %X, next = %X, last = %X, tp = %X\n\tlbp = %X, flags = %X\n",
X	    msg,nxt,nxt->next,nxt->last,nxt->ttyp,nxt->lbp,nxt->flags);
X	nxt = nxt->next;
X    }
X    while (nxt != tbp);
X
X}
X
Xstatic void             dump_ledbuf(lb,msg)
X    struct led_buf          *lb;
X    unchar                  *msg;
X{
X    struct led_buf          *nlb;
X
X    if (lb == 0)
X    {
X	printf("%s: lb = 0\n",msg);
X	return;
X    }
X
X    nlb = lb;
X    do
X    {
X	printf("%s: lb = %X, next = %X, last = %X, td = %X\n\tttybf = %X, flags = %X\n",
X	    msg,nlb,nlb->next,nlb->last,nlb->tbp->ttyp,nlb->tbp,nlb->flags);
X	nlb = nlb->next;
X    }
X    while (nlb != lb);
X}
X
X#if MULTILB
Xstatic void             dump_buftree(tbp)
X    struct tty_buf          *tbp;
X{
X    struct led_buf          *lb;
X    struct tty_buf          *utbp;
X
X    printf("Into dump_buftree(): tbp = %X\n",tbp);
X    dump_ttybuf(tty_used,"Used");
X    dump_ttybuf(tty_free,"Free");
X
X    if (tbp != 0)
X    {
X	printf("lbp = %X\n",tbp->lbp);
X	dump_ledbuf(tbp->lbp,"Used");
X    }
X
X    dump_ledbuf(ldb_free,"Free");
X    printf("Strike any key to continue: ");
X    (void) getchar();
X    printf("Out of dump_buftree()\n");
X}
X#endif
X
Xstatic void             dump_clist(clp,msg)
X    struct clist            *clp;
X    unchar                  *msg;
X{
X    struct cblock           *cbp;
X    int                     size;
X
X    printf("dump_clist() [%s]: ptr = %x\n",msg,clp);
X
X    if (clp == 0 || clp->c_cf == 0)
X    {
X	printf("clist is empty\n");
X	return;
X    }
X
X    printf("\tcc = %d, first = %x, last = %x\n",clp->c_cc,clp->c_cf,clp->c_cl);
X    cbp = clp->c_cf;
X
X    size = 0;
X    do
X    {
X	unchar                  tstr[clsize + 1],*dst,*src;
X	int                     i;
X
X	dst = tstr;
X	src = &cbp->c_data[(i = cbp->c_first)];
X	for (; i < cbp->c_last; ++i)
X	{
X	    unchar                  c;
X
X	    c = *src++;
X	    if (!CLEKEY_CHAR(c))
X		c = '.';
X	    *dst++ = c;
X	}
X	*dst = '\0';
X
X	printf("\t%x, next = %x, first = %d, last = %d, size = %d\n",
X	    cbp,cbp->c_next,cbp->c_first,cbp->c_last,cbp->c_last - cbp->c_first);
X	printf("\tstr = {%s}\n",tstr);
X
X	size += cbp->c_last - cbp->c_first;
X	if (cbp == clp->c_cl)
X	    break;
X
X	cbp = cbp->c_next;
X    }
X    while (cbp != 0);
X
X    if (size != clp->c_cc)
X	printf("\taccumulated size of %d doesn't match c_cc size of %d\n",
X	    size,clp->c_cc);
X
X    printf("type any char to continue");
X    (void) getchar();
X    putchar('\n');
X}
X#endif /* DEBUG */
X
X/*
X    set the keymap to defaults.
X*/
X
Xstatic void             setup_key_defaults(tbp)
X    struct tty_buf          *tbp;
X{
X    register int            cnt;
X    register unchar	    *kmap;
X
X    for (kmap = tbp->keymap, cnt = 0; cnt < CLEKEY_MAX; ++cnt)
X	*kmap++ = CLEFUN_BELL;
X
X    kmap = tbp->keymap;
X
X    kmap['\0']			= CLEFUN_NOP;
X    kmap[CLEKEY_CTL('A')]	= CLEFUN_GOTOBOL;
X    kmap[CLEKEY_CTL('B')]	= CLEFUN_CURSL;
X    kmap[CLEKEY_CTL('D')]	= CLEFUN_DELCRIT;
X    kmap[CLEKEY_CTL('E')]	= CLEFUN_GOTOEOL;
X    kmap[CLEKEY_CTL('F')]	= CLEFUN_CURSR;
X    kmap[CLEKEY_CTL('G')]	= CLEFUN_BELL;
X    kmap[CLEKEY_CTL('H')]	= CLEFUN_DELCRIT;
X    kmap[CLEKEY_CTL('I')]	= CLEFUN_CHAR;
X    kmap['\n']			= CLEFUN_NEWLINE;
X    kmap[CLEKEY_CTL('K')]	= CLEFUN_DELEOL;
X    kmap[CLEKEY_CTL('L')]	= CLEFUN_REFRESH;
X    kmap['\r']			= CLEFUN_NEWLINE;
X    kmap[CLEKEY_CTL('N')]	= CLEFUN_NEXT;
X    kmap[CLEKEY_CTL('P')]	= CLEFUN_PREVIOUS;
X    kmap[CLEKEY_CTL('Q')]	= CLEFUN_ESCAPE;
X    kmap[CLEKEY_CTL('R')]	= CLEFUN_FIND;
X    kmap[CLEKEY_CTL('U')]	= CLEFUN_DELBOL;
X    kmap[CLEKEY_CTL('V')]	= CLEFUN_ESCAPE;
X    kmap[CLEKEY_CTL('W')]	= CLEFUN_DELWLFT;
X    kmap[CLEKEY_CTL('Y')]	= CLEFUN_PURGE;
X    kmap[CLEKEY_CTL('^')]	= CLEFUN_ESCAPE;
X    kmap[ESC]			= CLEFUN_META;
X    kmap[CLEKEY_CTL('\\')]	= CLEFUN_FIND;
X    kmap[RUB]			= CLEFUN_DELCLFT;
X
X    kmap[CLEKEY_ESC('d')]	= CLEFUN_DELWRIT;
X    kmap[CLEKEY_ESC('f')]	= CLEFUN_SKIPWR;
X    kmap[CLEKEY_ESC('b')]	= CLEFUN_SKIPWL;
X    kmap[CLEKEY_ESC('p')]	= CLEFUN_FIND;
X    kmap[CLEKEY_ESC('[')]	= CLEFUN_ANSI;
X    kmap[CLEKEY_ESC('-')]	= CLEFUN_DELWLFT;
X}
X
X/*
X    The following init's setup the strings required to make a terminal do the
X    given functions.
X*/
X
Xstatic void             setup_tcap_defaults(tbp)
X    struct tty_buf          *tbp;
X{
X    tbp->tcap[TCAP_CLREOL]	= TCAP_CLREOL_STR;
X    tbp->tcap[TCAP_SETINV]	= TCAP_SETINV_STR;
X    tbp->tcap[TCAP_SETNORM]	= TCAP_SETNORM_STR;
X    tbp->tcap[TCAP_SAVE]	= TCAP_SAVE_STR;
X    tbp->tcap[TCAP_RESTORE]	= TCAP_RESTORE_STR;
X    tbp->tcap[TCAP_FLASH]	= TCAP_FLASH_STR;
X
X#if M_SPTALLOC
X    if (tbp->tcstrings != 0)
X    {
X	sptfree(tbp->tcstrings,tbp->tclen,1);
X	tbp->tcstrings = 0;
X	tbp->tclen = 0;
X    }
X#else
X    tbp->tclen = 0;
X#endif
X}
X
X/*
X    Initialize the tty and led free lists. Threads the entries in the
X    tty buf array together and on the tty_free list, and either
X    assigns each led buf to a tty buf or threads the led buf array in
X    a free list, depending on whether MULTILB is true.
X*/
X
Xstatic void		freelist_init()
X{
X    register int	    cnt;
X
X#if MULTILB
X    {
X	register struct led_buf	*lastlb;
X
X	/*
X	    Thread first led buf
X	*/
X	lastlb = &cle_buffers[0];
X	lastlb->next = lastlb->last = lastlb;
X
X	/*
X	    Append the rest, if any, to the last
X	*/
X	for (lastlb, cnt = 1; cnt < cle_ttys; cnt++,lastlb++)
X	{
X	    register struct led_buf *lb = lastlb+1;
X
X	    lb->next = lastlb->next;
X	    lb->last = lastlb;
X	    lb->next->last = lastlb->next = lb;
X	}
X
X	ldb_free = cle_buffers;
X    }
X#endif
X
X    {
X	register struct tty_buf *lasttbp;
X
X	/*
X	    Thread first tty buf
X	*/
X	lasttbp = cle_ttybuf;
X	lasttbp->next = lasttbp->last = lasttbp;
X	lasttbp->lbp = 0;
X
X	/*
X	    Append rest, if any, to the end
X	*/
X	for (lasttbp, cnt = 1; cnt < cle_ttys; cnt++, lasttbp++)
X	{
X	    register struct tty_buf *tbp = lasttbp+1;
X
X	    tbp->lbp = 0;
X	    tbp->next = lasttbp->next;
X	    tbp->last = lasttbp;
X	    tbp->next->last = lasttbp->next = tbp;
X 	}
X    }
X
X#if !MULTILB
X    {
X	register struct led_buf *lb = &cle_buffers[0];
X	register struct tty_buf *tbp = &cle_ttybuf[0];
X
X	for (lb, tbp, cnt = 0; cnt < cle_ttys; cnt++, lb++, tbp++)
X	{
X	    lb->tbp = tbp;
X	    tbp->lbp = lb;
X	}
X    }
X#endif
X
X    tty_free = cle_ttybuf;
X}
X
Xstatic void		zap_lb(lb,flags)
X    register struct led_buf *lb;
X    int			    flags;
X{
X    lb->flags = (flags&TB_INSERT) ? LD_INSERT : 0;
X
X    /*
X	Last byte is a fake key definition buffer, length 1 byte, empty.
X    */
X
X    *lb->history	= 1;
X    *(lb->history+1)	= '\0';
X
X    lb->historyend	= lb->history+MAXHISTORY - 1;
X    *lb->historyend	= '\0';
X
X    lb->owed = 0;
X
X    lb->promptlen = 0;
X}
X
X#if INKERNEL
X
X#if MULTILB
X/*
X    Put the led buf beloning to the given tty buf back on the free list.
X*/
X
Xstatic struct led_buf	*free_lb(lb,tbp)
X    register struct led_buf *lb;
X    register struct tty_buf *tbp;
X{
X    struct led_buf	    *nxtlb;
X
X    /*
X	Detach from the list of led bufs for this tty buf
X    */
X
X    if (lb == lb->next)
X	nxtlb = tbp->lbp = 0;
X    else
X    {
X	nxtlb = lb->next;
X	lb->next->last = lb->last;
X	lb->last->next = lb->next;
X	if (lb == tbp->lbp)
X	    tbp->lbp = lb->next;
X    }
X
X    /*
X	Clean things up
X    */
X
X    lb->proc = 0;
X    lb->flags = 0;
X    lb->tbp = 0;
X
X    /*
X	Attach to free list
X    */
X
X    if (ldb_free == 0)
X	lb->next = lb->last = lb;
X    else
X    {
X	lb->last = ldb_free->last;
X	lb->next = ldb_free;
X	lb->last->next = ldb_free->last = lb;
X    }
X
X    ldb_free = lb;
X
X    return nxtlb;
X}
X
X/*
X    For a given tty buffer, scan the chain of led bufs and free any that
X    are attached to defunct processes.
X*/
X
Xstatic void		free_leds(tbp)
X    register struct tty_buf *tbp;
X{
X    register struct led_buf *lb;
X
X    lb = tbp->lbp;
X
X    do
X    {
X	lb = 
X	(
X	    lb->tbp == 0
X	    || lb->proc == 0
X	    || lb->proc->p_pid != lb->pid
X	    || lb->proc->p_ppid != lb->ppid
X	)
X	    ? free_lb(lb,tbp) : lb->next;
X    }
X    while (lb != tbp->lbp);
X}
X#endif /* MULTILB */
X
Xstatic struct tty_buf	*free_ttybuf(tbp)
X    register struct tty_buf *tbp;
X{
X    struct tty_buf	    *nxttbp;
X
X    /*
X	Detach it from the tty_used list
X    */
X
X    if (tbp == tbp->next)
X	nxttbp = tty_used = 0;
X    else
X    {
X	int                     ospl;
X
X	ospl = spl6();
X	{
X	    tbp->next->last = tbp->last;
X	    tbp->last->next = tbp->next;
X	    if (tbp == tty_used)
X		tty_used = tbp->next;
X	    nxttbp = tbp->next;
X	}
X	splx(ospl);
X    }
X
X    /*
X	Disgorge any pending output, and clean things up
X    */
X
X    if (tbp->broadcast.c_cf != 0)
X    {
X	struct cblock           *cnxt;
X	int                     ospl;
X
X	ospl = spl6();
X	{
X	    while ((cnxt = getcb(&tbp->broadcast)) != 0)
X		putcf(cnxt);
X	    kick_out(tbp->ttyp);
X	}
X	splx(ospl);
X    }
X
X    tbp->flags = 0;
X    tbp->dorefresh = tbp->readsleep = NO;
X
X    /*
X	Attach to the free list
X    */
X
X    if (tty_free == 0)
X	tbp->next = tbp->last = tbp;
X    else
X    {
X	register struct tty_buf	*tn;
X
X	tn = tty_free->next;
X	(tbp->next = tn)->last = (tbp->last = tty_free)->next = tbp;
X    }
X
X    tty_free = tbp;
X
X    return nxttbp;
X}
X
X/*
X    For each tty buf, free up all the led bufs not attached to
X    any process, and free all those (except for the one passed
X    as an argument) that have no led bufs left and are not
X    open.
X*/
X
Xstatic void             free_ttys(oldtbp)
X    struct tty_buf          *oldtbp;
X{
X    register struct tty_buf *tbp;
X
X    tbp = tty_used;
X
X    do
X    {
X	register struct tty_buf *nxttbp = tbp->next;
X
X#if MULTILB
X	if (tbp->lbp != 0)
X	    free_leds(tbp);
X#endif
X
X	tbp =
X	(
X	    tbp != oldtbp
X	    && tbp->readsleep == NO
X#if MULTILB
X	    && !(tbp->flags&TB_OPENING)
X	    && tbp->lbp == 0
X#else
X	    && !(tbp->flags&TB_OPEN)
X#endif
X	)
X	    ? free_ttybuf(tbp) : tbp->next;
X    }
X    while (tbp != tty_used);
X}
X#endif /* INKERNEL */
X
X/*
X    Ran out of tty_buf's or led_buf's so ripple through the allocated ones and
X    deallocate any that are no longer being used. It is also called to init
X    the links in the structures.
X
X    ENTRY: Process context: task. NEVER call from an interrupt routine.
X    oldtbp: ptr to tbp buffer of buffer not to put on free list; tty_free:
X    points to first element in free list for tty_buf's; tty_used: points to
X    first element in used list for tty_buf's; ldb_free: points to first element
X    in free list for led_buf's
X
X    EXIT: all led_buff's assigned to defunct processes are placed back in
X    the free list. All tty_buf's that have no led_buf's assigned are
X    placed back in the free list.
X*/
X
Xstatic void             garbage_collect(oldtbp)
X    struct tty_buf          *oldtbp;
X{
X    if (tty_used == 0 && tty_free == 0)
X	freelist_init();
X#if INKERNEL
X    else if (tty_used != 0)
X	free_ttys(oldtbp);
X#endif
X}
X
X/*
X    Get the next available led_buf from the freelist.
X
X    ENTRY: Process context: task. Must never be called from
X    interrupt routine. tbp - ptr to tty_buf assigned to tty
X    struct to which led_buf is to be attached.
X
X    EXIT: returns ptr to led_buf if one is available else returns 0. If
X    led_buf is attached, it is placed at the head of the que; tbp->lbp
X    will be moved in that case. The led_buf is initialised for use.  The
X    history buffer is purged.
X*/
X
Xstatic struct led_buf   *get_ledbuf(tbp)
X    struct tty_buf          *tbp;
X{
X    struct led_buf          *lb,*next,*last;
X    int                     cnt;
X    unchar                  *chr;
X
X#if !MULTILB
X    lb = tbp->lbp;
X#else
X    if (ldb_free == 0)
X    {
X	garbage_collect(tbp);
X	if (ldb_free == 0)
X	    return 0;
X    }
X
X    /*
X	Detach first led buf in free chain
X    */
X
X    lb = ldb_free;
X
X    if (lb->next == lb)
X	ldb_free = 0;
X    else
X    {
X	lb->next->last = lb->last;
X	lb->last->next = lb->next;
X	ldb_free = lb->next;
X    }
X
X    /*
X	Attach as first led buf in the tty buf' chain.
X    */
X
X    if (tbp->lbp == 0)
X	lb->next = lb->last = lb;
X    else
X    {
X	next = tbp->lbp;
X	last = next->last;
X	(lb->last = last)->next = (lb->next = next)->last = lb;
X    }
X
X#if INKERNEL
X    lb->proc	= u.u_procp;
X    lb->pid	= u.u_procp->p_pid;
X    lb->ppid	= u.u_procp->p_ppid;
X#endif
X
X    lb->tbp = tbp;
X    tbp->lbp = lb;
X#endif /* MULTILB */
X
X    zap_lb(lb,tbp->flags);
X
X    return lb;
X}
X
X#if MULTILB
X/*
X    Find the led_buf in the list belonging to this process.
X
X    ENTRY: Process context: task. Must never be called from interrupt
X    routine. tbp - ptr to tty_buf assigned to tty struct which has led_buf que
X    uproc - pointer to element in process table (used only to inspect the
X    p_pid and p_ppid fields).
X
X    EXIT: returns ptr to led_buf if one is found else returns 0. If
X    led_buf is found, it is placed at the head of the que; tbp->lbp may
X    be moved in that case.
X*/
X
Xstatic struct led_buf   *find_ledbuf(tbp,uproc)
X    struct tty_buf          *tbp;
X    struct proc             *uproc;
X{
X    register struct led_buf *lb;
X    int                     cnt = 0;
X
X    if (tbp->lbp == 0)
X	return 0;
X
X    lb = tbp->lbp;
X
X    do
X    {
X#if INKERNEL
X	if ((lb->proc != 0 && lb->proc == uproc)
X	    && (lb->pid == uproc->p_pid && lb->ppid == uproc->p_ppid))
X	{
X	    if (lb != tbp->lbp)
X	    {
X		struct led_buf          *next,*last;
X
X		lb->last->next = lb->next;
X		lb->next->last = lb->last;
X		next = tbp->lbp;
X		last = next->last;
X		(lb->last = last)->next = (lb->next = next)->last = lb;
X		tbp->lbp = lb;
X	    }
X
X	    return lb;
X	}
X#endif /* INKERNEL */
X
X	lb = lb->next;
X	cnt++;
X    }
X    while (lb != tbp->lbp && cnt < cle_leds);
X
X    return 0;
X}
X
X#endif /* MULTILB */
X
Xstatic void		setcols(tbp,cols)
X    register struct tty_buf *tbp;
X    int			    cols;
X{
X    if (cols < 8)
X    {
X	u.u_error = ERR_BADPARAM;
X	return;
X    }
X
X    if (cols >= MAXLINE)
X	cols = MAXLINE;
X
X    tbp->cols   = cols;
X}
X
X/*
X    Get a tty_buf from the free pool.
X*/
X
X/*
X    ENTRY: Process context: task. Never call this from an interrupt
X    routine. tp - ptr to tty struct to which the tty_buf will become
X    associated.
X
X   
X    EXIT: returns ptr to tty_buf or 0 if there aren't any available.
X    tty_buf is removed from the free list and inserted into the used list
X    which may make tty_used and tty_free change values.
X*/
X
Xstatic struct tty_buf   *get_ttybuf(tp)
X    struct tty              *tp;
X{
X    struct tty_buf          *tbp,*next,*last;
X    int                     cnt;
X    unchar                  *chr;
X
X    if (tty_free == 0)
X    {
X	garbage_collect(tbp);
X	if (tty_free == 0)
X	    return 0;
X    }
X
X    tbp = tty_free;
X
X    if (tbp->next == tbp)
X	tty_free = 0;
X    else
X    {
X	tbp->next->last = tbp->last;
X	tbp->last->next = tbp->next;
X	tty_free = tbp->next;
X    }
X
X    tbp->ttyp = tp;
X    setcols(tbp,COLUMNS);
X    tbp->dorefresh = tbp->readsleep = NO;
X
X#if !MULTILB
X    get_ledbuf(tbp);
X#else
X    tbp->lbp = 0;
X#endif
X
X    next = tty_used;
X    if (next == 0)
X	tbp->next = tbp->last = tbp;
X#if INKERNEL
X    else
END_OF_FILE
if test 44477 -ne `wc -c <'cled.c.1'`; then
    echo shar: \"'cled.c.1'\" unpacked with wrong size!
fi
# end of 'cled.c.1'
fi
if cat cled.c.1 cled.c.2 >cled.c
then
	echo 'cled.c rebuilt'
	rm cled.c.[12]
else
	echo 'trouble concatenating cled.c.[12]'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk