38
38
***********************************************************************/
39
39
40
40
#include <stdio.h>
41
+ #include <stdlib.h>
41
42
#include <string.h>
42
43
#include <unistd.h>
43
44
#include <sys/types.h>
44
45
#include <sys/stat.h>
45
46
#include <fcntl.h>
46
47
#include <dirent.h>
47
48
#include <errno.h>
49
+ #include <glob.h>
48
50
49
51
#include "reb-host.h"
50
52
#include "host-lib.h"
@@ -191,21 +193,25 @@ static int Get_File_Info(REBREQ *file)
191
193
**
192
194
***********************************************************************/
193
195
{
194
- struct stat info ;
195
196
struct dirent * d ;
196
197
char * cp ;
197
198
DIR * h ;
198
- int n ;
199
-
200
- // Remove * from tail, if present. (Allowed because the
201
- // path was copied into to-local-path first).
202
- n = strlen (cp = dir -> file .path );
203
- if (n > 0 && cp [n - 1 ] == '*' ) cp [n - 1 ] = 0 ;
199
+ int len , n = 0 ;
204
200
205
201
// If no dir handle, open the dir:
206
202
if (!(h = dir -> handle )) {
203
+ // Remove * from tail, if present. (Allowed because the
204
+ // path was copied into to-local-path first).
205
+ len = strlen ((cp = dir -> file .path ));
206
+ if (len > 0 && cp [len - 1 ] == '*' ) {
207
+ // keep track that we removed *
208
+ n = len - 1 ;
209
+ cp [n ] = 0 ;
210
+ }
207
211
h = opendir (dir -> file .path );
208
212
if (!h ) {
213
+ // revert back the * char as it may be part of pattern matching
214
+ if (n > 0 ) cp [n ] = '*' ;
209
215
dir -> error = errno ;
210
216
return DR_ERROR ;
211
217
}
@@ -237,6 +243,8 @@ static int Get_File_Info(REBREQ *file)
237
243
// most efficient, because it does not require a separate
238
244
// file system call for determining directories.
239
245
if (d -> d_type == DT_DIR ) SET_FLAG (file -> modes , RFM_DIR );
246
+ // NOTE: DT_DIR may be enabled using _BSD_SOURCE define
247
+ // https://stackoverflow.com/a/9241608/494472
240
248
#else
241
249
if (Is_Dir (dir -> file .path , file -> file .path )) SET_FLAG (file -> modes , RFM_DIR );
242
250
#endif
@@ -248,6 +256,85 @@ static int Get_File_Info(REBREQ *file)
248
256
}
249
257
250
258
259
+ /***********************************************************************
260
+ **
261
+ */ static int Read_Pattern (REBREQ * dir , REBREQ * file )
262
+ /*
263
+ ** This function will read a file with wildcards, one file entry
264
+ ** at a time, then close when no more files are found.
265
+ **
266
+ ** Although GLOB allows to pass patterns which match content
267
+ ** thru multiple directories, that is intentionally disabled,
268
+ ** because such a functionality would not be easy to implement
269
+ ** and because result would have to be full path, which also
270
+ ** may not be the best choice from user's view.
271
+ **
272
+ ** Actually the result is truncated so only files are returned and
273
+ ** not complete paths.
274
+ **
275
+ ***********************************************************************/
276
+ {
277
+ char * cp ;
278
+ glob_t * g ;
279
+ int n , p , end ;
280
+ int wld = -1 ;
281
+
282
+ if (!(g = dir -> handle )) {
283
+ //printf("init pattern: %s\n", dir->file.path);
284
+
285
+ n = strlen ((cp = dir -> file .path ));
286
+ for (p = 0 ; p < n ; p ++ ) {
287
+ if (cp [p ] == '/' ) {
288
+ // store position of the directory separator
289
+ end = p ;
290
+ if (wld > 0 ) {
291
+ // don't support wildcards thru multiple directories
292
+ // like: %../?/?.png
293
+ // as this is not available on Windows
294
+
295
+ //puts("Not supported pattern!");
296
+ dir -> error = - GLOB_NOMATCH ; // result will be []
297
+ return DR_ERROR ;
298
+ }
299
+ }
300
+ else if (cp [p ] == '*' || cp [p ] == '?' ) wld = p ;
301
+ }
302
+ // keep position of the last directory separator so it can be used
303
+ // to limit result into just a file and not a full path
304
+ dir -> clen = end + 1 ;
305
+
306
+ g = MAKE_NEW (glob_t ); // deallocate once done!
307
+ n = glob (dir -> file .path , GLOB_MARK , NULL , g );
308
+ if (n ) {
309
+ //printf("glob: %s err: %i errno: %i\n", dir->file.path, n, errno);
310
+ globfree (g );
311
+ OS_Free (g );
312
+ dir -> error = - n ; // using negative number as on Windows
313
+ return DR_ERROR ;
314
+ }
315
+ //printf("found patterns: %li\n", g->gl_pathc);
316
+ // all patterns are already in the glob buffer,
317
+ // but we will not report them all at once
318
+ dir -> handle = g ;
319
+ dir -> actual = 0 ;
320
+ dir -> length = g -> gl_pathc ;
321
+ dir -> modes = 1 << RFM_PATTERN ; // changing mode from RFM_DIR to RFM_PATTERN
322
+ CLR_FLAG (dir -> flags , RRF_DONE );
323
+ }
324
+ if (dir -> actual >= g -> gl_pathc ) {
325
+ globfree (g );
326
+ OS_Free (g );
327
+ SET_FLAG (dir -> flags , RRF_DONE ); // no more files
328
+ return DR_DONE ;
329
+ }
330
+ //printf("path[%i]: %s\n", dir->actual, g->gl_pathv[dir->actual]);
331
+ file -> modes = 0 ;
332
+ //TODO: assert if: 0 <= dir->clen <= MAX_FILE_NAME ???
333
+ // only file part is returned...
334
+ COPY_BYTES (file -> file .path , g -> gl_pathv [dir -> actual ++ ] + dir -> clen , MAX_FILE_NAME - dir -> clen );
335
+ return DR_DONE ;
336
+ }
337
+
251
338
/***********************************************************************
252
339
**
253
340
*/ DEVICE_CMD Open_File (REBREQ * file )
@@ -355,7 +442,15 @@ static int Get_File_Info(REBREQ *file)
355
442
ssize_t num_bytes ;
356
443
357
444
if (GET_FLAG (file -> modes , RFM_DIR )) {
358
- return Read_Directory (file , (REBREQ * )file -> data );
445
+ int ret = Read_Directory (file , (REBREQ * )file -> data );
446
+ // If there is no id yet and reading failed, we will
447
+ // try to use file as a pattern...
448
+ if (ret == DR_ERROR && !file -> id ) goto init_pattern ;
449
+ return ret ;
450
+ }
451
+ else if (GET_FLAG (file -> modes , RFM_PATTERN )) {
452
+ init_pattern :
453
+ return Read_Pattern (file , (REBREQ * )file -> data );
359
454
}
360
455
361
456
if (!file -> id ) {
0 commit comments