URI:
       tcomplete.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tcomplete.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 }