#include #include /* exit() */ #include /* strncpy() */ #include /* struct stat */ #include /* O_RDONLY */ #include /* lseek() */ #include /* toupper() */ #include #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 (resultVTOC_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 (resultfree_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>8)&0xff,ts_list&0xff),SEEK_SET); result=write(fd,ts_buffer,BYTES_PER_SECTOR); if (result>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>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>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>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>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>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=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>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>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 (result29) { 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>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\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 \n"); printf("\tSAVE type local_file \n"); printf("\tBSAVE [-a addr] [-l len] local_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;i30) { 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;ioptind) { 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; }