1 /****************************************************************************
4 * Author: Marc van Kempen
5 * desc: Directory routines, sorting and reading
7 * Copyright (c) 1995, Marc van Kempen
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
19 ****************************************************************************/
21 #include <sys/types.h>
24 #include <unistd.h> /* XXX for _POSIX_VERSION ifdefs */
26 #if !defined sgi && !defined _POSIX_VERSION
30 #include <sys/dirent.h>
32 #if defined sgi || defined _POSIX_VERSION
40 #include <sys/param.h>
43 /****************************************************************************
47 ****************************************************************************/
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
);
56 /****************************************************************************
60 ****************************************************************************/
63 /* This is user-selectable, I've set them fixed for now however */
65 void *_sort_func
= dir_alphasort
;
66 static int _showdotfiles
= TRUE
;
68 /****************************************************************************
72 ****************************************************************************/
77 const struct dirent
*d
83 * desc: allways include a directory entry <d>, except
84 * for the current directory and other dot-files
86 * pre: <d> points to a dirent
87 * post: returns TRUE if d->d_name != "." else FALSE
90 if (strcmp(d
->d_name
, ".")==0 ||
91 (d
->d_name
[0] == '.' && strlen(d
->d_name
) > 1 && d
->d_name
[1] != '.')) {
96 }/* dir_select_nd() */
102 const struct dirent
*d
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
114 if (strcmp(d
->d_name
, ".")==0) { /* don't include the current directory */
124 const struct dirent
*d
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
137 if (d
->d_name
[0] == '.') { /* don't include the current directory */
138 return(FALSE
); /* nor the parent directory */
142 } /* dir_select_root_nd() */
148 const struct dirent
*d
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
160 if (strcmp(d
->d_name
, ".") == 0 || strcmp(d
->d_name
, "..") == 0) {
165 }/* dir_select_root() */
170 alphasort(const void *d1
, const void *d2
)
172 * desc: a replacement for what should be in the library
175 return(strcmp(((struct dirent
*) d1
)->d_name
,
176 ((struct dirent
*) d2
)->d_name
));
181 dir_alphasort(const void *d1
, const void *d2
)
183 * desc: compare d1 and d2, but put directories always first
184 * put '..' always on top
188 DirList
*f1
= ((DirList
*) d1
),
189 *f2
= ((DirList
*) d2
);
190 struct stat
*s1
= &(f1
->filestatus
);
191 struct stat
*s2
= &(f2
->filestatus
);
194 if (strcmp(((DirList
*) d1
)->filename
, "..") == 0) {
197 if (strcmp(((DirList
*) d2
)->filename
, "..") == 0) {
201 /* put directories first */
202 if ((s1
->st_mode
& S_IFDIR
) && (s2
->st_mode
& S_IFDIR
)) {
203 return(strcmp(f1
->filename
, f2
->filename
));
205 if (s1
->st_mode
& S_IFDIR
) {
208 if (s2
->st_mode
& S_IFDIR
) {
211 return(strcmp(f1
->filename
, f2
->filename
));
213 } /* dir_alphasort() */
217 dir_sizesort(const void *d1
, const void *d2
)
219 * desc: compare d1 and d2, but put directories always first
223 DirList
*f1
= ((DirList
*) d1
),
224 *f2
= ((DirList
*) d2
);
225 struct stat
*s1
= &(f1
->filestatus
);
226 struct stat
*s2
= &(f2
->filestatus
);
229 if (strcmp(((DirList
*) d1
)->filename
, "..") == 0) {
232 if (strcmp(((DirList
*) d2
)->filename
, "..") == 0) {
236 /* put directories first */
237 if ((s1
->st_mode
& S_IFDIR
) && (s2
->st_mode
& S_IFDIR
)) {
238 return(s1
->st_size
< s2
->st_size
?
241 s1
->st_size
>= s2
->st_size
);
243 if (s1
->st_mode
& S_IFDIR
) {
246 if (s2
->st_mode
& S_IFDIR
) {
249 return(s1
->st_size
< s2
->st_size
?
252 s1
->st_size
>= s2
->st_size
);
254 } /* dir_sizesort() */
257 dir_datesort(const void *d1
, const void *d2
)
259 * desc: compare d1 and d2 on date, but put directories always first
262 DirList
*f1
= ((DirList
*) d1
),
263 *f2
= ((DirList
*) d2
);
264 struct stat
*s1
= &(f1
->filestatus
);
265 struct stat
*s2
= &(f2
->filestatus
);
269 if (strcmp(((DirList
*) d1
)->filename
, "..") == 0) {
272 if (strcmp(((DirList
*) d2
)->filename
, "..") == 0) {
276 /* put directories first */
277 if ((s1
->st_mode
& S_IFDIR
) && (s2
->st_mode
& S_IFDIR
)) {
278 return(s1
->st_mtime
< s2
->st_mtime
?
281 s1
->st_mtime
>= s2
->st_mtime
);
283 if (s1
->st_mode
& S_IFDIR
) {
286 if (s2
->st_mode
& S_IFDIR
) {
289 return(s1
->st_mtime
< s2
->st_mtime
?
292 s1
->st_mtime
>= s2
->st_mtime
);
294 } /* dir_datesort() */
298 null_strcmp(char *s1
, char *s2
)
300 * desc: compare strings allowing NULL pointers
303 if ((s1
== NULL
) && (s2
== NULL
)) {
312 return(strcmp(s1
, s2
));
313 } /* null_strcmp() */
317 dir_extsort(const void *d1
, const void *d2
)
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
325 DirList
*f1
= ((DirList
*) d1
),
326 *f2
= ((DirList
*) d2
);
327 struct stat
*s1
= &(f1
->filestatus
);
328 struct stat
*s2
= &(f2
->filestatus
);
334 if (strcmp(((DirList
*) d1
)->filename
, "..") == 0) {
337 if (strcmp(((DirList
*) d2
)->filename
, "..") == 0) {
342 /* find the first extension */
344 ext1
= f1
->filename
+ strlen(f1
->filename
);
346 while (!extf
&& (ext1
> f1
->filename
)) {
347 extf
= (*--ext1
== '.');
354 /* ext1 == NULL if there's no "extension" else ext1 points */
355 /* to the first character of the extension string */
357 /* find the second extension */
359 ext2
= f2
->filename
+ strlen(f2
->filename
);
361 while (!extf
&& (ext2
> f2
->filename
)) {
362 extf
= (*--ext2
== '.');
369 /* idem as for ext1 */
371 if ((s1
->st_mode
& S_IFDIR
) && (s2
->st_mode
& S_IFDIR
)) {
372 ret
= null_strcmp(ext1
, ext2
);
374 return(strcmp(f1
->filename
, f2
->filename
));
379 if (s1
->st_mode
& S_IFDIR
) {
382 if (s2
->st_mode
& S_IFDIR
) {
385 ret
= null_strcmp(ext1
, ext2
);
387 return(strcmp(f1
->filename
, f2
->filename
));
392 } /* dir_extsort() */
396 get_dir(char *dirname
, char *fmask
, DirList
**dir
, int *n
)
398 * desc: get the files in the current directory
400 * post: <dir> contains <n> dir-entries
403 char cwd
[MAXPATHLEN
];
405 struct dirent
**dire
;
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
);
416 *n
= scandir(dirname
, &dire
, dir_select_root_nd
, alphasort
);
419 if (show_dotfiles()) {
420 *n
= scandir(dirname
, &dire
, dir_select
, alphasort
);
422 *n
= scandir(dirname
, &dire
, dir_select_nd
, alphasort
);
426 /* There is the possibility that we have entered a directory */
427 /* which we are not allowed to read, scandir thus returning */
429 /* Actually I should also check for lack of memory, but I'll */
430 /* let my application happily crash if this is the case */
432 /* manually insert the parent directory as the only */
433 /* directory entry, and return. */
437 *dir
= (DirList
*) malloc(sizeof(DirList
));
438 strcpy((*dir
)[0].filename
, "..");
439 lstat("..", &status
);
440 (*dir
)[0].filestatus
= status
;
441 (*dir
)[0].link
= FALSE
;
445 *dir
= (DirList
*) malloc( *n
* sizeof(DirList
) );
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);
462 printf("get_dir(): Error reading link: %s\n", dire
[j
]->d_name
);
465 (*dir
)[i
].linkname
= malloc(sizeof(char) * nb
+ 1);
466 strncpy((*dir
)[i
].linkname
, buf
, nb
);
467 (*dir
)[i
].linkname
[nb
] = 0;
469 (*dir
)[i
].filestatus
= status
;
471 (*dir
)[i
].link
= FALSE
;
472 (*dir
)[i
].linkname
= NULL
;
476 /* skip this entry */
482 /* sort the directory with the directory names on top */
483 qsort((*dir
), *n
, sizeof(DirList
), _sort_func
);
485 /* Free the allocated memory */
486 for (i
=0; i
<*n
; i
++) {
496 FreeDir(DirList
*d
, int n
)
498 * desc: free the dirlist d
500 * post: memory allocated to d has been released
506 for (i
=0; i
<n
; i
++) {
513 printf("dir.c:FreeDir(): d == NULL\n");
521 toggle_dotfiles(void)
523 * desc: toggle visibility of dot-files
526 _showdotfiles
= !_showdotfiles
;
529 } /* toggle_dotfiles() */
534 * desc: return the value of _showdotfiles
537 return(_showdotfiles
);
538 } /* show_dotfiles() */
543 * desc: set the value of _showdotfiles
549 } /* set_dotfiles() */