| 47 |
#endif |
#endif |
| 48 |
|
|
| 49 |
|
|
|
// Access modes |
|
|
enum { |
|
|
FMODE_READ, FMODE_WRITE, FMODE_APPEND |
|
|
}; |
|
|
|
|
|
// File types |
|
|
enum { |
|
|
FTYPE_PRG, FTYPE_SEQ |
|
|
}; |
|
|
|
|
| 50 |
// Prototypes |
// Prototypes |
| 51 |
static bool match(char *p, char *n); |
static bool match(const char *p, const char *n); |
| 52 |
|
|
| 53 |
|
|
| 54 |
/* |
/* |
| 117 |
* Open channel |
* Open channel |
| 118 |
*/ |
*/ |
| 119 |
|
|
| 120 |
uint8 FSDrive::Open(int channel, char *filename) |
uint8 FSDrive::Open(int channel, const uint8 *name, int name_len) |
| 121 |
{ |
{ |
| 122 |
set_error(ERR_OK); |
set_error(ERR_OK); |
| 123 |
|
|
| 124 |
// Channel 15: Execute file name as command |
// Channel 15: Execute file name as command |
| 125 |
if (channel == 15) { |
if (channel == 15) { |
| 126 |
execute_command(filename); |
execute_cmd(name, name_len); |
| 127 |
return ST_OK; |
return ST_OK; |
| 128 |
} |
} |
| 129 |
|
|
| 133 |
file[channel] = NULL; |
file[channel] = NULL; |
| 134 |
} |
} |
| 135 |
|
|
| 136 |
if (filename[0] == '$') |
if (name[0] == '#') { |
|
return open_directory(channel, filename+1); |
|
|
|
|
|
if (filename[0] == '#') { |
|
| 137 |
set_error(ERR_NOCHANNEL); |
set_error(ERR_NOCHANNEL); |
| 138 |
return ST_OK; |
return ST_OK; |
| 139 |
} |
} |
| 140 |
|
|
| 141 |
return open_file(channel, filename); |
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 |
|
|
| 149 |
* Open file |
* Open file |
| 150 |
*/ |
*/ |
| 151 |
|
|
| 152 |
uint8 FSDrive::open_file(int channel, char *filename) |
uint8 FSDrive::open_file(int channel, const uint8 *name, int name_len) |
| 153 |
{ |
{ |
| 154 |
char plainname[NAMEBUF_LENGTH]; |
char plain_name[NAMEBUF_LENGTH]; |
| 155 |
int filemode = FMODE_READ; |
int plain_name_len; |
| 156 |
int filetype = FTYPE_PRG; |
int mode = FMODE_READ; |
| 157 |
bool wildflag = false; |
int type = FTYPE_PRG; |
| 158 |
char *mode = "rb"; |
int rec_len = 0; |
| 159 |
|
parse_file_name(name, name_len, (uint8 *)plain_name, plain_name_len, mode, type, rec_len, true); |
| 160 |
convert_filename(filename, plainname, &filemode, &filetype, &wildflag); |
|
| 161 |
|
// Channel 0 is READ, channel 1 is WRITE |
| 162 |
// Channel 0 is READ PRG, channel 1 is WRITE PRG |
if (channel == 0 || channel == 1) { |
| 163 |
if (!channel) { |
mode = channel ? FMODE_WRITE : FMODE_READ; |
| 164 |
filemode = FMODE_READ; |
if (type == FTYPE_DEL) |
| 165 |
filetype = FTYPE_PRG; |
type = FTYPE_PRG; |
| 166 |
} |
} |
| 167 |
if (channel == 1) { |
|
| 168 |
filemode = FMODE_WRITE; |
bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND); |
| 169 |
filetype = FTYPE_PRG; |
|
| 170 |
} |
// Expand wildcards (only allowed on reading) |
| 171 |
|
if (strchr(plain_name, '*') || strchr(plain_name, '?')) { |
| 172 |
// Wildcards are only allowed on reading |
if (writing) { |
|
if (wildflag) { |
|
|
if (filemode != FMODE_READ) { |
|
| 173 |
set_error(ERR_SYNTAX33); |
set_error(ERR_SYNTAX33); |
| 174 |
return ST_OK; |
return ST_OK; |
| 175 |
|
} else |
| 176 |
|
find_first_file(plain_name); |
| 177 |
} |
} |
| 178 |
find_first_file(plainname); |
|
| 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 |
// Select fopen() mode according to file mode |
| 186 |
switch (filemode) { |
const char *mode_str = "rb"; |
| 187 |
case FMODE_READ: |
switch (mode) { |
|
mode = "rb"; |
|
|
break; |
|
| 188 |
case FMODE_WRITE: |
case FMODE_WRITE: |
| 189 |
mode = "wb"; |
mode_str = "wb"; |
| 190 |
break; |
break; |
| 191 |
case FMODE_APPEND: |
case FMODE_APPEND: |
| 192 |
mode = "ab"; |
mode_str = "ab"; |
| 193 |
break; |
break; |
| 194 |
} |
} |
| 195 |
|
|
| 197 |
#ifndef __riscos__ |
#ifndef __riscos__ |
| 198 |
if (chdir(dir_path)) |
if (chdir(dir_path)) |
| 199 |
set_error(ERR_NOTREADY); |
set_error(ERR_NOTREADY); |
| 200 |
else if ((file[channel] = fopen(plainname, mode)) != NULL) { |
else if ((file[channel] = fopen(plain_name, mode_str)) != NULL) { |
| 201 |
if (filemode == FMODE_READ) // Read and buffer first byte |
if (mode == FMODE_READ || mode == FMODE_M) // Read and buffer first byte |
| 202 |
read_char[channel] = fgetc(file[channel]); |
read_char[channel] = fgetc(file[channel]); |
| 203 |
} else |
} else |
| 204 |
set_error(ERR_FILENOTFOUND); |
set_error(ERR_FILENOTFOUND); |
| 208 |
char fullname[NAMEBUF_LENGTH]; |
char fullname[NAMEBUF_LENGTH]; |
| 209 |
|
|
| 210 |
// On RISC OS make a full filename |
// On RISC OS make a full filename |
| 211 |
sprintf(fullname,"%s.%s",dir_path,plainname); |
sprintf(fullname,"%s.%s",dir_path,plain_name); |
| 212 |
if ((file[channel] = fopen(fullname, mode)) != NULL) |
if ((file[channel] = fopen(fullname, mode)) != NULL) |
| 213 |
{ |
{ |
| 214 |
if (filemode == FMODE_READ) |
if (mode == FMODE_READ || mode == FMODE_M) |
| 215 |
{ |
{ |
| 216 |
read_char[channel] = fgetc(file[channel]); |
read_char[channel] = fgetc(file[channel]); |
| 217 |
} |
} |
| 228 |
|
|
| 229 |
|
|
| 230 |
/* |
/* |
|
* Analyze file name, get access mode and type |
|
|
*/ |
|
|
|
|
|
void FSDrive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag) |
|
|
{ |
|
|
char *p, *q; |
|
|
int i; |
|
|
|
|
|
// Search for ':', p points to first character after ':' |
|
|
if ((p = strchr(srcname, ':')) != NULL) |
|
|
p++; |
|
|
else |
|
|
p = srcname; |
|
|
|
|
|
// Convert char set of the remaining string -> destname |
|
|
q = destname; |
|
|
for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++, true)); i++) ; |
|
|
|
|
|
// Look for mode parameters seperated by ',' |
|
|
p = destname; |
|
|
while ((p = strchr(p, ',')) != NULL) { |
|
|
|
|
|
// Cut string after the first ',' |
|
|
*p++ = 0; |
|
|
|
|
|
switch (*p) { |
|
|
case 'p': |
|
|
*filetype = FTYPE_PRG; |
|
|
break; |
|
|
case 's': |
|
|
*filetype = FTYPE_SEQ; |
|
|
break; |
|
|
case 'r': |
|
|
*filemode = FMODE_READ; |
|
|
break; |
|
|
case 'w': |
|
|
*filemode = FMODE_WRITE; |
|
|
break; |
|
|
case 'a': |
|
|
*filemode = FMODE_APPEND; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
// Search for wildcards |
|
|
*wildflag = (strchr(destname, '?') != NULL) || (strchr(destname, '*') != NULL); |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
| 231 |
* Find first file matching wildcard pattern and get its real name |
* Find first file matching wildcard pattern and get its real name |
| 232 |
*/ |
*/ |
| 233 |
|
|
| 234 |
// Return true if name 'n' matches pattern 'p' |
// Return true if name 'n' matches pattern 'p' |
| 235 |
static bool match(char *p, char *n) |
static bool match(const char *p, const char *n) |
| 236 |
{ |
{ |
| 237 |
if (!*p) // Null pattern matches everything |
if (!*p) // Null pattern matches everything |
| 238 |
return true; |
return true; |
| 248 |
return !*n; |
return !*n; |
| 249 |
} |
} |
| 250 |
|
|
| 251 |
void FSDrive::find_first_file(char *name) |
void FSDrive::find_first_file(char *pattern) |
| 252 |
{ |
{ |
| 253 |
#ifndef __riscos__ |
#ifndef __riscos__ |
| 254 |
DIR *dir; |
DIR *dir; |
| 264 |
while (de) { |
while (de) { |
| 265 |
|
|
| 266 |
// Match found? Then copy real file name |
// Match found? Then copy real file name |
| 267 |
if (match(name, de->d_name)) { |
if (match(pattern, de->d_name)) { |
| 268 |
strncpy(name, de->d_name, NAMEBUF_LENGTH); |
strncpy(pattern, de->d_name, NAMEBUF_LENGTH); |
| 269 |
closedir(dir); |
closedir(dir); |
| 270 |
return; |
return; |
| 271 |
} |
} |
| 297 |
* Open directory, create temporary file |
* Open directory, create temporary file |
| 298 |
*/ |
*/ |
| 299 |
|
|
| 300 |
uint8 FSDrive::open_directory(int channel, char *filename) |
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"; |
char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; |
| 303 |
char str[NAMEBUF_LENGTH]; |
char str[NAMEBUF_LENGTH]; |
|
char pattern[NAMEBUF_LENGTH]; |
|
| 304 |
char *p, *q; |
char *p, *q; |
| 305 |
int i; |
int i; |
| 306 |
int filemode; |
int filemode; |
| 313 |
struct stat statbuf; |
struct stat statbuf; |
| 314 |
|
|
| 315 |
// Special treatment for "$0" |
// Special treatment for "$0" |
| 316 |
if (filename[0] == '0' && filename[1] == 0) |
if (pattern[0] == '0' && pattern[1] == 0) { |
| 317 |
filename += 1; |
pattern++; |
| 318 |
|
pattern_len--; |
| 319 |
|
} |
| 320 |
|
|
| 321 |
// Convert filename ('$' already stripped), filemode/type are ignored |
// Skip everything before the ':' in the pattern |
| 322 |
convert_filename(filename, pattern, &filemode, &filetype, &wildflag); |
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 '..' |
// Open directory for reading and skip '.' and '..' |
| 331 |
if ((dir = opendir(dir_path)) == NULL) { |
if ((dir = opendir(dir_path)) == NULL) { |
| 345 |
// Create directory title |
// Create directory title |
| 346 |
p = &buf[8]; |
p = &buf[8]; |
| 347 |
for (i=0; i<16 && dir_title[i]; i++) |
for (i=0; i<16 && dir_title[i]; i++) |
| 348 |
*p++ = conv_to_64(dir_title[i], false); |
*p++ = ascii2petscii(dir_title[i]); |
| 349 |
fwrite(buf, 1, 32, file[channel]); |
fwrite(buf, 1, 32, file[channel]); |
| 350 |
|
|
| 351 |
// Create and write one line for every directory entry |
// Create and write one line for every directory entry |
| 352 |
while (de) { |
while (de) { |
| 353 |
|
|
| 354 |
// Include only files matching the pattern |
// Include only files matching the ascii_pattern |
| 355 |
if (match(pattern, de->d_name)) { |
if (match(ascii_pattern, de->d_name)) { |
| 356 |
|
|
| 357 |
// Get file statistics |
// Get file statistics |
| 358 |
chdir(dir_path); |
chdir(dir_path); |
| 381 |
*p++ = '\"'; |
*p++ = '\"'; |
| 382 |
q = p; |
q = p; |
| 383 |
for (i=0; i<16 && str[i]; i++) |
for (i=0; i<16 && str[i]; i++) |
| 384 |
*q++ = conv_to_64(str[i], true); |
*q++ = ascii2petscii(str[i]); |
| 385 |
*q++ = '\"'; |
*q++ = '\"'; |
| 386 |
p += 18; |
p += 18; |
| 387 |
|
|
| 409 |
unsigned char c; |
unsigned char c; |
| 410 |
|
|
| 411 |
// Much of this is very similar to the original |
// Much of this is very similar to the original |
| 412 |
if ((filename[0] == '0') && (filename[1] == 0)) {filename++;} |
if ((pattern[0] == '0') && (pattern[1] == 0)) {pattern++;} |
| 413 |
|
|
| 414 |
// Concatenate dir_path and pattern in buffer pattern ==> read subdirs! |
// Concatenate dir_path and ascii_pattern in buffer ascii_pattern ==> read subdirs! |
| 415 |
strcpy(pattern,dir_path); i = strlen(pattern); pattern[i++] = '.'; pattern[i] = 0; |
strcpy(ascii_pattern,dir_path); i = strlen(ascii_pattern); ascii_pattern[i++] = '.'; ascii_pattern[i] = 0; |
| 416 |
convert_filename(filename, pattern + i, &filemode, &filetype, &wildflag); |
convert_filename(pattern, ascii_pattern + i, &filemode, &filetype, &wildflag); |
| 417 |
p = pattern + i; q = p; |
p = ascii_pattern + i; q = p; |
| 418 |
do {c = *q++; if (c == '.') p = q;} while (c >= 32); |
do {c = *q++; if (c == '.') p = q;} while (c >= 32); |
| 419 |
*(p-1) = 0; // separate directory-path and pattern |
*(p-1) = 0; // separate directory-path and ascii_pattern |
| 420 |
if ((uint8)(*p) < 32) {*p = '*'; *(p+1) = 0;} |
if ((uint8)(*p) < 32) {*p = '*'; *(p+1) = 0;} |
| 421 |
|
|
| 422 |
// We don't use tmpfile() -- problems involved! |
// We don't use tmpfile() -- problems involved! |
| 433 |
|
|
| 434 |
do { |
do { |
| 435 |
de.readno = 1; |
de.readno = 1; |
| 436 |
if (ReadDirNameInfo(pattern,&di,&de) != NULL) |
if (ReadDirNameInfo(ascii_pattern,&di,&de) != NULL) |
| 437 |
de.offset = -1; |
de.offset = -1; |
| 438 |
else if (de.readno > 0) { // don't have to check for match here |
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 |
memset(buf,' ',31); buf[31] = 0; // most of this: see above |
| 550 |
{ |
{ |
| 551 |
// Channel 15: Collect chars and execute command on EOI |
// Channel 15: Collect chars and execute command on EOI |
| 552 |
if (channel == 15) { |
if (channel == 15) { |
| 553 |
if (cmd_len >= 40) |
if (cmd_len >= 58) |
| 554 |
return ST_TIMEOUT; |
return ST_TIMEOUT; |
| 555 |
|
|
| 556 |
cmd_buffer[cmd_len++] = byte; |
cmd_buf[cmd_len++] = byte; |
| 557 |
|
|
| 558 |
if (eoi) { |
if (eoi) { |
| 559 |
cmd_buffer[cmd_len] = 0; |
execute_cmd(cmd_buf, cmd_len); |
| 560 |
cmd_len = 0; |
cmd_len = 0; |
|
execute_command(cmd_buffer); |
|
| 561 |
} |
} |
| 562 |
return ST_OK; |
return ST_OK; |
| 563 |
} |
} |
| 567 |
return ST_TIMEOUT; |
return ST_TIMEOUT; |
| 568 |
} |
} |
| 569 |
|
|
| 570 |
if (fputc(byte, file[channel]) == EOF) { |
if (putc(byte, file[channel]) == EOF) { |
| 571 |
set_error(ERR_WRITEERROR); |
set_error(ERR_WRITE25); |
| 572 |
return ST_TIMEOUT; |
return ST_TIMEOUT; |
| 573 |
} |
} |
| 574 |
|
|
| 577 |
|
|
| 578 |
|
|
| 579 |
/* |
/* |
| 580 |
* Execute command string |
* Execute drive commands |
| 581 |
*/ |
*/ |
| 582 |
|
|
| 583 |
void FSDrive::execute_command(char *command) |
// INITIALIZE |
| 584 |
|
void FSDrive::initialize_cmd(void) |
| 585 |
{ |
{ |
|
switch (command[0]) { |
|
|
case 'I': |
|
| 586 |
close_all_channels(); |
close_all_channels(); |
|
set_error(ERR_OK); |
|
|
break; |
|
|
|
|
|
case 'U': |
|
|
if ((command[1] & 0x0f) == 0x0a) { |
|
|
Reset(); |
|
|
} else |
|
|
set_error(ERR_SYNTAX30); |
|
|
break; |
|
|
|
|
|
case 'G': |
|
|
if (command[1] != ':') |
|
|
set_error(ERR_SYNTAX30); |
|
|
else |
|
|
chdir_cmd(&command[2]); |
|
|
break; |
|
|
|
|
|
default: |
|
|
set_error(ERR_SYNTAX30); |
|
|
} |
|
| 587 |
} |
} |
| 588 |
|
|
| 589 |
|
// VALIDATE |
| 590 |
/* |
void FSDrive::validate_cmd(void) |
|
* Execute 'G' command |
|
|
*/ |
|
|
|
|
|
void FSDrive::chdir_cmd(char *dirpath) |
|
| 591 |
{ |
{ |
|
char str[NAMEBUF_LENGTH]; |
|
|
char *p = str; |
|
|
|
|
|
close_all_channels(); |
|
|
|
|
|
// G:. resets the directory path to its original setting |
|
|
if (dirpath[0] == '.' && dirpath[1] == 0) { |
|
|
change_dir(orig_dir_path); |
|
|
} else { |
|
|
|
|
|
// Convert directory name |
|
|
for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*dirpath++, false)); i++) ; |
|
|
|
|
|
if (!change_dir(str)) |
|
|
set_error(ERR_NOTREADY); |
|
|
} |
|
| 592 |
} |
} |
| 593 |
|
|
| 594 |
|
|
| 602 |
cmd_len = 0; |
cmd_len = 0; |
| 603 |
set_error(ERR_STARTUP); |
set_error(ERR_STARTUP); |
| 604 |
} |
} |
|
|
|
|
|
|
|
/* |
|
|
* Conversion PETSCII->ASCII |
|
|
*/ |
|
|
|
|
|
uint8 FSDrive::conv_from_64(uint8 c, bool map_slash) |
|
|
{ |
|
|
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) |
|
|
return c ^ 0x20; |
|
|
if ((c >= 0xc1) && (c <= 0xda)) |
|
|
return c ^ 0x80; |
|
|
if ((c == '/') && map_slash && ThePrefs.MapSlash) |
|
|
#ifdef __riscos__ |
|
|
return '.'; // directory separator is '.' in RO |
|
|
if (c == '.') {return('_');} // convert dot to underscore |
|
|
if (c == ' ') {return(0xa0);} // space --> hard space |
|
|
#else |
|
|
return '\\'; |
|
|
#endif |
|
|
return c; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
* Conversion ASCII->PETSCII |
|
|
*/ |
|
|
|
|
|
uint8 FSDrive::conv_to_64(uint8 c, bool map_slash) |
|
|
{ |
|
|
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) |
|
|
return c ^ 0x20; |
|
|
#ifdef __riscos__ |
|
|
if ((c == '.') && map_slash && ThePrefs.MapSlash) |
|
|
#else |
|
|
if ((c == '\\') && map_slash && ThePrefs.MapSlash) |
|
|
#endif |
|
|
return '/'; |
|
|
#ifdef __riscos__ |
|
|
if (c == '_') {return('.');} // convert underscore to dot |
|
|
if (c == 0xa0) {return(' ');} // hard space -> space |
|
|
#endif |
|
|
return c; |
|
|
} |
|