Program extar; { Extract from TAR file, correcting names to be acceptable for MS-DOS } { No checking performed. } { FreeWare by TapirSoft Gisbert W.Selke, Feb 1990 } {$A+,B-,D+,E+,F-,I-,L+,N-,O-,R-,S+,V- } {$M 16384,0,16384 } Uses Dos; Const progname = 'ExTAR'; version = '1.0'; copyright = 'FreeWare by TapirSoft Gisbert W.Selke, Feb 1990'; secsize = 512; hdrlen = secsize; secsperblock = 120; bufsize = secsize * secsperblock; CR = #13; Type buf = Array [0..Pred(bufsize)] Of byte; Var tar, outf : File; tarname, outname : string; buffer : buf; dt : DateTime; i : byte; iread, ibuf, nbufs, nrest : word; nsecs, memberlen, datestamp : longint; finish : boolean; Function ReadKey : char; { don't need CRT unit for this! } Inline( $B4/$08/ { Mov ah, $08 } $CD/$21); { Int $21 } Procedure abort(msg : string; ierr : byte); { display an error message and die with error code } Begin { abort } If IOResult <> 0 Then; If msg <> '' Then writeln(progname,': ',msg); Halt(ierr); End; { abort } Procedure usage; { give hints on usage and die } Begin { usage } writeln('A simple programme to extract all members from a TAR file'); writeln('Usage: ',progname,' '); abort('',1); End; { usage } Procedure crackutime(datestamp : longint; Var dt : DateTime); { extracts date and time from Unix time stamp, assuming TZ = GMT + 8 } Const monlen : Array [1..12] Of byte = (31,28,31,30,31,30,31,31,30,31,30,31); Begin { crackutime } With dt Do Begin datestamp := datestamp - 8*3600; sec := datestamp Mod 60; datestamp := datestamp Div 60; min := datestamp Mod 60; datestamp := datestamp Div 60; hour:= datestamp Mod 24; datestamp := datestamp Div 24; year := 1970; While datestamp > 0 Do Begin Inc(year); If (year Mod 4) = 0 Then day := 366 Else day := 365; datestamp := datestamp - day; End; Dec(year); day := datestamp + day + 1; month := 1; While day > monlen[month] Do Begin day := day - monlen[month]; If (month = 2) And ((year Mod 4) = 0) Then Dec(day); Inc(month); End; End; End; { crackutime } Procedure openfile(Var outname : string); { make a name acceptable for DOS and open the file for output } Const badletter : Set Of char = ['.','+',' ',':','<','>','|']; yesset : Set Of char = ['Y','J','1']; noset : Set Of char = ['N','0']; Var i : byte; ch : char; temp, drive, dir, name, ext : string; ok : boolean; Procedure makedirs(Var dir1 : string; dir2 : string); { make a directory recursively, if necessary } Var i : byte; dire, temp : string; sr : SearchRec; Begin { makedirs } If dir2 = '' Then Exit; i := Pos('\',dir2); temp := Copy(dir2,1,Pred(i)); Delete(dir2,1,i); If temp[1] = '.' Then Delete(temp,1,1); i := Pos('.',temp); If i > 0 Then Begin dire := Copy(temp,Succ(i),255); Delete(temp,i,255); End Else dire := ''; If Length(temp) > 8 Then Begin dire := Copy(temp,9,255); Delete(temp,9,255); End; If Length(dire) > 3 Then Delete(dire,4,255); If Pos('.',dire) > 0 Then Delete(dire,Pos('.',dire),255); dir1 := dir1 + temp + '.' + dire; FindFirst(dir1,directory,sr); If DosError <> 0 Then Begin MkDir(dir1); If IOResult <> 0 Then abort('Error making directory '+dir1,2); End; dir1 := dir1 + '\'; makedirs(dir1,dir2); End; { makedirs } Procedure filesplit(path : string; Var drive, dir, name, ext : string); { splits path spec into component parts. like Borland FSplit, but } { more liberal. } Var k : byte; Begin { filesplit } drive := ''; dir := ''; name := ''; ext := ''; If (Length(path) >= 2) And (path[2] = ':') Then Begin drive := Copy(path,1,2); Delete(path,1,2); End; k := Pos('\',path); While k > 0 Do Begin dir := dir + Copy(path,1,k); Delete(path,1,k); k := Pos('\',path); End; name := path; If name[1] = '.' Then Delete(name,1,1); k := Pos('.',name); If k > 0 Then Begin ext := Copy(name,k,255); Delete(name,k,255); End; End; { filesplit } Begin { openfile } temp := outname; ok := True; For i := Length(temp) DownTo 1 Do Begin If temp[i] = '.' Then Begin If Not ok Then temp[i] := '_'; ok := False; End Else Begin If temp[i] = '/' Then temp[i] := '\'; If temp[i] = '\' Then ok := True; If temp[i] In badletter Then temp[i] := '_'; temp[i] := UpCase(temp[i]); End; End; ok := False; filesplit(temp,drive,dir,name,ext); temp := ''; makedirs(temp,dir); dir := temp; If ext = '' Then ext := '.'; If Length(name) > 8 Then Begin If Length(ext) = 1 Then ext := '.' + Copy(name,9,3); Delete(name,9,255); End; If name = '' Then Begin name := Copy(ext,2,255); ext := ''; End; If Length(ext) > 4 Then Delete(ext,5,255); Repeat Assign(outf,dir+name+ext); Reset(outf,1); If IOResult <> 0 Then ok := True Else Begin Close(outf); write(dir+name+ext,' already exists. Overwrite? (y/n) '); Repeat ch := UpCase(ReadKey); Until ch In yesset + noset; ok := ch in yesset; write(CR); End; If Not ok Then Begin While Length(name) < 8 Do name := name + '0'; i := Length(name); While (name[i] = '9') And (i > 1) Do Begin name[i] := '0'; Dec(i); End; If i = 0 Then abort('Cannot fix name '+outname,3); If Not (name[i] In ['0'..'9']) Then name[i] := '0' Else name[i] := Succ(name[i]); End; Until ok; temp := dir + name + ext; write('Original name: ',outname,', DOS name ',temp); outname := temp; Rewrite(outf,1); IF IOResult <> 0 Then abort('Cannot output to file '+outname+'??',4); End; { openfile } Begin { main } writeln(progname,' ',version,' - extract files from a TAR'); writeln(copyright); If ParamCount <> 1 Then usage; tarname := ParamStr(1); If Pos('.',tarname) = 0 Then tarname := tarname + '.TAR'; Assign(tar,tarname); i := FileMode; FileMode := 0; Reset(tar,1); FileMode := i; If IOResult <> 0 Then abort('Cannot open TAR file '+tarname,4); finish := False; While Not (EoF(tar) Or finish) Do Begin BlockRead(tar,buffer,hdrlen,iread); If iread <> hdrlen Then abort('Illegal header in TAR file',5); i := 0; While (buffer[i] <> 0) And (i < 254) Do Inc(i); finish := i = 0; If Not finish Then Begin Move(buffer,outname[1],i); outname[0] := char(i); memberlen := 0; For i := $7C To $86 Do Begin If (buffer[i] >= 48) And (buffer[i] <= 55) Then memberlen := 8*memberlen + buffer[i] - 48; End; If memberlen > 0 Then Begin datestamp := 0; For i := $88 To $92 Do Begin If (buffer[i] >= 48) And (buffer[i] <= 55) Then datestamp := 8*datestamp + buffer[i] - 48; End; crackutime(datestamp,dt); PackTime(dt,datestamp); openfile(outname); nsecs := (memberlen + Pred(secsize)) Div secsize; nbufs := (nsecs + Pred(secsperblock)) Div secsperblock; For ibuf := 1 To Pred(nbufs) Do Begin write('.'); BlockRead(tar,buffer,bufsize,iread); If iread <> bufsize Then abort('Input file too short',6); BlockWrite(outf,buffer,bufsize,iread); If iread <> bufsize Then abort('Error writing to output file',7); End; nsecs := nsecs - Pred(nbufs)*secsperblock; If nsecs > 0 Then Begin write('.'); nrest := nsecs*secsize; BlockRead(tar,buffer,nrest,iread); If iread <> nrest Then abort('Input file too short',6); nrest := memberlen - longint(Pred(nbufs))*bufsize; BlockWrite(outf,buffer,nrest,iread); If iread <> nrest Then abort('Error writing to output file',7); End; SetFTime(outf,datestamp); Close(outf); writeln; End; End; End; Close(tar); End.