1bc093719SEd Schouten /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 4bc093719SEd Schouten * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 5bc093719SEd Schouten * All rights reserved. 6bc093719SEd Schouten * 7bc093719SEd Schouten * Portions of this software were developed under sponsorship from Snow 8bc093719SEd Schouten * B.V., the Netherlands. 9bc093719SEd Schouten * 10bc093719SEd Schouten * Redistribution and use in source and binary forms, with or without 11bc093719SEd Schouten * modification, are permitted provided that the following conditions 12bc093719SEd Schouten * are met: 13bc093719SEd Schouten * 1. Redistributions of source code must retain the above copyright 14bc093719SEd Schouten * notice, this list of conditions and the following disclaimer. 15bc093719SEd Schouten * 2. Redistributions in binary form must reproduce the above copyright 16bc093719SEd Schouten * notice, this list of conditions and the following disclaimer in the 17bc093719SEd Schouten * documentation and/or other materials provided with the distribution. 18bc093719SEd Schouten * 19bc093719SEd Schouten * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20bc093719SEd Schouten * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21bc093719SEd Schouten * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22bc093719SEd Schouten * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23bc093719SEd Schouten * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24bc093719SEd Schouten * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25bc093719SEd Schouten * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26bc093719SEd Schouten * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27bc093719SEd Schouten * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28bc093719SEd Schouten * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29bc093719SEd Schouten * SUCH DAMAGE. 30bc093719SEd Schouten */ 31bc093719SEd Schouten 32bc093719SEd Schouten #include <sys/param.h> 33bc093719SEd Schouten #include <sys/fcntl.h> 34bc093719SEd Schouten #include <sys/filio.h> 35bc093719SEd Schouten #include <sys/kernel.h> 36bc093719SEd Schouten #include <sys/signal.h> 37bc093719SEd Schouten #include <sys/sysctl.h> 38bc093719SEd Schouten #include <sys/systm.h> 39bc093719SEd Schouten #include <sys/tty.h> 40bc093719SEd Schouten #include <sys/ttycom.h> 41bc093719SEd Schouten #include <sys/ttydefaults.h> 42bc093719SEd Schouten #include <sys/uio.h> 43bc093719SEd Schouten #include <sys/vnode.h> 44bc093719SEd Schouten 459e589b09SBojan Novković #include <teken/teken.h> 469e589b09SBojan Novković #include <teken/teken_wcwidth.h> 479e589b09SBojan Novković 48bc093719SEd Schouten /* 49bc093719SEd Schouten * Standard TTYDISC `termios' line discipline. 50bc093719SEd Schouten */ 51bc093719SEd Schouten 52bc093719SEd Schouten /* Statistics. */ 531d952ed2SEd Schouten static unsigned long tty_nin = 0; 541d952ed2SEd Schouten SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, 55bc093719SEd Schouten &tty_nin, 0, "Total amount of bytes received"); 561d952ed2SEd Schouten static unsigned long tty_nout = 0; 571d952ed2SEd Schouten SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, 58bc093719SEd Schouten &tty_nout, 0, "Total amount of bytes transmitted"); 59bc093719SEd Schouten 60bc093719SEd Schouten /* termios comparison macro's. */ 61bc093719SEd Schouten #define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ 62bc093719SEd Schouten tp->t_termios.c_cc[v] == (c)) 63bc093719SEd Schouten #define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) 64bc093719SEd Schouten 65bc093719SEd Schouten /* Characters that cannot be modified through c_cc. */ 66bc093719SEd Schouten #define CTAB '\t' 67bc093719SEd Schouten #define CNL '\n' 68bc093719SEd Schouten #define CCR '\r' 69bc093719SEd Schouten 70bc093719SEd Schouten /* Character is a control character. */ 71bc093719SEd Schouten #define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) 72bc093719SEd Schouten /* Control character should be processed on echo. */ 73bc093719SEd Schouten #define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ 74bc093719SEd Schouten (c) == CNL || (c) == CCR)) 75bc093719SEd Schouten /* Control character should be printed using ^X notation. */ 76bc093719SEd Schouten #define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ 77bc093719SEd Schouten ((q) || ((c) != CTAB && (c) != CNL)))) 78bc093719SEd Schouten /* Character is whitespace. */ 79bc093719SEd Schouten #define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) 80bc093719SEd Schouten /* Character is alphanumeric. */ 81bc093719SEd Schouten #define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ 82bc093719SEd Schouten ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 839e589b09SBojan Novković /* Character is UTF8-encoded. */ 849e589b09SBojan Novković #define CTL_UTF8(c) (!!((c) & 0x80)) 859e589b09SBojan Novković /* Character is a UTF8 continuation byte. */ 869e589b09SBojan Novković #define CTL_UTF8_CONT(c) (((c) & 0xc0) == 0x80) 87bc093719SEd Schouten 88a1215e37SEd Schouten #define TTY_STACKBUF 256 899e589b09SBojan Novković #define UTF8_STACKBUF 4 90a1215e37SEd Schouten 91bc093719SEd Schouten void 92bc093719SEd Schouten ttydisc_open(struct tty *tp) 93bc093719SEd Schouten { 94bc093719SEd Schouten ttydisc_optimize(tp); 95bc093719SEd Schouten } 96bc093719SEd Schouten 97bc093719SEd Schouten void 98bc093719SEd Schouten ttydisc_close(struct tty *tp) 99bc093719SEd Schouten { 100bc093719SEd Schouten 101bc093719SEd Schouten /* Clean up our flags when leaving the discipline. */ 102bc093719SEd Schouten tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); 103cbda6f66SWarner Losh tp->t_termios.c_lflag &= ~FLUSHO; 104bc093719SEd Schouten 1052cd5358aSKonstantin Belousov /* 1062cd5358aSKonstantin Belousov * POSIX states that we must drain output and flush input on 1072cd5358aSKonstantin Belousov * last close. Draining has already been done if possible. 1082cd5358aSKonstantin Belousov */ 1092cd5358aSKonstantin Belousov tty_flush(tp, FREAD | FWRITE); 110a1215e37SEd Schouten 111a1215e37SEd Schouten if (ttyhook_hashook(tp, close)) 112a1215e37SEd Schouten ttyhook_close(tp); 113bc093719SEd Schouten } 114bc093719SEd Schouten 115*d51dac5fSKyle Evans /* 116*d51dac5fSKyle Evans * Populate our break array; it should likely be at least 4 bytes in size to 117*d51dac5fSKyle Evans * allow for \n, VEOF, and VEOL. 118*d51dac5fSKyle Evans */ 119*d51dac5fSKyle Evans static void 120*d51dac5fSKyle Evans ttydisc_read_break(struct tty *tp, char *breakc, size_t breaksz) 121bc093719SEd Schouten { 122*d51dac5fSKyle Evans size_t n = 0; 123bc093719SEd Schouten 124*d51dac5fSKyle Evans MPASS(breaksz != 0); 125*d51dac5fSKyle Evans 126*d51dac5fSKyle Evans breakc[n++] = CNL; 127bc093719SEd Schouten #define BREAK_ADD(c) do { \ 128*d51dac5fSKyle Evans MPASS(n < breaksz - 1); /* NUL terminated */ \ 129bc093719SEd Schouten if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ 130bc093719SEd Schouten breakc[n++] = tp->t_termios.c_cc[c]; \ 131bc093719SEd Schouten } while (0) 132bc093719SEd Schouten /* Determine which characters we should trigger on. */ 133bc093719SEd Schouten BREAK_ADD(VEOF); 134bc093719SEd Schouten BREAK_ADD(VEOL); 135bc093719SEd Schouten #undef BREAK_ADD 136*d51dac5fSKyle Evans 137bc093719SEd Schouten breakc[n] = '\0'; 138*d51dac5fSKyle Evans } 139*d51dac5fSKyle Evans 140*d51dac5fSKyle Evans size_t 141*d51dac5fSKyle Evans ttydisc_bytesavail(struct tty *tp) 142*d51dac5fSKyle Evans { 143*d51dac5fSKyle Evans size_t clen; 144*d51dac5fSKyle Evans char breakc[4]; 145*d51dac5fSKyle Evans unsigned char lastc = _POSIX_VDISABLE; 146*d51dac5fSKyle Evans 147*d51dac5fSKyle Evans clen = ttyinq_bytescanonicalized(&tp->t_inq); 148*d51dac5fSKyle Evans if (!CMP_FLAG(l, ICANON) || clen == 0) 149*d51dac5fSKyle Evans return (clen); 150*d51dac5fSKyle Evans 151*d51dac5fSKyle Evans ttydisc_read_break(tp, &breakc[0], sizeof(breakc)); 152*d51dac5fSKyle Evans clen = ttyinq_findchar(&tp->t_inq, breakc, clen, &lastc); 153*d51dac5fSKyle Evans 154*d51dac5fSKyle Evans /* 155*d51dac5fSKyle Evans * We might have a partial line canonicalized in the input queue if we, 156*d51dac5fSKyle Evans * for instance, switched to ICANON after taking some input in raw mode. 157*d51dac5fSKyle Evans * In this case, read(2) will block because we only have a partial line. 158*d51dac5fSKyle Evans */ 159*d51dac5fSKyle Evans if (lastc == _POSIX_VDISABLE) 160*d51dac5fSKyle Evans return (0); 161*d51dac5fSKyle Evans 162*d51dac5fSKyle Evans /* If VEOF was our terminal, it must be discarded (not counted). */ 163*d51dac5fSKyle Evans if (CMP_CC(VEOF, lastc)) 164*d51dac5fSKyle Evans clen--; 165*d51dac5fSKyle Evans 166*d51dac5fSKyle Evans return (clen); 167*d51dac5fSKyle Evans } 168*d51dac5fSKyle Evans 169*d51dac5fSKyle Evans static int 170*d51dac5fSKyle Evans ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) 171*d51dac5fSKyle Evans { 172*d51dac5fSKyle Evans char breakc[4]; /* enough to hold \n, VEOF and VEOL. */ 173*d51dac5fSKyle Evans int error; 174*d51dac5fSKyle Evans size_t clen, flen = 0; 175*d51dac5fSKyle Evans unsigned char lastc = _POSIX_VDISABLE; 176*d51dac5fSKyle Evans 177*d51dac5fSKyle Evans ttydisc_read_break(tp, &breakc[0], sizeof(breakc)); 178bc093719SEd Schouten 179bc093719SEd Schouten do { 1801da7bb41SEd Schouten error = tty_wait_background(tp, curthread, SIGTTIN); 1811da7bb41SEd Schouten if (error) 1821da7bb41SEd Schouten return (error); 1831da7bb41SEd Schouten 184bc093719SEd Schouten /* 185bc093719SEd Schouten * Quite a tricky case: unlike the old TTY 186bc093719SEd Schouten * implementation, this implementation copies data back 187bc093719SEd Schouten * to userspace in large chunks. Unfortunately, we can't 188bc093719SEd Schouten * calculate the line length on beforehand if it crosses 189bc093719SEd Schouten * ttyinq_block boundaries, because multiple reads could 190bc093719SEd Schouten * then make this code read beyond the newline. 191bc093719SEd Schouten * 192bc093719SEd Schouten * This is why we limit the read to: 193bc093719SEd Schouten * - The size the user has requested 194bc093719SEd Schouten * - The blocksize (done in tty_inq.c) 195bc093719SEd Schouten * - The amount of bytes until the newline 196bc093719SEd Schouten * 197bc093719SEd Schouten * This causes the line length to be recalculated after 198bc093719SEd Schouten * each block has been copied to userspace. This will 199bc093719SEd Schouten * cause the TTY layer to return data in chunks using 200bc093719SEd Schouten * the blocksize (except the first and last blocks). 201bc093719SEd Schouten */ 202*d51dac5fSKyle Evans clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid + 1, 203bc093719SEd Schouten &lastc); 204bc093719SEd Schouten 205bc093719SEd Schouten /* No more data. */ 206bc093719SEd Schouten if (clen == 0) { 2076b1b791dSEd Schouten if (tp->t_flags & TF_ZOMBIE) 208bc093719SEd Schouten return (0); 2096b1b791dSEd Schouten else if (ioflag & IO_NDELAY) 2106b1b791dSEd Schouten return (EWOULDBLOCK); 211bc093719SEd Schouten 212bc093719SEd Schouten error = tty_wait(tp, &tp->t_inwait); 213bc093719SEd Schouten if (error) 214bc093719SEd Schouten return (error); 215bc093719SEd Schouten continue; 216bc093719SEd Schouten } 217bc093719SEd Schouten 218*d51dac5fSKyle Evans /* 219*d51dac5fSKyle Evans * Don't send the EOF char back to userspace. Our above call to 220*d51dac5fSKyle Evans * ttyinq_findchar overreads by 1 character in case we would 221*d51dac5fSKyle Evans * otherwise be leaving an EOF for the next read(). We'll trim 222*d51dac5fSKyle Evans * clen back down to uio_resid whether we find our EOF or not. 223*d51dac5fSKyle Evans */ 224bc093719SEd Schouten if (CMP_CC(VEOF, lastc)) 225bc093719SEd Schouten flen = 1; 226bc093719SEd Schouten 227*d51dac5fSKyle Evans /* 228*d51dac5fSKyle Evans * Trim clen back down to the buffer size, since we had 229*d51dac5fSKyle Evans * intentionally over-read. 230*d51dac5fSKyle Evans */ 231*d51dac5fSKyle Evans clen = MIN(uio->uio_resid + flen, clen); 232bc093719SEd Schouten MPASS(flen <= clen); 233bc093719SEd Schouten 234bc093719SEd Schouten /* Read and throw away the EOF character. */ 235bc093719SEd Schouten error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); 236bc093719SEd Schouten if (error) 237bc093719SEd Schouten return (error); 238bc093719SEd Schouten 239bc093719SEd Schouten } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); 240bc093719SEd Schouten 241bc093719SEd Schouten return (0); 242bc093719SEd Schouten } 243bc093719SEd Schouten 244bc093719SEd Schouten static int 245bc093719SEd Schouten ttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) 246bc093719SEd Schouten { 247bc093719SEd Schouten size_t vmin = tp->t_termios.c_cc[VMIN]; 248526d0bd5SKonstantin Belousov ssize_t oresid = uio->uio_resid; 249bc093719SEd Schouten int error; 250bc093719SEd Schouten 251bc093719SEd Schouten MPASS(tp->t_termios.c_cc[VTIME] == 0); 252bc093719SEd Schouten 253bc093719SEd Schouten /* 254bc093719SEd Schouten * This routine implements the easy cases of read()s while in 255bc093719SEd Schouten * non-canonical mode, namely case B and D, where we don't have 256bc093719SEd Schouten * any timers at all. 257bc093719SEd Schouten */ 258bc093719SEd Schouten 259bc093719SEd Schouten for (;;) { 2601da7bb41SEd Schouten error = tty_wait_background(tp, curthread, SIGTTIN); 2611da7bb41SEd Schouten if (error) 2621da7bb41SEd Schouten return (error); 2631da7bb41SEd Schouten 264bc093719SEd Schouten error = ttyinq_read_uio(&tp->t_inq, tp, uio, 265bc093719SEd Schouten uio->uio_resid, 0); 266bc093719SEd Schouten if (error) 267bc093719SEd Schouten return (error); 268bc093719SEd Schouten if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 269bc093719SEd Schouten return (0); 270bc093719SEd Schouten 271bc093719SEd Schouten /* We have to wait for more. */ 2726b1b791dSEd Schouten if (tp->t_flags & TF_ZOMBIE) 273bc093719SEd Schouten return (0); 2746b1b791dSEd Schouten else if (ioflag & IO_NDELAY) 2756b1b791dSEd Schouten return (EWOULDBLOCK); 276bc093719SEd Schouten 277bc093719SEd Schouten error = tty_wait(tp, &tp->t_inwait); 278bc093719SEd Schouten if (error) 279bc093719SEd Schouten return (error); 280bc093719SEd Schouten } 281bc093719SEd Schouten } 282bc093719SEd Schouten 283bc093719SEd Schouten static int 284bc093719SEd Schouten ttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, 285bc093719SEd Schouten int oresid) 286bc093719SEd Schouten { 287bc093719SEd Schouten size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); 288bc093719SEd Schouten unsigned int vtime = tp->t_termios.c_cc[VTIME]; 289bc093719SEd Schouten struct timeval end, now, left; 290bc093719SEd Schouten int error, hz; 291bc093719SEd Schouten 292bc093719SEd Schouten MPASS(tp->t_termios.c_cc[VTIME] != 0); 293bc093719SEd Schouten 294bc093719SEd Schouten /* Determine when the read should be expired. */ 295bc093719SEd Schouten end.tv_sec = vtime / 10; 296bc093719SEd Schouten end.tv_usec = (vtime % 10) * 100000; 297bc093719SEd Schouten getmicrotime(&now); 298bc093719SEd Schouten timevaladd(&end, &now); 299bc093719SEd Schouten 300bc093719SEd Schouten for (;;) { 3011da7bb41SEd Schouten error = tty_wait_background(tp, curthread, SIGTTIN); 3021da7bb41SEd Schouten if (error) 3031da7bb41SEd Schouten return (error); 3041da7bb41SEd Schouten 305bc093719SEd Schouten error = ttyinq_read_uio(&tp->t_inq, tp, uio, 306bc093719SEd Schouten uio->uio_resid, 0); 307bc093719SEd Schouten if (error) 308bc093719SEd Schouten return (error); 309bc093719SEd Schouten if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 310bc093719SEd Schouten return (0); 311bc093719SEd Schouten 312bc093719SEd Schouten /* Calculate how long we should wait. */ 313bc093719SEd Schouten getmicrotime(&now); 314bc093719SEd Schouten if (timevalcmp(&now, &end, >)) 315bc093719SEd Schouten return (0); 316bc093719SEd Schouten left = end; 317bc093719SEd Schouten timevalsub(&left, &now); 318bc093719SEd Schouten hz = tvtohz(&left); 319bc093719SEd Schouten 320bc093719SEd Schouten /* 321bc093719SEd Schouten * We have to wait for more. If the timer expires, we 322bc093719SEd Schouten * should return a 0-byte read. 323bc093719SEd Schouten */ 3246b1b791dSEd Schouten if (tp->t_flags & TF_ZOMBIE) 325bc093719SEd Schouten return (0); 3266b1b791dSEd Schouten else if (ioflag & IO_NDELAY) 3276b1b791dSEd Schouten return (EWOULDBLOCK); 328bc093719SEd Schouten 329bc093719SEd Schouten error = tty_timedwait(tp, &tp->t_inwait, hz); 330bc093719SEd Schouten if (error) 331bc093719SEd Schouten return (error == EWOULDBLOCK ? 0 : error); 332bc093719SEd Schouten } 333bc093719SEd Schouten 334bc093719SEd Schouten return (0); 335bc093719SEd Schouten } 336bc093719SEd Schouten 337bc093719SEd Schouten static int 338bc093719SEd Schouten ttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) 339bc093719SEd Schouten { 340bc093719SEd Schouten size_t vmin = tp->t_termios.c_cc[VMIN]; 341526d0bd5SKonstantin Belousov ssize_t oresid = uio->uio_resid; 342bc093719SEd Schouten int error; 343bc093719SEd Schouten 344bc093719SEd Schouten MPASS(tp->t_termios.c_cc[VMIN] != 0); 345bc093719SEd Schouten MPASS(tp->t_termios.c_cc[VTIME] != 0); 346bc093719SEd Schouten 347bc093719SEd Schouten /* 348bc093719SEd Schouten * When using the interbyte timer, the timer should be started 349bc093719SEd Schouten * after the first byte has been received. We just call into the 350bc093719SEd Schouten * generic read timer code after we've received the first byte. 351bc093719SEd Schouten */ 352bc093719SEd Schouten 353bc093719SEd Schouten for (;;) { 3541da7bb41SEd Schouten error = tty_wait_background(tp, curthread, SIGTTIN); 3551da7bb41SEd Schouten if (error) 3561da7bb41SEd Schouten return (error); 3571da7bb41SEd Schouten 358bc093719SEd Schouten error = ttyinq_read_uio(&tp->t_inq, tp, uio, 359bc093719SEd Schouten uio->uio_resid, 0); 360bc093719SEd Schouten if (error) 361bc093719SEd Schouten return (error); 362bc093719SEd Schouten if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 363bc093719SEd Schouten return (0); 364bc093719SEd Schouten 365bc093719SEd Schouten /* 366bc093719SEd Schouten * Not enough data, but we did receive some, which means 367bc093719SEd Schouten * we'll now start using the interbyte timer. 368bc093719SEd Schouten */ 369bc093719SEd Schouten if (oresid != uio->uio_resid) 370bc093719SEd Schouten break; 371bc093719SEd Schouten 372bc093719SEd Schouten /* We have to wait for more. */ 3736b1b791dSEd Schouten if (tp->t_flags & TF_ZOMBIE) 374bc093719SEd Schouten return (0); 3756b1b791dSEd Schouten else if (ioflag & IO_NDELAY) 3766b1b791dSEd Schouten return (EWOULDBLOCK); 377bc093719SEd Schouten 378bc093719SEd Schouten error = tty_wait(tp, &tp->t_inwait); 379bc093719SEd Schouten if (error) 380bc093719SEd Schouten return (error); 381bc093719SEd Schouten } 382bc093719SEd Schouten 383bc093719SEd Schouten return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); 384bc093719SEd Schouten } 385bc093719SEd Schouten 386bc093719SEd Schouten int 387bc093719SEd Schouten ttydisc_read(struct tty *tp, struct uio *uio, int ioflag) 388bc093719SEd Schouten { 389bc093719SEd Schouten int error; 390bc093719SEd Schouten 39123d53268SKyle Evans tty_assert_locked(tp); 392bc093719SEd Schouten 393bc093719SEd Schouten if (uio->uio_resid == 0) 394bc093719SEd Schouten return (0); 395bc093719SEd Schouten 396bc093719SEd Schouten if (CMP_FLAG(l, ICANON)) 397bc093719SEd Schouten error = ttydisc_read_canonical(tp, uio, ioflag); 398bc093719SEd Schouten else if (tp->t_termios.c_cc[VTIME] == 0) 399bc093719SEd Schouten error = ttydisc_read_raw_no_timer(tp, uio, ioflag); 400bc093719SEd Schouten else if (tp->t_termios.c_cc[VMIN] == 0) 401bc093719SEd Schouten error = ttydisc_read_raw_read_timer(tp, uio, ioflag, 402bc093719SEd Schouten uio->uio_resid); 403bc093719SEd Schouten else 404bc093719SEd Schouten error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); 405bc093719SEd Schouten 406a15ec0a5SEd Schouten if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow || 407a15ec0a5SEd Schouten ttyinq_bytescanonicalized(&tp->t_inq) == 0) { 408bc093719SEd Schouten /* Unset the input watermark when we've got enough space. */ 409bc093719SEd Schouten tty_hiwat_in_unblock(tp); 410bc093719SEd Schouten } 411bc093719SEd Schouten 412bc093719SEd Schouten return (error); 413bc093719SEd Schouten } 414bc093719SEd Schouten 415bc093719SEd Schouten static __inline unsigned int 416bc093719SEd Schouten ttydisc_findchar(const char *obstart, unsigned int oblen) 417bc093719SEd Schouten { 418bc093719SEd Schouten const char *c = obstart; 419bc093719SEd Schouten 420bc093719SEd Schouten while (oblen--) { 421bc093719SEd Schouten if (CTL_VALID(*c)) 422bc093719SEd Schouten break; 423bc093719SEd Schouten c++; 424bc093719SEd Schouten } 425bc093719SEd Schouten 426bc093719SEd Schouten return (c - obstart); 427bc093719SEd Schouten } 428bc093719SEd Schouten 429bc093719SEd Schouten static int 430bc093719SEd Schouten ttydisc_write_oproc(struct tty *tp, char c) 431bc093719SEd Schouten { 432bc093719SEd Schouten unsigned int scnt, error; 433bc093719SEd Schouten 434bc093719SEd Schouten MPASS(CMP_FLAG(o, OPOST)); 435bc093719SEd Schouten MPASS(CTL_VALID(c)); 436bc093719SEd Schouten 437bc093719SEd Schouten #define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) 438bc093719SEd Schouten switch (c) { 439bc093719SEd Schouten case CEOF: 440bc093719SEd Schouten /* End-of-text dropping. */ 441bc093719SEd Schouten if (CMP_FLAG(o, ONOEOT)) 442bc093719SEd Schouten return (0); 443bc093719SEd Schouten return PRINT_NORMAL(); 444bc093719SEd Schouten 445bc093719SEd Schouten case CERASE2: 446bc093719SEd Schouten /* Handle backspace to fix tab expansion. */ 447bc093719SEd Schouten if (PRINT_NORMAL() != 0) 448bc093719SEd Schouten return (-1); 449bc093719SEd Schouten if (tp->t_column > 0) 450bc093719SEd Schouten tp->t_column--; 451bc093719SEd Schouten return (0); 452bc093719SEd Schouten 453bc093719SEd Schouten case CTAB: 454bc093719SEd Schouten /* Tab expansion. */ 455bc093719SEd Schouten scnt = 8 - (tp->t_column & 7); 456bc093719SEd Schouten if (CMP_FLAG(o, TAB3)) { 457bc093719SEd Schouten error = ttyoutq_write_nofrag(&tp->t_outq, 458bc093719SEd Schouten " ", scnt); 459bc093719SEd Schouten } else { 460bc093719SEd Schouten error = PRINT_NORMAL(); 461bc093719SEd Schouten } 462bc093719SEd Schouten if (error) 463bc093719SEd Schouten return (-1); 464bc093719SEd Schouten 465bc093719SEd Schouten tp->t_column += scnt; 466bc093719SEd Schouten MPASS((tp->t_column % 8) == 0); 467bc093719SEd Schouten return (0); 468bc093719SEd Schouten 469bc093719SEd Schouten case CNL: 470bc093719SEd Schouten /* Newline conversion. */ 471bc093719SEd Schouten if (CMP_FLAG(o, ONLCR)) { 472bc093719SEd Schouten /* Convert \n to \r\n. */ 473bc093719SEd Schouten error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); 474bc093719SEd Schouten } else { 475bc093719SEd Schouten error = PRINT_NORMAL(); 476bc093719SEd Schouten } 477bc093719SEd Schouten if (error) 478bc093719SEd Schouten return (-1); 479bc093719SEd Schouten 480bc093719SEd Schouten if (CMP_FLAG(o, ONLCR|ONLRET)) { 481bc093719SEd Schouten tp->t_column = tp->t_writepos = 0; 482bc093719SEd Schouten ttyinq_reprintpos_set(&tp->t_inq); 483bc093719SEd Schouten } 484bc093719SEd Schouten return (0); 485bc093719SEd Schouten 486bc093719SEd Schouten case CCR: 487bc093719SEd Schouten /* Carriage return to newline conversion. */ 488bc093719SEd Schouten if (CMP_FLAG(o, OCRNL)) 489bc093719SEd Schouten c = CNL; 490bc093719SEd Schouten /* Omit carriage returns on column 0. */ 491bc093719SEd Schouten if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) 492bc093719SEd Schouten return (0); 493bc093719SEd Schouten if (PRINT_NORMAL() != 0) 494bc093719SEd Schouten return (-1); 495bc093719SEd Schouten 496bc093719SEd Schouten tp->t_column = tp->t_writepos = 0; 497bc093719SEd Schouten ttyinq_reprintpos_set(&tp->t_inq); 498bc093719SEd Schouten return (0); 499bc093719SEd Schouten } 500bc093719SEd Schouten 501bc093719SEd Schouten /* 502bc093719SEd Schouten * Invisible control character. Print it, but don't 503bc093719SEd Schouten * increase the column count. 504bc093719SEd Schouten */ 505bc093719SEd Schouten return PRINT_NORMAL(); 506bc093719SEd Schouten #undef PRINT_NORMAL 507bc093719SEd Schouten } 508bc093719SEd Schouten 509bc093719SEd Schouten /* 510bc093719SEd Schouten * Just like the old TTY implementation, we need to copy data in chunks 511bc093719SEd Schouten * into a temporary buffer. One of the reasons why we need to do this, 512bc093719SEd Schouten * is because output processing (only TAB3 though) may allow the buffer 513bc093719SEd Schouten * to grow eight times. 514bc093719SEd Schouten */ 515bc093719SEd Schouten int 516bc093719SEd Schouten ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) 517bc093719SEd Schouten { 518a1215e37SEd Schouten char ob[TTY_STACKBUF]; 519bc093719SEd Schouten char *obstart; 520bc093719SEd Schouten int error = 0; 521bc093719SEd Schouten unsigned int oblen = 0; 522bc093719SEd Schouten 52323d53268SKyle Evans tty_assert_locked(tp); 524bc093719SEd Schouten 525bc093719SEd Schouten if (tp->t_flags & TF_ZOMBIE) 526bc093719SEd Schouten return (EIO); 527bc093719SEd Schouten 528bc093719SEd Schouten /* 529bc093719SEd Schouten * We don't need to check whether the process is the foreground 530bc093719SEd Schouten * process group or if we have a carrier. This is already done 531bc093719SEd Schouten * in ttydev_write(). 532bc093719SEd Schouten */ 533bc093719SEd Schouten 534bc093719SEd Schouten while (uio->uio_resid > 0) { 535bc093719SEd Schouten unsigned int nlen; 536bc093719SEd Schouten 537bc093719SEd Schouten MPASS(oblen == 0); 538bc093719SEd Schouten 539cbda6f66SWarner Losh if (CMP_FLAG(l, FLUSHO)) { 540cbda6f66SWarner Losh uio->uio_offset += uio->uio_resid; 541cbda6f66SWarner Losh uio->uio_resid = 0; 542cbda6f66SWarner Losh return (0); 543cbda6f66SWarner Losh } 544cbda6f66SWarner Losh 545bc093719SEd Schouten /* Step 1: read data. */ 546bc093719SEd Schouten obstart = ob; 547bc093719SEd Schouten nlen = MIN(uio->uio_resid, sizeof ob); 54887fe0fa8SEd Schouten tty_unlock(tp); 549bc093719SEd Schouten error = uiomove(ob, nlen, uio); 55087fe0fa8SEd Schouten tty_lock(tp); 551bc093719SEd Schouten if (error != 0) 552bc093719SEd Schouten break; 553bc093719SEd Schouten oblen = nlen; 554bc093719SEd Schouten 555bc093719SEd Schouten if (tty_gone(tp)) { 556bc093719SEd Schouten error = ENXIO; 557bc093719SEd Schouten break; 558bc093719SEd Schouten } 559bc093719SEd Schouten 560bc093719SEd Schouten MPASS(oblen > 0); 561bc093719SEd Schouten 562bc093719SEd Schouten /* Step 2: process data. */ 563bc093719SEd Schouten do { 564bc093719SEd Schouten unsigned int plen, wlen; 565bc093719SEd Schouten 566cbda6f66SWarner Losh if (CMP_FLAG(l, FLUSHO)) { 567cbda6f66SWarner Losh uio->uio_offset += uio->uio_resid; 568cbda6f66SWarner Losh uio->uio_resid = 0; 569cbda6f66SWarner Losh return (0); 570cbda6f66SWarner Losh } 571cbda6f66SWarner Losh 572bc093719SEd Schouten /* Search for special characters for post processing. */ 573bc093719SEd Schouten if (CMP_FLAG(o, OPOST)) { 574bc093719SEd Schouten plen = ttydisc_findchar(obstart, oblen); 575bc093719SEd Schouten } else { 576bc093719SEd Schouten plen = oblen; 577bc093719SEd Schouten } 578bc093719SEd Schouten 579bc093719SEd Schouten if (plen == 0) { 580bc093719SEd Schouten /* 581bc093719SEd Schouten * We're going to process a character 582bc093719SEd Schouten * that needs processing 583bc093719SEd Schouten */ 584bc093719SEd Schouten if (ttydisc_write_oproc(tp, *obstart) == 0) { 585bc093719SEd Schouten obstart++; 586bc093719SEd Schouten oblen--; 587bc093719SEd Schouten 588bc093719SEd Schouten tp->t_writepos = tp->t_column; 589bc093719SEd Schouten ttyinq_reprintpos_set(&tp->t_inq); 590bc093719SEd Schouten continue; 591bc093719SEd Schouten } 592bc093719SEd Schouten } else { 593bc093719SEd Schouten /* We're going to write regular data. */ 594bc093719SEd Schouten wlen = ttyoutq_write(&tp->t_outq, obstart, plen); 595bc093719SEd Schouten obstart += wlen; 596bc093719SEd Schouten oblen -= wlen; 597bc093719SEd Schouten tp->t_column += wlen; 598bc093719SEd Schouten 599bc093719SEd Schouten tp->t_writepos = tp->t_column; 600bc093719SEd Schouten ttyinq_reprintpos_set(&tp->t_inq); 601bc093719SEd Schouten 602bc093719SEd Schouten if (wlen == plen) 603bc093719SEd Schouten continue; 604bc093719SEd Schouten } 605bc093719SEd Schouten 606bc093719SEd Schouten /* Watermark reached. Try to sleep. */ 607bc093719SEd Schouten tp->t_flags |= TF_HIWAT_OUT; 608bc093719SEd Schouten 609bc093719SEd Schouten if (ioflag & IO_NDELAY) { 610bc093719SEd Schouten error = EWOULDBLOCK; 611bc093719SEd Schouten goto done; 612bc093719SEd Schouten } 613bc093719SEd Schouten 614bc093719SEd Schouten /* 615bc093719SEd Schouten * The driver may write back the data 616bc093719SEd Schouten * synchronously. Be sure to check the high 617bc093719SEd Schouten * water mark before going to sleep. 618bc093719SEd Schouten */ 619bc093719SEd Schouten ttydevsw_outwakeup(tp); 620bc093719SEd Schouten if ((tp->t_flags & TF_HIWAT_OUT) == 0) 621bc093719SEd Schouten continue; 622bc093719SEd Schouten 623bc093719SEd Schouten error = tty_wait(tp, &tp->t_outwait); 624bc093719SEd Schouten if (error) 625bc093719SEd Schouten goto done; 626bc093719SEd Schouten 627bc093719SEd Schouten if (tp->t_flags & TF_ZOMBIE) { 628bc093719SEd Schouten error = EIO; 629bc093719SEd Schouten goto done; 630bc093719SEd Schouten } 631bc093719SEd Schouten } while (oblen > 0); 632bc093719SEd Schouten } 633bc093719SEd Schouten 634bc093719SEd Schouten done: 635856ebf85SChristian S.J. Peron if (!tty_gone(tp)) 636bc093719SEd Schouten ttydevsw_outwakeup(tp); 637bc093719SEd Schouten 638bc093719SEd Schouten /* 639bc093719SEd Schouten * Add the amount of bytes that we didn't process back to the 640bc093719SEd Schouten * uio counters. We need to do this to make sure write() doesn't 641bc093719SEd Schouten * count the bytes we didn't store in the queue. 642bc093719SEd Schouten */ 643bc093719SEd Schouten uio->uio_resid += oblen; 644bc093719SEd Schouten return (error); 645bc093719SEd Schouten } 646bc093719SEd Schouten 647bc093719SEd Schouten void 648bc093719SEd Schouten ttydisc_optimize(struct tty *tp) 649bc093719SEd Schouten { 65023d53268SKyle Evans tty_assert_locked(tp); 651bc093719SEd Schouten 65214358b0fSEd Schouten if (ttyhook_hashook(tp, rint_bypass)) { 65314358b0fSEd Schouten tp->t_flags |= TF_BYPASS; 65414358b0fSEd Schouten } else if (ttyhook_hashook(tp, rint)) { 65514358b0fSEd Schouten tp->t_flags &= ~TF_BYPASS; 65614358b0fSEd Schouten } else if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && 657bc093719SEd Schouten (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && 658bc093719SEd Schouten (!CMP_FLAG(i, PARMRK) || 659bc093719SEd Schouten CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && 66014358b0fSEd Schouten !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { 661bc093719SEd Schouten tp->t_flags |= TF_BYPASS; 662bc093719SEd Schouten } else { 663bc093719SEd Schouten tp->t_flags &= ~TF_BYPASS; 664bc093719SEd Schouten } 665bc093719SEd Schouten } 666bc093719SEd Schouten 667bc093719SEd Schouten void 668bc093719SEd Schouten ttydisc_modem(struct tty *tp, int open) 669bc093719SEd Schouten { 670bc093719SEd Schouten 67123d53268SKyle Evans tty_assert_locked(tp); 672bc093719SEd Schouten 673bc093719SEd Schouten if (open) 674bc093719SEd Schouten cv_broadcast(&tp->t_dcdwait); 675bc093719SEd Schouten 676bc093719SEd Schouten /* 677bc093719SEd Schouten * Ignore modem status lines when CLOCAL is turned on, but don't 678bc093719SEd Schouten * enter the zombie state when the TTY isn't opened, because 679bc093719SEd Schouten * that would cause the TTY to be in zombie state after being 680bc093719SEd Schouten * opened. 681bc093719SEd Schouten */ 682bc093719SEd Schouten if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) 683bc093719SEd Schouten return; 684bc093719SEd Schouten 685bc093719SEd Schouten if (open == 0) { 686bc093719SEd Schouten /* 687bc093719SEd Schouten * Lost carrier. 688bc093719SEd Schouten */ 689bc093719SEd Schouten tp->t_flags |= TF_ZOMBIE; 690bc093719SEd Schouten 691bc093719SEd Schouten tty_signal_sessleader(tp, SIGHUP); 692bc093719SEd Schouten tty_flush(tp, FREAD|FWRITE); 693bc093719SEd Schouten } else { 694bc093719SEd Schouten /* 695bc093719SEd Schouten * Carrier is back again. 696bc093719SEd Schouten */ 697bc093719SEd Schouten 698bc093719SEd Schouten /* XXX: what should we do here? */ 699bc093719SEd Schouten } 700bc093719SEd Schouten } 701bc093719SEd Schouten 702bc093719SEd Schouten static int 703bc093719SEd Schouten ttydisc_echo_force(struct tty *tp, char c, int quote) 704bc093719SEd Schouten { 705bc093719SEd Schouten 706cbda6f66SWarner Losh if (CMP_FLAG(l, FLUSHO)) 707cbda6f66SWarner Losh return 0; 708cbda6f66SWarner Losh 709bc093719SEd Schouten if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { 710bc093719SEd Schouten /* 711bc093719SEd Schouten * Only perform postprocessing when OPOST is turned on 712bc093719SEd Schouten * and the character is an unquoted BS/TB/NL/CR. 713bc093719SEd Schouten */ 714bc093719SEd Schouten return ttydisc_write_oproc(tp, c); 715bc093719SEd Schouten } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { 716bc093719SEd Schouten /* 717bc093719SEd Schouten * Only use ^X notation when ECHOCTL is turned on and 718bc093719SEd Schouten * we've got an quoted control character. 71939410373SEd Schouten * 72039410373SEd Schouten * Print backspaces when echoing an end-of-file. 721bc093719SEd Schouten */ 72239410373SEd Schouten char ob[4] = "^?\b\b"; 723bc093719SEd Schouten 724bc093719SEd Schouten /* Print ^X notation. */ 725bc093719SEd Schouten if (c != 0x7f) 726bc093719SEd Schouten ob[1] = c + 'A' - 1; 727bc093719SEd Schouten 72839410373SEd Schouten if (!quote && CMP_CC(VEOF, c)) { 72939410373SEd Schouten return ttyoutq_write_nofrag(&tp->t_outq, ob, 4); 73039410373SEd Schouten } else { 731bc093719SEd Schouten tp->t_column += 2; 732bc093719SEd Schouten return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); 73339410373SEd Schouten } 734bc093719SEd Schouten } else { 735bc093719SEd Schouten /* Can just be printed. */ 736bc093719SEd Schouten tp->t_column++; 737bc093719SEd Schouten return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); 738bc093719SEd Schouten } 739bc093719SEd Schouten } 740bc093719SEd Schouten 741bc093719SEd Schouten static int 742bc093719SEd Schouten ttydisc_echo(struct tty *tp, char c, int quote) 743bc093719SEd Schouten { 744bc093719SEd Schouten 745bc093719SEd Schouten /* 746bc093719SEd Schouten * Only echo characters when ECHO is turned on, or ECHONL when 747bc093719SEd Schouten * the character is an unquoted newline. 748bc093719SEd Schouten */ 749bc093719SEd Schouten if (!CMP_FLAG(l, ECHO) && 750bc093719SEd Schouten (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) 751bc093719SEd Schouten return (0); 752bc093719SEd Schouten 753bc093719SEd Schouten return ttydisc_echo_force(tp, c, quote); 754bc093719SEd Schouten } 755bc093719SEd Schouten 756bc093719SEd Schouten static void 757bc093719SEd Schouten ttydisc_reprint_char(void *d, char c, int quote) 758bc093719SEd Schouten { 759bc093719SEd Schouten struct tty *tp = d; 760bc093719SEd Schouten 761bc093719SEd Schouten ttydisc_echo(tp, c, quote); 762bc093719SEd Schouten } 763bc093719SEd Schouten 764bc093719SEd Schouten static void 765bc093719SEd Schouten ttydisc_reprint(struct tty *tp) 766bc093719SEd Schouten { 767bc093719SEd Schouten cc_t c; 768bc093719SEd Schouten 769bc093719SEd Schouten /* Print ^R\n, followed by the line. */ 770bc093719SEd Schouten c = tp->t_termios.c_cc[VREPRINT]; 771bc093719SEd Schouten if (c != _POSIX_VDISABLE) 772bc093719SEd Schouten ttydisc_echo(tp, c, 0); 773bc093719SEd Schouten ttydisc_echo(tp, CNL, 0); 774bc093719SEd Schouten ttyinq_reprintpos_reset(&tp->t_inq); 775bc093719SEd Schouten 776bc093719SEd Schouten ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); 777bc093719SEd Schouten } 778bc093719SEd Schouten 779bc093719SEd Schouten struct ttydisc_recalc_length { 780bc093719SEd Schouten struct tty *tp; 781bc093719SEd Schouten unsigned int curlen; 782bc093719SEd Schouten }; 783bc093719SEd Schouten 784bc093719SEd Schouten static void 785bc093719SEd Schouten ttydisc_recalc_charlength(void *d, char c, int quote) 786bc093719SEd Schouten { 787bc093719SEd Schouten struct ttydisc_recalc_length *data = d; 788bc093719SEd Schouten struct tty *tp = data->tp; 789bc093719SEd Schouten 790bc093719SEd Schouten if (CTL_PRINT(c, quote)) { 791bc093719SEd Schouten if (CMP_FLAG(l, ECHOCTL)) 792bc093719SEd Schouten data->curlen += 2; 793bc093719SEd Schouten } else if (c == CTAB) { 794bc093719SEd Schouten data->curlen += 8 - (data->curlen & 7); 795bc093719SEd Schouten } else { 796bc093719SEd Schouten data->curlen++; 797bc093719SEd Schouten } 798bc093719SEd Schouten } 799bc093719SEd Schouten 800bc093719SEd Schouten static unsigned int 801bc093719SEd Schouten ttydisc_recalc_linelength(struct tty *tp) 802bc093719SEd Schouten { 803bc093719SEd Schouten struct ttydisc_recalc_length data = { tp, tp->t_writepos }; 804bc093719SEd Schouten 805bc093719SEd Schouten ttyinq_line_iterate_from_reprintpos(&tp->t_inq, 806bc093719SEd Schouten ttydisc_recalc_charlength, &data); 807bc093719SEd Schouten return (data.curlen); 808bc093719SEd Schouten } 809bc093719SEd Schouten 810bc093719SEd Schouten static int 811bc093719SEd Schouten ttydisc_rubchar(struct tty *tp) 812bc093719SEd Schouten { 813bc093719SEd Schouten char c; 814bc093719SEd Schouten int quote; 815bc093719SEd Schouten unsigned int prevpos, tablen; 816bc093719SEd Schouten 817bc093719SEd Schouten if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 818bc093719SEd Schouten return (-1); 819bc093719SEd Schouten ttyinq_unputchar(&tp->t_inq); 820bc093719SEd Schouten 821bc093719SEd Schouten if (CMP_FLAG(l, ECHO)) { 822bc093719SEd Schouten /* 823bc093719SEd Schouten * Remove the character from the screen. This is even 824bc093719SEd Schouten * safe for characters that span multiple characters 825bc093719SEd Schouten * (tabs, quoted, etc). 826bc093719SEd Schouten */ 827bc093719SEd Schouten if (tp->t_writepos >= tp->t_column) { 828bc093719SEd Schouten /* Retype the sentence. */ 829bc093719SEd Schouten ttydisc_reprint(tp); 830bc093719SEd Schouten } else if (CMP_FLAG(l, ECHOE)) { 831bc093719SEd Schouten if (CTL_PRINT(c, quote)) { 832bc093719SEd Schouten /* Remove ^X formatted chars. */ 833bc093719SEd Schouten if (CMP_FLAG(l, ECHOCTL)) { 834bc093719SEd Schouten tp->t_column -= 2; 835bc093719SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, 836bc093719SEd Schouten "\b\b \b\b", 6); 837bc093719SEd Schouten } 838bc093719SEd Schouten } else if (c == ' ') { 839bc093719SEd Schouten /* Space character needs no rubbing. */ 840bc093719SEd Schouten tp->t_column -= 1; 841bc093719SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); 842bc093719SEd Schouten } else if (c == CTAB) { 843bc093719SEd Schouten /* 844bc093719SEd Schouten * Making backspace work with tabs is 845bc093719SEd Schouten * quite hard. Recalculate the length of 846bc093719SEd Schouten * this character and remove it. 847bc093719SEd Schouten * 848bc093719SEd Schouten * Because terminal settings could be 849bc093719SEd Schouten * changed while the line is being 850bc093719SEd Schouten * inserted, the calculations don't have 851bc093719SEd Schouten * to be correct. Make sure we keep the 852bc093719SEd Schouten * tab length within proper bounds. 853bc093719SEd Schouten */ 854bc093719SEd Schouten prevpos = ttydisc_recalc_linelength(tp); 855bc093719SEd Schouten if (prevpos >= tp->t_column) 856bc093719SEd Schouten tablen = 1; 857bc093719SEd Schouten else 858bc093719SEd Schouten tablen = tp->t_column - prevpos; 859bc093719SEd Schouten if (tablen > 8) 860bc093719SEd Schouten tablen = 8; 861bc093719SEd Schouten 862bc093719SEd Schouten tp->t_column = prevpos; 863bc093719SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, 864bc093719SEd Schouten "\b\b\b\b\b\b\b\b", tablen); 865bc093719SEd Schouten return (0); 8669e589b09SBojan Novković } else if ((tp->t_termios.c_iflag & IUTF8) != 0 && 8679e589b09SBojan Novković CTL_UTF8(c)) { 8689e589b09SBojan Novković uint8_t bytes[UTF8_STACKBUF] = { 0 }; 8699e589b09SBojan Novković int curidx = UTF8_STACKBUF - 1, cwidth = 1, 8709e589b09SBojan Novković nb = 0; 8719e589b09SBojan Novković teken_char_t codepoint; 8729e589b09SBojan Novković 8739e589b09SBojan Novković /* Save current byte. */ 8749e589b09SBojan Novković bytes[curidx] = c; 8759e589b09SBojan Novković curidx--; 8769e589b09SBojan Novković nb++; 8779e589b09SBojan Novković /* Loop back through inq until we hit the 8789e589b09SBojan Novković * leading byte. */ 8799e589b09SBojan Novković while (CTL_UTF8_CONT(c) && nb < UTF8_STACKBUF) { 880c6d7be21SBojan Novković /* 881c6d7be21SBojan Novković * Check if we've reached the beginning 882c6d7be21SBojan Novković * of the line. 883c6d7be21SBojan Novković */ 884c6d7be21SBojan Novković if (ttyinq_peekchar(&tp->t_inq, &c, 885c6d7be21SBojan Novković "e) != 0) 886c6d7be21SBojan Novković break; 8879e589b09SBojan Novković ttyinq_unputchar(&tp->t_inq); 8889e589b09SBojan Novković bytes[curidx] = c; 8899e589b09SBojan Novković curidx--; 8909e589b09SBojan Novković nb++; 8919e589b09SBojan Novković } 8929e589b09SBojan Novković /* 8939e589b09SBojan Novković * Shift array so that the leading 8949e589b09SBojan Novković * byte ends up at idx 0. 8959e589b09SBojan Novković */ 8969e589b09SBojan Novković if (nb < UTF8_STACKBUF) 8979e589b09SBojan Novković memmove(&bytes[0], &bytes[curidx + 1], 8989e589b09SBojan Novković nb * sizeof(uint8_t)); 8999e589b09SBojan Novković /* Check for malformed UTF8 characters. */ 9009e589b09SBojan Novković if (nb == UTF8_STACKBUF && 9019e589b09SBojan Novković CTL_UTF8_CONT(bytes[0])) { 9029e589b09SBojan Novković /* 9039e589b09SBojan Novković * Place all bytes back into the inq and 9049e589b09SBojan Novković * delete the last byte only. 9059e589b09SBojan Novković */ 9069e589b09SBojan Novković ttyinq_write(&tp->t_inq, bytes, 9079e589b09SBojan Novković UTF8_STACKBUF, 0); 9082fed1c57SBojan Novković ttyinq_unputchar(&tp->t_inq); 9099e589b09SBojan Novković } else { 9109e589b09SBojan Novković /* Find codepoint and width. */ 9119e589b09SBojan Novković codepoint = 9129e589b09SBojan Novković teken_utf8_bytes_to_codepoint(bytes, 9139e589b09SBojan Novković nb); 9142fed1c57SBojan Novković if (codepoint == 9152fed1c57SBojan Novković TEKEN_UTF8_INVALID_CODEPOINT || 9162fed1c57SBojan Novković (cwidth = teken_wcwidth( 9172fed1c57SBojan Novković codepoint)) == -1) { 9189e589b09SBojan Novković /* 9199e589b09SBojan Novković * Place all bytes back into the 9209e589b09SBojan Novković * inq and fall back to 9219e589b09SBojan Novković * default behaviour. 9229e589b09SBojan Novković */ 9232fed1c57SBojan Novković cwidth = 1; 9249e589b09SBojan Novković ttyinq_write(&tp->t_inq, bytes, 9259e589b09SBojan Novković nb, 0); 9262fed1c57SBojan Novković ttyinq_unputchar(&tp->t_inq); 9279e589b09SBojan Novković } 9289e589b09SBojan Novković } 9299e589b09SBojan Novković tp->t_column -= cwidth; 9309e589b09SBojan Novković /* 9319e589b09SBojan Novković * Delete character by punching 9329e589b09SBojan Novković * 'cwidth' spaces over it. 9339e589b09SBojan Novković */ 9349e589b09SBojan Novković if (cwidth == 1) 9359e589b09SBojan Novković ttyoutq_write_nofrag(&tp->t_outq, 9369e589b09SBojan Novković "\b \b", 3); 9379e589b09SBojan Novković else if (cwidth == 2) 9389e589b09SBojan Novković ttyoutq_write_nofrag(&tp->t_outq, 9399e589b09SBojan Novković "\b\b \b\b", 6); 940bc093719SEd Schouten } else { 941bc093719SEd Schouten /* 942bc093719SEd Schouten * Remove a regular character by 943bc093719SEd Schouten * punching a space over it. 944bc093719SEd Schouten */ 945bc093719SEd Schouten tp->t_column -= 1; 946bc093719SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); 947bc093719SEd Schouten } 948bc093719SEd Schouten } else { 949bc093719SEd Schouten /* Don't print spaces. */ 950bc093719SEd Schouten ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); 951bc093719SEd Schouten } 952bc093719SEd Schouten } 953bc093719SEd Schouten 954bc093719SEd Schouten return (0); 955bc093719SEd Schouten } 956bc093719SEd Schouten 957bc093719SEd Schouten static void 958bc093719SEd Schouten ttydisc_rubword(struct tty *tp) 959bc093719SEd Schouten { 960bc093719SEd Schouten char c; 961bc093719SEd Schouten int quote, alnum; 962bc093719SEd Schouten 963bc093719SEd Schouten /* Strip whitespace first. */ 964bc093719SEd Schouten for (;;) { 965bc093719SEd Schouten if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 966bc093719SEd Schouten return; 967bc093719SEd Schouten if (!CTL_WHITE(c)) 968bc093719SEd Schouten break; 969bc093719SEd Schouten ttydisc_rubchar(tp); 970bc093719SEd Schouten } 971bc093719SEd Schouten 972bc093719SEd Schouten /* 973bc093719SEd Schouten * Record whether the last character from the previous iteration 974bc093719SEd Schouten * was alphanumeric or not. We need this to implement ALTWERASE. 975bc093719SEd Schouten */ 976bc093719SEd Schouten alnum = CTL_ALNUM(c); 977bc093719SEd Schouten for (;;) { 978bc093719SEd Schouten ttydisc_rubchar(tp); 979bc093719SEd Schouten 980bc093719SEd Schouten if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 981bc093719SEd Schouten return; 982bc093719SEd Schouten if (CTL_WHITE(c)) 983bc093719SEd Schouten return; 984bc093719SEd Schouten if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) 985bc093719SEd Schouten return; 986bc093719SEd Schouten } 987bc093719SEd Schouten } 988bc093719SEd Schouten 989bc093719SEd Schouten int 990bc093719SEd Schouten ttydisc_rint(struct tty *tp, char c, int flags) 991bc093719SEd Schouten { 992bc093719SEd Schouten int signal, quote = 0; 993bc093719SEd Schouten char ob[3] = { 0xff, 0x00 }; 994bc093719SEd Schouten size_t ol; 995bc093719SEd Schouten 99623d53268SKyle Evans tty_assert_locked(tp); 997bc093719SEd Schouten 998bc093719SEd Schouten atomic_add_long(&tty_nin, 1); 999bc093719SEd Schouten 1000a1215e37SEd Schouten if (ttyhook_hashook(tp, rint)) 1001a1215e37SEd Schouten return ttyhook_rint(tp, c, flags); 1002a1215e37SEd Schouten 1003bc093719SEd Schouten if (tp->t_flags & TF_BYPASS) 1004bc093719SEd Schouten goto processed; 1005bc093719SEd Schouten 1006bc093719SEd Schouten if (flags) { 1007bc093719SEd Schouten if (flags & TRE_BREAK) { 1008bc093719SEd Schouten if (CMP_FLAG(i, IGNBRK)) { 1009bc093719SEd Schouten /* Ignore break characters. */ 1010bc093719SEd Schouten return (0); 1011bc093719SEd Schouten } else if (CMP_FLAG(i, BRKINT)) { 1012bc093719SEd Schouten /* Generate SIGINT on break. */ 1013bc093719SEd Schouten tty_flush(tp, FREAD|FWRITE); 1014bc093719SEd Schouten tty_signal_pgrp(tp, SIGINT); 1015bc093719SEd Schouten return (0); 1016bc093719SEd Schouten } else { 1017bc093719SEd Schouten /* Just print it. */ 1018bc093719SEd Schouten goto parmrk; 1019bc093719SEd Schouten } 1020bc093719SEd Schouten } else if (flags & TRE_FRAMING || 1021bc093719SEd Schouten (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { 1022bc093719SEd Schouten if (CMP_FLAG(i, IGNPAR)) { 1023bc093719SEd Schouten /* Ignore bad characters. */ 1024bc093719SEd Schouten return (0); 1025bc093719SEd Schouten } else { 1026bc093719SEd Schouten /* Just print it. */ 1027bc093719SEd Schouten goto parmrk; 1028bc093719SEd Schouten } 1029bc093719SEd Schouten } 1030bc093719SEd Schouten } 1031bc093719SEd Schouten 1032bc093719SEd Schouten /* Allow any character to perform a wakeup. */ 1033cbda6f66SWarner Losh if (CMP_FLAG(i, IXANY)) { 1034bc093719SEd Schouten tp->t_flags &= ~TF_STOPPED; 1035cbda6f66SWarner Losh tp->t_termios.c_lflag &= ~FLUSHO; 1036cbda6f66SWarner Losh } 1037bc093719SEd Schouten 1038bc093719SEd Schouten /* Remove the top bit. */ 1039bc093719SEd Schouten if (CMP_FLAG(i, ISTRIP)) 1040bc093719SEd Schouten c &= ~0x80; 1041bc093719SEd Schouten 1042bc093719SEd Schouten /* Skip input processing when we want to print it literally. */ 1043bc093719SEd Schouten if (tp->t_flags & TF_LITERAL) { 1044bc093719SEd Schouten tp->t_flags &= ~TF_LITERAL; 1045bc093719SEd Schouten quote = 1; 1046bc093719SEd Schouten goto processed; 1047bc093719SEd Schouten } 1048bc093719SEd Schouten 1049bc093719SEd Schouten /* Special control characters that are implementation dependent. */ 1050bc093719SEd Schouten if (CMP_FLAG(l, IEXTEN)) { 1051bc093719SEd Schouten /* Accept the next character as literal. */ 1052bc093719SEd Schouten if (CMP_CC(VLNEXT, c)) { 1053bc093719SEd Schouten if (CMP_FLAG(l, ECHO)) { 1054bc093719SEd Schouten if (CMP_FLAG(l, ECHOE)) 1055bc093719SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); 1056bc093719SEd Schouten else 1057bc093719SEd Schouten ttydisc_echo(tp, c, 0); 1058bc093719SEd Schouten } 1059bc093719SEd Schouten tp->t_flags |= TF_LITERAL; 1060bc093719SEd Schouten return (0); 1061bc093719SEd Schouten } 1062cbda6f66SWarner Losh /* Discard processing */ 1063cbda6f66SWarner Losh if (CMP_CC(VDISCARD, c)) { 1064cbda6f66SWarner Losh if (CMP_FLAG(l, FLUSHO)) { 1065cbda6f66SWarner Losh tp->t_termios.c_lflag &= ~FLUSHO; 1066cbda6f66SWarner Losh } else { 1067cbda6f66SWarner Losh tty_flush(tp, FWRITE); 1068cbda6f66SWarner Losh ttydisc_echo(tp, c, 0); 1069cbda6f66SWarner Losh if (tp->t_inq.ti_end > 0) 1070cbda6f66SWarner Losh ttydisc_reprint(tp); 1071cbda6f66SWarner Losh tp->t_termios.c_lflag |= FLUSHO; 1072cbda6f66SWarner Losh } 1073cbda6f66SWarner Losh } 1074bc093719SEd Schouten } 1075bc093719SEd Schouten 1076bc093719SEd Schouten /* 1077bc093719SEd Schouten * Handle signal processing. 1078bc093719SEd Schouten */ 1079bc093719SEd Schouten if (CMP_FLAG(l, ISIG)) { 1080bc093719SEd Schouten if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { 1081bc093719SEd Schouten if (CMP_CC(VSTATUS, c)) { 1082bc093719SEd Schouten tty_signal_pgrp(tp, SIGINFO); 1083bc093719SEd Schouten return (0); 1084bc093719SEd Schouten } 1085bc093719SEd Schouten } 1086bc093719SEd Schouten 1087bc093719SEd Schouten /* 1088bc093719SEd Schouten * When compared to the old implementation, this 1089bc093719SEd Schouten * implementation also flushes the output queue. POSIX 1090bc093719SEd Schouten * is really brief about this, but does makes us assume 1091bc093719SEd Schouten * we have to do so. 1092bc093719SEd Schouten */ 1093bc093719SEd Schouten signal = 0; 1094bc093719SEd Schouten if (CMP_CC(VINTR, c)) { 1095bc093719SEd Schouten signal = SIGINT; 1096bc093719SEd Schouten } else if (CMP_CC(VQUIT, c)) { 1097bc093719SEd Schouten signal = SIGQUIT; 1098bc093719SEd Schouten } else if (CMP_CC(VSUSP, c)) { 1099bc093719SEd Schouten signal = SIGTSTP; 1100bc093719SEd Schouten } 1101bc093719SEd Schouten 1102bc093719SEd Schouten if (signal != 0) { 1103bc093719SEd Schouten /* 1104bc093719SEd Schouten * Echo the character before signalling the 1105bc093719SEd Schouten * processes. 1106bc093719SEd Schouten */ 1107bc093719SEd Schouten if (!CMP_FLAG(l, NOFLSH)) 1108bc093719SEd Schouten tty_flush(tp, FREAD|FWRITE); 1109bc093719SEd Schouten ttydisc_echo(tp, c, 0); 1110bc093719SEd Schouten tty_signal_pgrp(tp, signal); 1111bc093719SEd Schouten return (0); 1112bc093719SEd Schouten } 1113bc093719SEd Schouten } 1114bc093719SEd Schouten 1115bc093719SEd Schouten /* 1116bc093719SEd Schouten * Handle start/stop characters. 1117bc093719SEd Schouten */ 1118bc093719SEd Schouten if (CMP_FLAG(i, IXON)) { 1119bc093719SEd Schouten if (CMP_CC(VSTOP, c)) { 1120bc093719SEd Schouten /* Stop it if we aren't stopped yet. */ 1121bc093719SEd Schouten if ((tp->t_flags & TF_STOPPED) == 0) { 1122bc093719SEd Schouten tp->t_flags |= TF_STOPPED; 1123bc093719SEd Schouten return (0); 1124bc093719SEd Schouten } 1125bc093719SEd Schouten /* 1126bc093719SEd Schouten * Fallthrough: 1127bc093719SEd Schouten * When VSTART == VSTOP, we should make this key 1128bc093719SEd Schouten * toggle it. 1129bc093719SEd Schouten */ 1130bc093719SEd Schouten if (!CMP_CC(VSTART, c)) 1131bc093719SEd Schouten return (0); 1132bc093719SEd Schouten } 1133bc093719SEd Schouten if (CMP_CC(VSTART, c)) { 1134bc093719SEd Schouten tp->t_flags &= ~TF_STOPPED; 1135bc093719SEd Schouten return (0); 1136bc093719SEd Schouten } 1137bc093719SEd Schouten } 1138bc093719SEd Schouten 1139bc093719SEd Schouten /* Conversion of CR and NL. */ 1140bc093719SEd Schouten switch (c) { 1141bc093719SEd Schouten case CCR: 1142bc093719SEd Schouten if (CMP_FLAG(i, IGNCR)) 1143bc093719SEd Schouten return (0); 1144bc093719SEd Schouten if (CMP_FLAG(i, ICRNL)) 1145bc093719SEd Schouten c = CNL; 1146bc093719SEd Schouten break; 1147bc093719SEd Schouten case CNL: 1148bc093719SEd Schouten if (CMP_FLAG(i, INLCR)) 1149bc093719SEd Schouten c = CCR; 1150bc093719SEd Schouten break; 1151bc093719SEd Schouten } 1152bc093719SEd Schouten 1153bc093719SEd Schouten /* Canonical line editing. */ 1154bc093719SEd Schouten if (CMP_FLAG(l, ICANON)) { 1155bc093719SEd Schouten if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { 1156bc093719SEd Schouten ttydisc_rubchar(tp); 1157bc093719SEd Schouten return (0); 1158bc093719SEd Schouten } else if (CMP_CC(VKILL, c)) { 1159bc093719SEd Schouten while (ttydisc_rubchar(tp) == 0); 1160bc093719SEd Schouten return (0); 1161bc093719SEd Schouten } else if (CMP_FLAG(l, IEXTEN)) { 1162bc093719SEd Schouten if (CMP_CC(VWERASE, c)) { 1163bc093719SEd Schouten ttydisc_rubword(tp); 1164bc093719SEd Schouten return (0); 1165bc093719SEd Schouten } else if (CMP_CC(VREPRINT, c)) { 1166bc093719SEd Schouten ttydisc_reprint(tp); 1167bc093719SEd Schouten return (0); 1168bc093719SEd Schouten } 1169bc093719SEd Schouten } 1170bc093719SEd Schouten } 1171bc093719SEd Schouten 1172bc093719SEd Schouten processed: 1173bc093719SEd Schouten if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { 1174bc093719SEd Schouten /* Print 0xff 0xff. */ 1175bc093719SEd Schouten ob[1] = 0xff; 1176bc093719SEd Schouten ol = 2; 1177bc093719SEd Schouten quote = 1; 1178bc093719SEd Schouten } else { 1179bc093719SEd Schouten ob[0] = c; 1180bc093719SEd Schouten ol = 1; 1181bc093719SEd Schouten } 1182bc093719SEd Schouten 1183bc093719SEd Schouten goto print; 1184bc093719SEd Schouten 1185bc093719SEd Schouten parmrk: 1186bc093719SEd Schouten if (CMP_FLAG(i, PARMRK)) { 1187bc093719SEd Schouten /* Prepend 0xff 0x00 0x.. */ 1188bc093719SEd Schouten ob[2] = c; 1189bc093719SEd Schouten ol = 3; 1190bc093719SEd Schouten quote = 1; 1191bc093719SEd Schouten } else { 1192bc093719SEd Schouten ob[0] = c; 1193bc093719SEd Schouten ol = 1; 1194bc093719SEd Schouten } 1195bc093719SEd Schouten 1196bc093719SEd Schouten print: 1197bc093719SEd Schouten /* See if we can store this on the input queue. */ 1198bc093719SEd Schouten if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { 1199a15ec0a5SEd Schouten if (CMP_FLAG(i, IMAXBEL)) 1200a15ec0a5SEd Schouten ttyoutq_write_nofrag(&tp->t_outq, "\a", 1); 1201a15ec0a5SEd Schouten 1202a15ec0a5SEd Schouten /* 1203a15ec0a5SEd Schouten * Prevent a deadlock here. It may be possible that a 1204a15ec0a5SEd Schouten * user has entered so much data, there is no data 1205a15ec0a5SEd Schouten * available to read(), but the buffers are full anyway. 1206a15ec0a5SEd Schouten * 1207a15ec0a5SEd Schouten * Only enter the high watermark if the device driver 1208a15ec0a5SEd Schouten * can actually transmit something. 1209a15ec0a5SEd Schouten */ 1210a15ec0a5SEd Schouten if (ttyinq_bytescanonicalized(&tp->t_inq) == 0) 1211a15ec0a5SEd Schouten return (0); 1212a15ec0a5SEd Schouten 1213bc093719SEd Schouten tty_hiwat_in_block(tp); 1214bc093719SEd Schouten return (-1); 1215bc093719SEd Schouten } 1216bc093719SEd Schouten 1217bc093719SEd Schouten /* 1218bc093719SEd Schouten * In raw mode, we canonicalize after receiving a single 1219bc093719SEd Schouten * character. Otherwise, we canonicalize when we receive a 1220bc093719SEd Schouten * newline, VEOL or VEOF, but only when it isn't quoted. 1221bc093719SEd Schouten */ 1222bc093719SEd Schouten if (!CMP_FLAG(l, ICANON) || 1223bc093719SEd Schouten (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { 1224bc093719SEd Schouten ttyinq_canonicalize(&tp->t_inq); 1225bc093719SEd Schouten } 1226bc093719SEd Schouten 1227bc093719SEd Schouten ttydisc_echo(tp, c, quote); 1228bc093719SEd Schouten 1229bc093719SEd Schouten return (0); 1230bc093719SEd Schouten } 1231bc093719SEd Schouten 1232bc093719SEd Schouten size_t 12335c67885aSEd Schouten ttydisc_rint_simple(struct tty *tp, const void *buf, size_t len) 12345c67885aSEd Schouten { 12355c67885aSEd Schouten const char *cbuf; 12365c67885aSEd Schouten 12375c67885aSEd Schouten if (ttydisc_can_bypass(tp)) 12385c67885aSEd Schouten return (ttydisc_rint_bypass(tp, buf, len)); 12395c67885aSEd Schouten 12405c67885aSEd Schouten for (cbuf = buf; len-- > 0; cbuf++) { 12415c67885aSEd Schouten if (ttydisc_rint(tp, *cbuf, 0) != 0) 12425c67885aSEd Schouten break; 12435c67885aSEd Schouten } 12445c67885aSEd Schouten 12455c67885aSEd Schouten return (cbuf - (const char *)buf); 12465c67885aSEd Schouten } 12475c67885aSEd Schouten 12485c67885aSEd Schouten size_t 1249d344ffe5SEd Schouten ttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len) 1250bc093719SEd Schouten { 1251bc093719SEd Schouten size_t ret; 1252bc093719SEd Schouten 125323d53268SKyle Evans tty_assert_locked(tp); 1254bc093719SEd Schouten 1255bc093719SEd Schouten MPASS(tp->t_flags & TF_BYPASS); 1256bc093719SEd Schouten 1257bc093719SEd Schouten atomic_add_long(&tty_nin, len); 1258bc093719SEd Schouten 1259a1215e37SEd Schouten if (ttyhook_hashook(tp, rint_bypass)) 1260a1215e37SEd Schouten return ttyhook_rint_bypass(tp, buf, len); 1261a1215e37SEd Schouten 1262bc093719SEd Schouten ret = ttyinq_write(&tp->t_inq, buf, len, 0); 1263bc093719SEd Schouten ttyinq_canonicalize(&tp->t_inq); 1264d40b91cbSEd Schouten if (ret < len) 1265d40b91cbSEd Schouten tty_hiwat_in_block(tp); 1266bc093719SEd Schouten 1267bc093719SEd Schouten return (ret); 1268bc093719SEd Schouten } 1269bc093719SEd Schouten 1270bc093719SEd Schouten void 1271bc093719SEd Schouten ttydisc_rint_done(struct tty *tp) 1272bc093719SEd Schouten { 1273bc093719SEd Schouten 127423d53268SKyle Evans tty_assert_locked(tp); 1275bc093719SEd Schouten 1276a1215e37SEd Schouten if (ttyhook_hashook(tp, rint_done)) 1277a1215e37SEd Schouten ttyhook_rint_done(tp); 1278a1215e37SEd Schouten 1279bc093719SEd Schouten /* Wake up readers. */ 1280bc093719SEd Schouten tty_wakeup(tp, FREAD); 1281bc093719SEd Schouten /* Wake up driver for echo. */ 1282bc093719SEd Schouten ttydevsw_outwakeup(tp); 1283bc093719SEd Schouten } 1284bc093719SEd Schouten 1285a1215e37SEd Schouten size_t 1286a1215e37SEd Schouten ttydisc_rint_poll(struct tty *tp) 1287a1215e37SEd Schouten { 1288a1215e37SEd Schouten size_t l; 1289a1215e37SEd Schouten 129023d53268SKyle Evans tty_assert_locked(tp); 1291a1215e37SEd Schouten 1292a1215e37SEd Schouten if (ttyhook_hashook(tp, rint_poll)) 1293a1215e37SEd Schouten return ttyhook_rint_poll(tp); 1294a1215e37SEd Schouten 1295a1215e37SEd Schouten /* 1296a1215e37SEd Schouten * XXX: Still allow character input when there's no space in the 1297a1215e37SEd Schouten * buffers, but we haven't entered the high watermark. This is 1298a1215e37SEd Schouten * to allow backspace characters to be inserted when in 1299a1215e37SEd Schouten * canonical mode. 1300a1215e37SEd Schouten */ 1301a1215e37SEd Schouten l = ttyinq_bytesleft(&tp->t_inq); 1302a1215e37SEd Schouten if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) 1303a1215e37SEd Schouten return (1); 1304a1215e37SEd Schouten 1305a1215e37SEd Schouten return (l); 1306a1215e37SEd Schouten } 1307a1215e37SEd Schouten 1308bc093719SEd Schouten static void 1309bc093719SEd Schouten ttydisc_wakeup_watermark(struct tty *tp) 1310bc093719SEd Schouten { 1311bc093719SEd Schouten size_t c; 1312bc093719SEd Schouten 1313bc093719SEd Schouten c = ttyoutq_bytesleft(&tp->t_outq); 1314bc093719SEd Schouten if (tp->t_flags & TF_HIWAT_OUT) { 1315bc093719SEd Schouten /* Only allow us to run when we're below the watermark. */ 1316bc093719SEd Schouten if (c < tp->t_outlow) 1317bc093719SEd Schouten return; 1318bc093719SEd Schouten 1319bc093719SEd Schouten /* Reset the watermark. */ 1320bc093719SEd Schouten tp->t_flags &= ~TF_HIWAT_OUT; 1321bc093719SEd Schouten } else { 1322bc093719SEd Schouten /* Only run when we have data at all. */ 1323bc093719SEd Schouten if (c == 0) 1324bc093719SEd Schouten return; 1325bc093719SEd Schouten } 1326bc093719SEd Schouten tty_wakeup(tp, FWRITE); 1327bc093719SEd Schouten } 1328bc093719SEd Schouten 1329bc093719SEd Schouten size_t 1330bc093719SEd Schouten ttydisc_getc(struct tty *tp, void *buf, size_t len) 1331bc093719SEd Schouten { 1332bc093719SEd Schouten 133323d53268SKyle Evans tty_assert_locked(tp); 1334bc093719SEd Schouten 1335bc093719SEd Schouten if (tp->t_flags & TF_STOPPED) 1336bc093719SEd Schouten return (0); 1337bc093719SEd Schouten 1338a1215e37SEd Schouten if (ttyhook_hashook(tp, getc_inject)) 1339a1215e37SEd Schouten return ttyhook_getc_inject(tp, buf, len); 1340bc093719SEd Schouten 1341a1215e37SEd Schouten len = ttyoutq_read(&tp->t_outq, buf, len); 1342a1215e37SEd Schouten 1343a1215e37SEd Schouten if (ttyhook_hashook(tp, getc_capture)) 1344a1215e37SEd Schouten ttyhook_getc_capture(tp, buf, len); 1345a1215e37SEd Schouten 1346a1215e37SEd Schouten ttydisc_wakeup_watermark(tp); 1347ffffa83bSEd Schouten atomic_add_long(&tty_nout, len); 1348bc093719SEd Schouten 1349ffffa83bSEd Schouten return (len); 1350bc093719SEd Schouten } 1351bc093719SEd Schouten 1352bc093719SEd Schouten int 1353bc093719SEd Schouten ttydisc_getc_uio(struct tty *tp, struct uio *uio) 1354bc093719SEd Schouten { 1355a1215e37SEd Schouten int error = 0; 1356526d0bd5SKonstantin Belousov ssize_t obytes = uio->uio_resid; 1357a1215e37SEd Schouten size_t len; 1358a1215e37SEd Schouten char buf[TTY_STACKBUF]; 1359bc093719SEd Schouten 136023d53268SKyle Evans tty_assert_locked(tp); 1361bc093719SEd Schouten 1362bc093719SEd Schouten if (tp->t_flags & TF_STOPPED) 1363bc093719SEd Schouten return (0); 1364bc093719SEd Schouten 1365a1215e37SEd Schouten /* 1366a1215e37SEd Schouten * When a TTY hook is attached, we cannot perform unbuffered 1367a1215e37SEd Schouten * copying to userspace. Just call ttydisc_getc() and 1368a1215e37SEd Schouten * temporarily store data in a shadow buffer. 1369a1215e37SEd Schouten */ 1370a1215e37SEd Schouten if (ttyhook_hashook(tp, getc_capture) || 1371a1215e37SEd Schouten ttyhook_hashook(tp, getc_inject)) { 1372a1215e37SEd Schouten while (uio->uio_resid > 0) { 1373a1215e37SEd Schouten /* Read to shadow buffer. */ 1374a1215e37SEd Schouten len = ttydisc_getc(tp, buf, 1375a1215e37SEd Schouten MIN(uio->uio_resid, sizeof buf)); 1376a1215e37SEd Schouten if (len == 0) 1377a1215e37SEd Schouten break; 1378bc093719SEd Schouten 1379a1215e37SEd Schouten /* Copy to userspace. */ 1380a1215e37SEd Schouten tty_unlock(tp); 1381a1215e37SEd Schouten error = uiomove(buf, len, uio); 1382a1215e37SEd Schouten tty_lock(tp); 1383a1215e37SEd Schouten 1384a1215e37SEd Schouten if (error != 0) 1385a1215e37SEd Schouten break; 1386a1215e37SEd Schouten } 1387a1215e37SEd Schouten } else { 1388a1215e37SEd Schouten error = ttyoutq_read_uio(&tp->t_outq, tp, uio); 1389a1215e37SEd Schouten 1390a1215e37SEd Schouten ttydisc_wakeup_watermark(tp); 1391bc093719SEd Schouten atomic_add_long(&tty_nout, obytes - uio->uio_resid); 1392a1215e37SEd Schouten } 1393bc093719SEd Schouten 1394bc093719SEd Schouten return (error); 1395bc093719SEd Schouten } 1396bc093719SEd Schouten 1397a1215e37SEd Schouten size_t 1398a1215e37SEd Schouten ttydisc_getc_poll(struct tty *tp) 1399a1215e37SEd Schouten { 1400a1215e37SEd Schouten 140123d53268SKyle Evans tty_assert_locked(tp); 1402a1215e37SEd Schouten 1403a1215e37SEd Schouten if (tp->t_flags & TF_STOPPED) 1404a1215e37SEd Schouten return (0); 1405a1215e37SEd Schouten 1406a1215e37SEd Schouten if (ttyhook_hashook(tp, getc_poll)) 1407a1215e37SEd Schouten return ttyhook_getc_poll(tp); 1408a1215e37SEd Schouten 1409a1215e37SEd Schouten return ttyoutq_bytesused(&tp->t_outq); 1410a1215e37SEd Schouten } 1411a1215e37SEd Schouten 1412bc093719SEd Schouten /* 1413bc093719SEd Schouten * XXX: not really related to the TTYDISC, but we'd better put 1414bc093719SEd Schouten * tty_putchar() here, because we need to perform proper output 1415bc093719SEd Schouten * processing. 1416bc093719SEd Schouten */ 1417bc093719SEd Schouten 1418bc093719SEd Schouten int 14196858c2ccSConrad Meyer tty_putstrn(struct tty *tp, const char *p, size_t n) 1420bc093719SEd Schouten { 14216858c2ccSConrad Meyer size_t i; 14226858c2ccSConrad Meyer 142323d53268SKyle Evans tty_assert_locked(tp); 1424bc093719SEd Schouten 1425bc093719SEd Schouten if (tty_gone(tp)) 1426bc093719SEd Schouten return (-1); 1427bc093719SEd Schouten 14286858c2ccSConrad Meyer for (i = 0; i < n; i++) 14296858c2ccSConrad Meyer ttydisc_echo_force(tp, p[i], 0); 14306858c2ccSConrad Meyer 1431bc093719SEd Schouten tp->t_writepos = tp->t_column; 1432bc093719SEd Schouten ttyinq_reprintpos_set(&tp->t_inq); 1433bc093719SEd Schouten 1434bc093719SEd Schouten ttydevsw_outwakeup(tp); 1435bc093719SEd Schouten return (0); 1436bc093719SEd Schouten } 14376858c2ccSConrad Meyer 14386858c2ccSConrad Meyer int 14396858c2ccSConrad Meyer tty_putchar(struct tty *tp, char c) 14406858c2ccSConrad Meyer { 14416858c2ccSConrad Meyer return (tty_putstrn(tp, &c, 1)); 14426858c2ccSConrad Meyer } 1443