| /** |
| * This file is part of the mingw-w64 runtime package. |
| * No warranty is given; refer to the file DISCLAIMER within this package. |
| */ |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <malloc.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include <ftw.h> |
| |
| #ifdef IMPL_FTW64 |
| #define stat stat64 |
| #define nftw nftw64 |
| #define ftw ftw64 |
| #endif |
| |
| typedef struct dir_data_t { |
| DIR *h; |
| char *buf; |
| } dir_data_t; |
| |
| typedef struct node_t { |
| struct node_t *l, *r; |
| unsigned int colored : 1; |
| } node_t; |
| |
| typedef struct ctx_t { |
| node_t *objs; |
| dir_data_t **dirs; |
| char *buf; |
| struct FTW ftw; |
| int (*fcb) (const char *, const struct stat *, int , struct FTW *); |
| size_t cur_dir, msz_dir, buf_sz; |
| int flags; |
| dev_t dev; |
| } ctx_t; |
| |
| static int add_object (ctx_t *); |
| static int do_dir (ctx_t *, struct stat *, dir_data_t *); |
| static int do_entity (ctx_t *, dir_data_t *, const char *, size_t); |
| static int do_it (const char *, int, void *, int, int); |
| |
| static int open_directory (ctx_t *, dir_data_t *); |
| |
| static void |
| prepare_for_insert (int forced, node_t **bp, node_t **pp1, node_t **pp2, int p1_c, int p2_c) |
| { |
| node_t *p1, *p2, **rp, **lp, *b = *bp; |
| |
| rp = &(*bp)->r; |
| lp = &(*bp)->l; |
| |
| if (!forced && ((*lp) == NULL || (*lp)->colored == 0 || (*rp) == NULL || (*rp)->colored == 0)) |
| return; |
| |
| b->colored = 1; |
| |
| if (*rp) |
| (*rp)->colored = 0; |
| |
| if (*lp) |
| (*lp)->colored = 0; |
| |
| if (!pp1 || (*pp1)->colored == 0) |
| return; |
| |
| p1 = *pp1; |
| p2 = *pp2; |
| |
| if ((p1_c > 0) == (p2_c > 0)) |
| { |
| *pp2 = *pp1; |
| p1->colored = 0; |
| p2->colored = 1; |
| *(p1_c < 0 ? &p2->l : &p2->r) = (p1_c < 0 ? p1->r : p1->l); |
| *(p1_c < 0 ? &p1->r : &p1->l) = p2; |
| return; |
| } |
| |
| b->colored = 0; |
| p1->colored = p2->colored = 1; |
| *(p1_c < 0 ? &p1->l : &p1->r) = (p1_c < 0 ? *rp : *lp); |
| *(p1_c < 0 ? rp : lp) = p1; |
| *(p1_c < 0 ? &p2->r : &p2->l) = (p1_c < 0 ? *lp : *rp); |
| *(p1_c < 0 ? lp : rp) = p2; |
| *pp2 = b; |
| } |
| |
| static int |
| add_object (ctx_t *ctx) |
| { |
| node_t **bp, **np, *b, *n, **pp1 = NULL, **pp2 = NULL; |
| int c = 0, p1_c = 0, p2_c = 0; |
| |
| if (ctx->objs) |
| ctx->objs->colored = 0; |
| |
| np = bp = &ctx->objs; |
| |
| if (ctx->objs != NULL) |
| { |
| c = 1; |
| |
| do |
| { |
| b = *bp; |
| prepare_for_insert (0, bp, pp1, pp2, p1_c, p2_c); |
| np = &b->r; |
| |
| if (*np == NULL) |
| break; |
| |
| pp2 = pp1; |
| p2_c = p1_c; |
| pp1 = bp; |
| p1_c = 1; |
| bp = np; |
| } |
| while (*np != NULL); |
| } |
| |
| if (!(n = (node_t *) malloc (sizeof (node_t)))) |
| return -1; |
| |
| *np = n; |
| n->l = n->r = NULL; |
| n->colored = 1; |
| |
| if (np != bp) |
| prepare_for_insert (1, np, bp, pp1, c, p1_c); |
| |
| return 0; |
| } |
| |
| static int |
| open_directory (ctx_t *ctx, dir_data_t *dirp) |
| { |
| DIR *st; |
| struct dirent *d; |
| char *buf, *h; |
| size_t cur_sz, buf_sz, sz; |
| int sv_e, ret = 0; |
| |
| if (ctx->dirs[ctx->cur_dir] != NULL) |
| { |
| if (!(buf = malloc (1024))) |
| return -1; |
| |
| st = ctx->dirs[ctx->cur_dir]->h; |
| |
| buf_sz = 1024; |
| cur_sz = 0; |
| |
| while ((d = readdir (st)) != NULL) |
| { |
| sz = strlen (d->d_name); |
| |
| if ((cur_sz + sz + 2) >= buf_sz) |
| { |
| buf_sz += ((2 * sz) < 1024 ? 1024 : (2 * sz)); |
| if (!(h = (char *) realloc (buf, buf_sz))) |
| { |
| sv_e = errno; |
| free (buf); |
| errno = (sv_e); |
| |
| return -1; |
| } |
| |
| buf = h; |
| } |
| |
| *((char *) memcpy (buf + cur_sz, d->d_name, sz) + sz) = 0; |
| cur_sz += sz + 1; |
| } |
| |
| buf[cur_sz++] = 0; |
| |
| ctx->dirs[ctx->cur_dir]->buf = realloc (buf, cur_sz); |
| |
| if (ctx->dirs[ctx->cur_dir]->buf == NULL) |
| { |
| sv_e = errno; |
| free (buf); |
| errno = sv_e; |
| ret = -1; |
| } |
| else |
| { |
| closedir (st); |
| |
| ctx->dirs[ctx->cur_dir]->h = NULL; |
| ctx->dirs[ctx->cur_dir] = NULL; |
| } |
| } |
| |
| if (!ret) |
| { |
| dirp->h = opendir (ctx->buf); |
| |
| if (dirp->h == NULL) |
| ret = -1; |
| else |
| { |
| dirp->buf = NULL; |
| ctx->dirs[ctx->cur_dir] = dirp; |
| ctx->cur_dir += 1; |
| |
| if (ctx->cur_dir == ctx->msz_dir) |
| ctx->cur_dir = 0; |
| } |
| } |
| |
| return ret; |
| } |
| |
| |
| static int |
| do_entity (ctx_t *ctx, dir_data_t *dir, const char *name, size_t namlen) |
| { |
| struct stat st; |
| char *h; |
| size_t cnt_sz; |
| int ret = 0, flag = 0; |
| |
| if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) |
| return 0; |
| |
| cnt_sz = ctx->ftw.base + namlen + 2; |
| |
| if (ctx->buf_sz < cnt_sz) |
| { |
| ctx->buf_sz = cnt_sz * 2; |
| |
| if (!(h = (char *) realloc (ctx->buf, ctx->buf_sz))) |
| return -1; |
| |
| ctx->buf = h; |
| } |
| |
| *((char *) memcpy (ctx->buf + ctx->ftw.base, name, namlen) + namlen) = 0; |
| |
| name = ctx->buf; |
| |
| if (stat (name, &st) < 0) |
| { |
| if (errno != EACCES && errno != ENOENT) |
| ret = -1; |
| else |
| flag = FTW_NS; |
| |
| if (!(ctx->flags & FTW_PHYS)) |
| stat (name, &st); |
| } |
| else |
| flag = (S_ISDIR (st.st_mode) ? FTW_D : FTW_F); |
| |
| if (!ret && (flag == FTW_NS || !(ctx->flags & FTW_MOUNT) || st.st_dev == ctx->dev)) |
| { |
| if (flag == FTW_D) |
| { |
| if ((ctx->flags & FTW_PHYS) || !(ret = add_object (ctx))) |
| ret = do_dir (ctx, &st, dir); |
| } |
| else |
| ret = (*ctx->fcb) (ctx->buf, &st, flag, &ctx->ftw); |
| } |
| |
| if ((ctx->flags & FTW_ACTIONRETVAL) && ret == FTW_SKIP_SUBTREE) |
| ret = 0; |
| |
| return ret; |
| } |
| |
| |
| static int |
| do_dir (ctx_t *ctx, struct stat *st, __UNUSED_PARAM(dir_data_t *old_dir)) |
| { |
| dir_data_t dir; |
| struct dirent *d; |
| char *startp, *runp, *endp; |
| int sv_e, ret, previous_base = ctx->ftw.base; |
| |
| if ((ret = open_directory (ctx, &dir)) != 0) |
| { |
| if (errno == EACCES) |
| ret = (*ctx->fcb) (ctx->buf, st, FTW_DNR, &ctx->ftw); |
| |
| return ret; |
| } |
| |
| if (!(ctx->flags & FTW_DEPTH) && (ret = (*ctx->fcb) (ctx->buf, st, FTW_D, &ctx->ftw)) != 0) |
| { |
| sv_e = errno; |
| closedir (dir.h); |
| errno = sv_e; |
| |
| if (ctx->cur_dir-- == 0) |
| ctx->cur_dir = ctx->msz_dir - 1; |
| |
| ctx->dirs[ctx->cur_dir] = NULL; |
| |
| return ret; |
| } |
| |
| ctx->ftw.level += 1; |
| startp = memchr (ctx->buf, 0, 1024); |
| |
| if (startp[-1] != '/') |
| *startp++ = '/'; |
| |
| ctx->ftw.base = (startp - ctx->buf); |
| |
| while (dir.h != NULL && (d = readdir (dir.h)) != NULL |
| && !(ret = do_entity (ctx, &dir, d->d_name, strlen (d->d_name)))) |
| ; |
| |
| if (dir.h != NULL) |
| { |
| sv_e = errno; |
| closedir (dir.h); |
| errno = sv_e; |
| |
| if (ctx->cur_dir-- == 0) |
| ctx->cur_dir = ctx->msz_dir - 1; |
| |
| ctx->dirs[ctx->cur_dir] = NULL; |
| } |
| else |
| { |
| runp = dir.buf; |
| |
| while (!ret && *runp != 0) |
| { |
| endp = strchr (runp, 0); |
| ret = do_entity (ctx, &dir, runp, endp - runp); |
| runp = endp + 1; |
| } |
| |
| sv_e = errno; |
| free (dir.buf); |
| errno = sv_e; |
| } |
| |
| if ((ctx->flags & FTW_ACTIONRETVAL) && ret == FTW_SKIP_SIBLINGS) |
| ret = 0; |
| |
| ctx->buf[ctx->ftw.base - 1] = 0; |
| ctx->ftw.level -= 1; |
| ctx->ftw.base = previous_base; |
| |
| if (!ret && (ctx->flags & FTW_DEPTH)) |
| ret = (*ctx->fcb) (ctx->buf, st, FTW_DP, &ctx->ftw); |
| |
| return ret; |
| } |
| |
| static void |
| free_objs (node_t *r) |
| { |
| if (r->l) |
| free_objs (r->l); |
| |
| if (r->r) |
| free_objs (r->r); |
| |
| free (r); |
| } |
| |
| static int |
| do_it (const char *dir, __UNUSED_PARAM(int is_nftw), void *fcb, int descriptors, int flags) |
| { |
| struct ctx_t ctx; |
| struct stat st; |
| int ret = 0; |
| int sv_e; |
| char *cp; |
| |
| if (dir[0] == 0) |
| { |
| errno = (ENOENT); |
| return -1; |
| } |
| |
| ctx.msz_dir = descriptors < 1 ? 1 : descriptors; |
| ctx.cur_dir = 0; |
| ctx.dirs = (dir_data_t **) alloca (ctx.msz_dir * sizeof (dir_data_t *)); |
| memset (ctx.dirs, 0, ctx.msz_dir * sizeof (dir_data_t *)); |
| |
| ctx.buf_sz = 2 * strlen (dir); |
| |
| if (ctx.buf_sz <= 1024) |
| ctx.buf_sz = 1024; |
| |
| ctx.buf = (char *) malloc (ctx.buf_sz); |
| |
| if (ctx.buf == NULL) |
| return -1; |
| |
| cp = strcpy (ctx.buf, dir) + strlen (dir); |
| |
| while (cp > (ctx.buf + 1) && cp[-1] == '/') |
| --cp; |
| |
| *cp = 0; |
| |
| while (cp > ctx.buf && cp[-1] != '/') |
| --cp; |
| |
| ctx.ftw.level = 0; |
| ctx.ftw.base = cp - ctx.buf; |
| ctx.flags = flags; |
| ctx.fcb = (int (*) (const char *, const struct stat *, int , struct FTW *)) fcb; |
| ctx.objs = NULL; |
| |
| if (!ret) |
| { |
| if (stat (ctx.buf, &st) < 0) |
| ret = -1; |
| else if (S_ISDIR (st.st_mode)) |
| { |
| ctx.dev = st.st_dev; |
| |
| if (!(flags & FTW_PHYS)) |
| ret = add_object (&ctx); |
| |
| if (!ret) |
| ret = do_dir (&ctx, &st, NULL); |
| } |
| else |
| ret = (*ctx.fcb) (ctx.buf, &st, FTW_F, &ctx.ftw); |
| |
| if ((flags & FTW_ACTIONRETVAL) && (ret == FTW_SKIP_SUBTREE || ret == FTW_SKIP_SIBLINGS)) |
| ret = 0; |
| } |
| |
| sv_e = errno; |
| if (ctx.objs) |
| free_objs (ctx.objs); |
| free (ctx.buf); |
| errno = (sv_e); |
| |
| return ret; |
| } |
| |
| int |
| ftw (const char *path, int (*fcb) (const char *, const struct stat *, int), int descriptors) |
| { |
| return do_it (path, 0, fcb, descriptors, 0); |
| } |
| |
| int |
| nftw (const char *path, int (*fcb) (const char *, const struct stat *, int , struct FTW *), int descriptors, int flags) |
| { |
| return do_it (path, 1, fcb, descriptors, flags); |
| } |