FreeBASIC  0.91.0
file_dir.c
Go to the documentation of this file.
1 /* dir() */
2 
3 #include "../fb.h"
4 #include <sys/stat.h>
5 #include <dirent.h>
6 
7 typedef struct _FB_DIRCTX {
8  int in_use;
9  int attrib;
10  DIR *dir;
11  char filespec[MAX_PATH];
12  char dirname[MAX_PATH];
13 } FB_DIRCTX;
14 
15 static void close_dir ( void )
16 {
17  FB_DIRCTX *ctx = FB_TLSGETCTX( DIR );
18 
19  closedir( ctx->dir );
20  ctx->in_use = FALSE;
21 }
22 
23 static int get_attrib ( char *name, struct stat *info )
24 {
25  int attrib = 0, mask;
26 
27  /* read only */
28  if( info->st_uid == geteuid() )
29  mask = S_IWUSR;
30  else if( info->st_gid == getegid() )
31  mask = S_IWGRP;
32  else
33  mask = S_IWOTH;
34 
35  if( (info->st_mode & mask) == 0 )
36  attrib |= 0x1;
37 
38  if( name[0] == '.' )
39  attrib |= 0x2; /* hidden */
40 
41  if( S_ISCHR( info->st_mode ) || S_ISBLK( info->st_mode ) || S_ISFIFO( info->st_mode ) || S_ISSOCK( info->st_mode ) )
42  attrib |= 0x4; /* system */
43 
44  if( S_ISDIR( info->st_mode ) )
45  attrib |= 0x10; /* directory */
46  else
47  attrib |= 0x20; /* archive */
48 
49  return attrib;
50 }
51 
52 static int match_spec( char *name )
53 {
54  FB_DIRCTX *ctx = FB_TLSGETCTX( DIR );
55  char *any = NULL;
56  char *spec;
57 
58  spec = ctx->filespec;
59 
60  while( ( *spec ) || ( *name ) )
61  {
62  switch( *spec )
63  {
64  case '*':
65  any = spec;
66  spec++;
67  while( ( *name != *spec ) && ( *name ) )
68  name++;
69  break;
70 
71  case '?':
72  spec++;
73  if( *name )
74  name++;
75  break;
76 
77  default:
78  if( *spec != *name )
79  {
80  if( ( any ) && ( *name ) )
81  spec = any;
82  else
83  return FALSE;
84  }
85  else
86  {
87  spec++;
88  name++;
89  }
90  break;
91  }
92  }
93 
94  return TRUE;
95 }
96 
97 static char *find_next ( int *attrib )
98 {
99  FB_DIRCTX *ctx = FB_TLSGETCTX( DIR );
100  char *name = NULL;
101  struct stat info;
102  struct dirent *entry;
103  char buffer[MAX_PATH];
104 
105  do
106  {
107  entry = readdir( ctx->dir );
108  if( !entry )
109  {
110  close_dir( );
111  return NULL;
112  }
113  name = entry->d_name;
114  strcpy( buffer, ctx->dirname );
115  strncat( buffer, name, MAX_PATH - strlen( buffer ) - 1 );
116  buffer[MAX_PATH-1] = '\0';
117 
118  if( stat( buffer, &info ) )
119  continue;
120 
121  *attrib = get_attrib( name, &info );
122  }
123  while( ( *attrib & ~ctx->attrib ) || !match_spec( name ) );
124 
125  return name;
126 }
127 
128 FBCALL FBSTRING *fb_Dir( FBSTRING *filespec, int attrib, int *out_attrib )
129 {
130  FB_DIRCTX *ctx;
131  FBSTRING *res;
132  int len, tmp_attrib;
133  char *name, *p;
134  struct stat info;
135 
136  if( out_attrib == NULL )
137  out_attrib = &tmp_attrib;
138 
139  len = FB_STRSIZE( filespec );
140  name = NULL;
141 
142  ctx = FB_TLSGETCTX( DIR );
143 
144  if( len > 0 )
145  {
146  /* findfirst */
147 
148  if( ctx->in_use )
149  close_dir( );
150 
151  if( strchr( filespec->data, '*' ) || strchr( filespec->data, '?' ) )
152  {
153  /* we have a pattern */
154 
155  p = strrchr( filespec->data, '/' );
156  if( p )
157  {
158  strncpy( ctx->filespec, p + 1, MAX_PATH );
159  ctx->filespec[MAX_PATH-1] = '\0';
160  len = (p - filespec->data) + 1;
161  if( len > MAX_PATH - 1 )
162  len = MAX_PATH - 1;
163  memcpy( ctx->dirname, filespec->data, len );
164  ctx->dirname[len] = '\0';
165  }
166  else
167  {
168  strncpy( ctx->filespec, filespec->data, MAX_PATH );
169  ctx->filespec[MAX_PATH-1] = '\0';
170  strcpy( ctx->dirname, "./");
171  }
172 
173  /* Make sure these patterns work just like on Win32/DOS */
174  if( (!strcmp( ctx->filespec, "*.*" )) || (!strcmp( ctx->filespec, "*." )) )
175  strcpy( ctx->filespec, "*" );
176 
177  if( (attrib & 0x10) == 0 )
178  attrib |= 0x20;
179  ctx->attrib = attrib;
180  ctx->dir = opendir( ctx->dirname );
181  if( ctx->dir )
182  {
183  name = find_next( out_attrib );
184  if( name )
185  ctx->in_use = TRUE;
186  }
187  }
188  else
189  {
190  /* no pattern, use stat on single file */
191  if( !stat( filespec->data, &info ) )
192  {
193  tmp_attrib = get_attrib( filespec->data, &info );
194  if( (tmp_attrib & ~attrib ) == 0 )
195  {
196  name = strrchr( filespec->data, '/' );
197  if( !name )
198  name = filespec->data;
199  else
200  name++;
201  *out_attrib = tmp_attrib;
202  }
203  }
204  }
205  }
206  else
207  {
208  /* findnext */
209  if( ctx->in_use )
210  name = find_next( out_attrib );
211  }
212 
213  FB_STRLOCK();
214 
215  /* store filename if found */
216  if( name ) {
217  len = strlen( name );
218  res = fb_hStrAllocTemp_NoLock( NULL, len );
219  if( res )
220  fb_hStrCopy( res->data, name, len );
221  else
222  res = &__fb_ctx.null_desc;
223  } else {
224  res = &__fb_ctx.null_desc;
225  *out_attrib = 0;
226  }
227 
228  fb_hStrDelTemp_NoLock( filespec );
229 
230  FB_STRUNLOCK();
231 
232  return res;
233 }