#include <sys/param.h>
#include <sys/types.h>
#include <sys/termio.h>
#include <sys/stream.h>
#include <sys/conf.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/strtty.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/euc.h>
#include <sys/eucioctl.h>
#include <sys/csiioctl.h>
#include <sys/ptms.h>
#include <sys/ldterm.h>
#include <sys/cred.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
int ldterm_drain_limit = 15000000;
#define ORDINARY 0
#define CONTROL 1
#define BACKSPACE 2
#define NEWLINE 3
#define TAB 4
#define VTAB 5
#define RETURN 6
#define T_SS2 7
#define T_SS3 8
static char typetab[256] = {
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
BACKSPACE, TAB, NEWLINE, CONTROL,
VTAB, RETURN, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, T_SS2, T_SS3,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
ORDINARY, ORDINARY, ORDINARY, ORDINARY,
};
static unsigned char notrantab[256] = {
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', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', 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,
0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
};
static unsigned char lcuctab[256] = {
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', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '{', '|', '}', '~', 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,
0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
};
static char imaptab[256] = {
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, 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, 0, 0, 0, 0,
0, 0, 0, 0, '\\', 0, '~', 0,
0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 0, 0, 0, 0, 0,
};
static char omaptab[256] = {
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,
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, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 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,
};
static unsigned char enotrantab[256] = {
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', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', 0,
};
static unsigned char elcuctab[256] = {
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', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '{', '|', '}', '~', 0,
};
static struct streamtab ldtrinfo;
static struct fmodsw fsw = {
"ldterm",
&ldtrinfo,
D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "terminal line discipline", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int ldtermopen(queue_t *, dev_t *, int, int, cred_t *);
static int ldtermclose(queue_t *, int, cred_t *);
static int ldtermrput(queue_t *, mblk_t *);
static int ldtermrsrv(queue_t *);
static int ldtermrmsg(queue_t *, mblk_t *);
static int ldtermwput(queue_t *, mblk_t *);
static int ldtermwsrv(queue_t *);
static int ldtermwmsg(queue_t *, mblk_t *);
static mblk_t *ldterm_docanon(unsigned char, mblk_t *, size_t, queue_t *,
ldtermstd_state_t *, int *);
static int ldterm_unget(ldtermstd_state_t *);
static void ldterm_trim(ldtermstd_state_t *);
static void ldterm_rubout(unsigned char, queue_t *, size_t,
ldtermstd_state_t *);
static int ldterm_tabcols(ldtermstd_state_t *);
static void ldterm_erase(queue_t *, size_t, ldtermstd_state_t *);
static void ldterm_werase(queue_t *, size_t, ldtermstd_state_t *);
static void ldterm_kill(queue_t *, size_t, ldtermstd_state_t *);
static void ldterm_reprint(queue_t *, size_t, ldtermstd_state_t *);
static mblk_t *ldterm_dononcanon(mblk_t *, mblk_t *, size_t, queue_t *,
ldtermstd_state_t *);
static int ldterm_echo(unsigned char, queue_t *, size_t,
ldtermstd_state_t *);
static void ldterm_outchar(unsigned char, queue_t *, size_t,
ldtermstd_state_t *);
static void ldterm_outstring(unsigned char *, int, queue_t *, size_t,
ldtermstd_state_t *tp);
static mblk_t *newmsg(ldtermstd_state_t *);
static void ldterm_msg_upstream(queue_t *, ldtermstd_state_t *);
static void ldterm_wenable(void *);
static mblk_t *ldterm_output_msg(queue_t *, mblk_t *, mblk_t **,
ldtermstd_state_t *, size_t, int);
static void ldterm_flush_output(unsigned char, queue_t *,
ldtermstd_state_t *);
static void ldterm_dosig(queue_t *, int, unsigned char, int, int);
static void ldterm_do_ioctl(queue_t *, mblk_t *);
static int chgstropts(struct termios *, ldtermstd_state_t *, queue_t *);
static void ldterm_ioctl_reply(queue_t *, mblk_t *);
static void vmin_satisfied(queue_t *, ldtermstd_state_t *, 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 *);
static void cp_eucwioc(eucioc_t *, eucioc_t *, int);
static int ldterm_codeset(uchar_t, uchar_t);
static void ldterm_csi_erase(queue_t *, size_t, ldtermstd_state_t *);
static void ldterm_csi_werase(queue_t *, size_t, ldtermstd_state_t *);
static uchar_t ldterm_utf8_width(uchar_t *, int);
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 *);
static const ldterm_cs_methods_t cs_methods[LDTERM_CS_TYPE_MAX + 1] = {
{
NULL,
NULL
},
{
__ldterm_dispwidth_euc,
__ldterm_memwidth_euc
},
{
__ldterm_dispwidth_pccs,
__ldterm_memwidth_pccs
},
{
__ldterm_dispwidth_utf8,
__ldterm_memwidth_utf8
}
};
static const ldterm_cs_data_t default_cs_data = {
LDTERM_DATA_VERSION,
LDTERM_CS_TYPE_EUC,
(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'
}
};
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[];
extern const ldterm_unicode_data_cell_t ldterm_ucode[][16384];
#ifdef LDDEBUG
int ldterm_debug = 0;
#define DEBUG1(a) if (ldterm_debug == 1) printf a
#define DEBUG2(a) if (ldterm_debug >= 2) printf a
#define DEBUG3(a) if (ldterm_debug >= 3) printf a
#define DEBUG4(a) if (ldterm_debug >= 4) printf a
#define DEBUG5(a) if (ldterm_debug >= 5) printf a
#define DEBUG6(a) if (ldterm_debug >= 6) printf a
#define DEBUG7(a) if (ldterm_debug >= 7) printf a
#else
#define DEBUG1(a)
#define DEBUG2(a)
#define DEBUG3(a)
#define DEBUG4(a)
#define DEBUG5(a)
#define DEBUG6(a)
#define DEBUG7(a)
#endif
static struct module_info ldtermmiinfo = {
0x0bad,
"ldterm",
0,
_TTY_BUFSIZ,
_TTY_BUFSIZ,
LOWAT
};
static struct qinit ldtermrinit = {
ldtermrput,
ldtermrsrv,
ldtermopen,
ldtermclose,
NULL,
&ldtermmiinfo
};
static struct module_info ldtermmoinfo = {
0x0bad,
"ldterm",
0,
INFPSZ,
1,
0
};
static struct qinit ldtermwinit = {
ldtermwput,
ldtermwsrv,
ldtermopen,
ldtermclose,
NULL,
&ldtermmoinfo
};
static struct streamtab ldtrinfo = {
&ldtermrinit,
&ldtermwinit,
NULL,
NULL
};
static void
dummy_callback(void *arg)
{}
static mblk_t *
open_ioctl(queue_t *q, uint_t cmd)
{
mblk_t *mp;
bufcall_id_t id;
int retv;
while ((mp = mkiocb(cmd)) == NULL) {
id = qbufcall(q, sizeof (struct iocblk), BPRI_MED,
dummy_callback, NULL);
retv = qwait_sig(q);
qunbufcall(q, id);
if (retv == 0)
break;
}
return (mp);
}
static mblk_t *
open_mblk(queue_t *q, size_t len)
{
mblk_t *mp;
bufcall_id_t id;
int retv;
while ((mp = allocb(len, BPRI_MED)) == NULL) {
id = qbufcall(q, len, BPRI_MED, dummy_callback, NULL);
retv = qwait_sig(q);
qunbufcall(q, id);
if (retv == 0)
break;
}
return (mp);
}
static int
ldtermopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
{
ldtermstd_state_t *tp;
mblk_t *bp, *qryp;
int len;
struct stroptions *strop;
struct termios *termiosp;
queue_t *wq;
if (q->q_ptr != NULL) {
return (0);
}
tp = (ldtermstd_state_t *)kmem_zalloc(sizeof (ldtermstd_state_t),
KM_SLEEP);
if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_NOTPROM,
"ttymodes", (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
len == sizeof (struct termios)) {
tp->t_modes = *termiosp;
tp->t_amodes = *termiosp;
kmem_free(termiosp, len);
} else {
cmn_err(CE_WARN, "ldterm: Couldn't get ttymodes property!");
}
bzero(&tp->t_dmodes, sizeof (struct termios));
tp->t_state = 0;
tp->t_line = 0;
tp->t_col = 0;
tp->t_rocount = 0;
tp->t_rocol = 0;
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rd_request = 0;
tp->t_echomp = NULL;
tp->t_iocid = 0;
tp->t_wbufcid = 0;
tp->t_vtid = 0;
q->q_ptr = (caddr_t)tp;
WR(q)->q_ptr = (caddr_t)tp;
tp->t_codeset = tp->t_eucleft = tp->t_eucign = tp->t_scratch_len = 0;
bzero(&tp->eucwioc, EUCSIZE);
tp->eucwioc.eucw[0] = 1;
tp->eucwioc.scrw[0] = 1;
tp->t_maxeuc = 1;
tp->t_eucp = NULL;
tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
tp->t_csdata = default_cs_data;
if ((tp->t_eucp_mp = allocb(_TTY_BUFSIZ, BPRI_HI)) != NULL) {
tp->t_eucp = tp->t_eucp_mp->b_rptr;
tp->t_state = TS_MEUC;
tp->t_maxeuc = 4;
tp->t_csdata.codeset_type = LDTERM_CS_TYPE_UTF8;
tp->t_csdata.csinfo_num = 4;
tp->t_csdata.locale_name = (char *)kmem_alloc(6, KM_SLEEP);
(void) strcpy(tp->t_csdata.locale_name, "UTF-8");
tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_UTF8];
}
tp->t_eucwarn = 0;
qprocson(q);
if ((qryp = open_ioctl(q, MC_CANONQUERY)) == NULL)
goto open_abort;
qryp->b_datap->db_type = M_CTL;
wq = OTHERQ(q);
putnext(wq, qryp);
if ((qryp = open_ioctl(q, TCSBRK)) == NULL)
goto open_abort;
tp->t_drainmsg = qryp;
if ((bp = open_mblk(q, sizeof (int))) == NULL)
goto open_abort;
qryp->b_cont = bp;
if (ldterm_drain_limit != 0) {
if ((qryp = open_ioctl(q, MC_POSIXQUERY)) == NULL)
goto open_abort;
qryp->b_datap->db_type = M_CTL;
putnext(wq, qryp);
}
if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
goto open_abort;
tp->t_closeopts = bp;
if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
goto open_abort;
strop = (struct stroptions *)bp->b_wptr;
strop->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT|SO_NDELON|SO_ISTTY;
strop->so_readopt = RMSGN;
strop->so_hiwat = _TTY_BUFSIZ;
strop->so_lowat = LOWAT;
bp->b_wptr += sizeof (struct stroptions);
bp->b_datap->db_type = M_SETOPTS;
putnext(q, bp);
return (0);
open_abort:
qprocsoff(q);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
freemsg(tp->t_closeopts);
freemsg(tp->t_drainmsg);
kmem_free(tp, sizeof (ldtermstd_state_t));
return (EINTR);
}
struct close_timer {
timeout_id_t id;
ldtermstd_state_t *tp;
};
static void
drain_timed_out(void *arg)
{
struct close_timer *ctp = arg;
ctp->id = 0;
ctp->tp->t_state &= ~TS_IOCWAIT;
}
static int
ldtermclose(queue_t *q, int cflag, cred_t *crp)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
struct stroptions *strop;
mblk_t *bp;
struct close_timer cltimer;
tp->t_state |= TS_CLOSE;
if (tp->t_vtid != 0)
(void) quntimeout(q, tp->t_vtid);
tp->t_vtid = 0;
if (tp->t_wbufcid != 0)
qunbufcall(q, tp->t_wbufcid);
bp = tp->t_closeopts;
strop = (struct stroptions *)bp->b_wptr;
strop->so_flags = SO_READOPT|SO_NDELOFF;
strop->so_readopt = RNORM;
bp->b_wptr += sizeof (struct stroptions);
bp->b_datap->db_type = M_SETOPTS;
putnext(q, bp);
if (cflag & (FNDELAY|FNONBLOCK)) {
freemsg(tp->t_drainmsg);
} else if ((bp = tp->t_drainmsg) != NULL) {
struct iocblk *iocb;
iocb = (struct iocblk *)bp->b_rptr;
iocb->ioc_count = sizeof (int);
*(int *)bp->b_cont->b_rptr = 1;
bp->b_cont->b_wptr += sizeof (int);
tp->t_state |= TS_IOCWAIT;
tp->t_iocid = iocb->ioc_id;
if (!putq(WR(q), bp))
putnext(WR(q), bp);
cltimer.id = 0;
if (!ddi_can_receive_sig() && ldterm_drain_limit != 0) {
cltimer.tp = tp;
cltimer.id = qtimeout(q, drain_timed_out, &cltimer,
drv_usectohz(ldterm_drain_limit));
}
while (tp->t_state & TS_IOCWAIT) {
if (qwait_sig(q) == 0)
break;
}
if (cltimer.id != 0)
(void) quntimeout(q, cltimer.id);
}
qprocsoff(q);
freemsg(tp->t_message);
freemsg(tp->t_eucp_mp);
if (tp->t_csdata.locale_name != NULL)
kmem_free(tp->t_csdata.locale_name,
strlen(tp->t_csdata.locale_name) + 1);
kmem_free(tp, sizeof (ldtermstd_state_t));
q->q_ptr = NULL;
return (0);
}
static int
ldtermrput(queue_t *q, mblk_t *mp)
{
ldtermstd_state_t *tp;
unsigned char c;
queue_t *wrq = WR(q);
queue_t *nextq = q->q_next;
mblk_t *bp;
struct iocblk *qryp;
unsigned char *readp;
unsigned char *writep;
struct termios *emodes;
int dbtype;
tp = (ldtermstd_state_t *)q->q_ptr;
dbtype = DB_TYPE(mp);
if ((dbtype == M_IOCACK || dbtype == M_IOCNAK) &&
(tp->t_state & (TS_CLOSE|TS_IOCWAIT)) == (TS_CLOSE|TS_IOCWAIT)) {
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
if (iocp->ioc_id == tp->t_iocid) {
tp->t_state &= ~TS_IOCWAIT;
freemsg(mp);
return (0);
}
}
switch (dbtype) {
default:
(void) putq(q, mp);
return (0);
case M_PCSIG:
case M_SIG:
case M_IOCNAK:
putnext(q, mp);
return (0);
case M_IOCACK:
ldterm_ioctl_reply(q, mp);
return (0);
case M_BREAK:
if (mp->b_wptr - mp->b_rptr == 1) {
if (!(tp->t_amodes.c_iflag & IGNPAR)) {
mp->b_wptr = mp->b_rptr;
if (tp->t_amodes.c_iflag & PARMRK) {
unsigned char c;
c = *mp->b_rptr;
freemsg(mp);
if ((mp = allocb(3, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldtermrput: no blocks");
return (0);
}
mp->b_datap->db_type = M_DATA;
*mp->b_wptr++ = (uchar_t)'\377';
*mp->b_wptr++ = '\0';
*mp->b_wptr++ = c;
putnext(q, mp);
} else {
mp->b_datap->db_type = M_DATA;
*mp->b_wptr++ = '\0';
putnext(q, mp);
}
} else {
freemsg(mp);
}
return (0);
}
if (!(tp->t_amodes.c_iflag & IGNBRK)) {
if (tp->t_amodes.c_iflag & BRKINT) {
ldterm_dosig(q, SIGINT, '\0', M_PCSIG, FLUSHRW);
freemsg(mp);
} else if (tp->t_amodes.c_iflag & PARMRK) {
freemsg(mp);
if ((mp = allocb(3, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldtermrput: no blocks");
return (0);
}
mp->b_datap->db_type = M_DATA;
*mp->b_wptr++ = (uchar_t)'\377';
*mp->b_wptr++ = '\0';
*mp->b_wptr++ = '\0';
putnext(q, mp);
} else {
freemsg(mp);
if ((mp = allocb(1, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldtermrput: no blocks");
return (0);
}
mp->b_datap->db_type = M_DATA;
*mp->b_wptr++ = '\0';
putnext(q, mp);
}
} else {
freemsg(mp);
}
return (0);
case M_CTL:
DEBUG3(("ldtermrput: M_CTL received\n"));
if ((mp->b_wptr - mp->b_rptr) != sizeof (struct iocblk)) {
DEBUG3((
"Non standard M_CTL received by ldterm module\n"));
putnext(q, mp);
return (0);
}
qryp = (struct iocblk *)mp->b_rptr;
switch (qryp->ioc_cmd) {
case MC_PART_CANON:
DEBUG3(("ldtermrput: M_CTL Query Reply\n"));
if (!mp->b_cont) {
DEBUG3(("No information in Query Message\n"));
break;
}
if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) ==
sizeof (struct termios)) {
DEBUG3(("ldtermrput: M_CTL GrandScheme\n"));
emodes = (struct termios *)mp->b_cont->b_rptr;
bcopy(emodes, &tp->t_dmodes,
sizeof (struct termios));
ldterm_adjust_modes(tp);
break;
} else {
DEBUG3(("Incorrect query replysize\n"));
break;
}
case MC_NO_CANON:
tp->t_state |= TS_NOCANON;
if (tp->t_message != NULL) {
freemsg(tp->t_message);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rocount = 0;
tp->t_rocol = 0;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
tp->t_codeset = 0;
tp->t_eucleft = 0;
}
}
break;
case MC_DO_CANON:
tp->t_state &= ~TS_NOCANON;
break;
case MC_HAS_POSIX:
if (ldterm_drain_limit != 0) {
freemsg(tp->t_drainmsg);
tp->t_drainmsg = NULL;
}
break;
default:
DEBUG3(("Unknown M_CTL Message\n"));
break;
}
putnext(q, mp);
return (0);
case M_FLUSH:
if ((tp->t_state & TS_ISPTSTTY) && (*mp->b_rptr & FLUSHBAND))
flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
else
flushq(q, FLUSHDATA);
freemsg(tp->t_message);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rocount = 0;
tp->t_rocol = 0;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
putnext(q, mp);
if ((tp->t_modes.c_iflag & IXOFF) &&
(tp->t_state & TS_TBLOCK) &&
!(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
tp->t_state &= ~TS_TBLOCK;
(void) putnextctl(wrq, M_STARTI);
DEBUG1(("M_STARTI down\n"));
}
return (0);
case M_DATA:
break;
}
(void) drv_setparm(SYSRAWC, msgdsize(mp));
if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
!(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
tp->t_state &= ~TS_TBLOCK;
(void) putnextctl(wrq, M_STARTI);
DEBUG1(("M_STARTI down\n"));
}
if (tp->t_state & TS_NOCANON) {
(void) putq(q, mp);
return (0);
}
bp = mp;
do {
readp = bp->b_rptr;
writep = readp;
if (tp->t_modes.c_iflag & (INLCR|IGNCR|ICRNL|IUCLC|IXON) ||
tp->t_modes.c_lflag & (ISIG|ICANON)) {
while (readp < bp->b_wptr) {
c = *readp++;
if (tp->t_modes.c_iflag & ISTRIP)
c &= 0177;
if (tp->t_state & TS_PLNCH) {
tp->t_state &= ~TS_PLNCH;
tp->t_modes.c_lflag &= ~FLUSHO;
*writep++ = c;
continue;
}
if (c == _POSIX_VDISABLE) {
if (tp->t_modes.c_iflag & IXON &&
tp->t_state & TS_TTSTOP &&
tp->t_modes.c_lflag & IEXTEN &&
tp->t_modes.c_iflag & IXANY) {
tp->t_state &=
~(TS_TTSTOP|TS_OFBLOCK);
(void) putnextctl(wrq, M_START);
}
tp->t_modes.c_lflag &= ~FLUSHO;
*writep++ = c;
continue;
}
if (tp->t_modes.c_iflag & IXON) {
if (tp->t_state & TS_TTSTOP) {
if (c ==
tp->t_modes.c_cc[VSTART] ||
(tp->t_modes.c_lflag &
IEXTEN &&
tp->t_modes.c_iflag &
IXANY)) {
tp->t_state &=
~(TS_TTSTOP |
TS_OFBLOCK);
(void) putnextctl(wrq,
M_START);
}
} else {
if (c ==
tp->t_modes.c_cc[VSTOP]) {
tp->t_state |=
TS_TTSTOP;
(void) putnextctl(wrq,
M_STOP);
}
}
if (c == tp->t_modes.c_cc[VSTOP] ||
c == tp->t_modes.c_cc[VSTART])
continue;
}
if (tp->t_modes.c_lflag & IEXTEN) {
if (c == tp->t_modes.c_cc[VLNEXT]) {
tp->t_state |= TS_PLNCH;
tp->t_modes.c_lflag &= ~FLUSHO;
*writep++ = c;
continue;
}
if (c == tp->t_modes.c_cc[VDISCARD]) {
ldterm_flush_output(c, wrq, tp);
continue;
}
}
tp->t_modes.c_lflag &= ~FLUSHO;
if (tp->t_modes.c_lflag & ISIG) {
if (c == tp->t_modes.c_cc[VINTR]) {
ldterm_dosig(q, SIGINT, c,
M_PCSIG, FLUSHRW);
continue;
}
if (c == tp->t_modes.c_cc[VQUIT]) {
ldterm_dosig(q, SIGQUIT, c,
M_PCSIG, FLUSHRW);
continue;
}
if (c == tp->t_modes.c_cc[VSWTCH]) {
continue;
}
if (c == tp->t_modes.c_cc[VSUSP]) {
ldterm_dosig(q, SIGTSTP, c,
M_PCSIG, FLUSHRW);
continue;
}
if ((tp->t_modes.c_lflag & IEXTEN) &&
(c == tp->t_modes.c_cc[VDSUSP])) {
ldterm_dosig(q, SIGTSTP, c,
M_SIG, 0);
continue;
}
if (c == tp->t_modes.c_cc[VSTATUS]) {
ldterm_dosig(q, SIGINFO, '\0',
M_PCSIG, FLUSHRW);
continue;
}
}
if (c == '\r') {
if (tp->t_modes.c_iflag & IGNCR)
continue;
if (tp->t_modes.c_iflag & ICRNL)
c = '\n';
} else {
if (c == '\n' &&
tp->t_modes.c_iflag & INLCR)
c = '\r';
}
if (tp->t_modes.c_iflag & IUCLC &&
c >= 'A' && c <= 'Z')
c += 'a' - 'A';
*writep++ = c;
}
bp->b_wptr -= (readp - writep);
} else {
if (tp->t_modes.c_iflag & ISTRIP) {
while (readp < bp->b_wptr)
*writep++ = *readp++ & 0177;
}
tp->t_modes.c_lflag &= ~FLUSHO;
}
} while ((bp = bp->b_cont) != NULL);
if (q->q_first != NULL || !bcanputnext(q, mp->b_band) ||
(tp->t_state & TS_RESCAN))
(void) putq(q, mp);
else
(void) ldtermrmsg(q, mp);
if ((tp->t_modes.c_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK) &&
q->q_count >= TTXOHI) {
mutex_enter(QLOCK(nextq));
nextq->q_flag |= QWANTW;
mutex_exit(QLOCK(nextq));
tp->t_state |= TS_TBLOCK;
(void) putnextctl(wrq, M_STOPI);
DEBUG1(("M_STOPI down\n"));
}
return (0);
}
static int
ldtermrsrv(queue_t *q)
{
ldtermstd_state_t *tp;
mblk_t *mp;
tp = (ldtermstd_state_t *)q->q_ptr;
if (tp->t_state & TS_RESCAN) {
if (tp->t_message != NULL) {
DEBUG5(("RESCAN WAS SET; put back in q\n"));
if (tp->t_msglen != 0)
(void) putbq(q, tp->t_message);
else
freemsg(tp->t_message);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
}
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
tp->t_codeset = 0;
tp->t_eucleft = 0;
}
tp->t_state &= ~TS_RESCAN;
}
while ((mp = getq(q)) != NULL) {
if (!ldtermrmsg(q, mp))
break;
}
if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
!(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
tp->t_state &= ~TS_TBLOCK;
(void) putctl(WR(q), M_STARTI);
}
return (0);
}
static int
ldtermrmsg(queue_t *q, mblk_t *mp)
{
unsigned char c;
int dofree;
int status = 1;
size_t ebsize;
mblk_t *bp;
mblk_t *bpt;
ldtermstd_state_t *tp;
bpt = NULL;
tp = (ldtermstd_state_t *)q->q_ptr;
if (mp->b_datap->db_type <= QPCTL && !bcanputnext(q, mp->b_band)) {
if (tp->t_modes.c_lflag & ECHO) {
if ((tp->t_modes.c_iflag & IMAXBEL) &&
(tp->t_modes.c_lflag & ICANON)) {
freemsg(mp);
if (canputnext(WR(q)))
ldterm_outchar(CTRL('g'), WR(q), 4, tp);
status = 0;
goto echo;
} else {
(void) putctl1(q, M_FLUSH, FLUSHR);
}
} else {
(void) putbq(q, mp);
status = 0;
goto out;
}
}
switch (mp->b_datap->db_type) {
default:
putnext(q, mp);
goto out;
case M_HANGUP:
flushq(q, FLUSHDATA);
freemsg(tp->t_message);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rocount = 0;
tp->t_rocol = 0;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
if (tp->t_state & TS_TTSTOP) {
tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
(void) putnextctl(WR(q), M_START);
}
(void) putnextctl1(q, M_FLUSH, FLUSHW);
(void) putctl1(WR(q), M_FLUSH, FLUSHR);
putnext(q, mp);
goto out;
case M_IOCACK:
ldterm_ioctl_reply(q, mp);
goto out;
case M_DATA:
break;
}
if (tp->t_state & TS_NOCANON) {
putnext(q, mp);
goto out;
}
bp = mp;
if ((bpt = newmsg(tp)) != NULL) {
mblk_t *bcont;
do {
ASSERT(bp->b_wptr >= bp->b_rptr);
ebsize = bp->b_wptr - bp->b_rptr;
if (ebsize > EBSIZE)
ebsize = EBSIZE;
bcont = bp->b_cont;
if (CANON_MODE) {
dofree = 1;
while (bp->b_rptr < bp->b_wptr) {
c = *bp->b_rptr++;
if ((bpt = ldterm_docanon(c,
bpt, ebsize, q, tp, &dofree)) ==
NULL)
break;
}
if (dofree)
freeb(bp);
else {
(void) putbq(q, bp);
break;
}
} else
bpt = ldterm_dononcanon(bp, bpt, ebsize, q, tp);
if (bpt == NULL) {
cmn_err(CE_WARN,
"ldtermrsrv: out of blocks");
freemsg(bcont);
break;
}
} while ((bp = bcont) != NULL);
}
echo:
if (tp->t_echomp != NULL) {
if (canputnext(WR(q)))
putnext(WR(q), tp->t_echomp);
else
freemsg(tp->t_echomp);
tp->t_echomp = NULL;
}
out:
return (status);
}
static mblk_t *
ldterm_docanon(uchar_t c, mblk_t *bpt, size_t ebsize, queue_t *q,
ldtermstd_state_t *tp, int *dofreep)
{
queue_t *wrq = WR(q);
int i;
if (tp->t_state & TS_SLNCH)
goto escaped;
if (c == _POSIX_VDISABLE) {
tp->t_state &= ~TS_QUOT;
goto escaped;
}
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VLNEXT]) {
if (tp->t_modes.c_lflag & ECHO)
ldterm_outstring((unsigned char *)"^\b", 2, wrq,
ebsize, tp);
tp->t_state |= TS_SLNCH;
goto out;
}
if (c == tp->t_modes.c_cc[VERASE] || c == tp->t_modes.c_cc[VERASE2]) {
if (tp->t_state & TS_QUOT) {
ldterm_erase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
goto escaped;
} else {
if ((tp->t_state & TS_MEUC) && tp->t_msglen &&
(*(tp->t_eucp - 1) != 1 &&
*(tp->t_eucp - 1) <= UNKNOWN_WIDTH))
ldterm_csi_erase(wrq, ebsize, tp);
else
ldterm_erase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
goto out;
}
}
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VWERASE]) {
if (tp->t_state & TS_MEUC)
ldterm_csi_werase(wrq, ebsize, tp);
else
ldterm_werase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
goto out;
}
if (c == tp->t_modes.c_cc[VKILL]) {
if (tp->t_state & TS_QUOT) {
ldterm_erase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
goto escaped;
} else {
ldterm_kill(wrq, ebsize, tp);
bpt = tp->t_endmsg;
goto out;
}
}
if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VREPRINT]) {
ldterm_reprint(wrq, ebsize, tp);
goto out;
}
if (tp->t_state & TS_QUOT) {
tp->t_state &= ~TS_QUOT;
if (c == tp->t_modes.c_cc[VEOF]) {
ldterm_erase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
} else {
if ((tp->t_modes.c_lflag & XCASE) &&
imaptab[c] != '\0') {
ldterm_erase(wrq, ebsize, tp);
bpt = tp->t_endmsg;
c = imaptab[c];
}
}
} else {
if (c == tp->t_modes.c_cc[VEOF]) {
if ((tp->t_modes.c_lflag & ECHOCTL) &&
(tp->t_modes.c_lflag & IEXTEN) &&
(tp->t_modes.c_lflag & ECHO)) {
i = ldterm_echo(c, wrq, ebsize, tp);
while (i > 0) {
ldterm_outchar('\b', wrq, ebsize, tp);
i--;
}
}
bpt->b_datap->db_type = M_DATA;
ldterm_msg_upstream(q, tp);
if (!canputnext(q)) {
bpt = NULL;
*dofreep = 0;
} else {
bpt = newmsg(tp);
*dofreep = 1;
}
goto out;
}
}
escaped:
if ((tp->t_msglen > ((_TTY_BUFSIZ + 1) - (int)tp->t_maxeuc)) &&
!((tp->t_state & TS_MEUC) && tp->t_eucleft)) {
if (tp->t_modes.c_iflag & IMAXBEL) {
if (canputnext(wrq))
ldterm_outchar(CTRL('g'), wrq, ebsize, tp);
goto out;
} else {
DEBUG7(("ldterm_docanon: MAX_CANON processing\n"));
freemsg(tp->t_message);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rocount = 0;
tp->t_rocol = 0;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
tp->t_state &= ~TS_SLNCH;
bpt = newmsg(tp);
}
}
if (bpt->b_wptr >= bpt->b_datap->db_lim) {
bpt->b_datap->db_type = M_DATA;
if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL)
goto out;
tp->t_endmsg->b_cont = bpt;
tp->t_endmsg = bpt;
}
*bpt->b_wptr++ = c;
tp->t_msglen++;
if (tp->t_state & TS_MEUC) {
if (tp->t_eucleft) {
--tp->t_eucleft;
*tp->t_eucp++ = 0;
if (c < (uchar_t)0x20)
ldterm_eucwarn(tp);
} else {
if (ISASCII(c)) {
*tp->t_eucp++ =
tp->t_csmethods.ldterm_dispwidth(c,
(void *)tp, tp->t_modes.c_lflag & ECHOCTL);
tp->t_codeset = 0;
} else {
*tp->t_eucp++ =
tp->t_csmethods.ldterm_dispwidth(c,
(void *)tp, tp->t_modes.c_lflag & ECHOCTL);
tp->t_eucleft =
tp->t_csmethods.ldterm_memwidth(c,
(void *)tp) - 1;
tp->t_codeset = ldterm_codeset(
tp->t_csdata.codeset_type, c);
}
}
}
if (!(tp->t_state & TS_SLNCH) &&
(c == '\n' || (c != '\0' && (c == tp->t_modes.c_cc[VEOL] ||
(c == tp->t_modes.c_cc[VEOL2]))))) {
bpt->b_datap->db_type = M_DATA;
ldterm_msg_upstream(q, tp);
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
if ((bpt = newmsg(tp)) == NULL)
goto out;
} else {
if (tp->t_rocount++ == 0)
tp->t_rocol = tp->t_col;
tp->t_state &= ~(TS_SLNCH|TS_QUOT);
if ((c == '\\') && (tp->t_modes.c_lflag & IEXTEN) &&
(!(tp->t_state & TS_MEUC) ||
((tp->t_state & TS_MEUC) && (!tp->t_eucleft))))
tp->t_state |= TS_QUOT;
}
if (tp->t_state & TS_ERASE) {
tp->t_state &= ~TS_ERASE;
if (tp->t_modes.c_lflag & ECHO)
ldterm_outchar('/', wrq, ebsize, tp);
}
if (tp->t_modes.c_lflag & ECHO)
(void) ldterm_echo(c, wrq, ebsize, tp);
else {
if (c == '\n' && (tp->t_modes.c_lflag & ECHONL))
ldterm_outchar(c, wrq, ebsize, tp);
}
out:
return (bpt);
}
static int
ldterm_unget(ldtermstd_state_t *tp)
{
mblk_t *bpt;
if ((bpt = tp->t_endmsg) == NULL)
return (-1);
if (bpt->b_rptr == bpt->b_wptr)
return (-1);
tp->t_msglen--;
return (*--bpt->b_wptr);
}
static void
ldterm_trim(ldtermstd_state_t *tp)
{
mblk_t *bpt;
mblk_t *bp;
ASSERT(tp->t_endmsg);
bpt = tp->t_endmsg;
if (bpt->b_rptr == bpt->b_wptr) {
bp = tp->t_message;
if (bp != bpt) {
while (bp->b_cont != bpt) {
ASSERT(bp->b_cont);
bp = bp->b_cont;
}
bp->b_cont = NULL;
freeb(bpt);
tp->t_endmsg = bp;
}
}
}
static void
ldterm_rubout(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int tabcols;
static unsigned char crtrubout[] = "\b \b\b \b";
#define RUBOUT1 &crtrubout[3]
#define RUBOUT2 &crtrubout[0]
if (!(tp->t_modes.c_lflag & ECHO))
return;
if (tp->t_modes.c_lflag & ECHOE) {
if (tp->t_rocount == 0) {
ldterm_reprint(q, ebsize, tp);
return;
} else {
switch (typetab[c]) {
case ORDINARY:
if ((tp->t_modes.c_lflag & XCASE) &&
omaptab[c])
ldterm_outstring(RUBOUT1, 3, q, ebsize,
tp);
ldterm_outstring(RUBOUT1, 3, q, ebsize, tp);
break;
case VTAB:
case BACKSPACE:
case CONTROL:
case RETURN:
case NEWLINE:
if ((tp->t_modes.c_lflag & ECHOCTL) &&
(tp->t_modes.c_lflag & IEXTEN))
ldterm_outstring(RUBOUT2, 6, q, ebsize,
tp);
break;
case TAB:
if (tp->t_rocount < tp->t_msglen) {
ldterm_reprint(q, ebsize, tp);
return;
}
tabcols = ldterm_tabcols(tp);
while (--tabcols >= 0)
ldterm_outchar('\b', q, ebsize, tp);
break;
}
}
} else if ((tp->t_modes.c_lflag & ECHOPRT) &&
(tp->t_modes.c_lflag & IEXTEN)) {
if (!(tp->t_state & TS_ERASE)) {
ldterm_outchar('\\', q, ebsize, tp);
tp->t_state |= TS_ERASE;
}
(void) ldterm_echo(c, q, ebsize, tp);
} else
(void) ldterm_echo(tp->t_modes.c_cc[VERASE], q, ebsize, tp);
tp->t_rocount--;
}
static int
ldterm_tabcols(ldtermstd_state_t *tp)
{
int col;
int i;
mblk_t *bp;
unsigned char *readp, *endp;
unsigned char c;
uchar_t *startp;
char errflg;
uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
col = tp->t_rocol;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
bp = tp->t_message;
startp = bp->b_datap->db_base;
readp = tp->t_eucp_mp->b_rptr;
endp = tp->t_eucp;
errflg = (char)0;
while (readp < endp) {
switch (*readp) {
case EUC_TWIDTH:
col |= 07;
col++;
break;
case EUC_BSWIDTH:
if (col)
col--;
break;
case EUC_NLWIDTH:
if (tp->t_modes.c_oflag & ONLRET)
col = 0;
break;
case EUC_CRWIDTH:
col = 0;
break;
case UNKNOWN_WIDTH:
if (tp->t_csdata.codeset_type !=
LDTERM_CS_TYPE_UTF8 || errflg) {
*readp = 1;
col++;
break;
}
u8[0] = *startp;
for (i = 1; (i < LDTERM_CS_MAX_BYTE_LENGTH) &&
(*(readp + i) == 0); i++) {
startp++;
if (startp >= bp->b_datap->db_lim) {
if (bp->b_cont) {
bp = bp->b_cont;
startp =
bp->b_datap->
db_base;
} else {
*readp = 1;
col++;
break;
}
}
u8[i] = *startp;
}
if (*readp == 1)
break;
*readp = ldterm_utf8_width(u8, i);
col += *readp;
readp += (i - 1);
break;
default:
col += *readp;
break;
}
++readp;
++startp;
if (startp >= bp->b_datap->db_lim) {
if (bp->b_cont) {
bp = bp->b_cont;
startp = bp->b_datap->db_base;
} else {
errflg = (char)1;
startp--;
}
}
}
goto eucout;
}
bp = tp->t_message;
do {
readp = bp->b_rptr;
while (readp < bp->b_wptr) {
c = *readp++;
if ((tp->t_modes.c_lflag & ECHOCTL) &&
(tp->t_modes.c_lflag & IEXTEN)) {
if (c <= 037 && c != '\t' && c != '\n' ||
c == 0177) {
col += 2;
continue;
}
}
switch (typetab[c]) {
case ORDINARY:
col++;
break;
case CONTROL:
break;
case BACKSPACE:
if (col != 0)
col--;
break;
case NEWLINE:
if (tp->t_modes.c_oflag & ONLRET)
col = 0;
break;
case TAB:
col |= 07;
col++;
break;
case VTAB:
break;
case RETURN:
col = 0;
break;
}
}
} while ((bp = bp->b_cont) != NULL);
eucout:
col = tp->t_col - col;
if (col > 8)
col = 8;
return (col);
}
static void
ldterm_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int c;
if ((c = ldterm_unget(tp)) != -1) {
ldterm_rubout((unsigned char) c, q, ebsize, tp);
ldterm_trim(tp);
if (tp->t_state & TS_MEUC)
--tp->t_eucp;
}
}
static void
ldterm_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int c;
while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
ldterm_rubout((unsigned char) c, q, ebsize, tp);
ldterm_trim(tp);
}
while (c != -1 && c != ' ' && c != '\t') {
ldterm_rubout((unsigned char) c, q, ebsize, tp);
ldterm_trim(tp);
c = ldterm_unget(tp);
}
if (c != -1) {
tp->t_endmsg->b_wptr++;
tp->t_msglen++;
}
}
static void
ldterm_csi_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int c, i;
int len;
uchar_t *ip;
uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
uchar_t u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
ip = tp->t_eucp - 1;
while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
tp->t_eucp--;
ldterm_rubout((unsigned char) c, q, ebsize, tp);
ldterm_trim(tp);
--ip;
}
len = 0;
while (c != -1 && c != ' ' && c != '\t') {
tp->t_eucp--;
if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
u8[len++] = (uchar_t)c;
}
if ((*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH) &&
ISASCII(c)) {
ldterm_rubout((unsigned char) c, q, ebsize, tp);
len = 0;
} else if (*ip) {
if (*ip == UNKNOWN_WIDTH) {
if (tp->t_csdata.codeset_type ==
LDTERM_CS_TYPE_UTF8) {
for (i = 0; i < len; i++)
u8_2[i] = u8[len - i - 1];
*ip = ldterm_utf8_width(u8_2, len);
} else {
*ip = 1;
}
}
for (i = 0; i < (int)*ip; i++)
ldterm_rubout(' ', q, ebsize, tp);
len = 0;
}
ldterm_trim(tp);
--ip;
c = ldterm_unget(tp);
}
if (c != -1) {
tp->t_endmsg->b_wptr++;
tp->t_msglen++;
}
}
static void
ldterm_kill(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int c, i;
int len;
uchar_t *ip;
uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
uchar_t u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
if ((tp->t_modes.c_lflag & ECHOKE) &&
(tp->t_modes.c_lflag & IEXTEN) &&
(tp->t_msglen == tp->t_rocount)) {
if (tp->t_state & TS_MEUC) {
ip = tp->t_eucp - 1;
len = 0;
while ((c = ldterm_unget(tp)) != (-1)) {
tp->t_eucp--;
if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
u8[len++] = (uchar_t)c;
}
if ((*ip == 1 || *ip == 2 ||
*ip > UNKNOWN_WIDTH) && ISASCII(c)) {
ldterm_rubout((unsigned char) c, q,
ebsize, tp);
len = 0;
} else if (*ip) {
if (*ip == UNKNOWN_WIDTH) {
if (tp->t_csdata.codeset_type
== LDTERM_CS_TYPE_UTF8) {
for (i = 0; i < len;
i++)
u8_2[i] =
u8[len-i-1];
*ip = ldterm_utf8_width(
u8_2, len);
} else {
*ip = 1;
}
}
for (i = 0; i < (int)*ip; i++)
ldterm_rubout(' ', q, ebsize,
tp);
len = 0;
}
ldterm_trim(tp);
--ip;
}
} else {
while ((c = ldterm_unget(tp)) != -1) {
ldterm_rubout((unsigned char) c, q, ebsize, tp);
ldterm_trim(tp);
}
}
} else {
(void) ldterm_echo(tp->t_modes.c_cc[VKILL], q, ebsize, tp);
if (tp->t_modes.c_lflag & ECHOK)
(void) ldterm_echo('\n', q, ebsize, tp);
while (ldterm_unget(tp) != -1) {
if (tp->t_state & TS_MEUC)
--tp->t_eucp;
ldterm_trim(tp);
}
tp->t_rocount = 0;
if (tp->t_state & TS_MEUC)
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
tp->t_state &= ~(TS_QUOT|TS_ERASE|TS_SLNCH);
}
static void
ldterm_reprint(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
mblk_t *bp;
unsigned char *readp;
if (tp->t_modes.c_cc[VREPRINT] != (unsigned char) 0)
(void) ldterm_echo(tp->t_modes.c_cc[VREPRINT], q, ebsize, tp);
ldterm_outchar('\n', q, ebsize, tp);
bp = tp->t_message;
do {
readp = bp->b_rptr;
while (readp < bp->b_wptr)
(void) ldterm_echo(*readp++, q, ebsize, tp);
} while ((bp = bp->b_cont) != NULL);
tp->t_state &= ~TS_ERASE;
tp->t_rocount = tp->t_msglen;
tp->t_rocol = 0;
}
static mblk_t *
ldterm_dononcanon(mblk_t *bp, mblk_t *bpt, size_t ebsize, queue_t *q,
ldtermstd_state_t *tp)
{
queue_t *wrq = WR(q);
unsigned char *rptr;
size_t bytes_in_bp;
size_t roomleft;
size_t bytes_to_move;
int free_flag = 0;
if (tp->t_modes.c_lflag & (ECHO|ECHONL|IEXTEN)) {
unsigned char *wptr;
unsigned char c;
rptr = bp->b_rptr;
wptr = bp->b_rptr;
while (rptr < bp->b_wptr) {
c = *rptr++;
if ((tp->t_modes.c_lflag & IEXTEN) &&
!(tp->t_state & TS_SLNCH) &&
c != _POSIX_VDISABLE &&
c == tp->t_modes.c_cc[VLNEXT]) {
if (tp->t_modes.c_lflag & ECHO)
ldterm_outstring(
(unsigned char *)"^\b",
2, wrq, ebsize, tp);
tp->t_state |= TS_SLNCH;
continue;
}
tp->t_state &= ~TS_SLNCH;
*wptr++ = c;
if (tp->t_modes.c_lflag & ECHO) {
(void) ldterm_echo(c, wrq, ebsize, tp);
} else if (tp->t_modes.c_lflag & ECHONL) {
if (c == '\n')
ldterm_outchar('\n', wrq, 1, tp);
}
}
bp->b_wptr = wptr;
} else {
if (bp->b_rptr != bp->b_wptr)
tp->t_state &= ~TS_SLNCH;
}
ASSERT(bp->b_wptr >= bp->b_rptr);
bytes_in_bp = bp->b_wptr - bp->b_rptr;
rptr = bp->b_rptr;
while (bytes_in_bp != 0) {
roomleft = bpt->b_datap->db_lim - bpt->b_wptr;
if (roomleft == 0) {
if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL) {
freeb(bp);
DEBUG4(("ldterm_do_noncanon: allcob failed\n"));
return (bpt);
}
tp->t_endmsg->b_cont = bpt;
tp->t_endmsg = bpt;
roomleft = IBSIZE;
}
DEBUG5(("roomleft=%d, bytes_in_bp=%d, tp->t_rd_request=%d\n",
roomleft, bytes_in_bp, tp->t_rd_request));
if (tp->t_rd_request == 0)
bytes_to_move = MIN(roomleft, bytes_in_bp);
else
bytes_to_move =
MIN(MIN(roomleft, bytes_in_bp), tp->t_rd_request);
DEBUG5(("Bytes to move = %lu\n", bytes_to_move));
if (bytes_to_move == 0)
break;
bcopy(rptr, bpt->b_wptr, bytes_to_move);
bpt->b_wptr += bytes_to_move;
rptr += bytes_to_move;
tp->t_msglen += bytes_to_move;
bytes_in_bp -= bytes_to_move;
}
if (bytes_in_bp == 0) {
DEBUG4(("bytes_in_bp is zero\n"));
freeb(bp);
} else
free_flag = 1;
DEBUG4(("ldterm_do_noncanon: VMIN = %d, VTIME = %d, msglen = %d, \
tid = %d\n", V_MIN, V_TIME, tp->t_msglen, tp->t_vtid));
DEBUG4(("Incoming data while M_READ'ing\n"));
if (V_MIN == 0 && V_TIME > 0) {
if (tp->t_msglen)
vmin_satisfied(q, tp, 1);
else {
DEBUG4(("ldterm_do_noncanon called, but no data!\n"));
}
} else if (V_MIN > 0 && V_TIME == 0) {
if (tp->t_msglen >= (int)V_MIN)
vmin_satisfied(q, tp, 1);
} else if (V_MIN > 0 && V_TIME > 0) {
if (tp->t_msglen >= (int)V_MIN)
vmin_satisfied(q, tp, 1);
else
vmin_settimer(q);
} else {
vmin_satisfied(q, tp, 1);
}
if (free_flag) {
DEBUG4(("CAUTION message block not freed\n"));
}
return (newmsg(tp));
}
static int
ldterm_echo(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int i;
if (!(tp->t_modes.c_lflag & ECHO))
return (0);
i = 0;
if ((tp->t_modes.c_lflag & ECHOCTL) &&
(tp->t_modes.c_lflag & IEXTEN)) {
if (c <= 037 && c != '\t' && c != '\n') {
ldterm_outchar('^', q, ebsize, tp);
i++;
if (tp->t_modes.c_oflag & OLCUC)
c += 'a' - 1;
else
c += 'A' - 1;
} else if (c == 0177) {
ldterm_outchar('^', q, ebsize, tp);
i++;
c = '?';
}
ldterm_outchar(c, q, ebsize, tp);
return (i + 1);
} else if ((c > 037 && c != 0177) || c == '\t' || c == '\n' ||
c == '\r' || c == '\b' || c == 007 ||
c == tp->t_modes.c_cc[VKILL]) {
ldterm_outchar(c, q, ebsize, tp);
return (i + 1);
}
return (i);
}
static void
ldterm_outchar(uchar_t c, queue_t *q, size_t bsize, ldtermstd_state_t *tp)
{
mblk_t *curbp;
if ((tp->t_modes.c_oflag & OPOST) ||
((tp->t_modes.c_lflag & XCASE) &&
(tp->t_modes.c_lflag & ICANON))) {
mblk_t *mp;
if ((mp = allocb(4, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldterm: (ldterm_outchar) out of blocks");
return;
}
*mp->b_wptr++ = c;
mp = ldterm_output_msg(q, mp, &tp->t_echomp, tp, bsize, 1);
if (mp != NULL)
freemsg(mp);
} else {
if ((curbp = tp->t_echomp) != NULL) {
while (curbp->b_cont != NULL)
curbp = curbp->b_cont;
if (curbp->b_datap->db_lim == curbp->b_wptr) {
mblk_t *newbp;
if ((newbp = allocb(bsize, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldterm_outchar: out of blocks");
return;
}
curbp->b_cont = newbp;
curbp = newbp;
}
} else {
if ((curbp = allocb(bsize, BPRI_HI)) == NULL) {
cmn_err(CE_WARN,
"ldterm_outchar: out of blocks");
return;
}
tp->t_echomp = curbp;
}
*curbp->b_wptr++ = c;
}
}
static void
ldterm_outstring(uchar_t *cp, int len, queue_t *q, size_t bsize,
ldtermstd_state_t *tp)
{
while (len > 0) {
ldterm_outchar(*cp++, q, bsize, tp);
len--;
}
}
static mblk_t *
newmsg(ldtermstd_state_t *tp)
{
mblk_t *bp;
if ((bp = tp->t_endmsg) == NULL) {
if ((bp = allocb(IBSIZE, BPRI_MED)) == NULL) {
cmn_err(CE_WARN,
"ldterm: (ldtermrsrv/newmsg) out of blocks");
return (bp);
}
tp->t_message = bp;
tp->t_endmsg = bp;
}
return (bp);
}
static void
ldterm_msg_upstream(queue_t *q, ldtermstd_state_t *tp)
{
ssize_t s;
mblk_t *bp;
bp = tp->t_message;
s = msgdsize(bp);
if (bp)
putnext(q, tp->t_message);
if (CANON_MODE)
(void) drv_setparm(SYSCANC, s);
tp->t_message = NULL;
tp->t_endmsg = NULL;
tp->t_msglen = 0;
tp->t_rocount = 0;
tp->t_rd_request = 0;
if (tp->t_state & TS_MEUC) {
ASSERT(tp->t_eucp_mp);
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
}
static void
ldterm_wenable(void *addr)
{
queue_t *q = addr;
ldtermstd_state_t *tp;
tp = (ldtermstd_state_t *)q->q_ptr;
tp->t_wbufcid = 0;
enableok(q);
qenable(q);
}
static int
ldtermwput(queue_t *q, mblk_t *mp)
{
ldtermstd_state_t *tp;
unsigned char type = mp->b_datap->db_type;
tp = (ldtermstd_state_t *)q->q_ptr;
if (type >= QPCTL) {
switch (type) {
case M_FLUSH:
if ((tp->t_state & TS_FLUSHWAIT) &&
(*mp->b_rptr == FLUSHW)) {
tp->t_state &= ~TS_FLUSHWAIT;
freemsg(mp);
return (0);
}
if (*mp->b_rptr & FLUSHW) {
if ((tp->t_state & TS_ISPTSTTY) &&
(*mp->b_rptr & FLUSHBAND))
flushband(q, *(mp->b_rptr + 1),
FLUSHDATA);
else
flushq(q, FLUSHDATA);
}
putnext(q, mp);
if (tp->t_state & TS_MREAD)
vmin_satisfied(RD(q), tp, 0);
break;
case M_READ:
DEBUG1(("ldtermwmsg:M_READ RECEIVED\n"));
DEBUG4((
"M_READ: RAW_MODE=%d, CNT=%d, VMIN=%d, VTIME=%d\n",
RAW_MODE, *(unsigned int *)mp->b_rptr, V_MIN,
V_TIME));
tp->t_rd_request = *(unsigned int *)mp->b_rptr;
if (RAW_MODE) {
if (newmsg(tp) != NULL) {
if (tp->t_state & TS_MREAD) {
vmin_satisfied(RD(q),
tp, 0);
}
tp->t_state |= TS_MREAD;
if (V_MIN == 0 && V_TIME > 0) {
if (tp->t_msglen)
vmin_satisfied(RD(q),
tp, 1);
else
vmin_settimer(RD(q));
} else if (V_MIN > 0 && V_TIME == 0) {
if (tp->t_msglen >= (int)V_MIN)
vmin_satisfied(RD(q),
tp, 1);
} else if (V_MIN > 0 && V_TIME > 0) {
if (tp->t_msglen >= (int)V_MIN)
vmin_satisfied(RD(q),
tp, 1);
else if (tp->t_msglen)
vmin_settimer(RD(q));
} else {
vmin_satisfied(RD(q), tp, 1);
}
} else
cmn_err(CE_WARN,
"ldtermwmsg: out of blocks");
}
putnext(q, mp);
break;
default:
putnext(q, mp);
break;
}
return (0);
}
if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
if (type == M_IOCTL) {
struct iocblk *iocp;
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
break;
default:
(void) ldtermwmsg(q, mp);
return (0);
}
}
(void) putq(q, mp);
return (0);
}
(void) ldtermwmsg(q, mp);
return (0);
}
static int
ldtermwsrv(queue_t *q)
{
mblk_t *mp;
while ((mp = getq(q)) != NULL) {
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
break;
}
if (!ldtermwmsg(q, mp)) {
break;
}
}
return (0);
}
static int
ldtermwmsg(queue_t *q, mblk_t *mp)
{
ldtermstd_state_t *tp;
mblk_t *residmp = NULL;
size_t size;
tp = (ldtermstd_state_t *)q->q_ptr;
switch (mp->b_datap->db_type) {
case M_IOCTL:
ldterm_do_ioctl(q, mp);
break;
case M_DATA:
{
mblk_t *omp = NULL;
if ((tp->t_modes.c_lflag & FLUSHO) &&
(tp->t_modes.c_lflag & IEXTEN)) {
freemsg(mp);
break;
}
tp->t_rocount = 0;
if (((tp->t_modes.c_oflag & OPOST) ||
((tp->t_modes.c_lflag & XCASE) &&
(tp->t_modes.c_lflag & ICANON))) &&
(msgdsize(mp) || !(tp->t_state & TS_ISPTSTTY))) {
unsigned char band = mp->b_band;
short flag = mp->b_flag;
residmp = ldterm_output_msg(q, mp, &omp,
tp, OBSIZE, 0);
if ((mp = omp) == NULL)
break;
mp->b_band |= band;
mp->b_flag |= flag;
}
(void) drv_setparm(SYSOUTC, msgdsize(mp));
putnext(q, mp);
break;
}
default:
putnext(q, mp);
break;
}
if (residmp == NULL)
return (1);
noenable(q);
(void) putbq(q, residmp);
size = msgdsize(residmp);
if (size > OBSIZE)
size = OBSIZE;
if (tp->t_wbufcid)
qunbufcall(q, tp->t_wbufcid);
tp->t_wbufcid = qbufcall(q, size, BPRI_MED, ldterm_wenable, q);
return (0);
}
static mblk_t *
ldterm_output_msg(queue_t *q, mblk_t *imp, mblk_t **omp,
ldtermstd_state_t *tp, size_t bsize, int echoing)
{
mblk_t *ibp;
mblk_t *obp;
mblk_t *cbp;
mblk_t *oobp;
mblk_t **contpp;
unsigned char c, n;
int count, ctype;
ssize_t bytes_left;
mblk_t *bp;
#define NEW_BLOCK(x) \
{ \
oobp = obp; \
if ((obp = allocb(bsize, BPRI_MED)) == NULL) { \
if (x == 0) \
goto outofbufs; \
} else { \
*contpp = obp; \
contpp = &obp->b_cont; \
bytes_left = obp->b_datap->db_lim - obp->b_wptr; \
} \
}
ibp = imp;
if ((obp = *omp) != NULL) {
while (obp->b_cont != NULL)
obp = obp->b_cont;
contpp = &obp->b_cont;
bytes_left = obp->b_datap->db_lim - obp->b_wptr;
} else {
contpp = omp;
bytes_left = 0;
}
do {
while (ibp->b_rptr < ibp->b_wptr) {
if ((bytes_left < (int)tp->t_maxeuc)) {
NEW_BLOCK(0);
}
if ((tp->t_modes.c_lflag & XCASE) &&
(tp->t_modes.c_lflag & ICANON)) {
c = *ibp->b_rptr++;
if ((tp->t_state & TS_MEUC) &&
tp->t_eucign == 0 && NOTASCII(c)) {
tp->t_eucign =
tp->t_csmethods.ldterm_memwidth(
c, (void *)tp);
tp->t_scratch_len = tp->t_eucign;
if (tp->t_csdata.codeset_type !=
LDTERM_CS_TYPE_UTF8) {
tp->t_col +=
tp->
t_csmethods.
ldterm_dispwidth(c,
(void *)tp,
tp->t_modes.c_lflag &
ECHOCTL);
}
}
if (tp->t_eucign == 0 && omaptab[c] != 0 &&
(!echoing || c != '\\')) {
tp->t_col++;
*obp->b_wptr++ = '\\';
bytes_left--;
if (bytes_left == 0) {
NEW_BLOCK(1);
}
if (obp == NULL) {
ibp->b_rptr--;
tp->t_col--;
oobp->b_wptr--;
goto outofbufs;
}
c = omaptab[c];
}
if (!(tp->t_modes.c_oflag & OPOST)) {
if (tp->t_eucign > 0) {
--tp->t_eucign;
} else {
tp->t_col++;
}
*obp->b_wptr++ = c;
bytes_left--;
continue;
}
if (tp->t_eucign == 0 &&
(tp->t_modes.c_oflag & OLCUC) &&
c >= 'a' && c <= 'z')
c -= 'a' - 'A';
} else {
size_t bytes_to_move;
size_t bytes_moved;
ASSERT(ibp->b_wptr >= ibp->b_rptr);
bytes_to_move = ibp->b_wptr - ibp->b_rptr;
if (bytes_to_move > bytes_left)
bytes_to_move = bytes_left;
if (tp->t_state & TS_MEUC) {
bytes_moved = movtuc(bytes_to_move,
ibp->b_rptr, obp->b_wptr,
(tp->t_modes.c_oflag & OLCUC ?
elcuctab : enotrantab));
} else {
bytes_moved = movtuc(bytes_to_move,
ibp->b_rptr, obp->b_wptr,
(tp->t_modes.c_oflag & OLCUC ?
lcuctab : notrantab));
}
tp->t_col += bytes_moved;
ibp->b_rptr += bytes_moved;
obp->b_wptr += bytes_moved;
bytes_left -= bytes_moved;
if (ibp->b_rptr >= ibp->b_wptr)
continue;
if (bytes_left == 0) {
NEW_BLOCK(0);
}
c = *ibp->b_rptr++;
}
if ((tp->t_state & TS_MEUC) && tp->t_eucign == 0 &&
NOTASCII(c)) {
tp->t_eucign = tp->t_csmethods.ldterm_memwidth(
c, (void *)tp);
tp->t_scratch_len = tp->t_eucign;
if (tp->t_csdata.codeset_type !=
LDTERM_CS_TYPE_UTF8) {
tp->t_col +=
tp->t_csmethods.ldterm_dispwidth(c,
(void *)tp,
tp->t_modes.c_lflag & ECHOCTL);
}
}
if (c == '\r' && (tp->t_modes.c_oflag & OCRNL)) {
c = '\n';
ctype = typetab[c];
goto jocrnl;
}
if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_EUC) {
ctype = typetab[c];
} else {
if (tp->t_eucign == 0)
ctype = typetab[c];
else
ctype = ORDINARY;
}
if (c == '\n' && (tp->t_modes.c_oflag & ONLCR)) {
if (!(tp->t_state & TS_TTCR)) {
tp->t_state |= TS_TTCR;
c = '\r';
ctype = typetab['\r'];
--ibp->b_rptr;
} else
tp->t_state &= ~TS_TTCR;
}
jocrnl:
count = 0;
switch (ctype) {
case T_SS2:
case T_SS3:
case ORDINARY:
if (tp->t_state & TS_MEUC) {
if (tp->t_eucign) {
*obp->b_wptr++ = c;
bytes_left--;
tp->t_scratch[tp->t_scratch_len
- tp->t_eucign] = c;
--tp->t_eucign;
if (tp->t_csdata.codeset_type
== LDTERM_CS_TYPE_UTF8 &&
tp->t_eucign <= 0) {
tp->t_col +=
ldterm_utf8_width(
tp->t_scratch,
tp->t_scratch_len);
}
} else {
if (tp->t_modes.c_oflag & OLCUC)
n = elcuctab[c];
else
n = enotrantab[c];
if (n)
c = n;
tp->t_col++;
*obp->b_wptr++ = c;
bytes_left--;
}
} else {
if (tp->t_modes.c_oflag & OLCUC)
n = lcuctab[c];
else
n = notrantab[c];
if (n)
c = n;
tp->t_col++;
*obp->b_wptr++ = c;
bytes_left--;
}
break;
case CONTROL:
*obp->b_wptr++ = c;
bytes_left--;
break;
case BACKSPACE:
if (tp->t_col)
tp->t_col--;
if (tp->t_modes.c_oflag & BSDLY) {
if (tp->t_modes.c_oflag & OFILL)
count = 1;
}
*obp->b_wptr++ = c;
bytes_left--;
break;
case NEWLINE:
if (tp->t_modes.c_oflag & ONLRET)
goto cr;
if ((tp->t_modes.c_oflag & NLDLY) == NL1)
count = 2;
*obp->b_wptr++ = c;
bytes_left--;
break;
case TAB:
if ((tp->t_modes.c_oflag & TABDLY) == XTABS) {
for (;;) {
*obp->b_wptr++ = ' ';
bytes_left--;
tp->t_col++;
if ((tp->t_col & 07) == 0)
break;
if (obp->b_wptr >=
obp->b_datap->db_lim) {
ibp->b_rptr--;
break;
}
}
} else {
tp->t_col |= 07;
tp->t_col++;
if (tp->t_modes.c_oflag & OFILL) {
if (tp->t_modes.c_oflag &
TABDLY)
count = 2;
} else {
switch (tp->t_modes.c_oflag &
TABDLY) {
case TAB2:
count = 6;
break;
case TAB1:
count = 1 + (tp->t_col |
~07);
if (count < 5)
count = 0;
break;
}
}
*obp->b_wptr++ = c;
bytes_left--;
}
break;
case VTAB:
if ((tp->t_modes.c_oflag & VTDLY) &&
!(tp->t_modes.c_oflag & OFILL))
count = 127;
*obp->b_wptr++ = c;
bytes_left--;
break;
case RETURN:
if (tp->t_col == 0 &&
(tp->t_modes.c_oflag & ONOCR))
break;
cr:
switch (tp->t_modes.c_oflag & CRDLY) {
case CR1:
if (tp->t_modes.c_oflag & OFILL)
count = 2;
else
count = tp->t_col % 2;
break;
case CR2:
if (tp->t_modes.c_oflag & OFILL)
count = 4;
else
count = 6;
break;
case CR3:
if (tp->t_modes.c_oflag & OFILL)
count = 0;
else
count = 9;
break;
}
tp->t_col = 0;
*obp->b_wptr++ = c;
bytes_left--;
break;
}
if (count != 0) {
if (tp->t_modes.c_oflag & OFILL) {
do {
if (bytes_left == 0) {
NEW_BLOCK(0);
}
if (tp->t_modes.c_oflag & OFDEL)
*obp->b_wptr++ = CDEL;
else
*obp->b_wptr++ = CNUL;
bytes_left--;
} while (--count != 0);
} else {
if ((tp->t_modes.c_lflag & FLUSHO) &&
(tp->t_modes.c_lflag & IEXTEN)) {
freemsg(*omp);
} else {
(void) drv_setparm(SYSOUTC,
msgdsize(*omp));
putnext(q, *omp);
if ((bp =
allocb(1, BPRI_MED)) !=
NULL) {
bp->b_datap->db_type =
M_DELAY;
*bp->b_wptr++ =
(uchar_t)count;
putnext(q, bp);
}
}
bytes_left = 0;
*omp = NULL;
contpp = omp;
}
}
}
cbp = ibp->b_cont;
freeb(ibp);
} while ((ibp = cbp) != NULL);
outofbufs:
return (ibp);
#undef NEW_BLOCK
}
#if !defined(__sparc)
int
movtuc(size_t size, unsigned char *from, unsigned char *origto,
unsigned char *table)
{
unsigned char *to = origto;
unsigned char c;
while (size != 0 && (c = table[*from++]) != 0) {
*to++ = c;
size--;
}
return (to - origto);
}
#endif
static void
ldterm_flush_output(uchar_t c, queue_t *q, ldtermstd_state_t *tp)
{
if (tp->t_modes.c_lflag & FLUSHO)
tp->t_modes.c_lflag &= ~FLUSHO;
else {
flushq(q, FLUSHDATA);
(void) putnextctl1(q, M_FLUSH, FLUSHW);
if ((tp->t_echomp = allocb(EBSIZE, BPRI_HI)) != NULL) {
(void) ldterm_echo(c, q, 1, tp);
if (tp->t_msglen != 0)
ldterm_reprint(q, EBSIZE, tp);
if (tp->t_echomp != NULL) {
putnext(q, tp->t_echomp);
tp->t_echomp = NULL;
}
}
tp->t_modes.c_lflag |= FLUSHO;
}
}
static void
ldterm_dosig(queue_t *q, int sig, uchar_t c, int mtype, int mode)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
int sndsig = 0;
if ((!(tp->t_modes.c_lflag & NOFLSH)) || (c == '\0')) {
if (mode) {
if (tp->t_state & TS_TTSTOP) {
sndsig = 1;
(void) putnextctl1(q, mtype, sig);
}
if (mode & FLUSHR) {
flushq(q, FLUSHDATA);
(void) putnextctl1(WR(q), M_FLUSH, mode);
if (tp->t_state & (TS_TBLOCK|TS_IFBLOCK)) {
(void) putnextctl(WR(q), M_STARTI);
tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
}
}
if (mode & FLUSHW) {
flushq(WR(q), FLUSHDATA);
tp->t_state |= TS_FLUSHWAIT;
(void) putnextctl1(q, M_FLUSH, FLUSHW);
if (tp->t_state & TS_TTSTOP) {
(void) putnextctl(WR(q), M_START);
tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
}
}
}
}
tp->t_state &= ~TS_QUOT;
if (sndsig == 0)
(void) putnextctl1(q, mtype, sig);
if (c != '\0') {
if ((tp->t_echomp = allocb(4, BPRI_HI)) != NULL) {
if (ldterm_echo(c, WR(q), 4, tp) > 0 ||
(tp->t_state & TS_ISPTSTTY))
putnext(WR(q), tp->t_echomp);
else
freemsg(tp->t_echomp);
tp->t_echomp = NULL;
}
}
}
static void
ldterm_do_ioctl(queue_t *q, mblk_t *mp)
{
ldtermstd_state_t *tp;
struct iocblk *iocp;
struct eucioc *euciocp;
ldterm_cs_data_user_t *csdp;
int i;
int locale_name_sz;
uchar_t maxbytelen;
uchar_t maxscreenlen;
int error;
iocp = (struct iocblk *)mp->b_rptr;
tp = (ldtermstd_state_t *)q->q_ptr;
switch (iocp->ioc_cmd) {
case TCSETS:
case TCSETSW:
case TCSETSF:
{
struct termios *cb;
struct termios oldmodes;
error = miocpullup(mp, sizeof (struct termios));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
cb = (struct termios *)mp->b_cont->b_rptr;
oldmodes = tp->t_amodes;
tp->t_amodes = *cb;
if ((tp->t_amodes.c_lflag & PENDIN) &&
(tp->t_modes.c_lflag & IEXTEN)) {
if (tp->t_message != NULL) {
tp->t_state |= TS_RESCAN;
qenable(RD(q));
}
tp->t_amodes.c_lflag &= ~PENDIN;
}
bcopy(tp->t_amodes.c_cc, tp->t_modes.c_cc, NCCS);
tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
ldterm_adjust_modes(tp);
if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
miocnak(q, mp, 0, EAGAIN);
return;
}
break;
}
case TCSETA:
case TCSETAW:
case TCSETAF:
{
struct termio *cb;
struct termios oldmodes;
error = miocpullup(mp, sizeof (struct termio));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
cb = (struct termio *)mp->b_cont->b_rptr;
oldmodes = tp->t_amodes;
tp->t_amodes.c_iflag =
(tp->t_amodes.c_iflag & 0xffff0000 | cb->c_iflag);
tp->t_amodes.c_oflag =
(tp->t_amodes.c_oflag & 0xffff0000 | cb->c_oflag);
tp->t_amodes.c_cflag =
(tp->t_amodes.c_cflag & 0xffff0000 | cb->c_cflag);
tp->t_amodes.c_lflag =
(tp->t_amodes.c_lflag & 0xffff0000 | cb->c_lflag);
bcopy(cb->c_cc, tp->t_modes.c_cc, NCC);
bcopy(cb->c_cc, tp->t_amodes.c_cc, NCC);
tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
ldterm_adjust_modes(tp);
if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
miocnak(q, mp, 0, EAGAIN);
return;
}
break;
}
case TCFLSH:
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
ASSERT(mp->b_datap != NULL);
if (*(int *)mp->b_cont->b_rptr == 0) {
ASSERT(mp->b_datap != NULL);
(void) putnextctl1(q, M_FLUSH, FLUSHR);
(void) putctl1(RD(q), M_FLUSH, FLUSHR);
} else if (*(int *)mp->b_cont->b_rptr == 1) {
flushq(q, FLUSHDATA);
ASSERT(mp->b_datap != NULL);
tp->t_state |= TS_FLUSHWAIT;
(void) putnextctl1(RD(q), M_FLUSH, FLUSHW);
(void) putnextctl1(q, M_FLUSH, FLUSHW);
} else if (*(int *)mp->b_cont->b_rptr == 2) {
flushq(q, FLUSHDATA);
ASSERT(mp->b_datap != NULL);
(void) putnextctl1(q, M_FLUSH, FLUSHRW);
tp->t_state |= TS_FLUSHWAIT;
(void) putnextctl1(RD(q), M_FLUSH, FLUSHRW);
} else {
miocnak(q, mp, 0, EINVAL);
return;
}
ASSERT(mp->b_datap != NULL);
iocp->ioc_rval = 0;
miocack(q, mp, 0, 0);
return;
case TCXONC:
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
switch (*(int *)mp->b_cont->b_rptr) {
case 0:
if (!(tp->t_state & TS_TTSTOP)) {
(void) putnextctl(q, M_STOP);
tp->t_state |= (TS_TTSTOP|TS_OFBLOCK);
}
break;
case 1:
if (tp->t_state & TS_TTSTOP) {
(void) putnextctl(q, M_START);
tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
}
break;
case 2:
(void) putnextctl(q, M_STOPI);
tp->t_state |= (TS_TBLOCK|TS_IFBLOCK);
break;
case 3:
(void) putnextctl(q, M_STARTI);
tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
break;
default:
miocnak(q, mp, 0, EINVAL);
return;
}
ASSERT(mp->b_datap != NULL);
iocp->ioc_rval = 0;
miocack(q, mp, 0, 0);
return;
case EUC_WSET:
{
struct iocblk *riocp;
mblk_t *dmp, *dmp_cont;
error = miocpullup(mp, sizeof (struct eucioc));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
euciocp = (struct eucioc *)mp->b_cont->b_rptr;
for (i = 0; i < 4; i++) {
if ((euciocp->eucw[i] > EUC_MAXW) ||
(euciocp->scrw[i] > EUC_MAXW)) {
miocnak(q, mp, 0, ERANGE);
return;
}
}
cp_eucwioc(euciocp, &tp->eucwioc, EUCIN);
tp->eucwioc.eucw[0] = tp->eucwioc.scrw[0] = 1;
tp->t_maxeuc = 0;
tp->t_state &= ~TS_MEUC;
for (i = 0; i < 4; i++) {
if (tp->eucwioc.eucw[i] > tp->t_maxeuc)
tp->t_maxeuc = tp->eucwioc.eucw[i];
if (tp->eucwioc.scrw[i] > 1)
tp->t_state |= TS_MEUC;
}
if ((tp->t_maxeuc > 1) || (tp->t_state & TS_MEUC)) {
if (!tp->t_eucp_mp) {
if ((tp->t_eucp_mp = allocb(_TTY_BUFSIZ,
BPRI_HI)) == NULL) {
tp->t_maxeuc = 1;
tp->t_state &= ~TS_MEUC;
cmn_err(CE_WARN,
"Can't allocate eucp_mp");
miocnak(q, mp, 0, ENOSR);
return;
}
if (tp->t_msglen) {
tp->t_eucp =
tp->t_eucp_mp->b_rptr;
for (i = tp->t_msglen; i; i--)
*tp->t_eucp++ = 1;
} else {
tp->t_eucp =
tp->t_eucp_mp->b_rptr;
}
}
tp->t_state |= TS_MEUC;
} else if (tp->t_eucp_mp) {
freemsg(tp->t_eucp_mp);
tp->t_eucp_mp = NULL;
tp->t_eucp = NULL;
}
bzero(&tp->t_csdata.eucpc_data,
(sizeof (ldterm_eucpc_data_t) *
LDTERM_CS_MAX_CODESETS));
tp->t_csdata.eucpc_data[0].byte_length =
tp->eucwioc.eucw[1];
tp->t_csdata.eucpc_data[0].screen_width =
tp->eucwioc.scrw[1];
tp->t_csdata.eucpc_data[1].byte_length =
tp->eucwioc.eucw[2];
tp->t_csdata.eucpc_data[1].screen_width =
tp->eucwioc.scrw[2];
tp->t_csdata.eucpc_data[2].byte_length =
tp->eucwioc.eucw[3];
tp->t_csdata.eucpc_data[2].screen_width =
tp->eucwioc.scrw[3];
tp->t_csdata.version = LDTERM_DATA_VERSION;
tp->t_csdata.codeset_type = LDTERM_CS_TYPE_EUC;
tp->t_csdata.csinfo_num =
LDTERM_CS_TYPE_EUC_MAX_SUBCS;
if (tp->t_csdata.locale_name != (char *)NULL) {
kmem_free(tp->t_csdata.locale_name,
strlen(tp->t_csdata.locale_name) + 1);
tp->t_csdata.locale_name = (char *)NULL;
}
tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
if ((dmp = mkiocb(EUC_WSET)) == NULL) {
miocnak(q, mp, 0, ENOSR);
return;
}
if ((dmp_cont = allocb(EUCSIZE, BPRI_HI)) == NULL) {
freemsg(dmp);
miocnak(q, mp, 0, ENOSR);
return;
}
bcopy(mp->b_cont->b_rptr, dmp_cont->b_rptr, EUCSIZE);
dmp_cont->b_wptr += EUCSIZE;
dmp->b_cont = dmp_cont;
dmp->b_datap->db_type = M_CTL;
dmp_cont->b_datap->db_type = M_DATA;
riocp = (struct iocblk *)dmp->b_rptr;
riocp->ioc_count = EUCSIZE;
putnext(q, dmp);
iocp->ioc_rval = 0;
miocack(q, mp, 0, 0);
return;
}
case EUC_WGET:
error = miocpullup(mp, sizeof (struct eucioc));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
euciocp = (struct eucioc *)mp->b_cont->b_rptr;
cp_eucwioc(&tp->eucwioc, euciocp, EUCOUT);
iocp->ioc_rval = 0;
miocack(q, mp, EUCSIZE, 0);
return;
case CSDATA_SET:
error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
if (csdp->version > LDTERM_DATA_VERSION ||
csdp->codeset_type < LDTERM_CS_TYPE_MIN ||
csdp->codeset_type > LDTERM_CS_TYPE_MAX) {
miocnak(q, mp, 0, ERANGE);
return;
}
if ((csdp->codeset_type == LDTERM_CS_TYPE_EUC &&
csdp->csinfo_num > LDTERM_CS_TYPE_EUC_MAX_SUBCS) ||
(csdp->codeset_type == LDTERM_CS_TYPE_PCCS &&
(csdp->csinfo_num < LDTERM_CS_TYPE_PCCS_MIN_SUBCS ||
csdp->csinfo_num > LDTERM_CS_TYPE_PCCS_MAX_SUBCS))) {
miocnak(q, mp, 0, ERANGE);
return;
}
maxbytelen = maxscreenlen = 0;
if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
for (i = 0; i < LDTERM_CS_TYPE_EUC_MAX_SUBCS; i++) {
if (csdp->eucpc_data[i].byte_length >
EUC_MAXW ||
csdp->eucpc_data[i].screen_width >
EUC_MAXW) {
miocnak(q, mp, 0, ERANGE);
return;
}
if (csdp->eucpc_data[i].byte_length >
maxbytelen)
maxbytelen =
csdp->eucpc_data[i].byte_length;
if (csdp->eucpc_data[i].screen_width >
maxscreenlen)
maxscreenlen =
csdp->eucpc_data[i].screen_width;
}
if (maxbytelen == 0 && maxscreenlen == 0)
maxbytelen = maxscreenlen = 1;
} else if (csdp->codeset_type == LDTERM_CS_TYPE_PCCS) {
for (i = 0; i < LDTERM_CS_MAX_CODESETS; i++) {
if (csdp->eucpc_data[i].byte_length >
LDTERM_CS_MAX_BYTE_LENGTH) {
miocnak(q, mp, 0, ERANGE);
return;
}
if (csdp->eucpc_data[i].byte_length >
maxbytelen)
maxbytelen =
csdp->eucpc_data[i].byte_length;
if (csdp->eucpc_data[i].screen_width >
maxscreenlen)
maxscreenlen =
csdp->eucpc_data[i].screen_width;
}
} else if (csdp->codeset_type == LDTERM_CS_TYPE_UTF8) {
maxbytelen = 4;
maxscreenlen = 2;
}
locale_name_sz = 0;
for (i = 0; i < MAXNAMELEN; i++)
if (csdp->locale_name[i] == '\0')
break;
if (i >= MAXNAMELEN) {
miocnak(q, mp, 0, ERANGE);
return;
}
locale_name_sz = i + 1;
if (maxbytelen <= 0 || maxscreenlen <= 0) {
miocnak(q, mp, 0, ERANGE);
return;
}
tp->t_maxeuc = maxbytelen;
tp->t_state &= ~TS_MEUC;
if (maxbytelen > 1 || maxscreenlen > 1) {
if (!tp->t_eucp_mp) {
if (!(tp->t_eucp_mp = allocb(_TTY_BUFSIZ,
BPRI_HI))) {
cmn_err(CE_WARN,
"Can't allocate eucp_mp");
miocnak(q, mp, 0, ENOSR);
return;
}
if (tp->t_msglen) {
tp->t_eucp = tp->t_eucp_mp->b_rptr;
for (i = tp->t_msglen; i; i--)
*tp->t_eucp++ = 1;
} else {
tp->t_eucp = tp->t_eucp_mp->b_rptr;
}
}
tp->t_state |= TS_MEUC;
tp->t_csdata.version = csdp->version;
tp->t_csdata.codeset_type = csdp->codeset_type;
tp->t_csdata.csinfo_num = csdp->csinfo_num;
bcopy(csdp->eucpc_data, tp->t_csdata.eucpc_data,
sizeof (ldterm_eucpc_data_t) *
LDTERM_CS_MAX_CODESETS);
tp->t_csmethods = cs_methods[csdp->codeset_type];
if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
tp->eucwioc.eucw[0] = 1;
tp->eucwioc.scrw[0] = 1;
tp->eucwioc.eucw[1] =
csdp->eucpc_data[0].byte_length;
tp->eucwioc.scrw[1] =
csdp->eucpc_data[0].screen_width;
tp->eucwioc.eucw[2] =
csdp->eucpc_data[1].byte_length + 1;
tp->eucwioc.scrw[2] =
csdp->eucpc_data[1].screen_width;
tp->eucwioc.eucw[3] =
csdp->eucpc_data[2].byte_length + 1;
tp->eucwioc.scrw[3] =
csdp->eucpc_data[2].screen_width;
} else {
bzero(&tp->eucwioc, EUCSIZE);
}
} else {
if (tp->t_eucp_mp) {
freemsg(tp->t_eucp_mp);
tp->t_eucp_mp = NULL;
tp->t_eucp = NULL;
}
bzero(&tp->eucwioc, EUCSIZE);
tp->eucwioc.eucw[0] = 1;
tp->eucwioc.scrw[0] = 1;
if (tp->t_csdata.locale_name != (char *)NULL) {
kmem_free(tp->t_csdata.locale_name,
strlen(tp->t_csdata.locale_name) + 1);
}
tp->t_csdata = default_cs_data;
tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
}
if (tp->t_csdata.locale_name != (char *)NULL) {
kmem_free(tp->t_csdata.locale_name,
strlen(tp->t_csdata.locale_name) + 1);
}
if (locale_name_sz > 1) {
tp->t_csdata.locale_name = (char *)kmem_alloc(
locale_name_sz, KM_SLEEP);
(void) strcpy(tp->t_csdata.locale_name,
csdp->locale_name);
} else {
tp->t_csdata.locale_name = (char *)NULL;
}
iocp->ioc_rval = 0;
miocack(q, mp, 0, 0);
return;
case CSDATA_GET:
error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
csdp->version = tp->t_csdata.version;
csdp->codeset_type = tp->t_csdata.codeset_type;
csdp->csinfo_num = tp->t_csdata.csinfo_num;
csdp->pad = tp->t_csdata.pad;
if (tp->t_csdata.locale_name) {
(void) strcpy(csdp->locale_name,
tp->t_csdata.locale_name);
} else {
csdp->locale_name[0] = '\0';
}
bcopy(tp->t_csdata.eucpc_data, csdp->eucpc_data,
sizeof (ldterm_eucpc_data_t) * LDTERM_CS_MAX_CODESETS);
if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
if (csdp->eucpc_data[1].byte_length)
csdp->eucpc_data[1].byte_length -= 1;
if (csdp->eucpc_data[2].byte_length)
csdp->eucpc_data[2].byte_length -= 1;
}
iocp->ioc_rval = 0;
miocack(q, mp, sizeof (ldterm_cs_data_user_t), 0);
return;
case PTSSTTY:
tp->t_state |= TS_ISPTSTTY;
break;
}
putnext(q, mp);
}
static int
chgstropts(struct termios *oldmodep, ldtermstd_state_t *tp, queue_t *q)
{
struct stroptions optbuf;
mblk_t *bp;
optbuf.so_flags = 0;
if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & ICANON) {
if (tp->t_modes.c_lflag & ICANON) {
DEBUG4(("CHANGING TO CANON MODE\n"));
optbuf.so_flags = SO_READOPT|SO_MREADOFF;
optbuf.so_readopt = RMSGN;
vmin_satisfied(q, tp, 0);
} else {
DEBUG4(("CHANGING TO RAW MODE\n"));
optbuf.so_flags = SO_READOPT|SO_MREADON;
optbuf.so_readopt = RNORM;
}
}
if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & TOSTOP) {
if (tp->t_modes.c_lflag & TOSTOP)
optbuf.so_flags |= SO_TOSTOP;
else
optbuf.so_flags |= SO_TONSTOP;
}
if (optbuf.so_flags != 0) {
if ((bp = allocb(sizeof (struct stroptions), BPRI_HI)) ==
NULL) {
return (-1);
}
*(struct stroptions *)bp->b_wptr = optbuf;
bp->b_wptr += sizeof (struct stroptions);
bp->b_datap->db_type = M_SETOPTS;
DEBUG4(("M_SETOPTS to stream head\n"));
putnext(q, bp);
}
return (0);
}
static void
ldterm_ioctl_reply(queue_t *q, mblk_t *mp)
{
ldtermstd_state_t *tp;
struct iocblk *iocp;
iocp = (struct iocblk *)mp->b_rptr;
tp = (ldtermstd_state_t *)q->q_ptr;
switch (iocp->ioc_cmd) {
case TCGETS:
{
struct termios *cb =
(struct termios *)mp->b_cont->b_rptr;
tcflag_t cflag = cb->c_cflag;
*cb = tp->t_amodes;
if (cflag != 0)
cb->c_cflag = cflag;
break;
}
case TCGETA:
{
struct termio *cb =
(struct termio *)mp->b_cont->b_rptr;
cb->c_iflag = tp->t_amodes.c_iflag;
cb->c_oflag = tp->t_amodes.c_oflag;
cb->c_lflag = tp->t_amodes.c_lflag;
if (cb->c_cflag == 0)
cb->c_cflag = tp->t_amodes.c_cflag;
cb->c_line = 0;
bcopy(tp->t_amodes.c_cc, cb->c_cc, NCC);
break;
}
}
putnext(q, mp);
}
static void
vmin_satisfied(queue_t *q, ldtermstd_state_t *tp, int sendup)
{
ASSERT(q);
if (tp->t_vtid != 0) {
DEBUG4(("vmin_satisfied: cancelled timer id %d\n", tp->t_vtid));
(void) quntimeout(q, tp->t_vtid);
tp->t_vtid = 0;
}
if (sendup) {
if (tp->t_msglen == 0 && V_MIN) {
DEBUG4(("vmin_satisfied: data swiped, msglen = 0\n"));
} else {
if ((!q->q_first) ||
(q->q_first->b_datap->db_type != M_DATA) ||
(tp->t_msglen >= LDCHUNK)) {
ldterm_msg_upstream(q, tp);
DEBUG4(("vmin_satisfied: delivering data\n"));
}
}
} else {
DEBUG4(("vmin_satisfied: VMIN/TIME state reset\n"));
}
tp->t_state &= ~TS_MREAD;
}
static void
vmin_settimer(queue_t *q)
{
ldtermstd_state_t *tp;
tp = (ldtermstd_state_t *)q->q_ptr;
if (tp->t_state & TS_CLOSE)
return;
if (tp->t_vtid) {
if (V_MIN && V_TIME) {
DEBUG4(("vmin_settimer: timer restarted, old tid=%d\n",
tp->t_vtid));
} else {
DEBUG4(("vmin_settimer: tid = %d was still active!\n",
tp->t_vtid));
}
(void) quntimeout(q, tp->t_vtid);
tp->t_vtid = 0;
}
tp->t_vtid = qtimeout(q, vmin_timed_out, q,
(clock_t)(V_TIME * (hz / 10)));
DEBUG4(("vmin_settimer: timer started, tid = %d\n", tp->t_vtid));
}
static void
vmin_timed_out(void *arg)
{
queue_t *q = arg;
ldtermstd_state_t *tp;
tp = (ldtermstd_state_t *)q->q_ptr;
DEBUG4(("vmin_timed_out: tid = %d\n", tp->t_vtid));
tp->t_vtid = 0;
vmin_satisfied(q, tp, 1);
}
static void
ldterm_adjust_modes(ldtermstd_state_t *tp)
{
DEBUG6(("original iflag = %o\n", tp->t_modes.c_iflag));
tp->t_modes.c_iflag = tp->t_amodes.c_iflag & ~(tp->t_dmodes.c_iflag);
tp->t_modes.c_oflag = tp->t_amodes.c_oflag & ~(tp->t_dmodes.c_oflag);
tp->t_modes.c_lflag = tp->t_amodes.c_lflag & ~(tp->t_dmodes.c_lflag);
DEBUG6(("driver iflag = %o\n", tp->t_dmodes.c_iflag));
DEBUG6(("apparent iflag = %o\n", tp->t_amodes.c_iflag));
DEBUG6(("effective iflag = %o\n", tp->t_modes.c_iflag));
}
static void
ldterm_csi_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
{
int i, ung;
uchar_t *p, *bottom;
uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
int c;
int j;
int len;
if (tp->t_eucleft) {
ldterm_eucwarn(tp);
return;
}
bottom = tp->t_eucp_mp->b_rptr;
p = tp->t_eucp - 1;
if (p < bottom)
return;
ung = 1;
while ((*p == 0) && (p > bottom)) {
p--;
++ung;
}
if (ung >= LDTERM_CS_MAX_BYTE_LENGTH) {
j = len = LDTERM_CS_MAX_BYTE_LENGTH;
} else {
j = len = ung;
}
for (i = 0; i < ung; i++) {
if ((c = ldterm_unget(tp)) != (-1)) {
ldterm_trim(tp);
if (j > 0)
u8[--j] = (uchar_t)c;
}
}
if (*p == UNKNOWN_WIDTH) {
if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_UTF8) {
*p = ldterm_utf8_width(u8, len);
} else {
*p = 1;
}
}
for (i = 0; i < (int)*p; i++)
ldterm_rubout(' ', q, ebsize, tp);
tp->t_eucp = p;
*p = 0;
}
static void
ldterm_eucwarn(ldtermstd_state_t *tp)
{
++tp->t_eucwarn;
#ifdef DEBUG
if ((tp->t_eucwarn > EUC_WARNCNT) && !(tp->t_state & TS_WARNED)) {
cmn_err(CE_WARN,
"ldterm: tty at addr %p in multi-byte mode --",
(void *)tp);
cmn_err(CE_WARN,
"Over %d bad EUC characters this session", EUC_WARNCNT);
tp->t_state |= TS_WARNED;
}
#endif
}
static void
cp_eucwioc(eucioc_t *from, eucioc_t *to, int dir)
{
bcopy(from, to, EUCSIZE);
if (dir == EUCOUT) {
if (to->eucw[2])
--to->eucw[2];
if (to->eucw[3])
--to->eucw[3];
} else {
if (to->eucw[2])
++to->eucw[2];
if (to->eucw[3])
++to->eucw[3];
}
}
static int
ldterm_codeset(uchar_t codeset_type, uchar_t c)
{
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);
}
}
static int
__ldterm_dispwidth_euc(uchar_t c, void *p, int mode)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
return (mode ? 2 : EUC_BSWIDTH);
case '\n':
return (EUC_NLWIDTH);
case '\r':
return (mode ? 2 : EUC_CRWIDTH);
default:
return (mode ? 2 : 0);
}
}
return (1);
}
switch (c) {
case SS2:
return (tp->eucwioc.scrw[2]);
case SS3:
return (tp->eucwioc.scrw[3]);
default:
return (tp->eucwioc.scrw[1]);
}
}
static int
__ldterm_memwidth_euc(uchar_t c, void *p)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
if (ISASCII(c))
return (1);
switch (c) {
case SS2:
return (tp->eucwioc.eucw[2]);
case SS3:
return (tp->eucwioc.eucw[3]);
default:
return (tp->eucwioc.eucw[1]);
}
}
static int
__ldterm_dispwidth_pccs(uchar_t c, void *p, int mode)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
int i;
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
return (mode ? 2 : EUC_BSWIDTH);
case '\n':
return (EUC_NLWIDTH);
case '\r':
return (mode ? 2 : EUC_CRWIDTH);
default:
return (mode ? 2 : 0);
}
}
return (1);
}
for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
c <= tp->t_csdata.eucpc_data[i].msb_end)
return (tp->t_csdata.eucpc_data[i].screen_width);
}
return (1);
}
static int
__ldterm_memwidth_pccs(uchar_t c, void *p)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
int i;
for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
c <= tp->t_csdata.eucpc_data[i].msb_end)
return (tp->t_csdata.eucpc_data[i].byte_length);
}
return (1);
}
static int
__ldterm_dispwidth_utf8(uchar_t c, void *p, int mode)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
if (ISASCII(c)) {
if (c <= '\037') {
switch (c) {
case '\t':
return (EUC_TWIDTH);
case '\b':
return (mode ? 2 : EUC_BSWIDTH);
case '\n':
return (EUC_NLWIDTH);
case '\r':
return (mode ? 2 : EUC_CRWIDTH);
default:
return (mode ? 2 : 0);
}
}
return (1);
}
if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
return (1);
if (c >= (uchar_t)0xc0 && c <= (uchar_t)0xfd)
return (UNKNOWN_WIDTH);
return (1);
}
static int
__ldterm_memwidth_utf8(uchar_t c, void *p)
{
ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
int len;
if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
return (1);
len = u8_number_of_bytes[c];
return ((len <= 0) ? 1 : len);
}
static uchar_t
ldterm_utf8_width(uchar_t *u8, int length)
{
int i;
int j;
uint_t intcode = 0;
if (length == 0)
return ('\0');
j = u8_number_of_bytes[u8[0]] - 1;
if (length > 4 || j <= 0)
return ('\1');
intcode = u8[0] & u8_masks_tbl[j];
for (i = 1; j > 0; j--, i++) {
if (i == 1) {
if (u8[i] < u8_valid_min_2nd_byte[u8[0]] ||
u8[i] > u8_valid_max_2nd_byte[u8[0]])
return ('\1');
} else if (u8[i] < (uchar_t)LDTERM_CS_TYPE_UTF8_MIN_BYTE ||
u8[i] > (uchar_t)LDTERM_CS_TYPE_UTF8_MAX_BYTE)
return ('\1');
intcode = (intcode << LDTERM_CS_TYPE_UTF8_SHIFT_BITS) |
(u8[i] & LDTERM_CS_TYPE_UTF8_BIT_MASK);
}
i = 0;
if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P00) {
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) {
intcode = intcode & (uint_t)0xffff;
i = intcode / 4;
j = intcode % 4;
switch (j) {
case 0:
i = ldterm_ucode[1][i].u0;
break;
case 1:
i = ldterm_ucode[1][i].u1;
break;
case 2:
i = ldterm_ucode[1][i].u2;
break;
case 3:
i = ldterm_ucode[1][i].u3;
break;
}
} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKEXTB &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKEXTB) ||
(intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKCOMP &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKCOMP) ||
(intcode >= LDTERM_CS_TYPE_UTF8_MIN_P15 &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_P15) ||
(intcode >= LDTERM_CS_TYPE_UTF8_MIN_P16 &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_P16)) {
return ('\2');
} else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_P14 &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_P14) ||
(intcode >= LDTERM_CS_TYPE_UTF8_MIN_VARSEL &&
intcode <= LDTERM_CS_TYPE_UTF8_MAX_VARSEL)) {
return ('\0');
}
return ((i == 0) ? '\1' : (uchar_t)i);
}