1/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
2This file is part of the GNU C Library.
3Contributed by Ian Lance Taylor (ian@airs.com).
4
5The GNU C Library is free software; you can redistribute it and/or
6modify it under the terms of the GNU Library General Public License as
7published by the Free Software Foundation; either version 2 of the
8License, or (at your option) any later version.
9
10The GNU C Library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13Library General Public License for more details.
14
15You should have received a copy of the GNU Library General Public
16License along with the GNU C Library; see the file COPYING.LIB.  If
17not, write to the Free Software Foundation, Inc., 59 Temple Place -
18Suite 330, Boston, MA 02111-1307, USA.
19
20Modified by Ian Lance Taylor for Taylor UUCP, June 1992, and October 1993.  */
21
22#include "uucp.h"
23
24#include "sysdep.h"
25
26#include <errno.h>
27
28#if HAVE_LIMITS_H
29#include <limits.h>
30#endif
31
32#if HAVE_SYS_PARAM_H
33#include <sys/param.h>
34#endif
35
36#if HAVE_OPENDIR
37#if HAVE_DIRENT_H
38#include <dirent.h>
39#else /* ! HAVE_DIRENT_H */
40#include <sys/dir.h>
41#define dirent direct
42#endif /* ! HAVE_DIRENT_H */
43#endif /* HAVE_OPENDIR */
44
45#if HAVE_FTW_H
46#include <ftw.h>
47#endif
48
49#ifndef PATH_MAX
50#ifdef MAXPATHLEN
51#define PATH_MAX MAXPATHLEN
52#else
53#define PATH_MAX 1024
54#endif
55#endif
56
57/* Traverse one level of a directory tree.  */
58
59static int
60ftw_dir (dirs, level, descriptors, dir, len, func)
61     DIR **dirs;
62     int level;
63     int descriptors;
64     char *dir;
65     size_t len;
66     int (*func) P((const char *file, const struct stat *status, int flag));
67{
68  int got;
69  struct dirent *entry;
70
71  got = 0;
72
73  errno = 0;
74
75  while ((entry = readdir (dirs[level])) != NULL)
76    {
77      size_t namlen;
78      struct stat s;
79      int flag, ret, newlev = 0;
80
81      ++got;
82
83      namlen = strlen (entry->d_name);
84      if (entry->d_name[0] == '.'
85	  && (namlen == 1 ||
86	      (namlen == 2 && entry->d_name[1] == '.')))
87	{
88	  errno = 0;
89	  continue;
90	}
91
92      if (namlen + len + 1 > PATH_MAX)
93	{
94#ifdef ENAMETOOLONG
95	  errno = ENAMETOOLONG;
96#else
97	  errno = ENOMEM;
98#endif
99	  return -1;
100	}
101
102      dir[len] = '/';
103      memcpy ((dir + len + 1), entry->d_name, namlen + 1);
104
105      if (stat (dir, &s) < 0)
106	{
107	  if (errno != EACCES)
108	    return -1;
109	  flag = FTW_NS;
110	}
111      else if (S_ISDIR (s.st_mode))
112	{
113	  newlev = (level + 1) % descriptors;
114
115	  if (dirs[newlev] != NULL)
116	    closedir (dirs[newlev]);
117
118	  dirs[newlev] = opendir (dir);
119	  if (dirs[newlev] != NULL)
120	    flag = FTW_D;
121	  else
122	    {
123	      if (errno != EACCES)
124		return -1;
125	      flag = FTW_DNR;
126	    }
127	}
128      else
129	flag = FTW_F;
130
131      ret = (*func) (dir, &s, flag);
132
133      if (flag == FTW_D)
134	{
135	  if (ret == 0)
136	    ret = ftw_dir (dirs, newlev, descriptors, dir,
137			   namlen + len + 1, func);
138	  if (dirs[newlev] != NULL)
139	    {
140	      int save;
141
142	      save = errno;
143	      closedir (dirs[newlev]);
144	      errno = save;
145	      dirs[newlev] = NULL;
146	    }
147	}
148
149      if (ret != 0)
150	return ret;
151
152      if (dirs[level] == NULL)
153	{
154	  int skip;
155
156	  dir[len] = '\0';
157	  dirs[level] = opendir (dir);
158	  if (dirs[level] == NULL)
159	    return -1;
160	  skip = got;
161	  while (skip-- != 0)
162	    {
163	      errno = 0;
164	      if (readdir (dirs[level]) == NULL)
165		return errno == 0 ? 0 : -1;
166	    }
167	}
168
169      errno = 0;
170    }
171
172  return errno == 0 ? 0 : -1;
173}
174
175/* Call a function on every element in a directory tree.  */
176
177int
178ftw (dir, func, descriptors)
179     const char *dir;
180     int (*func) P((const char *file, const struct stat *status, int flag));
181     int descriptors;
182{
183  DIR **dirs;
184  int c;
185  DIR **p;
186  size_t len;
187  char buf[PATH_MAX + 1];
188  struct stat s;
189  int flag, ret;
190
191  if (descriptors <= 0)
192    descriptors = 1;
193
194  dirs = (DIR **) malloc (descriptors * sizeof (DIR *));
195  if (dirs == NULL)
196    return -1;
197  c = descriptors;
198  p = dirs;
199  while (c-- != 0)
200    *p++ = NULL;
201
202  len = strlen (dir);
203  memcpy (buf, dir, len + 1);
204
205  if (stat (dir, &s) < 0)
206    {
207      if (errno != EACCES)
208	{
209	  free ((pointer) dirs);
210	  return -1;
211	}
212      flag = FTW_NS;
213    }
214  else if (S_ISDIR (s.st_mode))
215    {
216      dirs[0] = opendir (dir);
217      if (dirs[0] != NULL)
218	flag = FTW_D;
219      else
220	{
221	  if (errno != EACCES)
222	    {
223	      free ((pointer) dirs);
224	      return -1;
225	    }
226	  flag = FTW_DNR;
227	}
228    }
229  else
230    flag = FTW_F;
231
232  ret = (*func) (buf, &s, flag);
233
234  if (flag == FTW_D)
235    {
236      if (ret == 0)
237	{
238	  if (len == 1 && *buf == '/')
239	    len = 0;
240	  ret = ftw_dir (dirs, 0, descriptors, buf, len, func);
241	}
242      if (dirs[0] != NULL)
243	{
244	  int save;
245
246	  save = errno;
247	  closedir (dirs[0]);
248	  errno = save;
249	}
250    }
251
252  free ((pointer) dirs);
253  return ret;
254}
255