1 /*
2  * The ARAnyM MetaDOS driver.
3  *
4  * 2002 STan
5  *
6  * Based on:
7  * dosfile.c,v 1.20 2001/06/13 20:21: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 file handling routines */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 
25 # include "dosfile.h"
26 # include "bosmeta.h"
27 
28 # include "metados.h"
29 
30 #if 0
31 #include "nfd.h"
32 #define TRACE(x) NFD(x)
33 #define DEBUG(x) NFD(x)
34 #else
35 #define TRACE(x)
36 #define DEBUG(x)
37 #endif
38 
39 
40 typedef struct {
41 	FCOOKIE 	*fc;
42 	unsigned long	offset;
43 } FILEPTR;
44 
45 
46 static inline long
do_open(FILEPTR * f,FCOOKIE * fc,short mode)47 do_open( FILEPTR *f, FCOOKIE *fc, short mode)
48 {
49 	metaopen_t metaopen;
50 	long ret = Metaopen(fc->bos_dev, &metaopen);
51 	if ( ret ) return ret;
52 
53 	/* store the FCOOKIE* */
54 	f->fc = fc;
55 	f->offset = 0;
56 	return 0;
57 }
58 
59 static inline long
do_close(FILEPTR * f)60 do_close( FILEPTR *f)
61 {
62 	Metaclose(f->fc->bos_dev);
63 
64 	f->fc = NULL;
65 	return 0;
66 }
67 
68 
69 static inline long
tty_read(FILEPTR * f,char * buf,long count)70 tty_read( FILEPTR *f, char *buf, long count)
71 {
72 	return Metaread(f->fc->bos_dev, buf, 0, count);
73 }
74 
75 static inline long
tty_write(FILEPTR * f,const char * buf,long count)76 tty_write( FILEPTR *f, const char *buf, long count)
77 {
78 	return Metawrite(f->fc->bos_dev, buf, 0, count);
79 }
80 
81 
82 #define BLOCK_SIZE 2048
83 
84 static inline long
block_read(FILEPTR * f,char * buf,long count)85 block_read( FILEPTR *f, char *buf, long count)
86 {
87 	unsigned long boffset = f->offset / BLOCK_SIZE;
88 	unsigned long bcount = ( count + BLOCK_SIZE - 1) / BLOCK_SIZE;
89 
90 	DEBUG(("block_read: '%s'\n", f->fc->name));
91 
92 	if (	(f->offset % BLOCK_SIZE) == 0 &&
93 		(count % BLOCK_SIZE) == 0) {
94 
95 		long ret = Metaread(f->fc->bos_dev, buf, boffset, bcount);
96 		if ( ret < 0 )
97 			return ret;
98 		f->offset += ret * BLOCK_SIZE;
99 		return ret;
100 	} else {
101 		char *buffer = malloc( bcount * BLOCK_SIZE);
102 		long ret = Metaread(f->fc->bos_dev, buffer, boffset, bcount);
103 		if ( ret < 0 ) {
104 			free(buffer);
105 			return ret;
106 		}
107 
108 		count = (ret * BLOCK_SIZE < count) ? ret * BLOCK_SIZE : count;
109 		memcpy( buf, &buffer[f->offset - (boffset * BLOCK_SIZE)], count);
110 		free( buffer);
111 		return ret;
112 	}
113 }
114 
115 static inline long
block_write(FILEPTR * f,const char * buf,long count)116 block_write( FILEPTR *f, const char *buf, long count)
117 {
118 	unsigned long boffset = f->offset / BLOCK_SIZE;
119 	unsigned long bcount = ( count + BLOCK_SIZE - 1) / BLOCK_SIZE;
120 
121 	DEBUG(("block_write: '%s'\n", f->fc->name));
122 
123 	if (	(f->offset % BLOCK_SIZE) == 0 &&
124 		(count % BLOCK_SIZE) == 0) {
125 
126 		long ret = Metawrite(f->fc->bos_dev, buf, boffset, bcount);
127 		if ( ret < 0 )
128 			return ret;
129 		return count;
130 	} else {
131 		char *buffer = malloc( bcount * BLOCK_SIZE);
132 		long ret = Metaread(f->fc->bos_dev, buffer, boffset, bcount);
133 		if ( ret < 0 ) {
134 			free(buffer);
135 			return ret;
136 		}
137 
138 		count = (ret * BLOCK_SIZE < count) ? ret * BLOCK_SIZE : count;
139 		memcpy( &buffer[f->offset - (boffset * BLOCK_SIZE)], buf, count);
140 
141 		ret = Metawrite(f->fc->bos_dev, buffer, boffset, bcount);
142 		free(buffer);
143 		if ( ret < 0 )
144 			return ret;
145 		return count;
146 	}
147 }
148 
149 
150 static inline long
do_lseek(FILEPTR * f,long place,short how)151 do_lseek( FILEPTR *f, long place, short how)
152 {
153 	switch (how) {
154 		case 0:	/* whence = SEEK_SET; */
155 			f->offset = place;
156 			break;
157 		case 1:	/* whence = SEEK_CUR; */
158 			f->offset += place;
159 			break;
160 		case 2:	/* whence = SEEK_END; */
161 			/* FIXME? How to find the size of a device? */
162 		default:
163 			;
164 	}
165 
166 	return -ENOSYS;
167 }
168 
169 
170 
171 long __CDECL
sys_f_open(MetaDOSFile const char * name,short mode)172 sys_f_open (MetaDOSFile const char *name, short mode)
173 {
174 	FILEPTR *fp = (FILEPTR*)fpMD;
175 	FCOOKIE *fc;
176 
177 	long ret = path2cookie( name, NULL, &fc);
178 	if (ret) return ret;
179 
180 #if 0
181 	/* make sure the mode is legal */
182 	mode &= O_USER;
183 
184 	/* note: file mode 3 is reserved for the kernel;
185 	 * for users, transmogrify it into O_RDWR (mode 2)
186 	 */
187 	if ((mode & O_RWMODE) == O_EXEC)
188 		mode = (mode & ~O_RWMODE) | O_RDWR;
189 #endif
190 
191 	return do_open (fp, fc, mode);
192 }
193 
194 long __CDECL
sys_f_create(MetaDOSFile const char * name,short attrib)195 sys_f_create (MetaDOSFile const char *name, short attrib)
196 {
197 	FILEPTR *fp = (FILEPTR*)fpMD;
198 	FCOOKIE *fc;
199 
200 	long ret = path2cookie( name, NULL, &fc);
201 	if (ret) return ret;
202 
203 	return do_open (fp, fc, 1);
204 	// return EPERM;
205 }
206 
207 long __CDECL
sys_f_close(MetaDOSFile short fd)208 sys_f_close (MetaDOSFile short fd)
209 {
210 	FILEPTR *fp = (FILEPTR*)fpMD;
211 	return do_close (fp);
212 }
213 
214 long __CDECL
sys_f_read(MetaDOSFile short fd,long count,char * buf)215 sys_f_read (MetaDOSFile short fd, long count, char *buf)
216 {
217 	FILEPTR *f = (FILEPTR*)fpMD;
218 
219 	DEBUG(("f_read: '%s' %lx\n", f->fc->name, f->fc->bos_info_flags));
220 	if (is_terminal (f->fc))
221 		return tty_read (f, buf, count);
222 
223 	DEBUG(("f_read: block '%s'\n", f->fc->name));
224 	return block_read (f, buf, count);
225 }
226 
227 long __CDECL
sys_f_write(MetaDOSFile short fd,long count,const char * buf)228 sys_f_write (MetaDOSFile short fd, long count, const char *buf)
229 {
230 	long r;
231 	FILEPTR *f = (FILEPTR*)fpMD;
232 
233 	if (is_terminal (f->fc))
234 		return tty_write (f, buf, count);
235 
236 	/* Prevent broken device drivers from wiping the disk.
237 	 * We return a zero rather than a negative error code
238 	 * to help programs those don't handle GEMDOS errors
239 	 * returned by Fwrite()
240 	 */
241 	if (count <= 0)
242 		return 0;
243 
244 #if 0
245 	/* it would be faster to do this in the device driver, but this
246 	 * way the drivers are easier to write
247 	 */
248 	if (f->flags & O_APPEND)
249 	{
250 		r = do_lseek (f, 0L, SEEK_END);
251 		/* ignore errors from unseekable files (e.g. pipes) */
252 		if (r == EACCES)
253 			r = 0;
254 	} else
255 #endif
256 		r = 0;
257 
258 	if (r < 0)
259 		return r;
260 
261 	return block_write (f, buf, count);
262 }
263 
264 long __CDECL
sys_f_seek(MetaDOSFile long place,short fd,short how)265 sys_f_seek (MetaDOSFile long place, short fd, short how)
266 {
267 	FILEPTR *f = (FILEPTR*)fpMD;
268 
269 	if (is_terminal (f->fc))
270 		return 0;
271 
272 	return do_lseek (f, place, how);
273 }
274 
275 
276 long __CDECL
sys_f_datime(MetaDOSFile unsigned short * timeptr,short fd,short wflag)277 sys_f_datime (MetaDOSFile unsigned short *timeptr, short fd, short wflag)
278 {
279 	FILEPTR *f = (FILEPTR*)fpMD;
280 
281 	/* some programs use Fdatime to test for TTY devices */
282 	if (is_terminal (f->fc))
283 		return EACCES;
284 
285 	return EACCES; //FIXME: xdd_datime (f, timeptr, wflag);
286 }
287 
288