unstack, sort: cleanup and improvement
[minix.git] / commands / ash / complete.c
blobb7ebd30a839ca5f376d63836a41be62811bae0e6
1 /*
2 complete.c
4 Created: July 1995 by Philip Homburg <philip@cs.vu.nl>
5 */
7 #include <ctype.h>
8 #include <dirent.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include "myhistedit.h"
14 #include "shell.h"
16 #include "complete.h"
17 #include "error.h"
18 #include "expand.h"
19 #include "nodes.h"
20 #include "memalloc.h"
22 static char **getlist(EditLine *el, int *baselen, int *isdir);
23 static char **getlist_tilde(char *prefix);
24 static int vstrcmp(const void *v1, const void *v2);
25 static void print_list(char **list);
26 static int install_extra(EditLine *el, char **list, int baselen, int isdir);
28 unsigned char complete(EditLine *el, int ch)
30 struct stackmark mark;
31 const LineInfo *lf;
32 char **list;
33 int baselen, prefix, isdir;
35 /* Direct the cursor the the end of the word. */
36 for(;;)
38 lf = el_line(el);
39 if (lf->cursor < lf->lastchar &&
40 !isspace((unsigned char)*lf->cursor))
42 (*(char **)&lf->cursor)++; /* XXX */
44 else
45 break;
48 setstackmark(&mark);
49 list= getlist(el, &baselen, &isdir);
50 if (list)
52 prefix= install_extra(el, list, baselen, isdir);
53 el_push(el, "i");
55 popstackmark(&mark);
56 if (list)
57 return CC_REFRESH;
58 else
59 return CC_ERROR;
62 unsigned char complete_list(EditLine *el, int ch)
64 struct stackmark mark;
65 char **list;
67 setstackmark(&mark);
68 list= getlist(el, NULL, NULL);
69 if (list)
71 print_list(list);
72 re_goto_bottom(el);
74 popstackmark(&mark);
75 if (list)
77 return CC_REFRESH;
79 else
80 return CC_ERROR;
83 unsigned char complete_or_list(EditLine *el, int ch)
85 struct stackmark mark;
86 const LineInfo *lf;
87 char **list;
88 int baselen, prefix, isdir;
90 setstackmark(&mark);
91 list= getlist(el, &baselen, &isdir);
92 if (list)
94 prefix= install_extra(el, list, baselen, isdir);
95 if (prefix == baselen)
97 print_list(list);
98 re_goto_bottom(el);
101 popstackmark(&mark);
102 if (list)
103 return CC_REFRESH;
104 else
105 return CC_ERROR;
108 unsigned char complete_expand(EditLine *el, int ch)
110 printf("complete_expand\n");
111 return CC_ERROR;
114 static char **getlist(EditLine *el, int *baselen, int *isdir)
116 const LineInfo *lf;
117 const char *begin, *end;
118 char *dirnam, *basenam;
119 union node arg;
120 struct arglist arglist;
121 DIR *dir;
122 struct dirent *dirent;
123 int i, l, n;
124 char *p, **list;
125 struct strlist *slp, *nslp;
126 struct stat sb;
128 lf = el_line(el);
130 /* Try to find to begin and end of the word that we have to comple. */
131 begin= lf->cursor;
132 while (begin > lf->buffer && !isspace((unsigned char)begin[-1]))
133 begin--;
134 end= lf->cursor;
135 while (end < lf->lastchar && !isspace((unsigned char)end[0]))
136 end++;
138 *(const char **)&lf->cursor= end; /* XXX */
140 /* Copy the word to a string */
141 dirnam= stalloc(end-begin+1);
142 strncpy(dirnam, begin, end-begin);
143 dirnam[end-begin]= '\0';
145 /* Cut the word in two pieces: a path and a (partial) component. */
146 basenam= strrchr(dirnam, '/');
147 if (basenam)
149 basenam++;
150 p= stalloc(strlen(basenam) + 1);
151 strcpy(p, basenam);
152 *basenam= '\0';
153 basenam= p;
155 else
157 if (dirnam[0] == '~')
158 return getlist_tilde(dirnam);
159 basenam= dirnam;
160 dirnam= "./";
162 if (baselen)
163 *baselen= strlen(basenam);
165 arg.type= NARG;
166 arg.narg.next= NULL;
167 arg.narg.text= dirnam;
168 arg.narg.backquote= NULL;
169 arglist.list= NULL;
170 arglist.lastp= &arglist.list;
171 expandarg(&arg, &arglist, EXP_TILDE);
173 INTOFF;
174 list= NULL;
175 dir= opendir(arglist.list->text);
176 if (dir)
178 slp= NULL;
179 n= 0;
180 l= strlen(basenam);
181 while(dirent= readdir(dir))
183 if (strncmp(dirent->d_name, basenam, l) != 0)
184 continue;
185 if (l == 0 && dirent->d_name[0] == '.')
186 continue;
187 nslp= stalloc(sizeof(*nslp));
188 nslp->next= slp;
189 slp= nslp;
190 slp->text= stalloc(strlen(dirent->d_name)+1);
191 strcpy(slp->text, dirent->d_name);
192 n++;
193 if (n == 1 && isdir != NULL)
195 /* Try to findout whether this entry is a
196 * file or a directory.
198 p= stalloc(strlen(arglist.list->text) +
199 strlen(dirent->d_name) + 1);
200 strcpy(p, arglist.list->text);
201 strcat(p, dirent->d_name);
202 if (stat(p, &sb) == -1)
203 printf("stat '%s' failed: %s\n",
204 p, strerror(errno));
205 if (stat(p, &sb) == 0 && S_ISDIR(sb.st_mode))
206 *isdir= 1;
207 else
208 *isdir= 0;
211 closedir(dir);
212 if (n != 0)
214 list= stalloc((n+1)*sizeof(*list));
215 for(i= 0; slp; i++, slp= slp->next)
216 list[i]= slp->text;
217 if (i != n)
218 error("complete'make_list: i != n");
219 list[i]= NULL;
220 qsort(list, n, sizeof(*list), vstrcmp);
223 INTON;
224 return list;
227 static char **getlist_tilde(char *prefix)
229 printf("should ~-complete '%s'\n", prefix);
230 return NULL;
233 static int vstrcmp(const void *v1, const void *v2)
235 return strcmp(*(char **)v1, *(char **)v2);
238 #define MAXCOLS 40
239 #define SEPWIDTH 4
241 static void print_list(char **list)
243 struct
245 int cols;
246 int start[MAXCOLS+1];
247 int width[MAXCOLS];
248 } best, next;
249 int e, i, j, l, n, o, cols, maxw, width;
250 int linewidth= 80;
252 /* Count the number of entries. */
253 for (n= 0; list[n]; n++)
254 ; /* do nothing */
255 if (n == 0)
256 error("complete'print_list: n= 0");
258 /* Try to maximize the number of columns */
259 for (cols= 1; cols<= MAXCOLS; cols++)
261 next.cols= cols;
263 o= 0;
264 width= 0;
265 for(j= 0; j<cols; j++)
267 next.start[j]= o;
269 /* Number of entries in this column. */
270 e= (n-o)/(cols-j);
271 if ((n-o)%(cols-j))
272 e++;
274 maxw= 0;
275 for (i= 0; i<e; i++)
277 l= strlen(list[o+i]);
278 if (l < 6)
279 l= 6;
280 l += SEPWIDTH;
281 if (l > maxw)
282 maxw= l;
284 next.width[j]= maxw;
285 width += maxw;
286 o += e;
288 next.start[j]= o;
289 if (cols > 1 && width-SEPWIDTH>linewidth)
290 break;
291 best= next;
293 cols= best.cols;
294 e= best.start[1];
295 printf("\n");
296 for(i= 0; i<e; i++)
298 for (j= 0; j<cols; j++)
300 if (best.start[j]+i == best.start[j+1])
301 continue;
302 if (j < cols-1)
304 printf("%-*s", best.width[j],
305 list[best.start[j]+i]);
307 else
309 printf("%s", list[best.start[j]+i]);
312 if (i < e-1)
313 printf("\n");
315 fflush(stdout);
318 static int install_extra(EditLine *el, char **list, int baselen, int isdir)
320 int l;
321 char *p, **lp;
323 l= strlen(list[0]);
324 for (lp= &list[1]; *lp; lp++)
326 while(l>0)
328 if (strncmp(list[0], *lp, l) != 0)
329 l--;
330 else
331 break;
334 if (l > baselen || list[1] == NULL)
336 p= stalloc(l-baselen+2);
337 strncpy(p, list[0]+baselen, l-baselen);
338 if (list[1] == NULL)
340 p[l-baselen]= isdir ? '/' : ' ';
341 p[l-baselen+1]= '\0';
342 l++;
344 else
345 p[l-baselen]= '\0';
346 if (el_insertstr(el, p) == -1)
347 return -1;
349 return l;
353 * $PchId: complete.c,v 1.2 2006/04/10 14:35:53 philip Exp $