ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541fs.cpp
Revision: 1.4
Committed: 2004-01-11T00:09:51Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.3: +83 -220 lines
Log Message:
some cleanups and refactoring

File Contents

# Content
1 /*
2 * 1541fs.cpp - 1541 emulation in host file system
3 *
4 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * Notes:
23 * ------
24 *
25 * - If the directory is opened (file name "$"), a temporary file
26 * with the structure of a 1541 directory file is created and
27 * opened. It can then be accessed in the same way as all other
28 * files.
29 *
30 * Incompatibilities:
31 * ------------------
32 *
33 * - No "raw" directory reading
34 * - No relative/sequential/user files
35 * - Only "I" and "UJ" commands implemented
36 */
37
38 #include "sysdeps.h"
39
40 #include "1541fs.h"
41 #include "IEC.h"
42 #include "main.h"
43 #include "Prefs.h"
44
45 #ifdef __riscos__
46 #include "ROlib.h"
47 #endif
48
49
50 // Prototypes
51 static bool match(const char *p, const char *n);
52
53
54 /*
55 * Constructor: Prepare emulation
56 */
57
58 FSDrive::FSDrive(IEC *iec, char *path) : Drive(iec)
59 {
60 strcpy(orig_dir_path, path);
61 dir_path[0] = 0;
62
63 if (change_dir(orig_dir_path)) {
64 for (int i=0; i<16; i++)
65 file[i] = NULL;
66
67 Reset();
68
69 Ready = true;
70 }
71 }
72
73
74 /*
75 * Destructor
76 */
77
78 FSDrive::~FSDrive()
79 {
80 if (Ready) {
81 close_all_channels();
82 Ready = false;
83 }
84 }
85
86
87 /*
88 * Change emulation directory
89 */
90
91 bool FSDrive::change_dir(char *dirpath)
92 {
93 #ifndef __riscos__
94 DIR *dir;
95
96 if ((dir = opendir(dirpath)) != NULL) {
97 closedir(dir);
98 strcpy(dir_path, dirpath);
99 strncpy(dir_title, dir_path, 16);
100 return true;
101 } else
102 return false;
103 #else
104 int Info[4];
105
106 if ((ReadCatalogueInfo(dirpath,Info) & 2) != 0) { // Directory or image file
107 strcpy(dir_path, dirpath);
108 strncpy(dir_title, dir_path, 16);
109 return true;
110 } else
111 return false;
112 #endif
113 }
114
115
116 /*
117 * Open channel
118 */
119
120 uint8 FSDrive::Open(int channel, const uint8 *name, int name_len)
121 {
122 set_error(ERR_OK);
123
124 // Channel 15: Execute file name as command
125 if (channel == 15) {
126 execute_cmd(name, name_len);
127 return ST_OK;
128 }
129
130 // Close previous file if still open
131 if (file[channel]) {
132 fclose(file[channel]);
133 file[channel] = NULL;
134 }
135
136 if (name[0] == '#') {
137 set_error(ERR_NOCHANNEL);
138 return ST_OK;
139 }
140
141 if (name[0] == '$')
142 return open_directory(channel, name + 1, name_len - 1);
143
144 return open_file(channel, name, name_len);
145 }
146
147
148 /*
149 * Open file
150 */
151
152 uint8 FSDrive::open_file(int channel, const uint8 *name, int name_len)
153 {
154 char plain_name[NAMEBUF_LENGTH];
155 int plain_name_len;
156 int mode = FMODE_READ;
157 int type = FTYPE_PRG;
158 int rec_len = 0;
159 parse_file_name(name, name_len, (uint8 *)plain_name, plain_name_len, mode, type, rec_len, true);
160
161 // Channel 0 is READ, channel 1 is WRITE
162 if (channel == 0 || channel == 1) {
163 mode = channel ? FMODE_WRITE : FMODE_READ;
164 if (type == FTYPE_DEL)
165 type = FTYPE_PRG;
166 }
167
168 bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND);
169
170 // Expand wildcards (only allowed on reading)
171 if (strchr(plain_name, '*') || strchr(plain_name, '?')) {
172 if (writing) {
173 set_error(ERR_SYNTAX33);
174 return ST_OK;
175 } else
176 find_first_file(plain_name);
177 }
178
179 // Relative files are not supported
180 if (type == FTYPE_REL) {
181 set_error(ERR_UNIMPLEMENTED);
182 return ST_OK;
183 }
184
185 // Select fopen() mode according to file mode
186 const char *mode_str = "rb";
187 switch (mode) {
188 case FMODE_WRITE:
189 mode_str = "wb";
190 break;
191 case FMODE_APPEND:
192 mode_str = "ab";
193 break;
194 }
195
196 // Open file
197 #ifndef __riscos__
198 if (chdir(dir_path))
199 set_error(ERR_NOTREADY);
200 else if ((file[channel] = fopen(plain_name, mode_str)) != NULL) {
201 if (mode == FMODE_READ || mode == FMODE_M) // Read and buffer first byte
202 read_char[channel] = fgetc(file[channel]);
203 } else
204 set_error(ERR_FILENOTFOUND);
205 chdir(AppDirPath);
206 #else
207 {
208 char fullname[NAMEBUF_LENGTH];
209
210 // On RISC OS make a full filename
211 sprintf(fullname,"%s.%s",dir_path,plain_name);
212 if ((file[channel] = fopen(fullname, mode)) != NULL)
213 {
214 if (mode == FMODE_READ || mode == FMODE_M)
215 {
216 read_char[channel] = fgetc(file[channel]);
217 }
218 }
219 else
220 {
221 set_error(ERR_FILENOTFOUND);
222 }
223 }
224 #endif
225
226 return ST_OK;
227 }
228
229
230 /*
231 * Find first file matching wildcard pattern and get its real name
232 */
233
234 // Return true if name 'n' matches pattern 'p'
235 static bool match(const char *p, const char *n)
236 {
237 if (!*p) // Null pattern matches everything
238 return true;
239
240 do {
241 if (*p == '*') // Wildcard '*' matches all following characters
242 return true;
243 if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
244 return false;
245 p++; n++;
246 } while (*p);
247
248 return !*n;
249 }
250
251 void FSDrive::find_first_file(char *pattern)
252 {
253 #ifndef __riscos__
254 DIR *dir;
255 struct dirent *de;
256
257 // Open directory for reading and skip '.' and '..'
258 if ((dir = opendir(dir_path)) == NULL)
259 return;
260 de = readdir(dir);
261 while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
262 de = readdir(dir);
263
264 while (de) {
265
266 // Match found? Then copy real file name
267 if (match(pattern, de->d_name)) {
268 strncpy(pattern, de->d_name, NAMEBUF_LENGTH);
269 closedir(dir);
270 return;
271 }
272
273 // Get next directory entry
274 de = readdir(dir);
275 }
276
277 closedir(dir);
278 #else
279 dir_env de;
280 char Buffer[NAMEBUF_LENGTH];
281
282 de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = name;
283 do {
284 de.readno = 1;
285 if (ReadDirName(dir_path,Buffer,&de) != NULL)
286 de.offset = -1;
287 else if (de.offset != -1 && match(name,Buffer)) {
288 strncpy(name, Buffer, NAMEBUF_LENGTH);
289 return;
290 }
291 } while (de.readno > 0);
292 #endif
293 }
294
295
296 /*
297 * Open directory, create temporary file
298 */
299
300 uint8 FSDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
301 {
302 char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
303 char str[NAMEBUF_LENGTH];
304 char *p, *q;
305 int i;
306 int filemode;
307 int filetype;
308 bool wildflag;
309
310 #ifndef __riscos__
311 DIR *dir;
312 struct dirent *de;
313 struct stat statbuf;
314
315 // Special treatment for "$0"
316 if (pattern[0] == '0' && pattern[1] == 0) {
317 pattern++;
318 pattern_len--;
319 }
320
321 // Skip everything before the ':' in the pattern
322 uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
323 if (t)
324 pattern = t + 1;
325
326 // Convert pattern to ASCII
327 char ascii_pattern[NAMEBUF_LENGTH];
328 petscii2ascii(ascii_pattern, (const char *)pattern, NAMEBUF_LENGTH);
329
330 // Open directory for reading and skip '.' and '..'
331 if ((dir = opendir(dir_path)) == NULL) {
332 set_error(ERR_NOTREADY);
333 return ST_OK;
334 }
335 de = readdir(dir);
336 while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
337 de = readdir(dir);
338
339 // Create temporary file
340 if ((file[channel] = tmpfile()) == NULL) {
341 closedir(dir);
342 return ST_OK;
343 }
344
345 // Create directory title
346 p = &buf[8];
347 for (i=0; i<16 && dir_title[i]; i++)
348 *p++ = ascii2petscii(dir_title[i]);
349 fwrite(buf, 1, 32, file[channel]);
350
351 // Create and write one line for every directory entry
352 while (de) {
353
354 // Include only files matching the ascii_pattern
355 if (match(ascii_pattern, de->d_name)) {
356
357 // Get file statistics
358 chdir(dir_path);
359 stat(de->d_name, &statbuf);
360 chdir(AppDirPath);
361
362 // Clear line with spaces and terminate with null byte
363 memset(buf, ' ', 31);
364 buf[31] = 0;
365
366 p = buf;
367 *p++ = 0x01; // Dummy line link
368 *p++ = 0x01;
369
370 // Calculate size in blocks (254 bytes each)
371 i = (statbuf.st_size + 254) / 254;
372 *p++ = i & 0xff;
373 *p++ = (i >> 8) & 0xff;
374
375 p++;
376 if (i < 10) p++; // Less than 10: add one space
377 if (i < 100) p++; // Less than 100: add another space
378
379 // Convert and insert file name
380 strcpy(str, de->d_name);
381 *p++ = '\"';
382 q = p;
383 for (i=0; i<16 && str[i]; i++)
384 *q++ = ascii2petscii(str[i]);
385 *q++ = '\"';
386 p += 18;
387
388 // File type
389 if (S_ISDIR(statbuf.st_mode)) {
390 *p++ = 'D';
391 *p++ = 'I';
392 *p++ = 'R';
393 } else {
394 *p++ = 'P';
395 *p++ = 'R';
396 *p++ = 'G';
397 }
398
399 // Write line
400 fwrite(buf, 1, 32, file[channel]);
401 }
402
403 // Get next directory entry
404 de = readdir(dir);
405 }
406 #else
407 dir_full_info di;
408 dir_env de;
409 unsigned char c;
410
411 // Much of this is very similar to the original
412 if ((pattern[0] == '0') && (pattern[1] == 0)) {pattern++;}
413
414 // Concatenate dir_path and ascii_pattern in buffer ascii_pattern ==> read subdirs!
415 strcpy(ascii_pattern,dir_path); i = strlen(ascii_pattern); ascii_pattern[i++] = '.'; ascii_pattern[i] = 0;
416 convert_filename(pattern, ascii_pattern + i, &filemode, &filetype, &wildflag);
417 p = ascii_pattern + i; q = p;
418 do {c = *q++; if (c == '.') p = q;} while (c >= 32);
419 *(p-1) = 0; // separate directory-path and ascii_pattern
420 if ((uint8)(*p) < 32) {*p = '*'; *(p+1) = 0;}
421
422 // We don't use tmpfile() -- problems involved!
423 DeleteFile(RO_TEMPFILE); // first delete it, if it exists
424 if ((file[channel] = fopen(RO_TEMPFILE,"wb+")) == NULL)
425 return(ST_OK);
426 de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = p;
427
428 // Create directory title - copied from above
429 p = &buf[8];
430 for (i=0; i<16 && dir_title[i]; i++)
431 *p++ = conv_to_64(dir_title[i], false);
432 fwrite(buf, 1, 32, file[channel]);
433
434 do {
435 de.readno = 1;
436 if (ReadDirNameInfo(ascii_pattern,&di,&de) != NULL)
437 de.offset = -1;
438 else if (de.readno > 0) { // don't have to check for match here
439 memset(buf,' ',31); buf[31] = 0; // most of this: see above
440 p = buf; *p++ = 0x01; *p++ = 0x01;
441 i = (di.length + 254) / 254; *p++ = i & 0xff; *p++ = (i>>8) & 0xff;
442 p++;
443 if (i < 10)
444 *p++ = ' ';
445 if (i < 100)
446 *p++ = ' ';
447 strcpy(str, di.name);
448 *p++ = '\"'; q = p;
449 for (i=0; (i<16 && str[i]); i++)
450 *q++ = conv_to_64(str[i], true);
451 *q++ = '\"'; p += 18;
452 if ((di.otype & 2) == 0) {
453 *p++ = 'P'; *p++ = 'R'; *p++ = 'G';
454 } else {
455 *p++ = 'D'; *p++ = 'I'; *p++ = 'R';
456 }
457 fwrite(buf, 1, 32, file[channel]);
458 }
459 } while (de.offset != -1);
460 #endif
461
462 // Final line
463 fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
464
465 // Rewind file for reading and read first byte
466 rewind(file[channel]);
467 read_char[channel] = fgetc(file[channel]);
468
469 #ifndef __riscos
470 // Close directory
471 closedir(dir);
472 #endif
473
474 return ST_OK;
475 }
476
477
478 /*
479 * Close channel
480 */
481
482 uint8 FSDrive::Close(int channel)
483 {
484 if (channel == 15) {
485 close_all_channels();
486 return ST_OK;
487 }
488
489 if (file[channel]) {
490 fclose(file[channel]);
491 file[channel] = NULL;
492 }
493
494 return ST_OK;
495 }
496
497
498 /*
499 * Close all channels
500 */
501
502 void FSDrive::close_all_channels(void)
503 {
504 for (int i=0; i<15; i++)
505 Close(i);
506
507 cmd_len = 0;
508 }
509
510
511 /*
512 * Read from channel
513 */
514
515 uint8 FSDrive::Read(int channel, uint8 *byte)
516 {
517 int c;
518
519 // Channel 15: Error channel
520 if (channel == 15) {
521 *byte = *error_ptr++;
522
523 if (*byte != '\r')
524 return ST_OK;
525 else { // End of message
526 set_error(ERR_OK);
527 return ST_EOF;
528 }
529 }
530
531 if (!file[channel]) return ST_READ_TIMEOUT;
532
533 // Read one byte
534 *byte = read_char[channel];
535 c = fgetc(file[channel]);
536 if (c == EOF)
537 return ST_EOF;
538 else {
539 read_char[channel] = c;
540 return ST_OK;
541 }
542 }
543
544
545 /*
546 * Write to channel
547 */
548
549 uint8 FSDrive::Write(int channel, uint8 byte, bool eoi)
550 {
551 // Channel 15: Collect chars and execute command on EOI
552 if (channel == 15) {
553 if (cmd_len >= 58)
554 return ST_TIMEOUT;
555
556 cmd_buf[cmd_len++] = byte;
557
558 if (eoi) {
559 execute_cmd(cmd_buf, cmd_len);
560 cmd_len = 0;
561 }
562 return ST_OK;
563 }
564
565 if (!file[channel]) {
566 set_error(ERR_FILENOTOPEN);
567 return ST_TIMEOUT;
568 }
569
570 if (putc(byte, file[channel]) == EOF) {
571 set_error(ERR_WRITE25);
572 return ST_TIMEOUT;
573 }
574
575 return ST_OK;
576 }
577
578
579 /*
580 * Execute drive commands
581 */
582
583 // INITIALIZE
584 void FSDrive::initialize_cmd(void)
585 {
586 close_all_channels();
587 }
588
589 // VALIDATE
590 void FSDrive::validate_cmd(void)
591 {
592 }
593
594
595 /*
596 * Reset drive
597 */
598
599 void FSDrive::Reset(void)
600 {
601 close_all_channels();
602 cmd_len = 0;
603 set_error(ERR_STARTUP);
604 }