180e0677aSDavid du Colombier #include <u.h>
280e0677aSDavid du Colombier #include <libc.h>
380e0677aSDavid du Colombier #include "complete.h"
480e0677aSDavid du Colombier
580e0677aSDavid du Colombier static int
longestprefixlength(char * a,char * b,int n)680e0677aSDavid du Colombier longestprefixlength(char *a, char *b, int n)
780e0677aSDavid du Colombier {
880e0677aSDavid du Colombier int i, w;
980e0677aSDavid du Colombier Rune ra, rb;
1080e0677aSDavid du Colombier
1180e0677aSDavid du Colombier for(i=0; i<n; i+=w){
1280e0677aSDavid du Colombier w = chartorune(&ra, a);
1380e0677aSDavid du Colombier chartorune(&rb, b);
1480e0677aSDavid du Colombier if(ra != rb)
1580e0677aSDavid du Colombier break;
1680e0677aSDavid du Colombier a += w;
1780e0677aSDavid du Colombier b += w;
1880e0677aSDavid du Colombier }
1980e0677aSDavid du Colombier return i;
2080e0677aSDavid du Colombier }
2180e0677aSDavid du Colombier
2280e0677aSDavid du Colombier void
freecompletion(Completion * c)2380e0677aSDavid du Colombier freecompletion(Completion *c)
2480e0677aSDavid du Colombier {
2580e0677aSDavid du Colombier if(c){
2680e0677aSDavid du Colombier free(c->filename);
2780e0677aSDavid du Colombier free(c);
2880e0677aSDavid du Colombier }
2980e0677aSDavid du Colombier }
3080e0677aSDavid du Colombier
3180e0677aSDavid du Colombier static int
strpcmp(const void * va,const void * vb)3280e0677aSDavid du Colombier strpcmp(const void *va, const void *vb)
3380e0677aSDavid du Colombier {
3480e0677aSDavid du Colombier char *a, *b;
3580e0677aSDavid du Colombier
3680e0677aSDavid du Colombier a = *(char**)va;
3780e0677aSDavid du Colombier b = *(char**)vb;
3880e0677aSDavid du Colombier return strcmp(a, b);
3980e0677aSDavid du Colombier }
4080e0677aSDavid du Colombier
4180e0677aSDavid du Colombier Completion*
complete(char * dir,char * s)4280e0677aSDavid du Colombier complete(char *dir, char *s)
4380e0677aSDavid du Colombier {
44abe9e455SDavid du Colombier long i, l, n, nfile, len, nbytes;
4580e0677aSDavid du Colombier int fd, minlen;
4680e0677aSDavid du Colombier Dir *dirp;
4780e0677aSDavid du Colombier char **name, *p;
4880e0677aSDavid du Colombier ulong* mode;
4980e0677aSDavid du Colombier Completion *c;
5080e0677aSDavid du Colombier
5180e0677aSDavid du Colombier if(strchr(s, '/') != nil){
5280e0677aSDavid du Colombier werrstr("slash character in name argument to complete()");
5380e0677aSDavid du Colombier return nil;
5480e0677aSDavid du Colombier }
5580e0677aSDavid du Colombier
5680e0677aSDavid du Colombier fd = open(dir, OREAD);
5780e0677aSDavid du Colombier if(fd < 0)
5880e0677aSDavid du Colombier return nil;
5980e0677aSDavid du Colombier
6080e0677aSDavid du Colombier n = dirreadall(fd, &dirp);
61*ce941d97SDavid du Colombier if(n <= 0){
62*ce941d97SDavid du Colombier close(fd);
6380e0677aSDavid du Colombier return nil;
64*ce941d97SDavid du Colombier }
6580e0677aSDavid du Colombier
6680e0677aSDavid du Colombier /* find longest string, for allocation */
6780e0677aSDavid du Colombier len = 0;
6880e0677aSDavid du Colombier for(i=0; i<n; i++){
6980e0677aSDavid du Colombier l = strlen(dirp[i].name) + 1 + 1; /* +1 for / +1 for \0 */
7080e0677aSDavid du Colombier if(l > len)
7180e0677aSDavid du Colombier len = l;
7280e0677aSDavid du Colombier }
7380e0677aSDavid du Colombier
7480e0677aSDavid du Colombier name = malloc(n*sizeof(char*));
7580e0677aSDavid du Colombier mode = malloc(n*sizeof(ulong));
7680e0677aSDavid du Colombier c = malloc(sizeof(Completion) + len);
7780e0677aSDavid du Colombier if(name == nil || mode == nil || c == nil)
7880e0677aSDavid du Colombier goto Return;
7980e0677aSDavid du Colombier memset(c, 0, sizeof(Completion));
8080e0677aSDavid du Colombier
8180e0677aSDavid du Colombier /* find the matches */
8280e0677aSDavid du Colombier len = strlen(s);
83abe9e455SDavid du Colombier nfile = 0;
8480e0677aSDavid du Colombier minlen = 1000000;
8580e0677aSDavid du Colombier for(i=0; i<n; i++)
8680e0677aSDavid du Colombier if(strncmp(s, dirp[i].name, len) == 0){
87abe9e455SDavid du Colombier name[nfile] = dirp[i].name;
88abe9e455SDavid du Colombier mode[nfile] = dirp[i].mode;
8980e0677aSDavid du Colombier if(minlen > strlen(dirp[i].name))
9080e0677aSDavid du Colombier minlen = strlen(dirp[i].name);
91abe9e455SDavid du Colombier nfile++;
9280e0677aSDavid du Colombier }
9380e0677aSDavid du Colombier
94abe9e455SDavid du Colombier if(nfile > 0) {
9580e0677aSDavid du Colombier /* report interesting results */
9680e0677aSDavid du Colombier /* trim length back to longest common initial string */
97abe9e455SDavid du Colombier for(i=1; i<nfile; i++)
9880e0677aSDavid du Colombier minlen = longestprefixlength(name[0], name[i], minlen);
9980e0677aSDavid du Colombier
10080e0677aSDavid du Colombier /* build the answer */
101abe9e455SDavid du Colombier c->complete = (nfile == 1);
10280e0677aSDavid du Colombier c->advance = c->complete || (minlen > len);
10380e0677aSDavid du Colombier c->string = (char*)(c+1);
10480e0677aSDavid du Colombier memmove(c->string, name[0]+len, minlen-len);
10580e0677aSDavid du Colombier if(c->complete)
10680e0677aSDavid du Colombier c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
10780e0677aSDavid du Colombier c->string[minlen - len] = '\0';
108abe9e455SDavid du Colombier c->nmatch = nfile;
10980e0677aSDavid du Colombier } else {
11080e0677aSDavid du Colombier /* no match, so return all possible strings */
11180e0677aSDavid du Colombier for(i=0; i<n; i++){
11280e0677aSDavid du Colombier name[i] = dirp[i].name;
11380e0677aSDavid du Colombier mode[i] = dirp[i].mode;
11480e0677aSDavid du Colombier }
115abe9e455SDavid du Colombier nfile = n;
116abe9e455SDavid du Colombier c->nmatch = 0;
11780e0677aSDavid du Colombier }
11880e0677aSDavid du Colombier
11980e0677aSDavid du Colombier /* attach list of names */
120abe9e455SDavid du Colombier nbytes = nfile * sizeof(char*);
121abe9e455SDavid du Colombier for(i=0; i<nfile; i++)
12280e0677aSDavid du Colombier nbytes += strlen(name[i]) + 1 + 1;
12380e0677aSDavid du Colombier c->filename = malloc(nbytes);
12480e0677aSDavid du Colombier if(c->filename == nil)
12580e0677aSDavid du Colombier goto Return;
126abe9e455SDavid du Colombier p = (char*)(c->filename + nfile);
127abe9e455SDavid du Colombier for(i=0; i<nfile; i++){
12880e0677aSDavid du Colombier c->filename[i] = p;
12980e0677aSDavid du Colombier strcpy(p, name[i]);
13080e0677aSDavid du Colombier p += strlen(p);
13180e0677aSDavid du Colombier if(mode[i] & DMDIR)
13280e0677aSDavid du Colombier *p++ = '/';
13380e0677aSDavid du Colombier *p++ = '\0';
13480e0677aSDavid du Colombier }
135abe9e455SDavid du Colombier c->nfile = nfile;
13680e0677aSDavid du Colombier qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
13780e0677aSDavid du Colombier
13880e0677aSDavid du Colombier Return:
13980e0677aSDavid du Colombier free(name);
14080e0677aSDavid du Colombier free(mode);
14180e0677aSDavid du Colombier free(dirp);
142*ce941d97SDavid du Colombier close(fd);
14380e0677aSDavid du Colombier return c;
14480e0677aSDavid du Colombier }
145