1945 lines
43 KiB
C
1945 lines
43 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2022 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
/*
|
|
* edit.c - common routines for vi and emacs one line editors in shell
|
|
*
|
|
* David Korn P.D. Sullivan
|
|
* AT&T Labs
|
|
*
|
|
* Coded April 1983.
|
|
*/
|
|
|
|
#include <ast.h>
|
|
#include <errno.h>
|
|
#include <ccode.h>
|
|
#include <fault.h>
|
|
#include "FEATURE/options"
|
|
#include "FEATURE/time"
|
|
#include "FEATURE/cmds"
|
|
#ifdef _hdr_utime
|
|
# include <utime.h>
|
|
# include <ls.h>
|
|
#endif
|
|
|
|
#include "defs.h"
|
|
#include "variables.h"
|
|
#include "io.h"
|
|
#include "terminal.h"
|
|
#include "history.h"
|
|
#include "edit.h"
|
|
#include "shlex.h"
|
|
|
|
static char CURSOR_UP[20] = { ESC, '[', 'A', 0 };
|
|
static char KILL_LINE[20] = { ESC, '[', 'J', 0 };
|
|
static Lex_t *savelex;
|
|
|
|
|
|
#if SHOPT_MULTIBYTE
|
|
# define is_cntrl(c) ((c<=STRIP) && iscntrl(c))
|
|
# define is_print(c) ((c&~STRIP) || isprint(c))
|
|
# define genlen(str) ed_genlen(str)
|
|
#else
|
|
# define is_cntrl(c) iscntrl(c)
|
|
# define is_print(c) isprint(c)
|
|
# define genlen(str) strlen(str)
|
|
#endif
|
|
|
|
#if (CC_NATIVE == CC_ASCII)
|
|
# define printchar(c) ((c) ^ ('A'-cntl('A')))
|
|
#else
|
|
static int printchar(int c)
|
|
{
|
|
switch(c)
|
|
{
|
|
|
|
case cntl('A'): return('A');
|
|
case cntl('B'): return('B');
|
|
case cntl('C'): return('C');
|
|
case cntl('D'): return('D');
|
|
case cntl('E'): return('E');
|
|
case cntl('F'): return('F');
|
|
case cntl('G'): return('G');
|
|
case cntl('H'): return('H');
|
|
case cntl('I'): return('I');
|
|
case cntl('J'): return('J');
|
|
case cntl('K'): return('K');
|
|
case cntl('L'): return('L');
|
|
case cntl('M'): return('M');
|
|
case cntl('N'): return('N');
|
|
case cntl('O'): return('O');
|
|
case cntl('P'): return('P');
|
|
case cntl('Q'): return('Q');
|
|
case cntl('R'): return('R');
|
|
case cntl('S'): return('S');
|
|
case cntl('T'): return('T');
|
|
case cntl('U'): return('U');
|
|
case cntl('V'): return('V');
|
|
case cntl('W'): return('W');
|
|
case cntl('X'): return('X');
|
|
case cntl('Y'): return('Y');
|
|
case cntl('Z'): return('Z');
|
|
case cntl(']'): return(']');
|
|
case cntl('['): return('[');
|
|
}
|
|
return('?');
|
|
}
|
|
#endif
|
|
#define MINWINDOW 15 /* minimum width window */
|
|
#define DFLTWINDOW 80 /* default window width */
|
|
#define RAWMODE 1
|
|
#define ALTMODE 2
|
|
#define ECHOMODE 3
|
|
#define SYSERR -1
|
|
|
|
#if SHOPT_OLDTERMIO
|
|
# undef tcgetattr
|
|
# undef tcsetattr
|
|
#endif /* SHOPT_OLDTERMIO */
|
|
|
|
#ifdef RT
|
|
# define VENIX 1
|
|
#endif /* RT */
|
|
|
|
|
|
#ifdef _hdr_sgtty
|
|
# ifdef TIOCGETP
|
|
static int l_mask;
|
|
static struct tchars l_ttychars;
|
|
static struct ltchars l_chars;
|
|
static char l_changed; /* set if mode bits changed */
|
|
# define L_CHARS 4
|
|
# define T_CHARS 2
|
|
# define L_MASK 1
|
|
# endif /* TIOCGETP */
|
|
#endif /* _hdr_sgtty */
|
|
|
|
static int keytrap(Edit_t *,char*, int, int, int);
|
|
|
|
#ifndef _POSIX_DISABLE
|
|
# define _POSIX_DISABLE 0
|
|
#endif
|
|
|
|
#define ttyparm (ep->e_ttyparm)
|
|
#define nttyparm (ep->e_nttyparm)
|
|
static const char bellchr[] = "\a"; /* bell char */
|
|
|
|
|
|
/*
|
|
* This routine returns true if fd refers to a terminal
|
|
* This should be equivalent to isatty
|
|
*/
|
|
int tty_check(int fd)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
struct termios tty;
|
|
Sfio_t *sp;
|
|
ep->e_savefd = -1;
|
|
if(fd < 0 || fd > sh.lim.open_max || sh.fdstatus[fd] == IOCLOSE
|
|
|| (sp = sh.sftable[fd]) && (sfset(sp,0,0) & SF_STRING))
|
|
return(0);
|
|
return(tty_get(fd,&tty)==0);
|
|
}
|
|
|
|
/*
|
|
* Get the current terminal attributes
|
|
* This routine remembers the attributes and just returns them if it
|
|
* is called again without an intervening tty_set()
|
|
*/
|
|
|
|
int tty_get(register int fd, register struct termios *tty)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
if(fd == ep->e_savefd)
|
|
*tty = ep->e_savetty;
|
|
else
|
|
{
|
|
while(tcgetattr(fd,tty) == SYSERR)
|
|
{
|
|
if(errno !=EINTR)
|
|
return(SYSERR);
|
|
errno = 0;
|
|
}
|
|
/* save terminal settings if in canonical state */
|
|
if(ep->e_raw==0)
|
|
{
|
|
ep->e_savetty = *tty;
|
|
ep->e_savefd = fd;
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Set the terminal attributes
|
|
* If fd<0, then current attributes are invalidated
|
|
*/
|
|
|
|
int tty_set(int fd, int action, struct termios *tty)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
if(fd >=0)
|
|
{
|
|
while(tcsetattr(fd, action, tty) == SYSERR)
|
|
{
|
|
if(errno !=EINTR)
|
|
return(SYSERR);
|
|
errno = 0;
|
|
}
|
|
ep->e_savetty = *tty;
|
|
}
|
|
ep->e_savefd = fd;
|
|
return(0);
|
|
}
|
|
|
|
/*{ TTY_COOKED( fd )
|
|
*
|
|
* This routine will set the tty in cooked mode.
|
|
* It is also called by error.done().
|
|
*
|
|
}*/
|
|
|
|
void tty_cooked(register int fd)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
if(sh.st.trap[SH_KEYTRAP] && savelex)
|
|
memcpy(sh.lex_context,savelex,sizeof(Lex_t));
|
|
ep->e_keytrap = 0;
|
|
if(ep->e_raw==0)
|
|
return;
|
|
if(fd < 0)
|
|
fd = ep->e_savefd;
|
|
#ifdef L_MASK
|
|
/* restore flags */
|
|
if(l_changed&L_MASK)
|
|
ioctl(fd,TIOCLSET,&l_mask);
|
|
if(l_changed&T_CHARS)
|
|
/* restore alternate break character */
|
|
ioctl(fd,TIOCSETC,&l_ttychars);
|
|
if(l_changed&L_CHARS)
|
|
/* restore alternate break character */
|
|
ioctl(fd,TIOCSLTC,&l_chars);
|
|
l_changed = 0;
|
|
#endif /* L_MASK */
|
|
/*** don't do tty_set unless ttyparm has valid data ***/
|
|
if(tty_set(fd, TCSANOW, &ttyparm) == SYSERR)
|
|
return;
|
|
ep->e_raw = 0;
|
|
return;
|
|
}
|
|
|
|
/*{ TTY_RAW( fd )
|
|
*
|
|
* This routine will set the tty in raw mode.
|
|
*
|
|
}*/
|
|
|
|
int tty_raw(register int fd, int echomode)
|
|
{
|
|
int echo = echomode;
|
|
#ifdef L_MASK
|
|
struct ltchars lchars;
|
|
#endif /* L_MASK */
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
if(ep->e_raw==RAWMODE)
|
|
return(echo?-1:0);
|
|
else if(ep->e_raw==ECHOMODE)
|
|
return(echo?0:-1);
|
|
#if !SHOPT_RAWONLY
|
|
if(ep->e_raw != ALTMODE)
|
|
#endif /* SHOPT_RAWONLY */
|
|
{
|
|
if(tty_get(fd,&ttyparm) == SYSERR)
|
|
return(-1);
|
|
}
|
|
#if L_MASK || VENIX
|
|
if(ttyparm.sg_flags&LCASE)
|
|
return(-1);
|
|
if(!(ttyparm.sg_flags&ECHO))
|
|
{
|
|
if(!echomode)
|
|
return(-1);
|
|
echo = 0;
|
|
}
|
|
nttyparm = ttyparm;
|
|
if(!echo)
|
|
nttyparm.sg_flags &= ~(ECHO | TBDELAY);
|
|
# ifdef CBREAK
|
|
nttyparm.sg_flags |= CBREAK;
|
|
# else
|
|
nttyparm.sg_flags |= RAW;
|
|
# endif /* CBREAK */
|
|
ep->e_erase = ttyparm.sg_erase;
|
|
ep->e_kill = ttyparm.sg_kill;
|
|
ep->e_eof = cntl('D');
|
|
ep->e_werase = cntl('W');
|
|
ep->e_lnext = cntl('V');
|
|
if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
|
|
return(-1);
|
|
ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
|
|
# ifdef TIOCGLTC
|
|
/* try to remove effect of ^V and ^Y and ^O */
|
|
if(ioctl(fd,TIOCGLTC,&l_chars) != SYSERR)
|
|
{
|
|
lchars = l_chars;
|
|
lchars.t_lnextc = -1;
|
|
lchars.t_flushc = -1;
|
|
lchars.t_dsuspc = -1; /* no delayed stop process signal */
|
|
if(ioctl(fd,TIOCSLTC,&lchars) != SYSERR)
|
|
l_changed |= L_CHARS;
|
|
}
|
|
# endif /* TIOCGLTC */
|
|
#else
|
|
if (!(ttyparm.c_lflag & ECHO ))
|
|
{
|
|
if(!echomode)
|
|
return(-1);
|
|
echo = 0;
|
|
}
|
|
# ifdef FLUSHO
|
|
ttyparm.c_lflag &= ~FLUSHO;
|
|
# endif /* FLUSHO */
|
|
nttyparm = ttyparm;
|
|
# ifndef u370
|
|
nttyparm.c_iflag &= ~(IGNPAR|PARMRK|INLCR|IGNCR|ICRNL);
|
|
nttyparm.c_iflag |= BRKINT;
|
|
# else
|
|
nttyparm.c_iflag &=
|
|
~(IGNBRK|PARMRK|INLCR|IGNCR|ICRNL|INPCK);
|
|
nttyparm.c_iflag |= (BRKINT|IGNPAR);
|
|
# endif /* u370 */
|
|
if(echo)
|
|
nttyparm.c_lflag &= ~(ICANON);
|
|
else
|
|
nttyparm.c_lflag &= ~(ICANON|ISIG|ECHO|ECHOK);
|
|
nttyparm.c_cc[VTIME] = 0;
|
|
nttyparm.c_cc[VMIN] = 1;
|
|
# ifdef VREPRINT
|
|
nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
|
|
# endif /* VREPRINT */
|
|
# ifdef VDISCARD
|
|
nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
|
|
# endif /* VDISCARD */
|
|
# ifdef VDSUSP
|
|
nttyparm.c_cc[VDSUSP] = _POSIX_DISABLE;
|
|
# endif /* VDSUSP */
|
|
# ifdef VWERASE
|
|
if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
|
|
ep->e_werase = cntl('W');
|
|
else
|
|
ep->e_werase = nttyparm.c_cc[VWERASE];
|
|
nttyparm.c_cc[VWERASE] = _POSIX_DISABLE;
|
|
# else
|
|
ep->e_werase = cntl('W');
|
|
# endif /* VWERASE */
|
|
# ifdef VLNEXT
|
|
if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
|
|
ep->e_lnext = cntl('V');
|
|
else
|
|
ep->e_lnext = nttyparm.c_cc[VLNEXT];
|
|
nttyparm.c_cc[VLNEXT] = _POSIX_DISABLE;
|
|
# else
|
|
ep->e_lnext = cntl('V');
|
|
# endif /* VLNEXT */
|
|
ep->e_intr = ttyparm.c_cc[VINTR];
|
|
ep->e_eof = ttyparm.c_cc[VEOF];
|
|
ep->e_erase = ttyparm.c_cc[VERASE];
|
|
ep->e_kill = ttyparm.c_cc[VKILL];
|
|
if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
|
|
return(-1);
|
|
ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
|
|
#endif
|
|
ep->e_raw = (echomode?ECHOMODE:RAWMODE);
|
|
return(0);
|
|
}
|
|
|
|
#if !SHOPT_RAWONLY
|
|
|
|
/*
|
|
*
|
|
* Get tty parameters and make ESC and '\r' wakeup characters.
|
|
*
|
|
*/
|
|
|
|
# ifdef TIOCGETC
|
|
int tty_alt(register int fd)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
int mask;
|
|
struct tchars ttychars;
|
|
switch(ep->e_raw)
|
|
{
|
|
case ECHOMODE:
|
|
return(-1);
|
|
case ALTMODE:
|
|
return(0);
|
|
case RAWMODE:
|
|
tty_cooked(fd);
|
|
}
|
|
l_changed = 0;
|
|
if( ep->e_ttyspeed == 0)
|
|
{
|
|
if((tty_get(fd,&ttyparm) != SYSERR))
|
|
ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
|
|
ep->e_raw = ALTMODE;
|
|
}
|
|
if(ioctl(fd,TIOCGETC,&l_ttychars) == SYSERR)
|
|
return(-1);
|
|
if(ioctl(fd,TIOCLGET,&l_mask)==SYSERR)
|
|
return(-1);
|
|
ttychars = l_ttychars;
|
|
mask = LCRTBS|LCRTERA|LCTLECH|LPENDIN|LCRTKIL;
|
|
if((l_mask|mask) != l_mask)
|
|
l_changed = L_MASK;
|
|
if(ioctl(fd,TIOCLBIS,&mask)==SYSERR)
|
|
return(-1);
|
|
if(ttychars.t_brkc!=ESC)
|
|
{
|
|
ttychars.t_brkc = ESC;
|
|
l_changed |= T_CHARS;
|
|
if(ioctl(fd,TIOCSETC,&ttychars) == SYSERR)
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
# else
|
|
# ifndef PENDIN
|
|
# define PENDIN 0
|
|
# endif /* PENDIN */
|
|
# ifndef IEXTEN
|
|
# define IEXTEN 0
|
|
# endif /* IEXTEN */
|
|
|
|
int tty_alt(register int fd)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
switch(ep->e_raw)
|
|
{
|
|
case ECHOMODE:
|
|
return(-1);
|
|
case ALTMODE:
|
|
return(0);
|
|
case RAWMODE:
|
|
tty_cooked(fd);
|
|
}
|
|
if((tty_get(fd, &ttyparm)==SYSERR) || (!(ttyparm.c_lflag&ECHO)))
|
|
return(-1);
|
|
# ifdef FLUSHO
|
|
ttyparm.c_lflag &= ~FLUSHO;
|
|
# endif /* FLUSHO */
|
|
nttyparm = ttyparm;
|
|
ep->e_eof = ttyparm.c_cc[VEOF];
|
|
# ifdef ECHOCTL
|
|
/* escape character echos as ^[ */
|
|
nttyparm.c_lflag |= (ECHOE|ECHOK|ECHOCTL|PENDIN|IEXTEN);
|
|
nttyparm.c_cc[VEOL] = ESC;
|
|
# else
|
|
/* switch VEOL2 and EOF, since EOF isn't echo'd by driver */
|
|
nttyparm.c_lflag |= (ECHOE|ECHOK);
|
|
nttyparm.c_cc[VEOF] = ESC; /* make ESC the eof char */
|
|
# ifdef VEOL2
|
|
nttyparm.c_iflag &= ~(IGNCR|ICRNL);
|
|
nttyparm.c_iflag |= INLCR;
|
|
nttyparm.c_cc[VEOL] = '\r'; /* make CR an eol char */
|
|
nttyparm.c_cc[VEOL2] = ep->e_eof; /* make EOF an eol char */
|
|
# else
|
|
nttyparm.c_cc[VEOL] = ep->e_eof; /* make EOF an eol char */
|
|
# endif /* VEOL2 */
|
|
# endif /* ECHOCTL */
|
|
# ifdef VREPRINT
|
|
nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
|
|
# endif /* VREPRINT */
|
|
# ifdef VDISCARD
|
|
nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
|
|
# endif /* VDISCARD */
|
|
# ifdef VWERASE
|
|
if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
|
|
nttyparm.c_cc[VWERASE] = cntl('W');
|
|
ep->e_werase = nttyparm.c_cc[VWERASE];
|
|
# else
|
|
ep->e_werase = cntl('W');
|
|
# endif /* VWERASE */
|
|
# ifdef VLNEXT
|
|
if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
|
|
nttyparm.c_cc[VLNEXT] = cntl('V');
|
|
ep->e_lnext = nttyparm.c_cc[VLNEXT];
|
|
# else
|
|
ep->e_lnext = cntl('V');
|
|
# endif /* VLNEXT */
|
|
ep->e_erase = ttyparm.c_cc[VERASE];
|
|
ep->e_kill = ttyparm.c_cc[VKILL];
|
|
if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
|
|
return(-1);
|
|
ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
|
|
ep->e_raw = ALTMODE;
|
|
return(0);
|
|
}
|
|
|
|
# endif /* TIOCGETC */
|
|
#endif /* SHOPT_RAWONLY */
|
|
|
|
/*
|
|
* ED_WINDOW()
|
|
*
|
|
* return the window size
|
|
*/
|
|
int ed_window(void)
|
|
{
|
|
int rows,cols;
|
|
register char *cp = nv_getval(COLUMNS);
|
|
if(cp)
|
|
cols = (int)strtol(cp, (char**)0, 10)-1;
|
|
else
|
|
{
|
|
astwinsize(2,&rows,&cols);
|
|
if(--cols <0)
|
|
cols = DFLTWINDOW-1;
|
|
}
|
|
if(cols < MINWINDOW)
|
|
cols = MINWINDOW;
|
|
else if(cols > MAXWINDOW)
|
|
cols = MAXWINDOW;
|
|
return(cols);
|
|
}
|
|
|
|
/* E_FLUSH()
|
|
*
|
|
* Flush the output buffer.
|
|
*
|
|
*/
|
|
|
|
void ed_flush(Edit_t *ep)
|
|
{
|
|
register int n = ep->e_outptr-ep->e_outbase;
|
|
register int fd = ERRIO;
|
|
if(n<=0)
|
|
return;
|
|
write(fd,ep->e_outbase,(unsigned)n);
|
|
ep->e_outptr = ep->e_outbase;
|
|
}
|
|
|
|
/*
|
|
* send the bell character ^G to the terminal
|
|
*/
|
|
|
|
void ed_ringbell(void)
|
|
{
|
|
write(ERRIO,bellchr,1);
|
|
}
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
/*
|
|
* send a carriage return line feed to the terminal
|
|
*/
|
|
|
|
void ed_crlf(register Edit_t *ep)
|
|
{
|
|
#ifdef cray
|
|
ed_putchar(ep,'\r');
|
|
#endif /* cray */
|
|
#ifdef u370
|
|
ed_putchar(ep,'\r');
|
|
#endif /* u370 */
|
|
#ifdef VENIX
|
|
ed_putchar(ep,'\r');
|
|
#endif /* VENIX */
|
|
ed_putchar(ep,'\n');
|
|
ed_flush(ep);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
/* ED_SETUP( max_prompt_size )
|
|
*
|
|
* This routine sets up the prompt string
|
|
* The following is an unadvertised feature.
|
|
* Escape sequences in the prompt can be excluded from the calculated
|
|
* prompt length. This is accomplished as follows:
|
|
* - if the prompt string starts with "%\r, or contains \r%\r", where %
|
|
* represents any char, then % is taken to be the quote character.
|
|
* - strings enclosed by this quote character, and the quote character,
|
|
* are not counted as part of the prompt length.
|
|
*/
|
|
|
|
void ed_setup(register Edit_t *ep, int fd, int reedit)
|
|
{
|
|
register char *pp;
|
|
register char *last, *prev;
|
|
char *ppmax;
|
|
int myquote = 0, n;
|
|
register int qlen = 1, qwid;
|
|
char inquote = 0;
|
|
ep->e_fd = fd;
|
|
#if SHOPT_ESH && SHOPT_VSH
|
|
ep->e_multiline = sh_isoption(SH_MULTILINE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS) || sh_isoption(SH_VI));
|
|
#elif SHOPT_ESH
|
|
ep->e_multiline = sh_isoption(SH_MULTILINE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS));
|
|
#else
|
|
ep->e_multiline = sh_isoption(SH_MULTILINE) && sh_isoption(SH_VI);
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
if(!(sh.sigflag[SIGWINCH]&SH_SIGFAULT))
|
|
{
|
|
signal(SIGWINCH,sh_fault);
|
|
sh.sigflag[SIGWINCH] |= SH_SIGFAULT;
|
|
}
|
|
pp = sh.st.trapcom[SIGWINCH];
|
|
sh.st.trapcom[SIGWINCH] = 0;
|
|
sh_fault(SIGWINCH);
|
|
sh.st.trapcom[SIGWINCH] = pp;
|
|
sh.winch = 0;
|
|
#endif
|
|
#if SHOPT_EDPREDICT
|
|
ep->hlist = 0;
|
|
ep->nhlist = 0;
|
|
ep->hoff = 0;
|
|
#endif /* SHOPT_EDPREDICT */
|
|
ep->e_stkoff = staktell();
|
|
ep->e_stkptr = stakfreeze(0);
|
|
if(!(last = sh.prompt))
|
|
last = "";
|
|
sh.prompt = 0;
|
|
if(sh.hist_ptr)
|
|
{
|
|
register History_t *hp = sh.hist_ptr;
|
|
ep->e_hismax = hist_max(hp);
|
|
ep->e_hismin = hist_min(hp);
|
|
}
|
|
else
|
|
{
|
|
ep->e_hismax = ep->e_hismin = ep->e_hloff = 0;
|
|
}
|
|
ep->e_hline = ep->e_hismax;
|
|
#if SHOPT_ESH && SHOPT_VSH
|
|
if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
|
#elif SHOPT_ESH
|
|
if(!sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
|
#elif SHOPT_VSH
|
|
if(!sh_isoption(SH_VI))
|
|
#else
|
|
if(1)
|
|
#endif /* SHOPT_ESH && SHOPT_VSH */
|
|
ep->e_wsize = MAXLINE;
|
|
else
|
|
ep->e_wsize = ed_window()-2;
|
|
ep->e_winsz = ep->e_wsize+2;
|
|
ep->e_crlf = 1;
|
|
ep->e_plen = 0;
|
|
/*
|
|
* Prepare e_prompt buffer for use when redrawing the command line.
|
|
* Use only the last line of the potentially multi-line prompt.
|
|
*/
|
|
pp = ep->e_prompt;
|
|
ppmax = pp+PRSIZE-1;
|
|
*pp++ = '\r';
|
|
{
|
|
register int c;
|
|
while(prev = last, c = mbchar(last)) switch(c)
|
|
{
|
|
case ESC:
|
|
{
|
|
int skip=0;
|
|
if(*last == ']')
|
|
{
|
|
/*
|
|
* Cut out dtterm/xterm Operating System Commands that set window/icon title, etc.
|
|
* See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
|
*/
|
|
char *cp = last + 1;
|
|
while(*cp >= '0' && *cp <= '9')
|
|
cp++;
|
|
if(*cp++ == ';')
|
|
{
|
|
while(c = *cp++)
|
|
{
|
|
if(c == '\a') /* legacy terminator */
|
|
break;
|
|
if(c == ESC && *cp == '\\') /* recommended terminator */
|
|
{
|
|
cp++;
|
|
break;
|
|
}
|
|
}
|
|
if(!c)
|
|
break;
|
|
last = cp;
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Try to add the length of included escape sequences to qlen
|
|
* which is subtracted from the physical length of the prompt.
|
|
*/
|
|
ep->e_crlf = 0;
|
|
if(pp<ppmax)
|
|
*pp++ = ESC;
|
|
for(n=1; c = *last++; n++)
|
|
{
|
|
if(pp < ppmax)
|
|
*pp++ = c;
|
|
if(c=='\a' || c==ESC || c=='\r')
|
|
break;
|
|
if(skip || (c>='0' && c<='9'))
|
|
{
|
|
skip = 0;
|
|
continue;
|
|
}
|
|
if(n==3 && (c=='?' || c=='!'))
|
|
continue;
|
|
else if(n>1 && c==';')
|
|
skip = 1;
|
|
else if(n>2 || (c!='[' && c!=']' && c!='('))
|
|
break;
|
|
}
|
|
if(c==0 || c==ESC || c=='\r')
|
|
last--;
|
|
qlen += (n+1);
|
|
break;
|
|
}
|
|
case '\b':
|
|
if(pp>ep->e_prompt+1)
|
|
pp--;
|
|
break;
|
|
case '\r':
|
|
if(pp == (ep->e_prompt+2)) /* quote char */
|
|
myquote = *(pp-1);
|
|
/* FALLTHROUGH */
|
|
|
|
case '\n':
|
|
/* start again */
|
|
ep->e_crlf = 1;
|
|
qlen = 1;
|
|
inquote = 0;
|
|
pp = ep->e_prompt+1;
|
|
break;
|
|
|
|
case '\t':
|
|
/* expand tabs */
|
|
while((pp-ep->e_prompt)%TABSIZE)
|
|
{
|
|
if(pp >= ppmax)
|
|
break;
|
|
*pp++ = ' ';
|
|
}
|
|
break;
|
|
|
|
case '\a':
|
|
/* cut out bells */
|
|
break;
|
|
|
|
default:
|
|
if(c==myquote)
|
|
{
|
|
qlen += inquote;
|
|
inquote ^= 1;
|
|
}
|
|
if(pp < ppmax)
|
|
{
|
|
if(inquote)
|
|
qlen++;
|
|
else if(!is_print(c))
|
|
ep->e_crlf = 0;
|
|
if((qwid = last - prev) > 1)
|
|
qlen += qwid - mbwidth(c);
|
|
while(prev < last && pp < ppmax)
|
|
*pp++ = *prev++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(pp-ep->e_prompt > qlen)
|
|
ep->e_plen = pp - ep->e_prompt - qlen;
|
|
*pp = 0;
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
if(!ep->e_multiline && (ep->e_wsize -= ep->e_plen) < 7)
|
|
{
|
|
register int shift = 7-ep->e_wsize;
|
|
ep->e_wsize = 7;
|
|
pp = ep->e_prompt+1;
|
|
strcpy(pp,pp+shift);
|
|
ep->e_plen -= shift;
|
|
last[-ep->e_plen-2] = '\r';
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
sfsync(sfstderr);
|
|
if(fd == sffileno(sfstderr))
|
|
{
|
|
/* can't use output buffer when reading from stderr */
|
|
static char *buff;
|
|
if(!buff)
|
|
buff = (char*)sh_malloc(MAXLINE);
|
|
ep->e_outbase = ep->e_outptr = buff;
|
|
ep->e_outlast = ep->e_outptr + MAXLINE;
|
|
return;
|
|
}
|
|
qlen = sfset(sfstderr,SF_READ,0);
|
|
/* make sure SF_READ not on */
|
|
ep->e_outbase = ep->e_outptr = (char*)sfreserve(sfstderr,SF_UNBOUND,SF_LOCKR);
|
|
ep->e_outlast = ep->e_outptr + sfvalue(sfstderr);
|
|
if(qlen)
|
|
sfset(sfstderr,SF_READ,1);
|
|
sfwrite(sfstderr,ep->e_outptr,0);
|
|
ep->e_eol = reedit;
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
if(ep->e_multiline)
|
|
{
|
|
#if defined(_pth_tput) && (_tput_terminfo || _tput_termcap)
|
|
char *term;
|
|
if(!ep->e_term)
|
|
ep->e_term = nv_search("TERM",sh.var_tree,0);
|
|
if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname))
|
|
{
|
|
Shopt_t o = sh.options;
|
|
sigblock(SIGINT);
|
|
sh_offoption(SH_RESTRICTED);
|
|
sh_offoption(SH_VERBOSE);
|
|
sh_offoption(SH_XTRACE);
|
|
/* get the cursor up sequence from tput */
|
|
#if _tput_terminfo
|
|
sh_trap(".sh.subscript=$(" _pth_tput " cuu1 2>/dev/null)",0);
|
|
#elif _tput_termcap
|
|
sh_trap(".sh.subscript=$(" _pth_tput " up 2>/dev/null)",0);
|
|
#else
|
|
#error no tput method
|
|
#endif
|
|
if((pp = nv_getval(SH_SUBSCRNOD)) && strlen(pp) < sizeof(CURSOR_UP))
|
|
strcpy(CURSOR_UP,pp);
|
|
else
|
|
CURSOR_UP[0] = '\0'; /* no escape sequence is better than a faulty one */
|
|
nv_unset(SH_SUBSCRNOD);
|
|
strcpy(ep->e_termname,term);
|
|
sh.options = o;
|
|
sigrelease(SIGINT);
|
|
}
|
|
#endif
|
|
ep->e_wsize = MAXLINE - (ep->e_plen+1);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
if(ep->e_default && (pp = nv_getval(ep->e_default)))
|
|
{
|
|
n = strlen(pp);
|
|
if(n > LOOKAHEAD)
|
|
n = LOOKAHEAD;
|
|
ep->e_lookahead = n;
|
|
while(n-- > 0)
|
|
ep->e_lbuf[n] = *pp++;
|
|
ep->e_default = 0;
|
|
}
|
|
if(sh.st.trap[SH_KEYTRAP])
|
|
{
|
|
if(!savelex)
|
|
savelex = (Lex_t*)sh_malloc(sizeof(Lex_t));
|
|
memcpy(savelex, sh.lex_context, sizeof(Lex_t));
|
|
}
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
static void ed_putstring(register Edit_t *ep, const char *str)
|
|
{
|
|
register int c;
|
|
while(c = *str++)
|
|
ed_putchar(ep,c);
|
|
}
|
|
|
|
static void ed_nputchar(register Edit_t *ep, int n, int c)
|
|
{
|
|
while(n-->0)
|
|
ed_putchar(ep,c);
|
|
}
|
|
|
|
/*
|
|
* Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set
|
|
* Use select(2) (via sfpkrd()) to wait for input if possible
|
|
*
|
|
* The return value is the number of bytes read, or < 0 for EOF.
|
|
*
|
|
* Unfortunately, systems that get interrupted from slow reads update
|
|
* this access time for the terminal (in violation of POSIX).
|
|
* The fixtime() macro resets the time to the time at entry in
|
|
* this case. This is not necessary for systems that have select().
|
|
*/
|
|
int ed_read(void *context, int fd, char *buff, int size, int reedit)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)context;
|
|
register int rv= -1;
|
|
register int delim = ((ep->e_raw&RAWMODE)?nttyparm.c_cc[VEOL]:'\n');
|
|
int mode = -1;
|
|
int (*waitevent)(int,long,int) = sh.waitevent;
|
|
/* sfpkrd must use select(2) to intercept SIGWINCH for ed_read */
|
|
if(ep->e_raw==ALTMODE)
|
|
mode = 2;
|
|
if(size < 0)
|
|
{
|
|
mode = 2;
|
|
size = -size;
|
|
}
|
|
sh_onstate(SH_TTYWAIT);
|
|
errno = EINTR;
|
|
sh.waitevent = 0;
|
|
while(rv<0 && errno==EINTR)
|
|
{
|
|
if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP))
|
|
goto done;
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
#if SHOPT_ESH && SHOPT_VSH
|
|
if(sh.winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
|
|
#elif SHOPT_ESH
|
|
if(sh.winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
|
|
#else
|
|
if(sh.winch && sh_isstate(SH_INTERACTIVE) && sh_isoption(SH_VI))
|
|
#endif
|
|
{
|
|
/* redraw the prompt after receiving SIGWINCH */
|
|
Edpos_t lastpos;
|
|
int n, rows, newsize;
|
|
/* move cursor to start of first line */
|
|
ed_putchar(ep,'\r');
|
|
ed_flush(ep);
|
|
astwinsize(2,&rows,&newsize);
|
|
n = (ep->e_plen+ep->e_cur)/++ep->e_winsz;
|
|
while(n--)
|
|
ed_putstring(ep,CURSOR_UP);
|
|
if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz))
|
|
{
|
|
/* clear the current command line */
|
|
n = lastpos.line;
|
|
while(lastpos.line--)
|
|
{
|
|
ed_nputchar(ep,ep->e_winsz,' ');
|
|
ed_putchar(ep,'\n');
|
|
}
|
|
ed_nputchar(ep,ep->e_winsz,' ');
|
|
while(n--)
|
|
ed_putstring(ep,CURSOR_UP);
|
|
}
|
|
ed_flush(ep);
|
|
sh_delay(.05,0);
|
|
astwinsize(2,&rows,&newsize);
|
|
ep->e_winsz = newsize-1;
|
|
if(ep->e_winsz < MINWINDOW)
|
|
ep->e_winsz = MINWINDOW;
|
|
if(!ep->e_multiline && ep->e_wsize < MAXLINE)
|
|
ep->e_wsize = ep->e_winsz-2;
|
|
#if SHOPT_ESH && SHOPT_VSH
|
|
if(sh_isoption(SH_VI))
|
|
vi_redraw(ep->e_vi);
|
|
else
|
|
emacs_redraw(ep->e_emacs);
|
|
#elif SHOPT_VSH
|
|
vi_redraw(ep->e_vi);
|
|
#elif SHOPT_ESH
|
|
emacs_redraw(ep->e_emacs);
|
|
#endif
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
sh.winch = 0;
|
|
/* an interrupt that should be ignored */
|
|
errno = 0;
|
|
if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
|
|
rv = sfpkrd(fd,buff,size,delim,-1L,mode);
|
|
}
|
|
if(rv < 0)
|
|
{
|
|
#ifdef _hdr_utime
|
|
# define fixtime() if(isdevtty)utime(ep->e_tty,&utimes)
|
|
int isdevtty=0;
|
|
struct stat statb;
|
|
struct utimbuf utimes;
|
|
if(errno==0 && !ep->e_tty)
|
|
{
|
|
if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0)
|
|
{
|
|
ep->e_tty_ino = statb.st_ino;
|
|
ep->e_tty_dev = statb.st_dev;
|
|
}
|
|
}
|
|
if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev)
|
|
{
|
|
utimes.actime = statb.st_atime;
|
|
utimes.modtime = statb.st_mtime;
|
|
isdevtty=1;
|
|
}
|
|
#else
|
|
# define fixtime()
|
|
#endif /* _hdr_utime */
|
|
while(1)
|
|
{
|
|
rv = read(fd,buff,size);
|
|
if(rv>=0 || errno!=EINTR)
|
|
break;
|
|
if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP))
|
|
goto done;
|
|
/* an interrupt that should be ignored */
|
|
fixtime();
|
|
}
|
|
}
|
|
else if(rv>=0 && mode>0)
|
|
rv = read(fd,buff,rv>0?rv:1);
|
|
done:
|
|
sh.waitevent = waitevent;
|
|
sh_offstate(SH_TTYWAIT);
|
|
return(rv);
|
|
}
|
|
|
|
|
|
/*
|
|
* put <string> of length <nbyte> onto lookahead stack
|
|
* if <type> is non-zero, the negation of the character is put
|
|
* onto the stack so that it can be checked for KEYTRAP
|
|
* putstack() returns 1 except when in the middle of a multi-byte char
|
|
*/
|
|
static int putstack(Edit_t *ep,char string[], register int nbyte, int type)
|
|
{
|
|
register int c;
|
|
#if SHOPT_MULTIBYTE
|
|
char *endp, *p=string;
|
|
int size, offset = ep->e_lookahead + nbyte;
|
|
*(endp = &p[nbyte]) = 0;
|
|
endp = &p[nbyte];
|
|
do
|
|
{
|
|
c = (int)((*p) & STRIP);
|
|
if(c< 0x80 && c!='<')
|
|
{
|
|
if (type)
|
|
c = -c;
|
|
# ifndef CBREAK
|
|
if(c == '\0')
|
|
{
|
|
/*** user break key ***/
|
|
ep->e_lookahead = 0;
|
|
sh_fault(SIGINT);
|
|
siglongjmp(ep->e_env, UINTR);
|
|
}
|
|
# endif /* CBREAK */
|
|
|
|
}
|
|
else
|
|
{
|
|
again:
|
|
if((c=mbchar(p)) >=0)
|
|
{
|
|
p--; /* incremented below */
|
|
if(type)
|
|
c = -c;
|
|
}
|
|
#ifdef EILSEQ
|
|
else if(errno == EILSEQ)
|
|
errno = 0;
|
|
#endif
|
|
else if((endp-p) < mbmax())
|
|
{
|
|
if ((c=ed_read(ep,ep->e_fd,endp, 1,0)) == 1)
|
|
{
|
|
*++endp = 0;
|
|
goto again;
|
|
}
|
|
return(c);
|
|
}
|
|
else
|
|
{
|
|
ed_ringbell();
|
|
c = -(int)((*p) & STRIP);
|
|
offset += mbmax()-1;
|
|
}
|
|
}
|
|
ep->e_lbuf[--offset] = c;
|
|
p++;
|
|
}
|
|
while (p < endp);
|
|
/* shift lookahead buffer if necessary */
|
|
if(offset -= ep->e_lookahead)
|
|
{
|
|
for(size=offset;size < nbyte;size++)
|
|
ep->e_lbuf[ep->e_lookahead+size-offset] = ep->e_lbuf[ep->e_lookahead+size];
|
|
}
|
|
ep->e_lookahead += nbyte-offset;
|
|
#else
|
|
while (nbyte > 0)
|
|
{
|
|
c = string[--nbyte] & STRIP;
|
|
ep->e_lbuf[ep->e_lookahead++] = (type?-c:c);
|
|
# ifndef CBREAK
|
|
if( c == '\0' )
|
|
{
|
|
/*** user break key ***/
|
|
ep->e_lookahead = 0;
|
|
sh_fault(SIGINT);
|
|
siglongjmp(ep->e_env, UINTR);
|
|
}
|
|
# endif /* CBREAK */
|
|
}
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* routine to perform read from terminal for vi and emacs mode
|
|
* <mode> can be one of the following:
|
|
* -2 vi insert mode - key binding is in effect
|
|
* -1 vi control mode - key binding is in effect
|
|
* 0 normal command mode - key binding is in effect
|
|
* 1 edit keys not mapped
|
|
* 2 Next key is literal
|
|
*/
|
|
int ed_getchar(register Edit_t *ep,int mode)
|
|
{
|
|
register int n, c;
|
|
char readin[LOOKAHEAD+1];
|
|
if(!ep->e_lookahead)
|
|
{
|
|
ed_flush(ep);
|
|
ep->e_inmacro = 0;
|
|
*ep->e_vi_insert = (mode==-2);
|
|
if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
|
|
n = putstack(ep,readin,n,1);
|
|
*ep->e_vi_insert = 0;
|
|
}
|
|
if(ep->e_lookahead)
|
|
{
|
|
/* check for possible key mapping */
|
|
if((c = ep->e_lbuf[--ep->e_lookahead]) < 0)
|
|
{
|
|
if(mode<=0 && -c == ep->e_intr)
|
|
{
|
|
killpg(getpgrp(),SIGINT);
|
|
siglongjmp(ep->e_env, UINTR);
|
|
}
|
|
if(mode<=0 && sh.st.trap[SH_KEYTRAP]
|
|
/* workaround for <https://github.com/ksh93/ksh/issues/307>:
|
|
* do not trigger KEYBD for non-ASCII in multibyte locale */
|
|
&& (CC_NATIVE!=CC_ASCII || !mbwide() || c > -128))
|
|
{
|
|
ep->e_keytrap = 1;
|
|
n=1;
|
|
if((readin[0]= -c) == ESC)
|
|
{
|
|
while(1)
|
|
{
|
|
if(!ep->e_lookahead)
|
|
{
|
|
if((c=sfpkrd(ep->e_fd,readin+n,1,'\r',(mode?400L:-1L),0))>0)
|
|
putstack(ep,readin+n,c,1);
|
|
}
|
|
if(!ep->e_lookahead)
|
|
break;
|
|
if((c=ep->e_lbuf[--ep->e_lookahead])>=0)
|
|
{
|
|
ep->e_lookahead++;
|
|
break;
|
|
}
|
|
c = -c;
|
|
readin[n++] = c;
|
|
if(c>='0' && c<='9' && n>2)
|
|
continue;
|
|
if(n>2 || (c!= '[' && c!= 'O'))
|
|
break;
|
|
}
|
|
}
|
|
if(n=keytrap(ep,readin,n,LOOKAHEAD-n,mode))
|
|
{
|
|
putstack(ep,readin,n,0);
|
|
c = ep->e_lbuf[--ep->e_lookahead];
|
|
}
|
|
else
|
|
c = ed_getchar(ep,mode);
|
|
ep->e_keytrap = 0;
|
|
}
|
|
else
|
|
c = -c;
|
|
}
|
|
/*** map '\r' to '\n' ***/
|
|
if(c == '\r' && mode!=2)
|
|
c = '\n';
|
|
if(ep->e_tabcount && !(c=='\t'||c==ESC || c=='\\' || c=='=' || c==cntl('L') || isdigit(c)))
|
|
ep->e_tabcount = 0;
|
|
}
|
|
else
|
|
siglongjmp(ep->e_env,(n==0?UEOF:UINTR));
|
|
return(c);
|
|
}
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
void ed_ungetchar(Edit_t *ep,register int c)
|
|
{
|
|
if (ep->e_lookahead < LOOKAHEAD)
|
|
ep->e_lbuf[ep->e_lookahead++] = c;
|
|
return;
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
/*
|
|
* put a character into the output buffer
|
|
*/
|
|
|
|
void ed_putchar(register Edit_t *ep,register int c)
|
|
{
|
|
char buf[8];
|
|
register char *dp = ep->e_outptr;
|
|
register int i,size=1;
|
|
if(!dp)
|
|
return;
|
|
buf[0] = c;
|
|
#if SHOPT_MULTIBYTE
|
|
/* check for place holder */
|
|
if(c == MARKER)
|
|
return;
|
|
if((size = mbconv(buf, (wchar_t)c)) > 1)
|
|
{
|
|
for (i = 0; i < (size-1); i++)
|
|
*dp++ = buf[i];
|
|
c = buf[i];
|
|
}
|
|
else
|
|
{
|
|
buf[0] = c;
|
|
size = 1;
|
|
}
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
if (buf[0] == '_' && size==1)
|
|
{
|
|
*dp++ = ' ';
|
|
*dp++ = '\b';
|
|
}
|
|
*dp++ = c;
|
|
*dp = '\0';
|
|
if(dp >= ep->e_outlast)
|
|
ed_flush(ep);
|
|
else
|
|
ep->e_outptr = dp;
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
/*
|
|
* returns the line and column corresponding to offset <off> in the physical buffer
|
|
* if <cur> is non-zero and <= <off>, then corresponding <curpos> will start the search
|
|
*/
|
|
Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos)
|
|
{
|
|
register genchar *sp=phys;
|
|
register int c=1, col=ep->e_plen;
|
|
Edpos_t pos;
|
|
#if SHOPT_MULTIBYTE
|
|
char p[16];
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
if(cur && off>=cur)
|
|
{
|
|
sp += cur;
|
|
off -= cur;
|
|
pos = curpos;
|
|
col = pos.col;
|
|
}
|
|
else
|
|
{
|
|
pos.line = 0;
|
|
while(col > ep->e_winsz)
|
|
{
|
|
pos.line++;
|
|
col -= (ep->e_winsz+1);
|
|
}
|
|
}
|
|
while(off-->0)
|
|
{
|
|
if(c)
|
|
c = *sp++;
|
|
#if SHOPT_MULTIBYTE
|
|
if(c && (mbconv(p, (wchar_t)c))==1 && p[0]=='\n')
|
|
#else
|
|
if(c=='\n')
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
col = 0;
|
|
else
|
|
col++;
|
|
if(col > ep->e_winsz)
|
|
col = 0;
|
|
if(col==0)
|
|
pos.line++;
|
|
}
|
|
pos.col = col;
|
|
return(pos);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first)
|
|
{
|
|
static int oldline;
|
|
register int delta;
|
|
int clear = 0;
|
|
Edpos_t newpos;
|
|
|
|
delta = new - old;
|
|
if(first < 0)
|
|
{
|
|
first = 0;
|
|
clear = 1;
|
|
}
|
|
if( delta == 0 && !clear)
|
|
return(new);
|
|
if(ep->e_multiline)
|
|
{
|
|
ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos);
|
|
if(clear && old>=ep->e_peol && (clear=ep->e_winsz-ep->e_curpos.col)>0)
|
|
{
|
|
ed_nputchar(ep,clear,' ');
|
|
ed_nputchar(ep,clear,'\b');
|
|
return(new);
|
|
}
|
|
newpos = ed_curpos(ep, physical, new,old,ep->e_curpos);
|
|
if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldline<ep->e_curpos.line && delta<0)
|
|
ed_putstring(ep,"\r\n");
|
|
oldline = newpos.line;
|
|
if(ep->e_curpos.line > newpos.line)
|
|
{
|
|
int n,pline,plen=ep->e_plen;
|
|
for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--)
|
|
ed_putstring(ep,CURSOR_UP);
|
|
pline = plen/(ep->e_winsz+1);
|
|
if(newpos.line <= pline)
|
|
plen -= pline*(ep->e_winsz+1);
|
|
else
|
|
plen = 0;
|
|
if((n=plen- ep->e_curpos.col)>0)
|
|
{
|
|
ep->e_curpos.col += n;
|
|
ed_putchar(ep,'\r');
|
|
if(!ep->e_crlf && pline==0)
|
|
ed_putstring(ep,ep->e_prompt);
|
|
else
|
|
{
|
|
int m = ep->e_winsz+1-plen;
|
|
ed_putchar(ep,'\n');
|
|
n = plen;
|
|
if(m < genlen(physical))
|
|
{
|
|
while(physical[m] && n-->0)
|
|
ed_putchar(ep,physical[m++]);
|
|
}
|
|
ed_nputchar(ep,n,' ');
|
|
ed_putstring(ep,CURSOR_UP);
|
|
}
|
|
}
|
|
}
|
|
else if(ep->e_curpos.line < newpos.line)
|
|
{
|
|
ed_nputchar(ep, newpos.line-ep->e_curpos.line,'\n');
|
|
ep->e_curpos.line = newpos.line;
|
|
ed_putchar(ep,'\r');
|
|
ep->e_curpos.col = 0;
|
|
}
|
|
delta = newpos.col - ep->e_curpos.col;
|
|
old = new - delta;
|
|
}
|
|
else
|
|
newpos.line=0;
|
|
if(delta<0)
|
|
{
|
|
int bs= newpos.line && ep->e_plen>ep->e_winsz;
|
|
/*** move to left ***/
|
|
delta = -delta;
|
|
/*** attempt to optimize cursor movement ***/
|
|
if(!ep->e_crlf || bs || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) )
|
|
{
|
|
ed_nputchar(ep,delta,'\b');
|
|
delta = 0;
|
|
}
|
|
else
|
|
{
|
|
if(newpos.line==0)
|
|
ed_putstring(ep,ep->e_prompt);
|
|
else
|
|
{
|
|
first = 1+(newpos.line*ep->e_winsz - ep->e_plen);
|
|
ed_putchar(ep,'\r');
|
|
}
|
|
old = first;
|
|
delta = new-first;
|
|
}
|
|
}
|
|
while(delta-->0)
|
|
ed_putchar(ep,physical[old++]);
|
|
return(new);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
/*
|
|
* copy virtual to physical and return the index for cursor in physical buffer
|
|
*/
|
|
int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int poff)
|
|
{
|
|
register genchar *sp = virt;
|
|
register genchar *dp = phys;
|
|
register int c;
|
|
genchar *curp = sp + cur;
|
|
genchar *dpmax = phys+MAXLINE;
|
|
int d, r;
|
|
sp += voff;
|
|
dp += poff;
|
|
for(r=poff;c= *sp;sp++)
|
|
{
|
|
if(curp == sp)
|
|
r = dp - phys;
|
|
#if SHOPT_MULTIBYTE
|
|
d = mbwidth((wchar_t)c);
|
|
if(d==1 && is_cntrl(c))
|
|
d = -1;
|
|
if(d>1)
|
|
{
|
|
/* multiple width character put in place holders */
|
|
*dp++ = c;
|
|
while(--d >0)
|
|
*dp++ = MARKER;
|
|
/* in vi mode the cursor is at the last character */
|
|
if(dp>=dpmax)
|
|
break;
|
|
continue;
|
|
}
|
|
else
|
|
#else
|
|
d = (is_cntrl(c)?-1:1);
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
if(d<0)
|
|
{
|
|
if(c=='\t')
|
|
{
|
|
c = dp-phys;
|
|
c += ep->e_plen;
|
|
c = TABSIZE - c%TABSIZE;
|
|
while(--c>0)
|
|
*dp++ = ' ';
|
|
c = ' ';
|
|
}
|
|
else
|
|
{
|
|
*dp++ = '^';
|
|
c = printchar(c);
|
|
}
|
|
if(curp == sp)
|
|
r = dp - phys;
|
|
}
|
|
*dp++ = c;
|
|
if(dp>=dpmax)
|
|
break;
|
|
}
|
|
*dp = 0;
|
|
ep->e_peol = dp-phys;
|
|
return(r);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#if (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE
|
|
/*
|
|
* convert external representation <src> to an array of genchars <dest>
|
|
* <src> and <dest> can be the same
|
|
* returns number of chars in dest
|
|
*/
|
|
|
|
int ed_internal(const char *src, genchar *dest)
|
|
{
|
|
register const unsigned char *cp = (unsigned char *)src;
|
|
register int c;
|
|
register wchar_t *dp = (wchar_t*)dest;
|
|
if(dest == (genchar*)roundof(cp-(unsigned char*)0,sizeof(genchar)))
|
|
{
|
|
genchar buffer[MAXLINE];
|
|
c = ed_internal(src,buffer);
|
|
ed_gencpy((genchar*)dp,buffer);
|
|
return(c);
|
|
}
|
|
while(*cp)
|
|
*dp++ = mbchar(cp);
|
|
*dp = 0;
|
|
return(dp-(wchar_t*)dest);
|
|
}
|
|
#endif /* (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE */
|
|
|
|
#if SHOPT_MULTIBYTE
|
|
/*
|
|
* convert internal representation <src> into character array <dest>.
|
|
* The <src> and <dest> may be the same.
|
|
* returns number of chars in dest.
|
|
*/
|
|
|
|
int ed_external(const genchar *src, char *dest)
|
|
{
|
|
register genchar wc;
|
|
register char *dp = dest;
|
|
char *dpmax = dp+sizeof(genchar)*MAXLINE-2;
|
|
if((char*)src == dp)
|
|
{
|
|
int c;
|
|
char buffer[MAXLINE*sizeof(genchar)] = "";
|
|
c = ed_external(src,buffer);
|
|
|
|
#ifdef _lib_wcscpy
|
|
wcscpy((wchar_t *)dest,(const wchar_t *)buffer);
|
|
#else
|
|
strcpy(dest,buffer);
|
|
#endif
|
|
return(c);
|
|
}
|
|
while((wc = *src++) && dp<dpmax)
|
|
{
|
|
ssize_t size;
|
|
if((size = mbconv(dp, wc)) < 0)
|
|
{
|
|
/* copy the character as is */
|
|
size = 1;
|
|
*dp = wc;
|
|
}
|
|
dp += size;
|
|
}
|
|
*dp = 0;
|
|
return(dp-dest);
|
|
}
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
|
|
#if (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE
|
|
/*
|
|
* copy <sp> to <dp>
|
|
*/
|
|
|
|
void ed_gencpy(genchar *dp,const genchar *sp)
|
|
{
|
|
dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
|
|
sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
|
|
while(*dp++ = *sp++);
|
|
}
|
|
#endif /* (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE */
|
|
|
|
#if (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE
|
|
/*
|
|
* copy at most <n> items from <sp> to <dp>
|
|
*/
|
|
|
|
void ed_genncpy(register genchar *dp,register const genchar *sp, int n)
|
|
{
|
|
dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
|
|
sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
|
|
while(n-->0 && (*dp++ = *sp++));
|
|
}
|
|
#endif /* (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE */
|
|
|
|
#if (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE
|
|
/*
|
|
* find the string length of <str>
|
|
*/
|
|
|
|
int ed_genlen(register const genchar *str)
|
|
{
|
|
register const genchar *sp = str;
|
|
sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
|
|
while(*sp++);
|
|
return(sp-str-1);
|
|
}
|
|
#endif /* (SHOPT_ESH || SHOPT_VSH) && SHOPT_MULTIBYTE */
|
|
|
|
#if SHOPT_OLDTERMIO
|
|
|
|
# include <sys/termio.h>
|
|
|
|
#ifndef ECHOCTL
|
|
# define ECHOCTL 0
|
|
#endif /* !ECHOCTL */
|
|
#define ott ep->e_ott
|
|
|
|
/*
|
|
* For backward compatibility only
|
|
* This version will use termios when possible, otherwise termio
|
|
*/
|
|
|
|
int tcgetattr(int fd, struct termios *tt)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
register int r,i;
|
|
ep->e_tcgeta = 0;
|
|
ep->e_echoctl = (ECHOCTL!=0);
|
|
if((r=ioctl(fd,TCGETS,tt))>=0 || errno!=EINVAL)
|
|
return(r);
|
|
if((r=ioctl(fd,TCGETA,&ott)) >= 0)
|
|
{
|
|
tt->c_lflag = ott.c_lflag;
|
|
tt->c_oflag = ott.c_oflag;
|
|
tt->c_iflag = ott.c_iflag;
|
|
tt->c_cflag = ott.c_cflag;
|
|
for(i=0; i<NCC; i++)
|
|
tt->c_cc[i] = ott.c_cc[i];
|
|
ep->e_tcgeta++;
|
|
ep->e_echoctl = 0;
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
int tcsetattr(int fd,int mode,struct termios *tt)
|
|
{
|
|
register Edit_t *ep = (Edit_t*)(sh.ed_context);
|
|
register int r;
|
|
if(ep->e_tcgeta)
|
|
{
|
|
register int i;
|
|
ott.c_lflag = tt->c_lflag;
|
|
ott.c_oflag = tt->c_oflag;
|
|
ott.c_iflag = tt->c_iflag;
|
|
ott.c_cflag = tt->c_cflag;
|
|
for(i=0; i<NCC; i++)
|
|
ott.c_cc[i] = tt->c_cc[i];
|
|
if(tt->c_lflag&ECHOCTL)
|
|
{
|
|
ott.c_lflag &= ~(ECHOCTL|IEXTEN);
|
|
ott.c_iflag &= ~(IGNCR|ICRNL);
|
|
ott.c_iflag |= INLCR;
|
|
ott.c_cc[VEOF]= ESC; /* ESC -> eof char */
|
|
ott.c_cc[VEOL] = '\r'; /* CR -> eol char */
|
|
ott.c_cc[VEOL2] = tt->c_cc[VEOF]; /* EOF -> eol char */
|
|
}
|
|
switch(mode)
|
|
{
|
|
case TCSANOW:
|
|
mode = TCSETA;
|
|
break;
|
|
case TCSADRAIN:
|
|
mode = TCSETAW;
|
|
break;
|
|
case TCSAFLUSH:
|
|
mode = TCSETAF;
|
|
}
|
|
return(ioctl(fd,mode,&ott));
|
|
}
|
|
return(ioctl(fd,mode,tt));
|
|
}
|
|
#endif /* SHOPT_OLDTERMIO */
|
|
|
|
/*
|
|
* Execute keyboard trap on given buffer <inbuff> of given size <isize>
|
|
* <mode> < 0 for vi insert mode
|
|
*/
|
|
static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int mode)
|
|
{
|
|
register char *cp;
|
|
int savexit;
|
|
#if SHOPT_MULTIBYTE
|
|
char buff[MAXLINE];
|
|
ed_external(ep->e_inbuf,cp=buff);
|
|
#else
|
|
cp = ep->e_inbuf;
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
inbuff[insize] = 0;
|
|
ep->e_col = ep->e_cur;
|
|
if(mode== -2)
|
|
{
|
|
ep->e_col++;
|
|
*ep->e_vi_insert = ESC;
|
|
}
|
|
else
|
|
*ep->e_vi_insert = 0;
|
|
nv_putval(ED_CHRNOD,inbuff,NV_NOFREE);
|
|
nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER);
|
|
nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE);
|
|
nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE);
|
|
savexit = sh.savexit;
|
|
sh_trap(sh.st.trap[SH_KEYTRAP],0);
|
|
sh.savexit = savexit;
|
|
if((cp = nv_getval(ED_CHRNOD)) == inbuff)
|
|
nv_unset(ED_CHRNOD);
|
|
else if(bufsize>0)
|
|
{
|
|
strncpy(inbuff,cp,bufsize);
|
|
inbuff[bufsize-1]='\0';
|
|
insize = strlen(inbuff);
|
|
}
|
|
else
|
|
insize = 0;
|
|
nv_unset(ED_TXTNOD);
|
|
return(insize);
|
|
}
|
|
|
|
#if SHOPT_EDPREDICT
|
|
static int ed_sortdata(const char *s1, const char *s2)
|
|
{
|
|
Histmatch_t *m1 = (Histmatch_t*)s1;
|
|
Histmatch_t *m2 = (Histmatch_t*)s2;
|
|
return(strcmp(m1->data,m2->data));
|
|
}
|
|
|
|
static int ed_sortindex(const char *s1, const char *s2)
|
|
{
|
|
Histmatch_t *m1 = (Histmatch_t*)s1;
|
|
Histmatch_t *m2 = (Histmatch_t*)s2;
|
|
return(m2->index-m1->index);
|
|
}
|
|
|
|
static int ed_histlencopy(const char *cp, char *dp)
|
|
{
|
|
int c,n=1,col=1;
|
|
const char *oldcp=cp;
|
|
for(n=0;c = mbchar(cp);oldcp=cp,col++)
|
|
{
|
|
if(c=='\n' && *cp)
|
|
{
|
|
n += 2;
|
|
if(dp)
|
|
{
|
|
*dp++ = '^';
|
|
*dp++ = 'J';
|
|
col +=2;
|
|
}
|
|
}
|
|
else if(c=='\t')
|
|
{
|
|
n++;
|
|
if(dp)
|
|
*dp++ = ' ';
|
|
}
|
|
else
|
|
{
|
|
n += cp-oldcp;
|
|
if(dp)
|
|
{
|
|
while(oldcp < cp)
|
|
*dp++ = *oldcp++;
|
|
}
|
|
}
|
|
}
|
|
return(n);
|
|
}
|
|
|
|
int ed_histgen(Edit_t *ep,const char *pattern)
|
|
{
|
|
Histmatch_t *mp,*mplast=0;
|
|
History_t *hp;
|
|
off_t offset;
|
|
int ac=0,l,n,index1,index2;
|
|
size_t m;
|
|
char *cp, **argv=0, **av, **ar;
|
|
static int maxmatch;
|
|
if(!(hp=sh.hist_ptr) && (!nv_getval(HISTFILE) || !sh_histinit()))
|
|
return(0);
|
|
if(ep->e_cur <=2)
|
|
maxmatch = 0;
|
|
else if(maxmatch && ep->e_cur > maxmatch)
|
|
{
|
|
ep->hlist = 0;
|
|
ep->hfirst = 0;
|
|
return(0);
|
|
}
|
|
hp = sh.hist_ptr;
|
|
if(*pattern=='#' && *++pattern=='#')
|
|
return(0);
|
|
cp = stakalloc(m=strlen(pattern)+6);
|
|
sfsprintf(cp,m,"@(%s)*%c",pattern,0);
|
|
if(ep->hlist)
|
|
{
|
|
m = strlen(ep->hpat)-4;
|
|
if(memcmp(pattern,ep->hpat+2,m)==0)
|
|
{
|
|
n = strcmp(cp,ep->hpat)==0;
|
|
for(argv=av=(char**)ep->hlist,mp=ep->hfirst; mp;mp= mp->next)
|
|
{
|
|
if(n || strmatch(mp->data,cp))
|
|
*av++ = (char*)mp;
|
|
}
|
|
*av = 0;
|
|
ep->hmax = av-argv;
|
|
if(ep->hmax==0)
|
|
maxmatch = ep->e_cur;
|
|
return(ep->hmax=av-argv);
|
|
}
|
|
stakset(ep->e_stkptr,ep->e_stkoff);
|
|
}
|
|
if((m=strlen(cp)) >= sizeof(ep->hpat))
|
|
m = sizeof(ep->hpat)-1;
|
|
memcpy(ep->hpat,cp,m);
|
|
ep->hpat[m] = 0;
|
|
pattern = cp;
|
|
index1 = (int)hp->histind;
|
|
for(index2=index1-hp->histsize; index1>index2; index1--)
|
|
{
|
|
offset = hist_tell(hp,index1);
|
|
sfseek(hp->histfp,offset,SEEK_SET);
|
|
if(!(cp = sfgetr(hp->histfp,0,0)))
|
|
continue;
|
|
if(*cp=='#')
|
|
continue;
|
|
if(strmatch(cp,pattern))
|
|
{
|
|
l = ed_histlencopy(cp,(char*)0);
|
|
mp = (Histmatch_t*)stakalloc(sizeof(Histmatch_t)+l);
|
|
mp->next = mplast;
|
|
mplast = mp;
|
|
mp->len = l;
|
|
ed_histlencopy(cp,mp->data);
|
|
mp->count = 1;
|
|
mp->data[l] = 0;
|
|
mp->index = index1;
|
|
ac++;
|
|
}
|
|
}
|
|
if(ac>0)
|
|
{
|
|
l = ac;
|
|
argv = av = (char**)stakalloc((ac+1)*sizeof(char*));
|
|
for(; l>=0 && (*av= (char*)mp); mp=mp->next,av++)
|
|
l--;
|
|
*av = 0;
|
|
strsort(argv,ac,ed_sortdata);
|
|
mplast = (Histmatch_t*)argv[0];
|
|
for(ar= av= &argv[1]; mp=(Histmatch_t*)*av; av++)
|
|
{
|
|
if(strcmp(mp->data,mplast->data)==0)
|
|
{
|
|
mplast->count++;
|
|
if(mp->index> mplast->index)
|
|
mplast->index = mp->index;
|
|
continue;
|
|
}
|
|
*ar++ = (char*)(mplast=mp);
|
|
}
|
|
*ar = 0;
|
|
mplast->next = 0;
|
|
ac = ar-argv;
|
|
strsort(argv,ac,ed_sortindex);
|
|
mplast = (Histmatch_t*)argv[0];
|
|
for(av= &argv[1]; mp=(Histmatch_t*)*av; av++, mplast=mp)
|
|
mplast->next = mp;
|
|
mplast->next = 0;
|
|
}
|
|
if (argv)
|
|
{
|
|
ep->hlist = (Histmatch_t**)argv;
|
|
ep->hfirst = ep->hlist?ep->hlist[0]:0;
|
|
}
|
|
else
|
|
ep->hfirst = 0;
|
|
return(ep->hmax=ac);
|
|
}
|
|
|
|
#if SHOPT_ESH || SHOPT_VSH
|
|
void ed_histlist(Edit_t *ep,int n)
|
|
{
|
|
Histmatch_t *mp,**mpp = ep->hlist+ep->hoff;
|
|
int i,last=0,save[2];
|
|
if(n)
|
|
{
|
|
/* don't bother updating the screen if there is typeahead */
|
|
if(!ep->e_lookahead && sfpkrd(ep->e_fd,save,1,'\r',200L,-1)>0)
|
|
ed_ungetchar(ep,save[0]);
|
|
if(ep->e_lookahead)
|
|
return;
|
|
ed_putchar(ep,'\n');
|
|
ed_putchar(ep,'\r');
|
|
}
|
|
else
|
|
{
|
|
stakset(ep->e_stkptr,ep->e_stkoff);
|
|
ep->hlist = 0;
|
|
ep->nhlist = 0;
|
|
}
|
|
ed_putstring(ep,KILL_LINE);
|
|
if(n)
|
|
{
|
|
for(i=1; (mp= *mpp) && i <= 16 ; i++,mpp++)
|
|
{
|
|
last = 0;
|
|
if(mp->len >= ep->e_winsz-4)
|
|
{
|
|
last = ep->e_winsz-4;
|
|
save[0] = mp->data[last-1];
|
|
save[1] = mp->data[last];
|
|
mp->data[last-1] = '\n';
|
|
mp->data[last] = 0;
|
|
}
|
|
ed_putchar(ep,i<10?' ':'1');
|
|
ed_putchar(ep,i<10?'0'+i:'0'+i-10);
|
|
ed_putchar(ep,')');
|
|
ed_putchar(ep,' ');
|
|
ed_putstring(ep,mp->data);
|
|
if(last)
|
|
{
|
|
mp->data[last-1] = save[0];
|
|
mp->data[last] = save[1];
|
|
}
|
|
ep->nhlist = i;
|
|
}
|
|
last = i-1;
|
|
while(i-->0)
|
|
ed_putstring(ep,CURSOR_UP);
|
|
}
|
|
ed_flush(ep);
|
|
}
|
|
#endif /* SHOPT_ESH || SHOPT_VSH */
|
|
|
|
#endif /* SHOPT_EDPREDICT */
|
|
|
|
void *ed_open(void)
|
|
{
|
|
Edit_t *ed = sh_newof(0,Edit_t,1,0);
|
|
strcpy(ed->e_macro,"_??");
|
|
return((void*)ed);
|
|
}
|
|
|
|
#undef ioctl
|
|
int sh_ioctl(int fd, int cmd, void* val, int sz)
|
|
{
|
|
int r,err=errno;
|
|
if(sz == sizeof(void*))
|
|
{
|
|
while((r=ioctl(fd,cmd,val)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
}
|
|
else
|
|
{
|
|
Sflong_t l = (Sflong_t)val;
|
|
if(sizeof(val)==sizeof(long))
|
|
{
|
|
while((r=ioctl(fd,cmd,(unsigned long)l)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
}
|
|
else if(sizeof(int)!=sizeof(long))
|
|
{
|
|
while((r=ioctl(fd,cmd,(unsigned int)l)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
}
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
#ifdef _lib_tcgetattr
|
|
# undef tcgetattr
|
|
int sh_tcgetattr(int fd, struct termios *tty)
|
|
{
|
|
int r,err = errno;
|
|
while((r=tcgetattr(fd,tty)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
return(r);
|
|
}
|
|
|
|
# undef tcsetattr
|
|
int sh_tcsetattr(int fd, int cmd, struct termios *tty)
|
|
{
|
|
int r,err = errno;
|
|
while((r=tcsetattr(fd,cmd,tty)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
return(r);
|
|
}
|
|
#endif
|