mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
1. The editor accepted literal tabs without escaping in certain cases, causing buggy and inconsistent completion behaviour. https://github.com/ksh93/ksh/issues/71#issuecomment-656970959 https://github.com/ksh93/ksh/issues/71#issuecomment-657216472 2. After completing a filename by choosing from a file completion menu, the terminal cursor was placed one position too far to the right, corrupting command line display. This happened with multiline active. https://github.com/ksh93/ksh/issues/71#issue-655093805 3. A completion menu was displayed if the file name to be completed was at the point where the rest of it started with a number, even if that part uniquely identified it so the menu had 1 item. https://www.mail-archive.com/ast-users@lists.research.att.com/msg00436.html src/cmd/ksh93/edit/emacs.c: - Cosmetic consistency: change two instances of cntl('[') to ESC. - ed_emacsread(): Fix number 1 by refusing to continue into default processing if a tab character was not used for tab completion. Instead, beep and continue to the next read loop iteration. This behaviour is consistent with most other shells, so I doubt there will be objections. To enter a literal tab it's simple enough to escape it with ^V (the 'stty lnext' character) or \. - draw(): Fix number 2 by correcting an off-by-one error in the ed_setcursor() call that updates the terminal's cursor display in multiline mode. The 'old' and 'new' parameters need to have identical values in this particular call to avoid the cursor position being off by one to the right. This change makes it match the corresponding ed_setcursor() call in vi.c. See below* for details. Thanks to Lev Kujawski for the help in analysing. src/cmd/ksh93/edit/completion.c: ed_expand(): - Fix number 3 by changing from '=' mode (menu-based completion) to '\' mode (ordinary filename completion) if the menu would only show one option, which was pointless and annoying. This never happened in vi mode, so possibly the ed_expand() call in emacs.c could have been improved instead. But I'm comfortable with fixing it here and not in emacs.c, because this fixes it at a more fundamental level, plus it's straightforward and obvious here. Resolves: https://github.com/ksh93/ksh/issues/71 ____ * Further details on bug number 2: At https://github.com/ksh93/ksh/issues/71#issuecomment-786391565 Martijn Dekker wrote: > I'm back to my original hypothesis that there is somehow an > off-by-one error related to the ed_setcursor() call that gets > executed when in multiline mode. I cannot confirm whether that > off-by-one error is actually in the call itself, or occurs > sometime earlier on one of the many possible occasions where > ep->cursor is changed. But everything else appears to work > correctly, so it's not unlikely that the problem is in the call > itself. > > For reference, this is the original version of that call in > emacs.c: > > ksh/src/cmd/ksh93/edit/emacs.c > Lines 1556 to 1557 indf2b9bf> if(ep->ed->e_multiline && option == REFRESH) > ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1); > > There is a corresponding call in the vi.c refresh() function > (which does the same thing as draw() in emacs.c), where the third > (old) and fourth (new) arguments are actually identical: > > ksh/src/cmd/ksh93/edit/vi.c > > Lines 2086 to 2087 indf2b9bf> if(vp->ed->e_multiline && vp->ofirst_wind==INVALID) > ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1); > > The expectation for this particular call is in fact that they > should be identical, so that a delta of zero is calculated in > that function. Delta not being zero is what causes the cursor to > be positioned wrong. > > In vi.c, last_phys is a macro that is defined as editb.e_peol, > and editb is a macro that is defined as (*vp->ed). Which means > last_phys means vp->ed->e_peol, which is the same as > ep->ed->e_peol in emacs.c. (These editors were originally > separate programs by different authors, and I suppose this is how > it shows. Korn didn't want to change all the variable names to > integrate them, so made macros instead.) > > That leaves the question of why vi.c adds 1 to both last_phys > a.k.a. e_peol arguments, and emacs.c uses e_peol for new without > adding anything. Analysing the ed_setcursor() code could answer > that question. > > So, this patch makes emacs.c do it the same way vi.c does. Let's > make the third argument identical to the fourth. My brief testing > shows the bug is fixed, and the regression tests yield no > failures. This fix is also the most specific change possible, so > there are few opportunities for side effects (I hope). At https://github.com/ksh93/ksh/issues/71#issuecomment-786466652 Lev Kujawski wrote: > I did a bit of research on this, and I think the fix to have the > Emacs editing mode do the same as Vi is correct. > > From RELEASE: > 08-05-01 In multiline edit mode, the refresh operation will now clear > the remaining portion of the last line. > > Here's a fragment from the completion.c of the venerable but > dated CDE DtKsh: > > else > while (*com) > { > *out++ = ' '; > out = strcopy(out,*com++); > } > *cur = (out-outbuff); > /* restore rest of buffer */ > out = strcopy(out,stakptr(0)); > *eol = (out-outbuff); > > Noticeably missing is the code to add a space after file name > completions. So, it seems plausible that if multiline editing > mode was added beforehand,the ep->ed->p_eol != > ep->cursor-ep->screen case might never have occurred during > testing. > > Setting the 'first' parameter to -1 seems to be a pretty explicit > indicator that the author(s) intended the line clearing code to > run, hence the entry in RELASE. > > The real issue is that if we update the cursor by calling > ed_setcursor on line 1554 with old != new, the later call to > setcursor on line 1583, here: > > I = (ncursor-nscreen) - ep->offset; > setcursor(ep,i,0); > > will use outdated screen information to call setcursor, which, > coincidentally, calls ed_setcursor.
584 lines
13 KiB
C
584 lines
13 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2012 AT&T Intellectual Property *
|
|
* 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> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma prototyped
|
|
/*
|
|
* completion.c - command and file completion for shell editors
|
|
*
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <ast_wchar.h>
|
|
#include "lexstates.h"
|
|
#include "path.h"
|
|
#include "io.h"
|
|
#include "edit.h"
|
|
#include "history.h"
|
|
|
|
static char *fmtx(const char *string)
|
|
{
|
|
register const char *cp = string;
|
|
register int n,c;
|
|
unsigned char *state = (unsigned char*)sh_lexstates[2];
|
|
int offset = staktell();
|
|
if(*cp=='#' || *cp=='~')
|
|
stakputc('\\');
|
|
while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0 || n==S_EPAT);
|
|
if(n==S_EOF && *string!='#')
|
|
return((char*)string);
|
|
stakwrite(string,--cp-string);
|
|
for(string=cp;c=mbchar(cp);string=cp)
|
|
{
|
|
if((n=cp-string)==1)
|
|
{
|
|
if((n=state[c]) && n!=S_EPAT)
|
|
stakputc('\\');
|
|
stakputc(c);
|
|
}
|
|
else
|
|
stakwrite(string,n);
|
|
}
|
|
stakputc(0);
|
|
return(stakptr(offset));
|
|
}
|
|
|
|
static int charcmp(int a, int b, int nocase)
|
|
{
|
|
if(nocase)
|
|
{
|
|
if(isupper(a))
|
|
a = tolower(a);
|
|
if(isupper(b))
|
|
b = tolower(b);
|
|
}
|
|
return(a==b);
|
|
}
|
|
|
|
/*
|
|
* overwrites <str> to common prefix of <str> and <newstr>
|
|
* if <str> is equal to <newstr> returns <str>+strlen(<str>)+1
|
|
* otherwise returns <str>+strlen(<str>)
|
|
*/
|
|
static char *overlaid(register char *str,register const char *newstr,int nocase)
|
|
{
|
|
register int c,d;
|
|
while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
|
|
str++;
|
|
if(*str)
|
|
*str = 0;
|
|
else if(*newstr==0)
|
|
str++;
|
|
return(str);
|
|
}
|
|
|
|
|
|
/*
|
|
* returns pointer to beginning of expansion and sets type of expansion
|
|
*/
|
|
static char *find_begin(char outbuff[], char *last, int endchar, int *type)
|
|
{
|
|
register char *cp=outbuff, *bp, *xp;
|
|
register int c,inquote = 0, inassign=0;
|
|
int mode=*type;
|
|
bp = outbuff;
|
|
*type = 0;
|
|
while(cp < last)
|
|
{
|
|
xp = cp;
|
|
switch(c= mbchar(cp))
|
|
{
|
|
case '\'': case '"':
|
|
if(!inquote)
|
|
{
|
|
inquote = c;
|
|
bp = xp;
|
|
break;
|
|
}
|
|
if(inquote==c)
|
|
inquote = 0;
|
|
break;
|
|
case '\\':
|
|
if(inquote != '\'')
|
|
mbchar(cp);
|
|
break;
|
|
case '$':
|
|
if(inquote == '\'')
|
|
break;
|
|
c = *(unsigned char*)cp;
|
|
if(mode!='*' && (isaletter(c) || c=='{'))
|
|
{
|
|
int dot = '.';
|
|
if(c=='{')
|
|
{
|
|
xp = cp;
|
|
mbchar(cp);
|
|
c = *(unsigned char*)cp;
|
|
if(c!='.' && !isaletter(c))
|
|
break;
|
|
}
|
|
else
|
|
dot = 'a';
|
|
while(cp < last)
|
|
{
|
|
if((c= mbchar(cp)) , c!=dot && !isaname(c))
|
|
break;
|
|
}
|
|
if(cp>=last)
|
|
{
|
|
if(c==dot || isaname(c))
|
|
{
|
|
*type='$';
|
|
return(++xp);
|
|
}
|
|
if(c!='}')
|
|
bp = cp;
|
|
}
|
|
}
|
|
else if(c=='(')
|
|
{
|
|
*type = mode;
|
|
xp = find_begin(cp,last,')',type);
|
|
if(*(cp=xp)!=')')
|
|
bp = xp;
|
|
else
|
|
cp++;
|
|
}
|
|
break;
|
|
case '=':
|
|
if(!inquote)
|
|
{
|
|
bp = cp;
|
|
inassign = 1;
|
|
}
|
|
break;
|
|
case ':':
|
|
if(!inquote && inassign)
|
|
bp = cp;
|
|
break;
|
|
case '~':
|
|
if(*cp=='(')
|
|
break;
|
|
/* fall through */
|
|
default:
|
|
if(c && c==endchar)
|
|
return(xp);
|
|
if(!inquote && ismeta(c))
|
|
{
|
|
bp = cp;
|
|
inassign = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(inquote && *bp==inquote)
|
|
*type = *bp++;
|
|
return(bp);
|
|
}
|
|
|
|
/*
|
|
* file name generation for edit modes
|
|
* non-zero exit for error, <0 ring bell
|
|
* don't search back past beginning of the buffer
|
|
* mode is '*' for inline expansion,
|
|
* mode is '\' for filename completion
|
|
* mode is '=' cause files to be listed in select format
|
|
*/
|
|
|
|
int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
|
|
{
|
|
struct comnod *comptr;
|
|
struct argnod *ap;
|
|
register char *out;
|
|
char *av[2], *begin , *dir=0;
|
|
int addstar=0, rval=0, var=0, strip=1;
|
|
int nomarkdirs = !sh_isoption(SH_MARKDIRS);
|
|
sh_onstate(SH_FCOMPLETE);
|
|
if(ep->e_nlist)
|
|
{
|
|
if(mode=='=' && count>0)
|
|
{
|
|
if(count> ep->e_nlist)
|
|
return(-1);
|
|
mode = '?';
|
|
av[0] = ep->e_clist[count-1];
|
|
av[1] = 0;
|
|
}
|
|
else
|
|
{
|
|
stakset(ep->e_stkptr,ep->e_stkoff);
|
|
ep->e_nlist = 0;
|
|
}
|
|
}
|
|
comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
|
|
ap = (struct argnod*)stakseek(ARGVAL);
|
|
#if SHOPT_MULTIBYTE
|
|
{
|
|
register int c = *cur;
|
|
register genchar *cp;
|
|
/* adjust cur */
|
|
cp = (genchar *)outbuff + *cur;
|
|
c = *cp;
|
|
*cp = 0;
|
|
*cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
|
|
*cp = c;
|
|
*eol = ed_external((genchar*)outbuff,outbuff);
|
|
}
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
#if SHOPT_VSH
|
|
out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
|
|
#else
|
|
out = outbuff + *cur;
|
|
#endif
|
|
if(out[-1]=='"' || out[-1]=='\'')
|
|
{
|
|
#if SHOPT_VSH
|
|
rval = -(sh_isoption(SH_VI)!=0);
|
|
#else
|
|
rval = 0;
|
|
#endif
|
|
goto done;
|
|
}
|
|
comptr->comtyp = COMSCAN;
|
|
comptr->comarg = ap;
|
|
ap->argflag = (ARG_MAC|ARG_EXP);
|
|
ap->argnxt.ap = 0;
|
|
ap->argchn.cp = 0;
|
|
{
|
|
register int c;
|
|
char *last = out;
|
|
c = *(unsigned char*)out;
|
|
var = mode;
|
|
begin = out = find_begin(outbuff,last,0,&var);
|
|
/* addstar set to zero if * should not be added */
|
|
if(var=='$')
|
|
{
|
|
stakputs("${!");
|
|
stakwrite(out,last-out);
|
|
stakputs("@}");
|
|
out = last;
|
|
}
|
|
else
|
|
{
|
|
addstar = '*';
|
|
while(out < last)
|
|
{
|
|
c = *(unsigned char*)out;
|
|
if(isexp(c))
|
|
addstar = 0;
|
|
if (c == '/')
|
|
{
|
|
if(addstar == 0)
|
|
strip = 0;
|
|
dir = out+1;
|
|
}
|
|
stakputc(c);
|
|
out++;
|
|
}
|
|
}
|
|
if(mode=='?')
|
|
mode = '*';
|
|
if(var!='$' && mode=='\\' && out[-1]!='*')
|
|
addstar = '*';
|
|
if(*begin=='~' && !strchr(begin,'/'))
|
|
addstar = 0;
|
|
stakputc(addstar);
|
|
ap = (struct argnod*)stakfreeze(1);
|
|
}
|
|
if(mode!='*')
|
|
sh_onoption(SH_MARKDIRS);
|
|
{
|
|
register char **com;
|
|
char *cp=begin, *left=0, *saveout=".";
|
|
int nocase=0,narg,cmd_completion=0;
|
|
register int size='x';
|
|
while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
|
|
cp--;
|
|
if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
|
|
{
|
|
cmd_completion=1;
|
|
sh_onstate(SH_COMPLETE);
|
|
}
|
|
if(ep->e_nlist)
|
|
{
|
|
narg = 1;
|
|
com = av;
|
|
if(dir)
|
|
begin += (dir-begin);
|
|
}
|
|
else
|
|
{
|
|
com = sh_argbuild(ep->sh,&narg,comptr,0);
|
|
/* special handling for leading quotes */
|
|
if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
|
|
begin--;
|
|
}
|
|
sh_offstate(SH_COMPLETE);
|
|
/* allow a search to be aborted */
|
|
if(ep->sh->trapnote&SH_SIGSET)
|
|
{
|
|
rval = -1;
|
|
goto done;
|
|
}
|
|
/* match? */
|
|
if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
|
|
{
|
|
rval = -1;
|
|
goto done;
|
|
}
|
|
if(mode=='\\' && out[-1]=='/' && narg>1)
|
|
mode = '=';
|
|
else if(mode=='=' && narg<2)
|
|
mode = '\\'; /* no filename menu if there is only one choice */
|
|
if(mode=='=')
|
|
{
|
|
if (strip && !cmd_completion)
|
|
{
|
|
register char **ptrcom;
|
|
for(ptrcom=com;*ptrcom;ptrcom++)
|
|
/* trim directory prefix */
|
|
*ptrcom = path_basename(*ptrcom);
|
|
}
|
|
sfputc(sfstderr,'\n');
|
|
sh_menu(sfstderr,narg,com);
|
|
sfsync(sfstderr);
|
|
ep->e_nlist = narg;
|
|
ep->e_clist = com;
|
|
goto done;
|
|
}
|
|
/* see if there is enough room */
|
|
size = *eol - (out-begin);
|
|
if(mode=='\\')
|
|
{
|
|
int c;
|
|
if(dir)
|
|
{
|
|
c = *dir;
|
|
*dir = 0;
|
|
saveout = begin;
|
|
}
|
|
if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
|
|
nocase = (strchr(saveout,'c')!=0);
|
|
if(dir)
|
|
*dir = c;
|
|
/* just expand until name is unique */
|
|
size += strlen(*com);
|
|
}
|
|
else
|
|
{
|
|
size += narg;
|
|
{
|
|
char **savcom = com;
|
|
while (*com)
|
|
size += strlen(cp=fmtx(*com++));
|
|
com = savcom;
|
|
}
|
|
}
|
|
/* see if room for expansion */
|
|
if(outbuff+size >= &outbuff[MAXLINE])
|
|
{
|
|
com[0] = ap->argval;
|
|
com[1] = 0;
|
|
}
|
|
/* save remainder of the buffer */
|
|
if(*out)
|
|
left=stakcopy(out);
|
|
if(cmd_completion && mode=='\\')
|
|
out = strcopy(begin,path_basename(cp= *com++));
|
|
else if(mode=='*')
|
|
{
|
|
if(ep->e_nlist && dir && var)
|
|
{
|
|
if(*cp==var)
|
|
cp++;
|
|
else
|
|
*begin++ = var;
|
|
out = strcopy(begin,cp);
|
|
var = 0;
|
|
}
|
|
else
|
|
out = strcopy(begin,fmtx(*com));
|
|
com++;
|
|
}
|
|
else
|
|
out = strcopy(begin,*com++);
|
|
if(mode=='\\')
|
|
{
|
|
saveout= ++out;
|
|
while (*com && *begin)
|
|
{
|
|
if(cmd_completion)
|
|
out = overlaid(begin,path_basename(*com++),nocase);
|
|
else
|
|
out = overlaid(begin,*com++,nocase);
|
|
}
|
|
mode = (out==saveout);
|
|
if(out[-1]==0)
|
|
out--;
|
|
if(mode && out[-1]!='/')
|
|
{
|
|
if(cmd_completion)
|
|
{
|
|
Namval_t *np;
|
|
/* add as tracked alias */
|
|
Pathcomp_t *pp;
|
|
if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD)))
|
|
path_alias(np,pp);
|
|
out = strcopy(begin,cp);
|
|
}
|
|
/* add quotes if necessary */
|
|
if((cp=fmtx(begin))!=begin)
|
|
out = strcopy(begin,cp);
|
|
if(var=='$' && begin[-1]=='{')
|
|
*out = '}';
|
|
else
|
|
*out = ' ';
|
|
*++out = 0;
|
|
}
|
|
else if((cp=fmtx(begin))!=begin)
|
|
{
|
|
out = strcopy(begin,cp);
|
|
if(out[-1] =='"' || out[-1]=='\'')
|
|
*--out = 0;
|
|
}
|
|
if(*begin==0)
|
|
ed_ringbell();
|
|
}
|
|
else
|
|
{
|
|
while (*com)
|
|
{
|
|
*out++ = ' ';
|
|
out = strcopy(out,fmtx(*com++));
|
|
}
|
|
}
|
|
if(ep->e_nlist)
|
|
{
|
|
cp = com[-1];
|
|
if(cp[strlen(cp)-1]!='/')
|
|
{
|
|
if(var=='$' && begin[-1]=='{')
|
|
*out = '}';
|
|
else
|
|
*out = ' ';
|
|
out++;
|
|
}
|
|
else if(out[-1] =='"' || out[-1]=='\'')
|
|
out--;
|
|
*out = 0;
|
|
}
|
|
*cur = (out-outbuff);
|
|
/* restore rest of buffer */
|
|
if(left)
|
|
out = strcopy(out,left);
|
|
*eol = (out-outbuff);
|
|
}
|
|
done:
|
|
sh_offstate(SH_FCOMPLETE);
|
|
if(!ep->e_nlist)
|
|
stakset(ep->e_stkptr,ep->e_stkoff);
|
|
if(nomarkdirs)
|
|
sh_offoption(SH_MARKDIRS);
|
|
#if SHOPT_MULTIBYTE
|
|
{
|
|
register int c,n=0;
|
|
/* first re-adjust cur */
|
|
c = outbuff[*cur];
|
|
outbuff[*cur] = 0;
|
|
for(out=outbuff; *out;n++)
|
|
mbchar(out);
|
|
outbuff[*cur] = c;
|
|
*cur = n;
|
|
outbuff[*eol+1] = 0;
|
|
*eol = ed_internal(outbuff,(genchar*)outbuff);
|
|
}
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
return(rval);
|
|
}
|
|
|
|
/*
|
|
* look for edit macro named _i
|
|
* if found, puts the macro definition into lookahead buffer and returns 1
|
|
*/
|
|
int ed_macro(Edit_t *ep, register int i)
|
|
{
|
|
register char *out;
|
|
Namval_t *np;
|
|
genchar buff[LOOKAHEAD+1];
|
|
if(i != '@')
|
|
ep->e_macro[1] = i;
|
|
/* undocumented feature, macros of the form <ESC>[c evoke alias __c */
|
|
if(i=='_')
|
|
ep->e_macro[2] = ed_getchar(ep,1);
|
|
else
|
|
ep->e_macro[2] = 0;
|
|
if (isalnum(i)&&(np=nv_search(ep->e_macro,ep->sh->alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
|
|
{
|
|
#if SHOPT_MULTIBYTE
|
|
/* copy to buff in internal representation */
|
|
int c = 0;
|
|
if( strlen(out) > LOOKAHEAD )
|
|
{
|
|
c = out[LOOKAHEAD];
|
|
out[LOOKAHEAD] = 0;
|
|
}
|
|
i = ed_internal(out,buff);
|
|
if(c)
|
|
out[LOOKAHEAD] = c;
|
|
#else
|
|
strncpy((char*)buff,out,LOOKAHEAD);
|
|
buff[LOOKAHEAD] = 0;
|
|
i = strlen((char*)buff);
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
while(i-- > 0)
|
|
ed_ungetchar(ep,buff[i]);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Enter the fc command on the current history line
|
|
*/
|
|
int ed_fulledit(Edit_t *ep)
|
|
{
|
|
register char *cp;
|
|
if(!shgd->hist_ptr)
|
|
return(-1);
|
|
/* use EDITOR on current command */
|
|
if(ep->e_hline == ep->e_hismax)
|
|
{
|
|
if(ep->e_eol<0)
|
|
return(-1);
|
|
#if SHOPT_MULTIBYTE
|
|
ep->e_inbuf[ep->e_eol+1] = 0;
|
|
ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
|
|
#endif /* SHOPT_MULTIBYTE */
|
|
sfwrite(shgd->hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
|
|
sh_onstate(SH_HISTORY);
|
|
hist_flush(shgd->hist_ptr);
|
|
}
|
|
cp = strcopy((char*)ep->e_inbuf,e_runvi);
|
|
cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
|
|
#if SHOPT_VSH
|
|
ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
|
|
#else
|
|
ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf);
|
|
#endif
|
|
return(0);
|
|
}
|