Mon Feb 26 10:22:30 1996 Roland McGrath <roland@charlie-brown.gnu.ai.mit.edu>
[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
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       size_t namlen;
57
58       ++got;
59
60       if (entry->d_name[0] == '.'
61           && (entry->d_name[1] == '\0' ||
62               (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
63         {
64           errno = 0;
65           continue;
66         }
67
68       namlen = _D_EXACT_NAMLEN (entry);
69
70       if (namlen + len + 1 > PATH_MAX)
71         {
72 #ifdef ENAMETOOLONG
73           errno = ENAMETOOLONG;
74 #else
75           errno = ENOMEM;
76 #endif
77           return -1;
78         }
79
80       dir[len] = '/';
81       memcpy ((PTR) (dir + len + 1), (PTR) entry->d_name,
82               namlen + 1);
83
84       if (stat (dir, &s) < 0)
85         {
86           if (errno != EACCES && errno != ENOENT)
87             return -1;
88           flag = FTW_NS;
89         }
90       else if (S_ISDIR (s.st_mode))
91         {
92           newlev = (level + 1) % descriptors;
93
94           if (dirs[newlev] != NULL)
95             closedir (dirs[newlev]);
96
97           dirs[newlev] = opendir (dir);
98           if (dirs[newlev] != NULL)
99             flag = FTW_D;
100           else
101             {
102               if (errno != EACCES)
103                 return -1;
104               flag = FTW_DNR;
105             }
106         }
107       else
108         flag = FTW_F;
109
110       retval = (*func) (dir, &s, flag);
111
112       if (flag == FTW_D)
113         {
114           if (retval == 0)
115             retval = ftw_dir (dirs, newlev, descriptors, dir,
116                               namlen + len + 1, func);
117           if (dirs[newlev] != NULL)
118             {
119               int save;
120
121               save = errno;
122               closedir (dirs[newlev]);
123               errno = save;
124               dirs[newlev] = NULL;
125             }
126         }
127
128       if (retval != 0)
129         return retval;
130
131       if (dirs[level] == NULL)
132         {
133           int skip;
134
135           dir[len] = '\0';
136           dirs[level] = opendir (dir);
137           if (dirs[level] == NULL)
138             return -1;
139           skip = got;
140           while (skip-- != 0)
141             {
142               errno = 0;
143               if (readdir (dirs[level]) == NULL)
144                 return errno == 0 ? 0 : -1;
145             }
146         }
147
148       errno = 0;
149     }
150
151   return errno == 0 ? 0 : -1;
152 }
153
154 /* Call a function on every element in a directory tree.  */
155
156 int
157 DEFUN(ftw, (dir, func, descriptors),
158       CONST char *dir AND
159       int EXFUN((*func), (CONST char *file, struct stat *status,
160                           int flag)) AND
161       int descriptors)
162 {
163   DIR **dirs;
164   size_t len;
165   char buf[PATH_MAX + 1];
166   struct stat s;
167   int flag, retval;
168   int i;
169
170   if (descriptors <= 0)
171     descriptors = 1;
172
173   dirs = (DIR **) __alloca (descriptors * sizeof (DIR *));
174   i = descriptors;
175   while (i-- > 0)
176     dirs[i] = NULL;
177
178   if (stat (dir, &s) < 0)
179     {
180       if (errno != EACCES && errno != ENOENT)
181         return -1;
182       flag = FTW_NS;
183     }
184   else if (S_ISDIR (s.st_mode))
185     {
186       dirs[0] = opendir (dir);
187       if (dirs[0] != NULL)
188         flag = FTW_D;
189       else
190         {
191           if (errno != EACCES)
192             return -1;
193           flag = FTW_DNR;
194         }
195     }
196   else
197     flag = FTW_F;
198
199   len = strlen (dir);
200   memcpy ((PTR) buf, (PTR) dir, len + 1);
201
202   retval = (*func) (buf, &s, flag);
203
204   if (flag == FTW_D)
205     {
206       if (retval == 0)
207         retval = ftw_dir (dirs, 0, descriptors, buf, len, func);
208       if (dirs[0] != NULL)
209         {
210           int save;
211
212           save = errno;
213           closedir (dirs[0]);
214           errno = save;
215         }
216     }
217
218   return retval;
219 }