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