diff options
author | chin <none@none> | 2007-08-17 12:01:52 -0700 |
---|---|---|
committer | chin <none@none> | 2007-08-17 12:01:52 -0700 |
commit | da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch) | |
tree | 5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libshell/common/edit/edit.c | |
parent | 073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff) | |
download | illumos-joyent-da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968.tar.gz |
6437624 RFE: Add ksh93 (as /usr/bin/ksh93) and libshell.so to OS/Net
6505835 AST tools and library (libpp) required for creating l10n messages for ksh93
PSARC/2006/550 Korn Shell 93 Integration
PSARC/2006/587 /etc/ksh.kshrc for ksh93
PSARC/2007/035 ksh93 Amendments
Contributed by Roland Mainz <roland.mainz@nrubsig.org>
--HG--
rename : usr/src/lib/libcmd/common/mapfile-vers => deleted_files/usr/src/lib/libcmd/common/mapfile-vers
rename : usr/src/lib/libcmd/common/placeholder.c => deleted_files/usr/src/lib/libcmd/common/placeholder.c
Diffstat (limited to 'usr/src/lib/libshell/common/edit/edit.c')
-rw-r--r-- | usr/src/lib/libshell/common/edit/edit.c | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/edit/edit.c b/usr/src/lib/libshell/common/edit/edit.c new file mode 100644 index 0000000000..a832a4a965 --- /dev/null +++ b/usr/src/lib/libshell/common/edit/edit.c @@ -0,0 +1,1489 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * 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 <ctype.h> +#include "FEATURE/options" +#include "FEATURE/time" +#include "FEATURE/cmds" +#ifdef _hdr_utime +# include <utime.h> +# include <ls.h> +#endif + +#if KSHELL +# include "defs.h" +# include "variables.h" +#else + extern char ed_errbuf[]; + char e_version[] = "\n@(#)$Id: Editlib version 1993-12-28 r $\0\n"; +#endif /* KSHELL */ +#include "io.h" +#include "terminal.h" +#include "history.h" +#include "edit.h" + +static char CURSOR_UP[20] = { ESC, '[', 'A', 0 }; + +#if SHOPT_MULTIBYTE +# define is_cntrl(c) ((c<=STRIP) && iscntrl(c)) +# define is_print(c) ((c&~STRIP) || isprint(c)) +#else +# define is_cntrl(c) iscntrl(c) +# define is_print(c) isprint(c) +#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 */ + +#if KSHELL + static int keytrap(Edit_t *,char*, int, int, int); +#else + Edit_t editb; +#endif /* KSHELL */ + + +#ifndef _POSIX_DISABLE +# define _POSIX_DISABLE 0 +#endif + +#ifdef future + static int compare(const char*, const char*, int); +#endif /* future */ +#if SHOPT_VSH || SHOPT_ESH +# define ttyparm (ep->e_ttyparm) +# define nttyparm (ep->e_nttyparm) + static const char bellchr[] = "\a"; /* bell char */ +#endif /* SHOPT_VSH || SHOPT_ESH */ + + +/* + * 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_getinterp()->ed_context); + struct termios tty; + ep->e_savefd = -1; + 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_getinterp()->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 cannonical 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_getinterp()->ed_context); + if(fd >=0) + { +#ifdef future + if(ep->e_savefd>=0 && compare(&ep->e_savetty,tty,sizeof(struct termios))) + return(0); +#endif + while(tcsetattr(fd, action, tty) == SYSERR) + { + if(errno !=EINTR) + return(SYSERR); + errno = 0; + } + ep->e_savetty = *tty; + } + ep->e_savefd = fd; + return(0); +} + +#if SHOPT_ESH || SHOPT_VSH +/*{ 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_getinterp()->ed_context); + 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_getinterp()->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|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_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_getinterp()->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_getinterp()->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); +} + +/* + * 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); +} + +/* 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; + char *ppmax; + int myquote = 0, n; + register int qlen = 1; + char inquote = 0; + ep->e_fd = fd; + ep->e_multiline = sh_isoption(SH_MULTILINE)!=0; +#ifdef SIGWINCH + if(!(sh.sigflag[SIGWINCH]&SH_SIGFAULT)) + { + signal(SIGWINCH,sh_fault); + sh.sigflag[SIGWINCH] |= SH_SIGFAULT; + } + sh_fault(SIGWINCH); +#endif +#if KSHELL + ep->e_stkptr = stakptr(0); + ep->e_stkoff = staktell(); + if(!(last = sh.prompt)) + last = ""; + sh.prompt = 0; +#else + last = ep->e_prbuff; +#endif /* KSHELL */ + 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(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS)) + 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; + pp = ep->e_prompt; + ppmax = pp+PRSIZE-1; + *pp++ = '\r'; + { + register int c; + while(c= *last++) switch(c) + { + case ESC: + { + int skip=0; + ep->e_crlf = 0; + *pp++ = c; + for(n=1; c = *last++; n++) + { + if(pp < ppmax) + *pp++ = c; + if(c=='\a') + break; + if(skip || (c>='0' && c<='9')) + continue; + if(n>1 && c==';') + skip = 1; + else if(n>2 || (c!= '[' && c!= ']')) + break; + } + 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) + { + qlen += inquote; + *pp++ = c; + if(!inquote && !is_print(c)) + ep->e_crlf = 0; + } + } + } + if(pp-ep->e_prompt > qlen) + ep->e_plen = pp - ep->e_prompt - qlen; + *pp = 0; + if((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'; + } + sfsync(sfstderr); + if(fd == sffileno(sfstderr)) + { + /* can't use output buffer when reading from stderr */ + static char *buff; + if(!buff) + buff = (char*)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(ep->e_multiline) + { +#ifdef _cmd_tput + 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)) + { + sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0); + if(pp=nv_getval(SH_SUBSCRNOD)) + strncpy(CURSOR_UP,pp,sizeof(CURSOR_UP)-1); + nv_unset(SH_SUBSCRNOD); + strcpy(ep->e_termname,term); + } +#endif + ep->e_wsize = MAXLINE - (ep->e_plen-2); + } + 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; + } +} + +/* + * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set + * Use sfpkrd() to poll() or select() to wait for input if possible + * Unfortunately, systems that get interrupted from slow reads update + * this access time for 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 can handle + * sfpkrd() correctly (i,e., those that support poll() or 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?'\r':'\n'); + int mode = -1; + int (*waitevent)(int,long,int) = sh.waitevent; + if(ep->e_raw==ALTMODE) + mode = 1; + if(size < 0) + { + mode = 1; + 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; + /* 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; +# if KSHELL + sh_fault(SIGINT); + siglongjmp(ep->e_env, UINTR); +# endif /* KSHELL */ + } +# 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; +# if KSHELL + sh_fault(SIGINT); + siglongjmp(ep->e_env, UINTR); +# endif /* KSHELL */ + } +# 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; + /* The while is necessary for reads of partial multbyte chars */ + if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0) + n = putstack(ep,readin,n,1); + } + if(ep->e_lookahead) + { + /* check for possible key mapping */ + if((c = ep->e_lbuf[--ep->e_lookahead]) < 0) + { + if(mode<=0 && sh.st.trap[SH_KEYTRAP]) + { + 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); + } + 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); +} + +void ed_ungetchar(Edit_t *ep,register int c) +{ + if (ep->e_lookahead < LOOKAHEAD) + ep->e_lbuf[ep->e_lookahead++] = c; + return; +} + +/* + * 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; + 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; +} + +/* + * returns the line and column corresponding to offset <off> in the physical buffer + * if <cur> is non-zero and <= <off>, then correspodning <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(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); +} + +static void ed_putstring(register Edit_t *ep, const char *str) +{ + register int c; + while(c = *str++) + ed_putchar(ep,c); +} + +int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first) +{ + static int oldline; + register int delta; + Edpos_t newpos; + + delta = new - old; + if( delta == 0 ) + return(new); + if(ep->e_multiline) + { + ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos); + 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; + for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--) + ed_putstring(ep,CURSOR_UP); + if(newpos.line==0 && (n=ep->e_plen- ep->e_curpos.col)>0) + { + ep->e_curpos.col += n; + ed_putchar(ep,'\r'); + if(!ep->e_crlf) + ed_putstring(ep,ep->e_prompt); + else + { + int m = ep->e_winsz+1-ep->e_plen; + ed_putchar(ep,'\n'); + n = ep->e_plen; + if(m < ed_genlen(physical)) + { + while(physical[m] && n-->0) + ed_putchar(ep,physical[m++]); + } + while(n-->0) + ed_putchar(ep,' '); + ed_putstring(ep,CURSOR_UP); + } + } + } + else if(ep->e_curpos.line < newpos.line) + { + for(;ep->e_curpos.line < newpos.line;ep->e_curpos.line++) + ed_putchar(ep,'\n'); + 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) + { + /*** move to left ***/ + delta = -delta; + /*** attempt to optimize cursor movement ***/ + if(!ep->e_crlf || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) ) + { + for( ; delta; delta-- ) + ed_putchar(ep,'\b'); + } + else + { + if(newpos.line==0) + ed_putstring(ep,ep->e_prompt); + old = first; + delta = new-first; + } + } + while(delta-->0) + ed_putchar(ep,physical[old++]); + return(new); +} + +/* + * 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; + if(sh_isoption(SH_VI)) + c += ep->e_plen; + c = TABSIZE - c%TABSIZE; + while(--c>0) + *dp++ = ' '; + c = ' '; + } + else + { + *dp++ = '^'; + c = printchar(c); + } + /* in vi mode the cursor is at the last character */ + if(curp == sp && sh_isoption(SH_VI)) + r = dp - phys; + } + *dp++ = c; + if(dp>=dpmax) + break; + } + *dp = 0; + return(r); +} + +#if 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); +} + +/* + * 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 int c,size; + register char *dp = dest; + char *dpmax = dp+sizeof(genchar)*MAXLINE-2; + if((char*)src == dp) + { + 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) + { + if((size = mbconv(dp, wc)) < 0) + { + /* copy the character as is */ + size = 1; + *dp = wc; + } + dp += size; + } + *dp = 0; + return(dp-dest); +} + +/* + * 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++); +} + +/* + * 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++)); +} + +/* + * 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_MULTIBYTE */ +#endif /* SHOPT_ESH || SHOPT_VSH */ + +#ifdef future +/* + * returns 1 when <n> bytes starting at <a> and <b> are equal + */ +static int compare(register const char *a,register const char *b,register int n) +{ + while(n-->0) + { + if(*a++ != *b++) + return(0); + } + return(1); +} +#endif + +#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 + */ + + +tcgetattr(int fd, struct termios *tt) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->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); +} + +tcsetattr(int fd,int mode,struct termios *tt) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->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 */ + +#if KSHELL +/* + * 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 + { + strncpy(inbuff,cp,bufsize); + insize = strlen(inbuff); + } + nv_unset(ED_TXTNOD); + return(insize); +} +#endif /* KSHELL */ + +void *ed_open(Shell_t *shp) +{ + Edit_t *ed = newof(0,Edit_t,1,0); + ed->sh = shp; + strcpy(ed->e_macro,"_??"); + return((void*)ed); +} |