Blob


1 #include <u.h>
2 #include <libc.h>
3 #include "complete.h"
5 static int
6 longestprefixlength(char *a, char *b, int n)
7 {
8 int i, w;
9 Rune ra, rb;
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 }
22 void
23 freecompletion(Completion *c)
24 {
25 if(c){
26 free(c->filename);
27 free(c);
28 }
29 }
31 static int
32 strpcmp(const void *va, const void *vb)
33 {
34 char *a, *b;
36 a = *(char**)va;
37 b = *(char**)vb;
38 return strcmp(a, b);
39 }
41 Completion*
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;
51 if(strchr(s, '/') != nil){
52 werrstr("slash character in name argument to complete()");
53 return nil;
54 }
56 fd = open(dir, OREAD);
57 if(fd < 0)
58 return nil;
60 n = dirreadall(fd, &dirp);
61 if(n <= 0){
62 close(fd);
63 return nil;
64 }
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 }
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));
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 }
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);
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;
115 nfile = n;
116 c->nmatch = 0;
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';
135 c->nfile = nfile;
136 qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
138 Return:
139 free(name);
140 free(mode);
141 free(dirp);
142 close(fd);
143 return c;