1 /****************************************************************************/
2 /* */
3 /* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved. */
4 /* */
5 /* This software is not subject to any license of the American Telephone */
6 /* and Telegraph Company or of the Regents of the University of California. */
7 /* */
8 /* Permission is granted to anyone to use this software for any purpose on */
9 /* any computer system, and to alter it and redistribute it freely, subject */
10 /* to the following restrictions: */
11 /* 1. The authors are not responsible for the consequences of use of this */
12 /* software, no matter how awful, even if they arise from flaws in it. */
13 /* 2. The origin of this software must not be misrepresented, either by */
14 /* explicit claim or by omission. Since few users ever read sources, */
15 /* credits must appear in the documentation. */
16 /* 3. Altered versions must be plainly marked as such, and must not be */
17 /* misrepresented as being the original software. Since few users */
18 /* ever read sources, credits must appear in the documentation. */
19 /* 4. This notice may not be removed or altered. */
20 /* */
21 /****************************************************************************/
22 /* */
23 /* This is a line-editing library, it can be linked into almost any */
24 /* program to provide command-line editing and recall. */
25 /* */
26 /* Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT */
27 /* by rsalz@osf.org (Rich $alz) */
28 /* */
29 /****************************************************************************/
30 /* */
31 /* The version contained here has some modifications by awb@cstr.ed.ac.uk */
32 /* (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */
33 /* library and Scheme-in-one-defun in particular. All modifications to */
34 /* to this work are continued with the same copyright above. That is */
35 /* This version editline does not have the the "no commercial use" */
36 /* restriction that some of the rest of the EST library may have */
37 /* awb Dec 30 1998 */
38 /* */
39 /****************************************************************************/
40 /* $Revision: 1.3 $
41 **
42 ** History and file completion functions for editline library.
43 */
44 #include "editline.h"
45
46
47 #if defined(NEED_STRDUP)
48 /*
49 ** Return an allocated copy of a string.
50 */
strdup(char * p)51 char * strdup(char *p)
52 {
53 char *new;
54
55 if ((new = NEW(char, strlen(p) + 1)) != NULL)
56 (void)strcpy(new, p);
57 return new;
58 }
59 #endif /* defined(NEED_STRDUP) */
60
61 /*
62 ** strcmp-like sorting predicate for qsort.
63 */
compare(CONST void * p1,CONST void * p2)64 STATIC int compare(CONST void *p1,CONST void *p2)
65 {
66 CONST char **v1;
67 CONST char **v2;
68
69 v1 = (CONST char **)p1;
70 v2 = (CONST char **)p2;
71 return strcmp(*v1, *v2);
72 }
73
74 /*
75 ** Fill in *avp with an array of names that match file, up to its length.
76 ** Ignore . and .. .
77 */
FindMatches(char * dir,char * file,char *** avp)78 STATIC int FindMatches(char *dir,char *file,char ***avp)
79 {
80 #if !defined(SYSTEM_IS_WIN32)
81 char **av;
82 char **neww;
83 char *p;
84 DIR *dp;
85 DIRENTRY *ep;
86 ESIZE_T ac;
87 ESIZE_T len;
88
89 if ((dp = opendir(dir)) == NULL)
90 return 0;
91
92 av = NULL;
93 ac = 0;
94 len = strlen(file);
95 while ((ep = readdir(dp)) != NULL) {
96 p = ep->d_name;
97 if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
98 continue;
99 if (len && strncmp(p, file, len) != 0)
100 continue;
101
102 if ((ac % MEM_INC) == 0) {
103 if ((neww = NEW(char*, ac + MEM_INC)) == NULL)
104 break;
105 if (ac) {
106 COPYFROMTO(neww, av, ac * sizeof (char **));
107 DISPOSE(av);
108 }
109 *avp = av = neww;
110 }
111
112 if ((av[ac] = STRDUP(p)) == NULL) {
113 if (ac == 0)
114 DISPOSE(av);
115 break;
116 }
117 ac++;
118 }
119
120 /* Clean up and return. */
121 (void)closedir(dp);
122 if (ac)
123 qsort(av, ac, sizeof (char **), compare);
124 return ac;
125 #else
126 *avp=NULL;
127 return 0;
128 #endif
129 }
130
131 /*
132 ** Split a pathname into allocated directory and trailing filename parts.
133 */
SplitPath(char * path,char ** dirpart,char ** filepart)134 STATIC int SplitPath(char *path,char **dirpart,char **filepart)
135 {
136 static char DOT[] = ".";
137 char *dpart;
138 char *fpart;
139
140 if ((fpart = strrchr(path, '/')) == NULL) {
141 if ((dpart = STRDUP(DOT)) == NULL)
142 return -1;
143 if ((fpart = STRDUP(path)) == NULL) {
144 DISPOSE(dpart);
145 return -1;
146 }
147 }
148 else {
149 if ((dpart = STRDUP(path)) == NULL)
150 return -1;
151 dpart[fpart - path] = '\0';
152 if ((fpart = STRDUP(++fpart)) == NULL) {
153 DISPOSE(dpart);
154 return -1;
155 }
156 if (dpart[0] == '\0') /* special case for root */
157 {
158 dpart[0] = '/';
159 dpart[1] = '\0';
160 }
161 }
162 *dirpart = dpart;
163 *filepart = fpart;
164 return 0;
165 }
166
167 /*
168 ** Attempt to complete the pathname, returning an allocated copy.
169 ** Fill in *unique if we completed it, or set it to 0 if ambiguous.
170 */
rl_complete(char * pathname,int * unique)171 char *rl_complete(char *pathname,int *unique)
172 {
173 char **av;
174 char *dir;
175 char *file;
176 char *neww;
177 char *p;
178 ESIZE_T ac;
179 ESIZE_T end;
180 ESIZE_T i;
181 ESIZE_T j;
182 ESIZE_T len;
183
184 if (SplitPath(pathname, &dir, &file) < 0)
185 return NULL;
186 if ((ac = FindMatches(dir, file, &av)) == 0) {
187 DISPOSE(dir);
188 DISPOSE(file);
189 return NULL;
190 }
191
192 p = NULL;
193 len = strlen(file);
194 if (ac == 1) {
195 /* Exactly one match -- finish it off. */
196 *unique = 1;
197 j = strlen(av[0]) - len + 2;
198 if ((p = NEW(char, j + 1)) != NULL) {
199 COPYFROMTO(p, av[0] + len, j);
200 if ((neww = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) {
201 (void)strcpy(neww, dir);
202 (void)strcat(neww, "/");
203 (void)strcat(neww, av[0]);
204 rl_add_slash(neww, p);
205 DISPOSE(neww);
206 }
207 }
208 }
209 else {
210 *unique = 0;
211 if (len) {
212 /* Find largest matching substring. */
213 for (i = len, end = strlen(av[0]); i < end; i++)
214 for (j = 1; j < ac; j++)
215 if (av[0][i] != av[j][i])
216 goto breakout;
217 breakout:
218 if (i > len) {
219 j = i - len + 1;
220 if ((p = NEW(char, j)) != NULL) {
221 COPYFROMTO(p, av[0] + len, j);
222 p[j - 1] = '\0';
223 }
224 }
225 }
226 }
227
228 /* Clean up and return. */
229 DISPOSE(dir);
230 DISPOSE(file);
231 for (i = 0; i < ac; i++)
232 DISPOSE(av[i]);
233 DISPOSE(av);
234 return p;
235 }
236
237 /*
238 ** Return all possible completions.
239 */
rl_list_possib(char * pathname,char *** avp)240 int rl_list_possib(char *pathname,char ***avp)
241 {
242 char *dir;
243 char *file, *path, *tt;
244 int ac,i;
245
246 if (SplitPath(pathname, &dir, &file) < 0)
247 return 0;
248 ac = FindMatches(dir, file, avp);
249 /* Identify directories with trailing / */
250 for (i = 0; i < ac; i++)
251 {
252 path = walloc(char,strlen(dir)+strlen((*avp)[i])+3);
253 sprintf(path,"%s/%s",dir,(*avp)[i]);
254 if (el_is_directory(path))
255 {
256 tt = walloc(char,strlen((*avp)[i])+2);
257 sprintf(tt,"%s/",(*avp)[i]);
258 wfree((*avp)[i]);
259 (*avp)[i] = tt;
260 }
261 wfree(path);
262 }
263 DISPOSE(dir);
264 DISPOSE(file);
265 return ac;
266 }
267