1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
22 * completion.c - command and file completion for shell editors
27 #include <ast_wchar.h>
28 #include "lexstates.h"
35 #define mbchar(p) (*(unsigned char*)p++)
38 static char *fmtx(const char *string
)
40 register const char *cp
= string
;
42 unsigned char *state
= (unsigned char*)sh_lexstates
[2];
44 while((c
=mbchar(cp
)),(c
>UCHAR_MAX
)||(n
=state
[c
])==0);
46 return((char*)string
);
48 stakwrite(string
,--cp
-string
);
56 return(stakptr(offset
));
59 static int charcmp(int a
, int b
, int nocase
)
72 * overwrites <str> to common prefix of <str> and <newstr>
73 * if <str> is equal to <newstr> returns <str>+strlen(<str>)+1
74 * otherwise returns <str>+strlen(<str>)
76 static char *overlaid(register char *str
,register const char *newstr
,int nocase
)
79 while((c
= *(unsigned char *)str
) && ((d
= *(unsigned char*)newstr
++),charcmp(c
,d
,nocase
)))
90 * returns pointer to beginning of expansion and sets type of expansion
92 static char *find_begin(char outbuff
[], char *last
, int endchar
, int *type
)
94 register char *cp
=outbuff
, *bp
, *xp
;
95 register int c
,inquote
= 0, inassign
=0;
102 switch(c
= mbchar(cp
))
121 c
= *(unsigned char*)cp
;
122 if(mode
!='*' && (isaletter(c
) || c
=='{'))
129 c
= *(unsigned char*)cp
;
130 if(c
!='.' && !isaletter(c
))
137 if((c
= mbchar(cp
)) , c
!=dot
&& !isaname(c
))
140 if(cp
>=last
&& c
!= '}')
149 xp
= find_begin(cp
,last
,')',type
);
164 if(!inquote
&& inassign
)
174 if(!inquote
&& ismeta(c
))
182 if(inquote
&& *bp
==inquote
)
188 * file name generation for edit modes
189 * non-zero exit for error, <0 ring bell
190 * don't search back past beginning of the buffer
191 * mode is '*' for inline expansion,
192 * mode is '\' for filename completion
193 * mode is '=' cause files to be listed in select format
196 int ed_expand(Edit_t
*ep
, char outbuff
[],int *cur
,int *eol
,int mode
, int count
)
198 struct comnod
*comptr
;
201 char *av
[2], *begin
, *dir
=0;
202 int addstar
=0, rval
=0, var
=0, strip
=1;
203 int nomarkdirs
= !sh_isoption(SH_MARKDIRS
);
204 sh_onstate(SH_FCOMPLETE
);
207 if(mode
=='=' && count
>0)
209 if(count
> ep
->e_nlist
)
212 av
[0] = ep
->e_clist
[count
-1];
217 stakset(ep
->e_stkptr
,ep
->e_stkoff
);
221 comptr
= (struct comnod
*)stakalloc(sizeof(struct comnod
));
222 ap
= (struct argnod
*)stakseek(ARGVAL
);
225 register int c
= *cur
;
226 register genchar
*cp
;
228 cp
= (genchar
*)outbuff
+ *cur
;
231 *cur
= ed_external((genchar
*)outbuff
,(char*)stakptr(0));
233 *eol
= ed_external((genchar
*)outbuff
,outbuff
);
235 #endif /* SHOPT_MULTIBYTE */
236 out
= outbuff
+ *cur
+ (sh_isoption(SH_VI
)!=0);
237 comptr
->comtyp
= COMSCAN
;
239 ap
->argflag
= (ARG_MAC
|ARG_EXP
);
245 c
= *(unsigned char*)out
;
247 begin
= out
= find_begin(outbuff
,last
,0,&var
);
248 /* addstar set to zero if * should not be added */
252 stakwrite(out
,last
-out
);
261 c
= *(unsigned char*)out
;
276 if(var
!='$' && mode
=='\\' && out
[-1]!='*')
278 if(*begin
=='~' && !strchr(begin
,'/'))
281 ap
= (struct argnod
*)stakfreeze(1);
284 sh_onoption(SH_MARKDIRS
);
287 char *cp
=begin
, *left
=0, *saveout
=".";
288 int nocase
=0,narg
,cmd_completion
=0;
289 register int size
='x';
290 while(cp
>outbuff
&& ((size
=cp
[-1])==' ' || size
=='\t'))
292 if(!var
&& !strchr(ap
->argval
,'/') && (((cp
==outbuff
&&sh
.nextprompt
==1) || (strchr(";&|(",size
)) && (cp
==outbuff
+1||size
=='('||cp
[-2]!='>') && *begin
!='~' )))
295 sh_onstate(SH_COMPLETE
);
302 begin
+= (dir
-begin
);
306 com
= sh_argbuild(ep
->sh
,&narg
,comptr
,0);
307 /* special handling for leading quotes */
308 if(begin
>outbuff
&& (begin
[-1]=='"' || begin
[-1]=='\''))
311 sh_offstate(SH_COMPLETE
);
312 /* allow a search to be aborted */
313 if(sh
.trapnote
&SH_SIGSET
)
319 if (*com
==0 || (narg
<= 1 && (strcmp(ap
->argval
,*com
)==0) || (addstar
&& com
[0][strlen(*com
)-1]=='*')))
326 if (strip
&& !cmd_completion
)
328 register char **ptrcom
;
329 for(ptrcom
=com
;*ptrcom
;ptrcom
++)
330 /* trim directory prefix */
331 *ptrcom
= path_basename(*ptrcom
);
333 sfputc(sfstderr
,'\n');
334 sh_menu(sfstderr
,narg
,com
);
340 /* see if there is enough room */
341 size
= *eol
- (out
-begin
);
351 if(saveout
=astconf("PATH_ATTRIBUTES",saveout
,(char*)0))
352 nocase
= (strchr(saveout
,'c')!=0);
355 /* just expand until name is unique */
356 size
+= strlen(*com
);
364 size
+= strlen(cp
=fmtx(*com
++));
368 /* see if room for expansion */
369 if(outbuff
+size
>= &outbuff
[MAXLINE
])
374 /* save remainder of the buffer */
377 if(cmd_completion
&& mode
=='\\')
378 out
= strcopy(begin
,path_basename(cp
= *com
++));
381 if(ep
->e_nlist
&& dir
&& var
)
387 out
= strcopy(begin
,cp
);
391 out
= strcopy(begin
,fmtx(*com
));
395 out
= strcopy(begin
,*com
++);
399 while (*com
&& *begin
)
402 out
= overlaid(begin
,path_basename(*com
++),nocase
);
404 out
= overlaid(begin
,*com
++,nocase
);
406 mode
= (out
==saveout
);
409 if(mode
&& out
[-1]!='/')
414 /* add as tracked alias */
416 if(*cp
=='/' && (pp
=path_dirfind(sh
.pathlist
,cp
,'/')) && (np
=nv_search(begin
,sh
.track_tree
,NV_ADD
)))
418 out
= strcopy(begin
,cp
);
420 /* add quotes if necessary */
421 if((cp
=fmtx(begin
))!=begin
)
422 out
= strcopy(begin
,cp
);
423 if(var
=='$' && begin
[-1]=='{')
429 else if((cp
=fmtx(begin
))!=begin
)
431 out
= strcopy(begin
,cp
);
432 if(out
[-1] =='"' || out
[-1]=='\'')
443 out
= strcopy(out
,fmtx(*com
++));
449 if(cp
[strlen(cp
)-1]!='/')
451 if(var
=='$' && begin
[-1]=='{')
457 else if(out
[-1] =='"' || out
[-1]=='\'')
461 *cur
= (out
-outbuff
);
462 /* restore rest of buffer */
464 out
= strcopy(out
,left
);
465 *eol
= (out
-outbuff
);
468 sh_offstate(SH_FCOMPLETE
);
470 stakset(ep
->e_stkptr
,ep
->e_stkoff
);
472 sh_offoption(SH_MARKDIRS
);
476 /* first re-adjust cur */
479 for(out
=outbuff
; *out
;n
++)
484 *eol
= ed_internal(outbuff
,(genchar
*)outbuff
);
486 #endif /* SHOPT_MULTIBYTE */
491 * look for edit macro named _i
492 * if found, puts the macro definition into lookahead buffer and returns 1
494 int ed_macro(Edit_t
*ep
, register int i
)
498 genchar buff
[LOOKAHEAD
+1];
501 /* undocumented feature, macros of the form <ESC>[c evoke alias __c */
503 ep
->e_macro
[2] = ed_getchar(ep
,1);
506 if (isalnum(i
)&&(np
=nv_search(ep
->e_macro
,sh
.alias_tree
,HASH_SCOPE
))&&(out
=nv_getval(np
)))
509 /* copy to buff in internal representation */
511 if( strlen(out
) > LOOKAHEAD
)
516 i
= ed_internal(out
,buff
);
520 strncpy((char*)buff
,out
,LOOKAHEAD
);
522 i
= strlen((char*)buff
);
523 #endif /* SHOPT_MULTIBYTE */
525 ed_ungetchar(ep
,buff
[i
]);
532 * Enter the fc command on the current history line
534 int ed_fulledit(Edit_t
*ep
)
539 /* use EDITOR on current command */
540 if(ep
->e_hline
== ep
->e_hismax
)
545 ep
->e_inbuf
[ep
->e_eol
+1] = 0;
546 ed_external(ep
->e_inbuf
, (char *)ep
->e_inbuf
);
547 #endif /* SHOPT_MULTIBYTE */
548 sfwrite(sh
.hist_ptr
->histfp
,(char*)ep
->e_inbuf
,ep
->e_eol
+1);
549 sh_onstate(SH_HISTORY
);
550 hist_flush(sh
.hist_ptr
);
552 cp
= strcopy((char*)ep
->e_inbuf
,e_runvi
);
553 cp
= strcopy(cp
, fmtbase((long)ep
->e_hline
,10,0));
554 ep
->e_eol
= ((unsigned char*)cp
- (unsigned char*)ep
->e_inbuf
)-(sh_isoption(SH_VI
)!=0);