mirror of
https://github.com/deater/dos33fsprogs.git
synced 2024-11-01 01:06:33 +00:00
1486 lines
37 KiB
C
1486 lines
37 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h> /* exit() */
|
|
#include <string.h> /* strncpy() */
|
|
#include <sys/stat.h> /* struct stat */
|
|
#include <fcntl.h> /* O_RDONLY */
|
|
#include <unistd.h> /* lseek() */
|
|
#include <ctype.h> /* toupper() */
|
|
#include <errno.h>
|
|
|
|
#include "version.h"
|
|
|
|
#include "dos33.h"
|
|
|
|
int debug=0;
|
|
|
|
static int ignore_errors=0;
|
|
|
|
static unsigned char get_high_byte(int value) {
|
|
return (value>>8)&0xff;
|
|
}
|
|
|
|
static unsigned char get_low_byte(int value) {
|
|
return (value&0xff);
|
|
}
|
|
|
|
|
|
|
|
/* Read VTOC into a buffer */
|
|
static int dos33_read_vtoc(int fd, unsigned char *vtoc) {
|
|
|
|
int result;
|
|
|
|
/* Seek to VTOC */
|
|
lseek(fd,DISK_OFFSET(VTOC_TRACK,VTOC_SECTOR),SEEK_SET);
|
|
|
|
/* read in VTOC */
|
|
result=read(fd,vtoc,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error reading VTOC: %s\n",strerror(errno));
|
|
return -ERROR_VTOC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Checks if "filename" exists */
|
|
/* returns entry/track/sector */
|
|
static int dos33_check_file_exists(int fd,
|
|
char *filename,
|
|
int file_deleted) {
|
|
|
|
int catalog_track,catalog_sector;
|
|
int i,file_track;
|
|
char file_name[31];
|
|
int result;
|
|
unsigned char vtoc[BYTES_PER_SECTOR];
|
|
unsigned char catalog_buffer[BYTES_PER_SECTOR];
|
|
|
|
/* read the VTOC into buffer */
|
|
result=dos33_read_vtoc(fd,vtoc);
|
|
if (result<0) return result;
|
|
|
|
/* FIXME: we have a function for this */
|
|
/* get the catalog track and sector from the VTOC */
|
|
catalog_track=vtoc[VTOC_CATALOG_T];
|
|
catalog_sector=vtoc[VTOC_CATALOG_S];
|
|
|
|
repeat_catalog:
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* scan all file entries in catalog sector */
|
|
for(i=0;i<7;i++) {
|
|
file_track=catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)];
|
|
/* 0xff means file deleted */
|
|
/* 0x0 means empty */
|
|
if (file_track!=0x0) {
|
|
|
|
if (file_track==0xff) {
|
|
dos33_filename_to_ascii(file_name,
|
|
catalog_buffer+(CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE+FILE_NAME)),29);
|
|
|
|
if (file_deleted) {
|
|
/* return if we found the file */
|
|
if (!strncmp(filename,file_name,29)) {
|
|
return ((i<<16)+(catalog_track<<8)+catalog_sector);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
dos33_filename_to_ascii(file_name,
|
|
catalog_buffer+(CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE+FILE_NAME)),30);
|
|
/* return if we found the file */
|
|
if (!strncmp(filename,file_name,30)) {
|
|
return ((i<<16)+(catalog_track<<8)+catalog_sector);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* point to next catalog track/sector */
|
|
catalog_track=catalog_buffer[CATALOG_NEXT_T];
|
|
catalog_sector=catalog_buffer[CATALOG_NEXT_S];
|
|
|
|
if (catalog_sector!=0) goto repeat_catalog;
|
|
|
|
return -ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
static int dos33_free_sector(unsigned char *vtoc,int fd,int track,int sector) {
|
|
|
|
int result;
|
|
|
|
/* mark as free in VTOC */
|
|
dos33_vtoc_free_sector(vtoc,track,sector);
|
|
|
|
/* write modified VTOC back out */
|
|
lseek(fd,DISK_OFFSET(VTOC_TRACK,VTOC_SECTOR),SEEK_SET);
|
|
result=write(fd,vtoc,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"dos33_free_sector: error writing VTOC\n");
|
|
return -ERROR_VTOC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dos33_allocate_sector(int fd, unsigned char *vtoc) {
|
|
|
|
int found_track=0,found_sector=0;
|
|
int result;
|
|
|
|
/* Find an empty sector */
|
|
result=dos33_vtoc_find_free_sector(vtoc,&found_track,&found_sector,0);
|
|
|
|
if (result<0) {
|
|
fprintf(stderr,"ERROR: dos33_allocate_sector: Disk full!\n");
|
|
return -ERROR_NO_SPACE;
|
|
}
|
|
|
|
|
|
/* store new track/direction info */
|
|
vtoc[VTOC_LAST_ALLOC_T]=found_track;
|
|
|
|
if (found_track>VTOC_TRACK) vtoc[VTOC_ALLOC_DIRECT]=1;
|
|
else vtoc[VTOC_ALLOC_DIRECT]=-1;
|
|
|
|
/* Seek to VTOC */
|
|
lseek(fd,DISK_OFFSET(VTOC_TRACK,VTOC_SECTOR),SEEK_SET);
|
|
|
|
/* Write out VTOC */
|
|
result=write(fd,vtoc,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_VTOC;
|
|
}
|
|
|
|
return ((found_track<<8)+found_sector);
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ADD_RAW 0
|
|
#define ADD_BINARY 1
|
|
|
|
/* creates file apple_filename on the image from local file filename */
|
|
/* returns ?? */
|
|
static int dos33_add_file(unsigned char *vtoc,
|
|
int fd, char dos_type,
|
|
int file_type, int address, int length,
|
|
char *filename, char *apple_filename) {
|
|
|
|
int free_space,file_size,needed_sectors;
|
|
struct stat file_info;
|
|
int size_in_sectors=0;
|
|
int initial_ts_list=0,ts_list=0,i,data_ts,x,bytes_read=0,old_ts_list;
|
|
int catalog_track,catalog_sector,sectors_used=0;
|
|
int input_fd;
|
|
int result;
|
|
int first_write=1;
|
|
unsigned char ts_buffer[BYTES_PER_SECTOR];
|
|
unsigned char catalog_buffer[BYTES_PER_SECTOR];
|
|
unsigned char data_buffer[BYTES_PER_SECTOR];
|
|
|
|
if (apple_filename[0]<64) {
|
|
fprintf(stderr,"Error! First char of filename "
|
|
"must be ASCII 64 or above!\n");
|
|
if (!ignore_errors) return -ERROR_INVALID_FILENAME;
|
|
}
|
|
|
|
/* Check for comma in filename */
|
|
for(i=0;i<strlen(apple_filename);i++) {
|
|
if (apple_filename[i]==',') {
|
|
fprintf(stderr,"Error! "
|
|
"Cannot have , in a filename!\n");
|
|
return -ERROR_INVALID_FILENAME;
|
|
}
|
|
}
|
|
|
|
/* FIXME */
|
|
/* check type */
|
|
/* and sanity check a/b filesize is set properly */
|
|
|
|
/* Determine size of file to upload */
|
|
if (stat(filename,&file_info)<0) {
|
|
fprintf(stderr,"Error! %s not found!\n",filename);
|
|
return -ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
file_size=(int)file_info.st_size;
|
|
|
|
if (debug) printf("Filesize: %d\n",file_size);
|
|
|
|
if (file_type==ADD_BINARY) {
|
|
if (debug) printf("Adding 4 bytes for size/offset\n");
|
|
if (length==0) length=file_size;
|
|
file_size+=4;
|
|
}
|
|
|
|
/* We need to round up to nearest sector size */
|
|
/* Add an extra sector for the T/S list */
|
|
/* Then add extra sector for a T/S list every 122*256 bytes (~31k) */
|
|
needed_sectors=(file_size/BYTES_PER_SECTOR)+ /* round sectors */
|
|
((file_size%BYTES_PER_SECTOR)!=0)+/* tail if needed */
|
|
1+/* first T/S list */
|
|
(file_size/(122*BYTES_PER_SECTOR)); /* extra t/s lists */
|
|
|
|
/* Get free space on device */
|
|
free_space=dos33_vtoc_free_space(vtoc);
|
|
|
|
/* Check for free space */
|
|
if (needed_sectors*BYTES_PER_SECTOR>free_space) {
|
|
fprintf(stderr,"Error! Not enough free space "
|
|
"on disk image (need %d have %d)\n",
|
|
needed_sectors*BYTES_PER_SECTOR,free_space);
|
|
return -ERROR_NO_SPACE;
|
|
}
|
|
|
|
/* plus one because we need a sector for the tail */
|
|
size_in_sectors=(file_size/BYTES_PER_SECTOR)+
|
|
((file_size%BYTES_PER_SECTOR)!=0);
|
|
if (debug) printf("Need to allocate %i data sectors\n",size_in_sectors);
|
|
if (debug) printf("Need to allocate %i total sectors\n",needed_sectors);
|
|
|
|
/* Open the local file */
|
|
input_fd=open(filename,O_RDONLY);
|
|
if (input_fd<0) {
|
|
fprintf(stderr,"Error! could not open %s\n",filename);
|
|
return -ERROR_IMAGE_NOT_FOUND;
|
|
}
|
|
|
|
i=0;
|
|
while (i<size_in_sectors) {
|
|
|
|
/* Create new T/S list if necessary */
|
|
/* This is called for the initial creation */
|
|
/* but also every 122 sectors which is */
|
|
/* the max a T/S list can hold (30.5k) */
|
|
if (i%TSL_MAX_NUMBER==0) {
|
|
old_ts_list=ts_list;
|
|
|
|
/* allocate a sector for the new T/S list */
|
|
ts_list=dos33_allocate_sector(fd,vtoc);
|
|
sectors_used++;
|
|
if (ts_list<0) return ts_list;
|
|
|
|
/* clear the t/s sector */
|
|
memset(ts_buffer,0,BYTES_PER_SECTOR);
|
|
|
|
/* This field in the TSL descibes the file offset */
|
|
/* (in sectors from the beginning) that the first */
|
|
/* T/S entry represents */
|
|
/* We were setting this wrong for a while as I don't */
|
|
/* think DOS33 uses it, but apparently some programs do */
|
|
ts_buffer[TSL_OFFSET_H]=get_high_byte(i);
|
|
ts_buffer[TSL_OFFSET_L]=get_low_byte(i);
|
|
|
|
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
|
|
result=write(fd,ts_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
|
|
if (i==0) {
|
|
initial_ts_list=ts_list;
|
|
}
|
|
else {
|
|
/* we aren't the first t/s list so do special stuff */
|
|
|
|
/* load in the old t/s list */
|
|
lseek(fd,
|
|
DISK_OFFSET(get_high_byte(old_ts_list),
|
|
get_low_byte(old_ts_list)),
|
|
SEEK_SET);
|
|
|
|
result=read(fd,ts_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* point from old ts list to new one we just made */
|
|
ts_buffer[TSL_NEXT_TRACK]=get_high_byte(ts_list);
|
|
ts_buffer[TSL_NEXT_SECTOR]=get_low_byte(ts_list);
|
|
|
|
/* write out the old t/s list with updated info */
|
|
lseek(fd,
|
|
DISK_OFFSET(get_high_byte(old_ts_list),
|
|
get_low_byte(old_ts_list)),
|
|
SEEK_SET);
|
|
|
|
result=write(fd,ts_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* allocate a sector */
|
|
data_ts=dos33_allocate_sector(fd,vtoc);
|
|
|
|
/* handle error */
|
|
if (data_ts<0) return data_ts;
|
|
|
|
sectors_used++;
|
|
|
|
/* clear data sector */
|
|
memset(data_buffer,0,BYTES_PER_SECTOR);
|
|
|
|
/* read from input */
|
|
if ((first_write) && (file_type==ADD_BINARY)) {
|
|
first_write=0;
|
|
data_buffer[0]=address&0xff;
|
|
data_buffer[1]=(address>>8)&0xff;
|
|
data_buffer[2]=(length)&0xff;
|
|
data_buffer[3]=((length)>>8)&0xff;
|
|
result=read(input_fd,data_buffer+4,
|
|
BYTES_PER_SECTOR-4);
|
|
bytes_read=result+4;
|
|
}
|
|
else {
|
|
result=read(input_fd,data_buffer,
|
|
BYTES_PER_SECTOR);
|
|
bytes_read=result;
|
|
}
|
|
|
|
/* Note, we might not read a full sector worth */
|
|
/* if the file is smaller */
|
|
if (result<0) {
|
|
fprintf(stderr,"Error reading input file!\n");
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
first_write=0;
|
|
|
|
/* write to disk image */
|
|
lseek(fd,DISK_OFFSET((data_ts>>8)&0xff,data_ts&0xff),SEEK_SET);
|
|
result=write(fd,data_buffer,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
|
|
if (debug) {
|
|
printf("Wrote %i bytes to %i/%i\n",
|
|
bytes_read,(data_ts>>8)&0xff,data_ts&0xff);
|
|
}
|
|
|
|
/* add to T/s table */
|
|
|
|
/* read in t/s list */
|
|
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
|
|
result=read(fd,ts_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* point to new data sector */
|
|
ts_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST]=(data_ts>>8)&0xff;
|
|
ts_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST+1]=(data_ts&0xff);
|
|
|
|
/* write t/s list back out */
|
|
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
|
|
result=write(fd,ts_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* Add new file to Catalog */
|
|
|
|
catalog_track=vtoc[VTOC_CATALOG_T];
|
|
catalog_sector=vtoc[VTOC_CATALOG_S];
|
|
|
|
continue_parsing_catalog:
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result!=BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Catalog: Error, only read %d bytes at $%02X:$%02X (%s)\n",
|
|
result,catalog_track,catalog_sector,strerror(errno));
|
|
return -ERROR_NO_SPACE;
|
|
}
|
|
|
|
/* Find empty directory entry */
|
|
i=0;
|
|
while(i<7) {
|
|
/* for undelete purposes might want to skip 0xff */
|
|
/* (deleted) files first and only use if no room */
|
|
|
|
if ((catalog_buffer[CATALOG_FILE_LIST+
|
|
(i*CATALOG_ENTRY_SIZE)]==0xff) ||
|
|
(catalog_buffer[CATALOG_FILE_LIST+
|
|
(i*CATALOG_ENTRY_SIZE)]==0x00)) {
|
|
goto got_a_dentry;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
catalog_track=catalog_buffer[CATALOG_NEXT_T];
|
|
catalog_sector=catalog_buffer[CATALOG_NEXT_S];
|
|
|
|
if ((catalog_track==0x0) && (catalog_sector==0)) {
|
|
/* FIXME: should really check this before we */
|
|
/* allocate space for the file, we leak */
|
|
|
|
/* if full, we have no recourse! */
|
|
/* can we auto-allocate new catalog sectors? */
|
|
/* and point to them?? */
|
|
fprintf(stderr,"Error! No more room for files!\n");
|
|
return -ERROR_CATALOG_FULL;
|
|
}
|
|
|
|
goto continue_parsing_catalog;
|
|
|
|
got_a_dentry:
|
|
// printf("Adding file at entry %i of catalog 0x%x:0x%x\n",
|
|
// i,catalog_track,catalog_sector);
|
|
|
|
/* Point entry to initial t/s list */
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)]=(initial_ts_list>>8)&0xff;
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+1]=(initial_ts_list&0xff);
|
|
/* set file type */
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_TYPE]=
|
|
dos33_char_to_type(dos_type,0);
|
|
|
|
// printf("Pointing T/S to %x/%x\n",(initial_ts_list>>8)&0xff,initial_ts_list&0xff);
|
|
|
|
/* copy over filename */
|
|
for(x=0;x<strlen(apple_filename);x++) {
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=
|
|
apple_filename[x]^0x80;
|
|
}
|
|
|
|
/* pad out the filename with spaces */
|
|
for(x=strlen(apple_filename);x<FILE_NAME_SIZE;x++) {
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=' '^0x80;
|
|
}
|
|
|
|
/* fill in filesize in sectors */
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_L]=
|
|
sectors_used&0xff;
|
|
catalog_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_H]=
|
|
(sectors_used>>8)&0xff;
|
|
|
|
/* write out catalog sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=write(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_CATALOG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* load a file. fts=entry/track/sector */
|
|
static int dos33_load_file(int fd,int fts,char *filename) {
|
|
|
|
int output_fd;
|
|
int catalog_file,catalog_track,catalog_sector;
|
|
int file_type,file_size=-1,tsl_track,tsl_sector,data_t,data_s;
|
|
unsigned char data_sector[BYTES_PER_SECTOR];
|
|
unsigned char sector_buffer[BYTES_PER_SECTOR];
|
|
int tsl_pointer=0,output_pointer=0;
|
|
int result;
|
|
int total_sectors=0,last_output_pointer=0;
|
|
|
|
/* FIXME! Warn if overwriting file! */
|
|
output_fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
|
|
if (output_fd<0) {
|
|
fprintf(stderr,"Error! could not open %s for local save\n",
|
|
filename);
|
|
return -ERROR_CANNOT_OPEN;
|
|
}
|
|
|
|
catalog_file=fts>>16;
|
|
catalog_track=(fts>>8)&0xff;
|
|
catalog_sector=(fts&0xff);
|
|
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
tsl_track=sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+FILE_TS_LIST_T];
|
|
tsl_sector=sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+FILE_TS_LIST_S];
|
|
file_type=dos33_file_type(sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+FILE_TYPE]);
|
|
|
|
if (debug) printf("Using TSL at %02X:%02X\n",tsl_track,tsl_sector);
|
|
if (debug) printf("file_type: %c\n",file_type);
|
|
|
|
keep_saving:
|
|
/* Read in TSL Sector */
|
|
lseek(fd,DISK_OFFSET(tsl_track,tsl_sector),SEEK_SET);
|
|
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
tsl_pointer=0;
|
|
|
|
/* check each track/sector pair in the list */
|
|
while(tsl_pointer<TSL_MAX_NUMBER) {
|
|
|
|
/* get the t/s value */
|
|
data_t=sector_buffer[TSL_LIST+(tsl_pointer*TSL_ENTRY_SIZE)];
|
|
data_s=sector_buffer[TSL_LIST+(tsl_pointer*TSL_ENTRY_SIZE)+1];
|
|
|
|
if ((data_s==0) && (data_t==0)) {
|
|
/* empty */
|
|
/* this is complicated, can be a "hole" if in middle */
|
|
/* or else ignored if at the end */
|
|
}
|
|
else {
|
|
lseek(fd,DISK_OFFSET(data_t,data_s),SEEK_SET);
|
|
result=read(fd,&data_sector,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* some file formats have the size in the first sector */
|
|
/* so cheat and get real file size from file itself */
|
|
if (output_pointer==0) {
|
|
switch(file_type) {
|
|
case 'A':
|
|
case 'I':
|
|
file_size=data_sector[0]+
|
|
(data_sector[1]<<8)+2;
|
|
break;
|
|
case 'B':
|
|
file_size=data_sector[2]+
|
|
(data_sector[3]<<8)+4;
|
|
break;
|
|
default:
|
|
file_size=-1;
|
|
}
|
|
if (debug) printf("File size = %d\n",file_size);
|
|
}
|
|
|
|
/* write the block read in out to the output file */
|
|
lseek(output_fd,output_pointer*BYTES_PER_SECTOR,SEEK_SET);
|
|
result=write(output_fd,&data_sector,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
last_output_pointer=output_pointer+1;
|
|
}
|
|
output_pointer++;
|
|
tsl_pointer++;
|
|
}
|
|
|
|
total_sectors=last_output_pointer;
|
|
|
|
/* finished with TSL sector, see if we have another */
|
|
tsl_track=sector_buffer[TSL_NEXT_TRACK];
|
|
tsl_sector=sector_buffer[TSL_NEXT_SECTOR];
|
|
|
|
if ((tsl_track==0) && (tsl_sector==0)) {
|
|
}
|
|
else {
|
|
if (debug) printf("Next track/sector=%02X:%02X op=%d\n",
|
|
tsl_track,tsl_sector,
|
|
output_pointer*BYTES_PER_SECTOR);
|
|
goto keep_saving;
|
|
}
|
|
|
|
/* Correct the file size */
|
|
/* Note: this can cause issues if the original disk image */
|
|
/* was doing something extra-clever and intentionally */
|
|
/* had a mis-matched file size */
|
|
if (file_size>=0) {
|
|
|
|
if (debug) printf("Total size %d sectors (%d bytes)\n",
|
|
total_sectors,total_sectors*BYTES_PER_SECTOR);
|
|
|
|
/* Tricky, 0...255 will give sectors of 0 */
|
|
/* 256...511 will give sectors of 1 */
|
|
if (((file_size-1)/256)!=total_sectors-1) {
|
|
printf("Warning! Total size %d not within "
|
|
"256 bytes of sector total (%d %d)!\n",
|
|
file_size,file_size/256,total_sectors);
|
|
printf("This could mean your program is trying "
|
|
"to be clever. Not truncating.\n");
|
|
}
|
|
else {
|
|
printf("Auto-truncating file size to %d\n",file_size);
|
|
result=ftruncate(output_fd,file_size);
|
|
if (result<0) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* lock a file. fts=entry/track/sector */
|
|
static int dos33_lock_file(int fd,int fts,int lock) {
|
|
|
|
int catalog_file,catalog_track,catalog_sector;
|
|
int file_type,result;
|
|
unsigned char sector_buffer[BYTES_PER_SECTOR];
|
|
|
|
catalog_file=fts>>16;
|
|
catalog_track=(fts>>8)&0xff;
|
|
catalog_sector=(fts&0xff);
|
|
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
file_type=sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)
|
|
+FILE_TYPE];
|
|
|
|
if (lock) file_type|=0x80;
|
|
else file_type&=0x7f;
|
|
|
|
sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)
|
|
+FILE_TYPE]=file_type;
|
|
|
|
/* write back modified catalog sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_CATALOG;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* rename a file. fts=entry/track/sector */
|
|
/* FIXME: can we rename a locked file? */
|
|
/* FIXME: validate the new filename is valid */
|
|
static int dos33_rename_file(int fd,int fts,char *new_name) {
|
|
|
|
int catalog_file,catalog_track,catalog_sector;
|
|
int x,result;
|
|
unsigned char sector_buffer[BYTES_PER_SECTOR];
|
|
|
|
catalog_file=fts>>16;
|
|
catalog_track=(fts>>8)&0xff;
|
|
catalog_sector=(fts&0xff);
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* copy over filename */
|
|
for(x=0;x<strlen(new_name);x++) {
|
|
sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+
|
|
FILE_NAME+x]=new_name[x]^0x80;
|
|
}
|
|
|
|
/* pad out the filename with spaces */
|
|
for(x=strlen(new_name);x<FILE_NAME_SIZE;x++) {
|
|
sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+
|
|
FILE_NAME+x]=' '^0x80;
|
|
}
|
|
|
|
/* write back modified catalog sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_CATALOG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* undelete a file. fts=entry/track/sector */
|
|
/* FIXME: validate the new filename is valid */
|
|
static int dos33_undelete_file(int fd,int fts,char *new_name) {
|
|
|
|
int catalog_file,catalog_track,catalog_sector;
|
|
char replacement_char;
|
|
int result;
|
|
unsigned char sector_buffer[BYTES_PER_SECTOR];
|
|
|
|
catalog_file=fts>>16;
|
|
catalog_track=(fts>>8)&0xff;
|
|
catalog_sector=(fts&0xff);
|
|
|
|
/* Read in Catalog Sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* get the stored track value, and put it back */
|
|
/* FIXME: should walk file to see if T/s valild */
|
|
/* by setting the track value to FF which indicates deleted file */
|
|
sector_buffer[CATALOG_FILE_LIST+(catalog_file*CATALOG_ENTRY_SIZE)]=
|
|
sector_buffer[CATALOG_FILE_LIST+
|
|
(catalog_file*CATALOG_ENTRY_SIZE)+
|
|
FILE_NAME+29];
|
|
|
|
/* restore file name if possible */
|
|
replacement_char=0xa0;
|
|
if (strlen(new_name)>29) {
|
|
replacement_char=new_name[29]^0x80;
|
|
}
|
|
|
|
sector_buffer[CATALOG_FILE_LIST+(catalog_file*CATALOG_ENTRY_SIZE)+
|
|
FILE_NAME+29]=replacement_char;
|
|
|
|
/* write back modified catalog sector */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
|
|
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"Error on I/O\n");
|
|
return -ERROR_CATALOG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int dos33_delete_file(unsigned char *vtoc,int fd,int fsl) {
|
|
|
|
int i;
|
|
int catalog_track,catalog_sector,catalog_entry;
|
|
int ts_track,ts_sector;
|
|
char file_type;
|
|
int result;
|
|
unsigned char catalog_buffer[BYTES_PER_SECTOR];
|
|
|
|
/* unpack file/track/sector info */
|
|
catalog_entry=fsl>>16;
|
|
catalog_track=(fsl>>8)&0xff;
|
|
catalog_sector=(fsl&0xff);
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"DELETE: deleting file T=%d S=%d E=%d\n",
|
|
catalog_track,catalog_sector,catalog_entry);
|
|
}
|
|
|
|
/* Load in the catalog table for the file */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
file_type=catalog_buffer[CATALOG_FILE_LIST+
|
|
(catalog_entry*CATALOG_ENTRY_SIZE)
|
|
+FILE_TYPE];
|
|
|
|
if (file_type&0x80) {
|
|
fprintf(stderr,"File is locked! Unlock before deleting!\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* get pointer to t/s list */
|
|
ts_track=catalog_buffer[CATALOG_FILE_LIST+
|
|
catalog_entry*CATALOG_ENTRY_SIZE+FILE_TS_LIST_T];
|
|
ts_sector=catalog_buffer[CATALOG_FILE_LIST+
|
|
catalog_entry*CATALOG_ENTRY_SIZE+FILE_TS_LIST_S];
|
|
|
|
keep_deleting:
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"\tLoading T/S list T=%d S=%d\n",
|
|
ts_track,ts_sector);
|
|
}
|
|
|
|
/* load in the t/s list info */
|
|
lseek(fd,DISK_OFFSET(ts_track,ts_sector),SEEK_SET);
|
|
result=read(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"delete: error reading catalog\n");
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* Free each sector listed by t/s list */
|
|
for(i=0;i<TSL_MAX_NUMBER;i++) {
|
|
/* If t/s = 0/0 then no need to clear */
|
|
if ((catalog_buffer[TSL_LIST+2*i]==0) &&
|
|
(catalog_buffer[TSL_LIST+2*i+1]==0)) {
|
|
}
|
|
else {
|
|
if (debug) {
|
|
fprintf(stderr,"\tfreeing T=%d S=%d\n",
|
|
catalog_buffer[TSL_LIST+2*i],
|
|
catalog_buffer[TSL_LIST+2*i+1]);
|
|
}
|
|
dos33_free_sector(vtoc,fd,catalog_buffer[TSL_LIST+2*i],
|
|
catalog_buffer[TSL_LIST+2*i+1]);
|
|
}
|
|
}
|
|
|
|
/* free the t/s list */
|
|
dos33_free_sector(vtoc,fd,ts_track,ts_sector);
|
|
if (debug) {
|
|
fprintf(stderr,"\tfreeing T/S list T=%d S=%d\n",
|
|
ts_track,ts_sector);
|
|
}
|
|
|
|
/* Point to next t/s list */
|
|
ts_track=catalog_buffer[TSL_NEXT_TRACK];
|
|
ts_sector=catalog_buffer[TSL_NEXT_SECTOR];
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"\tNext T/S list T=%d S=%d\n",
|
|
ts_track,ts_sector);
|
|
}
|
|
|
|
/* If more tsl lists, keep looping */
|
|
if ((ts_track==0x0) && (ts_sector==0x0)) {
|
|
}
|
|
else {
|
|
goto keep_deleting;
|
|
}
|
|
|
|
/* Erase file from catalog entry */
|
|
|
|
/* First reload proper catalog sector */
|
|
if (debug) {
|
|
fprintf(stderr,"\treloading T=%d S=%d\n",
|
|
catalog_track,catalog_sector);
|
|
}
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=read(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"delete: error reading catalog\n");
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
/* save track as last char of name, for undelete purposes */
|
|
catalog_buffer[CATALOG_FILE_LIST+(catalog_entry*CATALOG_ENTRY_SIZE)+
|
|
(FILE_NAME+FILE_NAME_SIZE-1)]=
|
|
catalog_buffer[CATALOG_FILE_LIST+(catalog_entry*CATALOG_ENTRY_SIZE)];
|
|
|
|
/* Actually delete the file */
|
|
/* by setting the track value to FF which indicates deleted file */
|
|
catalog_buffer[CATALOG_FILE_LIST+(catalog_entry*CATALOG_ENTRY_SIZE)]=
|
|
0xff;
|
|
|
|
/* re seek to catalog position and write out changes */
|
|
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
|
|
result=write(fd,catalog_buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
fprintf(stderr,"delete: error writing catalog\n");
|
|
return -ERROR_CATALOG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ??? */
|
|
static int dos33_rename_hello(int fd, char *new_name) {
|
|
|
|
char buffer[BYTES_PER_SECTOR];
|
|
int i,result;
|
|
|
|
lseek(fd,DISK_OFFSET(1,9),SEEK_SET);
|
|
result=read(fd,buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_READ;
|
|
}
|
|
|
|
for(i=0;i<30;i++) {
|
|
if (i<strlen(new_name)) {
|
|
buffer[0x75+i]=new_name[i]|0x80;
|
|
}
|
|
else {
|
|
buffer[0x75+i]=' '|0x80;
|
|
}
|
|
}
|
|
|
|
lseek(fd,DISK_OFFSET(1,9),SEEK_SET);
|
|
result=write(fd,buffer,BYTES_PER_SECTOR);
|
|
if (result<BYTES_PER_SECTOR) {
|
|
return -ERROR_FILE_WRITE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void display_help(char *name, int version_only) {
|
|
printf("\ndos33 version %s\n",VERSION);
|
|
printf("by Vince Weaver <vince@deater.net>\n");
|
|
printf("\n");
|
|
|
|
if (version_only) return;
|
|
|
|
printf("Usage: %s [-h] [-d] [-y] [-x] disk_image COMMAND [options]\n",name);
|
|
printf("\t-h : this help message\n");
|
|
printf("\t-d : enable debugging\n");
|
|
printf("\t-y : always answer yes for anying warning questions\n");
|
|
printf("\t-x : ignore errors (useful for making invalid filenames)\n");
|
|
printf("\n");
|
|
printf(" Where disk_image is a valid dos3.3 disk image\n"
|
|
" and COMMAND is one of the following:\n");
|
|
printf("\tCATALOG\n");
|
|
printf("\tLOAD apple_file <local_file>\n");
|
|
printf("\tSAVE type local_file <apple_file>\n");
|
|
printf("\tBSAVE [-a addr] [-l len] local_file <apple_file>\n");
|
|
printf("\tDELETE apple_file\n");
|
|
printf("\tLOCK apple_file\n");
|
|
printf("\tUNLOCK apple_file\n");
|
|
printf("\tRENAME apple_file_old apple_file_new\n");
|
|
printf("\tUNDELETE apple_file\n");
|
|
printf("\tDUMP\n");
|
|
printf("\tHELLO apple_file\n");
|
|
#if 0
|
|
printf("\tINIT\n");
|
|
printf("\tCOPY\n");
|
|
#endif
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
#define COMMAND_LOAD 0
|
|
#define COMMAND_SAVE 1
|
|
#define COMMAND_CATALOG 2
|
|
#define COMMAND_DELETE 3
|
|
#define COMMAND_UNDELETE 4
|
|
#define COMMAND_LOCK 5
|
|
#define COMMAND_UNLOCK 6
|
|
#define COMMAND_INIT 7
|
|
#define COMMAND_RENAME 8
|
|
#define COMMAND_COPY 9
|
|
#define COMMAND_DUMP 10
|
|
#define COMMAND_HELLO 11
|
|
#define COMMAND_BSAVE 12
|
|
#define COMMAND_BLOAD 13
|
|
#define COMMAND_SHOWFREE 14
|
|
#define COMMAND_RAW_WRITE 15
|
|
|
|
#define MAX_COMMAND 15
|
|
#define COMMAND_UNKNOWN 255
|
|
|
|
static struct command_type {
|
|
int type;
|
|
char name[32];
|
|
} commands[MAX_COMMAND] = {
|
|
{COMMAND_LOAD,"LOAD"},
|
|
{COMMAND_SAVE,"SAVE"},
|
|
{COMMAND_CATALOG,"CATALOG"},
|
|
{COMMAND_DELETE,"DELETE"},
|
|
{COMMAND_UNDELETE,"UNDELETE"},
|
|
{COMMAND_LOCK,"LOCK"},
|
|
{COMMAND_UNLOCK,"UNLOCK"},
|
|
{COMMAND_INIT,"INIT"},
|
|
{COMMAND_RENAME,"RENAME"},
|
|
{COMMAND_COPY,"COPY"},
|
|
{COMMAND_DUMP,"DUMP"},
|
|
{COMMAND_HELLO,"HELLO"},
|
|
{COMMAND_BSAVE,"BSAVE"},
|
|
{COMMAND_SHOWFREE,"SHOWFREE"},
|
|
{COMMAND_RAW_WRITE,"RAWWRITE"},
|
|
};
|
|
|
|
static int lookup_command(char *name) {
|
|
|
|
int which=COMMAND_UNKNOWN,i;
|
|
|
|
for(i=0;i<MAX_COMMAND;i++) {
|
|
if(!strncmp(name,commands[i].name,strlen(commands[i].name))) {
|
|
which=commands[i].type;
|
|
break;
|
|
}
|
|
}
|
|
return which;
|
|
|
|
}
|
|
|
|
static int truncate_filename(char *out, char *in) {
|
|
|
|
int truncated=0;
|
|
|
|
/* Truncate filename if too long */
|
|
if (strlen(in)>30) {
|
|
fprintf(stderr,"Warning! Truncating %s to 30 chars\n",in);
|
|
truncated=1;
|
|
}
|
|
strncpy(out,in,30);
|
|
out[30]='\0';
|
|
|
|
return truncated;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
char image[BUFSIZ];
|
|
unsigned char type='b';
|
|
int dos_fd=0,i;
|
|
|
|
int command,catalog_entry;
|
|
char temp_string[BUFSIZ];
|
|
char apple_filename[31],new_filename[31];
|
|
char local_filename[BUFSIZ];
|
|
char *result_string;
|
|
int always_yes=0;
|
|
char *temp,*endptr;
|
|
int c;
|
|
int address=0, length=0;
|
|
unsigned char vtoc[BYTES_PER_SECTOR];
|
|
int retval=0;
|
|
|
|
/* Check command line arguments */
|
|
while ((c = getopt (argc, argv,"a:l:t:s:dhvxy"))!=-1) {
|
|
switch (c) {
|
|
|
|
case 'd':
|
|
fprintf(stderr,"DEBUG enabled\n");
|
|
debug=1;
|
|
break;
|
|
case 'a':
|
|
address=strtol(optarg,&endptr,0);
|
|
if (debug) fprintf(stderr,"Address=%d\n",address);
|
|
break;
|
|
case 'l':
|
|
length=strtol(optarg,&endptr,0);
|
|
if (debug) fprintf(stderr,"Length=%d\n",address);
|
|
break;
|
|
#if 0
|
|
case 't':
|
|
track=strtol(optarg,&endptr,0);
|
|
if (debug) fprintf(stderr,"Track=%d\n",address);
|
|
break;
|
|
case 's':
|
|
sector=strtol(optarg,&endptr,0);
|
|
if (debug) fprintf(stderr,"Sector=%d\n",address);
|
|
break;
|
|
#endif
|
|
case 'v':
|
|
display_help(argv[0],1);
|
|
return 0;
|
|
case 'h': display_help(argv[0],0);
|
|
return 0;
|
|
case 'x':
|
|
ignore_errors=1;
|
|
break;
|
|
case 'y':
|
|
always_yes=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind==argc) {
|
|
fprintf(stderr,"ERROR! Must specify disk image!\n\n");
|
|
return -ERROR_INVALID_PARAMATER;
|
|
}
|
|
|
|
/* get argument 1, which is image name */
|
|
strncpy(image,argv[optind],BUFSIZ-1);
|
|
dos_fd=open(image,O_RDWR);
|
|
if (dos_fd<0) {
|
|
fprintf(stderr,"Error opening disk_image: %s\n",image);
|
|
return -ERROR_FILE_NOT_FOUND;
|
|
}
|
|
retval=dos33_read_vtoc(dos_fd,vtoc);
|
|
if (retval<0) goto exit_and_close;
|
|
|
|
/* Move to next argument */
|
|
optind++;
|
|
|
|
if (optind==argc) {
|
|
fprintf(stderr,"ERROR! Must specify command!\n\n");
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
/* Grab command */
|
|
strncpy(temp_string,argv[optind],BUFSIZ-1);
|
|
|
|
/* Make command be uppercase */
|
|
for(i=0;i<strlen(temp_string);i++) {
|
|
temp_string[i]=toupper(temp_string[i]);
|
|
}
|
|
|
|
/* Move to next argument */
|
|
optind++;
|
|
|
|
command=lookup_command(temp_string);
|
|
|
|
switch(command) {
|
|
|
|
case COMMAND_UNKNOWN:
|
|
fprintf(stderr,"ERROR! Unknown command %s\n",temp_string);
|
|
fprintf(stderr,"\tTry \"%s -h\" for help.\n\n",argv[0]);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
break;
|
|
|
|
/* Load a file from disk image to local machine */
|
|
case COMMAND_LOAD:
|
|
|
|
/* check and make sure we have apple_filename */
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need apple file_name\n");
|
|
fprintf(stderr,"%s %s LOAD apple_filename\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
|
|
if (debug) printf("\tApple filename: %s\n",apple_filename);
|
|
|
|
/* get output filename */
|
|
optind++;
|
|
if (argc>optind) {
|
|
if (debug) printf("Using %s for filename\n",
|
|
local_filename);
|
|
strncpy(local_filename,argv[optind],BUFSIZ-1);
|
|
}
|
|
else {
|
|
if (debug) printf("Using %s for filename\n",
|
|
apple_filename);
|
|
strncpy(local_filename,apple_filename,31);
|
|
}
|
|
|
|
if (debug) printf("\tOutput filename: %s\n",local_filename);
|
|
|
|
|
|
/* get the entry/track/sector for file */
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr,"Error! %s not found!\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
retval=dos33_load_file(dos_fd,catalog_entry,local_filename);
|
|
|
|
break;
|
|
|
|
case COMMAND_CATALOG:
|
|
retval=dos33_read_vtoc(dos_fd,vtoc);
|
|
if (retval<0) return retval;
|
|
|
|
dos33_catalog(dos_fd,vtoc);
|
|
|
|
break;
|
|
|
|
case COMMAND_SAVE:
|
|
/* argv3 == type == A,B,T,I,N,L etc */
|
|
/* argv4 == name of local file */
|
|
/* argv5 == optional name of file on disk image */
|
|
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need type and file_name\n");
|
|
fprintf(stderr,"%s %s SAVE type "
|
|
"file_name apple_filename\n\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
type=argv[optind][0];
|
|
optind++;
|
|
|
|
case COMMAND_BSAVE:
|
|
|
|
if (debug) printf("\ttype=%c\n",type);
|
|
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need file_name\n");
|
|
|
|
if (command==COMMAND_BSAVE) {
|
|
fprintf(stderr,"%s %s BSAVE "
|
|
"file_name apple_filename\n\n",
|
|
argv[0],image);
|
|
|
|
}
|
|
else {
|
|
fprintf(stderr,"%s %s SAVE type "
|
|
"file_name apple_filename\n\n",
|
|
argv[0],image);
|
|
}
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
strncpy(local_filename,argv[optind],BUFSIZ-1);
|
|
optind++;
|
|
|
|
if (debug) printf("\tLocal filename: %s\n",local_filename);
|
|
|
|
if (argc>optind) {
|
|
/* apple filename specified */
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
}
|
|
else {
|
|
/* If no filename specified for apple name */
|
|
/* Then use the input name. Note, we strip */
|
|
/* everything up to the last slash so useless */
|
|
/* path info isn't used */
|
|
|
|
temp=local_filename+(strlen(local_filename)-1);
|
|
|
|
while(temp!=local_filename) {
|
|
temp--;
|
|
if (*temp == '/') {
|
|
temp++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
truncate_filename(apple_filename,temp);
|
|
}
|
|
|
|
if (debug) printf("\tApple filename: %s\n",apple_filename);
|
|
|
|
catalog_entry=dos33_check_file_exists(dos_fd,apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
|
|
if (catalog_entry>=0) {
|
|
fprintf(stderr,"Warning! %s exists!\n",apple_filename);
|
|
if (!always_yes) {
|
|
printf("Over-write (y/n)?");
|
|
result_string=fgets(temp_string,BUFSIZ,stdin);
|
|
if ((result_string==NULL) || (temp_string[0]!='y')) {
|
|
printf("Exiting early...\n");
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
}
|
|
fprintf(stderr,"Deleting previous version...\n");
|
|
dos33_delete_file(vtoc,dos_fd,catalog_entry);
|
|
}
|
|
|
|
if (command==COMMAND_SAVE) {
|
|
retval=dos33_add_file(vtoc,dos_fd,type,
|
|
ADD_RAW, address, length,
|
|
local_filename,apple_filename);
|
|
}
|
|
else {
|
|
retval=dos33_add_file(vtoc,dos_fd,type,
|
|
ADD_BINARY, address, length,
|
|
local_filename,apple_filename);
|
|
}
|
|
break;
|
|
|
|
|
|
case COMMAND_RAW_WRITE:
|
|
|
|
fprintf(stderr,"ERROR! Not implemented!\n\n");
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
|
|
break;
|
|
|
|
case COMMAND_DELETE:
|
|
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need file_name\n");
|
|
fprintf(stderr,"%s %s DELETE apple_filename\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr, "Error! File %s does not exist\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
goto exit_and_close;
|
|
}
|
|
retval=dos33_delete_file(vtoc,dos_fd,catalog_entry);
|
|
|
|
break;
|
|
|
|
case COMMAND_DUMP:
|
|
printf("Dumping %s!\n",image);
|
|
dos33_dump(vtoc,dos_fd);
|
|
break;
|
|
|
|
case COMMAND_SHOWFREE:
|
|
printf("Showing Free %s!\n",image);
|
|
dos33_showfree(vtoc,dos_fd);
|
|
break;
|
|
|
|
case COMMAND_LOCK:
|
|
case COMMAND_UNLOCK:
|
|
/* check and make sure we have apple_filename */
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need apple file_name\n");
|
|
fprintf(stderr,"%s %s %s apple_filename\n",
|
|
argv[0],image,temp_string);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
|
|
/* get the entry/track/sector for file */
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr,"Error! %s not found!\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
retval=dos33_lock_file(dos_fd,catalog_entry,command==COMMAND_LOCK);
|
|
|
|
break;
|
|
|
|
case COMMAND_RENAME:
|
|
/* check and make sure we have apple_filename */
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need two filenames\n");
|
|
fprintf(stderr,"%s %s RENAME apple_filename_old "
|
|
"apple_filename_new\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
/* Truncate filename if too long */
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
optind++;
|
|
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need two filenames\n");
|
|
fprintf(stderr,"%s %s RENAME apple_filename_old "
|
|
"apple_filename_new\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
truncate_filename(new_filename,argv[optind]);
|
|
|
|
/* get the entry/track/sector for file */
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr,"Error! %s not found!\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
dos33_rename_file(dos_fd,catalog_entry,new_filename);
|
|
|
|
break;
|
|
|
|
case COMMAND_UNDELETE:
|
|
/* check and make sure we have apple_filename */
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need apple file_name\n");
|
|
fprintf(stderr,"%s %s UNDELETE apple_filename\n\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
/* Truncate filename if too long */
|
|
/* what to do about last char ? */
|
|
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
|
|
/* get the entry/track/sector for file */
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_DELETED);
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr,"Error! %s not found!\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
retval=dos33_undelete_file(dos_fd,catalog_entry,apple_filename);
|
|
|
|
break;
|
|
|
|
case COMMAND_HELLO:
|
|
if (argc==optind) {
|
|
fprintf(stderr,"Error! Need file_name\n");
|
|
fprintf(stderr,"%s %s HELLO apple_filename\n\n",
|
|
argv[0],image);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
truncate_filename(apple_filename,argv[optind]);
|
|
|
|
catalog_entry=dos33_check_file_exists(dos_fd,
|
|
apple_filename,
|
|
DOS33_FILE_NORMAL);
|
|
|
|
if (catalog_entry<0) {
|
|
fprintf(stderr,
|
|
"Warning! File %s does not exist\n",
|
|
apple_filename);
|
|
retval=catalog_entry;
|
|
}
|
|
dos33_rename_hello(dos_fd,apple_filename);
|
|
break;
|
|
|
|
case COMMAND_INIT:
|
|
/* use common code from mkdos33fs? */
|
|
case COMMAND_COPY:
|
|
/* use temp file? Walking a sector at a time seems a pain */
|
|
default:
|
|
fprintf(stderr,"Sorry, unsupported command %s\n\n",temp_string);
|
|
retval=-ERROR_INVALID_PARAMATER;
|
|
goto exit_and_close;
|
|
}
|
|
|
|
exit_and_close:
|
|
close(dos_fd);
|
|
|
|
return retval;
|
|
}
|