/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Christos Zoulas of Cornell University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* tty_getty():
* Wrapper for tcgetattr to handle EINTR
*/
static int
tty_getty(EditLine *el, struct termios *t)
{
int rv;
while ((rv = tcgetattr(el->el_infd, t)) == -1 && errno == EINTR)
continue;
return rv;
}
/* tty_setty():
* Wrapper for tcsetattr to handle EINTR
*/
static int
tty_setty(EditLine *el, int action, const struct termios *t)
{
int rv;
while ((rv = tcsetattr(el->el_infd, action, t)) == -1 && errno == EINTR)
continue;
return rv;
}
/* tty_setup():
* Get the tty parameters and initialize the editing state
*/
static int
tty_setup(EditLine *el)
{
int rst = (el->el_flags & NO_RESET) == 0;
/* tty_end():
* Restore the tty to its original settings
*/
libedit_private void
/*ARGSUSED*/
tty_end(EditLine *el, int how)
{
if (el->el_flags & EDIT_DISABLED)
return;
map = el->el_map.key;
alt = el->el_map.alt;
if (el->el_map.type == MAP_VI) {
dmap = el->el_map.vii;
dalt = el->el_map.vic;
} else {
dmap = el->el_map.emacs;
dalt = NULL;
}
for (tp = tty_map; tp->nch != (wint_t)-1; tp++) {
new[0] = (wchar_t)t_n[tp->nch];
old[0] = (wchar_t)t_o[tp->och];
if (new[0] == old[0] && !force)
continue;
/* Put the old default binding back, and set the new binding */
keymacro_clear(el, map, old);
map[(unsigned char)old[0]] = dmap[(unsigned char)old[0]];
keymacro_clear(el, map, new);
/* MAP_VI == 1, MAP_EMACS == 0... */
map[(unsigned char)new[0]] = tp->bind[el->el_map.type];
if (dalt) {
keymacro_clear(el, alt, old);
alt[(unsigned char)old[0]] =
dalt[(unsigned char)old[0]];
keymacro_clear(el, alt, new);
alt[(unsigned char)new[0]] =
tp->bind[el->el_map.type + 1];
}
}
}
static tcflag_t *
tty__get_flag(struct termios *t, int kind) {
switch (kind) {
case MD_INP:
return &t->c_iflag;
case MD_OUT:
return &t->c_oflag;
case MD_CTL:
return &t->c_cflag;
case MD_LIN:
return &t->c_lflag;
default:
abort();
/*NOTREACHED*/
}
}
static tcflag_t
tty_update_flag(EditLine *el, tcflag_t f, int mode, int kind)
{
f &= ~el->el_tty.t_t[mode][kind].t_clrmask;
f |= el->el_tty.t_t[mode][kind].t_setmask;
return f;
}
static void
tty_update_flags(EditLine *el, int kind)
{
tcflag_t *tt, *ed, *ex;
tt = tty__get_flag(&el->el_tty.t_ts, kind);
ed = tty__get_flag(&el->el_tty.t_ed, kind);
ex = tty__get_flag(&el->el_tty.t_ex, kind);
static void
tty_update_char(EditLine *el, int mode, int c) {
if (!((el->el_tty.t_t[mode][MD_CHAR].t_setmask & C_SH(c)))
&& (el->el_tty.t_c[TS_IO][c] != el->el_tty.t_c[EX_IO][c]))
el->el_tty.t_c[mode][c] = el->el_tty.t_c[TS_IO][c];
if (el->el_tty.t_t[mode][MD_CHAR].t_clrmask & C_SH(c))
el->el_tty.t_c[mode][c] = el->el_tty.t_vdisable;
}
/* tty_rawmode():
* Set terminal into 1 character at a time mode.
*/
libedit_private int
tty_rawmode(EditLine *el)
{
if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO)
return 0;
if (el->el_flags & EDIT_DISABLED)
return 0;
if (tty_getty(el, &el->el_tty.t_ts) == -1) {
#ifdef DEBUG_TTY
(void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
return -1;
}
/*
* We always keep up with the eight bit setting and the speed of the
* tty. But we only believe changes that are made to cooked mode!
*/
el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts);
el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts);
if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed ||
tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) {
(void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed);
(void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed);
(void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed);
(void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
}
if (tty__cooked_mode(&el->el_tty.t_ts)) {
int i;
for (i = MD_INP; i <= MD_LIN; i++)
tty_update_flags(el, i);
tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
/*
* Check if the user made any changes.
* If he did, then propagate the changes to the
* edit and execute data structures.
*/
for (i = 0; i < C_NCC; i++)
if (el->el_tty.t_c[TS_IO][i] !=
el->el_tty.t_c[EX_IO][i])
break;
if (i != C_NCC) {
/*
* Propagate changes only to the unlibedit_private
* chars that have been modified just now.
*/
for (i = 0; i < C_NCC; i++)
tty_update_char(el, ED_IO, i);
/* tty_stty():
* Stty builtin
*/
libedit_private int
/*ARGSUSED*/
tty_stty(EditLine *el, int argc __attribute__((__unused__)),
const wchar_t **argv)
{
const ttymodes_t *m;
char x;
int aflag = 0;
const wchar_t *s, *d;
char name[EL_BUFSIZ];
struct termios *tios = &el->el_tty.t_ex;
int z = EX_IO;
if (argv == NULL)
return -1;
strlcpy(name, ct_encode_string(*argv++, &el->el_scratch), sizeof(name));
while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0')
switch (argv[0][1]) {
case 'a':
aflag++;
argv++;
break;
case 'd':
argv++;
tios = &el->el_tty.t_ed;
z = ED_IO;
break;
case 'x':
argv++;
tios = &el->el_tty.t_ex;
z = EX_IO;
break;
case 'q':
argv++;
tios = &el->el_tty.t_ts;
z = QU_IO;
break;
default:
(void) fprintf(el->el_errfile,
"%s: Unknown switch `%lc'.\n",
name, (wint_t)argv[0][1]);
return -1;
}
if (!argv || !*argv) {
int i = -1;
size_t len = 0, st = 0, cu;
for (m = ttymodes; m->m_name; m++) {
if (m->m_type != i) {
(void) fprintf(el->el_outfile, "%s%s",
i != -1 ? "\n" : "",
el->el_tty.t_t[z][m->m_type].t_name);
i = m->m_type;
st = len =
strlen(el->el_tty.t_t[z][m->m_type].t_name);
}
if (i != -1) {
x = (el->el_tty.t_t[z][i].t_setmask & m->m_value)
? '+' : '\0';
if (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
x = '-';
} else {
x = '\0';
}
if (x != '\0' || aflag) {
cu = strlen(m->m_name) + (x != '\0') + 1;
if (len + cu >=
(size_t)el->el_terminal.t_size.h) {
(void) fprintf(el->el_outfile, "\n%*s",
(int)st, "");
len = st + cu;
} else
len += cu;
if (x != '\0')
(void) fprintf(el->el_outfile, "%c%s ",
x, m->m_name);
else
(void) fprintf(el->el_outfile, "%s ",
m->m_name);
}
}
(void) fprintf(el->el_outfile, "\n");
return 0;
}
while (argv && (s = *argv++)) {
const wchar_t *p;
switch (*s) {
case '+':
case '-':
x = (char)*s++;
break;
default:
x = '\0';
break;
}
d = s;
p = wcschr(s, L'=');
for (m = ttymodes; m->m_name; m++)
if ((p ? strncmp(m->m_name, ct_encode_string(d,
&el->el_scratch), (size_t)(p - d)) :
strcmp(m->m_name, ct_encode_string(d,
&el->el_scratch))) == 0 &&
(p == NULL || m->m_type == MD_CHAR))
break;
if (!m->m_name) {
(void) fprintf(el->el_errfile,
"%s: Invalid argument `%ls'.\n", name, d);
return -1;
}
if (p) {
int c = ffs((int)m->m_value);
int v = *++p ? parse__escape(&p) :
el->el_tty.t_vdisable;
assert(c != 0);
c--;
c = tty__getcharindex(c);
assert(c != -1);
tios->c_cc[c] = (cc_t)v;
continue;
}
switch (x) {
case '+':
el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value;
el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
break;
case '-':
el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value;
break;
default:
el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
break;
}
}