/* $Revision: 1.1.1.1 $ ** ** History and file completion functions for editline library. */ #include "editline.h" #if defined(NEED_STRDUP) /* ** Return an allocated copy of a string. */ char * strdup(p) char *p; { char *new; if ((new = NEW(char, strlen(p) + 1)) != NULL) (void)strcpy(new, p); return new; } #endif /* defined(NEED_STRDUP) */ /* ** strcmp-like sorting predicate for qsort. */ STATIC int compare(p1, p2) CONST void *p1; CONST void *p2; { CONST char **v1; CONST char **v2; v1 = (CONST char **)p1; v2 = (CONST char **)p2; return strcmp(*v1, *v2); } /* ** Fill in *avp with an array of names that match file, up to its length. ** Ignore . and .. . */ STATIC int FindMatches(dir, file, avp) char *dir; char *file; char ***avp; { char **av; char **new; char *p; DIR *dp; DIRENTRY *ep; SIZE_T ac; SIZE_T len; SIZE_T choices; SIZE_T total; #define MAX_TOTAL (256 << sizeof(char *)) if ((dp = opendir(dir)) == NULL) return 0; av = NULL; ac = 0; len = strlen(file); choices = 0; total = 0; while ((ep = readdir(dp)) != NULL) { p = ep->d_name; if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))) continue; if (len && strncmp(p, file, len) != 0) continue; choices++; if ((total += strlen(p)) > MAX_TOTAL) { /* This is a bit too much. */ while (ac > 0) DISPOSE(av[--ac]); continue; } if ((ac % MEM_INC) == 0) { if ((new = NEW(char*, ac + MEM_INC)) == NULL) { total = 0; break; } if (ac) { COPYFROMTO(new, av, ac * sizeof (char **)); DISPOSE(av); } *avp = av = new; } if ((av[ac] = strdup(p)) == NULL) { if (ac == 0) DISPOSE(av); total = 0; break; } ac++; } /* Clean up and return. */ (void)closedir(dp); if (total > MAX_TOTAL) { char many[sizeof(total) * 3]; p = many + sizeof(many); *--p = '\0'; while (choices > 0) { *--p = '0' + choices % 10; choices /= 10; } while (p > many + sizeof(many) - 8) *--p = ' '; if ((p = strdup(p)) != NULL) av[ac++] = p; if ((p = strdup("choices")) != NULL) av[ac++] = p; } else { if (ac) qsort(av, ac, sizeof (char **), compare); } return ac; } /* ** Split a pathname into allocated directory and trailing filename parts. */ STATIC int SplitPath(path, dirpart, filepart) char *path; char **dirpart; char **filepart; { static char DOT[] = "."; char *dpart; char *fpart; if ((fpart = strrchr(path, '/')) == NULL) { if ((dpart = strdup(DOT)) == NULL) return -1; if ((fpart = strdup(path)) == NULL) { DISPOSE(dpart); return -1; } } else { if ((dpart = strdup(path)) == NULL) return -1; dpart[fpart - path + 1] = '\0'; if ((fpart = strdup(++fpart)) == NULL) { DISPOSE(dpart); return -1; } } *dirpart = dpart; *filepart = fpart; return 0; } /* ** Attempt to complete the pathname, returning an allocated copy. ** Fill in *unique if we completed it, or set it to 0 if ambiguous. */ char * rl_complete(pathname, unique) char *pathname; int *unique; { char **av; char *dir; char *file; char *new; char *p; SIZE_T ac; SIZE_T end; SIZE_T i; SIZE_T j; SIZE_T len; if (SplitPath(pathname, &dir, &file) < 0) return NULL; if ((ac = FindMatches(dir, file, &av)) == 0) { DISPOSE(dir); DISPOSE(file); return NULL; } p = NULL; len = strlen(file); if (ac == 1) { /* Exactly one match -- finish it off. */ *unique = 1; j = strlen(av[0]) - len + 2; if ((p = NEW(char, j + 1)) != NULL) { COPYFROMTO(p, av[0] + len, j); if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) { (void)strcpy(new, dir); (void)strcat(new, "/"); (void)strcat(new, av[0]); rl_add_slash(new, p); DISPOSE(new); } } } else { *unique = 0; if (len) { /* Find largest matching substring. */ for (i = len, end = strlen(av[0]); i < end; i++) for (j = 1; j < ac; j++) if (av[0][i] != av[j][i]) goto breakout; breakout: if (i > len) { j = i - len + 1; if ((p = NEW(char, j)) != NULL) { COPYFROMTO(p, av[0] + len, j); p[j - 1] = '\0'; } } } } /* Clean up and return. */ DISPOSE(dir); DISPOSE(file); for (i = 0; i < ac; i++) DISPOSE(av[i]); DISPOSE(av); return p; } /* ** Return all possible completions. */ int rl_list_possib(pathname, avp) char *pathname; char ***avp; { char *dir; char *file; int ac; if (SplitPath(pathname, &dir, &file) < 0) return 0; ac = FindMatches(dir, file, avp); DISPOSE(dir); DISPOSE(file); return ac; } /* * $PchId: complete.c,v 1.3 1996/02/22 21:18:51 philip Exp $ */