(ftw, ftw_dir): Treat ENOENT from stat like EACCES.
[kopensolaris-gnu/glibc.git] / io / ftw.c
1 /* Copyright (C) 1992, 1995 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
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <ansidecl.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <dirent.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <ftw.h>
29
30
31 #ifndef PATH_MAX
32 #define PATH_MAX 1024           /* XXX */
33 #endif
34
35
36 /* Traverse one level of a directory tree.  */
37
38 static int
39 DEFUN (ftw_dir, (dirs, level, descriptors, dir, len, func),
40        DIR **dirs AND int level AND int descriptors AND
41        char *dir AND size_t len AND
42        int EXFUN((*func), (CONST char *file, struct stat *status,
43                            int flag)))
44 {
45   int got;
46   struct dirent *entry;
47
48   got = 0;
49
50   errno = 0;
51
52   while ((entry = readdir (dirs[level])) != NULL)
53     {
54       struct stat s;
55       int flag, retval, newlev;
56
57       ++got;
58
59       if (entry->d_name[0] == '.'
60           && (entry->d_namlen == 1 ||
61               (entry->d_namlen == 2 && entry->d_name[1] == '.')))
62         {
63           errno = 0;
64           continue;
65         }
66
67       if (entry->d_namlen + len + 1 > PATH_MAX)
68         {
69 #ifdef ENAMETOOLONG
70           errno = ENAMETOOLONG;
71 #else
72           errno = ENOMEM;
73 #endif
74           return -1;
75         }
76
77       dir[len] = '/';
78       memcpy ((PTR) (dir + len + 1), (PTR) entry->d_name,
79               entry->d_namlen + 1);
80
81       if (stat (dir, &s) < 0)
82         {
83           if (errno != EACCES && errno != ENOENT)
84             return -1;
85           flag = FTW_NS;
86         }
87       else if (S_ISDIR (s.st_mode))
88         {
89           newlev = (level + 1) % descriptors;
90
91           if (dirs[newlev] != NULL)
92             closedir (dirs[newlev]);
93
94           dirs[newlev] = opendir (dir);
95           if (dirs[newlev] != NULL)
96             flag = FTW_D;
97           else
98             {
99               if (errno != EACCES)
100                 return -1;
101               flag = FTW_DNR;
102             }
103         }
104       else
105         flag = FTW_F;
106
107       retval = (*func) (dir, &s, flag);
108
109       if (flag == FTW_D)
110         {
111           if (retval == 0)
112             retval = ftw_dir (dirs, newlev, descriptors, dir,
113                               entry->d_namlen + len + 1, func);
114           if (dirs[newlev] != NULL)
115             {
116               int save;
117
118               save = errno;
119               closedir (dirs[newlev]);
120               errno = save;
121               dirs[newlev] = NULL;
122             }
123         }
124
125       if (retval != 0)
126         return retval;
127
128       if (dirs[level] == NULL)
129         {
130           int skip;
131
132           dir[len] = '\0';
133           dirs[level] = opendir (dir);
134           if (dirs[level] == NULL)
135             return -1;
136           skip = got;
137           while (skip-- != 0)
138             {
139               errno = 0;
140               if (readdir (dirs[level]) == NULL)
141                 return errno == 0 ? 0 : -1;
142             }
143         }
144
145       errno = 0;
146     }
147
148   return errno == 0 ? 0 : -1;
149 }
150
151 /* Call a function on every element in a directory tree.  */
152
153 int
154 DEFUN(ftw, (dir, func, descriptors),
155       CONST char *dir AND
156       int EXFUN((*func), (CONST char *file, struct stat *status,
157                           int flag)) AND
158       int descriptors)
159 {
160   DIR **dirs;
161   size_t len;
162   char buf[PATH_MAX + 1];
163   struct stat s;
164   int flag, retval;
165   int i;
166
167   if (descriptors <= 0)
168     descriptors = 1;
169
170   dirs = (DIR **) __alloca (descriptors * sizeof (DIR *));
171   i = descriptors;
172   while (i-- > 0)
173     dirs[i] = NULL;
174
175   if (stat (dir, &s) < 0)
176     {
177       if (errno != EACCES && errno != ENOENT)
178         return -1;
179       flag = FTW_NS;
180     }
181   else if (S_ISDIR (s.st_mode))
182     {
183       dirs[0] = opendir (dir);
184       if (dirs[0] != NULL)
185         flag = FTW_D;
186       else
187         {
188           if (errno != EACCES)
189             return -1;
190           flag = FTW_DNR;
191         }
192     }
193   else
194     flag = FTW_F;
195
196   len = strlen (dir);
197   memcpy ((PTR) buf, (PTR) dir, len + 1);
198
199   retval = (*func) (buf, &s, flag);
200
201   if (flag == FTW_D)
202     {
203       if (retval == 0)
204         retval = ftw_dir (dirs, 0, descriptors, buf, len, func);
205       if (dirs[0] != NULL)
206         {
207           int save;
208
209           save = errno;
210           closedir (dirs[0]);
211           errno = save;
212         }
213     }
214
215   return retval;
216 }