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 2 (of 3)." # Contents: cled.c.2 # Wrapped by sw@aware on Tue Aug 28 13:36:10 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'cled.c.2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cled.c.2'\" else echo shar: Extracting \"'cled.c.2'\" \(37029 characters\) sed "s/^X//" >'cled.c.2' <<'END_OF_FILE' X { X int ospl; X X ospl = spl6(); X { X last = next->last; X (tbp->last = last)->next = (tbp->next = next)->last = tbp; X } X splx(ospl); X } X#endif X X tbp->flags = TB_INSERT; X setup_tcap_defaults(tbp); X setup_key_defaults(tbp); X X tty_used = tbp; X return tbp; X} X X/* X Find a tty_buf that's attached to a tty struct and move it to the head of X the que if it's in the list. X X ENTRY: Process context: task. Must not be called from interrupt. tp - X ptr to tty struct to match X X EXIT: returns ptr to tty_buf or 0 if none found. Does not modify the X contents of any structures, so it is re-entrant and interruptable. X*/ X Xstatic struct tty_buf *find_ttybuf(tp) X struct tty *tp; X{ X register struct tty_buf *tbp; X int cnt = 0; X X if (tty_used == 0) X return 0; X X tbp = tty_used; X X do X { X if (tbp->ttyp == tp) X { X#if INKERNEL X if (tbp != tty_used) X { X int ospl; X X ospl = spl6(); X { X register struct tty_buf *next,*last; X X tbp->next->last = tbp->last; X tbp->last->next = tbp->next; X next = tty_used; X last = next->last; X (tbp->last = last)->next = (tbp->next = next)->last = tbp; X tty_used = tbp; X } X splx(ospl); X } X#endif /* INKERNEL */ X return tbp; X } X X tbp = tbp->next; X cnt++; X } X while (tbp != tty_used && cnt < cle_ttys); X X return 0; X} X X/* X The routines cleopen, cleclose, cleread, clewrite and cleioctl are called X by the kernel (via a dispatch through the linesw array) and are all X executed at process task time. The routines cleinput and cleouput are X interrupt routines. Except for cleinput and cleioctl all the routines have X the same interface: a pointer to the tty struct assigned to the real or X pseudo terminal. cleinput has an extra flag argument which indicates X whether the input character is real or the result of the terminal sending X a break. cleioctl has an extra argument that has the function code the X ioctl is to do. X*/ X X/* X Send saved broadcast message to terminal. X X ENTRY: Process context: task. tbp - ptr to tty_buf which contains the X message clr_flg - true if the message line is to be precleared X X EXIT: the cblock(s) holding the message are moved from the clist in X tty_buf to the t_outq clist. X*/ X Xstatic void send_brdcst(tbp,clr_flg) X struct tty_buf *tbp; X int clr_flg; X{ X#if INKERNEL X int ospl; X unchar c; X struct cblock *bp; X struct clist *bl,*tpc; X X tpc = &tbp->ttyp->t_outq; X bl = &tbp->broadcast; X if (clr_flg) X { X cle_putc('\r',tbp->ttyp); X cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp); X } X X ospl = spl6(); X { X while ((bp = getcb(bl)) != 0) X putcb(bp,tpc); X kick_out(tbp->ttyp); X } X splx(ospl); X#endif /* INKERNEL */ X} X X/* X Open line discpline X X ENTRY: tbp - ptr to tty_buf to open (or 0 if not preassigned) td - ptr X to tty struct to open X*/ X Xstatic struct tty_buf *open_it(tbp,td) X register struct tty_buf *tbp; X struct tty *td; X{ X int wasclosed; X X#if INKERNEL X if (tbp != 0) X wasclosed = !(tbp->flags&TB_OPEN); X else X { X garbage_collect(); X tbp = get_ttybuf(td); X wasclosed = YES; X } X X if (tbp != 0) X { X tbp->flags |= (TB_OPEN|TB_OPENING); X if (wasclosed) X { X#if VERBOSE X show_pid(1," started by process ",td); X#endif X } X } X else X { X if (wasclosed) X { X cle_puts("\rNo buffers available to start cled\r\n",td); X u.u_error = ERR_NOTTYBUF; X } X } X X if (wasclosed) X kick_out(td); X#endif X X return tbp; X} X X/* X Open the line discipline. This procedure is called by kernel code when the X line discipline is selected. I haven't yet determined what exactly the X open is supposed to do, but since cled uses discipline 0, if IT (ld0) X isn't opened too, very strange terminal behavior results. X X ENTRY: tp - ptr to tty struct process context: task. X X EXIT: discipline 0 is opened. X*/ X Xcleopen(tp) X struct tty *tp; X{ X#if INKERNEL X (*linesw[0].l_open) (tp); X (void) open_it(find_ttybuf(tp),tp); X#endif X X return 1; X} X X/* X Close the line discpline X X ENTRY: tbp - ptr to tty_buf to close (or 0 if not preassigned) td - ptr X to tty struct to open X*/ X Xstatic void close_it(tbp,td) X struct tty_buf *tbp; X struct tty *td; X{ X#if INKERNEL X if (tbp != 0) X { X int ospl; X X if (td->t_state&ISOPEN) X { X#if VERBOSE X show_pid(0,"\rcled stopped by process ",td); X#endif X kick_out(td); X } X X if (tbp->readsleep) X { X wakeup(tbp); X tbp->readsleep = NO; X if (td->t_state&ISOPEN) X tbp->flags |= TB_FLUSHIT; X } X X ospl = spl6(); X { X tbp->flags &=~ (TB_OPEN|TB_OPENING); X } X splx(ospl); X } X X#if MULTILB X free_leds(tbp); X#endif X X#endif X} X X/* X Close the line discipline. This procedure is called by kernel code when X the line discipline is deselected. I haven't yet determined what exactly X the close is supposed to do, but a call to close discipline 0 is done X because it apparently needs it. X X ENTRY: tp - ptr to tty struct process context: task. X X EXIT: discipline 0 is closed. X*/ X Xcleclose(tp) X struct tty *tp; X{ X struct tty_buf *tbp; X X tbp = find_ttybuf(tp); X X#if INKERNEL X (*tp->t_proc) (tp,T_RESUME); X close_it(tbp,tp); X delay(HZ); X ttyflush(tp,FWRITE|FREAD); X (*linesw[0].l_close) (tp); X#endif X X return 1; X} X X#if INKERNEL X X/* X Input interrupt routine. This routine is called after n characters have X been placed in the interrupt holding buffer (t_rbuf) and/or a certain X time has elapsed since the last input interrupt occurred (This technique X tends to reduce the CPU load somewhat by bunching input character X processing on dumb terminal ports). X X The input routine processes the characters in the device buffer, which X is an array, and then appends them to the raw queue, which is a clist. X It wakes up the waiting process, which will eventually call cleread that X will get the characters from the raw queue, to its internal history, and X then to the user. X X If the line discipline is in raw mode, we check for VINTR and VQUIT and X send the appropriate signal. Notice that we cannot do this in cleread, X because cleread is only called when the user reads. We want to be able X to interrupt even if no read is outstanding. X*/ X Xcleinput(td,bflg) X struct tty *td; X int bflg; X{ X int i,indx,ospl; X unchar ch,*cp,*msg; X struct tty_buf *tbp; X X i = 0; X tbp = tty_used; X X if (tbp != 0) X do X { X if (tbp->ttyp == td) X break; X tbp = tbp->next; X ++i; X } X while ((tbp != tty_used) && i < cle_ttys); X X /* X If we cannot run, let ldisc 0 sort it out... X */ X X if (CLEDOFF(td->t_lflag) || tbp == 0 || tbp->ttyp != td X || (tbp->ttyp == td && (tbp->flags&TB_NOLINE))) X { X (*linesw[0].l_input) (td,bflg); X return; X } X X /* X Our input is either a break condition or rbuf. X */ X X if (bflg == L_BREAK) X { X if (!((td->t_lflag&ISIG) && (td->t_iflag&BRKINT))) X return; X X cp = &ch; X ch = td->t_cc[VINTR]; X i = 1; X } X else X { X if (td->t_rbuf.c_ptr == 0 || td->t_rbuf.c_count >= td->t_rbuf.c_size) X return; X X i = td->t_rbuf.c_size - td->t_rbuf.c_count; X cp = (unchar *) td->t_rbuf.c_ptr; X td->t_rbuf.c_count = td->t_rbuf.c_size; X } X X /* X Here we have cp that points at the array of chars to process, X and i is the number of such characters. X */ X X for (i, cp; i > 0; --i, cp++) X { X register unchar c; X int quote; X X c = *cp; X X if (c == '\0') X continue; X X /* X The switch character is very special. We cannot even X escape it. X */ X if (c == td->t_cc[VSWTCH]) X { X if ((*td->t_proc) (td,T_SWTCH) != T_SWTCH) X return; X } X X if (quote = (td->t_state & CLESC)) X td->t_state &=~ CLESC; X X if (!quote) X { X unchar quit,intr; X X quit = td->t_cc[VQUIT]; X intr = td->t_cc[VINTR]; X X if (c == quit || c == intr) X { X if (!(td->t_lflag&NOFLSH)) X ttyflush(td,FWRITE|FREAD); X X kick_out(td); X X tbp->lbp->flags |= (c == intr) ? LD_INTR : LD_QUIT; X X if (tbp->readsleep) X { X tbp->flags |= TB_FLUSHIT; X tbp->readsleep = NO; X wakeup(tbp); X } X X signal(td->t_pgrp,(c == intr) ? SIGINT : SIGQUIT); X X return; X } X else if (!CLEKEY_CHAR(c) X && tbp->keymap[CLEKEY_CMD(c)] == CLEFUN_PURGE) X { X ttyflush(td,FREAD); X if (tbp->readsleep) X { X tbp->flags |= TB_FLUSHIT; X break; X } X } X } X X if (td->t_rawq.c_cc > (TTYHOG-3) || putc(c,&td->t_rawq) == -1) X { X tbp->flags |= TB_OVERUN; X cle_putc(BEL,td); X continue; X } X X if (!quote) X { X if (!CLEKEY_CHAR(c) && tbp->keymap[CLEKEY_CMD(c)] == CLEFUN_ESCAPE) X td->t_state |= CLESC; X else if X ( X c == td->t_cc[VEOL] X || c == td->t_cc[VEOL2] X || c == td->t_cc[VEOF] X || (!CLEKEY_CHAR(c) X && tbp->keymap[CLEKEY_CMD(c)] == CLEFUN_NEWLINE)) X { X /* count it so rdchk() works */ X td->t_delct++; X } X } X } X X if (tbp->readsleep) X { X tbp->readsleep = NO; X wakeup(tbp); X } X X#if M_UNIX X /* X if by some chance, we're turned on while LD 0 is reading, wake it up X too X */ X if (td->t_state & IASLP) X#if M_ATT X ttrstrt(td); X#else X ttiwake(td); X#endif X#endif X} X X/* X Output interrupt routine. This routine does nothing at this time. Control X is passed to discipline 0 for output. It normally just moves text from the X t_outq clist to the t_obuf output que. Null chars and timers may be set X appropriately depending on the char being output. This discipline has no X interest in changing the behaviour of the output routines. X X ENTRY: td - ptr to tty struct Process context: Interrupt. X X EXIT: Characters may have been moved from t_outq to t_obuf and the X output started. X*/ X Xcleoutput(td) X struct tty *td; X{ X return (*linesw[0].l_output) (td); X} X X/* X read timeout routine. This is a a kludge cuz I can't figure out who's X zapping the tty struct out from under cled. It usually happens after the X cleread routine has gone to sleep. It can be forced, however by some other X process intentionally changing the line number or other tty flags while a X read is in progress. This routine helps correct the situation. X X ENTRY: arg - ptr to the tty_buf belonging to the process doing the read. X X EXIT: TB_TIMEOUT set in the tty flags and a wakeup is delivered. X*/ X Xstatic void read_timeout(arg) X struct tty_buf *arg; X{ X if (arg->readsleep) X wakeup(arg); X arg->readsleep = NO; X} X X/* X Announce that's there's no room at the inn X X ENTRY: tp - ptr to tty struct typ - ascii string describing buffer type X that we're out of X X EXIT: message output to the terminal line discipline reset to ld 0. X*/ X Xstatic int no_room(tp,typ) X struct tty *tp; X unchar *typ; X{ X cle_puts("\rInsufficient ",tp); X cle_puts(typ,tp); X cle_puts(" buffers to run cled at this time.\r",tp); X tp->t_line = 0; X (*linesw[0].l_read) (tp); X return 1; X} X X#endif /* INKERNEL */ X X/* X Read a line from the terminal. This routine exits when the user types the X specified eol character (normally a \r). X X ENTRY: Process context: task. tp - ptr to tty struct X X EXIT: process sleeps until user types one of an EOL, an EOF, a QUIT or X an INTR character (normally an \r, ^D, DEL and ^C characters X respectively). The line of text input from terminal is moved to user's X buffer as specified by u.u_count and u.u_base. An EOF results in 0 X chars being returned. This routine will not exit normally if an X interrupt or quit character is typed (the kernel takes control via the X signal and will cancel the I/O request without this routine going X through its normal exit sequence). If the terminal has been set to X "raw" mode, this read passes control to discipline 0, otherwise the X line editing functions of cled become active while the line of input is X being typed. X*/ X Xcleread(tp) X struct tty *tp; X{ X struct led_buf *lb; X struct tty_buf *tbp; X unchar *cp; X int c,len,time_id; X X tbp = find_ttybuf(tp); X X#if INKERNEL X if (tbp == 0) X return no_room(tp,"tty"); X X if (u.u_count == 0 || CLEDOFF(tp->t_lflag)) X { X if (tbp != 0) X tbp->flags |= TB_NOLINE; X (*linesw[0].l_read) (tp); X return 1; X } X X if (!(tbp->flags&TB_OPEN)) X open_it(tbp,tp); X#endif X X#if !MULTILB X lb = tbp->lbp; X#else X lb = find_ledbuf(tbp,u.u_procp); X if (lb == 0) X lb = get_ledbuf(tbp); X#endif X X if (tbp->broadcast.c_cc != 0) X { X send_brdcst(tbp,0); X cle_putc('\n',tp); X tbp->dorefresh = YES; X } X X#if INKERNEL X if (lb == 0) X { X close_it(tbp,tp); X return no_room(tp,"led"); X } X#endif X X tbp->flags &=~ (TB_NOLINE|TB_OPENING); X X#if INKERNEL X if (lb->owed != 0) X { X len = lb->lcurs - lb->owed; X X if (len > 0) X { X if (len > u.u_count) X len = u.u_count; X if (copyout(lb->owed,u.u_base,len) < 0) X { X u.u_error = EFAULT; X return 1; X } X lb->owed += len; X u.u_base += len; X u.u_count -= len; X if (lb->owed >= lb->lcurs) X lb->owed = 0; X return 1; X } X X lb->owed = 0; X } X#endif X X /* X Initialize command buffer and display to empty line X */ X lb->current = 0; X lb->lastchar = sizeof lb->line - 1; X X lb->lcurs = lb->line; X lb->rcurs = lb->lineend = lb->line + lb->lastchar; X *lb->lcurs = *lb->rcurs = '\0'; X X lb->state = NORMAL; X X /* X Reset history pointers to bottom X */ X lb->lastline = 0; X lb->matchlen = 0; X X /* X Initialize flags X */ X lb->flags &=~ (LD_DONE|LD_EOF|LD_INTR|LD_QUIT|LD_INSERT); X lb->flags |= (LD_DIRTY); X if (tbp->flags&TB_INSERT) X lb->flags |= LD_INSERT; X tbp->flags |= TB_READING; X X#if INKERNEL X tbp->iflag = tp->t_iflag; X tbp->oflag = tp->t_oflag; X tbp->lflag = tp->t_lflag; X tbp->cflag = tp->t_cflag; X for (c = 0; c < NCC + 2; c++) X tbp->cc[c] = tp->t_cc[c]; X X /* X Print any pending output X */ X while (tp->t_outq.c_cc != 0 || (tbp->flags&TB_WRITING)) X { X kick_out(tp); X delay(HZ/10); X } X#endif X X while (!(lb->flags&LD_DONE)) X { X#if INKERNEL X int ospl; X X ospl = spl6(); X { X /* X Wait for input if there is none queued X */ X X if (tp->t_rawq.c_cc == 0) X { X if (tbp->dorefresh) X { X tbp->dorefresh = NO; X splx(ospl); X X reprint(lb); X kick_out(tp); X continue; X } X X if (!(tbp->flags&TB_OPEN)) X { X lb->flags |= (LD_DONE|LD_EOF); X msg("Trying to cleread while CLED turned off",lb); X break; X } X X if (tp->t_line != our_lineno || CLEDOFF(tp->t_lflag)) X { X tp->t_line = our_lineno; X tp->t_iflag = tbp->iflag; X tp->t_oflag = tbp->oflag; X tp->t_lflag = tbp->lflag; X tp->t_cflag = tbp->cflag; X for (c = 0; c < NCC + 2; c++) X tp->t_cc[c] = tbp->cc[c]; X X tp->t_state |= ISOPEN; X tbp->dorefresh = YES; X X msg("CLED: tty struct has been zapped. Resetting it.",lb); X continue; X } X X /* X Sleep for a while for input to arrive X */ X time_id = timeout(read_timeout,tbp,HZ*15); X tbp->readsleep = YES; X X if (sleep(tbp,(PZERO+8)|PCATCH)) X { X ospl = spl6(); X { X tbp->flags &=~ (TB_FLUSHIT|TB_READING); X tbp->dorefresh = tbp->readsleep = NO; X } X splx(ospl); X X untimeout(time_id); X u.u_error = EINTR; X X return -1; X } X X /* X Alleluiah! We may have gotten something from cleinput X */ X X untimeout(time_id); X splx(ospl); X X while (tp->t_outq.c_cc != 0 || (tbp->flags&TB_WRITING)) X { X kick_out(tp); X delay(HZ/10); X } X X if (tbp->flags&TB_FLUSHIT) X { X bol(lb); X d_eol(lb); X X ospl = spl6(); X { X tbp->flags &=~ TB_FLUSHIT; X tbp->dorefresh = tbp->readsleep; X } X splx(ospl); X X if (!(tbp->flags & TB_OPEN)) X break; X } X continue; X } X } X splx(ospl); X#endif /* INKERNEL */ X X /* X Get next char from the raw input queue, filled by cleinput X */ X X c = cle_getc(tp); X X if (c != 0 && lb->state == NORMAL) X { X if (c == tp->t_cc[VERASE]) X c = 0, lb->state = (NCC_SPC(VERASE)); X else if (c == tp->t_cc[VKILL]) X c = 0, lb->state = (NCC_SPC(VKILL)); X else if (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOL2]) X c = 0, lb->state = (NCC_SPC(VEOL)); X else if (c == tp->t_cc[VEOF]) X c = 0, lb->state = (NCC_SPC(VEOL)), lb->flags = LD_EOF; X } X X lb->c = c; X parse_it(lb); X X#if INKERNEL X kick_out(tp); X#endif X } X X tbp->flags &=~ TB_READING; X X cp = lb->lcurs; X if (!(lb->flags&(LD_EOF/*|LD_INTR|LD_QUIT*/))) X { X *cp++ = '\n'; X lb->lcurs = cp; X } X X len = cp - lb->line; X X#if INKERNEL X if (len != 0 && !(lb->flags&(LD_INTR|LD_QUIT))) X { X if (len > u.u_count) X len = u.u_count; X if (copyout(lb->line,u.u_base,len) < 0) X { X u.u_error = EFAULT; X return; X } X u.u_base += len; X u.u_count -= len; X tp->t_col = 0; X cle_putc('\r',tp); /* ICRNL */ X cle_putc('\n',tp); /* OCRNL */ X } X X /* count down the delimiter */ X if (tp->t_delct > 0) X --tp->t_delct; X X if (tbp->broadcast.c_cc != 0) X { X send_brdcst(tbp,0); X cle_putc('\r',tp); X cle_putc('\n',tp); X } X X kick_out(tp); X#else X strncpy(u.u_base,lb->line,len); X *(u.u_base + len) = 0; X#endif /* INKERNEL */ X X lb->owed = (lb->line + len >= lb->lcurs) ? (unchar *) 0 : lb->line+len; X X lb->promptlen = 0; X lb->prompt[0] = '\0'; X X return 1; X} X X#if INKERNEL X/* X Write some text to the terminal but catch any trailing data into a prompt X buffer. This routine is executed when no read is in progress on the X terminal, an lb buffer is assigned to the terminal and a write comes X through. X X ENTRY: tbp - ptr to tty_buf assigned to terminal lb - ptr to X led_buf assigned to terminal u.u_base - ptr to message X (u.u_count has length of message) X X EXIT: the text from the trailing \n (if any) of the message has been X copied to the prompt buffer in the led_buf. If the last char of the X message is a \n, then prompt buffer is left empty. If there are no X \n's in the message, then the whole message is appended to any X existing data in the prompt buffer. Chars are removed from the X beginning of the buffer if the length of the new message exceeds the X MAXPROMPT parameter. X*/ X Xstatic void catch_prompt(tbp,lb) X struct tty_buf *tbp; X struct led_buf *lb; X{ X#if M_SPTALLOC X unchar *prompt; X#else X unchar prompt[MAXPROMPT+1]; X#endif X int maxlen; X register unchar *tail,*old; X register int headlen; X X if (tbp->broadcast.c_cc != 0) X send_brdcst(tbp,1); X X /* X If the write is zero length, we just return; if the last X character of the write is a newline, than the write is surely X not for a prompt; prompts are non NL terminated writes. X */ X X { X unchar ch; X X if (copyin(u.u_base + u.u_count - 1,&ch,sizeof ch) < 0) X return; X X if (ch == '\n') X { X lb->promptlen = 0; X lb->prompt[0] = '\0'; X return; X } X } X X#if M_SPTALLOC X prompt = (unchar *) Sptalloc(maxlen+1); X#endif X X /* X Fetch the last maxlen characters of the prompt, and null X terminate them. X */ X maxlen = MIN(u.u_count,MAXPROMPT); /* This is at least 1 */ X if (copyin(u.u_base + (u.u_count - maxlen),prompt,maxlen) < 0) X return; X *(tail = prompt + maxlen) = '\0'; X X /* X Determine the real prompt, which is the tail after the last X '\r' or '\n'. We *know* that maxlen != 0 implies tail > prompt initially. X */ X do --tail; while (tail > prompt && !(*tail == '\r' || *tail == '\n')); X if (*tail == '\r' || *tail == '\n') tail++; X X /* X First we assume that the head of the new prompt is empty, then we X check; if the old prompt was not empty and the new prompt tail did not X start with '\r' or '\n', and leaves some space in the prompt buffer we X prepend the tail of the old prompt as the head of the new one, X shifting it into place if necessary. We expect this to be executed X very rarely; actually we should probably not bother at all... X */ X X old = lb->prompt; X X if (tail == prompt && lb->promptlen && !(tbp->flags&TB_WRITING) X && (headlen = tail-prompt + MAXPROMPT-maxlen) > 0) X { X if (headlen >= lb->promptlen) X old += lb->promptlen; X else X { X bcopy(old+lb->promptlen-headlen,old,headlen); X old += headlen; X } X } X X /* X A simple while will do instead of bcopy; we assume the tail will X usually be very small, i.e. <= 8 chars. X */ X while (*old++ = *tail++); X lb->promptlen = old-1 - lb->prompt; X X#if M_SPTALLOC X Sptfree(temp,maxlen+1); X#endif X} X X/* X Breakthru. This routine is called when a message is to be sent to the X terminal while a read is in progress. The message is prefixed with a X \r<clear_to_eol> to make room on the current line for the new message and X all text up to and including the last \n is transmitted to the terminal. X Any text from the last \n to the end of the buffer is saved in the X broadcast clist which is transmitted either with the next message to be X written or when the read completes. In any case, the refresh bit is set to X cause the user's command line to be reprinted after the write completes. X X ENTRY: tbp - ptr to tty_buf u.u_base - ptr to message to send to X terminal. X X EXIT: user data is transmitted to the terminal. X*/ X X#if BREAKTHRU Xstatic void breakthru(tbp) X struct tty_buf *tbp; X{ X unchar last; X int len; X struct clist *bcl; X X if (copyin(u.u_base + u.u_count - 1,&last,1) < 0) X return; X X bcl = &tbp->broadcast; X X if (last == '\n') X { X if (bcl->c_cc > 0) X send_brdcst(tbp,YES); X else X { X cle_putc('\r',tbp->ttyp); X cle_puts(tbp->tcap[TCAP_CLREOL],tbp->ttyp); X } X tbp->dorefresh = YES; X } X else X { X int len,oldlen; X unchar *src,*dst; X#if M_SPTALLOC X unchar *temp; X#else X unchar temp[MAXPROMPT]; X#endif X X len = 132; /* assume max length */ X if (len <= u.u_count) X { X if (bcl->c_cc > 0) X send_brdcst(tbp,1); X } X else X { X /* user data is smaller than 1 line */ X X /* but would overflow */ X if (bcl->c_cc + u.u_count > len) X { X send_brdcst(tbp,1); X cle_putc('\r',tbp->ttyp); X cle_putc('\n',tbp->ttyp); X tbp->dorefresh = YES; X } X X len = u.u_count; X } X X oldlen = len; X X#if M_SPTALLOC X temp = (unchar *) Sptalloc(oldlen + 1); X#endif X X if (copyin(u.u_base + (u.u_count - len),temp,len) < 0) X return; X X src = temp + len; X *src = '\0'; X X for (; len > 0; --len) X { X unchar c; X X c = *--src; X if (c == '\r' || c == '\n') X { X ++src; X break; X } X } X X /* compute real length of message */ X len = oldlen - len; X if (bcl->c_cl != 0) X { X int i; X struct cblock *cbkp; X X cbkp = bcl->c_cl; X i = CLSIZE - cbkp->c_last; /* room in last cblock */ X if (i > 0) X { X dst = &cbkp->c_data[cbkp->c_last]; X if (i > len) X i = len; X len -= i; X u.u_count -= i; X bcl->c_cc += i; X /* move end ptr */ X cbkp->c_last += i; X while (i-- > 0) X *dst++ = *src++; X } X } X X if (len > 0) X { X int ospl; X struct clist tcl; X X /* init our dummy clist */ X tcl.c_cc = 0; X tcl.c_cf = tcl.c_cl = 0; X u.u_count -= len; X X ospl = spl6(); X { X putcbp(&tcl,src,len); X } X splx(ospl); X X /* if broadcast is empty */ X if (bcl->c_cf == 0) X bcl->c_cf = tcl.c_cf; X else X bcl->c_cl->c_next = tcl.c_cf; X X bcl->c_cc += tcl.c_cc; X bcl->c_cl = tcl.c_cl; X } X X#if M_SPTALLOC X Sptfree(temp,oldlen + 1); X#endif X } X X kick_out(tbp->ttyp); X} X#endif /* BREAKTHRU */ X X/* X Write some text to the terminal. Writes to the terminal can occur at any X time by any suitably privledged process. An attempt is made to determine X what writes constitute the output of a "prompt" string. This is done by X capturing and remembering in the brodcast clist a copy of the data to X write from the last \r or \n in the message to the end of the message. X Data that has no \r or \n in it is appended to any existing data in the X clist (often processes do single char output to the terminal so its no X wonder the system gets slow at times). If a led_buf has been assigned to X the process doing the write, then the "prompt" data is placed in the X led_buf instead of the broadcast clist. If a read is pending on the X terminal when a write is issued, only the data up to and including the X last \n is transmitted. The remainder is saved in the broadcast clist. If X data ends up being sent to the terminal, then the refresh bit is set and X the read process is awakened (which causes broadcast messages to X automatically refresh the input line). X X ENTRY: Process context: task. td - ptr to tty struct X X EXIT: Write data sent to the terminal and/or stored in the broadcast X clist or led_buf (if one assigned). X*/ X Xclewrite(td) X struct tty *td; X{ X int ospl; X struct led_buf *lb; X struct tty_buf *tbp; X X if (CLEDOFF(td->t_lflag)) X { X (*linesw[0].l_write)(td); X return; X } X X tbp = find_ttybuf(td); X if (tbp != 0) X { X#if BREAKTHRU X if (tbp->flags&TB_READING) X breakthru(tbp); X else X#else X if (!(tbp->flags&TB_READING)) X#endif X { X#if !MULTILB X lb = tbp->lbp; X if (u.u_count > 0) X catch_prompt(tbp,lb); X#else X lb = find_ledbuf(tbp,u.u_procp); X if (lb != 0) X { X if (u.u_count > 0) X catch_prompt(tbp,lb); X } X else X { X if (tbp->broadcast.c_cc > 0) X send_brdcst(tbp,0); X } X#endif X } X tbp->flags |= TB_WRITING; X } X X if (u.u_count > 0) X (*linesw[0].l_write)(td); X else kick_out(td); X X if (tbp != 0) X { X tbp->flags &=~ TB_WRITING; X X ospl = spl6(); X { X if (tbp->dorefresh && tbp->readsleep) X { X tbp->readsleep = NO; X wakeup(tbp); X } X } X splx(ospl); X } X} X X#if VERBOSE Xstatic void show_pid(disp,str,td) X int disp; X unchar *str; X struct tty *td; X{ X unchar tmp[10],*s; X X if (disp != 0) X { X cle_puts("\rcled version ",td); X cle_puts(VERSION,td); X cle_putc('.',td); X s = itoa(PATCHLEVEL,tmp,10); *s = '\0'; cle_puts(s,td); X cle_putc(' ',td); X } X cle_puts(str,td); X s = itoa(u.u_procp->p_pid,tmp,10); *s = '\0'; cle_puts(tmp,td); X cle_puts(" (",td); X s = itoa(u.u_procp->p_ppid,tmp,10); *s = '\0'; cle_puts(tmp,td); X cle_puts(")\r\n",td); X return; X} X#endif /* VERBOSE */ X X/* X Line discipline IOCTL routine. X X ENTRY: Process context: task. td - ptr to tty struct f1 - function to X perform X X EXIT: ioctl function is performed X*/ X Xcleioctl(td,f1,arg,mode) X struct tty *td; X int f1,mode; X faddr_t arg; X{ X struct tty_buf *tbp; X struct led_buf *lb; X X tbp = find_ttybuf(td); X if (tbp != 0 X && ((tbp->flags&(TB_READING|TB_WRITING)) || !tbp->readsleep)) X { X#if VERBOSE X cle_puts("CLED: ioctl issued while terminal busy. stty bits may be zapped",tbp->tbp); X#endif X kick_out(td); X } X X if (f1 < LDGETCOLS) X { X if (f1 == LDCLOSE) X close_it(tbp,td); X else if (f1 == LDOPEN || f1 == LDCHG) X open_it(tbp,td); X (*linesw[0].l_ioctl) (td,f1,arg,mode); X kick_out(td); X return; X } X X#if CLEDIO X X garbage_collect(tbp); X X if (tbp == 0) X tbp = get_ttybuf(td); X X if (tbp == 0) X { X u.u_error = ERR_NOTTYBUF; X return; X } X X#if MULTILB X if ((lb = tbp->lbp) != 0) X { X struct led_buf *us = 0,*parent = 0; X X do X { X if (u.u_procp->p_ppid == lb->pid) parent = lb; X if (u.u_procp->p_pid == lb->pid) us = lb; X } X while ((lb = lb->next) != tbp->lbp); X X lb = (us != 0) ? us : (parent != 0) ? parent : 0; X } X#else X lb = tbp->lbp; X#endif X X switch (f1) X { X case LDGETCOLS: X if (copyout(&tbp->cols,arg,sizeof tbp->cols) < 0) X { X u.u_error = EFAULT; X return; X } X break; X X case LDSETCOLS: X { X int cols; X X if (copyin(arg,&cols,sizeof cols) < 0) X { X u.u_error = EFAULT; X return; X } X setcols(tbp,cols); X } X break; X X case LDGETBF: X { X struct set_key sk; X char cnt,len; X faddr_t outp; X X sk.modes = 0; X sk.modes |= (tbp->flags&TB_INSERT) ? CLEMODE_INSERT : CLEMODE_OVER; X sk.kdbuf_len = CLEKEY_MAX; X X if (copyout(tbp->keymap,arg + sizeof (sk),CLEKEY_MAX) < 0) X { X u.u_error = EFAULT; X return; X } X outp = arg + sizeof (sk) + CLEKEY_MAX; X X for (cnt = 0; cnt < TCAP_COUNT; ++cnt) X { X unchar *s; X X if (copyout(&cnt,outp++,1) < 0) X { X u.u_error = EFAULT; X return; X } X s = (unchar *) tbp->tcap[cnt]; X while (*s++); X X len = s - (unchar *) tbp->tcap[cnt]; X if (copyout(tbp->tcap[cnt],outp,len) < 0) X { X u.u_error = EFAULT; X return; X } X outp += len; X } X X sk.tcapbuf_len = outp - (arg + sizeof (sk) + CLEKEY_MAX); X if (copyout(&sk,arg,sizeof (sk)) < 0) X { X u.u_error = EFAULT; X return; X } X } X break; X X case LDSETBF: X { X struct set_key sk; X int oldflag; X X if (copyin(arg,&sk,sizeof (sk)) < 0) X { X u.u_error = EFAULT; X return; X } X oldflag = tbp->flags; X if (sk.modes & CLEMODE_INSERT) tbp->flags |= TB_INSERT; X else if (sk.modes & CLEMODE_OVER) tbp->flags &= ~TB_INSERT; X X if (sk.kdbuf_len > CLEKEY_MAX) X { X sk.kdbuf_len = CLEKEY_MAX; X if (copyout(&sk,arg,sizeof (sk)) < 0) X { X u.u_error = EFAULT; X return; X } X u.u_error = ERR_BADPARAM; X return; X } X X if (sk.kdbuf_len > 0) X { X#if M_SPTALLOC X unchar *tmp,*kp; X#else X unchar tmp[100],*kp; X#endif X int cnt,size; X X size = sk.kdbuf_len * 2; X#if M_SPTALLOC X kp = tmp = (unchar *) Sptalloc(size); X#else X if (size > sizeof (tmp)) X { X u.u_error = ERR_BADPARAM; X return; X } X kp = tmp; X#endif X X if (copyin(arg + sizeof (sk),tmp,size) < 0) X { X u.u_error = EFAULT; X return; X } X for (cnt = 0; cnt < sk.kdbuf_len; ++cnt) X { X int key,func; X X key = *kp++; X func = *kp++; X if (key >= CLEKEY_MAX || func >= CLEFUN_MAX) X { X#if M_SPTALLOC X Sptfree(tmp,size); X#endif X sk.kdbuf_len = cnt; X copyout(&sk,arg,sizeof (sk)); X u.u_error = ERR_BADPARAM; X return; X } X tbp->keymap[CLEKEY_CMD(key)] = func; X } X#if M_SPTALLOC X Sptfree(tmp,size); X#endif X } X X if (sk.tcapbuf_len > 0) X { X setup_tcap_defaults(tbp); X if (sk.tcapbuf_len > 1) X { X unchar *s; X int nfg = 0; X#if M_SPTALLOC X s = tbp->tcstrings = (unchar *) Sptalloc(sk.tcapbuf_len); X#else X s = tbp->tcstrings; X if (sk.tcapbuf_len > TCAP_SIZE) X { X sk.tcapbuf_len = TCAP_SIZE; X copyout(&sk,arg,sizeof (sk)); X u.u_error = ERR_BADPARAM; X return; X } X#endif X tbp->tclen = sk.tcapbuf_len; X if (copyin(arg + sizeof (sk) + sk.kdbuf_len*2,s,tbp->tclen) < 0) X { X u.u_error = EFAULT; X return; X } X while (s < tbp->tcstrings + tbp->tclen) X { X unchar *ap; X int str; X X str = *s++; X if (str >= TCAP_COUNT) X { X nfg = 1; X break; X } X ap = s; X while (*ap && ap < tbp->tcstrings + tbp->tclen) X ++ap; X if (*ap != 0) X { X nfg = 1; X break; X } X X tbp->tcap[str] = (char *) s; X s = ap + 1; X } X if (nfg) X { X sk.tcapbuf_len = s - tbp->tcstrings; X copyout(&sk,arg,sizeof (sk)); X u.u_error = ERR_BADPARAM; X return; X } X } X } X } X break; X X case LDGETHB: X if (lb == 0) X { X u.u_error = ERR_NOLBASS; X return; X } X X if (copyout(lb->history,arg,MAXHISTORY) < 0) X { X u.u_error = EFAULT; X return; X } X break; X X default: X u.u_error = ERR_BADIOCTL; X } X#endif /* CLEDIO */ X} X X/* X Device driver entry points; used for backdoor setup of CLED. X*/ X Xcledinit(dev) X int dev; X{ X int cnt; X X for (cnt = 0; cnt < linecnt; ++cnt) X if (linesw[cnt].l_open == cleopen) X break; X X if (cnt < linecnt) X { X our_lineno = cnt; X printf("CLED %s PL%d is ldisc %d.\n",VERSION,PATCHLEVEL,cnt); X garbage_collect((struct tty_buf *) 0); X } X else X { X#if M_UNIX X cmn_err(CE_WARN,"CLED %s not in linesw table. Not installed",VERSION); X#else X printf("CLED %s not in linesw table. Not installed.\n",VERSION); X#endif X } X return; X} X Xcledioctl(dev,cmd,arg,mode) X int dev,cmd,mode; X faddr_t arg; X{ X switch (cmd) X { X X#if CLEDIO X case LDGETS: X { X int cnt; X unchar *s; X struct led_buf *lb; X struct tty_buf *tbp; X struct cle_stats sts; X X sts.line = our_lineno; X sts.ledbufs = cle_leds; X sts.ttybufs = cle_ttys; X sts.promptsize = MAXPROMPT; X sts.linesize = MAXLINE; X sts.histsize = MAXHISTORY; X sts.multi_lb = MULTILB; X sts.spt = M_SPTALLOC; X#if M_SPTALLOC X sts.tcapsize = 256; X#else X sts.tcapsize = TCAP_SIZE; X#endif X X s = (unchar *) VERSION; X for (cnt = 0; cnt < 4; ++cnt) X { X if ((sts.vers[cnt] = *s++) == 0) X break; X } X X tbp = tty_free; X cnt = 0; X if (tbp != 0) X { X do X { X tbp = tbp->next; X ++cnt; X } while (tbp != tty_free && cnt < cle_ttys); X } X sts.ttybufs_used = cle_ttys - cnt; X X#if !MULTILB X sts.ledbufs_used = sts.ttybufs_used; X#else X cnt = 0; X lb = ldb_free; X if (lb != 0) X { X do X { X lb = lb->next; X ++cnt; X } X while (lb != ldb_free && cnt < cle_leds); X } X X sts.ledbufs_used = cle_leds - cnt; X#endif X X if (copyout(&sts,arg,sizeof (struct cle_stats)) < 0) X { X u.u_error = EFAULT; X return; X } X } X break; X#endif /* CLEDIO */ X X#if DEBUG X case LDGETB: X { X struct cle_buf bfs; X X bfs.lbsize = cle_leds * sizeof (struct led_buf); X bfs.tbsize = cle_ttys * sizeof (struct tty_buf); X X bfs.lbbase = cle_buffers; X#if MULTILB X bfs.lbfree = ldb_free; X#else X bfs.lbfree = 0; X#endif X X bfs.tbbase = cle_ttybuf; X bfs.tbused = tty_used; X bfs.tbfree = tty_free; X X bfs.procbase = proc; X X if (copyout(&bfs,arg,sizeof (struct cle_buf)) < 0) X { X u.u_error = EFAULT; X return; X } X arg += sizeof (struct cle_buf); X X if (copyout(cle_buffers,arg,bfs.lbsize) < 0) X { X u.u_error = EFAULT; X return; X } X arg += bfs.lbsize; X X if (copyout(cle_ttybuf,arg,bfs.tbsize) < 0) X { X u.u_error = EFAULT; X return; X } X } X break; X X case LDGETTTY: X { X struct tty *ttyp; X X if (copyin(arg,&ttyp,sizeof (ttyp)) < 0 X || copyout(ttyp,arg,sizeof (struct tty)) < 0) X { X u.u_error = EFAULT; X return; X } X } X break; X X case LDGETC: X { X struct clist *clp; X struct cblock *cbp; X int cnt; X X if (copyin(arg,&clp,sizeof (clp)) < 0) X { X u.u_error = EFAULT; X return; X } X if (clp->c_cc != 0) X { X int ospl; X X ospl = spl6(); X { X cbp = clp->c_cf; X do X { X unchar siz; X X siz = cbp->c_last - cbp->c_first; X if (siz != 0) X { X if (copyout(&siz,arg,1) < 0) X { X u.u_error = EFAULT; X return; X } X ++arg; X if (copyout(&cbp->c_data[cbp->c_first],arg,siz) < 0) X { X u.u_error = EFAULT; X return; X } X arg += siz; X } X cbp = cbp->c_next; X } while (cbp != 0); X } X splx(ospl); X } X X cnt = 0; X if (copyout(&cnt,arg,1) < 0) X { X u.u_error = EFAULT; X return; X } X } X break; X#endif /* M_DEBUG */ X default: X u.u_error = ERR_BADIOCTL; X } X} X X#else /* INKERNEL */ X Xstatic unchar cmd_line[256]; Xstatic struct tty dum_tty; X Xstruct termio ostate; /* saved tty state */ Xstruct termio nstate; /* values for editor mode */ X Xmain() X{ X int c; X X dum_tty.t_cc[VINTR] = 'C' & 0x1f; X dum_tty.t_cc[VQUIT] = '\\' & 0x1f; X dum_tty.t_cc[VERASE] = RUB; X dum_tty.t_cc[VKILL] = 'U' & 0x1f; X dum_tty.t_cc[VEOF] = 'D' & 0x1f; X dum_tty.t_cc[VEOL] = 'M' & 0x1f; X X ioctl(0,TCGETA,&ostate); X ioctl(0,TCGETA,&nstate); X X nstate.c_iflag = 0; X nstate.c_oflag = 0; X nstate.c_lflag = 0; X nstate.c_cc[VEOF] = 1; X nstate.c_cc[VEOL] = 0; X X ioctl(0,TCSETA,&nstate); X X u.u_base = cmd_line; X u.u_count = sizeof (cmd_line); X X garbage_collect((struct tty_buf *) 0); X X tty_used = cle_ttybuf; X get_ttybuf(&dum_tty); X X cle_puts("Outputting controls\r\n",&dum_tty); X X while (cleread(&dum_tty) && strlen(cmd_line)) X fprintf(stderr,"\r\nRead %d chars\r\n",strlen(cmd_line)); X X ioctl(0,TCSETA,&ostate); X return; X} X X#endif /* INKERNEL */ END_OF_FILE if test 37029 -ne `wc -c <'cled.c.2'`; then echo shar: \"'cled.c.2'\" unpacked with wrong size! fi # end of 'cled.c.2' fi echo shar: End of archive 2 \(of 3\). cp /dev/null ark2isdone 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