plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }