2004-02-09 19:33:05 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include "complete.h"
|
|
|
|
|
|
|
|
static int
|
|
|
|
longestprefixlength(char *a, char *b, int n)
|
|
|
|
{
|
|
|
|
int i, w;
|
|
|
|
Rune ra, rb;
|
|
|
|
|
|
|
|
for(i=0; i<n; i+=w){
|
|
|
|
w = chartorune(&ra, a);
|
|
|
|
chartorune(&rb, b);
|
|
|
|
if(ra != rb)
|
|
|
|
break;
|
|
|
|
a += w;
|
|
|
|
b += w;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freecompletion(Completion *c)
|
|
|
|
{
|
|
|
|
if(c){
|
|
|
|
free(c->filename);
|
|
|
|
free(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
strpcmp(const void *va, const void *vb)
|
|
|
|
{
|
|
|
|
char *a, *b;
|
|
|
|
|
|
|
|
a = *(char**)va;
|
|
|
|
b = *(char**)vb;
|
|
|
|
return strcmp(a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
Completion*
|
|
|
|
complete(char *dir, char *s)
|
|
|
|
{
|
2004-02-29 22:10:26 +00:00
|
|
|
long i, l, n, nfile, len, nbytes;
|
2004-02-09 19:33:05 +00:00
|
|
|
int fd, minlen;
|
|
|
|
Dir *dirp;
|
|
|
|
char **name, *p;
|
|
|
|
ulong* mode;
|
|
|
|
Completion *c;
|
|
|
|
|
|
|
|
if(strchr(s, '/') != nil){
|
|
|
|
werrstr("slash character in name argument to complete()");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(dir, OREAD);
|
|
|
|
if(fd < 0)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
n = dirreadall(fd, &dirp);
|
2004-02-29 22:10:26 +00:00
|
|
|
if(n <= 0){
|
|
|
|
close(fd);
|
2004-02-09 19:33:05 +00:00
|
|
|
return nil;
|
2004-02-29 22:10:26 +00:00
|
|
|
}
|
2004-02-09 19:33:05 +00:00
|
|
|
|
|
|
|
/* find longest string, for allocation */
|
|
|
|
len = 0;
|
|
|
|
for(i=0; i<n; i++){
|
|
|
|
l = strlen(dirp[i].name) + 1 + 1; /* +1 for / +1 for \0 */
|
|
|
|
if(l > len)
|
|
|
|
len = l;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = malloc(n*sizeof(char*));
|
|
|
|
mode = malloc(n*sizeof(ulong));
|
|
|
|
c = malloc(sizeof(Completion) + len);
|
|
|
|
if(name == nil || mode == nil || c == nil)
|
|
|
|
goto Return;
|
|
|
|
memset(c, 0, sizeof(Completion));
|
|
|
|
|
|
|
|
/* find the matches */
|
|
|
|
len = strlen(s);
|
2004-02-29 22:10:26 +00:00
|
|
|
nfile = 0;
|
2004-02-09 19:33:05 +00:00
|
|
|
minlen = 1000000;
|
|
|
|
for(i=0; i<n; i++)
|
|
|
|
if(strncmp(s, dirp[i].name, len) == 0){
|
2004-02-29 22:10:26 +00:00
|
|
|
name[nfile] = dirp[i].name;
|
|
|
|
mode[nfile] = dirp[i].mode;
|
2004-02-09 19:33:05 +00:00
|
|
|
if(minlen > strlen(dirp[i].name))
|
|
|
|
minlen = strlen(dirp[i].name);
|
2004-02-29 22:10:26 +00:00
|
|
|
nfile++;
|
2004-02-09 19:33:05 +00:00
|
|
|
}
|
|
|
|
|
2004-02-29 22:10:26 +00:00
|
|
|
if(nfile > 0) {
|
2004-02-09 19:33:05 +00:00
|
|
|
/* report interesting results */
|
|
|
|
/* trim length back to longest common initial string */
|
2004-02-29 22:10:26 +00:00
|
|
|
for(i=1; i<nfile; i++)
|
2004-02-09 19:33:05 +00:00
|
|
|
minlen = longestprefixlength(name[0], name[i], minlen);
|
|
|
|
|
|
|
|
/* build the answer */
|
2004-02-29 22:10:26 +00:00
|
|
|
c->complete = (nfile == 1);
|
2004-02-09 19:33:05 +00:00
|
|
|
c->advance = c->complete || (minlen > len);
|
|
|
|
c->string = (char*)(c+1);
|
|
|
|
memmove(c->string, name[0]+len, minlen-len);
|
|
|
|
if(c->complete)
|
|
|
|
c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
|
|
|
|
c->string[minlen - len] = '\0';
|
2004-02-29 22:10:26 +00:00
|
|
|
c->nmatch = nfile;
|
2004-02-09 19:33:05 +00:00
|
|
|
} else {
|
|
|
|
/* no match, so return all possible strings */
|
|
|
|
for(i=0; i<n; i++){
|
|
|
|
name[i] = dirp[i].name;
|
|
|
|
mode[i] = dirp[i].mode;
|
|
|
|
}
|
2004-02-29 22:10:26 +00:00
|
|
|
nfile = n;
|
|
|
|
c->nmatch = 0;
|
2004-02-09 19:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* attach list of names */
|
2004-02-29 22:10:26 +00:00
|
|
|
nbytes = nfile * sizeof(char*);
|
|
|
|
for(i=0; i<nfile; i++)
|
2004-02-09 19:33:05 +00:00
|
|
|
nbytes += strlen(name[i]) + 1 + 1;
|
|
|
|
c->filename = malloc(nbytes);
|
|
|
|
if(c->filename == nil)
|
|
|
|
goto Return;
|
2004-02-29 22:10:26 +00:00
|
|
|
p = (char*)(c->filename + nfile);
|
|
|
|
for(i=0; i<nfile; i++){
|
2004-02-09 19:33:05 +00:00
|
|
|
c->filename[i] = p;
|
|
|
|
strcpy(p, name[i]);
|
|
|
|
p += strlen(p);
|
|
|
|
if(mode[i] & DMDIR)
|
|
|
|
*p++ = '/';
|
|
|
|
*p++ = '\0';
|
|
|
|
}
|
2004-02-29 22:10:26 +00:00
|
|
|
c->nfile = nfile;
|
2004-02-09 19:33:05 +00:00
|
|
|
qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
|
|
|
|
|
|
|
|
Return:
|
|
|
|
free(name);
|
|
|
|
free(mode);
|
|
|
|
free(dirp);
|
2004-02-29 22:10:26 +00:00
|
|
|
close(fd);
|
2004-02-09 19:33:05 +00:00
|
|
|
return c;
|
|
|
|
}
|