master
  1/**
  2 * This file is part of the mingw-w64 runtime package.
  3 * No warranty is given; refer to the file DISCLAIMER within this package.
  4 */
  5
  6#include <stdlib.h>
  7#include <unistd.h>
  8#include <malloc.h>
  9#include <string.h>
 10#include <errno.h>
 11#include <limits.h>
 12#include <fcntl.h>
 13#include <sys/param.h>
 14#include <sys/stat.h>
 15#include <dirent.h>
 16#include <ftw.h>
 17
 18int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat);
 19int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat);
 20int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat);
 21
 22typedef struct dir_data_t {
 23  DIR *h;
 24  char *buf;
 25} dir_data_t;
 26
 27typedef struct node_t {
 28  struct node_t *l, *r;
 29  unsigned int colored : 1;
 30} node_t;
 31
 32typedef struct ctx_t {
 33  node_t *objs;
 34  dir_data_t **dirs;
 35  char *buf;
 36  struct FTW ftw;
 37  int (*fcb) (const char *, const STRUCT_STAT *, int , struct FTW *);
 38  size_t cur_dir, msz_dir, buf_sz;
 39  int flags;
 40  dev_t dev;
 41} ctx_t;
 42
 43static int add_object (ctx_t *);
 44static int do_dir (ctx_t *, STRUCT_STAT *, dir_data_t *);
 45static int do_entity (ctx_t *, dir_data_t *, const char *, size_t);
 46static int do_it (const char *, int, void *, int, int);
 47
 48static int open_directory (ctx_t *, dir_data_t *);
 49
 50static void
 51prepare_for_insert (int forced, node_t **bp, node_t **pp1, node_t **pp2, int p1_c, int p2_c)
 52{
 53  node_t *p1, *p2, **rp, **lp, *b = *bp;
 54
 55  rp = &(*bp)->r;
 56  lp = &(*bp)->l;
 57
 58  if (!forced && ((*lp) == NULL || (*lp)->colored == 0 || (*rp) == NULL || (*rp)->colored == 0))
 59    return;
 60
 61  b->colored = 1;
 62
 63  if (*rp)
 64    (*rp)->colored = 0;
 65
 66  if (*lp)
 67    (*lp)->colored = 0;
 68
 69  if (!pp1 || (*pp1)->colored == 0)
 70    return;
 71
 72  p1 = *pp1;
 73  p2 = *pp2;
 74
 75  if ((p1_c > 0) == (p2_c > 0))
 76    {
 77      *pp2 = *pp1;
 78      p1->colored = 0;
 79      p2->colored = 1;
 80      *(p1_c < 0 ? &p2->l : &p2->r) = (p1_c < 0 ? p1->r : p1->l);
 81      *(p1_c < 0 ? &p1->r : &p1->l) = p2;
 82      return;
 83    }
 84
 85  b->colored = 0;
 86  p1->colored = p2->colored = 1;
 87  *(p1_c < 0 ? &p1->l : &p1->r) = (p1_c < 0 ? *rp : *lp);
 88  *(p1_c < 0 ? rp : lp) = p1;
 89  *(p1_c < 0 ? &p2->r : &p2->l) = (p1_c < 0 ? *lp : *rp);
 90  *(p1_c < 0 ? lp : rp) = p2;
 91  *pp2 = b;
 92}
 93
 94static int
 95add_object (ctx_t *ctx)
 96{
 97  node_t **bp, **np, *b, *n, **pp1 = NULL, **pp2 = NULL;
 98  int c = 0, p1_c = 0, p2_c = 0;
 99
100  if (ctx->objs)
101    ctx->objs->colored = 0;
102
103  np = bp = &ctx->objs;
104
105  if (ctx->objs != NULL)
106    {
107      c = 1;
108
109      do
110	{
111	  b = *bp;
112	  prepare_for_insert (0, bp, pp1, pp2, p1_c, p2_c);
113	  np = &b->r;
114
115	  if (*np == NULL)
116	    break;
117
118	  pp2 = pp1;
119	  p2_c = p1_c;
120	  pp1 = bp;
121	  p1_c = 1;
122	  bp = np;
123	}
124      while (*np != NULL);
125    }
126
127  if (!(n = (node_t *) malloc (sizeof (node_t))))
128    return -1;
129
130  *np = n;
131  n->l = n->r = NULL;
132  n->colored = 1;
133
134  if (np != bp)
135    prepare_for_insert (1, np, bp, pp1, c, p1_c);
136
137  return 0;
138}
139
140static int
141open_directory (ctx_t *ctx, dir_data_t *dirp)
142{
143  DIR *st;
144  struct dirent *d;
145  char *buf, *h;
146  size_t cur_sz, buf_sz, sz;
147  int sv_e, ret = 0;
148
149  if (ctx->dirs[ctx->cur_dir] != NULL)
150    {
151      if (!(buf = malloc (1024)))
152	return -1;
153
154      st = ctx->dirs[ctx->cur_dir]->h;
155
156      buf_sz = 1024;
157      cur_sz = 0;
158
159      while ((d = readdir (st)) != NULL)
160	{
161	  sz = strlen (d->d_name);
162
163	  if ((cur_sz + sz + 2) >= buf_sz)
164	    {
165	      buf_sz += ((2 * sz) < 1024 ? 1024 : (2 * sz));
166	      if (!(h = (char *) realloc (buf, buf_sz)))
167		{
168		  sv_e = errno;
169		  free (buf);
170		  errno =  (sv_e);
171
172		  return -1;
173		}
174
175	      buf = h;
176	    }
177
178	  *((char *) memcpy (buf + cur_sz, d->d_name, sz) + sz) = 0;
179	  cur_sz += sz + 1;
180	}
181
182      buf[cur_sz++] = 0;
183
184      ctx->dirs[ctx->cur_dir]->buf = realloc (buf, cur_sz);
185
186      if (ctx->dirs[ctx->cur_dir]->buf == NULL)
187	{
188	  sv_e = errno;
189	  free (buf);
190	  errno = sv_e;
191	  ret = -1;
192	}
193      else
194	{
195	  closedir (st);
196
197	  ctx->dirs[ctx->cur_dir]->h = NULL;
198	  ctx->dirs[ctx->cur_dir] = NULL;
199	}
200    }
201
202  if (!ret)
203    {
204      dirp->h = opendir (ctx->buf);
205
206      if (dirp->h == NULL)
207	ret = -1;
208      else
209	{
210	  dirp->buf = NULL;
211	  ctx->dirs[ctx->cur_dir] = dirp;
212	  ctx->cur_dir += 1;
213
214	  if (ctx->cur_dir == ctx->msz_dir)
215	    ctx->cur_dir = 0;
216	}
217    }
218
219  return ret;
220}
221
222
223static int
224do_entity (ctx_t *ctx, dir_data_t *dir, const char *name, size_t namlen)
225{
226  STRUCT_STAT st;
227  char *h;
228  size_t cnt_sz;
229  int ret = 0, flag = 0;
230
231  if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
232    return 0;
233
234  cnt_sz = ctx->ftw.base + namlen + 2;
235
236  if (ctx->buf_sz < cnt_sz)
237    {
238      ctx->buf_sz = cnt_sz * 2;
239      
240      if (!(h = (char *) realloc (ctx->buf, ctx->buf_sz)))
241	return -1;
242
243      ctx->buf = h;
244    }
245
246  *((char *) memcpy (ctx->buf + ctx->ftw.base, name, namlen) + namlen) = 0;
247
248  name = ctx->buf;
249
250  if (FUNC_STAT (name, &st) < 0)
251    {
252      if (errno != EACCES && errno != ENOENT)
253	ret = -1;
254      else
255	flag = FTW_NS;
256
257      if (!(ctx->flags & FTW_PHYS))
258	FUNC_STAT (name, &st);
259    }
260  else
261    flag = (S_ISDIR (st.st_mode) ? FTW_D : FTW_F);
262
263  if (!ret && (flag == FTW_NS || !(ctx->flags & FTW_MOUNT) || st.st_dev == ctx->dev))
264    {
265      if (flag == FTW_D)
266	{
267	  if ((ctx->flags & FTW_PHYS) || !(ret = add_object (ctx)))
268	    ret = do_dir (ctx, &st, dir);
269	}
270      else
271	ret = (*ctx->fcb) (ctx->buf, &st, flag, &ctx->ftw);
272    }
273
274  if ((ctx->flags & FTW_ACTIONRETVAL) && ret == FTW_SKIP_SUBTREE)
275    ret = 0;
276
277  return ret;
278}
279
280
281static int
282do_dir (ctx_t *ctx, STRUCT_STAT *st, __UNUSED_PARAM(dir_data_t *old_dir))
283{
284  dir_data_t dir;
285  struct dirent *d;
286  char *startp, *runp, *endp;
287  int sv_e, ret, previous_base = ctx->ftw.base;
288
289  if ((ret = open_directory (ctx, &dir)) != 0)
290    {
291      if (errno == EACCES)
292	ret = (*ctx->fcb) (ctx->buf, st, FTW_DNR, &ctx->ftw);
293
294      return ret;
295    }
296
297  if (!(ctx->flags & FTW_DEPTH) && (ret = (*ctx->fcb) (ctx->buf, st, FTW_D, &ctx->ftw)) != 0)
298    {
299      sv_e = errno;
300      closedir (dir.h);
301      errno = sv_e;
302
303      if (ctx->cur_dir-- == 0)
304	ctx->cur_dir = ctx->msz_dir - 1;
305
306      ctx->dirs[ctx->cur_dir] = NULL;
307
308      return ret;
309    }
310
311  ctx->ftw.level += 1;
312  startp = memchr (ctx->buf, 0, 1024);
313
314  if (startp[-1] != '/')
315    *startp++ = '/';
316
317  ctx->ftw.base = (startp - ctx->buf);
318
319  while (dir.h != NULL && (d = readdir (dir.h)) != NULL
320         && !(ret = do_entity (ctx, &dir, d->d_name, strlen (d->d_name))))
321      ;
322
323  if (dir.h != NULL)
324    {
325      sv_e = errno;
326      closedir (dir.h);
327      errno = sv_e;
328
329      if (ctx->cur_dir-- == 0)
330	ctx->cur_dir = ctx->msz_dir - 1;
331
332      ctx->dirs[ctx->cur_dir] = NULL;
333    }
334  else
335    {
336      runp = dir.buf;
337
338      while (!ret && *runp != 0)
339	{
340	  endp = strchr (runp, 0);
341	  ret = do_entity (ctx, &dir, runp, endp - runp);
342	  runp = endp + 1;
343	}
344
345      sv_e = errno;
346      free (dir.buf);
347      errno = sv_e;
348    }
349
350  if ((ctx->flags & FTW_ACTIONRETVAL) && ret == FTW_SKIP_SIBLINGS)
351    ret = 0;
352
353  ctx->buf[ctx->ftw.base - 1] = 0;
354  ctx->ftw.level -= 1;
355  ctx->ftw.base = previous_base;
356
357  if (!ret && (ctx->flags & FTW_DEPTH))
358    ret = (*ctx->fcb) (ctx->buf, st, FTW_DP, &ctx->ftw);
359
360  return ret;
361}
362
363static void
364free_objs (node_t *r)
365{
366  if (r->l)
367    free_objs (r->l);
368
369  if (r->r)
370    free_objs (r->r);
371
372  free (r);
373}
374
375static int
376do_it (const char *dir, __UNUSED_PARAM(int is_nftw), void *fcb, int descriptors, int flags)
377{
378  struct ctx_t ctx;
379  STRUCT_STAT st;
380  int ret = 0;
381  int sv_e;
382  char *cp;
383
384  if (dir[0] == 0)
385  {
386    errno =  (ENOENT);
387    return -1;
388  }
389
390  ctx.msz_dir = descriptors < 1 ? 1 : descriptors;
391  ctx.cur_dir = 0;
392  ctx.dirs = (dir_data_t **) alloca (ctx.msz_dir * sizeof (dir_data_t *));
393  memset (ctx.dirs, 0, ctx.msz_dir * sizeof (dir_data_t *));
394
395  ctx.buf_sz = 2 * strlen (dir);
396
397  if (ctx.buf_sz <= 1024)
398    ctx.buf_sz = 1024;
399
400  ctx.buf = (char *) malloc (ctx.buf_sz);
401
402  if (ctx.buf == NULL)
403    return -1;
404
405  cp = strcpy (ctx.buf, dir) + strlen (dir);
406
407  while (cp > (ctx.buf + 1) && cp[-1] == '/')
408    --cp;
409
410  *cp = 0;
411
412  while (cp > ctx.buf && cp[-1] != '/')
413    --cp;
414
415  ctx.ftw.level = 0;
416  ctx.ftw.base = cp - ctx.buf;
417  ctx.flags = flags;
418  ctx.fcb = (int (*) (const char *, const STRUCT_STAT *, int , struct FTW *)) fcb;
419  ctx.objs = NULL;
420
421  if (!ret)
422    {
423      if (FUNC_STAT (ctx.buf, &st) < 0)
424	ret = -1;
425      else if (S_ISDIR (st.st_mode))
426	{
427	  ctx.dev = st.st_dev;
428
429	  if (!(flags & FTW_PHYS))
430	    ret = add_object (&ctx);
431
432	  if (!ret)
433	    ret = do_dir (&ctx, &st, NULL);
434	}
435      else
436	ret = (*ctx.fcb) (ctx.buf, &st, FTW_F, &ctx.ftw);
437
438      if ((flags & FTW_ACTIONRETVAL) && (ret == FTW_SKIP_SUBTREE || ret == FTW_SKIP_SIBLINGS))
439	ret = 0;
440    }
441
442  sv_e = errno;
443  if (ctx.objs)
444    free_objs (ctx.objs);
445  free (ctx.buf);
446  errno =  (sv_e);
447
448  return ret;
449}
450
451int
452FUNC_FTW (const char *path, int (*fcb) (const char *, const STRUCT_STAT *, int), int descriptors);
453int
454FUNC_FTW (const char *path, int (*fcb) (const char *, const STRUCT_STAT *, int), int descriptors)
455{
456  return do_it (path, 0, fcb, descriptors, 0);
457}
458
459int
460FUNC_NFTW (const char *path, int (*fcb) (const char *, const STRUCT_STAT *, int , struct FTW *), int descriptors, int flags);
461int
462FUNC_NFTW (const char *path, int (*fcb) (const char *, const STRUCT_STAT *, int , struct FTW *), int descriptors, int flags)
463{
464  return do_it (path, 1, fcb, descriptors, flags);
465}