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