123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include <u.h>
- #include <libc.h>
- #include "complete.h"
- static int
- longestprefixlength(char *a, char *b, int n)
- {
- int i, w;
- Rune ra, rb;
- for(i=0; i<n; i+=w){
- w = chartorune(&ra, a);
- chartorune(&rb, b);
- if(ra != rb)
- break;
- a += w;
- b += w;
- }
- return i;
- }
- void
- freecompletion(Completion *c)
- {
- if(c){
- free(c->filename);
- free(c);
- }
- }
- static int
- strpcmp(const void *va, const void *vb)
- {
- char *a, *b;
- a = *(char**)va;
- b = *(char**)vb;
- return strcmp(a, b);
- }
- Completion*
- complete(char *dir, char *s)
- {
- int32_t i, l, n, nfile, len, nbytes;
- int fd, minlen;
- Dir *dirp;
- char **name, *p;
- uint32_t* mode;
- Completion *c;
- if(strchr(s, '/') != nil){
- werrstr("slash character in name argument to complete()");
- return nil;
- }
- fd = open(dir, OREAD);
- if(fd < 0)
- return nil;
- n = dirreadall(fd, &dirp);
- if(n <= 0){
- close(fd);
- return nil;
- }
- /* find longest string, for allocation */
- len = 0;
- for(i=0; i<n; i++){
- l = strlen(dirp[i].name) + 1 + 1; /* +1 for / +1 for \0 */
- if(l > len)
- len = l;
- }
- name = malloc(n*sizeof(char*));
- mode = malloc(n*sizeof(uint32_t));
- c = malloc(sizeof(Completion) + len);
- if(name == nil || mode == nil || c == nil)
- goto Return;
- memset(c, 0, sizeof(Completion));
- /* find the matches */
- len = strlen(s);
- nfile = 0;
- minlen = 1000000;
- for(i=0; i<n; i++)
- if(strncmp(s, dirp[i].name, len) == 0){
- name[nfile] = dirp[i].name;
- mode[nfile] = dirp[i].mode;
- if(minlen > strlen(dirp[i].name))
- minlen = strlen(dirp[i].name);
- nfile++;
- }
- if(nfile > 0) {
- /* report interesting results */
- /* trim length back to longest common initial string */
- for(i=1; i<nfile; i++)
- minlen = longestprefixlength(name[0], name[i], minlen);
- /* build the answer */
- c->complete = (nfile == 1);
- c->advance = c->complete || (minlen > len);
- c->string = (char*)(c+1);
- memmove(c->string, name[0]+len, minlen-len);
- if(c->complete)
- c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
- c->string[minlen - len] = '\0';
- c->nmatch = nfile;
- } else {
- /* no match, so return all possible strings */
- for(i=0; i<n; i++){
- name[i] = dirp[i].name;
- mode[i] = dirp[i].mode;
- }
- nfile = n;
- c->nmatch = 0;
- }
- /* attach list of names */
- nbytes = nfile * sizeof(char*);
- for(i=0; i<nfile; i++)
- nbytes += strlen(name[i]) + 1 + 1;
- c->filename = malloc(nbytes);
- if(c->filename == nil)
- goto Return;
- p = (char*)(c->filename + nfile);
- for(i=0; i<nfile; i++){
- c->filename[i] = p;
- strcpy(p, name[i]);
- p += strlen(p);
- if(mode[i] & DMDIR)
- *p++ = '/';
- *p++ = '\0';
- }
- c->nfile = nfile;
- qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
- Return:
- free(name);
- free(mode);
- free(dirp);
- close(fd);
- return c;
- }
|