(* >>>> KERMPACK.TEXT  *************************************************)

(*$I-*)
(*$R-*)
(*$S+*)
(*$V-*)

UNIT kermpack;  INTRINSIC CODE 21  ;


INTERFACE

USES  kermglob,
      kermutil;


PROCEDURE spar;

PROCEDURE rpar;

PROCEDURE spack( ptype: CHAR; num, len: INTEGER );

PROCEDURE send_errpack( num : INTEGER );

FUNCTION  rpack(spnum: INTEGER; VAR len, rpnum: INTEGER; VAR data: packettype;
                timeout: INTEGER; soh_char: CHAR ) : CHAR;

FUNCTION  bufill_t : INTEGER;

FUNCTION  bufill_i : INTEGER;

PROCEDURE bufemp_t( len : INTEGER );

PROCEDURE bufemp_i( len : INTEGER );


IMPLEMENTATION


FUNCTION bufill_t (* : integer*);

(* fill a packet with data from a textfile...manages a 2 block buffer *)

var i, j, count: integer;
    ch : char;

begin
  i := 4; (* start at packet[4] for data chars *)
  (* while file has some data & packet has some room we'll keep going *)
  while ((bufpos <= bufend) or (not eof(applefile))) and (i < max1_data) do
    begin
      (* if we need more data from disk then *)
      if (bufpos > bufend) and (not eof(applefile)) then
        begin
          (* read a textpage = 2 blocks *)
          bufend := blockread(applefile,filebuf[1],2) * blksize;
          io_status := ioresult;
          if io_status <> 0 then exit( bufill_t );
          (* and adjust buffer pointer *)
          bufpos := 1
        end; (* if *)
      if (bufpos <= bufend) then          (* if we're within buffer bounds *)
        begin
          ch := filebuf[bufpos];          (* get a character *)
          bufpos := bufpos + 1;           (* increase buffer pointer *)
          if (ch = xdle_char) then        (* if it's space compression char, *)
            begin
              count := ord(unchar(filebuf[bufpos])); (* get # of spaces *)
              bufpos := bufpos + 1;       (* read past # *)
              ch := ' ';                  (* and make current char a space *)
            end (* if *)
            else                          (* otherwise, it's just a char *)
              count := 1;                 (* so only 1 copy of it *)
          if (ch in ctlq_set) then        (* if a control char *)
            begin
              if (ch = cr) then           (* if a carriage return *)
                begin
                  packet[i] := quote;     (* put (quoted) CR in packet *)
                  i := i + 1;
                  packet[i] := ctl( cr );
                  i := i + 1;
                  ch := lf;               (* and we'll stick a LF after *)
                end; (* if *)
              packet[i] := quote;         (* put the quote in packet *)
              i := i + 1;
              if ch <> quote then
                      ch := ctl(ch);      (* and un-controllify char *)
            end (* if *)
        end; (* if *)
      j := 1;
      while (j <= count) and (i < max2_data) do
        begin                             (* put all the chars in packet *)
          if ch <> chr(0) then            (* so long as not a NUL *)
            begin
              packet[i] := ch;
              i := i + 1;
            end (* if *)
          else bufpos := bufend +1;       (* if is a NUL so *)
                                          (* skip to end of block *)
                                          (* since rest will be NULs *)
          j := j + 1
        end; (* while *)
    end; (* while *)
  if (i = 4) then                         (* if we're at end of file, *)
      bufill_t := (at_eof)                (* indicate it *)
  else                                    (* else *)
    begin
      if (j <= count) then                (* if didn't all fit in packet *)
        begin
          bufpos := bufpos - 2;           (* put buf pointer at DLE *)
                                          (* and update compress count *)
          filebuf[bufpos + 1] := tochar(chr(count-j+1));
        end; (* if *)
      bufill_t := i                       (* return # of data in packet + 4 *)
    end; (* else *)
end; (* bufill_t *)


FUNCTION bufill_i { : integer };

 fills packet with data form another type of file than a textfile.  
 This will only work if serial wordlength can be set to 8 databits, 
 no parity and if both sides plus the transport medium do not change
 in any way the most significant bit of the byte send.              

var i : integer;
    ch : char;
 
begin
  i := 4; ch := ' ';
  while ((bufpos <= bufend) or ( not eof(applefile))) and ( i < spsiz ) do
    begin
      if (bufpos > bufend) and ( not eof(applefile) ) then
        begin
          bufend := blockread( applefile, filebuf[1], 1) * blksize;
          io_status := ioresult;
          if io_status <> 0 then exit( bufill_i );
          bufpos := 1;
        end;
      if (bufpos <= bufend) then
        begin
          ch := filebuf[bufpos];
          bufpos := bufpos + 1;
          if ch in ctlq_set then begin
                                   packet[i] := quote;
                                   i := i + 1;
                                   if ch <> quote then ch := ctl( ch );
                                  end;
          packet[i] := ch;
          i := i + 1;
        end;
    end; { while }
  if i = 4 then bufill_i := at_eof
           else bufill_i := i;
end; { bufill_i }



PROCEDURE bufemp_t { len : integer };

var ch : char;
    i, j : integer;
 
begin
  i := 0;
  while i < len do
  begin
    if bufpos < ( page_size - 1 )
      then begin
             ch := rec_pkt[i];
             if ch = quote
               then begin
                      i := i + 1;
                      ch := rec_pkt[i];
                      if ch = quote
                        then begin
                               filebuf[bufpos] := ch;
                               bufpos := bufpos + 1;
                             end
                        else begin
                               ch := ctl( ch );
                               if ch in [ cr, ff ] then
                                 begin
                                   if ch = ff then if no_ffeed
                                                   then ch := cr;
                                   filebuf[bufpos]   := ch;
                                   filebuf[bufpos+1] := xdle_char;
                                   filebuf[bufpos+2] := ' ';
                                   crpos := bufpos;
                                   bufpos := bufpos + 3;
                                   dle_flag := true;
                                 end;
                             end;
                    end
               else begin
                      if ( ch = ' ' ) and dle_flag
                        then filebuf[bufpos-1] := succ( filebuf[bufpos-1] )
                        else begin
                               dle_flag := false;
                               filebuf[bufpos] := ch;
                               bufpos := bufpos + 1;
                             end;
                    end;
             i := i + 1;
           end
      else begin
             j := blockwrite( rec_file, filebuf[1], 1 );
             bufpos := bufpos - crpos;
             moveleft( filebuf[crpos], filebuf[1], bufpos );
             fillchar( filebuf[crpos], pagesize + 1 - crpos, chr(0) );
             j := blockwrite( rec_file, filebuf[blk_size + 1], 1 );
             io_status := ioresult;
             if j <> 1 then io_status := 8;
             if io_status <> 0 then exit( bufemp_t );
             bufpos := bufpos + 1;
             crpos := pagesize - 1;
           end;
  end;
end;  { bufemp_t }
 
 


PROCEDURE bufemp_i { len : integer };

var ch : char;
    i, j : integer;
 
begin
  i := 0;
  while i < len do
  begin
    if bufpos <= blk_size
      then begin
             ch := rec_pkt[i];
             if ch = quote
               then begin
                      i := i + 1;
                      ch := rec_pkt[i];
                      if ch <> quote then ch := ctl( ch );
                    end;
             filebuf[bufpos] := ch;
             bufpos := bufpos + 1;
             i := i + 1;
           end
      else begin
             j := blockwrite( rec_file, filebuf[1], 1 );
             bufpos := 1;
             io_status := ioresult;
             if j <> 1 then io_status := 8;
             if io_status <> 0 then exit( bufemp_i );
           end;
  end;
end;  { bufemp_i }
 

 


PROCEDURE spar;

(* fills packet with my send-init parameters *)

  begin
    packet[4] := tochar(chr(maxpack));   (* biggest packet i can receive *)
    packet[5] := tochar(chr(mytime));    (* when i want to be timed out *)
    packet[6] := tochar(chr(mypad));     (* how much padding i need *)
    packet[7] := ctl(mypchar);           (* padding char i want *)
    packet[8] := tochar(eoln_char);      (* end of line character i want *)
    packet[9] := myquote;                (* control-quote char i want *)
    packet[10]:= chr(0);                 (* I won't do 8-bit quoting *)
  end; (* spar *)



PROCEDURE rpar;

(* gets their init params *)

begin
  spsiz     := ord(unchar(rec_pkt[0]));         (* max send packet size     *)
  max1_data := spsiz - 2;                       (* calculate maximal        *)
  max2_data := spsiz + 1;                       (* data limits for bufill_t *)
  xtime     := ord(unchar(rec_pkt[1]));         (* when i should time out   *)
  pad       := ord(unchar(rec_pkt[2]));         (* number of pads to send   *)
  padchar   := ctl(rec_pkt[3]);                 (* padding char to send     *)
  xeol_char := unchar(rec_pkt[4]);              (* eol char i must send     *)
  quote     := rec_pkt[5];                      (* incoming data quote char *)
end; (* rpar *)


PROCEDURE spack(*ptype: char; num: integer; len: integer*);

(* send a packet *)

const mtry = 10000;

var j, i, count: integer;
    ch: char;

begin
  if ibm and (currstate <> 's') then           (* if ibm and not SINIT then *)
    begin
      count := 0; ch := ' ';
      repeat                                   (* wait for an xon *)
        repeat
            count := count + 1;
            unitstatus( inport, j, control_word );
        until ( j > 0 ) or ( count > mtry );
        unitread( inport, ch, 1,, 12 );
      until (ch = xon_char) or (count > mtry);
      if count > mtry then exit( spack ); (* if wait too long then get out *)
    end; (* if *)

  if pad > 0 then
    begin
      for i := 1 to pad do
      unitwrite( outport, padchar, 1,, 12 ); (* write out any padding chars *)
    end;
  packet[0] := soh_char;                     (* packet sync character *)
  packet[1] := tochar(chr(len - 1));         (* character count *)
  packet[2] := tochar(chr(num));             (* packet number *)
  packet[3] := ptype;                        (* packet type *)
   (* data chars have already been filled in by by the bufill procedure *)
                                             (* compute final chksum *)
                                             (* len=data chars + 4 *)
  packet[len]   := tochar( calc_checksum( packet, len ) );
  packet[len+1] := xeol_char;
  if debug then packet_write( packet, len+2 );
  unitwrite( outport, packet[0], len+2,, 12 );
end; (* spack *)


PROCEDURE  send_errpack { num : integer };

var len : integer;

begin
  len := length ( err_string );
  moveleft( err_string[1], packet[4], len );
  spack( 'E', num, len+4 );
end;  { send_errpack }


FUNCTION rpack{ (spnum:integer; var len,rpnum:integer; data:packettype;  }
              {  timeout:integer; soh_char:char) : char } ; EXTERNAL;
 
 this function listens to the serial input port, detects a kermit 
 package, decodes it and returns the data part of the packet.     
 its function value is the type of the received packet. If there  
 was a receive error or the timeout period (1..31 sec) was        
 exhausted without receiving a valid packet the function returns  
 with '@' as value, with rpnum=spnum and with len = 0.            


begin
end. { kermpack }
