Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/edit/completion.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* David Korn <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* completion.c - command and file completion for shell editors
23
*
24
*/
25
26
#include "defs.h"
27
#include <ast_wchar.h>
28
#include "lexstates.h"
29
#include "path.h"
30
#include "io.h"
31
#include "edit.h"
32
#include "history.h"
33
34
#if !SHOPT_MULTIBYTE
35
#define mbchar(p) (*(unsigned char*)p++)
36
#endif
37
38
static char *fmtx(const char *string)
39
{
40
register const char *cp = string;
41
register int n,c;
42
unsigned char *state = (unsigned char*)sh_lexstates[2];
43
int offset = staktell();
44
if(*cp=='#' || *cp=='~')
45
stakputc('\\');
46
while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0 || n==S_EPAT);
47
if(n==S_EOF && *string!='#')
48
return((char*)string);
49
stakwrite(string,--cp-string);
50
for(string=cp;c=mbchar(cp);string=cp)
51
{
52
if((n=cp-string)==1)
53
{
54
if((n=state[c]) && n!=S_EPAT)
55
stakputc('\\');
56
stakputc(c);
57
}
58
else
59
stakwrite(string,n);
60
}
61
stakputc(0);
62
return(stakptr(offset));
63
}
64
65
static int charcmp(int a, int b, int nocase)
66
{
67
if(nocase)
68
{
69
if(isupper(a))
70
a = tolower(a);
71
if(isupper(b))
72
b = tolower(b);
73
}
74
return(a==b);
75
}
76
77
/*
78
* overwrites <str> to common prefix of <str> and <newstr>
79
* if <str> is equal to <newstr> returns <str>+strlen(<str>)+1
80
* otherwise returns <str>+strlen(<str>)
81
*/
82
static char *overlaid(register char *str,register const char *newstr,int nocase)
83
{
84
register int c,d;
85
while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
86
str++;
87
if(*str)
88
*str = 0;
89
else if(*newstr==0)
90
str++;
91
return(str);
92
}
93
94
95
/*
96
* returns pointer to beginning of expansion and sets type of expansion
97
*/
98
static char *find_begin(char outbuff[], char *last, int endchar, int *type)
99
{
100
register char *cp=outbuff, *bp, *xp;
101
register int c,inquote = 0, inassign=0;
102
int mode=*type;
103
bp = outbuff;
104
*type = 0;
105
while(cp < last)
106
{
107
xp = cp;
108
switch(c= mbchar(cp))
109
{
110
case '\'': case '"':
111
if(!inquote)
112
{
113
inquote = c;
114
bp = xp;
115
break;
116
}
117
if(inquote==c)
118
inquote = 0;
119
break;
120
case '\\':
121
if(inquote != '\'')
122
mbchar(cp);
123
break;
124
case '$':
125
if(inquote == '\'')
126
break;
127
c = *(unsigned char*)cp;
128
if(mode!='*' && (isaletter(c) || c=='{'))
129
{
130
int dot = '.';
131
if(c=='{')
132
{
133
xp = cp;
134
mbchar(cp);
135
c = *(unsigned char*)cp;
136
if(c!='.' && !isaletter(c))
137
break;
138
}
139
else
140
dot = 'a';
141
while(cp < last)
142
{
143
if((c= mbchar(cp)) , c!=dot && !isaname(c))
144
break;
145
}
146
if(cp>=last)
147
{
148
if(c==dot || isaname(c))
149
{
150
*type='$';
151
return(++xp);
152
}
153
if(c!='}')
154
bp = cp;
155
}
156
}
157
else if(c=='(')
158
{
159
*type = mode;
160
xp = find_begin(cp,last,')',type);
161
if(*(cp=xp)!=')')
162
bp = xp;
163
else
164
cp++;
165
}
166
break;
167
case '=':
168
if(!inquote)
169
{
170
bp = cp;
171
inassign = 1;
172
}
173
break;
174
case ':':
175
if(!inquote && inassign)
176
bp = cp;
177
break;
178
case '~':
179
if(*cp=='(')
180
break;
181
/* fall through */
182
default:
183
if(c && c==endchar)
184
return(xp);
185
if(!inquote && ismeta(c))
186
{
187
bp = cp;
188
inassign = 0;
189
}
190
break;
191
}
192
}
193
if(inquote && *bp==inquote)
194
*type = *bp++;
195
return(bp);
196
}
197
198
/*
199
* file name generation for edit modes
200
* non-zero exit for error, <0 ring bell
201
* don't search back past beginning of the buffer
202
* mode is '*' for inline expansion,
203
* mode is '\' for filename completion
204
* mode is '=' cause files to be listed in select format
205
*/
206
207
int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
208
{
209
struct comnod *comptr;
210
struct argnod *ap;
211
register char *out;
212
char *av[2], *begin , *dir=0;
213
int addstar=0, rval=0, var=0, strip=1;
214
int nomarkdirs = !sh_isoption(SH_MARKDIRS);
215
sh_onstate(SH_FCOMPLETE);
216
if(ep->e_nlist)
217
{
218
if(mode=='=' && count>0)
219
{
220
if(count> ep->e_nlist)
221
return(-1);
222
mode = '?';
223
av[0] = ep->e_clist[count-1];
224
av[1] = 0;
225
}
226
else
227
{
228
stakset(ep->e_stkptr,ep->e_stkoff);
229
ep->e_nlist = 0;
230
}
231
}
232
comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
233
ap = (struct argnod*)stakseek(ARGVAL);
234
#if SHOPT_MULTIBYTE
235
{
236
register int c = *cur;
237
register genchar *cp;
238
/* adjust cur */
239
cp = (genchar *)outbuff + *cur;
240
c = *cp;
241
*cp = 0;
242
*cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
243
*cp = c;
244
*eol = ed_external((genchar*)outbuff,outbuff);
245
}
246
#endif /* SHOPT_MULTIBYTE */
247
out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
248
if(out[-1]=='"' || out[-1]=='\'')
249
{
250
rval = -(sh_isoption(SH_VI)!=0);
251
goto done;
252
}
253
comptr->comtyp = COMSCAN;
254
comptr->comarg = ap;
255
ap->argflag = (ARG_MAC|ARG_EXP);
256
ap->argnxt.ap = 0;
257
ap->argchn.cp = 0;
258
{
259
register int c;
260
char *last = out;
261
c = *(unsigned char*)out;
262
var = mode;
263
begin = out = find_begin(outbuff,last,0,&var);
264
/* addstar set to zero if * should not be added */
265
if(var=='$')
266
{
267
stakputs("${!");
268
stakwrite(out,last-out);
269
stakputs("@}");
270
out = last;
271
}
272
else
273
{
274
addstar = '*';
275
while(out < last)
276
{
277
c = *(unsigned char*)out;
278
if(isexp(c))
279
addstar = 0;
280
if (c == '/')
281
{
282
if(addstar == 0)
283
strip = 0;
284
dir = out+1;
285
}
286
stakputc(c);
287
out++;
288
}
289
}
290
if(mode=='?')
291
mode = '*';
292
if(var!='$' && mode=='\\' && out[-1]!='*')
293
addstar = '*';
294
if(*begin=='~' && !strchr(begin,'/'))
295
addstar = 0;
296
stakputc(addstar);
297
ap = (struct argnod*)stakfreeze(1);
298
}
299
if(mode!='*')
300
sh_onoption(SH_MARKDIRS);
301
{
302
register char **com;
303
char *cp=begin, *left=0, *saveout=".";
304
int nocase=0,narg,cmd_completion=0;
305
register int size='x';
306
while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
307
cp--;
308
if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
309
{
310
cmd_completion=1;
311
sh_onstate(SH_COMPLETE);
312
}
313
if(ep->e_nlist)
314
{
315
narg = 1;
316
com = av;
317
if(dir)
318
begin += (dir-begin);
319
}
320
else
321
{
322
com = sh_argbuild(ep->sh,&narg,comptr,0);
323
/* special handling for leading quotes */
324
if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
325
begin--;
326
}
327
sh_offstate(SH_COMPLETE);
328
/* allow a search to be aborted */
329
if(ep->sh->trapnote&SH_SIGSET)
330
{
331
rval = -1;
332
goto done;
333
}
334
/* match? */
335
if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
336
{
337
rval = -1;
338
goto done;
339
}
340
if(mode=='\\' && out[-1]=='/' && narg>1)
341
mode = '=';
342
if(mode=='=')
343
{
344
if (strip && !cmd_completion)
345
{
346
register char **ptrcom;
347
for(ptrcom=com;*ptrcom;ptrcom++)
348
/* trim directory prefix */
349
*ptrcom = path_basename(*ptrcom);
350
}
351
sfputc(sfstderr,'\n');
352
sh_menu(sfstderr,narg,com);
353
sfsync(sfstderr);
354
ep->e_nlist = narg;
355
ep->e_clist = com;
356
goto done;
357
}
358
/* see if there is enough room */
359
size = *eol - (out-begin);
360
if(mode=='\\')
361
{
362
int c;
363
if(dir)
364
{
365
c = *dir;
366
*dir = 0;
367
saveout = begin;
368
}
369
if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
370
nocase = (strchr(saveout,'c')!=0);
371
if(dir)
372
*dir = c;
373
/* just expand until name is unique */
374
size += strlen(*com);
375
}
376
else
377
{
378
size += narg;
379
{
380
char **savcom = com;
381
while (*com)
382
size += strlen(cp=fmtx(*com++));
383
com = savcom;
384
}
385
}
386
/* see if room for expansion */
387
if(outbuff+size >= &outbuff[MAXLINE])
388
{
389
com[0] = ap->argval;
390
com[1] = 0;
391
}
392
/* save remainder of the buffer */
393
if(*out)
394
left=stakcopy(out);
395
if(cmd_completion && mode=='\\')
396
out = strcopy(begin,path_basename(cp= *com++));
397
else if(mode=='*')
398
{
399
if(ep->e_nlist && dir && var)
400
{
401
if(*cp==var)
402
cp++;
403
else
404
*begin++ = var;
405
out = strcopy(begin,cp);
406
var = 0;
407
}
408
else
409
out = strcopy(begin,fmtx(*com));
410
com++;
411
}
412
else
413
out = strcopy(begin,*com++);
414
if(mode=='\\')
415
{
416
saveout= ++out;
417
while (*com && *begin)
418
{
419
if(cmd_completion)
420
out = overlaid(begin,path_basename(*com++),nocase);
421
else
422
out = overlaid(begin,*com++,nocase);
423
}
424
mode = (out==saveout);
425
if(out[-1]==0)
426
out--;
427
if(mode && out[-1]!='/')
428
{
429
if(cmd_completion)
430
{
431
Namval_t *np;
432
/* add as tracked alias */
433
Pathcomp_t *pp;
434
if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD)))
435
path_alias(np,pp);
436
out = strcopy(begin,cp);
437
}
438
/* add quotes if necessary */
439
if((cp=fmtx(begin))!=begin)
440
out = strcopy(begin,cp);
441
if(var=='$' && begin[-1]=='{')
442
*out = '}';
443
else
444
*out = ' ';
445
*++out = 0;
446
}
447
else if((cp=fmtx(begin))!=begin)
448
{
449
out = strcopy(begin,cp);
450
if(out[-1] =='"' || out[-1]=='\'')
451
*--out = 0;
452
}
453
if(*begin==0)
454
ed_ringbell();
455
}
456
else
457
{
458
while (*com)
459
{
460
*out++ = ' ';
461
out = strcopy(out,fmtx(*com++));
462
}
463
}
464
if(ep->e_nlist)
465
{
466
cp = com[-1];
467
if(cp[strlen(cp)-1]!='/')
468
{
469
if(var=='$' && begin[-1]=='{')
470
*out = '}';
471
else
472
*out = ' ';
473
out++;
474
}
475
else if(out[-1] =='"' || out[-1]=='\'')
476
out--;
477
*out = 0;
478
}
479
*cur = (out-outbuff);
480
/* restore rest of buffer */
481
if(left)
482
out = strcopy(out,left);
483
*eol = (out-outbuff);
484
}
485
done:
486
sh_offstate(SH_FCOMPLETE);
487
if(!ep->e_nlist)
488
stakset(ep->e_stkptr,ep->e_stkoff);
489
if(nomarkdirs)
490
sh_offoption(SH_MARKDIRS);
491
#if SHOPT_MULTIBYTE
492
{
493
register int c,n=0;
494
/* first re-adjust cur */
495
c = outbuff[*cur];
496
outbuff[*cur] = 0;
497
for(out=outbuff; *out;n++)
498
mbchar(out);
499
outbuff[*cur] = c;
500
*cur = n;
501
outbuff[*eol+1] = 0;
502
*eol = ed_internal(outbuff,(genchar*)outbuff);
503
}
504
#endif /* SHOPT_MULTIBYTE */
505
return(rval);
506
}
507
508
/*
509
* look for edit macro named _i
510
* if found, puts the macro definition into lookahead buffer and returns 1
511
*/
512
int ed_macro(Edit_t *ep, register int i)
513
{
514
register char *out;
515
Namval_t *np;
516
genchar buff[LOOKAHEAD+1];
517
if(i != '@')
518
ep->e_macro[1] = i;
519
/* undocumented feature, macros of the form <ESC>[c evoke alias __c */
520
if(i=='_')
521
ep->e_macro[2] = ed_getchar(ep,1);
522
else
523
ep->e_macro[2] = 0;
524
if (isalnum(i)&&(np=nv_search(ep->e_macro,ep->sh->alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
525
{
526
#if SHOPT_MULTIBYTE
527
/* copy to buff in internal representation */
528
int c = 0;
529
if( strlen(out) > LOOKAHEAD )
530
{
531
c = out[LOOKAHEAD];
532
out[LOOKAHEAD] = 0;
533
}
534
i = ed_internal(out,buff);
535
if(c)
536
out[LOOKAHEAD] = c;
537
#else
538
strncpy((char*)buff,out,LOOKAHEAD);
539
buff[LOOKAHEAD] = 0;
540
i = strlen((char*)buff);
541
#endif /* SHOPT_MULTIBYTE */
542
while(i-- > 0)
543
ed_ungetchar(ep,buff[i]);
544
return(1);
545
}
546
return(0);
547
}
548
549
/*
550
* Enter the fc command on the current history line
551
*/
552
int ed_fulledit(Edit_t *ep)
553
{
554
register char *cp;
555
if(!shgd->hist_ptr)
556
return(-1);
557
/* use EDITOR on current command */
558
if(ep->e_hline == ep->e_hismax)
559
{
560
if(ep->e_eol<0)
561
return(-1);
562
#if SHOPT_MULTIBYTE
563
ep->e_inbuf[ep->e_eol+1] = 0;
564
ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
565
#endif /* SHOPT_MULTIBYTE */
566
sfwrite(shgd->hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
567
sh_onstate(SH_HISTORY);
568
hist_flush(shgd->hist_ptr);
569
}
570
cp = strcopy((char*)ep->e_inbuf,e_runvi);
571
cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
572
ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
573
return(0);
574
}
575
576