xref: /onnv-gate/usr/src/uts/common/io/ldterm.c (revision 7012:932cd76b5633)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7012Sis  * Common Development and Distribution License (the "License").
6*7012Sis  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7012Sis  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /* All Rights Reserved					*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
400Sstevel@tonic-gate 
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * Standard Streams Terminal Line Discipline module.
430Sstevel@tonic-gate  */
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include <sys/param.h>
460Sstevel@tonic-gate #include <sys/types.h>
470Sstevel@tonic-gate #include <sys/termio.h>
480Sstevel@tonic-gate #include <sys/stream.h>
490Sstevel@tonic-gate #include <sys/conf.h>
500Sstevel@tonic-gate #include <sys/stropts.h>
510Sstevel@tonic-gate #include <sys/strsubr.h>
520Sstevel@tonic-gate #include <sys/strsun.h>
530Sstevel@tonic-gate #include <sys/strtty.h>
540Sstevel@tonic-gate #include <sys/signal.h>
550Sstevel@tonic-gate #include <sys/file.h>
560Sstevel@tonic-gate #include <sys/errno.h>
570Sstevel@tonic-gate #include <sys/debug.h>
580Sstevel@tonic-gate #include <sys/cmn_err.h>
590Sstevel@tonic-gate #include <sys/euc.h>
600Sstevel@tonic-gate #include <sys/eucioctl.h>
610Sstevel@tonic-gate #include <sys/csiioctl.h>
620Sstevel@tonic-gate #include <sys/ptms.h>
630Sstevel@tonic-gate #include <sys/ldterm.h>
640Sstevel@tonic-gate #include <sys/cred.h>
650Sstevel@tonic-gate #include <sys/ddi.h>
660Sstevel@tonic-gate #include <sys/sunddi.h>
670Sstevel@tonic-gate #include <sys/kmem.h>
680Sstevel@tonic-gate #include <sys/modctl.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate /* Time limit when draining during a close(9E) invoked by exit(2) */
710Sstevel@tonic-gate /* Can be set to zero to emulate the old, broken behavior */
720Sstevel@tonic-gate int ldterm_drain_limit = 15000000;
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate  * Character types.
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate #define	ORDINARY	0
780Sstevel@tonic-gate #define	CONTROL		1
790Sstevel@tonic-gate #define	BACKSPACE	2
800Sstevel@tonic-gate #define	NEWLINE		3
810Sstevel@tonic-gate #define	TAB		4
820Sstevel@tonic-gate #define	VTAB		5
830Sstevel@tonic-gate #define	RETURN		6
840Sstevel@tonic-gate 
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate  * The following for EUC handling:
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate #define	T_SS2		7
890Sstevel@tonic-gate #define	T_SS3		8
900Sstevel@tonic-gate 
910Sstevel@tonic-gate /*
920Sstevel@tonic-gate  * Table indicating character classes to tty driver.  In particular,
930Sstevel@tonic-gate  * if the class is ORDINARY, then the character needs no special
940Sstevel@tonic-gate  * processing on output.
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  * Characters in the C1 set are all considered CONTROL; this will
970Sstevel@tonic-gate  * work with terminals that properly use the ANSI/ISO extensions,
980Sstevel@tonic-gate  * but might cause distress with terminals that put graphics in
990Sstevel@tonic-gate  * the range 0200-0237.  On the other hand, characters in that
1000Sstevel@tonic-gate  * range cause even greater distress to other UNIX terminal drivers....
1010Sstevel@tonic-gate  */
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate static char typetab[256] = {
1040Sstevel@tonic-gate /* 000 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1050Sstevel@tonic-gate /* 004 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1060Sstevel@tonic-gate /* 010 */	BACKSPACE,	TAB,		NEWLINE,	CONTROL,
1070Sstevel@tonic-gate /* 014 */	VTAB,		RETURN,		CONTROL,	CONTROL,
1080Sstevel@tonic-gate /* 020 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1090Sstevel@tonic-gate /* 024 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1100Sstevel@tonic-gate /* 030 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1110Sstevel@tonic-gate /* 034 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1120Sstevel@tonic-gate /* 040 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1130Sstevel@tonic-gate /* 044 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1140Sstevel@tonic-gate /* 050 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1150Sstevel@tonic-gate /* 054 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1160Sstevel@tonic-gate /* 060 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1170Sstevel@tonic-gate /* 064 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1180Sstevel@tonic-gate /* 070 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1190Sstevel@tonic-gate /* 074 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1200Sstevel@tonic-gate /* 100 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1210Sstevel@tonic-gate /* 104 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1220Sstevel@tonic-gate /* 110 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1230Sstevel@tonic-gate /* 114 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1240Sstevel@tonic-gate /* 120 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1250Sstevel@tonic-gate /* 124 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1260Sstevel@tonic-gate /* 130 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1270Sstevel@tonic-gate /* 134 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1280Sstevel@tonic-gate /* 140 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1290Sstevel@tonic-gate /* 144 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1300Sstevel@tonic-gate /* 150 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1310Sstevel@tonic-gate /* 154 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1320Sstevel@tonic-gate /* 160 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1330Sstevel@tonic-gate /* 164 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1340Sstevel@tonic-gate /* 170 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1350Sstevel@tonic-gate /* 174 */	ORDINARY,	ORDINARY,	ORDINARY,	CONTROL,
1360Sstevel@tonic-gate /* 200 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1370Sstevel@tonic-gate /* 204 */	CONTROL,	CONTROL,	T_SS2,		T_SS3,
1380Sstevel@tonic-gate /* 210 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1390Sstevel@tonic-gate /* 214 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1400Sstevel@tonic-gate /* 220 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1410Sstevel@tonic-gate /* 224 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1420Sstevel@tonic-gate /* 230 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1430Sstevel@tonic-gate /* 234 */	CONTROL,	CONTROL,	CONTROL,	CONTROL,
1440Sstevel@tonic-gate /* 240 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1450Sstevel@tonic-gate /* 244 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1460Sstevel@tonic-gate /* 250 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1470Sstevel@tonic-gate /* 254 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1480Sstevel@tonic-gate /* 260 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1490Sstevel@tonic-gate /* 264 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1500Sstevel@tonic-gate /* 270 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1510Sstevel@tonic-gate /* 274 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1520Sstevel@tonic-gate /* 300 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1530Sstevel@tonic-gate /* 304 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1540Sstevel@tonic-gate /* 310 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1550Sstevel@tonic-gate /* 314 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1560Sstevel@tonic-gate /* 320 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1570Sstevel@tonic-gate /* 324 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1580Sstevel@tonic-gate /* 330 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1590Sstevel@tonic-gate /* 334 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1600Sstevel@tonic-gate /* 340 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1610Sstevel@tonic-gate /* 344 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1620Sstevel@tonic-gate /* 350 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1630Sstevel@tonic-gate /* 354 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1640Sstevel@tonic-gate /* 360 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1650Sstevel@tonic-gate /* 364 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1660Sstevel@tonic-gate /* 370 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate  * WARNING:  For EUC, 0xFF must be an ordinary character.  It is used with
1690Sstevel@tonic-gate  * single-byte EUC in some of the "ISO Latin Alphabet" codesets, and occupies
1700Sstevel@tonic-gate  * a screen position; in those ISO sets where that position isn't used, it
1710Sstevel@tonic-gate  * shouldn't make any difference.
1720Sstevel@tonic-gate  */
1730Sstevel@tonic-gate /* 374 */	ORDINARY,	ORDINARY,	ORDINARY,	ORDINARY,
1740Sstevel@tonic-gate };
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate /*
1770Sstevel@tonic-gate  * Translation table for output without OLCUC.  All ORDINARY-class characters
1780Sstevel@tonic-gate  * translate to themselves.  All other characters have a zero in the table,
1790Sstevel@tonic-gate  * which stops the copying.
1800Sstevel@tonic-gate  */
1810Sstevel@tonic-gate static unsigned char notrantab[256] = {
1820Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
1830Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
1840Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
1850Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
1860Sstevel@tonic-gate /* 040 */	' ',	'!',	'"',	'#',	'$',	'%',	'&',	'\'',
1870Sstevel@tonic-gate /* 050 */	'(',	')',	'*',	'+',	',',	'-',	'.',	'/',
1880Sstevel@tonic-gate /* 060 */	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
1890Sstevel@tonic-gate /* 070 */	'8',	'9',	':',	';',	'<',	'=',	'>',	'?',
1900Sstevel@tonic-gate /* 100 */	'@',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
1910Sstevel@tonic-gate /* 110 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
1920Sstevel@tonic-gate /* 120 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
1930Sstevel@tonic-gate /* 130 */	'X',	'Y',	'Z',	'[',	'\\',	']',	'^',	'_',
1940Sstevel@tonic-gate /* 140 */	'`',	'a',	'b',	'c',	'd',	'e',	'f',	'g',
1950Sstevel@tonic-gate /* 150 */	'h',	'i',	'j',	'k',	'l',	'm',	'n',	'o',
1960Sstevel@tonic-gate /* 160 */	'p',	'q',	'r',	's',	't',	'u',	'v',	'w',
1970Sstevel@tonic-gate /* 170 */	'x',	'y',	'z',	'{',	'|',	'}',	'~',	0,
1980Sstevel@tonic-gate /* 200 */	0,	0,	0,	0,	0,	0,	0,	0,
1990Sstevel@tonic-gate /* 210 */	0,	0,	0,	0,	0,	0,	0,	0,
2000Sstevel@tonic-gate /* 220 */	0,	0,	0,	0,	0,	0,	0,	0,
2010Sstevel@tonic-gate /* 230 */	0,	0,	0,	0,	0,	0,	0,	0,
2020Sstevel@tonic-gate /* 240 */	0240,	0241,	0242,	0243,	0244,	0245,	0246,	0247,
2030Sstevel@tonic-gate /* 250 */	0250,	0251,	0252,	0253,	0254,	0255,	0256,	0257,
2040Sstevel@tonic-gate /* 260 */	0260,	0261,	0262,	0263,	0264,	0265,	0266,	0267,
2050Sstevel@tonic-gate /* 270 */	0270,	0271,	0272,	0273,	0274,	0275,	0276,	0277,
2060Sstevel@tonic-gate /* 300 */	0300,	0301,	0302,	0303,	0304,	0305,	0306,	0307,
2070Sstevel@tonic-gate /* 310 */	0310,	0311,	0312,	0313,	0314,	0315,	0316,	0317,
2080Sstevel@tonic-gate /* 320 */	0320,	0321,	0322,	0323,	0324,	0325,	0326,	0327,
2090Sstevel@tonic-gate /* 330 */	0330,	0331,	0332,	0333,	0334,	0335,	0336,	0337,
2100Sstevel@tonic-gate /* 340 */	0340,	0341,	0342,	0343,	0344,	0345,	0346,	0347,
2110Sstevel@tonic-gate /* 350 */	0350,	0351,	0352,	0353,	0354,	0355,	0356,	0357,
2120Sstevel@tonic-gate /* 360 */	0360,	0361,	0362,	0363,	0364,	0365,	0366,	0367,
2130Sstevel@tonic-gate /*
2140Sstevel@tonic-gate  * WARNING: as for above ISO sets, \377 may be used.  Translate it to
2150Sstevel@tonic-gate  * itself.
2160Sstevel@tonic-gate  */
2170Sstevel@tonic-gate /* 370 */	0370,	0371,	0372,	0373,	0374,	0375,	0376,	0377,
2180Sstevel@tonic-gate };
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate /*
2210Sstevel@tonic-gate  * Translation table for output with OLCUC.  All ORDINARY-class characters
2220Sstevel@tonic-gate  * translate to themselves, except for lower-case letters which translate
2230Sstevel@tonic-gate  * to their upper-case equivalents.  All other characters have a zero in
2240Sstevel@tonic-gate  * the table, which stops the copying.
2250Sstevel@tonic-gate  */
2260Sstevel@tonic-gate static unsigned char lcuctab[256] = {
2270Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
2280Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
2290Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
2300Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
2310Sstevel@tonic-gate /* 040 */	' ',	'!',	'"',	'#',	'$',	'%',	'&',	'\'',
2320Sstevel@tonic-gate /* 050 */	'(',	')',	'*',	'+',	',',	'-',	'.',	'/',
2330Sstevel@tonic-gate /* 060 */	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
2340Sstevel@tonic-gate /* 070 */	'8',	'9',	':',	';',	'<',	'=',	'>',	'?',
2350Sstevel@tonic-gate /* 100 */	'@',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
2360Sstevel@tonic-gate /* 110 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
2370Sstevel@tonic-gate /* 120 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
2380Sstevel@tonic-gate /* 130 */	'X',	'Y',	'Z',	'[',	'\\',	']',	'^',	'_',
2390Sstevel@tonic-gate /* 140 */	'`',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
2400Sstevel@tonic-gate /* 150 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
2410Sstevel@tonic-gate /* 160 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
2420Sstevel@tonic-gate /* 170 */	'X',	'Y',	'Z',	'{',	'|',	'}',	'~',	0,
2430Sstevel@tonic-gate /* 200 */	0,	0,	0,	0,	0,	0,	0,	0,
2440Sstevel@tonic-gate /* 210 */	0,	0,	0,	0,	0,	0,	0,	0,
2450Sstevel@tonic-gate /* 220 */	0,	0,	0,	0,	0,	0,	0,	0,
2460Sstevel@tonic-gate /* 230 */	0,	0,	0,	0,	0,	0,	0,	0,
2470Sstevel@tonic-gate /* 240 */	0240,	0241,	0242,	0243,	0244,	0245,	0246,	0247,
2480Sstevel@tonic-gate /* 250 */	0250,	0251,	0252,	0253,	0254,	0255,	0256,	0257,
2490Sstevel@tonic-gate /* 260 */	0260,	0261,	0262,	0263,	0264,	0265,	0266,	0267,
2500Sstevel@tonic-gate /* 270 */	0270,	0271,	0272,	0273,	0274,	0275,	0276,	0277,
2510Sstevel@tonic-gate /* 300 */	0300,	0301,	0302,	0303,	0304,	0305,	0306,	0307,
2520Sstevel@tonic-gate /* 310 */	0310,	0311,	0312,	0313,	0314,	0315,	0316,	0317,
2530Sstevel@tonic-gate /* 320 */	0320,	0321,	0322,	0323,	0324,	0325,	0326,	0327,
2540Sstevel@tonic-gate /* 330 */	0330,	0331,	0332,	0333,	0334,	0335,	0336,	0337,
2550Sstevel@tonic-gate /* 340 */	0340,	0341,	0342,	0343,	0344,	0345,	0346,	0347,
2560Sstevel@tonic-gate /* 350 */	0350,	0351,	0352,	0353,	0354,	0355,	0356,	0357,
2570Sstevel@tonic-gate /* 360 */	0360,	0361,	0362,	0363,	0364,	0365,	0366,	0367,
2580Sstevel@tonic-gate /*
2590Sstevel@tonic-gate  * WARNING: as for above ISO sets, \377 may be used.  Translate it to
2600Sstevel@tonic-gate  * itself.
2610Sstevel@tonic-gate  */
2620Sstevel@tonic-gate /* 370 */	0370,	0371,	0372,	0373,	0374,	0375,	0376,	0377,
2630Sstevel@tonic-gate };
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate  * Input mapping table -- if an entry is non-zero, and XCASE is set,
2670Sstevel@tonic-gate  * when the corresponding character is typed preceded by "\" the escape
2680Sstevel@tonic-gate  * sequence is replaced by the table value.  Mostly used for
2690Sstevel@tonic-gate  * upper-case only terminals.
2700Sstevel@tonic-gate  */
2710Sstevel@tonic-gate static char	imaptab[256] = {
2720Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
2730Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
2740Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
2750Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
2760Sstevel@tonic-gate /* 040 */	0,	'|',	0,	0,	0,	0,	0,	'`',
2770Sstevel@tonic-gate /* 050 */	'{',	'}',	0,	0,	0,	0,	0,	0,
2780Sstevel@tonic-gate /* 060 */	0,	0,	0,	0,	0,	0,	0,	0,
2790Sstevel@tonic-gate /* 070 */	0,	0,	0,	0,	0,	0,	0,	0,
2800Sstevel@tonic-gate /* 100 */	0,	0,	0,	0,	0,	0,	0,	0,
2810Sstevel@tonic-gate /* 110 */	0,	0,	0,	0,	0,	0,	0,	0,
2820Sstevel@tonic-gate /* 120 */	0,	0,	0,	0,	0,	0,	0,	0,
2830Sstevel@tonic-gate /* 130 */	0,	0,	0,	0,	'\\',	0,	'~',	0,
2840Sstevel@tonic-gate /* 140 */	0,	'A',	'B',	'C',	'D',	'E',	'F',	'G',
2850Sstevel@tonic-gate /* 150 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
2860Sstevel@tonic-gate /* 160 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
2870Sstevel@tonic-gate /* 170 */	'X',	'Y',	'Z',	0,	0,	0,	0,	0,
2880Sstevel@tonic-gate /* 200-377 aren't mapped */
2890Sstevel@tonic-gate };
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate  * Output mapping table -- if an entry is non-zero, and XCASE is set,
2930Sstevel@tonic-gate  * the corresponding character is printed as "\" followed by the table
2940Sstevel@tonic-gate  * value.  Mostly used for upper-case only terminals.
2950Sstevel@tonic-gate  */
2960Sstevel@tonic-gate static char	omaptab[256] = {
2970Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
2980Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
2990Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
3000Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
3010Sstevel@tonic-gate /* 040 */	0,	0,	0,	0,	0,	0,	0,	0,
3020Sstevel@tonic-gate /* 050 */	0,	0,	0,	0,	0,	0,	0,	0,
3030Sstevel@tonic-gate /* 060 */	0,	0,	0,	0,	0,	0,	0,	0,
3040Sstevel@tonic-gate /* 070 */	0,	0,	0,	0,	0,	0,	0,	0,
3050Sstevel@tonic-gate /* 100 */	0,	'A',	'B',	'C',	'D',	'E',	'F',	'G',
3060Sstevel@tonic-gate /* 110 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
3070Sstevel@tonic-gate /* 120 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
3080Sstevel@tonic-gate /* 130 */	'X',	'Y',	'Z',	0,	0,	0,	0,	0,
3090Sstevel@tonic-gate /* 140 */	'\'',	0,	0,	0,	0,	0,	0,	0,
3100Sstevel@tonic-gate /* 150 */	0,	0,	0,	0,	0,	0,	0,	0,
3110Sstevel@tonic-gate /* 160 */	0,	0,	0,	0,	0,	0,	0,	0,
3120Sstevel@tonic-gate /* 170 */	0,	0,	0,	'(',	'!',	')',	'^',	0,
3130Sstevel@tonic-gate /* 200-377 aren't mapped */
3140Sstevel@tonic-gate };
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate /*
3170Sstevel@tonic-gate  * Translation table for TS_MEUC output without OLCUC.  All printing ASCII
3180Sstevel@tonic-gate  * characters translate to themselves.  All other _bytes_ have a zero in
3190Sstevel@tonic-gate  * the table, which stops the copying.  This and the following table exist
3200Sstevel@tonic-gate  * only so we can use the existing movtuc processing with or without OLCUC.
3210Sstevel@tonic-gate  * Maybe it speeds up something...because we can copy a block of characters
3220Sstevel@tonic-gate  * by only looking for zeros in the table.
3230Sstevel@tonic-gate  *
3240Sstevel@tonic-gate  * If we took the simple expedient of DISALLOWING "olcuc" with multi-byte
3250Sstevel@tonic-gate  * processing, we could rid ourselves of both these tables and save 512 bytes;
3260Sstevel@tonic-gate  * seriously, it doesn't make much sense to use olcuc with multi-byte, and
3270Sstevel@tonic-gate  * it will probably never be used.  Consideration should be given to disallowing
3280Sstevel@tonic-gate  * the combination TS_MEUC & OLCUC.
3290Sstevel@tonic-gate  */
3300Sstevel@tonic-gate static unsigned char enotrantab[256] = {
3310Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
3320Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
3330Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
3340Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
3350Sstevel@tonic-gate /* 040 */	' ',	'!',	'"',	'#',	'$',	'%',	'&',	'\'',
3360Sstevel@tonic-gate /* 050 */	'(',	')',	'*',	'+',	',',	'-',	'.',	'/',
3370Sstevel@tonic-gate /* 060 */	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
3380Sstevel@tonic-gate /* 070 */	'8',	'9',	':',	';',	'<',	'=',	'>',	'?',
3390Sstevel@tonic-gate /* 100 */	'@',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
3400Sstevel@tonic-gate /* 110 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
3410Sstevel@tonic-gate /* 120 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
3420Sstevel@tonic-gate /* 130 */	'X',	'Y',	'Z',	'[',	'\\',	']',	'^',	'_',
3430Sstevel@tonic-gate /* 140 */	'`',	'a',	'b',	'c',	'd',	'e',	'f',	'g',
3440Sstevel@tonic-gate /* 150 */	'h',	'i',	'j',	'k',	'l',	'm',	'n',	'o',
3450Sstevel@tonic-gate /* 160 */	'p',	'q',	'r',	's',	't',	'u',	'v',	'w',
3460Sstevel@tonic-gate /* 170 */	'x',	'y',	'z',	'{',	'|',	'}',	'~',	0,
3470Sstevel@tonic-gate /* 200 - 377 aren't mapped (they're stoppers). */
3480Sstevel@tonic-gate };
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate /*
3510Sstevel@tonic-gate  * Translation table for TS_MEUC output with OLCUC.  All printing ASCII
3520Sstevel@tonic-gate  * translate to themselves, except for lower-case letters which translate
3530Sstevel@tonic-gate  * to their upper-case equivalents.  All other bytes have a zero in
3540Sstevel@tonic-gate  * the table, which stops the copying.  Useless for ISO Latin Alphabet
3550Sstevel@tonic-gate  * translations, but *sigh* OLCUC is really only defined for ASCII anyway.
3560Sstevel@tonic-gate  * We only have this table so we can use the existing OLCUC processing with
3570Sstevel@tonic-gate  * TS_MEUC set (multi-byte mode).  Nobody would ever think of actually
3580Sstevel@tonic-gate  * _using_ it...would they?
3590Sstevel@tonic-gate  */
3600Sstevel@tonic-gate static unsigned char elcuctab[256] = {
3610Sstevel@tonic-gate /* 000 */	0,	0,	0,	0,	0,	0,	0,	0,
3620Sstevel@tonic-gate /* 010 */	0,	0,	0,	0,	0,	0,	0,	0,
3630Sstevel@tonic-gate /* 020 */	0,	0,	0,	0,	0,	0,	0,	0,
3640Sstevel@tonic-gate /* 030 */	0,	0,	0,	0,	0,	0,	0,	0,
3650Sstevel@tonic-gate /* 040 */	' ',	'!',	'"',	'#',	'$',	'%',	'&',	'\'',
3660Sstevel@tonic-gate /* 050 */	'(',	')',	'*',	'+',	',',	'-',	'.',	'/',
3670Sstevel@tonic-gate /* 060 */	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
3680Sstevel@tonic-gate /* 070 */	'8',	'9',	':',	';',	'<',	'=',	'>',	'?',
3690Sstevel@tonic-gate /* 100 */	'@',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
3700Sstevel@tonic-gate /* 110 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
3710Sstevel@tonic-gate /* 120 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
3720Sstevel@tonic-gate /* 130 */	'X',	'Y',	'Z',	'[',	'\\',	']',	'^',	'_',
3730Sstevel@tonic-gate /* 140 */	'`',	'A',	'B',	'C',	'D',	'E',	'F',	'G',
3740Sstevel@tonic-gate /* 150 */	'H',	'I',	'J',	'K',	'L',	'M',	'N',	'O',
3750Sstevel@tonic-gate /* 160 */	'P',	'Q',	'R',	'S',	'T',	'U',	'V',	'W',
3760Sstevel@tonic-gate /* 170 */	'X',	'Y',	'Z',	'{',	'|',	'}',	'~',	0,
3770Sstevel@tonic-gate /* 200 - 377 aren't mapped (they're stoppers). */
3780Sstevel@tonic-gate };
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate static struct streamtab ldtrinfo;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate static struct fmodsw fsw = {
3830Sstevel@tonic-gate 	"ldterm",
3840Sstevel@tonic-gate 	&ldtrinfo,
3850Sstevel@tonic-gate 	D_MTQPAIR | D_MP
3860Sstevel@tonic-gate };
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate static struct modlstrmod modlstrmod = {
3890Sstevel@tonic-gate 	&mod_strmodops, "terminal line discipline", &fsw
3900Sstevel@tonic-gate };
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate static struct modlinkage modlinkage = {
3940Sstevel@tonic-gate 	MODREV_1, &modlstrmod, NULL
3950Sstevel@tonic-gate };
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate int
_init(void)3990Sstevel@tonic-gate _init(void)
4000Sstevel@tonic-gate {
4010Sstevel@tonic-gate 	return (mod_install(&modlinkage));
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate int
_fini(void)4050Sstevel@tonic-gate _fini(void)
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate int
_info(struct modinfo * modinfop)4110Sstevel@tonic-gate _info(struct modinfo *modinfop)
4120Sstevel@tonic-gate {
4130Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate static int	ldtermopen(queue_t *, dev_t *, int, int, cred_t *);
4180Sstevel@tonic-gate static int	ldtermclose(queue_t *, int, cred_t *);
4190Sstevel@tonic-gate static void	ldtermrput(queue_t *, mblk_t *);
4200Sstevel@tonic-gate static void	ldtermrsrv(queue_t *);
4210Sstevel@tonic-gate static int	ldtermrmsg(queue_t *, mblk_t *);
4220Sstevel@tonic-gate static void	ldtermwput(queue_t *, mblk_t *);
4230Sstevel@tonic-gate static void	ldtermwsrv(queue_t *);
4240Sstevel@tonic-gate static int	ldtermwmsg(queue_t *, mblk_t *);
4250Sstevel@tonic-gate static mblk_t	*ldterm_docanon(unsigned char, mblk_t *, size_t, queue_t *,
4260Sstevel@tonic-gate 				ldtermstd_state_t *, int *);
4270Sstevel@tonic-gate static int	ldterm_unget(ldtermstd_state_t *);
4280Sstevel@tonic-gate static void	ldterm_trim(ldtermstd_state_t *);
4290Sstevel@tonic-gate static void	ldterm_rubout(unsigned char, queue_t *, size_t,
4300Sstevel@tonic-gate 				ldtermstd_state_t *);
4310Sstevel@tonic-gate static int	ldterm_tabcols(ldtermstd_state_t *);
4320Sstevel@tonic-gate static void	ldterm_erase(queue_t *, size_t, ldtermstd_state_t *);
4330Sstevel@tonic-gate static void	ldterm_werase(queue_t *, size_t, ldtermstd_state_t *);
4340Sstevel@tonic-gate static void	ldterm_kill(queue_t *, size_t, ldtermstd_state_t *);
4350Sstevel@tonic-gate static void	ldterm_reprint(queue_t *, size_t, ldtermstd_state_t *);
4360Sstevel@tonic-gate static mblk_t	*ldterm_dononcanon(mblk_t *, mblk_t *, size_t, queue_t *,
4370Sstevel@tonic-gate 					ldtermstd_state_t *);
4380Sstevel@tonic-gate static int	ldterm_echo(unsigned char, queue_t *, size_t,
4390Sstevel@tonic-gate 				ldtermstd_state_t *);
4400Sstevel@tonic-gate static void	ldterm_outchar(unsigned char, queue_t *, size_t,
4410Sstevel@tonic-gate 				ldtermstd_state_t *);
4420Sstevel@tonic-gate static void	ldterm_outstring(unsigned char *, int, queue_t *, size_t,
4430Sstevel@tonic-gate 					ldtermstd_state_t *tp);
4440Sstevel@tonic-gate static mblk_t	*newmsg(ldtermstd_state_t *);
4450Sstevel@tonic-gate static void	ldterm_msg_upstream(queue_t *, ldtermstd_state_t *);
4460Sstevel@tonic-gate static void	ldterm_wenable(void *);
4470Sstevel@tonic-gate static mblk_t	*ldterm_output_msg(queue_t *, mblk_t *, mblk_t **,
4480Sstevel@tonic-gate 				ldtermstd_state_t *, size_t, int);
4490Sstevel@tonic-gate static void	ldterm_flush_output(unsigned char, queue_t *,
4500Sstevel@tonic-gate 					ldtermstd_state_t *);
4510Sstevel@tonic-gate static void	ldterm_dosig(queue_t *, int, unsigned char, int, int);
4520Sstevel@tonic-gate static void	ldterm_do_ioctl(queue_t *, mblk_t *);
4530Sstevel@tonic-gate static int	chgstropts(struct termios *, ldtermstd_state_t *, queue_t *);
4540Sstevel@tonic-gate static void	ldterm_ioctl_reply(queue_t *, mblk_t *);
4550Sstevel@tonic-gate static void	vmin_satisfied(queue_t *, ldtermstd_state_t *, int);
4560Sstevel@tonic-gate static void	vmin_settimer(queue_t *);
4570Sstevel@tonic-gate static void	vmin_timed_out(void *);
4580Sstevel@tonic-gate static void	ldterm_adjust_modes(ldtermstd_state_t *);
4590Sstevel@tonic-gate static void	ldterm_eucwarn(ldtermstd_state_t *);
4600Sstevel@tonic-gate static void	cp_eucwioc(eucioc_t *, eucioc_t *, int);
4610Sstevel@tonic-gate static int	ldterm_codeset(uchar_t, uchar_t);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate static void	ldterm_csi_erase(queue_t *, size_t, ldtermstd_state_t *);
4640Sstevel@tonic-gate static void	ldterm_csi_werase(queue_t *, size_t, ldtermstd_state_t *);
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate static uchar_t	ldterm_utf8_width(uchar_t *, int);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate /* Codeset type specific methods for EUC, PCCS, and, UTF-8 codeset types. */
4690Sstevel@tonic-gate static int	__ldterm_dispwidth_euc(uchar_t, void *, int);
4700Sstevel@tonic-gate static int	__ldterm_memwidth_euc(uchar_t, void *);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate static int	__ldterm_dispwidth_pccs(uchar_t, void *, int);
4730Sstevel@tonic-gate static int	__ldterm_memwidth_pccs(uchar_t, void *);
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate static int	__ldterm_dispwidth_utf8(uchar_t, void *, int);
4760Sstevel@tonic-gate static int	__ldterm_memwidth_utf8(uchar_t, void *);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate static const ldterm_cs_methods_t cs_methods[LDTERM_CS_TYPE_MAX + 1] = {
4790Sstevel@tonic-gate 	{
4800Sstevel@tonic-gate 		NULL,
4810Sstevel@tonic-gate 		NULL
4820Sstevel@tonic-gate 	},
4830Sstevel@tonic-gate 	{
4840Sstevel@tonic-gate 		__ldterm_dispwidth_euc,
4850Sstevel@tonic-gate 		__ldterm_memwidth_euc
4860Sstevel@tonic-gate 	},
4870Sstevel@tonic-gate 	{
4880Sstevel@tonic-gate 		__ldterm_dispwidth_pccs,
4890Sstevel@tonic-gate 		__ldterm_memwidth_pccs
4900Sstevel@tonic-gate 	},
4910Sstevel@tonic-gate 	{
4920Sstevel@tonic-gate 		__ldterm_dispwidth_utf8,
4930Sstevel@tonic-gate 		__ldterm_memwidth_utf8
4940Sstevel@tonic-gate 	}
4950Sstevel@tonic-gate };
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate /*
4980Sstevel@tonic-gate  * The default codeset is presumably C locale's ISO 646 in EUC but
4990Sstevel@tonic-gate  * the data structure at below defined as the default codeset data also
5000Sstevel@tonic-gate  * support any single byte (EUC) locales.
5010Sstevel@tonic-gate  */
5020Sstevel@tonic-gate static const ldterm_cs_data_t default_cs_data = {
5030Sstevel@tonic-gate 	LDTERM_DATA_VERSION,
5040Sstevel@tonic-gate 	LDTERM_CS_TYPE_EUC,
5050Sstevel@tonic-gate 	(uchar_t)0,
5060Sstevel@tonic-gate 	(uchar_t)0,
5070Sstevel@tonic-gate 	(char *)NULL,
5080Sstevel@tonic-gate 	{
5090Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5100Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5110Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5120Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5130Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5140Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5150Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5160Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5170Sstevel@tonic-gate 		'\0', '\0', '\0', '\0',
5180Sstevel@tonic-gate 		'\0', '\0', '\0', '\0'
5190Sstevel@tonic-gate 	}
5200Sstevel@tonic-gate };
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate /*
523*7012Sis  * The following tables are from either u8_textprep.c or uconv.c at
524*7012Sis  * usr/src/common/unicode/. The tables are used to figure out corresponding
525*7012Sis  * UTF-8 character byte lengths and also the validity of given character bytes.
5260Sstevel@tonic-gate  */
527*7012Sis extern const int8_t u8_number_of_bytes[];
528*7012Sis extern const uchar_t u8_masks_tbl[];
529*7012Sis extern const uint8_t u8_valid_min_2nd_byte[];
530*7012Sis extern const uint8_t u8_valid_max_2nd_byte[];
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate  * Unicode character width definition tables from uwidth.c:
5340Sstevel@tonic-gate  */
5350Sstevel@tonic-gate extern const ldterm_unicode_data_cell_t ldterm_ucode[][16384];
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate #ifdef LDDEBUG
5380Sstevel@tonic-gate int	ldterm_debug = 0;
5390Sstevel@tonic-gate #define	DEBUG1(a)	if (ldterm_debug == 1) printf a
5400Sstevel@tonic-gate #define	DEBUG2(a)	if (ldterm_debug >= 2) printf a	/* allocations */
5410Sstevel@tonic-gate #define	DEBUG3(a)	if (ldterm_debug >= 3) printf a	/* M_CTL Stuff */
5420Sstevel@tonic-gate #define	DEBUG4(a)	if (ldterm_debug >= 4) printf a	/* M_READ Stuff */
5430Sstevel@tonic-gate #define	DEBUG5(a)	if (ldterm_debug >= 5) printf a
5440Sstevel@tonic-gate #define	DEBUG6(a)	if (ldterm_debug >= 6) printf a
5450Sstevel@tonic-gate #define	DEBUG7(a)	if (ldterm_debug >= 7) printf a
5460Sstevel@tonic-gate #else
5470Sstevel@tonic-gate #define	DEBUG1(a)
5480Sstevel@tonic-gate #define	DEBUG2(a)
5490Sstevel@tonic-gate #define	DEBUG3(a)
5500Sstevel@tonic-gate #define	DEBUG4(a)
5510Sstevel@tonic-gate #define	DEBUG5(a)
5520Sstevel@tonic-gate #define	DEBUG6(a)
5530Sstevel@tonic-gate #define	DEBUG7(a)
5540Sstevel@tonic-gate #endif		/* LDDEBUG */
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate  * Since most of the buffering occurs either at the stream head or in
5590Sstevel@tonic-gate  * the "message currently being assembled" buffer, we have a
5600Sstevel@tonic-gate  * relatively small input queue, so that blockages above us get
5610Sstevel@tonic-gate  * reflected fairly quickly to the module below us.  We also have a
5620Sstevel@tonic-gate  * small maximum packet size, since you can put a message of that
5630Sstevel@tonic-gate  * size on an empty queue no matter how much bigger than the high
5640Sstevel@tonic-gate  * water mark it is.
5650Sstevel@tonic-gate  */
5660Sstevel@tonic-gate static struct module_info ldtermmiinfo = {
5670Sstevel@tonic-gate 	0x0bad,
5680Sstevel@tonic-gate 	"ldterm",
5690Sstevel@tonic-gate 	0,
5700Sstevel@tonic-gate 	256,
5710Sstevel@tonic-gate 	HIWAT,
5720Sstevel@tonic-gate 	LOWAT
5730Sstevel@tonic-gate };
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate static struct qinit ldtermrinit = {
5770Sstevel@tonic-gate 	(int (*)())ldtermrput,
5780Sstevel@tonic-gate 	(int (*)())ldtermrsrv,
5790Sstevel@tonic-gate 	ldtermopen,
5800Sstevel@tonic-gate 	ldtermclose,
5810Sstevel@tonic-gate 	NULL,
5820Sstevel@tonic-gate 	&ldtermmiinfo
5830Sstevel@tonic-gate };
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate static struct module_info ldtermmoinfo = {
5870Sstevel@tonic-gate 	0x0bad,
5880Sstevel@tonic-gate 	"ldterm",
5890Sstevel@tonic-gate 	0,
5900Sstevel@tonic-gate 	INFPSZ,
5910Sstevel@tonic-gate 	1,
5920Sstevel@tonic-gate 	0
5930Sstevel@tonic-gate };
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate static struct qinit ldtermwinit = {
5970Sstevel@tonic-gate 	(int (*)())ldtermwput,
5980Sstevel@tonic-gate 	(int (*)())ldtermwsrv,
5990Sstevel@tonic-gate 	ldtermopen,
6000Sstevel@tonic-gate 	ldtermclose,
6010Sstevel@tonic-gate 	NULL,
6020Sstevel@tonic-gate 	&ldtermmoinfo
6030Sstevel@tonic-gate };
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate static struct streamtab ldtrinfo = {
6070Sstevel@tonic-gate 	&ldtermrinit,
6080Sstevel@tonic-gate 	&ldtermwinit,
6090Sstevel@tonic-gate 	NULL,
6100Sstevel@tonic-gate 	NULL
6110Sstevel@tonic-gate };
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate /*
6140Sstevel@tonic-gate  * Dummy qbufcall callback routine used by open and close.
6150Sstevel@tonic-gate  * The framework will wake up qwait_sig when we return from
6160Sstevel@tonic-gate  * this routine (as part of leaving the perimeters.)
6170Sstevel@tonic-gate  * (The framework enters the perimeters before calling the qbufcall() callback
6180Sstevel@tonic-gate  * and leaves the perimeters after the callback routine has executed. The
6190Sstevel@tonic-gate  * framework performs an implicit wakeup of any thread in qwait/qwait_sig
6200Sstevel@tonic-gate  * when it leaves the perimeter. See qwait(9E).)
6210Sstevel@tonic-gate  */
6220Sstevel@tonic-gate /* ARGSUSED */
6230Sstevel@tonic-gate static void
dummy_callback(void * arg)6240Sstevel@tonic-gate dummy_callback(void *arg)
6250Sstevel@tonic-gate {}
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate static mblk_t *
open_ioctl(queue_t * q,uint_t cmd)6290Sstevel@tonic-gate open_ioctl(queue_t *q, uint_t cmd)
6300Sstevel@tonic-gate {
6310Sstevel@tonic-gate 	mblk_t *mp;
6320Sstevel@tonic-gate 	bufcall_id_t id;
6330Sstevel@tonic-gate 	int retv;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	while ((mp = mkiocb(cmd)) == NULL) {
6360Sstevel@tonic-gate 		id = qbufcall(q, sizeof (struct iocblk), BPRI_MED,
6370Sstevel@tonic-gate 		    dummy_callback, NULL);
6380Sstevel@tonic-gate 		retv = qwait_sig(q);
6390Sstevel@tonic-gate 		qunbufcall(q, id);
6400Sstevel@tonic-gate 		if (retv == 0)
6410Sstevel@tonic-gate 			break;
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate 	return (mp);
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate static mblk_t *
open_mblk(queue_t * q,size_t len)6470Sstevel@tonic-gate open_mblk(queue_t *q, size_t len)
6480Sstevel@tonic-gate {
6490Sstevel@tonic-gate 	mblk_t *mp;
6500Sstevel@tonic-gate 	bufcall_id_t id;
6510Sstevel@tonic-gate 	int retv;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	while ((mp = allocb(len, BPRI_MED)) == NULL) {
6540Sstevel@tonic-gate 		id = qbufcall(q, len, BPRI_MED, dummy_callback, NULL);
6550Sstevel@tonic-gate 		retv = qwait_sig(q);
6560Sstevel@tonic-gate 		qunbufcall(q, id);
6570Sstevel@tonic-gate 		if (retv == 0)
6580Sstevel@tonic-gate 			break;
6590Sstevel@tonic-gate 	}
6600Sstevel@tonic-gate 	return (mp);
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate /*
6640Sstevel@tonic-gate  * Line discipline open.
6650Sstevel@tonic-gate  */
6660Sstevel@tonic-gate /* ARGSUSED1 */
6670Sstevel@tonic-gate static int
ldtermopen(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * crp)6680Sstevel@tonic-gate ldtermopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
6690Sstevel@tonic-gate {
6700Sstevel@tonic-gate 	ldtermstd_state_t *tp;
6710Sstevel@tonic-gate 	mblk_t *bp, *qryp;
6720Sstevel@tonic-gate 	int len;
6730Sstevel@tonic-gate 	struct stroptions *strop;
6740Sstevel@tonic-gate 	struct termios *termiosp;
6750Sstevel@tonic-gate 	queue_t *wq;
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	if (q->q_ptr != NULL) {
6780Sstevel@tonic-gate 		return (0);	/* already attached */
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)kmem_zalloc(sizeof (ldtermstd_state_t),
682*7012Sis 	    KM_SLEEP);
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 	/*
6850Sstevel@tonic-gate 	 * Get termios defaults.  These are stored as
6860Sstevel@tonic-gate 	 * a property in the "options" node.
6870Sstevel@tonic-gate 	 */
6880Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_NOTPROM,
6890Sstevel@tonic-gate 	    "ttymodes", (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
6900Sstevel@tonic-gate 	    len == sizeof (struct termios)) {
6910Sstevel@tonic-gate 		tp->t_modes = *termiosp;
6920Sstevel@tonic-gate 		tp->t_amodes = *termiosp;
6930Sstevel@tonic-gate 		kmem_free(termiosp, len);
6940Sstevel@tonic-gate 	} else {
6950Sstevel@tonic-gate 		/*
6960Sstevel@tonic-gate 		 * Gack!  Whine about it.
6970Sstevel@tonic-gate 		 */
6980Sstevel@tonic-gate 		cmn_err(CE_WARN, "ldterm: Couldn't get ttymodes property!");
6990Sstevel@tonic-gate 	}
7000Sstevel@tonic-gate 	bzero(&tp->t_dmodes, sizeof (struct termios));
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	tp->t_state = 0;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	tp->t_line = 0;
7050Sstevel@tonic-gate 	tp->t_col = 0;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	tp->t_rocount = 0;
7080Sstevel@tonic-gate 	tp->t_rocol = 0;
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	tp->t_message = NULL;
7110Sstevel@tonic-gate 	tp->t_endmsg = NULL;
7120Sstevel@tonic-gate 	tp->t_msglen = 0;
7130Sstevel@tonic-gate 	tp->t_rd_request = 0;
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	tp->t_echomp = NULL;
7160Sstevel@tonic-gate 	tp->t_iocid = 0;
7170Sstevel@tonic-gate 	tp->t_wbufcid = 0;
7180Sstevel@tonic-gate 	tp->t_vtid = 0;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	q->q_ptr = (caddr_t)tp;
7210Sstevel@tonic-gate 	WR(q)->q_ptr = (caddr_t)tp;
7220Sstevel@tonic-gate 	/*
7230Sstevel@tonic-gate 	 * The following for EUC and also non-EUC codesets:
7240Sstevel@tonic-gate 	 */
7250Sstevel@tonic-gate 	tp->t_codeset = tp->t_eucleft = tp->t_eucign = tp->t_scratch_len = 0;
7260Sstevel@tonic-gate 	bzero(&tp->eucwioc, EUCSIZE);
7270Sstevel@tonic-gate 	tp->eucwioc.eucw[0] = 1;	/* ASCII mem & screen width */
7280Sstevel@tonic-gate 	tp->eucwioc.scrw[0] = 1;
7290Sstevel@tonic-gate 	tp->t_maxeuc = 1;	/* the max len in bytes of an EUC char */
7300Sstevel@tonic-gate 	tp->t_eucp = NULL;
7310Sstevel@tonic-gate 	tp->t_eucp_mp = NULL;
7320Sstevel@tonic-gate 	tp->t_eucwarn = 0;	/* no bad chars seen yet */
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	tp->t_csdata = default_cs_data;
7350Sstevel@tonic-gate 	tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	qprocson(q);
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	/*
7400Sstevel@tonic-gate 	 * Find out if the module below us does canonicalization; if
7410Sstevel@tonic-gate 	 * so, we won't do it ourselves.
7420Sstevel@tonic-gate 	 */
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if ((qryp = open_ioctl(q, MC_CANONQUERY)) == NULL)
7450Sstevel@tonic-gate 		goto open_abort;
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	/*
7480Sstevel@tonic-gate 	 * Reformulate as an M_CTL message. The actual data will
7490Sstevel@tonic-gate 	 * be in the b_cont field.
7500Sstevel@tonic-gate 	 */
7510Sstevel@tonic-gate 	qryp->b_datap->db_type = M_CTL;
7520Sstevel@tonic-gate 	wq = OTHERQ(q);
7530Sstevel@tonic-gate 	putnext(wq, qryp);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	/* allocate a TCSBRK ioctl in case we'll need it on close */
7560Sstevel@tonic-gate 	if ((qryp = open_ioctl(q, TCSBRK)) == NULL)
7570Sstevel@tonic-gate 		goto open_abort;
7580Sstevel@tonic-gate 	tp->t_drainmsg = qryp;
7590Sstevel@tonic-gate 	if ((bp = open_mblk(q, sizeof (int))) == NULL)
7600Sstevel@tonic-gate 		goto open_abort;
7610Sstevel@tonic-gate 	qryp->b_cont = bp;
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	/*
7640Sstevel@tonic-gate 	 * Find out if the underlying driver supports proper POSIX close
7650Sstevel@tonic-gate 	 * semantics.  If not, we'll have to approximate it using TCSBRK.  If
7660Sstevel@tonic-gate 	 * it does, it will respond with MC_HAS_POSIX, and we'll catch that in
7670Sstevel@tonic-gate 	 * the ldtermrput routine.
7680Sstevel@tonic-gate 	 *
7690Sstevel@tonic-gate 	 * When the ldterm_drain_limit tunable is set to zero, we behave the
7700Sstevel@tonic-gate 	 * same as old ldterm: don't send this new message, and always use
7710Sstevel@tonic-gate 	 * TCSBRK during close.
7720Sstevel@tonic-gate 	 */
7730Sstevel@tonic-gate 	if (ldterm_drain_limit != 0) {
7740Sstevel@tonic-gate 		if ((qryp = open_ioctl(q, MC_POSIXQUERY)) == NULL)
7750Sstevel@tonic-gate 			goto open_abort;
7760Sstevel@tonic-gate 		qryp->b_datap->db_type = M_CTL;
7770Sstevel@tonic-gate 		putnext(wq, qryp);
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 	/* prepare to clear the water marks on close */
7810Sstevel@tonic-gate 	if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
7820Sstevel@tonic-gate 		goto open_abort;
7830Sstevel@tonic-gate 	tp->t_closeopts = bp;
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	/*
7860Sstevel@tonic-gate 	 * Set the high-water and low-water marks on the stream head
7870Sstevel@tonic-gate 	 * to values appropriate for a terminal.  Also set the "vmin"
7880Sstevel@tonic-gate 	 * and "vtime" values to 1 and 0, turn on message-nondiscard
7890Sstevel@tonic-gate 	 * mode (as we're in ICANON mode), and turn on "old-style
7900Sstevel@tonic-gate 	 * NODELAY" mode.
7910Sstevel@tonic-gate 	 */
7920Sstevel@tonic-gate 	if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
7930Sstevel@tonic-gate 		goto open_abort;
7940Sstevel@tonic-gate 	strop = (struct stroptions *)bp->b_wptr;
7950Sstevel@tonic-gate 	strop->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT|SO_NDELON|SO_ISTTY;
7960Sstevel@tonic-gate 	strop->so_readopt = RMSGN;
7970Sstevel@tonic-gate 	strop->so_hiwat = HIWAT;
7980Sstevel@tonic-gate 	strop->so_lowat = LOWAT;
7990Sstevel@tonic-gate 	bp->b_wptr += sizeof (struct stroptions);
8000Sstevel@tonic-gate 	bp->b_datap->db_type = M_SETOPTS;
8010Sstevel@tonic-gate 	putnext(q, bp);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	return (0);		/* this can become a controlling TTY */
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate open_abort:
8060Sstevel@tonic-gate 	qprocsoff(q);
8070Sstevel@tonic-gate 	q->q_ptr = NULL;
8080Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
8090Sstevel@tonic-gate 	freemsg(tp->t_closeopts);
8100Sstevel@tonic-gate 	freemsg(tp->t_drainmsg);
8110Sstevel@tonic-gate 	/* Dump the state structure */
8120Sstevel@tonic-gate 	kmem_free(tp, sizeof (ldtermstd_state_t));
8130Sstevel@tonic-gate 	return (EINTR);
8140Sstevel@tonic-gate }
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate struct close_timer {
8170Sstevel@tonic-gate 	timeout_id_t id;
8180Sstevel@tonic-gate 	ldtermstd_state_t *tp;
8190Sstevel@tonic-gate };
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate static void
drain_timed_out(void * arg)8220Sstevel@tonic-gate drain_timed_out(void *arg)
8230Sstevel@tonic-gate {
8240Sstevel@tonic-gate 	struct close_timer *ctp = arg;
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	ctp->id = 0;
8270Sstevel@tonic-gate 	ctp->tp->t_state &= ~TS_IOCWAIT;
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate /* ARGSUSED2 */
8310Sstevel@tonic-gate static int
ldtermclose(queue_t * q,int cflag,cred_t * crp)8320Sstevel@tonic-gate ldtermclose(queue_t *q, int cflag, cred_t *crp)
8330Sstevel@tonic-gate {
8340Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
8350Sstevel@tonic-gate 	struct stroptions *strop;
8360Sstevel@tonic-gate 	mblk_t *bp;
8370Sstevel@tonic-gate 	struct close_timer cltimer;
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	/*
8400Sstevel@tonic-gate 	 * If we have an outstanding vmin timeout, cancel it.
8410Sstevel@tonic-gate 	 */
8420Sstevel@tonic-gate 	tp->t_state |= TS_CLOSE;
8430Sstevel@tonic-gate 	if (tp->t_vtid != 0)
8440Sstevel@tonic-gate 		(void) quntimeout(q, tp->t_vtid);
8450Sstevel@tonic-gate 	tp->t_vtid = 0;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	/*
8480Sstevel@tonic-gate 	 * Cancel outstanding qbufcall request.
8490Sstevel@tonic-gate 	 */
8500Sstevel@tonic-gate 	if (tp->t_wbufcid != 0)
8510Sstevel@tonic-gate 		qunbufcall(q, tp->t_wbufcid);
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	/*
8540Sstevel@tonic-gate 	 * Reset the high-water and low-water marks on the stream
8550Sstevel@tonic-gate 	 * head (?), turn on byte-stream mode, and turn off
8560Sstevel@tonic-gate 	 * "old-style NODELAY" mode.
8570Sstevel@tonic-gate 	 */
8580Sstevel@tonic-gate 	bp = tp->t_closeopts;
8590Sstevel@tonic-gate 	strop = (struct stroptions *)bp->b_wptr;
8600Sstevel@tonic-gate 	strop->so_flags = SO_READOPT|SO_NDELOFF;
8610Sstevel@tonic-gate 	strop->so_readopt = RNORM;
8620Sstevel@tonic-gate 	bp->b_wptr += sizeof (struct stroptions);
8630Sstevel@tonic-gate 	bp->b_datap->db_type = M_SETOPTS;
8640Sstevel@tonic-gate 	putnext(q, bp);
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	if (cflag & (FNDELAY|FNONBLOCK)) {
8670Sstevel@tonic-gate 		freemsg(tp->t_drainmsg);
8680Sstevel@tonic-gate 	} else if ((bp = tp->t_drainmsg) != NULL) {
8690Sstevel@tonic-gate 		struct iocblk *iocb;
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 		/*
8720Sstevel@tonic-gate 		 * If the driver isn't known to have POSIX close semantics,
8730Sstevel@tonic-gate 		 * then we have to emulate this the old way.  This is done by
8740Sstevel@tonic-gate 		 * sending down TCSBRK,1 to drain the output and waiting for
8750Sstevel@tonic-gate 		 * the reply.
8760Sstevel@tonic-gate 		 */
8770Sstevel@tonic-gate 		iocb = (struct iocblk *)bp->b_rptr;
8780Sstevel@tonic-gate 		iocb->ioc_count = sizeof (int);
8790Sstevel@tonic-gate 		*(int *)bp->b_cont->b_rptr = 1;
8800Sstevel@tonic-gate 		bp->b_cont->b_wptr += sizeof (int);
8810Sstevel@tonic-gate 		tp->t_state |= TS_IOCWAIT;
8820Sstevel@tonic-gate 		tp->t_iocid = iocb->ioc_id;
8830Sstevel@tonic-gate 		if (!putq(WR(q), bp))
8840Sstevel@tonic-gate 			putnext(WR(q), bp);
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 		/*
8870Sstevel@tonic-gate 		 * If we're not able to receive signals at this point, then
8880Sstevel@tonic-gate 		 * launch a timer.  This timer will prevent us from waiting
8890Sstevel@tonic-gate 		 * forever for a signal that won't arrive.
8900Sstevel@tonic-gate 		 */
8910Sstevel@tonic-gate 		cltimer.id = 0;
8920Sstevel@tonic-gate 		if (!ddi_can_receive_sig() && ldterm_drain_limit != 0) {
8930Sstevel@tonic-gate 			cltimer.tp = tp;
8940Sstevel@tonic-gate 			cltimer.id = qtimeout(q, drain_timed_out, &cltimer,
8950Sstevel@tonic-gate 			    drv_usectohz(ldterm_drain_limit));
8960Sstevel@tonic-gate 		}
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 		/*
8990Sstevel@tonic-gate 		 * Note that the read side of ldterm and the qtimeout are
9000Sstevel@tonic-gate 		 * protected by D_MTQPAIR, so no additional locking is needed
9010Sstevel@tonic-gate 		 * here.
9020Sstevel@tonic-gate 		 */
9030Sstevel@tonic-gate 		while (tp->t_state & TS_IOCWAIT) {
9040Sstevel@tonic-gate 			if (qwait_sig(q) == 0)
9050Sstevel@tonic-gate 				break;
9060Sstevel@tonic-gate 		}
9070Sstevel@tonic-gate 		if (cltimer.id != 0)
9080Sstevel@tonic-gate 			(void) quntimeout(q, cltimer.id);
9090Sstevel@tonic-gate 	}
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	/*
9120Sstevel@tonic-gate 	 * From here to the end, the routine does not sleep and does not
9130Sstevel@tonic-gate 	 * reference STREAMS, so it's guaranteed to run to completion.
9140Sstevel@tonic-gate 	 */
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	qprocsoff(q);
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	freemsg(tp->t_message);
9190Sstevel@tonic-gate 	freemsg(tp->t_eucp_mp);
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	/* Dump the state structure, then unlink it */
9220Sstevel@tonic-gate 	if (tp->t_csdata.locale_name != NULL)
9230Sstevel@tonic-gate 		kmem_free(tp->t_csdata.locale_name,
9240Sstevel@tonic-gate 		    strlen(tp->t_csdata.locale_name) + 1);
9250Sstevel@tonic-gate 	kmem_free(tp, sizeof (ldtermstd_state_t));
9260Sstevel@tonic-gate 	q->q_ptr = NULL;
9270Sstevel@tonic-gate 	return (0);
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate /*
9320Sstevel@tonic-gate  * Put procedure for input from driver end of stream (read queue).
9330Sstevel@tonic-gate  */
9340Sstevel@tonic-gate static void
ldtermrput(queue_t * q,mblk_t * mp)9350Sstevel@tonic-gate ldtermrput(queue_t *q, mblk_t *mp)
9360Sstevel@tonic-gate {
9370Sstevel@tonic-gate 	ldtermstd_state_t *tp;
9380Sstevel@tonic-gate 	unsigned char c;
9390Sstevel@tonic-gate 	queue_t *wrq = WR(q);		/* write queue of ldterm mod */
9400Sstevel@tonic-gate 	queue_t *nextq = q->q_next;	/* queue below us */
9410Sstevel@tonic-gate 	mblk_t *bp;
9420Sstevel@tonic-gate 	struct iocblk *qryp;
9430Sstevel@tonic-gate 	unsigned char *readp;
9440Sstevel@tonic-gate 	unsigned char *writep;
9450Sstevel@tonic-gate 	struct termios *emodes;		/* effective modes set by driver */
9460Sstevel@tonic-gate 	int dbtype;
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
9490Sstevel@tonic-gate 	/*
9500Sstevel@tonic-gate 	 * We received our ack from the driver saying there is nothing left to
9510Sstevel@tonic-gate 	 * shovel out, so wake up the close routine.
9520Sstevel@tonic-gate 	 */
9530Sstevel@tonic-gate 	dbtype = DB_TYPE(mp);
9540Sstevel@tonic-gate 	if ((dbtype == M_IOCACK || dbtype == M_IOCNAK) &&
9550Sstevel@tonic-gate 	    (tp->t_state & (TS_CLOSE|TS_IOCWAIT)) == (TS_CLOSE|TS_IOCWAIT)) {
9560Sstevel@tonic-gate 		struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 		if (iocp->ioc_id == tp->t_iocid) {
9590Sstevel@tonic-gate 			tp->t_state &= ~TS_IOCWAIT;
9600Sstevel@tonic-gate 			freemsg(mp);
9610Sstevel@tonic-gate 			return;
9620Sstevel@tonic-gate 		}
9630Sstevel@tonic-gate 	}
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	switch (dbtype) {
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	default:
9680Sstevel@tonic-gate 		(void) putq(q, mp);
9690Sstevel@tonic-gate 		return;
9700Sstevel@tonic-gate 
9710Sstevel@tonic-gate 		/*
9720Sstevel@tonic-gate 		 * Send these up unmolested
9730Sstevel@tonic-gate 		 *
9740Sstevel@tonic-gate 		 */
9750Sstevel@tonic-gate 	case M_PCSIG:
9760Sstevel@tonic-gate 	case M_SIG:
9770Sstevel@tonic-gate 	case M_IOCNAK:
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 		putnext(q, mp);
9800Sstevel@tonic-gate 		return;
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 	case M_IOCACK:
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 		ldterm_ioctl_reply(q, mp);
9850Sstevel@tonic-gate 		return;
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	case M_BREAK:
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 		/*
9900Sstevel@tonic-gate 		 * Parity errors are sent up as M_BREAKS with single
9910Sstevel@tonic-gate 		 * character data (formerly handled in the driver)
9920Sstevel@tonic-gate 		 */
9930Sstevel@tonic-gate 		if (mp->b_wptr - mp->b_rptr == 1) {
9940Sstevel@tonic-gate 			/*
9950Sstevel@tonic-gate 			 * IGNPAR	PARMRK		RESULT
9960Sstevel@tonic-gate 			 * off		off		0
9970Sstevel@tonic-gate 			 * off		on		3 byte sequence
9980Sstevel@tonic-gate 			 * on		either		ignored
9990Sstevel@tonic-gate 			 */
10000Sstevel@tonic-gate 			if (!(tp->t_amodes.c_iflag & IGNPAR)) {
10010Sstevel@tonic-gate 				mp->b_wptr = mp->b_rptr;
10020Sstevel@tonic-gate 				if (tp->t_amodes.c_iflag & PARMRK) {
10030Sstevel@tonic-gate 					unsigned char c;
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 					c = *mp->b_rptr;
10060Sstevel@tonic-gate 					freemsg(mp);
10070Sstevel@tonic-gate 					if ((mp = allocb(3, BPRI_HI)) == NULL) {
10080Sstevel@tonic-gate 						cmn_err(CE_WARN,
10090Sstevel@tonic-gate 						    "ldtermrput: no blocks");
10100Sstevel@tonic-gate 						return;
10110Sstevel@tonic-gate 					}
10120Sstevel@tonic-gate 					mp->b_datap->db_type = M_DATA;
10130Sstevel@tonic-gate 					*mp->b_wptr++ = (uchar_t)'\377';
10140Sstevel@tonic-gate 					*mp->b_wptr++ = '\0';
10150Sstevel@tonic-gate 					*mp->b_wptr++ = c;
10160Sstevel@tonic-gate 					putnext(q, mp);
10170Sstevel@tonic-gate 				} else {
10180Sstevel@tonic-gate 					mp->b_datap->db_type = M_DATA;
10190Sstevel@tonic-gate 					*mp->b_wptr++ = '\0';
10200Sstevel@tonic-gate 					putnext(q, mp);
10210Sstevel@tonic-gate 				}
10220Sstevel@tonic-gate 			} else {
10230Sstevel@tonic-gate 				freemsg(mp);
10240Sstevel@tonic-gate 			}
10250Sstevel@tonic-gate 			return;
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		/*
10280Sstevel@tonic-gate 		 * We look at the apparent modes here instead of the
10290Sstevel@tonic-gate 		 * effective modes. Effective modes cannot be used if
10300Sstevel@tonic-gate 		 * IGNBRK, BRINT and PARMRK have been negotiated to
10310Sstevel@tonic-gate 		 * be handled by the driver. Since M_BREAK should be
10320Sstevel@tonic-gate 		 * sent upstream only if break processing was not
10330Sstevel@tonic-gate 		 * already done, it should be ok to use the apparent
10340Sstevel@tonic-gate 		 * modes.
10350Sstevel@tonic-gate 		 */
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate 		if (!(tp->t_amodes.c_iflag & IGNBRK)) {
10380Sstevel@tonic-gate 			if (tp->t_amodes.c_iflag & BRKINT) {
10390Sstevel@tonic-gate 				ldterm_dosig(q, SIGINT, '\0', M_PCSIG, FLUSHRW);
10400Sstevel@tonic-gate 				freemsg(mp);
10410Sstevel@tonic-gate 			} else if (tp->t_amodes.c_iflag & PARMRK) {
10420Sstevel@tonic-gate 				/*
10430Sstevel@tonic-gate 				 * Send '\377','\0', '\0'.
10440Sstevel@tonic-gate 				 */
10450Sstevel@tonic-gate 				freemsg(mp);
10460Sstevel@tonic-gate 				if ((mp = allocb(3, BPRI_HI)) == NULL) {
10470Sstevel@tonic-gate 					cmn_err(CE_WARN,
1048*7012Sis 					    "ldtermrput: no blocks");
10490Sstevel@tonic-gate 					return;
10500Sstevel@tonic-gate 				}
10510Sstevel@tonic-gate 				mp->b_datap->db_type = M_DATA;
10520Sstevel@tonic-gate 				*mp->b_wptr++ = (uchar_t)'\377';
10530Sstevel@tonic-gate 				*mp->b_wptr++ = '\0';
10540Sstevel@tonic-gate 				*mp->b_wptr++ = '\0';
10550Sstevel@tonic-gate 				putnext(q, mp);
10560Sstevel@tonic-gate 			} else {
10570Sstevel@tonic-gate 				/*
10580Sstevel@tonic-gate 				 * Act as if a '\0' came in.
10590Sstevel@tonic-gate 				 */
10600Sstevel@tonic-gate 				freemsg(mp);
10610Sstevel@tonic-gate 				if ((mp = allocb(1, BPRI_HI)) == NULL) {
10620Sstevel@tonic-gate 					cmn_err(CE_WARN,
1063*7012Sis 					    "ldtermrput: no blocks");
10640Sstevel@tonic-gate 					return;
10650Sstevel@tonic-gate 				}
10660Sstevel@tonic-gate 				mp->b_datap->db_type = M_DATA;
10670Sstevel@tonic-gate 				*mp->b_wptr++ = '\0';
10680Sstevel@tonic-gate 				putnext(q, mp);
10690Sstevel@tonic-gate 			}
10700Sstevel@tonic-gate 		} else {
10710Sstevel@tonic-gate 			freemsg(mp);
10720Sstevel@tonic-gate 		}
10730Sstevel@tonic-gate 		return;
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate 	case M_CTL:
10760Sstevel@tonic-gate 		DEBUG3(("ldtermrput: M_CTL received\n"));
10770Sstevel@tonic-gate 		/*
10780Sstevel@tonic-gate 		 * The M_CTL has been standardized to look like an
10790Sstevel@tonic-gate 		 * M_IOCTL message.
10800Sstevel@tonic-gate 		 */
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) != sizeof (struct iocblk)) {
10830Sstevel@tonic-gate 			DEBUG3((
10840Sstevel@tonic-gate 			    "Non standard M_CTL received by ldterm module\n"));
10850Sstevel@tonic-gate 			/* May be for someone else; pass it on */
10860Sstevel@tonic-gate 			putnext(q, mp);
10870Sstevel@tonic-gate 			return;
10880Sstevel@tonic-gate 		}
10890Sstevel@tonic-gate 		qryp = (struct iocblk *)mp->b_rptr;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		switch (qryp->ioc_cmd) {
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 		case MC_PART_CANON:
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 			DEBUG3(("ldtermrput: M_CTL Query Reply\n"));
10960Sstevel@tonic-gate 			if (!mp->b_cont) {
10970Sstevel@tonic-gate 				DEBUG3(("No information in Query Message\n"));
10980Sstevel@tonic-gate 				break;
10990Sstevel@tonic-gate 			}
11000Sstevel@tonic-gate 			if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) ==
11010Sstevel@tonic-gate 			    sizeof (struct termios)) {
11020Sstevel@tonic-gate 				DEBUG3(("ldtermrput: M_CTL GrandScheme\n"));
11030Sstevel@tonic-gate 				/* elaborate turning off scheme */
11040Sstevel@tonic-gate 				emodes = (struct termios *)mp->b_cont->b_rptr;
11050Sstevel@tonic-gate 				bcopy(emodes, &tp->t_dmodes,
11060Sstevel@tonic-gate 					sizeof (struct termios));
11070Sstevel@tonic-gate 				ldterm_adjust_modes(tp);
11080Sstevel@tonic-gate 				break;
11090Sstevel@tonic-gate 			} else {
11100Sstevel@tonic-gate 				DEBUG3(("Incorrect query replysize\n"));
11110Sstevel@tonic-gate 				break;
11120Sstevel@tonic-gate 			}
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 		case MC_NO_CANON:
11150Sstevel@tonic-gate 			tp->t_state |= TS_NOCANON;
11160Sstevel@tonic-gate 			/*
11170Sstevel@tonic-gate 			 * Note: this is very nasty.  It's not clear
11180Sstevel@tonic-gate 			 * what the right thing to do with a partial
11190Sstevel@tonic-gate 			 * message is; We throw it out
11200Sstevel@tonic-gate 			 */
11210Sstevel@tonic-gate 			if (tp->t_message != NULL) {
11220Sstevel@tonic-gate 				freemsg(tp->t_message);
11230Sstevel@tonic-gate 				tp->t_message = NULL;
11240Sstevel@tonic-gate 				tp->t_endmsg = NULL;
11250Sstevel@tonic-gate 				tp->t_msglen = 0;
11260Sstevel@tonic-gate 				tp->t_rocount = 0;
11270Sstevel@tonic-gate 				tp->t_rocol = 0;
11280Sstevel@tonic-gate 				if (tp->t_state & TS_MEUC) {
11290Sstevel@tonic-gate 					ASSERT(tp->t_eucp_mp);
11300Sstevel@tonic-gate 					tp->t_eucp = tp->t_eucp_mp->b_rptr;
11310Sstevel@tonic-gate 					tp->t_codeset = 0;
11320Sstevel@tonic-gate 					tp->t_eucleft = 0;
11330Sstevel@tonic-gate 				}
11340Sstevel@tonic-gate 			}
11350Sstevel@tonic-gate 			break;
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 		case MC_DO_CANON:
11380Sstevel@tonic-gate 			tp->t_state &= ~TS_NOCANON;
11390Sstevel@tonic-gate 			break;
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 		case MC_HAS_POSIX:
11420Sstevel@tonic-gate 			/* no longer any reason to drain from ldterm */
11430Sstevel@tonic-gate 			if (ldterm_drain_limit != 0) {
11440Sstevel@tonic-gate 				freemsg(tp->t_drainmsg);
11450Sstevel@tonic-gate 				tp->t_drainmsg = NULL;
11460Sstevel@tonic-gate 			}
11470Sstevel@tonic-gate 			break;
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 		default:
11500Sstevel@tonic-gate 			DEBUG3(("Unknown M_CTL Message\n"));
11510Sstevel@tonic-gate 			break;
11520Sstevel@tonic-gate 		}
11530Sstevel@tonic-gate 		putnext(q, mp);	/* In case anyone else has to see it */
11540Sstevel@tonic-gate 		return;
11550Sstevel@tonic-gate 
11560Sstevel@tonic-gate 	case M_FLUSH:
11570Sstevel@tonic-gate 		/*
11580Sstevel@tonic-gate 		 * Flush everything we haven't looked at yet.
11590Sstevel@tonic-gate 		 */
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 		if ((tp->t_state & TS_ISPTSTTY) && (*mp->b_rptr & FLUSHBAND))
11620Sstevel@tonic-gate 			flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
11630Sstevel@tonic-gate 		else
11640Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 		/*
11670Sstevel@tonic-gate 		 * Flush everything we have looked at.
11680Sstevel@tonic-gate 		 */
11690Sstevel@tonic-gate 		freemsg(tp->t_message);
11700Sstevel@tonic-gate 		tp->t_message = NULL;
11710Sstevel@tonic-gate 		tp->t_endmsg = NULL;
11720Sstevel@tonic-gate 		tp->t_msglen = 0;
11730Sstevel@tonic-gate 		tp->t_rocount = 0;
11740Sstevel@tonic-gate 		tp->t_rocol = 0;
11750Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC) {	/* EUC multi-byte */
11760Sstevel@tonic-gate 			ASSERT(tp->t_eucp_mp);
11770Sstevel@tonic-gate 			tp->t_eucp = tp->t_eucp_mp->b_rptr;
11780Sstevel@tonic-gate 		}
11790Sstevel@tonic-gate 		putnext(q, mp);	/* pass it on */
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 		/*
11820Sstevel@tonic-gate 		 * Relieve input flow control
11830Sstevel@tonic-gate 		 */
11840Sstevel@tonic-gate 		if ((tp->t_modes.c_iflag & IXOFF) &&
11850Sstevel@tonic-gate 		    (tp->t_state & TS_TBLOCK) &&
11860Sstevel@tonic-gate 		    !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
11870Sstevel@tonic-gate 			tp->t_state &= ~TS_TBLOCK;
11880Sstevel@tonic-gate 			(void) putnextctl(wrq, M_STARTI);
11890Sstevel@tonic-gate 			DEBUG1(("M_STARTI down\n"));
11900Sstevel@tonic-gate 		}
11910Sstevel@tonic-gate 		return;
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 	case M_DATA:
11940Sstevel@tonic-gate 		break;
11950Sstevel@tonic-gate 	}
11960Sstevel@tonic-gate 	(void) drv_setparm(SYSRAWC, msgdsize(mp));
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	/*
11990Sstevel@tonic-gate 	 * Flow control: send "start input" message if blocked and
12000Sstevel@tonic-gate 	 * our queue is below its low water mark.
12010Sstevel@tonic-gate 	 */
12020Sstevel@tonic-gate 	if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
12030Sstevel@tonic-gate 	    !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
12040Sstevel@tonic-gate 		tp->t_state &= ~TS_TBLOCK;
12050Sstevel@tonic-gate 		(void) putnextctl(wrq, M_STARTI);
12060Sstevel@tonic-gate 		DEBUG1(("M_STARTI down\n"));
12070Sstevel@tonic-gate 	}
12080Sstevel@tonic-gate 	/*
12090Sstevel@tonic-gate 	 * If somebody below us ("intelligent" communications
12100Sstevel@tonic-gate 	 * board, pseudo-tty controlled by an editor) is doing
12110Sstevel@tonic-gate 	 * canonicalization, don't scan it for special characters.
12120Sstevel@tonic-gate 	 */
12130Sstevel@tonic-gate 	if (tp->t_state & TS_NOCANON) {
12140Sstevel@tonic-gate 		(void) putq(q, mp);
12150Sstevel@tonic-gate 		return;
12160Sstevel@tonic-gate 	}
12170Sstevel@tonic-gate 	bp = mp;
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 	do {
12200Sstevel@tonic-gate 		readp = bp->b_rptr;
12210Sstevel@tonic-gate 		writep = readp;
12220Sstevel@tonic-gate 		if (tp->t_modes.c_iflag & (INLCR|IGNCR|ICRNL|IUCLC|IXON) ||
12230Sstevel@tonic-gate 		    tp->t_modes.c_lflag & (ISIG|ICANON)) {
12240Sstevel@tonic-gate 			/*
12250Sstevel@tonic-gate 			 * We're doing some sort of non-trivial
12260Sstevel@tonic-gate 			 * processing of input; look at every
12270Sstevel@tonic-gate 			 * character.
12280Sstevel@tonic-gate 			 */
12290Sstevel@tonic-gate 			while (readp < bp->b_wptr) {
12300Sstevel@tonic-gate 				c = *readp++;
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 				if (tp->t_modes.c_iflag & ISTRIP)
12330Sstevel@tonic-gate 					c &= 0177;
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 				/*
12360Sstevel@tonic-gate 				 * First, check that this hasn't been
12370Sstevel@tonic-gate 				 * escaped with the "literal next"
12380Sstevel@tonic-gate 				 * character.
12390Sstevel@tonic-gate 				 */
12400Sstevel@tonic-gate 				if (tp->t_state & TS_PLNCH) {
12410Sstevel@tonic-gate 					tp->t_state &= ~TS_PLNCH;
12420Sstevel@tonic-gate 					tp->t_modes.c_lflag &= ~FLUSHO;
12430Sstevel@tonic-gate 					*writep++ = c;
12440Sstevel@tonic-gate 					continue;
12450Sstevel@tonic-gate 				}
12460Sstevel@tonic-gate 				/*
12470Sstevel@tonic-gate 				 * Setting a special character to NUL
12480Sstevel@tonic-gate 				 * disables it, so if this character
12490Sstevel@tonic-gate 				 * is NUL, it should not be compared
12500Sstevel@tonic-gate 				 * with any of the special characters.
12510Sstevel@tonic-gate 				 * It should, however, restart frozen
12520Sstevel@tonic-gate 				 * output if IXON and IXANY are set.
12530Sstevel@tonic-gate 				 */
12540Sstevel@tonic-gate 				if (c == _POSIX_VDISABLE) {
12550Sstevel@tonic-gate 					if (tp->t_modes.c_iflag & IXON &&
12560Sstevel@tonic-gate 					    tp->t_state & TS_TTSTOP &&
12570Sstevel@tonic-gate 					    tp->t_modes.c_lflag & IEXTEN &&
12580Sstevel@tonic-gate 					    tp->t_modes.c_iflag & IXANY) {
12590Sstevel@tonic-gate 						tp->t_state &=
12600Sstevel@tonic-gate 						    ~(TS_TTSTOP|TS_OFBLOCK);
12610Sstevel@tonic-gate 						(void) putnextctl(wrq, M_START);
12620Sstevel@tonic-gate 					}
12630Sstevel@tonic-gate 					tp->t_modes.c_lflag &= ~FLUSHO;
12640Sstevel@tonic-gate 					*writep++ = c;
12650Sstevel@tonic-gate 					continue;
12660Sstevel@tonic-gate 				}
12670Sstevel@tonic-gate 				/*
12680Sstevel@tonic-gate 				 * If stopped, start if you can; if
12690Sstevel@tonic-gate 				 * running, stop if you must.
12700Sstevel@tonic-gate 				 */
12710Sstevel@tonic-gate 				if (tp->t_modes.c_iflag & IXON) {
12720Sstevel@tonic-gate 					if (tp->t_state & TS_TTSTOP) {
12730Sstevel@tonic-gate 						if (c ==
12740Sstevel@tonic-gate 						    tp->t_modes.c_cc[VSTART] ||
12750Sstevel@tonic-gate 						    (tp->t_modes.c_lflag &
12760Sstevel@tonic-gate 						    IEXTEN &&
12770Sstevel@tonic-gate 						    tp->t_modes.c_iflag &
12780Sstevel@tonic-gate 						    IXANY)) {
12790Sstevel@tonic-gate 							tp->t_state &=
1280*7012Sis 							    ~(TS_TTSTOP |
1281*7012Sis 							    TS_OFBLOCK);
12820Sstevel@tonic-gate 							(void) putnextctl(wrq,
12830Sstevel@tonic-gate 							    M_START);
12840Sstevel@tonic-gate 						}
12850Sstevel@tonic-gate 					} else {
12860Sstevel@tonic-gate 						if (c ==
12870Sstevel@tonic-gate 						    tp->t_modes.c_cc[VSTOP]) {
12880Sstevel@tonic-gate 							tp->t_state |=
12890Sstevel@tonic-gate 							    TS_TTSTOP;
12900Sstevel@tonic-gate 							(void) putnextctl(wrq,
12910Sstevel@tonic-gate 							    M_STOP);
12920Sstevel@tonic-gate 						}
12930Sstevel@tonic-gate 					}
12940Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VSTOP] ||
12950Sstevel@tonic-gate 					    c == tp->t_modes.c_cc[VSTART])
12960Sstevel@tonic-gate 						continue;
12970Sstevel@tonic-gate 				}
12980Sstevel@tonic-gate 				/*
12990Sstevel@tonic-gate 				 * Check for "literal next" character
13000Sstevel@tonic-gate 				 * and "flush output" character.
13010Sstevel@tonic-gate 				 * Note that we omit checks for ISIG
13020Sstevel@tonic-gate 				 * and ICANON, since the IEXTEN
13030Sstevel@tonic-gate 				 * setting subsumes them.
13040Sstevel@tonic-gate 				 */
13050Sstevel@tonic-gate 				if (tp->t_modes.c_lflag & IEXTEN) {
13060Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VLNEXT]) {
13070Sstevel@tonic-gate 						/*
13080Sstevel@tonic-gate 						 * Remember that we saw a
13090Sstevel@tonic-gate 						 * "literal next" while
13100Sstevel@tonic-gate 						 * scanning input, but leave
13110Sstevel@tonic-gate 						 * leave it in the message so
13120Sstevel@tonic-gate 						 * that the service routine
13130Sstevel@tonic-gate 						 * can see it too.
13140Sstevel@tonic-gate 						 */
13150Sstevel@tonic-gate 						tp->t_state |= TS_PLNCH;
13160Sstevel@tonic-gate 						tp->t_modes.c_lflag &= ~FLUSHO;
13170Sstevel@tonic-gate 						*writep++ = c;
13180Sstevel@tonic-gate 						continue;
13190Sstevel@tonic-gate 					}
13200Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VDISCARD]) {
13210Sstevel@tonic-gate 						ldterm_flush_output(c, wrq, tp);
13220Sstevel@tonic-gate 						continue;
13230Sstevel@tonic-gate 					}
13240Sstevel@tonic-gate 				}
13250Sstevel@tonic-gate 				tp->t_modes.c_lflag &= ~FLUSHO;
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 				/*
13280Sstevel@tonic-gate 				 * Check for signal-generating
13290Sstevel@tonic-gate 				 * characters.
13300Sstevel@tonic-gate 				 */
13310Sstevel@tonic-gate 				if (tp->t_modes.c_lflag & ISIG) {
13320Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VINTR]) {
13330Sstevel@tonic-gate 						ldterm_dosig(q, SIGINT, c,
13340Sstevel@tonic-gate 						    M_PCSIG, FLUSHRW);
13350Sstevel@tonic-gate 						continue;
13360Sstevel@tonic-gate 					}
13370Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VQUIT]) {
13380Sstevel@tonic-gate 						ldterm_dosig(q, SIGQUIT, c,
13390Sstevel@tonic-gate 						    M_PCSIG, FLUSHRW);
13400Sstevel@tonic-gate 						continue;
13410Sstevel@tonic-gate 					}
13420Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VSWTCH]) {
13430Sstevel@tonic-gate 						/*
13440Sstevel@tonic-gate 						 * Ancient SXT support; discard
13450Sstevel@tonic-gate 						 * character without action.
13460Sstevel@tonic-gate 						 */
13470Sstevel@tonic-gate 						continue;
13480Sstevel@tonic-gate 					}
13490Sstevel@tonic-gate 					if (c == tp->t_modes.c_cc[VSUSP]) {
13500Sstevel@tonic-gate 						ldterm_dosig(q, SIGTSTP, c,
13510Sstevel@tonic-gate 						    M_PCSIG, FLUSHRW);
13520Sstevel@tonic-gate 						continue;
13530Sstevel@tonic-gate 					}
13540Sstevel@tonic-gate 					if ((tp->t_modes.c_lflag & IEXTEN) &&
13550Sstevel@tonic-gate 					    (c == tp->t_modes.c_cc[VDSUSP])) {
13560Sstevel@tonic-gate 						ldterm_dosig(q, SIGTSTP, c,
13570Sstevel@tonic-gate 						    M_SIG, 0);
13580Sstevel@tonic-gate 						continue;
13590Sstevel@tonic-gate 					}
13600Sstevel@tonic-gate 				}
13610Sstevel@tonic-gate 				/*
13620Sstevel@tonic-gate 				 * Throw away CR if IGNCR set, or
13630Sstevel@tonic-gate 				 * turn it into NL if ICRNL set.
13640Sstevel@tonic-gate 				 */
13650Sstevel@tonic-gate 				if (c == '\r') {
13660Sstevel@tonic-gate 					if (tp->t_modes.c_iflag & IGNCR)
13670Sstevel@tonic-gate 						continue;
13680Sstevel@tonic-gate 					if (tp->t_modes.c_iflag & ICRNL)
13690Sstevel@tonic-gate 						c = '\n';
13700Sstevel@tonic-gate 				} else {
13710Sstevel@tonic-gate 					/*
13720Sstevel@tonic-gate 					 * Turn NL into CR if INLCR
13730Sstevel@tonic-gate 					 * set.
13740Sstevel@tonic-gate 					 */
13750Sstevel@tonic-gate 					if (c == '\n' &&
13760Sstevel@tonic-gate 					    tp->t_modes.c_iflag & INLCR)
13770Sstevel@tonic-gate 						c = '\r';
13780Sstevel@tonic-gate 				}
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 				/*
13810Sstevel@tonic-gate 				 * Map upper case input to lower case
13820Sstevel@tonic-gate 				 * if IUCLC flag set.
13830Sstevel@tonic-gate 				 */
13840Sstevel@tonic-gate 				if (tp->t_modes.c_iflag & IUCLC &&
13850Sstevel@tonic-gate 				    c >= 'A' && c <= 'Z')
13860Sstevel@tonic-gate 					c += 'a' - 'A';
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 				/*
13890Sstevel@tonic-gate 				 * Put the possibly-transformed
13900Sstevel@tonic-gate 				 * character back in the message.
13910Sstevel@tonic-gate 				 */
13920Sstevel@tonic-gate 				*writep++ = c;
13930Sstevel@tonic-gate 			}
13940Sstevel@tonic-gate 
13950Sstevel@tonic-gate 			/*
13960Sstevel@tonic-gate 			 * If we didn't copy some characters because
13970Sstevel@tonic-gate 			 * we were ignoring them, fix the size of the
13980Sstevel@tonic-gate 			 * data block by adjusting the write pointer.
13990Sstevel@tonic-gate 			 * XXX This may result in a zero-length
14000Sstevel@tonic-gate 			 * block; will this cause anybody gastric
14010Sstevel@tonic-gate 			 * distress?
14020Sstevel@tonic-gate 			 */
14030Sstevel@tonic-gate 			bp->b_wptr -= (readp - writep);
14040Sstevel@tonic-gate 		} else {
14050Sstevel@tonic-gate 			/*
14060Sstevel@tonic-gate 			 * We won't be doing anything other than
14070Sstevel@tonic-gate 			 * possibly stripping the input.
14080Sstevel@tonic-gate 			 */
14090Sstevel@tonic-gate 			if (tp->t_modes.c_iflag & ISTRIP) {
14100Sstevel@tonic-gate 				while (readp < bp->b_wptr)
14110Sstevel@tonic-gate 					*writep++ = *readp++ & 0177;
14120Sstevel@tonic-gate 			}
14130Sstevel@tonic-gate 			tp->t_modes.c_lflag &= ~FLUSHO;
14140Sstevel@tonic-gate 		}
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	} while ((bp = bp->b_cont) != NULL);	/* next block, if any */
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	/*
14190Sstevel@tonic-gate 	 * Queue the message for service procedure if the
14200Sstevel@tonic-gate 	 * queue is not empty or canputnext() fails or
14210Sstevel@tonic-gate 	 * tp->t_state & TS_RESCAN is true.
14220Sstevel@tonic-gate 	 */
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 	if (q->q_first != NULL || !bcanputnext(q, mp->b_band) ||
1425*7012Sis 	    (tp->t_state & TS_RESCAN))
14260Sstevel@tonic-gate 		(void) putq(q, mp);
14270Sstevel@tonic-gate 	else
14280Sstevel@tonic-gate 		(void) ldtermrmsg(q, mp);
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	/*
14310Sstevel@tonic-gate 	 * Flow control: send "stop input" message if our queue is
14320Sstevel@tonic-gate 	 * approaching its high-water mark. The message will be
14330Sstevel@tonic-gate 	 * dropped on the floor in the service procedure, if we
14340Sstevel@tonic-gate 	 * cannot ship it up and we have had it upto our neck!
14350Sstevel@tonic-gate 	 *
14360Sstevel@tonic-gate 	 * Set QWANTW to ensure that the read queue service procedure
14370Sstevel@tonic-gate 	 * gets run when nextq empties up again, so that it can
14380Sstevel@tonic-gate 	 * unstop the input.
14390Sstevel@tonic-gate 	 */
14400Sstevel@tonic-gate 	if ((tp->t_modes.c_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK) &&
14410Sstevel@tonic-gate 	    q->q_count >= TTXOHI) {
14420Sstevel@tonic-gate 		mutex_enter(QLOCK(nextq));
14430Sstevel@tonic-gate 		nextq->q_flag |= QWANTW;
14440Sstevel@tonic-gate 		mutex_exit(QLOCK(nextq));
14450Sstevel@tonic-gate 		tp->t_state |= TS_TBLOCK;
14460Sstevel@tonic-gate 		(void) putnextctl(wrq, M_STOPI);
14470Sstevel@tonic-gate 		DEBUG1(("M_STOPI down\n"));
14480Sstevel@tonic-gate 	}
14490Sstevel@tonic-gate }
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate /*
14530Sstevel@tonic-gate  * Line discipline input server processing.  Erase/kill and escape
14540Sstevel@tonic-gate  * ('\') processing, gathering into messages, upper/lower case input
14550Sstevel@tonic-gate  * mapping.
14560Sstevel@tonic-gate  */
14570Sstevel@tonic-gate static void
ldtermrsrv(queue_t * q)14580Sstevel@tonic-gate ldtermrsrv(queue_t *q)
14590Sstevel@tonic-gate {
14600Sstevel@tonic-gate 	ldtermstd_state_t *tp;
14610Sstevel@tonic-gate 	mblk_t *mp;
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	if (tp->t_state & TS_RESCAN) {
14660Sstevel@tonic-gate 		/*
14670Sstevel@tonic-gate 		 * Canonicalization was turned on or off. Put the
14680Sstevel@tonic-gate 		 * message being assembled back in the input queue,
14690Sstevel@tonic-gate 		 * so that we rescan it.
14700Sstevel@tonic-gate 		 */
14710Sstevel@tonic-gate 		if (tp->t_message != NULL) {
14720Sstevel@tonic-gate 			DEBUG5(("RESCAN WAS SET; put back in q\n"));
14730Sstevel@tonic-gate 			if (tp->t_msglen != 0)
14740Sstevel@tonic-gate 				(void) putbq(q, tp->t_message);
14750Sstevel@tonic-gate 			else
14760Sstevel@tonic-gate 				freemsg(tp->t_message);
14770Sstevel@tonic-gate 			tp->t_message = NULL;
14780Sstevel@tonic-gate 			tp->t_endmsg = NULL;
14790Sstevel@tonic-gate 			tp->t_msglen = 0;
14800Sstevel@tonic-gate 		}
14810Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC) {
14820Sstevel@tonic-gate 			ASSERT(tp->t_eucp_mp);
14830Sstevel@tonic-gate 			tp->t_eucp = tp->t_eucp_mp->b_rptr;
14840Sstevel@tonic-gate 			tp->t_codeset = 0;
14850Sstevel@tonic-gate 			tp->t_eucleft = 0;
14860Sstevel@tonic-gate 		}
14870Sstevel@tonic-gate 		tp->t_state &= ~TS_RESCAN;
14880Sstevel@tonic-gate 	}
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
14910Sstevel@tonic-gate 		if (!ldtermrmsg(q, mp))
14920Sstevel@tonic-gate 			break;
14930Sstevel@tonic-gate 	}
14940Sstevel@tonic-gate 
14950Sstevel@tonic-gate 	/*
14960Sstevel@tonic-gate 	 * Flow control: send start message if blocked and our queue
14970Sstevel@tonic-gate 	 * is below its low water mark.
14980Sstevel@tonic-gate 	 */
14990Sstevel@tonic-gate 	if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
15000Sstevel@tonic-gate 	    !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
15010Sstevel@tonic-gate 		tp->t_state &= ~TS_TBLOCK;
15020Sstevel@tonic-gate 		(void) putctl(WR(q), M_STARTI);
15030Sstevel@tonic-gate 	}
15040Sstevel@tonic-gate }
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate /*
15070Sstevel@tonic-gate  * This routine is called from both ldtermrput and ldtermrsrv to
15080Sstevel@tonic-gate  * do the actual work of dealing with mp. Return 1 on sucesss and
15090Sstevel@tonic-gate  * 0 on failure.
15100Sstevel@tonic-gate  */
15110Sstevel@tonic-gate static int
ldtermrmsg(queue_t * q,mblk_t * mp)15120Sstevel@tonic-gate ldtermrmsg(queue_t *q, mblk_t *mp)
15130Sstevel@tonic-gate {
15140Sstevel@tonic-gate 	unsigned char c;
15150Sstevel@tonic-gate 	int dofree;
15160Sstevel@tonic-gate 	int status = 1;
15170Sstevel@tonic-gate 	size_t   ebsize;
15180Sstevel@tonic-gate 	mblk_t *bp;
15190Sstevel@tonic-gate 	mblk_t *bpt;
15200Sstevel@tonic-gate 	ldtermstd_state_t *tp;
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 	bpt = NULL;
15230Sstevel@tonic-gate 
15240Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	if (mp->b_datap->db_type <= QPCTL && !bcanputnext(q, mp->b_band)) {
15270Sstevel@tonic-gate 		/*
15280Sstevel@tonic-gate 		 * Stream head is flow controlled. If echo is
15290Sstevel@tonic-gate 		 * turned on, flush the read side or send a
15300Sstevel@tonic-gate 		 * bell down the line to stop input and
15310Sstevel@tonic-gate 		 * process the current message.
15320Sstevel@tonic-gate 		 * Otherwise(putbq) the user will not see any
15330Sstevel@tonic-gate 		 * response to to the typed input. Typically
15340Sstevel@tonic-gate 		 * happens if there is no reader process.
15350Sstevel@tonic-gate 		 * Note that you will loose the data in this
15360Sstevel@tonic-gate 		 * case if the data is coming too fast. There
15370Sstevel@tonic-gate 		 * is an assumption here that if ECHO is
15380Sstevel@tonic-gate 		 * turned on its some user typing the data on
15390Sstevel@tonic-gate 		 * a terminal and its not network.
15400Sstevel@tonic-gate 		 */
15410Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & ECHO) {
15420Sstevel@tonic-gate 			if ((tp->t_modes.c_iflag & IMAXBEL) &&
15430Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & ICANON)) {
15440Sstevel@tonic-gate 				freemsg(mp);
15450Sstevel@tonic-gate 				if (canputnext(WR(q)))
15460Sstevel@tonic-gate 					ldterm_outchar(CTRL('g'), WR(q), 4, tp);
15470Sstevel@tonic-gate 				status = 0;
15480Sstevel@tonic-gate 				goto echo;
15490Sstevel@tonic-gate 			} else {
15500Sstevel@tonic-gate 				(void) putctl1(q, M_FLUSH, FLUSHR);
15510Sstevel@tonic-gate 			}
15520Sstevel@tonic-gate 		} else {
15530Sstevel@tonic-gate 			(void) putbq(q, mp);
15540Sstevel@tonic-gate 			status = 0;
15550Sstevel@tonic-gate 			goto out;	/* read side is blocked */
15560Sstevel@tonic-gate 		}
15570Sstevel@tonic-gate 	}
15580Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	default:
15610Sstevel@tonic-gate 		putnext(q, mp);	/* pass it on */
15620Sstevel@tonic-gate 		goto out;
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate 	case M_HANGUP:
15650Sstevel@tonic-gate 		/*
15660Sstevel@tonic-gate 		 * Flush everything we haven't looked at yet.
15670Sstevel@tonic-gate 		 */
15680Sstevel@tonic-gate 		flushq(q, FLUSHDATA);
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 		/*
15710Sstevel@tonic-gate 		 * Flush everything we have looked at.
15720Sstevel@tonic-gate 		 */
15730Sstevel@tonic-gate 		freemsg(tp->t_message);
15740Sstevel@tonic-gate 		tp->t_message = NULL;
15750Sstevel@tonic-gate 		tp->t_endmsg = NULL;
15760Sstevel@tonic-gate 		tp->t_msglen = 0;
15770Sstevel@tonic-gate 		/*
15780Sstevel@tonic-gate 		 * XXX  should we set read request
15790Sstevel@tonic-gate 		 * tp->t_rd_request to NULL?
15800Sstevel@tonic-gate 		 */
15810Sstevel@tonic-gate 		tp->t_rocount = 0;	/* if it hasn't been typed */
15820Sstevel@tonic-gate 		tp->t_rocol = 0;	/* it hasn't been echoed :-) */
15830Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC) {
15840Sstevel@tonic-gate 			ASSERT(tp->t_eucp_mp);
15850Sstevel@tonic-gate 			tp->t_eucp = tp->t_eucp_mp->b_rptr;
15860Sstevel@tonic-gate 		}
15870Sstevel@tonic-gate 		/*
15880Sstevel@tonic-gate 		 * Restart output, since it's probably got
15890Sstevel@tonic-gate 		 * nowhere to go anyway, and we're probably
15900Sstevel@tonic-gate 		 * not going to see another ^Q for a while.
15910Sstevel@tonic-gate 		 */
15920Sstevel@tonic-gate 		if (tp->t_state & TS_TTSTOP) {
15930Sstevel@tonic-gate 			tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
15940Sstevel@tonic-gate 			(void) putnextctl(WR(q), M_START);
15950Sstevel@tonic-gate 		}
15960Sstevel@tonic-gate 		/*
15970Sstevel@tonic-gate 		 * This message will travel up the read
15980Sstevel@tonic-gate 		 * queue, flushing as it goes, get turned
15990Sstevel@tonic-gate 		 * around at the stream head, and travel back
16000Sstevel@tonic-gate 		 * down the write queue, flushing as it goes.
16010Sstevel@tonic-gate 		 */
16020Sstevel@tonic-gate 		(void) putnextctl1(q, M_FLUSH, FLUSHW);
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 		/*
16050Sstevel@tonic-gate 		 * This message will travel down the write
16060Sstevel@tonic-gate 		 * queue, flushing as it goes, get turned
16070Sstevel@tonic-gate 		 * around at the driver, and travel back up
16080Sstevel@tonic-gate 		 * the read queue, flushing as it goes.
16090Sstevel@tonic-gate 		 */
16100Sstevel@tonic-gate 		(void) putctl1(WR(q), M_FLUSH, FLUSHR);
16110Sstevel@tonic-gate 
16120Sstevel@tonic-gate 		/*
16130Sstevel@tonic-gate 		 * Now that that's done, we send a SIGCONT
16140Sstevel@tonic-gate 		 * upstream, followed by the M_HANGUP.
16150Sstevel@tonic-gate 		 */
16160Sstevel@tonic-gate 		/* (void) putnextctl1(q, M_PCSIG, SIGCONT); */
16170Sstevel@tonic-gate 		putnext(q, mp);
16180Sstevel@tonic-gate 		goto out;
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	case M_IOCACK:
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate 		/*
16230Sstevel@tonic-gate 		 * Augment whatever information the driver is
16240Sstevel@tonic-gate 		 * returning  with the information we supply.
16250Sstevel@tonic-gate 		 */
16260Sstevel@tonic-gate 		ldterm_ioctl_reply(q, mp);
16270Sstevel@tonic-gate 		goto out;
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 	case M_DATA:
16300Sstevel@tonic-gate 		break;
16310Sstevel@tonic-gate 	}
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate 	/*
16340Sstevel@tonic-gate 	 * This is an M_DATA message.
16350Sstevel@tonic-gate 	 */
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	/*
16380Sstevel@tonic-gate 	 * If somebody below us ("intelligent" communications
16390Sstevel@tonic-gate 	 * board, pseudo-tty controlled by an editor) is
16400Sstevel@tonic-gate 	 * doing canonicalization, don't scan it for special
16410Sstevel@tonic-gate 	 * characters.
16420Sstevel@tonic-gate 	 */
16430Sstevel@tonic-gate 	if (tp->t_state & TS_NOCANON) {
16440Sstevel@tonic-gate 		putnext(q, mp);
16450Sstevel@tonic-gate 		goto out;
16460Sstevel@tonic-gate 	}
16470Sstevel@tonic-gate 	bp = mp;
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	if ((bpt = newmsg(tp)) != NULL) {
16500Sstevel@tonic-gate 		mblk_t *bcont;
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 		do {
16530Sstevel@tonic-gate 			ASSERT(bp->b_wptr >= bp->b_rptr);
16540Sstevel@tonic-gate 			ebsize = bp->b_wptr - bp->b_rptr;
16550Sstevel@tonic-gate 			if (ebsize > EBSIZE)
16560Sstevel@tonic-gate 				ebsize = EBSIZE;
16570Sstevel@tonic-gate 			bcont = bp->b_cont;
16580Sstevel@tonic-gate 			if (CANON_MODE) {
16590Sstevel@tonic-gate 				/*
16600Sstevel@tonic-gate 				 * By default, free the message once processed
16610Sstevel@tonic-gate 				 */
16620Sstevel@tonic-gate 				dofree = 1;
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate 				/*
16650Sstevel@tonic-gate 				 * update sysinfo canch
16660Sstevel@tonic-gate 				 * character. The value of
16670Sstevel@tonic-gate 				 * canch may vary as compared
16680Sstevel@tonic-gate 				 * to character tty
16690Sstevel@tonic-gate 				 * implementation.
16700Sstevel@tonic-gate 				 */
16710Sstevel@tonic-gate 				while (bp->b_rptr < bp->b_wptr) {
16720Sstevel@tonic-gate 					c = *bp->b_rptr++;
16730Sstevel@tonic-gate 					if ((bpt = ldterm_docanon(c,
16740Sstevel@tonic-gate 					    bpt, ebsize, q, tp, &dofree)) ==
16750Sstevel@tonic-gate 					    NULL)
16760Sstevel@tonic-gate 						break;
16770Sstevel@tonic-gate 				}
16780Sstevel@tonic-gate 				/*
16790Sstevel@tonic-gate 				 * Release this block or put back on queue.
16800Sstevel@tonic-gate 				 */
16810Sstevel@tonic-gate 				if (dofree)
16820Sstevel@tonic-gate 					freeb(bp);
16830Sstevel@tonic-gate 				else {
16840Sstevel@tonic-gate 					(void) putbq(q, bp);
16850Sstevel@tonic-gate 					break;
16860Sstevel@tonic-gate 				}
16870Sstevel@tonic-gate 			} else
1688*7012Sis 				bpt = ldterm_dononcanon(bp, bpt, ebsize, q, tp);
16890Sstevel@tonic-gate 			if (bpt == NULL) {
16900Sstevel@tonic-gate 				cmn_err(CE_WARN,
16910Sstevel@tonic-gate 				    "ldtermrsrv: out of blocks");
16920Sstevel@tonic-gate 				freemsg(bcont);
16930Sstevel@tonic-gate 				break;
16940Sstevel@tonic-gate 			}
16950Sstevel@tonic-gate 		} while ((bp = bcont) != NULL);
16960Sstevel@tonic-gate 	}
16970Sstevel@tonic-gate echo:
16980Sstevel@tonic-gate 	/*
16990Sstevel@tonic-gate 	 * Send whatever we echoed downstream.
17000Sstevel@tonic-gate 	 */
17010Sstevel@tonic-gate 	if (tp->t_echomp != NULL) {
17020Sstevel@tonic-gate 		if (canputnext(WR(q)))
17030Sstevel@tonic-gate 			putnext(WR(q), tp->t_echomp);
17040Sstevel@tonic-gate 		else
17050Sstevel@tonic-gate 			freemsg(tp->t_echomp);
17060Sstevel@tonic-gate 		tp->t_echomp = NULL;
17070Sstevel@tonic-gate 	}
17080Sstevel@tonic-gate 
17090Sstevel@tonic-gate out:
17100Sstevel@tonic-gate 	return (status);
17110Sstevel@tonic-gate }
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate /*
17150Sstevel@tonic-gate  * Do canonical mode input; check whether this character is to be
17160Sstevel@tonic-gate  * treated as a special character - if so, check whether it's equal
17170Sstevel@tonic-gate  * to any of the special characters and handle it accordingly.
17180Sstevel@tonic-gate  * Otherwise, just add it to the current line.
17190Sstevel@tonic-gate  */
17200Sstevel@tonic-gate static mblk_t *
ldterm_docanon(uchar_t c,mblk_t * bpt,size_t ebsize,queue_t * q,ldtermstd_state_t * tp,int * dofreep)17210Sstevel@tonic-gate ldterm_docanon(uchar_t c, mblk_t *bpt, size_t ebsize, queue_t *q,
17220Sstevel@tonic-gate     ldtermstd_state_t *tp, int *dofreep)
17230Sstevel@tonic-gate {
17240Sstevel@tonic-gate 	queue_t *wrq = WR(q);
17250Sstevel@tonic-gate 	int i;
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	/*
17280Sstevel@tonic-gate 	 * If the previous character was the "literal next"
17290Sstevel@tonic-gate 	 * character, treat this character as regular input.
17300Sstevel@tonic-gate 	 */
17310Sstevel@tonic-gate 	if (tp->t_state & TS_SLNCH)
17320Sstevel@tonic-gate 		goto escaped;
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 	/*
17350Sstevel@tonic-gate 	 * Setting a special character to NUL disables it, so if this
17360Sstevel@tonic-gate 	 * character is NUL, it should not be compared with any of
17370Sstevel@tonic-gate 	 * the special characters.
17380Sstevel@tonic-gate 	 */
17390Sstevel@tonic-gate 	if (c == _POSIX_VDISABLE) {
17400Sstevel@tonic-gate 		tp->t_state &= ~TS_QUOT;
17410Sstevel@tonic-gate 		goto escaped;
17420Sstevel@tonic-gate 	}
17430Sstevel@tonic-gate 	/*
17440Sstevel@tonic-gate 	 * If this character is the literal next character, echo it
17450Sstevel@tonic-gate 	 * as '^', backspace over it, and record that fact.
17460Sstevel@tonic-gate 	 */
17470Sstevel@tonic-gate 	if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VLNEXT]) {
17480Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & ECHO)
17490Sstevel@tonic-gate 			ldterm_outstring((unsigned char *)"^\b", 2, wrq,
1750*7012Sis 			    ebsize, tp);
17510Sstevel@tonic-gate 		tp->t_state |= TS_SLNCH;
17520Sstevel@tonic-gate 		goto out;
17530Sstevel@tonic-gate 	}
17540Sstevel@tonic-gate 	/*
17550Sstevel@tonic-gate 	 * Check for the editing character. If the display width of
17560Sstevel@tonic-gate 	 * the last byte at the canonical buffer is not one and also
17570Sstevel@tonic-gate 	 * smaller than or equal to UNKNOWN_WIDTH, the character at
17580Sstevel@tonic-gate 	 * the end of the buffer is a multi-byte and/or multi-column
17590Sstevel@tonic-gate 	 * character.
17600Sstevel@tonic-gate 	 */
17610Sstevel@tonic-gate 	if (c == tp->t_modes.c_cc[VERASE]) {
17620Sstevel@tonic-gate 		if (tp->t_state & TS_QUOT) {
17630Sstevel@tonic-gate 			/*
17640Sstevel@tonic-gate 			 * Get rid of the backslash, and put the
17650Sstevel@tonic-gate 			 * erase character in its place.
17660Sstevel@tonic-gate 			 */
17670Sstevel@tonic-gate 			ldterm_erase(wrq, ebsize, tp);
17680Sstevel@tonic-gate 			bpt = tp->t_endmsg;
17690Sstevel@tonic-gate 			goto escaped;
17700Sstevel@tonic-gate 		} else {
17710Sstevel@tonic-gate 			if ((tp->t_state & TS_MEUC) && tp->t_msglen &&
17720Sstevel@tonic-gate 			    (*(tp->t_eucp - 1) != 1 &&
17730Sstevel@tonic-gate 			    *(tp->t_eucp - 1) <= UNKNOWN_WIDTH))
17740Sstevel@tonic-gate 				ldterm_csi_erase(wrq, ebsize, tp);
17750Sstevel@tonic-gate 			else
17760Sstevel@tonic-gate 				ldterm_erase(wrq, ebsize, tp);
17770Sstevel@tonic-gate 			bpt = tp->t_endmsg;
17780Sstevel@tonic-gate 			goto out;
17790Sstevel@tonic-gate 		}
17800Sstevel@tonic-gate 	}
17810Sstevel@tonic-gate 	if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VWERASE]) {
17820Sstevel@tonic-gate 		/*
17830Sstevel@tonic-gate 		 * Do "ASCII word" or "multibyte character token/chunk" erase.
17840Sstevel@tonic-gate 		 */
17850Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC)
17860Sstevel@tonic-gate 			ldterm_csi_werase(wrq, ebsize, tp);
17870Sstevel@tonic-gate 		else
17880Sstevel@tonic-gate 			ldterm_werase(wrq, ebsize, tp);
17890Sstevel@tonic-gate 		bpt = tp->t_endmsg;
17900Sstevel@tonic-gate 		goto out;
17910Sstevel@tonic-gate 	}
17920Sstevel@tonic-gate 	if (c == tp->t_modes.c_cc[VKILL]) {
17930Sstevel@tonic-gate 		if (tp->t_state & TS_QUOT) {
17940Sstevel@tonic-gate 			/*
17950Sstevel@tonic-gate 			 * Get rid of the backslash, and put the kill
17960Sstevel@tonic-gate 			 * character in its place.
17970Sstevel@tonic-gate 			 */
17980Sstevel@tonic-gate 			ldterm_erase(wrq, ebsize, tp);
17990Sstevel@tonic-gate 			bpt = tp->t_endmsg;
18000Sstevel@tonic-gate 			goto escaped;
18010Sstevel@tonic-gate 		} else {
18020Sstevel@tonic-gate 			ldterm_kill(wrq, ebsize, tp);
18030Sstevel@tonic-gate 			bpt = tp->t_endmsg;
18040Sstevel@tonic-gate 			goto out;
18050Sstevel@tonic-gate 		}
18060Sstevel@tonic-gate 	}
18070Sstevel@tonic-gate 	if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VREPRINT]) {
18080Sstevel@tonic-gate 		ldterm_reprint(wrq, ebsize, tp);
18090Sstevel@tonic-gate 		goto out;
18100Sstevel@tonic-gate 	}
18110Sstevel@tonic-gate 	/*
18120Sstevel@tonic-gate 	 * If the preceding character was a backslash: if the current
18130Sstevel@tonic-gate 	 * character is an EOF, get rid of the backslash and treat
18140Sstevel@tonic-gate 	 * the EOF as data; if we're in XCASE mode and the current
18150Sstevel@tonic-gate 	 * character is part of a backslash-X escape sequence,
18160Sstevel@tonic-gate 	 * process it; otherwise, just treat the current character
18170Sstevel@tonic-gate 	 * normally.
18180Sstevel@tonic-gate 	 */
18190Sstevel@tonic-gate 	if (tp->t_state & TS_QUOT) {
18200Sstevel@tonic-gate 		tp->t_state &= ~TS_QUOT;
18210Sstevel@tonic-gate 		if (c == tp->t_modes.c_cc[VEOF]) {
18220Sstevel@tonic-gate 			/*
18230Sstevel@tonic-gate 			 * EOF character. Since it's escaped, get rid
18240Sstevel@tonic-gate 			 * of the backslash and put the EOF character
18250Sstevel@tonic-gate 			 * in its place.
18260Sstevel@tonic-gate 			 */
18270Sstevel@tonic-gate 			ldterm_erase(wrq, ebsize, tp);
18280Sstevel@tonic-gate 			bpt = tp->t_endmsg;
18290Sstevel@tonic-gate 		} else {
18300Sstevel@tonic-gate 			/*
18310Sstevel@tonic-gate 			 * If we're in XCASE mode, and the current
18320Sstevel@tonic-gate 			 * character is part of a backslash-X
18330Sstevel@tonic-gate 			 * sequence, get rid of the backslash and
18340Sstevel@tonic-gate 			 * replace the current character with what
18350Sstevel@tonic-gate 			 * that sequence maps to.
18360Sstevel@tonic-gate 			 */
18370Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & XCASE) &&
18380Sstevel@tonic-gate 			    imaptab[c] != '\0') {
18390Sstevel@tonic-gate 				ldterm_erase(wrq, ebsize, tp);
18400Sstevel@tonic-gate 				bpt = tp->t_endmsg;
18410Sstevel@tonic-gate 				c = imaptab[c];
18420Sstevel@tonic-gate 			}
18430Sstevel@tonic-gate 		}
18440Sstevel@tonic-gate 	} else {
18450Sstevel@tonic-gate 		/*
18460Sstevel@tonic-gate 		 * Previous character wasn't backslash; check whether
18470Sstevel@tonic-gate 		 * this was the EOF character.
18480Sstevel@tonic-gate 		 */
18490Sstevel@tonic-gate 		if (c == tp->t_modes.c_cc[VEOF]) {
18500Sstevel@tonic-gate 			/*
18510Sstevel@tonic-gate 			 * EOF character. Don't echo it unless
18520Sstevel@tonic-gate 			 * ECHOCTL is set, don't stuff it in the
18530Sstevel@tonic-gate 			 * current line, but send the line up the
18540Sstevel@tonic-gate 			 * stream.
18550Sstevel@tonic-gate 			 */
18560Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & ECHOCTL) &&
18570Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & IEXTEN) &&
18580Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & ECHO)) {
18590Sstevel@tonic-gate 				i = ldterm_echo(c, wrq, ebsize, tp);
18600Sstevel@tonic-gate 				while (i > 0) {
18610Sstevel@tonic-gate 					ldterm_outchar('\b', wrq, ebsize, tp);
18620Sstevel@tonic-gate 					i--;
18630Sstevel@tonic-gate 				}
18640Sstevel@tonic-gate 			}
18650Sstevel@tonic-gate 			bpt->b_datap->db_type = M_DATA;
18660Sstevel@tonic-gate 			ldterm_msg_upstream(q, tp);
18670Sstevel@tonic-gate 			if (!canputnext(q)) {
18680Sstevel@tonic-gate 				bpt = NULL;
18690Sstevel@tonic-gate 				*dofreep = 0;
18700Sstevel@tonic-gate 			} else {
18710Sstevel@tonic-gate 				bpt = newmsg(tp);
18720Sstevel@tonic-gate 				*dofreep = 1;
18730Sstevel@tonic-gate 			}
18740Sstevel@tonic-gate 			goto out;
18750Sstevel@tonic-gate 		}
18760Sstevel@tonic-gate 	}
18770Sstevel@tonic-gate 
18780Sstevel@tonic-gate escaped:
18790Sstevel@tonic-gate 	/*
18800Sstevel@tonic-gate 	 * First, make sure we can fit one WHOLE multi-byte char in the
18810Sstevel@tonic-gate 	 * buffer.  This is one place where we have overhead even if
18820Sstevel@tonic-gate 	 * not in multi-byte mode; the overhead is subtracting
18830Sstevel@tonic-gate 	 * tp->t_maxeuc from MAX_CANON before checking.
18840Sstevel@tonic-gate 	 *
18850Sstevel@tonic-gate 	 * Allows MAX_CANON bytes in the buffer before throwing awaying
18860Sstevel@tonic-gate 	 * the the overflow of characters.
18870Sstevel@tonic-gate 	 */
18880Sstevel@tonic-gate 	if ((tp->t_msglen > ((MAX_CANON + 1) - (int)tp->t_maxeuc)) &&
18890Sstevel@tonic-gate 	    !((tp->t_state & TS_MEUC) && tp->t_eucleft)) {
18900Sstevel@tonic-gate 
18910Sstevel@tonic-gate 		/*
18920Sstevel@tonic-gate 		 * Byte will cause line to overflow, or the next EUC
18930Sstevel@tonic-gate 		 * won't fit: Ring the bell or discard all input, and
18940Sstevel@tonic-gate 		 * don't save the byte away.
18950Sstevel@tonic-gate 		 */
18960Sstevel@tonic-gate 		if (tp->t_modes.c_iflag & IMAXBEL) {
18970Sstevel@tonic-gate 			if (canputnext(wrq))
18980Sstevel@tonic-gate 				ldterm_outchar(CTRL('g'), wrq, ebsize, tp);
18990Sstevel@tonic-gate 			goto out;
19000Sstevel@tonic-gate 		} else {
19010Sstevel@tonic-gate 			/*
19020Sstevel@tonic-gate 			 * MAX_CANON processing. free everything in
19030Sstevel@tonic-gate 			 * the current line and start with the
19040Sstevel@tonic-gate 			 * current character as the first character.
19050Sstevel@tonic-gate 			 */
19060Sstevel@tonic-gate 			DEBUG7(("ldterm_docanon: MAX_CANON processing\n"));
19070Sstevel@tonic-gate 			freemsg(tp->t_message);
19080Sstevel@tonic-gate 			tp->t_message = NULL;
19090Sstevel@tonic-gate 			tp->t_endmsg = NULL;
19100Sstevel@tonic-gate 			tp->t_msglen = 0;
19110Sstevel@tonic-gate 			tp->t_rocount = 0;	/* if it hasn't been type */
19120Sstevel@tonic-gate 			tp->t_rocol = 0;	/* it hasn't been echoed :-) */
19130Sstevel@tonic-gate 			if (tp->t_state & TS_MEUC) {
19140Sstevel@tonic-gate 				ASSERT(tp->t_eucp_mp);
19150Sstevel@tonic-gate 				tp->t_eucp = tp->t_eucp_mp->b_rptr;
19160Sstevel@tonic-gate 			}
19170Sstevel@tonic-gate 			tp->t_state &= ~TS_SLNCH;
19180Sstevel@tonic-gate 			bpt = newmsg(tp);
19190Sstevel@tonic-gate 		}
19200Sstevel@tonic-gate 	}
19210Sstevel@tonic-gate 	/*
19220Sstevel@tonic-gate 	 * Add the character to the current line.
19230Sstevel@tonic-gate 	 */
19240Sstevel@tonic-gate 	if (bpt->b_wptr >= bpt->b_datap->db_lim) {
19250Sstevel@tonic-gate 		/*
19260Sstevel@tonic-gate 		 * No more room in this mblk; save this one away, and
19270Sstevel@tonic-gate 		 * allocate a new one.
19280Sstevel@tonic-gate 		 */
19290Sstevel@tonic-gate 		bpt->b_datap->db_type = M_DATA;
19300Sstevel@tonic-gate 		if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL)
19310Sstevel@tonic-gate 			goto out;
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 		/*
19340Sstevel@tonic-gate 		 * Chain the new one to the end of the old one, and
19350Sstevel@tonic-gate 		 * mark it as the last block in the current line.
19360Sstevel@tonic-gate 		 */
19370Sstevel@tonic-gate 		tp->t_endmsg->b_cont = bpt;
19380Sstevel@tonic-gate 		tp->t_endmsg = bpt;
19390Sstevel@tonic-gate 	}
19400Sstevel@tonic-gate 	*bpt->b_wptr++ = c;
19410Sstevel@tonic-gate 	tp->t_msglen++;		/* message length in BYTES */
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate 	/*
19440Sstevel@tonic-gate 	 * In multi-byte mode, we have to keep track of where we are.
19450Sstevel@tonic-gate 	 * The first bytes of multi-byte chars get the full count for the
19460Sstevel@tonic-gate 	 * whole character.  We don't do any column calculations
19470Sstevel@tonic-gate 	 * here, but we need the information for when we do. We could
19480Sstevel@tonic-gate 	 * come across cases where we are getting garbage on the
19490Sstevel@tonic-gate 	 * line, but we're in multi-byte mode.  In that case, we may
19500Sstevel@tonic-gate 	 * see ASCII controls come in the middle of what should have been a
19510Sstevel@tonic-gate 	 * multi-byte character.  Call ldterm_eucwarn...eventually, a
19520Sstevel@tonic-gate 	 * warning message will be printed about it.
19530Sstevel@tonic-gate 	 */
19540Sstevel@tonic-gate 	if (tp->t_state & TS_MEUC) {
19550Sstevel@tonic-gate 		if (tp->t_eucleft) {	/* if in a multi-byte char already */
19560Sstevel@tonic-gate 			--tp->t_eucleft;
19570Sstevel@tonic-gate 			*tp->t_eucp++ = 0;	/* is a subsequent byte */
19580Sstevel@tonic-gate 			if (c < (uchar_t)0x20)
19590Sstevel@tonic-gate 				ldterm_eucwarn(tp);
19600Sstevel@tonic-gate 		} else { /* is the first byte of a multi-byte, or is ASCII */
19610Sstevel@tonic-gate 			if (ISASCII(c)) {
19620Sstevel@tonic-gate 				*tp->t_eucp++ =
1963*7012Sis 				    tp->t_csmethods.ldterm_dispwidth(c,
1964*7012Sis 				    (void *)tp, tp->t_modes.c_lflag & ECHOCTL);
19650Sstevel@tonic-gate 				tp->t_codeset = 0;
19660Sstevel@tonic-gate 			} else {
19670Sstevel@tonic-gate 				*tp->t_eucp++ =
1968*7012Sis 				    tp->t_csmethods.ldterm_dispwidth(c,
1969*7012Sis 				    (void *)tp, tp->t_modes.c_lflag & ECHOCTL);
19700Sstevel@tonic-gate 				tp->t_eucleft =
1971*7012Sis 				    tp->t_csmethods.ldterm_memwidth(c,
1972*7012Sis 				    (void *)tp) - 1;
19730Sstevel@tonic-gate 				tp->t_codeset = ldterm_codeset(
1974*7012Sis 				    tp->t_csdata.codeset_type, c);
19750Sstevel@tonic-gate 			}
19760Sstevel@tonic-gate 		}
19770Sstevel@tonic-gate 	}
19780Sstevel@tonic-gate 	/*
19790Sstevel@tonic-gate 	 * AT&T is concerned about the following but we aren't since
19800Sstevel@tonic-gate 	 * we have already shipped code that works.
19810Sstevel@tonic-gate 	 *
19820Sstevel@tonic-gate 	 * EOL2/XCASE should be conditioned with IEXTEN to be truly
19830Sstevel@tonic-gate 	 * POSIX conformant. This is going to cause problems for
19840Sstevel@tonic-gate 	 * pre-SVR4.0 programs that don't know about IEXTEN. Hence
19850Sstevel@tonic-gate 	 * EOL2/IEXTEN is not conditioned with IEXTEN.
19860Sstevel@tonic-gate 	 */
19870Sstevel@tonic-gate 	if (!(tp->t_state & TS_SLNCH) &&
19880Sstevel@tonic-gate 	    (c == '\n' || (c != '\0' && (c == tp->t_modes.c_cc[VEOL] ||
19890Sstevel@tonic-gate 	    (c == tp->t_modes.c_cc[VEOL2]))))) {
19900Sstevel@tonic-gate 		/*
19910Sstevel@tonic-gate 		 * || ((tp->t_modes.c_lflag & IEXTEN) && c ==
19920Sstevel@tonic-gate 		 * tp->t_modes.c_cc[VEOL2]))))) {
19930Sstevel@tonic-gate 		 */
19940Sstevel@tonic-gate 		/*
19950Sstevel@tonic-gate 		 * It's a line-termination character; send the line
19960Sstevel@tonic-gate 		 * up the stream.
19970Sstevel@tonic-gate 		 */
19980Sstevel@tonic-gate 		bpt->b_datap->db_type = M_DATA;
19990Sstevel@tonic-gate 		ldterm_msg_upstream(q, tp);
20000Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC) {
20010Sstevel@tonic-gate 			ASSERT(tp->t_eucp_mp);
20020Sstevel@tonic-gate 			tp->t_eucp = tp->t_eucp_mp->b_rptr;
20030Sstevel@tonic-gate 		}
20040Sstevel@tonic-gate 		if ((bpt = newmsg(tp)) == NULL)
20050Sstevel@tonic-gate 			goto out;
20060Sstevel@tonic-gate 	} else {
20070Sstevel@tonic-gate 		/*
20080Sstevel@tonic-gate 		 * Character was escaped with LNEXT.
20090Sstevel@tonic-gate 		 */
20100Sstevel@tonic-gate 		if (tp->t_rocount++ == 0)
20110Sstevel@tonic-gate 			tp->t_rocol = tp->t_col;
20120Sstevel@tonic-gate 		tp->t_state &= ~(TS_SLNCH|TS_QUOT);
20130Sstevel@tonic-gate 		/*
20140Sstevel@tonic-gate 		 * If the current character is a single byte and single
20150Sstevel@tonic-gate 		 * column character and it is the backslash character and
20160Sstevel@tonic-gate 		 * IEXTEN, then the state will have TS_QUOT.
20170Sstevel@tonic-gate 		 */
20180Sstevel@tonic-gate 		if ((c == '\\') && (tp->t_modes.c_lflag & IEXTEN) &&
20190Sstevel@tonic-gate 		    (!(tp->t_state & TS_MEUC) ||
20200Sstevel@tonic-gate 		    ((tp->t_state & TS_MEUC) && (!tp->t_eucleft))))
20210Sstevel@tonic-gate 			tp->t_state |= TS_QUOT;
20220Sstevel@tonic-gate 	}
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 	/*
20250Sstevel@tonic-gate 	 * Echo it.
20260Sstevel@tonic-gate 	 */
20270Sstevel@tonic-gate 	if (tp->t_state & TS_ERASE) {
20280Sstevel@tonic-gate 		tp->t_state &= ~TS_ERASE;
20290Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & ECHO)
20300Sstevel@tonic-gate 			ldterm_outchar('/', wrq, ebsize, tp);
20310Sstevel@tonic-gate 	}
20320Sstevel@tonic-gate 	if (tp->t_modes.c_lflag & ECHO)
20330Sstevel@tonic-gate 		(void) ldterm_echo(c, wrq, ebsize, tp);
20340Sstevel@tonic-gate 	else {
20350Sstevel@tonic-gate 		/*
20360Sstevel@tonic-gate 		 * Echo NL when ECHO turned off, if ECHONL flag is
20370Sstevel@tonic-gate 		 * set.
20380Sstevel@tonic-gate 		 */
20390Sstevel@tonic-gate 		if (c == '\n' && (tp->t_modes.c_lflag & ECHONL))
20400Sstevel@tonic-gate 			ldterm_outchar(c, wrq, ebsize, tp);
20410Sstevel@tonic-gate 	}
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate out:
20440Sstevel@tonic-gate 
20450Sstevel@tonic-gate 	return (bpt);
20460Sstevel@tonic-gate }
20470Sstevel@tonic-gate 
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate static int
ldterm_unget(ldtermstd_state_t * tp)20500Sstevel@tonic-gate ldterm_unget(ldtermstd_state_t *tp)
20510Sstevel@tonic-gate {
20520Sstevel@tonic-gate 	mblk_t *bpt;
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate 	if ((bpt = tp->t_endmsg) == NULL)
20550Sstevel@tonic-gate 		return (-1);	/* no buffers */
20560Sstevel@tonic-gate 	if (bpt->b_rptr == bpt->b_wptr)
20570Sstevel@tonic-gate 		return (-1);	/* zero-length record */
20580Sstevel@tonic-gate 	tp->t_msglen--;		/* one fewer character */
20590Sstevel@tonic-gate 	return (*--bpt->b_wptr);
20600Sstevel@tonic-gate }
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 
20630Sstevel@tonic-gate static void
ldterm_trim(ldtermstd_state_t * tp)20640Sstevel@tonic-gate ldterm_trim(ldtermstd_state_t *tp)
20650Sstevel@tonic-gate {
20660Sstevel@tonic-gate 	mblk_t *bpt;
20670Sstevel@tonic-gate 	mblk_t *bp;
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 	ASSERT(tp->t_endmsg);
20700Sstevel@tonic-gate 	bpt = tp->t_endmsg;
20710Sstevel@tonic-gate 
20720Sstevel@tonic-gate 	if (bpt->b_rptr == bpt->b_wptr) {
20730Sstevel@tonic-gate 		/*
20740Sstevel@tonic-gate 		 * This mblk is now empty. Find the previous mblk;
20750Sstevel@tonic-gate 		 * throw this one away, unless it's the first one.
20760Sstevel@tonic-gate 		 */
20770Sstevel@tonic-gate 		bp = tp->t_message;
20780Sstevel@tonic-gate 		if (bp != bpt) {
20790Sstevel@tonic-gate 			while (bp->b_cont != bpt) {
20800Sstevel@tonic-gate 				ASSERT(bp->b_cont);
20810Sstevel@tonic-gate 				bp = bp->b_cont;
20820Sstevel@tonic-gate 			}
20830Sstevel@tonic-gate 			bp->b_cont = NULL;
20840Sstevel@tonic-gate 			freeb(bpt);
20850Sstevel@tonic-gate 			tp->t_endmsg = bp;	/* point to that mblk */
20860Sstevel@tonic-gate 		}
20870Sstevel@tonic-gate 	}
20880Sstevel@tonic-gate }
20890Sstevel@tonic-gate 
20900Sstevel@tonic-gate 
20910Sstevel@tonic-gate /*
20920Sstevel@tonic-gate  * Rubout one character from the current line being built for tp as
20930Sstevel@tonic-gate  * cleanly as possible.  q is the write queue for tp. Most of this
20940Sstevel@tonic-gate  * can't be applied to multi-byte processing.  We do our own thing
20950Sstevel@tonic-gate  * for that... See the "ldterm_eucerase" routine.  We never call
20960Sstevel@tonic-gate  * ldterm_rubout on a multi-byte or multi-column character.
20970Sstevel@tonic-gate  */
20980Sstevel@tonic-gate static void
ldterm_rubout(uchar_t c,queue_t * q,size_t ebsize,ldtermstd_state_t * tp)20990Sstevel@tonic-gate ldterm_rubout(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
21000Sstevel@tonic-gate {
21010Sstevel@tonic-gate 	int tabcols;
21020Sstevel@tonic-gate 	static unsigned char crtrubout[] = "\b \b\b \b";
21030Sstevel@tonic-gate #define	RUBOUT1	&crtrubout[3]	/* rub out one position */
21040Sstevel@tonic-gate #define	RUBOUT2	&crtrubout[0]	/* rub out two positions */
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	if (!(tp->t_modes.c_lflag & ECHO))
21070Sstevel@tonic-gate 		return;
21080Sstevel@tonic-gate 	if (tp->t_modes.c_lflag & ECHOE) {
21090Sstevel@tonic-gate 		/*
21100Sstevel@tonic-gate 		 * "CRT rubout"; try erasing it from the screen.
21110Sstevel@tonic-gate 		 */
21120Sstevel@tonic-gate 		if (tp->t_rocount == 0) {
21130Sstevel@tonic-gate 			/*
21140Sstevel@tonic-gate 			 * After the character being erased was
21150Sstevel@tonic-gate 			 * echoed, some data was written to the
21160Sstevel@tonic-gate 			 * terminal; we can't erase it cleanly, so we
21170Sstevel@tonic-gate 			 * just reprint the whole line as if the user
21180Sstevel@tonic-gate 			 * had typed the reprint character.
21190Sstevel@tonic-gate 			 */
21200Sstevel@tonic-gate 			ldterm_reprint(q, ebsize, tp);
21210Sstevel@tonic-gate 			return;
21220Sstevel@tonic-gate 		} else {
21230Sstevel@tonic-gate 			/*
21240Sstevel@tonic-gate 			 * XXX what about escaped characters?
21250Sstevel@tonic-gate 			 */
21260Sstevel@tonic-gate 			switch (typetab[c]) {
21270Sstevel@tonic-gate 
21280Sstevel@tonic-gate 			case ORDINARY:
21290Sstevel@tonic-gate 				if ((tp->t_modes.c_lflag & XCASE) &&
21300Sstevel@tonic-gate 				    omaptab[c])
21310Sstevel@tonic-gate 					ldterm_outstring(RUBOUT1, 3, q, ebsize,
2132*7012Sis 					    tp);
21330Sstevel@tonic-gate 				ldterm_outstring(RUBOUT1, 3, q, ebsize, tp);
21340Sstevel@tonic-gate 				break;
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 			case VTAB:
21370Sstevel@tonic-gate 			case BACKSPACE:
21380Sstevel@tonic-gate 			case CONTROL:
21390Sstevel@tonic-gate 			case RETURN:
21400Sstevel@tonic-gate 			case NEWLINE:
21410Sstevel@tonic-gate 				if ((tp->t_modes.c_lflag & ECHOCTL) &&
21420Sstevel@tonic-gate 				    (tp->t_modes.c_lflag & IEXTEN))
21430Sstevel@tonic-gate 					ldterm_outstring(RUBOUT2, 6, q, ebsize,
2144*7012Sis 					    tp);
21450Sstevel@tonic-gate 				break;
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 			case TAB:
21480Sstevel@tonic-gate 				if (tp->t_rocount < tp->t_msglen) {
21490Sstevel@tonic-gate 					/*
21500Sstevel@tonic-gate 					 * While the tab being erased was
21510Sstevel@tonic-gate 					 * expanded, some data was written
21520Sstevel@tonic-gate 					 * to the terminal; we can't erase
21530Sstevel@tonic-gate 					 * it cleanly, so we just reprint
21540Sstevel@tonic-gate 					 * the whole line as if the user
21550Sstevel@tonic-gate 					 * had typed the reprint character.
21560Sstevel@tonic-gate 					 */
21570Sstevel@tonic-gate 					ldterm_reprint(q, ebsize, tp);
21580Sstevel@tonic-gate 					return;
21590Sstevel@tonic-gate 				}
21600Sstevel@tonic-gate 				tabcols = ldterm_tabcols(tp);
21610Sstevel@tonic-gate 				while (--tabcols >= 0)
21620Sstevel@tonic-gate 					ldterm_outchar('\b', q, ebsize, tp);
21630Sstevel@tonic-gate 				break;
21640Sstevel@tonic-gate 			}
21650Sstevel@tonic-gate 		}
21660Sstevel@tonic-gate 	} else if ((tp->t_modes.c_lflag & ECHOPRT) &&
2167*7012Sis 	    (tp->t_modes.c_lflag & IEXTEN)) {
21680Sstevel@tonic-gate 		/*
21690Sstevel@tonic-gate 		 * "Printing rubout"; echo it between \ and /.
21700Sstevel@tonic-gate 		 */
21710Sstevel@tonic-gate 		if (!(tp->t_state & TS_ERASE)) {
21720Sstevel@tonic-gate 			ldterm_outchar('\\', q, ebsize, tp);
21730Sstevel@tonic-gate 			tp->t_state |= TS_ERASE;
21740Sstevel@tonic-gate 		}
21750Sstevel@tonic-gate 		(void) ldterm_echo(c, q, ebsize, tp);
21760Sstevel@tonic-gate 	} else
21770Sstevel@tonic-gate 		(void) ldterm_echo(tp->t_modes.c_cc[VERASE], q, ebsize, tp);
21780Sstevel@tonic-gate 	tp->t_rocount--;	/* we "unechoed" this character */
21790Sstevel@tonic-gate }
21800Sstevel@tonic-gate 
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate /*
21830Sstevel@tonic-gate  * Find the number of characters the tab we just deleted took up by
21840Sstevel@tonic-gate  * zipping through the current line and recomputing the column
21850Sstevel@tonic-gate  * number.
21860Sstevel@tonic-gate  */
21870Sstevel@tonic-gate static int
ldterm_tabcols(ldtermstd_state_t * tp)21880Sstevel@tonic-gate ldterm_tabcols(ldtermstd_state_t *tp)
21890Sstevel@tonic-gate {
21900Sstevel@tonic-gate 	int col;
21910Sstevel@tonic-gate 	int i;
21920Sstevel@tonic-gate 	mblk_t *bp;
21930Sstevel@tonic-gate 	unsigned char *readp, *endp;
21940Sstevel@tonic-gate 	unsigned char c;
21950Sstevel@tonic-gate 	uchar_t *startp;
21960Sstevel@tonic-gate 	char errflg;
21970Sstevel@tonic-gate 	uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	col = tp->t_rocol;
22000Sstevel@tonic-gate 	/*
22010Sstevel@tonic-gate 	 * If we're doing multi-byte stuff, zip through the list of
22020Sstevel@tonic-gate 	 * widths to figure out where we are (we've kept track in most
22030Sstevel@tonic-gate 	 * cases).
22040Sstevel@tonic-gate 	 */
22050Sstevel@tonic-gate 	if (tp->t_state & TS_MEUC) {
22060Sstevel@tonic-gate 		ASSERT(tp->t_eucp_mp);
22070Sstevel@tonic-gate 		bp = tp->t_message;
22080Sstevel@tonic-gate 		startp = bp->b_datap->db_base;
22090Sstevel@tonic-gate 		readp = tp->t_eucp_mp->b_rptr;
22100Sstevel@tonic-gate 		endp = tp->t_eucp;
22110Sstevel@tonic-gate 		errflg = (char)0;
22120Sstevel@tonic-gate 		while (readp < endp) {
22130Sstevel@tonic-gate 			switch (*readp) {
22140Sstevel@tonic-gate 			case EUC_TWIDTH:	/* it's a tab */
22150Sstevel@tonic-gate 				col |= 07;	/* bump up */
22160Sstevel@tonic-gate 				col++;
22170Sstevel@tonic-gate 				break;
22180Sstevel@tonic-gate 			case EUC_BSWIDTH:	/* backspace */
22190Sstevel@tonic-gate 				if (col)
22200Sstevel@tonic-gate 					col--;
22210Sstevel@tonic-gate 				break;
22220Sstevel@tonic-gate 			case EUC_NLWIDTH:	/* newline */
22230Sstevel@tonic-gate 				if (tp->t_modes.c_oflag & ONLRET)
22240Sstevel@tonic-gate 					col = 0;
22250Sstevel@tonic-gate 				break;
22260Sstevel@tonic-gate 			case EUC_CRWIDTH:	/* return */
22270Sstevel@tonic-gate 				col = 0;
22280Sstevel@tonic-gate 				break;
22290Sstevel@tonic-gate 			case UNKNOWN_WIDTH:	/* UTF-8 unknown width */
22300Sstevel@tonic-gate 				if (tp->t_csdata.codeset_type !=
22310Sstevel@tonic-gate 				    LDTERM_CS_TYPE_UTF8 || errflg) {
22320Sstevel@tonic-gate 					*readp = 1;
22330Sstevel@tonic-gate 					col++;
22340Sstevel@tonic-gate 					break;
22350Sstevel@tonic-gate 				}
22360Sstevel@tonic-gate 				/*
22370Sstevel@tonic-gate 				 * Collect the current UTF-8 character bytes
22380Sstevel@tonic-gate 				 * from (possibly multiple) data buffers so
22390Sstevel@tonic-gate 				 * that we can figure out the display width.
22400Sstevel@tonic-gate 				 */
22410Sstevel@tonic-gate 				u8[0] = *startp;
22420Sstevel@tonic-gate 				for (i = 1; (i < LDTERM_CS_MAX_BYTE_LENGTH) &&
2243*7012Sis 				    (*(readp + i) == 0); i++) {
22440Sstevel@tonic-gate 					startp++;
22450Sstevel@tonic-gate 					if (startp >= bp->b_datap->db_lim) {
22460Sstevel@tonic-gate 						if (bp->b_cont) {
22470Sstevel@tonic-gate 							bp = bp->b_cont;
22480Sstevel@tonic-gate 							startp =
2249*7012Sis 							    bp->b_datap->
2250*7012Sis 							    db_base;
22510Sstevel@tonic-gate 						} else {
22520Sstevel@tonic-gate 							*readp = 1;
22530Sstevel@tonic-gate 							col++;
22540Sstevel@tonic-gate 							break;
22550Sstevel@tonic-gate 						}
22560Sstevel@tonic-gate 					}
22570Sstevel@tonic-gate 					u8[i] = *startp;
22580Sstevel@tonic-gate 				}
22590Sstevel@tonic-gate 
22600Sstevel@tonic-gate 				/* tp->t_eucp_mp contains wrong info?? */
22610Sstevel@tonic-gate 				if (*readp == 1)
22620Sstevel@tonic-gate 					break;
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 				*readp = ldterm_utf8_width(u8, i);
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 				col += *readp;
22670Sstevel@tonic-gate 				readp += (i - 1);
22680Sstevel@tonic-gate 				break;
22690Sstevel@tonic-gate 			default:
22700Sstevel@tonic-gate 				col += *readp;
22710Sstevel@tonic-gate 				break;
22720Sstevel@tonic-gate 			}
22730Sstevel@tonic-gate 			++readp;
22740Sstevel@tonic-gate 			++startp;
22750Sstevel@tonic-gate 			if (startp >= bp->b_datap->db_lim) {
22760Sstevel@tonic-gate 				if (bp->b_cont) {
22770Sstevel@tonic-gate 					bp = bp->b_cont;
22780Sstevel@tonic-gate 					startp = bp->b_datap->db_base;
22790Sstevel@tonic-gate 				} else {
22800Sstevel@tonic-gate 					/*
22810Sstevel@tonic-gate 					 * This will happen only if
22820Sstevel@tonic-gate 					 * tp->t_eucp_mp contains wrong
22830Sstevel@tonic-gate 					 * display width info.
22840Sstevel@tonic-gate 					 */
22850Sstevel@tonic-gate 					errflg = (char)1;
22860Sstevel@tonic-gate 					startp--;
22870Sstevel@tonic-gate 				}
22880Sstevel@tonic-gate 			}
22890Sstevel@tonic-gate 		}
22900Sstevel@tonic-gate 		goto eucout;	/* finished! */
22910Sstevel@tonic-gate 	}
22920Sstevel@tonic-gate 	bp = tp->t_message;
22930Sstevel@tonic-gate 	do {
22940Sstevel@tonic-gate 		readp = bp->b_rptr;
22950Sstevel@tonic-gate 		while (readp < bp->b_wptr) {
22960Sstevel@tonic-gate 			c = *readp++;
22970Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & ECHOCTL) &&
22980Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & IEXTEN)) {
22990Sstevel@tonic-gate 				if (c <= 037 && c != '\t' && c != '\n' ||
23000Sstevel@tonic-gate 				    c == 0177) {
23010Sstevel@tonic-gate 					col += 2;
23020Sstevel@tonic-gate 					continue;
23030Sstevel@tonic-gate 				}
23040Sstevel@tonic-gate 			}
23050Sstevel@tonic-gate 			/*
23060Sstevel@tonic-gate 			 * Column position calculated here.
23070Sstevel@tonic-gate 			 */
23080Sstevel@tonic-gate 			switch (typetab[c]) {
23090Sstevel@tonic-gate 
23100Sstevel@tonic-gate 				/*
23110Sstevel@tonic-gate 				 * Ordinary characters; advance by
23120Sstevel@tonic-gate 				 * one.
23130Sstevel@tonic-gate 				 */
23140Sstevel@tonic-gate 			case ORDINARY:
23150Sstevel@tonic-gate 				col++;
23160Sstevel@tonic-gate 				break;
23170Sstevel@tonic-gate 
23180Sstevel@tonic-gate 				/*
23190Sstevel@tonic-gate 				 * Non-printing characters; nothing
23200Sstevel@tonic-gate 				 * happens.
23210Sstevel@tonic-gate 				 */
23220Sstevel@tonic-gate 			case CONTROL:
23230Sstevel@tonic-gate 				break;
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate 				/* Backspace */
23260Sstevel@tonic-gate 			case BACKSPACE:
23270Sstevel@tonic-gate 				if (col != 0)
23280Sstevel@tonic-gate 					col--;
23290Sstevel@tonic-gate 				break;
23300Sstevel@tonic-gate 
23310Sstevel@tonic-gate 				/* Newline; column depends on flags. */
23320Sstevel@tonic-gate 			case NEWLINE:
23330Sstevel@tonic-gate 				if (tp->t_modes.c_oflag & ONLRET)
23340Sstevel@tonic-gate 					col = 0;
23350Sstevel@tonic-gate 				break;
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 				/* tab */
23380Sstevel@tonic-gate 			case TAB:
23390Sstevel@tonic-gate 				col |= 07;
23400Sstevel@tonic-gate 				col++;
23410Sstevel@tonic-gate 				break;
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 				/* vertical motion */
23440Sstevel@tonic-gate 			case VTAB:
23450Sstevel@tonic-gate 				break;
23460Sstevel@tonic-gate 
23470Sstevel@tonic-gate 				/* carriage return */
23480Sstevel@tonic-gate 			case RETURN:
23490Sstevel@tonic-gate 				col = 0;
23500Sstevel@tonic-gate 				break;
23510Sstevel@tonic-gate 			}
23520Sstevel@tonic-gate 		}
23530Sstevel@tonic-gate 	} while ((bp = bp->b_cont) != NULL);	/* next block, if any */
23540Sstevel@tonic-gate 
23550Sstevel@tonic-gate 	/*
23560Sstevel@tonic-gate 	 * "col" is now the column number before the tab. "tp->t_col"
23570Sstevel@tonic-gate 	 * is still the column number after the tab, since we haven't
23580Sstevel@tonic-gate 	 * erased the tab yet. Thus "tp->t_col - col" is the number
23590Sstevel@tonic-gate 	 * of positions the tab moved.
23600Sstevel@tonic-gate 	 */
23610Sstevel@tonic-gate eucout:
23620Sstevel@tonic-gate 	col = tp->t_col - col;
23630Sstevel@tonic-gate 	if (col > 8)
23640Sstevel@tonic-gate 		col = 8;	/* overflow screw */
23650Sstevel@tonic-gate 	return (col);
23660Sstevel@tonic-gate }
23670Sstevel@tonic-gate 
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate /*
23700Sstevel@tonic-gate  * Erase a single character; We ONLY ONLY deal with ASCII or
23710Sstevel@tonic-gate  * single-column single-byte codeset character.  For multi-byte characters,
23720Sstevel@tonic-gate  * see "ldterm_csi_erase".
23730Sstevel@tonic-gate  */
23740Sstevel@tonic-gate static void
ldterm_erase(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)23750Sstevel@tonic-gate ldterm_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
23760Sstevel@tonic-gate {
23770Sstevel@tonic-gate 	int c;
23780Sstevel@tonic-gate 
23790Sstevel@tonic-gate 	if ((c = ldterm_unget(tp)) != -1) {
23800Sstevel@tonic-gate 		ldterm_rubout((unsigned char) c, q, ebsize, tp);
23810Sstevel@tonic-gate 		ldterm_trim(tp);
23820Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC)
23830Sstevel@tonic-gate 			--tp->t_eucp;
23840Sstevel@tonic-gate 	}
23850Sstevel@tonic-gate }
23860Sstevel@tonic-gate 
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate /*
23890Sstevel@tonic-gate  * Erase an entire word, single-byte EUC only please.
23900Sstevel@tonic-gate  */
23910Sstevel@tonic-gate static void
ldterm_werase(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)23920Sstevel@tonic-gate ldterm_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
23930Sstevel@tonic-gate {
23940Sstevel@tonic-gate 	int c;
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 	/*
23970Sstevel@tonic-gate 	 * Erase trailing white space, if any.
23980Sstevel@tonic-gate 	 */
23990Sstevel@tonic-gate 	while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
24000Sstevel@tonic-gate 		ldterm_rubout((unsigned char) c, q, ebsize, tp);
24010Sstevel@tonic-gate 		ldterm_trim(tp);
24020Sstevel@tonic-gate 	}
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 	/*
24050Sstevel@tonic-gate 	 * Erase non-white-space characters, if any.
24060Sstevel@tonic-gate 	 */
24070Sstevel@tonic-gate 	while (c != -1 && c != ' ' && c != '\t') {
24080Sstevel@tonic-gate 		ldterm_rubout((unsigned char) c, q, ebsize, tp);
24090Sstevel@tonic-gate 		ldterm_trim(tp);
24100Sstevel@tonic-gate 		c = ldterm_unget(tp);
24110Sstevel@tonic-gate 	}
24120Sstevel@tonic-gate 	if (c != -1) {
24130Sstevel@tonic-gate 		/*
24140Sstevel@tonic-gate 		 * We removed one too many characters; put the last
24150Sstevel@tonic-gate 		 * one back.
24160Sstevel@tonic-gate 		 */
24170Sstevel@tonic-gate 		tp->t_endmsg->b_wptr++;	/* put 'c' back */
24180Sstevel@tonic-gate 		tp->t_msglen++;
24190Sstevel@tonic-gate 	}
24200Sstevel@tonic-gate }
24210Sstevel@tonic-gate 
24220Sstevel@tonic-gate 
24230Sstevel@tonic-gate /*
24240Sstevel@tonic-gate  * ldterm_csi_werase - This is multi-byte equivalent of "word erase".
24250Sstevel@tonic-gate  * "Word erase" only makes sense in languages which space between words,
24260Sstevel@tonic-gate  * and it's presumptuous for us to attempt "word erase" when we don't
24270Sstevel@tonic-gate  * know anything about what's really going on.  It makes no sense for
24280Sstevel@tonic-gate  * many languages, as the criteria for defining words and tokens may
24290Sstevel@tonic-gate  * be completely different.
24300Sstevel@tonic-gate  *
24310Sstevel@tonic-gate  * In the TS_MEUC case (which is how we got here), we define a token to
24320Sstevel@tonic-gate  * be space- or tab-delimited, and erase one of them.  It helps to
24330Sstevel@tonic-gate  * have this for command lines, but it's otherwise useless for text
24340Sstevel@tonic-gate  * editing applications; you need more sophistication than we can
24350Sstevel@tonic-gate  * provide here.
24360Sstevel@tonic-gate  */
24370Sstevel@tonic-gate static void
ldterm_csi_werase(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)24380Sstevel@tonic-gate ldterm_csi_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
24390Sstevel@tonic-gate {
24400Sstevel@tonic-gate 	int c, i;
24410Sstevel@tonic-gate 	int len;
24420Sstevel@tonic-gate 	uchar_t *ip;
24430Sstevel@tonic-gate 	uchar_t	u8[LDTERM_CS_MAX_BYTE_LENGTH];
24440Sstevel@tonic-gate 	uchar_t	u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
24450Sstevel@tonic-gate 
24460Sstevel@tonic-gate 	/*
24470Sstevel@tonic-gate 	 * ip points to the width of the actual bytes.  t_eucp points
24480Sstevel@tonic-gate 	 * one byte beyond, where the next thing will be inserted.
24490Sstevel@tonic-gate 	 */
24500Sstevel@tonic-gate 	ip = tp->t_eucp - 1;
24510Sstevel@tonic-gate 	/*
24520Sstevel@tonic-gate 	 * Erase trailing white space, if any.
24530Sstevel@tonic-gate 	 */
24540Sstevel@tonic-gate 	while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
24550Sstevel@tonic-gate 		tp->t_eucp--;
24560Sstevel@tonic-gate 		ldterm_rubout((unsigned char) c, q, ebsize, tp);
24570Sstevel@tonic-gate 		ldterm_trim(tp);
24580Sstevel@tonic-gate 		--ip;
24590Sstevel@tonic-gate 	}
24600Sstevel@tonic-gate 
24610Sstevel@tonic-gate 	/*
24620Sstevel@tonic-gate 	 * Erase non-white-space characters, if any.  The outer loop
24630Sstevel@tonic-gate 	 * bops through each byte in the buffer. Multi-byte is removed, as
24640Sstevel@tonic-gate 	 * is ASCII, one byte at a time. The inner loop (for) is only
24650Sstevel@tonic-gate 	 * executed for first bytes of multi-byte.  The inner loop erases
24660Sstevel@tonic-gate 	 * the number of columns required for the multi-byte char.  We check
24670Sstevel@tonic-gate 	 * for ASCII first, and ldterm_rubout knows about ASCII.
24680Sstevel@tonic-gate 	 */
24690Sstevel@tonic-gate 	len = 0;
24700Sstevel@tonic-gate 	while (c != -1 && c != ' ' && c != '\t') {
24710Sstevel@tonic-gate 		tp->t_eucp--;
24720Sstevel@tonic-gate 		if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
24730Sstevel@tonic-gate 			u8[len++] = (uchar_t)c;
24740Sstevel@tonic-gate 		}
24750Sstevel@tonic-gate 		/*
24760Sstevel@tonic-gate 		 * Unlike EUC, except the leading byte, some bytes of
24770Sstevel@tonic-gate 		 * a non-EUC multi-byte characters are in the ASCII code
24780Sstevel@tonic-gate 		 * range, esp., 0x41 ~ 0x7a. Thus, we cannot simply check
24790Sstevel@tonic-gate 		 * ISASCII().
24800Sstevel@tonic-gate 		 * Checking the (*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH)
24810Sstevel@tonic-gate 		 * will ensure that it is a single byte character (even though
24820Sstevel@tonic-gate 		 * it is on display width not byte length) and can be further
24830Sstevel@tonic-gate 		 * checked whether it is an ASCII character or not.
24840Sstevel@tonic-gate 		 *
24850Sstevel@tonic-gate 		 * When ECHOCTL is on and 'c' is an ASCII control character,
24860Sstevel@tonic-gate 		 * *ip == 2 happens.
24870Sstevel@tonic-gate 		 */
24880Sstevel@tonic-gate 		if ((*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH) &&
24890Sstevel@tonic-gate 		    ISASCII(c)) {
24900Sstevel@tonic-gate 			ldterm_rubout((unsigned char) c, q, ebsize, tp);
24910Sstevel@tonic-gate 			len = 0;
24920Sstevel@tonic-gate 		} else if (*ip) {
24930Sstevel@tonic-gate 			if (*ip == UNKNOWN_WIDTH) {
24940Sstevel@tonic-gate 				if (tp->t_csdata.codeset_type ==
24950Sstevel@tonic-gate 				    LDTERM_CS_TYPE_UTF8) {
24960Sstevel@tonic-gate 					for (i = 0; i < len; i++)
24970Sstevel@tonic-gate 						u8_2[i] = u8[len - i - 1];
24980Sstevel@tonic-gate 					*ip = ldterm_utf8_width(u8_2, len);
24990Sstevel@tonic-gate 				} else {
25000Sstevel@tonic-gate 					*ip = 1;
25010Sstevel@tonic-gate 				}
25020Sstevel@tonic-gate 			}
25030Sstevel@tonic-gate 			/*
25040Sstevel@tonic-gate 			 * erase for number of columns required for
25050Sstevel@tonic-gate 			 * this multi-byte character. Hopefully, matches
25060Sstevel@tonic-gate 			 * ldterm_dispwidth!
25070Sstevel@tonic-gate 			 */
25080Sstevel@tonic-gate 			for (i = 0; i < (int)*ip; i++)
25090Sstevel@tonic-gate 				ldterm_rubout(' ', q, ebsize, tp);
25100Sstevel@tonic-gate 			len = 0;
25110Sstevel@tonic-gate 		}
25120Sstevel@tonic-gate 		ldterm_trim(tp);
25130Sstevel@tonic-gate 		--ip;
25140Sstevel@tonic-gate 		c = ldterm_unget(tp);
25150Sstevel@tonic-gate 	}
25160Sstevel@tonic-gate 	if (c != -1) {
25170Sstevel@tonic-gate 		/*
25180Sstevel@tonic-gate 		 * We removed one too many characters; put the last
25190Sstevel@tonic-gate 		 * one back.
25200Sstevel@tonic-gate 		 */
25210Sstevel@tonic-gate 		tp->t_endmsg->b_wptr++;	/* put 'c' back */
25220Sstevel@tonic-gate 		tp->t_msglen++;
25230Sstevel@tonic-gate 	}
25240Sstevel@tonic-gate }
25250Sstevel@tonic-gate 
25260Sstevel@tonic-gate 
25270Sstevel@tonic-gate /*
25280Sstevel@tonic-gate  * Kill an entire line, erasing each character one-by-one (if ECHOKE
25290Sstevel@tonic-gate  * is set) or just echoing the kill character, followed by a newline
25300Sstevel@tonic-gate  * (if ECHOK is set).  Multi-byte processing is included here.
25310Sstevel@tonic-gate  */
25320Sstevel@tonic-gate 
25330Sstevel@tonic-gate static void
ldterm_kill(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)25340Sstevel@tonic-gate ldterm_kill(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
25350Sstevel@tonic-gate {
25360Sstevel@tonic-gate 	int c, i;
25370Sstevel@tonic-gate 	int len;
25380Sstevel@tonic-gate 	uchar_t *ip;
25390Sstevel@tonic-gate 	uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
25400Sstevel@tonic-gate 	uchar_t u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	if ((tp->t_modes.c_lflag & ECHOKE) &&
25430Sstevel@tonic-gate 	    (tp->t_modes.c_lflag & IEXTEN) &&
25440Sstevel@tonic-gate 	    (tp->t_msglen == tp->t_rocount)) {
25450Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC) {
25460Sstevel@tonic-gate 			ip = tp->t_eucp - 1;
25470Sstevel@tonic-gate 			/*
25480Sstevel@tonic-gate 			 * This loop similar to "ldterm_csi_werase" above.
25490Sstevel@tonic-gate 			 */
25500Sstevel@tonic-gate 			len = 0;
25510Sstevel@tonic-gate 			while ((c = ldterm_unget(tp)) != (-1)) {
25520Sstevel@tonic-gate 				tp->t_eucp--;
25530Sstevel@tonic-gate 				if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
25540Sstevel@tonic-gate 					u8[len++] = (uchar_t)c;
25550Sstevel@tonic-gate 				}
25560Sstevel@tonic-gate 				if ((*ip == 1 || *ip == 2 ||
25570Sstevel@tonic-gate 				    *ip > UNKNOWN_WIDTH) && ISASCII(c)) {
25580Sstevel@tonic-gate 					ldterm_rubout((unsigned char) c, q,
2559*7012Sis 					    ebsize, tp);
25600Sstevel@tonic-gate 					len = 0;
25610Sstevel@tonic-gate 				} else if (*ip) {
25620Sstevel@tonic-gate 					if (*ip == UNKNOWN_WIDTH) {
25630Sstevel@tonic-gate 						if (tp->t_csdata.codeset_type
25640Sstevel@tonic-gate 						    == LDTERM_CS_TYPE_UTF8) {
25650Sstevel@tonic-gate 							for (i = 0; i < len;
25660Sstevel@tonic-gate 							    i++)
25670Sstevel@tonic-gate 								u8_2[i] =
25680Sstevel@tonic-gate 								    u8[len-i-1];
25690Sstevel@tonic-gate 							*ip = ldterm_utf8_width(
2570*7012Sis 							    u8_2, len);
25710Sstevel@tonic-gate 						} else {
25720Sstevel@tonic-gate 							*ip = 1;
25730Sstevel@tonic-gate 						}
25740Sstevel@tonic-gate 					}
25750Sstevel@tonic-gate 					for (i = 0; i < (int)*ip; i++)
25760Sstevel@tonic-gate 						ldterm_rubout(' ', q, ebsize,
25770Sstevel@tonic-gate 						    tp);
25780Sstevel@tonic-gate 					len = 0;
25790Sstevel@tonic-gate 				}
25800Sstevel@tonic-gate 				ldterm_trim(tp);
25810Sstevel@tonic-gate 				--ip;
25820Sstevel@tonic-gate 			}
25830Sstevel@tonic-gate 		} else {
25840Sstevel@tonic-gate 			while ((c = ldterm_unget(tp)) != -1) {
25850Sstevel@tonic-gate 				ldterm_rubout((unsigned char) c, q, ebsize, tp);
25860Sstevel@tonic-gate 				ldterm_trim(tp);
25870Sstevel@tonic-gate 			}
25880Sstevel@tonic-gate 		}
25890Sstevel@tonic-gate 	} else {
25900Sstevel@tonic-gate 		(void) ldterm_echo(tp->t_modes.c_cc[VKILL], q, ebsize, tp);
25910Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & ECHOK)
25920Sstevel@tonic-gate 			(void) ldterm_echo('\n', q, ebsize, tp);
25930Sstevel@tonic-gate 		while (ldterm_unget(tp) != -1) {
25940Sstevel@tonic-gate 			if (tp->t_state & TS_MEUC)
25950Sstevel@tonic-gate 				--tp->t_eucp;
25960Sstevel@tonic-gate 			ldterm_trim(tp);
25970Sstevel@tonic-gate 		}
25980Sstevel@tonic-gate 		tp->t_rocount = 0;
25990Sstevel@tonic-gate 		if (tp->t_state & TS_MEUC)
26000Sstevel@tonic-gate 			tp->t_eucp = tp->t_eucp_mp->b_rptr;
26010Sstevel@tonic-gate 	}
26020Sstevel@tonic-gate 	tp->t_state &= ~(TS_QUOT|TS_ERASE|TS_SLNCH);
26030Sstevel@tonic-gate }
26040Sstevel@tonic-gate 
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate /*
26070Sstevel@tonic-gate  * Reprint the current input line. We assume c_cc has already been
26080Sstevel@tonic-gate  * checked. XXX just the current line, not the whole queue? What
26090Sstevel@tonic-gate  * about DEFECHO mode?
26100Sstevel@tonic-gate  */
26110Sstevel@tonic-gate static void
ldterm_reprint(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)26120Sstevel@tonic-gate ldterm_reprint(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
26130Sstevel@tonic-gate {
26140Sstevel@tonic-gate 	mblk_t *bp;
26150Sstevel@tonic-gate 	unsigned char *readp;
26160Sstevel@tonic-gate 
26170Sstevel@tonic-gate 	if (tp->t_modes.c_cc[VREPRINT] != (unsigned char) 0)
26180Sstevel@tonic-gate 		(void) ldterm_echo(tp->t_modes.c_cc[VREPRINT], q, ebsize, tp);
26190Sstevel@tonic-gate 	ldterm_outchar('\n', q, ebsize, tp);
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 	bp = tp->t_message;
26220Sstevel@tonic-gate 	do {
26230Sstevel@tonic-gate 		readp = bp->b_rptr;
26240Sstevel@tonic-gate 		while (readp < bp->b_wptr)
26250Sstevel@tonic-gate 			(void) ldterm_echo(*readp++, q, ebsize, tp);
26260Sstevel@tonic-gate 	} while ((bp = bp->b_cont) != NULL);	/* next block, if any */
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate 	tp->t_state &= ~TS_ERASE;
26290Sstevel@tonic-gate 	tp->t_rocount = tp->t_msglen;	/* we reechoed the entire line */
26300Sstevel@tonic-gate 	tp->t_rocol = 0;
26310Sstevel@tonic-gate }
26320Sstevel@tonic-gate 
26330Sstevel@tonic-gate 
26340Sstevel@tonic-gate /*
26350Sstevel@tonic-gate  * Non canonical processing. Called with q locked from  ldtermrsrv.
26360Sstevel@tonic-gate  *
26370Sstevel@tonic-gate  */
26380Sstevel@tonic-gate static mblk_t *
ldterm_dononcanon(mblk_t * bp,mblk_t * bpt,size_t ebsize,queue_t * q,ldtermstd_state_t * tp)26390Sstevel@tonic-gate ldterm_dononcanon(mblk_t *bp, mblk_t *bpt, size_t ebsize, queue_t *q,
26400Sstevel@tonic-gate     ldtermstd_state_t *tp)
26410Sstevel@tonic-gate {
26420Sstevel@tonic-gate 	queue_t *wrq = WR(q);
26430Sstevel@tonic-gate 	unsigned char *rptr;
26440Sstevel@tonic-gate 	size_t bytes_in_bp;
26450Sstevel@tonic-gate 	size_t roomleft;
26460Sstevel@tonic-gate 	size_t bytes_to_move;
26470Sstevel@tonic-gate 	int free_flag = 0;
26480Sstevel@tonic-gate 
26490Sstevel@tonic-gate 	if (tp->t_modes.c_lflag & (ECHO|ECHONL|IEXTEN)) {
26500Sstevel@tonic-gate 		unsigned char *wptr;
26510Sstevel@tonic-gate 		unsigned char c;
26520Sstevel@tonic-gate 
26530Sstevel@tonic-gate 		/*
26540Sstevel@tonic-gate 		 * Either we must echo the characters, or we must
26550Sstevel@tonic-gate 		 * echo NL, or we must check for VLNEXT. Process
26560Sstevel@tonic-gate 		 * characters one at a time.
26570Sstevel@tonic-gate 		 */
26580Sstevel@tonic-gate 		rptr = bp->b_rptr;
26590Sstevel@tonic-gate 		wptr = bp->b_rptr;
26600Sstevel@tonic-gate 		while (rptr < bp->b_wptr) {
26610Sstevel@tonic-gate 			c = *rptr++;
26620Sstevel@tonic-gate 			/*
26630Sstevel@tonic-gate 			 * If this character is the literal next
26640Sstevel@tonic-gate 			 * character, echo it as '^' and backspace
26650Sstevel@tonic-gate 			 * over it if echoing is enabled, indicate
26660Sstevel@tonic-gate 			 * that the next character is to be treated
26670Sstevel@tonic-gate 			 * literally, and remove the LNEXT from the
26680Sstevel@tonic-gate 			 * input stream.
26690Sstevel@tonic-gate 			 *
26700Sstevel@tonic-gate 			 * If the *previous* character was the literal
26710Sstevel@tonic-gate 			 * next character, don't check whether this
26720Sstevel@tonic-gate 			 * is a literal next or not.
26730Sstevel@tonic-gate 			 */
26740Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & IEXTEN) &&
26750Sstevel@tonic-gate 			    !(tp->t_state & TS_SLNCH) &&
26760Sstevel@tonic-gate 			    c != _POSIX_VDISABLE &&
26770Sstevel@tonic-gate 			    c == tp->t_modes.c_cc[VLNEXT]) {
26780Sstevel@tonic-gate 				if (tp->t_modes.c_lflag & ECHO)
26790Sstevel@tonic-gate 					ldterm_outstring(
26800Sstevel@tonic-gate 					    (unsigned char *)"^\b",
2681*7012Sis 					    2, wrq, ebsize, tp);
26820Sstevel@tonic-gate 				tp->t_state |= TS_SLNCH;
26830Sstevel@tonic-gate 				continue;	/* and ignore it */
26840Sstevel@tonic-gate 			}
26850Sstevel@tonic-gate 			/*
26860Sstevel@tonic-gate 			 * Not a "literal next" character, so it
26870Sstevel@tonic-gate 			 * should show up as input. If it was
26880Sstevel@tonic-gate 			 * literal-nexted, turn off the literal-next
26890Sstevel@tonic-gate 			 * flag.
26900Sstevel@tonic-gate 			 */
26910Sstevel@tonic-gate 			tp->t_state &= ~TS_SLNCH;
26920Sstevel@tonic-gate 			*wptr++ = c;
26930Sstevel@tonic-gate 			if (tp->t_modes.c_lflag & ECHO) {
26940Sstevel@tonic-gate 				/*
26950Sstevel@tonic-gate 				 * Echo the character.
26960Sstevel@tonic-gate 				 */
26970Sstevel@tonic-gate 				(void) ldterm_echo(c, wrq, ebsize, tp);
26980Sstevel@tonic-gate 			} else if (tp->t_modes.c_lflag & ECHONL) {
26990Sstevel@tonic-gate 				/*
27000Sstevel@tonic-gate 				 * Echo NL, even though ECHO is not
27010Sstevel@tonic-gate 				 * set.
27020Sstevel@tonic-gate 				 */
27030Sstevel@tonic-gate 				if (c == '\n')
2704*7012Sis 					ldterm_outchar('\n', wrq, 1, tp);
27050Sstevel@tonic-gate 			}
27060Sstevel@tonic-gate 		}
27070Sstevel@tonic-gate 		bp->b_wptr = wptr;
27080Sstevel@tonic-gate 	} else {
27090Sstevel@tonic-gate 		/*
27100Sstevel@tonic-gate 		 * If there are any characters in this buffer, and
27110Sstevel@tonic-gate 		 * the first of them was literal-nexted, turn off the
27120Sstevel@tonic-gate 		 * literal-next flag.
27130Sstevel@tonic-gate 		 */
27140Sstevel@tonic-gate 		if (bp->b_rptr != bp->b_wptr)
27150Sstevel@tonic-gate 			tp->t_state &= ~TS_SLNCH;
27160Sstevel@tonic-gate 	}
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate 	ASSERT(bp->b_wptr >= bp->b_rptr);
27190Sstevel@tonic-gate 	bytes_in_bp = bp->b_wptr - bp->b_rptr;
27200Sstevel@tonic-gate 	rptr = bp->b_rptr;
27210Sstevel@tonic-gate 	while (bytes_in_bp != 0) {
27220Sstevel@tonic-gate 		roomleft = bpt->b_datap->db_lim - bpt->b_wptr;
27230Sstevel@tonic-gate 		if (roomleft == 0) {
27240Sstevel@tonic-gate 			/*
27250Sstevel@tonic-gate 			 * No more room in this mblk; save this one
27260Sstevel@tonic-gate 			 * away, and allocate a new one.
27270Sstevel@tonic-gate 			 */
27280Sstevel@tonic-gate 			if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL) {
27290Sstevel@tonic-gate 				freeb(bp);
27300Sstevel@tonic-gate 				DEBUG4(("ldterm_do_noncanon: allcob failed\n"));
27310Sstevel@tonic-gate 				return (bpt);
27320Sstevel@tonic-gate 			}
27330Sstevel@tonic-gate 			/*
27340Sstevel@tonic-gate 			 * Chain the new one to the end of the old
27350Sstevel@tonic-gate 			 * one, and mark it as the last block in the
27360Sstevel@tonic-gate 			 * current lump.
27370Sstevel@tonic-gate 			 */
27380Sstevel@tonic-gate 			tp->t_endmsg->b_cont = bpt;
27390Sstevel@tonic-gate 			tp->t_endmsg = bpt;
27400Sstevel@tonic-gate 			roomleft = IBSIZE;
27410Sstevel@tonic-gate 		}
27420Sstevel@tonic-gate 		DEBUG5(("roomleft=%d, bytes_in_bp=%d, tp->t_rd_request=%d\n",
2743*7012Sis 		    roomleft, bytes_in_bp, tp->t_rd_request));
27440Sstevel@tonic-gate 		/*
27450Sstevel@tonic-gate 		 * if there is a read pending before this data got
27460Sstevel@tonic-gate 		 * here move bytes according to the minimum of room
27470Sstevel@tonic-gate 		 * left in this buffer, bytes in the message and byte
27480Sstevel@tonic-gate 		 * count requested in the read. If there is no read
27490Sstevel@tonic-gate 		 * pending, move the minimum of the first two
27500Sstevel@tonic-gate 		 */
27510Sstevel@tonic-gate 		if (tp->t_rd_request == 0)
27520Sstevel@tonic-gate 			bytes_to_move = MIN(roomleft, bytes_in_bp);
27530Sstevel@tonic-gate 		else
27540Sstevel@tonic-gate 			bytes_to_move =
27550Sstevel@tonic-gate 			    MIN(MIN(roomleft, bytes_in_bp), tp->t_rd_request);
27560Sstevel@tonic-gate 		DEBUG5(("Bytes to move = %lu\n", bytes_to_move));
27570Sstevel@tonic-gate 		if (bytes_to_move == 0)
27580Sstevel@tonic-gate 			break;
27590Sstevel@tonic-gate 		bcopy(rptr, bpt->b_wptr, bytes_to_move);
27600Sstevel@tonic-gate 		bpt->b_wptr += bytes_to_move;
27610Sstevel@tonic-gate 		rptr += bytes_to_move;
27620Sstevel@tonic-gate 		tp->t_msglen += bytes_to_move;
27630Sstevel@tonic-gate 		bytes_in_bp -= bytes_to_move;
27640Sstevel@tonic-gate 	}
27650Sstevel@tonic-gate 	if (bytes_in_bp == 0) {
27660Sstevel@tonic-gate 		DEBUG4(("bytes_in_bp is zero\n"));
27670Sstevel@tonic-gate 		freeb(bp);
27680Sstevel@tonic-gate 	} else
27690Sstevel@tonic-gate 		free_flag = 1;	/* for debugging olny */
27700Sstevel@tonic-gate 
27710Sstevel@tonic-gate 	DEBUG4(("ldterm_do_noncanon: VMIN = %d, VTIME = %d, msglen = %d, \
27720Sstevel@tonic-gate 		tid = %d\n", V_MIN, V_TIME, tp->t_msglen, tp->t_vtid));
27730Sstevel@tonic-gate 	/*
27740Sstevel@tonic-gate 	 * If there is a pending read request at the stream head we
27750Sstevel@tonic-gate 	 * need to do VMIN/VTIME processing. The four possible cases
27760Sstevel@tonic-gate 	 * are:
27770Sstevel@tonic-gate 	 *	MIN = 0, TIME > 0
27780Sstevel@tonic-gate 	 *	MIN = >, TIME = 0
27790Sstevel@tonic-gate 	 *	MIN > 0, TIME > 0
27800Sstevel@tonic-gate 	 *	MIN = 0, TIME = 0
27810Sstevel@tonic-gate 	 * If we can satisfy VMIN, send it up, and start a new
27820Sstevel@tonic-gate 	 * timer if necessary.  These four cases of VMIN/VTIME
27830Sstevel@tonic-gate 	 * are also dealt with in the write side put routine
27840Sstevel@tonic-gate 	 * when the M_READ is first seen.
27850Sstevel@tonic-gate 	 */
27860Sstevel@tonic-gate 
27870Sstevel@tonic-gate 	DEBUG4(("Incoming data while M_READ'ing\n"));
27880Sstevel@tonic-gate 	/*
27890Sstevel@tonic-gate 	 * Case 1:  Any data will satisfy the read, so send
27900Sstevel@tonic-gate 	 * it upstream.
27910Sstevel@tonic-gate 	 */
27920Sstevel@tonic-gate 	if (V_MIN == 0 && V_TIME > 0) {
27930Sstevel@tonic-gate 		if (tp->t_msglen)
27940Sstevel@tonic-gate 			vmin_satisfied(q, tp, 1);
27950Sstevel@tonic-gate 		else {
27960Sstevel@tonic-gate 			/* EMPTY */
27970Sstevel@tonic-gate 			DEBUG4(("ldterm_do_noncanon called, but no data!\n"));
27980Sstevel@tonic-gate 		}
27990Sstevel@tonic-gate 		/*
28000Sstevel@tonic-gate 		 * Case 2:  This should never time out, so
28010Sstevel@tonic-gate 		 * until there's enough data, do nothing.
28020Sstevel@tonic-gate 		 */
28030Sstevel@tonic-gate 	} else if (V_MIN > 0 && V_TIME == 0) {
28040Sstevel@tonic-gate 		if (tp->t_msglen >= (int)V_MIN)
28050Sstevel@tonic-gate 			vmin_satisfied(q, tp, 1);
28060Sstevel@tonic-gate 
28070Sstevel@tonic-gate 		/*
28080Sstevel@tonic-gate 		 * Case 3:  If MIN is satisfied, send it up.
28090Sstevel@tonic-gate 		 * Also, remember to start a new timer *every*
28100Sstevel@tonic-gate 		 * time we see something if MIN isn't
28110Sstevel@tonic-gate 		 * safisfied
28120Sstevel@tonic-gate 		 */
28130Sstevel@tonic-gate 	} else if (V_MIN > 0 && V_TIME > 0) {
28140Sstevel@tonic-gate 		if (tp->t_msglen >= (int)V_MIN)
28150Sstevel@tonic-gate 			vmin_satisfied(q, tp, 1);
28160Sstevel@tonic-gate 		else
28170Sstevel@tonic-gate 			vmin_settimer(q);
28180Sstevel@tonic-gate 		/*
28190Sstevel@tonic-gate 		 * Case 4:  Not possible.  This request
28200Sstevel@tonic-gate 		 * should always be satisfied from the write
28210Sstevel@tonic-gate 		 * side, left here for debugging.
28220Sstevel@tonic-gate 		 */
28230Sstevel@tonic-gate 	} else {	/* V_MIN == 0 && V_TIME == 0 */
28240Sstevel@tonic-gate 			vmin_satisfied(q, tp, 1);
28250Sstevel@tonic-gate 	}
28260Sstevel@tonic-gate 
28270Sstevel@tonic-gate 	if (free_flag) {
28280Sstevel@tonic-gate 		/* EMPTY */
28290Sstevel@tonic-gate 		DEBUG4(("CAUTION message block not freed\n"));
28300Sstevel@tonic-gate 	}
28310Sstevel@tonic-gate 	return (newmsg(tp));
28320Sstevel@tonic-gate }
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate 
28350Sstevel@tonic-gate /*
28360Sstevel@tonic-gate  * Echo a typed byte to the terminal.  Returns the number of bytes
28370Sstevel@tonic-gate  * printed. Bytes of EUC characters drop through the ECHOCTL stuff
28380Sstevel@tonic-gate  * and are just output as themselves.
28390Sstevel@tonic-gate  */
28400Sstevel@tonic-gate static int
ldterm_echo(uchar_t c,queue_t * q,size_t ebsize,ldtermstd_state_t * tp)28410Sstevel@tonic-gate ldterm_echo(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
28420Sstevel@tonic-gate {
28430Sstevel@tonic-gate 	int i;
28440Sstevel@tonic-gate 
28450Sstevel@tonic-gate 	if (!(tp->t_modes.c_lflag & ECHO))
28460Sstevel@tonic-gate 		return (0);
28470Sstevel@tonic-gate 	i = 0;
28480Sstevel@tonic-gate 
28490Sstevel@tonic-gate 	/*
28500Sstevel@tonic-gate 	 * Echo control characters (c <= 37) only if the ECHOCTRL
28510Sstevel@tonic-gate 	 * flag is set as ^X.
28520Sstevel@tonic-gate 	 */
28530Sstevel@tonic-gate 
28540Sstevel@tonic-gate 	if ((tp->t_modes.c_lflag & ECHOCTL) &&
28550Sstevel@tonic-gate 	    (tp->t_modes.c_lflag & IEXTEN)) {
28560Sstevel@tonic-gate 		if (c <= 037 && c != '\t' && c != '\n') {
28570Sstevel@tonic-gate 			ldterm_outchar('^', q, ebsize, tp);
28580Sstevel@tonic-gate 			i++;
28590Sstevel@tonic-gate 			if (tp->t_modes.c_oflag & OLCUC)
28600Sstevel@tonic-gate 				c += 'a' - 1;
28610Sstevel@tonic-gate 			else
28620Sstevel@tonic-gate 				c += 'A' - 1;
28630Sstevel@tonic-gate 		} else if (c == 0177) {
28640Sstevel@tonic-gate 			ldterm_outchar('^', q, ebsize, tp);
28650Sstevel@tonic-gate 			i++;
28660Sstevel@tonic-gate 			c = '?';
28670Sstevel@tonic-gate 		}
28680Sstevel@tonic-gate 		ldterm_outchar(c, q, ebsize, tp);
28690Sstevel@tonic-gate 		return (i + 1);
28700Sstevel@tonic-gate 		/* echo only special control character and the Bell */
28710Sstevel@tonic-gate 	} else if ((c > 037 && c != 0177) || c == '\t' || c == '\n' ||
2872*7012Sis 	    c == '\r' || c == '\b' || c == 007 ||
2873*7012Sis 	    c == tp->t_modes.c_cc[VKILL]) {
28740Sstevel@tonic-gate 		ldterm_outchar(c, q, ebsize, tp);
28750Sstevel@tonic-gate 		return (i + 1);
28760Sstevel@tonic-gate 	}
28770Sstevel@tonic-gate 	return (i);
28780Sstevel@tonic-gate }
28790Sstevel@tonic-gate 
28800Sstevel@tonic-gate 
28810Sstevel@tonic-gate /*
28820Sstevel@tonic-gate  * Put a character on the output queue.
28830Sstevel@tonic-gate  */
28840Sstevel@tonic-gate static void
ldterm_outchar(uchar_t c,queue_t * q,size_t bsize,ldtermstd_state_t * tp)28850Sstevel@tonic-gate ldterm_outchar(uchar_t c, queue_t *q, size_t bsize, ldtermstd_state_t *tp)
28860Sstevel@tonic-gate {
28870Sstevel@tonic-gate 	mblk_t *curbp;
28880Sstevel@tonic-gate 
28890Sstevel@tonic-gate 	/*
28900Sstevel@tonic-gate 	 * Don't even look at the characters unless we have something
28910Sstevel@tonic-gate 	 * useful to do with them.
28920Sstevel@tonic-gate 	 */
28930Sstevel@tonic-gate 	if ((tp->t_modes.c_oflag & OPOST) ||
28940Sstevel@tonic-gate 	    ((tp->t_modes.c_lflag & XCASE) &&
28950Sstevel@tonic-gate 	    (tp->t_modes.c_lflag & ICANON))) {
28960Sstevel@tonic-gate 		mblk_t *mp;
28970Sstevel@tonic-gate 
28980Sstevel@tonic-gate 		if ((mp = allocb(4, BPRI_HI)) == NULL) {
28990Sstevel@tonic-gate 			cmn_err(CE_WARN,
29000Sstevel@tonic-gate 			    "ldterm: (ldterm_outchar) out of blocks");
29010Sstevel@tonic-gate 			return;
29020Sstevel@tonic-gate 		}
29030Sstevel@tonic-gate 		*mp->b_wptr++ = c;
29040Sstevel@tonic-gate 		mp = ldterm_output_msg(q, mp, &tp->t_echomp, tp, bsize, 1);
29050Sstevel@tonic-gate 		if (mp != NULL)
29060Sstevel@tonic-gate 			freemsg(mp);
29070Sstevel@tonic-gate 
29080Sstevel@tonic-gate 	} else {
29090Sstevel@tonic-gate 		if ((curbp = tp->t_echomp) != NULL) {
29100Sstevel@tonic-gate 			while (curbp->b_cont != NULL)
29110Sstevel@tonic-gate 				curbp = curbp->b_cont;
29120Sstevel@tonic-gate 			if (curbp->b_datap->db_lim == curbp->b_wptr) {
29130Sstevel@tonic-gate 				mblk_t *newbp;
29140Sstevel@tonic-gate 
29150Sstevel@tonic-gate 				if ((newbp = allocb(bsize, BPRI_HI)) == NULL) {
29160Sstevel@tonic-gate 					cmn_err(CE_WARN,
29170Sstevel@tonic-gate 					    "ldterm_outchar: out of blocks");
29180Sstevel@tonic-gate 					return;
29190Sstevel@tonic-gate 				}
29200Sstevel@tonic-gate 				curbp->b_cont = newbp;
29210Sstevel@tonic-gate 				curbp = newbp;
29220Sstevel@tonic-gate 			}
29230Sstevel@tonic-gate 		} else {
29240Sstevel@tonic-gate 			if ((curbp = allocb(bsize, BPRI_HI)) == NULL) {
29250Sstevel@tonic-gate 				cmn_err(CE_WARN,
29260Sstevel@tonic-gate 				    "ldterm_outchar: out of blocks");
29270Sstevel@tonic-gate 				return;
29280Sstevel@tonic-gate 			}
29290Sstevel@tonic-gate 			tp->t_echomp = curbp;
29300Sstevel@tonic-gate 		}
29310Sstevel@tonic-gate 		*curbp->b_wptr++ = c;
29320Sstevel@tonic-gate 	}
29330Sstevel@tonic-gate }
29340Sstevel@tonic-gate 
29350Sstevel@tonic-gate 
29360Sstevel@tonic-gate /*
29370Sstevel@tonic-gate  * Copy a string, of length len, to the output queue.
29380Sstevel@tonic-gate  */
29390Sstevel@tonic-gate static void
ldterm_outstring(uchar_t * cp,int len,queue_t * q,size_t bsize,ldtermstd_state_t * tp)29400Sstevel@tonic-gate ldterm_outstring(uchar_t *cp, int len, queue_t *q, size_t bsize,
29410Sstevel@tonic-gate     ldtermstd_state_t *tp)
29420Sstevel@tonic-gate {
29430Sstevel@tonic-gate 	while (len > 0) {
29440Sstevel@tonic-gate 		ldterm_outchar(*cp++, q, bsize, tp);
29450Sstevel@tonic-gate 		len--;
29460Sstevel@tonic-gate 	}
29470Sstevel@tonic-gate }
29480Sstevel@tonic-gate 
29490Sstevel@tonic-gate 
29500Sstevel@tonic-gate static mblk_t *
newmsg(ldtermstd_state_t * tp)29510Sstevel@tonic-gate newmsg(ldtermstd_state_t *tp)
29520Sstevel@tonic-gate {
29530Sstevel@tonic-gate 	mblk_t *bp;
29540Sstevel@tonic-gate 
29550Sstevel@tonic-gate 	/*
29560Sstevel@tonic-gate 	 * If no current message, allocate a block for it.
29570Sstevel@tonic-gate 	 */
29580Sstevel@tonic-gate 	if ((bp = tp->t_endmsg) == NULL) {
29590Sstevel@tonic-gate 		if ((bp = allocb(IBSIZE, BPRI_MED)) == NULL) {
29600Sstevel@tonic-gate 			cmn_err(CE_WARN,
29610Sstevel@tonic-gate 			    "ldterm: (ldtermrsrv/newmsg) out of blocks");
29620Sstevel@tonic-gate 			return (bp);
29630Sstevel@tonic-gate 		}
29640Sstevel@tonic-gate 		tp->t_message = bp;
29650Sstevel@tonic-gate 		tp->t_endmsg = bp;
29660Sstevel@tonic-gate 	}
29670Sstevel@tonic-gate 	return (bp);
29680Sstevel@tonic-gate }
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate 
29710Sstevel@tonic-gate static void
ldterm_msg_upstream(queue_t * q,ldtermstd_state_t * tp)29720Sstevel@tonic-gate ldterm_msg_upstream(queue_t *q, ldtermstd_state_t *tp)
29730Sstevel@tonic-gate {
29740Sstevel@tonic-gate 	ssize_t s;
29750Sstevel@tonic-gate 	mblk_t *bp;
29760Sstevel@tonic-gate 
29770Sstevel@tonic-gate 	bp = tp->t_message;
29780Sstevel@tonic-gate 	s = msgdsize(bp);
29790Sstevel@tonic-gate 	if (bp)
29800Sstevel@tonic-gate 		putnext(q, tp->t_message);
29810Sstevel@tonic-gate 
29820Sstevel@tonic-gate 	/*
29830Sstevel@tonic-gate 	 * update sysinfo canch character.
29840Sstevel@tonic-gate 	 */
29850Sstevel@tonic-gate 	if (CANON_MODE)
29860Sstevel@tonic-gate 		(void) drv_setparm(SYSCANC, s);
29870Sstevel@tonic-gate 	tp->t_message = NULL;
29880Sstevel@tonic-gate 	tp->t_endmsg = NULL;
29890Sstevel@tonic-gate 	tp->t_msglen = 0;
29900Sstevel@tonic-gate 	tp->t_rocount = 0;
29910Sstevel@tonic-gate 	tp->t_rd_request = 0;
29920Sstevel@tonic-gate 	if (tp->t_state & TS_MEUC) {
29930Sstevel@tonic-gate 		ASSERT(tp->t_eucp_mp);
29940Sstevel@tonic-gate 		tp->t_eucp = tp->t_eucp_mp->b_rptr;
29950Sstevel@tonic-gate 		/* can't reset everything, as we may have other input */
29960Sstevel@tonic-gate 	}
29970Sstevel@tonic-gate }
29980Sstevel@tonic-gate 
29990Sstevel@tonic-gate 
30000Sstevel@tonic-gate /*
30010Sstevel@tonic-gate  * Re-enable the write-side service procedure.  When an allocation
30020Sstevel@tonic-gate  * failure causes write-side processing to stall, we disable the
30030Sstevel@tonic-gate  * write side and arrange to call this function when allocation once
30040Sstevel@tonic-gate  * again becomes possible.
30050Sstevel@tonic-gate  */
30060Sstevel@tonic-gate static void
ldterm_wenable(void * addr)30070Sstevel@tonic-gate ldterm_wenable(void *addr)
30080Sstevel@tonic-gate {
30090Sstevel@tonic-gate 	queue_t *q = addr;
30100Sstevel@tonic-gate 	ldtermstd_state_t *tp;
30110Sstevel@tonic-gate 
30120Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
30130Sstevel@tonic-gate 	/*
30140Sstevel@tonic-gate 	 * The bufcall is no longer pending.
30150Sstevel@tonic-gate 	 */
30160Sstevel@tonic-gate 	tp->t_wbufcid = 0;
30170Sstevel@tonic-gate 	enableok(q);
30180Sstevel@tonic-gate 	qenable(q);
30190Sstevel@tonic-gate }
30200Sstevel@tonic-gate 
30210Sstevel@tonic-gate 
30220Sstevel@tonic-gate /*
30230Sstevel@tonic-gate  * Line discipline output queue put procedure.  Attempts to process
30240Sstevel@tonic-gate  * the message directly and send it on downstream, queueing it only
30250Sstevel@tonic-gate  * if there's already something pending or if its downstream neighbor
30260Sstevel@tonic-gate  * is clogged.
30270Sstevel@tonic-gate  */
30280Sstevel@tonic-gate static void
ldtermwput(queue_t * q,mblk_t * mp)30290Sstevel@tonic-gate ldtermwput(queue_t *q, mblk_t *mp)
30300Sstevel@tonic-gate {
30310Sstevel@tonic-gate 	ldtermstd_state_t *tp;
30320Sstevel@tonic-gate 	unsigned char type = mp->b_datap->db_type;
30330Sstevel@tonic-gate 
30340Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
30350Sstevel@tonic-gate 
30360Sstevel@tonic-gate 	/*
30370Sstevel@tonic-gate 	 * Always process priority messages, regardless of whether or
30380Sstevel@tonic-gate 	 * not our queue is nonempty.
30390Sstevel@tonic-gate 	 */
30400Sstevel@tonic-gate 	if (type >= QPCTL) {
30410Sstevel@tonic-gate 		switch (type) {
30420Sstevel@tonic-gate 
30430Sstevel@tonic-gate 		case M_FLUSH:
30440Sstevel@tonic-gate 			/*
30450Sstevel@tonic-gate 			 * Get rid of it, see comment in
30460Sstevel@tonic-gate 			 * ldterm_dosig().
30470Sstevel@tonic-gate 			 */
30480Sstevel@tonic-gate 			if ((tp->t_state & TS_FLUSHWAIT) &&
30490Sstevel@tonic-gate 			    (*mp->b_rptr == FLUSHW)) {
30500Sstevel@tonic-gate 				tp->t_state &= ~TS_FLUSHWAIT;
30510Sstevel@tonic-gate 				freemsg(mp);
30520Sstevel@tonic-gate 				return;
30530Sstevel@tonic-gate 			}
30540Sstevel@tonic-gate 			/*
30550Sstevel@tonic-gate 			 * This is coming from above, so we only
30560Sstevel@tonic-gate 			 * handle the write queue here.  If FLUSHR is
30570Sstevel@tonic-gate 			 * set, it will get turned around at the
30580Sstevel@tonic-gate 			 * driver, and the read procedure will see it
30590Sstevel@tonic-gate 			 * eventually.
30600Sstevel@tonic-gate 			 */
30610Sstevel@tonic-gate 			if (*mp->b_rptr & FLUSHW) {
3062*7012Sis 				if ((tp->t_state & TS_ISPTSTTY) &&
3063*7012Sis 				    (*mp->b_rptr & FLUSHBAND))
3064*7012Sis 					flushband(q, *(mp->b_rptr + 1),
3065*7012Sis 					    FLUSHDATA);
3066*7012Sis 				else
3067*7012Sis 					flushq(q, FLUSHDATA);
30680Sstevel@tonic-gate 			}
30690Sstevel@tonic-gate 
30700Sstevel@tonic-gate 			putnext(q, mp);
30710Sstevel@tonic-gate 			/*
30720Sstevel@tonic-gate 			 * If a timed read is interrupted, there is
30730Sstevel@tonic-gate 			 * no way to cancel an existing M_READ
30740Sstevel@tonic-gate 			 * request.  We kludge by allowing a flush to
30750Sstevel@tonic-gate 			 * do so.
30760Sstevel@tonic-gate 			 */
30770Sstevel@tonic-gate 			if (tp->t_state & TS_MREAD)
30780Sstevel@tonic-gate 				vmin_satisfied(RD(q), tp, 0);
30790Sstevel@tonic-gate 			break;
30800Sstevel@tonic-gate 
30810Sstevel@tonic-gate 		case M_READ:
30820Sstevel@tonic-gate 			DEBUG1(("ldtermwmsg:M_READ RECEIVED\n"));
30830Sstevel@tonic-gate 			/*
30840Sstevel@tonic-gate 			 * Stream head needs data to satisfy timed
30850Sstevel@tonic-gate 			 * read. Has meaning only if ICANON flag is
30860Sstevel@tonic-gate 			 * off indicating raw mode
30870Sstevel@tonic-gate 			 */
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 			DEBUG4((
30900Sstevel@tonic-gate 			    "M_READ: RAW_MODE=%d, CNT=%d, VMIN=%d, VTIME=%d\n",
30910Sstevel@tonic-gate 			    RAW_MODE, *(unsigned int *)mp->b_rptr, V_MIN,
30920Sstevel@tonic-gate 			    V_TIME));
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate 			tp->t_rd_request = *(unsigned int *)mp->b_rptr;
30950Sstevel@tonic-gate 
30960Sstevel@tonic-gate 			if (RAW_MODE) {
30970Sstevel@tonic-gate 				if (newmsg(tp) != NULL) {
30980Sstevel@tonic-gate 					/*
30990Sstevel@tonic-gate 					 * VMIN/VTIME processing...
31000Sstevel@tonic-gate 					 * The four possible cases are:
31010Sstevel@tonic-gate 					 *	MIN = 0, TIME > 0
31020Sstevel@tonic-gate 					 *	MIN = >, TIME = 0
31030Sstevel@tonic-gate 					 *	MIN > 0, TIME > 0
31040Sstevel@tonic-gate 					 *	MIN = 0, TIME = 0
31050Sstevel@tonic-gate 					 * These four conditions must be dealt
31060Sstevel@tonic-gate 					 * with on the read side as well in
31070Sstevel@tonic-gate 					 * ldterm_do_noncanon(). Set TS_MREAD
31080Sstevel@tonic-gate 					 * so that the read side will know
31090Sstevel@tonic-gate 					 * there is a pending read request
31100Sstevel@tonic-gate 					 * waiting at the stream head.  If we
31110Sstevel@tonic-gate 					 * can satisfy MIN do it here, rather
31120Sstevel@tonic-gate 					 * than on the read side.  If we can't,
31130Sstevel@tonic-gate 					 * start timers if necessary and let
31140Sstevel@tonic-gate 					 * the other side deal with it.
31150Sstevel@tonic-gate 					 *
31160Sstevel@tonic-gate 					 * We got another M_READ before the
31170Sstevel@tonic-gate 					 * pending one completed, cancel any
31180Sstevel@tonic-gate 					 * existing timeout.
31190Sstevel@tonic-gate 					 */
31200Sstevel@tonic-gate 					if (tp->t_state & TS_MREAD) {
31210Sstevel@tonic-gate 						vmin_satisfied(RD(q),
31220Sstevel@tonic-gate 						    tp, 0);
31230Sstevel@tonic-gate 					}
31240Sstevel@tonic-gate 					tp->t_state |= TS_MREAD;
31250Sstevel@tonic-gate 					/*
31260Sstevel@tonic-gate 					 * Case 1:  Any data will
31270Sstevel@tonic-gate 					 * satisfy read, otherwise
31280Sstevel@tonic-gate 					 * start timer
31290Sstevel@tonic-gate 					 */
31300Sstevel@tonic-gate 					if (V_MIN == 0 && V_TIME > 0) {
31310Sstevel@tonic-gate 						if (tp->t_msglen)
31320Sstevel@tonic-gate 							vmin_satisfied(RD(q),
31330Sstevel@tonic-gate 							    tp, 1);
31340Sstevel@tonic-gate 						else
31350Sstevel@tonic-gate 							vmin_settimer(RD(q));
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate 						/*
31380Sstevel@tonic-gate 						 * Case 2:  If we have enough
31390Sstevel@tonic-gate 						 * data, send up now.
31400Sstevel@tonic-gate 						 * Otherwise, the read side
31410Sstevel@tonic-gate 						 * should wait forever until MIN
31420Sstevel@tonic-gate 						 * is satisified.
31430Sstevel@tonic-gate 						 */
31440Sstevel@tonic-gate 					} else if (V_MIN > 0 && V_TIME == 0) {
31450Sstevel@tonic-gate 						if (tp->t_msglen >= (int)V_MIN)
31460Sstevel@tonic-gate 							vmin_satisfied(RD(q),
31470Sstevel@tonic-gate 							    tp, 1);
31480Sstevel@tonic-gate 
31490Sstevel@tonic-gate 						/*
31500Sstevel@tonic-gate 						 * Case 3:  If we can satisfy
31510Sstevel@tonic-gate 						 * the read, send it up. If we
31520Sstevel@tonic-gate 						 * don't have enough data, but
31530Sstevel@tonic-gate 						 * there is at least one char,
31540Sstevel@tonic-gate 						 * start a timer.  Otherwise,
31550Sstevel@tonic-gate 						 * let the read side start
31560Sstevel@tonic-gate 						 * the timer.
31570Sstevel@tonic-gate 						 */
31580Sstevel@tonic-gate 					} else if (V_MIN > 0 && V_TIME > 0) {
31590Sstevel@tonic-gate 						if (tp->t_msglen >= (int)V_MIN)
31600Sstevel@tonic-gate 							vmin_satisfied(RD(q),
31610Sstevel@tonic-gate 							    tp, 1);
31620Sstevel@tonic-gate 						else if (tp->t_msglen)
31630Sstevel@tonic-gate 							vmin_settimer(RD(q));
31640Sstevel@tonic-gate 						/*
31650Sstevel@tonic-gate 						 * Case 4:  Read returns
31660Sstevel@tonic-gate 						 * whatever data is available
31670Sstevel@tonic-gate 						 * or zero if none.
31680Sstevel@tonic-gate 						 */
31690Sstevel@tonic-gate 					} else { /* V_MIN == 0 && V_TIME == 0 */
31700Sstevel@tonic-gate 						vmin_satisfied(RD(q), tp, 1);
31710Sstevel@tonic-gate 					}
31720Sstevel@tonic-gate 
31730Sstevel@tonic-gate 				} else	/* should do bufcall, really! */
31740Sstevel@tonic-gate 					cmn_err(CE_WARN,
31750Sstevel@tonic-gate 					    "ldtermwmsg: out of blocks");
31760Sstevel@tonic-gate 			}
31770Sstevel@tonic-gate 			/*
31780Sstevel@tonic-gate 			 * pass M_READ down
31790Sstevel@tonic-gate 			 */
31800Sstevel@tonic-gate 			putnext(q, mp);
31810Sstevel@tonic-gate 			break;
31820Sstevel@tonic-gate 
31830Sstevel@tonic-gate 		default:
31840Sstevel@tonic-gate 			/* Pass it through unmolested. */
31850Sstevel@tonic-gate 			putnext(q, mp);
31860Sstevel@tonic-gate 			break;
31870Sstevel@tonic-gate 		}
31880Sstevel@tonic-gate 		return;
31890Sstevel@tonic-gate 	}
31900Sstevel@tonic-gate 	/*
31910Sstevel@tonic-gate 	 * If our queue is nonempty or there's a traffic jam
31920Sstevel@tonic-gate 	 * downstream, this message must get in line.
31930Sstevel@tonic-gate 	 */
31940Sstevel@tonic-gate 	if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
31950Sstevel@tonic-gate 		/*
31960Sstevel@tonic-gate 		 * Exception: ioctls, except for those defined to
31970Sstevel@tonic-gate 		 * take effect after output has drained, should be
31980Sstevel@tonic-gate 		 * processed immediately.
31990Sstevel@tonic-gate 		 */
32000Sstevel@tonic-gate 		if (type == M_IOCTL) {
32010Sstevel@tonic-gate 			struct iocblk *iocp;
32020Sstevel@tonic-gate 
32030Sstevel@tonic-gate 			iocp = (struct iocblk *)mp->b_rptr;
32040Sstevel@tonic-gate 			switch (iocp->ioc_cmd) {
32050Sstevel@tonic-gate 
32060Sstevel@tonic-gate 				/*
32070Sstevel@tonic-gate 				 * Queue these.
32080Sstevel@tonic-gate 				 */
32090Sstevel@tonic-gate 			case TCSETSW:
32100Sstevel@tonic-gate 			case TCSETSF:
32110Sstevel@tonic-gate 			case TCSETAW:
32120Sstevel@tonic-gate 			case TCSETAF:
32130Sstevel@tonic-gate 			case TCSBRK:
32140Sstevel@tonic-gate 				break;
32150Sstevel@tonic-gate 
32160Sstevel@tonic-gate 				/*
32170Sstevel@tonic-gate 				 * Handle all others immediately.
32180Sstevel@tonic-gate 				 */
32190Sstevel@tonic-gate 			default:
32200Sstevel@tonic-gate 				(void) ldtermwmsg(q, mp);
32210Sstevel@tonic-gate 				return;
32220Sstevel@tonic-gate 			}
32230Sstevel@tonic-gate 		}
32240Sstevel@tonic-gate 		(void) putq(q, mp);
32250Sstevel@tonic-gate 		return;
32260Sstevel@tonic-gate 	}
32270Sstevel@tonic-gate 	/*
32280Sstevel@tonic-gate 	 * We can take the fast path through, by simply calling
32290Sstevel@tonic-gate 	 * ldtermwmsg to dispose of mp.
32300Sstevel@tonic-gate 	 */
32310Sstevel@tonic-gate 	(void) ldtermwmsg(q, mp);
32320Sstevel@tonic-gate }
32330Sstevel@tonic-gate 
32340Sstevel@tonic-gate 
32350Sstevel@tonic-gate /*
32360Sstevel@tonic-gate  * Line discipline output queue service procedure.
32370Sstevel@tonic-gate  */
32380Sstevel@tonic-gate static void
ldtermwsrv(queue_t * q)32390Sstevel@tonic-gate ldtermwsrv(queue_t *q)
32400Sstevel@tonic-gate {
32410Sstevel@tonic-gate 	mblk_t *mp;
32420Sstevel@tonic-gate 
32430Sstevel@tonic-gate 	/*
32440Sstevel@tonic-gate 	 * We expect this loop to iterate at most once, but must be
32450Sstevel@tonic-gate 	 * prepared for more in case our upstream neighbor isn't
32460Sstevel@tonic-gate 	 * paying strict attention to what canput tells it.
32470Sstevel@tonic-gate 	 */
32480Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
32490Sstevel@tonic-gate 		/*
32500Sstevel@tonic-gate 		 * N.B.: ldtermwput has already handled high-priority
32510Sstevel@tonic-gate 		 * messages, so we don't have to worry about them
32520Sstevel@tonic-gate 		 * here. Hence, the putbq call is safe.
32530Sstevel@tonic-gate 		 */
32540Sstevel@tonic-gate 		if (!bcanputnext(q, mp->b_band)) {
32550Sstevel@tonic-gate 			(void) putbq(q, mp);
32560Sstevel@tonic-gate 			break;
32570Sstevel@tonic-gate 		}
32580Sstevel@tonic-gate 		if (!ldtermwmsg(q, mp)) {
32590Sstevel@tonic-gate 			/*
32600Sstevel@tonic-gate 			 * Couldn't handle the whole thing; give up
32610Sstevel@tonic-gate 			 * for now and wait to be rescheduled.
32620Sstevel@tonic-gate 			 */
32630Sstevel@tonic-gate 			break;
32640Sstevel@tonic-gate 		}
32650Sstevel@tonic-gate 	}
32660Sstevel@tonic-gate }
32670Sstevel@tonic-gate 
32680Sstevel@tonic-gate 
32690Sstevel@tonic-gate /*
32700Sstevel@tonic-gate  * Process the write-side message denoted by mp.  If mp can't be
32710Sstevel@tonic-gate  * processed completely (due to allocation failures), put the
32720Sstevel@tonic-gate  * residual unprocessed part on the front of the write queue, disable
32730Sstevel@tonic-gate  * the queue, and schedule a qbufcall to arrange to complete its
32740Sstevel@tonic-gate  * processing later.
32750Sstevel@tonic-gate  *
32760Sstevel@tonic-gate  * Return 1 if the message was processed completely and 0 if not.
32770Sstevel@tonic-gate  *
32780Sstevel@tonic-gate  * This routine is called from both ldtermwput and ldtermwsrv to do the
32790Sstevel@tonic-gate  * actual work of dealing with mp.  ldtermwput will have already
32800Sstevel@tonic-gate  * dealt with high priority messages.
32810Sstevel@tonic-gate  */
32820Sstevel@tonic-gate static int
ldtermwmsg(queue_t * q,mblk_t * mp)32830Sstevel@tonic-gate ldtermwmsg(queue_t *q, mblk_t *mp)
32840Sstevel@tonic-gate {
32850Sstevel@tonic-gate 	ldtermstd_state_t *tp;
32860Sstevel@tonic-gate 	mblk_t *residmp = NULL;
32870Sstevel@tonic-gate 	size_t size;
32880Sstevel@tonic-gate 
32890Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
32900Sstevel@tonic-gate 
32910Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
32920Sstevel@tonic-gate 
32930Sstevel@tonic-gate 	case M_IOCTL:
32940Sstevel@tonic-gate 		ldterm_do_ioctl(q, mp);
32950Sstevel@tonic-gate 		break;
32960Sstevel@tonic-gate 
32970Sstevel@tonic-gate 	case M_DATA:
32980Sstevel@tonic-gate 		{
32990Sstevel@tonic-gate 			mblk_t *omp = NULL;
33000Sstevel@tonic-gate 
33010Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & FLUSHO) &&
33020Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & IEXTEN)) {
33030Sstevel@tonic-gate 				freemsg(mp);	/* drop on floor */
33040Sstevel@tonic-gate 				break;
33050Sstevel@tonic-gate 			}
33060Sstevel@tonic-gate 			tp->t_rocount = 0;
33070Sstevel@tonic-gate 			/*
33080Sstevel@tonic-gate 			 * Don't even look at the characters unless
33090Sstevel@tonic-gate 			 * we have something useful to do with them.
33100Sstevel@tonic-gate 			 */
33110Sstevel@tonic-gate 			if (((tp->t_modes.c_oflag & OPOST) ||
33120Sstevel@tonic-gate 			    ((tp->t_modes.c_lflag & XCASE) &&
33130Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & ICANON))) &&
33140Sstevel@tonic-gate 			    (msgdsize(mp) || !(tp->t_state & TS_ISPTSTTY))) {
33150Sstevel@tonic-gate 				unsigned char band = mp->b_band;
33160Sstevel@tonic-gate 				short flag = mp->b_flag;
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate 				residmp = ldterm_output_msg(q, mp, &omp,
3319*7012Sis 				    tp, OBSIZE, 0);
33200Sstevel@tonic-gate 				if ((mp = omp) == NULL)
33210Sstevel@tonic-gate 					break;
33220Sstevel@tonic-gate 				mp->b_band |= band;
33230Sstevel@tonic-gate 				mp->b_flag |= flag;
33240Sstevel@tonic-gate 			}
33250Sstevel@tonic-gate 			/* Update sysinfo outch */
33260Sstevel@tonic-gate 			(void) drv_setparm(SYSOUTC, msgdsize(mp));
33270Sstevel@tonic-gate 			putnext(q, mp);
33280Sstevel@tonic-gate 			break;
33290Sstevel@tonic-gate 		}
33300Sstevel@tonic-gate 
33310Sstevel@tonic-gate 	default:
33320Sstevel@tonic-gate 		putnext(q, mp);	/* pass it through unmolested */
33330Sstevel@tonic-gate 		break;
33340Sstevel@tonic-gate 	}
33350Sstevel@tonic-gate 
33360Sstevel@tonic-gate 	if (residmp == NULL)
33370Sstevel@tonic-gate 		return (1);
33380Sstevel@tonic-gate 
33390Sstevel@tonic-gate 	/*
33400Sstevel@tonic-gate 	 * An allocation failure occurred that prevented the message
33410Sstevel@tonic-gate 	 * from being completely processed.  First, disable our
33420Sstevel@tonic-gate 	 * queue, since it's pointless to attempt further processing
33430Sstevel@tonic-gate 	 * until the allocation situation is resolved.  (This must
33440Sstevel@tonic-gate 	 * precede the putbq call below, which would otherwise mark
33450Sstevel@tonic-gate 	 * the queue to be serviced.)
33460Sstevel@tonic-gate 	 */
33470Sstevel@tonic-gate 	noenable(q);
33480Sstevel@tonic-gate 	/*
33490Sstevel@tonic-gate 	 * Stuff the remnant on our write queue so that we can
33500Sstevel@tonic-gate 	 * complete it later when times become less lean.  Note that
33510Sstevel@tonic-gate 	 * this sets QFULL, so that our upstream neighbor will be
33520Sstevel@tonic-gate 	 * blocked by flow control.
33530Sstevel@tonic-gate 	 */
33540Sstevel@tonic-gate 	(void) putbq(q, residmp);
33550Sstevel@tonic-gate 	/*
33560Sstevel@tonic-gate 	 * Schedule a qbufcall to re-enable the queue.  The failure
33570Sstevel@tonic-gate 	 * won't have been for an allocation of more than OBSIZE
33580Sstevel@tonic-gate 	 * bytes, so don't ask for more than that from bufcall.
33590Sstevel@tonic-gate 	 */
33600Sstevel@tonic-gate 	size = msgdsize(residmp);
33610Sstevel@tonic-gate 	if (size > OBSIZE)
33620Sstevel@tonic-gate 		size = OBSIZE;
33630Sstevel@tonic-gate 	if (tp->t_wbufcid)
33640Sstevel@tonic-gate 		qunbufcall(q, tp->t_wbufcid);
33650Sstevel@tonic-gate 	tp->t_wbufcid = qbufcall(q, size, BPRI_MED, ldterm_wenable, q);
33660Sstevel@tonic-gate 
33670Sstevel@tonic-gate 	return (0);
33680Sstevel@tonic-gate }
33690Sstevel@tonic-gate 
33700Sstevel@tonic-gate 
33710Sstevel@tonic-gate /*
33720Sstevel@tonic-gate  * Perform output processing on a message, accumulating the output
33730Sstevel@tonic-gate  * characters in a new message.
33740Sstevel@tonic-gate  */
33750Sstevel@tonic-gate static mblk_t *
ldterm_output_msg(queue_t * q,mblk_t * imp,mblk_t ** omp,ldtermstd_state_t * tp,size_t bsize,int echoing)33760Sstevel@tonic-gate ldterm_output_msg(queue_t *q, mblk_t *imp, mblk_t **omp,
33770Sstevel@tonic-gate     ldtermstd_state_t *tp, size_t bsize, int echoing)
33780Sstevel@tonic-gate {
33790Sstevel@tonic-gate 	mblk_t *ibp;		/* block we're examining from input message */
33800Sstevel@tonic-gate 	mblk_t *obp;		/* block we're filling in output message */
33810Sstevel@tonic-gate 	mblk_t *cbp;		/* continuation block */
33820Sstevel@tonic-gate 	mblk_t *oobp;		/* old value of obp; valid if NEW_BLOCK fails */
33830Sstevel@tonic-gate 	mblk_t **contpp;	/* where to stuff ptr to newly-allocated blk */
33840Sstevel@tonic-gate 	unsigned char c, n;
33850Sstevel@tonic-gate 	int count, ctype;
33860Sstevel@tonic-gate 	ssize_t bytes_left;
33870Sstevel@tonic-gate 
33880Sstevel@tonic-gate 	mblk_t *bp;		/* block to stuff an M_DELAY message in */
33890Sstevel@tonic-gate 
33900Sstevel@tonic-gate 
33910Sstevel@tonic-gate 	/*
33920Sstevel@tonic-gate 	 * Allocate a new block into which to put bytes. If we can't,
33930Sstevel@tonic-gate 	 * we just drop the rest of the message on the floor. If x is
33940Sstevel@tonic-gate 	 * non-zero, just fall thru; failure requires cleanup before
33950Sstevel@tonic-gate 	 * going out
33960Sstevel@tonic-gate 	 */
33970Sstevel@tonic-gate 
33980Sstevel@tonic-gate #define	NEW_BLOCK(x) \
33990Sstevel@tonic-gate 	{ \
34000Sstevel@tonic-gate 		oobp = obp; \
34010Sstevel@tonic-gate 		if ((obp = allocb(bsize, BPRI_MED)) == NULL) { \
34020Sstevel@tonic-gate 			if (x == 0) \
34030Sstevel@tonic-gate 				goto outofbufs; \
34040Sstevel@tonic-gate 		} else { \
34050Sstevel@tonic-gate 			*contpp = obp; \
34060Sstevel@tonic-gate 			contpp = &obp->b_cont; \
34070Sstevel@tonic-gate 			bytes_left = obp->b_datap->db_lim - obp->b_wptr; \
34080Sstevel@tonic-gate 		} \
34090Sstevel@tonic-gate 	}
34100Sstevel@tonic-gate 
34110Sstevel@tonic-gate 	ibp = imp;
34120Sstevel@tonic-gate 
34130Sstevel@tonic-gate 	/*
34140Sstevel@tonic-gate 	 * When we allocate the first block of a message, we should
34150Sstevel@tonic-gate 	 * stuff the pointer to it in "*omp".  All subsequent blocks
34160Sstevel@tonic-gate 	 * should have the pointer to them stuffed into the "b_cont"
34170Sstevel@tonic-gate 	 * field of the previous block.  "contpp" points to the place
34180Sstevel@tonic-gate 	 * where we should stuff the pointer.
34190Sstevel@tonic-gate 	 *
34200Sstevel@tonic-gate 	 * If we already have a message we're filling in, continue doing
34210Sstevel@tonic-gate 	 * so.
34220Sstevel@tonic-gate 	 */
34230Sstevel@tonic-gate 	if ((obp = *omp) != NULL) {
34240Sstevel@tonic-gate 		while (obp->b_cont != NULL)
34250Sstevel@tonic-gate 			obp = obp->b_cont;
34260Sstevel@tonic-gate 		contpp = &obp->b_cont;
34270Sstevel@tonic-gate 		bytes_left = obp->b_datap->db_lim - obp->b_wptr;
34280Sstevel@tonic-gate 	} else {
34290Sstevel@tonic-gate 		contpp = omp;
34300Sstevel@tonic-gate 		bytes_left = 0;
34310Sstevel@tonic-gate 	}
34320Sstevel@tonic-gate 
34330Sstevel@tonic-gate 	do {
34340Sstevel@tonic-gate 		while (ibp->b_rptr < ibp->b_wptr) {
34350Sstevel@tonic-gate 			/*
34360Sstevel@tonic-gate 			 * Make sure there's room for one more
34370Sstevel@tonic-gate 			 * character.  At most, we'll need "t_maxeuc"
34380Sstevel@tonic-gate 			 * bytes.
34390Sstevel@tonic-gate 			 */
34400Sstevel@tonic-gate 			if ((bytes_left < (int)tp->t_maxeuc)) {
34410Sstevel@tonic-gate 				/* LINTED */
34420Sstevel@tonic-gate 				NEW_BLOCK(0);
34430Sstevel@tonic-gate 			}
34440Sstevel@tonic-gate 			/*
34450Sstevel@tonic-gate 			 * If doing XCASE processing (not very
34460Sstevel@tonic-gate 			 * likely, in this day and age), look at each
34470Sstevel@tonic-gate 			 * character individually.
34480Sstevel@tonic-gate 			 */
34490Sstevel@tonic-gate 			if ((tp->t_modes.c_lflag & XCASE) &&
34500Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & ICANON)) {
34510Sstevel@tonic-gate 				c = *ibp->b_rptr++;
34520Sstevel@tonic-gate 
34530Sstevel@tonic-gate 				/*
34540Sstevel@tonic-gate 				 * We need to make sure that this is not
34550Sstevel@tonic-gate 				 * a following byte of a multibyte character
34560Sstevel@tonic-gate 				 * before applying an XCASE processing.
34570Sstevel@tonic-gate 				 *
34580Sstevel@tonic-gate 				 * tp->t_eucign will be 0 if and only
34590Sstevel@tonic-gate 				 * if the current 'c' is an ASCII character
34600Sstevel@tonic-gate 				 * and also a byte. Otherwise, it will have
34610Sstevel@tonic-gate 				 * the byte length of a multibyte character.
34620Sstevel@tonic-gate 				 */
34630Sstevel@tonic-gate 				if ((tp->t_state & TS_MEUC) &&
34640Sstevel@tonic-gate 				    tp->t_eucign == 0 && NOTASCII(c)) {
34650Sstevel@tonic-gate 					tp->t_eucign =
3466*7012Sis 					    tp->t_csmethods.ldterm_memwidth(
3467*7012Sis 					    c, (void *)tp);
34680Sstevel@tonic-gate 					tp->t_scratch_len = tp->t_eucign;
34690Sstevel@tonic-gate 
34700Sstevel@tonic-gate 					if (tp->t_csdata.codeset_type !=
34710Sstevel@tonic-gate 					    LDTERM_CS_TYPE_UTF8) {
34720Sstevel@tonic-gate 						tp->t_col +=
3473*7012Sis 						    tp->
3474*7012Sis 						    t_csmethods.
3475*7012Sis 						    ldterm_dispwidth(c,
3476*7012Sis 						    (void *)tp,
3477*7012Sis 						    tp->t_modes.c_lflag &
3478*7012Sis 						    ECHOCTL);
34790Sstevel@tonic-gate 					}
34800Sstevel@tonic-gate 				}
34810Sstevel@tonic-gate 
34820Sstevel@tonic-gate 				/*
34830Sstevel@tonic-gate 				 * If character is mapped on output,
34840Sstevel@tonic-gate 				 * put out a backslash followed by
34850Sstevel@tonic-gate 				 * what it is mapped to.
34860Sstevel@tonic-gate 				 */
34870Sstevel@tonic-gate 				if (tp->t_eucign == 0 && omaptab[c] != 0 &&
34880Sstevel@tonic-gate 				    (!echoing || c != '\\')) {
34890Sstevel@tonic-gate 					/* backslash is an ordinary character */
34900Sstevel@tonic-gate 					tp->t_col++;
34910Sstevel@tonic-gate 					*obp->b_wptr++ = '\\';
34920Sstevel@tonic-gate 					bytes_left--;
34930Sstevel@tonic-gate 					if (bytes_left == 0) {
34940Sstevel@tonic-gate 						/* LINTED */
34950Sstevel@tonic-gate 						NEW_BLOCK(1);
34960Sstevel@tonic-gate 					}
34970Sstevel@tonic-gate 					/*
34980Sstevel@tonic-gate 					 * Allocation failed, make
34990Sstevel@tonic-gate 					 * state consistent before
35000Sstevel@tonic-gate 					 * returning
35010Sstevel@tonic-gate 					 */
35020Sstevel@tonic-gate 					if (obp == NULL) {
35030Sstevel@tonic-gate 						ibp->b_rptr--;
35040Sstevel@tonic-gate 						tp->t_col--;
35050Sstevel@tonic-gate 						oobp->b_wptr--;
35060Sstevel@tonic-gate 						goto outofbufs;
35070Sstevel@tonic-gate 					}
35080Sstevel@tonic-gate 					c = omaptab[c];
35090Sstevel@tonic-gate 				}
35100Sstevel@tonic-gate 				/*
35110Sstevel@tonic-gate 				 * If no other output processing is
35120Sstevel@tonic-gate 				 * required, push the character into
35130Sstevel@tonic-gate 				 * the block and get another.
35140Sstevel@tonic-gate 				 */
35150Sstevel@tonic-gate 				if (!(tp->t_modes.c_oflag & OPOST)) {
35160Sstevel@tonic-gate 					if (tp->t_eucign > 0) {
35170Sstevel@tonic-gate 						--tp->t_eucign;
35180Sstevel@tonic-gate 					} else {
35190Sstevel@tonic-gate 						tp->t_col++;
35200Sstevel@tonic-gate 					}
35210Sstevel@tonic-gate 					*obp->b_wptr++ = c;
35220Sstevel@tonic-gate 					bytes_left--;
35230Sstevel@tonic-gate 					continue;
35240Sstevel@tonic-gate 				}
35250Sstevel@tonic-gate 				/*
35260Sstevel@tonic-gate 				 * OPOST output flag is set. Map
35270Sstevel@tonic-gate 				 * lower case to upper case if OLCUC
35280Sstevel@tonic-gate 				 * flag is set and the 'c' is a lowercase
35290Sstevel@tonic-gate 				 * ASCII character.
35300Sstevel@tonic-gate 				 */
35310Sstevel@tonic-gate 				if (tp->t_eucign == 0 &&
35320Sstevel@tonic-gate 				    (tp->t_modes.c_oflag & OLCUC) &&
35330Sstevel@tonic-gate 				    c >= 'a' && c <= 'z')
35340Sstevel@tonic-gate 					c -= 'a' - 'A';
35350Sstevel@tonic-gate 			} else {
35360Sstevel@tonic-gate 				/*
35370Sstevel@tonic-gate 				 * Copy all the ORDINARY characters,
35380Sstevel@tonic-gate 				 * possibly mapping upper case to
35390Sstevel@tonic-gate 				 * lower case.  We use "movtuc",
35400Sstevel@tonic-gate 				 * STOPPING when we can't move some
35410Sstevel@tonic-gate 				 * character. For multi-byte or
35420Sstevel@tonic-gate 				 * multi-column EUC, we can't depend
35430Sstevel@tonic-gate 				 * on the regular tables. Rather than
35440Sstevel@tonic-gate 				 * just drop through to the "big
35450Sstevel@tonic-gate 				 * switch" for all characters, it
35460Sstevel@tonic-gate 				 * _might_ be faster to let "movtuc"
35470Sstevel@tonic-gate 				 * move a bunch of characters.
35480Sstevel@tonic-gate 				 * Chances are, even in multi-byte
35490Sstevel@tonic-gate 				 * mode we'll have lots of ASCII
35500Sstevel@tonic-gate 				 * going through. We check the flag
35510Sstevel@tonic-gate 				 * once, and call movtuc with the
35520Sstevel@tonic-gate 				 * appropriate table as an argument.
35530Sstevel@tonic-gate 				 *
35540Sstevel@tonic-gate 				 * "movtuc will work for all codeset
35550Sstevel@tonic-gate 				 * types since it stops at the beginning
35560Sstevel@tonic-gate 				 * byte of a multibyte character.
35570Sstevel@tonic-gate 				 */
35580Sstevel@tonic-gate 				size_t bytes_to_move;
35590Sstevel@tonic-gate 				size_t bytes_moved;
35600Sstevel@tonic-gate 
35610Sstevel@tonic-gate 				ASSERT(ibp->b_wptr >= ibp->b_rptr);
35620Sstevel@tonic-gate 				bytes_to_move = ibp->b_wptr - ibp->b_rptr;
35630Sstevel@tonic-gate 				if (bytes_to_move > bytes_left)
35640Sstevel@tonic-gate 					bytes_to_move = bytes_left;
35650Sstevel@tonic-gate 				if (tp->t_state & TS_MEUC) {
35660Sstevel@tonic-gate 					bytes_moved = movtuc(bytes_to_move,
3567*7012Sis 					    ibp->b_rptr, obp->b_wptr,
3568*7012Sis 					    (tp->t_modes.c_oflag & OLCUC ?
3569*7012Sis 					    elcuctab : enotrantab));
35700Sstevel@tonic-gate 				} else {
35710Sstevel@tonic-gate 					bytes_moved = movtuc(bytes_to_move,
35720Sstevel@tonic-gate 					    ibp->b_rptr, obp->b_wptr,
35730Sstevel@tonic-gate 					    (tp->t_modes.c_oflag & OLCUC ?
35740Sstevel@tonic-gate 					    lcuctab : notrantab));
35750Sstevel@tonic-gate 				}
35760Sstevel@tonic-gate 				/*
35770Sstevel@tonic-gate 				 * We're save to just do this column
35780Sstevel@tonic-gate 				 * calculation, because if TS_MEUC is
35790Sstevel@tonic-gate 				 * set, we used the proper EUC
35800Sstevel@tonic-gate 				 * tables, and won't have copied any
35810Sstevel@tonic-gate 				 * EUC bytes.
35820Sstevel@tonic-gate 				 */
35830Sstevel@tonic-gate 				tp->t_col += bytes_moved;
35840Sstevel@tonic-gate 				ibp->b_rptr += bytes_moved;
35850Sstevel@tonic-gate 				obp->b_wptr += bytes_moved;
35860Sstevel@tonic-gate 				bytes_left -= bytes_moved;
35870Sstevel@tonic-gate 				if (ibp->b_rptr >= ibp->b_wptr)
35880Sstevel@tonic-gate 					continue;	/* moved all of block */
35890Sstevel@tonic-gate 				if (bytes_left == 0) {
35900Sstevel@tonic-gate 					/* LINTED */
35910Sstevel@tonic-gate 					NEW_BLOCK(0);
35920Sstevel@tonic-gate 				}
35930Sstevel@tonic-gate 				c = *ibp->b_rptr++;	/* stopper */
35940Sstevel@tonic-gate 			}
35950Sstevel@tonic-gate 
35960Sstevel@tonic-gate 			/*
35970Sstevel@tonic-gate 			 * Again, we need to make sure that this is not
35980Sstevel@tonic-gate 			 * a following byte of a multibyte character at
35990Sstevel@tonic-gate 			 * here.
36000Sstevel@tonic-gate 			 *
36010Sstevel@tonic-gate 			 * 'tp->t_eucign' will be 0 iff the current 'c' is
36020Sstevel@tonic-gate 			 * an ASCII character. Otherwise, it will have
36030Sstevel@tonic-gate 			 * the byte length of a multibyte character.
36040Sstevel@tonic-gate 			 * We also add the display width to 'tp->t_col' if
36050Sstevel@tonic-gate 			 * the current codeset is not UTF-8 since this is
36060Sstevel@tonic-gate 			 * a leading byte of a multibyte character.
36070Sstevel@tonic-gate 			 * For UTF-8 codeset type, we add the display width
36080Sstevel@tonic-gate 			 * when we get the last byte of a character.
36090Sstevel@tonic-gate 			 */
36100Sstevel@tonic-gate 			if ((tp->t_state & TS_MEUC) && tp->t_eucign == 0 &&
36110Sstevel@tonic-gate 			    NOTASCII(c)) {
36120Sstevel@tonic-gate 				tp->t_eucign = tp->t_csmethods.ldterm_memwidth(
3613*7012Sis 				    c, (void *)tp);
36140Sstevel@tonic-gate 				tp->t_scratch_len = tp->t_eucign;
36150Sstevel@tonic-gate 
36160Sstevel@tonic-gate 				if (tp->t_csdata.codeset_type !=
36170Sstevel@tonic-gate 				    LDTERM_CS_TYPE_UTF8) {
36180Sstevel@tonic-gate 					tp->t_col +=
36190Sstevel@tonic-gate 					    tp->t_csmethods.ldterm_dispwidth(c,
3620*7012Sis 					    (void *)tp,
3621*7012Sis 					    tp->t_modes.c_lflag & ECHOCTL);
36220Sstevel@tonic-gate 				}
36230Sstevel@tonic-gate 			}
36240Sstevel@tonic-gate 
36250Sstevel@tonic-gate 			/*
36260Sstevel@tonic-gate 			 * If the driver has requested, don't process
36270Sstevel@tonic-gate 			 * output flags.  However, if we're in
36280Sstevel@tonic-gate 			 * multi-byte mode, we HAVE to look at
36290Sstevel@tonic-gate 			 * EVERYTHING going out to maintain column
36300Sstevel@tonic-gate 			 * position properly. Therefore IF the driver
36310Sstevel@tonic-gate 			 * says don't AND we're not doing multi-byte,
36320Sstevel@tonic-gate 			 * then don't do it.  Otherwise, do it.
36330Sstevel@tonic-gate 			 *
36340Sstevel@tonic-gate 			 * NOTE:  Hardware USUALLY doesn't expand tabs
36350Sstevel@tonic-gate 			 * properly for multi-byte situations anyway;
36360Sstevel@tonic-gate 			 * that's a known problem with the 3B2
36370Sstevel@tonic-gate 			 * "PORTS" board firmware, and any other
36380Sstevel@tonic-gate 			 * hardware that doesn't ACTUALLY know about
36390Sstevel@tonic-gate 			 * the current EUC mapping that WE are using
36400Sstevel@tonic-gate 			 * at this very moment.  The problem is that
36410Sstevel@tonic-gate 			 * memory width is INDEPENDENT of screen
36420Sstevel@tonic-gate 			 * width - no relation - so WE know how wide
36430Sstevel@tonic-gate 			 * the characters are, but an off-the-host
36440Sstevel@tonic-gate 			 * board probably doesn't.  So, until we're
36450Sstevel@tonic-gate 			 * SURE that the hardware below us can
36460Sstevel@tonic-gate 			 * correctly expand tabs in a
36470Sstevel@tonic-gate 			 * multi-byte/multi-column EUC situation, we
36480Sstevel@tonic-gate 			 * do it ourselves.
36490Sstevel@tonic-gate 			 */
36500Sstevel@tonic-gate 			/*
36510Sstevel@tonic-gate 			 * Map <CR>to<NL> on output if OCRNL flag
36520Sstevel@tonic-gate 			 * set. ONLCR processing is not done if OCRNL
36530Sstevel@tonic-gate 			 * is set.
36540Sstevel@tonic-gate 			 */
36550Sstevel@tonic-gate 			if (c == '\r' && (tp->t_modes.c_oflag & OCRNL)) {
36560Sstevel@tonic-gate 				c = '\n';
36570Sstevel@tonic-gate 				ctype = typetab[c];
36580Sstevel@tonic-gate 				goto jocrnl;
36590Sstevel@tonic-gate 			}
36600Sstevel@tonic-gate 
36610Sstevel@tonic-gate 			if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_EUC) {
36620Sstevel@tonic-gate 				ctype = typetab[c];
36630Sstevel@tonic-gate 			} else {
36640Sstevel@tonic-gate 				/*
36650Sstevel@tonic-gate 				 * In other codeset types, we safely assume
36660Sstevel@tonic-gate 				 * any byte of a multibyte character will have
36670Sstevel@tonic-gate 				 * 'ORDINARY' type. For ASCII characters, we
36680Sstevel@tonic-gate 				 * still use the typetab[].
36690Sstevel@tonic-gate 				 */
36700Sstevel@tonic-gate 				if (tp->t_eucign == 0)
36710Sstevel@tonic-gate 					ctype = typetab[c];
36720Sstevel@tonic-gate 				else
36730Sstevel@tonic-gate 					ctype = ORDINARY;
36740Sstevel@tonic-gate 			}
36750Sstevel@tonic-gate 
36760Sstevel@tonic-gate 			/*
36770Sstevel@tonic-gate 			 * Map <NL> to <CR><NL> on output if ONLCR
36780Sstevel@tonic-gate 			 * flag is set.
36790Sstevel@tonic-gate 			 */
36800Sstevel@tonic-gate 			if (c == '\n' && (tp->t_modes.c_oflag & ONLCR)) {
36810Sstevel@tonic-gate 				if (!(tp->t_state & TS_TTCR)) {
36820Sstevel@tonic-gate 					tp->t_state |= TS_TTCR;
36830Sstevel@tonic-gate 					c = '\r';
36840Sstevel@tonic-gate 					ctype = typetab['\r'];
36850Sstevel@tonic-gate 					--ibp->b_rptr;
36860Sstevel@tonic-gate 				} else
36870Sstevel@tonic-gate 					tp->t_state &= ~TS_TTCR;
36880Sstevel@tonic-gate 			}
36890Sstevel@tonic-gate 			/*
36900Sstevel@tonic-gate 			 * Delay values and column position
36910Sstevel@tonic-gate 			 * calculated here.  For EUC chars in
36920Sstevel@tonic-gate 			 * multi-byte mode, we use "t_eucign" to help
36930Sstevel@tonic-gate 			 * calculate columns.  When we see the first
36940Sstevel@tonic-gate 			 * byte of an EUC, we set t_eucign to the
36950Sstevel@tonic-gate 			 * number of bytes that will FOLLOW it, and
36960Sstevel@tonic-gate 			 * we add the screen width of the WHOLE EUC
36970Sstevel@tonic-gate 			 * character to the column position.  In
36980Sstevel@tonic-gate 			 * particular, we can't count SS2 or SS3 as
36990Sstevel@tonic-gate 			 * printing characters.  Remember, folks, the
37000Sstevel@tonic-gate 			 * screen width and memory width are
37010Sstevel@tonic-gate 			 * independent - no relation. We could have
37020Sstevel@tonic-gate 			 * dropped through for ASCII, but we want to
37030Sstevel@tonic-gate 			 * catch any bad characters (i.e., t_eucign
37040Sstevel@tonic-gate 			 * set and an ASCII char received) and
37050Sstevel@tonic-gate 			 * possibly report the garbage situation.
37060Sstevel@tonic-gate 			 */
37070Sstevel@tonic-gate 	jocrnl:
37080Sstevel@tonic-gate 
37090Sstevel@tonic-gate 			count = 0;
37100Sstevel@tonic-gate 			switch (ctype) {
37110Sstevel@tonic-gate 
37120Sstevel@tonic-gate 			case T_SS2:
37130Sstevel@tonic-gate 			case T_SS3:
37140Sstevel@tonic-gate 			case ORDINARY:
37150Sstevel@tonic-gate 				if (tp->t_state & TS_MEUC) {
37160Sstevel@tonic-gate 					if (tp->t_eucign) {
37170Sstevel@tonic-gate 						*obp->b_wptr++ = c;
37180Sstevel@tonic-gate 						bytes_left--;
37190Sstevel@tonic-gate 
37200Sstevel@tonic-gate 						tp->t_scratch[tp->t_scratch_len
37210Sstevel@tonic-gate 						    - tp->t_eucign] = c;
37220Sstevel@tonic-gate 
37230Sstevel@tonic-gate 						--tp->t_eucign;
37240Sstevel@tonic-gate 
37250Sstevel@tonic-gate 						if (tp->t_csdata.codeset_type
37260Sstevel@tonic-gate 						    == LDTERM_CS_TYPE_UTF8 &&
37270Sstevel@tonic-gate 						    tp->t_eucign <= 0) {
37280Sstevel@tonic-gate 							tp->t_col +=
37290Sstevel@tonic-gate 							    ldterm_utf8_width(
37300Sstevel@tonic-gate 							    tp->t_scratch,
37310Sstevel@tonic-gate 							    tp->t_scratch_len);
37320Sstevel@tonic-gate 						}
37330Sstevel@tonic-gate 					} else {
37340Sstevel@tonic-gate 						if (tp->t_modes.c_oflag & OLCUC)
37350Sstevel@tonic-gate 							n = elcuctab[c];
37360Sstevel@tonic-gate 						else
37370Sstevel@tonic-gate 							n = enotrantab[c];
37380Sstevel@tonic-gate 						if (n)
37390Sstevel@tonic-gate 							c = n;
37400Sstevel@tonic-gate 						tp->t_col++;
37410Sstevel@tonic-gate 						*obp->b_wptr++ = c;
37420Sstevel@tonic-gate 						bytes_left--;
37430Sstevel@tonic-gate 					}
37440Sstevel@tonic-gate 				} else {	/* ho hum, ASCII mode... */
37450Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OLCUC)
37460Sstevel@tonic-gate 						n = lcuctab[c];
37470Sstevel@tonic-gate 					else
37480Sstevel@tonic-gate 						n = notrantab[c];
37490Sstevel@tonic-gate 					if (n)
37500Sstevel@tonic-gate 						c = n;
37510Sstevel@tonic-gate 					tp->t_col++;
37520Sstevel@tonic-gate 					*obp->b_wptr++ = c;
37530Sstevel@tonic-gate 					bytes_left--;
37540Sstevel@tonic-gate 				}
37550Sstevel@tonic-gate 				break;
37560Sstevel@tonic-gate 
37570Sstevel@tonic-gate 				/*
37580Sstevel@tonic-gate 				 * If we're doing ECHOCTL, we've
37590Sstevel@tonic-gate 				 * already mapped the thing during
37600Sstevel@tonic-gate 				 * the process of canonising.  Don't
37610Sstevel@tonic-gate 				 * bother here, as it's not one that
37620Sstevel@tonic-gate 				 * we did.
37630Sstevel@tonic-gate 				 */
37640Sstevel@tonic-gate 			case CONTROL:
37650Sstevel@tonic-gate 				*obp->b_wptr++ = c;
37660Sstevel@tonic-gate 				bytes_left--;
37670Sstevel@tonic-gate 				break;
37680Sstevel@tonic-gate 
37690Sstevel@tonic-gate 				/*
37700Sstevel@tonic-gate 				 * This is probably a backspace
37710Sstevel@tonic-gate 				 * received, not one that we're
37720Sstevel@tonic-gate 				 * echoing.  Let it go as a
37730Sstevel@tonic-gate 				 * single-column backspace.
37740Sstevel@tonic-gate 				 */
37750Sstevel@tonic-gate 			case BACKSPACE:
37760Sstevel@tonic-gate 				if (tp->t_col)
37770Sstevel@tonic-gate 					tp->t_col--;
37780Sstevel@tonic-gate 				if (tp->t_modes.c_oflag & BSDLY) {
37790Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OFILL)
37800Sstevel@tonic-gate 						count = 1;
37810Sstevel@tonic-gate 				}
37820Sstevel@tonic-gate 				*obp->b_wptr++ = c;
37830Sstevel@tonic-gate 				bytes_left--;
37840Sstevel@tonic-gate 				break;
37850Sstevel@tonic-gate 
37860Sstevel@tonic-gate 			case NEWLINE:
37870Sstevel@tonic-gate 				if (tp->t_modes.c_oflag & ONLRET)
37880Sstevel@tonic-gate 					goto cr;
37890Sstevel@tonic-gate 				if ((tp->t_modes.c_oflag & NLDLY) == NL1)
37900Sstevel@tonic-gate 					count = 2;
37910Sstevel@tonic-gate 				*obp->b_wptr++ = c;
37920Sstevel@tonic-gate 				bytes_left--;
37930Sstevel@tonic-gate 				break;
37940Sstevel@tonic-gate 
37950Sstevel@tonic-gate 			case TAB:
37960Sstevel@tonic-gate 				/*
37970Sstevel@tonic-gate 				 * Map '\t' to spaces if XTABS flag
37980Sstevel@tonic-gate 				 * is set.  The calculation of
37990Sstevel@tonic-gate 				 * "t_eucign" has probably insured
38000Sstevel@tonic-gate 				 * that column will be correct, as we
38010Sstevel@tonic-gate 				 * bumped t_col by the DISP width,
38020Sstevel@tonic-gate 				 * not the memory width.
38030Sstevel@tonic-gate 				 */
38040Sstevel@tonic-gate 				if ((tp->t_modes.c_oflag & TABDLY) == XTABS) {
38050Sstevel@tonic-gate 					for (;;) {
38060Sstevel@tonic-gate 						*obp->b_wptr++ = ' ';
38070Sstevel@tonic-gate 						bytes_left--;
38080Sstevel@tonic-gate 						tp->t_col++;
38090Sstevel@tonic-gate 						if ((tp->t_col & 07) == 0)
38100Sstevel@tonic-gate 							break;	/* every 8th */
38110Sstevel@tonic-gate 						/*
38120Sstevel@tonic-gate 						 * If we don't have
38130Sstevel@tonic-gate 						 * room to fully
38140Sstevel@tonic-gate 						 * expand this tab in
38150Sstevel@tonic-gate 						 * this block, back
38160Sstevel@tonic-gate 						 * up to continue
38170Sstevel@tonic-gate 						 * expanding it into
38180Sstevel@tonic-gate 						 * the next block.
38190Sstevel@tonic-gate 						 */
38200Sstevel@tonic-gate 						if (obp->b_wptr >=
38210Sstevel@tonic-gate 						    obp->b_datap->db_lim) {
38220Sstevel@tonic-gate 							ibp->b_rptr--;
38230Sstevel@tonic-gate 							break;
38240Sstevel@tonic-gate 						}
38250Sstevel@tonic-gate 					}
38260Sstevel@tonic-gate 				} else {
38270Sstevel@tonic-gate 					tp->t_col |= 07;
38280Sstevel@tonic-gate 					tp->t_col++;
38290Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OFILL) {
38300Sstevel@tonic-gate 						if (tp->t_modes.c_oflag &
38310Sstevel@tonic-gate 						    TABDLY)
38320Sstevel@tonic-gate 							count = 2;
38330Sstevel@tonic-gate 					} else {
38340Sstevel@tonic-gate 						switch (tp->t_modes.c_oflag &
38350Sstevel@tonic-gate 						    TABDLY) {
38360Sstevel@tonic-gate 						case TAB2:
38370Sstevel@tonic-gate 							count = 6;
38380Sstevel@tonic-gate 							break;
38390Sstevel@tonic-gate 
38400Sstevel@tonic-gate 						case TAB1:
38410Sstevel@tonic-gate 							count = 1 + (tp->t_col |
38420Sstevel@tonic-gate 							    ~07);
38430Sstevel@tonic-gate 							if (count < 5)
38440Sstevel@tonic-gate 								count = 0;
38450Sstevel@tonic-gate 							break;
38460Sstevel@tonic-gate 						}
38470Sstevel@tonic-gate 					}
38480Sstevel@tonic-gate 					*obp->b_wptr++ = c;
38490Sstevel@tonic-gate 					bytes_left--;
38500Sstevel@tonic-gate 				}
38510Sstevel@tonic-gate 				break;
38520Sstevel@tonic-gate 
38530Sstevel@tonic-gate 			case VTAB:
38540Sstevel@tonic-gate 				if ((tp->t_modes.c_oflag & VTDLY) &&
38550Sstevel@tonic-gate 				    !(tp->t_modes.c_oflag & OFILL))
38560Sstevel@tonic-gate 					count = 127;
38570Sstevel@tonic-gate 				*obp->b_wptr++ = c;
38580Sstevel@tonic-gate 				bytes_left--;
38590Sstevel@tonic-gate 				break;
38600Sstevel@tonic-gate 
38610Sstevel@tonic-gate 			case RETURN:
38620Sstevel@tonic-gate 				/*
38630Sstevel@tonic-gate 				 * Ignore <CR> in column 0 if ONOCR
38640Sstevel@tonic-gate 				 * flag set.
38650Sstevel@tonic-gate 				 */
38660Sstevel@tonic-gate 				if (tp->t_col == 0 &&
38670Sstevel@tonic-gate 				    (tp->t_modes.c_oflag & ONOCR))
38680Sstevel@tonic-gate 					break;
38690Sstevel@tonic-gate 
38700Sstevel@tonic-gate 		cr:
38710Sstevel@tonic-gate 				switch (tp->t_modes.c_oflag & CRDLY) {
38720Sstevel@tonic-gate 
38730Sstevel@tonic-gate 				case CR1:
38740Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OFILL)
38750Sstevel@tonic-gate 						count = 2;
38760Sstevel@tonic-gate 					else
38770Sstevel@tonic-gate 						count = tp->t_col % 2;
38780Sstevel@tonic-gate 					break;
38790Sstevel@tonic-gate 
38800Sstevel@tonic-gate 				case CR2:
38810Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OFILL)
38820Sstevel@tonic-gate 						count = 4;
38830Sstevel@tonic-gate 					else
38840Sstevel@tonic-gate 						count = 6;
38850Sstevel@tonic-gate 					break;
38860Sstevel@tonic-gate 
38870Sstevel@tonic-gate 				case CR3:
38880Sstevel@tonic-gate 					if (tp->t_modes.c_oflag & OFILL)
38890Sstevel@tonic-gate 						count = 0;
38900Sstevel@tonic-gate 					else
38910Sstevel@tonic-gate 						count = 9;
38920Sstevel@tonic-gate 					break;
38930Sstevel@tonic-gate 				}
38940Sstevel@tonic-gate 				tp->t_col = 0;
38950Sstevel@tonic-gate 				*obp->b_wptr++ = c;
38960Sstevel@tonic-gate 				bytes_left--;
38970Sstevel@tonic-gate 				break;
38980Sstevel@tonic-gate 			}
38990Sstevel@tonic-gate 
39000Sstevel@tonic-gate 			if (count != 0) {
39010Sstevel@tonic-gate 				if (tp->t_modes.c_oflag & OFILL) {
39020Sstevel@tonic-gate 					do {
39030Sstevel@tonic-gate 						if (bytes_left == 0) {
39040Sstevel@tonic-gate 							/* LINTED */
39050Sstevel@tonic-gate 							NEW_BLOCK(0);
39060Sstevel@tonic-gate 						}
39070Sstevel@tonic-gate 						if (tp->t_modes.c_oflag & OFDEL)
39080Sstevel@tonic-gate 							*obp->b_wptr++ = CDEL;
39090Sstevel@tonic-gate 						else
39100Sstevel@tonic-gate 							*obp->b_wptr++ = CNUL;
39110Sstevel@tonic-gate 						bytes_left--;
39120Sstevel@tonic-gate 					} while (--count != 0);
39130Sstevel@tonic-gate 				} else {
39140Sstevel@tonic-gate 					if ((tp->t_modes.c_lflag & FLUSHO) &&
39150Sstevel@tonic-gate 					    (tp->t_modes.c_lflag & IEXTEN)) {
39160Sstevel@tonic-gate 						/* drop on floor */
39170Sstevel@tonic-gate 						freemsg(*omp);
39180Sstevel@tonic-gate 					} else {
39190Sstevel@tonic-gate 						/*
39200Sstevel@tonic-gate 						 * Update sysinfo
39210Sstevel@tonic-gate 						 * outch
39220Sstevel@tonic-gate 						 */
39230Sstevel@tonic-gate 						(void) drv_setparm(SYSOUTC,
39240Sstevel@tonic-gate 						    msgdsize(*omp));
39250Sstevel@tonic-gate 						putnext(q, *omp);
39260Sstevel@tonic-gate 						/*
39270Sstevel@tonic-gate 						 * Send M_DELAY
39280Sstevel@tonic-gate 						 * downstream
39290Sstevel@tonic-gate 						 */
39300Sstevel@tonic-gate 						if ((bp =
39310Sstevel@tonic-gate 						    allocb(1, BPRI_MED)) !=
39320Sstevel@tonic-gate 						    NULL) {
39330Sstevel@tonic-gate 							bp->b_datap->db_type =
39340Sstevel@tonic-gate 							    M_DELAY;
39350Sstevel@tonic-gate 							*bp->b_wptr++ =
3936*7012Sis 							    (uchar_t)count;
39370Sstevel@tonic-gate 							putnext(q, bp);
39380Sstevel@tonic-gate 						}
39390Sstevel@tonic-gate 					}
39400Sstevel@tonic-gate 					bytes_left = 0;
39410Sstevel@tonic-gate 					/*
39420Sstevel@tonic-gate 					 * We have to start a new
39430Sstevel@tonic-gate 					 * message; the delay
39440Sstevel@tonic-gate 					 * introduces a break between
39450Sstevel@tonic-gate 					 * messages.
39460Sstevel@tonic-gate 					 */
39470Sstevel@tonic-gate 					*omp = NULL;
39480Sstevel@tonic-gate 					contpp = omp;
39490Sstevel@tonic-gate 				}
39500Sstevel@tonic-gate 			}
39510Sstevel@tonic-gate 		}
39520Sstevel@tonic-gate 		cbp = ibp->b_cont;
39530Sstevel@tonic-gate 		freeb(ibp);
39540Sstevel@tonic-gate 	} while ((ibp = cbp) != NULL);	/* next block, if any */
39550Sstevel@tonic-gate 
39560Sstevel@tonic-gate outofbufs:
39570Sstevel@tonic-gate 	return (ibp);
39580Sstevel@tonic-gate #undef NEW_BLOCK
39590Sstevel@tonic-gate }
39600Sstevel@tonic-gate 
39610Sstevel@tonic-gate 
39620Sstevel@tonic-gate #if !defined(__sparc)
39630Sstevel@tonic-gate int
movtuc(size_t size,unsigned char * from,unsigned char * origto,unsigned char * table)39640Sstevel@tonic-gate movtuc(size_t size, unsigned char *from, unsigned char *origto,
39650Sstevel@tonic-gate 	unsigned char *table)
39660Sstevel@tonic-gate {
39670Sstevel@tonic-gate 	unsigned char *to = origto;
39680Sstevel@tonic-gate 	unsigned char c;
39690Sstevel@tonic-gate 
39700Sstevel@tonic-gate 	while (size != 0 && (c = table[*from++]) != 0) {
39710Sstevel@tonic-gate 		*to++ = c;
39720Sstevel@tonic-gate 		size--;
39730Sstevel@tonic-gate 	}
39740Sstevel@tonic-gate 	return (to - origto);
39750Sstevel@tonic-gate }
39760Sstevel@tonic-gate #endif
39770Sstevel@tonic-gate 
39780Sstevel@tonic-gate static void
ldterm_flush_output(uchar_t c,queue_t * q,ldtermstd_state_t * tp)39790Sstevel@tonic-gate ldterm_flush_output(uchar_t c, queue_t *q, ldtermstd_state_t *tp)
39800Sstevel@tonic-gate {
39810Sstevel@tonic-gate 	/* Already conditioned with IEXTEN during VDISCARD processing */
39820Sstevel@tonic-gate 	if (tp->t_modes.c_lflag & FLUSHO)
39830Sstevel@tonic-gate 		tp->t_modes.c_lflag &= ~FLUSHO;
39840Sstevel@tonic-gate 	else {
39850Sstevel@tonic-gate 		flushq(q, FLUSHDATA);	/* flush our write queue */
39860Sstevel@tonic-gate 		/* flush ones below us */
39870Sstevel@tonic-gate 		(void) putnextctl1(q, M_FLUSH, FLUSHW);
39880Sstevel@tonic-gate 		if ((tp->t_echomp = allocb(EBSIZE, BPRI_HI)) != NULL) {
39890Sstevel@tonic-gate 			(void) ldterm_echo(c, q, 1, tp);
39900Sstevel@tonic-gate 			if (tp->t_msglen != 0)
39910Sstevel@tonic-gate 				ldterm_reprint(q, EBSIZE, tp);
39920Sstevel@tonic-gate 			if (tp->t_echomp != NULL) {
39930Sstevel@tonic-gate 				putnext(q, tp->t_echomp);
39940Sstevel@tonic-gate 				tp->t_echomp = NULL;
39950Sstevel@tonic-gate 			}
39960Sstevel@tonic-gate 		}
39970Sstevel@tonic-gate 		tp->t_modes.c_lflag |= FLUSHO;
39980Sstevel@tonic-gate 	}
39990Sstevel@tonic-gate }
40000Sstevel@tonic-gate 
40010Sstevel@tonic-gate 
40020Sstevel@tonic-gate /*
40030Sstevel@tonic-gate  * Signal generated by the reader: M_PCSIG and M_FLUSH messages sent.
40040Sstevel@tonic-gate  */
40050Sstevel@tonic-gate static void
ldterm_dosig(queue_t * q,int sig,uchar_t c,int mtype,int mode)40060Sstevel@tonic-gate ldterm_dosig(queue_t *q, int sig, uchar_t c, int mtype, int mode)
40070Sstevel@tonic-gate {
40080Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
40090Sstevel@tonic-gate 	int sndsig = 0;
40100Sstevel@tonic-gate 
40110Sstevel@tonic-gate 	/*
40120Sstevel@tonic-gate 	 * c == \0 is brk case; need to flush on BRKINT even if
40130Sstevel@tonic-gate 	 * noflsh is set.
40140Sstevel@tonic-gate 	 */
40150Sstevel@tonic-gate 	if ((!(tp->t_modes.c_lflag & NOFLSH)) || (c == '\0')) {
40160Sstevel@tonic-gate 		if (mode) {
40170Sstevel@tonic-gate 			if (tp->t_state & TS_TTSTOP) {
40180Sstevel@tonic-gate 				sndsig = 1;
40190Sstevel@tonic-gate 				(void) putnextctl1(q, mtype, sig);
40200Sstevel@tonic-gate 			}
40210Sstevel@tonic-gate 			/*
40220Sstevel@tonic-gate 			 * Flush read or write side.
40230Sstevel@tonic-gate 			 * Restart the input or output.
40240Sstevel@tonic-gate 			 */
40250Sstevel@tonic-gate 			if (mode & FLUSHR) {
40260Sstevel@tonic-gate 				flushq(q, FLUSHDATA);
40270Sstevel@tonic-gate 				(void) putnextctl1(WR(q), M_FLUSH, mode);
40280Sstevel@tonic-gate 				if (tp->t_state & (TS_TBLOCK|TS_IFBLOCK)) {
40290Sstevel@tonic-gate 					(void) putnextctl(WR(q), M_STARTI);
40300Sstevel@tonic-gate 					tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
40310Sstevel@tonic-gate 				}
40320Sstevel@tonic-gate 			}
40330Sstevel@tonic-gate 			if (mode & FLUSHW) {
40340Sstevel@tonic-gate 				flushq(WR(q), FLUSHDATA);
40350Sstevel@tonic-gate 				/*
40360Sstevel@tonic-gate 				 * XXX This is extremely gross.
40370Sstevel@tonic-gate 				 * Since we can't be sure our M_FLUSH
40380Sstevel@tonic-gate 				 * will have run its course by the
40390Sstevel@tonic-gate 				 * time we do the echo below, we set
40400Sstevel@tonic-gate 				 * state and toss it in the write put
40410Sstevel@tonic-gate 				 * routine to prevent flushing our
40420Sstevel@tonic-gate 				 * own data.  Note that downstream
40430Sstevel@tonic-gate 				 * modules on the write side will be
40440Sstevel@tonic-gate 				 * flushed by the M_FLUSH sent above.
40450Sstevel@tonic-gate 				 */
40460Sstevel@tonic-gate 				tp->t_state |= TS_FLUSHWAIT;
40470Sstevel@tonic-gate 				(void) putnextctl1(q, M_FLUSH, FLUSHW);
40480Sstevel@tonic-gate 				if (tp->t_state & TS_TTSTOP) {
40490Sstevel@tonic-gate 					(void) putnextctl(WR(q), M_START);
40500Sstevel@tonic-gate 					tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
40510Sstevel@tonic-gate 				}
40520Sstevel@tonic-gate 			}
40530Sstevel@tonic-gate 		}
40540Sstevel@tonic-gate 	}
40550Sstevel@tonic-gate 	tp->t_state &= ~TS_QUOT;
40560Sstevel@tonic-gate 	if (sndsig == 0)
40570Sstevel@tonic-gate 		(void) putnextctl1(q, mtype, sig);
40580Sstevel@tonic-gate 
40590Sstevel@tonic-gate 	if (c != '\0') {
40600Sstevel@tonic-gate 		if ((tp->t_echomp = allocb(4, BPRI_HI)) != NULL) {
40610Sstevel@tonic-gate 			(void) ldterm_echo(c, WR(q), 4, tp);
40620Sstevel@tonic-gate 			putnext(WR(q), tp->t_echomp);
40630Sstevel@tonic-gate 			tp->t_echomp = NULL;
40640Sstevel@tonic-gate 		}
40650Sstevel@tonic-gate 	}
40660Sstevel@tonic-gate }
40670Sstevel@tonic-gate 
40680Sstevel@tonic-gate 
40690Sstevel@tonic-gate /*
40700Sstevel@tonic-gate  * Called when an M_IOCTL message is seen on the write queue; does
40710Sstevel@tonic-gate  * whatever we're supposed to do with it, and either replies
40720Sstevel@tonic-gate  * immediately or passes it to the next module down.
40730Sstevel@tonic-gate  */
40740Sstevel@tonic-gate static void
ldterm_do_ioctl(queue_t * q,mblk_t * mp)40750Sstevel@tonic-gate ldterm_do_ioctl(queue_t *q, mblk_t *mp)
40760Sstevel@tonic-gate {
40770Sstevel@tonic-gate 	ldtermstd_state_t *tp;
40780Sstevel@tonic-gate 	struct iocblk *iocp;
40790Sstevel@tonic-gate 	struct eucioc *euciocp;	/* needed for EUC ioctls */
40800Sstevel@tonic-gate 	ldterm_cs_data_user_t *csdp;
40810Sstevel@tonic-gate 	int i;
40820Sstevel@tonic-gate 	int locale_name_sz;
40830Sstevel@tonic-gate 	uchar_t maxbytelen;
40840Sstevel@tonic-gate 	uchar_t maxscreenlen;
40850Sstevel@tonic-gate 	int error;
40860Sstevel@tonic-gate 
40870Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
40880Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
40910Sstevel@tonic-gate 
40920Sstevel@tonic-gate 	case TCSETS:
40930Sstevel@tonic-gate 	case TCSETSW:
40940Sstevel@tonic-gate 	case TCSETSF:
40950Sstevel@tonic-gate 		{
40960Sstevel@tonic-gate 			/*
40970Sstevel@tonic-gate 			 * Set current parameters and special
40980Sstevel@tonic-gate 			 * characters.
40990Sstevel@tonic-gate 			 */
41000Sstevel@tonic-gate 			struct termios *cb;
41010Sstevel@tonic-gate 			struct termios oldmodes;
41020Sstevel@tonic-gate 
41030Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct termios));
41040Sstevel@tonic-gate 			if (error != 0) {
41050Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
41060Sstevel@tonic-gate 				return;
41070Sstevel@tonic-gate 			}
41080Sstevel@tonic-gate 
41090Sstevel@tonic-gate 			cb = (struct termios *)mp->b_cont->b_rptr;
41100Sstevel@tonic-gate 
41110Sstevel@tonic-gate 			oldmodes = tp->t_amodes;
41120Sstevel@tonic-gate 			tp->t_amodes = *cb;
41130Sstevel@tonic-gate 			if ((tp->t_amodes.c_lflag & PENDIN) &&
41140Sstevel@tonic-gate 			    (tp->t_modes.c_lflag & IEXTEN)) {
41150Sstevel@tonic-gate 				/*
41160Sstevel@tonic-gate 				 * Yuk.  The C shell file completion
41170Sstevel@tonic-gate 				 * code actually uses this "feature",
41180Sstevel@tonic-gate 				 * so we have to support it.
41190Sstevel@tonic-gate 				 */
41200Sstevel@tonic-gate 				if (tp->t_message != NULL) {
41210Sstevel@tonic-gate 					tp->t_state |= TS_RESCAN;
41220Sstevel@tonic-gate 					qenable(RD(q));
41230Sstevel@tonic-gate 				}
41240Sstevel@tonic-gate 				tp->t_amodes.c_lflag &= ~PENDIN;
41250Sstevel@tonic-gate 			}
41260Sstevel@tonic-gate 			bcopy(tp->t_amodes.c_cc, tp->t_modes.c_cc, NCCS);
41270Sstevel@tonic-gate 
41280Sstevel@tonic-gate 			/*
41290Sstevel@tonic-gate 			 * ldterm_adjust_modes does not deal with
41300Sstevel@tonic-gate 			 * cflags
41310Sstevel@tonic-gate 			 */
41320Sstevel@tonic-gate 			tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
41330Sstevel@tonic-gate 
41340Sstevel@tonic-gate 			ldterm_adjust_modes(tp);
41350Sstevel@tonic-gate 			if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
41360Sstevel@tonic-gate 				miocnak(q, mp, 0, EAGAIN);
41370Sstevel@tonic-gate 				return;
41380Sstevel@tonic-gate 			}
41390Sstevel@tonic-gate 			/*
41400Sstevel@tonic-gate 			 * The driver may want to know about the
41410Sstevel@tonic-gate 			 * following iflags: IGNBRK, BRKINT, IGNPAR,
41420Sstevel@tonic-gate 			 * PARMRK, INPCK, IXON, IXANY.
41430Sstevel@tonic-gate 			 */
41440Sstevel@tonic-gate 			break;
41450Sstevel@tonic-gate 		}
41460Sstevel@tonic-gate 
41470Sstevel@tonic-gate 	case TCSETA:
41480Sstevel@tonic-gate 	case TCSETAW:
41490Sstevel@tonic-gate 	case TCSETAF:
41500Sstevel@tonic-gate 		{
41510Sstevel@tonic-gate 			/*
41520Sstevel@tonic-gate 			 * Old-style "ioctl" to set current
41530Sstevel@tonic-gate 			 * parameters and special characters. Don't
41540Sstevel@tonic-gate 			 * clear out the unset portions, leave them
41550Sstevel@tonic-gate 			 * as they are.
41560Sstevel@tonic-gate 			 */
41570Sstevel@tonic-gate 			struct termio *cb;
41580Sstevel@tonic-gate 			struct termios oldmodes;
41590Sstevel@tonic-gate 
41600Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct termio));
41610Sstevel@tonic-gate 			if (error != 0) {
41620Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
41630Sstevel@tonic-gate 				return;
41640Sstevel@tonic-gate 			}
41650Sstevel@tonic-gate 
41660Sstevel@tonic-gate 			cb = (struct termio *)mp->b_cont->b_rptr;
41670Sstevel@tonic-gate 
41680Sstevel@tonic-gate 			oldmodes = tp->t_amodes;
41690Sstevel@tonic-gate 			tp->t_amodes.c_iflag =
4170*7012Sis 			    (tp->t_amodes.c_iflag & 0xffff0000 | cb->c_iflag);
41710Sstevel@tonic-gate 			tp->t_amodes.c_oflag =
4172*7012Sis 			    (tp->t_amodes.c_oflag & 0xffff0000 | cb->c_oflag);
41730Sstevel@tonic-gate 			tp->t_amodes.c_cflag =
4174*7012Sis 			    (tp->t_amodes.c_cflag & 0xffff0000 | cb->c_cflag);
41750Sstevel@tonic-gate 			tp->t_amodes.c_lflag =
4176*7012Sis 			    (tp->t_amodes.c_lflag & 0xffff0000 | cb->c_lflag);
41770Sstevel@tonic-gate 
41780Sstevel@tonic-gate 			bcopy(cb->c_cc, tp->t_modes.c_cc, NCC);
41790Sstevel@tonic-gate 			/* TCGETS returns amodes, so update that too */
41800Sstevel@tonic-gate 			bcopy(cb->c_cc, tp->t_amodes.c_cc, NCC);
41810Sstevel@tonic-gate 
41820Sstevel@tonic-gate 			/* ldterm_adjust_modes does not deal with cflags */
41830Sstevel@tonic-gate 
41840Sstevel@tonic-gate 			tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
41850Sstevel@tonic-gate 
41860Sstevel@tonic-gate 			ldterm_adjust_modes(tp);
41870Sstevel@tonic-gate 			if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
41880Sstevel@tonic-gate 				miocnak(q, mp, 0, EAGAIN);
41890Sstevel@tonic-gate 				return;
41900Sstevel@tonic-gate 			}
41910Sstevel@tonic-gate 			/*
41920Sstevel@tonic-gate 			 * The driver may want to know about the
41930Sstevel@tonic-gate 			 * following iflags: IGNBRK, BRKINT, IGNPAR,
41940Sstevel@tonic-gate 			 * PARMRK, INPCK, IXON, IXANY.
41950Sstevel@tonic-gate 			 */
41960Sstevel@tonic-gate 			break;
41970Sstevel@tonic-gate 		}
41980Sstevel@tonic-gate 
41990Sstevel@tonic-gate 	case TCFLSH:
42000Sstevel@tonic-gate 		/*
42010Sstevel@tonic-gate 		 * Do the flush on the write queue immediately, and
42020Sstevel@tonic-gate 		 * queue up any flush on the read queue for the
42030Sstevel@tonic-gate 		 * service procedure to see.  Then turn it into the
42040Sstevel@tonic-gate 		 * appropriate M_FLUSH message, so that the module
42050Sstevel@tonic-gate 		 * below us doesn't have to know about TCFLSH.
42060Sstevel@tonic-gate 		 */
42070Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (int));
42080Sstevel@tonic-gate 		if (error != 0) {
42090Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
42100Sstevel@tonic-gate 			return;
42110Sstevel@tonic-gate 		}
42120Sstevel@tonic-gate 
42130Sstevel@tonic-gate 		ASSERT(mp->b_datap != NULL);
42140Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr == 0) {
42150Sstevel@tonic-gate 			ASSERT(mp->b_datap != NULL);
42160Sstevel@tonic-gate 			(void) putnextctl1(q, M_FLUSH, FLUSHR);
42170Sstevel@tonic-gate 			(void) putctl1(RD(q), M_FLUSH, FLUSHR);
42180Sstevel@tonic-gate 		} else if (*(int *)mp->b_cont->b_rptr == 1) {
42190Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
42200Sstevel@tonic-gate 			ASSERT(mp->b_datap != NULL);
42210Sstevel@tonic-gate 			tp->t_state |= TS_FLUSHWAIT;
42220Sstevel@tonic-gate 			(void) putnextctl1(RD(q), M_FLUSH, FLUSHW);
42230Sstevel@tonic-gate 			(void) putnextctl1(q, M_FLUSH, FLUSHW);
42240Sstevel@tonic-gate 		} else if (*(int *)mp->b_cont->b_rptr == 2) {
42250Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
42260Sstevel@tonic-gate 			ASSERT(mp->b_datap != NULL);
42270Sstevel@tonic-gate 			(void) putnextctl1(q, M_FLUSH, FLUSHRW);
42280Sstevel@tonic-gate 			tp->t_state |= TS_FLUSHWAIT;
42290Sstevel@tonic-gate 			(void) putnextctl1(RD(q), M_FLUSH, FLUSHRW);
42300Sstevel@tonic-gate 		} else {
42310Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
42320Sstevel@tonic-gate 			return;
42330Sstevel@tonic-gate 		}
42340Sstevel@tonic-gate 		ASSERT(mp->b_datap != NULL);
42350Sstevel@tonic-gate 		iocp->ioc_rval = 0;
42360Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
42370Sstevel@tonic-gate 		return;
42380Sstevel@tonic-gate 
42390Sstevel@tonic-gate 	case TCXONC:
42400Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (int));
42410Sstevel@tonic-gate 		if (error != 0) {
42420Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
42430Sstevel@tonic-gate 			return;
42440Sstevel@tonic-gate 		}
42450Sstevel@tonic-gate 
42460Sstevel@tonic-gate 		switch (*(int *)mp->b_cont->b_rptr) {
42470Sstevel@tonic-gate 		case 0:
42480Sstevel@tonic-gate 			if (!(tp->t_state & TS_TTSTOP)) {
42490Sstevel@tonic-gate 				(void) putnextctl(q, M_STOP);
42500Sstevel@tonic-gate 				tp->t_state |= (TS_TTSTOP|TS_OFBLOCK);
42510Sstevel@tonic-gate 			}
42520Sstevel@tonic-gate 			break;
42530Sstevel@tonic-gate 
42540Sstevel@tonic-gate 		case 1:
42550Sstevel@tonic-gate 			if (tp->t_state & TS_TTSTOP) {
42560Sstevel@tonic-gate 				(void) putnextctl(q, M_START);
42570Sstevel@tonic-gate 				tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
42580Sstevel@tonic-gate 			}
42590Sstevel@tonic-gate 			break;
42600Sstevel@tonic-gate 
42610Sstevel@tonic-gate 		case 2:
42620Sstevel@tonic-gate 			(void) putnextctl(q, M_STOPI);
42630Sstevel@tonic-gate 			tp->t_state |= (TS_TBLOCK|TS_IFBLOCK);
42640Sstevel@tonic-gate 			break;
42650Sstevel@tonic-gate 
42660Sstevel@tonic-gate 		case 3:
42670Sstevel@tonic-gate 			(void) putnextctl(q, M_STARTI);
42680Sstevel@tonic-gate 			tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
42690Sstevel@tonic-gate 			break;
42700Sstevel@tonic-gate 
42710Sstevel@tonic-gate 		default:
42720Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
42730Sstevel@tonic-gate 			return;
42740Sstevel@tonic-gate 		}
42750Sstevel@tonic-gate 		ASSERT(mp->b_datap != NULL);
42760Sstevel@tonic-gate 		iocp->ioc_rval = 0;
42770Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
42780Sstevel@tonic-gate 		return;
42790Sstevel@tonic-gate 		/*
42800Sstevel@tonic-gate 		 * TCSBRK is expected to be handled by the driver.
42810Sstevel@tonic-gate 		 * The reason its left for the driver is that when
42820Sstevel@tonic-gate 		 * the argument to TCSBRK is zero driver has to drain
42830Sstevel@tonic-gate 		 * the data and sending a M_IOCACK from LDTERM before
42840Sstevel@tonic-gate 		 * the driver drains the data is going to cause
42850Sstevel@tonic-gate 		 * problems.
42860Sstevel@tonic-gate 		 */
42870Sstevel@tonic-gate 
42880Sstevel@tonic-gate 		/*
42890Sstevel@tonic-gate 		 * The following are EUC related ioctls.  For
42900Sstevel@tonic-gate 		 * EUC_WSET, we have to pass the information on, even
42910Sstevel@tonic-gate 		 * though we ACK the call.  It's vital in the EUC
42920Sstevel@tonic-gate 		 * environment that everybody downstream knows about
42930Sstevel@tonic-gate 		 * the EUC codeset widths currently in use; we
42940Sstevel@tonic-gate 		 * therefore pass down the information in an M_CTL
42950Sstevel@tonic-gate 		 * message.  It will bottom out in the driver.
42960Sstevel@tonic-gate 		 */
42970Sstevel@tonic-gate 	case EUC_WSET:
42980Sstevel@tonic-gate 		{
42990Sstevel@tonic-gate 
43000Sstevel@tonic-gate 			/* only needed for EUC_WSET */
43010Sstevel@tonic-gate 			struct iocblk *riocp;
43020Sstevel@tonic-gate 
43030Sstevel@tonic-gate 			mblk_t *dmp, *dmp_cont;
43040Sstevel@tonic-gate 
43050Sstevel@tonic-gate 			/*
43060Sstevel@tonic-gate 			 * If the user didn't supply any information,
43070Sstevel@tonic-gate 			 * NAK it.
43080Sstevel@tonic-gate 			 */
43090Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct eucioc));
43100Sstevel@tonic-gate 			if (error != 0) {
43110Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
43120Sstevel@tonic-gate 				return;
43130Sstevel@tonic-gate 			}
43140Sstevel@tonic-gate 
43150Sstevel@tonic-gate 			euciocp = (struct eucioc *)mp->b_cont->b_rptr;
43160Sstevel@tonic-gate 			/*
43170Sstevel@tonic-gate 			 * Check here for something reasonable.  If
43180Sstevel@tonic-gate 			 * anything will take more than EUC_MAXW
43190Sstevel@tonic-gate 			 * columns or more than EUC_MAXW bytes
43200Sstevel@tonic-gate 			 * following SS2 or SS3, then just reject it
43210Sstevel@tonic-gate 			 * out of hand. It's not impossible for us to
43220Sstevel@tonic-gate 			 * do it, it just isn't reasonable.  So far,
43230Sstevel@tonic-gate 			 * in the world, we've seen the absolute max
43240Sstevel@tonic-gate 			 * columns to be 2 and the max number of
43250Sstevel@tonic-gate 			 * bytes to be 3.  This allows room for some
43260Sstevel@tonic-gate 			 * expansion of that, but it probably won't
43270Sstevel@tonic-gate 			 * even be necessary. At the moment, we
43280Sstevel@tonic-gate 			 * return a "range" error.  If you really
43290Sstevel@tonic-gate 			 * need to, you can push EUC_MAXW up to over
43300Sstevel@tonic-gate 			 * 200; it doesn't make sense, though, with
43310Sstevel@tonic-gate 			 * only a CANBSIZ sized input limit (usually
43320Sstevel@tonic-gate 			 * 256)!
43330Sstevel@tonic-gate 			 */
43340Sstevel@tonic-gate 			for (i = 0; i < 4; i++) {
43350Sstevel@tonic-gate 				if ((euciocp->eucw[i] > EUC_MAXW) ||
43360Sstevel@tonic-gate 				    (euciocp->scrw[i] > EUC_MAXW)) {
43370Sstevel@tonic-gate 					miocnak(q, mp, 0, ERANGE);
43380Sstevel@tonic-gate 					return;
43390Sstevel@tonic-gate 				}
43400Sstevel@tonic-gate 			}
43410Sstevel@tonic-gate 			/*
43420Sstevel@tonic-gate 			 * Otherwise, save the information in tp,
43430Sstevel@tonic-gate 			 * force codeset 0 (ASCII) to be one byte,
43440Sstevel@tonic-gate 			 * one column.
43450Sstevel@tonic-gate 			 */
43460Sstevel@tonic-gate 			cp_eucwioc(euciocp, &tp->eucwioc, EUCIN);
43470Sstevel@tonic-gate 			tp->eucwioc.eucw[0] = tp->eucwioc.scrw[0] = 1;
43480Sstevel@tonic-gate 			/*
43490Sstevel@tonic-gate 			 * Now, check out whether we're doing
43500Sstevel@tonic-gate 			 * multibyte processing. if we are, we need
43510Sstevel@tonic-gate 			 * to allocate a block to hold the parallel
43520Sstevel@tonic-gate 			 * array. By convention, we've been passed
43530Sstevel@tonic-gate 			 * what amounts to a CSWIDTH definition.  We
43540Sstevel@tonic-gate 			 * actually NEED the number of bytes for
43550Sstevel@tonic-gate 			 * Codesets 2 & 3.
43560Sstevel@tonic-gate 			 */
43570Sstevel@tonic-gate 			tp->t_maxeuc = 0;	/* reset to say we're NOT */
43580Sstevel@tonic-gate 
43590Sstevel@tonic-gate 			tp->t_state &= ~TS_MEUC;
43600Sstevel@tonic-gate 			/*
43610Sstevel@tonic-gate 			 * We'll set TS_MEUC if we're doing
43620Sstevel@tonic-gate 			 * multi-column OR multi- byte OR both.  It
43630Sstevel@tonic-gate 			 * makes things easier...  NOTE:  If we fail
43640Sstevel@tonic-gate 			 * to get the buffer we need to hold display
43650Sstevel@tonic-gate 			 * widths, then DON'T let the TS_MEUC bit get
43660Sstevel@tonic-gate 			 * set!
43670Sstevel@tonic-gate 			 */
43680Sstevel@tonic-gate 			for (i = 0; i < 4; i++) {
43690Sstevel@tonic-gate 				if (tp->eucwioc.eucw[i] > tp->t_maxeuc)
43700Sstevel@tonic-gate 					tp->t_maxeuc = tp->eucwioc.eucw[i];
43710Sstevel@tonic-gate 				if (tp->eucwioc.scrw[i] > 1)
43720Sstevel@tonic-gate 					tp->t_state |= TS_MEUC;
43730Sstevel@tonic-gate 			}
43740Sstevel@tonic-gate 			if ((tp->t_maxeuc > 1) || (tp->t_state & TS_MEUC)) {
43750Sstevel@tonic-gate 				if (!tp->t_eucp_mp) {
43760Sstevel@tonic-gate 					if (!(tp->t_eucp_mp = allocb(CANBSIZ,
43770Sstevel@tonic-gate 					    BPRI_HI))) {
43780Sstevel@tonic-gate 						tp->t_maxeuc = 1;
43790Sstevel@tonic-gate 						tp->t_state &= ~TS_MEUC;
43800Sstevel@tonic-gate 						cmn_err(CE_WARN,
43810Sstevel@tonic-gate 						    "Can't allocate eucp_mp");
43820Sstevel@tonic-gate 						miocnak(q, mp, 0, ENOSR);
43830Sstevel@tonic-gate 						return;
43840Sstevel@tonic-gate 					}
43850Sstevel@tonic-gate 					/*
43860Sstevel@tonic-gate 					 * here, if there's junk in
43870Sstevel@tonic-gate 					 * the canonical buffer, then
43880Sstevel@tonic-gate 					 * move the eucp pointer past
43890Sstevel@tonic-gate 					 * it, so we don't run off
43900Sstevel@tonic-gate 					 * the beginning.  This is a
43910Sstevel@tonic-gate 					 * total botch, but will
43920Sstevel@tonic-gate 					 * hopefully keep stuff from
43930Sstevel@tonic-gate 					 * getting too messed up
43940Sstevel@tonic-gate 					 * until the user flushes
43950Sstevel@tonic-gate 					 * this line!
43960Sstevel@tonic-gate 					 */
43970Sstevel@tonic-gate 					if (tp->t_msglen) {
4398*7012Sis 						tp->t_eucp =
4399*7012Sis 						    tp->t_eucp_mp->b_rptr;
44000Sstevel@tonic-gate 						for (i = tp->t_msglen; i; i--)
44010Sstevel@tonic-gate 							*tp->t_eucp++ = 1;
4402*7012Sis 					} else {
4403*7012Sis 						tp->t_eucp =
4404*7012Sis 						    tp->t_eucp_mp->b_rptr;
4405*7012Sis 					}
44060Sstevel@tonic-gate 				}
44070Sstevel@tonic-gate 				/* doing multi-byte handling */
44080Sstevel@tonic-gate 				tp->t_state |= TS_MEUC;
44090Sstevel@tonic-gate 
44100Sstevel@tonic-gate 			} else if (tp->t_eucp_mp) {
44110Sstevel@tonic-gate 				freemsg(tp->t_eucp_mp);
44120Sstevel@tonic-gate 				tp->t_eucp_mp = NULL;
44130Sstevel@tonic-gate 				tp->t_eucp = NULL;
44140Sstevel@tonic-gate 			}
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 			/*
44170Sstevel@tonic-gate 			 * Save the EUC width data we have at
44180Sstevel@tonic-gate 			 * the t_csdata, set t_csdata.codeset_type to
44190Sstevel@tonic-gate 			 * EUC one, and, switch the codeset methods at
44200Sstevel@tonic-gate 			 * t_csmethods.
44210Sstevel@tonic-gate 			 */
44220Sstevel@tonic-gate 			bzero(&tp->t_csdata.eucpc_data,
44230Sstevel@tonic-gate 			    (sizeof (ldterm_eucpc_data_t) *
4424*7012Sis 			    LDTERM_CS_MAX_CODESETS));
44250Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[0].byte_length =
4426*7012Sis 			    tp->eucwioc.eucw[1];
44270Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[0].screen_width =
4428*7012Sis 			    tp->eucwioc.scrw[1];
44290Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[1].byte_length =
4430*7012Sis 			    tp->eucwioc.eucw[2];
44310Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[1].screen_width =
4432*7012Sis 			    tp->eucwioc.scrw[2];
44330Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[2].byte_length =
4434*7012Sis 			    tp->eucwioc.eucw[3];
44350Sstevel@tonic-gate 			tp->t_csdata.eucpc_data[2].screen_width =
4436*7012Sis 			    tp->eucwioc.scrw[3];
44370Sstevel@tonic-gate 			tp->t_csdata.version = LDTERM_DATA_VERSION;
44380Sstevel@tonic-gate 			tp->t_csdata.codeset_type = LDTERM_CS_TYPE_EUC;
44390Sstevel@tonic-gate 			/*
44400Sstevel@tonic-gate 			 * We are not using the 'csinfo_num' anyway if the
44410Sstevel@tonic-gate 			 * current codeset type is EUC. So, set it to
44420Sstevel@tonic-gate 			 * the maximum possible.
44430Sstevel@tonic-gate 			 */
44440Sstevel@tonic-gate 			tp->t_csdata.csinfo_num =
4445*7012Sis 			    LDTERM_CS_TYPE_EUC_MAX_SUBCS;
44460Sstevel@tonic-gate 			if (tp->t_csdata.locale_name != (char *)NULL) {
44470Sstevel@tonic-gate 				kmem_free(tp->t_csdata.locale_name,
44480Sstevel@tonic-gate 				    strlen(tp->t_csdata.locale_name) + 1);
44490Sstevel@tonic-gate 				tp->t_csdata.locale_name = (char *)NULL;
44500Sstevel@tonic-gate 			}
44510Sstevel@tonic-gate 			tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
44520Sstevel@tonic-gate 
44530Sstevel@tonic-gate 			/*
44540Sstevel@tonic-gate 			 * If we are able to allocate two blocks (the
44550Sstevel@tonic-gate 			 * iocblk and the associated data), then pass
44560Sstevel@tonic-gate 			 * it downstream, otherwise we'll need to NAK
44570Sstevel@tonic-gate 			 * it, and drop whatever we WERE able to
44580Sstevel@tonic-gate 			 * allocate.
44590Sstevel@tonic-gate 			 */
44600Sstevel@tonic-gate 			if ((dmp = mkiocb(EUC_WSET)) == NULL) {
44610Sstevel@tonic-gate 				miocnak(q, mp, 0, ENOSR);
44620Sstevel@tonic-gate 				return;
44630Sstevel@tonic-gate 			}
44640Sstevel@tonic-gate 			if ((dmp_cont = allocb(EUCSIZE, BPRI_HI)) == NULL) {
44650Sstevel@tonic-gate 				freemsg(dmp);
44660Sstevel@tonic-gate 				miocnak(q, mp, 0, ENOSR);
44670Sstevel@tonic-gate 				return;
44680Sstevel@tonic-gate 			}
44690Sstevel@tonic-gate 
44700Sstevel@tonic-gate 			/*
44710Sstevel@tonic-gate 			 * We got both buffers.  Copy out the EUC
44720Sstevel@tonic-gate 			 * information (as we received it, not what
44730Sstevel@tonic-gate 			 * we're using!) & pass it on.
44740Sstevel@tonic-gate 			 */
44750Sstevel@tonic-gate 			bcopy(mp->b_cont->b_rptr, dmp_cont->b_rptr, EUCSIZE);
44760Sstevel@tonic-gate 			dmp_cont->b_wptr += EUCSIZE;
44770Sstevel@tonic-gate 			dmp->b_cont = dmp_cont;
44780Sstevel@tonic-gate 			dmp->b_datap->db_type = M_CTL;
44790Sstevel@tonic-gate 			dmp_cont->b_datap->db_type = M_DATA;
44800Sstevel@tonic-gate 			riocp = (struct iocblk *)dmp->b_rptr;
44810Sstevel@tonic-gate 			riocp->ioc_count = EUCSIZE;
44820Sstevel@tonic-gate 			putnext(q, dmp);
44830Sstevel@tonic-gate 
44840Sstevel@tonic-gate 			/*
44850Sstevel@tonic-gate 			 * Now ACK the ioctl.
44860Sstevel@tonic-gate 			 */
44870Sstevel@tonic-gate 			iocp->ioc_rval = 0;
44880Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
44890Sstevel@tonic-gate 			return;
44900Sstevel@tonic-gate 		}
44910Sstevel@tonic-gate 
44920Sstevel@tonic-gate 	case EUC_WGET:
44930Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (struct eucioc));
44940Sstevel@tonic-gate 		if (error != 0) {
44950Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
44960Sstevel@tonic-gate 			return;
44970Sstevel@tonic-gate 		}
44980Sstevel@tonic-gate 		euciocp = (struct eucioc *)mp->b_cont->b_rptr;
44990Sstevel@tonic-gate 		cp_eucwioc(&tp->eucwioc, euciocp, EUCOUT);
45000Sstevel@tonic-gate 		iocp->ioc_rval = 0;
45010Sstevel@tonic-gate 		miocack(q, mp, EUCSIZE, 0);
45020Sstevel@tonic-gate 		return;
45030Sstevel@tonic-gate 
45040Sstevel@tonic-gate 	case CSDATA_SET:
45050Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
45060Sstevel@tonic-gate 		if (error != 0) {
45070Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
45080Sstevel@tonic-gate 			return;
45090Sstevel@tonic-gate 		}
45100Sstevel@tonic-gate 
45110Sstevel@tonic-gate 		csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
45120Sstevel@tonic-gate 
45130Sstevel@tonic-gate 		/* Validate the codeset data provided. */
45140Sstevel@tonic-gate 		if (csdp->version > LDTERM_DATA_VERSION ||
45150Sstevel@tonic-gate 		    csdp->codeset_type < LDTERM_CS_TYPE_MIN ||
45160Sstevel@tonic-gate 		    csdp->codeset_type > LDTERM_CS_TYPE_MAX) {
45170Sstevel@tonic-gate 			miocnak(q, mp, 0, ERANGE);
45180Sstevel@tonic-gate 			return;
45190Sstevel@tonic-gate 		}
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate 		if ((csdp->codeset_type == LDTERM_CS_TYPE_EUC &&
4522*7012Sis 		    csdp->csinfo_num > LDTERM_CS_TYPE_EUC_MAX_SUBCS) ||
45230Sstevel@tonic-gate 		    (csdp->codeset_type == LDTERM_CS_TYPE_PCCS &&
4524*7012Sis 		    (csdp->csinfo_num < LDTERM_CS_TYPE_PCCS_MIN_SUBCS ||
4525*7012Sis 		    csdp->csinfo_num > LDTERM_CS_TYPE_PCCS_MAX_SUBCS))) {
45260Sstevel@tonic-gate 			miocnak(q, mp, 0, ERANGE);
45270Sstevel@tonic-gate 			return;
45280Sstevel@tonic-gate 		}
45290Sstevel@tonic-gate 
45300Sstevel@tonic-gate 		maxbytelen = maxscreenlen = 0;
45310Sstevel@tonic-gate 		if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
45320Sstevel@tonic-gate 			for (i = 0; i < LDTERM_CS_TYPE_EUC_MAX_SUBCS; i++) {
45330Sstevel@tonic-gate 				if (csdp->eucpc_data[i].byte_length >
45340Sstevel@tonic-gate 				    EUC_MAXW ||
45350Sstevel@tonic-gate 				    csdp->eucpc_data[i].screen_width >
45360Sstevel@tonic-gate 				    EUC_MAXW) {
45370Sstevel@tonic-gate 					miocnak(q, mp, 0, ERANGE);
45380Sstevel@tonic-gate 					return;
45390Sstevel@tonic-gate 				}
45400Sstevel@tonic-gate 
45410Sstevel@tonic-gate 				if (csdp->eucpc_data[i].byte_length >
45420Sstevel@tonic-gate 				    maxbytelen)
45430Sstevel@tonic-gate 					maxbytelen =
45440Sstevel@tonic-gate 					    csdp->eucpc_data[i].byte_length;
45450Sstevel@tonic-gate 				if (csdp->eucpc_data[i].screen_width >
45460Sstevel@tonic-gate 				    maxscreenlen)
45470Sstevel@tonic-gate 					maxscreenlen =
45480Sstevel@tonic-gate 					    csdp->eucpc_data[i].screen_width;
45490Sstevel@tonic-gate 			}
45500Sstevel@tonic-gate 			/* POSIX/C locale? */
45510Sstevel@tonic-gate 			if (maxbytelen == 0 && maxscreenlen == 0)
45520Sstevel@tonic-gate 				maxbytelen = maxscreenlen = 1;
45530Sstevel@tonic-gate 		} else if (csdp->codeset_type == LDTERM_CS_TYPE_PCCS) {
45540Sstevel@tonic-gate 			for (i = 0; i < LDTERM_CS_MAX_CODESETS; i++) {
45550Sstevel@tonic-gate 				if (csdp->eucpc_data[i].byte_length >
45560Sstevel@tonic-gate 				    LDTERM_CS_MAX_BYTE_LENGTH) {
45570Sstevel@tonic-gate 					miocnak(q, mp, 0, ERANGE);
45580Sstevel@tonic-gate 					return;
45590Sstevel@tonic-gate 				}
45600Sstevel@tonic-gate 				if (csdp->eucpc_data[i].byte_length >
45610Sstevel@tonic-gate 				    maxbytelen)
45620Sstevel@tonic-gate 					maxbytelen =
45630Sstevel@tonic-gate 					    csdp->eucpc_data[i].byte_length;
45640Sstevel@tonic-gate 				if (csdp->eucpc_data[i].screen_width >
45650Sstevel@tonic-gate 				    maxscreenlen)
45660Sstevel@tonic-gate 					maxscreenlen =
45670Sstevel@tonic-gate 					    csdp->eucpc_data[i].screen_width;
45680Sstevel@tonic-gate 			}
45690Sstevel@tonic-gate 		} else if (csdp->codeset_type == LDTERM_CS_TYPE_UTF8) {
45700Sstevel@tonic-gate 			maxbytelen = 4;
45710Sstevel@tonic-gate 			maxscreenlen = 2;
45720Sstevel@tonic-gate 		}
45730Sstevel@tonic-gate 
45740Sstevel@tonic-gate 		locale_name_sz = 0;
45750Sstevel@tonic-gate 		if (csdp->locale_name) {
45760Sstevel@tonic-gate 			for (i = 0; i < MAXNAMELEN; i++)
45770Sstevel@tonic-gate 				if (csdp->locale_name[i] == '\0')
45780Sstevel@tonic-gate 					break;
45790Sstevel@tonic-gate 			/*
45800Sstevel@tonic-gate 			 * We cannot have any string that is not NULL byte
45810Sstevel@tonic-gate 			 * terminated.
45820Sstevel@tonic-gate 			 */
45830Sstevel@tonic-gate 			if (i >= MAXNAMELEN) {
45840Sstevel@tonic-gate 				miocnak(q, mp, 0, ERANGE);
45850Sstevel@tonic-gate 				return;
45860Sstevel@tonic-gate 			}
45870Sstevel@tonic-gate 
45880Sstevel@tonic-gate 			locale_name_sz = i + 1;
45890Sstevel@tonic-gate 		}
45900Sstevel@tonic-gate 
45910Sstevel@tonic-gate 		/*
45920Sstevel@tonic-gate 		 * As the final check, if there was invalid codeset_type
45930Sstevel@tonic-gate 		 * given, or invalid byte_length was specified, it's an error.
45940Sstevel@tonic-gate 		 */
45950Sstevel@tonic-gate 		if (maxbytelen <= 0 || maxscreenlen <= 0) {
45960Sstevel@tonic-gate 			miocnak(q, mp, 0, ERANGE);
45970Sstevel@tonic-gate 			return;
45980Sstevel@tonic-gate 		}
45990Sstevel@tonic-gate 
46000Sstevel@tonic-gate 		/* Do the switching. */
46010Sstevel@tonic-gate 		tp->t_maxeuc = maxbytelen;
46020Sstevel@tonic-gate 		tp->t_state &= ~TS_MEUC;
46030Sstevel@tonic-gate 		if (maxbytelen > 1 || maxscreenlen > 1) {
46040Sstevel@tonic-gate 			if (!tp->t_eucp_mp) {
46050Sstevel@tonic-gate 				if (!(tp->t_eucp_mp = allocb(CANBSIZ,
46060Sstevel@tonic-gate 				    BPRI_HI))) {
46070Sstevel@tonic-gate 					cmn_err(CE_WARN,
4608*7012Sis 					    "Can't allocate eucp_mp");
46090Sstevel@tonic-gate 					miocnak(q, mp, 0, ENOSR);
46100Sstevel@tonic-gate 					return;
46110Sstevel@tonic-gate 				}
46120Sstevel@tonic-gate 				/*
46130Sstevel@tonic-gate 				 * If there's junk in the canonical buffer,
46140Sstevel@tonic-gate 				 * then move the eucp pointer past it,
46150Sstevel@tonic-gate 				 * so we don't run off the beginning. This is
46160Sstevel@tonic-gate 				 * a total botch, but will hopefully keep
46170Sstevel@tonic-gate 				 * stuff from getting too messed up until
46180Sstevel@tonic-gate 				 * the user flushes this line!
46190Sstevel@tonic-gate 				 */
46200Sstevel@tonic-gate 				if (tp->t_msglen) {
46210Sstevel@tonic-gate 					tp->t_eucp = tp->t_eucp_mp->b_rptr;
46220Sstevel@tonic-gate 					for (i = tp->t_msglen; i; i--)
46230Sstevel@tonic-gate 						*tp->t_eucp++ = 1;
46240Sstevel@tonic-gate 				} else {
46250Sstevel@tonic-gate 					tp->t_eucp = tp->t_eucp_mp->b_rptr;
46260Sstevel@tonic-gate 				}
46270Sstevel@tonic-gate 			}
46280Sstevel@tonic-gate 
46290Sstevel@tonic-gate 			/*
46300Sstevel@tonic-gate 			 * We only set TS_MEUC for a multibyte/multi-column
46310Sstevel@tonic-gate 			 * codeset.
46320Sstevel@tonic-gate 			 */
46330Sstevel@tonic-gate 			tp->t_state |= TS_MEUC;
46340Sstevel@tonic-gate 
46350Sstevel@tonic-gate 			tp->t_csdata.version = csdp->version;
46360Sstevel@tonic-gate 			tp->t_csdata.codeset_type = csdp->codeset_type;
46370Sstevel@tonic-gate 			tp->t_csdata.csinfo_num = csdp->csinfo_num;
46380Sstevel@tonic-gate 			bcopy(csdp->eucpc_data, tp->t_csdata.eucpc_data,
4639*7012Sis 			    sizeof (ldterm_eucpc_data_t) *
4640*7012Sis 			    LDTERM_CS_MAX_CODESETS);
46410Sstevel@tonic-gate 			tp->t_csmethods = cs_methods[csdp->codeset_type];
46420Sstevel@tonic-gate 
46430Sstevel@tonic-gate 			if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
46440Sstevel@tonic-gate 				tp->eucwioc.eucw[0] = 1;
46450Sstevel@tonic-gate 				tp->eucwioc.scrw[0] = 1;
46460Sstevel@tonic-gate 
46470Sstevel@tonic-gate 				tp->eucwioc.eucw[1] =
4648*7012Sis 				    csdp->eucpc_data[0].byte_length;
46490Sstevel@tonic-gate 				tp->eucwioc.scrw[1] =
4650*7012Sis 				    csdp->eucpc_data[0].screen_width;
46510Sstevel@tonic-gate 
46520Sstevel@tonic-gate 				tp->eucwioc.eucw[2] =
4653*7012Sis 				    csdp->eucpc_data[1].byte_length + 1;
46540Sstevel@tonic-gate 				tp->eucwioc.scrw[2] =
4655*7012Sis 				    csdp->eucpc_data[1].screen_width;
46560Sstevel@tonic-gate 
46570Sstevel@tonic-gate 				tp->eucwioc.eucw[3] =
4658*7012Sis 				    csdp->eucpc_data[2].byte_length + 1;
46590Sstevel@tonic-gate 				tp->eucwioc.scrw[3] =
4660*7012Sis 				    csdp->eucpc_data[2].screen_width;
46610Sstevel@tonic-gate 			} else {
46620Sstevel@tonic-gate 				/*
46630Sstevel@tonic-gate 				 * We are not going to use this data
46640Sstevel@tonic-gate 				 * structure. So, clear it. Also, stty(1) will
46650Sstevel@tonic-gate 				 * make use of the cleared tp->eucwioc when
46660Sstevel@tonic-gate 				 * it prints out codeset width setting.
46670Sstevel@tonic-gate 				 */
46680Sstevel@tonic-gate 				bzero(&tp->eucwioc, EUCSIZE);
46690Sstevel@tonic-gate 			}
46700Sstevel@tonic-gate 		} else {
46710Sstevel@tonic-gate 			/*
46720Sstevel@tonic-gate 			 * If this codeset is a single byte codeset that
46730Sstevel@tonic-gate 			 * requires only single display column for all
46740Sstevel@tonic-gate 			 * characters, we switch to default EUC codeset
46750Sstevel@tonic-gate 			 * methods and data setting.
46760Sstevel@tonic-gate 			 */
46770Sstevel@tonic-gate 
46780Sstevel@tonic-gate 			if (tp->t_eucp_mp) {
46790Sstevel@tonic-gate 				freemsg(tp->t_eucp_mp);
46800Sstevel@tonic-gate 				tp->t_eucp_mp = NULL;
46810Sstevel@tonic-gate 				tp->t_eucp = NULL;
46820Sstevel@tonic-gate 			}
46830Sstevel@tonic-gate 
46840Sstevel@tonic-gate 			bzero(&tp->eucwioc, EUCSIZE);
46850Sstevel@tonic-gate 			tp->eucwioc.eucw[0] = 1;
46860Sstevel@tonic-gate 			tp->eucwioc.scrw[0] = 1;
46870Sstevel@tonic-gate 			if (tp->t_csdata.locale_name != (char *)NULL) {
46880Sstevel@tonic-gate 				kmem_free(tp->t_csdata.locale_name,
46890Sstevel@tonic-gate 				    strlen(tp->t_csdata.locale_name) + 1);
46900Sstevel@tonic-gate 			}
46910Sstevel@tonic-gate 			tp->t_csdata = default_cs_data;
46920Sstevel@tonic-gate 			tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
46930Sstevel@tonic-gate 		}
46940Sstevel@tonic-gate 
46950Sstevel@tonic-gate 		/* Copy over locale_name. */
46960Sstevel@tonic-gate 		if (tp->t_csdata.locale_name != (char *)NULL) {
46970Sstevel@tonic-gate 			kmem_free(tp->t_csdata.locale_name,
46980Sstevel@tonic-gate 			    strlen(tp->t_csdata.locale_name) + 1);
46990Sstevel@tonic-gate 		}
47000Sstevel@tonic-gate 		if (locale_name_sz > 1) {
47010Sstevel@tonic-gate 			tp->t_csdata.locale_name = (char *)kmem_alloc(
4702*7012Sis 			    locale_name_sz, KM_SLEEP);
47030Sstevel@tonic-gate 			(void) strcpy(tp->t_csdata.locale_name,
47040Sstevel@tonic-gate 			    csdp->locale_name);
47050Sstevel@tonic-gate 		} else {
47060Sstevel@tonic-gate 			tp->t_csdata.locale_name = (char *)NULL;
47070Sstevel@tonic-gate 		}
47080Sstevel@tonic-gate 
47090Sstevel@tonic-gate 		/*
47100Sstevel@tonic-gate 		 * Now ACK the ioctl.
47110Sstevel@tonic-gate 		 */
47120Sstevel@tonic-gate 		iocp->ioc_rval = 0;
47130Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
47140Sstevel@tonic-gate 		return;
47150Sstevel@tonic-gate 
47160Sstevel@tonic-gate 	case CSDATA_GET:
47170Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
47180Sstevel@tonic-gate 		if (error != 0) {
47190Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
47200Sstevel@tonic-gate 			return;
47210Sstevel@tonic-gate 		}
47220Sstevel@tonic-gate 
47230Sstevel@tonic-gate 		csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
47240Sstevel@tonic-gate 
47250Sstevel@tonic-gate 		csdp->version = tp->t_csdata.version;
47260Sstevel@tonic-gate 		csdp->codeset_type = tp->t_csdata.codeset_type;
47270Sstevel@tonic-gate 		csdp->csinfo_num = tp->t_csdata.csinfo_num;
47280Sstevel@tonic-gate 		csdp->pad = tp->t_csdata.pad;
47290Sstevel@tonic-gate 		if (tp->t_csdata.locale_name) {
47300Sstevel@tonic-gate 			(void) strcpy(csdp->locale_name,
4731*7012Sis 			    tp->t_csdata.locale_name);
47320Sstevel@tonic-gate 		} else {
47330Sstevel@tonic-gate 			csdp->locale_name[0] = '\0';
47340Sstevel@tonic-gate 		}
47350Sstevel@tonic-gate 		bcopy(tp->t_csdata.eucpc_data, csdp->eucpc_data,
4736*7012Sis 		    sizeof (ldterm_eucpc_data_t) * LDTERM_CS_MAX_CODESETS);
47370Sstevel@tonic-gate 		/*
47380Sstevel@tonic-gate 		 * If the codeset is an EUC codeset and if it has 2nd and/or
47390Sstevel@tonic-gate 		 * 3rd supplementary codesets, we subtract one from each
47400Sstevel@tonic-gate 		 * byte length of the supplementary codesets. This is
47410Sstevel@tonic-gate 		 * because single shift characters, SS2 and SS3, are not
47420Sstevel@tonic-gate 		 * included in the byte lengths in the user space.
47430Sstevel@tonic-gate 		 */
47440Sstevel@tonic-gate 		if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
47450Sstevel@tonic-gate 			if (csdp->eucpc_data[1].byte_length)
47460Sstevel@tonic-gate 				csdp->eucpc_data[1].byte_length -= 1;
47470Sstevel@tonic-gate 			if (csdp->eucpc_data[2].byte_length)
47480Sstevel@tonic-gate 				csdp->eucpc_data[2].byte_length -= 1;
47490Sstevel@tonic-gate 		}
47500Sstevel@tonic-gate 		iocp->ioc_rval = 0;
47510Sstevel@tonic-gate 		miocack(q, mp, sizeof (ldterm_cs_data_user_t), 0);
47520Sstevel@tonic-gate 		return;
47530Sstevel@tonic-gate 
47540Sstevel@tonic-gate 	case PTSSTTY:
47550Sstevel@tonic-gate 		tp->t_state |= TS_ISPTSTTY;
47560Sstevel@tonic-gate 		break;
47570Sstevel@tonic-gate 
47580Sstevel@tonic-gate 	}
47590Sstevel@tonic-gate 
47600Sstevel@tonic-gate 	putnext(q, mp);
47610Sstevel@tonic-gate }
47620Sstevel@tonic-gate 
47630Sstevel@tonic-gate 
47640Sstevel@tonic-gate /*
47650Sstevel@tonic-gate  * Send an M_SETOPTS message upstream if any mode changes are being
47660Sstevel@tonic-gate  * made that affect the stream head options. returns -1 if allocb
47670Sstevel@tonic-gate  * fails, else returns 0.
47680Sstevel@tonic-gate  */
47690Sstevel@tonic-gate static int
chgstropts(struct termios * oldmodep,ldtermstd_state_t * tp,queue_t * q)47700Sstevel@tonic-gate chgstropts(struct termios *oldmodep, ldtermstd_state_t *tp, queue_t *q)
47710Sstevel@tonic-gate {
47720Sstevel@tonic-gate 	struct stroptions optbuf;
47730Sstevel@tonic-gate 	mblk_t *bp;
47740Sstevel@tonic-gate 
47750Sstevel@tonic-gate 	optbuf.so_flags = 0;
47760Sstevel@tonic-gate 	if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & ICANON) {
47770Sstevel@tonic-gate 		/*
47780Sstevel@tonic-gate 		 * Canonical mode is changing state; switch the
47790Sstevel@tonic-gate 		 * stream head to message-nondiscard or byte-stream
47800Sstevel@tonic-gate 		 * mode.  Also, rerun the service procedure so it can
47810Sstevel@tonic-gate 		 * change its mind about whether to send data
47820Sstevel@tonic-gate 		 * upstream or not.
47830Sstevel@tonic-gate 		 */
47840Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & ICANON) {
47850Sstevel@tonic-gate 			DEBUG4(("CHANGING TO CANON MODE\n"));
47860Sstevel@tonic-gate 			optbuf.so_flags = SO_READOPT|SO_MREADOFF;
47870Sstevel@tonic-gate 			optbuf.so_readopt = RMSGN;
47880Sstevel@tonic-gate 
47890Sstevel@tonic-gate 			/*
47900Sstevel@tonic-gate 			 * if there is a pending raw mode timeout,
47910Sstevel@tonic-gate 			 * clear it
47920Sstevel@tonic-gate 			 */
47930Sstevel@tonic-gate 
47940Sstevel@tonic-gate 			/*
47950Sstevel@tonic-gate 			 * Clear VMIN/VTIME state, cancel timers
47960Sstevel@tonic-gate 			 */
47970Sstevel@tonic-gate 			vmin_satisfied(q, tp, 0);
47980Sstevel@tonic-gate 		} else {
47990Sstevel@tonic-gate 			DEBUG4(("CHANGING TO RAW MODE\n"));
48000Sstevel@tonic-gate 			optbuf.so_flags = SO_READOPT|SO_MREADON;
48010Sstevel@tonic-gate 			optbuf.so_readopt = RNORM;
48020Sstevel@tonic-gate 		}
48030Sstevel@tonic-gate 	}
48040Sstevel@tonic-gate 	if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & TOSTOP) {
48050Sstevel@tonic-gate 		/*
48060Sstevel@tonic-gate 		 * The "stop on background write" bit is changing.
48070Sstevel@tonic-gate 		 */
48080Sstevel@tonic-gate 		if (tp->t_modes.c_lflag & TOSTOP)
48090Sstevel@tonic-gate 			optbuf.so_flags |= SO_TOSTOP;
48100Sstevel@tonic-gate 		else
48110Sstevel@tonic-gate 			optbuf.so_flags |= SO_TONSTOP;
48120Sstevel@tonic-gate 	}
48130Sstevel@tonic-gate 	if (optbuf.so_flags != 0) {
48140Sstevel@tonic-gate 		if ((bp = allocb(sizeof (struct stroptions), BPRI_HI)) ==
48150Sstevel@tonic-gate 		    NULL) {
48160Sstevel@tonic-gate 			return (-1);
48170Sstevel@tonic-gate 		}
48180Sstevel@tonic-gate 		*(struct stroptions *)bp->b_wptr = optbuf;
48190Sstevel@tonic-gate 		bp->b_wptr += sizeof (struct stroptions);
48200Sstevel@tonic-gate 		bp->b_datap->db_type = M_SETOPTS;
48210Sstevel@tonic-gate 		DEBUG4(("M_SETOPTS to stream head\n"));
48220Sstevel@tonic-gate 		putnext(q, bp);
48230Sstevel@tonic-gate 	}
48240Sstevel@tonic-gate 	return (0);
48250Sstevel@tonic-gate }
48260Sstevel@tonic-gate 
48270Sstevel@tonic-gate 
48280Sstevel@tonic-gate /*
48290Sstevel@tonic-gate  * Called when an M_IOCACK message is seen on the read queue;
48300Sstevel@tonic-gate  * modifies the data being returned, if necessary, and passes the
48310Sstevel@tonic-gate  * reply up.
48320Sstevel@tonic-gate  */
48330Sstevel@tonic-gate static void
ldterm_ioctl_reply(queue_t * q,mblk_t * mp)48340Sstevel@tonic-gate ldterm_ioctl_reply(queue_t *q, mblk_t *mp)
48350Sstevel@tonic-gate {
48360Sstevel@tonic-gate 	ldtermstd_state_t *tp;
48370Sstevel@tonic-gate 	struct iocblk *iocp;
48380Sstevel@tonic-gate 
48390Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
48400Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
48410Sstevel@tonic-gate 
48420Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
48430Sstevel@tonic-gate 
48440Sstevel@tonic-gate 	case TCGETS:
48450Sstevel@tonic-gate 		{
48460Sstevel@tonic-gate 			/*
48470Sstevel@tonic-gate 			 * Get current parameters and return them to
48480Sstevel@tonic-gate 			 * stream head eventually.
48490Sstevel@tonic-gate 			 */
48500Sstevel@tonic-gate 			struct termios *cb =
4851*7012Sis 			    (struct termios *)mp->b_cont->b_rptr;
48520Sstevel@tonic-gate 
48530Sstevel@tonic-gate 			/*
48540Sstevel@tonic-gate 			 * cflag has cflags sent upstream by the
48550Sstevel@tonic-gate 			 * driver
48560Sstevel@tonic-gate 			 */
48570Sstevel@tonic-gate 			tcflag_t cflag = cb->c_cflag;
48580Sstevel@tonic-gate 
48590Sstevel@tonic-gate 			*cb = tp->t_amodes;
48600Sstevel@tonic-gate 			if (cflag != 0)
48610Sstevel@tonic-gate 				cb->c_cflag = cflag;	/* set by driver */
48620Sstevel@tonic-gate 			break;
48630Sstevel@tonic-gate 		}
48640Sstevel@tonic-gate 
48650Sstevel@tonic-gate 	case TCGETA:
48660Sstevel@tonic-gate 		{
48670Sstevel@tonic-gate 			/*
48680Sstevel@tonic-gate 			 * Old-style "ioctl" to get current
48690Sstevel@tonic-gate 			 * parameters and return them to stream head
48700Sstevel@tonic-gate 			 * eventually.
48710Sstevel@tonic-gate 			 */
48720Sstevel@tonic-gate 			struct termio *cb =
4873*7012Sis 			    (struct termio *)mp->b_cont->b_rptr;
48740Sstevel@tonic-gate 
48750Sstevel@tonic-gate 			cb->c_iflag = tp->t_amodes.c_iflag; /* all except the */
48760Sstevel@tonic-gate 			cb->c_oflag = tp->t_amodes.c_oflag; /* cb->c_cflag */
48770Sstevel@tonic-gate 			cb->c_lflag = tp->t_amodes.c_lflag;
48780Sstevel@tonic-gate 
48790Sstevel@tonic-gate 			if (cb->c_cflag == 0)	/* not set by driver */
48800Sstevel@tonic-gate 				cb->c_cflag = tp->t_amodes.c_cflag;
48810Sstevel@tonic-gate 
48820Sstevel@tonic-gate 			cb->c_line = 0;
48830Sstevel@tonic-gate 			bcopy(tp->t_amodes.c_cc, cb->c_cc, NCC);
48840Sstevel@tonic-gate 			break;
48850Sstevel@tonic-gate 		}
48860Sstevel@tonic-gate 	}
48870Sstevel@tonic-gate 	putnext(q, mp);
48880Sstevel@tonic-gate }
48890Sstevel@tonic-gate 
48900Sstevel@tonic-gate 
48910Sstevel@tonic-gate /*
48920Sstevel@tonic-gate  * A VMIN/VTIME request has been satisfied. Cancel outstanding timers
48930Sstevel@tonic-gate  * if they exist, clear TS_MREAD state, and send upstream. If a NULL
48940Sstevel@tonic-gate  * queue ptr is passed, just reset VMIN/VTIME state.
48950Sstevel@tonic-gate  */
48960Sstevel@tonic-gate static void
vmin_satisfied(queue_t * q,ldtermstd_state_t * tp,int sendup)48970Sstevel@tonic-gate vmin_satisfied(queue_t *q, ldtermstd_state_t *tp, int sendup)
48980Sstevel@tonic-gate {
48990Sstevel@tonic-gate 	ASSERT(q);
49000Sstevel@tonic-gate 	if (tp->t_vtid != 0)  {
4901*7012Sis 		DEBUG4(("vmin_satisfied: cancelled timer id %d\n", tp->t_vtid));
49020Sstevel@tonic-gate 		(void) quntimeout(q, tp->t_vtid);
49030Sstevel@tonic-gate 		tp->t_vtid = 0;
49040Sstevel@tonic-gate 	}
49050Sstevel@tonic-gate 	if (sendup) {
49060Sstevel@tonic-gate 		if (tp->t_msglen == 0 && V_MIN) {
49070Sstevel@tonic-gate 			/* EMPTY */
49080Sstevel@tonic-gate 			DEBUG4(("vmin_satisfied: data swiped, msglen = 0\n"));
49090Sstevel@tonic-gate 		} else {
49100Sstevel@tonic-gate 			if ((!q->q_first) ||
49110Sstevel@tonic-gate 			    (q->q_first->b_datap->db_type != M_DATA) ||
49120Sstevel@tonic-gate 			    (tp->t_msglen >= LDCHUNK)) {
49130Sstevel@tonic-gate 				ldterm_msg_upstream(q, tp);
49140Sstevel@tonic-gate 				DEBUG4(("vmin_satisfied: delivering data\n"));
49150Sstevel@tonic-gate 			}
49160Sstevel@tonic-gate 		}
49170Sstevel@tonic-gate 	} else {
49180Sstevel@tonic-gate 		/* EMPTY */
49190Sstevel@tonic-gate 		DEBUG4(("vmin_satisfied: VMIN/TIME state reset\n"));
49200Sstevel@tonic-gate 	}
49210Sstevel@tonic-gate 	tp->t_state &= ~TS_MREAD;
49220Sstevel@tonic-gate }
49230Sstevel@tonic-gate 
49240Sstevel@tonic-gate static void
vmin_settimer(queue_t * q)49250Sstevel@tonic-gate vmin_settimer(queue_t *q)
49260Sstevel@tonic-gate {
49270Sstevel@tonic-gate 	ldtermstd_state_t *tp;
49280Sstevel@tonic-gate 
49290Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
49300Sstevel@tonic-gate 
49310Sstevel@tonic-gate 	/*
49320Sstevel@tonic-gate 	 * Don't start any time bombs.
49330Sstevel@tonic-gate 	 */
49340Sstevel@tonic-gate 	if (tp->t_state & TS_CLOSE)
49350Sstevel@tonic-gate 		return;
49360Sstevel@tonic-gate 
49370Sstevel@tonic-gate 	/*
49380Sstevel@tonic-gate 	 * tp->t_vtid should NOT be set here unless VMIN > 0 and
49390Sstevel@tonic-gate 	 * VTIME > 0.
49400Sstevel@tonic-gate 	 */
49410Sstevel@tonic-gate 	if (tp->t_vtid) {
49420Sstevel@tonic-gate 		if (V_MIN && V_TIME) {
49430Sstevel@tonic-gate 			/* EMPTY */
49440Sstevel@tonic-gate 			DEBUG4(("vmin_settimer: timer restarted, old tid=%d\n",
4945*7012Sis 			    tp->t_vtid));
49460Sstevel@tonic-gate 		} else {
49470Sstevel@tonic-gate 			/* EMPTY */
49480Sstevel@tonic-gate 			DEBUG4(("vmin_settimer: tid = %d was still active!\n",
4949*7012Sis 			    tp->t_vtid));
49500Sstevel@tonic-gate 		}
49510Sstevel@tonic-gate 		(void) quntimeout(q, tp->t_vtid);
49520Sstevel@tonic-gate 		tp->t_vtid = 0;
49530Sstevel@tonic-gate 	}
49540Sstevel@tonic-gate 	tp->t_vtid = qtimeout(q, vmin_timed_out, q,
49550Sstevel@tonic-gate 	    (clock_t)(V_TIME * (hz / 10)));
49560Sstevel@tonic-gate 	DEBUG4(("vmin_settimer: timer started, tid = %d\n", tp->t_vtid));
49570Sstevel@tonic-gate }
49580Sstevel@tonic-gate 
49590Sstevel@tonic-gate 
49600Sstevel@tonic-gate /*
49610Sstevel@tonic-gate  * BRRrrringgg!! VTIME was satisfied instead of VMIN
49620Sstevel@tonic-gate  */
49630Sstevel@tonic-gate static void
vmin_timed_out(void * arg)49640Sstevel@tonic-gate vmin_timed_out(void *arg)
49650Sstevel@tonic-gate {
49660Sstevel@tonic-gate 	queue_t *q = arg;
49670Sstevel@tonic-gate 	ldtermstd_state_t *tp;
49680Sstevel@tonic-gate 
49690Sstevel@tonic-gate 	tp = (ldtermstd_state_t *)q->q_ptr;
49700Sstevel@tonic-gate 
49710Sstevel@tonic-gate 	DEBUG4(("vmin_timed_out: tid = %d\n", tp->t_vtid));
49720Sstevel@tonic-gate 	/* don't call untimeout now that we are in the timeout */
49730Sstevel@tonic-gate 	tp->t_vtid = 0;
49740Sstevel@tonic-gate 	vmin_satisfied(q, tp, 1);
49750Sstevel@tonic-gate }
49760Sstevel@tonic-gate 
49770Sstevel@tonic-gate 
49780Sstevel@tonic-gate /*
49790Sstevel@tonic-gate  * Routine to adjust termios flags to be processed by the line
49800Sstevel@tonic-gate  * discipline. Driver below sends a termios structure, with the flags
49810Sstevel@tonic-gate  * the driver intends to process. XOR'ing the driver sent termios
49820Sstevel@tonic-gate  * structure with current termios structure with the default values
49830Sstevel@tonic-gate  * (or set by ioctls from userland), we come up with a new termios
49840Sstevel@tonic-gate  * structrue, the flags of which will be used by the line discipline
49850Sstevel@tonic-gate  * in processing input and output. On return from this routine, we
49860Sstevel@tonic-gate  * will have the following fields set in tp structure -->
49870Sstevel@tonic-gate  * tp->t_modes:	modes the line discipline will process tp->t_amodes:
49880Sstevel@tonic-gate  * modes the user process thinks the line discipline is processing
49890Sstevel@tonic-gate  */
49900Sstevel@tonic-gate 
49910Sstevel@tonic-gate static void
ldterm_adjust_modes(ldtermstd_state_t * tp)49920Sstevel@tonic-gate ldterm_adjust_modes(ldtermstd_state_t *tp)
49930Sstevel@tonic-gate {
49940Sstevel@tonic-gate 
49950Sstevel@tonic-gate 	DEBUG6(("original iflag = %o\n", tp->t_modes.c_iflag));
49960Sstevel@tonic-gate 	tp->t_modes.c_iflag = tp->t_amodes.c_iflag & ~(tp->t_dmodes.c_iflag);
49970Sstevel@tonic-gate 	tp->t_modes.c_oflag = tp->t_amodes.c_oflag & ~(tp->t_dmodes.c_oflag);
49980Sstevel@tonic-gate 	tp->t_modes.c_lflag = tp->t_amodes.c_lflag & ~(tp->t_dmodes.c_lflag);
49990Sstevel@tonic-gate 	DEBUG6(("driver iflag = %o\n", tp->t_dmodes.c_iflag));
50000Sstevel@tonic-gate 	DEBUG6(("apparent iflag = %o\n", tp->t_amodes.c_iflag));
50010Sstevel@tonic-gate 	DEBUG6(("effective iflag = %o\n", tp->t_modes.c_iflag));
50020Sstevel@tonic-gate 
50030Sstevel@tonic-gate 	/* No negotiation of clfags  c_cc array special characters */
50040Sstevel@tonic-gate 	/*
50050Sstevel@tonic-gate 	 * Copy from amodes to modes already done by TCSETA/TCSETS
50060Sstevel@tonic-gate 	 * code
50070Sstevel@tonic-gate 	 */
50080Sstevel@tonic-gate }
50090Sstevel@tonic-gate 
50100Sstevel@tonic-gate 
50110Sstevel@tonic-gate /*
50120Sstevel@tonic-gate  * Erase one multi-byte character.  If TS_MEUC is set AND this
50130Sstevel@tonic-gate  * is a multi-byte character, then this should be called instead of
50140Sstevel@tonic-gate  * ldterm_erase.  "ldterm_erase" will handle ASCII nicely, thank you.
50150Sstevel@tonic-gate  *
50160Sstevel@tonic-gate  * We'd better be pointing to the last byte.  If we aren't, it will get
50170Sstevel@tonic-gate  * screwed up.
50180Sstevel@tonic-gate  */
50190Sstevel@tonic-gate static void
ldterm_csi_erase(queue_t * q,size_t ebsize,ldtermstd_state_t * tp)50200Sstevel@tonic-gate ldterm_csi_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
50210Sstevel@tonic-gate {
50220Sstevel@tonic-gate 	int i, ung;
50230Sstevel@tonic-gate 	uchar_t *p, *bottom;
50240Sstevel@tonic-gate 	uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
50250Sstevel@tonic-gate 	int c;
50260Sstevel@tonic-gate 	int j;
50270Sstevel@tonic-gate 	int len;
50280Sstevel@tonic-gate 
50290Sstevel@tonic-gate 	if (tp->t_eucleft) {
50300Sstevel@tonic-gate 		/* XXX Ick.  We're in the middle of an EUC! */
50310Sstevel@tonic-gate 		/* What to do now? */
50320Sstevel@tonic-gate 		ldterm_eucwarn(tp);
50330Sstevel@tonic-gate 		return;		/* ignore it??? */
50340Sstevel@tonic-gate 	}
50350Sstevel@tonic-gate 	bottom = tp->t_eucp_mp->b_rptr;
50360Sstevel@tonic-gate 	p = tp->t_eucp - 1;	/* previous byte */
50370Sstevel@tonic-gate 	if (p < bottom)
50380Sstevel@tonic-gate 		return;
50390Sstevel@tonic-gate 	ung = 1;		/* number of bytes to un-get from buffer */
50400Sstevel@tonic-gate 	/*
50410Sstevel@tonic-gate 	 * go through the buffer until we find the beginning of the
50420Sstevel@tonic-gate 	 * multi-byte char.
50430Sstevel@tonic-gate 	 */
50440Sstevel@tonic-gate 	while ((*p == 0) && (p > bottom)) {
50450Sstevel@tonic-gate 		p--;
50460Sstevel@tonic-gate 		++ung;
50470Sstevel@tonic-gate 	}
50480Sstevel@tonic-gate 
50490Sstevel@tonic-gate 	/*
50500Sstevel@tonic-gate 	 * Now, "ung" is the number of bytes to unget from the buffer
50510Sstevel@tonic-gate 	 * and "*p" is the disp width of it. Fool "ldterm_rubout"
50520Sstevel@tonic-gate 	 * into thinking we're rubbing out ASCII characters.  Do that
50530Sstevel@tonic-gate 	 * for the display width of the character.
50540Sstevel@tonic-gate 	 *
50550Sstevel@tonic-gate 	 * Also we accumulate bytes of the character so that if the character
50560Sstevel@tonic-gate 	 * is a UTF-8 character, we will get the display width of the UTF-8
50570Sstevel@tonic-gate 	 * character.
50580Sstevel@tonic-gate 	 */
50590Sstevel@tonic-gate 	if (ung >= LDTERM_CS_MAX_BYTE_LENGTH) {
50600Sstevel@tonic-gate 		j = len = LDTERM_CS_MAX_BYTE_LENGTH;
50610Sstevel@tonic-gate 	} else {
50620Sstevel@tonic-gate 		j = len = ung;
50630Sstevel@tonic-gate 	}
50640Sstevel@tonic-gate 	for (i = 0; i < ung; i++) {	/* remove from buf */
50650Sstevel@tonic-gate 		if ((c = ldterm_unget(tp)) != (-1)) {
50660Sstevel@tonic-gate 			ldterm_trim(tp);
50670Sstevel@tonic-gate 			if (j > 0)
50680Sstevel@tonic-gate 				u8[--j] = (uchar_t)c;
50690Sstevel@tonic-gate 		}
50700Sstevel@tonic-gate 	}
50710Sstevel@tonic-gate 	if (*p == UNKNOWN_WIDTH) {
50720Sstevel@tonic-gate 		if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_UTF8) {
50730Sstevel@tonic-gate 			*p = ldterm_utf8_width(u8, len);
50740Sstevel@tonic-gate 		} else {
50750Sstevel@tonic-gate 			*p = 1;
50760Sstevel@tonic-gate 		}
50770Sstevel@tonic-gate 	}
50780Sstevel@tonic-gate 	for (i = 0; i < (int)*p; i++)	/* remove from screen */
50790Sstevel@tonic-gate 		ldterm_rubout(' ', q, ebsize, tp);
50800Sstevel@tonic-gate 	/*
50810Sstevel@tonic-gate 	 * Adjust the parallel array pointer.  Zero out the contents
50820Sstevel@tonic-gate 	 * of parallel array for this position, just to make sure...
50830Sstevel@tonic-gate 	 */
50840Sstevel@tonic-gate 	tp->t_eucp = p;
50850Sstevel@tonic-gate 	*p = 0;
50860Sstevel@tonic-gate }
50870Sstevel@tonic-gate 
50880Sstevel@tonic-gate 
50890Sstevel@tonic-gate /*
50900Sstevel@tonic-gate  * This is kind of a safety valve.  Whenever we see a bad sequence
50910Sstevel@tonic-gate  * come up, we call eucwarn.  It just tallies the junk until a
50920Sstevel@tonic-gate  * threshold is reached.  Then it prints ONE message on the console
50930Sstevel@tonic-gate  * and not any more. Hopefully, we can catch garbage; maybe it will
50940Sstevel@tonic-gate  * be useful to somebody.
50950Sstevel@tonic-gate  */
50960Sstevel@tonic-gate static void
ldterm_eucwarn(ldtermstd_state_t * tp)50970Sstevel@tonic-gate ldterm_eucwarn(ldtermstd_state_t *tp)
50980Sstevel@tonic-gate {
50990Sstevel@tonic-gate 	++tp->t_eucwarn;
51000Sstevel@tonic-gate #ifdef DEBUG
51010Sstevel@tonic-gate 	if ((tp->t_eucwarn > EUC_WARNCNT) && !(tp->t_state & TS_WARNED)) {
51020Sstevel@tonic-gate 		cmn_err(CE_WARN,
51030Sstevel@tonic-gate 		    "ldterm: tty at addr %p in multi-byte mode --",
51040Sstevel@tonic-gate 		    (void *)tp);
51050Sstevel@tonic-gate 		cmn_err(CE_WARN,
51060Sstevel@tonic-gate 		    "Over %d bad EUC characters this session", EUC_WARNCNT);
51070Sstevel@tonic-gate 		tp->t_state |= TS_WARNED;
51080Sstevel@tonic-gate 	}
51090Sstevel@tonic-gate #endif
51100Sstevel@tonic-gate }
51110Sstevel@tonic-gate 
51120Sstevel@tonic-gate 
51130Sstevel@tonic-gate /*
51140Sstevel@tonic-gate  * Copy an "eucioc_t" structure.  We use the structure with
51150Sstevel@tonic-gate  * incremented values for Codesets 2 & 3.  The specification in
51160Sstevel@tonic-gate  * eucioctl is that the sames values as the CSWIDTH definition at
51170Sstevel@tonic-gate  * user level are passed to us. When we copy it "in" to ourselves, we
51180Sstevel@tonic-gate  * do the increment.  That allows us to avoid treating each character
51190Sstevel@tonic-gate  * set separately for "t_eucleft" purposes. When we copy it "out" to
51200Sstevel@tonic-gate  * return it to the user, we decrement the values so the user gets
51210Sstevel@tonic-gate  * what it expects, and it matches CSWIDTH in the environment (if
51220Sstevel@tonic-gate  * things are consistent!).
51230Sstevel@tonic-gate  */
51240Sstevel@tonic-gate static void
cp_eucwioc(eucioc_t * from,eucioc_t * to,int dir)51250Sstevel@tonic-gate cp_eucwioc(eucioc_t *from, eucioc_t *to, int dir)
51260Sstevel@tonic-gate {
51270Sstevel@tonic-gate 	bcopy(from, to, EUCSIZE);
51280Sstevel@tonic-gate 	if (dir == EUCOUT) {	/* copying out to user */
51290Sstevel@tonic-gate 		if (to->eucw[2])
51300Sstevel@tonic-gate 			--to->eucw[2];
51310Sstevel@tonic-gate 		if (to->eucw[3])
51320Sstevel@tonic-gate 			--to->eucw[3];
51330Sstevel@tonic-gate 	} else {		/* copying in */
51340Sstevel@tonic-gate 		if (to->eucw[2])
51350Sstevel@tonic-gate 			++to->eucw[2];
51360Sstevel@tonic-gate 		if (to->eucw[3])
51370Sstevel@tonic-gate 			++to->eucw[3];
51380Sstevel@tonic-gate 	}
51390Sstevel@tonic-gate }
51400Sstevel@tonic-gate 
51410Sstevel@tonic-gate 
51420Sstevel@tonic-gate /*
51430Sstevel@tonic-gate  * Take the first byte of a multi-byte, or an ASCII char.  Return its
51440Sstevel@tonic-gate  * codeset. If it's NOT the first byte of an EUC, then the return
51450Sstevel@tonic-gate  * value may be garbage, as it's probably not SS2 or SS3, and
51460Sstevel@tonic-gate  * therefore must be in codeset 1.  Another bizarre catch here is the
51470Sstevel@tonic-gate  * fact that we don't do anything about the "C1" control codes.  In
51480Sstevel@tonic-gate  * real life, we should; but nobody's come up with a good way of
51490Sstevel@tonic-gate  * treating them.
51500Sstevel@tonic-gate  */
51510Sstevel@tonic-gate 
51520Sstevel@tonic-gate static int
ldterm_codeset(uchar_t codeset_type,uchar_t c)51530Sstevel@tonic-gate ldterm_codeset(uchar_t codeset_type, uchar_t c)
51540Sstevel@tonic-gate {
51550Sstevel@tonic-gate 
51560Sstevel@tonic-gate 	if (ISASCII(c))
51570Sstevel@tonic-gate 		return (0);
51580Sstevel@tonic-gate 
51590Sstevel@tonic-gate 	if (codeset_type != LDTERM_CS_TYPE_EUC)
51600Sstevel@tonic-gate 		return (1);
51610Sstevel@tonic-gate 
51620Sstevel@tonic-gate 	switch (c) {
51630Sstevel@tonic-gate 	case SS2:
51640Sstevel@tonic-gate 		return (2);
51650Sstevel@tonic-gate 	case SS3:
51660Sstevel@tonic-gate 		return (3);
51670Sstevel@tonic-gate 	default:
51680Sstevel@tonic-gate 		return (1);
51690Sstevel@tonic-gate 	}
51700Sstevel@tonic-gate }
51710Sstevel@tonic-gate 
51720Sstevel@tonic-gate /* The following two functions are additional EUC codeset specific methods. */
51730Sstevel@tonic-gate /*
51740Sstevel@tonic-gate  * ldterm_dispwidth - Take the first byte of an EUC (or ASCII) and
51750Sstevel@tonic-gate  * return the display width.  Since this is intended mostly for
51760Sstevel@tonic-gate  * multi-byte handling, it returns EUC_TWIDTH for tabs so they can be
51770Sstevel@tonic-gate  * differentiated from EUC characters (assumption: EUC require fewer
51780Sstevel@tonic-gate  * than 255 columns).  Also, if it's a backspace and !flag, it
51790Sstevel@tonic-gate  * returns EUC_BSWIDTH.  Newline & CR also depend on flag.  This
51800Sstevel@tonic-gate  * routine SHOULD be cleaner than this, but we have the situation
51810Sstevel@tonic-gate  * where we may or may not be counting control characters as having a
51820Sstevel@tonic-gate  * column width. Therefore, the computation of ASCII is pretty messy.
51830Sstevel@tonic-gate  * The caller will be storing the value, and then switching on it
51840Sstevel@tonic-gate  * when it's used.  We really should define the EUC_TWIDTH and other
51850Sstevel@tonic-gate  * constants in a header so that the routine could be used in other
51860Sstevel@tonic-gate  * modules in the kernel.
51870Sstevel@tonic-gate  */
51880Sstevel@tonic-gate static int
__ldterm_dispwidth_euc(uchar_t c,void * p,int mode)51890Sstevel@tonic-gate __ldterm_dispwidth_euc(uchar_t c, void *p, int mode)
51900Sstevel@tonic-gate {
51910Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
51920Sstevel@tonic-gate 
51930Sstevel@tonic-gate 	if (ISASCII(c)) {
51940Sstevel@tonic-gate 		if (c <= '\037') {
51950Sstevel@tonic-gate 			switch (c) {
51960Sstevel@tonic-gate 			case '\t':
51970Sstevel@tonic-gate 				return (EUC_TWIDTH);
51980Sstevel@tonic-gate 			case '\b':
51990Sstevel@tonic-gate 				return (mode ? 2 : EUC_BSWIDTH);
52000Sstevel@tonic-gate 			case '\n':
52010Sstevel@tonic-gate 				return (EUC_NLWIDTH);
52020Sstevel@tonic-gate 			case '\r':
52030Sstevel@tonic-gate 				return (mode ? 2 : EUC_CRWIDTH);
52040Sstevel@tonic-gate 			default:
52050Sstevel@tonic-gate 				return (mode ? 2 : 0);
52060Sstevel@tonic-gate 			}
52070Sstevel@tonic-gate 		}
52080Sstevel@tonic-gate 		return (1);
52090Sstevel@tonic-gate 	}
52100Sstevel@tonic-gate 	switch (c) {
52110Sstevel@tonic-gate 	case SS2:
52120Sstevel@tonic-gate 		return (tp->eucwioc.scrw[2]);
52130Sstevel@tonic-gate 	case SS3:
52140Sstevel@tonic-gate 		return (tp->eucwioc.scrw[3]);
52150Sstevel@tonic-gate 	default:
52160Sstevel@tonic-gate 		return (tp->eucwioc.scrw[1]);
52170Sstevel@tonic-gate 	}
52180Sstevel@tonic-gate }
52190Sstevel@tonic-gate 
52200Sstevel@tonic-gate /*
52210Sstevel@tonic-gate  * ldterm_memwidth_euc - Take the first byte of an EUC (or an ASCII char)
52220Sstevel@tonic-gate  * and return its memory width.  The routine could have been
52230Sstevel@tonic-gate  * implemented to use only the codeset number, but that would require
52240Sstevel@tonic-gate  * the caller to have that value available.  Perhaps the user doesn't
52250Sstevel@tonic-gate  * want to make the extra call or keep the value of codeset around.
52260Sstevel@tonic-gate  * Therefore, we use the actual character with which they're
52270Sstevel@tonic-gate  * concerned.  This should never be called with anything but the
52280Sstevel@tonic-gate  * first byte of an EUC, otherwise it will return a garbage value.
52290Sstevel@tonic-gate  */
52300Sstevel@tonic-gate static int
__ldterm_memwidth_euc(uchar_t c,void * p)52310Sstevel@tonic-gate __ldterm_memwidth_euc(uchar_t c, void *p)
52320Sstevel@tonic-gate {
52330Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
52340Sstevel@tonic-gate 
52350Sstevel@tonic-gate 	if (ISASCII(c))
52360Sstevel@tonic-gate 		return (1);
52370Sstevel@tonic-gate 	switch (c) {
52380Sstevel@tonic-gate 	case SS2:
52390Sstevel@tonic-gate 		return (tp->eucwioc.eucw[2]);
52400Sstevel@tonic-gate 	case SS3:
52410Sstevel@tonic-gate 		return (tp->eucwioc.eucw[3]);
52420Sstevel@tonic-gate 	default:
52430Sstevel@tonic-gate 		return (tp->eucwioc.eucw[1]);
52440Sstevel@tonic-gate 	}
52450Sstevel@tonic-gate }
52460Sstevel@tonic-gate 
52470Sstevel@tonic-gate 
52480Sstevel@tonic-gate /* The following two functions are PCCS codeset specific methods. */
52490Sstevel@tonic-gate static int
__ldterm_dispwidth_pccs(uchar_t c,void * p,int mode)52500Sstevel@tonic-gate __ldterm_dispwidth_pccs(uchar_t c, void *p, int mode)
52510Sstevel@tonic-gate {
52520Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
52530Sstevel@tonic-gate 	int i;
52540Sstevel@tonic-gate 
52550Sstevel@tonic-gate 	if (ISASCII(c)) {
52560Sstevel@tonic-gate 		if (c <= '\037') {
52570Sstevel@tonic-gate 			switch (c) {
52580Sstevel@tonic-gate 			case '\t':
52590Sstevel@tonic-gate 				return (EUC_TWIDTH);
52600Sstevel@tonic-gate 			case '\b':
52610Sstevel@tonic-gate 				return (mode ? 2 : EUC_BSWIDTH);
52620Sstevel@tonic-gate 			case '\n':
52630Sstevel@tonic-gate 				return (EUC_NLWIDTH);
52640Sstevel@tonic-gate 			case '\r':
52650Sstevel@tonic-gate 				return (mode ? 2 : EUC_CRWIDTH);
52660Sstevel@tonic-gate 			default:
52670Sstevel@tonic-gate 				return (mode ? 2 : 0);
52680Sstevel@tonic-gate 			}
52690Sstevel@tonic-gate 		}
52700Sstevel@tonic-gate 		return (1);
52710Sstevel@tonic-gate 	}
52720Sstevel@tonic-gate 
52730Sstevel@tonic-gate 	for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
52740Sstevel@tonic-gate 		if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
52750Sstevel@tonic-gate 		    c <= tp->t_csdata.eucpc_data[i].msb_end)
52760Sstevel@tonic-gate 			return (tp->t_csdata.eucpc_data[i].screen_width);
52770Sstevel@tonic-gate 	}
52780Sstevel@tonic-gate 
52790Sstevel@tonic-gate 	/*
52800Sstevel@tonic-gate 	 * If this leading byte is not in the range list, either provided
52810Sstevel@tonic-gate 	 * locale data is not sufficient or we encountered an invalid
52820Sstevel@tonic-gate 	 * character. We return 1 in this case as a fallback value.
52830Sstevel@tonic-gate 	 */
52840Sstevel@tonic-gate 	return (1);
52850Sstevel@tonic-gate }
52860Sstevel@tonic-gate 
52870Sstevel@tonic-gate static int
__ldterm_memwidth_pccs(uchar_t c,void * p)52880Sstevel@tonic-gate __ldterm_memwidth_pccs(uchar_t c, void *p)
52890Sstevel@tonic-gate {
52900Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
52910Sstevel@tonic-gate 	int i;
52920Sstevel@tonic-gate 
52930Sstevel@tonic-gate 	for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
52940Sstevel@tonic-gate 		if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
52950Sstevel@tonic-gate 		    c <= tp->t_csdata.eucpc_data[i].msb_end)
52960Sstevel@tonic-gate 			return (tp->t_csdata.eucpc_data[i].byte_length);
52970Sstevel@tonic-gate 	}
52980Sstevel@tonic-gate 
52990Sstevel@tonic-gate 	/*
53000Sstevel@tonic-gate 	 * If this leading byte is not in the range list, either provided
53010Sstevel@tonic-gate 	 * locale data is not sufficient or we encountered an invalid
53020Sstevel@tonic-gate 	 * character. We return 1 in this case as a fallback value.
53030Sstevel@tonic-gate 	 */
53040Sstevel@tonic-gate 	return (1);
53050Sstevel@tonic-gate }
53060Sstevel@tonic-gate 
53070Sstevel@tonic-gate 
53080Sstevel@tonic-gate /* The following two functions are UTF-8 codeset specific methods. */
53090Sstevel@tonic-gate static int
__ldterm_dispwidth_utf8(uchar_t c,void * p,int mode)53100Sstevel@tonic-gate __ldterm_dispwidth_utf8(uchar_t c, void *p, int mode)
53110Sstevel@tonic-gate {
53120Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
53130Sstevel@tonic-gate 
53140Sstevel@tonic-gate 	if (ISASCII(c)) {
53150Sstevel@tonic-gate 		if (c <= '\037') {
53160Sstevel@tonic-gate 			switch (c) {
53170Sstevel@tonic-gate 			case '\t':
53180Sstevel@tonic-gate 				return (EUC_TWIDTH);
53190Sstevel@tonic-gate 			case '\b':
53200Sstevel@tonic-gate 				return (mode ? 2 : EUC_BSWIDTH);
53210Sstevel@tonic-gate 			case '\n':
53220Sstevel@tonic-gate 				return (EUC_NLWIDTH);
53230Sstevel@tonic-gate 			case '\r':
53240Sstevel@tonic-gate 				return (mode ? 2 : EUC_CRWIDTH);
53250Sstevel@tonic-gate 			default:
53260Sstevel@tonic-gate 				return (mode ? 2 : 0);
53270Sstevel@tonic-gate 			}
53280Sstevel@tonic-gate 		}
53290Sstevel@tonic-gate 		return (1);
53300Sstevel@tonic-gate 	}
53310Sstevel@tonic-gate 
53320Sstevel@tonic-gate 	/* This is to silence the lint. */
53330Sstevel@tonic-gate 	if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
53340Sstevel@tonic-gate 		return (1);
53350Sstevel@tonic-gate 
53360Sstevel@tonic-gate 	/*
53370Sstevel@tonic-gate 	 * If it is a valid leading byte of a UTF-8 character, we set
53380Sstevel@tonic-gate 	 * the width as 'UNKNOWN_WIDTH' for now. We need to have all
53390Sstevel@tonic-gate 	 * the bytes to figure out the display width.
53400Sstevel@tonic-gate 	 */
53410Sstevel@tonic-gate 	if (c >= (uchar_t)0xc0 && c <= (uchar_t)0xfd)
53420Sstevel@tonic-gate 		return (UNKNOWN_WIDTH);
53430Sstevel@tonic-gate 
53440Sstevel@tonic-gate 	/*
53450Sstevel@tonic-gate 	 * If it is an invalid leading byte, we just do our best by
53460Sstevel@tonic-gate 	 * giving the display width of 1.
53470Sstevel@tonic-gate 	 */
53480Sstevel@tonic-gate 	return (1);
53490Sstevel@tonic-gate }
53500Sstevel@tonic-gate 
53510Sstevel@tonic-gate 
53520Sstevel@tonic-gate static int
__ldterm_memwidth_utf8(uchar_t c,void * p)53530Sstevel@tonic-gate __ldterm_memwidth_utf8(uchar_t c, void *p)
53540Sstevel@tonic-gate {
53550Sstevel@tonic-gate 	ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5356*7012Sis 	int len;
5357*7012Sis 
5358*7012Sis 	/*
5359*7012Sis 	 * If the codeset type doesn't match, we treat them as
5360*7012Sis 	 * an illegal character and return 1.
5361*7012Sis 	 */
53620Sstevel@tonic-gate 	if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
53630Sstevel@tonic-gate 		return (1);
53640Sstevel@tonic-gate 
5365*7012Sis 	len = u8_number_of_bytes[c];
5366*7012Sis 
5367*7012Sis 	/*
5368*7012Sis 	 * If this is a start of an illegal character, we treat
5369*7012Sis 	 * such as an 1 byte character and screen out.
5370*7012Sis 	 */
5371*7012Sis 	return ((len <= 0) ? 1 : len);
53720Sstevel@tonic-gate }
53730Sstevel@tonic-gate 
53740Sstevel@tonic-gate static uchar_t
ldterm_utf8_width(uchar_t * u8,int length)53750Sstevel@tonic-gate ldterm_utf8_width(uchar_t *u8, int length)
53760Sstevel@tonic-gate {
53770Sstevel@tonic-gate 	int i;
53780Sstevel@tonic-gate 	int j;
53790Sstevel@tonic-gate 	uint_t intcode = 0;
53800Sstevel@tonic-gate 
53810Sstevel@tonic-gate 	if (length == 0)
53820Sstevel@tonic-gate 		return ('\0');
53830Sstevel@tonic-gate 
5384*7012Sis 	j = u8_number_of_bytes[u8[0]] - 1;
53850Sstevel@tonic-gate 
53860Sstevel@tonic-gate 	/*
53870Sstevel@tonic-gate 	 * If the UTF-8 character is out of UTF-16 code range, or,
53880Sstevel@tonic-gate 	 * if it is either an ASCII character or an invalid leading byte for
53890Sstevel@tonic-gate 	 * a UTF-8 character, return 1.
53900Sstevel@tonic-gate 	 */
5391*7012Sis 	if (length > 4 || j <= 0)
53920Sstevel@tonic-gate 		return ('\1');
53930Sstevel@tonic-gate 
5394*7012Sis 	intcode = u8[0] & u8_masks_tbl[j];
53950Sstevel@tonic-gate 	for (i = 1; j > 0; j--, i++) {
53960Sstevel@tonic-gate 		/*
5397*7012Sis 		 * The following additional checking is needed to conform to
5398*7012Sis 		 * the "UTF-8 Corrigendum" introduced at the Unicode 3.1 and
5399*7012Sis 		 * then updated one more time at the Unicode 3.2.
54000Sstevel@tonic-gate 		 */
54010Sstevel@tonic-gate 		if (i == 1) {
5402*7012Sis 			if (u8[i] < u8_valid_min_2nd_byte[u8[0]] ||
5403*7012Sis 			    u8[i] > u8_valid_max_2nd_byte[u8[0]])
54040Sstevel@tonic-gate 				return ('\1');
54050Sstevel@tonic-gate 		} else if (u8[i] < (uchar_t)LDTERM_CS_TYPE_UTF8_MIN_BYTE ||
5406*7012Sis 		    u8[i] > (uchar_t)LDTERM_CS_TYPE_UTF8_MAX_BYTE)
54070Sstevel@tonic-gate 			return ('\1');
54080Sstevel@tonic-gate 
54090Sstevel@tonic-gate 		/*
54100Sstevel@tonic-gate 		 * All subsequent bytes of UTF-8 character has the following
54110Sstevel@tonic-gate 		 * binary encoding:
54120Sstevel@tonic-gate 		 *
54130Sstevel@tonic-gate 		 * 10xx xxxx
54140Sstevel@tonic-gate 		 *
54150Sstevel@tonic-gate 		 * hence left shift six bits to make space and then get
54160Sstevel@tonic-gate 		 * six bits from the new byte.
54170Sstevel@tonic-gate 		 */
54180Sstevel@tonic-gate 		intcode = (intcode << LDTERM_CS_TYPE_UTF8_SHIFT_BITS) |
5419*7012Sis 		    (u8[i] & LDTERM_CS_TYPE_UTF8_BIT_MASK);
54200Sstevel@tonic-gate 	}
54210Sstevel@tonic-gate 
5422*7012Sis 	i = 0;
54230Sstevel@tonic-gate 	if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P00) {
54240Sstevel@tonic-gate 		/* Basic Multilingual Plane. */
54250Sstevel@tonic-gate 		i = intcode / 4;
54260Sstevel@tonic-gate 		j = intcode % 4;
54270Sstevel@tonic-gate 		switch (j) {
54280Sstevel@tonic-gate 		case 0:
5429*7012Sis 			i = ldterm_ucode[0][i].u0;
5430*7012Sis 			break;
54310Sstevel@tonic-gate 		case 1:
5432*7012Sis 			i = ldterm_ucode[0][i].u1;
5433*7012Sis 			break;
54340Sstevel@tonic-gate 		case 2:
5435*7012Sis 			i = ldterm_ucode[0][i].u2;
5436*7012Sis 			break;
54370Sstevel@tonic-gate 		case 3:
5438*7012Sis 			i = ldterm_ucode[0][i].u3;
5439*7012Sis 			break;
54400Sstevel@tonic-gate 		}
54410Sstevel@tonic-gate 	} else if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P01) {
54420Sstevel@tonic-gate 		/* Secondary Multilingual Plane. */
54430Sstevel@tonic-gate 		intcode = intcode & (uint_t)0xffff;
54440Sstevel@tonic-gate 		i = intcode / 4;
54450Sstevel@tonic-gate 		j = intcode % 4;
54460Sstevel@tonic-gate 		switch (j) {
54470Sstevel@tonic-gate 		case 0:
5448*7012Sis 			i = ldterm_ucode[1][i].u0;
5449*7012Sis 			break;
54500Sstevel@tonic-gate 		case 1:
5451*7012Sis 			i = ldterm_ucode[1][i].u1;
5452*7012Sis 			break;
54530Sstevel@tonic-gate 		case 2:
5454*7012Sis 			i = ldterm_ucode[1][i].u2;
5455*7012Sis 			break;
54560Sstevel@tonic-gate 		case 3:
5457*7012Sis 			i = ldterm_ucode[1][i].u3;
5458*7012Sis 			break;
54590Sstevel@tonic-gate 		}
54600Sstevel@tonic-gate 	} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKEXTB &&
5461*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKEXTB) ||
5462*7012Sis 	    (intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKCOMP &&
5463*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKCOMP) ||
5464*7012Sis 	    (intcode >= LDTERM_CS_TYPE_UTF8_MIN_P15 &&
5465*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_P15) ||
5466*7012Sis 	    (intcode >= LDTERM_CS_TYPE_UTF8_MIN_P16 &&
5467*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_P16)) {
54680Sstevel@tonic-gate 		/*
54690Sstevel@tonic-gate 		 * Supplementary Plane for CJK Ideographs and
54700Sstevel@tonic-gate 		 * Private Use Planes.
54710Sstevel@tonic-gate 		 */
54720Sstevel@tonic-gate 		return ('\2');
54730Sstevel@tonic-gate 	} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_P14 &&
5474*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_P14) ||
5475*7012Sis 	    (intcode >= LDTERM_CS_TYPE_UTF8_MIN_VARSEL &&
5476*7012Sis 	    intcode <= LDTERM_CS_TYPE_UTF8_MAX_VARSEL)) {
5477*7012Sis 		/*
5478*7012Sis 		 * Some Special Purpose Plane characters:
5479*7012Sis 		 * These are like control characters and not printable.
5480*7012Sis 		 */
54810Sstevel@tonic-gate 		return ('\0');
54820Sstevel@tonic-gate 	}
54830Sstevel@tonic-gate 
5484*7012Sis 	/*
5485*7012Sis 	 * We return the display width of 1 for all character code points
5486*7012Sis 	 * that we didn't catch from the above logic and also for combining
5487*7012Sis 	 * and conjoining characters with width value of zero.
5488*7012Sis 	 *
5489*7012Sis 	 * In particular, the reason why we are returning 1 for combining
5490*7012Sis 	 * and conjoining characters is because the GUI-based terminal
5491*7012Sis 	 * emulators are not yet capable of properly handling such characters
5492*7012Sis 	 * and in most of the cases, they just treat such characters as if
5493*7012Sis 	 * they occupy a display cell. If the terminal emulators are capable of
5494*7012Sis 	 * handling the characters correctly, then, this logic of returning
5495*7012Sis 	 * 1 should be revisited and changed. See CR 6660526 for more
5496*7012Sis 	 * details on this.
5497*7012Sis 	 */
5498*7012Sis 	return ((i == 0) ? '\1' : (uchar_t)i);
54990Sstevel@tonic-gate }
5500