complete.c (2726B)
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 }