Edinburgh Speech Tools  2.4-release
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends Pages
el_complete.c
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 */
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 */
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 */
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 */
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 */
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 */
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 }