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 ****************************************************************************/
75 dir_select_nd(const struct dirent *d)
77 * desc: allways include a directory entry <d>, except
78 * for the current directory and other dot-files
80 * pre: <d> points to a dirent
81 * post: returns TRUE if d->d_name != "." else FALSE
84 if (strcmp(d->d_name, ".")==0 ||
85 (d->d_name[0] == '.' && strlen(d->d_name) > 1 && d->d_name[1] != '.')) {
90 }/* dir_select_nd() */
94 dir_select(const struct dirent *d)
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
102 if (strcmp(d->d_name, ".")==0) { /* don't include the current directory */
110 dir_select_root_nd(const struct dirent *d)
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
119 if (d->d_name[0] == '.') { /* don't include the current directory */
120 return(FALSE); /* nor the parent directory */
124 } /* dir_select_root_nd() */
128 dir_select_root(const struct dirent *d)
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
136 if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
141 }/* dir_select_root() */
146 alphasort(const void *d1, const void *d2)
148 * desc: a replacement for what should be in the library
151 return(strcmp(((struct dirent *) d1)->d_name,
152 ((struct dirent *) d2)->d_name));
157 dir_alphasort(const void *d1, const void *d2)
159 * desc: compare d1 and d2, but put directories always first
160 * put '..' always on top
164 DirList *f1 = ((DirList *) d1),
165 *f2 = ((DirList *) d2);
166 struct stat *s1 = &(f1->filestatus);
167 struct stat *s2 = &(f2->filestatus);
170 if (strcmp(((DirList *) d1)->filename, "..") == 0) {
173 if (strcmp(((DirList *) d2)->filename, "..") == 0) {
177 /* put directories first */
178 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
179 return(strcmp(f1->filename, f2->filename));
181 if (s1->st_mode & S_IFDIR) {
184 if (s2->st_mode & S_IFDIR) {
187 return(strcmp(f1->filename, f2->filename));
189 } /* dir_alphasort() */
193 dir_sizesort(const void *d1, const void *d2)
195 * desc: compare d1 and d2, but put directories always first
199 DirList *f1 = ((DirList *) d1),
200 *f2 = ((DirList *) d2);
201 struct stat *s1 = &(f1->filestatus);
202 struct stat *s2 = &(f2->filestatus);
205 if (strcmp(((DirList *) d1)->filename, "..") == 0) {
208 if (strcmp(((DirList *) d2)->filename, "..") == 0) {
212 /* put directories first */
213 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
214 return(s1->st_size < s2->st_size ?
217 s1->st_size >= s2->st_size);
219 if (s1->st_mode & S_IFDIR) {
222 if (s2->st_mode & S_IFDIR) {
225 return(s1->st_size < s2->st_size ?
228 s1->st_size >= s2->st_size);
230 } /* dir_sizesort() */
233 dir_datesort(const void *d1, const void *d2)
235 * desc: compare d1 and d2 on date, but put directories always first
238 DirList *f1 = ((DirList *) d1),
239 *f2 = ((DirList *) d2);
240 struct stat *s1 = &(f1->filestatus);
241 struct stat *s2 = &(f2->filestatus);
245 if (strcmp(((DirList *) d1)->filename, "..") == 0) {
248 if (strcmp(((DirList *) d2)->filename, "..") == 0) {
252 /* put directories first */
253 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
254 return(s1->st_mtime < s2->st_mtime ?
257 s1->st_mtime >= s2->st_mtime);
259 if (s1->st_mode & S_IFDIR) {
262 if (s2->st_mode & S_IFDIR) {
265 return(s1->st_mtime < s2->st_mtime ?
268 s1->st_mtime >= s2->st_mtime);
270 } /* dir_datesort() */
274 null_strcmp(char *s1, char *s2)
276 * desc: compare strings allowing NULL pointers
279 if ((s1 == NULL) && (s2 == NULL)) {
288 return(strcmp(s1, s2));
289 } /* null_strcmp() */
293 dir_extsort(const void *d1, const void *d2)
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
301 DirList *f1 = ((DirList *) d1),
302 *f2 = ((DirList *) d2);
303 struct stat *s1 = &(f1->filestatus);
304 struct stat *s2 = &(f2->filestatus);
310 if (strcmp(((DirList *) d1)->filename, "..") == 0) {
313 if (strcmp(((DirList *) d2)->filename, "..") == 0) {
318 /* find the first extension */
320 ext1 = f1->filename + strlen(f1->filename);
322 while (!extf && (ext1 > f1->filename)) {
323 extf = (*--ext1 == '.');
330 /* ext1 == NULL if there's no "extension" else ext1 points */
331 /* to the first character of the extension string */
333 /* find the second extension */
335 ext2 = f2->filename + strlen(f2->filename);
337 while (!extf && (ext2 > f2->filename)) {
338 extf = (*--ext2 == '.');
345 /* idem as for ext1 */
347 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) {
348 ret = null_strcmp(ext1, ext2);
350 return(strcmp(f1->filename, f2->filename));
355 if (s1->st_mode & S_IFDIR) {
358 if (s2->st_mode & S_IFDIR) {
361 ret = null_strcmp(ext1, ext2);
363 return(strcmp(f1->filename, f2->filename));
368 } /* dir_extsort() */
372 get_dir(char *dirname, char *fmask, DirList **dir, int *n)
374 * desc: get the files in the current directory
376 * post: <dir> contains <n> dir-entries
379 char cwd[MAXPATHLEN];
381 struct dirent **dire;
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);
392 *n = scandir(dirname, &dire, dir_select_root_nd, alphasort);
395 if (show_dotfiles()) {
396 *n = scandir(dirname, &dire, dir_select, alphasort);
398 *n = scandir(dirname, &dire, dir_select_nd, alphasort);
402 /* There is the possibility that we have entered a directory */
403 /* which we are not allowed to read, scandir thus returning */
405 /* Actually I should also check for lack of memory, but I'll */
406 /* let my application happily crash if this is the case */
408 /* manually insert the parent directory as the only */
409 /* directory entry, and return. */
413 *dir = (DirList *) malloc(sizeof(DirList));
414 strcpy((*dir)[0].filename, "..");
415 lstat("..", &status);
416 (*dir)[0].filestatus = status;
417 (*dir)[0].link = FALSE;
421 *dir = (DirList *) malloc( *n * sizeof(DirList) );
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);
438 printf("get_dir(): Error reading link: %s\n", dire[j]->d_name);
441 (*dir)[i].linkname = malloc(sizeof(char) * nb + 1);
442 strncpy((*dir)[i].linkname, buf, nb);
443 (*dir)[i].linkname[nb] = 0;
445 (*dir)[i].filestatus = status;
447 (*dir)[i].link = FALSE;
448 (*dir)[i].linkname = NULL;
452 /* skip this entry */
458 /* sort the directory with the directory names on top */
459 qsort((*dir), *n, sizeof(DirList), _sort_func);
461 /* Free the allocated memory */
462 for (i=0; i<*n; i++) {
472 FreeDir(DirList *d, int n)
474 * desc: free the dirlist d
476 * post: memory allocated to d has been released
482 for (i=0; i<n; i++) {
489 printf("dir.c:FreeDir(): d == NULL\n");
497 toggle_dotfiles(void)
499 * desc: toggle visibility of dot-files
502 _showdotfiles = !_showdotfiles;
505 } /* toggle_dotfiles() */
510 * desc: return the value of _showdotfiles
513 return(_showdotfiles);
514 } /* show_dotfiles() */
519 * desc: set the value of _showdotfiles
525 } /* set_dotfiles() */