New changes: Sender: cannot be blank, Sender: always overrides From:
[mmh] / sbr / crawl_folders.c
1
2 /*
3  * crawl_folders.c -- crawl folder hierarchy
4  *
5  * This code is Copyright (c) 2008, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #include <h/mh.h>
11 #include <h/crawl_folders.h>
12 #include <h/utils.h>
13
14 struct crawl_context {
15     int max;                    /* how many folders we currently can hold in
16                                  * the array `folders', increased by
17                                  * CRAWL_NUMFOLDERS at a time */
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         /* If the system supports it, try to skip processing of children we
76          * know are not directories or symlinks. */
77         child_is_folder = -1;
78 #if defined(HAVE_STRUCT_DIRENT_D_TYPE)
79         if (dp->d_type == DT_DIR) {
80             child_is_folder = 1;
81         } else if (dp->d_type != DT_LNK && dp->d_type != DT_UNKNOWN) {
82             continue;
83         }
84 #endif
85         if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) {
86             continue;
87         }
88         child = concat (prefix, dp->d_name, (void *)NULL);
89         /* If we have no d_type or d_type is DT_LNK or DT_UNKNOWN, stat the
90          * child to see what it is. */
91         if (child_is_folder == -1) {
92             child_is_folder = (stat (child, &st) != -1 && S_ISDIR(st.st_mode));
93         }
94         if (child_is_folder) {
95             /* add_folder saves child in the list, don't free it */
96             add_folder (child, crawl);
97         } else {
98             free (child);
99         }
100     }
101
102     closedir (dd);
103     free(prefix);
104 }
105
106 static void
107 crawl_folders_body (struct crawl_context *crawl,
108                     char *dir, crawl_callback_t *callback, void *baton)
109 {
110     int i;
111     int os = crawl->start;
112     int of = crawl->foldp;
113
114     crawl->start = crawl->foldp;
115
116     add_children (dir, crawl);
117
118     for (i = crawl->start; i < crawl->foldp; i++) {
119         char *fold = crawl->folders[i];
120         int crawl_children = 1;
121
122         if (callback != NULL) {
123             crawl_children = callback (fold, baton);
124         }
125
126         if (crawl_children) {
127             crawl_folders_body (crawl, fold, callback, baton);
128         }
129     }
130
131     crawl->start = os;
132     crawl->foldp = of;
133 }
134
135 void
136 crawl_folders (char *dir, crawl_callback_t *callback, void *baton)
137 {
138     struct crawl_context *crawl = mh_xmalloc (sizeof(*crawl));
139     crawl->max = CRAWL_NUMFOLDERS;
140     crawl->total = crawl->start = crawl->foldp = 0;
141     crawl->folders = mh_xmalloc (crawl->max * sizeof(*crawl->folders));
142
143     crawl_folders_body (crawl, dir, callback, baton);
144
145     /* Note that we "leak" the folder names, on the assumption that the caller
146      * is using them. */
147     free (crawl->folders);
148     free (crawl);
149 }