ldterm.c revision 85bb5f1d642e430b889478fb1200b511338085d7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Standard Streams Terminal Line Discipline module.
*/
#include <sys/eucioctl.h>
#include <sys/csiioctl.h>
/* Time limit when draining during a close(9E) invoked by exit(2) */
/* Can be set to zero to emulate the old, broken behavior */
int ldterm_drain_limit = 15000000;
/*
* Character types.
*/
#define ORDINARY 0
#define CONTROL 1
#define BACKSPACE 2
#define NEWLINE 3
#define TAB 4
#define VTAB 5
#define RETURN 6
/*
* The following for EUC handling:
*/
#define T_SS2 7
#define T_SS3 8
/*
* Table indicating character classes to tty driver. In particular,
* if the class is ORDINARY, then the character needs no special
* processing on output.
*
* Characters in the C1 set are all considered CONTROL; this will
* but might cause distress with terminals that put graphics in
* the range 0200-0237. On the other hand, characters in that
* range cause even greater distress to other UNIX terminal drivers....
*/
static char typetab[256] = {
/*
* WARNING: For EUC, 0xFF must be an ordinary character. It is used with
* single-byte EUC in some of the "ISO Latin Alphabet" codesets, and occupies
* a screen position; in those ISO sets where that position isn't used, it
* shouldn't make any difference.
*/
};
/*
* Translation table for output without OLCUC. All ORDINARY-class characters
* translate to themselves. All other characters have a zero in the table,
* which stops the copying.
*/
static unsigned char notrantab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
/* 140 */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
/* 150 */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
/* 160 */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
/* 170 */ 'x', 'y', 'z', '{', '|', '}', '~', 0,
/* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 210 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 220 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 230 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 240 */ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
/* 250 */ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
/* 260 */ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
/* 270 */ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
/* 300 */ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
/* 310 */ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
/* 320 */ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
/* 330 */ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
/* 340 */ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
/* 350 */ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
/* 360 */ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
/*
* WARNING: as for above ISO sets, \377 may be used. Translate it to
* itself.
*/
/* 370 */ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
};
/*
* Translation table for output with OLCUC. All ORDINARY-class characters
* translate to themselves, except for lower-case letters which translate
* to their upper-case equivalents. All other characters have a zero in
* the table, which stops the copying.
*/
static unsigned char lcuctab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
/* 140 */ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 150 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 160 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 170 */ 'X', 'Y', 'Z', '{', '|', '}', '~', 0,
/* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 210 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 220 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 230 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 240 */ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
/* 250 */ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
/* 260 */ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
/* 270 */ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
/* 300 */ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
/* 310 */ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
/* 320 */ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
/* 330 */ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
/* 340 */ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
/* 350 */ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
/* 360 */ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
/*
* WARNING: as for above ISO sets, \377 may be used. Translate it to
* itself.
*/
/* 370 */ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
};
/*
* Input mapping table -- if an entry is non-zero, and XCASE is set,
* when the corresponding character is typed preceded by "\" the escape
* sequence is replaced by the table value. Mostly used for
* upper-case only terminals.
*/
static char imaptab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ 0, '|', 0, 0, 0, 0, 0, '`',
/* 050 */ '{', '}', 0, 0, 0, 0, 0, 0,
/* 060 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 070 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 100 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 110 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 120 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 130 */ 0, 0, 0, 0, '\\', 0, '~', 0,
/* 140 */ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 150 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 160 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 170 */ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
/* 200-377 aren't mapped */
};
/*
* Output mapping table -- if an entry is non-zero, and XCASE is set,
* the corresponding character is printed as "\" followed by the table
* value. Mostly used for upper-case only terminals.
*/
static char omaptab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 050 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 060 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 070 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 100 */ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 130 */ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
/* 140 */ '\'', 0, 0, 0, 0, 0, 0, 0,
/* 150 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 160 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 170 */ 0, 0, 0, '(', '!', ')', '^', 0,
/* 200-377 aren't mapped */
};
/*
* Translation table for TS_MEUC output without OLCUC. All printing ASCII
* characters translate to themselves. All other _bytes_ have a zero in
* the table, which stops the copying. This and the following table exist
* only so we can use the existing movtuc processing with or without OLCUC.
* Maybe it speeds up something...because we can copy a block of characters
* by only looking for zeros in the table.
*
* If we took the simple expedient of DISALLOWING "olcuc" with multi-byte
* processing, we could rid ourselves of both these tables and save 512 bytes;
* seriously, it doesn't make much sense to use olcuc with multi-byte, and
* it will probably never be used. Consideration should be given to disallowing
* the combination TS_MEUC & OLCUC.
*/
static unsigned char enotrantab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
/* 140 */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
/* 150 */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
/* 160 */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
/* 170 */ 'x', 'y', 'z', '{', '|', '}', '~', 0,
/* 200 - 377 aren't mapped (they're stoppers). */
};
/*
* Translation table for TS_MEUC output with OLCUC. All printing ASCII
* translate to themselves, except for lower-case letters which translate
* to their upper-case equivalents. All other bytes have a zero in
* the table, which stops the copying. Useless for ISO Latin Alphabet
* translations, but *sigh* OLCUC is really only defined for ASCII anyway.
* We only have this table so we can use the existing OLCUC processing with
* TS_MEUC set (multi-byte mode). Nobody would ever think of actually
* _using_ it...would they?
*/
static unsigned char elcuctab[256] = {
/* 000 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 010 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 020 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 030 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 040 */ ' ', '!', '"', '#', '$', '%', '&', '\'',
/* 050 */ '(', ')', '*', '+', ',', '-', '.', '/',
/* 060 */ '0', '1', '2', '3', '4', '5', '6', '7',
/* 070 */ '8', '9', ':', ';', '<', '=', '>', '?',
/* 100 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 110 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 120 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 130 */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
/* 140 */ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 150 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 160 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 170 */ 'X', 'Y', 'Z', '{', '|', '}', '~', 0,
/* 200 - 377 aren't mapped (they're stoppers). */
};
"ldterm",
&ldtrinfo,
};
static struct modlstrmod modlstrmod = {
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static void ldtermrsrv(queue_t *);
static void ldtermwsrv(queue_t *);
ldtermstd_state_t *, int *);
static int ldterm_unget(ldtermstd_state_t *);
static void ldterm_trim(ldtermstd_state_t *);
static int ldterm_tabcols(ldtermstd_state_t *);
static void ldterm_wenable(void *);
ldtermstd_state_t *, size_t, int);
static void ldterm_flush_output(unsigned char, queue_t *,
static void ldterm_dosig(queue_t *, int, unsigned char, int, int);
static void vmin_settimer(queue_t *);
static void vmin_timed_out(void *);
static void ldterm_adjust_modes(ldtermstd_state_t *);
static void ldterm_eucwarn(ldtermstd_state_t *);
/* Codeset type specific methods for EUC, PCCS, and, UTF-8 codeset types. */
static int __ldterm_dispwidth_euc(uchar_t, void *, int);
static int __ldterm_memwidth_euc(uchar_t, void *);
static int __ldterm_dispwidth_pccs(uchar_t, void *, int);
static int __ldterm_memwidth_pccs(uchar_t, void *);
static int __ldterm_dispwidth_utf8(uchar_t, void *, int);
static int __ldterm_memwidth_utf8(uchar_t, void *);
{
NULL,
},
{
},
{
},
{
}
};
/*
* The default codeset is presumably C locale's ISO 646 in EUC but
* the data structure at below defined as the default codeset data also
* support any single byte (EUC) locales.
*/
static const ldterm_cs_data_t default_cs_data = {
(uchar_t)0,
(uchar_t)0,
(char *)NULL,
{
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0'
}
};
/*
* The following tables are from either u8_textprep.c or uconv.c at
* UTF-8 character byte lengths and also the validity of given character bytes.
*/
extern const int8_t u8_number_of_bytes[];
extern const uchar_t u8_masks_tbl[];
extern const uint8_t u8_valid_min_2nd_byte[];
extern const uint8_t u8_valid_max_2nd_byte[];
/*
* Unicode character width definition tables from uwidth.c:
*/
#ifdef LDDEBUG
int ldterm_debug = 0;
#else
#define DEBUG1(a)
#define DEBUG2(a)
#define DEBUG3(a)
#define DEBUG4(a)
#define DEBUG5(a)
#define DEBUG6(a)
#define DEBUG7(a)
#endif /* LDDEBUG */
/*
* Since most of the buffering occurs either at the stream head or in
* the "message currently being assembled" buffer, we have a
* relatively small input queue, so that blockages above us get
* reflected fairly quickly to the module below us. We also have a
* small maximum packet size, since you can put a message of that
* size on an empty queue no matter how much bigger than the high
* water mark it is.
*/
static struct module_info ldtermmiinfo = {
0x0bad,
"ldterm",
0,
256,
};
static struct qinit ldtermrinit = {
(int (*)())ldtermrput,
(int (*)())ldtermrsrv,
NULL,
};
static struct module_info ldtermmoinfo = {
0x0bad,
"ldterm",
0,
1,
0
};
static struct qinit ldtermwinit = {
(int (*)())ldtermwput,
(int (*)())ldtermwsrv,
NULL,
};
NULL,
};
/*
* Dummy qbufcall callback routine used by open and close.
* The framework will wake up qwait_sig when we return from
* this routine (as part of leaving the perimeters.)
* (The framework enters the perimeters before calling the qbufcall() callback
* and leaves the perimeters after the callback routine has executed. The
* when it leaves the perimeter. See qwait(9E).)
*/
/* ARGSUSED */
static void
dummy_callback(void *arg)
{}
static mblk_t *
{
int retv;
qunbufcall(q, id);
if (retv == 0)
break;
}
return (mp);
}
static mblk_t *
{
int retv;
qunbufcall(q, id);
if (retv == 0)
break;
}
return (mp);
}
/*
* Line discipline open.
*/
/* ARGSUSED1 */
static int
{
int len;
struct stroptions *strop;
return (0); /* already attached */
}
KM_SLEEP);
/*
* Get termios defaults. These are stored as
* a property in the "options" node.
*/
} else {
/*
* Gack! Whine about it.
*/
}
tp->t_rd_request = 0;
/*
* The following for EUC and also non-EUC codesets:
*/
qprocson(q);
/*
* Find out if the module below us does canonicalization; if
* so, we won't do it ourselves.
*/
goto open_abort;
/*
* Reformulate as an M_CTL message. The actual data will
* be in the b_cont field.
*/
/* allocate a TCSBRK ioctl in case we'll need it on close */
goto open_abort;
goto open_abort;
/*
* Find out if the underlying driver supports proper POSIX close
* semantics. If not, we'll have to approximate it using TCSBRK. If
* it does, it will respond with MC_HAS_POSIX, and we'll catch that in
* the ldtermrput routine.
*
* When the ldterm_drain_limit tunable is set to zero, we behave the
* same as old ldterm: don't send this new message, and always use
* TCSBRK during close.
*/
if (ldterm_drain_limit != 0) {
goto open_abort;
}
/* prepare to clear the water marks on close */
goto open_abort;
/*
* Set the high-water and low-water marks on the stream head
* to values appropriate for a terminal. Also set the "vmin"
* and "vtime" values to 1 and 0, turn on message-nondiscard
* mode (as we're in ICANON mode), and turn on "old-style
* NODELAY" mode.
*/
goto open_abort;
return (0); /* this can become a controlling TTY */
qprocsoff(q);
/* Dump the state structure */
return (EINTR);
}
struct close_timer {
};
static void
drain_timed_out(void *arg)
{
}
/* ARGSUSED2 */
static int
{
struct stroptions *strop;
struct close_timer cltimer;
/*
* If we have an outstanding vmin timeout, cancel it.
*/
/*
* Cancel outstanding qbufcall request.
*/
/*
* Reset the high-water and low-water marks on the stream
* head (?), turn on byte-stream mode, and turn off
* "old-style NODELAY" mode.
*/
/*
* If the driver isn't known to have POSIX close semantics,
* then we have to emulate this the old way. This is done by
* sending down TCSBRK,1 to drain the output and waiting for
* the reply.
*/
/*
* If we're not able to receive signals at this point, then
* launch a timer. This timer will prevent us from waiting
* forever for a signal that won't arrive.
*/
if (!ddi_can_receive_sig() && ldterm_drain_limit != 0) {
}
/*
* Note that the read side of ldterm and the qtimeout are
* protected by D_MTQPAIR, so no additional locking is needed
* here.
*/
if (qwait_sig(q) == 0)
break;
}
}
/*
* From here to the end, the routine does not sleep and does not
* reference STREAMS, so it's guaranteed to run to completion.
*/
qprocsoff(q);
/* Dump the state structure, then unlink it */
return (0);
}
/*
* Put procedure for input from driver end of stream (read queue).
*/
static void
{
unsigned char c;
unsigned char *readp;
unsigned char *writep;
int dbtype;
/*
* We received our ack from the driver saying there is nothing left to
* shovel out, so wake up the close routine.
*/
return;
}
}
switch (dbtype) {
default:
return;
/*
* Send these up unmolested
*
*/
case M_PCSIG:
case M_SIG:
case M_IOCNAK:
return;
case M_IOCACK:
ldterm_ioctl_reply(q, mp);
return;
case M_BREAK:
/*
* Parity errors are sent up as M_BREAKS with single
* character data (formerly handled in the driver)
*/
/*
* IGNPAR PARMRK RESULT
* off off 0
* off on 3 byte sequence
* on either ignored
*/
unsigned char c;
"ldtermrput: no blocks");
return;
}
} else {
}
} else {
}
return;
}
/*
* We look at the apparent modes here instead of the
* effective modes. Effective modes cannot be used if
* IGNBRK, BRINT and PARMRK have been negotiated to
* be handled by the driver. Since M_BREAK should be
* sent upstream only if break processing was not
* already done, it should be ok to use the apparent
* modes.
*/
/*
* Send '\377','\0', '\0'.
*/
"ldtermrput: no blocks");
return;
}
} else {
/*
* Act as if a '\0' came in.
*/
"ldtermrput: no blocks");
return;
}
}
} else {
}
return;
case M_CTL:
DEBUG3(("ldtermrput: M_CTL received\n"));
/*
* The M_CTL has been standardized to look like an
* M_IOCTL message.
*/
DEBUG3((
"Non standard M_CTL received by ldterm module\n"));
/* May be for someone else; pass it on */
return;
}
case MC_PART_CANON:
DEBUG3(("ldtermrput: M_CTL Query Reply\n"));
DEBUG3(("No information in Query Message\n"));
break;
}
sizeof (struct termios)) {
DEBUG3(("ldtermrput: M_CTL GrandScheme\n"));
/* elaborate turning off scheme */
sizeof (struct termios));
break;
} else {
DEBUG3(("Incorrect query replysize\n"));
break;
}
case MC_NO_CANON:
/*
* Note: this is very nasty. It's not clear
* what the right thing to do with a partial
* message is; We throw it out
*/
}
}
break;
case MC_DO_CANON:
break;
case MC_HAS_POSIX:
/* no longer any reason to drain from ldterm */
if (ldterm_drain_limit != 0) {
}
break;
default:
DEBUG3(("Unknown M_CTL Message\n"));
break;
}
return;
case M_FLUSH:
/*
* Flush everything we haven't looked at yet.
*/
else
/*
* Flush everything we have looked at.
*/
}
/*
* Relieve input flow control
*/
DEBUG1(("M_STARTI down\n"));
}
return;
case M_DATA:
break;
}
/*
* Flow control: send "start input" message if blocked and
* our queue is below its low water mark.
*/
DEBUG1(("M_STARTI down\n"));
}
/*
* If somebody below us ("intelligent" communications
* board, pseudo-tty controlled by an editor) is doing
* canonicalization, don't scan it for special characters.
*/
return;
}
do {
/*
* We're doing some sort of non-trivial
* processing of input; look at every
* character.
*/
c = *readp++;
c &= 0177;
/*
* First, check that this hasn't been
* escaped with the "literal next"
* character.
*/
*writep++ = c;
continue;
}
/*
* Setting a special character to NUL
* disables it, so if this character
* is NUL, it should not be compared
* with any of the special characters.
* It should, however, restart frozen
* output if IXON and IXANY are set.
*/
if (c == _POSIX_VDISABLE) {
~(TS_TTSTOP|TS_OFBLOCK);
}
*writep++ = c;
continue;
}
/*
* If stopped, start if you can; if
* running, stop if you must.
*/
if (c ==
IEXTEN &&
IXANY)) {
~(TS_TTSTOP |
(void) putnextctl(wrq,
M_START);
}
} else {
if (c ==
(void) putnextctl(wrq,
M_STOP);
}
}
continue;
}
/*
* Check for "literal next" character
* and "flush output" character.
* Note that we omit checks for ISIG
* and ICANON, since the IEXTEN
* setting subsumes them.
*/
/*
* Remember that we saw a
* "literal next" while
* scanning input, but leave
* leave it in the message so
* that the service routine
* can see it too.
*/
*writep++ = c;
continue;
}
continue;
}
}
/*
* Check for signal-generating
* characters.
*/
ldterm_dosig(q, SIGINT, c,
continue;
}
ldterm_dosig(q, SIGQUIT, c,
continue;
}
/*
* Ancient SXT support; discard
* character without action.
*/
continue;
}
ldterm_dosig(q, SIGTSTP, c,
continue;
}
ldterm_dosig(q, SIGTSTP, c,
M_SIG, 0);
continue;
}
}
/*
* Throw away CR if IGNCR set, or
* turn it into NL if ICRNL set.
*/
if (c == '\r') {
continue;
c = '\n';
} else {
/*
* Turn NL into CR if INLCR
* set.
*/
if (c == '\n' &&
c = '\r';
}
/*
* Map upper case input to lower case
* if IUCLC flag set.
*/
c >= 'A' && c <= 'Z')
c += 'a' - 'A';
/*
* Put the possibly-transformed
* character back in the message.
*/
*writep++ = c;
}
/*
* If we didn't copy some characters because
* we were ignoring them, fix the size of the
* data block by adjusting the write pointer.
* XXX This may result in a zero-length
* block; will this cause anybody gastric
* distress?
*/
} else {
/*
* We won't be doing anything other than
* possibly stripping the input.
*/
}
}
/*
* Queue the message for service procedure if the
* queue is not empty or canputnext() fails or
* tp->t_state & TS_RESCAN is true.
*/
else
(void) ldtermrmsg(q, mp);
/*
* Flow control: send "stop input" message if our queue is
* approaching its high-water mark. The message will be
* dropped on the floor in the service procedure, if we
* cannot ship it up and we have had it upto our neck!
*
* Set QWANTW to ensure that the read queue service procedure
* gets run when nextq empties up again, so that it can
* unstop the input.
*/
DEBUG1(("M_STOPI down\n"));
}
}
/*
* mapping.
*/
static void
ldtermrsrv(queue_t *q)
{
/*
* Canonicalization was turned on or off. Put the
* message being assembled back in the input queue,
* so that we rescan it.
*/
DEBUG5(("RESCAN WAS SET; put back in q\n"));
else
}
}
}
if (!ldtermrmsg(q, mp))
break;
}
/*
* Flow control: send start message if blocked and our queue
* is below its low water mark.
*/
}
}
/*
* This routine is called from both ldtermrput and ldtermrsrv to
* do the actual work of dealing with mp. Return 1 on sucesss and
* 0 on failure.
*/
static int
{
unsigned char c;
int dofree;
int status = 1;
/*
* Stream head is flow controlled. If echo is
* turned on, flush the read side or send a
* bell down the line to stop input and
* process the current message.
* Otherwise(putbq) the user will not see any
* response to to the typed input. Typically
* happens if there is no reader process.
* Note that you will loose the data in this
* case if the data is coming too fast. There
* is an assumption here that if ECHO is
* turned on its some user typing the data on
* a terminal and its not network.
*/
if (canputnext(WR(q)))
status = 0;
goto echo;
} else {
}
} else {
status = 0;
goto out; /* read side is blocked */
}
}
default:
goto out;
case M_HANGUP:
/*
* Flush everything we haven't looked at yet.
*/
/*
* Flush everything we have looked at.
*/
/*
* XXX should we set read request
* tp->t_rd_request to NULL?
*/
}
/*
* Restart output, since it's probably got
* nowhere to go anyway, and we're probably
* not going to see another ^Q for a while.
*/
}
/*
* This message will travel up the read
* queue, flushing as it goes, get turned
* around at the stream head, and travel back
* down the write queue, flushing as it goes.
*/
/*
* This message will travel down the write
* queue, flushing as it goes, get turned
* around at the driver, and travel back up
* the read queue, flushing as it goes.
*/
/*
* Now that that's done, we send a SIGCONT
* upstream, followed by the M_HANGUP.
*/
/* (void) putnextctl1(q, M_PCSIG, SIGCONT); */
goto out;
case M_IOCACK:
/*
* Augment whatever information the driver is
* returning with the information we supply.
*/
ldterm_ioctl_reply(q, mp);
goto out;
case M_DATA:
break;
}
/*
* This is an M_DATA message.
*/
/*
* If somebody below us ("intelligent" communications
* board, pseudo-tty controlled by an editor) is
* doing canonicalization, don't scan it for special
* characters.
*/
goto out;
}
do {
if (CANON_MODE) {
/*
* By default, free the message once processed
*/
dofree = 1;
/*
* update sysinfo canch
* character. The value of
* canch may vary as compared
* to character tty
* implementation.
*/
if ((bpt = ldterm_docanon(c,
NULL)
break;
}
/*
* Release this block or put back on queue.
*/
if (dofree)
else {
break;
}
} else
"ldtermrsrv: out of blocks");
break;
}
}
echo:
/*
* Send whatever we echoed downstream.
*/
if (canputnext(WR(q)))
else
}
out:
return (status);
}
/*
* Do canonical mode input; check whether this character is to be
* treated as a special character - if so, check whether it's equal
* to any of the special characters and handle it accordingly.
* Otherwise, just add it to the current line.
*/
static mblk_t *
{
int i;
/*
* If the previous character was the "literal next"
* character, treat this character as regular input.
*/
goto escaped;
/*
* Setting a special character to NUL disables it, so if this
* character is NUL, it should not be compared with any of
* the special characters.
*/
if (c == _POSIX_VDISABLE) {
goto escaped;
}
/*
* If this character is the literal next character, echo it
* as '^', backspace over it, and record that fact.
*/
goto out;
}
/*
* Check for the editing character. If the display width of
* the last byte at the canonical buffer is not one and also
* smaller than or equal to UNKNOWN_WIDTH, the character at
* character.
*/
/*
* Get rid of the backslash, and put the
* erase character in its place.
*/
goto escaped;
} else {
else
goto out;
}
}
/*
*/
else
goto out;
}
/*
* Get rid of the backslash, and put the kill
* character in its place.
*/
goto escaped;
} else {
goto out;
}
}
goto out;
}
/*
* If the preceding character was a backslash: if the current
* character is an EOF, get rid of the backslash and treat
* the EOF as data; if we're in XCASE mode and the current
* character is part of a backslash-X escape sequence,
* process it; otherwise, just treat the current character
* normally.
*/
/*
* EOF character. Since it's escaped, get rid
* of the backslash and put the EOF character
* in its place.
*/
} else {
/*
* If we're in XCASE mode, and the current
* character is part of a backslash-X
* sequence, get rid of the backslash and
* replace the current character with what
* that sequence maps to.
*/
imaptab[c] != '\0') {
c = imaptab[c];
}
}
} else {
/*
* Previous character wasn't backslash; check whether
* this was the EOF character.
*/
/*
* EOF character. Don't echo it unless
* ECHOCTL is set, don't stuff it in the
* current line, but send the line up the
* stream.
*/
while (i > 0) {
i--;
}
}
ldterm_msg_upstream(q, tp);
if (!canputnext(q)) {
*dofreep = 0;
} else {
*dofreep = 1;
}
goto out;
}
}
/*
* First, make sure we can fit one WHOLE multi-byte char in the
* buffer. This is one place where we have overhead even if
* not in multi-byte mode; the overhead is subtracting
* tp->t_maxeuc from MAX_CANON before checking.
*
* Allows MAX_CANON bytes in the buffer before throwing awaying
* the the overflow of characters.
*/
/*
* Byte will cause line to overflow, or the next EUC
* won't fit: Ring the bell or discard all input, and
* don't save the byte away.
*/
if (canputnext(wrq))
goto out;
} else {
/*
* MAX_CANON processing. free everything in
* the current line and start with the
* current character as the first character.
*/
DEBUG7(("ldterm_docanon: MAX_CANON processing\n"));
}
}
}
/*
* Add the character to the current line.
*/
/*
* No more room in this mblk; save this one away, and
* allocate a new one.
*/
goto out;
/*
* Chain the new one to the end of the old one, and
* mark it as the last block in the current line.
*/
}
/*
* In multi-byte mode, we have to keep track of where we are.
* The first bytes of multi-byte chars get the full count for the
* whole character. We don't do any column calculations
* here, but we need the information for when we do. We could
* come across cases where we are getting garbage on the
* line, but we're in multi-byte mode. In that case, we may
* see ASCII controls come in the middle of what should have been a
* multi-byte character. Call ldterm_eucwarn...eventually, a
* warning message will be printed about it.
*/
if (c < (uchar_t)0x20)
} else { /* is the first byte of a multi-byte, or is ASCII */
if (ISASCII(c)) {
} else {
(void *)tp) - 1;
}
}
}
/*
* AT&T is concerned about the following but we aren't since
* we have already shipped code that works.
*
* POSIX conformant. This is going to cause problems for
* pre-SVR4.0 programs that don't know about IEXTEN. Hence
*/
/*
* || ((tp->t_modes.c_lflag & IEXTEN) && c ==
* tp->t_modes.c_cc[VEOL2]))))) {
*/
/*
* It's a line-termination character; send the line
* up the stream.
*/
ldterm_msg_upstream(q, tp);
}
goto out;
} else {
/*
* Character was escaped with LNEXT.
*/
/*
* If the current character is a single byte and single
* column character and it is the backslash character and
* IEXTEN, then the state will have TS_QUOT.
*/
}
/*
* Echo it.
*/
}
else {
/*
* Echo NL when ECHO turned off, if ECHONL flag is
* set.
*/
}
out:
return (bpt);
}
static int
{
return (-1); /* no buffers */
return (-1); /* zero-length record */
}
static void
{
/*
* This mblk is now empty. Find the previous mblk;
* throw this one away, unless it's the first one.
*/
}
}
}
}
/*
* Rubout one character from the current line being built for tp as
* cleanly as possible. q is the write queue for tp. Most of this
* can't be applied to multi-byte processing. We do our own thing
* for that... See the "ldterm_eucerase" routine. We never call
* ldterm_rubout on a multi-byte or multi-column character.
*/
static void
{
int tabcols;
static unsigned char crtrubout[] = "\b \b\b \b";
return;
/*
* "CRT rubout"; try erasing it from the screen.
*/
/*
* After the character being erased was
* echoed, some data was written to the
* terminal; we can't erase it cleanly, so we
* just reprint the whole line as if the user
* had typed the reprint character.
*/
return;
} else {
/*
* XXX what about escaped characters?
*/
switch (typetab[c]) {
case ORDINARY:
omaptab[c])
tp);
break;
case VTAB:
case BACKSPACE:
case CONTROL:
case RETURN:
case NEWLINE:
tp);
break;
case TAB:
/*
* While the tab being erased was
* expanded, some data was written
* to the terminal; we can't erase
* it cleanly, so we just reprint
* the whole line as if the user
* had typed the reprint character.
*/
return;
}
while (--tabcols >= 0)
break;
}
}
/*
* "Printing rubout"; echo it between \ and /.
*/
}
} else
}
/*
* Find the number of characters the tab we just deleted took up by
* zipping through the current line and recomputing the column
* number.
*/
static int
{
int col;
int i;
unsigned char c;
char errflg;
/*
* If we're doing multi-byte stuff, zip through the list of
* widths to figure out where we are (we've kept track in most
* cases).
*/
errflg = (char)0;
switch (*readp) {
case EUC_TWIDTH: /* it's a tab */
col++;
break;
case EUC_BSWIDTH: /* backspace */
if (col)
col--;
break;
case EUC_NLWIDTH: /* newline */
col = 0;
break;
case EUC_CRWIDTH: /* return */
col = 0;
break;
case UNKNOWN_WIDTH: /* UTF-8 unknown width */
LDTERM_CS_TYPE_UTF8 || errflg) {
*readp = 1;
col++;
break;
}
/*
* Collect the current UTF-8 character bytes
* from (possibly multiple) data buffers so
* that we can figure out the display width.
*/
for (i = 1; (i < LDTERM_CS_MAX_BYTE_LENGTH) &&
(*(readp + i) == 0); i++) {
startp++;
startp =
} else {
*readp = 1;
col++;
break;
}
}
}
/* tp->t_eucp_mp contains wrong info?? */
if (*readp == 1)
break;
readp += (i - 1);
break;
default:
break;
}
++readp;
++startp;
} else {
/*
* This will happen only if
* tp->t_eucp_mp contains wrong
* display width info.
*/
errflg = (char)1;
startp--;
}
}
}
goto eucout; /* finished! */
}
do {
c = *readp++;
if (c <= 037 && c != '\t' && c != '\n' ||
c == 0177) {
col += 2;
continue;
}
}
/*
* Column position calculated here.
*/
switch (typetab[c]) {
/*
* Ordinary characters; advance by
* one.
*/
case ORDINARY:
col++;
break;
/*
* Non-printing characters; nothing
* happens.
*/
case CONTROL:
break;
/* Backspace */
case BACKSPACE:
if (col != 0)
col--;
break;
/* Newline; column depends on flags. */
case NEWLINE:
col = 0;
break;
/* tab */
case TAB:
col |= 07;
col++;
break;
/* vertical motion */
case VTAB:
break;
/* carriage return */
case RETURN:
col = 0;
break;
}
}
/*
* "col" is now the column number before the tab. "tp->t_col"
* is still the column number after the tab, since we haven't
* erased the tab yet. Thus "tp->t_col - col" is the number
* of positions the tab moved.
*/
if (col > 8)
return (col);
}
/*
* Erase a single character; We ONLY ONLY deal with ASCII or
* single-column single-byte codeset character. For multi-byte characters,
* see "ldterm_csi_erase".
*/
static void
{
int c;
}
}
/*
* Erase an entire word, single-byte EUC only please.
*/
static void
{
int c;
/*
* Erase trailing white space, if any.
*/
}
/*
* Erase non-white-space characters, if any.
*/
while (c != -1 && c != ' ' && c != '\t') {
c = ldterm_unget(tp);
}
if (c != -1) {
/*
* We removed one too many characters; put the last
* one back.
*/
}
}
/*
* ldterm_csi_werase - This is multi-byte equivalent of "word erase".
* "Word erase" only makes sense in languages which space between words,
* and it's presumptuous for us to attempt "word erase" when we don't
* know anything about what's really going on. It makes no sense for
* many languages, as the criteria for defining words and tokens may
* be completely different.
*
* In the TS_MEUC case (which is how we got here), we define a token to
* be space- or tab-delimited, and erase one of them. It helps to
* have this for command lines, but it's otherwise useless for text
* editing applications; you need more sophistication than we can
* provide here.
*/
static void
{
int c, i;
int len;
/*
* ip points to the width of the actual bytes. t_eucp points
* one byte beyond, where the next thing will be inserted.
*/
/*
* Erase trailing white space, if any.
*/
--ip;
}
/*
* Erase non-white-space characters, if any. The outer loop
* bops through each byte in the buffer. Multi-byte is removed, as
* is ASCII, one byte at a time. The inner loop (for) is only
* executed for first bytes of multi-byte. The inner loop erases
* the number of columns required for the multi-byte char. We check
* for ASCII first, and ldterm_rubout knows about ASCII.
*/
len = 0;
while (c != -1 && c != ' ' && c != '\t') {
if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
}
/*
* Unlike EUC, except the leading byte, some bytes of
* a non-EUC multi-byte characters are in the ASCII code
* range, esp., 0x41 ~ 0x7a. Thus, we cannot simply check
* ISASCII().
* Checking the (*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH)
* will ensure that it is a single byte character (even though
* it is on display width not byte length) and can be further
* checked whether it is an ASCII character or not.
*
* When ECHOCTL is on and 'c' is an ASCII control character,
* *ip == 2 happens.
*/
ISASCII(c)) {
len = 0;
} else if (*ip) {
if (*ip == UNKNOWN_WIDTH) {
for (i = 0; i < len; i++)
} else {
*ip = 1;
}
}
/*
* erase for number of columns required for
* this multi-byte character. Hopefully, matches
* ldterm_dispwidth!
*/
for (i = 0; i < (int)*ip; i++)
len = 0;
}
--ip;
c = ldterm_unget(tp);
}
if (c != -1) {
/*
* We removed one too many characters; put the last
* one back.
*/
}
}
/*
* Kill an entire line, erasing each character one-by-one (if ECHOKE
* is set) or just echoing the kill character, followed by a newline
* (if ECHOK is set). Multi-byte processing is included here.
*/
static void
{
int c, i;
int len;
/*
* This loop similar to "ldterm_csi_werase" above.
*/
len = 0;
if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
}
ldterm_rubout((unsigned char) c, q,
len = 0;
} else if (*ip) {
if (*ip == UNKNOWN_WIDTH) {
== LDTERM_CS_TYPE_UTF8) {
for (i = 0; i < len;
i++)
u8_2[i] =
*ip = ldterm_utf8_width(
} else {
*ip = 1;
}
}
for (i = 0; i < (int)*ip; i++)
tp);
len = 0;
}
--ip;
}
} else {
}
}
} else {
}
}
}
/*
* Reprint the current input line. We assume c_cc has already been
* checked. XXX just the current line, not the whole queue? What
* about DEFECHO mode?
*/
static void
{
unsigned char *readp;
do {
}
/*
* Non canonical processing. Called with q locked from ldtermrsrv.
*
*/
static mblk_t *
{
unsigned char *rptr;
int free_flag = 0;
unsigned char *wptr;
unsigned char c;
/*
* Either we must echo the characters, or we must
* echo NL, or we must check for VLNEXT. Process
* characters one at a time.
*/
c = *rptr++;
/*
* If this character is the literal next
* character, echo it as '^' and backspace
* over it if echoing is enabled, indicate
* that the next character is to be treated
* literally, and remove the LNEXT from the
* input stream.
*
* If the *previous* character was the literal
* next character, don't check whether this
* is a literal next or not.
*/
c != _POSIX_VDISABLE &&
(unsigned char *)"^\b",
continue; /* and ignore it */
}
/*
* Not a "literal next" character, so it
* should show up as input. If it was
* literal-nexted, turn off the literal-next
* flag.
*/
*wptr++ = c;
/*
* Echo the character.
*/
/*
* Echo NL, even though ECHO is not
* set.
*/
if (c == '\n')
}
}
} else {
/*
* If there are any characters in this buffer, and
* the first of them was literal-nexted, turn off the
* literal-next flag.
*/
}
while (bytes_in_bp != 0) {
if (roomleft == 0) {
/*
* No more room in this mblk; save this one
* away, and allocate a new one.
*/
DEBUG4(("ldterm_do_noncanon: allcob failed\n"));
return (bpt);
}
/*
* Chain the new one to the end of the old
* one, and mark it as the last block in the
* current lump.
*/
}
DEBUG5(("roomleft=%d, bytes_in_bp=%d, tp->t_rd_request=%d\n",
/*
* if there is a read pending before this data got
* here move bytes according to the minimum of room
* left in this buffer, bytes in the message and byte
* count requested in the read. If there is no read
* pending, move the minimum of the first two
*/
if (tp->t_rd_request == 0)
else
if (bytes_to_move == 0)
break;
rptr += bytes_to_move;
}
if (bytes_in_bp == 0) {
DEBUG4(("bytes_in_bp is zero\n"));
} else
DEBUG4(("ldterm_do_noncanon: VMIN = %d, VTIME = %d, msglen = %d, \
/*
* If there is a pending read request at the stream head we
* are:
* MIN = 0, TIME > 0
* MIN = >, TIME = 0
* MIN > 0, TIME > 0
* MIN = 0, TIME = 0
* If we can satisfy VMIN, send it up, and start a new
* are also dealt with in the write side put routine
* when the M_READ is first seen.
*/
DEBUG4(("Incoming data while M_READ'ing\n"));
/*
* Case 1: Any data will satisfy the read, so send
* it upstream.
*/
else {
/* EMPTY */
DEBUG4(("ldterm_do_noncanon called, but no data!\n"));
}
/*
* Case 2: This should never time out, so
* until there's enough data, do nothing.
*/
/*
* Case 3: If MIN is satisfied, send it up.
* Also, remember to start a new timer *every*
* time we see something if MIN isn't
* safisfied
*/
else
vmin_settimer(q);
/*
* Case 4: Not possible. This request
* should always be satisfied from the write
* side, left here for debugging.
*/
} else { /* V_MIN == 0 && V_TIME == 0 */
}
if (free_flag) {
/* EMPTY */
DEBUG4(("CAUTION message block not freed\n"));
}
}
/*
* Echo a typed byte to the terminal. Returns the number of bytes
* printed. Bytes of EUC characters drop through the ECHOCTL stuff
* and are just output as themselves.
*/
static int
{
int i;
return (0);
i = 0;
/*
* Echo control characters (c <= 37) only if the ECHOCTRL
* flag is set as ^X.
*/
if (c <= 037 && c != '\t' && c != '\n') {
i++;
c += 'a' - 1;
else
c += 'A' - 1;
} else if (c == 0177) {
i++;
c = '?';
}
return (i + 1);
/* echo only special control character and the Bell */
} else if ((c > 037 && c != 0177) || c == '\t' || c == '\n' ||
c == '\r' || c == '\b' || c == 007 ||
return (i + 1);
}
return (i);
}
/*
* Put a character on the output queue.
*/
static void
{
/*
* Don't even look at the characters unless we have something
* useful to do with them.
*/
"ldterm: (ldterm_outchar) out of blocks");
return;
}
} else {
"ldterm_outchar: out of blocks");
return;
}
}
} else {
"ldterm_outchar: out of blocks");
return;
}
}
}
}
/*
* Copy a string, of length len, to the output queue.
*/
static void
{
while (len > 0) {
len--;
}
}
static mblk_t *
{
/*
* If no current message, allocate a block for it.
*/
"ldterm: (ldtermrsrv/newmsg) out of blocks");
return (bp);
}
}
return (bp);
}
static void
{
ssize_t s;
if (bp)
/*
* update sysinfo canch character.
*/
if (CANON_MODE)
(void) drv_setparm(SYSCANC, s);
tp->t_rd_request = 0;
/* can't reset everything, as we may have other input */
}
}
/*
* Re-enable the write-side service procedure. When an allocation
* failure causes write-side processing to stall, we disable the
* write side and arrange to call this function when allocation once
* again becomes possible.
*/
static void
ldterm_wenable(void *addr)
{
/*
* The bufcall is no longer pending.
*/
enableok(q);
qenable(q);
}
/*
* Line discipline output queue put procedure. Attempts to process
* the message directly and send it on downstream, queueing it only
* if there's already something pending or if its downstream neighbor
* is clogged.
*/
static void
{
/*
* Always process priority messages, regardless of whether or
* not our queue is nonempty.
*/
switch (type) {
case M_FLUSH:
/*
* Get rid of it, see comment in
* ldterm_dosig().
*/
return;
}
/*
* This is coming from above, so we only
* handle the write queue here. If FLUSHR is
* set, it will get turned around at the
* driver, and the read procedure will see it
* eventually.
*/
else
}
/*
* If a timed read is interrupted, there is
* no way to cancel an existing M_READ
* request. We kludge by allowing a flush to
* do so.
*/
break;
case M_READ:
DEBUG1(("ldtermwmsg:M_READ RECEIVED\n"));
/*
* Stream head needs data to satisfy timed
* read. Has meaning only if ICANON flag is
* off indicating raw mode
*/
DEBUG4((
"M_READ: RAW_MODE=%d, CNT=%d, VMIN=%d, VTIME=%d\n",
V_TIME));
if (RAW_MODE) {
/*
* The four possible cases are:
* MIN = 0, TIME > 0
* MIN = >, TIME = 0
* MIN > 0, TIME > 0
* MIN = 0, TIME = 0
* These four conditions must be dealt
* with on the read side as well in
* ldterm_do_noncanon(). Set TS_MREAD
* so that the read side will know
* there is a pending read request
* waiting at the stream head. If we
* can satisfy MIN do it here, rather
* than on the read side. If we can't,
* start timers if necessary and let
* the other side deal with it.
*
* We got another M_READ before the
* pending one completed, cancel any
* existing timeout.
*/
vmin_satisfied(RD(q),
tp, 0);
}
/*
* Case 1: Any data will
* satisfy read, otherwise
* start timer
*/
vmin_satisfied(RD(q),
tp, 1);
else
vmin_settimer(RD(q));
/*
* Case 2: If we have enough
* data, send up now.
* Otherwise, the read side
* should wait forever until MIN
* is satisified.
*/
vmin_satisfied(RD(q),
tp, 1);
/*
* Case 3: If we can satisfy
* the read, send it up. If we
* don't have enough data, but
* there is at least one char,
* start a timer. Otherwise,
* let the read side start
* the timer.
*/
vmin_satisfied(RD(q),
tp, 1);
vmin_settimer(RD(q));
/*
* Case 4: Read returns
* whatever data is available
* or zero if none.
*/
} else { /* V_MIN == 0 && V_TIME == 0 */
}
} else /* should do bufcall, really! */
"ldtermwmsg: out of blocks");
}
/*
* pass M_READ down
*/
break;
default:
/* Pass it through unmolested. */
break;
}
return;
}
/*
* If our queue is nonempty or there's a traffic jam
* downstream, this message must get in line.
*/
/*
* Exception: ioctls, except for those defined to
* take effect after output has drained, should be
* processed immediately.
*/
/*
* Queue these.
*/
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
break;
/*
* Handle all others immediately.
*/
default:
(void) ldtermwmsg(q, mp);
return;
}
}
return;
}
/*
* We can take the fast path through, by simply calling
* ldtermwmsg to dispose of mp.
*/
(void) ldtermwmsg(q, mp);
}
/*
* Line discipline output queue service procedure.
*/
static void
ldtermwsrv(queue_t *q)
{
/*
* We expect this loop to iterate at most once, but must be
* prepared for more in case our upstream neighbor isn't
* paying strict attention to what canput tells it.
*/
/*
* N.B.: ldtermwput has already handled high-priority
* messages, so we don't have to worry about them
* here. Hence, the putbq call is safe.
*/
break;
}
if (!ldtermwmsg(q, mp)) {
/*
* Couldn't handle the whole thing; give up
* for now and wait to be rescheduled.
*/
break;
}
}
}
/*
* Process the write-side message denoted by mp. If mp can't be
* processed completely (due to allocation failures), put the
* residual unprocessed part on the front of the write queue, disable
* the queue, and schedule a qbufcall to arrange to complete its
* processing later.
*
* Return 1 if the message was processed completely and 0 if not.
*
* This routine is called from both ldtermwput and ldtermwsrv to do the
* actual work of dealing with mp. ldtermwput will have already
* dealt with high priority messages.
*/
static int
{
case M_IOCTL:
ldterm_do_ioctl(q, mp);
break;
case M_DATA:
{
break;
}
/*
* Don't even look at the characters unless
* we have something useful to do with them.
*/
break;
}
/* Update sysinfo outch */
break;
}
default:
break;
}
return (1);
/*
* An allocation failure occurred that prevented the message
* from being completely processed. First, disable our
* queue, since it's pointless to attempt further processing
* until the allocation situation is resolved. (This must
* precede the putbq call below, which would otherwise mark
* the queue to be serviced.)
*/
noenable(q);
/*
* Stuff the remnant on our write queue so that we can
* complete it later when times become less lean. Note that
* this sets QFULL, so that our upstream neighbor will be
* blocked by flow control.
*/
/*
* Schedule a qbufcall to re-enable the queue. The failure
* won't have been for an allocation of more than OBSIZE
* bytes, so don't ask for more than that from bufcall.
*/
return (0);
}
/*
* Perform output processing on a message, accumulating the output
* characters in a new message.
*/
static mblk_t *
{
unsigned char c, n;
/*
* Allocate a new block into which to put bytes. If we can't,
* we just drop the rest of the message on the floor. If x is
* non-zero, just fall thru; failure requires cleanup before
* going out
*/
#define NEW_BLOCK(x) \
{ \
if (x == 0) \
goto outofbufs; \
} else { \
} \
}
/*
* When we allocate the first block of a message, we should
* stuff the pointer to it in "*omp". All subsequent blocks
* should have the pointer to them stuffed into the "b_cont"
* field of the previous block. "contpp" points to the place
* where we should stuff the pointer.
*
* If we already have a message we're filling in, continue doing
* so.
*/
} else {
bytes_left = 0;
}
do {
/*
* Make sure there's room for one more
* character. At most, we'll need "t_maxeuc"
* bytes.
*/
/* LINTED */
NEW_BLOCK(0);
}
/*
* If doing XCASE processing (not very
* likely, in this day and age), look at each
* character individually.
*/
/*
* We need to make sure that this is not
* a following byte of a multibyte character
* before applying an XCASE processing.
*
* tp->t_eucign will be 0 if and only
* if the current 'c' is an ASCII character
* and also a byte. Otherwise, it will have
* the byte length of a multibyte character.
*/
c, (void *)tp);
tp->
(void *)tp,
ECHOCTL);
}
}
/*
* If character is mapped on output,
* put out a backslash followed by
* what it is mapped to.
*/
(!echoing || c != '\\')) {
/* backslash is an ordinary character */
bytes_left--;
if (bytes_left == 0) {
/* LINTED */
NEW_BLOCK(1);
}
/*
* Allocation failed, make
* state consistent before
* returning
*/
goto outofbufs;
}
c = omaptab[c];
}
/*
* If no other output processing is
* required, push the character into
* the block and get another.
*/
} else {
}
bytes_left--;
continue;
}
/*
* OPOST output flag is set. Map
* lower case to upper case if OLCUC
* flag is set and the 'c' is a lowercase
* ASCII character.
*/
c >= 'a' && c <= 'z')
c -= 'a' - 'A';
} else {
/*
* Copy all the ORDINARY characters,
* possibly mapping upper case to
* lower case. We use "movtuc",
* STOPPING when we can't move some
* character. For multi-byte or
* multi-column EUC, we can't depend
* on the regular tables. Rather than
* just drop through to the "big
* switch" for all characters, it
* _might_ be faster to let "movtuc"
* move a bunch of characters.
* Chances are, even in multi-byte
* mode we'll have lots of ASCII
* going through. We check the flag
* once, and call movtuc with the
* appropriate table as an argument.
*
* "movtuc will work for all codeset
* types since it stops at the beginning
* byte of a multibyte character.
*/
if (bytes_to_move > bytes_left)
elcuctab : enotrantab));
} else {
}
/*
* We're save to just do this column
* calculation, because if TS_MEUC is
* set, we used the proper EUC
* tables, and won't have copied any
* EUC bytes.
*/
continue; /* moved all of block */
if (bytes_left == 0) {
/* LINTED */
NEW_BLOCK(0);
}
}
/*
* Again, we need to make sure that this is not
* a following byte of a multibyte character at
* here.
*
* 'tp->t_eucign' will be 0 iff the current 'c' is
* an ASCII character. Otherwise, it will have
* the byte length of a multibyte character.
* We also add the display width to 'tp->t_col' if
* the current codeset is not UTF-8 since this is
* a leading byte of a multibyte character.
* For UTF-8 codeset type, we add the display width
* when we get the last byte of a character.
*/
NOTASCII(c)) {
c, (void *)tp);
(void *)tp,
}
}
/*
* If the driver has requested, don't process
* output flags. However, if we're in
* multi-byte mode, we HAVE to look at
* EVERYTHING going out to maintain column
* position properly. Therefore IF the driver
* says don't AND we're not doing multi-byte,
* then don't do it. Otherwise, do it.
*
* NOTE: Hardware USUALLY doesn't expand tabs
* properly for multi-byte situations anyway;
* that's a known problem with the 3B2
* "PORTS" board firmware, and any other
* hardware that doesn't ACTUALLY know about
* the current EUC mapping that WE are using
* at this very moment. The problem is that
* memory width is INDEPENDENT of screen
* width - no relation - so WE know how wide
* the characters are, but an off-the-host
* board probably doesn't. So, until we're
* SURE that the hardware below us can
* correctly expand tabs in a
* multi-byte/multi-column EUC situation, we
* do it ourselves.
*/
/*
* Map <CR>to<NL> on output if OCRNL flag
* set. ONLCR processing is not done if OCRNL
* is set.
*/
c = '\n';
goto jocrnl;
}
} else {
/*
* In other codeset types, we safely assume
* any byte of a multibyte character will have
* 'ORDINARY' type. For ASCII characters, we
* still use the typetab[].
*/
else
}
/*
* Map <NL> to <CR><NL> on output if ONLCR
* flag is set.
*/
c = '\r';
} else
}
/*
* Delay values and column position
* calculated here. For EUC chars in
* multi-byte mode, we use "t_eucign" to help
* calculate columns. When we see the first
* byte of an EUC, we set t_eucign to the
* number of bytes that will FOLLOW it, and
* we add the screen width of the WHOLE EUC
* character to the column position. In
* particular, we can't count SS2 or SS3 as
* printing characters. Remember, folks, the
* screen width and memory width are
* independent - no relation. We could have
* dropped through for ASCII, but we want to
* catch any bad characters (i.e., t_eucign
* set and an ASCII char received) and
* possibly report the garbage situation.
*/
count = 0;
switch (ctype) {
case T_SS2:
case T_SS3:
case ORDINARY:
bytes_left--;
== LDTERM_CS_TYPE_UTF8 &&
tp->t_scratch_len);
}
} else {
n = elcuctab[c];
else
n = enotrantab[c];
if (n)
c = n;
bytes_left--;
}
} else { /* ho hum, ASCII mode... */
n = lcuctab[c];
else
n = notrantab[c];
if (n)
c = n;
bytes_left--;
}
break;
/*
* If we're doing ECHOCTL, we've
* already mapped the thing during
* the process of canonising. Don't
* bother here, as it's not one that
* we did.
*/
case CONTROL:
bytes_left--;
break;
/*
* This is probably a backspace
* received, not one that we're
* echoing. Let it go as a
* single-column backspace.
*/
case BACKSPACE:
count = 1;
}
bytes_left--;
break;
case NEWLINE:
goto cr;
count = 2;
bytes_left--;
break;
case TAB:
/*
* Map '\t' to spaces if XTABS flag
* is set. The calculation of
* "t_eucign" has probably insured
* that column will be correct, as we
* bumped t_col by the DISP width,
* not the memory width.
*/
for (;;) {
bytes_left--;
break; /* every 8th */
/*
* If we don't have
* room to fully
* expand this tab in
* this block, back
* up to continue
* expanding it into
* the next block.
*/
break;
}
}
} else {
count = 2;
} else {
TABDLY) {
case TAB2:
count = 6;
break;
case TAB1:
~07);
if (count < 5)
count = 0;
break;
}
}
bytes_left--;
}
break;
case VTAB:
count = 127;
bytes_left--;
break;
case RETURN:
/*
* Ignore <CR> in column 0 if ONOCR
* flag set.
*/
break;
cr:
case CR1:
count = 2;
else
break;
case CR2:
count = 4;
else
count = 6;
break;
case CR3:
count = 0;
else
count = 9;
break;
}
bytes_left--;
break;
}
if (count != 0) {
do {
if (bytes_left == 0) {
/* LINTED */
NEW_BLOCK(0);
}
else
bytes_left--;
} while (--count != 0);
} else {
/* drop on floor */
} else {
/*
* Update sysinfo
* outch
*/
(void) drv_setparm(SYSOUTC,
/*
* Send M_DELAY
* downstream
*/
if ((bp =
NULL) {
}
}
bytes_left = 0;
/*
* We have to start a new
* message; the delay
* introduces a break between
* messages.
*/
}
}
}
return (ibp);
}
#if !defined(__sparc)
int
unsigned char *table)
{
unsigned char c;
*to++ = c;
size--;
}
}
#endif
static void
{
/* Already conditioned with IEXTEN during VDISCARD processing */
else {
/* flush ones below us */
}
}
}
}
/*
* Signal generated by the reader: M_PCSIG and M_FLUSH messages sent.
*/
static void
{
int sndsig = 0;
/*
* c == \0 is brk case; need to flush on BRKINT even if
* noflsh is set.
*/
if (mode) {
sndsig = 1;
}
/*
* Flush read or write side.
* Restart the input or output.
*/
}
}
/*
* XXX This is extremely gross.
* Since we can't be sure our M_FLUSH
* will have run its course by the
* time we do the echo below, we set
* state and toss it in the write put
* routine to prevent flushing our
* own data. Note that downstream
* modules on the write side will be
* flushed by the M_FLUSH sent above.
*/
}
}
}
}
if (sndsig == 0)
if (c != '\0') {
}
}
}
/*
* Called when an M_IOCTL message is seen on the write queue; does
* whatever we're supposed to do with it, and either replies
* immediately or passes it to the next module down.
*/
static void
{
int i;
int locale_name_sz;
int error;
case TCSETS:
case TCSETSW:
case TCSETSF:
{
/*
* Set current parameters and special
* characters.
*/
if (error != 0) {
return;
}
/*
* Yuk. The C shell file completion
* code actually uses this "feature",
* so we have to support it.
*/
}
}
/*
* ldterm_adjust_modes does not deal with
* cflags
*/
return;
}
/*
* The driver may want to know about the
* following iflags: IGNBRK, BRKINT, IGNPAR,
* PARMRK, INPCK, IXON, IXANY.
*/
break;
}
case TCSETA:
case TCSETAW:
case TCSETAF:
{
/*
* Old-style "ioctl" to set current
* parameters and special characters. Don't
* clear out the unset portions, leave them
* as they are.
*/
if (error != 0) {
return;
}
/* TCGETS returns amodes, so update that too */
/* ldterm_adjust_modes does not deal with cflags */
return;
}
/*
* The driver may want to know about the
* following iflags: IGNBRK, BRKINT, IGNPAR,
* PARMRK, INPCK, IXON, IXANY.
*/
break;
}
case TCFLSH:
/*
* Do the flush on the write queue immediately, and
* queue up any flush on the read queue for the
* service procedure to see. Then turn it into the
* appropriate M_FLUSH message, so that the module
* below us doesn't have to know about TCFLSH.
*/
if (error != 0) {
return;
}
} else {
return;
}
return;
case TCXONC:
if (error != 0) {
return;
}
case 0:
(void) putnextctl(q, M_STOP);
}
break;
case 1:
(void) putnextctl(q, M_START);
}
break;
case 2:
(void) putnextctl(q, M_STOPI);
break;
case 3:
(void) putnextctl(q, M_STARTI);
break;
default:
return;
}
return;
/*
* TCSBRK is expected to be handled by the driver.
* The reason its left for the driver is that when
* the argument to TCSBRK is zero driver has to drain
* the data and sending a M_IOCACK from LDTERM before
* the driver drains the data is going to cause
* problems.
*/
/*
* The following are EUC related ioctls. For
* EUC_WSET, we have to pass the information on, even
* though we ACK the call. It's vital in the EUC
* environment that everybody downstream knows about
* the EUC codeset widths currently in use; we
* therefore pass down the information in an M_CTL
* message. It will bottom out in the driver.
*/
case EUC_WSET:
{
/* only needed for EUC_WSET */
/*
* If the user didn't supply any information,
* NAK it.
*/
if (error != 0) {
return;
}
/*
* Check here for something reasonable. If
* anything will take more than EUC_MAXW
* columns or more than EUC_MAXW bytes
* following SS2 or SS3, then just reject it
* out of hand. It's not impossible for us to
* do it, it just isn't reasonable. So far,
* in the world, we've seen the absolute max
* columns to be 2 and the max number of
* bytes to be 3. This allows room for some
* expansion of that, but it probably won't
* even be necessary. At the moment, we
* return a "range" error. If you really
* need to, you can push EUC_MAXW up to over
* 200; it doesn't make sense, though, with
* only a CANBSIZ sized input limit (usually
* 256)!
*/
for (i = 0; i < 4; i++) {
return;
}
}
/*
* Otherwise, save the information in tp,
* force codeset 0 (ASCII) to be one byte,
* one column.
*/
/*
* Now, check out whether we're doing
* multibyte processing. if we are, we need
* to allocate a block to hold the parallel
* array. By convention, we've been passed
* what amounts to a CSWIDTH definition. We
* actually NEED the number of bytes for
* Codesets 2 & 3.
*/
/*
* We'll set TS_MEUC if we're doing
* multi-column OR multi- byte OR both. It
* makes things easier... NOTE: If we fail
* to get the buffer we need to hold display
* widths, then DON'T let the TS_MEUC bit get
* set!
*/
for (i = 0; i < 4; i++) {
}
BPRI_HI))) {
"Can't allocate eucp_mp");
return;
}
/*
* here, if there's junk in
* the canonical buffer, then
* move the eucp pointer past
* it, so we don't run off
* the beginning. This is a
* total botch, but will
* hopefully keep stuff from
* getting too messed up
* until the user flushes
* this line!
*/
} else {
}
}
/* doing multi-byte handling */
}
/*
* Save the EUC width data we have at
* the t_csdata, set t_csdata.codeset_type to
* EUC one, and, switch the codeset methods at
* t_csmethods.
*/
(sizeof (ldterm_eucpc_data_t) *
/*
* We are not using the 'csinfo_num' anyway if the
* current codeset type is EUC. So, set it to
* the maximum possible.
*/
}
/*
* If we are able to allocate two blocks (the
* iocblk and the associated data), then pass
* it downstream, otherwise we'll need to NAK
* it, and drop whatever we WERE able to
* allocate.
*/
return;
}
return;
}
/*
* We got both buffers. Copy out the EUC
* information (as we received it, not what
* we're using!) & pass it on.
*/
/*
* Now ACK the ioctl.
*/
return;
}
case EUC_WGET:
if (error != 0) {
return;
}
return;
case CSDATA_SET:
if (error != 0) {
return;
}
/* Validate the codeset data provided. */
return;
}
return;
}
maxbytelen = maxscreenlen = 0;
for (i = 0; i < LDTERM_CS_TYPE_EUC_MAX_SUBCS; i++) {
EUC_MAXW ||
EUC_MAXW) {
return;
}
}
/* POSIX/C locale? */
if (maxbytelen == 0 && maxscreenlen == 0)
for (i = 0; i < LDTERM_CS_MAX_CODESETS; i++) {
return;
}
}
maxbytelen = 4;
maxscreenlen = 2;
}
locale_name_sz = 0;
if (csdp->locale_name) {
for (i = 0; i < MAXNAMELEN; i++)
break;
/*
* We cannot have any string that is not NULL byte
* terminated.
*/
if (i >= MAXNAMELEN) {
return;
}
locale_name_sz = i + 1;
}
/*
* As the final check, if there was invalid codeset_type
* given, or invalid byte_length was specified, it's an error.
*/
if (maxbytelen <= 0 || maxscreenlen <= 0) {
return;
}
/* Do the switching. */
BPRI_HI))) {
"Can't allocate eucp_mp");
return;
}
/*
* If there's junk in the canonical buffer,
* then move the eucp pointer past it,
* so we don't run off the beginning. This is
* a total botch, but will hopefully keep
* stuff from getting too messed up until
* the user flushes this line!
*/
} else {
}
}
/*
* We only set TS_MEUC for a multibyte/multi-column
* codeset.
*/
sizeof (ldterm_eucpc_data_t) *
} else {
/*
* We are not going to use this data
* structure. So, clear it. Also, stty(1) will
* make use of the cleared tp->eucwioc when
* it prints out codeset width setting.
*/
}
} else {
/*
* If this codeset is a single byte codeset that
* requires only single display column for all
* characters, we switch to default EUC codeset
* methods and data setting.
*/
}
}
}
/* Copy over locale_name. */
}
if (locale_name_sz > 1) {
csdp->locale_name);
} else {
}
/*
* Now ACK the ioctl.
*/
return;
case CSDATA_GET:
if (error != 0) {
return;
}
} else {
}
sizeof (ldterm_eucpc_data_t) * LDTERM_CS_MAX_CODESETS);
/*
* 3rd supplementary codesets, we subtract one from each
* byte length of the supplementary codesets. This is
* because single shift characters, SS2 and SS3, are not
* included in the byte lengths in the user space.
*/
}
return;
case PTSSTTY:
break;
}
}
/*
* Send an M_SETOPTS message upstream if any mode changes are being
* made that affect the stream head options. returns -1 if allocb
* fails, else returns 0.
*/
static int
{
struct stroptions optbuf;
/*
* Canonical mode is changing state; switch the
* stream head to message-nondiscard or byte-stream
* mode. Also, rerun the service procedure so it can
* change its mind about whether to send data
* upstream or not.
*/
DEBUG4(("CHANGING TO CANON MODE\n"));
/*
* if there is a pending raw mode timeout,
* clear it
*/
/*
*/
vmin_satisfied(q, tp, 0);
} else {
DEBUG4(("CHANGING TO RAW MODE\n"));
}
}
/*
* The "stop on background write" bit is changing.
*/
else
}
NULL) {
return (-1);
}
DEBUG4(("M_SETOPTS to stream head\n"));
}
return (0);
}
/*
* Called when an M_IOCACK message is seen on the read queue;
* modifies the data being returned, if necessary, and passes the
* reply up.
*/
static void
{
case TCGETS:
{
/*
* Get current parameters and return them to
* stream head eventually.
*/
/*
* cflag has cflags sent upstream by the
* driver
*/
if (cflag != 0)
break;
}
case TCGETA:
{
/*
* Old-style "ioctl" to get current
* parameters and return them to stream head
* eventually.
*/
break;
}
}
}
/*
* if they exist, clear TS_MREAD state, and send upstream. If a NULL
*/
static void
{
ASSERT(q);
}
if (sendup) {
/* EMPTY */
DEBUG4(("vmin_satisfied: data swiped, msglen = 0\n"));
} else {
if ((!q->q_first) ||
ldterm_msg_upstream(q, tp);
DEBUG4(("vmin_satisfied: delivering data\n"));
}
}
} else {
/* EMPTY */
}
}
static void
vmin_settimer(queue_t *q)
{
/*
* Don't start any time bombs.
*/
return;
/*
* tp->t_vtid should NOT be set here unless VMIN > 0 and
* VTIME > 0.
*/
/* EMPTY */
DEBUG4(("vmin_settimer: timer restarted, old tid=%d\n",
} else {
/* EMPTY */
DEBUG4(("vmin_settimer: tid = %d was still active!\n",
}
}
}
/*
* BRRrrringgg!! VTIME was satisfied instead of VMIN
*/
static void
vmin_timed_out(void *arg)
{
/* don't call untimeout now that we are in the timeout */
}
/*
* Routine to adjust termios flags to be processed by the line
* discipline. Driver below sends a termios structure, with the flags
* the driver intends to process. XOR'ing the driver sent termios
* structure with current termios structure with the default values
* (or set by ioctls from userland), we come up with a new termios
* structrue, the flags of which will be used by the line discipline
* in processing input and output. On return from this routine, we
* will have the following fields set in tp structure -->
* tp->t_modes: modes the line discipline will process tp->t_amodes:
* modes the user process thinks the line discipline is processing
*/
static void
{
/* No negotiation of clfags c_cc array special characters */
/*
* code
*/
}
/*
* Erase one multi-byte character. If TS_MEUC is set AND this
* is a multi-byte character, then this should be called instead of
* ldterm_erase. "ldterm_erase" will handle ASCII nicely, thank you.
*
* We'd better be pointing to the last byte. If we aren't, it will get
* screwed up.
*/
static void
{
int i, ung;
int c;
int j;
int len;
/* XXX Ick. We're in the middle of an EUC! */
/* What to do now? */
return; /* ignore it??? */
}
if (p < bottom)
return;
/*
* go through the buffer until we find the beginning of the
* multi-byte char.
*/
while ((*p == 0) && (p > bottom)) {
p--;
++ung;
}
/*
* Now, "ung" is the number of bytes to unget from the buffer
* and "*p" is the disp width of it. Fool "ldterm_rubout"
* into thinking we're rubbing out ASCII characters. Do that
* for the display width of the character.
*
* Also we accumulate bytes of the character so that if the character
* is a UTF-8 character, we will get the display width of the UTF-8
* character.
*/
if (ung >= LDTERM_CS_MAX_BYTE_LENGTH) {
j = len = LDTERM_CS_MAX_BYTE_LENGTH;
} else {
}
for (i = 0; i < ung; i++) { /* remove from buf */
if (j > 0)
}
}
if (*p == UNKNOWN_WIDTH) {
} else {
*p = 1;
}
}
for (i = 0; i < (int)*p; i++) /* remove from screen */
/*
* Adjust the parallel array pointer. Zero out the contents
* of parallel array for this position, just to make sure...
*/
*p = 0;
}
/*
* This is kind of a safety valve. Whenever we see a bad sequence
* come up, we call eucwarn. It just tallies the junk until a
* threshold is reached. Then it prints ONE message on the console
* and not any more. Hopefully, we can catch garbage; maybe it will
* be useful to somebody.
*/
static void
{
#ifdef DEBUG
"ldterm: tty at addr %p in multi-byte mode --",
(void *)tp);
"Over %d bad EUC characters this session", EUC_WARNCNT);
}
#endif
}
/*
* Copy an "eucioc_t" structure. We use the structure with
* incremented values for Codesets 2 & 3. The specification in
* eucioctl is that the sames values as the CSWIDTH definition at
* user level are passed to us. When we copy it "in" to ourselves, we
* do the increment. That allows us to avoid treating each character
* set separately for "t_eucleft" purposes. When we copy it "out" to
* return it to the user, we decrement the values so the user gets
* what it expects, and it matches CSWIDTH in the environment (if
* things are consistent!).
*/
static void
{
} else { /* copying in */
}
}
/*
* Take the first byte of a multi-byte, or an ASCII char. Return its
* codeset. If it's NOT the first byte of an EUC, then the return
* value may be garbage, as it's probably not SS2 or SS3, and
* therefore must be in codeset 1. Another bizarre catch here is the
* fact that we don't do anything about the "C1" control codes. In
* real life, we should; but nobody's come up with a good way of
* treating them.
*/
static int
{
if (ISASCII(c))
return (0);
if (codeset_type != LDTERM_CS_TYPE_EUC)
return (1);
switch (c) {
case SS2:
return (2);
case SS3:
return (3);
default:
return (1);
}
}
/* The following two functions are additional EUC codeset specific methods. */
/*
* ldterm_dispwidth - Take the first byte of an EUC (or ASCII) and
* return the display width. Since this is intended mostly for
* multi-byte handling, it returns EUC_TWIDTH for tabs so they can be
* differentiated from EUC characters (assumption: EUC require fewer
* than 255 columns). Also, if it's a backspace and !flag, it
* returns EUC_BSWIDTH. Newline & CR also depend on flag. This
* routine SHOULD be cleaner than this, but we have the situation
* where we may or may not be counting control characters as having a
* column width. Therefore, the computation of ASCII is pretty messy.
* The caller will be storing the value, and then switching on it
* when it's used. We really should define the EUC_TWIDTH and other
* constants in a header so that the routine could be used in other
* modules in the kernel.
*/
static int
{
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
case '\n':
return (EUC_NLWIDTH);
case '\r':
default:
return (mode ? 2 : 0);
}
}
return (1);
}
switch (c) {
case SS2:
case SS3:
default:
}
}
/*
* ldterm_memwidth_euc - Take the first byte of an EUC (or an ASCII char)
* and return its memory width. The routine could have been
* implemented to use only the codeset number, but that would require
* the caller to have that value available. Perhaps the user doesn't
* want to make the extra call or keep the value of codeset around.
* Therefore, we use the actual character with which they're
* concerned. This should never be called with anything but the
* first byte of an EUC, otherwise it will return a garbage value.
*/
static int
__ldterm_memwidth_euc(uchar_t c, void *p)
{
if (ISASCII(c))
return (1);
switch (c) {
case SS2:
case SS3:
default:
}
}
/* The following two functions are PCCS codeset specific methods. */
static int
{
int i;
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
case '\n':
return (EUC_NLWIDTH);
case '\r':
default:
return (mode ? 2 : 0);
}
}
return (1);
}
}
/*
* If this leading byte is not in the range list, either provided
* locale data is not sufficient or we encountered an invalid
* character. We return 1 in this case as a fallback value.
*/
return (1);
}
static int
__ldterm_memwidth_pccs(uchar_t c, void *p)
{
int i;
}
/*
* If this leading byte is not in the range list, either provided
* locale data is not sufficient or we encountered an invalid
* character. We return 1 in this case as a fallback value.
*/
return (1);
}
/* The following two functions are UTF-8 codeset specific methods. */
static int
{
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
case '\n':
return (EUC_NLWIDTH);
case '\r':
default:
return (mode ? 2 : 0);
}
}
return (1);
}
/* This is to silence the lint. */
return (1);
/*
* If it is a valid leading byte of a UTF-8 character, we set
* the width as 'UNKNOWN_WIDTH' for now. We need to have all
* the bytes to figure out the display width.
*/
return (UNKNOWN_WIDTH);
/*
* If it is an invalid leading byte, we just do our best by
* giving the display width of 1.
*/
return (1);
}
static int
__ldterm_memwidth_utf8(uchar_t c, void *p)
{
int len;
/*
* If the codeset type doesn't match, we treat them as
* an illegal character and return 1.
*/
return (1);
len = u8_number_of_bytes[c];
/*
* If this is a start of an illegal character, we treat
* such as an 1 byte character and screen out.
*/
}
static uchar_t
{
int i;
int j;
if (length == 0)
return ('\0');
/*
* If the UTF-8 character is out of UTF-16 code range, or,
* if it is either an ASCII character or an invalid leading byte for
* a UTF-8 character, return 1.
*/
if (length > 4 || j <= 0)
return ('\1');
for (i = 1; j > 0; j--, i++) {
/*
* The following additional checking is needed to conform to
* the "UTF-8 Corrigendum" introduced at the Unicode 3.1 and
* then updated one more time at the Unicode 3.2.
*/
if (i == 1) {
return ('\1');
return ('\1');
/*
* All subsequent bytes of UTF-8 character has the following
* binary encoding:
*
* 10xx xxxx
*
* hence left shift six bits to make space and then get
* six bits from the new byte.
*/
(u8[i] & LDTERM_CS_TYPE_UTF8_BIT_MASK);
}
i = 0;
if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P00) {
/* Basic Multilingual Plane. */
i = intcode / 4;
j = intcode % 4;
switch (j) {
case 0:
i = ldterm_ucode[0][i].u0;
break;
case 1:
i = ldterm_ucode[0][i].u1;
break;
case 2:
i = ldterm_ucode[0][i].u2;
break;
case 3:
i = ldterm_ucode[0][i].u3;
break;
}
} else if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P01) {
/* Secondary Multilingual Plane. */
i = intcode / 4;
j = intcode % 4;
switch (j) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKEXTB &&
/*
* Supplementary Plane for CJK Ideographs and
* Private Use Planes.
*/
return ('\2');
} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_P14 &&
/*
* Some Special Purpose Plane characters:
* These are like control characters and not printable.
*/
return ('\0');
}
/*
* We return the display width of 1 for all character code points
* that we didn't catch from the above logic and also for combining
* and conjoining characters with width value of zero.
*
* In particular, the reason why we are returning 1 for combining
* and conjoining characters is because the GUI-based terminal
* emulators are not yet capable of properly handling such characters
* and in most of the cases, they just treat such characters as if
* they occupy a display cell. If the terminal emulators are capable of
* handling the characters correctly, then, this logic of returning
* 1 should be revisited and changed. See CR 6660526 for more
* details on this.
*/
return ((i == 0) ? '\1' : (uchar_t)i);
}