29c339c0879c3f0f2e32a5eeacc3d883f27de849
[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 #ifdef COMPILER_MSVC
22 #include <stdlib.h>
23 #include <stdio.h>
24 using PBD::readdir;
25 using PBD::opendir;
26 using PBD::closedir;
27 #define strtok_r strtok_s // @john: this should probably go to msvc_extra_headers/ardourext/misc.h.input instead of the current define there
28 #else
29 #include <dirent.h>
30 #include <cstdlib>
31 #include <cstdio>
32 #endif
33 #include <cstring>
34 #include <vector>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <glibmm/miscutils.h>
39
40 #include "pbd/error.h"
41 #include "pbd/pathexpand.h"
42 #include "pbd/pathscanner.h"
43
44 using namespace std;
45 using namespace PBD;
46
47 static
48 bool
49 regexp_filter (const string& str, void *arg)
50 {
51         regex_t* pattern = (regex_t*)arg;
52         return regexec (pattern, str.c_str(), 0, 0, 0) == 0;
53 }
54
55 void
56 PathScanner::find_files_matching_regex (vector<string>& result,
57                 const std::string& dirpath,
58                 const std::string& regexp,
59                 bool match_fullpath, bool return_fullpath,
60                 long limit,
61                 bool recurse)
62 {
63         int err;
64         char msg[256];
65         regex_t compiled_pattern;
66
67         if ((err = regcomp (&compiled_pattern, regexp.c_str(),
68                             REG_EXTENDED|REG_NOSUB))) {
69                 
70                 regerror (err, &compiled_pattern,
71                           msg, sizeof (msg));
72                 
73                 error << "Cannot compile soundfile regexp for use (" 
74                       << msg 
75                       << ")" 
76                       << endmsg;
77                 
78                 return;
79         }
80
81         run_scan_internal (result, dirpath,
82                            regexp_filter, &compiled_pattern,
83                            match_fullpath, return_fullpath,
84                            limit, recurse);
85
86         regfree (&compiled_pattern);
87 }
88
89 vector<string>
90 PathScanner::operator() (const string &dirpath, const string &regexp,
91                          bool match_fullpath, bool return_fullpath,
92                          long limit, bool recurse)
93
94 {
95         vector<string> result;
96
97         find_files_matching_regex (result,
98                         dirpath,
99                         regexp,
100                         match_fullpath,
101                         return_fullpath,
102                         limit, recurse);
103
104         return result;
105 }       
106         
107 void
108 PathScanner::run_scan_internal (vector<string>& result,
109                                 const string &dirpath, 
110                                 bool (*filter)(const string &, void *),
111                                 void *arg,
112                                 bool match_fullpath, bool return_fullpath,
113                                 long limit,
114                                 bool recurse)
115 {
116         DIR *dir;
117         struct dirent *finfo;
118         char *pathcopy = strdup (search_path_expand (dirpath).c_str());
119         char *thisdir;
120         string fullpath;
121         string search_str;
122         long nfound = 0;
123         char *saveptr;
124
125         if ((thisdir = strtok_r (pathcopy, G_SEARCHPATH_SEPARATOR_S, &saveptr)) == 0 ||
126             strlen (thisdir) == 0) {
127                 free (pathcopy);
128                 return;
129         }
130
131         do {
132
133                 if ((dir = opendir (thisdir)) == 0) {
134                         continue;
135                 }
136                 
137                 while ((finfo = readdir (dir)) != 0) {
138
139                         if ((finfo->d_name[0] == '.' && finfo->d_name[1] == '\0') ||
140                             (finfo->d_name[0] == '.' && finfo->d_name[1] == '.' && finfo->d_name[2] == '\0')) {
141                                 continue;
142                         }
143                         
144                         fullpath = Glib::build_filename (thisdir, finfo->d_name);
145
146                         struct stat statbuf;
147                         if (stat (fullpath.c_str(), &statbuf) < 0) {
148                                 continue;
149                         }
150
151                         if (statbuf.st_mode & S_IFDIR && recurse) {
152                                 run_scan_internal (result, fullpath, filter, arg, match_fullpath, return_fullpath, limit, recurse);
153                         } else {
154                                 
155                                 if (match_fullpath) {
156                                         search_str = fullpath;
157                                 } else {
158                                         search_str = finfo->d_name;
159                                 }
160                                 
161                                 if (!filter(search_str, arg)) {
162                                         continue;
163                                 }
164
165                                 if (return_fullpath) {
166                                         result.push_back(fullpath);
167                                 } else {
168                                         result.push_back(finfo->d_name);
169                                 } 
170                                 
171                                 nfound++;
172                         }
173                 }
174                 closedir (dir);
175                 
176         } while ((limit < 0 || (nfound < limit)) && (thisdir = strtok_r (0, G_SEARCHPATH_SEPARATOR_S, &saveptr)));
177
178         free (pathcopy);
179         return;
180 }
181
182 string
183 PathScanner::find_first (const string &dirpath,
184                          const string &regexp,
185                          bool match_fullpath,
186                          bool return_fullpath)
187 {
188         vector<string> res;
189
190         find_files_matching_regex (res, dirpath, regexp,
191                                    match_fullpath, return_fullpath, 1);
192         
193         if (res.size() == 0) {
194                 return string();
195         }
196
197         return res.front();
198 }
199
200 string
201 PathScanner::find_first (const string &dirpath,
202                          bool (*filter)(const string &, void *),
203                          void * /*arg*/,
204                          bool match_fullpath,
205                          bool return_fullpath)
206 {
207         vector<string> res;
208         string ret;
209
210         run_scan_internal (res,
211                            dirpath,
212                            filter,
213                            0,
214                            match_fullpath,
215                            return_fullpath, 1);
216         
217         if (res.size() == 0) {
218                 return string();
219         }
220
221         return res.front();
222 }