H-READ ME 9CPM INC@lDOS INCW/9SQZ PAS?=@SQZMAIN INCy/ETURBOSQZDQC><rUSQZ PASzKaUSQZMAININC  [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] [] [] [] [] SQZ.PAS USQZ.PAS CPM.INC TURBOSQZ.DQC [] [] SQZMAIN.INC USQZMAIN.INC DOS.INC [] [] [] [] [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] Turbo Pascal (Version 2.x or 3.x) source for a CP/M or MsDos/PcDos file "Squeezer" and "UnSqueezer". These file compression programs are compatible with CP/M public domain SQ.COM and USQ.COM (the "old-timers' standard"), as well as a recent MsDos public domain Squeezer, which stores the original file date and time. File CheckSums are developed and verified. The CPM.INC or DOS.INC files provide source compatibility between CP/M and MSDOS/PCDOS. Usage notes: (Sqz and USqz are parallel in operation.) 1 A file name (including wildcards) may be entered on the command line: A>SQZ *.PAS A>USQZ B:*.?Q? 2 Printer Echo may be turned on: A>SQZ *.PAS /P A>USQZ /P 3 If no file name was entered, you will be prompted for one (again, may include wildcards.) 4 Respond to prompt 3 with a blank file name (just ) to exit the program. 5 If the file/(s) are not found you return to prompt 3. 6 If the file(s) are found you may specify a different output drive. 7 File statistics are displayed, showing input and output file sizes and the output file size as a percentage of the input file size. 8 The CheckSum of the original file is stored upon Squeezing, and verified upon UnSqueezing (any discrepancy is displayed.) 9 When the file / all files have been processed, you return to prompt 3 to continue with more, or to exit. For a relatively complete version history and some specific programming notes, see TURBOSQZ.DOC, which is included in this library in squeezed form: USQZ TURBOSQZ.DQC Bob Berry CompuServe 76555,167  { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] [] [] The Squeeze/UnSqueeze CP/M routines. [] [] [] [] CP/M files are all multiples of 128 byte records. I/O must be [] [] handled with BlockRead and BlockWrite. Files opened for input [] [] only do not need to be closed. The file size in bytes (real) [] [] must be calculated as (turbo function) FileSize * 128.0 [] [] [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } Const CommandLineLoc = $80; DisplayCharacter = $02; PrintCharacter = $05; SearchForFirst = $11; SearchForNext = $12; ReturnLoggedDrive = $19; SetDMA = $1A; Type AFile = File; FCBType = Array[0..31] of Char; DMAType = Record Drive: Byte; FName: Array[1..11] of Char; Extnt: Byte; Rsrvd: Array[0..1] of Byte; RcCnt: Byte; Block: Array[1..8] of Integer; end; Var CommandLine: String[127] Absolute CommandLineLoc; InFile, OutFile: AFile; DMA: Array[0..3] of DMAType; FCB: FCBType; InBuffer, OutBuffer: Array[1..128] of Char; InBuffPointer, OutBuffPointer: Byte; Procedure GetLoggedDrive; begin LoggedDrive:=chr(BDOS(ReturnLoggedDrive)+ord('A')); end; { Procedure GetLoggedDrive } Procedure FindFiles(FileMask: FileName); Var FoundFile: FileName; FCBDone, DoingExt: Boolean; SearchFunction, SearchReturn, NByte: Byte; begin FillChar(FCB,SizeOf(FCB),0); For i:=1 to 11 do FCB[i]:=' '; FCB[0]:=Chr(Ord(FileMask[1])-Ord('@')); { eg. A: -> 1 } FCBDone:=False; i:=2; NByte:=1; DoingExt:=False; While not FCBDone do begin i:=Succ(i); If i > Length(FileMask) then FCBDone:=true else Case FileMask[i] of ^@..' ', '_', ';', ':', '=', '<', '>': FCBDone:=true; '.': If DoingExt then FCBDone:=True else begin DoingExt:=True; NByte:=9; end; '*': If DoingExt then begin FCBDone:=True; While NByte<12 do begin FCB[NByte]:='?'; NByte:=Succ(NByte); end; end else begin While NByte<9 do begin FCB[NByte]:='?'; NByte:=Succ(NByte); end; end; else If (DoingExt and (NByte<12)) or (NByte<9) then begin FCB[NByte]:=FileMask[i]; NByte:=Succ(NByte); end; end; { Case FileMask[i] } end; FFirst:=Nil; FLast:=Nil; BDOS(SetDMA,Addr(DMA)); SearchFunction:=SearchForFirst; Repeat { Until SearchReturn>3 } SearchReturn:=BDOS(SearchFunction,Addr(FCB)); If SearchReturn<4 then begin FoundFile:=''; For i:=1 to 8 do If DMA[SearchReturn].FName[i]<> ' ' then FoundFile:=FoundFile+DMA[SearchReturn].FName[i]; FoundFile:=FoundFile+'.'; For i:=9 to 11 do If DMA[SearchReturn].FName[i]<> ' ' then FoundFile:=FoundFile+DMA[SearchReturn].FName[i]; New(FCurrent); FCurrent^.FNme:=FoundFile; FCurrent^.NxtF:=Nil; If FFirst=Nil then FFirst:= FCurrent else FLast^.NxtF:= FCurrent; FLast:=FCurrent; SearchFunction:=SearchForNext; end; Until SearchReturn>3; FCurrent:=FFirst; end; { Procedure FindFiles } Function NextFile: FileName; begin If FCurrent=Nil then NextFile:='' else begin NextFile:=FCurrent^.FNme; FCurrent:=FCurrent^.NxtF; end; end; { Function NextFile } Procedure WriteCharToCon(AChar: Char); begin BDOS(DisplayCharacter,Ord(AChar)); BDOS(PrintCharacter, Ord(AChar)); end; { Procedure WriteCharToCon } Procedure SetEchoToPrinter; begin ConOutPtr:=Addr(WriteCharToCon); end; { Procedure SetEchoToPrinter } Function TheSizeOf(Var TheFile: AFile): Real; { CP/M must calculate } begin TheSizeOf:=(128.0*FileSize(TheFile)); end; { Function TheSizeOf } Procedure ResetInFile; { CP/M: do reset and initialize pointer } begin Reset(InFile); InBuffPointer:=129; end; { Procedure ResetInFile } Procedure ReadInFile(Var C: Char); { CP/M move char from buffer, read block } begin If InBuffPointer>128 then begin BlockRead(InFile,InBuffer,1); InBuffPointer:=1; end; C:=InBuffer[InBuffPointer]; InBuffPointer:=succ(InBuffPointer); end; { Procedure ReadInFile } Function GetC: Char; { CP/M return next char or EOFile } var C: Char; begin If ((InBuffPointer>128) and EOF(InFile)) then EOFile:=true else begin ReadInFile(c); crc:=crc+ord(C); end; GetC:=C; end; { Function GetC } Procedure CloseInFile; { CP/M doesn't need to close } begin end; { Procedure CloseInFile } Procedure InitializeOutBuffer; { Fill Buffer with ^Z's } begin FillChar(OutBuffer,SizeOf(OutBuffer),26); OutBuffPointer:=1; end; { Procedure InitializeOutBuffer } Procedure ReWriteOutFile; { CP/M reset drives, do ReWrite, init pointer } begin BDOS(13); BDOS(14,ord(LoggedDrive)-ord('A')); ReWrite(OutFile); InitializeOutBuffer; end; { Procedure ReWriteOutFile } Procedure WriteOutFile(Var C: Char); { CP/M move char to buffer, write block } begin If OutBuffPointer>128 then begin BlockWrite(OutFile,OutBuffer,1); InitializeOutBuffer; end; OutBuffer[OutBuffPointer]:=C; OutBuffPointer:=succ(OutBuffPointer); end; { Procedure WriteOutFile } Procedure CloseOutFile; { CP/M write block before close } begin If OutBuffPointer>1 then BlockWrite(OutFile,OutBuffer,1); Close(OutFile); end; { Procedure CloseOutFile } Function GetSizeOfOutFile: Real; { CP/M doesn't need to close } begin Reset(OutFile); GetSizeOfOutFile:=TheSizeOf(OutFile); end; { Function GetSizeOfOutFile } Pointer:=1; end; { Procedure InitializeOutBuffer } Procedure ReWriteOutFile; { CP/M reset drives, do ReWrite, init pointer } begin BDOS(13); BDOS(14,ord(LoggedDrive)-ord('A')); ReWrite(OutFile); InitializeOutBuffer; end; { Procedure ReWriteOutFile } Procedure WriteOutFile(Var C: Char); { CP/M move char to buffer, write block } begin If OutBuffPointer>128 then begin BlockWrite(OutFile,OutBuffer,1); InitializeOutBuffer; end; OutBuffer[OutBuffPointer]:=C; OutBuffPointer:=succ(OutBuffPointer); end; { Procedure WriteOutFile } Procedure CloseOutFile; { CP/M write block before close } begin If OutBuffPointer>1 then BlockWrite(OutFile,OutBuffer,1); Clo { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] [] [] The Squeeze/UnSqueeze MsDos routines. [] [] [] [] DOS files are "file of char", which keeps read/write procedures [] [] simple. DOS Turbo 3.0 must close all file (handles), even if [] [] read only. File sizes are determined with LongFileSize function. [] [] [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } Const CommandLineLoc = $80; DisplayCharacter = $0200; PrintCharacter = $0500; ReturnLoggedDrive = $1900; SetDTA = $1A00; FindFirstFile = $4E00; FindNextFile = $4F00; Type AFile = File of Char; FlagType = (CarryFlag, NoFlag2, ParityFlag, NoFlag8, AuxCarryFlag, NoFlag20, ZeroFlag, SignFlag, StepTrapFlag, InterruptFlag, DirectionFlag, OverflowFlag, NoFlag100, NoFlag200, NoFlag400, NoFlag800); FlagSet = Set of FlagType; MsDosRegisters = Record Case Byte of 0: (Ax,Bx,Cx,Dx,Bp,Si,Di,Ds,Es,Flags: Integer); 1: (Al,Ah,Bl,Bh,Cl,Ch,Dl,Dh: Byte); 2: (RegArray: Array[1..9] of Integer; FlagRegister: FlagSet); end; DTAType = Record Reserved: Array[1..21] of Byte; Attribute: Byte; FileTime, FileDate: Integer; FileSize: Array[0..1] of Integer; FName: Array[1..13] of Char; end; Var CommandLine: String[127] Absolute CSeg:CommandLineLoc; InFile, OutFile: AFile; DTA: DTAType; DosRec: MsDosRegisters; Procedure GetLoggedDrive; begin With DosRec do begin Ax:=ReturnLoggedDrive; MsDos(DosRec); LoggedDrive:=chr(Lo(ax)+65); end; end; { Procedure GetLoggedDrive } Procedure FindFiles(FileMask: FileName); Var FindFile, FoundFile: FileName; begin With DosRec do begin Ax:=SetDTA; Ds:=Seg(DTA); Dx:=Ofs(DTA); MsDos(DosRec); FindFile:=FileMask+#0; FFirst:=Nil; FLast:=Nil; Ax:=FindFirstFile; Repeat { Until CarryFlag in FlagRegister } Ds:=Seg(FindFile[1]); Dx:=Ofs(FindFile[1]); Cx:=0; MsDos(DosRec); If not (CarryFlag in FlagRegister) then begin FoundFile[0]:=#30; Move(DTA.FName,FoundFile[1],13); FoundFile[0]:=Chr(Pred(Pos(#0,FoundFile))); New(FCurrent); FCurrent^.FNme:=FoundFile; FCurrent^.NxtF:=Nil; If FFirst=Nil then FFirst:= FCurrent else FLast^.NxtF:= FCurrent; FLast:=FCurrent; end; Ax:=FindNextFile; Until CarryFlag in FlagRegister; FCurrent:=FFirst; end; end; { Procedure FindFiles } Function NextFile: FileName; begin If FCurrent=Nil then NextFile:='' else begin NextFile:=FCurrent^.FNme; FCurrent:=FCurrent^.NxtF; end; end; { Function NextFile } Procedure WriteCharToCon(AChar: Char); begin With DosRec do begin Ax:=DisplayCharacter; Dl:=Ord(AChar); MsDos(DosRec); Ax:=PrintCharacter; Dl:=Ord(AChar); MsDos(DosRec); end; end; { Procedure WriteCharToCon } Procedure SetEchoToPrinter; begin ConOutPtr:=Ofs(WriteCharToCon); end; { Procedure SetEchoToPrinter } Function TheSizeOf(Var TheFile: AFile): Real; { DOS can use LongFileSize } begin TheSizeOf:=LongFileSize(TheFile); end; { Function TheSizeOf } Procedure ResetInFile; { DOS reads file of char } begin Reset(InFile); end; { Procedure ResetInFile } Procedure ReadInFile(Var C: Char); { DOS just reads the next char } begin Read(InFile,C); end; { Procedure ReadInFile } Function GetC: Char; { DOS get next character } var C: Char; begin If EOF(InFile) then EOFile:=true else begin ReadInFile(c); crc:=crc+ord(C); end; GetC:=C; end; { Function GetC } Procedure CloseInFile; { DOS Turbo 3.0 needs to close the handle } begin Close(InFile); end; { Procedure CloseInFile } Procedure ReWriteOutFile; { DOS just writes a file of char } begin ReWrite(OutFile); end; { Procedure ReWriteOutFile } Procedure WriteOutFile(Var C: Char); { DOS just writes a char } begin Write(OutFile,C); end; { Procedure WriteOutFile } Procedure CloseOutFile; { DOS just closes } begin Close(OutFile); end; { Procedure CloseOutFile } Function GetSizeOfOutFile: Real; { DOS Turbo 3.0 needs to close the handle } begin Reset(OutFile); GetSizeOfOutFile:=TheSizeOf(OutFile); CloseOutFile; end; { Function GetSizeOfOutFile } Program Squeeze; (* Written: 01/29/1986 17:31:13 *) { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] Program Squeeze [] [] [] [] A file compression program. Compatible with CP/M or DOS, Turbo [] [] Pascal Version 2.0 and above. [] [] [] [] [] [] Bob Berry, CompuServe 76555,167 [] [] [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } Const Version = 'Version 2.1 Last Update 01/29/1986'; PrinterToggle = '/P'; FormFeed = ^L; Space = ' '; Error = -1; Null = -2; Recognize = $FF76; DLE = #$90; SPEOF = 256; { special endfile token } NumVals = 257; { 256 data values plus SPEOF } NumNodes = 513; { 2*NUMVALS-1 = number of nodes } NoChild = -1; { indicates end of path } MaxCount = MAXINT; { biggest UNSIGNED integer } Type FileName = String[30]; ValType = Array[0..NumVals] of integer; StateTypes = (NoHist,SentChar,SendNewC, SendCnt,EndFile); NodeType = Record Weight: real; Tdepth: integer; LChild, RChild: integer; end; FlePtr = ^FileLst; FileLst = Record FNme: FileName; NxtF: FlePtr; end; Var InFileName, OutFileName, FMask, DrivePrefix, OutDrive: FileName; InFileSize, OutFileSize: real; Finish, i, Crc, DcTreeHd, LikeCt: integer; HeapTop: ^Integer; FFirst, FLast, FCurrent: FlePtr; LoggedDrive, LastChar, NewChar: char; State: StateTypes; PrinterEcho, EOFile, EOFlag, AllDone, Done: boolean; Node: array[0..NumNodes] of NodeType; { This is the encoding table: The bit strings have first bit in = low bit. Note that counts were scaled so code fits UNSIGNED integer } CodeLen, Code: array[0..numvals] of integer; { number of bits in code & code itself, right adjusted } TCode, { temporary code value } CurIn, { Value currently being encoded } CBitsRem, { Number of code string bits remaining } CCode: integer; { Current code shifted so next code bit is at right } { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] Pick one: CP/M or DOS and comment out the one that doesn't apply [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } (* {$I cpm.inc } *) {$I dos.inc } {$I sqzmain.inc } Procedure Compress(Var TheString: FileName); begin While Pos(' ',TheString) > 0 do Delete(TheString,Pos(' ',TheString),1); end; { Procedure Compress } Procedure Squeeze; Var C: Char; begin InFileName:=DrivePrefix+InFileName; OutFileName:=InFileName; If Length(OutDrive)>0 then OutFileName[1]:=UpCase(OutDrive[1]); While (Pos('.',OutFileName)+3)>Length(OutFileName) do OutFileName:=OutFileName+Space; Finish:=succ(Pos('.',OutFileName)); OutFileName[succ(Finish)]:='Q'; If OutFileName[Finish]=Space then begin OutFileName[Finish]:='Q'; OutFileName[succ(succ(Finish))]:='Q'; end; Assign(InFile,InFileName); Reset(InFile); InFileSize:=TheSizeOf(InFile); If InFileSize=0 then begin WriteLn('Input file ',InFileName,' is empty.'); CloseInFile; end else begin WriteLn; WriteLn('The file ',InFileName,' (',InFileSize:6:0, ' bytes ) is being squeezed to ',OutFilename); InitializeHuffman; WriteLn('.'); Assign(OutFile,OutFileName); ReWriteOutFile; Write('Pass 2: Squeezing,'); CloseInFile; ResetInFile; EOFile:=false; EOFlag:=false; Write(' header,'); WriteHeader; Write(' body,'); State:=NoHist; Done:=false; C:=GetHuff; While not Done do begin WriteOutFile(C); C:=GetHuff; end; CloseInFile; CloseOutFile; OutFileSize:=GetSizeOfOutFile; WriteLn(' Done.'); WriteLn('The file ',OutFileName,' is',OutFileSize:6:0, ' bytes (',(100.0*OutFileSize/InFileSize):5:1,'%).'); end; end; { Procedure Squeeze } { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] Squeeze MainLine [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } begin ClrScr; GetLoggedDrive; If CommandLine > '' then { Get InFileName from Command Line } begin WriteLn; InFileName:=CommandLine; For i:=1 to Length(InFileName) do InFileName[i]:=UpCase(InFileName[i]); PrinterEcho:= ( Pos(PrinterToggle,InFileName) > 0 ); If PrinterEcho then begin SetEchoToPrinter; Delete(InFileName,Pos(PrinterToggle,InFileName),2); end; Compress(InFileName); end else begin InFileName:=''; PrinterEcho:=False; end; Write('File Squeezer'); For i:=1 to 66-Length(Version) do Write(Space); WriteLn(Version); Repeat { Until AllDone } AllDone:=false; If InFileName='' then begin WriteLn; Write('Enter file to squeeze ( or to exit ) >'); ReadLn(InFileName); For i:=1 to Length(InFileName) do InFileName[i]:=UpCase(InFileName[i]); Compress(InFileName); end; If Pos('.',InFileName)=0 then InFileName:=InFileName+'.'; If Pos(':',InFileName)=0 then InFileName:=LoggedDrive+':'+InFileName; DrivePrefix:=Copy(InFileName,1,2); If Length(InFileName)<4 then AllDone:=true { <== Blank name, AllDone } else begin Mark(HeapTop); FindFiles(InFileName); If FFirst=Nil then WriteLn('Input file(s) ',InFileName,' not found.') else begin Write('Output Drive ( or for ',DrivePrefix,' ) >'); ReadLn(OutDrive); Repeat { Until InFileName='' } InFileName:=NextFile; If InFileName > '' then Squeeze; Until InFileName=''; end; Release(HeapTop); end; InFileName:=''; Until AllDone; If PrinterEcho then Write(Lst,FormFeed); end.{ Initialize all nodes to single element binary trees with zero weight and depth. } Procedure ZeroTree; var i: integer; begin For i := 0 to NumNodes do with Node[i] do begin Weight:=0.0; TDepth:=0; LChild:=NoChild; RChild:=NoChild; end; end; { Procedure ZeroTree } Procedure PutWe(w: integer); var b1, b2: char; begin b1 := chr(w and $FF); b2 := chr(w shr 8); WriteOutFile(b1); WriteOutFile(b2); end; { Procedure PutWe } Function GetCNr: char; Var return: char; Function Alike: boolean; begin If EOFile then EOFlag:=true else NewChar:=GetC; If EOFile then Alike:=false else Alike:=( (NewChar=LastChar) and (LikeCt<255) ); end; { Function Alike } Procedure NoHistory; {set up the state machine} begin State:=SentChar; If not EOFile then LastChar:=GetC; EoFlag:=EoFile; If EOFlag then State:=EndFile; Return:=LastChar; end; { Procedure NoHistory } Procedure SentAChar; {LastChar is sent, need lookahead} Procedure SentDLE; begin State:=NoHist; Return:=chr(0); end; { Procedure SentDLE } Procedure CheckAlike; begin LikeCt:=1; While alike do LikeCt:=succ(LikeCt); Case LikeCt of 1: begin LastChar:=NewChar; Return:=LastChar; EoFlag:=EoFile; end; 2: begin { just pass through } State:=SendNewC; Return:=LastChar; end; else begin State:=SendCnt; Return:=DLE; end; end; end; { Procedure CheckAlike } begin If EOFlag then State := EndFile {no return value, set to SPEOF in calling routine} else If LastChar=DLE then SentDLE else CheckAlike; end; { Procedure SentAChar } Procedure SendNewChar; {Previous sequence complete, newchar set} begin EOFlag:=EOFile; State:=SentChar; LastChar:=NewChar; Return:=LastChar; end; { Procedure SendNewChar } Procedure SendCount; {Sent DLE for repeat sequence, send count} begin State:=SendNewC; Return:=chr(LikeCt); end; { Procedure SendCount } begin Case State of NoHist: NoHistory; SentChar: SentAChar; SendNewC: SendNewChar; SendCnt: SendCount; else WriteLn('Program Bug - Bad State!'); end; GetCnr:=Return; end; { Function GetCNr } Procedure WriteHeader; Var i, k, l, r, NumNodes: integer; { NumNodes: nbr of nodes in simplified tree } begin PutWe(Recognize); { identifies as compressed } PutWe(Crc); { unsigned sum of original data } { Record the original file name w/o drive } If (InFileName[2]=':') then InFileName:=copy(InFileName,3,length(InFileName)-2); InFileName:=InFileName+chr(0); {mark end of file name} For i:=1 to Length(InFileName) do WriteOutFile(InFileName[i]); { Write out a simplified decoding tree. Only the interior nodes are written. When a child is a leaf index (representing a data value) it is recoded as -(index + 1) to distinguish it from interior indexes which are recoded as positive indexes in the new tree. Note that this tree will be empty for an empty file. } If DcTreeHd b return true, else return false. } Function CmpTrees(a, b: integer): boolean; {entry with root nodes} begin CmpTrees:=false; If Node[a].Weight>Node[b].Weight then CmpTrees:=true else If Node[a].Weight=Node[b].Weight then If Node[a].TDepth>Node[b].TDepth then CmpTrees:=true; end; { Function CmpTrees } begin k:=succ(2*Top); { left child of top } Temp:=List[Top]; { remember root node of top tree } If (k<=Bottom) then begin If ( k(Ceil-Sum) then OvFlw:=succ(OvFlw); Sum:=Sum+Node[i].Weight; end; Divisor:=succ(Ovflw); { Ensure no non-zero values are lost } Increased:=false; For i:=0 to pred(NumVals) do begin w:=Node[i].Weight; If (w0) then begin { Don't fail to provide a code if it's used at all } Node[i].Weight:=Divisor; Increased:=true; end; end; Until not Increased; { Scaling factor choosen, now scale } If Divisor>1 then For i:=0 to pred(NumVals) do with Node[i] do Weight:=int((Weight/Divisor)+0.5); end; { Procedure Scale } {$A-} { Recursive routine to walk the indicated subtree and level and maintain the current path code in bstree. When a leaf is found the entire code string and length are put into the encoding table entry for the leaf's data value. Returns ERROR if codes are too long. } Function BuildEnc(Level, Root: integer): integer; {returns error or null} Var l, r, Return: integer; begin Return:=Null; l:=Node[Root].LChild; r:=Node[Root].RChild; If (l=NoChild) and (r=NoChild) then begin {have a leaf} CodeLen[Root]:=Level; Code[Root]:=TCode and ($FFFF shr (16-Level)); If Level>16 then Return:=Error else Return:=Null; end else begin If l<>NoChild then begin {Clear path bit and go deeper} TCode:=TCode and not(1 shl Level); If BuildEnc(succ(Level),l)=Error then Return:=Error; end; If r<>NoChild then begin {Set path bit and go deeper} TCode:=TCode or (1 shl Level); If BuildEnc(succ(Level),r)=Error then Return:=Error; end; end; BuildEnc:=Return; end; { Function BuildEnc } {$A+} Procedure BuildTree(Var List: ValType; Len: integer); {Huffman algorithm} Var FreeNode: integer; {next free node in tree} LCh, RCh: integer; {temporaries for left, right children} i: integer; Function Maximum(a, b: integer): integer; begin If a>b then Maximum:=a else Maximum:=b; end; { Function Maximum } begin { Initialize index to next available (non-leaf) node. Lower numbered nodes correspond to leaves (data values). } FreeNode:=NumVals; { Take from list two btrees with least weight and build an interior node pointing to them. This forms a new tree. } While (Len>1) do begin LCh:=List[0]; { This one will be left child } { delete top (least) tree from the list of trees } Len:=pred(Len); List[0]:=List[Len]; Adjust(0,pred(Len),List); { Take new top (least) tree. Reuse list slot later } RCh:=List[0]; { This one will be right child } { Form new tree from the two least trees using a free node as root. Put the new tree in the list. } With Node[FreeNode] do begin LChild:=LCh; RChild:=RCh; Weight:=Node[LCh].Weight+Node[RCh].Weight; TDepth:=succ(Maximum(Node[LCh].TDepth,Node[RCh].TDepth)); end; List[0]:=FreeNode; {put at top for now} FreeNode:=succ(FreeNode); {next free node} { reheap list to get least tree at top } Adjust(0,pred(Len),List); end; DcTreeHd:=List[0]; { head of final tree } end; { Procedure BuildTree } { Initialize the Huffman translation. This requires reading the input file through any preceding translation functions to get the frequency distribution of the various values. } Procedure InitializeHuffman; var c, i: integer; BtList: ValType; { list of intermediate binary trees } ListLen: integer; { length of btlist } Ceiling: integer; { limit for scaling } { Heap and Adjust maintain a list of binary trees as a heap with the top indexing the binary tree on the list which has the least weight or, in case of equal weights, least depth in its longest path. The depth part is not strictly necessary, but tends to avoid long codes which might provoke rescaling. } Procedure Heap(Var List: ValType; l: integer); Var i, len: integer; begin Len:=(l-2) div 2; For i:=Len DownTo 0 do Adjust(i,pred(l),List); end; { Procedure Heap } begin Write('Pass 1: Analysis,'); Crc:=0; ZeroTree; State:=NoHist; EOFile:=false; EOFlag:=false; Repeat { Until EOFlag } { Build frequency info in tree } C:=ord(GetCnr); If EOFlag then C:=SpEOF; With Node[C] do If Weight 16 bits long. } Repeat { Until BuildEnc(0,DcTreeHd) <> Error } If (Ceiling<>MaxCount) then Write(' *** rescaling ***,'); Scale(Ceiling); Ceiling:=Ceiling div 2; {in case we rescale again} ListLen:=0; {find length of list and build single nodes} For i:=0 to pred(NumVals) do If Node[i].Weight>0.0 then begin Node[i].TDepth:=0; BtList[listlen]:=i; ListLen:=succ(ListLen); end; Heap(BtList,pred(ListLen)); Write(' Building tree'); BuildTree(BtList,ListLen); For i:=0 to pred(NumVals) do CodeLen[i]:=0; until (BuildEnc(0,DcTreeHd)<>Error); { PrintList;} { Initialize encoding variables } CBitsRem:=0; CurIn:=0; end; { Procedure InitializeHuffman } { Get an encoded byte or EOF. Reads from specified stream AS NEEDED. There are two unsynchronized bit-byte relationships here: The input stream bytes are converted to bit strings of various lengths via the static variables named Cxxxxx. These bit strings are concatenated without padding to become the stream of encoded result bytes, which this function returns one at a time. The EOF (end of file) is converted to SPEOF for convenience and encoded like any other input value. True EOF is returned after that. } Function GetHuff: char; {returns byte values except for EOF} Var RByte: integer; {Result byte value} Need, Take: integer; {numbers of bits} Return: integer; begin RByte:=0; Need:=8; {build one byte per call} Return:=Error; {start off with an error} { Loop to build a byte of encoded data. Initialization forces read the first time} While Return=Error do begin If CBitsRem>=Need then begin {Current code fullfills our needs} If need = 0 then Return:=RByte and $00FF else begin RByte:=RByte or (CCode shl (8-Need)); {take what we need} CCode:=CCode shr Need; {and leave the rest} CBitsRem:=CBitsRem-Need; Return:=RByte and $00FF; end; end else begin If CBitsRem>0 then begin {We need more than current code} RByte:=RByte or (CCode shl (8-Need)); {take what there is} Need:=Need-CBitsRem; end; If curin=SpEOF then begin CBitsRem:=0; If Need=8 then begin {end of file} Done:=true; Return:=0; {any valid char value} end else Return:=RByte and $00FF; {data first} end else begin CurIn:=ord(GetCnr); If EOFlag then CurIn:=SpEOF; CCode:=Code[CurIn]; CBitsRem:=CodeLen[CurIn]; end; end; end; GetHuff:=chr(Return); end; { Function GetHuff } v!TURBOSQZ.DOCd    !#"$%&o')(+*-,/.1032546798:;<=?>@BADCFEGHIJKLMNOQPRSTUVXWYZ[]^\_`cbaɳFFz&'k{"k~G2שRu&BTQEUؕeزXvK o![`U QEյ ~)n p#owHnϝdXl@2U=aV*d]t Vp0=ԑO#Ta}l2VR>/^B^p$={8JETQE5OORdV'mN(k\kk ^k"򏔤Oյ'mN#X'[݋mNxpco'>Cr&闩|Q9.dIrWzE/}h4ZkZbd{-|ofH'cWu^S ̛ 39ФΕk{&'t~GN*yj}o=299rB}oK76A}Ǔ׿5p'Z~' TB71`pxe+\ڏp^T]kW T {=//V)/#==T]k^cmI#wdїWǮ?|>1&(0Mo9Mpi_4u!?UךO4ҿ16OX~e16d^Q~|7er¥}Eյys ?ݓژw2Jzip[NC$+?}9]0b#q}SxjZGHic-HMgHRu>?LNZeaSWߑ8=299rB}>xoWlJյ29Ka>ysI02H2u'9PkZyW&㷷L0F2;)XVpk?M$ kޘx^Y:yLUײp8T|=N[z y}/ pط{?lcÔ*2Z8\6F#?9 @*bvi?Xy)Wc Oc6F|ı/rߗ9bkS#KW-'xr20sؼK(x4o,Ѡe儮eN=|^k`O8) OX}O?wQ'2'yUC>^y FBo9j_ʦL?)d]{L/ wip[NyR*>c?X7%y-'1UX>FWL!\d#қ{GS*evek׿l7ζWw=a^\株W`)'y82=r?9LW+^~=!MXZZacDms0uscoz'7x˻ I0;joei=N~{%2^ozg7D:)SEյ潱dʲ.v}sŗ|r9?]Hl~#TMSwt~l|9;%MSdoXWBUcɖ^3`|ls 7r JnTz:[y+Y:sw(wxXz|ýãNVãN*ev6ɗ?l{lH߀$6om"vKU52cnwK{8ew1E*ѓ9|t!U#P9aWQek[=ɼ'cʦ*F7.̕e=F['̟xAJUSBvv;d.P&T-2'pX^3` X U=xI!R {ˍ|, /ɼvKpxSB6Qeo(10ETQu-ژ-=Fz8|<=aQ/e{=[Ne`c>Kcuoz _#ǹ+a\$8BvZvw{]ӻ2'o8]2/ԇ|L乘x8pbcMT]!zG2שRTQEUbWb5,[,u Aڵһ.QeUT]n$'1y 6&nyj*e~qc`y LNTQu- Ů,Ŗ ?-07cs*Wz8/kƮ#KWo,yY:]wnߞ{H>x-'Pu-cިe<8^BZsYt4R:!R@6 U2xHháⷷ{Vv?VR>/o^3`Dյn`YETQu-âoYcW?3XҊ?^f -'km!;-鴸KyRe.eX,ַln:8w8L;\p8kĕ#On1v=ekPZE)pnv5[N9p_sJoz`FȦq+ Wy w~p骘dOn1}8#,EյLII0(cW?漇}iĠ7s-'? X))IXOQo@ SDU2,J[Z>y|gj 8|5V0o9 aQ ܲ[.z8\6Fz _#kN遝SZF$}[5yyΎ ^cpp3eedJJ҇E_sJoݰל;556/]T]ȔsR?֋wv4hXKkr'7枿`b))I?r;sNյLII0(cW?S 1|D[N2U22%%ܢTu^[pN%]o99T]ȔsR?y|lk{ؗK jkanQ*ă ص@SkanQ*5V02UT]v\{G9k Nµ-'k oe#^>U2&0u-O#X}NF*աZz$=v}c)xboxx.fN*SEյ BY7W2ܾ$ I %Vv>k}Ǔq-ou5c+?6/5V%:f [=z-˿p5)]k}Gյ1]?I'Y8q(Ru-#x95.b2vE*V-wkKQKUTQEU[II+Fs=)&\#UTQ%g7ٔwQEU$,]O#^f sp(v\{FbHl?d$?5v {b/pI /ZVc%a΋]q{-;XCQ k*]cM8yMoFyv8$s7Ly%8`^B*~o|\ړGbsH3y<^ܶXɗ򏍇#ͨRlXo>^mL<ұL '4㓓>gc ~H3H)oiݙUcf*~op Hbׇ+/?Hȱⱁz^!x]zlU+;ν@ :f_bP>wQy<^Rzi?Rz8A Vԏe;'W2wQ?6Fw쮇z8k'1`Ѿ<ҌcMb`،zi->xc#\}0[÷H<іx ԏ \0o; kWz.d^i8r+2p6/d(SE-oF56FHRp[fQ# v]D5O vps=$棾pL5O0'4qy qI G}#y.$2UT 5 * p#>^ܶX}yq^:r|yҾQnҝ$pxK+jnR2{Y} ˼?xojG.s}ov\OƮ,D >e x|X#Xֹ~#6ÔvxGҾƞ7'Y1xՇ×Kpxإvoy׸":/%cAF>QpH3ꥥR^&10<^mL<ұL wfY7OտĠIr<+H_bP/᱁2UTY[=ZD5O=H1\Zv#Y|U` 9i'Vt4jLnWKDa^BPuN,?_dkƚp# T]tp?_.W,I_6"jynÜ:R=LL$wQHˇâ{yQuD<+YUH@Zky7sQsN87{2I[E]T]#=!ZsHn^kt@XH d^qokX*e2ڼ0 ZFc5 jؼ| ZC@յ|^T]ḱ«oEp!) Sx5]T]kNsa=y#ծE5ғhla{o f1(E4STk$.LĀh&L(VeUHߠ}")a=k !fbb@4E5#U@E@5ee=颊LRuNlY;D[=ϫz̀[NxF}X^3`sٟ$7jyU'<^1^3olnv/Q?"c U%8\6F:7G`,]z8i,>ʿz{GN?>W|^k'w7xX7_Iy%?,]R 䕪r[N s^k`Gvx`rf|0px1sck`rfxrd>K I\'79rC>>Ino klތ{;T^3`:OmuI [N(SE՟EĢpz)í x ܛ|B{s'sGze?J>~8LUؕe`%u-[bI"^X@]Pʲ^\eA]Kt ܿ10=z8JymoQzRuo9\6/ㅡ3J'`KT=͵/,[ߒXb-u ѭE{=y֨Hwx"*MbProgram UnSqueeze; (* Written: 01/29/1986 15:59:57 *) { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] Program UnSqueeze [] [] [] [] A file de-compression program. Compatible with CP/M or DOS, [] [] Turbo Pascal Version 2.0 and above. [] [] [] [] [] [] Bob Berry, CompuServe 76555,167 [] [] [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } Const Version = 'Version 2.1 Last Update 01/29/1986'; PrinterToggle = '/P'; FormFeed = ^L; Recognize = $FF76; Recognize2 = $FFFA; NumVals = 257; { max tree size + 1 } SpEOF = 256; { special end of file marker } DLE: char = #$90; Space = ' '; Type Tree = array [0..255,0..1] of integer; HexStr = string[4]; FileName = string[30]; FlePtr = ^FileLst; FileLst = Record FNme: FileName; NxtF: FlePtr; end; Var InFileName, OutFileName, FMask, FileDateString, AnotherString, DrivePrefix, OutDrive: FileName; InFileSize, OutFileSize: real; DNode: Tree; InChar, CurIn, FileCkSum, Crc, BPos, FileDate, FileTime, i, RepCt, NumNodes: integer; HeapTop: ^Integer; FFirst, FLast, FCurrent: FlePtr; LoggedDrive, C, LastChar: char; PrinterEcho, AllDone, EoFile: boolean; { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] Pick One: CP/M or DOS and comment out the one that doesn't apply [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } (* {$I cpm.inc } *) {$I dos.inc } {$I usqzmain.inc } Procedure Compress(Var TheString: FileName); begin While Pos(' ',TheString) > 0 do Delete(TheString,Pos(' ',TheString),1); end; { Procedure Compress } Procedure UnSqueeze; begin NumNodes:=ord(GetW); If (NumNodes<0) or (NumNodes>=NumVals) then begin WriteLn('File has invalid decode tree size.'); CloseInFile; end else begin Assign(OutFile,OutFileName); ReWriteOutFile; WriteLn; WriteLn('The file ',InFileName,' (',InFileSize:6:0, ' bytes ) is being UnSqueezed to ',OutFilename); DNode[0,0]:=-(succ(SpEOF)); DNode[0,1]:=-(succ(SpEOF)); NumNodes:=pred(NumNodes); For i:=0 to NumNodes do begin DNode[i,0]:=GetW; DNode[i,1]:=GetW; end; Crc:=0; If FileDateString>'' then Write('[ File Date: ',FileDateString,' ] '); Write('UnSqueezing,'); While not EOF(InFile) or (not EoFile) do begin C:=GetCr; If not EoFile then begin WriteOutFile(C); Crc:=Crc+ord(C); end; end; CloseInFile; CloseOutFile; WriteLn(' Done.'); If Crc<>FileCkSum then begin WriteLn('File CheckSum Was ',Hex(FileCkSum),', Is ',Hex(Crc)); end; OutFileSize:=GetSizeOfOutFile; WriteLn('The file ',OutFileName,' is',OutFileSize:6:0, ' bytes (',(100.0*OutFileSize/InFileSize):5:1,'%).'); end; end; { Procedure UnSqueeze } { [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [] UnSqueeze MainLine [] [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] } begin ClrScr; GetLoggedDrive; If CommandLine > '' then { Get InFileName from Command Line } begin WriteLn; InFileName:=CommandLine; For i:=1 to Length(InFileName) do InFileName[i]:=UpCase(InFileName[i]); PrinterEcho:= ( Pos(PrinterToggle,InFileName) > 0 ); If PrinterEcho then begin SetEchoToPrinter; Delete(InFileName,Pos(PrinterToggle,InFileName),2); end; Compress(InFileName); end else begin InFileName:=''; PrinterEcho:=False; end; Write('File UnSqueezer'); For i:=1 to 64-Length(Version) do Write(Space); WriteLn(Version); Repeat AllDone:=false; If InFileName='' then begin WriteLn; Write('Enter file to UnSqueeze ( or to exit ) >'); ReadLn(InFileName); For i:=1 to Length(InFileName) do InFileName[i]:=UpCase(InFileName[i]); Compress(InFileName); end; If Pos('.',InFileName)=0 then InFileName:=InFileName+'.'; If Pos(':',InFileName)=0 then InFileName:=LoggedDrive+':'+InFileName; DrivePrefix:=Copy(InFileName,1,2); If Length(InFileName)<4 then AllDone:=true { <= Blank name, AllDone } else begin Mark(HeapTop); FindFiles(InFileName); If FFirst=Nil then WriteLn('Input file(s) ',InFileName,' not found.') else begin Write('Output Drive ( or for ',InFileName[1],': ) >'); ReadLn(OutDrive); Repeat { Until InFileName='' } InFileName:=NextFile; If InFileName>'' then begin InFileName:=DrivePrefix+InFileName; OutFileName:=Copy(InFileName,1,2); If Length(OutDrive)>0 then OutFileName[1]:=UpCase(OutDrive[1]); Assign(InFile,InFileName); Reset(InFile); InFileSize:=TheSizeOf(InFile); If InFileSize=0 then begin WriteLn('Input file ',InFileName,' is empty.'); CloseInFile; end else begin CloseInFile; ResetInFile; RepCt:=0; BPos:=99; EoFile:=false; FileDateString:=''; AnotherString:=''; i:=GetW; Case i of Recognize: begin FileCkSum:=GetW; Repeat { Until InChar=0 } InChar:=GetI; If InChar<>0 then OutFileName:=OutFileName+Chr(InChar); Until InChar=0; UnSqueeze; end; Recognize2: begin Repeat { Until InChar=0 } InChar:=GetI; If InChar<>0 then OutFileName:=OutFileName+Chr(InChar); Until InChar=0; Repeat { Until InChar=0 } InChar:=GetI; If InChar<>0 then FileDateString:=FileDateString+ Chr(InChar); Until InChar=0; Repeat { Until InChar=0 } InChar:=GetI; If InChar<>0 then AnotherString:=AnotherString+ Chr(InChar); Until InChar=0; InChar:=GetI; { ^Z (ASCII EOF) } FileCkSum:=GetW; FileDate:=GetW; FileTime:=GetW; UnSqueeze; end; Else begin CloseInFile; WriteLn('I don''t recognize ',InFileName, ' as a squeezed file.'); end; end; { Case i } end; end; Until InFileName=''; end; end; InFileName:=''; Until AllDone; If PrinterEcho then Write(Lst,FormFeed); end.Function Hex(num: integer): HexStr; Const h: String[16]='0123456789ABCDEF'; Var i, j: integer; str: hexstr; begin Str:='0000'; j:=num; For i:=4 DownTo 1 do begin Str[i]:=h[succ(j and $F)]; j:=j shr 4; end; Hex:=Str; end; { Function Hex } { getw - get a word value from the input file } Function GetW: integer; Var in1,in2: char; begin ReadInFile(In1); ReadInFile(In2); GetW:=ord(in1) + ord(in2) shl 8; end; { Function GetW } Function GetI: integer; Var ch: Char; begin ReadInFile(ch); GetI:=ord(ch); end; { Function GetI } Function GetUHuff: char; Var i: integer; begin i:=0; Repeat BPos:=succ(BPos); If BPos>7 then begin CurIn:=GetI; BPos:=0; end else CurIn:=CurIn shr 1; i:=ord(DNode[i,ord(CurIn and $0001)]); Until (i<0); i:=-(succ(i)); If i=SpEOF then begin EoFile:=true; GetuHuff:=chr(26) end else GetUHuff:=chr(i); end; { Function GetUHuff } Function GetCr: char; var C: char; begin If (RepCt>0) then begin RepCt:=pred(RepCt); GetCr:=LastChar; end else begin C:=GetuHuff; If C<>DLE then begin GetCr:=C; LastChar:=C; end else begin RepCt:=ord(GetuHuff); If RepCt=0 then GetCr:=DLE else begin RepCt:=pred(pred(RepCt)); GetCr:=LastChar; end; end; end; end; { Function GetCr }