xref: /freebsd/lib/libc/gen/ftw.c (revision 6fd05b64b5b65dd4ba9b86482a0634a5f0b96c29)
1 /*
2  * Copyright (c) 2003 by Joel Baker.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Author nor the names of any contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h> /* Because fts(3) says so */
33 #include <sys/stat.h>
34 #include <fts.h>
35 
36 #include <unistd.h> /* We want strcpy */
37 
38 #include <errno.h> /* Because errno is our friend */
39 
40 #include "ftw.h"
41 
42 /* I like symbolic values - this is only used in this file. */
43 
44 enum __ftw_modes {
45     MODE_FTW,
46     MODE_NFTW
47 };
48 
49 /* Prototype this so that we can have it later */
50 
51 static int __ftw_core(const char *, void *, int, int, enum __ftw_modes);
52 
53 /*
54  * The external function calls are really just wrappers around __ftw_core,
55  * since the work they do is 90% the same.
56  */
57 
58 int ftw (const char *dir, __ftw_func_t func, int descr) {
59     return __ftw_core(dir, func, descr, 0, MODE_FTW);
60 }
61 
62 int nftw (const char *dir, __nftw_func_t func, int descr, int flags) {
63     return __ftw_core(dir, func, descr, flags, MODE_NFTW);
64 }
65 
66 /*
67 typedef int (*__ftw_func_t) \
68     (const char *file, const struct stat status, int flag);
69 typedef int (*__nftw_func_t) \
70     (const char *file, const struct stat status, int flag, struct FTW detail);
71 */
72 
73 static int __ftw_core(const char *dir, void *func, int descr, int flags,
74                       enum __ftw_modes mode) {
75     FTS *hierarchy;
76     FTSENT *entry;
77     int fts_options;
78     const char *paths[2];
79     int ftw_flag, func_ret;
80     struct FTW ftw_st;
81     int skip_entry;
82     __ftw_func_t ftw_func;
83     __nftw_func_t nftw_func;
84     int saved_errno;
85 
86     errno = 0;
87 
88     /* We need at least one descriptor to call fts */
89 
90     if (descr < 1) {
91         errno = EINVAL;
92         return -1;
93     }
94 
95     /* Decide which mode we're running in, and set the FTS options suitably. */
96 
97     if (MODE_NFTW == mode) { /* NFTW mode, with all the bells and whistles. */
98         fts_options = (flags & FTW_PHYS) ? FTS_PHYSICAL : FTS_LOGICAL;
99         fts_options |= (flags & FTW_CHDIR) ? 0 : FTS_NOCHDIR;
100         fts_options |= (flags & FTW_MOUNT) ? FTS_XDEV : 0;
101     } else { /* We must be in FTW mode. Nothing else makes sense. */
102         fts_options = FTS_LOGICAL;
103     }
104 
105     /* FTW gets a const char *, but FTS expects a null-term array of them. */
106 
107     paths[0] = dir;
108     paths[1] = NULL;
109 
110     /* Open the file hierarchy. */
111 
112     if (!(hierarchy = fts_open((char * const *)paths, fts_options, NULL))) {
113         if (EACCES == errno) {
114             return 0;
115         } else {
116             return -1;
117         }
118     }
119 
120     /* The main loop. Is it not nifty? Worship the loop. */
121 
122     while ((entry = fts_read(hierarchy))) {
123         skip_entry = 0;
124 
125         switch (entry->fts_info) {
126 
127             case FTS_D:
128                 if ((MODE_NFTW != mode) || !(flags & FTW_DEPTH)) {
129                     ftw_flag = FTW_D;
130                 } else {
131                     skip_entry = 1;
132                 }
133                 break;
134 
135             case FTS_DNR:
136                 ftw_flag = FTW_DNR;
137                 break;
138 
139             case FTS_F:
140                 ftw_flag = FTW_F;
141                 break;
142 
143             case FTS_SL:
144                 ftw_flag = FTW_SL;
145                 break;
146 
147             case FTS_NS:
148                 ftw_flag = FTW_NS;
149                 break;
150 
151             /* Values that should only occur in nftw mode */
152 
153             case FTS_SLNONE:
154                 if (MODE_NFTW == mode) {
155                     ftw_flag = FTW_SLN;
156                 } else {
157                     ftw_flag = FTW_SL;
158                 }
159                 break;
160 
161             case FTS_DP:
162                 if ((MODE_NFTW == mode) && (flags & FTW_DEPTH)) {
163                     ftw_flag = FTW_D;
164                 } else {
165                     skip_entry = 1;
166                 }
167                 break;
168 
169             default:
170                 /* I'm not sure this is right, but we don't have a valid FTW
171                  * type to call with, so cowardice seems the better part of
172                  * guessing.
173                  */
174 
175                 skip_entry = 1;
176         }
177 
178         if (MODE_FTW == mode) {
179             ftw_func = (__ftw_func_t) func;
180             func_ret = (*ftw_func)
181                 (entry->fts_path, entry->fts_statp, ftw_flag);
182         } else if (MODE_NFTW == mode) {
183             ftw_st.base = (entry->fts_pathlen - entry->fts_namelen);
184             ftw_st.level = entry->fts_level;
185 
186             nftw_func = (__nftw_func_t) func;
187             func_ret = (*nftw_func)
188                 (entry->fts_path, entry->fts_statp, ftw_flag, &ftw_st);
189         }
190 
191         if (0 != func_ret) {
192 	    saved_errno = errno;
193 	    fts_close(hierarchy);
194 	    errno = saved_errno;
195             return func_ret;
196         }
197     }
198 
199     /* The janitors will be upset if we don't clean up after ourselves. */
200 
201     saved_errno = errno;
202     fts_close(hierarchy);
203     if (0 != saved_errno) { /* fts_read returned NULL, and set errno - bail */
204 	errno = saved_errno;
205     }
206 
207     return errno ? -1 : 0;
208 }
209