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