xref: /plan9/sys/src/libcomplete/complete.c (revision ce941d9701512f376c7b3f526a3145bed10e7bee)
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