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