1 #include <u.h>
2 #include <libc.h>
3 #include "complete.h"
4 
5 static int
longestprefixlength(char * a,char * b,int n)6 longestprefixlength(char *a, char *b, int n)
7 {
8 	int i, w;
9 	Rune ra, rb;
10 
11 	for(i=0; i<n; i+=w){
12 		w = chartorune(&ra, a);
13 		chartorune(&rb, b);
14 		if(ra != rb)
15 			break;
16 		a += w;
17 		b += w;
18 	}
19 	return i;
20 }
21 
22 void
freecompletion(Completion * c)23 freecompletion(Completion *c)
24 {
25 	if(c){
26 		free(c->filename);
27 		free(c);
28 	}
29 }
30 
31 static int
strpcmp(const void * va,const void * vb)32 strpcmp(const void *va, const void *vb)
33 {
34 	char *a, *b;
35 
36 	a = *(char**)va;
37 	b = *(char**)vb;
38 	return strcmp(a, b);
39 }
40 
41 Completion*
complete(char * dir,char * s)42 complete(char *dir, char *s)
43 {
44 	long i, l, n, nfile, len, nbytes;
45 	int fd, minlen;
46 	Dir *dirp;
47 	char **name, *p;
48 	ulong* mode;
49 	Completion *c;
50 
51 	if(strchr(s, '/') != nil){
52 		werrstr("slash character in name argument to complete()");
53 		return nil;
54 	}
55 
56 	fd = open(dir, OREAD);
57 	if(fd < 0)
58 		return nil;
59 
60 	n = dirreadall(fd, &dirp);
61 	if(n <= 0){
62 		close(fd);
63 		return nil;
64 	}
65 
66 	/* find longest string, for allocation */
67 	len = 0;
68 	for(i=0; i<n; i++){
69 		l = strlen(dirp[i].name) + 1 + 1; /* +1 for /   +1 for \0 */
70 		if(l > len)
71 			len = l;
72 	}
73 
74 	name = malloc(n*sizeof(char*));
75 	mode = malloc(n*sizeof(ulong));
76 	c = malloc(sizeof(Completion) + len);
77 	if(name == nil || mode == nil || c == nil)
78 		goto Return;
79 	memset(c, 0, sizeof(Completion));
80 
81 	/* find the matches */
82 	len = strlen(s);
83 	nfile = 0;
84 	minlen = 1000000;
85 	for(i=0; i<n; i++)
86 		if(strncmp(s, dirp[i].name, len) == 0){
87 			name[nfile] = dirp[i].name;
88 			mode[nfile] = dirp[i].mode;
89 			if(minlen > strlen(dirp[i].name))
90 				minlen = strlen(dirp[i].name);
91 			nfile++;
92 		}
93 
94 	if(nfile > 0) {
95 		/* report interesting results */
96 		/* trim length back to longest common initial string */
97 		for(i=1; i<nfile; i++)
98 			minlen = longestprefixlength(name[0], name[i], minlen);
99 
100 		/* build the answer */
101 		c->complete = (nfile == 1);
102 		c->advance = c->complete || (minlen > len);
103 		c->string = (char*)(c+1);
104 		memmove(c->string, name[0]+len, minlen-len);
105 		if(c->complete)
106 			c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
107 		c->string[minlen - len] = '\0';
108 		c->nmatch = nfile;
109 	} else {
110 		/* no match, so return all possible strings */
111 		for(i=0; i<n; i++){
112 			name[i] = dirp[i].name;
113 			mode[i] = dirp[i].mode;
114 		}
115 		nfile = n;
116 		c->nmatch = 0;
117 	}
118 
119 	/* attach list of names */
120 	nbytes = nfile * sizeof(char*);
121 	for(i=0; i<nfile; i++)
122 		nbytes += strlen(name[i]) + 1 + 1;
123 	c->filename = malloc(nbytes);
124 	if(c->filename == nil)
125 		goto Return;
126 	p = (char*)(c->filename + nfile);
127 	for(i=0; i<nfile; i++){
128 		c->filename[i] = p;
129 		strcpy(p, name[i]);
130 		p += strlen(p);
131 		if(mode[i] & DMDIR)
132 			*p++ = '/';
133 		*p++ = '\0';
134 	}
135 	c->nfile = nfile;
136 	qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
137 
138   Return:
139 	free(name);
140 	free(mode);
141 	free(dirp);
142 	close(fd);
143 	return c;
144 }
145