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() */