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