1 /*
2 * The ARAnyM MetaDOS driver.
3 *
4 * 2002 STanda
5 *
6 * Based on:
7 * dosdir.c,v 1.11 2001/10/23 09:09:14 fna Exp
8 *
9 * This file has been modified as part of the FreeMiNT project. See
10 * the file Changes.MH for details and dates.
11 *
12 *
13 * Copyright 1990,1991,1992 Eric R. Smith.
14 * Copyright 1992,1993,1994 Atari Corporation.
15 * All rights reserved.
16 *
17 */
18
19 /* DOS directory functions */
20
21 #include <string.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 #include <osbind.h>
26
27 #include "bosmeta.h"
28 #include "list.h"
29 #include "dosdir.h"
30
31 #include "emu_tos.h"
32 #include "mintfake.h"
33
34 #if 0
35 #include "nfd.h"
36 #define TRACE(x) NFD(x)
37 #define DEBUG(x) NFD(x)
38 #else
39 #define TRACE(x)
40 #define DEBUG(x)
41 #endif
42
43
44 #define TOS_SEARCH 0x01
45
46 #define E_OK 0
47
48
49 #define NUM_SEARCH 10
50
51 static struct {
52 DIR dirh;
53 unsigned long srchtim;
54 } search_dirs[NUM_SEARCH];
55
56
57 static inline void
release_cookie(FCOOKIE ** dir)58 release_cookie( FCOOKIE **dir) {
59 /* dummy as there is no dup_cookie in the path2cookie() */
60 }
61
62 /*
63 * returns 1 if the given name contains a wildcard character
64 */
65
66 static int
has_wild(const char * name)67 has_wild (const char *name)
68 {
69 char c;
70
71 while ((c = *name++) != 0)
72 if (c == '*' || c == '?')
73 return 1;
74
75 return 0;
76 }
77
78 /*
79 * void copy8_3(dest, src): convert a file name (src) into DOS 8.3 format
80 * (in dest). Note the following things:
81 * if a field has less than the required number of characters, it is
82 * padded with blanks
83 * a '*' means to pad the rest of the field with '?' characters
84 * special things to watch for:
85 * "." and ".." are more or less left alone
86 * "*.*" is recognized as a special pattern, for which dest is set
87 * to just "*"
88 * Long names are truncated. Any extensions after the first one are
89 * ignored, i.e. foo.bar.c -> foo.bar, foo.c.bar->foo.c.
90 */
91
92 static void
copy8_3(char * dest,const char * src)93 copy8_3 (char *dest, const char *src)
94 {
95 char fill = ' ', c;
96 int i;
97
98 if (src[0] == '.')
99 {
100 if (src[1] == 0)
101 {
102 strcpy (dest, ". . ");
103 return;
104 }
105
106 if (src[1] == '.' && src[2] == 0)
107 {
108 strcpy (dest, ".. . ");
109 return;
110 }
111 }
112
113 if (src[0] == '*' && src[1] == '.' && src[2] == '*' && src[3] == 0)
114 {
115 dest[0] = '*';
116 dest[1] = 0;
117 return;
118 }
119
120 for (i = 0; i < 8; i++)
121 {
122 c = *src++;
123
124 if (!c || c == '.')
125 break;
126 if (c == '*')
127 fill = c = '?';
128
129 *dest++ = toupper((int)c & 0xff);
130 }
131
132 while (i++ < 8)
133 *dest++ = fill;
134
135 *dest++ = '.';
136 i = 0;
137 fill = ' ';
138 while (c && c != '.')
139 c = *src++;
140
141 if (c)
142 {
143 for( ;i < 3; i++)
144 {
145 c = *src++;
146
147 if (!c || c == '.')
148 break;
149
150 if (c == '*')
151 c = fill = '?';
152
153 *dest++ = toupper((int)c & 0xff);
154 }
155 }
156
157 while (i++ < 3)
158 *dest++ = fill;
159
160 *dest = 0;
161 }
162
163 /*
164 * int pat_match(name, patrn): returns 1 if "name" matches the template in
165 * "patrn", 0 if not. "patrn" is assumed to have been expanded in 8.3
166 * format by copy8_3; "name" need not be. Any '?' characters in patrn
167 * will match any character in name. Note that if "patrn" has a '*' as
168 * the first character, it will always match; this will happen only if
169 * the original pattern (before copy8_3 was applied) was "*.*".
170 *
171 * BUGS: acts a lot like the silly TOS pattern matcher.
172 */
173
174 static int
pat_match(const char * name,const char * template)175 pat_match (const char *name, const char *template)
176 {
177 char expname [TOS_NAMELEN+1];
178 register char *s;
179 register char c;
180
181 if (*template == '*')
182 return 1;
183
184 copy8_3 (expname, name);
185
186 s = expname;
187 while ((c = *template++) != 0)
188 {
189 if (c != *s && c != '?')
190 return 0;
191 s++;
192 }
193
194 return 1;
195 }
196
197
198 /*
199 * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
200 */
201
202 long __CDECL
sys_f_sfirst(MetaDOSDTA const char * path,int attrib)203 sys_f_sfirst (MetaDOSDTA const char *path, int attrib)
204 {
205 static short initialized = 0;
206 char *s, *slash;
207 FCOOKIE *dir, *newdir;
208 struct dtabuf *dta = (struct dtabuf*)dtaMD;
209 long r;
210 int i, havelabel;
211 char temp1[PATH_MAX];
212
213 TRACE(("Fsfirst(%s, %x)", path, attrib));
214
215 if ( !initialized ) {
216 initialized = 1;
217 for (i = 0; i < NUM_SEARCH; i++) {
218 search_dirs[i].dirh.list = NULL;
219 search_dirs[i].dirh.current = NULL;
220 search_dirs[i].srchtim = 0;
221 }
222 }
223
224 r = path2cookie (path, temp1, &dir);
225 if (r)
226 {
227 DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
228 return r;
229 }
230
231 /* we need to split the last name (which may be a pattern) off from
232 * the rest of the path, even if FS_KNOPARSE is true
233 */
234 slash = 0;
235 s = temp1;
236 while (*s)
237 {
238 if (*s == '\\')
239 slash = s;
240 s++;
241 }
242
243 if (slash)
244 {
245 *slash++ = 0; /* slash now points to a name or pattern */
246 r = name2cookie (dir->folder, temp1, &newdir);
247 release_cookie (&dir);
248 if (r)
249 {
250 DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
251 return r;
252 }
253 dir = newdir;
254 }
255 else
256 slash = temp1;
257
258 /* BUG? what if there really is an empty file name?
259 */
260 if (!*slash)
261 {
262 DEBUG(("Fsfirst: empty pattern"));
263 return -ENOENT;
264 }
265
266 /* copy the pattern over into dta_pat into TOS 8.3 form
267 * remember that "slash" now points at the pattern
268 * (it follows the last, if any)
269 */
270 copy8_3 (dta->dta_pat, slash);
271
272 /* if (attrib & FA_LABEL), read the volume label
273 *
274 * BUG: the label date and time are wrong. ISO/IEC 9293 14.3.3 allows this.
275 * The Desktop set also date and time to 0 when formatting a floppy disk.
276 */
277 havelabel = 0;
278 if (attrib & FA_LABEL)
279 {
280 r = sys_dl_readlabel (dta->dta_name, TOS_NAMELEN+1);
281 dta->dta_attrib = FA_LABEL;
282 dta->dta_time = dta->dta_date = 0;
283 dta->dta_size = 0;
284 dta->magic = EVALID;
285 if (r == E_OK && !pat_match (dta->dta_name, dta->dta_pat))
286 r = -ENOENT;
287 if ((attrib & (FA_DIR|FA_LABEL)) == FA_LABEL)
288 return r;
289 else if (r == E_OK)
290 havelabel = 1;
291 }
292
293 DEBUG(("Fsfirst(): havelabel = %d",havelabel));
294
295 if (!havelabel && has_wild (slash) == 0)
296 {
297 struct xattr xattr;
298
299 /* no wild cards in pattern */
300 r = name2cookie (dir->folder, slash, &newdir);
301 if (r == E_OK)
302 {
303 r = getxattr (newdir, &xattr);
304 release_cookie (&newdir);
305 }
306 release_cookie (&dir);
307 if (r)
308 {
309 DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
310 return r;
311 }
312
313 dta->magic = EVALID;
314 dta->dta_attrib = xattr.attr;
315 dta->dta_size = xattr.size;
316
317 strncpy (dta->dta_name, slash, TOS_NAMELEN-1);
318 dta->dta_name[TOS_NAMELEN-1] = 0;
319 return E_OK;
320 }
321
322 /* There is a wild card. Try to find a slot for an opendir/readdir
323 * search. NOTE: we also come here if we were asked to search for
324 * volume labels and found one.
325 */
326 for (i = 0; i < NUM_SEARCH; i++)
327 if (! search_dirs[i].dirh.list)
328 break;
329
330 if (i == NUM_SEARCH)
331 {
332 int oldest = 0;
333
334 DEBUG(("Fsfirst(%s): having to re-use a directory slot!", path));
335 for (i = 1; i < NUM_SEARCH; i++)
336 if ( search_dirs[i].srchtim < search_dirs[oldest].srchtim )
337 oldest = i;
338
339 /* OK, close this directory for re-use */
340 i = oldest;
341 sys_dl_closedir (&search_dirs[i].dirh);
342
343 /* invalidate re-used DTA */
344 dta->magic = EVALID;
345 }
346
347 r = sys_dl_opendir (&search_dirs[i].dirh, dir->folder, TOS_SEARCH);
348 DEBUG(("Fsfirst opendir(%s, %d) -> %d", dir->name, i, r));
349 if (r != E_OK)
350 {
351 DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)", path, r));
352 release_cookie(&dir);
353 return r;
354 }
355
356 /* set up the DTA for Fsnext */
357 dta->index = i;
358 dta->magic = SVALID;
359 dta->dta_sattrib = attrib;
360
361 /* OK, now basically just do Fsnext, except that instead of ENMFILES we
362 * return ENOENT.
363 * NOTE: If we already have found a volume label from the search above,
364 * then we skip the sys_f_snext and just return that.
365 */
366 if (havelabel)
367 return E_OK;
368
369 r = sys_f_snext(MetaDOSDTA0pass);
370 if (r == ENMFILES) r = -ENOENT;
371 if (r)
372 TRACE(("Fsfirst: returning %ld", r));
373
374 /* release_cookie isn't necessary, since &dir is now stored in the
375 * DIRH structure and will be released when the search is completed
376 */
377 return r;
378 }
379
380 /*
381 * Counter for Fsfirst/Fsnext, so that we know which search slots are
382 * least recently used. This is updated once per second by the code
383 * in timeout.c.
384 *
385 * BUG: 1/second is pretty low granularity
386 */
387
388 long __CDECL
sys_f_snext(MetaDOSDTA0)389 sys_f_snext (MetaDOSDTA0)
390 {
391 DIR *dirh;
392 struct dtabuf *dta = (struct dtabuf*)dtaMD;
393 short attr;
394
395 TRACE (("Fsnext"));
396
397 if (dta->magic == EVALID)
398 {
399 DEBUG (("Fsnext(%lx): DTA marked a failing search", dta));
400 return -ENMFILES;
401 }
402
403 if (dta->magic != SVALID)
404 {
405 DEBUG (("Fsnext(%lx): dta incorrectly set up", dta));
406 return -ENOSYS;
407 }
408
409 /* get the slot pointer */
410 search_dirs[ dta->index ].srchtim++;
411 dirh = &search_dirs[ dta->index ].dirh;
412
413 /* BUG: sys_f_snext and readdir should check for disk media changes
414 */
415 for(;;)
416 {
417 char buf[TOS_NAMELEN+1];
418 long r = sys_dl_readdir( dirh, buf, TOS_NAMELEN+1);
419 if (r == EBADARG)
420 {
421 DEBUG(("Fsnext: name too long"));
422 continue; /* TOS programs never see these names */
423 }
424
425 if (r != E_OK)
426 {
427 sys_dl_closedir (dirh);
428
429 dta->magic = EVALID;
430
431 if (r != ENMFILES)
432 DEBUG(("Fsnext: returning %ld", r));
433 return r;
434 }
435
436 if (!pat_match (buf, dta->dta_pat))
437 continue; /* different patterns */
438 attr = dirh->current->attr;
439
440 /* silly TOS rules for matching attributes */
441 if (attr == 0)
442 break;
443
444 if (attr & (FA_CHANGED|FA_RDONLY))
445 break;
446
447 if (attr & dta->dta_sattrib)
448 break;
449 }
450
451 dta->dta_attrib = attr;
452 dta->dta_size = 0;
453 strncpy (dta->dta_name, dirh->current->name, TOS_NAMELEN-1);
454
455 /* convert to upper characters (we are in TOS domain) */
456 strupr (dta->dta_name);
457
458 DEBUG(("Fsnext: %s\n", dta->dta_name));
459 return E_OK;
460 }
461
462