mirror of
				https://github.com/deater/dos33fsprogs.git
				synced 2025-10-31 09:16:03 +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("\tAhpple 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;
 | |
| }
 |