$SEARCH 'KRMWNDW', '*IO.', '*INTERFACE.'$
{

This file, KRMRPT.TEXT, contains the error and status reporting
modules used by all other Kermit modules.  The following modules
reside in this file:

        ERR_CODES       Error code definitions
        KRMRPT          Error and status reporting procedures
        
The module KRMRPT includes the file KRMVERS.TEXT, which declares the version
string constant VERSION_STRING.
}


{

Module ERR_CODES defines the integer error code values that can be
returned by a procedure to indicate whether it completed successfully,
or, if not, what error occurred.  Successful and warning error codes
are odd (low order bit set), and indicate that all went reasonably well.
Failing error codes are even (low order bit clear), and indicate that
something happened that kept a routine from doing what it was supposed to.

Function ERRSTR returns the error message string associated with the
given  error code.

All Kermit modules have access to ERR_CODES.

}
module err_codes;
export

const
  error_string_length = 80;       { Maximum length of error strings }

{ Facility code definitions.  These codes identify the facility generating
  the error.
}

cmdfac = 1*128;             { command interpreter }
trmfac = 2*128;             { terminal emulation code }
iofac  = 3*128;             { IO error }
krmfac = 4*128;             { kermit protocol machine }

{  Status code definitions.  Successful return codes are odd, those
   corresponding to error conditions are even.  Returned by functions,
   etc.
}
  
  success       = 1;
  file_rcvd_ok  = 3;    { file received successfully }
  file_sent_ok  = 5;    { file transmitted successfully }
  
  inv_packet_type = 7;  { unexpected packet type received }
  
{  Error condition status codes }
{  Codes returned by lookup_key in module command }
  
  ambig_keyword = 2;
  no_keyword    = 4;

{  Codes returned by parse in module command }

  not_confirmed = 6;
  integer_error = 8;
  no_match      = 10;
  non_digit     = 12;        { Non-digit character encountered }
  integer_overflow = 14;     { Integer overflow }
  null_string   = 16;        { null string given as argument }
  parse_after_eol = 18;      { parse called after eol parsed }
  
{  Codes returned by read_break in module command }
  
  back_past_field = 20;
  abort_line    = 22;
  
{ Codes returned by Kermit protocol procedures in module krmguts }

  retry_exhausted = 24;
  timeout = 26;
  abort_file = 28;
  abort_group = 30;
  abort_errpack = 32;
  rcvd_bad_init = 34;
  cant_read_file = 36;
  cant_write_file = 38;
  cant_create_file = 40;
  cant_find_file = 42;

type
   error_string = string[error_string_length];


function errstr ( errcode : integer ) : error_string;
  
$page$
implement

function errstr ( errcode : integer ) : error_string;
{

Returns the error string associated with each error.
}
  var s : error_string;
  begin
  case errcode of
    success:            s := 'Success';
    inv_packet_type:    s := 'Unexpected packet type received';
    file_rcvd_ok:       s := 'File received successfully';
    file_sent_ok:       s := 'File sent successfully';
    
  { Codes returned by lookup_key }
  
    ambig_keyword:      s := 'Ambiguous keyword';
    no_keyword:         s := 'No keywords match this input';
  
  {  Codes returned by parse  }
  
    not_confirmed:      s := 'Not confirmed';
    integer_error:      s := 'Error reading integer';
    no_match:           s := 'No defined keywords match this input';
    non_digit:          s := 'Non-digit character encountered';
    integer_overflow:   s := 'Integer overflow';
    null_string:        s := 'Null string given as argument';
    parse_after_eol:    s := 'Parse called after end of line parsed';
    
  {  Codes returned by read_break }
    
    back_past_field:    s := 'Input deleted past beginning of field';
    abort_line:         s := 'Line aborted by CTRL-U';

  { Codes returned by Kermit protocol procedures }

    retry_exhausted:    s := 'Retry count exhausted';
    timeout:            s := 'Timeout';
    abort_file:         s := 'File transfer aborted by user';
    abort_group:        s := 'File group transfer aborted by user';
    abort_errpack:      s := 'Transfer aborted by error packet from remote';
    cant_read_file:     s := 'Cannot open file for reading';
    cant_write_file:    s := 'Cannot open file for writing';
    cant_create_file:   s := 'Cannot create file';
    cant_find_file:     s := 'File does not exist';
   end;  { case }
  errstr := s;
  end;  { function errstr }

end;  { module err_codes }


$PAGE$
{

Module KRMRPT handles error and status reporting for the rest of
Kermit.  Basically, except for command echoing, anything that is
displayed on the screen is put there by procedures in this module.
These procedures do the proper text formatting, positioning, etc., and
then call procedures in module WINDOWLIB (in file KRMWNDW.TEXT) to
actually do the screen output.

All Kermit modules have access to KRMRPT.

}
module  krmrpt;
import  windowlib,
        err_codes;

export

$INCLUDE 'KRMVERS.TEXT'$     { This file has const declarations for the
                               version variables VERSION_NUM and VERSION_DATE,
                               which are string constants }

var
  help_window, command_window, error_window, stat_window : window_ptr;
  
type

  { Packet transfer statistics record }

  kermit_statistics = record
         NumSendPacks : integer;   { number of packets sent }
         NumRecvPacks : integer;   { number of packets received }
         NumACKsent : integer;     { number of ACKs we've sent }
         NumNAKsent : integer;     { number of NAKs we've sent }
         NumACKrecv : integer;     { number of ACKs we've received }
         NumNAKrecv : integer;     { number of NAKs we've received }
         NumBADrecv : integer;     { number of non-ACKs we've received when }
                                   { waiting for an ACK }
         RunTime: integer;         { elapsed time for current transaction }
         ChInFile : integer;       { number of characters in file }
         ChInPack : integer;       { number of characters in packets }
         packet_overhead : integer; { percent overhead of packetizing }
         effrate : integer;        { effective baud rate of transfer }
         end;  { record }

  KermitStates = (FileData,RecvInit,SendInit,Break,
                  FileHeader,EOFile,Complete,Abort);
  
  Transfer_type = (Transmit, Receive, Invalid);
  

procedure set_logfile( var fnm : string );
procedure get_logfile( var fnm : string );
procedure report_version;
procedure report_status( var report : string );
procedure report_log( var report : string );
procedure report_error( code : integer; var where_msg : string );
procedure init_cmd_windows;
procedure clear_status_window;
procedure init_packet_display( runtype : transfer_type );
procedure clean_packet_display( runtype : transfer_type );
procedure report_send_packet( seq : integer );
procedure report_receive_file( var fnm : string );
procedure report_send_file( var fnm : string );
procedure report_packet_statistics( stats : kermit_statistics;
                                    runtype : transfer_type );
function check_error( code : integer; var where_msg : string ) : boolean;

implement

const
  send_packet_y = 2;
  packet_stat_x = 25;
  packet_stat_y = 4;
  stat_random_x = 0;
  stat_random_y = 14;
  file_report_x = 0;
  file_report_y = 0;
  
var
  log_filename : string[50];
  log_file : text;
  log_on : boolean;
  send_packet_x : integer;            { window coords of send packet # }
  report : string[80];
  rpos : integer;
  
procedure set_logfile( var fnm : string );
  begin
  if strlen(fnm) = 0
     then begin
       log_on := false;
       log_filename := 'OFF';
       end
     else begin
          log_on := true;
          log_filename := fnm;
          rewrite(log_file,log_filename);
          end;
  end;  { procedure set_logfile }
  
procedure get_logfile( var fnm : string );
  begin
  fnm := log_filename;
  end;  { procedure get_logfile }
  
procedure report_version;
  var vs : string[80];
      p : integer;              { dummy for strwrite }
  begin
  strwrite(vs,1,p,VERSION_STRING);
  writeln_window_string(stat_window,vs);
  end;  { procedure report_version }
  
procedure report_status( var report : string );
  begin
  writeln_window_string( stat_window, report );
  end;  { procedure report_status }

procedure report_log( var report : string );
  begin
  if log_on
    then writeln(log_file, report);
  end;  { procedure report_status }

procedure report_error( code : integer; var where_msg : string );
  var report : string [80];
      rpos : integer;
  begin
  setstrlen(report,0);
  if odd(code)
    then strwrite(report,1,rpos,errstr(code))
    else strwrite(report,1,rpos,'?Error ',where_msg, ' - ', errstr(code));
  clear_window(error_window);
  writeln_window_string( error_window, report );
  end;  { procedure report_error }

$page$
procedure init_cmd_windows;
  begin
  stat_window    := init_window(0,screen_x_max, 0,16);
  help_window    := init_window(0,screen_x_max, 17,20);
  command_window := init_window(0,screen_x_max, 21,21);
  error_window   := init_window(0,screen_x_max, 22,23);
  end;  { procedure init_cmd_windows }
  
procedure clear_status_window;
  begin
  clear_window(stat_window);
  end;  { procedure clear_status_window }
  
procedure init_packet_display( runtype : transfer_type );
  var lab : string[80];
  begin
  clear_window(stat_window);
  lab := 'Sending Packet # ';
  send_packet_x := strlen(lab);
  gotoxy_window(stat_window, 0, send_packet_y);
  writeln_window_string( stat_window, lab);
  clear_eol_window(stat_window);
  
  gotoxy_window( stat_window, 0, packet_stat_y );
  setstrlen(report,0);
  strwrite(report,1,rpos,'Packets sent');
  report_status(report);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Packets received');
  report_status(report);
  setstrlen(report,0);
  if runtype = transmit
     then strwrite(report,1,rpos,'Total chars. sent')
     else strwrite(report,1,rpos,'Total chars. rcvd');
  report_status(report);
  setstrlen(report,0);
  if runtype = transmit
     then strwrite(report,1,rpos,'Data chars. sent')
     else strwrite(report,1,rpos,'Data chars. rcvd');
  report_status(report);
  
  setstrlen(report,0);
  strwrite(report,1,rpos,'Overhead (%)');
  report_status(report);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Effective Rate');
  report_status(report);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Number of ACK');
  report_status(report);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Number of NAK');
  report_status(report);
  IF (RunType = Transmit)
    THEN BEGIN
      setstrlen(report,0);
      strwrite(report,1,rpos,'Number of BAD');
      report_status(report);
      END;
  gotoxy_window(stat_window, stat_random_x, stat_random_y);
  end;  { procedure init_packet_display }
  
procedure clean_packet_display( runtype : transfer_type );
  begin
  gotoxy_window(stat_window, 0, send_packet_y);
  clear_eol_window(stat_window);
  end;  { procedure clean_packet_display }
  
procedure report_send_packet( seq : integer );
  begin
  gotoxy_window(stat_window, send_packet_x, send_packet_y);
  setstrlen(report,0);
  strwrite(report,1,rpos,seq:1);
  write_window_string(stat_window, report);
  clear_eol_window(stat_window);
  gotoxy_window(stat_window, stat_random_x, stat_random_y);
  end;  { report_send_packet }
  
procedure report_send_file( var fnm : string );
  begin
  gotoxy_window(stat_window, file_report_x, file_report_y);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Sending file   ',fnm);
  write_window_string(stat_window, report);
  clear_eol_window(stat_window);
  gotoxy_window(stat_window, stat_random_x, stat_random_y);
  end;  { procedure report_send_file }

procedure report_receive_file( var fnm : string );
  begin
  gotoxy_window(stat_window, file_report_x, file_report_y);
  setstrlen(report,0);
  strwrite(report,1,rpos,'Receiving file ',fnm);
  write_window_string(stat_window, report);
  clear_eol_window(stat_window);
  gotoxy_window(stat_window, stat_random_x, stat_random_y);
  end;  { procedure report_receive_file }

procedure report_packet_statistics( stats : kermit_statistics;
                                    runtype : transfer_type );
  var row : integer;
  procedure report_num( i : integer );
    begin
    setstrlen(report,0);
    strwrite(report,1,rpos,i:5);
    gotoxy_window(stat_window, packet_stat_x, row);
    write_window_string(stat_window,report);
    row := row + 1;
    end;  { procedure report_num }
    
  begin
  row := packet_stat_y;
  report_num(stats.NumSendPacks);
  report_num(stats.NumRecvPacks);
  report_num(stats.ChInPack);
  report_num(stats.ChInFile);
  report_num(stats.packet_overhead);
  report_num(stats.effrate);
  IF (RunType = Transmit)
    THEN BEGIN  { for transmit }
      report_num(stats.NumACKrecv);
      report_num(stats.NumNAKrecv);
      report_num(stats.NumBADrecv);
      END  { for transmit }
    ELSE BEGIN   { for Receive }
      report_num(stats.NumACKsent);
      report_num(stats.NumNAKsent);
      END;  { for Receive }
    gotoxy_window(stat_window, stat_random_x, stat_random_y);
  end;  { procedure report_packet_statistics }


$page$
{ check_error           Checks given condition code.  Returns false if code
  is successful.  If code is error code, prints associated error message
  and returns true.
}
function check_error( code : integer; var where_msg : string ) : boolean;
  var ret : boolean;
  begin
    ret := false;
    if not odd(code)       { successful conditions are odd, failing (error)
                          conditions are even }
       then begin
       report_error( code, where_msg );
       ret := true;
       end;
    check_error := ret;
  end;  { procedure check_error }
     
end.  { module krmrpt }
