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;
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  ssize_t len;
133  int tmp_attrib;
134  char *name, *p;
135  struct stat info;
136 
137  if( out_attrib == NULL )
138  out_attrib = &tmp_attrib;
139 
140  len = FB_STRSIZE( filespec );
141  name = NULL;
142 
143  ctx = FB_TLSGETCTX( DIR );
144 
145  if( len > 0 )
146  {
147  /* findfirst */
148 
149  if( ctx->in_use )
150  close_dir( );
151 
152  if( strchr( filespec->data, '*' ) || strchr( filespec->data, '?' ) )
153  {
154  /* we have a pattern */
155 
156  p = strrchr( filespec->data, '/' );
157  if( p )
158  {
159  strncpy( ctx->filespec, p + 1, MAX_PATH );
160  ctx->filespec[MAX_PATH-1] = '\0';
161  len = (p - filespec->data) + 1;
162  if( len > MAX_PATH - 1 )
163  len = MAX_PATH - 1;
164  memcpy( ctx->dirname, filespec->data, len );
165  ctx->dirname[len] = '\0';
166  }
167  else
168  {
169  strncpy( ctx->filespec, filespec->data, MAX_PATH );
170  ctx->filespec[MAX_PATH-1] = '\0';
171  strcpy( ctx->dirname, "./");
172  }
173 
174  /* Make sure these patterns work just like on Win32/DOS */
175  if( (!strcmp( ctx->filespec, "*.*" )) || (!strcmp( ctx->filespec, "*." )) )
176  strcpy( ctx->filespec, "*" );
177 
178  if( (attrib & 0x10) == 0 )
179  attrib |= 0x20;
180  ctx->attrib = attrib;
181  ctx->dir = opendir( ctx->dirname );
182  if( ctx->dir )
183  {
184  name = find_next( out_attrib );
185  if( name )
186  ctx->in_use = TRUE;
187  }
188  }
189  else
190  {
191  /* no pattern, use stat on single file */
192  if( !stat( filespec->data, &info ) )
193  {
194  tmp_attrib = get_attrib( filespec->data, &info );
195  if( (tmp_attrib & ~attrib ) == 0 )
196  {
197  name = strrchr( filespec->data, '/' );
198  if( !name )
199  name = filespec->data;
200  else
201  name++;
202  *out_attrib = tmp_attrib;
203  }
204  }
205  }
206  }
207  else
208  {
209  /* findnext */
210  if( ctx->in_use )
211  name = find_next( out_attrib );
212  }
213 
214  FB_STRLOCK();
215 
216  /* store filename if found */
217  if( name ) {
218  len = strlen( name );
219  res = fb_hStrAllocTemp_NoLock( NULL, len );
220  if( res )
221  fb_hStrCopy( res->data, name, len );
222  else
223  res = &__fb_ctx.null_desc;
224  } else {
225  res = &__fb_ctx.null_desc;
226  *out_attrib = 0;
227  }
228 
229  fb_hStrDelTemp_NoLock( filespec );
230 
231  FB_STRUNLOCK();
232 
233  return res;
234 }