program CAT; {$I-} uses dos, files, { see end for this unit } crt; type arraybuf = array[1..65535] of byte; buffer = ^arraybuf; chksum = file of searchrec; procedure error(mess:string); var code:integer; begin code:= ioresult; writeln('ERROR: ', mess); {writeln('ERROR CODE: ', code);} halt(1); end; procedure delete(drive:char; var success:boolean); procedure recurse(tree:directory_tree; var success:boolean); var info:searchrec; buffer:text; success2:boolean; d:string[79]; begin if tree <> nil then begin success2:= true; d:= tree^.dir; begin recurse(tree^.lower_dir, success2); tree:= tree^.next; success:= success and success2; recurse(tree, success2); success:= success and success2; end; chdir(d); findfirst('*.*', anyfile, info); while (doserror = 0) and (success) do begin if (info.name <> '.') and (info.name <> '..') then begin assign(buffer, info.name); case info.attr of $10: rmdir(info.name); $20: erase(buffer); else success:= false; end; end; findnext(info); end; end; end; var tree:directory_tree; begin tree:= nil; chdir(drive+':\'); fill_dirtree(drive+':\', tree); success:= true; recurse(tree, success); end; function DriveExist(drive:char):boolean; var fileinfo:searchrec; begin findfirst(drive+':\*.*', anyfile, fileinfo); if doserror = 3 then driveexist:= false else driveexist:= true; end; procedure work(max,done:longint); begin write(100*(done/max):4:1, '% complete.'); gotoxy(1, wherey); end; procedure help; begin writeln('The Concatinator Version 1.0 Copyright 1996 by Jack Neely'); writeln('A large file disk storage and retrieval program.'); writeln; writeln('Usage: CAT s '); writeln(' CAT r '); writeln; writeln('Commands: ''s'' = Store ''r'' = Retrive'); writeln('Storage drive must be the disk drive to store or that a large file is'); writeln('stored apon. Specify a path where the file will be placed when'); writeln('retriving a file. Specify a filemane when storing a large file.'); writeln; writeln('You can use this program to store those large files that are larger'); writeln('than a single disk onto multiple disks. Anything on the disk prior'); writeln('to storage will be erased. A checksum file will also be stored on the'); writeln('first disk of each set.'); writeln; writeln('The author can be reached at hneely@ac.net'); writeln; halt(0); end; function num(d:char):word; begin num:= ord(upcase(d)) - 64; end; function strn(a:integer):string; var s:string; i:integer; begin str(a, s); if length(s) < 4 then for i:= 1 to 4 - length(s) do s:= '0' + s; strn:= s; end; function return(s:string; b:boolean):integer; var str:string; i, c:integer; begin str:= ''; if b then for i:= 1 to 4 do str:= str + s[i] else for i:= 5 to 8 do str:= str + s[i]; val(str, i, c); return:= i; end; procedure store(filename:string; drive:char); var input, output:file; fileinfo, test:searchrec; filedata:chksum; c, full, disk:longint; diskdone:boolean; fset, disknum:word; success:boolean; data:buffer; buffersize, readcount, writecount:word; ch:char; begin findfirst(filename, anyfile, fileinfo); if doserror <> 0 then error('File not found: ' + filename); new(data); c:= 0; disknum:= 0; diskdone:= true; if not DriveExist(drive) then error(drive+': does not exist.'); randomize; fset:= random(9999); writeln('This is file set number ', fset, '.'); assign(input, filename); reset(input, 1); while c < fileinfo.size do begin if diskdone then begin if disknum <> 0 then close(output); clreol; disk:= 0; disknum:= disknum + 1; write('Insert disk ', disknum, ' and press [ENTER].'); readln; diskdone:= false; buffersize:= sizeof(arraybuf); full:= disksize(num(drive)); if disknum = 1 then begin writeln('Approximately ', (1+(fileinfo.size div disksize(num(drive)))), ' of these disks are needed.'); write('Continue? (Y/N)'); ch:= readkey; if not ((ch = 'y') or (ch = 'Y')) then halt(0); writeln; end; if disksize(num(drive)) <> diskfree(num(drive)) then begin findfirst(drive+':\*.cat', anyfile, test); if return(test.name, true) = fset then error('This disk is of this same set.'); delete(drive, success); if not success then error('Some existing file(s) on destination disk could not be removed.'); end; assign(output, drive+':\'+strn(fset)+strn(disknum)+'.cat'); rewrite(output, 1); if disknum = 1 then begin assign(filedata, drive+':\check.sum'); rewrite(filedata); write(filedata, fileinfo); close(filedata); full:= diskfree(num(drive)); end; end; if full - disk < buffersize then begin buffersize:= full - disk; diskdone:= true; end; blockread(input, data^, buffersize, readcount); if ioresult <> 0 then error('Errors on source disk.'); blockwrite(output, data^, readcount, writecount); if ioresult <> 0 then error('Errors on target disk.'); c:= c + readcount; disk:= disk + readcount; work(fileinfo.size, c); if readcount <> writecount then error('Unable to write to disk'); end; clreol; close(input); close(output); dispose(data); end; procedure retrive(drive:char; path:string); var setnum, disknum:word; diskdone, complete:boolean; newfile, store:file; cs:chksum; fileinfo, data:searchrec; d:buffer; c:longint; buffersize, readcount, writecount:word; begin complete:= false; chdir(path); new(d); c:= 0; if ioresult <> 0 then error(path+' does not exist.'); diskdone:= true; disknum:= 0; while not complete do begin if diskdone then begin clreol; disknum:= disknum + 1; if disknum > 1 then close(store); diskdone:= false; write('Insert disk ', disknum, ' and press [ENTER].'); readln; buffersize:= sizeof(arraybuf); if disknum = 1 then begin assign(cs, drive+':\check.sum'); reset(cs); if ioresult <> 0 then error('No check sum file.'); read(cs, fileinfo); close(cs); assign(newfile, fileinfo.name); rewrite(newfile, 1); findfirst(drive+':\*.cat', archive, data); if doserror = 18 then begin close(newfile); erase(newfile); error('Disk does not contain storage data.'); end; assign(store, drive+':\'+data.name); reset(store, 1); setnum:= return(data.name, true); if return(data.name, false) <> disknum then begin close(newfile); erase(newfile); error('Wrong disk.'); end; writeln('File set number is: ', setnum); end else begin findfirst(drive+':\*.cat', archive, data); if doserror = 18 then begin close(newfile); erase(newfile); error('Disk does not contain storage data.'); end; assign(store, drive+':\'+data.name); reset(store, 1); if setnum <> return(data.name, true) then begin close(newfile); erase(newfile); error('Disk is of a different set.'); end; if disknum <> return(data.name, false) then begin close(newfile); erase(newfile); error('Wrong disk.'); end; end; end; blockread(store, d^, buffersize, readcount); if ioresult <> 0 then begin close(newfile); erase(newfile); error('Errors on source disk.'); end; blockwrite(newfile, d^, readcount, writecount); if ioresult <> 0 then begin close(newfile); erase(newfile); error('Errors on target disk.'); end; c:= c + readcount; if writecount <> readcount then begin close(newfile); erase(newfile); error('Unable to write to disk.'); end; if buffersize <> readcount then diskdone:= true; if fileinfo.size = c then complete:= true; work(fileinfo.size, c); end; clreol; close(newfile); close(store); dispose(d); end; var c1, c2:string; begin if paramcount = 0 then help; if paramcount <> 3 then error('Incorect number of parameters.'); c1:= paramstr(1); c2:= paramstr(2); case c1[1] of 's', 'S' : store(paramstr(3), c2[1]); 'r', 'R' : retrive(c2[1], paramstr(3)); else error('Incorect parameters.'); end; writeln('Complete!'); end. { --------------- CUT ---------------- } unit files; interface uses dos; type filetype = string[12]; {searchrec = record This is how searchrec is defined in the DOS unit. Fill: array[1..21] of Byte; Attr: Byte; Time: Longint; Size: Longint; Name: string[12]; end; } filestack = ^ node; node = record fileinfo:searchrec; next:filestack; end; directory_tree = ^dnode; dnode = record dir:string; lower_dir:directory_tree; next:directory_tree; end; procedure fill_filestack(var stack:filestack); {Fills stack of type filestack with all the file enteries in the current directory. Includes directoies and hidden file types.} procedure push_filestack(var stack:filestack; item:searchrec); {Pushes in alfa order a new node on a filestack.} procedure fill_dirtree(dir:string; var tree:directory_tree); {Fills a tree sturcture with the directory structure using dir string as the root.} implementation procedure push_filestack(var stack:filestack; item:searchrec); var temp:filestack; procedure insert(var stack, prev:filestack); begin if (stack = nil) then begin temp^.next:= stack; stack:= temp; end else if temp^.fileinfo.name > stack^.fileinfo.name then insert(stack^.next, stack) else if temp^.fileinfo.name < stack^.fileinfo.name then begin if prev = stack then begin temp^.next:= stack; stack:= temp; end else begin temp^.next:= stack; prev^.next:= temp; end; end; end; begin new(temp); temp^.fileinfo:= item; insert(stack, stack); end; procedure fill_filestack(var stack:filestack); var dirinfo:searchrec; begin findfirst('*.*', anyfile, dirinfo); while doserror <> 18 do begin push_filestack(stack, dirinfo); findnext(dirinfo); end; end; procedure push(var head:directory_tree; item:string); var temp:directory_tree; begin new(temp); temp^.dir:= item; temp^.next:= head; head:= temp; head^.lower_dir:= nil; end; procedure fill_dirtree(dir:string; var tree:directory_tree); procedure fill_dirlist(var head:directory_tree; directory:string; s:integer); var place:directory_tree; dirinfo:searchrec; found:boolean; begin writeln(directory); chdir(directory); findfirst('*.*', 16, dirinfo); while doserror = 0 do begin if (dirinfo.attr = 16) and ((dirinfo.name <> '..') and (dirinfo.name <> '.'))then begin push(head, fexpand(dirinfo.name)); found:= true; end; findnext(dirinfo); end; if found then begin place:= head; while place <> nil do begin fill_dirlist(place^.lower_dir, place^.dir, s+3); place:= place^.next; end; end; end; var temp:directory_tree; begin tree:= nil; fill_dirlist(tree, dir, 0); new(temp); temp^.dir:= dir; temp^.lower_dir:= tree; temp^.next:= nil; tree:= temp; end; end.