2bb93784c6
zig can now cross compile hello.c targeting windows
464 lines
8.6 KiB
C
464 lines
8.6 KiB
C
/**
|
|
* 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);
|
|
}
|