Merge from vendor branch HEIMDAL:
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / editline / complete.c
1 /*  Copyright 1992 Simmule Turner and Rich Salz.  All rights reserved. 
2  *
3  *  This software is not subject to any license of the American Telephone 
4  *  and Telegraph Company or of the Regents of the University of California. 
5  *
6  *  Permission is granted to anyone to use this software for any purpose on
7  *  any computer system, and to alter it and redistribute it freely, subject
8  *  to the following restrictions:
9  *  1. The authors are not responsible for the consequences of use of this
10  *     software, no matter how awful, even if they arise from flaws in it.
11  *  2. The origin of this software must not be misrepresented, either by
12  *     explicit claim or by omission.  Since few users ever read sources,
13  *     credits must appear in the documentation.
14  *  3. Altered versions must be plainly marked as such, and must not be
15  *     misrepresented as being the original software.  Since few users
16  *     ever read sources, credits must appear in the documentation.
17  *  4. This notice may not be removed or altered.
18  */
19
20 /*
21 **  History and file completion functions for editline library.
22 */
23 #include <config.h>
24 #include "editline.h"
25
26 RCSID("$Id: complete.c,v 1.5 1999/04/10 21:01:16 joda Exp $");
27
28 /*
29 **  strcmp-like sorting predicate for qsort.
30 */
31 static int
32 compare(const void *p1, const void *p2)
33 {
34     const char  **v1;
35     const char  **v2;
36     
37     v1 = (const char **)p1;
38     v2 = (const char **)p2;
39     return strcmp(*v1, *v2);
40 }
41
42 /*
43 **  Fill in *avp with an array of names that match file, up to its length.
44 **  Ignore . and .. .
45 */
46 static int
47 FindMatches(char *dir, char *file, char ***avp)
48 {
49     char        **av;
50     char        **new;
51     char        *p;
52     DIR         *dp;
53     DIRENTRY    *ep;
54     size_t      ac;
55     size_t      len;
56
57     if ((dp = opendir(dir)) == NULL)
58         return 0;
59
60     av = NULL;
61     ac = 0;
62     len = strlen(file);
63     while ((ep = readdir(dp)) != NULL) {
64         p = ep->d_name;
65         if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
66             continue;
67         if (len && strncmp(p, file, len) != 0)
68             continue;
69
70         if ((ac % MEM_INC) == 0) {
71             if ((new = malloc(sizeof(char*) * (ac + MEM_INC))) == NULL)
72                 break;
73             if (ac) {
74                 memcpy(new, av, ac * sizeof (char **));
75                 free(av);
76             }
77             *avp = av = new;
78         }
79
80         if ((av[ac] = strdup(p)) == NULL) {
81             if (ac == 0)
82                 free(av);
83             break;
84         }
85         ac++;
86     }
87
88     /* Clean up and return. */
89     (void)closedir(dp);
90     if (ac)
91         qsort(av, ac, sizeof (char **), compare);
92     return ac;
93 }
94
95 /*
96 **  Split a pathname into allocated directory and trailing filename parts.
97 */
98 static int SplitPath(char *path, char **dirpart, char **filepart)
99 {
100     static char DOT[] = ".";
101     char        *dpart;
102     char        *fpart;
103
104     if ((fpart = strrchr(path, '/')) == NULL) {
105         if ((dpart = strdup(DOT)) == NULL)
106             return -1;
107         if ((fpart = strdup(path)) == NULL) {
108             free(dpart);
109             return -1;
110         }
111     }
112     else {
113         if ((dpart = strdup(path)) == NULL)
114             return -1;
115         dpart[fpart - path] = '\0';
116         if ((fpart = strdup(++fpart)) == NULL) {
117             free(dpart);
118             return -1;
119         }
120     }
121     *dirpart = dpart;
122     *filepart = fpart;
123     return 0;
124 }
125
126 /*
127 **  Attempt to complete the pathname, returning an allocated copy.
128 **  Fill in *unique if we completed it, or set it to 0 if ambiguous.
129 */
130
131 static char *
132 rl_complete_filename(char *pathname, int *unique)
133 {
134     char        **av;
135     char        *new;
136     char        *p;
137     size_t      ac;
138     size_t      end;
139     size_t      i;
140     size_t      j;
141     size_t      len;
142     char *s;
143     
144     ac = rl_list_possib(pathname, &av);
145     if(ac == 0)
146         return NULL;
147
148     s = strrchr(pathname, '/');
149     if(s == NULL)
150         len = strlen(pathname);
151     else
152         len = strlen(s + 1);
153
154     p = NULL;
155     if (ac == 1) {
156         /* Exactly one match -- finish it off. */
157         *unique = 1;
158         j = strlen(av[0]) - len + 2;
159         if ((p = malloc(j + 1)) != NULL) {
160             memcpy(p, av[0] + len, j);
161             asprintf(&new, "%s%s", pathname, p);
162             if(new != NULL) {
163                 rl_add_slash(new, p);
164                 free(new);
165             }
166         }
167     }
168     else {
169         *unique = 0;
170         if (len) {
171             /* Find largest matching substring. */
172             for (i = len, end = strlen(av[0]); i < end; i++)
173                 for (j = 1; j < ac; j++)
174                     if (av[0][i] != av[j][i])
175                         goto breakout;
176   breakout:
177             if (i > len) {
178                 j = i - len + 1;
179                 if ((p = malloc(j)) != NULL) {
180                     memcpy(p, av[0] + len, j);
181                     p[j - 1] = '\0';
182                 }
183             }
184         }
185     }
186
187     /* Clean up and return. */
188     for (i = 0; i < ac; i++)
189         free(av[i]);
190     free(av);
191     return p;
192 }
193
194 static rl_complete_func_t complete_func = rl_complete_filename;
195
196 char *
197 rl_complete(char *pathname, int *unique)
198 {
199     return (*complete_func)(pathname, unique);
200 }
201
202 rl_complete_func_t
203 rl_set_complete_func(rl_complete_func_t func)
204 {
205     rl_complete_func_t old = complete_func;
206     complete_func = func;
207     return old;
208 }
209
210
211 /*
212 **  Return all possible completions.
213 */
214 static int
215 rl_list_possib_filename(char *pathname, char ***avp)
216 {
217     char        *dir;
218     char        *file;
219     int         ac;
220
221     if (SplitPath(pathname, &dir, &file) < 0)
222         return 0;
223     ac = FindMatches(dir, file, avp);
224     free(dir);
225     free(file);
226     return ac;
227 }
228
229 static rl_list_possib_func_t list_possib_func = rl_list_possib_filename;
230
231 int
232 rl_list_possib(char *pathname, char ***avp)
233 {
234     return (*list_possib_func)(pathname, avp);
235 }
236
237 rl_list_possib_func_t
238 rl_set_list_possib_func(rl_list_possib_func_t func)
239 {
240     rl_list_possib_func_t old = list_possib_func;
241     list_possib_func = func;
242     return old;
243 }