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/completion.c | |
parent | 073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff) | |
download | illumos-gate-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/completion.c')
-rw-r--r-- | usr/src/lib/libshell/common/edit/completion.c | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/edit/completion.c b/usr/src/lib/libshell/common/edit/completion.c new file mode 100644 index 0000000000..050019fbe5 --- /dev/null +++ b/usr/src/lib/libshell/common/edit/completion.c @@ -0,0 +1,522 @@ +/*********************************************************************** +* * +* 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 +/* + * completion.c - command and file completion for shell editors + * + */ + +#include "defs.h" +#include <ctype.h> +#include <ast_wchar.h> +#include "lexstates.h" +#include "path.h" +#include "io.h" +#include "edit.h" +#include "history.h" + +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; + 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(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) + { + *type='$'; + return(++xp); + } + } + else if(c=='(') + { + xp = find_begin(cp,last,')',type); + if(*(cp=xp)!=')') + bp = xp; + else + cp++; + } + break; + case '=': + if(!inquote) + bp = cp; + break; + case '~': + if(*cp=='(') + break; + /* fall through */ + default: + if(c && c==endchar) + return(xp); + if(!inquote && ismeta(c)) + bp = cp; + 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 */ + out = outbuff + *cur + (sh_isoption(SH_VI)!=0); + 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; + 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(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&&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(&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(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=='=') + { + 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=sh_fmtq(*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,sh_fmtq(*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 */ +#ifdef PATH_BFPATH + Pathcomp_t *pp; + if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD))) + path_alias(np,pp); +#else + if(*cp=='/' && (np=nv_search(begin,sh.track_tree,NV_ADD))) + path_alias(np,cp); +#endif + out = strcopy(begin,cp); + } + /* add quotes if necessary */ + if((cp=sh_fmtq(begin))!=begin) + out = strcopy(begin,cp); + if(var=='$' && begin[-1]=='{') + *out = '}'; + else + *out = ' '; + *++out = 0; + } + else if(out[-1]=='/' && (cp=sh_fmtq(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,sh_fmtq(*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,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(!sh.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(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1); + sh_onstate(SH_HISTORY); + hist_flush(sh.hist_ptr); + } + cp = strcopy((char*)ep->e_inbuf,e_runvi); + cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0)); + ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); + return(0); +} |