#include <u.h>#include <libc.h>#include "complete.h" static intlongestprefixlength(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;} voidfreecompletion(Completion *c){ if(c){ free(c->filename); free(c); }} static intstrpcmp(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){ long i, l, n, nfile, len, nbytes; 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); if(n <= 0){ close(fd); return nil; } /* 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); nfile = 0; minlen = 1000000; for(i=0; i<n; i++) if(strncmp(s, dirp[i].name, len) == 0){ name[nfile] = dirp[i].name; mode[nfile] = dirp[i].mode; if(minlen > strlen(dirp[i].name)) minlen = strlen(dirp[i].name); nfile++; } if(nfile > 0) { /* report interesting results */ /* trim length back to longest common initial string */ for(i=1; i<nfile; i++) minlen = longestprefixlength(name[0], name[i], minlen); /* build the answer */ c->complete = (nfile == 1); 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'; c->nmatch = nfile; } else { /* no match, so return all possible strings */ for(i=0; i<n; i++){ name[i] = dirp[i].name; mode[i] = dirp[i].mode; } nfile = n; c->nmatch = 0; } /* attach list of names */ nbytes = nfile * sizeof(char*); for(i=0; i<nfile; i++) nbytes += strlen(name[i]) + 1 + 1; c->filename = malloc(nbytes); if(c->filename == nil) goto Return; p = (char*)(c->filename + nfile); for(i=0; i<nfile; i++){ c->filename[i] = p; strcpy(p, name[i]); p += strlen(p); if(mode[i] & DMDIR) *p++ = '/'; *p++ = '\0'; } c->nfile = nfile; qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp); Return: free(name); free(mode); free(dirp); close(fd); return c;}
#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)
{
long i, l, n, nfile, len, nbytes;
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);
if(n <= 0){
close(fd);
return nil;
}
/* 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);
nfile = 0;
minlen = 1000000;
for(i=0; i<n; i++)
if(strncmp(s, dirp[i].name, len) == 0){
name[nfile] = dirp[i].name;
mode[nfile] = dirp[i].mode;
if(minlen > strlen(dirp[i].name))
minlen = strlen(dirp[i].name);
nfile++;
}
if(nfile > 0) {
/* report interesting results */
/* trim length back to longest common initial string */
for(i=1; i<nfile; i++)
minlen = longestprefixlength(name[0], name[i], minlen);
/* build the answer */
c->complete = (nfile == 1);
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';
c->nmatch = nfile;
} else {
/* no match, so return all possible strings */
for(i=0; i<n; i++){
name[i] = dirp[i].name;
mode[i] = dirp[i].mode;
}
nfile = n;
c->nmatch = 0;
}
/* attach list of names */
nbytes = nfile * sizeof(char*);
for(i=0; i<nfile; i++)
nbytes += strlen(name[i]) + 1 + 1;
c->filename = malloc(nbytes);
if(c->filename == nil)
goto Return;
p = (char*)(c->filename + nfile);
for(i=0; i<nfile; i++){
c->filename[i] = p;
strcpy(p, name[i]);
p += strlen(p);
if(mode[i] & DMDIR)
*p++ = '/';
*p++ = '\0';
}
c->nfile = nfile;
qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
Return:
free(name);
free(mode);
free(dirp);
close(fd);
return c;
}