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