recursive operation for PBD::PathScanner, backported from 2.X
[ardour.git] / libs / pbd / pathscanner.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <cstdlib>
22 #include <cstdio>
23 #include <cstring>
24 #include <vector>
25 #include <dirent.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28
29 #include <pbd/error.h>
30 #include <pbd/pathscanner.h>
31 #include <pbd/stl_delete.h>
32
33 using namespace PBD;
34
35 vector<string *> *
36 PathScanner::operator() (const string &dirpath, const string &regexp,
37                          bool match_fullpath, bool return_fullpath, 
38                          long limit, bool recurse)
39
40 {
41         int err;
42         char msg[256];
43
44         if ((err = regcomp (&compiled_pattern, regexp.c_str(),
45                             REG_EXTENDED|REG_NOSUB))) {
46                 
47                 regerror (err, &compiled_pattern,
48                           msg, sizeof (msg));
49                 
50                 error << "Cannot compile soundfile regexp for use (" 
51                       << msg 
52                       << ")" 
53                       << endmsg;
54                 
55                 return 0;
56         }
57         
58         return run_scan (dirpath, &PathScanner::regexp_filter, 
59                          (bool (*)(const string &, void *)) 0,
60                          0,
61                          match_fullpath,
62                          return_fullpath,
63                          limit, recurse);
64 }       
65
66 vector<string *> *
67 PathScanner::run_scan (const string &dirpath, 
68                        bool (PathScanner::*memberfilter)(const string &),
69                        bool (*filter)(const string &, void *),
70                        void *arg,
71                        bool match_fullpath, bool return_fullpath,
72                        long limit,
73                        bool recurse)
74 {
75         return run_scan_internal ((vector<string*>*) 0, dirpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
76 }
77         
78 vector<string *> *
79 PathScanner::run_scan_internal (vector<string *> *result,
80                                 const string &dirpath, 
81                                 bool (PathScanner::*memberfilter)(const string &),
82                                 bool (*filter)(const string &, void *),
83                                 void *arg,
84                                 bool match_fullpath, bool return_fullpath,
85                                 long limit,
86                                 bool recurse)
87 {
88         DIR *dir;
89         struct dirent *finfo;
90         char *pathcopy = strdup (dirpath.c_str());
91         char *thisdir;
92         char fullpath[PATH_MAX+1];
93         string search_str;
94         string *newstr;
95         long nfound = 0;
96
97         if ((thisdir = strtok (pathcopy, ":")) == 0 ||
98             strlen (thisdir) == 0) {
99                 free (pathcopy);
100                 return 0;
101         }
102
103         if (result == 0) {
104                 result = new vector<string *>;
105         }
106
107         do {
108
109                 if ((dir = opendir (thisdir)) == 0) {
110                         continue;
111                 }
112                 
113                 while ((finfo = readdir (dir)) != 0) {
114
115                         if ((finfo->d_name[0] == '.' && finfo->d_name[1] == '\0') ||
116                             (finfo->d_name[0] == '.' && finfo->d_name[1] == '.' && finfo->d_name[2] == '\0')) {
117                                 continue;
118                         }
119
120                         snprintf (fullpath, sizeof(fullpath), "%s/%s",
121                                   thisdir, finfo->d_name);
122
123                         struct stat statbuf;
124                         if (stat (fullpath, &statbuf) < 0) {
125                                 continue;
126                         }
127
128                         if (statbuf.st_mode & S_IFDIR && recurse) {
129                                 run_scan_internal (result, fullpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
130                         } else {
131                                 
132                                 if (match_fullpath) {
133                                         search_str = fullpath;
134                                 } else {
135                                         search_str = finfo->d_name;
136                                 }
137                                 
138                                 /* handle either type of function ptr */
139                                 
140                                 if (memberfilter) {
141                                         if (!(this->*memberfilter)(search_str)) {
142                                                 continue;
143                                         } 
144                                 } else {
145                                         if (!filter(search_str, arg)) {
146                                                 continue;
147                                         }
148                                 }
149                                 
150                                 if (return_fullpath) {
151                                         newstr = new string (fullpath);
152                                 } else {
153                                         newstr = new string (finfo->d_name);
154                                 } 
155                                 
156                                 result->push_back (newstr);
157                                 nfound++;
158                         }
159                 }
160                 closedir (dir);
161                 
162         } while ((limit < 0 || (nfound < limit)) && (thisdir = strtok (0, ":")));
163
164         free (pathcopy);
165         return result;
166 }
167
168 string *
169 PathScanner::find_first (const string &dirpath,
170                          const string &regexp,
171                          bool match_fullpath,
172                          bool return_fullpath)
173 {
174         vector<string *> *res;
175         string *ret;
176         int err;
177         char msg[256];
178
179         if ((err = regcomp (&compiled_pattern, regexp.c_str(),
180                             REG_EXTENDED|REG_NOSUB))) {
181                 
182                 regerror (err, &compiled_pattern,
183                           msg, sizeof (msg));
184                 
185                 error << "Cannot compile soundfile regexp for use (" << msg << ")" << endmsg;
186
187                 
188                 return 0;
189         }
190         
191         res = run_scan (dirpath, 
192                         &PathScanner::regexp_filter,
193                         (bool (*)(const string &, void *)) 0,
194                         0,
195                         match_fullpath,
196                         return_fullpath, 
197                         1);
198         
199         if (res->size() == 0) {
200                 ret = 0;
201         } else {
202                 ret = res->front();
203         }
204         vector_delete (res);
205         delete res;
206         return ret;
207 }
208
209 string *
210 PathScanner::find_first (const string &dirpath,
211                          bool (*filter)(const string &, void *),
212                          void *arg,
213                          bool match_fullpath,
214                          bool return_fullpath)
215 {
216         vector<string *> *res;
217         string *ret;
218
219         res = run_scan (dirpath, 
220                         (bool (PathScanner::*)(const string &)) 0,
221                         filter,
222                         0,
223                         match_fullpath,
224                         return_fullpath, 1);
225         
226         if (res->size() == 0) {
227                 ret = 0;
228         } else {
229                 ret = res->front();
230         }
231         vector_delete (res);
232         delete res;
233         return ret;
234 }