/* complete.c Created: July 1995 by Philip Homburg */ #include #include #include #include #include #include #include "myhistedit.h" #include "shell.h" #include "complete.h" #include "error.h" #include "expand.h" #include "nodes.h" #include "memalloc.h" static char **getlist(EditLine *el, int *baselen, int *isdir); static char **getlist_tilde(char *prefix); static int vstrcmp(const void *v1, const void *v2); static void print_list(char **list); static int install_extra(EditLine *el, char **list, int baselen, int isdir); unsigned char complete(EditLine *el, int ch) { struct stackmark mark; const LineInfo *lf; char **list; int baselen, prefix, isdir; /* Direct the cursor the the end of the word. */ for(;;) { lf = el_line(el); if (lf->cursor < lf->lastchar && !isspace((unsigned char)*lf->cursor)) { (*(char **)&lf->cursor)++; /* XXX */ } else break; } setstackmark(&mark); list= getlist(el, &baselen, &isdir); if (list) { prefix= install_extra(el, list, baselen, isdir); el_push(el, "i"); } popstackmark(&mark); if (list) return CC_REFRESH; else return CC_ERROR; } unsigned char complete_list(EditLine *el, int ch) { struct stackmark mark; char **list; setstackmark(&mark); list= getlist(el, NULL, NULL); if (list) { print_list(list); re_goto_bottom(el); } popstackmark(&mark); if (list) { return CC_REFRESH; } else return CC_ERROR; } unsigned char complete_or_list(EditLine *el, int ch) { struct stackmark mark; const LineInfo *lf; char **list; int baselen, prefix, isdir; setstackmark(&mark); list= getlist(el, &baselen, &isdir); if (list) { prefix= install_extra(el, list, baselen, isdir); if (prefix == baselen) { print_list(list); re_goto_bottom(el); } } popstackmark(&mark); if (list) return CC_REFRESH; else return CC_ERROR; } unsigned char complete_expand(EditLine *el, int ch) { printf("complete_expand\n"); return CC_ERROR; } static char **getlist(EditLine *el, int *baselen, int *isdir) { const LineInfo *lf; const char *begin, *end; char *dirnam, *basenam; union node arg; struct arglist arglist; DIR *dir; struct dirent *dirent; int i, l, n; char *p, **list; struct strlist *slp, *nslp; struct stat sb; lf = el_line(el); /* Try to find to begin and end of the word that we have to comple. */ begin= lf->cursor; while (begin > lf->buffer && !isspace((unsigned char)begin[-1])) begin--; end= lf->cursor; while (end < lf->lastchar && !isspace((unsigned char)end[0])) end++; *(const char **)&lf->cursor= end; /* XXX */ /* Copy the word to a string */ dirnam= stalloc(end-begin+1); strncpy(dirnam, begin, end-begin); dirnam[end-begin]= '\0'; /* Cut the word in two pieces: a path and a (partial) component. */ basenam= strrchr(dirnam, '/'); if (basenam) { basenam++; p= stalloc(strlen(basenam) + 1); strcpy(p, basenam); *basenam= '\0'; basenam= p; } else { if (dirnam[0] == '~') return getlist_tilde(dirnam); basenam= dirnam; dirnam= "./"; } if (baselen) *baselen= strlen(basenam); arg.type= NARG; arg.narg.next= NULL; arg.narg.text= dirnam; arg.narg.backquote= NULL; arglist.list= NULL; arglist.lastp= &arglist.list; expandarg(&arg, &arglist, EXP_TILDE); INTOFF; list= NULL; dir= opendir(arglist.list->text); if (dir) { slp= NULL; n= 0; l= strlen(basenam); while(dirent= readdir(dir)) { if (strncmp(dirent->d_name, basenam, l) != 0) continue; if (l == 0 && dirent->d_name[0] == '.') continue; nslp= stalloc(sizeof(*nslp)); nslp->next= slp; slp= nslp; slp->text= stalloc(strlen(dirent->d_name)+1); strcpy(slp->text, dirent->d_name); n++; if (n == 1 && isdir != NULL) { /* Try to findout whether this entry is a * file or a directory. */ p= stalloc(strlen(arglist.list->text) + strlen(dirent->d_name) + 1); strcpy(p, arglist.list->text); strcat(p, dirent->d_name); if (stat(p, &sb) == -1) printf("stat '%s' failed: %s\n", p, strerror(errno)); if (stat(p, &sb) == 0 && S_ISDIR(sb.st_mode)) *isdir= 1; else *isdir= 0; } } closedir(dir); if (n != 0) { list= stalloc((n+1)*sizeof(*list)); for(i= 0; slp; i++, slp= slp->next) list[i]= slp->text; if (i != n) error("complete'make_list: i != n"); list[i]= NULL; qsort(list, n, sizeof(*list), vstrcmp); } } INTON; return list; } static char **getlist_tilde(char *prefix) { printf("should ~-complete '%s'\n", prefix); return NULL; } static int vstrcmp(const void *v1, const void *v2) { return strcmp(*(char **)v1, *(char **)v2); } #define MAXCOLS 40 #define SEPWIDTH 4 static void print_list(char **list) { struct { int cols; int start[MAXCOLS+1]; int width[MAXCOLS]; } best, next; int e, i, j, l, n, o, cols, maxw, width; int linewidth= 80; /* Count the number of entries. */ for (n= 0; list[n]; n++) ; /* do nothing */ if (n == 0) error("complete'print_list: n= 0"); /* Try to maximize the number of columns */ for (cols= 1; cols<= MAXCOLS; cols++) { next.cols= cols; o= 0; width= 0; for(j= 0; j maxw) maxw= l; } next.width[j]= maxw; width += maxw; o += e; } next.start[j]= o; if (cols > 1 && width-SEPWIDTH>linewidth) break; best= next; } cols= best.cols; e= best.start[1]; printf("\n"); for(i= 0; i0) { if (strncmp(list[0], *lp, l) != 0) l--; else break; } } if (l > baselen || list[1] == NULL) { p= stalloc(l-baselen+2); strncpy(p, list[0]+baselen, l-baselen); if (list[1] == NULL) { p[l-baselen]= isdir ? '/' : ' '; p[l-baselen+1]= '\0'; l++; } else p[l-baselen]= '\0'; if (el_insertstr(el, p) == -1) return -1; } return l; } /* * $PchId: complete.c,v 1.2 2006/04/10 14:35:53 philip Exp $ */