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