home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c019 / 5.ddi / VITTER.ZIP / VITTER.PAS next >
Encoding:
Pascal/Delphi Source File  |  1993-05-08  |  18.7 KB  |  717 lines

  1. Program Vitter;
  2.  
  3.  
  4. {$R-}
  5.  
  6. uses CRT,DOS;
  7.  
  8.  
  9. {
  10.   This is a implementation of the dynamic Huffman coding algorithm presented
  11.   by Jeffrey Scott Vitter in ACM Transactions on Mathematical Software,
  12.   Vol, 15, June 1989, p 158, and described in Journal of the ACM, Vol. 34,
  13.   October 1987, p 825.
  14.  
  15.   The algorithm is implemented in the context of a stand-alone file
  16.    compression utility, which can be used to compress/decompress files
  17.    one at a time. This program serves only to illustrate the use of
  18.    the algorithm.
  19.  
  20.  
  21.  
  22.   This compression algorithm has several good features:
  23.     1) It's single pass, so its suitable for pipelining or I/O.
  24.     2) As the Huffman tree is built dynamically, there is no overhead
  25.         involved in storing/sending the tree itself to the decoder, the
  26.         decoder builds the tree as it goes just like the encoder.
  27.     3) Compression is theoretically optimal among one-pass huffman type
  28.         encoders.
  29.  
  30.   Disadvantages:
  31.     1) This implementation is fairly slow. (This implementation
  32.         was written with clarity, rather than speed in mind, and there are
  33.         several optimizations possible.)
  34.     2) Memory requirements are fairly large as compared to some other huffman
  35.         type compression routines. (Low as compared to many LZ-based
  36.         techniques.)
  37.     3) Compression may not be as good as a regular huffman tree. It seems to
  38.         vary between about 33% and 60% on text files, based on very limited
  39.         and informal testing.
  40.  
  41.  
  42.   Programmer : Douglas P. Webb
  43.   Last Updated: 14 Sept. 1992
  44. }
  45.  
  46.  
  47. CONST
  48.   CharBufSize = 2048;      { I/O Buffer. }
  49.   WordBufSize = 1024;      { I/O Buffer. }
  50.   N =  256;                { Alphabet size. 256 chars in ASCII }
  51.  
  52.  
  53. TYPE
  54.   Vitter_Header_Type = RECORD                   { 17 bytes in size. }
  55.                          Name : String[12];
  56.                          FSize : LongInt;
  57.                        END;
  58.  
  59.   CharBuffer = Array[0..PRED(CharBufSize)] OF Char;
  60.   WordBuffer = Array[0..PRED(WordBufSize)] OF WORD;
  61.  
  62.  
  63. CONST
  64.   Bytes_Left : BOOLEAN = TRUE;
  65.  
  66.      { Used by Transmit. }
  67.   OBufPosn : Word = 0;
  68.   WriteWord : Word = 0;
  69.   WShifts : WORD = 15;
  70.  
  71.      { Used by Receive. }
  72.   BufRead : Integer = 0;
  73.   BufPosn : Integer = 0;
  74.   Shifts  : WORD = 0;
  75.   ReadWord: WORD = 0;
  76.  
  77.  
  78. VAR
  79.   Header  : Vitter_Header_Type;
  80.  
  81.   Alpha     : Array[0..N] OF WORD;
  82.   Rep       : Array[0..N] OF Integer;
  83.   Block     : Array[1..2*N-1] OF Integer;
  84.   Weight    : Array[1..2*N-1] OF LongInt;
  85.   Parent    : Array[1..2*N-1] OF Integer;
  86.   Parity    : Array[1..2*N-1] OF Integer;
  87.   RtChild   : Array[1..2*N-1] OF Integer;
  88.   First     : Array[1..2*N-1] OF Integer;
  89.   Last      : Array[1..2*N-1] OF Integer;
  90.   PrevBlock : Array[1..2*N-1] OF Integer;
  91.   NextBlock : Array[1..2*N-1] OF Integer;
  92.   Stack     : Array[1..2*N-1] OF Integer;
  93.   AvailBlock : Integer;
  94.  
  95.   M,E,R,Z : Integer;
  96.  
  97.  
  98.  
  99.   CInBuf,COutBuf : ^CharBuffer;
  100.   WInBuf,WOutBuf : ^WordBuffer;
  101.  
  102.   VitFile,InFile,OutFile : File;
  103.   FileName : String[12];
  104.   Dir: DirStr;
  105.   Name: NameStr;
  106.   Ext: ExtStr;
  107.   FoundFile : SearchRec;
  108.   Ch : Char;
  109.  
  110.  
  111.  
  112.  
  113. Procedure Initialize;
  114. {
  115.   This procedure form an initial Huffman tree consisting of a single leaf
  116.   0-node. The global variable Z is always equal to 2n-1.
  117. }
  118. VAR
  119.   I : Integer;
  120. BEGIN
  121.   Bytes_Left := TRUE;
  122.  
  123.   OBufPosn  := 0;          { Variables used by 'Transmit' }
  124.   WriteWord := 0;
  125.   WShifts   := 15;
  126.  
  127.   BufRead := 0;            { Variables used by 'Receive' }
  128.   BufPosn := 0;
  129.   Shifts  := 0;
  130.   ReadWord:= 0;
  131.  
  132.   M := 0;
  133.   E := 0;
  134.   R := -1;
  135.   Z := 2*N -1;
  136.  
  137.   Alpha[0] := 0;
  138.   Rep[0] := 0;
  139.   FOR I := 1 TO N DO
  140.     BEGIN
  141.       INC(M);
  142.       INC(R);
  143.       IF R*2 = M THEN
  144.         BEGIN
  145.           INC(E);
  146.           R := 0;
  147.         END;
  148.       Alpha[I] := I;
  149.       Rep[I] := I;
  150.     END;
  151.  
  152.  { Initialize node N as the 0-node }
  153.   Block[N] := 1;
  154.   PrevBlock[1] := 1;
  155.   NextBlock[1] := 1;
  156.   Weight[1] := 0;
  157.   First[1] := N;
  158.   Last[1] := N;
  159.   Parity[1] := 0;
  160.   Parent[1] := 0;
  161.  
  162.  { Initialize available block list }
  163.  
  164.   AvailBlock := 2;
  165.   FOR I := AvailBlock to Z-1 DO
  166.     NextBlock[I] := I+1;
  167.   NextBlock[Z] := 0;
  168.  
  169. END;
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176. Function FindChild(J,Parity: Integer):Integer;
  177.  
  178. {
  179.   This function returns the node number of either the left or right child
  180.   of node j, depending on whether the parity parameter is set to 0 or 1.
  181. }
  182.  
  183. VAR
  184.   Delta, Right, Gap : Integer;
  185.  
  186. BEGIN
  187.   Delta := 2*(First[Block[J]] - J) + 1 - parity;
  188.   Right := rtChild[Block[J]];
  189.   Gap := Right - Last[Block[Right]];
  190.   IF Delta <= Gap THEN
  191.     FindChild := Right - Delta
  192.   ELSE
  193.     BEGIN
  194.       DEC(Delta,SUCC(Gap));
  195.       Right := First[PrevBlock[Block[Right]]];
  196.       Gap := Right - Last[Block[Right]];
  197.       IF Delta <= Gap THEN
  198.         FindChild := Right - Delta
  199.       ELSE FindChild := First[PrevBlock[Block[Right]]] - Delta + Gap + 1;
  200.     END;
  201. END;
  202.  
  203.  
  204.  
  205.  
  206. Procedure InterchangeLeaves(E1,E2 : Integer);
  207. VAR
  208.   Temp : Integer;
  209. BEGIN
  210.   Rep[Alpha[E1]] := E2;
  211.   Rep[Alpha[E2]] := E1;
  212.   Temp := Alpha[E1];
  213.   Alpha[E1] := Alpha[E2];
  214.   Alpha[E2] := Temp;
  215. END;
  216.  
  217.  
  218.  
  219.  
  220.  
  221. Procedure Update(K : Char);
  222.  
  223. {
  224.   This procedure is the main component of the algorithm. It is called by
  225.   both 'EncodeAndTransmit' and 'ReceiveAndDecode' in order to modify the
  226.   dynamic Huffman tree to account for the letter just processed.
  227. }
  228.  
  229. VAR
  230.   Q,LeafToIncrement,Bq,B,OldParent,OldParity,Nbq,Par,Bpar : Integer;
  231.   Slide : Boolean;
  232.  
  233.  
  234.  
  235.   Procedure FindNode;
  236.   {
  237.     This procedure sets Q to point to the leaf to process. If that leaf is the
  238.     0-node, which corresponds to the transmission of a letter that has not
  239.     been transmitted earlier in the message, the 0-node is split to form an
  240.     extra leaf if there is still an untransmitted letter left in the alphabet.
  241.     Otherwise, Q is interchanged with the leader of its block.
  242.   }
  243.   BEGIN
  244.     Q := Rep[Byte(K)];
  245.     LeafToIncrement := 0;
  246.     IF q <=M THEN         { A zero weight becomes positive. }
  247.       BEGIN
  248.         InterchangeLeaves(Q,M);
  249.         IF R = 0 THEN
  250.           BEGIN
  251.             R := M DIV 2;
  252.             IF R > 0 THEN
  253.               E := E - 1;
  254.           END;
  255.         M := M-1;
  256.         R := R-1;
  257.         Q := SUCC(M);
  258.         Bq := Block[Q];
  259.         IF M > 0 THEN
  260.           BEGIN
  261.             { Split the 0-node into an internal node with two children.
  262.               The new 0-node is node M; the old 0-node is node M+1; the
  263.               new parent nodes M and M+1 is node M+N. }
  264.             Block[M] := Bq;
  265.             Last[Bq] := M;
  266.             OldParent := Parent[Bq];
  267.             Parent[Bq] := M+N;
  268.             Parity[Bq] := 1;
  269.  
  270.             { Create a new internal block of zero weight for node M + N }
  271.             B := AvailBlock;
  272.             AvailBlock := NextBlock[AvailBlock];
  273.             PrevBlock[B] := Bq;
  274.             NextBlock[B] := NextBlock[Bq];
  275.             PrevBlock[NextBlock[Bq]] := B;
  276.             NextBlock[Bq] := B;
  277.             Parent[B] := OldParent;
  278.             Parity[B] := 0;
  279.             RtChild[B] := Q;
  280.             Block[M+N] := B;
  281.             Weight[B] := 0;
  282.             First[B] := M + N;
  283.             Last[B] := M + N;
  284.             LeafToIncrement := Q;
  285.             Q := M + N;
  286.           END;
  287.       END
  288.     ELSE       { Interchange Q with the first node in Q's block }
  289.       BEGIN
  290.         InterchangeLeaves(Q,First[Block[Q]]);
  291.         Q := First[Block[Q]];
  292.         IF (Q= SUCC(M)) AND (M>0) THEN
  293.           BEGIN
  294.             LeafToIncrement := Q;
  295.             Q := Parent[Block[Q]];
  296.           END;
  297.       END;
  298.   END;
  299.  
  300.  
  301.  
  302.   Procedure SlideAndIncrement;
  303.   {
  304.     This procedure incrments the weight of node Q by 1 and adjusts the tree
  305.     pointers to reflect the new implicit numbering.  Finally, Q is set to
  306.     point to the node one level higher in the tree that needs incrementing
  307.     next.
  308.   }
  309.   BEGIN   { Q is currently the first node in its block. }
  310.     Bq :=  Block[Q];
  311.     Nbq := nextBlock[Bq];
  312.     Par := Parent[Bq];
  313.     OldParent := Par;
  314.     OldParity := Parity[Bq];
  315.     IF ((Q<=N) AND (First[Nbq] > N) AND (Weight[Nbq] = Weight[Bq])) OR
  316.       ((Q>N) AND (First[Nbq] <= N) AND (Weight[Nbq] = SUCC(Weight[Bq]))) THEN
  317.       BEGIN    { Slide Q over the next Block }
  318.         Slide := TRUE;
  319.         OldParent := Parent[Nbq];
  320.         OldParity := Parity[Nbq];
  321.  
  322.      { Adjust child pointers for next higher level in tree. }
  323.         IF Par > 0 THEN
  324.           BEGIN
  325.             Bpar := Block[Par];
  326.             IF RtChild[BPar] = Q THEN
  327.               RtChild[BPar] := Last[Nbq]
  328.             ELSE IF RtChild[BPar] = First[Nbq] THEN
  329.              RtChild[Bpar] := Q
  330.             ELSE RtChild[Bpar] := SUCC(RtChild[Bpar]);
  331.             IF Par <> Z THEN
  332.               IF Block[SUCC(Par)] <> Bpar THEN
  333.                 IF RtChild[Block[SUCC(Par)]] = First[Nbq] THEN
  334.                   RtChild[Block[SUCC(Par)]] := Q
  335.                 ELSE IF Block[RtChild[Block[SUCC(Par)]]] = Nbq THEN
  336.                   RtChild[Block[SUCC(Par)]] := SUCC(RtChild[Block[SUCC(Par)]]);
  337.           END;
  338.  
  339.      { Adjust parent pointers for block Nbq }
  340.         Parent[Nbq] := Parent[Nbq] -1 + Parity[Nbq];
  341.         Parity[Nbq] := 1 - Parity[Nbq];
  342.         Nbq := NextBlock[Nbq];
  343.       END
  344.     ELSE Slide := FALSE;
  345.  
  346.     IF (((Q <= N) AND (First[Nbq] <= N)) OR ((Q>N) AND (First[Nbq] > N))) AND
  347.        (Weight[Nbq] = SUCC(Weight[Bq])) THEN
  348.       BEGIN   { Merge Q into the block of weight one higher }
  349.         Block[Q] := Nbq;
  350.         Last[Nbq] := Q;
  351.         IF Last[Bq] = Q THEN     { Q's old block disappears }
  352.           BEGIN
  353.             NextBlock[PrevBlock[Bq]] := NextBlock[Bq];
  354.             PrevBlock[NextBlock[Bq]] := PrevBlock[Bq];
  355.             NextBlock[Bq] := AvailBlock;
  356.             AvailBlock := Bq;
  357.           END
  358.         ELSE
  359.           BEGIN
  360.             IF Q > N THEN
  361.               RtChild[Bq] := FindChild(PRED(Q),1);
  362.             IF Parity[Bq] = 0 THEN
  363.               DEC(Parent[Bq]);
  364.             Parity[Bq] := 1 - Parity[Bq];
  365.             First[Bq] := PRED(Q);
  366.           END;
  367.       END
  368.     ELSE IF Last[Bq] = Q THEN
  369.       BEGIN
  370.         IF Slide THEN       { Q's block is slid forward in the block list }
  371.           BEGIN
  372.              PrevBlock[NextBlock[Bq]] := PrevBlock[Bq];
  373.              NextBlock[PrevBlock[Bq]] := NextBlock[Bq];
  374.              PrevBlock[Bq] := PrevBlock[Nbq];
  375.              NextBlock[Bq] := Nbq;
  376.              PrevBlock[Nbq] := Bq;
  377.              NextBlock[PrevBlock[Bq]] := Bq;
  378.              Parent[Bq] := OldParent;
  379.              Parity[Bq] := OldParity;
  380.           END;
  381.         INC(Weight[Bq]);
  382.       END
  383.     ELSE                    { A new Block is created for Q. }
  384.       BEGIN
  385.         B := AvailBlock;
  386.         AvailBlock := nextBlock[AvailBlock];
  387.         Block[Q] := B;
  388.         First[B] := Q;
  389.         last[B] := Q;
  390.         IF Q > N THEN
  391.           BEGIN
  392.             RtChild[B] := RtChild[Bq];
  393.             RtChild[Bq] := FindChild(Pred(Q),1);
  394.             IF RtChild[B] = PRED(Q) THEN
  395.               Parent[Bq] := Q
  396.             ELSE IF Parity[Bq] = 0 THEN
  397.               DEC(Parent[Bq]);
  398.           END
  399.         ELSE IF Parity[Bq] = 0 THEN
  400.           DEC(Parent[Bq]);
  401.         First[Bq] := PRED(Q);
  402.         Parity[Bq] := 1 - Parity[Bq];
  403.  
  404.      { Insert Q's Block in its proper place in the block list. }
  405.         PrevBlock[B] := PrevBlock[Nbq];
  406.         NextBlock[B] := Nbq;
  407.         PrevBlock[Nbq] := B;
  408.         NextBlock[PrevBlock[B]] := B;
  409.         Weight[B] := SUCC(Weight[Bq]);
  410.         Parent[B] := OldParent;
  411.         Parity[B] := OldParity;
  412.       END;
  413.    { Move Q one level higher in the tree. }
  414.     IF Q <= N THEN
  415.       Q := OldParent
  416.     ELSE Q := Par;
  417.   END;
  418.  
  419.  
  420.  
  421.  
  422. BEGIN
  423.  { Set Q to the node whose weight should increase }
  424.   FindNode;
  425.   WHILE Q > 0 DO
  426.     { At this point , Q is the first node in its block. Increment Q's
  427.       weight by 1 and slide Q if necesary over the next block to maintain
  428.       the invariant. Then set Q to the node one level higher that needs
  429.       incrementing text. }
  430.     SlideAndIncrement;
  431.  
  432.  { Finish up some special cases involving the 0-node }
  433.   IF LeaftoIncrement <> 0 THEN
  434.     BEGIN
  435.       Q := LeafToIncrement;
  436.       SlideAndIncrement;
  437.     END;
  438. END;
  439.  
  440.  
  441.  
  442.  
  443. Procedure Transmit(I : Integer);
  444.  
  445. CONST
  446.   One = 32768;
  447.  
  448. BEGIN
  449.   IF I = 1 THEN
  450.     INC(WriteWord,One);
  451.   WriteWord := WriteWord SHR 1;
  452.   DEC(WShifts);
  453.   IF WSHifts = 0 THEN
  454.     BEGIN
  455.       WOutBuf^[OBufPosn] := WriteWord;
  456.       IF OBufPosn = PRED(WordBufSize) THEN
  457.         BEGIN
  458.           BlockWrite(OutFile,WOutBuf^,2*WordBufSize,OBufPosn);
  459.           Write('-');
  460.           OBufPosn := 0;
  461.         END
  462.       ELSE  INC(OBufPosn);
  463.       WShifts := 15;
  464.     END;
  465. END;
  466.  
  467.  
  468.  
  469.  
  470. Procedure EncodeAndTransmit(J: Char);
  471. {
  472.   This procedure determines the encoding of letter Aj on the basis of the
  473.   path from the root of the Huffman tree to Aj's leaf, using the convention
  474.   that 0 means "go to the left child" and 1 means "go to the right child".
  475.   If Aj has not appeared previously in the message, extra bits are sent to
  476.   specify which one of the zero-weight letters has been encountered. These
  477.   extra bits are computed by the following minimum prefix code:
  478.   IF 1 <= J <= 2*R THE Aj is specified by the (E+1)-bit binary
  479.   representation of j-1; otherwise, Aj is specified by the E-bit binary
  480.   representation of J-R-1.
  481.  
  482.   The system precedure 'Transmit' is called for each bit in the encoding to
  483.   send it to the reciever.
  484. }
  485. VAR
  486.   I,II,Q,T,Root : Integer;
  487.  
  488. BEGIN
  489.   Q := Rep[ORD(J)];
  490.   I := 0;
  491.   IF Q <= M THEN    { Encode letter of zero weight }
  492.     BEGIN
  493.       DEC(Q);
  494.       IF Q < 2*R THEN
  495.         T := SUCC(E)
  496.       ELSE
  497.         BEGIN
  498.           DEC(Q,R);
  499.           T := E;
  500.         END;
  501.       FOR II := 1 to T DO
  502.         BEGIN
  503.           INC(I);
  504.           Stack[I] := Q MOD 2;
  505.           Q := Q DIV 2;
  506.         END;
  507.       Q := M;
  508.     END;
  509.   IF M = N THEN
  510.     Root := N
  511.   ELSE Root := Z;
  512.  
  513.   While Q <> Root DO   { Traverse up the tree. }
  514.     BEGIN
  515.       INC(I);
  516.       Stack[I] := (First[Block[Q]]-Q+Parity[BLock[Q]]) MOD 2;
  517.       Q := Parent[Block[Q]]-(First[Block[Q]]-Q+1-Parity[Block[Q]]) DIV 2;
  518.     END;
  519.   FOR II := I DOWNTO 1 DO
  520.     Transmit(Stack[II]);
  521. END;
  522.  
  523.  
  524.  
  525.  
  526. Function Receive: WORD;
  527.  
  528.  
  529. BEGIN
  530.   IF (BufPosn = BufRead) AND (Shifts = 0) THEN
  531.     BEGIN
  532.       BlockRead(InFile,WInBuf^,2*WordBufSize,BufRead);
  533.       BufRead := BufRead DIV 2;
  534.       Write('+');
  535.       If BufRead = 0 THEN Bytes_Left := FALSE;
  536.       BufPosn := 0;
  537.     END;
  538.   IF Shifts = 0 THEN
  539.     BEGIN
  540.       ReadWord := WInBuf^[BufPosn];
  541.       INC(BufPosn);
  542.       Shifts := 15;
  543.     END;
  544.   IF BOOLEAN(ReadWord AND 1) THEN
  545.     Receive := 1
  546.   ELSE Receive := 0;
  547.  
  548.   DEC(Shifts);
  549.   ReadWord := ReadWord SHR 1;
  550. END;
  551.  
  552.  
  553.  
  554. Function ReceiveAndDecode: Word;
  555.  
  556. {
  557.   This Function repeatedly calls a system function 'Recieve' to
  558.   read one more bit of input until the inputed sequence of 0's and
  559.   1's has specified a path to a leaf node in the huffman tree.
  560.  
  561.   Extra bits are read when K < N-1 and a 0-node is reached in order
  562.   to determine which zero-weigth letter is being transmitted.
  563. }
  564.  
  565. VAR
  566.   I,Q : Integer;
  567.  
  568. BEGIN
  569.   IF M = N THEN
  570.     Q:= N
  571.   ELSE Q := Z;                { Set Q to the root node. }
  572.   WHILE Q > N DO              { Transverse down the tree. }
  573.     Q := FindChild(Q,Receive);
  574.   IF Q = M THEN               { Decode 0-node }
  575.     BEGIN
  576.       Q := 0;
  577.       FOR I := 1 to E DO
  578.         Q := Q*2+Receive;
  579.       IF Q < R THEN
  580.         Q := Q*2 + Receive
  581.       ELSE INC(Q,R);
  582.       INC(Q);
  583.     END;
  584.   ReceiveAndDecode := Alpha[Q];
  585. END;
  586.  
  587.  
  588.  
  589.  
  590. Procedure Encode;
  591. CONST
  592.   BufRead : Word = 0;
  593.   BufPosn : Word = 0;
  594. VAR
  595.   X : Word;
  596.  
  597. BEGIN
  598.   Initialize;
  599.   BlockRead(InFile,CInBuf^,CharBufSize,BufRead);
  600.   If BufRead = 0 THEN Bytes_Left := FALSE;
  601.   BufPosn := 0;
  602.   WHILE Bytes_Left DO     { Continue until all characters encoded. }
  603.     BEGIN
  604.       { Let Aj be the next letter to encode }
  605.       EncodeAndTransmit(CInBuf^[BufPosn]);
  606.       Update(CInBuf^[BufPosn]);
  607.       INC(BufPosn);
  608.       IF BufPosn = BufRead THEN
  609.         BEGIN
  610.           BlockRead(InFile,CInBuf^,CharBufSize,BufRead);
  611.           If BufRead = 0 THEN Bytes_Left := FALSE;
  612.           BufPosn := 0;
  613.         END;
  614.     END;
  615.  
  616.   FOR X := WShifts DownTO 1 DO
  617.     WriteWord := WriteWord SHR 1;
  618.  
  619.   WOutBuf^[OBufPosn] := WriteWord;
  620.   INC(OBufPosn);
  621.   BlockWrite(OutFile,WOutBuf^,2*OBufPosn,OBufPosn);
  622. END;
  623.  
  624.  
  625.  
  626.  
  627. Procedure Decode(FSize: LongInt);
  628. Var
  629.   BufPosn : Word;
  630.   X : LongInt;
  631.  
  632. BEGIN
  633.   Initialize;
  634.   BufPosn := 0;
  635.   FOR X := PRED(FSize) DOWNTO 0 DO
  636.     BEGIN
  637.       COutBuf^[BufPosn] := Char(ReceiveAndDecode);
  638.       Update(CoutBuf^[BufPosn]);
  639.       IF BufPosn = PRED(CharBufSize) THEN
  640.         BEGIN
  641.           BlockWrite(OutFile,COutBuf^,SUCC(BufPosn),BufPosn);
  642.           BufPosn := 0;
  643.         END
  644.       ELSE  INC(BufPosn);
  645.     END;
  646.   BlockWrite(OutFile,COutBuf^,BufPosn,BufPosn);
  647. END;
  648.  
  649.  
  650.  
  651. Procedure DumpSyntax;
  652. BEGIN
  653.   CLRSCR;
  654.   GotoXY(5,3); Writeln('Vitterpack 1.01');
  655.   GotoXY(5,5); Writeln('The correct syntax for this program is:');
  656.   GotoXY(8,7); Writeln('Vitter <Filename>');
  657.   GotoXY(5,9); Writeln('If the file specified is not a VitterPack file it will be compressed.');
  658.   GotoXY(5,10); Writeln('If it is a VitterPack file it will be decompressed.');
  659. END;
  660.  
  661.  
  662.  
  663.  
  664. BEGIN
  665.   IF Paramcount < 1 THEN
  666.     BEGIN
  667.       DumpSyntax;
  668.       HALT;
  669.     END;
  670.   Filename := ParamStr(1);
  671.   FSplit(Filename,Dir,Name,Ext);
  672.   FOR Z := 1 TO 4 DO
  673.     Ext[Z] := Upcase(Ext[Z]);
  674.   IF (Ext <> '.VIT') AND (Ext <> '.') AND(Ext <> '') THEN     { Compress. }
  675.     BEGIN
  676.       New(CInBuf);
  677.       New(WOutBuf);
  678.       Header.Name := Name + Ext;
  679.       Assign(Infile,Filename);
  680.       Assign(OutFile,Name + '.Vit');
  681.       RESET(InFile,1);                { used for compression }
  682.       REwrite(OutFile,1);
  683.       Header.FSize := FIleSize(InFile);
  684.       BlockWrite(OutFile,Header,SizeOf(Header),Z);   { Save space for the header. }
  685.  
  686.       Encode;
  687.  
  688.       Close(Infile);
  689.       Close(outfile);
  690.       Dispose(CInBuf);
  691.       Dispose(WOutBuf);
  692.     END
  693.   ELSE                  { Decompress. }
  694.     BEGIN
  695.       New(WInBuf);
  696.       New(COutBuf);
  697.       Assign(Infile,Name + '.VIT');
  698.       Reset(InFile,1);
  699.       Blockread(InFile,Header,SizeOf(Header),Z);
  700.       FindFirst(Header.Name,$27,Foundfile);    { See if the file to be decompressed }
  701.       If DOSError = 0 THEN                     { already exists.                    }
  702.         BEGIN
  703.           Writeln(Header.Name,' already exists, decompress anyway ? (Y/N)');
  704.           Ch := Readkey;
  705.           IF NOT (Ch IN ['y','Y']) THEN HALT;
  706.         END;
  707.       Assign(OutFile,Header.Name);
  708.       ReWrite(OutFile,1);                { used for decompression }
  709.  
  710.       Decode(Header.FSize);
  711.  
  712.       Close(Outfile);
  713.       Close(Infile);
  714.       Dispose(WInBuf);
  715.       Dispose(COutBuf);
  716.     END;
  717. END.