(* TURBO pascal version of MSBMKB                            *)
(*                                                           *)
(* Author: Gisbert W.Selke (RECK@DBNUAMA1.BITNET)            *)
(*         Wissenschaftliches Institut der Ortskrankenkassen *)
(*         Kortrijker Strasse 1                              *)
(*         D-5300 Bonn 1                                     *)
(*         West Germany                                      *)
(*         10 February 1988                                  *)
(*         RECK@DBNUAMA1.BITNET                              *)
(*                                                           *)
(*  Produces boo-encoding of a binary file for transfer over *)
(*  data links. Beware of EBCDIC <-> ASCII gremlins, however!*)
(*                                                           *)
(*  Version 1.2: change for Turbo-Pascal 4.0                 *)
(*                                                           *)

(*$S-*)     (* stack checking off *)
(*$R-*)     (* Range checking off *)
(*$B-*)     (* Boolean complete evaluation off *)
(*$I+*)     (* I/O checking on *)
(*$N-*)     (* No numeric coprocessor *)
(*$M 65500,16384,16384*)  (* Reduce maximum heap *)

program msbmkb;

uses crt;

const repchar  : char = '~';
      nullbyte : byte = $00;
      b2       : byte = $03;
      b4       : byte = $0F;
      b6       : byte = $3F;
      blocksize       = 128;
      offset          = 48;  (* ord('0') *)
      maxrep          = 78;
      bufsize         = 32000;
      maxlinlength    = 76;
      defaultext      = '.BOO';

type buftype = array (.1..bufsize.) of byte;

var a, b, c :  byte;
    bytect, buffct, restbytes, maxblocks, bbufsize, linlength, repct : integer;
    fs, rin, rout : longint;
    reff : real;
    isend,preend : boolean;
    infilename, outfilename, sname : string(.63.);
                                         (* maximum path length in DOS *)
    buffer, outbuffer : buftype;
    infile  : file;
    outfile : text;

 function getbyte : byte;
  (* get one byte from input stream; mark eof and yield 0 afterwards *)
   var ires : word;
  begin  (* getbyte *)
   if isend then
    begin  (* end of file *)
     getbyte := nullbyte;
     exit;
    end;  (* end of file *)
   if bytect >= bbufsize then
    begin  (* read next buffer *)
     if preend then
      begin  (* end of file *)
       getbyte := 0;
       isend := true;
       exit;
      end;  (* end of file *)
     blockread(infile,buffer,maxblocks,ires);
     if ires <> maxblocks then
      begin  (* last buffer! *)
       preend := true;
       bbufsize := restbytes;
      end;   (* last buffer! *)
     bytect := 0;
     inc(buffct);
     write(chr(13),'Buffer ',buffct);
    end;  (* read next buffer *)
   inc(bytect);
   getbyte := buffer(.bytect.);
  end;  (* getbyte *)

 procedure prepare;
 (* get input and output file names; open files; get input file size *)

   procedure getnames;
   (* get input and output file names from command line *)
    var i : integer;
   begin  (* getnames *)
    if not (paramcount in (.1..2.)) then
     Begin  (* argument number error *)
      writeln('Wrong number of parameters.');
      writeln('Usage: MSBMKB <input file name> (<output file name>)');
      halt(1);
     end;  (* argument number error *)
    infilename := paramstr(1);
    for i := 1 to length(infilename) do infilename(.i.) :=
                                        UpCase(infilename(.i.));
    sname := infilename;
    while pos(':',sname) <> 0 do delete(sname,1,pos(':',sname));
    while pos('\',sname) <> 0 do delete(sname,1,pos('\',sname));
    outfilename := sname;
    if pos('.',outfilename) <> 0 then delete(outfilename,
                                            pos('.',outfilename),999);
    outfilename := outfilename + defaultext;
    if outfilename = infilename then outfilename(.length(infilename).) :=
                                succ(outfilename(.length(infilename).));
    if paramcount = 2 then outfilename := paramstr(2);
    for i := 1 to length(outfilename) do outfilename(.i.) :=
                                        UpCase(outfilename(.i.));
   end;  (* getnames *)

   procedure openfiles;
   (* open input and output files; abort if error *)
    var ch : char;
   begin  (* openfiles *)
    assign(infile,infilename);
    (*$I-*) reset(infile,blocksize); (*$I+*)
    if IOResult <> 0 then
     begin
      writeln('Can''t find ',infilename);
      halt(1);
     end;
    assign(outfile,outfilename);
    settextbuf(outfile,outbuffer);
    (*$I-*) reset(outfile); (*$I+*)
    if IOResult = 0 then
     begin  (* overwrite existing file? *)
      write('Output file ',outfilename,
            ' already exists. Continue (y/n)? ');
      repeat
       ch := readkey;
       ch := upcase(ch);
       until ch in (.'N','0','J','Y','1'.);
      writeln;
      if ch in (.'N','0'.) then halt(1);
     end;  (* overwrite existing file? *)
    (*$I-*) rewrite(outfile); (*$I+*)
    if IOResult<>0 then
     begin
      writeln('Can''t open output file ',outfilename);
      halt(1);
     end;
   end;  (* openfiles *)

   procedure getsize;
   (* get size of input file; initialize certain variables *)
    var dummyfile : file of byte;
   begin  (* getsize *)
    assign(dummyfile,infilename);
    reset(dummyfile);
    fs := filesize(dummyfile);
    close(dummyfile);
    restbytes := fs - (pred(fs) div bufsize) * bufsize;
    buffct := 0;
    bbufsize := bufsize;
    bytect := succ(bbufsize);
    maxblocks := bufsize div blocksize;
  end;  (* getsize *)

  begin  (* prepare *)
   getnames;
   openfiles;
   getsize;
   checkbreak := false;
  end; (* prepare *)

begin  (* main *)
 writeln('MSBPCT 1.2');
 prepare;
 writeln('Encoding ',infilename,' to ',outfilename);
 writeln(outfile,sname);
 isend  := false;
 preend := false;
 linlength := 0;
 rout := length(sname) + 2;
 a := getbyte;
 while not isend do
  begin  (* get all chunks *)
   b := getbyte;
   if (a=0) and (b=0) then
    begin  (* repeatnull *)
     repct := 1;
     repeat
       inc(repct);
       a := getbyte;
      until isend or (a <> nullbyte) or (repct >= maxrep);
     if linlength+2 > maxlinlength then
      begin  (* finish line *)
       writeln(outfile);
       rout := rout + linlength + 2;
       linlength := 0;
      end;  (* finish line *)
     write(outfile,repchar,chr(repct+offset));
     inc(linlength,2);
    end  (* repeatnull *)  else
    begin  (* ordinary chunk *)
     c := getbyte;
     if linlength+4 > maxlinlength then
      begin  (* finish line *)
       writeln(outfile);
       rout := rout + linlength + 2;
       linlength := 0;
      end;  (* finish line *)
     write(outfile,chr((a shr 2) + offset),
                   chr((((a and b2) shl 4) or (b shr 4)) + offset),
                   chr((((b and b4) shl 2) or (c shr 6)) + offset),
                   chr((c and b6) + offset));
     inc(linlength,4);
     a := getbyte;
    end;  (* ordinary chunk *)
  end;  (* get all chunks *)
 writeln(outfile);
 rout := rout + linlength + 2;
 flush(outfile);
 close(infile);
 close(outfile);
 rin := longint(pred(buffct))*bufsize + bytect;
 reff := 100.0 * rin / rout;
 writeln(chr(13),rin:0,' bytes in, ',rout:0,
         ' bytes out; efficiency: ',reff:0:1,'%');
end. (* main *)
