Revert a few additions of __unused that need some more thought.
[dragonfly.git] / gnu / lib / libdialog / dir.c
1 /****************************************************************************
2  *
3  *      Program:        dir.c
4  *      Author:         Marc van Kempen
5  *      desc:           Directory routines, sorting and reading
6  *
7  * Copyright (c) 1995, Marc van Kempen
8  *
9  * All rights reserved.
10  *
11  * This software may be used, modified, copied, distributed, and
12  * sold, in both source and binary form provided that the above
13  * copyright and these terms are retained, verbatim, as the first
14  * lines of this file.  Under no circumstances is the author
15  * responsible for the proper functioning of this software, nor does
16  * the author assume any responsibility for damages incurred with
17  * its use.
18  *
19  ****************************************************************************/
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23
24 #include <unistd.h>             /* XXX for _POSIX_VERSION ifdefs */
25
26 #if !defined sgi && !defined _POSIX_VERSION
27 #include <sys/dir.h>
28 #endif
29 #if defined __sun__
30 #include <sys/dirent.h>
31 #endif
32 #if defined sgi || defined _POSIX_VERSION
33 #include <dirent.h>
34 #endif
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <fnmatch.h>
40 #include <sys/param.h>
41 #include "dir.h"
42
43 /****************************************************************************
44  *
45  *      local prototypes
46  *
47  ****************************************************************************/
48
49 void toggle_dotfiles(void);
50 int  show_dotfiles(void);
51 int  dir_alphasort(const void *d1, const void *d2);
52 int  dir_sizesort(const void *d1, const void *d2);
53 int  dir_datesort(const void *d1, const void *d2);
54 int  dir_extsort(const void *d1, const void *d2);
55
56 /****************************************************************************
57  *
58  *      global variables
59  *
60  ****************************************************************************/
61
62
63 /* This is user-selectable, I've set them fixed for now however */
64
65 void            *_sort_func = dir_alphasort;
66 static int      _showdotfiles = TRUE;
67
68 /****************************************************************************
69  *
70  *      Functions
71  *
72  ****************************************************************************/
73
74 int
75 dir_select_nd(const struct dirent *d)
76 /*
77  *      desc:   allways include a directory entry <d>, except
78  *              for the current directory and other dot-files
79  *              keep '..' however.
80  *      pre:    <d> points to a dirent
81  *      post:   returns TRUE if d->d_name != "." else FALSE
82  */
83 {
84     if (strcmp(d->d_name, ".")==0 ||
85           (d->d_name[0] == '.' && strlen(d->d_name) > 1 && d->d_name[1] != '.')) {
86         return(FALSE);
87     } else {
88         return(TRUE);
89     }
90 }/* dir_select_nd() */
91
92
93 int
94 dir_select(const struct dirent *d)
95 /*
96  *      desc:   allways include a directory entry <d>, except
97  *              for the current directory
98  *      pre:    <d> points to a dirent
99  *      post:   returns TRUE if d->d_name != "." else FALSE
100  */
101 {
102         if (strcmp(d->d_name, ".")==0) {        /* don't include the current directory */
103                 return(FALSE);
104         } else {
105             return(TRUE);
106         }
107 } /* dir_select() */
108
109 int
110 dir_select_root_nd(const struct dirent *d)
111 /*
112  *      desc:   allways include a directory entry <d>, except
113  *              for the current directory and the parent directory.
114  *              Also skip any other dot-files.
115  *      pre:    <d> points to a dirent
116  *      post:   returns TRUE if d->d_name[0] != "." else FALSE
117  */
118 {
119         if (d->d_name[0] == '.') {      /* don't include the current directory */
120                 return(FALSE);          /* nor the parent directory */
121         } else {
122             return(TRUE);
123         }
124 } /* dir_select_root_nd() */
125
126
127 int
128 dir_select_root(const struct dirent *d)
129 /*
130  *      desc:   allways include a directory entry <d>, except
131  *              for the current directory and the parent directory
132  *      pre:    <d> points to a dirent
133  *      post:   returns TRUE if d->d_name[0] != "." else FALSE
134  */
135 {
136         if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
137                 return(FALSE);
138         } else {
139             return(TRUE);
140         }
141 }/* dir_select_root() */
142
143
144 #ifdef NO_ALPHA_SORT
145 int
146 alphasort(const void *d1, const void *d2)
147 /*
148  *      desc:   a replacement for what should be in the library
149  */
150 {
151     return(strcmp(((struct dirent *) d1)->d_name,
152                   ((struct dirent *) d2)->d_name));
153 } /* alphasort() */
154 #endif
155
156 int
157 dir_alphasort(const void *d1, const void *d2)
158 /*
159  *      desc:   compare d1 and d2, but put directories always first
160  *              put '..' always on top
161  *
162  */
163 {
164     DirList     *f1 = ((DirList *) d1),
165                 *f2 = ((DirList *) d2);
166     struct stat *s1 = &(f1->filestatus);
167     struct stat *s2 = &(f2->filestatus);
168
169     /* check for '..' */
170     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
171         return(-1);
172     }
173     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
174         return(1);
175     }
176
177     /* put directories first */
178     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
179         return(strcmp(f1->filename, f2->filename));
180     };
181     if (s1->st_mode & S_IFDIR) {
182         return(-1);
183     }
184     if (s2->st_mode & S_IFDIR) {
185         return(1);
186     }
187     return(strcmp(f1->filename, f2->filename));
188
189 } /* dir_alphasort() */
190
191
192 int
193 dir_sizesort(const void *d1, const void *d2)
194 /*
195  *      desc:   compare d1 and d2, but put directories always first
196  *
197  */
198 {
199     DirList     *f1 = ((DirList *) d1),
200                 *f2 = ((DirList *) d2);
201     struct stat *s1 = &(f1->filestatus);
202     struct stat *s2 = &(f2->filestatus);
203
204     /* check for '..' */
205     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
206         return(-1);
207     }
208     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
209         return(1);
210     }
211
212     /* put directories first */
213     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
214         return(s1->st_size < s2->st_size ?
215                -1
216                :
217                s1->st_size >= s2->st_size);
218     };
219     if (s1->st_mode & S_IFDIR) {
220         return(-1);
221     }
222     if (s2->st_mode & S_IFDIR) {
223         return(1);
224     }
225     return(s1->st_size < s2->st_size ?
226            -1
227            :
228            s1->st_size >= s2->st_size);
229
230 } /* dir_sizesort() */
231
232 int
233 dir_datesort(const void *d1, const void *d2)
234 /*
235  *      desc:   compare d1 and d2 on date, but put directories always first
236  */
237 {
238     DirList     *f1 = ((DirList *) d1),
239                 *f2 = ((DirList *) d2);
240     struct stat *s1 = &(f1->filestatus);
241     struct stat *s2 = &(f2->filestatus);
242
243
244     /* check for '..' */
245     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
246         return(-1);
247     }
248     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
249         return(1);
250     }
251
252     /* put directories first */
253     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
254         return(s1->st_mtime < s2->st_mtime ?
255                -1
256                :
257                s1->st_mtime >= s2->st_mtime);
258     };
259     if (s1->st_mode & S_IFDIR) {
260         return(-1);
261     }
262     if (s2->st_mode & S_IFDIR) {
263         return(1);
264     }
265     return(s1->st_mtime < s2->st_mtime ?
266            -1
267            :
268            s1->st_mtime >= s2->st_mtime);
269
270 } /* dir_datesort() */
271
272
273 int
274 null_strcmp(char *s1, char *s2)
275 /*
276  *      desc:   compare strings allowing NULL pointers
277  */
278 {
279         if ((s1 == NULL) && (s2 == NULL)) {
280                 return(0);
281         }
282         if (s1 == NULL) {
283                 return(-1);
284         }
285         if (s2 == NULL) {
286                 return(1);
287         }
288         return(strcmp(s1, s2));
289 } /* null_strcmp() */
290
291
292 int
293 dir_extsort(const void *d1, const void *d2)
294 /*
295  *      desc:   compare d1 and d2 on extension, but put directories always first
296  *              extension = "the characters after the last dot in the filename"
297  *      pre:    d1 and d2 are pointers to  DirList type records
298  *      post:   see code
299  */
300 {
301     DirList     *f1 = ((DirList *) d1),
302                 *f2 = ((DirList *) d2);
303     struct stat *s1 = &(f1->filestatus);
304     struct stat *s2 = &(f2->filestatus);
305     char        *ext1, *ext2;
306     int         extf, ret;
307
308
309     /* check for '..' */
310     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
311         return(-1);
312     }
313     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
314         return(1);
315     }
316
317
318     /* find the first extension */
319
320     ext1 = f1->filename + strlen(f1->filename);
321     extf = FALSE;
322     while (!extf && (ext1 > f1->filename)) {
323         extf = (*--ext1 == '.');
324     }
325     if (!extf) {
326         ext1 = NULL;
327     } else {
328         ext1++;
329     }
330     /* ext1 == NULL if there's no "extension" else ext1 points */
331     /* to the first character of the extension string */
332
333     /* find the second extension */
334
335     ext2 = f2->filename + strlen(f2->filename);
336     extf = FALSE;
337     while (!extf && (ext2 > f2->filename)) {
338         extf = (*--ext2 == '.');
339     }
340     if (!extf) {
341         ext2 = NULL;
342     } else {
343         ext2++;
344     }
345     /* idem as for ext1 */
346
347     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
348         ret = null_strcmp(ext1, ext2);
349         if (ret == 0) {
350             return(strcmp(f1->filename, f2->filename));
351         } else {
352             return(ret);
353         }
354     };
355     if (s1->st_mode & S_IFDIR) {
356         return(-1);
357     }
358     if (s2->st_mode & S_IFDIR) {
359         return(1);
360     }
361     ret = null_strcmp(ext1, ext2);
362     if (ret == 0) {
363         return(strcmp(f1->filename, f2->filename));
364     } else {
365         return(ret);
366     }
367
368 } /* dir_extsort() */
369
370
371 void
372 get_dir(char *dirname, char *fmask, DirList **dir, int *n)
373 /*
374  *      desc:   get the files in the current directory
375  *      pre:    <dir> == NULL
376  *      post:   <dir> contains <n> dir-entries
377  */
378 {
379         char            cwd[MAXPATHLEN];
380         char            buf[256];
381         struct dirent   **dire;
382         struct stat     status;
383         int             i, j, nb;
384         long            d;
385
386
387         getcwd(cwd, MAXPATHLEN);
388         if (strcmp(cwd, "/") == 0) {    /* we are in the root directory */
389             if (show_dotfiles()) {
390                 *n = scandir(dirname, &dire, dir_select_root, alphasort);
391             } else {
392                 *n = scandir(dirname, &dire, dir_select_root_nd, alphasort);
393             }
394         } else {
395             if (show_dotfiles()) {
396                 *n = scandir(dirname, &dire, dir_select, alphasort);
397             } else {
398                 *n = scandir(dirname, &dire, dir_select_nd, alphasort);
399             }
400         }
401
402         /* There is the possibility that we have entered a directory    */
403         /* which we are not allowed to read, scandir thus returning     */
404         /* -1 for *n.                                                   */
405         /* Actually I should also check for lack of memory, but I'll    */
406         /* let my application happily crash if this is the case         */
407         /* Solution:                                                    */
408         /*      manually insert the parent directory as the only        */
409         /*      directory entry, and return.                            */
410
411         if (*n == -1) {
412             *n = 1;
413             *dir = (DirList *) malloc(sizeof(DirList));
414             strcpy((*dir)[0].filename, "..");
415             lstat("..", &status);
416             (*dir)[0].filestatus = status;
417             (*dir)[0].link = FALSE;
418             return;
419         }
420
421         *dir = (DirList *) malloc( *n * sizeof(DirList) );
422         d = 0;
423         i = 0;
424         j = 0;
425         while (j<*n) {
426             lstat(dire[j]->d_name, &status);
427             /* check if this file is to be included */
428             /* always include directories, the rest is subject to fmask */
429             if (S_ISDIR(status.st_mode)
430                 || fnmatch(fmask, dire[j]->d_name, FNM_NOESCAPE) != FNM_NOMATCH) {
431                 strcpy((*dir)[i].filename, dire[j]->d_name);
432                 (*dir)[i].filestatus = status;
433                 if ((S_IFMT & status.st_mode) == S_IFLNK) {  /* handle links */
434                     (*dir)[i].link = TRUE;
435                     stat(dire[j]->d_name, &status);
436                     nb = readlink(dire[j]->d_name, buf, sizeof(buf) - 1);
437                     if (nb == -1) {
438                         printf("get_dir(): Error reading link: %s\n", dire[j]->d_name);
439                         exit(-1);
440                     } else {
441                         (*dir)[i].linkname = malloc(sizeof(char) * nb + 1);
442                         strncpy((*dir)[i].linkname, buf, nb);
443                         (*dir)[i].linkname[nb] = 0;
444                     }
445                     (*dir)[i].filestatus = status;
446                 } else {
447                     (*dir)[i].link = FALSE;
448                     (*dir)[i].linkname = NULL;
449                 }
450                 i++;
451             } else {
452                 /* skip this entry */
453             }
454             j++;
455         }
456         *n = i;
457
458         /* sort the directory with the directory names on top */
459         qsort((*dir), *n, sizeof(DirList), _sort_func);
460
461         /* Free the allocated memory */
462         for (i=0; i<*n; i++) {
463             free(dire[i]);
464         }
465         free(dire);
466
467         return;
468 }/* get_dir() */
469
470
471 void
472 FreeDir(DirList *d, int n)
473 /*
474  *      desc:   free the dirlist d
475  *      pre:    d != NULL
476  *      post:   memory allocated to d has been released
477  */
478 {
479     int i;
480
481     if (d) {
482         for (i=0; i<n; i++) {
483             if (d[i].linkname) {
484                 free(d[i].linkname);
485             }
486         }
487         free(d);
488     } else {
489         printf("dir.c:FreeDir(): d == NULL\n");
490         exit(-1);
491     }
492
493     return;
494 } /* FreeDir() */
495
496 void
497 toggle_dotfiles(void)
498 /*
499  *      desc: toggle visibility of dot-files
500  */
501 {
502     _showdotfiles = !_showdotfiles;
503
504     return;
505 } /* toggle_dotfiles() */
506
507 int
508 show_dotfiles(void)
509 /*
510  *      desc: return the value of _showdotfiles
511  */
512 {
513     return(_showdotfiles);
514 } /* show_dotfiles() */
515
516 void
517 set_dotfiles(int b)
518 /*
519  *      desc: set the value of _showdotfiles
520  */
521 {
522     _showdotfiles = b;
523
524     return;
525 } /* set_dotfiles() */