]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libcomplete/complete.c
ip/ipconfig: don't null terminate the dhcp string options.
[plan9front.git] / sys / src / libcomplete / complete.c
1 #include <u.h>
2 #include <libc.h>
3 #include "complete.h"
4
5 static int
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
23 freecompletion(Completion *c)
24 {
25         if(c){
26                 free(c->filename);
27                 free(c);
28         }
29 }
30
31 static int
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*
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 }