COPY Mark II

Re-wrote the COPY command to work better with directories and different drives.
This commit is contained in:
Peter Weingartner 2022-05-19 17:41:10 -04:00
parent cb71665c0c
commit 60ba071966
14 changed files with 9152 additions and 8704 deletions

Binary file not shown.

View file

@ -15,6 +15,7 @@
#include "sys_general.h"
#include "timers.h"
#include "cli/cli.h"
#include "cli/dos_copy.h"
#include "cli/dos_cmds.h"
#include "cli/mem_cmds.h"
#include "cli/settings.h"

View file

@ -145,88 +145,6 @@ short cmd_del(short screen, int argc, const char * argv[]) {
}
}
short cmd_copy(short screen, int argc, const char * argv[]) {
FRESULT find_result;
FRESULT result;
DIR dir; /* Directory object */
FILINFO src_info; /* File information */
FILINFO dst_info;
FIL src_file;
FIL dst_file;
BYTE buffer[4096]; /* File copy buffer */
UINT br, bw; /* File read/write count */
char path[MAX_PATH_LEN];
bool is_directory = false;
bool is_append_file = false;
TRACE("cmd_copy");
if (argc > 2) {
strcpy(path, argv[2]);
result = f_stat(argv[2], &dst_info);
if (result == FR_OK) {
is_directory = dst_info.fattrib & AM_DIR;
} else if (result == FR_NO_FILE) {
is_directory = false;
} else {
goto error;
}
find_result = f_findfirst(&dir, &src_info, "", argv[1]);
while (find_result == FR_OK && src_info.fname[0]) {
if (strcmp(src_info.fname, path) == 0) goto skip; // Skip copying file to self.
result = f_open(&src_file, src_info.fname, FA_READ);
if (result != FR_OK) goto error;
if (is_directory) {
sprintf(path, "%s/%s", dst_info.fname, src_info.fname);
result = f_open(&dst_file, path, FA_WRITE | FA_CREATE_ALWAYS);
} else if (is_append_file) {
result = f_open(&dst_file, path, FA_WRITE | FA_OPEN_APPEND);
} else {
result = f_open(&dst_file, path, FA_WRITE | FA_CREATE_ALWAYS);
}
if (result != FR_OK) goto error;
print(screen, (is_append_file) ? "Appending " : "Copying ");
print(screen, src_info.fname);
print(screen, " to ");
print(screen, path);
print(screen, "\n");
/* Copy source to destination */
for (;;) {
result = f_read(&src_file, buffer, sizeof buffer, &br); /* Read a chunk of data from the source file */
if (br == 0) break; /* error or eof */
result = f_write(&dst_file, buffer, br, &bw); /* Write it to the destination file */
if (bw < br) break; /* error or disk full */
}
f_close(&src_file);
f_close(&dst_file);
skip:
find_result = f_findnext(&dir, &src_info);
is_append_file = true; // If copying more than one file to a file, then open for append.
}
f_closedir(&dir);
return 0;
error:
err_print(screen, "Unable to copy file(s)", result);
f_close(&src_file);
f_close(&dst_file);
return result;
}
}
/*
* Change the directory
*/
@ -607,7 +525,7 @@ short cmd_mkboot(short screen, int argc, const char * argv[]) {
} else if (argc == 4) {
// Can be either -b or -s
dev = cli_eval_number(argv[1]);
if (strcmp("-b", argv[2]) == 0) {
// -b
mode = 1;

View file

@ -25,11 +25,6 @@ extern short cmd_mkdir(short screen, int argc, const char * argv[]);
*/
extern short cmd_del(short screen, int argc, const char * argv[]);
/*
* Copies file(s) to destination
*/
extern short cmd_copy(short screen, int argc, const char * argv[]);
/*
* Set the current working directory
*/

365
src/cli/dos_copy.c Normal file
View file

@ -0,0 +1,365 @@
/** @file: dos_copy.c
* Provide the various functions needed for the COPY command
*/
#include <stdlib.h>
#include <string.h>
#include "dos_copy.h"
#include "constants.h"
#include "errors.h"
#include "simpleio.h"
#include "syscalls.h"
#include "utilities.h"
#include "dev/fsys.h"
/*
* Custom error codes for the COPY command
*/
#define ERR_COPY_SELF -1000
#define ERR_COPY_SRC_IS_DIR -1001
#define ERR_COPY_DST_IS_DIR -1002
#define ERR_COPY_NO_SRC_DIR -1003
#define ERR_COPY_NO_DST_DIR -1004
/**
* COPY is a complicated command, hence getting its own file.
* The command needs to deal with several types of source and destination path types:
*
* For the source file(s):
* S1. Fully specified absolute path to a single source file
* S2. Relative path to a single source file
* S3. Absolute path to one or more source files (pattern matching)
* S4. Relative path to one or more source files (pattern matching)
*
* For the destination:
* D1. Absolute path to the directory to store the file (filename implied)
* D2. Relative path to the directory (filename inmplied)
* D3. Absolute path to the file to create (filename provided)
* D4. Relative path to the file to create (filename provided)
*
* There are limitations: D1 and D2 may be used only if the source and destination
* directories are different, since we can't have the same file name twice in a directory.
* D3 and D4 may only be used in conjunction with S1 and S2, since S3 and S4 can specify
* multiple files, which cannot be copied to a single file.
*
* Additional rules:
* 1. COPY will not support copying an entire directory.
* (This means all source paths _must_ include a filename or pattern)
* 2. A file cannot be copied to the same directory with the same name.
* 3. Patterns (wildcards) may not be used for directory names.
*/
/**
* Check to see if the path points to a directory
*
* @param path the path to check
* @return 1 if the path points to a directory, 0 otherwise
*/
short is_directory(const char * path) {
t_file_info file;
short result = sys_fsys_stat(path, &file);
if ((result < 0) || ((file.attributes & FSYS_AM_DIR) == 0)) {
return 0;
}
return 1;
}
/**
* Perform the actual copy of the files, given the full paths to the files
*
* @param src_path the path to the source file to be copied
* @param dst_path the path to the destination file (will be deleted if it already exists)
* @return 0 on success, a negative number on error
*/
static short fsys_copy(const char * src_path, const char * dst_path) {
unsigned char buffer[256];
t_file_info file;
short fd_src = -1;
short fd_dst = -1;
short result = 0;
short n = 0;
// Check to make sure the source and destination paths are not the same
if (strcicmp(src_path, dst_path) == 0) {
// the two paths are the same... this is an error
return ERR_COPY_SELF;
}
// Check the source to make sure it exists and is not a directory
result = sys_fsys_stat(src_path, &file);
if (result < 0) {
// There was an error... file not found most likely
return result;
} else {
if (file.attributes & FSYS_AM_DIR) {
// The source path was a directory... this isn't allowed
return ERR_COPY_SRC_IS_DIR;
}
}
// Check the destination to make sure it is not a directory, if it exists
if (is_directory(dst_path)) {
// The source path was a directory... this isn't allowed
return ERR_COPY_DST_IS_DIR;
}
// Try to open the source file...
fd_src = sys_fsys_open(src_path, FSYS_READ);
if (fd_src > 0) {
// Ok... now try to open and create the destination file for writing...
fd_dst = sys_fsys_open(dst_path, FSYS_WRITE | FSYS_CREATE_ALWAYS);
if (fd_dst > 0) {
// Copy the file...
do {
// Try to read a chunk of data from the file
n = sys_chan_read(fd_src, buffer, 256);
if (n > 0) {
// Try to write what we got
result = sys_chan_write(fd_dst, buffer, n);
}
} while ((n > 0) && (result >= 0));
sys_fsys_close(fd_src);
sys_fsys_close(fd_dst);
return 0;
} else {
// Close the source file and return the error
sys_fsys_close(fd_src);
return fd_dst;
}
} else {
// There was an error... just return it
return fd_src;
}
}
/**
* Copy one or more file when the source contains a pattern
*
* NOTE: it is an error if the source and destination paths are the same
*
* @param src_path the path to the directory containing the source files
* @param src_pattern the search pattern used to find the files
* @param dst_path the path to the directory in which to store the files
* @result 0 on success, negative number on error
*/
static short fsys_copy_pattern(const char * src_path, const char * src_pattern, const char * dst_path) {
char src_file_path[MAX_PATH_LEN];
char dst_file_path[MAX_PATH_LEN];
char tmp[80];
t_file_info file;
short result = 0;
short dir = -1;
// Make sure the source and destination paths are not the same
if (strcicmp(src_path, dst_path) == 0) {
// the two paths are the same... this is an error
return ERR_COPY_SELF;
}
// Make sure the src_path exists and is a directory
result = sys_fsys_stat(src_path, &file);
if ((result < 0) || ((file.attributes & FSYS_AM_DIR) == 0)) {
return ERR_COPY_NO_SRC_DIR;
}
// Make sure the dst_path exists and is a directory
result = sys_fsys_stat(dst_path, &file);
if ((result < 0) || ((file.attributes & FSYS_AM_DIR) == 0)) {
return ERR_COPY_NO_DST_DIR;
}
dir = sys_fsys_findfirst(src_path, src_pattern, &file);
if (dir < 0) {
// Could not open the source directory for some reason
return dir;
}
// Try to copy each match...
while (strlen(file.name) > 0) {
// Figure out the source and destination paths to copy
sprintf(src_file_path, "%s%s", src_path, file.name);
sprintf(dst_file_path, "%s/%s", dst_path, file.name);
// Try to actually copy them
result = fsys_copy(src_file_path, dst_file_path);
if (result < 0) {
sys_fsys_closedir(dir);
return result;
}
// Try to get the next match
result = sys_fsys_findnext(dir, &file);
if (result < 0) {
sys_fsys_closedir(dir);
return result;
}
}
sys_fsys_closedir(dir);
return 0;
}
/**
* Make sure the provided path is absolute.
*
* If the path is not absolute, tack the current working directory onto the front of the path
*
* @param path the path to verify
* @return 0 on success, any other number is an error
*/
short fsys_copy_path_absolute(char * path) {
char tmp_path[MAX_PATH_LEN];
char cwd[MAX_PATH_LEN];
short result = 0;
if (path[0] != '/') {
// Path is not absolute... let's get the current working directory and fix that
result = fsys_get_cwd(cwd, MAX_PATH_LEN);
if (result) {
return result;
}
// Make a new, absolute path
sprintf(tmp_path, "%s%s", cwd, path);
// And copy it over the old path
strncpy(path, tmp_path, MAX_PATH_LEN);
}
return 0;
}
static fsys_copy_error(short screen, short n) {
char line[80];
switch (n) {
case ERR_COPY_SELF:
print(screen, "Unable to copy a file onto itself.\n");
break;
case ERR_COPY_NO_SRC_DIR:
print(screen, "Unable to copy a file: no source directory found.\n");
break;
case ERR_COPY_NO_DST_DIR:
print(screen, "Unable to copy a file: no destination directory found.\n");
break;
case ERR_COPY_SRC_IS_DIR:
print(screen, "Unable to copy a file: cannot copy a directory.\n");
break;
case ERR_COPY_DST_IS_DIR:
print(screen, "Unable to copy a file: cannot copy to a directory.\n");
break;
default:
sprintf(line, "Unable to copy the files: %d\n", n);
print(screen, line);
break;
}
}
/**
* The DOS COPY command itself:
* COPY <src path> <dst path>
*/
short cmd_copy(short screen, int argc, char * argv[]) {
char *x = 0;
char *src_path = 0;
char *src_pattern = 0;
char *dst_path = 0;
char *dst_name = 0;
char *tmp_path = 0;
char * src_solidus = 0;
char * dst_solidus = 0;
short result = 0;
// Allocate memory for the paths, etc.
x = (char *)malloc(5 * MAX_PATH_LEN);
if (x == 0) {
print(screen, "Cannot copy: out of memory\n");
return -1;
}
// Distribute the memory across our buffers...
src_path = x;
src_pattern = src_path + MAX_PATH_LEN;
dst_path = src_pattern + MAX_PATH_LEN;
dst_name = dst_path + MAX_PATH_LEN;
tmp_path = dst_name + MAX_PATH_LEN;
if (argc != 3) {
print(screen, "USAGE: COPY <src path> <dst path>\n");
free(x);
return -1;
}
// Copy the source path and make sure it's absolute
strncpy(src_path, argv[1], MAX_PATH_LEN);
if (fsys_copy_path_absolute(src_path)) {
print(screen, "Unable to find the current working directory.\n");
free(x);
return -1;
}
// Copy the destination path and make sure it's absolute
strncpy(dst_path, argv[2], MAX_PATH_LEN);
if (fsys_copy_path_absolute(dst_path)) {
print(screen, "Unable to find the current working directory.\n");
free(x);
return -1;
}
// Find the last slash, so we can check the file name portion
src_solidus = strrchr(src_path, '/');
if (src_solidus) {
// Copy the file name field...
strncpy(src_pattern, src_solidus + 1, MAX_PATH_LEN);
if (strchr(src_pattern, '*') || strchr(src_pattern, '?')) {
// There is a pattern in the file name field... remove the pattern from the path
src_solidus[1] = 0;
result = fsys_copy_pattern(src_path, src_pattern, dst_path);
if (result) {
fsys_copy_error(screen, result);
free(x);
return -1;
}
} else {
// Just one file to copy...
// If the destination is a directory, add the source file name
if (is_directory(dst_path)) {
if (dst_path[strlen(dst_path)-1] == '/') {
sprintf(tmp_path, "%s%s", dst_path, src_pattern);
} else {
sprintf(tmp_path, "%s/%s", dst_path, src_pattern);
}
strcpy(dst_path, tmp_path);
}
// Attempt to make the copy
result = fsys_copy(src_path, dst_path);
if (result) {
char line[80];
fsys_copy_error(screen, result);
free(x);
return -1;
}
}
} else {
print(screen, "Unable to parse the source path.\n");
free(x);
return -1;
}
free(x);
return 0;
}

14
src/cli/dos_copy.h Normal file
View file

@ -0,0 +1,14 @@
/** @file: dos_copy.h
* Provide the various functions needed for the COPY command
*/
#ifndef __DOS_COPY_H
#define __DOS_COPY_H
/**
* The DOS COPY command itself:
* COPY <src path> <dst path>
*/
extern short cmd_copy(short screen, int argc, char * argv[]);
#endif

View file

@ -376,7 +376,6 @@ short chan_write(short channel, const uint8_t * buffer, short size) {
}
} else {
log_num(LOG_ERROR, "chan_write error: ", res);
while (1) ;
return res;
}
}

View file

@ -317,11 +317,34 @@ short fsys_readdir(short dir, p_file_info file) {
short fsys_stat(const char * path, p_file_info file) {
FRESULT fres;
FILINFO finfo;
char match1[10], match2[10];
short i = 0;
// If the file being checked is on the floppy drive, make sure the FDC status
// is updated correctly for disk change by spinning up the motor and checking the DIR register
fsys_update_stat(path);
// FatFS's f_stat function does not handle root directories so bodge this in...
// For each drive...
for (i = 0; i < 3; i++) {
// Compute two legitimate paths to it
strcpy(match1, "/");
strcat(match1, (char *)VolumeStr[i]);
strcpy(match2, match1);
strcat(match2, "/");
// Check to see if the path is the same (barring letter case)
if ((strcicmp(path, match1) == 0) || (strcicmp(path, match2) == 0)) {
// It's a match... return the record for it
file->size = 0;
file->date = 0;
file->time = 0;
file->attributes = FSYS_AM_DIR;
strcpy(file->name, (char *)VolumeStr[i]);
return 0;
}
}
fres = f_stat(path, &finfo);
if (fres == FR_OK) {
int i;

View file

@ -123,7 +123,7 @@ void initialize() {
short res;
/* Set the logging level */
log_setlevel(LOG_ERROR);
log_setlevel(LOG_FATAL);
/* Initialize the memory system */
mem_init(0x3d0000);

View file

@ -24,4 +24,24 @@
#define CHAR_NL '\x0A' /* Linefeed */
#define CHAR_BS '\b' /* Backspace */
/*
* File access mode and open method flags
*/
#define FSYS_READ 0x01
#define FSYS_WRITE 0x02
#define FSYS_OPEN_EXISTING 0x00
#define FSYS_CREATE_NEW 0x04
#define FSYS_CREATE_ALWAYS 0x08
#define FSYS_OPEN_ALWAYS 0x10
#define FSYS_OPEN_APPEND 0x30
/*
* File attribute bits for directory entry
*/
#define FSYS_AM_RDO 0x01 /* Read only */
#define FSYS_AM_HID 0x02 /* Hidden */
#define FSYS_AM_SYS 0x04 /* System */
#define FSYS_AM_DIR 0x10 /* Directory */
#define FSYS_AM_ARC 0x20 /* Archive */
#endif

17314
src/mapfile

File diff suppressed because it is too large Load diff

View file

@ -43,3 +43,19 @@ char * strtok_r(char * source, const char * delimiter, char ** saveptr) {
*saveptr = y + 1;
return x;
}
/**
* Perform a case-insensitive string comparison
*
* @param str1 the first string
* @param str2 the second string
* @return 0 if str1 = str2, -1 if str1 < str2, 1 if str1 > str2
*/
short strcicmp(char const *str1, char const *str2) {
for (;; str1++, str2++) {
short diff = tolower((unsigned char)*str1) - tolower((unsigned char)*str2);
if (diff != 0 || !*str1) {
return diff;
}
}
}

View file

@ -26,4 +26,13 @@
*/
extern char * strtok_r(char * source, const char * delimiter, char ** saveptr);
/**
* Perform a case-insensitive string comparison
*
* @param str1 the first string
* @param str2 the second string
* @return 0 if str1 = str2, -1 if str1 < str2, 1 if str1 > str2
*/
extern short strcicmp(char const *str1, char const *str2);
#endif

View file

@ -7,6 +7,6 @@
#define VER_MAJOR 0
#define VER_MINOR 75
#define VER_BUILD 3
#define VER_BUILD 5
#endif