1 /* This file contains all routines for creating and managing a file
2 * requestor. The programmer's only interface to the file requestor
3 * is the function GetFile(). See the description for that function
4 * for usage information (it's pretty trivial).
5 *
6 * Originally written by Allen Martin (amartin@cs.wpi.edu).
7 *
8 * Significant modifications by me (Dominic Giampaolo, dbg@sgi.com)
9 * to clean up some bugs, do double-clicks correctly, and make
10 * relative path browsing work better.
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/param.h>
17 #include <limits.h>
18 #include <sys/times.h>
19
20 #include "libsx.h"
21
22 #ifndef CLK_TCK
23 #include <unistd.h>
24 #define CLK_TCK sysconf(_SC_CLK_TCK) /* seems to work right */
25 #endif /* CLK_TCK */
26
27
28 /*
29 * Some ugly hacks to make this work better under ultrix.
30 */
31 #ifdef ultrix
32
33 /*
34 * Can you say "Let's be pinheaded and follow the letter of the spec without
35 * regard for what might make sense"? I knew you could. And so can the
36 * boys and girls at DEC.
37 *
38 * Which is why they don't provide strdup() in their libc. Gay or what?
39 */
strdup(const char * str)40 char *strdup(const char *str)
41 {
42 char *new;
43
44 new = malloc(strlen(str)+1);
45 if (new)
46 strcpy(new, str);
47
48 return new;
49 }
50
51 #endif /* ultrix */
52
53
54 /*
55 * No one seems to have a prototype for strdup(). What a pain
56 * in the butt. Why on earth isn't strdup() in the POSIX standard
57 * but something completely useless like mbstowcs() is?
58 */
59 char *strdup(const char *str);
60
61
62 /*
63 * Here's where the real code begins.
64 */
65
66 typedef struct {
67 Widget freq_window;
68 Widget file_path;
69 Widget file_name;
70 Widget file_list;
71
72 char **dirlist; /* used by the list widget */
73 char fpath[MAXPATHLEN];
74 char fname[MAXPATHLEN];
75
76 clock_t last_click; /* time of last click */
77
78 } FReqData;
79
80 static void load_cancel(Widget w, FReqData *fdata);
81 static void load_ok(Widget w, FReqData *fdata);
82 static void load_list(Widget w, char *string, int index, FReqData *fdata);
83 static void load_dir(Widget w, char *string, FReqData *fdata);
84 static void load_name(Widget w, char *string, FReqData *fdata);
85 static int mystrcmp(const void *a, const void *b);
86
87 char **get_dir_list(char *pname, int *num);
88 void free_dirlist(char **table);
89
90
91 /*
92 * GetFile() - This is the entry point to the file requestor. A single
93 * argument is passed - the path name for the initial list.
94 * If this path name is passed as NULL, the current directory
95 * is used instead. The function returns a character string
96 * that is the name of the selected file, path included. If
97 * an error occurs, or the user selects CANCEL, NULL is returned.
98 */
GetFile(char * _path)99 char *GetFile(char *_path)
100 {
101 FReqData fdata;
102 Widget w[8];
103 int num_dir;
104 char path[MAXPATHLEN];
105
106 if(!_path || strcmp(_path, ".") == 0 || strcmp(_path, "./") == 0)
107 getcwd(path, MAXPATHLEN);
108 else
109 strcpy(path, _path);
110
111 if(path[strlen(path)-1] != '/')
112 strcat(path, "/");
113
114 if(!(fdata.dirlist = get_dir_list(path, &num_dir)))
115 return(NULL);
116
117 qsort(fdata.dirlist, num_dir, sizeof(char *), mystrcmp);
118
119 fdata.freq_window = MakeWindow("File Requestor", SAME_DISPLAY,
120 EXCLUSIVE_WINDOW);
121
122 w[0] = MakeLabel("Path:");
123 w[1] = MakeStringEntry(path, 300, (void *)load_dir, &fdata);
124 w[2] = MakeScrollList(fdata.dirlist, 350, 300, (void *)load_list, &fdata);
125 w[3] = MakeLabel("File:");
126 w[4] = MakeStringEntry("", 300, (void *)load_name, &fdata);
127 w[5] = MakeButton("Ok", (void *)load_ok, &fdata);
128 w[6] = MakeLabel("Select a File from the List or Enter a Name");
129 w[7] = MakeButton("Cancel", (void *)load_cancel, &fdata);
130
131 SetWidgetPos(w[1], PLACE_RIGHT, w[0], NO_CARE, NULL);
132 SetWidgetPos(w[2], PLACE_UNDER, w[0], NO_CARE, NULL);
133 SetWidgetPos(w[3], PLACE_UNDER, w[2], NO_CARE, NULL);
134 SetWidgetPos(w[4], PLACE_UNDER, w[2], PLACE_RIGHT, w[3]);
135 SetWidgetPos(w[5], PLACE_UNDER, w[3], NO_CARE, NULL);
136 SetWidgetPos(w[6], PLACE_UNDER, w[3], PLACE_RIGHT, w[5]);
137 SetWidgetPos(w[7], PLACE_UNDER, w[3], PLACE_RIGHT, w[6]);
138
139 /* save the file name & file list widgets, so we can use them later */
140 fdata.file_path = w[1];
141 fdata.file_list = w[2];
142 fdata.file_name = w[4];
143
144 fdata.last_click = 0;
145
146 /* set up the file path */
147 strcpy(fdata.fpath, path);
148
149 ShowDisplay();
150 MainLoop();
151
152 /* free the directory list */
153 if (fdata.dirlist)
154 free_dirlist(fdata.dirlist);
155
156 SetCurrentWindow(ORIGINAL_WINDOW);
157
158 if(fdata.fname[0] == '\0')
159 return(NULL);
160 else
161 return(strdup(fdata.fname));
162 }
163
164 /*
165 * load_cancel() - Callback routine for CANCEL button
166 */
load_cancel(Widget w,FReqData * fdata)167 static void load_cancel(Widget w, FReqData *fdata)
168 {
169 SetCurrentWindow(fdata->freq_window);
170 CloseWindow();
171 strcpy(fdata->fname, "");
172 }
173
174 /*
175 * load_ok() - Callback routine for OK button
176 */
load_ok(Widget w,FReqData * fdata)177 static void load_ok(Widget w, FReqData *fdata)
178 {
179 char *fpath, *fname;
180 char fullname[MAXPATHLEN];
181
182 fpath = GetStringEntry(fdata->file_path);
183 fname = GetStringEntry(fdata->file_name);
184
185 sprintf(fullname, "%s%s", fpath, fname);
186
187 /* right here we should check the validity of the file name */
188 /* and abort if invalid */
189
190 strcpy(fdata->fname, fullname);
191
192 SetCurrentWindow(fdata->freq_window);
193 CloseWindow();
194 }
195
196 /*
197 * load_list() - Callback routine for scrollable list widget
198 */
load_list(Widget w,char * string,int index,FReqData * fdata)199 static void load_list(Widget w, char *string, int index, FReqData *fdata)
200 {
201 char newpath[MAXPATHLEN], *cptr, *fpath, fullname[MAXPATHLEN];
202 char **old_dirlist=NULL;
203 static char oldfile[MAXPATHLEN] = { '\0', };
204 clock_t cur_click;
205 struct tms junk_tms; /* not used, but passed to times() */
206 int num_dir;
207 float tdiff; /* difference in time between two clicks as % of a second */
208
209 /*
210 * First we check fora double click.
211 *
212 * If the time between the last click and this click is greater than
213 * 0.5 seconds or the last filename and the current file name
214 * are different, then it's not a double click, so we just return.
215 */
216 cur_click = times(&junk_tms);
217 tdiff = ((float)(cur_click - fdata->last_click) / CLK_TCK);
218
219 if(tdiff > 0.50 || strcmp(oldfile, string) != 0)
220 {
221 fdata->last_click = cur_click;
222 strcpy(oldfile, string);
223 SetStringEntry(fdata->file_name, string);
224 return;
225 }
226
227 /* check if a directory was selected */
228 if(string[strlen(string)-1] != '/') /* a regular file double click */
229 {
230 fpath = GetStringEntry(fdata->file_path);
231
232 sprintf(fullname, "%s%s", fpath, string);
233
234 /* right here we should check the validity of the file name */
235 /* and abort if invalid */
236
237 strcpy(fdata->fname, fullname);
238
239 SetCurrentWindow(fdata->freq_window);
240 CloseWindow();
241 return;
242 }
243
244 /*
245 * Else, we've got a directory name and should deal with it
246 * as approrpriate.
247 */
248
249 /* check for special cases "./" and "../" */
250 if(strcmp(string, "./") == 0)
251 {
252 if (fdata->fpath)
253 strcpy(newpath, fdata->fpath);
254 else
255 strcpy(newpath, "./");
256 }
257 else if(strcmp(string, "../") == 0)
258 {
259 strcpy(newpath, fdata->fpath);
260
261 if (strcmp(newpath, "./") == 0)
262 strcpy(newpath, string);
263 else
264 {
265 /*
266 * chop off the last path component and look at what it
267 * is to determine what to do with the `..' we just got.
268 */
269 cptr = strrchr(newpath, '/');
270 if (cptr)
271 *cptr = '\0';
272 cptr = strrchr(newpath, '/');
273 if (cptr)
274 *cptr = '\0';
275
276 if ( (cptr != NULL && strcmp(cptr+1, "..") == 0)
277 ||(cptr == NULL && strcmp(newpath, "..") == 0))
278 {
279 if (cptr)
280 *cptr = '/';
281
282 strcat(newpath, "/"); /* put back the / we took out */
283 strcat(newpath, "../"); /* and append the new ../ */
284 }
285 else
286 {
287 if(cptr == NULL && strcmp(fdata->fpath, "/") == 0)
288 strcpy(newpath, "/");
289 else if (cptr == NULL)
290 strcpy(newpath, "./");
291
292 if (newpath[strlen(newpath)-1] != '/')
293 strcat(newpath, "/");
294 }
295 }
296 }
297 else /* not a `./' or `../', so it's just a regular old directory name */
298 {
299 if (fdata->fpath[strlen(fdata->fpath)-1] == '/')
300 sprintf(newpath, "%s%s", fdata->fpath, string);
301 else
302 sprintf(newpath, "%s/%s", fdata->fpath, string);
303 }
304
305 old_dirlist = fdata->dirlist;
306 if(!(fdata->dirlist = get_dir_list(newpath, &num_dir)))
307 /* should beep the display or something here */
308 return;
309
310 qsort(fdata->dirlist, num_dir, sizeof(char *), mystrcmp);
311
312 strcpy(fdata->fpath, newpath);
313 SetStringEntry(fdata->file_path, fdata->fpath);
314 SetStringEntry(fdata->file_name, "");
315 strcpy(fdata->fpath, newpath);
316 ChangeScrollList(fdata->file_list, fdata->dirlist);
317
318 /* free the directory list */
319 if (old_dirlist)
320 free_dirlist(old_dirlist);
321
322
323 fdata->last_click = 0; /* re-init double-click time */
324
325 return;
326 }
327
328 /*
329 * load_dir() - Callback routine for pathname string entry widget
330 */
load_dir(Widget w,char * string,FReqData * fdata)331 static void load_dir(Widget w, char *string, FReqData *fdata)
332 {
333 char **old_dirlist, temp[MAXPATHLEN];
334 int num_dir;
335
336 /* make sure the name has a '/' at the end */
337 strcpy(temp, string);
338 if(temp[strlen(temp)-1] != '/')
339 strcat(temp, "/");
340
341 old_dirlist = fdata->dirlist;
342
343 if(!(fdata->dirlist = get_dir_list(temp, &num_dir)))
344 {
345 /* bad path - reset the file path and return */
346 SetStringEntry(fdata->file_path, fdata->fpath);
347 return;
348 }
349
350 qsort(fdata->dirlist, num_dir, sizeof(char *), mystrcmp);
351
352 strcpy(fdata->fpath, temp);
353 SetStringEntry(fdata->file_path, temp);
354 ChangeScrollList(fdata->file_list, fdata->dirlist);
355
356 /* free the directory list */
357 if (old_dirlist)
358 free_dirlist(old_dirlist);
359 }
360
361 /*
362 * load_name() - Callback routine for file name string entry widget
363 */
load_name(Widget w,char * string,FReqData * fdata)364 static void load_name(Widget w, char *string, FReqData *fdata)
365 {
366 char *fpath, fullname[MAXPATHLEN];
367
368 fpath = GetStringEntry(fdata->file_path);
369
370 sprintf(fullname, "%s%s", fpath, string);
371
372 /* right here we should check the validity of the file name */
373 /* and abort if invalid */
374
375 strcpy(fdata->fname, fullname);
376
377 SetCurrentWindow(fdata->freq_window);
378 CloseWindow();
379 return;
380 }
381
382
383 /*
384 * This function is just a wrapper for mystrcmp(), and is called by qsort()
385 * (if used) down below.
386 */
mystrcmp(const void * a,const void * b)387 static int mystrcmp(const void *a, const void *b)
388 {
389 return strcmp(*(char **)a, *(char **)b);
390 }
391
392