/* crawl_folders.c -- crawl folder hierarchy * * This code is Copyright (c) 2008, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ #include #include #include struct crawl_context { int max; /* how many folders we currently can hold in * the array `folders', increased by * CRAWL_NUMFOLDERS at a time */ char **folders; /* the array of folders */ int start; int foldp; }; /* * Add the folder name into the * list in a sorted fashion. */ static void add_folder (char *fold, struct crawl_context *crawl) { int i, j; /* if necessary, reallocate the space for folder names */ if (crawl->foldp >= crawl->max) { crawl->max += CRAWL_NUMFOLDERS; crawl->folders = mh_xrealloc (crawl->folders, crawl->max * sizeof(char *)); } for (i = crawl->start; i < crawl->foldp; i++) if (strcmp (fold, crawl->folders[i]) < 0) { for (j = crawl->foldp - 1; j >= i; j--) crawl->folders[j + 1] = crawl->folders[j]; crawl->foldp++; crawl->folders[i] = fold; return; } crawl->folders[crawl->foldp++] = fold; } static void add_children (char *name, struct crawl_context *crawl) { char *prefix, *child; struct stat st; struct dirent *dp; DIR * dd; int child_is_folder; if (!(dd = opendir (name))) { admonish (name, "unable to read directory "); return; } if (strcmp (name, ".") == 0) { prefix = mh_xstrdup(""); } else { prefix = concat(name, "/", NULL); } while ((dp = readdir (dd))) { /* If the system supports it, try to skip processing of children we * know are not directories or symlinks. */ child_is_folder = -1; #if defined(HAVE_STRUCT_DIRENT_D_TYPE) if (dp->d_type == DT_DIR) { child_is_folder = 1; } else if (dp->d_type != DT_LNK && dp->d_type != DT_UNKNOWN) { continue; } #endif if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) { continue; } child = concat(prefix, dp->d_name, NULL); /* If we have no d_type or d_type is DT_LNK or DT_UNKNOWN, stat the * child to see what it is. */ if (child_is_folder == -1) { child_is_folder = (stat (child, &st) != -1 && S_ISDIR(st.st_mode)); } if (child_is_folder) { /* add_folder saves child in the list, don't free it */ add_folder (child, crawl); } else { free (child); } } closedir (dd); free(prefix); } static void crawl_folders_body (struct crawl_context *crawl, char *dir, crawl_callback_t *callback, void *baton) { int i; int os = crawl->start; int of = crawl->foldp; crawl->start = crawl->foldp; add_children (dir, crawl); for (i = crawl->start; i < crawl->foldp; i++) { char *fold = crawl->folders[i]; int crawl_children = 1; if (callback != NULL) { crawl_children = callback (fold, baton); } if (crawl_children) { crawl_folders_body (crawl, fold, callback, baton); } } crawl->start = os; crawl->foldp = of; } void crawl_folders (char *dir, crawl_callback_t *callback, void *baton) { struct crawl_context *crawl; NEW(crawl); crawl->max = CRAWL_NUMFOLDERS; crawl->start = crawl->foldp = 0; crawl->folders = mh_xmalloc (crawl->max * sizeof(*crawl->folders)); crawl_folders_body (crawl, dir, callback, baton); /* Note that we "leak" the folder names, on the assumption that the caller * is using them. */ free (crawl->folders); free (crawl); }