]> diplodocus.org Git - nmh/blob - sbr/crawl_folders.c
5359eae3880416f5d7814e5dd599eed622ee0814
[nmh] / 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
15 * the array `folders', increased by
16 * CRAWL_NUMFOLDERS at a time */
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 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->folders[crawl->foldp++] = fold;
49 }
50
51 static void
52 add_children (char *name, struct crawl_context *crawl)
53 {
54 char *prefix, *child;
55 struct stat st;
56 struct dirent *dp;
57 DIR * dd;
58 int child_is_folder;
59
60 if (!(dd = opendir (name))) {
61 admonish (name, "unable to read directory ");
62 return;
63 }
64
65 if (strcmp (name, ".") == 0) {
66 prefix = mh_xstrdup("");
67 } else {
68 prefix = concat (name, "/", (void *)NULL);
69 }
70
71 while ((dp = readdir (dd))) {
72 /* If the system supports it, try to skip processing of children we
73 * know are not directories or symlinks. */
74 child_is_folder = -1;
75 #if defined(HAVE_STRUCT_DIRENT_D_TYPE)
76 if (dp->d_type == DT_DIR) {
77 child_is_folder = 1;
78 } else if (dp->d_type != DT_LNK && dp->d_type != DT_UNKNOWN) {
79 continue;
80 }
81 #endif
82 if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) {
83 continue;
84 }
85 child = concat (prefix, dp->d_name, (void *)NULL);
86 /* If we have no d_type or d_type is DT_LNK or DT_UNKNOWN, stat the
87 * child to see what it is. */
88 if (child_is_folder == -1) {
89 child_is_folder = (stat (child, &st) != -1 && S_ISDIR(st.st_mode));
90 }
91 if (child_is_folder) {
92 /* add_folder saves child in the list, don't free it */
93 add_folder (child, crawl);
94 } else {
95 free (child);
96 }
97 }
98
99 closedir (dd);
100 free(prefix);
101 }
102
103 static void
104 crawl_folders_body (struct crawl_context *crawl,
105 char *dir, crawl_callback_t *callback, void *baton)
106 {
107 int i;
108 int os = crawl->start;
109 int of = crawl->foldp;
110
111 crawl->start = crawl->foldp;
112
113 add_children (dir, crawl);
114
115 for (i = crawl->start; i < crawl->foldp; i++) {
116 char *fold = crawl->folders[i];
117 int crawl_children = 1;
118
119 if (callback != NULL) {
120 crawl_children = callback (fold, baton);
121 }
122
123 if (crawl_children) {
124 crawl_folders_body (crawl, fold, callback, baton);
125 }
126 }
127
128 crawl->start = os;
129 crawl->foldp = of;
130 }
131
132 void
133 crawl_folders (char *dir, crawl_callback_t *callback, void *baton)
134 {
135 struct crawl_context *crawl;
136 NEW(crawl);
137 crawl->max = CRAWL_NUMFOLDERS;
138 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 }