355 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
| complete.c
 | |
| 
 | |
| Created:	July 1995 by Philip Homburg <philip@cs.vu.nl>
 | |
| */
 | |
| 
 | |
| #include <ctype.h>
 | |
| #include <dirent.h>
 | |
| #include <errno.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/stat.h>
 | |
| #include "myhistedit.h"
 | |
| #include "shell.h"
 | |
| 
 | |
| #include "complete.h"
 | |
| #include "error.h"
 | |
| #include "expand.h"
 | |
| #include "nodes.h"
 | |
| #include "memalloc.h"
 | |
| 
 | |
| static char **getlist(EditLine *el, int *baselen, int *isdir);
 | |
| static char **getlist_tilde(char *prefix);
 | |
| static int vstrcmp(const void *v1, const void *v2);
 | |
| static void print_list(char **list);
 | |
| static int install_extra(EditLine *el, char **list, int baselen, int isdir);
 | |
| 
 | |
| unsigned char complete(EditLine *el, int ch)
 | |
| {
 | |
| 	struct stackmark mark;
 | |
| 	const LineInfo *lf;
 | |
| 	char **list;
 | |
| 	int baselen, prefix, isdir;
 | |
| 
 | |
| 	/* Direct the cursor the the end of the word. */
 | |
| 	for(;;)
 | |
| 	{
 | |
| 		lf = el_line(el);
 | |
| 		if (lf->cursor < lf->lastchar &&
 | |
| 			!isspace((unsigned char)*lf->cursor))
 | |
| 		{
 | |
| 			(*(char **)&lf->cursor)++;	/* XXX */
 | |
| 		}
 | |
| 		else
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	setstackmark(&mark);
 | |
| 	list= getlist(el, &baselen, &isdir);
 | |
| 	if (list)
 | |
| 	{
 | |
| 		prefix= install_extra(el, list, baselen, isdir);
 | |
| 		el_push(el, "i");
 | |
| 	}
 | |
| 	popstackmark(&mark);
 | |
| 	if (list)
 | |
| 		return CC_REFRESH;
 | |
| 	else
 | |
| 		return CC_ERROR;
 | |
| }
 | |
| 
 | |
| unsigned char complete_list(EditLine *el, int ch)
 | |
| {
 | |
| 	struct stackmark mark;
 | |
| 	char **list;
 | |
| 
 | |
| 	setstackmark(&mark);
 | |
| 	list= getlist(el, NULL, NULL);
 | |
| 	if (list)
 | |
| 	{
 | |
| 		print_list(list);
 | |
| 		re_goto_bottom(el);
 | |
| 	}
 | |
| 	popstackmark(&mark);
 | |
| 	if (list)
 | |
| 	{
 | |
| 		return CC_REFRESH;
 | |
| 	}
 | |
| 	else
 | |
| 		return CC_ERROR;
 | |
| }
 | |
| 
 | |
| unsigned char complete_or_list(EditLine *el, int ch)
 | |
| {
 | |
| 	struct stackmark mark;
 | |
| 	const LineInfo *lf;
 | |
| 	char **list;
 | |
| 	int baselen, prefix, isdir;
 | |
| 
 | |
| 	setstackmark(&mark);
 | |
| 	list= getlist(el, &baselen, &isdir);
 | |
| 	if (list)
 | |
| 	{
 | |
| 		prefix= install_extra(el, list, baselen, isdir);
 | |
| 		if (prefix == baselen)
 | |
| 		{
 | |
| 			print_list(list);
 | |
| 			re_goto_bottom(el);
 | |
| 		}
 | |
| 	}
 | |
| 	popstackmark(&mark);
 | |
| 	if (list)
 | |
| 		return CC_REFRESH;
 | |
| 	else
 | |
| 		return CC_ERROR;
 | |
| }
 | |
| 
 | |
| unsigned char complete_expand(EditLine *el, int ch)
 | |
| {
 | |
| 	printf("complete_expand\n");
 | |
| 	return CC_ERROR;
 | |
| }
 | |
| 
 | |
| static char **getlist(EditLine *el, int *baselen, int *isdir)
 | |
| {
 | |
| 	const LineInfo *lf;
 | |
| 	const char *begin, *end;
 | |
| 	char *dirnam, *basenam;
 | |
| 	union node arg;
 | |
| 	struct arglist arglist;
 | |
| 	DIR *dir;
 | |
| 	struct dirent *dirent;
 | |
| 	int i, l, n;
 | |
| 	char *p, **list;
 | |
| 	struct strlist *slp, *nslp;
 | |
| 	struct stat sb;
 | |
| 
 | |
| 	lf = el_line(el);
 | |
| 
 | |
| 	/* Try to find to begin and end of the word that we have to comple. */
 | |
| 	begin= lf->cursor;
 | |
| 	while (begin > lf->buffer && !isspace((unsigned char)begin[-1]))
 | |
| 		begin--;
 | |
| 	end= lf->cursor;
 | |
| 	while (end < lf->lastchar && !isspace((unsigned char)end[0]))
 | |
| 		end++;
 | |
| 
 | |
| 	*(const char **)&lf->cursor= end; /* XXX */
 | |
| 
 | |
| 	/* Copy the word to a string */
 | |
| 	dirnam= stalloc(end-begin+1);
 | |
| 	strncpy(dirnam, begin, end-begin);
 | |
| 	dirnam[end-begin]= '\0';
 | |
| 
 | |
| 	/* Cut the word in two pieces: a path and a (partial) component. */
 | |
| 	basenam= strrchr(dirnam, '/');
 | |
| 	if (basenam)
 | |
| 	{
 | |
| 		basenam++;
 | |
| 		p= stalloc(strlen(basenam) + 1);
 | |
| 		strcpy(p, basenam);
 | |
| 		*basenam= '\0';
 | |
| 		basenam= p;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (dirnam[0] == '~')
 | |
| 			return getlist_tilde(dirnam);
 | |
| 		basenam= dirnam;
 | |
| 		dirnam= "./";
 | |
| 	}
 | |
| 	if (baselen)
 | |
| 		*baselen= strlen(basenam);
 | |
| 
 | |
| 	arg.type= NARG;
 | |
| 	arg.narg.next= NULL;
 | |
| 	arg.narg.text= dirnam;
 | |
| 	arg.narg.backquote= NULL;
 | |
| 	arglist.list= NULL;
 | |
| 	arglist.lastp= &arglist.list;
 | |
| 	expandarg(&arg, &arglist, EXP_TILDE);
 | |
| 
 | |
| 	INTOFF;
 | |
| 	list= NULL;
 | |
| 	dir= opendir(arglist.list->text);
 | |
| 	if (dir)
 | |
| 	{
 | |
| 		slp= NULL;
 | |
| 		n= 0;
 | |
| 		l= strlen(basenam);
 | |
| 		while(dirent= readdir(dir))
 | |
| 		{
 | |
| 			if (strncmp(dirent->d_name, basenam, l) != 0)
 | |
| 				continue;
 | |
| 			if (l == 0 && dirent->d_name[0] == '.')
 | |
| 				continue;
 | |
| 			nslp= stalloc(sizeof(*nslp));
 | |
| 			nslp->next= slp;
 | |
| 			slp= nslp;
 | |
| 			slp->text= stalloc(strlen(dirent->d_name)+1);
 | |
| 			strcpy(slp->text, dirent->d_name);
 | |
| 			n++;
 | |
| 			if (n == 1 && isdir != NULL)
 | |
| 			{
 | |
| 				/* Try to findout whether this entry is a
 | |
| 				 * file or a directory.
 | |
| 				 */
 | |
| 				p= stalloc(strlen(arglist.list->text) +
 | |
| 					strlen(dirent->d_name) + 1);
 | |
| 				strcpy(p, arglist.list->text);
 | |
| 				strcat(p, dirent->d_name);
 | |
| 				if (stat(p, &sb) == -1)
 | |
| 					printf("stat '%s' failed: %s\n",
 | |
| 						p, strerror(errno));
 | |
| 				if (stat(p, &sb) == 0 && S_ISDIR(sb.st_mode))
 | |
| 					*isdir= 1;
 | |
| 				else
 | |
| 					*isdir= 0;
 | |
| 			}
 | |
| 		}
 | |
| 		closedir(dir);
 | |
| 		if (n != 0)
 | |
| 		{
 | |
| 			list= stalloc((n+1)*sizeof(*list));
 | |
| 			for(i= 0; slp; i++, slp= slp->next)
 | |
| 				list[i]= slp->text;
 | |
| 			if (i != n)
 | |
| 				error("complete'make_list: i != n");
 | |
| 			list[i]= NULL;
 | |
| 			qsort(list, n, sizeof(*list), vstrcmp);
 | |
| 		}
 | |
| 	}
 | |
| 	INTON;
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| static char **getlist_tilde(char *prefix)
 | |
| {
 | |
| 	printf("should ~-complete '%s'\n", prefix);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int vstrcmp(const void *v1, const void *v2)
 | |
| {
 | |
| 	return strcmp(*(char **)v1, *(char **)v2);
 | |
| }
 | |
| 
 | |
| #define MAXCOLS 40
 | |
| #define SEPWIDTH 4
 | |
| 
 | |
| static void print_list(char **list)
 | |
| {
 | |
| 	struct
 | |
| 	{
 | |
| 		int cols;
 | |
| 		int start[MAXCOLS+1];
 | |
| 		int width[MAXCOLS];
 | |
| 	} best, next;
 | |
| 	int e, i, j, l, n, o, cols, maxw, width;
 | |
| 	int linewidth= 80;
 | |
| 
 | |
| 	/* Count the number of entries. */
 | |
| 	for (n= 0; list[n]; n++)
 | |
| 		;	/* do nothing */
 | |
| 	if (n == 0)
 | |
| 		error("complete'print_list: n= 0");
 | |
| 
 | |
| 	/* Try to maximize the number of columns */
 | |
| 	for (cols= 1; cols<= MAXCOLS; cols++)
 | |
| 	{
 | |
| 		next.cols= cols;
 | |
| 
 | |
| 		o= 0;
 | |
| 		width= 0;
 | |
| 		for(j= 0; j<cols; j++)
 | |
| 		{
 | |
| 			next.start[j]= o;
 | |
| 
 | |
| 			/* Number of entries in this column. */
 | |
| 			e= (n-o)/(cols-j);
 | |
| 			if ((n-o)%(cols-j))
 | |
| 				e++;
 | |
| 
 | |
| 			maxw= 0;
 | |
| 			for (i= 0; i<e; i++)
 | |
| 			{
 | |
| 				l= strlen(list[o+i]);
 | |
| 				if (l < 6)
 | |
| 					l= 6;
 | |
| 				l += SEPWIDTH;
 | |
| 				if (l > maxw)
 | |
| 					maxw= l;
 | |
| 			}
 | |
| 			next.width[j]= maxw;
 | |
| 			width += maxw;
 | |
| 			o += e;
 | |
| 		}
 | |
| 		next.start[j]= o;
 | |
| 		if (cols > 1 && width-SEPWIDTH>linewidth)
 | |
| 			break;
 | |
| 		best= next;
 | |
| 	}
 | |
| 	cols= best.cols;
 | |
| 	e= best.start[1];
 | |
| 	printf("\n");
 | |
| 	for(i= 0; i<e; i++)
 | |
| 	{
 | |
| 		for (j= 0; j<cols; j++)
 | |
| 		{
 | |
| 			if (best.start[j]+i == best.start[j+1])
 | |
| 				continue;
 | |
| 			if (j < cols-1)
 | |
| 			{
 | |
| 				printf("%-*s", best.width[j],
 | |
| 					list[best.start[j]+i]);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				printf("%s", list[best.start[j]+i]);
 | |
| 			}
 | |
| 		}
 | |
| 		if (i < e-1)
 | |
| 			printf("\n");
 | |
| 	}
 | |
| 	fflush(stdout);
 | |
| }
 | |
| 
 | |
| static int install_extra(EditLine *el, char **list, int baselen, int isdir)
 | |
| {
 | |
| 	int l;
 | |
| 	char *p, **lp;
 | |
| 
 | |
| 	l= strlen(list[0]);
 | |
| 	for (lp= &list[1]; *lp; lp++)
 | |
| 	{
 | |
| 		while(l>0)
 | |
| 		{
 | |
| 			if (strncmp(list[0], *lp, l) != 0)
 | |
| 				l--;
 | |
| 			else
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (l > baselen || list[1] == NULL)
 | |
| 	{
 | |
| 		p= stalloc(l-baselen+2);
 | |
| 		strncpy(p, list[0]+baselen, l-baselen);
 | |
| 		if (list[1] == NULL)
 | |
| 		{
 | |
| 			p[l-baselen]= isdir ? '/' : ' ';
 | |
| 			p[l-baselen+1]= '\0';
 | |
| 			l++;
 | |
| 		}
 | |
| 		else
 | |
| 			p[l-baselen]= '\0';
 | |
| 		if (el_insertstr(el, p) == -1)
 | |
| 			return -1;
 | |
| 	}
 | |
| 	return l;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * $PchId: complete.c,v 1.2 2006/04/10 14:35:53 philip Exp $
 | |
|  */
 | 
