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