/* Splitter 3.02 This program can split files into several portions with a defined size or into a defined number of portions and can join them together again. It's main purpose is to write larger files and especially archives to disks. It was designed to compile under ANSI C and with some small modifications even under K&R C, and thus to run on any system which supports these. Version 1.0 on 18-October-1993 First reliable version. Version 1.1 on 18-October-1993 Replaced fclose()/fopen() by rewind(). Version 1.2 on 24-November-1993 Works now with MSDOS systems; uses a buffer for join. Version 1.21 on 25-November-1993 Removed a small bug in the output. Version 2.0 on 22-September-1994 Added default sizes for filesystems. Uses Buffer for read/write. Uses fileheader for recognition and restauration. Version 2.1 on 31-March-1995 Options -r (splitted portion range, only with -p and -s) and -f (force overwrite) added. Bug removed when allocating smaller buffer. Multivolume joining enhanced. Version 3.0 on 22-February-1996 Complete rewrite. Enhanced command line parsing. Joining and multivolume via command name. Version 3.01 on 27-February-1996 Removed bug in splitpath generation. Version 3.02 on 5-March-1996 Removed another small bug in splitpath generation. Cleaned up usage info. Removed bug with empty files. This program was developed on an Amiga 1200 with the SAS/C development system and the GNU emacs editor, the UNIX adaptions were written on a Linux PC with gcc and emacs. Usage: Splitter [-n] [-d] [-l] [-m] [-g] [-f] [-q] [-j {} ] [-h] [[-p ] [-s [k|m]] [-s ] [-r []] ] Joiner [-h] [-d] [-n] [-m] [-g] [-f] {} file the file to be splitted. dir directory where to put the splitted parts. sources source directories and files. destination destination path for joined file. -h show help. -n write/expect no header. -d MSDOS filename option: eight characters without dot. -l long filenames; resets the MSDOS switch. -m multivolume split/join; pause after each file. -g go ahead; resets the multivolume switch. -f overwrite existing files without asking (force). -q ask before overwriting files (query). -j join splitted parts. -p split into portions. -s split into portions of bytes size each. -r extract only portions in the range from s(tart) to e(nd). General: To get from Splitter, type the option "-h". Without options, Splitter will will do the standard action depending on the command name. If the command name contains the string "join" or "Join", splitter will react as if called with the option "-j". Otherwise it will split to portions with the size of the first file system. If the command name contains "m" or "M", the multivolume option will be set automatically. If Splitter is compiled for a MSDOS platform, the MSDOS option is set. If the macro FORCE is defined, files are overwritten by default. To compile this program on a UNIX platform, the macro UNIX must be defined; for MSDOS computers, the MSDOS macro must be defined; the AMIGA macro is normally set by the compiler on the Amiga. The strings in the text define section may be changed to another language (or better english.. :-). If you do so, please let me have these. Splitting: There are two different approaches to split a file. The one produces the requested number of portion files with the same size each. Use "-p " as options for this. The other produces several portions of the requested size. The size may be given directly in bytes, kbytes or mbytes. Use "-s [k|m]" for this. Or it may be given as the destination disk type. Use option "-s ", where disktype may be any one of the filesystem sizes as listed by the option "-h". If no option is specified, the file is splitted as though had beed set to the first in the list. ist the path of the source file, is the path of the (optional) destination directory. The option "-d" makes the destination filenames msdos compliant. If "-n" is given, no header is written, the portions may be joined then by AmigaDOS' Join or unix' cat command. "-r []" extracts only the portion or the prtions to . "-m" lets Splitter pause before writing each portion so that a removable medium may be changed. If "-f" is specified, Splitter overwrites files without asking. Joining: Calling Splitter with a name that contains the word 'join' (i.e. via softlink) or with the option "-j" will create the original file from the portions. If the destination file name is given, only one source directory is required. This and any further source directory is scanned for the needed portions, and any source file is checked if it is a portion. If the destination is a directory at least one source file must be given. The destination file name is then derived from the header or from this file's name if no header can be found. The option "-d" makes Splitter look for source files with msdos compliant filenames. "-m" lets Splitter pause before reading the next directory. The option "-f" makes Splitter overwrite files without asking. If "-n" is given, Splitter does not look for headers. DISCLAIMER: I am in no way responsible for any problems that arise directly or indirectly from the use of this program. Copyright notice: This is freeware. You may give this program to anyone you like and upload it to any mailbox, bulletin board or software server or include it on any public domain distribution on disk or on CD-ROM as long as you charge only a small fee for medium and copy and leave this file as it is. But you may not use this program for commercial purposes without prior written permission from me. You may not distribute changed versions of this program. If you have any ideas how to improve this program, please mail them to me. My mail address is included in the info #define. The author: I'm a student of computer science at the university of Tuebingen, Germany. If you like this program, please let me know. This program was intentionally made freeware. If you have written some shareware program, be so kind to let me have it for free if you use Splitter frequently. Please feel free to mail me any ideas concerning this program. My address: Martin Schlodder Uhlandstr. 18 D-72336 Balingen EMAIL: schlodder@student.uni-tuebingen.de */ /* Select the appropriate #define statement or use the define compiler option to choose the system on which you want to compile this source. These defines are neccessary to select the right path seperators. If you know any more file systems, please mail their path rules to me and i will include them. #define AMIGA #define MSDOS #define UNIX */ /* Some more filesystems for the Amiga may be enabled by the following: #define AMIFS Amiga file systems #define AMIDC AmigaOS 3.0 DirCache file system #define AMIPFS PFS file system #define AMIDS DiskSpare device */ /* If FORCE is defined, Splitter does not ask before overwriting files by default. #define FORCE */ /* If DBUG is defined, each function will print it's arguments. #define DBUG */ /* The following #define block contains every text printed by Splitter. The messages are collected to make translation easier. */ /* Info is printed if the -h option is set. */ #define info1 "\nSplitter V3.02 on March 5, 1996\n\ (c)1993-1996 by Martin Schlodder\n\n" #define info1s info1 "\ Usage: %s [-n] [-d] [-l] [-m] [-g] [-f] [-q] [-j {} ]\n\ [-h] [[-p ] [-s [k|m]] [-s ] [-r []] ]\n\ file\t\tthe file to be splitted.\n\ dir\t\tdirectory where tp put the splitted parts.\n\ sources\tsource directories and files.\n\ destination\tdestination path for joined file.\n\ -h\t\tshow this help page.\n\ -n\t\twrite/expect no header.\n\ -d\t\tMSDOS filename option: eight chars without dot.\n\ -l\t\tlong filenames; resets the MSDOS switch.\n\ -m\t\tmultivolume split/join; pause after each file.\n\ -g\t\tgo ahead; resets the multivolume switch.\n\ -f\t\toverwrite existing files fithout asking (force).\n\ -q\t\task before overwriting files (query).\n\ -j\t\tjoin splitted parts.\n\ -p\t\tsplit into portions.\n\ -s\t\tsplit into portions of [k|m]bytes size each.\n\ -r\t\textract only portions in the range from s(tart) to e(end).\n\ If neither -j nor -p nor -s are set, Splitter will create portions of\n\ the size from the first line of the list below.\n\n\ [Press to continue]" #define info1j info1 "\ Usage: %s [-h] [-n] [-d] [-l] [-m] [-g] [-f] [-q] {} \n\ sources\tsource directories and files.\n\ destination\tdestination path for joined file.\n\ -h\t\tshow this help page.\n\ -n\t\twrite/expect no header.\n\ -d\t\tMSDOS filename option: eight chars without dot.\n\ -l\t\tlong filenames; resets the MSDOS switch.\n\ -m\t\tmultivolume split/join; pause after each file.\n\ -g\t\tgo ahead; resets the multivolume switch.\n\ -f\t\toverwrite existing files fithout asking (force).\n\ -q\t\task before overwriting files (query).\n" #define info2 "\nFile system sizes: (for )" #define main_fs " %-12s - %7d bytes - %s." #define main_end "\n\ Address:\n\ Martin Schlodder\n\ Uhlandstr. 18\n\ D-72336 Balingen\n\n\ Internet:\n\ schlodder@zdv.uni-tuebingen.de\n" /* This text is shown before waiting for a key press when the -w option is set. */ #define make_multi_part "Please insert volume and press " /* This text is shown when a file will be overwritten and the -q option is set. The key definition is the positive answer (overwrite). */ #define query_overwrite_a "Overwrite file \"%s\"? [y(es)|n(o)|a(ll)] " #define query_overwrite "Overwrite file \"%s\"? [y(es)|n(o)] " #define query_yes 'y' #define query_all 'a' /* These texts are used to print the success message after joining. */ #define join_heads "Headers found." #define join_Joined "Joined \"%s\"" #define join_file ", \"%s\"" #define join_to " to \"%s\"." #define multi_join "Joined one portion to \"%s\"." #define multi_join_n "Joined %d portions to \"%s\"." /* These texts are used to print the success message after splitting. */ #define split_Splitted "Splitted \"%s\" into " #define split_portion "one portion of %d bytes." #define split_portions "%d portions of %d bytes." #define split_portion_and "one portion of %d bytes and\none portion of %d bytes." #define split_portions_and "%d portions of %d bytes and\none portion of %d bytes." #define split_Extract "Extracted portions %d to %d." #define split_Extract_one "Extracted portion %d." /* These are the error messages. */ #define err_bad_dest "Destination must be a directory." #define err_bad_file "Bad file name definition." #define err_bad_files "No portions found." #define err_bad_num "Bad number specification for option '-p'." #define err_bad_range "Range parameters illegal." #define err_bad_size "Bad size specification for option '-s'." #define err_file_empty "Source file is empty." #define err_no_args "Need a file name to split. Type '%s -h' for help." #define err_no_file "Need a file name." #define err_no_files "Destination path or portion paths must contain a filename." #define err_no_mem "Not enough memory." #define err_no_name "The source files must have a splitter format name\nif joining portions without header." #define err_no_portion "Can't find any files to join." #define err_no_source "Need a source directory or source files to join." #define err_open_dest "Couldn't open \"%s\" as destination." #define err_open_dir "Could't read directory \"%s\"." #define err_open_source "Couldn't open \"%s\" as source." #define err_portion_missing "Can't find portion %d." #define err_read "Error while reading." #define err_read_head "Error while reading header." #define err_seek "Error while seeking file." #define err_unknown_option "Unknown option '-%c'." #define err_write "Error while writing." #define err_write_abort "File not overwritten. Execution aborted." #define err_write_head "Error while writing header." #define err_wrong_portion "Portion number differs from header number." /* These are the warning messages. */ #define warn_size "WARNING! Source file size changed." #define warn_t_size "WARNING! Destination file size changed." #define warn_files_missing "WARNING! Some portions are missing." #define warn_n_files_missing "WARNING! %d portions are missing." #define warn_one_file_missing "WARNING! One portion is missing." #define warn_wrong_portion "WARNING! Wrong portion in source file list." /* These are the known file systems. */ struct fs_item { char *name; unsigned long size; char *desc; }fs[]={ {"msd-dd",730112,"MSDOS Double Density Disk"}, {"msd-hd",1457664,"MSDOS High Density Disk"}, #ifdef AMIFS {"amiofs-dd",844240,"Amiga Double Density Disk, OldFileSystem"}, {"amiofs-hd",1691408,"Amiga High Density Disk, OldFileSystem"}, {"amiffs-dd",885760,"Amiga Double Density Disk, FastFileSystem"}, {"amiffs-hd",1774592,"Amiga High Density Disk, FastFileSystem"}, #ifdef AMIDC {"amiofsdc-dd",843752,"Amiga Double Density Disk, OldFileSystem, DirCache"}, {"amiofsdc-hd",1690920,"Amiga High Density Disk, OldFileSystem, DirCache"}, {"amiffsdc-dd",884736,"Amiga Double Density Disk, FastFileSystem, DirCache"}, {"amiffsdc-hd",1774080,"Amiga High Density Disk, FastFileSystem, DirCache"}, #endif #ifdef AMIPFS {"amipfs-dd",896512,"Amiga Double Density Disk, ProfFileSystem"}, {"amipfs-hd",1797632,"Amiga High Density Disk, ProfFileSystem"}, #endif #ifdef AMIDS {"amidsofs-dd",844240,"Amiga Double Density Disk, DiskSpare, OldFileSystem"}, {"amidsofs-hd",1691408,"Amiga High Density Disk, DiskSpare, OldFileSystem"}, {"amidsffs-dd",885760,"Amiga Double Density Disk, DiskSpare, FastFileSystem"}, {"amidsffs-hd",1774592,"Amiga High Density Disk, DiskSpare, FastFileSystem"}, #ifdef AMIDC {"amidsofsdc-dd",843752,"Amiga Double Density Disk, DiskSpare, OldFileSystem, DirCache"}, {"amidsofsdc-hd",1690920,"Amiga High Density Disk, DiskSpare, OldFileSystem, DirCache"}, {"amidsffsdc-dd",884736,"Amiga Double Density Disk, DiskSpare, FastFileSystem, DirCache"}, {"amidsffsdc-hd",1774080,"Amiga High Density Disk, DiskSpare, FastFileSystem, DirCache"}, #endif #ifdef AMIPFS {"amidspfs-dd",1003008,"Amiga Double Density Disk, DiskSpare, ProfFileSystem"}, {"amidspfs-hd",2010624,"Amiga High Density Disk, DiskSpare, ProfFileSystem"}, #endif #endif #endif }; /* The maximal length of an input string: */ #define STRMAX 256 /* Standard includes. */ #include #include #include #include #include /* Each file is preceded with a fileheader (if the -n option was not set) consisting of the following structure: */ #define hd_magic "Splitter" #define hd_name (8+4+8+4+8+1) #define hd_namesize 31 struct header{ short number; unsigned long size; short tnumber; unsigned long tsize; char name[hd_namesize]; }; #define hd_size (hd_name+hd_namesize) /* This is the version information for the Amiga's shell command 'Version'. */ #ifdef AMIGA char *amiga_version="$VER: Splitter 3.02 (5.3.96) ©1993-1996 by Martin Schlodder"; #endif /* The only global variables are some status flags, the range parameters and a string buffer. */ int rstart,rend,msdos,head,multi,query; char buf[STRMAX]; /* int WriteHead(FILE *fh,header *hd) fh: File handle. hd: Pointer to header structure. returns: Nonzero if error occurs. This function writes the header from the the structure hd into the file fh. */ int WriteHead(FILE *fh,struct header *hd){ char hdbuf[hd_size]; #ifdef DBUG printf("\nWriteHead($%08X,{%d,%lu,%d,%lu,\"%s\"});\n",fh,hd->number,hd->size,hd->tnumber,hd->tsize,hd->name); #endif sprintf(hdbuf,"%s%4X%8X%4X%8X ",hd_magic,hd->number,hd->size,hd->tnumber,hd->tsize); memcpy(hdbuf+hd_name,hd->name,hd_namesize); if(fwrite(hdbuf,1,hd_size,fh)!=hd_size) return(1); return(0); } /* int ReadHead(FILE *fh,header *hd) fh: File handle. hd: Pointer to header structure. returns: Nonzero if error occurs. This function reads the header from the file fh into the structure hd. */ int ReadHead(FILE *fh,struct header *hd){ char buf[hd_size]; #ifdef DBUG printf("\nReadHead($%x,$%x);\n",fh,hd); #endif if(fread(buf,1,hd_size,fh)!=hd_size){ #ifdef DBUG puts("\nReadHead() -> 1"); #endif return(1); } if(4!=sscanf(buf,hd_magic "%4hx%8lx%4hx%8lx ",&hd->number,&hd->size,&hd->tnumber,&hd->tsize)){ #ifdef DBUG puts("\nReadHead() -> 1"); #endif return(1); } memcpy(hd->name,buf+hd_name,hd_namesize); #ifdef DBUG printf("\nReadHead() -> 0 hd: {%d,%lu,%d,%lu,\"%s\"}\n",hd->number,hd->size,hd->tnumber,hd->tsize,hd->name); #endif return(0); } /* Join(char *dest,char *paths[],int num) dest: Destination path or empty string. paths: Array of source paths. num: Number of source paths. Global: msdos: -use- Filenames of portions are supposed to be MSDOS compatible (eight characters, no dots). head: -use- Try to find header. query: -use- Ask before overwriting existing files. multi: -use- Join from multiple volumes using MultiJoin(). Calls: ReadHead: Read header from portion file. Join() joins the portions to the original file. Dest is a directory or file, paths is NULL if num is 0. Frees 'paths' allocated by main(). */ void Join(char *dest,char **paths,int num){ int i,j,k,fnum,dnum,dir,tnum,chunks; unsigned long tsize,size,asize,read,written,twritten; int *isdir,*findex,*jindex; char *sbuf,*tbuf,*dfile,*pos,*name; char **sfiles,**files, **dirs; FILE *source,*dhand; DIR *dfd; struct header hd; #ifdef DBUG printf("\nJoin(\"%s\",({",dest); for(i=0;isize?hd.size:size; findex[0]=hd.number; } else{ if(fseek(source,0L,SEEK_END)){ puts(err_seek); fclose(source); free(findex); free(files); if(dnum) free(dirs); exit(EXIT_FAILURE); } size=(asize=ftell(source))>size?asize:size; } fclose(source); for(i=1;isize?hd.size:size; findex[i]=hd.number; } else{ if(fseek(source,0L,SEEK_END)){ puts(err_seek); fclose(source); free(findex); free(files); if(dnum) free(dirs); exit(EXIT_FAILURE); } size=(asize=ftell(source))>size?asize:size; } fclose(source); } if(head){ tnum=hd.tnumber; tsize=hd.tsize; } else{ for(i=0;itnum?findex[i]:tnum; } if(!(jindex=(int *)calloc(tnum,sizeof(int)))){ puts(err_no_mem); free(findex); free(files); if(dnum) free(dirs); exit(EXIT_FAILURE); } for(i=0;i8) name[8]='\0'; } if(dnum){ if(!(sfiles=(char **)malloc(dnum*sizeof(char *)))){ puts(err_no_mem); free(name); if(fnum){ free(jindex); free(files); } free(dirs); if(dir) free(dfile); exit(EXIT_FAILURE); } for(i=0;i10000 && !tbuf) tbuf=(char *)malloc(asize=(asize+1)/2); if(!tbuf){ puts(err_no_mem); fclose(dhand); free(name); if(dnum){ for(i=0;i0){ if(!(source=fopen(files[j-1],"rb"))){ printf(err_open_source "\n",files[j-1]); fclose(source); fclose(dhand); free(tbuf); free(name); if(dnum){ for(i=0;i=tnum) break; printf("\n" err_portion_missing "\n",i+1); fclose(dhand); free(tbuf); free(name); free(jindex); free(files); if(dir) free(dfile); exit(EXIT_FAILURE); } if(multi && (i||fnum)){ puts(make_multi_part); gets(buf); } for(j=0;j=tnum) break; printf("\n" err_portion_missing "\n",i+1); fclose(dhand); free(tbuf); free(name); for(i=0;i8) dfile[8]='\0'; } if(head) sprintf(hd.name,"%.*s",hd_namesize-1,name); if(!(dbuf=(char *)malloc(strlen(dpath)+strlen(dfile)+5))){ puts(err_no_mem); fclose(source); free(dfile); free(dpath); exit(EXIT_FAILURE); } if(fseek(source,0L,SEEK_END)){ puts(err_seek); fclose(source); free(dfile); free(dpath); free(dbuf); exit(EXIT_FAILURE); } if(!(tsize=ftell(source))){ puts(err_file_empty); fclose(source); free(dfile); free(dpath); free(dbuf); exit(EXIT_FAILURE); } if(size){ num=(tsize+size-1)/size; size=size10000 && !tbuf) tbuf=(char *)malloc(csize=(csize+1)/2); if(!tbuf){ puts(err_no_mem); fclose(source); free(dfile); free(dpath); free(dbuf); exit(EXIT_FAILURE); } } cnum=(size+csize-1)/csize; if(rstart){ i=rstart; if(!rend) rend=rstart; if(rend>num || rstart>rend){ puts(err_bad_range); fclose(source); free(dfile); free(dpath); free(dbuf); free(tbuf); exit(EXIT_FAILURE); } } else{ i=1; rend=num; } if(fseek(source,(i-1)*size,SEEK_SET)){ puts(err_seek); fclose(source); free(dfile); free(dpath); free(dbuf); free(tbuf); exit(EXIT_FAILURE); } for(rsize=tsize+(2-i)*size;i<=rend;i++){ rsize-=size; asize=(rsize