summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshell/common/edit/completion.c
diff options
context:
space:
mode:
authorchin <none@none>2007-08-17 12:01:52 -0700
committerchin <none@none>2007-08-17 12:01:52 -0700
commitda2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch)
tree5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libshell/common/edit/completion.c
parent073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff)
downloadillumos-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.c522
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);
+}