#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <mdb/mdb_types.h>
#include <mdb/mdb_cmdbuf.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_signal.h>
#include <mdb/mdb_callb.h>
#include <mdb/mdb_stdlib.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_tab.h>
#include <mdb/mdb.h>
#ifdef ERR
#undef ERR
#endif
#include <curses.h>
#define KEY_ESC (0x01b)
#define KEY_DEL (0x07f)
#define META(c) (((c) & 0x7f) | 0x80)
#define KPAD(c) (((c) < 'A' || (c) > 'Z') ? 0 : ((c) - 'A' + 0x100))
#define FKEY(c) (((c) < '0' || (c) > '9') ? 0 : ((c) - '0' + 0x11a))
#define TI_DECSET(Pm) "\033[?" Pm "h"
#define TI_DECRST(Pm) "\033[?" Pm "l"
#define TI_DECSAV(Pm) "\033[?" Pm "s"
#define TI_DECRES(Pm) "\033[?" Pm "r"
#define TI_DECCOLM "3"
#define TI_COLENAB "40"
#define TIO_DEFAULT_ROWS 24
#define TIO_DEFAULT_COLS 80
typedef union termio_attr_val {
const char *at_str;
int at_val;
} termio_attr_val_t;
typedef struct termio_info {
termio_attr_val_t ti_cub1;
termio_attr_val_t ti_cuf1;
termio_attr_val_t ti_cuu1;
termio_attr_val_t ti_cud1;
termio_attr_val_t ti_pad;
termio_attr_val_t ti_el;
termio_attr_val_t ti_am;
termio_attr_val_t ti_bw;
termio_attr_val_t ti_npc;
termio_attr_val_t ti_xenl;
termio_attr_val_t ti_xon;
termio_attr_val_t ti_cols;
termio_attr_val_t ti_lines;
termio_attr_val_t ti_pb;
termio_attr_val_t ti_smso;
termio_attr_val_t ti_rmso;
termio_attr_val_t ti_smul;
termio_attr_val_t ti_rmul;
termio_attr_val_t ti_enacs;
termio_attr_val_t ti_smacs;
termio_attr_val_t ti_rmacs;
termio_attr_val_t ti_smcup;
termio_attr_val_t ti_rmcup;
termio_attr_val_t ti_rev;
termio_attr_val_t ti_bold;
termio_attr_val_t ti_dim;
termio_attr_val_t ti_sgr0;
termio_attr_val_t ti_smir;
termio_attr_val_t ti_rmir;
termio_attr_val_t ti_ich1;
termio_attr_val_t ti_ip;
termio_attr_val_t ti_clear;
termio_attr_val_t ti_cnorm;
termio_attr_val_t ti_nel;
termio_attr_val_t ti_cr;
termio_attr_val_t ti_smam;
} termio_info_t;
typedef enum {
TIO_ATTR_REQSTR,
TIO_ATTR_STR,
TIO_ATTR_BOOL,
TIO_ATTR_INT
} termio_attr_type_t;
typedef struct termio_attr {
const char *ta_name;
termio_attr_type_t ta_type;
termio_attr_val_t *ta_valp;
} termio_attr_t;
struct termio_data;
typedef const char *(*keycb_t)(struct termio_data *, int);
typedef void (*putp_t)(struct termio_data *, const char *, uint_t);
#define TIO_FINDHIST 0x01
#define TIO_AUTOWRAP 0x02
#define TIO_BACKLEFT 0x04
#define TIO_INSERT 0x08
#define TIO_USECUP 0x10
#define TIO_TTYWARN 0x20
#define TIO_CAPWARN 0x40
#define TIO_XTERM 0x80
#define TIO_TAB 0x100
#define TIO_LAZYWRAP 0x200
static const mdb_bitmask_t tio_flag_masks[] = {
{ "FINDHIST", TIO_FINDHIST, TIO_FINDHIST },
{ "AUTOWRAP", TIO_AUTOWRAP, TIO_AUTOWRAP },
{ "BACKLEFT", TIO_BACKLEFT, TIO_BACKLEFT },
{ "INSERT", TIO_INSERT, TIO_INSERT },
{ "USECUP", TIO_USECUP, TIO_USECUP },
{ "TTYWARN", TIO_TTYWARN, TIO_TTYWARN },
{ "CAPWARN", TIO_CAPWARN, TIO_CAPWARN },
{ "XTERM", TIO_XTERM, TIO_XTERM },
{ "TAB", TIO_TAB, TIO_TAB },
{ "LAZYWRAP", TIO_LAZYWRAP, TIO_LAZYWRAP },
{ NULL, 0, 0 }
};
typedef struct termio_data {
mdb_io_t *tio_io;
mdb_io_t *tio_out_io;
mdb_io_t *tio_in_io;
mdb_iob_t *tio_out;
mdb_iob_t *tio_in;
mdb_iob_t *tio_link;
keycb_t tio_keymap[KEY_MAX];
mdb_cmdbuf_t tio_cmdbuf;
struct termios tio_ptios;
struct termios tio_ctios;
struct termios tio_rtios;
struct termios tio_dtios;
sigjmp_buf tio_env;
termio_info_t tio_info;
char *tio_attrs;
size_t tio_attrslen;
const char *tio_prompt;
size_t tio_promptlen;
size_t tio_rows;
size_t tio_cols;
size_t tio_x;
size_t tio_y;
size_t tio_max_x;
size_t tio_max_y;
int tio_intr;
int tio_quit;
int tio_erase;
int tio_werase;
int tio_kill;
int tio_eof;
int tio_susp;
uint_t tio_flags;
volatile mdb_bool_t tio_active;
volatile mdb_bool_t tio_rti_on;
putp_t tio_putp;
uint_t tio_baud;
uint_t tio_usecpc;
pid_t tio_opgid;
uint_t tio_suspended;
} termio_data_t;
static ssize_t termio_read(mdb_io_t *, void *, size_t);
static ssize_t termio_write(mdb_io_t *, const void *, size_t);
static off64_t termio_seek(mdb_io_t *, off64_t, int);
static int termio_ctl(mdb_io_t *, int, void *);
static void termio_close(mdb_io_t *);
static const char *termio_name(mdb_io_t *);
static void termio_link(mdb_io_t *, mdb_iob_t *);
static void termio_unlink(mdb_io_t *, mdb_iob_t *);
static int termio_setattr(mdb_io_t *, int, uint_t);
static void termio_suspend(mdb_io_t *);
static void termio_resume(mdb_io_t *);
static void termio_suspend_tty(termio_data_t *, struct termios *);
static void termio_resume_tty(termio_data_t *, struct termios *);
static void termio_putp(termio_data_t *, const char *, uint_t);
static void termio_puts(termio_data_t *, const char *, uint_t);
static void termio_tput(termio_data_t *, const char *, uint_t);
static void termio_addch(termio_data_t *, char, size_t);
static void termio_insch(termio_data_t *, char, size_t);
static void termio_mvcur(termio_data_t *);
static void termio_bspch(termio_data_t *);
static void termio_delch(termio_data_t *);
static void termio_clear(termio_data_t *);
static void termio_redraw(termio_data_t *);
static void termio_prompt(termio_data_t *);
static const char *termio_tab(termio_data_t *, int);
static const char *termio_insert(termio_data_t *, int);
static const char *termio_accept(termio_data_t *, int);
static const char *termio_backspace(termio_data_t *, int);
static const char *termio_delchar(termio_data_t *, int);
static const char *termio_fwdchar(termio_data_t *, int);
static const char *termio_backchar(termio_data_t *, int);
static const char *termio_transpose(termio_data_t *, int);
static const char *termio_home(termio_data_t *, int);
static const char *termio_end(termio_data_t *, int);
static const char *termio_fwdword(termio_data_t *, int);
static const char *termio_backword(termio_data_t *, int);
static const char *termio_kill(termio_data_t *, int);
static const char *termio_killfwdword(termio_data_t *, int);
static const char *termio_killbackword(termio_data_t *, int);
static const char *termio_reset(termio_data_t *, int);
static const char *termio_widescreen(termio_data_t *, int);
static const char *termio_prevhist(termio_data_t *, int);
static const char *termio_nexthist(termio_data_t *, int);
static const char *termio_accel(termio_data_t *, int);
static const char *termio_findhist(termio_data_t *, int);
static const char *termio_refresh(termio_data_t *, int);
static const char *termio_intr(termio_data_t *, int);
static const char *termio_quit(termio_data_t *, int);
static const char *termio_susp(termio_data_t *, int);
static void termio_winch(int, siginfo_t *, ucontext_t *, void *);
static void termio_tstp(int, siginfo_t *, ucontext_t *, void *);
extern const char *tigetstr(const char *);
extern int tigetflag(const char *);
extern int tigetnum(const char *);
static const mdb_io_ops_t termio_ops = {
.io_read = termio_read,
.io_write = termio_write,
.io_seek = termio_seek,
.io_ctl = termio_ctl,
.io_close = termio_close,
.io_name = termio_name,
.io_link = termio_link,
.io_unlink = termio_unlink,
.io_setattr = termio_setattr,
.io_suspend = termio_suspend,
.io_resume = termio_resume,
};
static termio_info_t termio_info;
static const termio_attr_t termio_attrs[] = {
{ "cub1", TIO_ATTR_REQSTR, &termio_info.ti_cub1 },
{ "cuf1", TIO_ATTR_REQSTR, &termio_info.ti_cuf1 },
{ "cuu1", TIO_ATTR_REQSTR, &termio_info.ti_cuu1 },
{ "cud1", TIO_ATTR_REQSTR, &termio_info.ti_cud1 },
{ "pad", TIO_ATTR_STR, &termio_info.ti_pad },
{ "el", TIO_ATTR_REQSTR, &termio_info.ti_el },
{ "am", TIO_ATTR_BOOL, &termio_info.ti_am },
{ "bw", TIO_ATTR_BOOL, &termio_info.ti_bw },
{ "npc", TIO_ATTR_BOOL, &termio_info.ti_npc },
{ "xenl", TIO_ATTR_BOOL, &termio_info.ti_xenl },
{ "xon", TIO_ATTR_BOOL, &termio_info.ti_xon },
{ "cols", TIO_ATTR_INT, &termio_info.ti_cols },
{ "lines", TIO_ATTR_INT, &termio_info.ti_lines },
{ "pb", TIO_ATTR_INT, &termio_info.ti_pb },
{ "smso", TIO_ATTR_STR, &termio_info.ti_smso },
{ "rmso", TIO_ATTR_STR, &termio_info.ti_rmso },
{ "smul", TIO_ATTR_STR, &termio_info.ti_smul },
{ "rmul", TIO_ATTR_STR, &termio_info.ti_rmul },
{ "enacs", TIO_ATTR_STR, &termio_info.ti_enacs },
{ "smacs", TIO_ATTR_STR, &termio_info.ti_smacs },
{ "rmacs", TIO_ATTR_STR, &termio_info.ti_rmacs },
{ "smcup", TIO_ATTR_STR, &termio_info.ti_smcup },
{ "rmcup", TIO_ATTR_STR, &termio_info.ti_rmcup },
{ "rev", TIO_ATTR_STR, &termio_info.ti_rev },
{ "bold", TIO_ATTR_STR, &termio_info.ti_bold },
{ "dim", TIO_ATTR_STR, &termio_info.ti_dim },
{ "sgr0", TIO_ATTR_STR, &termio_info.ti_sgr0 },
{ "smir", TIO_ATTR_STR, &termio_info.ti_smir },
{ "rmir", TIO_ATTR_STR, &termio_info.ti_rmir },
{ "ich1", TIO_ATTR_STR, &termio_info.ti_ich1 },
{ "ip", TIO_ATTR_STR, &termio_info.ti_ip },
{ "clear", TIO_ATTR_STR, &termio_info.ti_clear },
{ "cnorm", TIO_ATTR_STR, &termio_info.ti_cnorm },
{ "nel", TIO_ATTR_STR, &termio_info.ti_nel },
{ "cr", TIO_ATTR_STR, &termio_info.ti_cr },
{ "smam", TIO_ATTR_STR, &termio_info.ti_smam },
{ NULL, 0, NULL }
};
static const char *const termio_accelkeys = "[]";
static const char *const termio_accelstrings[] = {
"::step over",
"::step"
};
static const char *
termio_accel_lookup(int c)
{
const char *acc;
if ((acc = strchr(termio_accelkeys, c)) == NULL)
return (NULL);
return (termio_accelstrings[(int)(acc - termio_accelkeys)]);
}
static ssize_t
termio_read(mdb_io_t *io, void *buf, size_t nbytes)
{
termio_data_t *td = io->io_data;
mdb_bool_t esc = FALSE, pad = FALSE;
ssize_t rlen = 0;
int c, fkey = 0;
const char *s;
size_t len;
if (io->io_next != NULL)
return (IOP_READ(io->io_next, buf, nbytes));
td->tio_rti_on = TRUE;
if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1)
warn("failed to set terminal attributes");
if (nbytes == 1) {
if ((c = mdb_iob_getc(td->tio_in)) == EOF)
goto out;
*((uchar_t *)buf) = (uchar_t)c;
rlen = 1;
goto out;
}
if (td->tio_flags & TIO_TAB)
termio_redraw(td);
else
termio_prompt(td);
if (sigsetjmp(td->tio_env, 1) != 0) {
td->tio_active = FALSE;
td->tio_x = td->tio_y = 0;
len = td->tio_cmdbuf.cmd_buflen + td->tio_promptlen;
td->tio_max_x = len % td->tio_cols;
td->tio_max_y = len / td->tio_cols;
esc = pad = FALSE;
termio_tput(td, td->tio_info.ti_cr.at_str, 1);
mdb_iob_flush(td->tio_out);
termio_redraw(td);
}
if (td->tio_link != NULL)
mdb_iob_resize(td->tio_link, td->tio_rows, td->tio_cols);
td->tio_active = TRUE;
td->tio_flags &= ~TIO_TAB;
do {
char_loop:
if ((c = mdb_iob_getc(td->tio_in)) == EOF) {
td->tio_active = FALSE;
goto out;
}
if (c == KEY_ESC && esc == FALSE) {
esc = TRUE;
goto char_loop;
}
if (esc) {
esc = FALSE;
if (c == '[') {
pad++;
goto char_loop;
}
c = META(c);
}
if (pad) {
pad = FALSE;
if ((fkey = FKEY(c)) != 0) {
goto char_loop;
}
if ((c = KPAD(c)) == 0) {
goto char_loop;
}
}
if (fkey != 0) {
if (c == '~') {
c = fkey;
}
fkey = 0;
}
len = td->tio_cmdbuf.cmd_buflen + td->tio_promptlen;
td->tio_max_x = len % td->tio_cols;
td->tio_max_y = len / td->tio_cols;
} while ((s = (*td->tio_keymap[c])(td, c)) == NULL);
td->tio_active = FALSE;
mdb_iob_nl(td->tio_out);
if ((rlen = strlen(s)) >= nbytes - 1)
rlen = nbytes - 1;
(void) strncpy(buf, s, rlen);
((char *)buf)[rlen++] = '\n';
out:
td->tio_rti_on = FALSE;
if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
warn("failed to restore terminal attributes");
return (rlen);
}
static ssize_t
termio_write(mdb_io_t *io, const void *buf, size_t nbytes)
{
termio_data_t *td = io->io_data;
if (io->io_next != NULL)
return (IOP_WRITE(io->io_next, buf, nbytes));
return (IOP_WRITE(td->tio_out_io, buf, nbytes));
}
static off64_t
termio_seek(mdb_io_t *io, off64_t offset, int whence)
{
return (set_errno(ENOTSUP));
}
static int
termio_ctl(mdb_io_t *io, int req, void *arg)
{
termio_data_t *td = io->io_data;
if (io->io_next != NULL)
return (IOP_CTL(io->io_next, req, arg));
if (req == MDB_IOC_CTTY) {
bcopy(&td->tio_ptios, &td->tio_ctios, sizeof (struct termios));
return (0);
}
return (IOP_CTL(td->tio_in_io, req, arg));
}
static void
termio_close(mdb_io_t *io)
{
termio_data_t *td = io->io_data;
(void) mdb_signal_sethandler(SIGWINCH, MDB_SIG_DFL, NULL);
(void) mdb_signal_sethandler(SIGTSTP, MDB_SIG_DFL, NULL);
termio_suspend_tty(td, &td->tio_ptios);
if (td->tio_attrs)
mdb_free(td->tio_attrs, td->tio_attrslen);
mdb_cmdbuf_destroy(&td->tio_cmdbuf);
mdb_iob_destroy(td->tio_out);
mdb_iob_destroy(td->tio_in);
mdb_free(td, sizeof (termio_data_t));
}
static const char *
termio_name(mdb_io_t *io)
{
termio_data_t *td = io->io_data;
if (io->io_next != NULL)
return (IOP_NAME(io->io_next));
return (IOP_NAME(td->tio_in_io));
}
static void
termio_link(mdb_io_t *io, mdb_iob_t *iob)
{
termio_data_t *td = io->io_data;
if (io->io_next == NULL) {
mdb_iob_resize(iob, td->tio_rows, td->tio_cols);
td->tio_link = iob;
} else
IOP_LINK(io->io_next, iob);
}
static void
termio_unlink(mdb_io_t *io, mdb_iob_t *iob)
{
termio_data_t *td = io->io_data;
if (io->io_next == NULL) {
if (td->tio_link == iob)
td->tio_link = NULL;
} else
IOP_UNLINK(io->io_next, iob);
}
static int
termio_setattr(mdb_io_t *io, int req, uint_t attrs)
{
termio_data_t *td = io->io_data;
if (io->io_next != NULL)
return (IOP_SETATTR(io->io_next, req, attrs));
if ((req != ATT_ON && req != ATT_OFF) || (attrs & ~ATT_ALL) != 0)
return (set_errno(EINVAL));
if (req == ATT_ON) {
if (attrs & ATT_STANDOUT)
termio_tput(td, td->tio_info.ti_smso.at_str, 1);
if (attrs & ATT_UNDERLINE)
termio_tput(td, td->tio_info.ti_smul.at_str, 1);
if (attrs & ATT_REVERSE)
termio_tput(td, td->tio_info.ti_rev.at_str, 1);
if (attrs & ATT_BOLD)
termio_tput(td, td->tio_info.ti_bold.at_str, 1);
if (attrs & ATT_DIM)
termio_tput(td, td->tio_info.ti_dim.at_str, 1);
if (attrs & ATT_ALTCHARSET)
termio_tput(td, td->tio_info.ti_smacs.at_str, 1);
} else {
if (attrs & ATT_STANDOUT)
termio_tput(td, td->tio_info.ti_rmso.at_str, 1);
if (attrs & ATT_UNDERLINE)
termio_tput(td, td->tio_info.ti_rmul.at_str, 1);
if (attrs & ATT_ALTCHARSET)
termio_tput(td, td->tio_info.ti_rmacs.at_str, 1);
if (attrs & (ATT_REVERSE | ATT_BOLD | ATT_DIM))
termio_tput(td, td->tio_info.ti_sgr0.at_str, 1);
}
mdb_iob_flush(td->tio_out);
return (0);
}
static void
termio_warn(termio_data_t *td, uint_t flag, const char *format, ...)
{
if (!(td->tio_flags & flag)) {
va_list alist;
va_start(alist, format);
vwarn(format, alist);
va_end(alist);
td->tio_flags |= flag;
}
}
static void
termio_suspend_tty(termio_data_t *td, struct termios *iosp)
{
if (td->tio_suspended++ != 0)
return;
if (td->tio_flags & TIO_XTERM)
termio_tput(td, TI_DECRES(TI_COLENAB), 1);
if (td->tio_flags & TIO_USECUP)
termio_tput(td, td->tio_info.ti_rmcup.at_str, 1);
termio_tput(td, td->tio_info.ti_sgr0.at_str, 1);
mdb_iob_flush(td->tio_out);
if (termio_ctl(td->tio_io, TCSETSW, iosp) == -1)
warn("failed to restore terminal attributes");
if (td->tio_opgid > 0 && td->tio_opgid != mdb.m_pgid) {
mdb_dprintf(MDB_DBG_CMDBUF, "fg pgid=%d\n", (int)td->tio_opgid);
(void) mdb_signal_sethandler(SIGTTOU, MDB_SIG_IGN, NULL);
(void) termio_ctl(td->tio_io, TIOCSPGRP, &td->tio_opgid);
(void) mdb_signal_sethandler(SIGTTOU, MDB_SIG_DFL, NULL);
}
}
static void
termio_resume_tty(termio_data_t *td, struct termios *iosp)
{
static const uint_t baud[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600,
76800, 115200, 153600, 230400, 307200, 460800, 921600,
1000000, 1152000, 1500000, 2000000, 2500000, 3000000,
3500000, 4000000
};
struct termios *ntios;
struct winsize winsz;
uint_t speed;
if (td->tio_suspended == 0)
fail("termio_resume called without matching termio_suspend\n");
if (--td->tio_suspended != 0)
return;
td->tio_opgid = -1;
(void) termio_ctl(td->tio_io, TIOCGPGRP, &td->tio_opgid);
if (td->tio_opgid != mdb.m_pgid) {
(void) mdb_signal_sethandler(SIGTTOU, MDB_SIG_IGN, NULL);
(void) termio_ctl(td->tio_io, TIOCSPGRP, &mdb.m_pgid);
(void) mdb_signal_sethandler(SIGTTOU, MDB_SIG_DFL, NULL);
mdb_dprintf(MDB_DBG_CMDBUF, "fg pgid=%d\n", (int)mdb.m_pgid);
}
if (termio_ctl(td->tio_io, TCGETS, iosp) < 0)
warn("failed to get terminal attributes");
if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == 0) {
if (winsz.ws_row != 0)
td->tio_rows = (size_t)winsz.ws_row;
if (winsz.ws_col != 0)
td->tio_cols = (size_t)winsz.ws_col;
}
mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols);
td->tio_intr = td->tio_ptios.c_cc[VINTR];
td->tio_quit = td->tio_ptios.c_cc[VQUIT];
td->tio_erase = td->tio_ptios.c_cc[VERASE];
td->tio_werase = td->tio_ptios.c_cc[VWERASE];
td->tio_kill = td->tio_ptios.c_cc[VKILL];
td->tio_eof = td->tio_ptios.c_cc[VEOF];
td->tio_susp = td->tio_ptios.c_cc[VSUSP];
bcopy(&td->tio_ptios, &td->tio_rtios, sizeof (struct termios));
td->tio_rtios.c_iflag &= ~(ISTRIP | INPCK | ICRNL | INLCR | IUCLC);
td->tio_rtios.c_oflag &= ~(OCRNL | ONLRET);
td->tio_rtios.c_oflag |= ONLCR;
td->tio_rtios.c_lflag &= ~(ISIG | ICANON | ECHO);
td->tio_rtios.c_cflag |= CS8;
td->tio_rtios.c_cc[VTIME] = 0;
td->tio_rtios.c_cc[VMIN] = 1;
bcopy(&td->tio_ptios, &td->tio_dtios, sizeof (struct termios));
td->tio_dtios.c_oflag &= ~(OCRNL | ONLRET);
td->tio_dtios.c_oflag |= ONLCR;
td->tio_dtios.c_lflag |= ISIG | ICANON | ECHO;
if (td->tio_rti_on)
ntios = &td->tio_rtios;
else
ntios = &td->tio_dtios;
if (termio_ctl(td->tio_io, TCSETSW, ntios) < 0)
warn("failed to reset terminal attributes");
if (ntios->c_cflag & CBAUDEXT)
speed = (ntios->c_cflag & CBAUD) + CBAUD + 1;
else
speed = (ntios->c_cflag & CBAUD);
if (speed >= sizeof (baud) / sizeof (baud[0])) {
termio_warn(td, TIO_TTYWARN, "invalid speed %u -- assuming "
"9600 baud\n", speed);
speed = B9600;
}
td->tio_baud = baud[speed];
td->tio_usecpc = MICROSEC / td->tio_baud;
mdb_dprintf(MDB_DBG_CMDBUF, "speed = %u baud (%u usec / char), "
"putp = %s\n", td->tio_baud, td->tio_usecpc,
td->tio_putp == &termio_puts ? "fast" : "slow");
if (td->tio_flags & TIO_USECUP) {
termio_tput(td, td->tio_info.ti_smcup.at_str, 1);
if (td->tio_info.ti_clear.at_str) {
termio_tput(td, td->tio_info.ti_clear.at_str, 1);
td->tio_x = td->tio_y = 0;
}
}
if (td->tio_flags & TIO_XTERM) {
termio_tput(td, TI_DECSAV(TI_COLENAB), 1);
termio_tput(td, TI_DECSET(TI_COLENAB), 1);
}
termio_tput(td, td->tio_info.ti_cnorm.at_str, 1);
termio_tput(td, td->tio_info.ti_enacs.at_str, 1);
if ((td->tio_flags & TIO_AUTOWRAP) &&
td->tio_info.ti_smam.at_str != NULL) {
termio_tput(td, td->tio_info.ti_smam.at_str, 1);
}
mdb_iob_flush(td->tio_out);
}
static void
termio_suspend(mdb_io_t *io)
{
termio_data_t *td = io->io_data;
termio_suspend_tty(td, &td->tio_ctios);
}
static void
termio_resume(mdb_io_t *io)
{
termio_data_t *td = io->io_data;
termio_resume_tty(td, &td->tio_ctios);
}
static void
termio_delay(termio_data_t *td, uint_t usec)
{
char pad = td->tio_info.ti_pad.at_str[0];
uint_t usecpc = td->tio_usecpc;
for (usec = (usec + usecpc / 2) / usecpc; usec != 0; usec--) {
mdb_iob_putc(td->tio_out, pad);
mdb_iob_flush(td->tio_out);
}
}
static const char *
termio_pad(termio_data_t *td, const char *s, uint_t lines)
{
int xon = td->tio_info.ti_xon.at_val;
int pb = td->tio_info.ti_pb.at_val;
const char *p = s;
uint_t usec = 0;
while (*p >= '0' && *p <= '9')
usec = usec * 10 + *p++ - '0';
usec *= 1000;
if (*p == '.') {
if (p[1] >= '0' && p[1] <= '9')
usec += (p[1] - '0') * 100;
for (p++; *p >= '0' && *p <= '9'; p++)
continue;
}
for (;;) {
switch (*p++) {
case '/':
xon = FALSE;
continue;
case '*':
usec *= lines;
continue;
case '>':
if (xon == FALSE && usec != 0 && td->tio_baud >= pb)
termio_delay(td, usec);
return (p);
default:
mdb_iob_putc(td->tio_out, *s);
return (s + 1);
}
}
}
static void
termio_putp(termio_data_t *td, const char *s, uint_t lines)
{
while (s[0] != '\0') {
if (s[0] == '$' && s[1] == '<')
s = termio_pad(td, s + 2, lines);
else
mdb_iob_putc(td->tio_out, *s++);
}
mdb_iob_flush(td->tio_out);
}
static void
termio_puts(termio_data_t *td, const char *s, uint_t lines)
{
mdb_iob_puts(td->tio_out, s);
}
static void
termio_tput(termio_data_t *td, const char *s, uint_t lines)
{
if (s != NULL)
td->tio_putp(td, s, lines);
}
static void
termio_addch(termio_data_t *td, char c, size_t width)
{
if (width == 1) {
mdb_iob_putc(td->tio_out, c);
td->tio_x++;
if (td->tio_x >= td->tio_cols) {
if (!(td->tio_flags & TIO_AUTOWRAP))
termio_tput(td, td->tio_info.ti_nel.at_str, 1);
td->tio_x = 0;
td->tio_y++;
}
mdb_iob_flush(td->tio_out);
} else
termio_redraw(td);
}
static void
termio_insch(termio_data_t *td, char c, size_t width)
{
if (width == 1 && (td->tio_flags & TIO_INSERT) &&
td->tio_y == td->tio_max_y) {
termio_tput(td, td->tio_info.ti_smir.at_str, 1);
termio_tput(td, td->tio_info.ti_ich1.at_str, 1);
mdb_iob_putc(td->tio_out, c);
td->tio_x++;
termio_tput(td, td->tio_info.ti_ip.at_str, 1);
termio_tput(td, td->tio_info.ti_rmir.at_str, 1);
if (td->tio_x >= td->tio_cols) {
if (!(td->tio_flags & TIO_AUTOWRAP))
termio_tput(td, td->tio_info.ti_nel.at_str, 1);
td->tio_x = 0;
td->tio_y++;
}
mdb_iob_flush(td->tio_out);
} else
termio_redraw(td);
}
static void
termio_mvcur(termio_data_t *td)
{
size_t tipos = td->tio_cmdbuf.cmd_bufidx + td->tio_promptlen;
size_t dst_x = tipos % td->tio_cols;
size_t dst_y = tipos / td->tio_cols;
const char *str;
size_t cnt, i;
if (td->tio_y != dst_y) {
if (td->tio_y < dst_y) {
str = td->tio_info.ti_cud1.at_str;
cnt = dst_y - td->tio_y;
td->tio_x = 0;
} else {
str = td->tio_info.ti_cuu1.at_str;
cnt = td->tio_y - dst_y;
}
for (i = 0; i < cnt; i++)
termio_tput(td, str, 1);
mdb_iob_flush(td->tio_out);
td->tio_y = dst_y;
}
if (td->tio_x != dst_x) {
if (td->tio_x < dst_x) {
str = td->tio_info.ti_cuf1.at_str;
cnt = dst_x - td->tio_x;
} else {
str = td->tio_info.ti_cub1.at_str;
cnt = td->tio_x - dst_x;
}
for (i = 0; i < cnt; i++)
termio_tput(td, str, 1);
mdb_iob_flush(td->tio_out);
td->tio_x = dst_x;
}
}
static void
termio_backleft(termio_data_t *td)
{
size_t i;
if (td->tio_flags & TIO_BACKLEFT)
termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
else {
termio_tput(td, td->tio_info.ti_cuu1.at_str, 1);
for (i = 0; i < td->tio_cols - 1; i++)
termio_tput(td, td->tio_info.ti_cuf1.at_str, 1);
}
}
static void
termio_bspch(termio_data_t *td)
{
if (td->tio_x == 0) {
if (td->tio_flags & TIO_LAZYWRAP) {
mdb_iob_putc(td->tio_out, ' ');
termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
}
termio_backleft(td);
td->tio_x = td->tio_cols - 1;
td->tio_y--;
} else {
termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
td->tio_x--;
}
termio_delch(td);
}
static void
termio_delch(termio_data_t *td)
{
mdb_iob_putc(td->tio_out, ' ');
if (td->tio_x == td->tio_cols - 1 && (td->tio_flags & TIO_AUTOWRAP)) {
if (!(td->tio_flags & TIO_LAZYWRAP))
termio_backleft(td);
} else {
termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
}
mdb_iob_flush(td->tio_out);
}
static void
termio_clear(termio_data_t *td)
{
while (td->tio_x-- != 0)
termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
while (td->tio_y < td->tio_max_y) {
termio_tput(td, td->tio_info.ti_cud1.at_str, 1);
td->tio_y++;
}
while (td->tio_y-- != 0) {
termio_tput(td, td->tio_info.ti_el.at_str, 1);
termio_tput(td, td->tio_info.ti_cuu1.at_str, 1);
}
termio_tput(td, td->tio_info.ti_el.at_str, 1);
mdb_iob_flush(td->tio_out);
termio_prompt(td);
}
static void
termio_redraw(termio_data_t *td)
{
const char *buf = td->tio_cmdbuf.cmd_buf;
size_t len = td->tio_cmdbuf.cmd_buflen;
size_t pos, n;
termio_clear(td);
if (len == 0)
return;
if (td->tio_flags & TIO_AUTOWRAP)
mdb_iob_nputs(td->tio_out, buf, len);
else {
for (pos = td->tio_promptlen; len != 0; pos = 0) {
n = MIN(td->tio_cols - pos, len);
mdb_iob_nputs(td->tio_out, buf, n);
buf += n;
len -= n;
if (pos + n == td->tio_cols)
termio_tput(td, td->tio_info.ti_nel.at_str, 1);
}
}
pos = td->tio_promptlen + td->tio_cmdbuf.cmd_buflen;
td->tio_x = pos % td->tio_cols;
td->tio_y = pos / td->tio_cols;
mdb_iob_flush(td->tio_out);
termio_mvcur(td);
}
static void
termio_prompt(termio_data_t *td)
{
mdb_callb_fire(MDB_CALLB_PROMPT);
if (!(td->tio_flags & TIO_FINDHIST)) {
td->tio_prompt = mdb.m_prompt;
td->tio_promptlen = mdb.m_promptlen;
}
mdb_iob_puts(td->tio_out, td->tio_prompt);
mdb_iob_flush(td->tio_out);
td->tio_x = td->tio_promptlen;
td->tio_y = 0;
}
static void
termio_dump(termio_data_t *td, const termio_attr_t *ta)
{
char *str;
for (; ta->ta_name != NULL; ta++) {
switch (ta->ta_type) {
case TIO_ATTR_REQSTR:
case TIO_ATTR_STR:
if (ta->ta_valp->at_str != NULL) {
str = strchr2esc(ta->ta_valp->at_str,
strlen(ta->ta_valp->at_str));
mdb_dprintf(MDB_DBG_CMDBUF, "%s = \"%s\"\n",
ta->ta_name, str);
strfree(str);
} else {
mdb_dprintf(MDB_DBG_CMDBUF, "%s = <NULL>\n",
ta->ta_name);
}
break;
case TIO_ATTR_INT:
mdb_dprintf(MDB_DBG_CMDBUF, "%s = %d\n",
ta->ta_name, ta->ta_valp->at_val);
break;
case TIO_ATTR_BOOL:
mdb_dprintf(MDB_DBG_CMDBUF, "%s = %s\n", ta->ta_name,
ta->ta_valp->at_val ? "TRUE" : "FALSE");
break;
}
}
mdb_dprintf(MDB_DBG_CMDBUF, "tio_flags = <%#b>\n",
td->tio_flags, tio_flag_masks);
}
static int
termio_setup_attrs(termio_data_t *td, const char *name)
{
const termio_attr_t *ta;
const char *str;
size_t nbytes;
char *bufp;
int need_padding = 0;
int i;
for (nbytes = 0, ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) {
switch (ta->ta_type) {
case TIO_ATTR_REQSTR:
case TIO_ATTR_STR:
str = tigetstr(ta->ta_name);
if (str == (const char *)-1) {
termio_warn(td, TIO_CAPWARN,
"terminal capability '%s' is not of type "
"string as expected\n", ta->ta_name);
return (0);
}
if (str != NULL)
nbytes += strlen(str) + 1;
else if (ta->ta_type == TIO_ATTR_REQSTR) {
termio_warn(td, TIO_CAPWARN,
"terminal capability '%s' is not "
"available\n", ta->ta_name);
return (0);
}
break;
case TIO_ATTR_BOOL:
if (tigetflag(ta->ta_name) == -1) {
termio_warn(td, TIO_CAPWARN,
"terminal capability '%s' is not of type "
"boolean as expected\n", ta->ta_name);
return (0);
}
break;
case TIO_ATTR_INT:
if (tigetnum(ta->ta_name) == -2) {
termio_warn(td, TIO_CAPWARN,
"terminal capability '%s' is not of type "
"integer as expected\n", ta->ta_name);
return (0);
}
break;
}
}
if (nbytes != 0)
td->tio_attrs = mdb_alloc(nbytes, UM_SLEEP);
else
td->tio_attrs = NULL;
td->tio_attrslen = nbytes;
bufp = td->tio_attrs;
for (ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) {
switch (ta->ta_type) {
case TIO_ATTR_REQSTR:
case TIO_ATTR_STR:
if ((str = tigetstr(ta->ta_name)) != NULL) {
(void) strcpy(bufp, str);
ta->ta_valp->at_str = bufp;
bufp += strlen(str) + 1;
if ((str = strstr(ta->ta_valp->at_str,
"$<")) != NULL && strchr(str, '>') != NULL)
need_padding++;
} else {
ta->ta_valp->at_str = NULL;
}
break;
case TIO_ATTR_BOOL:
ta->ta_valp->at_val = tigetflag(ta->ta_name);
break;
case TIO_ATTR_INT:
ta->ta_valp->at_val = tigetnum(ta->ta_name);
break;
}
}
bcopy(&termio_info, &td->tio_info, sizeof (termio_info_t));
td->tio_rows = MAX(td->tio_info.ti_lines.at_val, 0);
td->tio_cols = MAX(td->tio_info.ti_cols.at_val, 0);
if (td->tio_rows == 0) {
if ((str = getenv("LINES")) != NULL && strisnum(str) != 0 &&
(i = strtoi(str)) > 0)
td->tio_rows = i;
else
td->tio_rows = TIO_DEFAULT_ROWS;
}
if (td->tio_cols == 0) {
if ((str = getenv("COLUMNS")) != NULL && strisnum(str) != 0 &&
(i = strtoi(str)) > 0)
td->tio_cols = i;
else
td->tio_cols = TIO_DEFAULT_COLS;
}
td->tio_flags = 0;
if (td->tio_info.ti_am.at_val)
td->tio_flags |= TIO_AUTOWRAP;
if (td->tio_info.ti_xenl.at_val)
td->tio_flags |= TIO_LAZYWRAP;
if (td->tio_info.ti_bw.at_val)
td->tio_flags |= TIO_BACKLEFT;
if (td->tio_info.ti_smir.at_str != NULL ||
td->tio_info.ti_ich1.at_str != NULL)
td->tio_flags |= TIO_INSERT;
if (mdb.m_flags & MDB_FL_USECUP)
td->tio_flags |= TIO_USECUP;
if (name != NULL && (strncmp(name, "xterm", 5) == 0 ||
strcmp(name, "dtterm") == 0))
td->tio_flags |= TIO_XTERM;
if (td->tio_info.ti_pad.at_str == NULL)
td->tio_info.ti_pad.at_str = "";
if (need_padding == 0)
td->tio_info.ti_npc.at_val = TRUE;
if (td->tio_info.ti_npc.at_val)
td->tio_putp = &termio_puts;
else
td->tio_putp = &termio_putp;
if (td->tio_info.ti_pb.at_val < 0)
td->tio_info.ti_pb.at_val = 0;
if (td->tio_info.ti_nel.at_str == NULL)
td->tio_info.ti_nel.at_str = "\r\n";
if (td->tio_info.ti_cr.at_str == NULL)
td->tio_info.ti_cr.at_str = "\r";
return (1);
}
mdb_io_t *
mdb_termio_create(const char *name, mdb_io_t *rio, mdb_io_t *wio)
{
struct termios otios;
termio_data_t *td;
int rv, err, i;
td = mdb_zalloc(sizeof (termio_data_t), UM_SLEEP);
td->tio_io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
if (IOP_CTL(rio, TCGETS, &otios) == -1) {
warn("failed to read terminal attributes for stdin");
goto err;
}
rv = setupterm((char *)name, IOP_CTL(rio, MDB_IOC_GETFD, NULL), &err);
IOP_CTL(rio, TCSETSW, &otios);
if (rv == ERR) {
if (err == 0)
warn("no terminal data available for TERM=%s\n", name);
else if (err == -1)
warn("failed to locate terminfo database\n");
else
warn("failed to initialize terminal (err=%d)\n", err);
goto err;
}
if (!termio_setup_attrs(td, name))
goto err;
if (mdb.m_flags & MDB_FL_EXEC)
td->tio_flags |= TIO_TTYWARN | TIO_CAPWARN;
td->tio_io->io_ops = &termio_ops;
td->tio_io->io_data = td;
td->tio_io->io_next = NULL;
td->tio_io->io_refcnt = 0;
td->tio_in_io = rio;
td->tio_in = mdb_iob_create(td->tio_in_io, MDB_IOB_RDONLY);
td->tio_out_io = wio;
td->tio_out = mdb_iob_create(td->tio_out_io, MDB_IOB_WRONLY);
mdb_iob_clrflags(td->tio_out, MDB_IOB_AUTOWRAP);
td->tio_link = NULL;
mdb_cmdbuf_create(&td->tio_cmdbuf);
for (i = 0; i < KEY_MAX; i++)
td->tio_keymap[i] = termio_insert;
td->tio_keymap['\n'] = termio_accept;
td->tio_keymap['\r'] = termio_accept;
td->tio_keymap[CTRL('f')] = termio_fwdchar;
td->tio_keymap[CTRL('b')] = termio_backchar;
td->tio_keymap[CTRL('t')] = termio_transpose;
td->tio_keymap[CTRL('a')] = termio_home;
td->tio_keymap[CTRL('e')] = termio_end;
td->tio_keymap[META('f')] = termio_fwdword;
td->tio_keymap[META('b')] = termio_backword;
td->tio_keymap[META('d')] = termio_killfwdword;
td->tio_keymap[META('\b')] = termio_killbackword;
td->tio_keymap[CTRL('k')] = termio_kill;
td->tio_keymap[CTRL('p')] = termio_prevhist;
td->tio_keymap[CTRL('n')] = termio_nexthist;
td->tio_keymap[CTRL('r')] = termio_findhist;
td->tio_keymap[CTRL('l')] = termio_refresh;
td->tio_keymap[CTRL('d')] = termio_delchar;
td->tio_keymap[CTRL('?')] = termio_widescreen;
td->tio_keymap[KPAD('A')] = termio_prevhist;
td->tio_keymap[KPAD('B')] = termio_nexthist;
td->tio_keymap[KPAD('C')] = termio_fwdchar;
td->tio_keymap[KPAD('D')] = termio_backchar;
td->tio_keymap[KPAD('H')] = termio_home;
td->tio_keymap[FKEY('1')] = termio_home;
td->tio_keymap[KPAD('F')] = termio_end;
td->tio_keymap[FKEY('4')] = termio_end;
td->tio_keymap[CTRL('h')] = termio_backspace;
td->tio_keymap[KEY_DEL] = termio_backspace;
td->tio_keymap['['] = termio_accel;
td->tio_keymap[']'] = termio_accel;
td->tio_keymap['\t'] = termio_tab;
td->tio_x = 0;
td->tio_y = 0;
td->tio_max_x = 0;
td->tio_max_y = 0;
td->tio_active = FALSE;
td->tio_rti_on = FALSE;
td->tio_suspended = 1;
termio_resume_tty(td, &td->tio_ptios);
bcopy(&td->tio_ptios, &td->tio_ctios, sizeof (struct termios));
td->tio_keymap[td->tio_intr] = termio_intr;
td->tio_keymap[td->tio_quit] = termio_quit;
td->tio_keymap[td->tio_erase] = termio_backspace;
td->tio_keymap[td->tio_werase] = termio_killbackword;
td->tio_keymap[td->tio_kill] = termio_reset;
td->tio_keymap[td->tio_susp] = termio_susp;
(void) mdb_signal_sethandler(SIGWINCH, termio_winch, td);
(void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td);
if (mdb.m_debug & MDB_DBG_CMDBUF)
termio_dump(td, &termio_attrs[0]);
return (td->tio_io);
err:
mdb_free(td->tio_io, sizeof (mdb_io_t));
mdb_free(td, sizeof (termio_data_t));
return (NULL);
}
int
mdb_iob_isatty(mdb_iob_t *iob)
{
mdb_io_t *io;
if (iob->iob_flags & MDB_IOB_TTYLIKE)
return (1);
for (io = iob->iob_iop; io != NULL; io = io->io_next) {
if (io->io_ops == &termio_ops)
return (1);
}
return (0);
}
static const char *
termio_insert(termio_data_t *td, int c)
{
size_t olen = td->tio_cmdbuf.cmd_buflen;
if (mdb_cmdbuf_insert(&td->tio_cmdbuf, c) == 0) {
if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
termio_addch(td, c, td->tio_cmdbuf.cmd_buflen - olen);
else
termio_insch(td, c, td->tio_cmdbuf.cmd_buflen - olen);
}
return (NULL);
}
static const char *
termio_accept(termio_data_t *td, int c)
{
if (td->tio_flags & TIO_FINDHIST) {
(void) mdb_cmdbuf_findhist(&td->tio_cmdbuf, c);
td->tio_prompt = mdb.m_prompt;
td->tio_promptlen = mdb.m_promptlen;
td->tio_flags &= ~TIO_FINDHIST;
termio_redraw(td);
return (NULL);
}
(void) termio_end(td, c);
return (mdb_cmdbuf_accept(&td->tio_cmdbuf));
}
static const char *
termio_backspace(termio_data_t *td, int c)
{
if (mdb_cmdbuf_backspace(&td->tio_cmdbuf, c) == 0) {
if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
termio_bspch(td);
else
termio_redraw(td);
}
return (NULL);
}
static const char *
termio_tab(termio_data_t *td, int c)
{
char *buf;
const char *result;
int nres;
mdb_tab_cookie_t *mtp;
if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
warn("failed to restore terminal attributes");
buf = mdb_alloc(td->tio_cmdbuf.cmd_bufidx + 1, UM_SLEEP | UM_GC);
(void) strncpy(buf, td->tio_cmdbuf.cmd_buf, td->tio_cmdbuf.cmd_bufidx);
buf[td->tio_cmdbuf.cmd_bufidx] = '\0';
td->tio_flags |= TIO_TAB;
mtp = mdb_tab_init();
nres = mdb_tab_command(mtp, buf);
if (nres == 0) {
result = NULL;
} else {
result = mdb_tab_match(mtp);
if (nres != 1) {
mdb_printf("\n");
mdb_tab_print(mtp);
}
}
if (result != NULL) {
int index = 0;
while (result[index] != '\0') {
(void) termio_insert(td, result[index]);
index++;
}
}
termio_redraw(td);
mdb_tab_fini(mtp);
td->tio_flags &= ~TIO_TAB;
if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1)
warn("failed to set terminal attributes");
return (NULL);
}
static const char *
termio_delchar(termio_data_t *td, int c)
{
if (!(mdb.m_flags & MDB_FL_IGNEOF) &&
mdb_cmdbuf_atend(&td->tio_cmdbuf) &&
mdb_cmdbuf_atstart(&td->tio_cmdbuf))
return (termio_quit(td, c));
if (mdb_cmdbuf_delchar(&td->tio_cmdbuf, c) == 0) {
if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
termio_delch(td);
else
termio_redraw(td);
}
return (NULL);
}
static const char *
termio_fwdchar(termio_data_t *td, int c)
{
if (mdb_cmdbuf_fwdchar(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_backchar(termio_data_t *td, int c)
{
if (mdb_cmdbuf_backchar(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_transpose(termio_data_t *td, int c)
{
if (mdb_cmdbuf_transpose(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_home(termio_data_t *td, int c)
{
if (mdb_cmdbuf_home(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_end(termio_data_t *td, int c)
{
if (mdb_cmdbuf_end(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_fwdword(termio_data_t *td, int c)
{
if (mdb_cmdbuf_fwdword(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_backword(termio_data_t *td, int c)
{
if (mdb_cmdbuf_backword(&td->tio_cmdbuf, c) == 0)
termio_mvcur(td);
return (NULL);
}
static const char *
termio_kill(termio_data_t *td, int c)
{
if (mdb_cmdbuf_kill(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_killfwdword(termio_data_t *td, int c)
{
if (mdb_cmdbuf_killfwdword(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_killbackword(termio_data_t *td, int c)
{
if (mdb_cmdbuf_killbackword(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_reset(termio_data_t *td, int c)
{
if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0)
termio_clear(td);
return (NULL);
}
static const char *
termio_widescreen(termio_data_t *td, int c)
{
if (td->tio_flags & TIO_XTERM) {
if (td->tio_cols == 80)
termio_tput(td, TI_DECSET(TI_DECCOLM), 1);
else
termio_tput(td, TI_DECRST(TI_DECCOLM), 1);
mdb_iob_flush(td->tio_out);
termio_winch(SIGWINCH, NULL, NULL, td);
}
return (NULL);
}
static const char *
termio_prevhist(termio_data_t *td, int c)
{
if (mdb_cmdbuf_prevhist(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_nexthist(termio_data_t *td, int c)
{
if (mdb_cmdbuf_nexthist(&td->tio_cmdbuf, c) == 0)
termio_redraw(td);
return (NULL);
}
static const char *
termio_accel(termio_data_t *td, int c)
{
const char *p;
if (td->tio_cmdbuf.cmd_buflen != 0 ||
(p = termio_accel_lookup(c)) == NULL)
return (termio_insert(td, c));
while (*p != '\0')
(void) termio_insert(td, *p++);
return (termio_accept(td, '\n'));
}
static const char *
termio_findhist(termio_data_t *td, int c)
{
if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0) {
td->tio_prompt = "Search: ";
td->tio_promptlen = strlen(td->tio_prompt);
td->tio_flags |= TIO_FINDHIST;
termio_redraw(td);
}
return (NULL);
}
static const char *
termio_refresh(termio_data_t *td, int c)
{
if (td->tio_info.ti_clear.at_str) {
termio_tput(td, td->tio_info.ti_clear.at_str, 1);
td->tio_x = td->tio_y = 0;
}
termio_redraw(td);
return (NULL);
}
static const char *
termio_abort(termio_data_t *td, int c, int err)
{
(void) mdb_cmdbuf_reset(&td->tio_cmdbuf, c);
td->tio_active = FALSE;
td->tio_rti_on = FALSE;
if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
warn("failed to restore terminal attributes");
longjmp(mdb.m_frame->f_pcb, err);
return (NULL);
}
static const char *
termio_intr(termio_data_t *td, int c)
{
return (termio_abort(td, c, MDB_ERR_SIGINT));
}
static const char *
termio_quit(termio_data_t *td, int c)
{
return (termio_abort(td, c, MDB_ERR_QUIT));
}
static const char *
termio_susp(termio_data_t *td, int c)
{
(void) mdb_signal_sethandler(SIGWINCH, MDB_SIG_IGN, NULL);
(void) mdb_signal_sethandler(SIGTSTP, MDB_SIG_IGN, NULL);
termio_suspend_tty(td, &td->tio_ptios);
mdb_iob_nl(td->tio_out);
(void) mdb_signal_sethandler(SIGTSTP, MDB_SIG_DFL, NULL);
(void) mdb_signal_pgrp(SIGTSTP);
(void) mdb_signal_sethandler(SIGTSTP, MDB_SIG_IGN, NULL);
termio_resume_tty(td, &td->tio_ptios);
(void) mdb_signal_sethandler(SIGWINCH, termio_winch, td);
(void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td);
if (td->tio_active)
siglongjmp(td->tio_env, SIGCONT);
return (NULL);
}
static void
termio_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
{
termio_data_t *td = data;
mdb_bool_t change = FALSE;
struct winsize winsz;
if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == -1)
return;
if (td->tio_rows != (size_t)winsz.ws_row ||
td->tio_cols != (size_t)winsz.ws_col) {
if (td->tio_active)
termio_clear(td);
if (winsz.ws_row != 0)
td->tio_rows = (size_t)winsz.ws_row;
if (winsz.ws_col != 0)
td->tio_cols = (size_t)winsz.ws_col;
if (td->tio_active)
termio_clear(td);
mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols);
change = TRUE;
}
if (change && td->tio_active)
siglongjmp(td->tio_env, sig);
if (change && td->tio_link != NULL)
mdb_iob_resize(td->tio_link, td->tio_rows, td->tio_cols);
}
static void
termio_tstp(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
{
(void) termio_susp(data, CTRL('Z'));
}