/** * This file is part of the mingw-w64 runtime package. * No warranty is given; refer to the file DISCLAIMER within this package. */ #include #include #include #include #include #include #include #include #include #include #include #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); }