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