Adjusted the default scan listing: remove the body preview
[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, ".") || !strcmp(dp->d_name, "..")) {
88                         continue;
89                 }
90                 child = concat(prefix, dp->d_name, (void *)NULL);
91                 /*
92                 ** If we have no d_type or d_type is DT_LNK or DT_UNKNOWN,
93                 ** stat the child to see what it is.
94                 */
95                 if (child_is_folder == -1) {
96                         child_is_folder = (stat(child, &st) != -1 && S_ISDIR(st.st_mode));
97                 }
98                 if (child_is_folder) {
99                         /* add_folder saves child in the list, don't free it */
100                         add_folder(child, crawl);
101                 } else {
102                         free(child);
103                 }
104         }
105
106         closedir(dd);
107         free(prefix);
108 }
109
110 static void
111 crawl_folders_body(struct crawl_context *crawl, char *dir,
112         crawl_callback_t *callback, void *baton)
113 {
114         int i;
115         int os = crawl->start;
116         int of = crawl->foldp;
117
118         crawl->start = crawl->foldp;
119
120         add_children(dir, crawl);
121
122         for (i = crawl->start; i < crawl->foldp; i++) {
123                 char *fold = crawl->folders[i];
124                 int crawl_children = 1;
125
126                 if (callback != NULL) {
127                         crawl_children = callback(fold, baton);
128                 }
129
130                 if (crawl_children) {
131                         crawl_folders_body(crawl, fold, callback, baton);
132                 }
133         }
134
135         crawl->start = os;
136         crawl->foldp = of;
137 }
138
139 void
140 crawl_folders(char *dir, crawl_callback_t *callback, void *baton)
141 {
142         struct crawl_context *crawl = mh_xmalloc(sizeof(*crawl));
143         crawl->max = CRAWL_NUMFOLDERS;
144         crawl->total = crawl->start = crawl->foldp = 0;
145         crawl->folders = mh_xmalloc(crawl->max * sizeof(*crawl->folders));
146
147         crawl_folders_body(crawl, dir, callback, baton);
148
149         /*
150         ** Note that we "leak" the folder names, on the assumption that the
151         ** caller is using them.
152         */
153         free(crawl->folders);
154         free(crawl);
155 }