28116b7ed24f699adf4bcff7c434ae5ca1539eae
[mmh] / sbr / crawl_folders.c
1 /*
2  * crawl_folders.c -- crawl folder hierarchy
3  *
4  * This code is Copyright (c) 2008, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include <h/crawl_folders.h>
11 #include <h/utils.h>
12
13 struct crawl_context {
14         int max;    /* how many folders we currently can hold in the array
15                 * `folders', increased by CRAWL_NUMFOLDERS at a time */
16         int total;  /* how many `folders' actually has */
17         char **folders;  /* the array of folders */
18         int start;
19         int foldp;
20 };
21
22 /*
23  * Add the folder name into the
24  * list in a sorted fashion.
25  */
26
27 static void
28 add_folder (char *fold, struct crawl_context *crawl)
29 {
30         register int i, j;
31
32         /* if necessary, reallocate the space for folder names */
33         if (crawl->foldp >= crawl->max) {
34                 crawl->max += CRAWL_NUMFOLDERS;
35                 crawl->folders = mh_xrealloc (crawl->folders,
36                         crawl->max * sizeof(char *));
37         }
38
39         for (i = crawl->start; i < crawl->foldp; i++)
40                 if (strcmp (fold, crawl->folders[i]) < 0) {
41                         for (j = crawl->foldp - 1; j >= i; j--)
42                                 crawl->folders[j + 1] = crawl->folders[j];
43                         crawl->foldp++;
44                         crawl->folders[i] = fold;
45                         return;
46                 }
47
48         crawl->total++;
49         crawl->folders[crawl->foldp++] = fold;
50 }
51
52 static void
53 add_children (char *name, struct crawl_context *crawl)
54 {
55         char *prefix, *child;
56         struct stat st;
57         struct dirent *dp;
58         DIR * dd;
59         int child_is_folder;
60
61         if (!(dd = opendir (name))) {
62                 admonish (name, "unable to read directory ");
63                 return;
64         }
65
66         if (strcmp (name, ".") == 0) {
67                 prefix = getcpy ("");
68         } else {
69                 prefix = concat (name, "/", (void *)NULL);
70         }
71
72         while ((dp = readdir (dd))) {
73                 /* If the system supports it, try to skip processing of
74                  * children we know are not directories or symlinks. */
75                 child_is_folder = -1;
76 #if defined(HAVE_STRUCT_DIRENT_D_TYPE)
77                 if (dp->d_type == DT_DIR) {
78                         child_is_folder = 1;
79                 } else if (dp->d_type != DT_LNK && dp->d_type != DT_UNKNOWN) {
80                         continue;
81                 }
82 #endif
83                 if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) {
84                         continue;
85                 }
86                 child = concat (prefix, dp->d_name, (void *)NULL);
87                 /* If we have no d_type or d_type is DT_LNK or DT_UNKNOWN, stat the
88                  * child to see what it is. */
89                 if (child_is_folder == -1) {
90                         child_is_folder = (stat (child, &st) != -1 && S_ISDIR(st.st_mode));
91                 }
92                 if (child_is_folder) {
93                         /* add_folder saves child in the list, don't free it */
94                         add_folder (child, crawl);
95                 } else {
96                         free (child);
97                 }
98         }
99
100         closedir (dd);
101         free(prefix);
102 }
103
104 static void
105 crawl_folders_body (struct crawl_context *crawl, char *dir,
106         crawl_callback_t *callback, void *baton)
107 {
108         int i;
109         int os = crawl->start;
110         int of = crawl->foldp;
111
112         crawl->start = crawl->foldp;
113
114         add_children (dir, crawl);
115
116         for (i = crawl->start; i < crawl->foldp; i++) {
117                 char *fold = crawl->folders[i];
118                 int crawl_children = 1;
119
120                 if (callback != NULL) {
121                         crawl_children = callback (fold, baton);
122                 }
123
124                 if (crawl_children) {
125                         crawl_folders_body (crawl, fold, callback, baton);
126                 }
127         }
128
129         crawl->start = os;
130         crawl->foldp = of;
131 }
132
133 void
134 crawl_folders (char *dir, crawl_callback_t *callback, void *baton)
135 {
136         struct crawl_context *crawl = mh_xmalloc (sizeof(*crawl));
137         crawl->max = CRAWL_NUMFOLDERS;
138         crawl->total = crawl->start = crawl->foldp = 0;
139         crawl->folders = mh_xmalloc (crawl->max * sizeof(*crawl->folders));
140
141         crawl_folders_body (crawl, dir, callback, baton);
142
143         /* Note that we "leak" the folder names, on the assumption that the caller
144          * is using them. */
145         free (crawl->folders);
146         free (crawl);
147 }