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