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