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