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