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, 10 months ago) by cebix
Branch: MAIN
Changes since 1.3: +83 -220 lines
Log Message:
some cleanups and refactoring

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * 1541fs.cpp - 1541 emulation in host file system
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 cebix 1.1 *
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 cebix 1.4 static bool match(const char *p, const char *n);
52 cebix 1.1
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 cebix 1.3 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 cebix 1.1 #endif
113     }
114    
115    
116     /*
117     * Open channel
118     */
119    
120 cebix 1.4 uint8 FSDrive::Open(int channel, const uint8 *name, int name_len)
121 cebix 1.1 {
122     set_error(ERR_OK);
123    
124     // Channel 15: Execute file name as command
125     if (channel == 15) {
126 cebix 1.4 execute_cmd(name, name_len);
127 cebix 1.1 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 cebix 1.4 if (name[0] == '#') {
137 cebix 1.1 set_error(ERR_NOCHANNEL);
138     return ST_OK;
139     }
140    
141 cebix 1.4 if (name[0] == '$')
142     return open_directory(channel, name + 1, name_len - 1);
143    
144     return open_file(channel, name, name_len);
145 cebix 1.1 }
146    
147    
148     /*
149     * Open file
150     */
151    
152 cebix 1.4 uint8 FSDrive::open_file(int channel, const uint8 *name, int name_len)
153 cebix 1.1 {
154 cebix 1.4 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 cebix 1.1 set_error(ERR_SYNTAX33);
174     return ST_OK;
175 cebix 1.4 } 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 cebix 1.1 }
184    
185     // Select fopen() mode according to file mode
186 cebix 1.4 const char *mode_str = "rb";
187     switch (mode) {
188 cebix 1.1 case FMODE_WRITE:
189 cebix 1.4 mode_str = "wb";
190 cebix 1.1 break;
191     case FMODE_APPEND:
192 cebix 1.4 mode_str = "ab";
193 cebix 1.1 break;
194     }
195    
196     // Open file
197     #ifndef __riscos__
198     if (chdir(dir_path))
199     set_error(ERR_NOTREADY);
200 cebix 1.4 else if ((file[channel] = fopen(plain_name, mode_str)) != NULL) {
201     if (mode == FMODE_READ || mode == FMODE_M) // Read and buffer first byte
202 cebix 1.1 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 cebix 1.4 sprintf(fullname,"%s.%s",dir_path,plain_name);
212 cebix 1.1 if ((file[channel] = fopen(fullname, mode)) != NULL)
213     {
214 cebix 1.4 if (mode == FMODE_READ || mode == FMODE_M)
215 cebix 1.1 {
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 cebix 1.4 static bool match(const char *p, const char *n)
236 cebix 1.1 {
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 cebix 1.4 void FSDrive::find_first_file(char *pattern)
252 cebix 1.1 {
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 cebix 1.4 if (match(pattern, de->d_name)) {
268     strncpy(pattern, de->d_name, NAMEBUF_LENGTH);
269 cebix 1.1 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 cebix 1.3 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 cebix 1.1 #endif
293     }
294    
295    
296     /*
297     * Open directory, create temporary file
298     */
299    
300 cebix 1.4 uint8 FSDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
301 cebix 1.1 {
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 cebix 1.4 if (pattern[0] == '0' && pattern[1] == 0) {
317     pattern++;
318     pattern_len--;
319     }
320 cebix 1.1
321 cebix 1.4 // 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 cebix 1.1
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 cebix 1.4 *p++ = ascii2petscii(dir_title[i]);
349 cebix 1.1 fwrite(buf, 1, 32, file[channel]);
350    
351     // Create and write one line for every directory entry
352     while (de) {
353    
354 cebix 1.4 // Include only files matching the ascii_pattern
355     if (match(ascii_pattern, de->d_name)) {
356 cebix 1.1
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 cebix 1.4 *q++ = ascii2petscii(str[i]);
385 cebix 1.1 *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 cebix 1.3 unsigned char c;
410 cebix 1.1
411     // Much of this is very similar to the original
412 cebix 1.4 if ((pattern[0] == '0') && (pattern[1] == 0)) {pattern++;}
413 cebix 1.3
414 cebix 1.4 // 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 cebix 1.3 do {c = *q++; if (c == '.') p = q;} while (c >= 32);
419 cebix 1.4 *(p-1) = 0; // separate directory-path and ascii_pattern
420 cebix 1.3 if ((uint8)(*p) < 32) {*p = '*'; *(p+1) = 0;}
421 cebix 1.1
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 cebix 1.3 return(ST_OK);
426     de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = p;
427 cebix 1.1
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 cebix 1.3 do {
435     de.readno = 1;
436 cebix 1.4 if (ReadDirNameInfo(ascii_pattern,&di,&de) != NULL)
437 cebix 1.3 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 cebix 1.1 #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 cebix 1.4 if (cmd_len >= 58)
554 cebix 1.1 return ST_TIMEOUT;
555    
556 cebix 1.4 cmd_buf[cmd_len++] = byte;
557 cebix 1.1
558     if (eoi) {
559 cebix 1.4 execute_cmd(cmd_buf, cmd_len);
560 cebix 1.1 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 cebix 1.4 if (putc(byte, file[channel]) == EOF) {
571     set_error(ERR_WRITE25);
572 cebix 1.1 return ST_TIMEOUT;
573     }
574    
575     return ST_OK;
576     }
577    
578    
579     /*
580 cebix 1.4 * Execute drive commands
581 cebix 1.1 */
582    
583 cebix 1.4 // INITIALIZE
584     void FSDrive::initialize_cmd(void)
585 cebix 1.1 {
586 cebix 1.4 close_all_channels();
587 cebix 1.1 }
588    
589 cebix 1.4 // VALIDATE
590     void FSDrive::validate_cmd(void)
591 cebix 1.1 {
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     }