1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /**
20  * \file listdir.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao
24  *  All rights reserved.
25  *
26  *  Read a directory. Hightly system dependent.
27  *  Seems opendir/readdir/closedir is the most portable.
28  *
29  *  Original BSD scandir is emulated using the opendir stuff.
30  *
31  *  This file has no xforms dependencies
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <limits.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44 
45 #ifndef _WIN32
46 #include <unistd.h>
47 #else
48 #include <io.h>
49 #include <time.h>
50 #include <direct.h>
51 #define FL_WIN32
52 #endif
53 
54 /* vms 7.0 has dirent.h */
55 
56 #if defined(__VMS) && __VMS_VER < 70000000
57 #include "dirent_vms.h"
58 #endif
59 
60 /* Work around the bugs in various cpp */
61 
62 #if defined Lynx && ! defined __VMS
63 #include <dir.h>
64 #include <dirent.h>
65 #endif
66 
67 #if ! defined __VMS  && ! defined Lynx && ! defined FL_WIN32
68 #include <dirent.h>
69 #endif
70 
71 #include <sys/stat.h>
72 #include "include/forms.h"      /* for definitation of FL_EXPORT */
73 #include "flinternal.h"
74 #include "local.h"
75 #include "ulib.h"
76 
77 
78 
79 /******** limits and macros *********/
80 
81 #ifndef S_ISREG
82 #define S_ISREG( m )      ( ( ( m ) & S_IFREG ) == S_IFREG )
83 #define S_ISDIR( m )      ( ( ( m ) & S_IFDIR ) == S_IFDIR )
84 #endif
85 
86 #if defined (__EMX__) || defined (FL_WIN32) || defined (opennt)
87 #define S_ISLNK( m )   0        /* no links in OS/2      */
88 #define S_ISBLB( m )   0        /* no blk devices in OS2 */
89 #else
90 #ifndef S_ISLNK
91 #define S_ISLNK( m )   ( ( ( m ) & S_IFLNK ) == S_IFLNK )
92 #endif
93 #ifndef S_ISBLK
94 #define S_ISBLK( m )   ( ( ( m ) & S_IFBLK ) == S_IFBLK )
95 #endif
96 #endif /* __EMX__ */
97 
98 #ifndef S_ISCHR
99 #define S_ISCHR( m )   ( ( ( m ) & S_IFCHR ) == S_IFCHR )
100 #endif
101 
102 #ifndef S_ISSOCK
103 # if defined( S_IFSOCK )
104 #   ifdef S_IFMT
105 #     define S_ISSOCK( m ) ( ( ( m ) & S_IFMT ) == S_IFSOCK )
106 #   else
107 #     define S_ISSOCK( m ) ( ( m ) & S_IFSOCK )
108 #   endif /* S_IFMT */
109 # elif defined( S_ISNAM )
110     /* SCO OpenServer 5.0.7 */
111 #   define S_ISSOCK S_ISNAM
112 # endif /* !S_IFSOCK && S_ISNAM */
113 #endif /* !S_ISSOCK */
114 
115 #define MAXCACHE 10                       /* upto MAXCACHE dir will be cached */
116 #define MAXFL    ( FL_PATH_MAX + FL_FLEN )/* maximum file length */
117 
118 #ifndef StrReDup
119 #define StrReDup( a, b )  do                       \
120                           {                        \
121                               if( a )              \
122                                   fl_free( a );    \
123                               a = fl_strdup( b );  \
124                           } while( 0 )
125 #endif
126 
127 #if defined NEED_DIRECT
128 #define DIRENT direct
129 #else
130 #define DIRENT dirent
131 #endif
132 
133 #if defined NEED_GETCWD
134 #if defined Lynx
135 
136 /***************************************
137  ***************************************/
138 
139 char *
getcwd(char * a,int b)140 getcwd( char * a,
141         int    b )
142 {
143     return getwd( a );
144 }
145 
146 #endif
147 #if defined __VMS
148 #define getcwd( a, b )    getcwd( a, b, 0 ) /* use unix file/path syntax */
149 #endif
150 #endif
151 
152 #ifdef FL_WIN32
153 
154 /***************************************
155  * Convert the backslash to slash
156  ***************************************/
157 
158 static char *
fl_b2f_slash(char * dir)159 fl_b2f_slash( char *dir )
160 {
161     char *p = dir;
162 
163     while ( ( p = strchr( p, '\\' ) ) )
164         *p = '/';
165 
166     return dir;
167 }
168 
169 #else
170 #define fl_b2f_slash( a )
171 #endif
172 
173 /************* local variables ****************/
174 
175 static const char *cpat;    /* current pattern          */
176 static const char *cdir;    /* current working directory */
177 static char fname[ MAXFL + 2 ];
178 
179 #define FL_NONE 0
180 
181 /******* local function forward dec **********/
182 
183 static int fli_wildmat( const char *,
184                         const char * );
185 static int tc_sort( const void *,
186                     const void * );
187 static int tc_scandir( const char *,
188                        struct DIRENT *** );
189 
190 static int default_filter( const char *,
191                            int );
192 
193 /* default filter and sort method */
194 
195 int fli_sort_method = FL_ALPHASORT;
196 static FL_DIRLIST_FILTER ffilter = default_filter;
197 static int filter_directory = 0;   /* true to filter directory entries */
198 
199 /*
200  * convert native file types to FL
201  */
202 
203 #ifndef FL_WIN32        /* [  */
204 
205 
206 /***************************************
207  ***************************************/
208 
209 static void
mode2type(unsigned int mode,int * type)210 mode2type( unsigned int   mode,
211            int          * type )
212 {
213     if ( S_ISDIR( mode ) )
214         *type = FT_DIR;
215     else if ( S_ISREG( mode ) )
216         *type = FT_FILE;
217     else if ( S_ISLNK( mode ) )
218         *type = FT_LINK;
219     else if ( S_ISSOCK(mode ) )
220         *type = FT_SOCK;
221     else if ( S_ISFIFO( mode ) )
222         *type = FT_FIFO;
223     else if ( S_ISCHR( mode ) )
224         *type = FT_CHR;
225 #if !defined __EMX__
226     else if ( S_ISBLK( mode ) )
227         *type = FT_BLK;
228 #endif
229     else
230         *type = FT_OTHER;
231 }
232 
233 
234 /******************************************************************
235  * Filter the filename before handing it over to the "file is here"
236  * list. Per default only files (including links) are shown.
237  ******************************************************************/
238 
239 static int
fselect(const char * d_name,struct stat * ffstat,int * type)240 fselect( const char  * d_name,
241          struct stat * ffstat,
242          int         * type )
243 {
244     int ret = 0;
245     unsigned int mode;
246 
247     strcat( strcpy( fname, cdir), d_name );
248     stat( fname, ffstat );
249     mode = ffstat->st_mode;
250     mode2type( mode, type );
251 
252     if ( ! ffilter )
253         ret = 1;
254     else if ( ffilter == default_filter )
255     {
256         /* Always keep directory and links */
257 
258         ret =    S_ISDIR(mode)
259               || (    ( S_ISREG( mode ) || S_ISLNK( mode ) )
260                    && fli_wildmat( d_name, cpat ) );
261     }
262     else
263     {
264         /* We don't filter directories unless requested */
265 
266         if ( ! filter_directory )
267            ret =    *type == FT_DIR
268                  || ( fli_wildmat( d_name, cpat ) && ffilter( fname, *type ) );
269         else
270             ret =    ( *type == FT_DIR || fli_wildmat( d_name, cpat ) )
271                   && ffilter( fname, *type );
272     }
273 
274     return ret;
275 }
276 
277 #else /* WIN 32 ][ */
278 
279 /***************************************
280  ***************************************/
281 
282 static int
fselect(struct _finddata_t * c_file,FL_Dirlist * dl)283 fselect( struct _finddata_t * c_file,
284          FL_Dirlist         * dl )
285 {
286     int type,
287         ret = 0;
288 
289     if ( c_file->attrib & _A_SUBDIR )
290         type = FT_DIR;
291     else
292         type = FT_FILE;
293 
294     if ( ! ffilter )
295         ret = 1;
296     else if ( ffilter == default_filter ) /* always keep directory and links */
297         ret = type == FT_DIR || fli_wildmat( c_file->name, cpat );
298     else
299     {
300         strcat( strcpy( fname, cdir ), c_file->name );
301         ret =    type == FT_DIR
302               || (    flo_wildmat( c_file->name, cpat )
303                    && ffilter( fname, type ) );
304     }
305 
306     if ( ret )
307     {
308         dl->name = fl_strdup( c_file->name );
309         dl->type = type;
310         dl->dl_mtime = c_file->time_write;
311         dl->dl_size = c_file->size;
312     }
313     return ret;
314 }
315 
316 #endif /* FL_WIN32 ] */
317 
318 
319 /***************************************
320  * Sort the directory entries according to the sort method
321  ***************************************/
322 
323 static int
tc_sort(const void * a,const void * b)324 tc_sort( const void * a,
325          const void * b )
326 {
327     FL_Dirlist *da = ( FL_Dirlist * ) a;
328     FL_Dirlist *db = ( FL_Dirlist * ) b;
329 
330     switch ( fli_sort_method )
331     {
332         case FL_RALPHASORT:
333             return strcmp( db->name, da->name );
334 
335         case FL_RCASEALPHASORT:
336             return strcasecmp( db->name, da->name );
337 
338         case FL_MTIMESORT:
339             return da->dl_mtime - db->dl_mtime;
340 
341         case FL_RMTIMESORT:
342             return db->dl_mtime - da->dl_mtime;
343 
344         case FL_SIZESORT:
345             return da->dl_size > db->dl_size ?
346                    1 : ( da->dl_size == db->dl_size ? 0 : -1 );
347 
348         case FL_RSIZESORT:
349             return da->dl_size < db->dl_size ?
350                    1 : ( da->dl_size == db->dl_size ? 0 : -1 );
351 
352         case FL_CASEALPHASORT:
353             return strcasecmp( da->name, db->name );
354 
355         case FL_ALPHASORT:
356         default:
357             return strcmp( da->name, db->name );
358     }
359 }
360 
361 
362 /*******************************************************************
363  * On entry, dir must be no zero and be terminated properly, i.e.,
364  * ends with /
365  *******************************************************************/
366 
367 #ifndef FL_WIN32
368 
369 static int
scandir_get_entries(const char * dir,const char * pat,FL_Dirlist ** dirlist)370 scandir_get_entries( const char  * dir,
371                      const char  * pat,
372                      FL_Dirlist ** dirlist )
373 {
374     static struct DIRENT **dlist;
375     FL_Dirlist *dl;
376     static int lastn;
377     static struct stat ffstat;
378     int n = 0;
379 
380     cpat = pat;
381     cdir = dir;
382 
383     /* Free all memory used last time we were here */
384 
385     if ( dlist )
386     {
387         while ( --lastn >= 0 )
388             if ( dlist[ lastn ] )
389                 fl_free( dlist[ lastn ]);
390         fl_free( dlist );
391         dlist = NULL;
392     }
393 
394     if ( ( lastn = tc_scandir( dir, &dlist ) ) > 0 )
395     {
396         int i;
397 
398         dl = *dirlist = fl_malloc( ( lastn + 1 ) * sizeof **dirlist );
399 
400         for ( i = n = 0; i < lastn; i++ )
401         {
402             if ( fselect( dlist[ i ]->d_name, &ffstat, &dl->type ) )
403             {
404                 dl->name = fl_strdup( dlist[ i ]->d_name );
405                 dl->dl_mtime = ffstat.st_mtime;
406                 dl->dl_size = ffstat.st_size;
407                 dl++;
408                 n++;
409             }
410         }
411 
412         dl->name = NULL;        /* sentinel */
413 
414         if ( fli_sort_method != FL_NONE )
415             qsort( *dirlist, n, sizeof **dirlist, tc_sort );
416     }
417 
418     return n;
419 }
420 
421 #else /* FL_WIN32 */
422 
423 /***************************************
424  ***************************************/
425 
426 static int
scandir_get_entries(const char * dir,const char * pat,FL_Dirlist ** dirlist)427 scandir_get_entries( const char  * dir,
428                      const char  * pat,
429                      FL_Dirlist ** dirlist )
430 {
431     FL_Dirlist *dl;
432     char cwd[ FL_PATH_MAX ];
433     struct _finddata_t c_file;
434     long hFile;
435     int n, lastn;
436 
437     cpat = pat;
438     cdir = dir;
439     n = 0;
440 
441     /* Save the working directory */
442 
443     getcwd( cwd, FL_PATH_MAX );
444     if ( chdir( dir ) != 0 )    /* invalid directory */
445         return 0;
446 
447     /* Find all files matched the pattern in current directory */
448 
449     if ( ( hFile = _findfirst( "*", &c_file ) ) == -1L )
450     {
451         /* Directory is empty, nothing to do */
452 
453         chdir( cwd );
454         return 0;
455     }
456 
457     lastn = 10;
458     dl = *dirlist = fl_malloc( ( lastn + 1 ) * sizeof **dirlist );
459 
460     if ( fselect( &c_file, dl ) )
461     {
462         dl++;
463         n++;
464     }
465 
466     /* Find the rest of the files */
467 
468     while ( _findnext( hFile, &c_file ) == 0 )
469     {
470         if ( fselect( &c_file, dl ) )
471         {
472             dl++;
473             n++;
474             if ( n > lastn )
475             {
476                 lastn += 10;
477                 *dirlist = fl_realloc( *dirlist,
478                                        ( lastn + 1 ) * sizeof **dirlist );
479                 dl = *dirlist + n;
480             }
481         }
482     }
483 
484     _findclose( hFile );
485     chdir( cwd );
486 
487     dl->name = NULL;        /* sentinel */
488 
489     if ( fli_sort_method != FL_NONE )
490         qsort( *dirlist, n, sizeof **dirlist, tc_sort );
491 
492     return n;
493 }
494 
495 #endif /* FL_WIN32 */
496 
497 
498 /********************************************************************
499  * The user routine.
500  *
501  * Get a list of files in directory, subject to pattern matching,
502  * and return the file list. Rescan will force a read even the requested
503  * directory is cached.
504  *
505  ********************************************************************/
506 
507 static char *lastdir[ MAXCACHE ],
508             *lastpat[ MAXCACHE ];
509 static int lastn[ MAXCACHE ],
510            last_sort[ MAXCACHE ];
511 static FL_Dirlist *dirlist[ MAXCACHE ];
512 
513 
514 /********************************************************************
515  * Check if a particular directory is cached
516  ********************************************************************/
517 
518 static int
is_cached(const char * dir,const char * pat,int * c)519 is_cached( const char * dir,
520            const char * pat,
521            int        * c )
522 {
523     int cached = 0,
524         i = 0;
525     static int lastcache;
526 
527     do
528     {
529         cached =    lastpat[ i ]
530                  && lastdir[ i ]
531                  && strcmp( lastdir[ i ], dir ) == 0
532                  && strcmp( lastpat[ i ], pat ) == 0
533                  && dirlist[ i ]
534                  && dirlist[ i ]->name;
535         *c = i++;
536     }
537     while ( ! cached && i < MAXCACHE );
538 
539     /* search for the least used slot if not cached */
540 
541     if ( ! cached )
542         *c = ++lastcache % MAXCACHE;
543 
544     lastcache = *c;
545     M_info( "is_cached", "%s is %s cached", dir, cached ? "" : "not" );
546     return cached;
547 }
548 
549 
550 /***************************************
551  ***************************************/
552 
553 void
fl_free_dirlist(FL_Dirlist * dl)554 fl_free_dirlist( FL_Dirlist * dl )
555 {
556     size_t i;
557 
558     for ( i = 0; i < MAXCACHE; i++ )
559         if ( dl == dirlist[ i ] )
560             break;
561 
562     if ( i >= MAXCACHE )
563     {
564         M_err( "fl_free_dirlist", "Bad list" );
565         return;
566     }
567 
568     while ( dl && dl->name )
569     {
570         fl_free( dl->name );
571         dl->name = NULL;        /* important: signifies empty list */
572         dl++;
573     }
574 
575     if ( dirlist[ i ] )
576     {
577         fl_free( dirlist[ i ] );
578         dirlist[ i ] = NULL;
579     }
580 }
581 
582 
583 /**********************************************************************
584  * The user callable routine to read a directory
585  *********************************************************************/
586 
587 const FL_Dirlist *
fl_get_dirlist(const char * dir,const char * pattern,int * n,int rescan)588 fl_get_dirlist( const char * dir,
589                 const char * pattern,
590                 int        * n,
591                 int          rescan )
592 {
593     int i,
594         c;
595     const char *pat = pattern;
596     char okdir[ FL_PATH_MAX + 1 ];
597 
598     if ( ! dir || ! *dir )
599         return NULL;
600 
601     if ( ! pat || ! *pat )
602         pat = "*";
603 
604     /* Fix the directory on the fly */
605 
606     i = strlen( strcpy( okdir, dir ) );
607     if ( okdir[ i - 1 ] != '/' )
608     {
609         okdir[ i ] = '/';
610         okdir[ ++i ] = '\0';
611     }
612 
613     /* is_cached must go first to get correct cache location */
614 
615     if ( ! is_cached( okdir, pat, &c ) || rescan )
616     {
617         fl_free_dirlist( dirlist[ c ] );
618         lastn[ c ] = scandir_get_entries( okdir, pat, dirlist + c );
619         last_sort[ c ] = fli_sort_method;
620         StrReDup( lastpat[ c ], pat );
621         StrReDup( lastdir[ c ], okdir );
622     }
623 
624     *n = lastn[ c ];
625     if ( last_sort[ c ] != fli_sort_method )
626     {
627         qsort( dirlist[ c ], *n, sizeof **dirlist, tc_sort );
628         last_sort[ c ] = fli_sort_method;
629     }
630 
631     return dirlist[ c ];
632 }
633 
634 
635 /***********************************************************************
636  * Misc. routines related to directory handling
637  **********************************************************************/
638 
639 int
fli_is_valid_dir(const char * name)640 fli_is_valid_dir( const char *name )
641 {
642     struct stat stbuf;
643 
644 #ifdef __EMX__
645     if (    name
646          && isalpha( ( unsigned char ) name[ 0 ] )
647          && name[ 1 ] == ':'
648          && name[ 2 ] == '0' )
649         return 1;
650 #endif
651 
652     /* On some machines name should be a plain char * (why? JTT) */
653 
654     return    name
655            && *name
656            && ! stat( ( char * ) name, &stbuf )
657            && S_ISDIR( stbuf.st_mode );
658 }
659 
660 
661 /***************************************
662  * Fix directory names such as ../../a/b etc
663  ***************************************/
664 
665 static void add_one( char *,
666                      char * );
667 
668 char *
fli_fix_dirname(char * dir)669 fli_fix_dirname( char * dir )
670 {
671     static char ldir[ FL_PATH_MAX ],
672                 one[ FL_PATH_MAX ];
673     char *p = ldir,
674          *q = one;
675 
676     fl_b2f_slash( dir );
677 
678     if ( ! *dir )    /* Here's some bullshit going one, what's ldir set to ? */
679         return fli_getcwd( dir ? dir : ldir, FL_PATH_MAX - 2 );
680     else if ( dir[ 0 ] == '.' && dir[ 1 ] == '.' && dir[ 2 ] == '\0' )
681     {
682         fli_getcwd( dir ? dir : ldir, FL_PATH_MAX - 2 );
683         if ( ( p = strrchr( dir ? dir : ldir, '/' ) ) )
684             *p = '\0';
685         return dir ? dir : ldir;
686     }
687     else if ( *dir == '/')
688     {
689         if ( dir[ 1 ] == '\0'
690              || (    dir[ 1 ] == '.' && dir[ 2 ] == '.'
691                   && ( dir[ 3 ] == '/' || dir[ 3 ] == '\0' ) ) )
692             return strcpy( dir, "/" );
693     }
694     strcpy( ldir, dir );
695     p = ldir;
696 
697 #if defined __EMX__ || defined FL_WIN32
698     if (    isalpha( ( unsigned char ) ldir[ 0 ] )
699          && ldir[ 1 ] == ':' )
700     {                               /* drive letter */
701         dir[ 0 ] = ldir[ 0 ];
702         dir[ 1 ] = ldir[ 1 ];
703         dir[ 2 ] = '\0';
704         p = ldir + 2;
705     }
706     else
707 #elif defined opennt
708     if (    ldir[ 0 ] == '/' && ldir[ 1 ] == '/'
709          && isalpha( ( unsigned char ) ldir[ 2 ] ) )
710     {               /* //E is E dirver */
711         dir[ 0 ] = ldir[ 0 ];
712         dir[ 1 ] = ldir[ 1 ];
713         dir[ 2 ] = ldir[ 2 ];
714         dir[ 3 ] = '\0';
715         p = ldir + 3;
716     }
717     else
718 #endif
719         if ( ldir[ 0 ] != '/' && ldir[ 0 ] != '~' )
720             fli_getcwd( dir, FL_PATH_MAX - 2 );
721         else
722             dir[ 0 ] = '\0';
723 
724     while ( *p )
725     {
726 #ifdef __EMX__
727         if ( *p == '/' || *p == '\\' )
728         {
729 #else
730         if ( *p == '/' )
731         {
732 #endif
733             *q = '\0';
734             if ( q > one )
735                 add_one( dir, q = one );
736         }
737         else
738             *q++ = *p;
739         p++;
740     }
741 
742     *q = '\0';
743     if ( q > one )
744         add_one( dir, one );
745 
746 #if defined __EMX__ || defined FL_WIN32
747     if ( strlen( dir ) == 2 && dir[ 1 ] == ':' )
748     {               /* fix a single "C:" */
749         dir[ 2 ] = '/';
750         dir[ 3 ] = '\0';
751     }
752 #endif
753 
754 #if defined opennt
755     if (    strlen( dir ) == 3
756          && dir[ 0 ] == '/'
757          && dir[ 1 ] == '/'
758          && isalpha( ( unsigned char ) ldir[ 2 ] ) )
759     {               /* fix  "//C" */
760         dir[ 3 ] = '/';
761         dir[ 4 ] = '\0';
762     }
763 #endif
764 
765     fl_b2f_slash( dir );
766 
767     return dir;
768 }
769 
770 
771 #ifndef FL_WIN32
772 #include <pwd.h>
773 #endif
774 
775 /***************************************
776  ***************************************/
777 
778 static void
779 add_one( char * dir,
780          char * one )
781 {
782     char *q;
783 
784     if ( one[ 0 ] == '.' && one[ 1] == '.' && one[ 2 ] == '\0' )
785     {
786         if ( ( q = strrchr( dir, '/' ) ) )
787             *( q + ( q == dir ) ) = '\0';
788 #ifndef FL_WIN32
789     }
790     else if ( one[ 0 ] == '~' )
791     {
792         if ( one[ 1 ] == '\0' )
793         {           /* must be ~/ ... */
794             strcat( dir, ( q = getenv( "HOME" ) ) ? q : "/" );
795         }
796         else
797         {           /* must be ~name */
798             /* Mathod: vms <7.0 has no getpwnam(). Ignore ~name */
799 #if !defined __VMS || __VMS_VER >=  70000000
800             struct passwd *p = getpwnam( one + 1 );
801 
802             strcat( dir, p ? p->pw_dir : "/" );
803 #ifndef opennt
804             endpwent( );
805 #endif
806 #endif
807         }
808 #endif /* FL_WIN32 */
809     }
810     else if ( ! ( one[0] == '.' && one[ 1 ] == '\0' ) )
811     {
812         strcat( strcat( dir, "/" ), one );
813     }
814 
815 #ifdef __VMS
816     /* VMS has directory extensions, strip it */
817     {
818         int n = strlen( dir );
819         char *p;
820 
821         if ( n > 4 )
822         {
823             for ( p = dir + n - 4; *p; p++ )
824                 *p = toupper( *p );
825             if ( strcmp( ( p = dir + n - 4 ), ".DIR" ) == 0 )
826                 *p = '\0';
827         }
828     }
829 #endif
830 }
831 
832 
833 /* String matching routine is adapted from Rick Salz */
834 
835 static int match_star( const char * s,
836                        const char * p );
837 
838 
839 /***************************************
840  * Match string "s" to pattern "p"
841  ***************************************/
842 
843 static int
844 do_matching( const char * s,
845              const char * p )
846 {
847     int last,
848         matched,
849         reverse;
850 
851     for ( ; *p; s++, p++ )
852     {
853         if ( *s == '\0' )
854             return *p == '*' && *++p == '\0' ? 1 : -1;
855 
856         switch ( *p )           /* parse pattern */
857         {
858             case '\\':      /* Literal match with following character. */
859                 if ( *s != *++p )
860                     return 0;
861                 continue;
862 
863             default:        /* literal match */
864 #ifdef __VMS            /* vms filenames are not case sensitive */
865                 if ( toupper( *s ) != toupper( *p ) )
866 #else
867                 if ( *s != *p )
868 #endif
869                     return 0;
870                 continue;
871 
872             case '?':       /* Match anything. */
873                 continue;
874 
875             case '*':       /* Trailing star matches everything. */
876                 return *++p ? match_star( s, p ) : 1;
877 
878             case '[':       /* [!....] means inverse character class. */
879                 if ( ( reverse = ( p[ 1 ] == '!' ) ) )
880                     p++;
881                 for ( last = 0400, matched = 0; *++p && *p != ']'; last = *p )
882                     if (    ( *p == '-' && *s <= *++p && *s >= last )
883                          || ( *p != '-' && *s == *p ) )
884                         matched = 1;
885                 if ( matched == reverse )
886                     return 0;
887                 continue;
888         }
889     }
890 
891     return *s == '\0';
892 }
893 
894 
895 /***************************************
896  ***************************************/
897 
898 static int
899 match_star( const char * s,
900             const char * p )
901 {
902     int result;
903 
904     while ( ( result = do_matching( s, p ) ) == 0 ) /* gobble up * match */
905         if ( *++s == '\0' )
906             return -1;
907 
908     return result;
909 }
910 
911 
912 
913 /***************************************
914  * check if s matches pattern p
915  ***************************************/
916 
917 static int
918 fli_wildmat( const char * s,
919              const char * p )
920 {
921     if ( *p == '\0' && *s != '.' )
922         return 1;
923     else if ( *p == '\0' )
924         return 0;
925     else if ( ( *p == '?' || *p == '*' ) && *s == '.' )
926         return 0;
927     else
928         return do_matching( s, p ) == 1;
929 }
930 
931 
932 /***************************************
933  * scandir emulation
934  ***************************************/
935 
936 #ifndef FL_WIN32
937 
938 static int
939 tc_scandir( const char      * dirname,
940             struct DIRENT *** namelist )
941 {
942     DIR *dir;
943     struct DIRENT *dentry,
944                   **head = NULL;
945     int n = 0,
946         total;
947     static int dname_is_1;
948 
949     if ( ! ( dir = opendir( dirname ) ) )
950         return -1;
951 
952     if ( sizeof( struct DIRENT ) < 100 && ! dname_is_1 )
953     {
954         M_warn("tc_scandir", "Bad dirent -- will fix it on the fly" );
955         dname_is_1 = 1;
956     }
957 
958     /* Start reading the darn thing */
959 
960     for ( n = 0; ( dentry = readdir( dir ) ) != NULL; n++ )
961     {
962         head = fl_realloc( head, ( n + 1 ) * sizeof *head );
963 
964         /* Here it is even weirder: some systems have d_reclen =
965            sizeof(struct dirent) + strlen(d_name) and some have it as
966            d_reclen = strlen(d_name) */
967 
968         /* Mathog, VMS<7.0, at least has no d_reclen *at all */
969 
970 #if defined __VMS && __VMS_VER < 70000000 || defined opennt || defined __CYGWIN__
971         total = dname_is_1 ? strlen( dentry->d_name ) : sizeof *dentry;
972 #else
973 #  ifdef __DragonFly__
974         total = dname_is_1 ? _DIRENT_RECLEN(dentry->d_namlen) : sizeof *dentry;
975 #  else
976         total = dname_is_1 ? dentry->d_reclen : sizeof *dentry;
977 #  endif
978 #endif
979         memcpy( head[ n ] = fl_malloc( total ), dentry, total );
980     }
981 
982     closedir( dir );
983     *namelist = head;
984 
985     return n;
986 }
987 
988 #endif /* ! FL_WIN32 */
989 
990 
991 /***************************************
992  ***************************************/
993 
994 FL_DIRLIST_FILTER
995 fl_set_dirlist_filter( FL_DIRLIST_FILTER filter )
996 {
997     FL_DIRLIST_FILTER old = ffilter;
998 
999     ffilter = filter;
1000     return old;
1001 }
1002 
1003 
1004 /***************************************
1005  ***************************************/
1006 
1007 int
1008 fl_set_dirlist_filterdir( int yes )
1009 {
1010     int old = filter_directory;
1011 
1012     filter_directory = yes;
1013     return old;
1014 }
1015 
1016 
1017 /***************************************
1018  * for application's benifit
1019  ***************************************/
1020 
1021 static int
1022 default_filter( const char * name  FL_UNUSED_ARG,
1023                 int          type )
1024 {
1025     return type == FT_FILE || type == FT_DIR || type == FT_LINK;
1026 }
1027 
1028 
1029 /***************************************
1030  ***************************************/
1031 
1032 int
1033 fl_set_dirlist_sort( int method )
1034 {
1035     int old = fli_sort_method;
1036 
1037     fli_sort_method = method;
1038     return old;
1039 }
1040 
1041 #ifdef __VMS
1042 #include "vms_readdir.c"
1043 #endif
1044 
1045 
1046 /***************************************
1047  ***************************************/
1048 
1049 char *
1050 fli_getcwd( char * buf,
1051            int    len )
1052 {
1053 #ifdef FL_WIN32
1054     return fl_b2f_slash( getcwd( buf, len ) );
1055 #else
1056     return getcwd( buf, len );
1057 #endif
1058 
1059 
1060 }
1061 
1062 
1063 /*
1064  * Local variables:
1065  * tab-width: 4
1066  * indent-tabs-mode: nil
1067  * End:
1068  */
1069