1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <pwd.h>
6 #include <dirent.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 
11 #include <X11/Intrinsic.h>
12 #include <X11/StringDefs.h>
13 #ifdef ATHENA
14 # include <X11/Xaw/AsciiText.h>
15 #endif
16 #ifdef MOTIF
17 # include <Xm/Text.h>
18 #endif
19 
20 #include "complete.h"
21 
22 #ifndef MIN
23 #define MIN(x,y)   ((x<y)?(x):(y))
24 #endif
25 #define PERROR(str)      fprintf(stderr,"%s:%d: %s: %s\n",__FILE__,__LINE__,str,strerror(errno))
26 
27 #undef DEBUG
28 
29 extern Display *dpy;
30 
31 /*-------------------------------------------------------------------------*/
32 
33 static int
my_cmp(const void * a,const void * b)34 my_cmp(const void *a, const void *b)
35 {
36     char           *aa, *bb;
37 
38     aa = *(char **) a;
39     bb = *(char **) b;
40     return strcmp(aa, bb);
41 }
42 
43 static int
my_scandir(char * dir,char * match,char *** namelist)44 my_scandir(char *dir, char *match, char ***namelist)
45 {
46     DIR            *d;
47     struct dirent  *e;
48     int             n = 0, len = strlen(match);
49 
50     *namelist = NULL;
51     if (NULL == (d = opendir(dir))) {
52 	return 0;
53     }
54     while (NULL != (e = readdir(d))) {
55 	if (0 != strncmp(e->d_name, match, len))
56 	    continue;
57 #if 0
58 	fprintf(stderr, "%s %s\n", e->d_name, match);
59 #endif
60 	if (0 == (n % 16)) {
61 	    if (0 == n)
62 		*namelist = malloc(16 * sizeof(**namelist));
63 	    else
64 		*namelist = realloc(*namelist, (n + 16) * sizeof(**namelist));
65 	}
66 	(*namelist)[n] = malloc(strlen(e->d_name) + 1);
67 	strcpy((*namelist)[n], e->d_name);
68 	n++;
69     }
70     closedir(d);
71     qsort(*namelist, n, sizeof(**namelist), my_cmp);
72     return n;
73 }
74 
75 void
CompleteAction(Widget widget,XEvent * event,String * params,Cardinal * num_params)76 CompleteAction(Widget widget,
77 	       XEvent * event,
78 	       String * params,
79 	       Cardinal * num_params)
80 {
81     static char     thisdir[] = ".", rootdir[] = "/", *file;
82     char           *fn, *fn2, *expand, *dir;
83     char            filename[513];
84     char          **list;
85     struct stat     st;
86     int             i, n, len;
87 #ifdef ATHENA
88     XawTextPosition pos;
89 #endif
90 #ifdef MOTIF
91     XmTextPosition  pos;
92 #endif
93     struct passwd  *pw;
94     char           *user, pwmatch[32];	/* anybody with more than 32 char uid ? */
95 
96 #ifdef ATHENA
97     XtVaGetValues(widget,
98 		  XtNstring, &fn,
99 		  NULL);
100     pos = XawTextGetInsertionPoint(widget);
101 #endif
102 #ifdef MOTIF
103     fn  = XmTextGetString(widget);
104     pos = XmTextGetInsertionPosition(widget);
105 #endif
106     fn2 = strdup(fn+pos);
107     fn[pos] = 0;
108     expand = tilde_expand(fn);
109     if (!expand)
110 	/* failsave */
111 	return;
112 
113     list = NULL;
114     memset(filename, 0, 513);
115 
116     if (expand[0] == '~') {
117 	/* try user name complete */
118 	if (strchr(expand, '/'))
119 	    /* ...but not if there is a slash */
120 	    return;
121 	user = &expand[1];
122 	len = strlen(user);
123 	n = 0;
124 	while ((pw = getpwent()) != NULL) {
125 	    if (!strncmp(user, pw->pw_name, len)) {
126 #ifdef DEBUG
127 		puts(pw->pw_name);
128 #endif
129 		if (0 == n) {
130 		    strcpy(pwmatch, pw->pw_name);
131 		} else {
132 		    for (i = len; !strncmp(pw->pw_name, pwmatch, i); i++);
133 		    pwmatch[i - 1] = 0;
134 #ifdef DEBUG
135 		    printf("%i: %s\n", i, pwmatch);
136 #endif
137 		}
138 		n++;
139 	    }
140 	}
141 	endpwent();
142 	if (0 == n) {
143 	    /* no match */
144 	    XBell(dpy, 100);
145 	    strcpy(filename, expand);
146 	} else if (1 == n) {
147 	    sprintf(filename, "~%s/", pwmatch);
148 	} else {
149 	    sprintf(filename, "~%s", pwmatch);
150 	}
151 
152     } else {
153 	/* try file name complete */
154 	file = strrchr(expand, '/');
155 	if (file) {
156 	    if (file == expand) {
157 		dir = rootdir;
158 	    } else {
159 		dir = expand;
160 	    }
161 	    *file = '\0';
162 	    file++;
163 	} else {
164 	    file = expand;
165 	    dir = thisdir;
166 	}
167 #ifdef DEBUG
168 	printf("%s %s\n", dir, file);
169 #endif
170 
171 	n = my_scandir(dir, file, &list);
172 #if 0
173 	for (i = 0; i < n; i++) {
174 	    printf("--> %s\n", list[i]);
175 	}
176 #endif
177 	if (-1 == n) {
178 	    /* Oops, maybe permission denied ??? */
179 	    PERROR("scandir");
180 	    strcpy(filename, fn);
181 	} else if (0 == n) {
182 	    /* no match */
183 	    XBell(dpy, 100);
184 	    strcpy(filename, fn);
185 	} else if (1 == n) {
186 	    /* one match */
187 	    sprintf(filename, "%s/%s", dir, list[0]);
188 	    stat(filename, &st);
189 	    if (strchr(fn, '/')) {
190 		strcpy(filename, fn);
191 		*(strrchr(filename, '/') + 1) = '\0';
192 		strcat(filename, list[0]);
193 	    } else {
194 		strcpy(filename, list[0]);
195 	    }
196 	    if (S_ISDIR(st.st_mode))
197 		strcat(filename, "/");
198 	    else
199 		strcat(filename, " ");
200 	} else {
201 	    /* more than one match */
202 	    len = MIN(strlen(list[0]), strlen(list[n - 1]));
203 	    for (i = 0; !strncmp(list[0], list[n - 1], i + 1) && i <= len; i++);
204 	    if (strchr(fn, '/')) {
205 		strcpy(filename, fn);
206 		*(strrchr(filename, '/') + 1) = '\0';
207 		strncat(filename, list[0], i);
208 	    } else {
209 		strncpy(filename, list[0], i);
210 	    }
211 	}
212     }
213 
214 #ifdef DEBUG
215     printf("result: `%s'\n", filename);
216 #endif
217     pos = strlen(filename);
218     strcat(filename,fn2);
219 #ifdef ATHENA
220     XtVaSetValues(widget,
221 		  XtNstring, filename,
222 		  NULL);
223     XawTextSetInsertionPoint(widget,pos);
224 #endif
225 #ifdef MOTIF
226     XmTextSetString(widget,filename);
227     XmTextSetInsertionPosition(widget,pos);
228 #endif
229 
230     if (list) {
231 	for (i = 0; i < n; i++)
232 	    free(list[i]);
233 	free(list);
234     }
235     free(expand);
236     return;
237 }
238 
239 /*-------------------------------------------------------------------------*/
240 
241 char*
tilde_expand(char * file)242 tilde_expand(char *file)
243 {
244     char           *ret, *user;
245     struct passwd  *pw;
246     int             len;
247 
248     if (!file)
249 	return NULL;
250 
251 #ifdef DEBUG
252     printf("tilde_expand: in : `%s'\n", file);
253 #endif
254     if (!(file[0] == '~' && strchr(file, '/'))) {
255 	ret = strdup(file);
256     } else {
257 	if (file[1] == '/') {
258 	    pw = getpwuid(getuid());
259 	} else {
260 	    user = strdup(&file[1]);
261 	    *(strchr(user, '/')) = '\0';
262 	    pw = getpwnam(user);
263 	    free(user);
264 	}
265 	if (pw == NULL) {
266 	    ret = strdup(file);
267 	} else {
268 #ifdef DEBUG
269 	    printf("tilde_expand: pw : %s=%s\n", pw->pw_name, pw->pw_dir);
270 #endif
271 	    ret = malloc(strlen(file) + strlen(pw->pw_dir));
272 	    sprintf(ret, "%s%s", pw->pw_dir, strchr(file, '/'));
273 	}
274     }
275     /* trim */
276     len = strlen(ret);
277     while (ret[len - 1] == ' ')
278 	ret[len - 1] = '\0', len--;
279 #ifdef DEBUG
280     printf("tilde_expand: out: `%s'\n", ret);
281 #endif
282     return ret;
283 }
284