Merge from vendor branch CVS:
[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(
76 #if defined __linux__
77   const struct dirent *d
78 #else
79   struct dirent *d
80 #endif
81 )
82 /*
83  *      desc:   allways include a directory entry <d>, except
84  *              for the current directory and other dot-files
85  *              keep '..' however.
86  *      pre:    <d> points to a dirent
87  *      post:   returns TRUE if d->d_name != "." else FALSE
88  */
89 {
90     if (strcmp(d->d_name, ".")==0 ||
91           (d->d_name[0] == '.' && strlen(d->d_name) > 1 && d->d_name[1] != '.')) {
92         return(FALSE);
93     } else {
94         return(TRUE);
95     }
96 }/* dir_select_nd() */
97
98
99 int
100 dir_select(
101 #ifdef __linux__
102   const struct dirent *d
103 #else
104   struct dirent *d
105 #endif
106 )
107 /*
108  *      desc:   allways include a directory entry <d>, except
109  *              for the current directory
110  *      pre:    <d> points to a dirent
111  *      post:   returns TRUE if d->d_name != "." else FALSE
112  */
113 {
114         if (strcmp(d->d_name, ".")==0) {        /* don't include the current directory */
115                 return(FALSE);
116         } else {
117             return(TRUE);
118         }
119 } /* dir_select() */
120
121 int
122 dir_select_root_nd(
123 #ifdef __linux__
124   const struct dirent *d
125 #else
126   struct dirent *d
127 #endif
128 )
129 /*
130  *      desc:   allways include a directory entry <d>, except
131  *              for the current directory and the parent directory.
132  *              Also skip any other dot-files.
133  *      pre:    <d> points to a dirent
134  *      post:   returns TRUE if d->d_name[0] != "." else FALSE
135  */
136 {
137         if (d->d_name[0] == '.') {      /* don't include the current directory */
138                 return(FALSE);          /* nor the parent directory */
139         } else {
140             return(TRUE);
141         }
142 } /* dir_select_root_nd() */
143
144
145 int
146 dir_select_root(
147 #ifdef __linux__
148   const struct dirent *d
149 #else
150   struct dirent *d
151 #endif
152 )
153 /*
154  *      desc:   allways include a directory entry <d>, except
155  *              for the current directory and the parent directory
156  *      pre:    <d> points to a dirent
157  *      post:   returns TRUE if d->d_name[0] != "." else FALSE
158  */
159 {
160         if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
161                 return(FALSE);
162         } else {
163             return(TRUE);
164         }
165 }/* dir_select_root() */
166
167
168 #ifdef NO_ALPHA_SORT
169 int
170 alphasort(const void *d1, const void *d2)
171 /*
172  *      desc:   a replacement for what should be in the library
173  */
174 {
175     return(strcmp(((struct dirent *) d1)->d_name,
176                   ((struct dirent *) d2)->d_name));
177 } /* alphasort() */
178 #endif
179
180 int
181 dir_alphasort(const void *d1, const void *d2)
182 /*
183  *      desc:   compare d1 and d2, but put directories always first
184  *              put '..' always on top
185  *
186  */
187 {
188     DirList     *f1 = ((DirList *) d1),
189                 *f2 = ((DirList *) d2);
190     struct stat *s1 = &(f1->filestatus);
191     struct stat *s2 = &(f2->filestatus);
192
193     /* check for '..' */
194     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
195         return(-1);
196     }
197     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
198         return(1);
199     }
200
201     /* put directories first */
202     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
203         return(strcmp(f1->filename, f2->filename));
204     };
205     if (s1->st_mode & S_IFDIR) {
206         return(-1);
207     }
208     if (s2->st_mode & S_IFDIR) {
209         return(1);
210     }
211     return(strcmp(f1->filename, f2->filename));
212
213 } /* dir_alphasort() */
214
215
216 int
217 dir_sizesort(const void *d1, const void *d2)
218 /*
219  *      desc:   compare d1 and d2, but put directories always first
220  *
221  */
222 {
223     DirList     *f1 = ((DirList *) d1),
224                 *f2 = ((DirList *) d2);
225     struct stat *s1 = &(f1->filestatus);
226     struct stat *s2 = &(f2->filestatus);
227
228     /* check for '..' */
229     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
230         return(-1);
231     }
232     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
233         return(1);
234     }
235
236     /* put directories first */
237     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
238         return(s1->st_size < s2->st_size ?
239                -1
240                :
241                s1->st_size >= s2->st_size);
242     };
243     if (s1->st_mode & S_IFDIR) {
244         return(-1);
245     }
246     if (s2->st_mode & S_IFDIR) {
247         return(1);
248     }
249     return(s1->st_size < s2->st_size ?
250            -1
251            :
252            s1->st_size >= s2->st_size);
253
254 } /* dir_sizesort() */
255
256 int
257 dir_datesort(const void *d1, const void *d2)
258 /*
259  *      desc:   compare d1 and d2 on date, but put directories always first
260  */
261 {
262     DirList     *f1 = ((DirList *) d1),
263                 *f2 = ((DirList *) d2);
264     struct stat *s1 = &(f1->filestatus);
265     struct stat *s2 = &(f2->filestatus);
266
267
268     /* check for '..' */
269     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
270         return(-1);
271     }
272     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
273         return(1);
274     }
275
276     /* put directories first */
277     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
278         return(s1->st_mtime < s2->st_mtime ?
279                -1
280                :
281                s1->st_mtime >= s2->st_mtime);
282     };
283     if (s1->st_mode & S_IFDIR) {
284         return(-1);
285     }
286     if (s2->st_mode & S_IFDIR) {
287         return(1);
288     }
289     return(s1->st_mtime < s2->st_mtime ?
290            -1
291            :
292            s1->st_mtime >= s2->st_mtime);
293
294 } /* dir_datesort() */
295
296
297 int
298 null_strcmp(char *s1, char *s2)
299 /*
300  *      desc:   compare strings allowing NULL pointers
301  */
302 {
303         if ((s1 == NULL) && (s2 == NULL)) {
304                 return(0);
305         }
306         if (s1 == NULL) {
307                 return(-1);
308         }
309         if (s2 == NULL) {
310                 return(1);
311         }
312         return(strcmp(s1, s2));
313 } /* null_strcmp() */
314
315
316 int
317 dir_extsort(const void *d1, const void *d2)
318 /*
319  *      desc:   compare d1 and d2 on extension, but put directories always first
320  *              extension = "the characters after the last dot in the filename"
321  *      pre:    d1 and d2 are pointers to  DirList type records
322  *      post:   see code
323  */
324 {
325     DirList     *f1 = ((DirList *) d1),
326                 *f2 = ((DirList *) d2);
327     struct stat *s1 = &(f1->filestatus);
328     struct stat *s2 = &(f2->filestatus);
329     char        *ext1, *ext2;
330     int         extf, ret;
331
332
333     /* check for '..' */
334     if (strcmp(((DirList *) d1)->filename, "..") == 0) {
335         return(-1);
336     }
337     if (strcmp(((DirList *) d2)->filename, "..") == 0) {
338         return(1);
339     }
340
341
342     /* find the first extension */
343
344     ext1 = f1->filename + strlen(f1->filename);
345     extf = FALSE;
346     while (!extf && (ext1 > f1->filename)) {
347         extf = (*--ext1 == '.');
348     }
349     if (!extf) {
350         ext1 = NULL;
351     } else {
352         ext1++;
353     }
354     /* ext1 == NULL if there's no "extension" else ext1 points */
355     /* to the first character of the extension string */
356
357     /* find the second extension */
358
359     ext2 = f2->filename + strlen(f2->filename);
360     extf = FALSE;
361     while (!extf && (ext2 > f2->filename)) {
362         extf = (*--ext2 == '.');
363     }
364     if (!extf) {
365         ext2 = NULL;
366     } else {
367         ext2++;
368     }
369     /* idem as for ext1 */
370
371     if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
372         ret = null_strcmp(ext1, ext2);
373         if (ret == 0) {
374             return(strcmp(f1->filename, f2->filename));
375         } else {
376             return(ret);
377         }
378     };
379     if (s1->st_mode & S_IFDIR) {
380         return(-1);
381     }
382     if (s2->st_mode & S_IFDIR) {
383         return(1);
384     }
385     ret = null_strcmp(ext1, ext2);
386     if (ret == 0) {
387         return(strcmp(f1->filename, f2->filename));
388     } else {
389         return(ret);
390     }
391
392 } /* dir_extsort() */
393
394
395 void
396 get_dir(char *dirname, char *fmask, DirList **dir, int *n)
397 /*
398  *      desc:   get the files in the current directory
399  *      pre:    <dir> == NULL
400  *      post:   <dir> contains <n> dir-entries
401  */
402 {
403         char            cwd[MAXPATHLEN];
404         char            buf[256];
405         struct dirent   **dire;
406         struct stat     status;
407         int             i, j, nb;
408         long            d;
409
410
411         getcwd(cwd, MAXPATHLEN);
412         if (strcmp(cwd, "/") == 0) {    /* we are in the root directory */
413             if (show_dotfiles()) {
414                 *n = scandir(dirname, &dire, dir_select_root, alphasort);
415             } else {
416                 *n = scandir(dirname, &dire, dir_select_root_nd, alphasort);
417             }
418         } else {
419             if (show_dotfiles()) {
420                 *n = scandir(dirname, &dire, dir_select, alphasort);
421             } else {
422                 *n = scandir(dirname, &dire, dir_select_nd, alphasort);
423             }
424         }
425
426         /* There is the possibility that we have entered a directory    */
427         /* which we are not allowed to read, scandir thus returning     */
428         /* -1 for *n.                                                   */
429         /* Actually I should also check for lack of memory, but I'll    */
430         /* let my application happily crash if this is the case         */
431         /* Solution:                                                    */
432         /*      manually insert the parent directory as the only        */
433         /*      directory entry, and return.                            */
434
435         if (*n == -1) {
436             *n = 1;
437             *dir = (DirList *) malloc(sizeof(DirList));
438             strcpy((*dir)[0].filename, "..");
439             lstat("..", &status);
440             (*dir)[0].filestatus = status;
441             (*dir)[0].link = FALSE;
442             return;
443         }
444
445         *dir = (DirList *) malloc( *n * sizeof(DirList) );
446         d = 0;
447         i = 0;
448         j = 0;
449         while (j<*n) {
450             lstat(dire[j]->d_name, &status);
451             /* check if this file is to be included */
452             /* always include directories, the rest is subject to fmask */
453             if (S_ISDIR(status.st_mode)
454                 || fnmatch(fmask, dire[j]->d_name, FNM_NOESCAPE) != FNM_NOMATCH) {
455                 strcpy((*dir)[i].filename, dire[j]->d_name);
456                 (*dir)[i].filestatus = status;
457                 if ((S_IFMT & status.st_mode) == S_IFLNK) {  /* handle links */
458                     (*dir)[i].link = TRUE;
459                     stat(dire[j]->d_name, &status);
460                     nb = readlink(dire[j]->d_name, buf, sizeof(buf) - 1);
461                     if (nb == -1) {
462                         printf("get_dir(): Error reading link: %s\n", dire[j]->d_name);
463                         exit(-1);
464                     } else {
465                         (*dir)[i].linkname = malloc(sizeof(char) * nb + 1);
466                         strncpy((*dir)[i].linkname, buf, nb);
467                         (*dir)[i].linkname[nb] = 0;
468                     }
469                     (*dir)[i].filestatus = status;
470                 } else {
471                     (*dir)[i].link = FALSE;
472                     (*dir)[i].linkname = NULL;
473                 }
474                 i++;
475             } else {
476                 /* skip this entry */
477             }
478             j++;
479         }
480         *n = i;
481
482         /* sort the directory with the directory names on top */
483         qsort((*dir), *n, sizeof(DirList), _sort_func);
484
485         /* Free the allocated memory */
486         for (i=0; i<*n; i++) {
487             free(dire[i]);
488         }
489         free(dire);
490
491         return;
492 }/* get_dir() */
493
494
495 void
496 FreeDir(DirList *d, int n)
497 /*
498  *      desc:   free the dirlist d
499  *      pre:    d != NULL
500  *      post:   memory allocated to d has been released
501  */
502 {
503     int i;
504
505     if (d) {
506         for (i=0; i<n; i++) {
507             if (d[i].linkname) {
508                 free(d[i].linkname);
509             }
510         }
511         free(d);
512     } else {
513         printf("dir.c:FreeDir(): d == NULL\n");
514         exit(-1);
515     }
516
517     return;
518 } /* FreeDir() */
519
520 void
521 toggle_dotfiles(void)
522 /*
523  *      desc: toggle visibility of dot-files
524  */
525 {
526     _showdotfiles = !_showdotfiles;
527
528     return;
529 } /* toggle_dotfiles() */
530
531 int
532 show_dotfiles(void)
533 /*
534  *      desc: return the value of _showdotfiles
535  */
536 {
537     return(_showdotfiles);
538 } /* show_dotfiles() */
539
540 void
541 set_dotfiles(int b)
542 /*
543  *      desc: set the value of _showdotfiles
544  */
545 {
546     _showdotfiles = b;
547
548     return;
549 } /* set_dotfiles() */