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