3134 lines
79 KiB
C
3134 lines
79 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)tty_ldterm.c 1.1 94/10/31 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Standard tty streams module.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/termio.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/user.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/dk.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/debug.h>
|
|
|
|
#define IBSIZE 16 /* "standard" input data block size */
|
|
#define OBSIZE 64 /* "standard" output data block size */
|
|
#define EBSIZE 16 /* "standard" echo data block size */
|
|
#define TTYHOG CANBSIZ /* from param.h; in case anyone uses it */
|
|
#define TTXOHI 80
|
|
#define TTXOLO 32
|
|
|
|
/*
|
|
* Should be "void", not "int" - they return no values!
|
|
*/
|
|
static int ldtermopen(/*queue_t *q, int dev, int oflag, int sflag*/);
|
|
static int ldtermclose(/*queue_t *q, int flag*/);
|
|
static int ldtermrput(/*queue_t *q, mblk_t *mp*/);
|
|
static int ldtermrsrv(/*queue_t *q*/);
|
|
static int ldtermwput(/*queue_t *q, mblk_t *mp*/);
|
|
static int ldtermwsrv(/*queue_t *q*/);
|
|
|
|
/*
|
|
* Since most of the buffering occurs either at the stream head or in
|
|
* the "message currently being assembled" buffer, we have a relatively
|
|
* small input queue, so that blockages above us get reflected fairly
|
|
* quickly to the module below us. We also have a small maximum packet
|
|
* size, since you can put a message of that size on an empty queue no
|
|
* matter how much bigger than the high water mark it is.
|
|
*/
|
|
static struct module_info ldtermmiinfo = {
|
|
0x0bad, /* to the bone */
|
|
"ldterm",
|
|
0,
|
|
128, /* never eat anything bigger than your head */
|
|
500,
|
|
200
|
|
};
|
|
|
|
static struct qinit ldtermrinit = {
|
|
ldtermrput,
|
|
ldtermrsrv,
|
|
ldtermopen,
|
|
ldtermclose,
|
|
NULL,
|
|
&ldtermmiinfo
|
|
};
|
|
|
|
/*
|
|
* Write side flow control strategy: We want the module to behave as much as
|
|
* possible as if it had no write service procedure. Thus, the write put
|
|
* procedure will process incoming messages and hand them to the downstream
|
|
* module whenever canput allows it. The high water mark is set to one, so
|
|
* that only a single message is queued between the time the downstream module
|
|
* blocks and this module reflects that state upward.
|
|
*/
|
|
static struct module_info ldtermmoinfo = {
|
|
0x0bad, /* to the bone */
|
|
"ldterm",
|
|
0,
|
|
INFPSZ,
|
|
1,
|
|
0
|
|
};
|
|
|
|
static struct qinit ldtermwinit = {
|
|
ldtermwput,
|
|
ldtermwsrv,
|
|
ldtermopen,
|
|
ldtermclose,
|
|
NULL,
|
|
&ldtermmoinfo
|
|
};
|
|
|
|
struct streamtab ldtrinfo = {
|
|
&ldtermrinit,
|
|
&ldtermwinit,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
typedef struct {
|
|
mblk_t *t_savbp; /* saved mblk that holds ld struct */
|
|
struct termios t_modes; /* modes */
|
|
unsigned long t_state; /* internal state of tty module */
|
|
int t_line; /* output line of tty */
|
|
int t_col; /* output column of tty */
|
|
int t_rocount; /* number of chars echoed since last output */
|
|
int t_rocol; /* column in which first such char appeared */
|
|
mblk_t *t_message; /* pointer to 1st mblk in message being built */
|
|
mblk_t *t_endmsg; /* pointer to last mblk in that message */
|
|
int t_msglen; /* number of characters in that message */
|
|
mblk_t *t_echomp; /* echoed output being assembled */
|
|
int t_iocid; /* ID of ioctl reply being awaited */
|
|
int t_wbufcid; /* ID of pending write-side bufcall */
|
|
} ldterm_state_t;
|
|
|
|
/*
|
|
* Internal state bits.
|
|
*/
|
|
#define TS_TTSTOP 0x00000001 /* output stopped by ^S */
|
|
#define TS_TBLOCK 0x00000002 /* input stopped by IXOFF mode */
|
|
#define TS_QUOT 0x00000004 /* last character input was \ */
|
|
#define TS_ERASE 0x00000008 /* within a \.../ for PRTRUB */
|
|
#define TS_SLNCH 0x00000010 /* next char svc rtn sees is literal */
|
|
#define TS_PLNCH 0x00000020 /* next char put rtn sees is literal */
|
|
|
|
#define TS_TTCR 0x00000040 /* mapping NL to CR-NL */
|
|
#define TS_NOCANPUT 0x00000080 /* canonicalization done by */
|
|
/* somebody below us */
|
|
#define TS_NOCANSRV 0x00000100
|
|
#define TS_RESCAN 0x00000200 /* canonicalization mode changed, */
|
|
/* rescan input queue */
|
|
|
|
#define TS_IOCWAIT 0x00000400 /* waiting for reply to ioctl message */
|
|
|
|
/*
|
|
* Statistics counters, for tuning.
|
|
*
|
|
* They record, respectively, the number of regular priority messages
|
|
* processed directly through the write put procedure and the number queued
|
|
* and later processed through the write service procedure.
|
|
*
|
|
* XXX: Is it worth retaining these counts? Should they be moved into
|
|
* ldterm_state_t?
|
|
*/
|
|
struct ldterm_counts {
|
|
int ld_wputmsgs;
|
|
int ld_wsrvmsgs;
|
|
} ldterm_stats;
|
|
#define ldterm_wputmsgs ldterm_stats.ld_wputmsgs
|
|
#define ldterm_wsrvmsgs ldterm_stats.ld_wsrvmsgs
|
|
|
|
/*
|
|
* Character types.
|
|
*/
|
|
#define PRINTABLE 0 /* ordinary printable character */
|
|
#define CONTROL 1 /* non-special control character */
|
|
#define BACKSPACE 2 /* BS */
|
|
#define NEWLINE 3 /* NL */
|
|
#define TAB 4 /* TAB */
|
|
#define VTAB 5 /* VT */
|
|
#define RETURN 6 /* CR */
|
|
|
|
/*
|
|
* Table indicating character classes to tty driver. In particular,
|
|
* if the class is PRINTABLE, then the character needs no special
|
|
* processing on output.
|
|
*
|
|
* Characters in the C1 set are all considered CONTROL; this will
|
|
* work with terminals that properly use the ANSI/ISO extensions,
|
|
* but might cause distress with terminals that put graphics in
|
|
* the range 0200-0237. On the other hand, characters in that
|
|
* range cause even greater distress to other UNIX terminal drivers....
|
|
*/
|
|
|
|
static char typetab[256] = {
|
|
/* 000 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 004 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 010 */ BACKSPACE, TAB, NEWLINE, CONTROL,
|
|
/* 014 */ VTAB, RETURN, CONTROL, CONTROL,
|
|
/* 020 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 024 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 030 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 034 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 040 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 044 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 050 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 054 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 060 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 064 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 070 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 074 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 100 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 104 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 110 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 114 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 120 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 124 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 130 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 134 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 140 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 144 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 150 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 154 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 160 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 164 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 170 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 174 */ PRINTABLE, PRINTABLE, PRINTABLE, CONTROL,
|
|
/* 200 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 204 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 210 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 214 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 220 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 224 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 230 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 234 */ CONTROL, CONTROL, CONTROL, CONTROL,
|
|
/* 240 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 244 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 250 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 254 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 260 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 264 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 270 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 274 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 300 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 304 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 310 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 314 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 320 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 324 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 330 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 334 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 340 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 344 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 350 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 354 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 360 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 364 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 370 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
/* 374 */ PRINTABLE, PRINTABLE, PRINTABLE, PRINTABLE,
|
|
};
|
|
|
|
/*
|
|
* Translation table for output without OLCUC. All PRINTABLE-class characters
|
|
* translate to themselves. All other characters have a zero in the table,
|
|
* which stops the copying.
|
|
*/
|
|
static unsigned char notrantab[256] = {
|
|
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
|
|
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
|
|
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
|
|
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
|
|
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
|
/* 140 */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
/* 150 */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
/* 160 */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
/* 170 */ 'x', 'y', 'z', '{', '|', '}', '~', 0,
|
|
/* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 210 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 220 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 230 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 240 */ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
|
|
/* 250 */ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
|
|
/* 260 */ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
|
|
/* 270 */ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
|
|
/* 300 */ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
|
|
/* 310 */ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
|
|
/* 320 */ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
|
|
/* 330 */ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
|
|
/* 340 */ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
|
|
/* 350 */ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
|
|
/* 360 */ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
|
|
/* 370 */ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
|
|
};
|
|
|
|
/*
|
|
* Translation table for output with OLCUC. All PRINTABLE-class characters
|
|
* translate to themselves, except for lower-case letters which translate
|
|
* to their upper-case equivalents. All other characters have a zero in
|
|
* the table, which stops the copying.
|
|
*/
|
|
static unsigned char lcuctab[256] = {
|
|
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
|
|
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
|
|
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
|
|
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
|
|
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
|
/* 140 */ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
/* 150 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
/* 160 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
/* 170 */ 'X', 'Y', 'Z', '{', '|', '}', '~', 0,
|
|
/* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 210 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 220 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 230 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 240 */ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
|
|
/* 250 */ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
|
|
/* 260 */ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
|
|
/* 270 */ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
|
|
/* 300 */ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
|
|
/* 310 */ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
|
|
/* 320 */ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
|
|
/* 330 */ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
|
|
/* 340 */ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
|
|
/* 350 */ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
|
|
/* 360 */ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
|
|
/* 370 */ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
|
|
};
|
|
|
|
/*
|
|
* Input mapping table -- if an entry is non-zero, and XCASE is set,
|
|
* when the corresponding character is typed preceded by "\" the escape
|
|
* sequence is replaced by the table value. Mostly used for
|
|
* upper-case only terminals.
|
|
*/
|
|
static char imaptab[256] = {
|
|
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 040 */ 0, '|', 0, 0, 0, 0, 0, '`',
|
|
/* 050 */ '{', '}', 0, 0, 0, 0, 0, 0,
|
|
/* 060 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 070 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 100 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 110 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 120 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 130 */ 0, 0, 0, 0, '\\', 0, '~', 0,
|
|
/* 140 */ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
/* 150 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
/* 160 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
/* 170 */ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
|
|
/* 200-377 aren't mapped */
|
|
};
|
|
|
|
/*
|
|
* Output mapping table -- if an entry is non-zero, and XCASE is set,
|
|
* the corresponding character is printed as "\" followed by the table
|
|
* value. Mostly used for upper-case only terminals.
|
|
*/
|
|
static char omaptab[256] = {
|
|
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 040 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 050 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 060 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 070 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 100 */ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
/* 130 */ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
|
|
/* 140 */ '\'', 0, 0, 0, 0, 0, 0, 0,
|
|
/* 150 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 160 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 170 */ 0, 0, 0, '(', '!', ')', '^', 0,
|
|
/* 200-377 aren't mapped */
|
|
};
|
|
|
|
static int ldtermopen_wakeup(/*caddr_t addr*/);
|
|
static mblk_t *do_canon_input(/*unsigned char c, mblk_t *bpt, int ebsize,
|
|
ldterm_state_t *tp*/);
|
|
static int curline_unget(/*ldterm_state_t *tp*/);
|
|
static void curline_trim(/*ldterm_state_t *tp*/);
|
|
static void curline_rubout(/*unsigned char c, queue_t *q, int ebsize,
|
|
ldterm_state_t *tp*/);
|
|
static int curline_tabcols(/*ldterm_state_t *tp*/);
|
|
static void curline_erase(/*queue_t *q, int ebsize, ldterm_state_t *tp*/);
|
|
static void curline_werase(/*queue_t *q, int ebsize, ldterm_state_t *tp*/);
|
|
static void curline_kill(/*queue_t *q, int ebsize, ldterm_state_t *tp*/);
|
|
static void curline_reprint(/*queue_t *q, int ebsize,
|
|
ldterm_state_t *tp*/);
|
|
static void do_noncanon_input(/*mblk_t *bp, queue_t *q,
|
|
ldterm_state_t *tp*/);
|
|
static int echo_char(/*unsigned char c, queue_t *q, int ebsize,
|
|
ldterm_state_t *tp*/);
|
|
static void output_echo_char(/*unsigned char c, queue_t *q, int bsize,
|
|
ldterm_state_t *tp*/);
|
|
static void output_echo_string(/*unsigned char *cp, int len, queue_t *q,
|
|
int ebsize, ldterm_state_t *tp*/);
|
|
static mblk_t *newmsg(/*ldterm_state_t *tp*/);
|
|
static void msg_upstream(/*queue_t *q, ldterm_state_t *tp*/);
|
|
static int ldterm_wenable(/*long addr*/);
|
|
static int ldtermwmsg(/*queue_t *q, mblk_t *mp*/);
|
|
static mblk_t *do_output(/*queue_t *q, mblk_t *imp, mblk_t **omp,
|
|
ldterm_state_t *tp, int echoing*/);
|
|
static void do_flush_output(/*unsigned char c, queue_t *q,
|
|
ldterm_state_t *tp*/);
|
|
static void do_signal(/*queue_t *q, int sig, unsigned char c, int doecho,
|
|
int ignore_noflsh*/);
|
|
static void do_ioctl(/*queue_t *q, mblk_t *mp*/);
|
|
static void chgstropts(/*struct termios *oldmodep, ldterm_state_t *tp,
|
|
queue_t *q*/);
|
|
static void ioctl_reply(/*queue_t *q, mblk_t *mp*/);
|
|
|
|
static struct termios initmodes = {
|
|
BRKINT|ICRNL|IXON|ISTRIP, /* iflag */
|
|
OPOST|ONLCR|XTABS, /* oflag */
|
|
0, /* cflag */
|
|
ISIG|ICANON|ECHO|IEXTEN, /* lflag */
|
|
0, /* line */
|
|
{ CINTR,
|
|
CQUIT,
|
|
CERASE,
|
|
CKILL,
|
|
CEOF,
|
|
CEOL,
|
|
CEOL2,
|
|
CSWTCH,
|
|
CSTART,
|
|
CSTOP,
|
|
CSUSP,
|
|
CDSUSP,
|
|
CRPRNT,
|
|
CFLUSH,
|
|
CWERASE,
|
|
CLNEXT,
|
|
0 /* nonexistent STATUS */
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Line discipline open.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static int
|
|
ldtermopen(q, dev, oflag, sflag)
|
|
queue_t *q;
|
|
int dev, oflag, sflag;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
register mblk_t *bp;
|
|
register int s;
|
|
register struct stroptions *strop;
|
|
int id;
|
|
|
|
if (q->q_ptr != NULL)
|
|
return (NEWCTTY); /* already attached */
|
|
|
|
if ((bp = allocb((int)sizeof (ldterm_state_t), BPRI_MED)) == NULL) {
|
|
printf("ldtermopen: open fails, can't alloc state structure\n");
|
|
return (OPENFAIL);
|
|
}
|
|
bp->b_wptr += sizeof (ldterm_state_t);
|
|
tp = (ldterm_state_t *)bp->b_rptr;
|
|
tp->t_savbp = bp;
|
|
|
|
tp->t_modes = initmodes;
|
|
|
|
tp->t_state = 0;
|
|
|
|
tp->t_line = 0;
|
|
tp->t_col = 0;
|
|
|
|
tp->t_rocount = 0;
|
|
tp->t_rocol = 0;
|
|
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
|
|
tp->t_echomp = NULL;
|
|
|
|
tp->t_iocid = 0;
|
|
tp->t_wbufcid = 0;
|
|
|
|
q->q_ptr = (caddr_t)tp;
|
|
WR(q)->q_ptr = (caddr_t)tp;
|
|
|
|
/*
|
|
* Find out if the module below us does canonicalization; if so,
|
|
* we won't do it ourselves.
|
|
*/
|
|
while (!putctl1(WR(q)->q_next, M_CTL, MC_CANONQUERY)) {
|
|
s = splstr();
|
|
id = bufcall(1, BPRI_HI, ldtermopen_wakeup, (long)&q->q_ptr);
|
|
if (sleep((caddr_t)&q->q_ptr, STIPRI|PCATCH)) {
|
|
unbufcall(id);
|
|
/* Dump the state structure, then unlink it */
|
|
freeb(tp->t_savbp);
|
|
q->q_ptr = NULL;
|
|
(void) splx(s);
|
|
u.u_error = EINTR;
|
|
return (OPENFAIL);
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* Set the high-water and low-water marks on the stream head to values
|
|
* appropriate for a terminal. Also disable vmin and vtime
|
|
* processing, turn on message-nondiscard mode (as we're in ICANON
|
|
* mode), and turn on "old-style NODELAY" mode.
|
|
*
|
|
* N.B.: Disabling vmin and vtime is a bit tricky. It turns out that
|
|
* we really want to send a boolean to the stream head that enables or
|
|
* disables them. However, adding such a field to the M_SETOPTS
|
|
* message would break compatibility with previous releases.
|
|
* Fortunately, there's an out. The so_vmin field is declared as
|
|
* ushort, but the only legal values are u_chars. Thus we can use
|
|
* "(ushort) -1" as an out of band value to encode the boolean. (This
|
|
* trick would have been cleaner had so_vmin been declared as short.)
|
|
*/
|
|
while ((bp =
|
|
allocb((int)sizeof (struct stroptions), BPRI_MED)) == NULL) {
|
|
s = splstr();
|
|
id = bufcall(sizeof (struct stroptions), BPRI_MED,
|
|
ldtermopen_wakeup, (long)&q->q_ptr);
|
|
if (sleep((caddr_t)&q->q_ptr, STIPRI|PCATCH)) {
|
|
unbufcall(id);
|
|
/* Dump the state structure, then unlink it */
|
|
freeb(tp->t_savbp);
|
|
q->q_ptr = NULL;
|
|
(void) splx(s);
|
|
u.u_error = EINTR;
|
|
return (OPENFAIL);
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
strop = (struct stroptions *)bp->b_wptr;
|
|
strop->so_flags =
|
|
SO_READOPT|SO_HIWAT|SO_LOWAT|SO_VMIN|SO_VTIME|SO_NDELON;
|
|
strop->so_readopt = RMSGN;
|
|
strop->so_hiwat = 300;
|
|
strop->so_lowat = 200;
|
|
strop->so_vmin = (ushort) -1;
|
|
strop->so_vtime = 0;
|
|
bp->b_wptr += sizeof (struct stroptions);
|
|
bp->b_datap->db_type = M_SETOPTS;
|
|
putnext(q, bp);
|
|
|
|
return (NEWCTTY); /* this can become a controlling TTY */
|
|
}
|
|
|
|
static int
|
|
ldtermopen_wakeup(addr)
|
|
long addr;
|
|
{
|
|
wakeup((caddr_t) addr);
|
|
}
|
|
|
|
/*
|
|
* Close this module instance, coordinating with the driver below to preserve
|
|
* the line discipline's semantics until all output has drained. If a signal
|
|
* occurs during this interval, give up and complete the close immediately.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static int
|
|
ldtermclose(q, flag)
|
|
register queue_t *q;
|
|
int flag;
|
|
{
|
|
ldterm_state_t *tp = (ldterm_state_t *)q->q_ptr;
|
|
register mblk_t *bp;
|
|
mblk_t *datap;
|
|
register struct iocblk *iocb;
|
|
register struct stroptions *strop;
|
|
register int s;
|
|
int id;
|
|
|
|
/*
|
|
* Reset the high-water and low-water marks on the stream head (?),
|
|
* turn on byte-stream mode, and turn off "old-style NODELAY" mode.
|
|
*/
|
|
while ((bp =
|
|
allocb((int)sizeof (struct stroptions), BPRI_MED)) == NULL) {
|
|
s = splstr();
|
|
id = bufcall(sizeof (struct stroptions), BPRI_MED,
|
|
ldtermopen_wakeup, (long)&q->q_ptr);
|
|
if (sleep((caddr_t)&q->q_ptr, STIPRI|PCATCH)) {
|
|
unbufcall(id);
|
|
(void) splx(s);
|
|
goto tidy_up;
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
strop = (struct stroptions *)bp->b_wptr;
|
|
strop->so_flags = SO_READOPT|SO_NDELOFF;
|
|
strop->so_readopt = RNORM;
|
|
bp->b_wptr += sizeof (struct stroptions);
|
|
bp->b_datap->db_type = M_SETOPTS;
|
|
putnext(q, bp);
|
|
|
|
/*
|
|
* Wait for output to drain completely from the modules and driver
|
|
* below us before completing the close. Doing so guarantees that the
|
|
* proper tty semantics -- in particular, flow control processing --
|
|
* still apply for the remaining pending output.
|
|
*
|
|
* We do this by sending a TCSBRK ioctl downstream and waiting for the
|
|
* driver's response. (When issued with a nonzero argument, this
|
|
* ioctl does nothing other than wait for output to drain, which is
|
|
* precisely what we want.)
|
|
*/
|
|
/*
|
|
* Set up the ioctl.
|
|
*/
|
|
while ((bp = allocb((int)sizeof (struct iocblk), BPRI_MED)) == NULL) {
|
|
s = splstr();
|
|
id = bufcall(sizeof (struct iocblk), BPRI_MED,
|
|
ldtermopen_wakeup, (long)&q->q_ptr);
|
|
if (sleep((caddr_t)&q->q_ptr, STIPRI|PCATCH)) {
|
|
unbufcall(id);
|
|
(void) splx(s);
|
|
goto tidy_up;
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
while ((datap = allocb((int)sizeof (int), BPRI_MED)) == NULL) {
|
|
s = splstr();
|
|
id = bufcall(sizeof (int), BPRI_MED,
|
|
ldtermopen_wakeup, (long)&q->q_ptr);
|
|
if (sleep((caddr_t)&q->q_ptr, STIPRI|PCATCH)) {
|
|
unbufcall(id);
|
|
(void) splx(s);
|
|
freeb(bp);
|
|
goto tidy_up;
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
iocb = (struct iocblk *)bp->b_wptr;
|
|
iocb->ioc_cmd = TCSBRK;
|
|
iocb->ioc_uid = 0;
|
|
iocb->ioc_gid = 0;
|
|
iocb->ioc_id = getiocseqno();
|
|
iocb->ioc_count = sizeof (int);
|
|
iocb->ioc_error = 0;
|
|
iocb->ioc_rval = 0;
|
|
bp->b_wptr += (sizeof *iocb)/(sizeof *bp->b_wptr);
|
|
bp->b_datap->db_type = M_IOCTL;
|
|
*(int*)datap->b_wptr = 1; /* arbitrary nonzero value */
|
|
datap->b_wptr += (sizeof (int))/(sizeof *datap->b_wptr);
|
|
bp->b_cont = datap;
|
|
/*
|
|
* Fire it off.
|
|
*/
|
|
tp->t_state |= TS_IOCWAIT;
|
|
tp->t_iocid = iocb->ioc_id;
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* We should check if ldterm write queue has anything here
|
|
* before sending TCSBRK downstream to make sure TCSBRK is always
|
|
* the last message. Supposingly, "putq()" can do the job.
|
|
* However, on 4m platform, since there is no service thread
|
|
* at system initialization time, service routines will not run
|
|
* until return to user level which does not occur until "init"
|
|
* issues system calls. "init" would sleep here forever. The
|
|
* following hack is put in which should work
|
|
* based on the assumption that at "init" time, no other
|
|
* message will exist on the ldterm write queue.
|
|
*/
|
|
if (!WR(q)->q_first)
|
|
putnext(WR(q), bp);
|
|
else
|
|
putq(WR(q), bp);
|
|
#else
|
|
/*
|
|
* We should check if ldterm write queue has anything here
|
|
* before sending TCSBRK downstream. putq() will do
|
|
* the job
|
|
*/
|
|
putq(WR(q), bp);
|
|
#endif
|
|
|
|
/*
|
|
* Now wait for it. Let our read queue put routine wake us up
|
|
* when it arrives.
|
|
*/
|
|
while (tp->t_state & TS_IOCWAIT) {
|
|
if (sleep((caddr_t)&tp->t_iocid, STIPRI|PCATCH))
|
|
break;
|
|
}
|
|
|
|
tidy_up:
|
|
/*
|
|
* From here to the end, the routine does not sleep, so it's
|
|
* guaranteed to run to completion.
|
|
*/
|
|
|
|
/*
|
|
* Restart output; since this module handles ^S and ^Q for most
|
|
* drivers, rather than those drivers handling it themselves, any
|
|
* subsequent ^Qs won't be able to restart it.
|
|
* Do this only if we stopped the output, and do it
|
|
* only after the output has drained, so we get flow
|
|
* control properly handled for all output that has gone
|
|
* before the module was popped.
|
|
*/
|
|
if (tp->t_state & TS_TTSTOP)
|
|
(void) putctl(WR(q)->q_next, M_START);
|
|
/*
|
|
* If we previously sent a M_STOPI (i.e. tandem mode flow control
|
|
* output to the other system), restart the other side.
|
|
*/
|
|
if (tp->t_state & TS_TBLOCK)
|
|
(void) putctl(WR(q)->q_next, M_STARTI);
|
|
|
|
/*
|
|
* Commit to the final stage of the close; beyond this point tp is
|
|
* invalid outside of this routine. Arrange for messages arriving
|
|
* from below to be shunted off to our upstream neighbor.
|
|
*/
|
|
q->q_ptr = NULL;
|
|
|
|
/*
|
|
* Cancel outstanding "bufcall" request.
|
|
*/
|
|
if (tp->t_wbufcid)
|
|
unbufcall(tp->t_wbufcid);
|
|
|
|
/*
|
|
* Dismantle state by freeing saved messages and then unlinking our
|
|
* per-instance state structure.
|
|
*/
|
|
if (tp->t_message != NULL)
|
|
freemsg(tp->t_message);
|
|
freeb(tp->t_savbp);
|
|
}
|
|
|
|
/*
|
|
* Put procedure for input from driver end of stream (read queue).
|
|
*/
|
|
static int
|
|
ldtermrput(q, mp)
|
|
queue_t *q;
|
|
mblk_t *mp;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
register unsigned char c;
|
|
queue_t *wrq = WR(q); /* write queue of tty mod */
|
|
queue_t *nextq = q->q_next; /* queue below us */
|
|
mblk_t *bp;
|
|
register unsigned char *readp;
|
|
register unsigned char *writep;
|
|
struct iocblk *iocp;
|
|
|
|
tp = (ldterm_state_t *)q->q_ptr;
|
|
if (tp == NULL) {
|
|
/*
|
|
* The message has arrived in the window between ldtermclose
|
|
* committing to dismantle our per-instance state and the
|
|
* STREAMS framework completing the close. Since we're
|
|
* effectively closed, pass it along for the stream head to
|
|
* worry about.
|
|
*/
|
|
putnext(q, mp);
|
|
return;
|
|
}
|
|
|
|
switch (mp->b_datap->db_type) {
|
|
|
|
default:
|
|
putq(q, mp);
|
|
return;
|
|
|
|
case M_IOCACK:
|
|
case M_IOCNAK:
|
|
/*
|
|
* If we are doing an "ioctl" ourselves, check if this
|
|
* is the reply to that code. If so, wake up the
|
|
* "close" routine, and toss the reply, otherwise just
|
|
* pass it up.
|
|
*/
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
if (!(tp->t_state & TS_IOCWAIT) ||
|
|
iocp->ioc_id != tp->t_iocid) {
|
|
/*
|
|
* This isn't the reply we're looking for. Pass it
|
|
* on.
|
|
*/
|
|
putq(q, mp);
|
|
} else {
|
|
tp->t_state &= ~TS_IOCWAIT;
|
|
wakeup((caddr_t)&tp->t_iocid);
|
|
freemsg(mp);
|
|
}
|
|
return;
|
|
|
|
case M_BREAK:
|
|
if (!(tp->t_modes.c_iflag & IGNBRK)) {
|
|
if (tp->t_modes.c_iflag & BRKINT) {
|
|
/*
|
|
* Needed for POSIX compliance:
|
|
* Ignore the NOFLSH flag if on.
|
|
* NOFLSH does not stop break-related flushing.
|
|
*/
|
|
do_signal(q, SIGINT, '\0', 0, 1);
|
|
freemsg(mp);
|
|
} else {
|
|
/*
|
|
* Convert the message into an M_DATA message
|
|
* containing a byte sequence matching the
|
|
* PARMRK setting.
|
|
*
|
|
* XXX: Sleazy code here; we take advantage of
|
|
* compatibility code in allocb that
|
|
* forces all requests to a minimum of 4
|
|
* bytes.
|
|
*/
|
|
mp->b_datap->db_type = M_DATA;
|
|
mp->b_rptr = mp->b_wptr = mp->b_datap->db_base;
|
|
if (tp->t_modes.c_iflag & PARMRK) {
|
|
/*
|
|
* Report the break as the sequence
|
|
* '\377' '\0' '\0'.
|
|
*/
|
|
*mp->b_wptr++ = '\377';
|
|
*mp->b_wptr++ = '\0';
|
|
} else {
|
|
/*
|
|
* Report as '\0'. (Fall through...)
|
|
*/
|
|
}
|
|
*mp->b_wptr++ = '\0';
|
|
break;
|
|
}
|
|
} else
|
|
freemsg(mp);
|
|
return;
|
|
|
|
case M_CTL:
|
|
switch (*mp->b_rptr) {
|
|
|
|
case MC_NOCANON:
|
|
tp->t_state |= TS_NOCANPUT;
|
|
break;
|
|
|
|
case MC_DOCANON:
|
|
tp->t_state &= ~TS_NOCANPUT;
|
|
break;
|
|
}
|
|
putq(q, mp);
|
|
return;
|
|
|
|
case M_DATA:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Flow control: send "start input" message if blocked and
|
|
* our queue is below its low water mark.
|
|
*/
|
|
if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
|
|
q->q_count <= TTXOLO) {
|
|
tp->t_state &= ~TS_TBLOCK;
|
|
(void) putctl(wrq->q_next, M_STARTI);
|
|
}
|
|
|
|
/*
|
|
* If somebody below us ("intelligent" communications board,
|
|
* pseudo-tty controlled by an editor) is doing
|
|
* canonicalization, don't scan it for special characters.
|
|
*/
|
|
if (tp->t_state & TS_NOCANPUT) {
|
|
tk_nin += msgdsize(mp);
|
|
putq(q, mp);
|
|
return;
|
|
}
|
|
|
|
bp = mp;
|
|
|
|
do {
|
|
readp = bp->b_rptr;
|
|
writep = readp;
|
|
tk_nin += bp->b_wptr - readp;
|
|
if (tp->t_modes.c_iflag & (INLCR|IGNCR|ICRNL|IUCLC|IXON) ||
|
|
tp->t_modes.c_lflag & (ISIG|ICANON)) {
|
|
/*
|
|
* We're doing some sort of non-trivial processing
|
|
* of input; look at every character.
|
|
*/
|
|
while (readp < bp->b_wptr) {
|
|
c = *readp++;
|
|
|
|
if (tp->t_modes.c_iflag & ISTRIP)
|
|
/*
|
|
* For POSIX compliance, \377 must
|
|
* pass thru unmolested
|
|
* for the case of PARMRK set.
|
|
*/
|
|
if ((!(tp->t_modes.c_iflag &
|
|
PARMRK)) || (c != 0377) ||
|
|
(readp >= bp->b_wptr) ||
|
|
(*readp != 0)) {
|
|
c &= 0177;
|
|
}
|
|
/*
|
|
* First, check that this hasn't been escaped
|
|
* with the "literal next" character.
|
|
*/
|
|
if (tp->t_state & TS_PLNCH) {
|
|
tp->t_state &= ~TS_PLNCH;
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
*writep++ = c;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Setting a special character to VDISABLE
|
|
* disables it, so if this character is
|
|
* VDISABLE, it should not be compared with
|
|
* any of the special characters.
|
|
* It should, however, restart frozen output if
|
|
* IXON and IXANY are set.
|
|
*/
|
|
if (c == VDISABLE) {
|
|
if (tp->t_modes.c_iflag & IXON &&
|
|
tp->t_state & TS_TTSTOP &&
|
|
tp->t_modes.c_iflag & IXANY) {
|
|
tp->t_state &= ~TS_TTSTOP;
|
|
(void) putctl(wrq->q_next,
|
|
M_START);
|
|
}
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
*writep++ = c;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If stopped, start if you can; if running,
|
|
* stop if you must.
|
|
*/
|
|
if (tp->t_modes.c_iflag & IXON) {
|
|
if (tp->t_state & TS_TTSTOP) {
|
|
if (c == tp->t_modes.c_cc[VSTART] ||
|
|
tp->t_modes.c_iflag & IXANY) {
|
|
tp->t_state &= ~TS_TTSTOP;
|
|
(void) putctl(wrq->q_next, M_START);
|
|
}
|
|
} else {
|
|
if (c == tp->t_modes.c_cc[VSTOP]) {
|
|
tp->t_state |= TS_TTSTOP;
|
|
(void) putctl(wrq->q_next, M_STOP);
|
|
}
|
|
}
|
|
if (c == tp->t_modes.c_cc[VSTOP] ||
|
|
c == tp->t_modes.c_cc[VSTART])
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Check for "literal next" character and
|
|
* "flush output" character. Note that we
|
|
* omit checks for ISIG and ICANON, since
|
|
* the IEXTEN setting subsumes them.
|
|
*/
|
|
if (tp->t_modes.c_lflag & IEXTEN) {
|
|
if (c == tp->t_modes.c_cc[VLNEXT]) {
|
|
/*
|
|
* Remember that we saw a
|
|
* "literal next" while
|
|
* scanning input, but leave
|
|
* it in the message so that
|
|
* the service routine can see
|
|
* it too.
|
|
*/
|
|
tp->t_state |= TS_PLNCH;
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
*writep++ = c;
|
|
continue;
|
|
}
|
|
if (c == tp->t_modes.c_cc[VDISCARD]) {
|
|
do_flush_output(c, wrq, tp);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
|
|
/*
|
|
* Check for signal-generating characters.
|
|
*/
|
|
if (tp->t_modes.c_lflag & ISIG) {
|
|
if (c == tp->t_modes.c_cc[VINTR]) {
|
|
do_signal(q, SIGINT, c, 1, 0);
|
|
continue;
|
|
}
|
|
if (c == tp->t_modes.c_cc[VQUIT]) {
|
|
do_signal(q, SIGQUIT, c, 1, 0);
|
|
continue;
|
|
}
|
|
if (c == tp->t_modes.c_cc[VSUSP]) {
|
|
do_signal(q, SIGTSTP, c, 1, 0);
|
|
continue;
|
|
}
|
|
if (c == tp->t_modes.c_cc[VSWTCH]) {
|
|
/*
|
|
* Shouldn't ever see this;
|
|
* ignore it.
|
|
*/
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Throw away CR if IGNCR set, or turn
|
|
* it into NL if ICRNL set.
|
|
*/
|
|
if (c == '\r') {
|
|
if (tp->t_modes.c_iflag & IGNCR)
|
|
continue;
|
|
if (tp->t_modes.c_iflag & ICRNL)
|
|
c = '\n';
|
|
} else {
|
|
/*
|
|
* Turn NL into CR if INLCR set.
|
|
*/
|
|
if (c == '\n' &&
|
|
tp->t_modes.c_iflag & INLCR)
|
|
c = '\r';
|
|
}
|
|
|
|
/*
|
|
* Map upper case input to lower case if
|
|
* IUCLC flag set.
|
|
*/
|
|
if ((tp->t_modes.c_iflag & IUCLC) &&
|
|
c >= 'A' && c <= 'Z')
|
|
c += 'a' - 'A';
|
|
|
|
/*
|
|
* Put the possibly-transformed character
|
|
* back in the message.
|
|
*/
|
|
*writep++ = c;
|
|
}
|
|
|
|
/*
|
|
* If we didn't copy some characters because
|
|
* we were ignoring them, fix the size of the
|
|
* data block by adjusting the write pointer.
|
|
* XXX This may result in a zero-length block;
|
|
* will this cause anybody gastric distress?
|
|
*/
|
|
bp->b_wptr -= (readp - writep);
|
|
} else {
|
|
/*
|
|
* We won't be doing anything other than possibly
|
|
* stripping the input.
|
|
*/
|
|
if (tp->t_modes.c_iflag & ISTRIP) {
|
|
while (readp < bp->b_wptr) {
|
|
/*
|
|
* For POSIX compliance, \377 must
|
|
* pass thru unmolested
|
|
* for the case of PARMRK set.
|
|
*/
|
|
if (!((!(tp->t_modes.c_iflag &
|
|
PARMRK)) || (*readp != 0377) ||
|
|
(readp >= bp->b_wptr) ||
|
|
(*(readp + 1) != 0)))
|
|
*writep++ = *readp++;
|
|
else
|
|
*writep++ = *readp++ & 0177;
|
|
|
|
}
|
|
}
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
}
|
|
|
|
/*
|
|
* Flow control: send "stop input" message if our queue is
|
|
* approaching its high-water mark.
|
|
*
|
|
* Set QWANTW to ensure that the read queue service
|
|
* procedure gets run when nextq empties up again, so that
|
|
* it can unstop the input.
|
|
*/
|
|
if ((tp->t_modes.c_iflag & IXOFF) &&
|
|
!(tp->t_state & TS_TBLOCK) &&
|
|
q->q_count >= TTXOHI) {
|
|
nextq->q_flag |= QWANTW;
|
|
tp->t_state |= TS_TBLOCK;
|
|
(void) putctl(wrq->q_next, M_STOPI);
|
|
}
|
|
} while ((bp = bp->b_cont) != NULL); /* next block, if any */
|
|
|
|
/*
|
|
* Queue the message for service procedure.
|
|
*/
|
|
putq(q, mp);
|
|
}
|
|
|
|
/*
|
|
* Line discipline input server processing. Erase/kill and escape ('\')
|
|
* processing, gathering into messages, upper/lower case input mapping.
|
|
*/
|
|
static int
|
|
ldtermrsrv(q)
|
|
register queue_t *q;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
mblk_t *mp;
|
|
register mblk_t *bp;
|
|
register mblk_t *bpt;
|
|
register unsigned char c;
|
|
int ebsize;
|
|
|
|
tp = (ldterm_state_t *)q->q_ptr;
|
|
|
|
if (tp->t_state & TS_RESCAN) {
|
|
/*
|
|
* Canonicalization was turned on or off.
|
|
* Put the message being assembled back in the input queue,
|
|
* so that we rescan it.
|
|
*/
|
|
if (tp->t_message != NULL) {
|
|
putbq(q, tp->t_message);
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
}
|
|
tp->t_state &= ~TS_RESCAN;
|
|
}
|
|
|
|
bpt = NULL;
|
|
|
|
while ((mp = getq(q)) != NULL) {
|
|
if (mp->b_datap->db_type <= QPCTL && !canput(q->q_next)) {
|
|
putbq(q, mp);
|
|
goto out; /* read side is blocked */
|
|
}
|
|
switch (mp->b_datap->db_type) {
|
|
|
|
default:
|
|
putnext(q, mp); /* pass it on */
|
|
continue;
|
|
|
|
case M_FLUSH:
|
|
/*
|
|
* Flush everything we haven't looked at yet.
|
|
*/
|
|
flushq(q, FLUSHDATA);
|
|
|
|
/*
|
|
* Flush everything we have looked at.
|
|
*/
|
|
freemsg(tp->t_message);
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
tp->t_rocount = 0; /* if it hasn't been typed, */
|
|
tp->t_rocol = 0; /* it hasn't been echoed :-) */
|
|
putnext(q, mp); /* pass it on */
|
|
continue;
|
|
|
|
case M_HANGUP:
|
|
/*
|
|
* Flush everything we haven't looked at yet.
|
|
*/
|
|
flushq(q, FLUSHDATA);
|
|
|
|
/*
|
|
* Flush everything we have looked at.
|
|
*/
|
|
freemsg(tp->t_message);
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
tp->t_rocount = 0; /* if it hasn't been typed, */
|
|
tp->t_rocol = 0; /* it hasn't been echoed :-) */
|
|
|
|
/*
|
|
* Restart output, since it's probably got nowhere to
|
|
* go anyway, and we're probably not going to see
|
|
* another ^Q for a while.
|
|
*/
|
|
tp->t_state &= ~TS_TTSTOP;
|
|
(void) putctl(WR(q)->q_next, M_START);
|
|
|
|
/*
|
|
* This guy will travel up the read queue, flushing
|
|
* as it goes, get turned around at the stream head,
|
|
* and travel back down the write queue, flushing as
|
|
* it goes.
|
|
*/
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHRW);
|
|
|
|
/*
|
|
* This guy will travel down the write queue, flushing
|
|
* as it goes, get turned around at the driver,
|
|
* and travel back up the read queue, flushing as
|
|
* it goes.
|
|
*/
|
|
(void) putctl1(WR(q), M_FLUSH, FLUSHRW);
|
|
|
|
/*
|
|
* Now that that's done, we send a SIGCONT upstream,
|
|
* followed by the M_HANGUP.
|
|
*/
|
|
(void) putctl1(q->q_next, M_PCSIG, SIGCONT);
|
|
(void) putnext(q, mp);
|
|
continue;
|
|
|
|
case M_IOCACK:
|
|
/*
|
|
* Augment whatever information the driver is
|
|
* returning with the information we supply.
|
|
*/
|
|
ioctl_reply(q, mp);
|
|
continue;
|
|
|
|
case M_CTL:
|
|
switch (*mp->b_rptr) {
|
|
|
|
case MC_NOCANON:
|
|
/*
|
|
* Note: this is very nasty. It's not
|
|
* clear what the right thing to do
|
|
* with a partial message is; we throw
|
|
* it out.
|
|
*/
|
|
if (tp->t_message != NULL) {
|
|
freemsg(tp->t_message);
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
tp->t_rocount = 0;
|
|
tp->t_rocol = 0;
|
|
}
|
|
tp->t_state |= TS_NOCANSRV;
|
|
break;
|
|
|
|
case MC_DOCANON:
|
|
tp->t_state &= ~TS_NOCANSRV;
|
|
break;
|
|
}
|
|
putnext(q, mp);
|
|
continue;
|
|
|
|
case M_DATA:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* This is an M_DATA message.
|
|
*/
|
|
|
|
/*
|
|
* If somebody below us ("intelligent" communications board,
|
|
* pseudo-tty controlled by an editor) is doing
|
|
* canonicalization, don't scan it for special characters.
|
|
*/
|
|
if (tp->t_state & TS_NOCANSRV) {
|
|
putnext(q, mp);
|
|
continue;
|
|
}
|
|
|
|
if (tp->t_modes.c_lflag & ICANON) {
|
|
bp = mp;
|
|
|
|
ebsize = bp->b_wptr - bp->b_rptr;
|
|
if (ebsize > EBSIZE)
|
|
ebsize = EBSIZE;
|
|
|
|
if ((bpt = newmsg(tp)) != NULL) {
|
|
mblk_t *bcont;
|
|
|
|
do {
|
|
bcont = bp->b_cont;
|
|
while (bp->b_rptr < bp->b_wptr) {
|
|
c = *bp->b_rptr++;
|
|
if ((bpt =
|
|
do_canon_input(c, bpt,
|
|
ebsize, q, tp)) == NULL)
|
|
break;
|
|
}
|
|
/*
|
|
* Release this block.
|
|
*/
|
|
freeb(bp);
|
|
if (bpt == NULL) {
|
|
printf("ldtermrsrv: out of blocks\n");
|
|
freemsg(bcont);
|
|
break;
|
|
}
|
|
} while ((bp = bcont) != NULL);
|
|
}
|
|
} else
|
|
do_noncanon_input(mp, q, tp);
|
|
|
|
/*
|
|
* Send whatever we echoed downstream.
|
|
*/
|
|
if (tp->t_echomp != NULL) {
|
|
putnext(WR(q), tp->t_echomp);
|
|
tp->t_echomp = NULL;
|
|
}
|
|
}
|
|
|
|
out:
|
|
/*
|
|
* Flow control: send start message if blocked and
|
|
* our queue is below its low water mark.
|
|
*/
|
|
if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
|
|
q->q_count <= TTXOLO) {
|
|
tp->t_state &= ~TS_TBLOCK;
|
|
(void) putctl(WR(q), M_STARTI);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do canonical mode input; check whether this character is to be treated as a
|
|
* special character - if so, check whether it's equal to any of the special
|
|
* characters and handle it accordingly. Otherwise, just add it to the current
|
|
* line.
|
|
*/
|
|
static mblk_t *
|
|
do_canon_input(c, bpt, ebsize, q, tp)
|
|
register unsigned char c;
|
|
register mblk_t *bpt;
|
|
int ebsize;
|
|
queue_t *q;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register queue_t *wrq = WR(q);
|
|
int i;
|
|
|
|
/*
|
|
* If the previous character was the "literal next" character, treat
|
|
* this character as regular input.
|
|
*/
|
|
if (tp->t_state & TS_SLNCH)
|
|
goto escaped;
|
|
|
|
/*
|
|
* Setting a special character to VDISABLE disables it, so if this
|
|
* character is VDISABLE, it should not be compared with any of the
|
|
* special characters.
|
|
*/
|
|
if (c == VDISABLE) {
|
|
tp->t_state &= ~TS_QUOT;
|
|
goto escaped;
|
|
}
|
|
|
|
/*
|
|
* If this character is the literal next character, echo it as '^',
|
|
* backspace over it, and record that fact.
|
|
*/
|
|
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VLNEXT]) {
|
|
if (tp->t_modes.c_lflag & ECHO)
|
|
output_echo_string((unsigned char *)"^\b", 2, wrq,
|
|
ebsize, tp);
|
|
tp->t_state &= ~TS_QUOT; /* cannot be escaped with \ */
|
|
tp->t_state |= TS_SLNCH;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check for the editing characters.
|
|
*/
|
|
if (c == tp->t_modes.c_cc[VERASE]) {
|
|
if (tp->t_state & TS_QUOT) {
|
|
/*
|
|
* Get rid of the backslash, and put the erase
|
|
* character in its place.
|
|
*/
|
|
curline_erase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
goto escaped;
|
|
} else {
|
|
/*
|
|
* Erase the most-recently-typed character.
|
|
*/
|
|
curline_erase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VWERASE]) {
|
|
tp->t_state &= ~TS_QUOT; /* cannot be escaped with \ */
|
|
curline_werase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
goto out;
|
|
}
|
|
|
|
if (c == tp->t_modes.c_cc[VKILL]) {
|
|
if (tp->t_state & TS_QUOT) {
|
|
/*
|
|
* Get rid of the backslash, and put the kill character
|
|
* in its place.
|
|
*/
|
|
curline_erase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
goto escaped;
|
|
} else {
|
|
/*
|
|
* Erase the entire line.
|
|
*/
|
|
curline_kill(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VREPRINT]) {
|
|
curline_reprint(wrq, ebsize, tp);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* If the preceding character was a backslash:
|
|
* if the current character is an EOF, get rid of the backslash
|
|
* and treat the EOF as data;
|
|
* if we're in XCASE mode and the current character is part
|
|
* of a backslash-X escape sequence, process it;
|
|
* otherwise, just treat the current character normally.
|
|
*/
|
|
if (tp->t_state & TS_QUOT) {
|
|
tp->t_state &= ~TS_QUOT;
|
|
if (c == tp->t_modes.c_cc[VEOF]) {
|
|
/*
|
|
* EOF character.
|
|
* Since it's escaped, get rid of the backslash and put
|
|
* the EOF character in its place.
|
|
*/
|
|
curline_erase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
} else {
|
|
/*
|
|
* If we're in XCASE mode, and the current character
|
|
* is part of a backslash-X sequence, get rid of the
|
|
* backslash and replace the current character with
|
|
* what that sequence maps to.
|
|
*/
|
|
if ((tp->t_modes.c_lflag & XCASE) &&
|
|
imaptab[c] != '\0') {
|
|
curline_erase(wrq, ebsize, tp);
|
|
bpt = tp->t_endmsg;
|
|
c = imaptab[c];
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* Previous character wasn't backslash; check whether this
|
|
* was the EOF character.
|
|
*/
|
|
if (c == tp->t_modes.c_cc[VEOF]) {
|
|
/*
|
|
* EOF character.
|
|
* Don't echo it unless ECHOCTL is set, don't stuff it
|
|
* in the current line, but send the line up the
|
|
* stream.
|
|
*/
|
|
if ((tp->t_modes.c_lflag & ECHOCTL) &&
|
|
(tp->t_modes.c_lflag & ECHO)) {
|
|
i = echo_char(c, wrq, ebsize, tp);
|
|
while (i > 0) {
|
|
output_echo_char('\b', wrq, ebsize,
|
|
tp);
|
|
i--;
|
|
}
|
|
}
|
|
bpt->b_datap->db_type = M_DATA;
|
|
msg_upstream(q, tp);
|
|
bpt = newmsg(tp);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
escaped:
|
|
if (tp->t_msglen == TTYHOG) {
|
|
/*
|
|
* Character will cause line to overflow.
|
|
* Ring the bell or discard all input, and don't save the
|
|
* character away.
|
|
*/
|
|
if (tp->t_modes.c_iflag & IMAXBEL)
|
|
output_echo_char(_CTRL(g), wrq, ebsize, tp);
|
|
else
|
|
(void) putctl1(q, M_FLUSH, FLUSHR);
|
|
tp->t_state &= ~TS_SLNCH;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Add the character to the current line.
|
|
*/
|
|
if (bpt->b_wptr >= bpt->b_datap->db_lim) {
|
|
/*
|
|
* No more room in this mblk; save this one away, and
|
|
* allocate a new one.
|
|
*/
|
|
bpt->b_datap->db_type = M_DATA;
|
|
if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL)
|
|
goto out;
|
|
|
|
/*
|
|
* Chain the new one to the end of the old one, and
|
|
* mark it as the last block in the current line.
|
|
*/
|
|
tp->t_endmsg->b_cont = bpt;
|
|
tp->t_endmsg = bpt;
|
|
}
|
|
*bpt->b_wptr++ = c;
|
|
tp->t_msglen++;
|
|
|
|
if (!(tp->t_state & TS_SLNCH) &&
|
|
(c == '\n' || (c != VDISABLE && (c == tp->t_modes.c_cc[VEOL] ||
|
|
((tp->t_modes.c_lflag & IEXTEN) &&
|
|
c == tp->t_modes.c_cc[VEOL2]))))) {
|
|
/*
|
|
* It's a line-termination character; send the line
|
|
* up the stream.
|
|
*/
|
|
bpt->b_datap->db_type = M_DATA;
|
|
msg_upstream(q, tp);
|
|
if ((bpt = newmsg(tp)) == NULL)
|
|
goto out;
|
|
} else {
|
|
/*
|
|
* Character was escaped with LNEXT.
|
|
*/
|
|
if (tp->t_rocount++ == 0)
|
|
tp->t_rocol = tp->t_col;
|
|
tp->t_state &= ~(TS_SLNCH|TS_QUOT);
|
|
if (c == '\\')
|
|
tp->t_state |= TS_QUOT;
|
|
}
|
|
|
|
/*
|
|
* Echo it.
|
|
*/
|
|
if (tp->t_state & TS_ERASE) {
|
|
tp->t_state &= ~TS_ERASE;
|
|
if (tp->t_modes.c_lflag & ECHO)
|
|
output_echo_char('/', wrq, ebsize, tp);
|
|
}
|
|
|
|
if (tp->t_modes.c_lflag & ECHO)
|
|
(void) echo_char(c, wrq, ebsize, tp);
|
|
else {
|
|
/*
|
|
* Echo NL when ECHO turned off, if ECHONL flag is set.
|
|
*/
|
|
if (c == '\n' && (tp->t_modes.c_lflag & ECHONL))
|
|
output_echo_char(c, wrq, ebsize, tp);
|
|
}
|
|
|
|
out:
|
|
return (bpt);
|
|
}
|
|
|
|
/*
|
|
* Get the character at the end of the current line, and remove it from the
|
|
* current line. Return -1 if the current line is empty.
|
|
*/
|
|
static int
|
|
curline_unget(tp)
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register mblk_t *bpt;
|
|
|
|
if ((bpt = tp->t_endmsg) == NULL)
|
|
return (-1); /* no buffers */
|
|
if (bpt->b_rptr == bpt->b_wptr)
|
|
return (-1); /* zero-length record */
|
|
tp->t_msglen--; /* one fewer character */
|
|
return (*--bpt->b_wptr);
|
|
}
|
|
|
|
/*
|
|
* Trim any zero-length mblks from the end of the current line (they may have
|
|
* been left there by erasing characters from the line).
|
|
*/
|
|
static void
|
|
curline_trim(tp)
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register mblk_t *bpt;
|
|
register mblk_t *bp;
|
|
|
|
if ((bpt = tp->t_endmsg) == NULL)
|
|
panic("curline_trim called with no message");
|
|
if (bpt->b_rptr == bpt->b_wptr) {
|
|
/*
|
|
* This mblk is now empty.
|
|
* Find the previous mblk; throw this one away, unless
|
|
* it's the first one.
|
|
*/
|
|
bp = tp->t_message;
|
|
if (bp != bpt) {
|
|
while (bp->b_cont != bpt) {
|
|
if ((bp = bp->b_cont) == NULL)
|
|
panic("curline_trim: current input line mislinked");
|
|
}
|
|
bp->b_cont = NULL;
|
|
freeb(bpt);
|
|
tp->t_endmsg = bp; /* point to that mblk */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Rubout one character from the current line being built for tp
|
|
* as cleanly as possible. q is the write queue for tp.
|
|
*/
|
|
static void
|
|
curline_rubout(c, q, ebsize, tp)
|
|
register unsigned char c;
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int tabcols;
|
|
static unsigned char crtrubout[] = "\b \b\b \b";
|
|
#define RUBOUT1 &crtrubout[3] /* rub out one position */
|
|
#define RUBOUT2 &crtrubout[0] /* rub out two positions */
|
|
|
|
if (!(tp->t_modes.c_lflag & ECHO))
|
|
return;
|
|
if (tp->t_modes.c_lflag & ECHOE) {
|
|
/*
|
|
* "CRT rubout"; try erasing it from the screen.
|
|
*/
|
|
if (tp->t_rocount == 0) {
|
|
/*
|
|
* After the character being erased was echoed,
|
|
* some data was written to the terminal; we
|
|
* can't erase it cleanly, so we just reprint the
|
|
* whole line as if the user had typed the
|
|
* reprint character.
|
|
*/
|
|
curline_reprint(q, ebsize, tp);
|
|
return;
|
|
} else {
|
|
/*
|
|
* XXX what about escaped characters?
|
|
*/
|
|
switch (typetab[c]) {
|
|
|
|
case PRINTABLE:
|
|
if ((tp->t_modes.c_lflag & XCASE) &&
|
|
omaptab[c])
|
|
output_echo_string(RUBOUT1, 3, q,
|
|
ebsize, tp);
|
|
output_echo_string(RUBOUT1, 3, q, ebsize, tp);
|
|
break;
|
|
|
|
case VTAB:
|
|
case BACKSPACE:
|
|
case CONTROL:
|
|
case RETURN:
|
|
case NEWLINE:
|
|
if (tp->t_modes.c_lflag & ECHOCTL)
|
|
output_echo_string(RUBOUT2, 6, q,
|
|
ebsize, tp);
|
|
break;
|
|
|
|
case TAB:
|
|
if (tp->t_rocount < tp->t_msglen) {
|
|
/*
|
|
* While the tab being erased was
|
|
* expanded, some data was written
|
|
* to the terminal; we can't erase it
|
|
* cleanly, so we just reprint the
|
|
* whole line as if the user had typed
|
|
* the reprint character.
|
|
*/
|
|
curline_reprint(q, ebsize, tp);
|
|
return;
|
|
}
|
|
tabcols = curline_tabcols(tp);
|
|
while (--tabcols >= 0)
|
|
output_echo_char('\b', q, ebsize, tp);
|
|
break;
|
|
}
|
|
}
|
|
} else if (tp->t_modes.c_lflag & ECHOPRT) {
|
|
/*
|
|
* "Printing rubout"; echo it between \ and /.
|
|
*/
|
|
if (!(tp->t_state & TS_ERASE)) {
|
|
output_echo_char('\\', q, ebsize, tp);
|
|
tp->t_state |= TS_ERASE;
|
|
}
|
|
(void) echo_char(c, q, ebsize, tp);
|
|
} else
|
|
(void) echo_char(tp->t_modes.c_cc[VERASE], q, ebsize, tp);
|
|
tp->t_rocount--; /* we "unechoed" this character */
|
|
}
|
|
|
|
/*
|
|
* Find the number of characters the tab we just deleted took up by
|
|
* zipping through the current line and recomputing the column number.
|
|
*/
|
|
static int
|
|
curline_tabcols(tp)
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int col;
|
|
register mblk_t *bp;
|
|
register unsigned char *readp;
|
|
register unsigned char c;
|
|
|
|
col = tp->t_rocol;
|
|
bp = tp->t_message;
|
|
do {
|
|
readp = bp->b_rptr;
|
|
while (readp < bp->b_wptr) {
|
|
c = *readp++;
|
|
if (tp->t_modes.c_lflag & ECHOCTL) {
|
|
if (c <= 037 && c != '\t' && c != '\n' ||
|
|
c == 0177) {
|
|
/*
|
|
* One column for '^' and one for
|
|
* (c + ' ').
|
|
*/
|
|
col += 2;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Column position calculated here.
|
|
*/
|
|
switch (typetab[c]) {
|
|
|
|
/* Ordinary characters; advance by one. */
|
|
case PRINTABLE:
|
|
col++;
|
|
break;
|
|
|
|
/* Non-printing characters; nothing happens. */
|
|
case CONTROL:
|
|
break;
|
|
|
|
/* Backspace */
|
|
case BACKSPACE:
|
|
if (col != 0)
|
|
col--;
|
|
break;
|
|
|
|
/* Newline; column depends on flags. */
|
|
case NEWLINE:
|
|
if (tp->t_modes.c_oflag & ONLRET)
|
|
col = 0;
|
|
break;
|
|
|
|
/* tab */
|
|
case TAB:
|
|
col |= 07;
|
|
col++;
|
|
break;
|
|
|
|
/* vertical motion */
|
|
case VTAB:
|
|
break;
|
|
|
|
/* carriage return */
|
|
case RETURN:
|
|
col = 0;
|
|
break;
|
|
}
|
|
}
|
|
} while ((bp = bp->b_cont) != NULL); /* next block, if any */
|
|
|
|
/*
|
|
* "col" is now the column number before the tab.
|
|
* "tp->t_col" is still the column number after the tab,
|
|
* since we haven't erased the tab yet.
|
|
* Thus "tp->t_col - col" is the number of positions the tab
|
|
* moved.
|
|
*/
|
|
col = tp->t_col - col;
|
|
if (col > 8)
|
|
col = 8; /* overflow screw */
|
|
return (col);
|
|
}
|
|
|
|
/*
|
|
* Erase a single character from the current line; remove the character and
|
|
* properly echo the erase character.
|
|
*/
|
|
static void
|
|
curline_erase(q, ebsize, tp)
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int c;
|
|
|
|
if ((c = curline_unget(tp)) != -1) {
|
|
curline_rubout((unsigned char)c, q, ebsize, tp);
|
|
curline_trim(tp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Erase an entire word from the current line; remove the characters and, for
|
|
* each one, echo an erase character.
|
|
*/
|
|
static void
|
|
curline_werase(q, ebsize, tp)
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int c;
|
|
|
|
/*
|
|
* Erase trailing white space, if any.
|
|
*/
|
|
while ((c = curline_unget(tp)) == ' ' || c == '\t') {
|
|
curline_rubout((unsigned char)c, q, ebsize, tp);
|
|
curline_trim(tp);
|
|
}
|
|
|
|
/*
|
|
* Erase non-white-space characters, if any.
|
|
*/
|
|
while (c != -1 && c != ' ' && c != '\t') {
|
|
curline_rubout((unsigned char)c, q, ebsize, tp);
|
|
curline_trim(tp);
|
|
c = curline_unget(tp);
|
|
}
|
|
if (c != -1) {
|
|
/*
|
|
* We removed one too many characters; put the last one
|
|
* back.
|
|
*/
|
|
tp->t_endmsg->b_wptr++; /* put 'c' back */
|
|
tp->t_msglen++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Kill the entire current line; remove the characters and, if ECHOKE is set,
|
|
* echo an erase character for each one, otherwise echo the kill character and,
|
|
* if ECHOK is set, a newline.
|
|
*/
|
|
static void
|
|
curline_kill(q, ebsize, tp)
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int c;
|
|
|
|
if ((tp->t_modes.c_lflag & ECHOKE) && tp->t_msglen == tp->t_rocount) {
|
|
while ((c = curline_unget(tp)) != -1) {
|
|
curline_rubout((unsigned char)c, q, ebsize, tp);
|
|
curline_trim(tp);
|
|
}
|
|
} else {
|
|
(void) echo_char(tp->t_modes.c_cc[VKILL], q, ebsize, tp);
|
|
if (tp->t_modes.c_lflag & ECHOK)
|
|
(void) echo_char('\n', q, ebsize, tp);
|
|
while (curline_unget(tp) != -1)
|
|
curline_trim(tp);
|
|
tp->t_rocount = 0;
|
|
}
|
|
tp->t_state &= ~(TS_QUOT|TS_ERASE|TS_SLNCH);
|
|
}
|
|
|
|
/*
|
|
* Reprint the current input line. First, echo the reprint character, if it
|
|
* hasn't been disabled.
|
|
* XXX just the current line, not the whole queue?
|
|
* What about DEFECHO mode?
|
|
*/
|
|
static void
|
|
curline_reprint(q, ebsize, tp)
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register mblk_t *bp;
|
|
register unsigned char *readp;
|
|
|
|
if (tp->t_modes.c_cc[VREPRINT] != (unsigned char)0)
|
|
(void) echo_char(tp->t_modes.c_cc[VREPRINT], q, ebsize, tp);
|
|
output_echo_char('\n', q, ebsize, tp);
|
|
|
|
bp = tp->t_message;
|
|
do {
|
|
readp = bp->b_rptr;
|
|
while (readp < bp->b_wptr)
|
|
(void) echo_char(*readp++, q, ebsize, tp);
|
|
} while ((bp = bp->b_cont) != NULL); /* next block, if any */
|
|
|
|
tp->t_state &= ~TS_ERASE;
|
|
tp->t_rocount = tp->t_msglen; /* we reechoed the entire line */
|
|
tp->t_rocol = 0;
|
|
}
|
|
|
|
/*
|
|
* Do non-canonical mode input.
|
|
*/
|
|
static void
|
|
do_noncanon_input(mp, q, tp)
|
|
register mblk_t *mp;
|
|
queue_t *q;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
queue_t *wrq = WR(q);
|
|
int ebsize;
|
|
register mblk_t *bp, *prevbp;
|
|
mblk_t *savebp;
|
|
register unsigned char *rptr;
|
|
|
|
for (bp = mp, prevbp = NULL; bp != NULL;
|
|
prevbp = bp, bp = bp->b_cont) {
|
|
while (bp->b_rptr == bp->b_wptr) {
|
|
/*
|
|
* Zero-length block. Throw it away.
|
|
*/
|
|
if (prevbp == NULL)
|
|
mp = bp->b_cont;
|
|
else
|
|
prevbp->b_cont = bp->b_cont;
|
|
savebp = bp;
|
|
bp = bp->b_cont;
|
|
savebp->b_cont = NULL;
|
|
freeb(savebp);
|
|
if (bp == NULL)
|
|
return; /* entire message gone */
|
|
}
|
|
if (tp->t_modes.c_lflag & (ECHO|ECHONL)) {
|
|
/*
|
|
* Echo the data in this message.
|
|
*/
|
|
if (tp->t_modes.c_lflag & ECHO) {
|
|
ebsize = bp->b_wptr - bp->b_rptr;
|
|
if (ebsize > EBSIZE)
|
|
ebsize = EBSIZE;
|
|
rptr = bp->b_rptr;
|
|
while (rptr < bp->b_wptr)
|
|
(void) echo_char(*rptr++, wrq,
|
|
ebsize, tp);
|
|
} else {
|
|
/*
|
|
* Echo NL, even though ECHO is not
|
|
* set.
|
|
*/
|
|
rptr = bp->b_rptr;
|
|
while (rptr < bp->b_wptr) {
|
|
if (*rptr++ == '\n')
|
|
output_echo_char('\n',
|
|
wrq, 1, tp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
putnext(q, mp);
|
|
|
|
/*
|
|
* Send whatever we echoed downstream.
|
|
*/
|
|
if (tp->t_echomp != NULL) {
|
|
putnext(wrq, tp->t_echomp);
|
|
tp->t_echomp = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Echo a typed character to the terminal.
|
|
* Returns the number of characters printed.
|
|
*/
|
|
static int
|
|
echo_char(c, q, ebsize, tp)
|
|
register unsigned char c;
|
|
register queue_t *q;
|
|
int ebsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register int i;
|
|
|
|
if (!(tp->t_modes.c_lflag & ECHO))
|
|
return (0);
|
|
i = 0;
|
|
if (tp->t_modes.c_lflag & ECHOCTL) {
|
|
if (c <= 037 && c != '\t' && c != '\n') {
|
|
output_echo_char('^', q, ebsize, tp);
|
|
i++;
|
|
if (tp->t_modes.c_oflag & OLCUC)
|
|
c += 'a' - 1;
|
|
else
|
|
c += 'A' - 1;
|
|
} else if (c == 0177) {
|
|
output_echo_char('^', q, ebsize, tp);
|
|
i++;
|
|
c = '?';
|
|
}
|
|
}
|
|
output_echo_char(c, q, ebsize, tp);
|
|
return (i + 1);
|
|
}
|
|
|
|
/*
|
|
* Put a character generated as part of an echo operation on the output queue.
|
|
*/
|
|
static void
|
|
output_echo_char(c, q, bsize, tp)
|
|
register unsigned char c;
|
|
register queue_t *q;
|
|
int bsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register mblk_t *curbp;
|
|
|
|
/*
|
|
* Don't even look at the characters unless we
|
|
* have something useful to do with them.
|
|
*/
|
|
if ((tp->t_modes.c_oflag & OPOST) ||
|
|
((tp->t_modes.c_lflag & XCASE) &&
|
|
(tp->t_modes.c_lflag & ICANON))) {
|
|
register mblk_t *mp;
|
|
|
|
if ((mp = allocb(4, BPRI_HI)) == NULL) {
|
|
printf("output_echo_char: out of blocks\n");
|
|
return;
|
|
}
|
|
*mp->b_wptr++ = c;
|
|
mp = do_output(q, mp, &tp->t_echomp, tp, bsize, 1);
|
|
if (mp != NULL)
|
|
freemsg(mp);
|
|
} else {
|
|
/*
|
|
* No fancy processing required; just glue the character onto
|
|
* the end of the to-be-echoed message, allocating or
|
|
* extending it if necessary.
|
|
*/
|
|
if ((curbp = tp->t_echomp) != NULL) {
|
|
while (curbp->b_cont != NULL)
|
|
curbp = curbp->b_cont;
|
|
if (curbp->b_datap->db_lim == curbp->b_wptr) {
|
|
register mblk_t *newbp;
|
|
|
|
if ((newbp = allocb(bsize, BPRI_HI)) == NULL) {
|
|
printf("output_echo_char: out of blocks\n");
|
|
return;
|
|
}
|
|
curbp->b_cont = newbp;
|
|
curbp = newbp;
|
|
}
|
|
} else {
|
|
if ((curbp = allocb(bsize, BPRI_HI)) == NULL) {
|
|
printf("output_echo_char: out of blocks\n");
|
|
return;
|
|
}
|
|
tp->t_echomp = curbp;
|
|
}
|
|
*curbp->b_wptr++ = c;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy a string, of length len, of characters generated as part of an echo
|
|
* operation to the output queue.
|
|
*/
|
|
static void
|
|
output_echo_string(cp, len, q, bsize, tp)
|
|
register unsigned char *cp;
|
|
register int len;
|
|
register queue_t *q;
|
|
int bsize;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
|
|
while (len > 0) {
|
|
output_echo_char(*cp++, q, bsize, tp);
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static mblk_t *
|
|
newmsg(tp)
|
|
register ldterm_state_t *tp;
|
|
{
|
|
register mblk_t *bp;
|
|
|
|
/*
|
|
* If no current message, allocate a block
|
|
* for it.
|
|
*/
|
|
if ((bp = tp->t_endmsg) == NULL) {
|
|
if ((bp = allocb(IBSIZE, BPRI_MED)) == NULL) {
|
|
printf("newmsg: out of blocks\n");
|
|
return (bp);
|
|
}
|
|
tp->t_message = bp;
|
|
tp->t_endmsg = bp;
|
|
}
|
|
return (bp);
|
|
}
|
|
|
|
static void
|
|
msg_upstream(q, tp)
|
|
register queue_t *q;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
putnext(q, tp->t_message);
|
|
tp->t_message = NULL;
|
|
tp->t_endmsg = NULL;
|
|
tp->t_msglen = 0;
|
|
tp->t_rocount = 0;
|
|
}
|
|
|
|
/*
|
|
* Re-enable the write-side service procedure. When an allocation failure
|
|
* causes write-side processing to stall, we disable the write side and
|
|
* arrange to call this function when allocation once again becomes possible.
|
|
*/
|
|
static int
|
|
ldterm_wenable(addr)
|
|
long addr;
|
|
{
|
|
register queue_t *q = (queue_t *)addr;
|
|
register ldterm_state_t *tp;
|
|
|
|
if ((tp = (ldterm_state_t *)q->q_ptr) == NULL)
|
|
return;
|
|
/*
|
|
* The bufcall is no longer pending.
|
|
*/
|
|
tp->t_wbufcid = 0;
|
|
enableok(q);
|
|
qenable(q);
|
|
}
|
|
|
|
/*
|
|
* Line discipline output queue put procedure. Attempts to process the
|
|
* message directly and send it on downstream, queueing it only if there's
|
|
* already something pending or if its downstream neighbor is clogged.
|
|
*/
|
|
static int
|
|
ldtermwput(q, mp)
|
|
register queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
unsigned char type = mp->b_datap->db_type;
|
|
|
|
/*
|
|
* Always process priority messages, regardless of whether or not our
|
|
* queue is nonempty.
|
|
*/
|
|
if (type >= QPCTL) {
|
|
switch (type) {
|
|
|
|
case M_FLUSH:
|
|
/*
|
|
* This is coming from above, so we only handle the
|
|
* write queue here. If FLUSHR is set, it will get
|
|
* turned around at the driver, and the read procedure
|
|
* will see it eventually.
|
|
*/
|
|
if (*mp->b_rptr & FLUSHW)
|
|
flushq(q, FLUSHDATA);
|
|
putnext(q, mp);
|
|
break;
|
|
|
|
default:
|
|
/* Pass it through unmolested. */
|
|
putnext(q, mp);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If our queue is nonempty or there's a traffic jam downstream, this
|
|
* message must get in line.
|
|
*/
|
|
if (q->q_first != NULL || !canput(q->q_next)) {
|
|
/*
|
|
* Exception: ioctls, except for those defined to take effect
|
|
* after output has drained, should be processed immediately.
|
|
*/
|
|
if (type == M_IOCTL) {
|
|
register struct iocblk *iocp;
|
|
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
switch (iocp->ioc_cmd) {
|
|
|
|
/*
|
|
* Queue these.
|
|
*/
|
|
case TCSETSW:
|
|
case TCSETSF:
|
|
case TCSETAW:
|
|
case TCSETAF:
|
|
case TCSBRK:
|
|
break;
|
|
|
|
/*
|
|
* Handle all others immediately.
|
|
*/
|
|
default:
|
|
ldterm_wputmsgs++;
|
|
(void) ldtermwmsg(q, mp);
|
|
return;
|
|
}
|
|
}
|
|
putq(q, mp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We can take the fast path through, by simply calling ldtermwmsg to
|
|
* dispose of mp.
|
|
*/
|
|
ldterm_wputmsgs++;
|
|
(void) ldtermwmsg(q, mp);
|
|
}
|
|
|
|
/*
|
|
* Line discipline output queue service procedure.
|
|
*/
|
|
static int
|
|
ldtermwsrv(q)
|
|
register queue_t *q;
|
|
{
|
|
register mblk_t *mp;
|
|
|
|
/*
|
|
* We expect this loop to iterate at most once, but must be prepared
|
|
* for more in case our upstream neighbor isn't paying strict
|
|
* attention to what canput tells it.
|
|
*/
|
|
while ((mp = getq(q)) != NULL) {
|
|
ldterm_wsrvmsgs++;
|
|
/*
|
|
* N.B.: ldtermwput has already handled high-priority
|
|
* messages, so we don't have to worry about them here.
|
|
* Hence, the putbq call is safe.
|
|
*/
|
|
if (!canput(q->q_next)) {
|
|
putbq(q, mp);
|
|
break;
|
|
}
|
|
if (!ldtermwmsg(q, mp)) {
|
|
/*
|
|
* Couldn't handle the whole thing; give up for now
|
|
* and wait to be rescheduled.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process the write-side message denoted by mp. If mp can't be processed
|
|
* completely (due to allocation failures), put the residual unprocessed part
|
|
* on the front of the write queue, disable the queue, and schedule a bufcall
|
|
* to arrange to complete its processing later.
|
|
*
|
|
* Return 1 if the message was processed completely and 0 if not.
|
|
*
|
|
* This routine is called from both ldtermwput and ldtermwsrv to do the actual
|
|
* work of dealing with mp. ldtermwput will have already dealt with high
|
|
* priority messages.
|
|
*/
|
|
static int
|
|
ldtermwmsg(q, mp)
|
|
register queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
register mblk_t *residmp = NULL;
|
|
register u_int size;
|
|
|
|
switch (mp->b_datap->db_type) {
|
|
|
|
case M_IOCTL:
|
|
do_ioctl(q, mp);
|
|
break;
|
|
|
|
case M_DATA: {
|
|
mblk_t *omp = NULL;
|
|
|
|
tp = (ldterm_state_t *)q->q_ptr;
|
|
tp->t_rocount = 0;
|
|
|
|
/*
|
|
* Don't even look at the characters unless we
|
|
* have something useful to do with them.
|
|
*/
|
|
if ((tp->t_modes.c_oflag & OPOST) ||
|
|
((tp->t_modes.c_lflag & XCASE) &&
|
|
(tp->t_modes.c_lflag & ICANON))) {
|
|
residmp = do_output(q, mp, &omp, tp, OBSIZE, 0);
|
|
if ((mp = omp) == NULL)
|
|
break;
|
|
}
|
|
if (!(tp->t_modes.c_lflag & FLUSHO)) {
|
|
tk_nout += msgdsize(mp);
|
|
putnext(q, mp);
|
|
} else
|
|
freemsg(mp); /* drop on floor */
|
|
break;
|
|
}
|
|
|
|
default:
|
|
putnext(q, mp); /* pass it through unmolested */
|
|
break;
|
|
}
|
|
|
|
if (residmp == NULL)
|
|
return (1);
|
|
|
|
/*
|
|
* An allocation failure occurred that prevented the message from
|
|
* being completely processed. First, disable our queue, since it's
|
|
* pointless to attempt further processing until the allocation
|
|
* situation is resolved. (This must precede the putbq call below,
|
|
* which would otherwise mark the queue to be serviced.)
|
|
*/
|
|
noenable(q);
|
|
/*
|
|
* Stuff the remnant on our write queue so that we can complete it
|
|
* later when times become less lean. Note that this sets QFULL, so
|
|
* that our upstream neighbor will be blocked by flow control.
|
|
*/
|
|
putbq(q, residmp);
|
|
/*
|
|
* Schedule a bufcall to re-enable the queue. The failure won't have
|
|
* been for an allocation of more than OBSIZE bytes, so don't ask for
|
|
* more than that from bufcall.
|
|
*/
|
|
size = msgdsize(residmp);
|
|
if (size > OBSIZE)
|
|
size = OBSIZE;
|
|
if (tp->t_wbufcid)
|
|
unbufcall(tp->t_wbufcid);
|
|
tp->t_wbufcid = bufcall(size, BPRI_MED, ldterm_wenable, (long)q);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Perform output processing on the message denoted by imp, adding the output
|
|
* characters to the message denoted by *omp (allocating it if necessary).
|
|
* Return the unprocessed residual input, freeing those blocks of the input
|
|
* that were completely processed.
|
|
*
|
|
* Normally, the return value will be NULL. If an allocation failure prevents
|
|
* the routine from having enough space to continue accumulating output, it
|
|
* stops where it is and hands back what it's done, leaving the caller to
|
|
* decide whether to free the remaining input or to arrange for a bufcall to
|
|
* try again later.
|
|
*/
|
|
static mblk_t *
|
|
do_output(q, imp, omp, tp, bsize, echoing)
|
|
register queue_t *q;
|
|
mblk_t *imp; /* head of input message we're examining */
|
|
mblk_t **omp; /* addr of head of output we're constructing */
|
|
register ldterm_state_t *tp;
|
|
int bsize;
|
|
int echoing;
|
|
{
|
|
register mblk_t *ibp; /* block we're examining from input message */
|
|
register mblk_t *obp; /* block we're filling in output message */
|
|
mblk_t *oobp; /* old value of obp; valid if NEW_BLOCK fails */
|
|
mblk_t **contpp; /* where to stuff ptr to newly-allocated blk */
|
|
register unsigned char c;
|
|
register int count, ctype;
|
|
register int chars_left;
|
|
|
|
/*
|
|
* Allocate a new block into which to put characters. If we can't,
|
|
* set oobp to the previous value of obp and indicate failure.
|
|
*/
|
|
#define NEW_BLOCK() \
|
|
(oobp = obp, !(obp = allocb(bsize, BPRI_MED)) ? 0 : \
|
|
(*contpp = obp, contpp = &obp->b_cont, \
|
|
chars_left = obp->b_datap->db_lim - obp->b_wptr, 1))
|
|
|
|
ibp = imp;
|
|
|
|
/*
|
|
* When we allocate the first block of a message, we should stuff
|
|
* the pointer to it in "*omp". All subsequent blocks should
|
|
* have the pointer to them stuffed into the "b_cont" field of the
|
|
* previous block. "contpp" points to the place where we should
|
|
* stuff the pointer.
|
|
*
|
|
* If we already have a message we're filling in, continue doing
|
|
* so.
|
|
*/
|
|
if ((obp = *omp) != NULL) {
|
|
/* Find end of previously accumulated output. */
|
|
for (; obp->b_cont != NULL; obp = obp->b_cont)
|
|
;
|
|
contpp = &obp->b_cont;
|
|
chars_left = obp->b_datap->db_lim - obp->b_wptr;
|
|
} else {
|
|
contpp = omp;
|
|
chars_left = 0;
|
|
}
|
|
|
|
for (;;) {
|
|
mblk_t *cibp = ibp->b_cont;
|
|
|
|
while (ibp->b_rptr < ibp->b_wptr) {
|
|
/*
|
|
* Make sure there's room for one more
|
|
* character.
|
|
*/
|
|
if (chars_left == 0 && !NEW_BLOCK())
|
|
goto outofbufs;
|
|
|
|
/*
|
|
* If doing XCASE processing (not very likely,
|
|
* in this day and age), look at each character
|
|
* individually.
|
|
*/
|
|
if ((tp->t_modes.c_lflag & XCASE) &&
|
|
(tp->t_modes.c_lflag & ICANON)) {
|
|
c = *ibp->b_rptr++;
|
|
|
|
/*
|
|
* If character is mapped on output, put out
|
|
* a backslash followed by what it is
|
|
* mapped to.
|
|
*/
|
|
if (omaptab[c] != 0 &&
|
|
(!echoing || c != '\\')) {
|
|
/*
|
|
* Backslash is an ordinary character.
|
|
*/
|
|
tp->t_col++;
|
|
*obp->b_wptr++ = '\\';
|
|
chars_left--;
|
|
if (chars_left == 0 && !NEW_BLOCK()) {
|
|
/*
|
|
* Make state consistent.
|
|
*/
|
|
ibp->b_rptr--;
|
|
tp->t_col--;
|
|
oobp->b_wptr--;
|
|
goto outofbufs;
|
|
}
|
|
c = omaptab[c];
|
|
}
|
|
|
|
/*
|
|
* If no other output processing is required,
|
|
* push the character into the block and
|
|
* get another.
|
|
*/
|
|
if (!(tp->t_modes.c_oflag & OPOST)) {
|
|
tp->t_col++;
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* OPOST output flag is set.
|
|
* Map lower case to upper case if OLCUC flag
|
|
* is set.
|
|
*/
|
|
if ((tp->t_modes.c_oflag & OLCUC) &&
|
|
c >= 'a' && c <= 'z')
|
|
c -= 'a' - 'A';
|
|
} else {
|
|
/*
|
|
* Copy all the ordinary characters,
|
|
* possibly mapping upper case
|
|
* to lower case.
|
|
*/
|
|
register int chars_to_move;
|
|
register int chars_moved;
|
|
|
|
chars_to_move = ibp->b_wptr - ibp->b_rptr;
|
|
if (chars_to_move > chars_left)
|
|
chars_to_move = chars_left;
|
|
chars_moved = movtuc(chars_to_move,
|
|
ibp->b_rptr, obp->b_wptr,
|
|
(tp->t_modes.c_oflag & OLCUC ?
|
|
lcuctab : notrantab));
|
|
tp->t_col += chars_moved;
|
|
ibp->b_rptr += chars_moved;
|
|
obp->b_wptr += chars_moved;
|
|
chars_left -= chars_moved;
|
|
if (ibp->b_rptr >= ibp->b_wptr) {
|
|
/* moved all of block */
|
|
continue;
|
|
}
|
|
if (chars_left == 0 && !NEW_BLOCK())
|
|
goto outofbufs;
|
|
c = *ibp->b_rptr++; /* stopper */
|
|
}
|
|
|
|
/*
|
|
* Map <CR> to <NL> on output if OCRNL flag set.
|
|
*/
|
|
if (c == '\r' && (tp->t_modes.c_oflag & OCRNL))
|
|
c = '\n';
|
|
|
|
ctype = typetab[c];
|
|
|
|
/*
|
|
* Map <NL> to <CR><NL> on output if ONLCR flag is set.
|
|
*/
|
|
if (c == '\n' && (tp->t_modes.c_oflag & ONLCR)) {
|
|
register int s;
|
|
|
|
s = splstr();
|
|
if (!(tp->t_state & TS_TTCR)) {
|
|
tp->t_state |= TS_TTCR;
|
|
c = '\r';
|
|
ctype = typetab['\r'];
|
|
--ibp->b_rptr;
|
|
} else
|
|
tp->t_state &= ~TS_TTCR;
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* Delay values and column position calculated here.
|
|
*/
|
|
count = 0;
|
|
switch (ctype) {
|
|
|
|
case PRINTABLE:
|
|
tp->t_col++;
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
|
|
case CONTROL:
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
|
|
case BACKSPACE:
|
|
if (tp->t_col)
|
|
tp->t_col--;
|
|
if (tp->t_modes.c_oflag & BSDLY) {
|
|
if (tp->t_modes.c_oflag & OFILL)
|
|
count = 2;
|
|
else
|
|
count = 3;
|
|
}
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
|
|
case NEWLINE:
|
|
if (tp->t_modes.c_oflag & ONLRET)
|
|
goto cr;
|
|
if ((tp->t_modes.c_oflag & NLDLY) == NL1)
|
|
count = 2;
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
|
|
case TAB:
|
|
/*
|
|
* Map '\t' to spaces if XTABS flag is set.
|
|
*/
|
|
if ((tp->t_modes.c_oflag & TABDLY) == XTABS) {
|
|
for (;;) {
|
|
*obp->b_wptr++ = ' ';
|
|
chars_left--;
|
|
tp->t_col++;
|
|
if ((tp->t_col & 07) == 0)
|
|
break; /* every 8th */
|
|
/*
|
|
* If we don't have room to
|
|
* fully expand this tab in
|
|
* this block, back up to
|
|
* continue expanding it
|
|
* into the next block.
|
|
*/
|
|
if (obp->b_wptr >=
|
|
obp->b_datap->db_lim) {
|
|
ibp->b_rptr--;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
tp->t_col |= 07;
|
|
tp->t_col++;
|
|
if (tp->t_modes.c_oflag & OFILL) {
|
|
if (tp->t_modes.c_oflag&TABDLY)
|
|
count = 2;
|
|
} else {
|
|
switch (tp->t_modes.c_oflag&TABDLY) {
|
|
case TAB2:
|
|
count = 6;
|
|
break;
|
|
|
|
case TAB1:
|
|
count = 1 +
|
|
(tp->t_col | ~07);
|
|
if (count < 5)
|
|
count = 0;
|
|
break;
|
|
}
|
|
}
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
}
|
|
break;
|
|
|
|
case VTAB:
|
|
if ((tp->t_modes.c_oflag & VTDLY) &&
|
|
!(tp->t_modes.c_oflag & OFILL))
|
|
count = 127;
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
|
|
case RETURN:
|
|
/*
|
|
* Ignore <CR> in column 0 if ONOCR flag set.
|
|
*/
|
|
if (tp->t_col == 0 &&
|
|
(tp->t_modes.c_oflag & ONOCR))
|
|
break;
|
|
|
|
cr:
|
|
switch (tp->t_modes.c_oflag & CRDLY) {
|
|
|
|
case CR1:
|
|
if (tp->t_modes.c_oflag & OFILL)
|
|
count = 2;
|
|
else
|
|
count = tp->t_col % 2;
|
|
break;
|
|
|
|
case CR2:
|
|
if (tp->t_modes.c_oflag & OFILL)
|
|
count = 4;
|
|
else
|
|
count = 6;
|
|
break;
|
|
|
|
case CR3:
|
|
if (tp->t_modes.c_oflag & OFILL)
|
|
count = 0;
|
|
else
|
|
count = 9;
|
|
break;
|
|
}
|
|
tp->t_col = 0;
|
|
*obp->b_wptr++ = c;
|
|
chars_left--;
|
|
break;
|
|
}
|
|
|
|
if (count != 0) {
|
|
/*
|
|
* At this point, the characters we've added
|
|
* to the output message are consistent with
|
|
* those we've processed from the input
|
|
* message. If an allocation failure occurs
|
|
* while handling the delay, it's too hard to
|
|
* back up to undo the input and output
|
|
* characters associated with the delay. This
|
|
* leaves us with two choices: punt (and drop
|
|
* the remaining delay) or add more state to
|
|
* the ldterm_state structure that records the
|
|
* value of count.
|
|
*
|
|
* In the second alternative, we would have to
|
|
* check the saved count value on entry to
|
|
* this routine (and quite likely in lots of
|
|
* other places, too) and jump here if it's
|
|
* nonzero (after suitable preparation).
|
|
*
|
|
* The second alternative looks too ugly for
|
|
* too little benefit, so we choose the
|
|
* first.
|
|
*/
|
|
if (tp->t_modes.c_oflag & OFILL) {
|
|
do {
|
|
if (chars_left == 0 &&
|
|
!NEW_BLOCK())
|
|
goto outofbufs;
|
|
if (tp->t_modes.c_oflag & OFDEL)
|
|
*obp->b_wptr++ = CDEL;
|
|
else
|
|
*obp->b_wptr++ = CNUL;
|
|
chars_left--;
|
|
} while (--count != 0);
|
|
} else {
|
|
if (!(tp->t_modes.c_lflag & FLUSHO)) {
|
|
tk_nout += msgdsize(*omp);
|
|
putnext(q, *omp);
|
|
(void) putctl1(q->q_next,
|
|
M_DELAY, count);
|
|
} else {
|
|
/* Flush it. */
|
|
freemsg(*omp);
|
|
}
|
|
chars_left = 0;
|
|
/*
|
|
* We have to start a new message;
|
|
* the delay introduces a break
|
|
* between messages.
|
|
*/
|
|
*omp = NULL;
|
|
contpp = omp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free the current input block and advance to the next.
|
|
*/
|
|
freeb(ibp);
|
|
ibp = cibp;
|
|
if (ibp == NULL)
|
|
break;
|
|
}
|
|
|
|
outofbufs:
|
|
return (ibp);
|
|
#undef NEW_BLOCK
|
|
}
|
|
|
|
#if !defined(vax) && !defined(sun)
|
|
movtuc(size, from, origto, table)
|
|
register int size;
|
|
register unsigned char *from;
|
|
unsigned char *origto;
|
|
register unsigned char *table;
|
|
{
|
|
register unsigned char *to = origto;
|
|
register unsigned char c;
|
|
|
|
while (size != 0 && (c = table[*from++]) != 0) {
|
|
*to++ = c;
|
|
size--;
|
|
}
|
|
return (to - origto);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Respond to the "flush output" character; if output is being flushed, stop
|
|
* flushing it, otherwise flush our write queue and the write queues below us,
|
|
* echo the "flush output" character, and start flushing subsequent output.
|
|
*/
|
|
static void
|
|
do_flush_output(c, q, tp)
|
|
unsigned char c;
|
|
register queue_t *q;
|
|
register ldterm_state_t *tp;
|
|
{
|
|
|
|
if (tp->t_modes.c_lflag & FLUSHO)
|
|
tp->t_modes.c_lflag &= ~FLUSHO;
|
|
else {
|
|
flushq(q, FLUSHDATA); /* flush our write queue */
|
|
/* flush the ones below us */
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHW);
|
|
if ((tp->t_echomp = allocb(EBSIZE, BPRI_HI)) != NULL) {
|
|
(void) echo_char(c, q, 1, tp);
|
|
if (tp->t_msglen != 0)
|
|
curline_reprint(q, EBSIZE, tp);
|
|
if (tp->t_echomp != NULL) {
|
|
putnext(q, tp->t_echomp);
|
|
tp->t_echomp = NULL;
|
|
}
|
|
}
|
|
tp->t_modes.c_lflag |= FLUSHO;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Respond to a signal-generating character or an M_BREAK message by echoing
|
|
* the signal-generating character (if "doecho" is set) and sending an M_PCSIG
|
|
* and M_FLUSH message upstream.
|
|
*/
|
|
static void
|
|
do_signal(q, sig, c, doecho, ignore_noflsh)
|
|
register queue_t *q;
|
|
int sig;
|
|
unsigned char c;
|
|
int doecho;
|
|
int ignore_noflsh;
|
|
{
|
|
register ldterm_state_t *tp = (ldterm_state_t *)q->q_ptr;
|
|
|
|
/*
|
|
* If ignore_noflsh, do the flushing regardless of NOFLSH.
|
|
*/
|
|
if (ignore_noflsh || (!(tp->t_modes.c_lflag & NOFLSH))) {
|
|
/*
|
|
* Put it on our read queue, so the service procedure
|
|
* will see it.
|
|
*/
|
|
if (sig != SIGTSTP) {
|
|
/*
|
|
* Flush our read and write queues, and send a
|
|
* "flush read and write" message downstream and
|
|
* upstream.
|
|
*/
|
|
flushq(WR(q), FLUSHDATA);
|
|
flushq(q, FLUSHDATA);
|
|
|
|
(void) putctl1(WR(q)->q_next, M_FLUSH, FLUSHRW);
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHRW);
|
|
} else {
|
|
/*
|
|
* Flush our read queue, and send a "flush read"
|
|
* message upstream and downstream.
|
|
*/
|
|
flushq(q, FLUSHDATA);
|
|
(void) putctl1(WR(q)->q_next, M_FLUSH, FLUSHR);
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHR);
|
|
}
|
|
}
|
|
tp->t_state &= ~TS_QUOT;
|
|
/* PCSIG, not SIG - do it NOW */
|
|
(void) putctl1(q->q_next, M_PCSIG, sig);
|
|
|
|
if (doecho) {
|
|
if ((tp->t_echomp = allocb(4, BPRI_HI)) != NULL) {
|
|
(void) echo_char(c, WR(q), 4, tp);
|
|
putnext(WR(q), tp->t_echomp);
|
|
tp->t_echomp = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when an M_IOCTL message is seen on the write queue; does whatever
|
|
* we're supposed to do with it, and either replies immediately or passes it
|
|
* to the next module down.
|
|
*/
|
|
static void
|
|
do_ioctl(q, mp)
|
|
queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
register struct iocblk *iocp;
|
|
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
tp = (ldterm_state_t *)q->q_ptr;
|
|
|
|
switch (iocp->ioc_cmd) {
|
|
|
|
case TCSETS:
|
|
case TCSETSW:
|
|
case TCSETSF: {
|
|
/*
|
|
* Set current parameters and special characters.
|
|
*/
|
|
register struct termios *cb =
|
|
(struct termios *)mp->b_cont->b_rptr;
|
|
struct termios oldmodes;
|
|
|
|
oldmodes = tp->t_modes;
|
|
tp->t_modes = *cb;
|
|
if (tp->t_modes.c_lflag & PENDIN) {
|
|
/*
|
|
* Yuk. The C shell file completion code actually
|
|
* uses this "feature", so we have to support it.
|
|
*/
|
|
if (tp->t_message != NULL) {
|
|
tp->t_state |= TS_RESCAN;
|
|
qenable(RD(q));
|
|
}
|
|
tp->t_modes.c_lflag &= ~PENDIN;
|
|
}
|
|
|
|
chgstropts(&oldmodes, tp, RD(q));
|
|
|
|
/*
|
|
* The driver may want to know about the following iflags:
|
|
* IGNBRK, BRKINT, IGNPAR, PARMRK, INPCK.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
case TCSETA:
|
|
case TCSETAW:
|
|
case TCSETAF: {
|
|
/*
|
|
* Old-style "ioctl" to set current parameters and
|
|
* special characters.
|
|
* Don't clear out the unset portions, leave them as
|
|
* they are.
|
|
*/
|
|
register struct termio *cb =
|
|
(struct termio *)mp->b_cont->b_rptr;
|
|
struct termios oldmodes;
|
|
|
|
oldmodes = tp->t_modes;
|
|
tp->t_modes.c_iflag =
|
|
(tp->t_modes.c_iflag & 0xffff0000 | cb->c_iflag);
|
|
tp->t_modes.c_oflag =
|
|
(tp->t_modes.c_oflag & 0xffff0000 | cb->c_oflag);
|
|
tp->t_modes.c_cflag =
|
|
(tp->t_modes.c_cflag & 0xffff0000 | cb->c_cflag);
|
|
tp->t_modes.c_lflag =
|
|
(tp->t_modes.c_lflag & 0xffff0000 | cb->c_lflag);
|
|
|
|
tp->t_modes.c_cc[VINTR] = cb->c_cc[VINTR];
|
|
tp->t_modes.c_cc[VQUIT] = cb->c_cc[VQUIT];
|
|
tp->t_modes.c_cc[VERASE] = cb->c_cc[VERASE];
|
|
tp->t_modes.c_cc[VKILL] = cb->c_cc[VKILL];
|
|
tp->t_modes.c_cc[VEOF] = cb->c_cc[VEOF];
|
|
tp->t_modes.c_cc[VEOL] = cb->c_cc[VEOL];
|
|
tp->t_modes.c_cc[VEOL2] = cb->c_cc[VEOL2];
|
|
tp->t_modes.c_cc[VSWTCH] = cb->c_cc[VSWTCH];
|
|
|
|
chgstropts(&oldmodes, tp, RD(q));
|
|
|
|
/*
|
|
* The driver may want to know about the following iflags:
|
|
* IGNBRK, BRKINT, IGNPAR, PARMRK, INPCK.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
case TCFLSH:
|
|
/*
|
|
* Do the flush on the write queue immediately, and queue
|
|
* up any flush on the read queue for the service procedure
|
|
* to see. Then turn it into the appropriate M_FLUSH message,
|
|
* so that the module below us doesn't have to know about
|
|
* TCFLSH.
|
|
*/
|
|
ASSERT(mp->b_datap != NULL);
|
|
switch (*(int *)mp->b_cont->b_rptr) {
|
|
|
|
default:
|
|
u.u_error = EINVAL;
|
|
break;
|
|
|
|
case TCIFLUSH:
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHR);
|
|
(void) putctl1(RD(q), M_FLUSH, FLUSHR);
|
|
break;
|
|
|
|
case TCOFLUSH:
|
|
flushq(q, FLUSHDATA);
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHW);
|
|
(void) putctl1(RD(q)->q_next, M_FLUSH, FLUSHW);
|
|
break;
|
|
|
|
case TCIOFLUSH:
|
|
flushq(q, FLUSHDATA);
|
|
(void) putctl1(q->q_next, M_FLUSH, FLUSHRW);
|
|
(void) putctl1(RD(q), M_FLUSH, FLUSHRW);
|
|
break;
|
|
}
|
|
mp->b_datap->db_type = M_IOCACK;
|
|
iocp->ioc_rval = 0;
|
|
iocp->ioc_count = 0;
|
|
qreply(q, mp);
|
|
return;
|
|
|
|
case TCXONC:
|
|
switch (*(int *)mp->b_cont->b_rptr) {
|
|
|
|
case TCOOFF:
|
|
(void) putctl(q->q_next, M_STOP);
|
|
tp->t_state |= TS_TTSTOP;
|
|
break;
|
|
|
|
case TCOON:
|
|
(void) putctl(q->q_next, M_START);
|
|
tp->t_state &= ~TS_TTSTOP;
|
|
break;
|
|
|
|
case TCIOFF:
|
|
(void) putctl(q->q_next, M_STOPI);
|
|
tp->t_state |= TS_TBLOCK;
|
|
break;
|
|
|
|
case TCION:
|
|
(void) putctl(q->q_next, M_STARTI);
|
|
tp->t_state &= ~TS_TBLOCK;
|
|
break;
|
|
}
|
|
mp->b_datap->db_type = M_IOCACK;
|
|
iocp->ioc_rval = 0;
|
|
iocp->ioc_count = 0;
|
|
qreply(q, mp);
|
|
return;
|
|
}
|
|
|
|
putnext(q, mp);
|
|
}
|
|
|
|
/*
|
|
* Send an M_SETOPTS message upstream if any mode changes are being made
|
|
* that affect the stream head options.
|
|
*/
|
|
static void
|
|
chgstropts(oldmodep, tp, q)
|
|
register struct termios *oldmodep;
|
|
register ldterm_state_t *tp;
|
|
queue_t *q;
|
|
{
|
|
struct stroptions optbuf;
|
|
register mblk_t *bp;
|
|
|
|
optbuf.so_flags = 0;
|
|
if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & ICANON) {
|
|
/*
|
|
* Canonical mode is changing state; switch the stream head
|
|
* to message-nondiscard or byte-stream mode. Also, rerun
|
|
* the service procedure so it can change its mind about
|
|
* whether to send data upstream or not.
|
|
*/
|
|
optbuf.so_flags = SO_READOPT|SO_VMIN|SO_VTIME;
|
|
if (tp->t_modes.c_lflag & ICANON) {
|
|
optbuf.so_readopt = RMSGN;
|
|
optbuf.so_vmin = (ushort) -1;
|
|
optbuf.so_vtime = 0;
|
|
} else {
|
|
optbuf.so_readopt = RNORM;
|
|
optbuf.so_vmin = tp->t_modes.c_cc[VMIN];
|
|
optbuf.so_vtime = tp->t_modes.c_cc[VTIME];
|
|
}
|
|
if (tp->t_message != NULL) {
|
|
tp->t_state |= TS_RESCAN;
|
|
qenable(q);
|
|
}
|
|
} else if (!(tp->t_modes.c_lflag & ICANON) &&
|
|
(oldmodep->c_cc[VMIN] != tp->t_modes.c_cc[VMIN] ||
|
|
oldmodep->c_cc[VTIME] != tp->t_modes.c_cc[VTIME])) {
|
|
/*
|
|
* Canonical mode is off, and the VMIN and VTIME values are
|
|
* changing; let the stream head know.
|
|
*/
|
|
optbuf.so_flags = SO_VMIN|SO_VTIME;
|
|
optbuf.so_vmin = tp->t_modes.c_cc[VMIN];
|
|
optbuf.so_vtime = tp->t_modes.c_cc[VTIME];
|
|
}
|
|
|
|
if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & TOSTOP) {
|
|
/*
|
|
* The "stop on background write" bit is changing.
|
|
*/
|
|
optbuf.so_flags |= SO_TOSTOP;
|
|
if (tp->t_modes.c_lflag & TOSTOP)
|
|
optbuf.so_tostop = 1;
|
|
else
|
|
optbuf.so_tostop = 0;
|
|
}
|
|
|
|
if (optbuf.so_flags != 0) {
|
|
if ((bp = allocb(sizeof (struct stroptions), BPRI_HI)) == NULL)
|
|
panic("chgstropts: can't allocate stroptions message");
|
|
/* XXX - should probably do bufcall */
|
|
*(struct stroptions *)bp->b_wptr = optbuf;
|
|
bp->b_wptr += sizeof (struct stroptions);
|
|
bp->b_datap->db_type = M_SETOPTS;
|
|
putnext(q, bp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when an M_IOCACK message is seen on the read queue; modifies
|
|
* the data being returned, if necessary, and passes the reply up.
|
|
*/
|
|
static void
|
|
ioctl_reply(q, mp)
|
|
queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
register ldterm_state_t *tp;
|
|
register struct iocblk *iocp;
|
|
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
tp = (ldterm_state_t *)q->q_ptr;
|
|
|
|
switch (iocp->ioc_cmd) {
|
|
|
|
case TCGETS: {
|
|
/*
|
|
* Get current parameters and return them to stream head
|
|
* eventually.
|
|
*/
|
|
register struct termios *cb =
|
|
(struct termios *)mp->b_cont->b_rptr;
|
|
register unsigned long cflag = cb->c_cflag;
|
|
|
|
*cb = tp->t_modes;
|
|
if (cflag != 0)
|
|
cb->c_cflag = cflag; /* set by driver */
|
|
break;
|
|
}
|
|
|
|
case TCGETA: {
|
|
/*
|
|
* Old-style "ioctl" to get current parameters and
|
|
* return them to stream head eventually.
|
|
*/
|
|
register struct termio *cb =
|
|
(struct termio *)mp->b_cont->b_rptr;
|
|
|
|
cb->c_iflag = tp->t_modes.c_iflag; /* all except the */
|
|
cb->c_oflag = tp->t_modes.c_oflag; /* cb->c_cflag */
|
|
cb->c_lflag = tp->t_modes.c_lflag;
|
|
|
|
if (cb->c_cflag == 0) /* not set by driver */
|
|
cb->c_cflag = tp->t_modes.c_cflag;
|
|
|
|
cb->c_line = 0;
|
|
cb->c_cc[VINTR] = tp->t_modes.c_cc[VINTR];
|
|
cb->c_cc[VQUIT] = tp->t_modes.c_cc[VQUIT];
|
|
cb->c_cc[VERASE] = tp->t_modes.c_cc[VERASE];
|
|
cb->c_cc[VKILL] = tp->t_modes.c_cc[VKILL];
|
|
cb->c_cc[VEOF] = tp->t_modes.c_cc[VEOF];
|
|
cb->c_cc[VEOL] = tp->t_modes.c_cc[VEOL];
|
|
cb->c_cc[VEOL2] = tp->t_modes.c_cc[VEOL2];
|
|
cb->c_cc[VSWTCH] = tp->t_modes.c_cc[VSWTCH];
|
|
break;
|
|
}
|
|
}
|
|
putnext(q, mp);
|
|
}
|