| /* $Revision: 1.3 $ |
| ** |
| ** 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; |
| |
| if ((dp = opendir(dir)) == NULL) |
| return 0; |
| |
| av = NULL; |
| ac = 0; |
| len = strlen(file); |
| 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; |
| |
| if ((ac % MEM_INC) == 0) { |
| if ((new = NEW(char*, ac + MEM_INC)) == NULL) |
| 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); |
| break; |
| } |
| ac++; |
| } |
| |
| /* Clean up and return. */ |
| (void)closedir(dp); |
| 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] = '\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; |
| } |