mirror of https://github.com/gungwald/copy.git
Reorg
This commit is contained in:
parent
caf1b7bce5
commit
332f067b2e
32
Makefile
32
Makefile
|
@ -8,10 +8,28 @@ ifndef CC65_TARGET
|
|||
CC65_TARGET:=apple2enh
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
ifndef MERLIN_DIR
|
||||
MERLIN_DIR=C:/opt/Merlin32_v1.0
|
||||
endif
|
||||
MERLIN_LIB=$(MERLIN_DIR)/Library
|
||||
MERLIN=$(MERLIN_DIR)/Windows/Merlin32
|
||||
COPY=copy
|
||||
APPLEWIN="c:\opt\AppleWin1.26.2.3\applewin.exe"
|
||||
else
|
||||
ifndef MERLIN_DIR
|
||||
MERLIN_DIR=$(HOME)/opt/Merlin32_v1.0
|
||||
endif
|
||||
MERLIN_LIB=$(MERLIN_DIR)/Library
|
||||
MERLIN=$(MERLIN_DIR)/Linux64/Merlin32
|
||||
COPY=cp
|
||||
APPLEWIN=applewin
|
||||
endif
|
||||
|
||||
CC=cl65
|
||||
AS=ca65
|
||||
OBJS=copy.o cui.o fileinfo.o prodos.o
|
||||
HDRS=fileinfo.h cui.h prodos.h
|
||||
OBJS=copy.o cui.o prodos.o prodosext.o libgen.o
|
||||
HDRS=cui.h prodos.h prodosext.h libgen.h
|
||||
CFLAGS=-O -t $(CC65_TARGET) -DTRACE
|
||||
# The -S $6000 makes the start address $6000 so that both hi-res
|
||||
# pages are available.
|
||||
|
@ -31,14 +49,22 @@ BIN_LOAD_ADDR=0x0803
|
|||
|
||||
all: $(DISK)
|
||||
|
||||
$(DISK): $(PGM)
|
||||
$(DISK): $(PGM) libgentest
|
||||
$(RM) $(DISK)
|
||||
$(AC) -pro140 $(DISK) $(DISK_VOL)
|
||||
$(AC) -as $(DISK) $(PGM) BIN < $(PGM)
|
||||
$(AC) -as $(DISK) libgentest BIN < libgentest
|
||||
|
||||
$(PGM): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
libgentest: libgen.o libgentest.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
test: $(DISK)
|
||||
$(APPLEWIN) -d1 $(DISK)
|
||||
#$(APPLEWIN) -s7 empty -d1 $(DISK)
|
||||
|
||||
clean:
|
||||
$(RM) *.o $(PGM) $(DISK)
|
||||
|
||||
|
|
53
copy.c
53
copy.c
|
@ -1,54 +1,59 @@
|
|||
#include <stdio.h> /* fopen, fread, fwrite, fclose */
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h> /* atexit */
|
||||
#include <string.h>
|
||||
#include "libgen.h" /* basename */
|
||||
|
||||
#include "fileinfo.h"
|
||||
#include "prodos.h"
|
||||
#include "prodosext.h"
|
||||
#include "cui.h"
|
||||
|
||||
#define BUFFER_SIZE 2048
|
||||
#define BF_SIZ 2048
|
||||
|
||||
void cleanup(void);
|
||||
FILE *openFile(const char *name, const char *mode);
|
||||
void closeFile(FILE *f, const char *name);
|
||||
void concatPath(char *dest, const char *src);
|
||||
|
||||
char sourceName[PRODOS_PATH_MAX + 1];
|
||||
char srcName[PRODOS_PATH_MAX + 1];
|
||||
char destName[PRODOS_PATH_MAX + 1];
|
||||
FILE *sourceFile = NULL;
|
||||
FILE *destFile = NULL;
|
||||
struct FileInfo sourceInfo;
|
||||
struct FileInfo destInfo;
|
||||
FILE *src = NULL;
|
||||
FILE *dest = NULL;
|
||||
struct GetFileInfoParams srcInfo;
|
||||
struct GetFileInfoParams destInfo;
|
||||
size_t n;
|
||||
char buffer[BUFFER_SIZE];
|
||||
char buf[BF_SIZ];
|
||||
size_t bytesRead;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
atexit(cleanup);
|
||||
|
||||
inputFileName("Source file or directory:", sourceName, sizeof(sourceName), &sourceInfo);
|
||||
inputFileName("Destination file or directory:", destName, sizeof(destName), &destInfo);
|
||||
if (! inputFileName("Source file or directory:", srcName,
|
||||
sizeof(srcName), &srcInfo))
|
||||
return;
|
||||
|
||||
sourceFile = openFile(sourceName, "r");
|
||||
if (! inputFileName("Destination file or directory:", destName,
|
||||
sizeof(destName), &destInfo))
|
||||
return;
|
||||
|
||||
if (isDir(&destInfo)) {
|
||||
concatPath(destName, basename(sourceName));
|
||||
}
|
||||
src = openFile(srcName, "r");
|
||||
|
||||
destFile = openFile(destName, "w");
|
||||
if (isDirectory(&destInfo))
|
||||
concatPath(destName, basename(srcName));
|
||||
|
||||
while ((bytesRead = fread(buffer, 1, sizeof(buffer), sourceFile)) == BUFFER_SIZE) {
|
||||
if (fwrite(buffer, 1, bytesRead, destFile) < bytesRead) {
|
||||
dest = openFile(destName, "w");
|
||||
|
||||
while ((bytesRead = fread(buf, 1, sizeof(buf), src)) == BF_SIZ)
|
||||
if (fwrite(buf, 1, bytesRead, dest) < bytesRead) {
|
||||
perror(destName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bytesRead > 0 && !feof(destFile) && !ferror(destFile)) {
|
||||
if (fwrite(buffer, 1, bytesRead, destFile) < bytesRead) {
|
||||
|
||||
if (bytesRead > 0 && !feof(dest) && !ferror(dest))
|
||||
if (fwrite(buf, 1, bytesRead, dest) < bytesRead)
|
||||
perror(destName);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
@ -83,7 +88,7 @@ void concatPath(char *dest, const char *src)
|
|||
|
||||
void cleanup(void)
|
||||
{
|
||||
closeFile(destFile, destName);
|
||||
closeFile(sourceFile, sourceName);
|
||||
closeFile(dest, destName);
|
||||
closeFile(src, srcName);
|
||||
}
|
||||
|
||||
|
|
38
cui.c
38
cui.c
|
@ -3,27 +3,44 @@
|
|||
#include <string.h> /* strlen */
|
||||
|
||||
#include "cui.h"
|
||||
#include "fileinfo.h"
|
||||
#include "prodos.h"
|
||||
#include "prodosext.h"
|
||||
|
||||
#define ESC '\x1b'
|
||||
|
||||
static char *readLine(char *line, size_t capacity);
|
||||
static void chomp(char *line);
|
||||
static bool complete;
|
||||
static uint8_t result;
|
||||
|
||||
void inputFileName(const char *prompt, char *name, size_t capacity, struct FileInfo *f)
|
||||
bool inputFileName(const char *prompt, char *name,
|
||||
size_t capacity,
|
||||
struct GetFileInfoParams *params)
|
||||
{
|
||||
complete = false;
|
||||
|
||||
while (! complete) {
|
||||
printf(prompt);
|
||||
readLine(name, capacity);
|
||||
initFileInfo(f, name);
|
||||
if (exists(f)) {
|
||||
if (strlen(name) == 0) {
|
||||
puts("Aborting");
|
||||
break;
|
||||
}
|
||||
else if (name[0] == ESC) {
|
||||
puts("Escaping");
|
||||
break;
|
||||
}
|
||||
params->param_count = GET_FILE_INFO_PARAM_COUNT;
|
||||
params->pathname = name;
|
||||
printf("params address = %x\n", &(params->param_count));
|
||||
printf("params address = %x\n", params);
|
||||
result = get_file_info(params);
|
||||
if (result == PRODOS_E_NONE)
|
||||
complete = true;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "File '%s' does not exist\n", name);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s: %s (code %d)\n", name, getMessage(result), result);
|
||||
}
|
||||
return complete;
|
||||
}
|
||||
|
||||
char *readLine(char *line, size_t capacity)
|
||||
|
@ -32,9 +49,8 @@ char *readLine(char *line, size_t capacity)
|
|||
|
||||
if ((result = fgets(line, capacity, stdin)) != NULL)
|
||||
chomp(line);
|
||||
else
|
||||
if (ferror(stdin))
|
||||
perror("stdin");
|
||||
else if (ferror(stdin))
|
||||
perror("stdin");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
3
cui.h
3
cui.h
|
@ -1,7 +1,8 @@
|
|||
#ifndef CUI_H
|
||||
#define CUI_H
|
||||
|
||||
extern void inputFileName(const char *prompt, char *name, size_t capacity, struct FileInfo *f);
|
||||
extern bool inputFileName(const char *prompt, char *name, size_t capacity,
|
||||
struct GetFileInfoParams *params);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
27
fileinfo.c
27
fileinfo.c
|
@ -1,27 +0,0 @@
|
|||
#include <apple2_filetype.h> /* PRODOS_T_DIR */
|
||||
#include "fileinfo.h"
|
||||
#include "prodos.h"
|
||||
|
||||
static struct GetFileInfoParams *infoParams;
|
||||
|
||||
void initFileInfo(struct FileInfo *f, const char *name)
|
||||
{
|
||||
infoParams = &(f->infoParams);
|
||||
infoParams->param_count = GET_FILE_INFO_PARAM_COUNT;
|
||||
infoParams->pathname = name;
|
||||
f->getFileInfoResult = get_file_info(infoParams);
|
||||
}
|
||||
|
||||
bool isDir(struct FileInfo *f)
|
||||
{
|
||||
return f->infoParams.file_type == PRODOS_T_DIR;
|
||||
}
|
||||
|
||||
bool exists(struct FileInfo *f)
|
||||
{
|
||||
if (f->getFileInfoResult == 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
18
fileinfo.h
18
fileinfo.h
|
@ -1,18 +0,0 @@
|
|||
#ifndef MY_FILE_H
|
||||
#define MY_FILE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "prodos.h"
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
struct GetFileInfoParams infoParams;
|
||||
uint8_t getFileInfoResult;
|
||||
};
|
||||
|
||||
extern void initFileInfo(struct FileInfo *f, const char *name);
|
||||
extern bool isDir(struct FileInfo *f);
|
||||
extern bool exists(struct FileInfo *f);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#include <string.h>
|
||||
|
||||
char *basename(char *path)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/* If path is NULL or the empty string, return "." */
|
||||
if (path == NULL || path[0] == '\0')
|
||||
return ".";
|
||||
|
||||
/* If path is "/", return "/" */
|
||||
if (path[0] == '/' && path[1] == '\0')
|
||||
return path;
|
||||
|
||||
/* If path does not contain a '/' then return path */
|
||||
if ((ptr = strrchr(path, '/')) == NULL)
|
||||
return path;
|
||||
|
||||
/* Path contains a slash so return the string following the last
|
||||
slash. */
|
||||
return ++ptr;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef GUNGWALDS_LIBGEN_H
|
||||
#define GUNGWALDS_LIBGEN_H
|
||||
|
||||
/* Conforms to POSIX but does not modify path. A pointer into path
|
||||
or a constant string value is returned. So don't attempt to
|
||||
modify the return value. */
|
||||
char *basename(char *path);
|
||||
|
||||
#endif
|
Binary file not shown.
|
@ -0,0 +1,20 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libgen.h"
|
||||
|
||||
void assertStringEquals(char *s, char *t, char *name)
|
||||
{
|
||||
if (strcmp(s, t) == 0)
|
||||
printf("Success - %s\n", name);
|
||||
else
|
||||
printf("FAILURE - %s\n", name);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
assertStringEquals(basename(NULL), ".", "NULL");
|
||||
assertStringEquals(basename(""), ".", "Empty");
|
||||
assertStringEquals(basename("/"), "/", "Slash");
|
||||
assertStringEquals(basename("/blah/"),"","Trailing slash");
|
||||
assertStringEquals(basename("/blah"), "blah", "Blah");
|
||||
}
|
33
prodos.h
33
prodos.h
|
@ -4,20 +4,41 @@
|
|||
#include <stdint.h>
|
||||
|
||||
/* PATH_MAX POSIX constant
|
||||
ProDOS has a 64 char prefix that can be combined with partial
|
||||
pathname of 64 characters. This is a total of 128 characters. */
|
||||
ProDOS has a 64 char prefix that can be combined
|
||||
with partial pathname of 64 characters. This is
|
||||
a total of 128 characters. */
|
||||
#define PRODOS_PATH_MAX 128
|
||||
|
||||
#define PRODOS_FILE_NAME_MAX 15
|
||||
|
||||
#define PRODOS_E_NONE 0x00
|
||||
#define PRODOS_E_BAD_SYS_CALL_NUM 0x01
|
||||
#define PRODOS_E_BAD_SYS_PARAM_COUNT 0x04
|
||||
#define PRODOS_E_INTERRUPT_VECTOR_TABLE_FULL 0x25
|
||||
#define PRODOS_E_IO_ERROR 0x27
|
||||
#define PRODOS_E_NO_DEVICE 0x28
|
||||
#define PRODOS_E_DISK_WRITE_PROTECTED 0x2b
|
||||
#define PRODOS_E_DISK_SWITCHED 0x2e
|
||||
#define PRODOS_E_INVALID_PATH_SYNTAX 0x40
|
||||
#define PRODOS_E_FILE_CTRL_BLK_TABLE_FULL 0x42
|
||||
#define PRODOS_E_INVALID_REF_NUM 0x43
|
||||
#define PRODOS_E_PATH_NOT_FOUND 0x44
|
||||
#define PRODOS_E_VOLUME_DIR_NOT_FOUND 0x45
|
||||
#define PRODOS_E_FILE_NOT_FOUND 0x46
|
||||
#define PRODOS_E_INCOMPAT_FILE_FORMAT 0x4A
|
||||
#define PRODOS_E_UNSUP_STORAGE_TYPE 0x4B
|
||||
#define PRODOS_E_INVALID_VALUE_IN_PARAM 0x53
|
||||
#define PRODOS_E_DUPLICATE_FILE_NAME 0x47
|
||||
#define PRODOS_E_OVERRUN_ERROR 0x48
|
||||
#define PRODOS_E_VOLUME_DIR_FULL 0x49
|
||||
#define PRODOS_E_INCOMPATIBLE_FILE_FORMAT 0x4A
|
||||
#define PRODOS_E_UNSUPPORTED_STORAGE_TYPE 0x4B
|
||||
#define PRODOS_E_END_OF_FILE 0x4C
|
||||
#define PRODOS_E_POSITION_OUT_OF_RANGE 0x4D
|
||||
#define PRODOS_E_ACCESS_ERROR 0x4E
|
||||
#define PRODOS_E_FILE_IS_OPEN 0x50
|
||||
#define PRODOS_E_DIR_COUNT_ERROR 0x51
|
||||
#define PRODOS_E_NOT_PRODOS_DISK 0x52
|
||||
#define PRODOS_E_INVALID_PARAM 0x53
|
||||
#define PRODOS_E_VOLUME_CTRL_BLK_TABLE_FULL 0x55
|
||||
#define PRODOS_E_BAD_BUFFER_ADDR 0x56
|
||||
#define PRODOS_E_DUPLICATE_VOLUME 0x57
|
||||
#define PRODOS_E_BITMAP_ADDR_IMPOSSIBLE 0x5A
|
||||
|
||||
#define GET_FILE_INFO_PARAM_COUNT 0x0a
|
||||
|
|
39
prodos.s
39
prodos.s
|
@ -1,33 +1,46 @@
|
|||
.export _get_file_info
|
||||
|
||||
|
||||
|
||||
MLI = $bf00
|
||||
GET_FILE_INFO = $c4
|
||||
COUT = $FDED ;SUB TO OUTPUT A CHARACTER
|
||||
PRBYTE = $FDDA ;SUB TO PRINT A BYTE
|
||||
CROUT = $FD8E ;SUB TO OUTPUT CARRIAGE RETURN
|
||||
|
||||
|
||||
|
||||
.bss
|
||||
|
||||
_file_info_addr: .word $00
|
||||
|
||||
|
||||
|
||||
.code
|
||||
.code
|
||||
|
||||
; This function is defined as "fastcall" which puts the right-most
|
||||
; parameter into A/X and the return value in A/X.
|
||||
|
||||
_get_file_info:
|
||||
sta _file_info_addr
|
||||
stx _file_info_addr+1
|
||||
.proc _get_file_info
|
||||
|
||||
sta params
|
||||
stx params+1
|
||||
|
||||
; Switch in ROM
|
||||
bit $c082
|
||||
|
||||
lda params
|
||||
jsr PRBYTE
|
||||
jsr CROUT
|
||||
|
||||
lda params+1
|
||||
jsr PRBYTE
|
||||
jsr CROUT
|
||||
|
||||
jsr MLI
|
||||
.byte GET_FILE_INFO
|
||||
.word _file_info_addr
|
||||
params: .word 0
|
||||
|
||||
; The system call leaves the error status in the Accumulator.
|
||||
; The "fastcall" calling convention returns the A register,
|
||||
; which is what we want.
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
.bss
|
||||
|
||||
file_info_addr: .res 2
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
#include <stdbool.h>
|
||||
#include <apple2_filetype.h>
|
||||
#include "prodos.h"
|
||||
#include "prodosext.h"
|
||||
|
||||
bool isDirectory(struct GetFileInfoParams *params)
|
||||
{
|
||||
return params->file_type == PRODOS_T_DIR;
|
||||
}
|
||||
|
||||
const char *getMessage(uint8_t errorCode)
|
||||
{
|
||||
switch (errorCode) {
|
||||
case PRODOS_E_NONE:
|
||||
return "No error";
|
||||
case PRODOS_E_BAD_SYS_CALL_NUM:
|
||||
return "Bad system call number";
|
||||
case PRODOS_E_BAD_SYS_PARAM_COUNT:
|
||||
return "Bad system call parameter count";
|
||||
case PRODOS_E_INTERRUPT_VECTOR_TABLE_FULL:
|
||||
return "Interrupt vector table full";
|
||||
case PRODOS_E_IO_ERROR:
|
||||
return "I/O error";
|
||||
case PRODOS_E_NO_DEVICE:
|
||||
return "No device detected/connected";
|
||||
case PRODOS_E_DISK_WRITE_PROTECTED:
|
||||
return "Disk is write protected";
|
||||
case PRODOS_E_DISK_SWITCHED:
|
||||
return "Disk switched";
|
||||
case PRODOS_E_INVALID_PATH_SYNTAX:
|
||||
return "Invalid path syntax";
|
||||
case PRODOS_E_FILE_CTRL_BLK_TABLE_FULL:
|
||||
return "File control block table full";
|
||||
case PRODOS_E_INVALID_REF_NUM:
|
||||
return "Invalid reference number";
|
||||
case PRODOS_E_PATH_NOT_FOUND:
|
||||
return "Path not found";
|
||||
case PRODOS_E_VOLUME_DIR_NOT_FOUND:
|
||||
return "Volume directory not found";
|
||||
case PRODOS_E_FILE_NOT_FOUND:
|
||||
return "File not found";
|
||||
case PRODOS_E_DUPLICATE_FILE_NAME:
|
||||
return "Duplicate file name";
|
||||
case PRODOS_E_OVERRUN_ERROR:
|
||||
return "No space left on device";
|
||||
case PRODOS_E_VOLUME_DIR_FULL:
|
||||
return "No entries left in volume directory";
|
||||
case PRODOS_E_INCOMPATIBLE_FILE_FORMAT:
|
||||
return "Incompatible file format";
|
||||
case PRODOS_E_UNSUPPORTED_STORAGE_TYPE:
|
||||
return "Unsupported storage type";
|
||||
case PRODOS_E_END_OF_FILE:
|
||||
return "End of file reached";
|
||||
case PRODOS_E_POSITION_OUT_OF_RANGE:
|
||||
return "File position out of range";
|
||||
case PRODOS_E_ACCESS_ERROR:
|
||||
return "File attribute forbids operation";
|
||||
case PRODOS_E_FILE_IS_OPEN:
|
||||
return "File is open";
|
||||
case PRODOS_E_DIR_COUNT_ERROR:
|
||||
return "Directory header / file entry mismatch";
|
||||
case PRODOS_E_NOT_PRODOS_DISK:
|
||||
"Not a ProDOS disk";
|
||||
case PRODOS_E_INVALID_PARAM:
|
||||
return "Invalid value in parameter";
|
||||
case PRODOS_E_VOLUME_CTRL_BLK_TABLE_FULL:
|
||||
return "Volume control block table full";
|
||||
case PRODOS_E_BAD_BUFFER_ADDR:
|
||||
return "Bad buffer address";
|
||||
case PRODOS_E_DUPLICATE_VOLUME:
|
||||
return "Duplicate volume";
|
||||
case PRODOS_E_BITMAP_ADDR_IMPOSSIBLE:
|
||||
return "Bitmap address impossible";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef PRODOSEXT_H
|
||||
#define PRODOSEXT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern bool isDirectory(struct GetFileInfoParams *params);
|
||||
extern const char *getMessage(uint8_t errorCode);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue