xref: /freebsd/lib/libc/gen/ftw.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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     __ftw_func_t ftw_func;
82     __nftw_func_t nftw_func;
83     int saved_errno;
84 
85     errno = 0;
86 
87     /* We need at least one descriptor to call fts */
88 
89     if (descr < 1) {
90         errno = EINVAL;
91         return -1;
92     }
93 
94     /* Decide which mode we're running in, and set the FTS options suitably. */
95 
96     if (MODE_NFTW == mode) { /* NFTW mode, with all the bells and whistles. */
97         fts_options = (flags & FTW_PHYS) ? FTS_PHYSICAL : FTS_LOGICAL;
98         fts_options |= (flags & FTW_CHDIR) ? 0 : FTS_NOCHDIR;
99         fts_options |= (flags & FTW_MOUNT) ? FTS_XDEV : 0;
100     } else { /* We must be in FTW mode. Nothing else makes sense. */
101         fts_options = FTS_LOGICAL;
102     }
103 
104     /* FTW gets a const char *, but FTS expects a null-term array of them. */
105 
106     paths[0] = dir;
107     paths[1] = NULL;
108 
109     /* Open the file hierarchy. */
110 
111     if (!(hierarchy = fts_open((char * const *)paths, fts_options, NULL))) {
112         if (EACCES == errno) {
113             return 0;
114         } else {
115             return -1;
116         }
117     }
118 
119     /* The main loop. Is it not nifty? Worship the loop. */
120 
121     while ((entry = fts_read(hierarchy))) {
122         switch (entry->fts_info) {
123 
124             case FTS_D:
125                 if ((MODE_NFTW != mode) || !(flags & FTW_DEPTH)) {
126                     ftw_flag = FTW_D;
127                 }
128                 break;
129 
130             case FTS_DNR:
131                 ftw_flag = FTW_DNR;
132                 break;
133 
134             case FTS_F:
135                 ftw_flag = FTW_F;
136                 break;
137 
138             case FTS_SL:
139                 ftw_flag = FTW_SL;
140                 break;
141 
142             case FTS_NS:
143                 ftw_flag = FTW_NS;
144                 break;
145 
146             /* Values that should only occur in nftw mode */
147 
148             case FTS_SLNONE:
149                 if (MODE_NFTW == mode) {
150                     ftw_flag = FTW_SLN;
151                 } else {
152                     ftw_flag = FTW_SL;
153                 }
154                 break;
155 
156             case FTS_DP:
157                 if ((MODE_NFTW == mode) && (flags & FTW_DEPTH)) {
158                     ftw_flag = FTW_D;
159                 }
160                 break;
161 
162             default:
163                 /* I'm not sure this is right, but we don't have a valid FTW
164                  * type to call with, so cowardice seems the better part of
165                  * guessing.
166                  */
167 		break;
168         }
169 
170         if (MODE_FTW == mode) {
171             ftw_func = (__ftw_func_t) func;
172             func_ret = (*ftw_func)
173                 (entry->fts_path, entry->fts_statp, ftw_flag);
174         } else if (MODE_NFTW == mode) {
175             ftw_st.base = (entry->fts_pathlen - entry->fts_namelen);
176             ftw_st.level = entry->fts_level;
177 
178             nftw_func = (__nftw_func_t) func;
179             func_ret = (*nftw_func)
180                 (entry->fts_path, entry->fts_statp, ftw_flag, &ftw_st);
181         }
182 
183         if (0 != func_ret) {
184 	    saved_errno = errno;
185 	    fts_close(hierarchy);
186 	    errno = saved_errno;
187             return func_ret;
188         }
189     }
190 
191     /* The janitors will be upset if we don't clean up after ourselves. */
192 
193     saved_errno = errno;
194     fts_close(hierarchy);
195     if (0 != saved_errno) { /* fts_read returned NULL, and set errno - bail */
196 	errno = saved_errno;
197     }
198 
199     return errno ? -1 : 0;
200 }
201