#include "sys/param.h" #include "sys/stream.h" #include "sys/conf.h" #include "sys/ttyio.h" #include "sys/xttyld.h" #define ICANBSIZ 256 #define CTRL(C) ((C) != '?' ? (C) & 0x1F : 0x7F) extern int xttycnt; extern struct xttyld xttyld[]; static long open(); static close(); static rsrv(), wsrv(); static struct qinit rinit = { putq, rsrv, open, close, 600, 300 }; static struct qinit winit = { putq, wsrv, open, close, 600, 300 }; struct streamtab xttystream = { &rinit, &winit }; static struct xttyld xttyproto = { B9600, B9600, /* sg_ispeed and sg_ospeed */ '\b', '@', /* sg_erase and sg_kill */ ECHO | CRMOD, /* sg_flags */ CTRL('?'), CTRL('\\'), /* t_intrc and t_quitc */ CTRL('Q'), CTRL('S'), /* t_startc and t_stopc */ CTRL('D'), 0377, /* t_eofc and t_brkc */ }; static char maptab[] = { 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,'|',000,000,000,000,000,'`','{','}',000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000,'\\',000,'~',000, 000,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z',000,000,000,000,000, }; static long open(qp, dev) register struct queue *qp; dev_t dev; { int i; for (i = 0; i < xttycnt; ++i) if (!xttyld[i].qp) { xttyld[i] = xttyproto; xttyld[i].qp = qp; qp->flag |= QDELIM; qp->ptr = (caddr_t) &xttyld[i]; WR(qp)->flag |= QDELIM; WR(qp)->ptr = (caddr_t) &xttyld[i]; return 1; } return 0; } static close(qp) struct queue *qp; { register struct xttyld *xt; xt = (struct xttyld *) qp->ptr; if (xt->icanb) { freeb(xt->icanb); xt->icanb = 0; } xt->qp = 0; } static void ctl(qp, bp) struct queue *qp; register struct block *bp; { register struct xttyld *xt; register u_char *data; xt = (struct xttyld *) qp->ptr; data = (u_char *) stiodata(bp); switch (stiocom(bp)) { case TIOCGETC: *(struct tchars *) data = xt->tc; bp->type = M_IOCACK; bp->wptr = bp->rptr + STIOCHDR + sizeof (struct tchars); break; case TIOCSETC: xt->tc = *(struct tchars *) data; bp->type = M_IOCACK; bp->wptr = bp->rptr + STIOCHDR; break; case TIOCGETP: *(struct sgttyb *) data = xt->sg; bp->type = M_IOCACK; bp->wptr = bp->rptr + STIOCHDR + sizeof (struct sgttyb); break; case TIOCSETN: case TIOCSETP: xt->sg = *(struct sgttyb *) data; bp->type = M_IOCACK; bp->wptr = bp->rptr + STIOCHDR; break; default: (*qp->next->qinfo->putp)(qp->next, bp); return; } (*OTHERQ(qp)->next->qinfo->putp)(OTHERQ(qp)->next, bp); if (xt->status & XTTY_FULL && !(xt->sg.sg_flags & TANDEM)) { xt->status &= ~XTTY_FULL; putctl1d(WR(xt->qp), M_DATA, xt->tc.t_startc); } if (xt->sg.sg_flags & RAW) { xt->status = 0; qenable(WR(xt->qp)); } if (xt->sg.sg_flags & (RAW | CBREAK)) xt->qp->flag &= ~QDELIM; else xt->qp->flag |= QDELIM; } static void sig(xt, sig) register struct xttyld *xt; int sig; { register struct queue *qp; qp = xt->qp; flushq(qp, 0); flushq(WR(qp), 0); if (xt->icanb) { freeb(xt->icanb); xt->icanb = 0; } xt->status &= ~(XTTY_ESCAPED | XTTY_NEWLINE | XTTY_STOPPED); qenable(WR(qp)); putctl(qp->next, M_FLUSH); putctl1(qp->next, M_SIGNAL, sig); putctl(WR(qp)->next, M_FLUSH); } static int icanon(xt, bp) register struct xttyld *xt; register struct block *bp; { register struct block *icanb; register int c, esc; icanb = xt->icanb; esc = xt->sg.sg_flags & CBREAK ? 0 : xt->status & XTTY_ESCAPED; xt->status &= ~XTTY_ESCAPED; while (bp->rptr < bp->wptr) { if (xt->status & XTTY_STOPPED) { xt->status &= ~XTTY_STOPPED; qenable(WR(xt->qp)); } c = *bp->rptr++; if (xt->sg.sg_flags & CRMOD && c == '\r') c = '\n'; if (xt->sg.sg_flags & LCASE && c >= 'A' && c <= 'Z') c += 'a' - 'A'; if (esc) { if (icanb->wptr > icanb->rptr && (c == xt->sg.sg_erase || c == xt->sg.sg_kill || c == xt->tc.t_eofc || c == xt->tc.t_brkc || c == xt->tc.t_intrc || c == xt->tc.t_quitc || c == xt->tc.t_startc || c == xt->tc.t_stopc)) --icanb->wptr; if (c < sizeof maptab && maptab[c]) c = maptab[c]; c |= 0x100; } if (c == xt->tc.t_intrc || c == xt->tc.t_quitc) { bp->rptr = bp->wptr; sig(xt, c == xt->tc.t_intrc ? SIGINT : SIGQUIT); return 0; } if (c == xt->tc.t_stopc) { xt->status |= XTTY_STOPPED; return 0; } if (c == xt->tc.t_startc) return 0; if (xt->sg.sg_flags & ECHO) putctl1d(WR(xt->qp), M_DATA, c); if (!(xt->sg.sg_flags & CBREAK)) { if (c == xt->sg.sg_erase) { if (icanb->wptr > icanb->rptr) --icanb->wptr; continue; } if (c == xt->sg.sg_kill) { icanb->wptr = icanb->rptr; if (xt->sg.sg_flags & ECHO) putctl1d(WR(xt->qp), M_DATA, '\n'); continue; } } if ((xt->sg.sg_flags & CBREAK) || c != xt->tc.t_eofc) if (icanb->wptr < icanb->lim) *icanb->wptr++ = c; else { *--bp->rptr = c; return 1; } if (!(xt->sg.sg_flags & CBREAK)) { if (c == xt->tc.t_eofc || c == xt->tc.t_brkc || c == '\n') return 1; esc = (c & 0xFF) == '\\' ? XTTY_ESCAPED : 0; } } xt->status |= esc; return xt->sg.sg_flags & CBREAK; } static rsrv(qp) struct queue *qp; { register struct xttyld *xt; struct block *bp; xt = (struct xttyld *) qp->ptr; if (!xt->qp) return; while ((qp->next->flag & QFULL) == 0) { if (xt->sg.sg_flags & RAW && xt->icanb) { (*qp->next->qinfo->putp)(qp->next, xt->icanb); xt->icanb = 0; continue; } bp = getq(qp); if (!bp) break; if (!(xt->sg.sg_flags & RAW) && bp->type == M_DATA) { do { if (!xt->icanb) { if (xt->sg.sg_flags & CBREAK) xt->icanb = allocb(bp->rptr - bp->wptr); else xt->icanb = allocb(ICANBSIZ); if (!xt->icanb) { putbq(qp, bp); qp->next->flag |= QWANTW; return; } xt->icanb->type = M_DATA; } if (icanon(xt, bp)) { if (!(xt->sg.sg_flags & CBREAK)) xt->icanb->class |= S_DELIM; (*qp->next->qinfo->putp)(qp->next, xt->icanb); xt->icanb = 0; } } while (bp->rptr != bp->wptr); freeb(bp); } else if (bp->type == M_IOCTL) ctl(qp, bp); else { switch (bp->type) { case M_BREAK: if (xt->sg.sg_flags & RAW) { bp->type = M_DATA; bp->class |= S_DELIM; if (bp->wptr < bp->lim) *bp->wptr++ = '\0'; } else { freeb(bp); sig(xt, SIGINT); return; } break; case M_FLUSH: if (xt->icanb) xt->icanb->wptr = xt->icanb->rptr; break; } bp->class &= ~S_DELIM; (*qp->next->qinfo->putp)(qp->next, bp); } } if ((xt->sg.sg_flags & (RAW | TANDEM)) == TANDEM) { if (!(xt->status & XTTY_FULL) && qp->next->flag & QFULL) { xt->status |= XTTY_FULL; putctl1d(WR(qp), M_DATA, xt->tc.t_stopc); } if (xt->status & XTTY_FULL && (!qp->next->flag & QFULL)) { xt->status &= ~XTTY_FULL; putctl1d(WR(qp), M_DATA, xt->tc.t_startc); } } if (qp->count) qp->next->flag |= QWANTW; } static int ocanon(xt, bp, qp) register struct xttyld *xt; register struct block *bp; struct queue *qp; { register struct block *ocanb; register int c, d, t; while (bp->rptr < bp->wptr) { ocanb = allocb(bp->wptr - bp->rptr + 8); if (!ocanb) return 0; ocanb->class |= S_DELIM; d = 0; while (bp->rptr < bp->wptr && ocanb->wptr < ocanb->lim - 8) { c = *bp->rptr++; t = 8 - (xt->col & 7); if (xt->status & XTTY_NEWLINE) xt->status &= ~XTTY_NEWLINE; else if (xt->sg.sg_flags & CRMOD) { if (c == '\n') { --bp->rptr; c = '\r'; xt->status |= XTTY_NEWLINE; } else if (c == '\r') { *--bp->rptr = '\n'; xt->status |= XTTY_NEWLINE; } } switch (c) { case '\v': case '\f': xt->col = 0; if (xt->sg.sg_flags & VTDELAY) d = 127; break; case '\r': xt->col = 0; switch (xt->sg.sg_flags & CRDELAY) { case CR1: d = 5; break; case CR2: d = 10; break; case CR3: d = 20; break; } break; case '\t': xt->col += t; switch (xt->sg.sg_flags & TBDELAY) { case TAB1: d = t; if (d < 5) d = 0; break; case XTABS: while (t--) *ocanb->wptr++ = ' '; continue; } break; case '\n': xt->col = 0; switch (xt->sg.sg_flags & NLDELAY) { case NL1: d = max((xt->col >> 4) + 3, 6); break; case NL2: d = 6; break; } break; case '\b': if (--xt->col < 0) xt->col = 0; break; default: ++xt->col; break; } *ocanb->wptr++ = c; if (d) break; } (qp->qinfo->putp)(qp, ocanb); if (d) putctl1d(qp, M_DELAY, d); } freeb(bp); return 1; } static wsrv(qp) register struct queue *qp; { register struct xttyld *xt; register struct block *bp; xt = (struct xttyld *) qp->ptr; while ((qp->next->flag & QFULL) == 0 && (bp = getq(qp))) { if (xt->status & XTTY_STOPPED && bp->type == M_DATA) { putbq(qp, bp); qp->flag |= QFULL; return; } if (!(xt->sg.sg_flags & RAW) && (xt->sg.sg_flags & (ALLDELAY | CRMOD)) && bp->type == M_DATA) { if (!ocanon(xt, bp, qp->next)) { putbq(qp, bp); qp->next->flag |= QWANTW; return; } } else if (bp->type == M_IOCTL) ctl(qp, bp); else (*qp->next->qinfo->putp)(qp->next, bp); } if (qp->count) qp->next->flag |= QWANTW; }