unit kermpack;

interface

   uses {$U kermglob.code} kermglob;

{Change log:
30 Apr 89, V1.1: Eliminated "no timeout on receive" checks   RTC
26 Apr 89, V1.1: Changed to "timer" controlled timeouts   RTC
19 Apr 89, V1.1: minor cleanups   RTC
13 Apr 89, V1.1: Added Version message        RTC
14 Aug 88: Fixed packetwrite to output to debf          RTC
31 Jul 88: Modified for exact size binary xfr, misc. cleanup    RTC
02 Jul 88: Added binary transfers        RTC

}

   procedure spar(var packet: packettype);

   procedure rpar(var packet: packettype; len : integer);

   procedure spack(ptype: char; num:integer; len: integer; data: packettype);

   function rpack(var len, num: integer; var data: packettype): char;

   procedure bufemp(buffer: packettype; len: integer);

   function bufill(var buffer: packettype): integer;
 
   procedure pak_version;


implementation

uses {$U kermutil.code} kermutil;

const
  my_version = '   Kermpack Unit V1.1, 30 Apr 89';


procedure bufemp(*buffer: packettype; var f: text; len: integer*);

(* empties a packet into a file *)
 Note: this strips out ALL linefeed characters! 

var i,ls: integer;
    r: char;
    set_bit_8 : boolean;
    s: string255;

procedure write_bin;

  var
    dummy : integer;
 
  begin {write_bin}
    filebuf[bufpos] := r;
    i := succ(i); bufpos := succ(bufpos);
    if bufpos > blksize then
      begin
        {$I-}
        dummy := blockwrite(b_file,filebuf,1);
        if io_result <> 0 then
          begin
            io_error(ioresult);         {tell them and...}
            currstate := 'a'            {abort}
          end;
        {$I+}
        bufpos := 1
      end
  end {write_bin};

procedure write_text;

  var
    dummy : integer;
 
  begin {write_text}
      if ord(r) = lf then { skip linefeeds SP }
         i := i + 1
      else if (ord(r) = cr) then begin     (* else if a carriage return then *)
         i := i + 1;
         (*$I-*)                           (* turn i/o checking off *)
         writeln(t_file,s);                (* and write out line to file *)
         s := copy('',0,0);                (* empty the string var *)
         ls := 0;
         (*$I+*)                           (* turn i/o checking back on *)
      end
      else begin                           (* else, is a regular char, so Q5R 	 $H     s := concat(s,' ');               (* and add character to out string *)
        ls := ls + 1;
         s[ls] := r;
         if length(s) >= 255 then          {dump full string  RTC}
           begin
             {$I-}
             write(t_file,s);
             s := ''; ls := 0
             {$I+}
           end;
         i := i + 1                (* increase buffer pointer *)
      end; (* else *)
      if (io_result <> 0) then begin (* if io_error *)
         io_error(ioresult);     (* tell them and *)
         currstate := 'a';           (* abort *)
      end (* if *)
  end {write_text};

begin
   s := copy('',0,0);
   ls := 0;
   i := 0;
   while i < len do begin
      r := buffer[i];          (* get a character *)
      if en_qbin and (r = qbin) then
        begin
          i := succ(i);
          r := buffer[i];      {get 8 bit quoted char}
          set_bit_8 := true
        end
      else set_bit_8 := false;
      if (r = myquote) then begin   (* if character is control quote *)
         i := i + 1;                (* skip over quote and *)
         r := buffer[i];            (* get quoted character *)
         if not (chr(aand(ord(r),127)) in
                 ctl_set - [chr(0)..chr(31),chr(del)]) then
            r := ctl(r);    (* controllify it *)
      end; (* if *)
      if set_bit_8 then r := chr(aor(ord(r),128));
      if f_is_binary
        then write_bin
        else write_text
   end; (* while *)                     (* and get another char *)
   if not f_is_binary then
     begin
       (*$I-*)                          (* turn i/o checking off *)
       write(t_file,s);                 (* and write out line to file *)
       if (io_result <> 0) then begin   (* if io_error *)
          io_error(ioresult);           (* tell them and *)
          currstate := 'a';             (* abort *)
       end (* if *)
       (*$I+*)                          (* turn i/o checking back on *)
     end
end; (* bufemp *)


function bufill(*var buffer: packettype): integer*);

(* fill a packet with data from a file *)

var i : integer;
    r : char;

  function done : boolean;
 
    begin {done}
      if f_is_binary
        then done := (bufpos > last_blksize) and eof(b_file)
        else done := eof(t_file)
    end {done};

  begin
    i := 0;
    (* while file has some data & packet has some room we'll keep going *)
    while not done and (i < spsiz-9) do
      begin
        if f_is_binary then
          begin
            (* if we need more data from disk then *)
            if (bufpos > bufend) and (not eof(b_file)) then
              begin
                {$I-}
                bufend := blockread(b_file,filebuf[1],1) * blksize;
                if io_result <> 0 then
                  begin
                    bufill := at_badblk;
                    exit(bufill)
                  end;
                {$I+}
                (* and adjust buffer pointer *)
                bufpos := 1
              end; (* if *)
            r := filebuf[bufpos];      (* get a character *)
            bufpos := bufpos + 1;         (* increase buffer pointer *)
          end
        else
          begin
            r := t_file^;
            {$I-}
            if eoln(t_file) then
              begin
                buffer[i] := quote;      (* put (quoted) CR in buffer *)
                i := i + 1;
                buffer[i] := ctl(chr(cr));
                i := i + 1;
                r := chr(lf);            (* and we'll stick a LF after *)
              end;
            get(t_file);
            if io_result <> 0 then
              begin
                bufill := at_badblk;
                exit(bufill)
              end
            {$I+}
          end;
        if en_qbin and (ord(r) > 127) then
          begin
            r := chr(ord(r)-128);       {remove the 8th bit}
            buffer[i] := qbin;          {insert prefix}
            i := succ(i)
          end;
        if chr(aand(ord(r),127)) in ctl_set then     (* if a control char *)
          begin
            buffer[i] := quote;      (* put the quote in buffer *)
            i := i + 1;
            if not (chr(aand(ord(r),127)) in
                    ctl_set - [chr(0)..chr(31),chr(del)]) then
                r := ctl(r);   (* and un-controllify char *)
          end (* if *);
        buffer[i] := r;
        i := i + 1;
      end; (* while *)
    if (i = 0) then                         (* if we're at end of file, *)
        bufill := at_eof                    (* indicate it *)
    else                                    (* else *)
        bufill := i                         (* return # of chars in packet *)
  end; (* bufill *)


procedure spar(*var packet: packettype*);

(* fills data array with my send-init parameters *)

  begin
    packet[0] := tochar(chr(maxpack+1));   (* biggest packet i can receive *)
    packet[1] := tochar(chr(mytime));    (* when i want to be timed out *)
    packet[2] := tochar(chr(mypad));     (* how much padding i need *)
    packet[3] := ctl(chr(mypchar));      (* padding char i want *)
    packet[4] := tochar(chr(myeol));     (* end of line character i want *)
    packet[5] := myquote;                (* control-quote char i want *)
    if parity = nopar
      then packet[6] := 'Y'              (* I will do 8-bit quoting *)
      else packet[6] := my_qbin;         { I need to do 8-bit quoting }
    packet[7] := '1';                    { checksum type I want }
    packet[8] := 'N';                    { I will not do run len encoding }
    packet[9] := tochar(chr(8));         { I can do attributes packets }
    debugwrite('spar:')
  end; (* spar *)

procedure rpar(*var packet: packettype; len : integer*);

(* gets their init params *)

  begin
    if len > 0
      then spsiz := ord(unchar(packet[0]))     (* max send packet size *)
      else spsiz := 80;
    if len > 1
      then timint := ord(unchar(packet[1]))    (* when i should time out *)
      else timint := my_time;
    if len > 2
      then pad := ord(unchar(packet[2]))       (* number of pads to send *)
      else pad := 0;
    if len > 3
      then padchar := ctl(packet[3])           (* padding char to send *)
      else padchar := chr(my_pchar);
    if len > 4
      then xeol := unchar(packet[4])           (* eol char i must send *)
      else xeol := chr(my_eol);
    if len > 5
      then quote := packet[5]                  (* incoming data quote char *)
      else quote := my_quote;
    if len > 6
      then qbin := packet[6]                   { incoming 8th bit quote }
      else qbin := 'N';
    if parity = nopar
      then en_qbin := qbin in [chr(33)..chr(62),chr(96)..chr(126)]
      else
        begin
          if q_bin = 'Y' then qbin := my_qbin;
          en_qbin := qbin = my_qbin
        end;
    if len > 9
      then en_attr := aand(ord(unchar(packet[9])),8) = 8
      else en_attr := false;
    debugwrite('rpar:')
  end; (* rpar *)

procedure packetwrite(p: packettype; len: integer);

(* writes out all of a packet for debugging purposes *)

var i: integer;

  begin
    gotoxy(0,debugline);
    for i := 0 to len-1 do
        write(debf,p[i])
  end; (* packetwrite *)

procedure spack(*ptype: char; num: integer; len: integer; data: packettype*);

(* send a packet *)

var i: integer;
    chksum: char;
    ch: char;

  begin
    debugwrite('spack:');
    if ibm and (currstate <> 's') then           (* if ibm and not SINIT then *)
      begin
        set_timer(timint);
        repeat                                 (* wait for an xon *)
            repeat
            until (readch(inport, ch)) or timeout;
        until (ch = xon) or timeout;
        if timeout then                 (* if wait too long then *)
          begin
            exit(spack)                          (* get out *)
          end; (* if *)
      end; (* if *)

    for i := 1 to pad do
        write_ch(oport,parity_array[padchar]);(* write out any padding chars *)
    write_ch(oport,parity_array[chr(soh)]);                (* packet sync character *)
    chksum := tochar(chr(len + 3));          (* init chksum *)
    write_ch(oport,parity_array[tochar(chr(len + 3))]);    (* character count *)
    chksum := chr(ord(chksum) + ord(tochar(chr(num))));
    write_ch(oport,parity_array[tochar(chr(num))]);
    chksum := chr(ord(chksum) + ord(ptype));
    write_ch(oport,parity_array[ptype]);                   (* packet type *)

    for i := 0 to len - 1 do                 (* loop through data chars *)
      begin
        write_ch(oport,parity_array[data[i]]);             (* store char *)
        chksum := chr(ord(chksum) + ord(data[i]))
      end; (* for i *)
                                             (* compute final chksum *)
    chksum := chr(aand(ord(chksum) + (aand(ord(chksum),192) div 64), 63));
    write_ch(oport,parity_array[tochar(chksum)]);
    write_ch(oport,parity_array[xeol]);

    if debug then
      begin
        write(debf,' len:',len,' num:',num,' ptype:',ptype);
        packetwrite(data,len); write(debf,' chksum:',tochar(chksum))
      end
  end; (* spack *)

(*$G+*) (* turn on goto option...need it for next routine *)

function rpack(*var len, num: integer; var data: packettype): char*);

(* read a packet *)

label 1; (* used to emulate C's CONTINUE statement *)

var i, ichksum: integer;
    chksum, ptype: char;
    r: char;

  begin
    debugwrite('rpack:');
    set_timer(timint);

    if not getsoh then                       (*if don't get synch char then *)
      begin
        rpack := 'N';                        (* treat as a NAK *)
        num := n mod 64;
        exit(rpack)                          (* and get out of here *)
      end;

  1: if timeout then                        (* if we've tried too many times *)
        begin                               (* and aren't waiting for init *)
          rpack := 'N';                      (* treat as NAK *)
          exit(rpack)                        (* and get out of here *)
        end; (* if *)

    if not getch(r) then                (* get a char and *)
            goto 1;                        (* resynch if soh *)

    ichksum := ord(r);                        (* start checksum *)
    len := ord(unchar(r)) - 3;          (* character count *)

    if not getch(r) then                (* get a char and *)
        goto 1;                            (* resynch if soh *)
    ichksum := ichksum + ord(r);
    num := ord(unchar(r));              (* packet number *)

    if not getch(r) then                (* get a char and *)
        goto 1;                            (* resynch if soh *)
    ichksum := ichksum + ord(r);
    ptype := r;                         (* packet type *)

    for i := 0 to len-1 do                 (* get any data *)
      begin
        if not getch(r) then            (* get a char and *)
            goto 1;                        (* resynch if soh *)
        ichksum := ichksum + ord(r);
        data[i] := r;
      end; (* for i *)
    data[len] := chr(0);                   (* mark end of data *)

    if not getch(r) then                (* get a char and *)
        goto 1;                            (* resynch if soh *)

                                           (* compute final checksum *)
    chksum := chr(aand(ichksum + (aand(ichksum,192) div 64), 63));

    if (chksum <> unchar(r)) then       (* if checksum bad *)
        rpack := chr(0)                      (* return 'false' indicator *)
    else                                   (* else *)
        rpack := ptype;                      (* return packet type *)

    if debug then
      begin
        write(debf,' len:',len,' num:',num,' ptype:',ptype);
        packetwrite(data,len); write(debf,' chksum:',r)
      end; (* if *)
  end; (* rpack *)

(*$G-*) (* turn off goto option...don't need it anymore *)

procedure pak_version;
 
  begin
    writeln(my_version)
  end {pak_version};

end. { kermpack }
