|x|*|f6|*|f5|*|f4|*|f3|*|f2|*|f1|{bootstrap the function keys}|.
jff/Change log:/|nsm$log|nqan|{locate & mark the Change log}|.
cfucpecan.p[begin,end]|n|f6ucpecan.p|n|{get specified part}|.
bsmbegin|n2fsbsmend|nqa,|{mark beginning and ending lines of this part}|.
jmend|nf/>>>>/ d|g}|!|*c|f1|f4ramdisk:|f1|n|f5|{save next part to ramdisk:}|.
|f3|f3|f3|f3|f3|f3|f3|f3|f3|f3|f3|f3|f37|n|*|f6|f3|{main extraction sequence}|.
|xsmend|njfk/|d|e|f2|{extraction initialization, replaced by filename}|.
jfd|n|eqa|{remove unwanted filename line}|.
{>>>> KERMIT.TEXT}
program kermit;

(* $R-*) (* turn range checking off *)
(* $L+*)

USES {$u kermglob.code} kermglob,
     {$U kermutil.code} kermutil,
     {$U parser.code}   parser,
     {$U helper.code}   helper,
     {$U sender.code}   sender,
     {$U receiver.code} receiver,
     {$U client.code}   client;

const
  my_version = 'Kermit-UCSD V1.1, 13 May 89';
  
{Change log:
  13 May 89, V1.1: Fixed "lost debug file" bug   RTC
  30 Apr 89, V1.1: Moved set/show & connect procedures to kermutil   RTC
  30 Apr 89, V1.1: Added KERMENUS unit   RTC
  26 Apr 89, V1.1: Fixed "chained TAKE commands" bug     RTC
  19 Apr 89, V1.1: minor cleanups   RTC
  16 Apr 89, V1.1: Added BYE & FINISH commands       RTC
  15 Apr 89, V1.1: Added GET and PUT commands       RTC
  13 Apr 89, V1.1: Began work on new Version   RTC
  17 Aug 88: Misc. cleanup and bug fixes in LOG command      RTC
  14 Aug 88: Added LOG and CLOSE commands         RTC
  31 Jul 88: Modified for variable system_id       RTC
  02 Jul 88: Added Binary transfers & TAKE command     RTC
  29 Jun 88: Fixed Assorted Bugs in "connect" escape functions  RTC
  Modifications by SP, 25 Oct 1983: adapt to IBM Version IV.1
  Delete keyboard and serial buffering: provided by system already.

  Additional mods by SP, 18 Mar 1984: make all strings 255 chars long

  13 May 84: Incorporate screen control through syscom record entries
             for portability
}

var
  taking_commands : boolean;

procedure initialize;

var ch: char;

  begin
    ker_version := my_version;
    writeln(ker_version);
    writeln(
'   This program uses Library Units (c) 1986 Pecan Software Systems, Inc.');
    writeln(
'   This program may be freely distributed for non-commercial purposes.');
    writeln;
    timint := mytime;
    pad := mypad;
    padchar := chr(mypchar);
    xeol := chr(my_eol);
    esc_char := chr(my_esc);
    quote := my_quote;
    ctlset := [chr(0)..chr(31),chr(del),quote];
    half_duplex := false;
    debug := false;
    {$I-}
    rewrite(debf,'CONSOLE:');
    {$I+}
    emulating := false;
    f_is_binary := false;
    lit_names := false;
    fwarn := false;
    spsiz := max_pack;
    rpsiz := max_pack;
    n := 0;
    parity := nopar;
    initvocab;
    fill_parity_array;
    ibm := false;
    xon := chr(17);
    bufpos := 1;
    bufend := 0;
    baud := defaultbaud;
    system_id := 'UNKNOWN';
    if setup_comm then {baud was ok};
    {$I-}
    reset(cmd_file,'*kermitinfo.text');
    taking_commands := io_result = 0;
    if ioresult <> 0 then close(cmd_file)
    {$I+}
  end; (* initialize *)


procedure closeup;

  begin
    close(debf,lock);
    page( output )
  end; (* closeup *)


  begin (* main kermit program *)
    initialize;
    repeat
        write('Kermit-UCSD> ');
        if taking_commands
          then
            begin
              readln(cmd_file,line);
              writeln(line);
              if eof(cmd_file) then
                begin
                  close(cmd_file);
                  taking_commands := false
                end
            end
          else readstr(keyport,line);
        case parse of
            unconfirmed: writeln('Unconfirmed');
            parm_expected: writeln('Parameter expected');
            ambiguous: writeln('Ambiguous');
            unrec: writeln('Unrecognized command');
            fn_expected: writeln('File name expected');
            ch_expected: writeln('Single character expected');
            null: case verb of
                      consym: connect;
                      helpsym: help;
                      logsym:   begin
                                  {$I-}
                                  case adj of
                                    debugsym:
                                      begin
                                        close(debf,lock);
                                        rewrite(debf,xfilename)
                                      end;
                                  end {case adj};
                                  if ioresult <> 0 then
                                    begin
                                      writeln('Unable to open ',xfilename);
                                      case adj of
                                        debugsym:
                                          begin
                                            close(debf);
                                            rewrite(debf,'CONSOLE:')
                                          end;
                                      end {case adj};
                                    end
                                  else {$I+}
                                    case adj of
                                      debugsym: write(debf,
                                          ker_version,' -- Debug log...');
                                    end
                                end;
                      closesym: begin
                                  {$I-}
                                  case adj of
                                    debugsym: close(debf,lock);
                                  end {case adj};
                                  if ioresult <> 0 then
                                    begin
                                      writeln('Unable to close file');
                                    end;
                                  case adj of
                                    debugsym: rewrite(debf,'CONSOLE:');
                                  end {case adj};
                                  {$I+}
                                end;
                      takesym : begin
                                  {$I-}
                                  if taking_commands
                                    then close(cmd_file);
                                  reset(cmd_file,xfilename);
                                  taking_commands := io_result = 0;
                                  if ioresult <> 0 then close(cmd_file)
                                  {$I+}
                                end;
                      getsym, recsym: begin
                          recsw(rec_ok,verb = getsym);
                          gotoxy(0,debugline);
                          write(chr(bell));
                          if rec_ok then
                              writeln('successful receive')
                          else
                              writeln('unsuccessful receive');
                          (*$I-*) (* set i/o checking off *)
                          if f_is_binary
                            then close(b_file)
                            else close(t_file);
                          (*$I+*) (* set i/o checking back on *)
                          gotoxy(0,promptline);
                        end; (* recsym *)
                      putsym, sendsym: begin
                          uppercase(xfilename);
                          sendsw(send_ok);
                          gotoxy(0,debugline);
                          write(chr(bell));
                          if send_ok then
                              writeln('successful send')
                          else
                              writeln('unsuccessful send');
                          (*$I-*) (* set i/o checking off *)
                          if f_is_binary
                            then close(b_file)
                            else close(t_file);
                          (*$I+*) (* set i/o checking back on *)
                          gotoxy(0,promptline);
                        end; (* sendsym *)
                      finsym,byesym: begin
                          case verb of
                            finsym: line := 'F';
                            byesym: line := 'L';
                          end {case};
                          clientsw(send_ok,'G',line);
                          gotoxy(0,debugline);
                          write(chr(bell));
                          if send_ok then
                              writeln('successful transaction')
                          else
                              writeln('unsuccessful transaction');
                          (*$I-*) (* set i/o checking off *)
                          close(t_file);
                          (*$I+*) (* set i/o checking back on *)
                          gotoxy(0,promptline);
                        end; {generic server command}
                      setsym: set_parms;
                      show_sym: show_parms;
                  end; (* case verb *)
        end; (* case parse *)
     until (verb = exitsym) or (verb = quitsym);
     closeup
   end. (* kermit *)
{>>>> SENDER.TEXT}
{$D AFS-}  { indicates to compile to run without Adv. File Sys.}

unit sender;

interface

{Change log:
13 May 89, V1.1: Misc. cleanups to debug messages   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Fixed "garbage in buffer" bug         RTC
13 Apr 89, V1.1: Added Version message          RTC
14 Aug 88: Fixed timeout state bug       RTC
07 Aug 88: Added conditional compilation for AFS/SFS difference    RTC
31 Jul 88: Added Attributes Packets & cancel xfr request from receiver  RTC
10 Jul 88: Converted to use screenops unit     RTC
10 Jul 88: Fixed cleareol problem on filenames      RTC
02 Jul 88: Fixed sinit 8th-bit prefix negotiation bug     RTC
30 Jun 88: Added Binary and multiple file transfers    RTC

}

   procedure sendsw(var send_ok: boolean);
   
   procedure sen_version;


implementation

uses
   screenops,   {RTC, 10 Jul 88}
   {$U kermglob.code} kermglob,
   {$U kermutil.code} kermutil,
   {$U kermpack.code} kermpack,
   {$B AFS+} {$U syslibr:attribute.code} attributes, {$E AFS+}
   {$U syslibr:wild.code} wild,
   {$U syslibr:dir.info.code} dirinfo;

const
  my_version = '   Sender Unit V1.1, 13 May 89';


procedure sendsw{(var send_ok: boolean)};

var
  do_attr, still_sending, discard, next_is_empty : boolean;
  files_to_send : D_listp;
  io_status: integer;
  heap: ^integer;
  {$B AFS-}
  this_file : D_listp;
  {$E AFS-}

procedure openfile;

(* resets file of appropriate type *)
  
  var
    dummy : boolean;

  begin
    if debug then
        debugwrite(concat('Opening ',xfilename));
    (*$I-*) (* turn off compiler i/o checking temporarily *)
    if f_is_binary
      then
        begin
          reset(b_file,xfilename);
          if io_result = 0 then
            {$B AFS+}
            dummy := get_attribute(b_file,FA_lastvalidbyte,last_blksize);
            {$E AFS+} {$B AFS-}
            last_blksize := 512;        {default as we can't determine it}
            {$E AFS-}
          bufend := 0                   {mark the buffer as empty!}
        end
      else reset(t_file,xfilename);
    (*$I+*) (* turn compiler i/o checking back on *)
    io_status := io_result;
    {$B AFS-}
    this_file := files_to_send;
    {$E AFS-}
  end; (* openfile *)

function sinit: char;

(* send init packet & receive other side's *)

var num, len, i: integer;  (* packet number and length *)
    ch: char;

  begin
    if debug then
        debugwrite('sinit');

    if numtry > maxtry then
      begin
        sinit := 'a';
        exit(sinit)
      end;

    num_try := num_try + 1;
    spar(packet);

    clear_buf(inport);

    refresh_screen(numtry,n);

    spack('S',n mod 64,10,packet);

    ch := rpack(len,num,recpkt);

    if (ch = 'N') then
      begin
        sinit := 's';
        exit(sinit)
      end (* if 'N' *)
    else if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then       (* not the right ack *)
          begin
            sinit := currstate;
            exit(sinit)
          end;
        rpar(recpkt,len);
        if (xeol = chr(0)) then   (* if they didn't spec eol *)
            xeol := chr(my_eol);    (* use mine *)
        if (quote = chr(0)) then (* if they didn't spec quote *)
            quote := my_quote;     (* use mine *)
        ctl_set := [chr(0)..chr(31),chr(del),quote];
        if en_qbin then ctl_set := ctl_set + [qbin];
        numtry := 0;
        n := n + 1;              (* increase packet number *)
        sinit := 'f';
        exit(sinit)
      end (* else if 'Y' *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sinit := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then
        sinit := currstate
    else if (ch <> 'N') then
        sinit := 'a'
  end; (* sinit *)

function sattr: char;

(* send attributes packet *)

var num, len: integer;
    ch: char;
    got_attr : boolean;
    {$B AFS+}
    file_date : FA_chron;
    {$E AFS+}
    packet : packettype;

  begin
    if debug then
        debugwrite('sattr');

    if numtry > maxtry then
      begin
        sattr := 'a';
        exit(sattr)
      end;

    num_try := num_try + 1;

    refresh_screen(numtry,n);
    
    packet[0] := '#';                   { creation date attribute }
    {$B AFS+}
    packet[1] := tochar(chr(12));       { length }
    if f_is_binary
      then got_attr := get_attribute(b_file,FA_revision_date,file_date)
      else got_attr := get_attribute(t_file,FA_revision_date,file_date);
    with file_date,date,time do
    {$E AFS+} {$B AFS-}
    packet[1] := tochar(chr(6));        { length }
    with this_file^.D_date do
    {$E AFS-}
      begin
        packet[2] := chr(year div 10 + ord('0'));
        packet[3] := chr(year mod 10 + ord('0'));
        packet[4] := chr(month div 10 + ord('0'));
        packet[5] := chr(month mod 10 + ord('0'));
        packet[6] := chr(day div 10 + ord('0'));
        packet[7] := chr(day mod 10 + ord('0'));
        {$B AFS+}
        packet[8] := ' ';
        packet[9] := chr(hour div 10 + ord('0'));
        packet[10] := chr(hour mod 10 + ord('0'));
        packet[11] := ':';
        packet[12] := chr(min div 10 + ord('0'));
        packet[13] := chr(min mod 10 + ord('0'))
        {$E AFS+}
      end;

    spack('A',n mod 64,{$B AFS+}14{$E AFS+} {$B AFS-}8{$E AFS-},packet);

    ch := rpack(len,num,recpkt);

    if (ch = 'N') then
      begin
        sattr := 'd';
        exit(sattr)
      end (* if 'N' *)
    else if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then       (* not the right ack *)
          begin
            sattr := currstate;
            exit(sattr)
          end;
        numtry := 0;
        n := n + 1;              (* increase packet number *)
        do_attr := false;
        discard := (len > 0) and (recpkt[0] = 'N');
        if discard
          then sattr := 'z'
          else sattr := 'd';
        exit(sattr)
      end (* else if 'Y' *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sattr := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then
        sattr := currstate
    else if (ch <> 'N') then
        sattr := 'a'
  end; (* sattr *)

function sdata: char;

(* send file data *)

var num, len: integer;
    ch: char;
    packarray: array[boolean] of packettype;
    sizearray: array[boolean] of integer;
    current: boolean;
    b: boolean;

function other(b: boolean): boolean;

(* complements a boolean which is used as array index *)

  begin
    if b then
        other := false
    else
        other := true
  end; (* other *)

  begin
    discard := false;
    current := true;
    packarray[current] := packet;
    sizearray[current] := size;
    next_is_empty := true;
    while (currstate = 'd') do
      begin
        if (numtry > maxtry) then             (* if too many tries, give up *)
            currstate := 'a';

        b := other(current);
        numtry := numtry + 1;

                                          (* send a data packet *)
        spack('D',n mod 64,sizearray[current],packarray[current]);

        refresh_screen(numtry,n);
        
        if next_is_empty then             (* set up next packet *)
          begin
            sizearray[b] := bufill(packarray[b]);
            next_is_empty := false
          end;

        ch := rpack(len,num,recpkt);      (* receive a packet *)
        if ch = 'N' then                  (* NAK, so just stay in this state *)
            if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
                sdata := currstate
            else                          (* is just like ACK for this packet *)
              begin
                if num > 0 then
                    num := (num - 1)      (* in which case, decrement num *)
                else
                    num := 63;
                ch := 'Y';                (* and indicate an ACK *)
              end; (* else *)

        if (ch = 'Y') then
           begin
             if ((n mod 64) <> num) then (* if wrong ACK *)
               (* stay in same state *)
             else
               begin
                 numtry := 0;
                 n := n + 1;
                 current := b;
                 next_is_empty := true;
                 discard := sizearray[current] = at_badblk;
                 if read_ch(keyport, ch) then {check for user canceling send}
                   begin
                     if ord(ch) in [can_cur,can_all]
                       then discard := true;
                     if ord(ch) = can_all
                       then files_to_send := nil
                   end;
                 if len = 1 then {check for receiver canceling send}
                   begin
                     if recpkt[0] in ['X','Z']
                       then discard := true;
                     if recpkt[0] = 'Z'
                       then files_to_send := nil
                   end;
                 if (sizearray[current] = at_eof) or discard then
                     currstate := 'z'            (* set state to eof *)
                 else
                     currstate := 'd'            (* else stay in data state *)
               end {else}
           end (* if *)
          else if (ch = 'E') then
            begin
              error(recpkt,len);
              currstate := 'a'
            end (* if 'E' *)
          else if (ch = chr(0)) then      (* receive failure, so stay in d *)
          else if (ch <> 'N') then
            currstate := 'a'                  (* on anything else goto abort state *)
      end; (* while *)
    size := sizearray[current];
    packet := packarray[current];
    sdata := currstate
  end; (* sdata *)

function sfile: char;

(* send file header *)

var num, len, i: integer;
    ch: char;
    fn: packettype;
    oldfn: string255;

procedure legalize(var fn: string255);

(* make sure we send only 1 '.' in filename *)

var count, i, j, l: integer;

  begin
    if not lit_names then
      begin
        count := 0;
        l := length(fn);
        for i := 1 to l do                                  (* count '.'s in fn *)
            if fn[i] = '.' then
                count := count + 1;
        for i := 1 to count-1 do                            (* remove all but 1 *)
          begin
            j := 1;
            while (j < l) and (fn[j] <> '.') do
                j := j + 1;                                 (* by finding it *)
            fn := concat(copy(fn,1,j-1),copy(fn,j+1,l-j));  (* and copying around it *)
            l := l - 1
          end (* for i *)
      end;
    i := pos(':',fn);
    if i <> 0 then
      fn := copy(fn,i+1,length(fn)-i)         {remove Vol. name}
  end; (* legalize *)

  begin
    if debug then
        debugwrite('sfile');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        sfile := 'a';
        exit(sfile)
      end;
    numtry := numtry + 1;

    oldfn := xfilename;
    legalize(xfilename);                (* make filename acceptable to remote *)
    len := length(xfilename);

    moveleft(xfilename[1],fn[0],len);   (* move filename into a packettype *)

    SC_erase_to_EOL(filepos,fileline);
    write(oldfn,' ==> ',xfilename);

    refresh_screen(numtry,n);

    spack('F',n mod 64,len,fn);               (* send file header packet *)

    if next_is_empty then
      begin
        size := bufill(packet);            (* get first data from file *)
        next_is_empty := false
      end;                             (* while waiting for response *)

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            sfile := 'f';
            exit(sfile)                (* is just like ACK for this packet *)
          end
        else
          begin
            if (num > 0) then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then  (* if wrong ACK, stay in F state *)
          begin
            sfile := 'f';
            exit(sfile)
          end;
        numtry := 0;
        n := n + 1;
        do_attr := en_attr;
        sfile := 'd';
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sfile := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then  {stay in f state}
        sfile := 'f'
    else if (ch <> 'N') then (* don't recognize it *)
        sfile := 'a'
  end; (* sfile *)

function seof: char;

(* send end of file *)

var num, len: integer;
    ch: char;

  begin
    if debug then
        debugwrite('seof');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        seof := 'a';
        exit(seof)
      end;
    numtry := numtry + 1;

    refresh_screen(numtry,n);

    packet[0] := 'D';           {set up in case of discard}
    
    spack('Z',(n mod 64),ord(discard),packet);    (* send end of file packet *)

    if debug then
        debugwrite('seof1');

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            seof := 'z';
            exit(seof)                 (* is just like ACK for this packet *)
          end
        else
          begin
            if num > 0 then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if debug then
            debugwrite('seof2');
        if ((n mod 64) <> num) then     (* if wrong ACK, stay in Z state *)
          begin
            seof := 'z';
            exit(seof)
          end;
        numtry := 0;
        n := n + 1;
        if debug then
            debugwrite(concat('Closing ',xfilename));
        if f_is_binary
          then close(b_file)
          else close(t_file);
        while files_to_send <> nil do with files_to_send^ do
          begin
            xfilename := concat(D_volume,':',D_title);
            seof := 'f';
            next_is_empty := true;
            
            openfile;
            files_to_send := D_next_entry;
            if io_status <> 0
              then io_error(io_status)
              else exit(seof)
          end {while};
        seof := 'b'
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        seof := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then         (* receive failed, so stay in z state *)
        seof := 'z'
    else if (ch <> 'N') then           (* other error, just abort *)
        seof := 'a'
  end; (* seof *)

function sbreak: char;

var num, len: integer;
    ch: char;

(* send break (end of transmission) *)

  begin
    if debug then
        debugwrite('sbreak');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        sbreak := 'a';
        exit(sbreak)
      end;
    numtry := numtry + 1;

    refresh_screen(numtry,n);

    spack('B',(n mod 64),0,packet);    (* send Break Transfer packet *)

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            sbreak := 'b';
            exit(sbreak)               (* is just like ACK for this packet *)
          end
        else
          begin
            if num > 0 then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then    (* if wrong ACK, stay in B state *)
          begin
            sbreak := 'b';
            exit(sbreak)
          end;
        numtry := 0;
        n := n + 1;
        sbreak := 'c'                  (* else, switch state to complete *)
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sbreak := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then         (* receive failed, so stay in b state *)
        sbreak := 'b'
    else if (ch <> 'N') then           (* other error, just abort *)
        sbreak := 'a'
  end; (* sbreak *)

(* state table switcher for sending *)

  begin (* sendsw *)
    mark(heap);
    send_ok := false;
    still_sending := 
        D_dirlist(xfilename,[D_code..D_svol],files_to_send,false) = D_okay;
    if files_to_send <> nil then with files_to_send^ do
      begin
        xfilename := concat(D_volume,':',D_title);
        next_is_empty := true;
        
        openfile;
        files_to_send := D_next_entry;
        if io_status <> 0 then
          begin
            io_error(io_status);
            still_sending := false
          end
      end;

    if still_sending then write_screen('Sending');
    currstate := 's';
    n := 0;       (* set packet # *)
    numtry := 0;
    flush_comm;         {flush any garbage in buffer}
    
    while still_sending do
        if currstate in ['d', 'f', 'z', 's', 'b', 'c', 'a'] then
          case currstate of
              'd': if do_attr
                     then currstate := sattr
                     else currstate := sdata;
              'f': currstate := sfile;
              'z': currstate := seof;
              's': currstate := sinit;
              'b': currstate := sbreak;
              'c': begin
                     send_ok := true;
                     still_sending := false
                   end; (* case c *)
              'a': still_sending := false
            end (* case *)
        else (* state not in legal states *)
          begin
            debugwrite('Unknown State');
            still_sending := false
          end (* else *);
    release(heap)
  end; (* sendsw *)

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

end. { sender }
{>>>> RECEIVER.TEXT}
{$D AFS-}       {indicates for compile to run without Adv. File Sys.}

unit receiver;

interface

{Change log:
18 May 89, V1.1: Added debugdate to reread file dates (fixed date bug[??]) RTC
13 May 89, V1.1: Misc. cleanup to debug messages   RTC
30 Apr 89, V1.1: Fixed receiver won't stop on maxtry bug   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Fixed "garbage in buffer" bug       RTC
16 Apr 89, V1.1: Fixed "short text filename" bug.   RTC
15 Apr 89, V1.1: Added GET protocol & debug logging of date set result    RTC
13 Apr 89, V1.1: Added version message          RTC
17 Aug 88: Fixed garbage after partial last block of bin. file    RTC
07 Aug 88: Added conditional compilation for AFS/SFS differences   RTC
31 Jul 88: Added Attribute Packets & user discard requests to sender   RTC
10 Jul 88: Converted to use screenops unit     RTC
10 Jul 88: Fixed cleareol problem on filenames     RTC
02 Jul 88: Added binary file transfer & discard protocol   RTC

}
  
  procedure recsw(var rec_ok: boolean; get_from_server : boolean);
  
  procedure rec_version;


implementation

uses
   screenops,   {RTC, 10 Jul 88}
   {$U kermglob.code} kermglob,
   {$U kermutil.code} kermutil,
   {$U kermpack.code} kermpack,
   {$B AFS+} 
   {$U syslibr:attribute.code} attributes; 
   {$E AFS+} {$B AFS-} 
   {$U syslibr:wild.code} wild,
   {$U syslibr:dir.info.code} dirinfo;
   {$E AFS-}

const
  my_version = '   Receiver Unit V1.1, 18 May 89';

{$B AFS-}
procedure debugdate;
  
  var
    heap : ^integer;
    list : D_listp;
    rslt : D_result;
  
  begin {debugdate}
    mark(heap);
    rslt := D_dirlist(xfilename,[Dvol..Ddir],list,false);
    if rslt <> D_okay then debugwrite('Can''t Access File Date');
    if debug then with list^,D_date do
      begin
        debugwrite('');
        write(debf,'File ',D_volume,':',D_title,' Current Date = ',
              month,'/',day,'/',year)
      end;
    release(heap)
  end {debugdate};
{$E AFS-}

procedure recsw{(var rec_ok: boolean; get_from_server : boolean)};

var
  date_attr : record
                valid : boolean;
                value : {$B AFS+} FA_chron {$E AFS+}
                        {$B AFS-} D_daterec {$E AFS-}
              end;

function bufattr(buffer : packettype; len : integer) : integer;
  
  var
    sp_pos,i,j,buffered : integer;
    tempattr : string;
  
  begin {bufattr}
    packet[0] := 'Y'; buffered := 1;    {agree to accept file}
    i := 0; while i < len do
      begin
        if buffer[i] in ['#'] then      {acceptable attribute}
          begin
            tempattr := '';
            for j := 1 to ord(unchar(buffer[succ(i)])) do
              begin
                tempattr := concat(tempattr,' ');
                tempattr[length(tempattr)] := buffer[succ(i) + j]
              end;
            case buffer[i] of
              '#' : with date_attr,value {$B AFS+},date,time{$E AFS+} do
                begin
                  sp_pos := pos(' ',tempattr);
                  if sp_pos = 0 then sp_pos := succ(length(tempattr));
                  year := (ord(tempattr[sp_pos-6]) - ord('0')) * 10
                        + (ord(tempattr[sp_pos-5]) - ord('0'));
                  month := (ord(tempattr[sp_pos-4]) - ord('0')) * 10
                         + (ord(tempattr[sp_pos-3]) - ord('0'));
                  day := (ord(tempattr[sp_pos-2]) - ord('0')) * 10
                       + (ord(tempattr[sp_pos-1]) - ord('0'));
                  {$B AFS+}
                  if length(tempattr) > sp_pos then
                    begin
                      hour := (ord(tempattr[sp_pos+1]) - ord('0')) * 10
                            + (ord(tempattr[sp_pos+2]) - ord('0'));
                      min := (ord(tempattr[sp_pos+4]) - ord('0')) * 10
                            + (ord(tempattr[sp_pos+5]) - ord('0'))
                    end
                  else          {no time provided}
                    begin
                      hour := 24 {non-valid time}; min := 0
                    end;
                  {$E AFS+}
                  valid := true
                end
            end {case}
          end
        else                            {reject attribute}
          begin
            packet[buffered] := buffer[i];
            buffered := succ(buffered)
          end;
        i := succ(succ(i) + ord(unchar(buffer[succ(i)])))
      end;
    bufattr := buffered
  end {bufattr};

function rdata: char;

(* receive file data *)

var dummy, num, len: integer;
    ch: char;
    {$B AFS+}
    did_attr : boolean;
    {$E AFS+} 
    i: integer;

  begin

    repeat
        debugwrite('rdata');
        
        if numtry > maxtry then
          begin
            currstate := 'a';
            exit(rdata)
          end;
        num_try := num_try + 1;

        ch := rpack(len,num,recpkt);   (* receive a packet *)

        refresh_screen(numtry,n);

        if (ch = 'D') then             (* got data packet *)
          begin
            if (num <> (n mod 64)) then (* wrong packet *)
              begin
                if (oldtry > maxtry) then
                  begin
                    rdata := 'a';      (* too many tries, abort *)
                    exit(rdata)
                  end; (* if *)

                if (num = (pred(n) mod 64)) then (* previous packet again *)
                  begin                (* so re-ACK it *)
                    spack('Y',num,0,packet);
                    numtry := 0;       (* reset try counter *)
                                       (* stay in same state *)
                  end (* if *)
                else                   (* wrong number *)
                    currstate := 'a'       (* so abort *)
              end (* if *)
            else                       (* right packet *)
              begin
                bufemp(recpkt,len);  (* write data to file *)
                if read_ch(keyport, ch) then {check if user wants to can}
                  packet[0] := ctl(ch);
                spack('Y',(n mod 64),ord(ord(ch) in [can_cur,can_all]),
                      packet); (* ACK packet *)
                oldtry := numtry;      (* reset try counters *)
                numtry := 0;
                n := n + 1             (* bump packet number *)
                                       (* stay in data receive state *)
              end (* else *)
          end (* if 'D' *)
        else if ch = 'A' then           { Attributes }
          begin
            if (num <> (n mod 64)) then (* wrong packet *)
              begin
                if (oldtry > maxtry) then
                  begin
                    rdata := 'a';      (* too many tries, abort *)
                    exit(rdata)
                  end; (* if *)

                if (num = (pred(n) mod 64)) then (* previous packet again *)
                  begin                (* so re-ACK it *)
                    spack('Y',num,0,packet);
                    numtry := 0;       (* reset try counter *)
                                       (* stay in same state *)
                  end (* if *)
                else                   (* wrong number *)
                    currstate := 'a'       (* so abort *)
              end (* if *)
            else                       (* right packet *)
              begin
                spack('Y',(n mod 64),bufattr(recpkt,len),packet); (* ACK packet *)
                oldtry := numtry;      (* reset try counters *)
                numtry := 0;
                n := n + 1             (* bump packet number *)
                                       (* stay in data receive state *)
              end (* else *)
          end {if 'A'}
        else if (ch = 'F') then        (* file header *)
          begin
            if (oldtry > maxtry) then
              begin
                rdata := 'a';          (* too many tries, abort *)
                exit(rdata)
              end; (* if *)

            if (num = (pred(n) mod 64)) then (* previous packet again *)
              begin                    (* so re-ACK it *)
                spack('Y',num,0,packet);
                numtry := 0;           (* reset try counter *)
                                               (* stay in same state *)
              end (* if *)
            else
                currstate := 'a'           (* not previous packet, abort *)
          end (* if 'F' *)
        else if (ch = 'Z') then        (* end of file *)
          begin
            if (num <> (n mod 64)) then(* wrong packet, abort *)
              begin
                rdata := 'a';
                exit(rdata)
              end; (* if *)
            spack('Y',n mod 64,0,packet); (* ok, ACK it *)
            if (len = 1) and (recpkt[0] = 'D')
              then
                begin
                  debugwrite(concat('Discarding ',xfilename));
                  if f_is_binary               {discard the file}
                    then close(b_file)
                    else close(t_file)
                end
              else
                begin
                  debugwrite(concat('Closing ',xfilename));
                  if f_is_binary               (* close up the file *)
                    then
                      begin
                        if bufpos > 1               {data in last block}
                          then
                            begin
                              for dummy := bufpos to blksize do
                                filebuf[dummy] := chr(0);
                              dummy := blockwrite(b_file,filebuf,1);
                              dummy := pred(bufpos);
                              {$B AFS+}
                              did_attr := 
                                  put_attribute(b_file,FA_lastvalidbyte,dummy)
                              {$E AFS+}
                            end;
                        {$B AFS+}
                        with date_attr do if valid then {set date}
                          did_attr :=
                              put_attribute(b_file,FA_revisiondate,value);
                        {$E AFS+}
                        close(b_file,lock)
                      end
                    else
                      begin
                        {$B AFS+}
                        with date_attr do if valid then {set date}
                          did_attr := 
                              put_attribute(t_file,FA_creationdate,value);
                        {$E AFS+}
                        close(t_file,lock)
                      end;
                  {$B AFS-}
                  debugdate;
                  with date_attr do if valid then {set date}
                    case D_changedate(xfilename,value,
                         [D_code,D_text,D_data,D_svol]) of
                      D_okay :      debugwrite('Date set OK');
                      D_notfound :  debugwrite('No such File, Date not set');
                      D_nameerror : debugwrite('Name error, Date not set');
                      D_offline :   debugwrite('Volume offline, Date not set');
                      D_other :     debugwrite('Unknown error, Date not set');
                    end {case};
                  debugdate;
                  {$E AFS-}
                end;
            bufpos := 1;                {clean up binary file buffer}
            n :=  n + 1;               (* bump packet counter *)
            currstate := 'f';              (* go to complete state *)
          end (* else if 'Z' *)
        else if (ch = 'E') then        (* error packet *)
          begin
            error(recpkt,len);         (* display error *)
            currstate := 'a'               (* and abort *)
          end (* if 'E' *)
        else if (ch <> chr(0)) then    (* some other packet type, *)
            currstate := 'a'               (* abort *)
    until (currstate <> 'd');
    rdata := currstate
  end; (* rdata *)

function rfile: char;

(* receive file header *)

var num, len: integer;
    ch: char;
    oldfn: string255;
    i: integer;

procedure makename(recpkt: packettype; var fn: string255; l: integer);

function exist(fn: string255): boolean;

(* returns true if file named fn exists *)

var f: file;
  
  begin
    (*$I-*) (* turn off i/o checking *)
    reset(f,fn);
    exist := (ioresult = 0);
    (*$I+*)
  end; (* exist *)

procedure checkname(var fn: string255);

(* if file fn exists, makes a new name which doesn't *)
(* does this by changing letters in file name until it *)
(* finds some combination which doesn't exitst *)

var ch: char;
    i: integer;

  begin
    i := 1;
    while (i <= length(fn)) and exist(fn) do
      begin
        ch := succ(fn[i]);    {RTC, 13 May 89}
        if not (ch in ['A'..'Z']) then ch := 'A';
        while (ch in ['A'..'Z']) and exist(fn) do
          begin
            fn[i] := ch;
            ch := succ(ch);
          end; (* while *)
        i := i + 1
      end; (* while *)
    end; (* checkname *)

  begin (* makename *)
    fn := copy('               ',1,15);    (* stretch length *)
    moveleft(recpkt[0],fn[1],l);           (* get filename from packet *)
    oldfn := copy(fn, 1,l);                (* save fn sent to show user *)
    fn := copy(fn,1,min(15,l));            (* set length of filename *)
                                           (* and make sure <= 15 *)
    uppercase(fn);
    if not f_is_binary then 
        if (pos('.TEXT',fn) <> length(fn)-4) or (length(fn) < 5) then
      begin
        if length(fn) > 10 then
            fn := copy(fn,1,10);           (* can only be 15 long in all *)
        fn := concat(fn,'.TEXT');          (* and we'll add .TEXT *)
      end; (* if *)
    if fwarn then                          (* if file warning is on *)
        checkname(fn);                     (* must check that name unique *)
  end; (* makename *)

  begin (* rfile *)
    debugwrite('rfile');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        rfile := 'a';
        exit(rfile)
      end;
    numtry := numtry + 1;

    ch := rpack(len,num,recpkt);      (* receive a packet *)

    refresh_screen(numtry,n);

    if ch = 'S' then                  (* send init, maybe our ACK lost *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end; (* if *)

        if num = (pred(n) mod 64) then      (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spar(packet);             (* with our send init params *)
            spack('Y',num,10,packet);
            numtry := 0;              (* reset try counter *)
            rfile := currstate;           (* stay in same state *)
          end (* if *)
        else                          (* not previous packet, abort *)
          rfile := 'a'
      end (* if 'S' *)
    else if (ch = 'Z') then           (* end of file *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end; (* if *)

        if num = (pred(n) mod 64) then       (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spack('Y',num,0,packet);
            numtry := 0;
            rfile := currstate            (* stay in same state *)
          end (* if *)
        else
            rfile := 'a'              (* no, abort *)
      end (* else if *)
    else if (ch = 'F') then           (* file header *)
      begin                           (* which is what we really want *)
        if (num <> (n mod 64)) then   (* if wrong packet, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end;

        makename(recpkt,xfilename,len); (* get filename, make unique if filew *)
        SC_erase_to_EOL(filepos,fileline);
        write(oldfn,' ==> ',xfilename);

        if not getfil(xfilename) then  (* try to open new file *)
          begin
            ioerror(ioresult);        (* if unsuccessful, tell them *)
            rfile := 'a';             (* and abort *)
            exit(rfile)
          end; (* if *)

        spack('Y',n mod 64,0,packet); (* ACK file header *)
        
        {initializations for file attribute data}
        date_attr.valid := false;
        {end of initializations for file attribute data}
        
        oldtry := numtry;             (* reset try counters *)
        numtry := 0;
        n := n + 1;                   (* bump packet number *)
        rfile := 'd';                 (* switch to data state *)
      end (* else if *)
    else if ch = 'B' then             (* break transmission *)
      begin
        if (num <> (n mod 64)) then            (* wrong packet, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end;
        spack('Y',n mod 64,0,packet); (* say ok *)
        rfile := 'c'                  (* go to complete state *)
      end (* else if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        rfile := 'a'
      end
    else if (ch = chr(0)) then        (* returned false *)
        rfile := currstate                (* so stay in same state *)
    else                              (* some weird state, so abort *)
        rfile := 'a'
  end; (* rfile *)

function rinit: char;

(* receive initialization *)

var num, len: integer;  (* packet number and length *)
    ch: char;
    fn : packettype;

  begin
    debugwrite('rinit');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        rinit := 'a';
        exit(rinit)
      end;
    numtry := numtry + 1;
    
    if get_from_server then {ask server for files}
      begin
        len := length(xfilename);
        moveleft(xfilename[1],fn[0],len);
        spack('R', n mod 64, len, fn)
      end;

    ch := rpack(len,num,recpkt); (* receive a packet *)
    refresh_screen(num_try,n);

    if (ch = 'S') then           (* send init packet *)
      begin
        rpar(recpkt,len);            (* get other side's init data *)
        spar(packet);            (* fill packet with my init data *)
        ctl_set := [chr(0)..chr(31),chr(del),quote];
        if en_qbin then ctl_set := ctl_set + [qbin];
        spack('Y',n mod 64,10,packet); (* ACK with my params *)
        get_from_server := false;
        oldtry := numtry;        (* save old try count *)
        numtry := 0;             (* start a new counter *)
        n := n + 1;              (* bump packet number *)
        rinit := 'f';            (* enter file receive state *)
      end (* if 'S' *)
    else if ch = 'Y' then
      begin
        rinit := 'r';
        if n mod 64 = num then {we have the right ACK}
          begin
            get_from_server := false;
            numtry := 0;
            n := n + 1
          end
      end {if 'Y'}
    else if (ch = 'E') then
      begin
        rinit := 'a';
        error(recpkt,len)
      end (* if 'E' *)
    else if (ch = chr(0)) or (ch = 'N')  then
        rinit := 'r'             (* stay in same state *)
    else
        rinit := 'a'             (* abort *)
  end; (* rinit *)

(* state table switcher for receiving packets *)

  begin (* recswok *)
    rec_ok := false;
    writescreen('Receiving');
    currstate := 'r';            (* initial state is receive *)
    n := 0;                  (* set packet # *)
    numtry := 0;             (* no tries yet *)
    flush_comm;         {flush any garbage in buffer}

    while true do
        if currstate in ['d', 'f', 'r', 'c', 'a'] then
          case currstate of
              'd': currstate := rdata;
              'f': currstate := rfile;
              'r': currstate := rinit;
              'c': begin
                     rec_ok := true;
                     exit(recsw)
                   end; (* case c *)
              'a': exit(recsw)
            end (* case *)
        else (* state not in legal states *)
          begin
            debugwrite('Unknown State');
            exit(recsw)
          end (* else *)
  end; (* recsw *)

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

end. { receiver }
{>>>> CLIENT.TEXT}

unit client;

interface

{Change log:
13 May 89, V1.1: Misc. cleanups to debug messages   RTC
30 Apr 89, V1.1: Fixed failure to terminate on maxtry bug   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Fixed "garbage in buffer" bug        RTC
16 Apr 89, V1.1: Adapted CLIENT Unit from RECEIVE Unit         RTC
}
  
  procedure clientsw(var cli_ok: boolean; ptype: char; data: string);
  
  procedure cli_version;


implementation

uses
   screenops,   {RTC, 10 Jul 88}
   {$U kermglob.code} kermglob,
   {$U kermutil.code} kermutil,
   {$U kermpack.code} kermpack;

const
  my_version = '   Client Unit V1.1, 13 May 89';

var
  f_save : boolean;             { save area for f_is_binary }

procedure clientsw{(var cli_ok: boolean; ptype: char; data: string)};

function cdata: char;

(* client text data *)

var dummy, num, len: integer;
    ch: char;
    i: integer;

  begin

    repeat
        debugwrite('cdata');
        
        if numtry > maxtry then
          begin
            currstate := 'a';
            exit(cdata)
          end;
        num_try := num_try + 1;

        ch := rpack(len,num,recpkt);   (* receive a packet *)

        refresh_screen(numtry,n);

        if (ch = 'D') then             (* got data packet *)
          begin
            if (num <> (n mod 64)) then (* wrong packet *)
              begin
                if (oldtry > maxtry) then
                  begin
                    cdata := 'a';      (* too many tries, abort *)
                    exit(cdata)
                  end; (* if *)

                if (num = (pred(n) mod 64)) then (* previous packet again *)
                  begin                (* so re-ACK it *)
                    spack('Y',num,0,packet);
                    numtry := 0;       (* reset try counter *)
                                       (* stay in same state *)
                  end (* if *)
                else                   (* wrong number *)
                    currstate := 'a'       (* so abort *)
              end (* if *)
            else                       (* right packet *)
              begin
                bufemp(recpkt,len);  (* write data to file *)
                if read_ch(keyport, ch) then {check if user wants to can}
                  packet[0] := ctl(ch);
                spack('Y',(n mod 64),ord(ord(ch) in [can_cur,can_all]),
                      packet); (* ACK packet *)
                oldtry := numtry;      (* reset try counters *)
                numtry := 0;
                n := n + 1             (* bump packet number *)
                                       (* stay in data receive state *)
              end (* else *)
          end (* if 'D' *)
        else if (ch = 'X') then        (* text header *)
          begin
            if (oldtry > maxtry) then
              begin
                cdata := 'a';          (* too many tries, abort *)
                exit(cdata)
              end; (* if *)

            if (num = (pred(n) mod 64)) then (* previous packet again *)
              begin                    (* so re-ACK it *)
                spack('Y',num,0,packet);
                numtry := 0;           (* reset try counter *)
                                               (* stay in same state *)
              end (* if *)
            else
                currstate := 'a'           (* not previous packet, abort *)
          end (* if 'X' *)
        else if (ch = 'Z') then        (* end of file *)
          begin
            if (num <> (n mod 64)) then(* wrong packet, abort *)
              begin
                cdata := 'a';
                exit(cdata)
              end; (* if *)
            spack('Y',n mod 64,0,packet); (* ok, ACK it *)
            close(t_file);
            n :=  n + 1;               (* bump packet counter *)
            currstate := 'f';              (* go to complete state *)
          end (* else if 'Z' *)
        else if (ch = 'E') then        (* error packet *)
          begin
            error(recpkt,len);         (* display error *)
            currstate := 'a'               (* and abort *)
          end (* if 'E' *)
        else if (ch <> chr(0)) then    (* some other packet type, *)
            currstate := 'a'               (* abort *)
    until (currstate <> 'd');
    cdata := currstate
  end; (* cdata *)

function cfile: char;

(* client text header *)

var num, len: integer;
    ch: char;
    i: integer;

  begin (* cfile *)
    debugwrite('cfile');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        cfile := 'a';
        exit(cfile)
      end;
    numtry := numtry + 1;

    ch := rpack(len,num,recpkt);      (* receive a packet *)

    refresh_screen(numtry,n);

    if ch = 'S' then                  (* send init, maybe our ACK lost *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            cfile := 'a';
            exit(cfile)
          end; (* if *)

        if num = (pred(n) mod 64) then      (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spar(packet);             (* with our send init params *)
            spack('Y',num,10,packet);
            numtry := 0;              (* reset try counter *)
            cfile := currstate;           (* stay in same state *)
          end (* if *)
        else                          (* not previous packet, abort *)
          cfile := 'a'
      end (* if 'S' *)
    else if (ch = 'Z') then           (* end of file *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            cfile := 'a';
            exit(cfile)
          end; (* if *)

        if num = (pred(n) mod 64) then       (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spack('Y',num,0,packet);
            numtry := 0;
            cfile := currstate            (* stay in same state *)
          end (* if *)
        else
            cfile := 'a'              (* no, abort *)
      end (* else if *)
    else if (ch = 'X') then           (* text header *)
      begin                           (* which is what we really want *)
        if (num <> (n mod 64)) then   (* if wrong packet, abort *)
          begin
            cfile := 'a';
            exit(cfile)
          end;
        
        if not getfil('console:') then  { try to open console output }
          begin
            ioerror(ioresult);          { if unsuccessful, tell them }
            cfile := 'a';               { and abort }
            exit(cfile)
          end;

        spack('Y',n mod 64,0,packet); (* ACK file header *)
        
        oldtry := numtry;             (* reset try counters *)
        numtry := 0;
        n := n + 1;                   (* bump packet number *)
        cfile := 'd';                 (* switch to data state *)
      end (* else if *)
    else if ch = 'B' then             (* break transmission *)
      begin
        if (num <> (n mod 64)) then            (* wrong packet, abort *)
          begin
            cfile := 'a';
            exit(cfile)
          end;
        spack('Y',n mod 64,0,packet); (* say ok *)
        cfile := 'c'                  (* go to complete state *)
      end (* else if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        cfile := 'a'
      end
    else if (ch = chr(0)) then        (* returned false *)
        cfile := currstate                (* so stay in same state *)
    else                              (* some weird state, so abort *)
        cfile := 'a'
  end; (* cfile *)

function cinit: char;

(* client initialization *)

var num, len: integer;  (* packet number and length *)
    ch: char;
    cmdpkt : packettype;

  begin
    debugwrite('cinit');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        cinit := 'a';
        exit(cinit)
      end;
    numtry := numtry + 1;
    len := length(data);
    moveleft(data[1],cmdpkt[0],len);
    spack(ptype, n mod 64, len, cmdpkt);

    ch := rpack(len,num,recpkt); (* receive a packet *)
    refresh_screen(num_try,n);

    if (ch = 'S') then           (* send init packet *)
      begin
        rpar(recpkt,len);            (* get other side's init data *)
        spar(packet);            (* fill packet with my init data *)
        ctl_set := [chr(0)..chr(31),chr(del),quote];
        if en_qbin then ctl_set := ctl_set + [qbin];
        spack('Y',n mod 64,10,packet); (* ACK with my params *)
        oldtry := numtry;        (* save old try count *)
        numtry := 0;             (* start a new counter *)
        n := n + 1;              (* bump packet number *)
        cinit := 'f';            (* enter file receive state *)
      end (* if 'S' *)
    else if ch = 'Y' then
      begin
        cinit := 'c';
        if n mod 64 = num then {we have the right ACK}
          begin
            numtry := 0;
            n := n + 1
          end
      end {if 'Y'}
    else if (ch = 'N') then
      cinit := 'r'
    else if (ch = 'E') then
      begin
        cinit := 'a';
        error(recpkt,len)
      end (* if 'E' *)
    else if (ch = chr(0)) then
        cinit := 'r'             (* stay in same state *)
    else
        cinit := 'a'             (* abort *)
  end; (* cinit *)

(* state table switcher for receiving packets *)

  begin (* clientsw *)
    cli_ok := false;
    writescreen('Talking to Server');
    f_save := f_is_binary; {save for later restore}
    f_is_binary := false;  {client ONLY recieves text}
    currstate := 'r';            (* initial state is receive *)
    n := 0;                  (* set packet # *)
    numtry := 0;             (* no tries yet *)
    flush_comm;         {flush any garbage in buffer}

    while true do
        if currstate in ['d', 'f', 'r', 'c', 'a'] then
          case currstate of
              'd': currstate := cdata;
              'f': currstate := cfile;
              'r': currstate := cinit;
              'c': begin
                     f_is_binary := f_save;
                     cli_ok := true;
                     exit(clientsw)
                   end; (* case c *)
              'a': begin
                     f_is_binary := f_save;
                     exit(clientsw)
                   end (* case a *)
            end (* case *)
        else (* state not in legal states *)
          begin
            debugwrite('Unknown State');
            f_is_binary := f_save;
            exit(clientsw)
          end (* else *)
  end; (* clientsw *)

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

end. { client }
{>>>> HELPER.TEXT}
unit helper;

interface

{Change log:
13 May 89, V1.1: Added SET INTERFACE, COMMENT, and "client" helps   RTC
26 Apr 89, V1.1: minor cleanups   RTC
13 Apr 89, V1.1: Added Version message         RTC
14 Aug 88: Added command helps for SET SYSTEM command      RTC
14 Aug 88: Added LOG and CLOSE help commands         RTC
31 Jul 88: Minor cleanups of help messages      RTC
30 Jun 88: Added -NAMES, -TYPE, and TAKE command helps   RTC

}

   procedure help;
   
   procedure hlp_version;
   

implementation

uses {$U kermglob.code} kermglob;

const
  my_version = '   Helper Unit V1.1, 13 May 89';

procedure keypress;

var ch: char;

  begin
    write('---------------Press any key to continue---------------');
    read( keyboard, ch );
    page(output); {SP}
  end; (* keypress *)

procedure help1;

  var ch: char;

  begin { help1 }
    if (noun = nullsym) then begin
      writeln('KERMIT is a family of  programs that do reliable file transfer');
      writeln('between computers over TTY lines.',
              '  KERMIT can also be used to make the ');
      writeln('microcomputer behave as a terminal',
              ' for a mainframe.  These are the ');
      writeln('commands for the UCSD p-System version, KERMIT-UCSD:');
      writeln
    end; (* if *)

    if (noun = nullsym) or (noun = consym) then begin
      writeln('  CONNECT     To make a "virtual terminal" connection to a remote');
      writeln('':14, 'system.');
      writeln;
      writeln('':14, 'To break the connection and "escape" back to the micro,');
      writeln('':14, 'type the escape sequence (CTRL-] C, that is Control ');
      writeln('':14, 'rightbracket followed immediately by the letter C.)');
      writeln;
    end; (* if *)

    if (noun = nullsym) or (noun = exitsym) then begin
      writeln('  EXIT        To return back to main command level of the p-system.');
    end; (* if *)

    if (noun = nullsym) or (noun = quitsym) then begin
      writeln('  QUIT        Same as EXIT.');
      writeln;
    end; (* if *)

    if (noun = nullsym) or (noun = helpsym) then begin
      writeln('  HELP        To get a list of KERMIT commands.');
      writeln;
    end; (* if *)

    if (noun = nullsym) or (noun = recsym) then begin
      writeln('  RECEIVE     To accept a file from the remote system.');
    end; (* if *)
    
    if (noun = nullsym) or (noun = sendsym) then begin
      writeln('  SEND        To send a file or group of files to the remote system.');
    end; (* if *)

    if (noun = nullsym) or (noun = getsym) then begin
      writeln('  GET         To request a file from a remote Kermit in SERVER mode.');
    end; (* if *)

    if (noun = nullsym) or (noun = putsym) then begin
      writeln('  PUT         To send a file to a remote Kermit in SERVER mode.');
      writeln;
    end; (* if *)

    if (noun = nullsym) or (noun = byesym) then begin
      writeln('  BYE         Shutdown and logout a remote Kermit in SERVER mode.');
    end; (* if *)

    if (noun = nullsym) or (noun = finsym) then begin
      writeln('  FINISH      Shutdown a remote Kermit in SERVER mode.');
    end; (* if *)

    if (noun = nullsym) then
        keypress;
  end; (* help1 *)

procedure help2;

var ch: char;

  begin { help2 }
    if (noun = nullsym) or (noun = setsym) then begin
      writeln('  SET         To establish system-dependent parameters.  The ');
      writeln('':14, 'SET options are as follows: ');
      writeln;
      if (adj = nullsym) or (adj = debugsym) then begin
        writeln('':14, 'DEBUG            To set debug mode ON or OFF ');
        writeln('':31, '(default is OFF).');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = escsym) then begin
        writeln('':14, 'ESCAPE           To change the escape sequence that ');
        writeln('':31, 'lets you return to the PC Kermit from');
        writeln('':31, 'the remote host.  The default is CTRL-] c.');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = filenamsym) then begin
        writeln('':14, 'FILE-NAMES       LITERAL/CONVERTED, Default is CONVERTED, ');
        writeln('':31, 'In this Kermit LITERAL Names have');
        writeln('':31, 'Volume name Stripped, while CONVERTED');
        writeln('':31, 'Names also have all but the final');
        writeln('':31, '''.'' removed.');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = filetypesym) then begin
        writeln('':14, 'FILE-TYPE        BINARY/TEXT Default is TEXT.');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = filewarnsym) then begin
        writeln('':14, 'FILE-WARNING     ON/OFF, default is OFF.  If ON, ');
        writeln('':31, 'Kermit will warn you and rename an incoming ');
        writeln('':31, 'file so as not to write over a file that ');
        writeln('':31, 'currently exists with the same name');
        writeln;
      end; (* if *)
      if (adj = nullsym) then
        keypress;
    end; (* if *)
  end; (* help2 *)

procedure help3;

  begin
    if (noun = nullsym) or (noun = setsym) then begin
      if (adj = nullsym) or (adj = baudsym) then begin
        writeln('':14, 'BAUD             To set the serial baud rate.' );
        writeln('':31, 'Choices are dependant on your Hardware.' );
        writeln('':31, 'The default is 1200.');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = ibmsym) then begin
        writeln('':14, 'IBM              ON/OFF, default is OFF.  This flag ');
        writeln('':31, 'should be ON only when transfering files');
        writeln('':31, 'between the micro and an IBM VM/CMS');
        writeln('':31, 'system.  It also causes the parity to');
        writeln('':31, 'be set appropriately (mark) and activates');
        writeln('':31, 'local echoing');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = intsym) then begin
        writeln('':14, 'INTERFACE        KERMIT/UCSD, default is KERMIT.');
        writeln('':31, 'Permits selection of prefered User Interface:');
        writeln('':31, 'KERMIT command line or UCSD menus.');
        writeln;
      end; (* if *)

      if (adj = nullsym) or (adj = localsym) then begin
        writeln('':14, 'LOCAL-ECHO       ON/OFF, default is OFF.  This sets the');
        writeln('':31, 'duplex.  It should be ON when using ');
        writeln('':31, 'the IBM and OFF for the DEC-20.');
        writeln;
      end; (* if *)

      if (adj = nullsym) or (adj = emulatesym) then begin
        writeln('':14, 'EMULATE          ON/OFF, default is OFF.  This sets the');
        writeln('':31, 'DataMedia 1520A terminal emulation on or off.');
        writeln;
      end; (* if *)
      if (adj = nullsym) then
        keypress;
    end; (* if *)
  end; (* help3 *)

procedure help4;

  begin
    if (noun = setsym) or (noun = nullsym) then begin
      if (adj = nullsym) or (adj = systemsym) then begin
        writeln('':14, 'SYSTEM-ID        Specify the System-ID for your REMUNIT');
        writeln('':31, 'if your REMUNIT needs it specified.');
        writeln('':31, 'Called "model" in the REMUNIT specs.');
        writeln('':31, 'Default System-ID is UNKNOWN');
        writeln;
      end; (* if *)

      if (adj = nullsym) or (adj = paritysym) then begin
        writeln('':14, 'PARITY           EVEN, ODD, MARK, SPACE, or NONE.');
        writeln('':31, 'NONE is the default but if the IBM ');
        writeln('':31, 'flag is set, parity is set to MARK.  ');
        writeln('':31, 'This flag selects the parity for ');
        writeln('':31, 'outgoing and incoming characters during');
        writeln('':31, 'CONNECT and file transfer to match the');
        writeln('':31, 'requirements of the host.');
        writeln;
      end; (* if *)
    end; (* if *)
    if (noun = nullsym) or (noun = showsym) then begin
      writeln('  SHOW        To see the values of parameters that can be modified');
      write('':14, 'via the SET command. ');
      if (adj in [paritysym, localsym, ibmsym, escsym, debugsym,
                  filenamsym, filetypesym, filewarnsym, baudsym, 
                  emulatesym, systemsym, nullsym]) then begin
        writeln('For an explanation of the parameter,');
        writeln('':14, 'see the help for the matching SET command.'); write('':14)
      end; (* if *)
      if (adj in [allsym, versionsym, nullsym]) then begin
        writeln('Additional SHOW options are as follows:');
      end; (* if *)
      writeln;
      if (adj = nullsym) or (adj = allsym) then begin
        writeln('':14, 'ALL              Show all parameters.');
        writeln;
      end; (* if *)
      if (adj = nullsym) or (adj = versionsym) then begin
        writeln('':14, 'VERSION          Show version information.');
        writeln;
      end; (* if *)
    end; (* if *)
    if (noun = nullsym) then
        keypress;

    if (noun = nullsym) or (noun = takesym) then begin
      writeln('  TAKE        This command instructs Kermit to take further');
      writeln('':14, 'commands from a specified file.');
    end; (* if *)
    if (noun = nullsym) or (noun = comsym) then begin
      writeln('  COMMENT     Comments a TAKE file. (ignored)');
      writeln;
    end; (* if *)
    if (noun = nullsym) or (noun = logsym) then begin
      writeln('  LOG         This command opens a selected log file.');
      writeln('':14, 'LOG options are as follows:');
      writeln;
      if (adj = nullsym) or (adj = debugsym) then begin
        writeln('':14, 'DEBUG            open specified file for debug output.');
        writeln;
      end; (* if *)
    end; (* if *)
    if (noun = nullsym) or (noun = closesym) then begin
      writeln('  CLOSE       This command closes a selected log file previously');
      writeln('':14, 'opened via the LOG command.');
    end; (* if *)
  end; (* help4 *)

procedure help;
begin
  help1;
  help2;
  help3;
  help4
end; (* help *)

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

end. { unit helper }
{>>>> PARSER.TEXT}
(*$S+*)
unit parser;

INTERFACE

uses {$U kermglob.code} kermglob;

{Change log:
13 May 89, V1.1: Fixed several bugs in parsing of HELP commands   RTC
13 May 89, V1.1: Added parsing for COMMENT command
30 Apr 89, V1.1: Added parsing for SET INTERFACE command   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Added BYE & FINISH command parsing       RTC
14 Apr 89, V1.1: Added parsing for GET, PUT & SHOW VERSION commands   RTC
13 Apr 89, V1.1: Added Version message       RTC
14 Aug 88: Added parsing for LOG, CLOSE, and SET SYSTEM commands   RTC
02 Jul 88: Added -NAMES, -TYPE, TAKE command parsing   RTC

}

   function parse: statustype;

   procedure initvocab;
   
   procedure par_version;


IMPLEMENTATION

uses
   {$U kermutil.code} kermutil;

const
  my_version = '   Parser Unit V1.1, 13 May 89';


procedure eatspaces(var s: string255);

var done: boolean;
    i: integer;

  begin
    done := (length(s) = 0);
    while not done do
      begin
        if s[1] = ' ' then
          begin
            i := length(s) - 1;
            s := copy(s,2,i);
            done := length(s) = 0
          end (* if *)
        else
            done := true
      end (* while *)
  end; (* eatspaces *)

procedure isolate_word(var line, s: string255);

var i: integer;
    done: boolean;

  begin
    done := false;
    i := 1;
    s := copy(' ',0,0);
    while (i <= length(line)) and not done do
      begin
        if line[i] = ' ' then
            done := true
        else
            s := concat(s,copy(line,i,1));
        i := i + 1;
      end; (* while *)
    line := copy(line,i,length(line)-i+1);
  end; (* isolate_word *)

function get_fn(var line, fn: string255): boolean;

var i, l: integer;

  begin
    get_fn := true;
    isolate_word(line, fn);
    l := length(fn);
    if (l < 1) then
        get_fn := false
  end; (* get_fn *)

function get_num( var line: string255; var n: integer ): boolean;

var
   numstr: string255;
   i, l: integer;
begin
   get_num := true;
   isolate_word( line, numstr );
   l := length(numstr);
   if (l>5) or (l<1) then begin
      n := 0;
      get_num := false
   end
   else begin
      n := 0; i := 1;
      numstr := concat( numstr, ' ' );
      while (numstr[i] in ['0'..'9']) do begin
         if n<(maxint div 10) then
            n := n*10 + ord( numstr[i] ) - ord( '0' );
         i := i + 1
      end
   end
end; { get_num }

function nextch(var ch: char): boolean;

var s: string255;

  begin
    isolate_word(line,s);
    if length(s) <> 1 then
        nextch := false
    else
      begin
        ch := s[1];
        nextch := true
      end (* else *)
  end; (* nextch *)

function parse(*: statustype*);

type states = (start, fin, get_filename, get_set_parm, get_parity, 
               get_on_off, get_f_type, get_char, get_show_parm, 
               get_help_show, get_int_type, get_naming, get_help_parm, 
               exitstate, get_baud, get_line, get_log_parm, get_help_log);

var status: statustype;
    word: vocab;
    state: states;

function get_a_sym(var word: vocab): statustype;

var i: vocab;
    s: string255;
    stat: statustype;
    done: boolean;
    matches: integer;

  begin
    eat_spaces(line);
    if length(line) = 0 then
        get_a_sym := ateol
    else
      begin
        stat := null;
        done := false;
        isolate_word(line,s);
        i := allsym;
        matches := 0;
        repeat
            if (pos(s,vocablist[i]) = 1) and (i in expected) then
              begin
                matches := matches + 1;
                word := i
              end
            else if (s[1] < vocablist[i,1]) then
                done := true;
            if (i = versionsym) then
                done := true
            else
                i := succ(i)
        until (matches > 1) or done;
        if matches > 1 then
            stat := ambiguous
        else if (matches = 0) then
            stat := unrec;
        get_a_sym := stat
      end (* else *)
  end; (* get_a_sym *)

  begin
    state := start;
    parse := null;
    noun := nullsym;
    verb := nullsym;
    adj := nullsym;
    uppercase(line);
    repeat
        case state of
          start:
              begin
                expected := [comsym, consym, exitsym, helpsym, quitsym,
                             logsym, closesym, getsym, putsym, byesym, finsym,
                             recsym, sendsym, setsym, showsym, takesym];
                status := get_a_sym(verb);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end (* if *)
                else if (status <> unrec) and (status <>  ambiguous) then
                    case verb of
                      comsym: state := get_line;
                      consym, exitsym, quitsym,
                      byesym, finsym, recsym: state := fin;
                      getsym, putsym,
                      sendsym, takesym: state := getfilename;
                      helpsym: state := get_help_parm;
                      logsym, closesym: state := get_log_param;
                      setsym: state := get_set_parm;
                      showsym: state := get_show_parm;
                    end (* case *)
              end; (* case start *)
          fin:
              begin
                expected := [];
                status := get_a_sym(verb);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end (* if status *)
                else
                    status := unconfirmed
              end; (* case fin *)
          getfilename:
            begin
              expected := [];
              if getfn(line,xfilename) then
                begin
                  status := null;
                  state := fin
                end (* if *)
              else
                  status := fnexpected
            end; (* case get file name *)
          get_set_parm:
              begin
                expected := [paritysym, localsym, ibmsym, emulatesym, 
                             escsym, debugsym, filenamsym, filetypesym,
                             intsym, filewarnsym, baudsym, systemsym];
                status := get_a_sym(noun);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <>  ambiguous) then
                    case noun of
                      paritysym: state := get_parity;
                      localsym: state := get_on_off;
                      ibmsym: state := get_on_off;
                      emulatesym: state := get_on_off;
                      escsym: state := getchar;
                      debugsym: state := get_on_off;
                      filenamsym : state := get_naming;
                      filetypesym : state := get_f_type;
                      filewarnsym: state := get_on_off;
                      intsym: state := get_int_type;
                      baudsym: state := get_baud;
                      systemsym: state := get_line
                    end (* case *)
            end; (* case get_set_parm *)
          get_log_parm:
              begin
                expected := [debugsym];
                status := get_a_sym(adj);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <> ambiguous) then
                    if verb = logsym
                      then state := getfilename
                      else state := fin
              end; (* case get_log_parm *)
          get_line:
              begin
                eat_spaces(line);
                parse := null;
                exit(parse)
              end; {case get_line}
          get_parity, get_naming, get_int_type, get_on_off, get_f_type:
              begin
                case state of
                  get_parity:   expected := [marksym, spacesym,
                                             nonesym, evensym, oddsym];
                  get_naming:   expected := [convsym, litsym];
                  get_int_type: expected := [kermitsym, ucsdsym];
                  get_on_off:   expected := [onsym, offsym];
                  get_f_type:   expected := [binsym, textsym];
                end {case state};
                status := get_a_sym(adj);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <> ambiguous) then
                    state := fin
              end; (* case get_parity  *)
          get_baud:
             begin
               expected := [];
               if get_num( line, newbaud ) then begin
                  status := null; state := fin
               end
               else begin
                  newbaud := 0;
                  status := parm_expected
               end
             end; (* case get_baud *)
          get_char:
              if nextch(newescchar) then
                 state := fin
              else
                 status := ch_expected;
          get_show_parm:
              begin
                expected := [allsym, paritysym, localsym, ibmsym, 
                             emulatesym, escsym, debugsym, 
                             filenamsym, filetypesym, filewarnsym, 
                             baudsym, systemsym, versionsym];
                status := get_a_sym(noun);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <>  ambiguous) then
                    state := fin
              end; (* case get_show_parm *)
          get_help_show, get_help_log:
              begin
                case noun of
                  logsym, closesym: 
                    expected := [debugsym];
                  setsym: 
                    expected := [paritysym, localsym, ibmsym, escsym,
                                 intsym, debugsym, filenamsym, filetypesym, 
                                 filewarnsym, baudsym, emulatesym, systemsym];
                  showsym:
                    expected := [paritysym, localsym, ibmsym, escsym,
                                 debugsym, filenamsym, filetypesym, 
                                 filewarnsym, baudsym, emulatesym, systemsym,
                                 allsym, versionsym];
                end {case noun};
                status := get_a_sym(adj);
                if (status = at_eol) then
                  begin
                    status := null;
                    state := fin
                  end
                else if (status <> unrec) and (status <>  ambiguous) then
                    state := fin
              end; (* case get_help_show *)
          get_help_parm:
              begin
                expected := [consym, exitsym, helpsym, quitsym, recsym,
                             comsym, getsym, putsym, byesym, finsym, takesym,
                             logsym, closesym, sendsym, setsym, showsym];
                status := get_a_sym(noun);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end;
                if (status <> unrec) and (status <>  ambiguous) then
                    case noun of
                      consym, comsym, getsym, putsym,
                      sendsym, finsym, byesym, takesym,
                      recsym: state := fin;
                      closesym, logsym: state := get_help_log;
                      showsym, setsym: state := get_help_show;
                      helpsym: state := fin;
                      exitsym, quitsym: state := fin;
                    end (* case *)
              end; (* case get_help_show *)
        end (* case *)
    until (status <> null);
    parse := status
  end; (* parse *)

procedure initvocab;

var i: integer;

  begin
    vocablist[allsym] :=        'ALL';
    vocablist[baudsym] :=       'BAUD';
    vocablist[binsym] :=        'BINARY';
    vocablist[byesym] :=        'BYE';
    vocablist[closesym] :=      'CLOSE';
    vocablist[comsym] :=        'COMMENT';
    vocablist[consym] :=        'CONNECT';
    vocablist[convsym] :=       'CONVERTED';
    vocablist[debugsym] :=      'DEBUG';
    vocablist[emulatesym] :=    'EMULATE';
    vocablist[escsym] :=        'ESCAPE';
    vocablist[evensym] :=       'EVEN';
    vocablist[exitsym] :=       'EXIT';
    vocablist[filenamsym] :=    'FILE-NAMES';
    vocablist[filetypesym] :=   'FILE-TYPE';
    vocablist[filewarnsym] :=   'FILE-WARNING';
    vocablist[finsym] :=        'FINISH';
    vocablist[getsym] :=        'GET';
    vocablist[helpsym] :=       'HELP';
    vocablist[ibmsym] :=        'IBM';
    vocablist[intsym] :=        'INTERFACE';
    vocablist[kermitsym] :=     'KERMIT';
    vocablist[litsym] :=        'LITERAL';
    vocablist[localsym] :=      'LOCAL-ECHO';
    vocablist[logsym] :=        'LOG';
    vocablist[marksym] :=       'MARK';
    vocablist[nonesym] :=       'NONE';
    vocablist[oddsym] :=        'ODD';
    vocablist[offsym] :=        'OFF';
    vocablist[onsym] :=         'ON';
    vocablist[paritysym] :=     'PARITY';
    vocablist[putsym] :=        'PUT';
    vocablist[quitsym] :=       'QUIT';
    vocablist[recsym] :=        'RECEIVE';
    vocablist[sendsym] :=       'SEND';
    vocablist[setsym] :=        'SET';
    vocablist[showsym] :=       'SHOW';
    vocablist[spacesym] :=      'SPACE';
    vocablist[systemsym] :=     'SYSTEM-ID';
    vocablist[takesym] :=       'TAKE';
    vocablist[textsym] :=       'TEXT';
    vocablist[ucsdsym] :=       'UCSD';
    vocablist[versionsym] :=    'VERSION';
  end; (* initvocab *)

procedure par_version;
  
  begin
    writeln(my_version)
  end {par_version};
  
end. (* end of unit *)

{>>>> INTFUTIL.TEXT}
interface

{Change log:
30 Apr 89, V1.1: Extracted from KERMUTIL   RTC
}
   
   uses
     {$U kermglob.code} kermglob;
   
   procedure fill_parity_array;
   
   procedure set_parms;
   
   procedure show_parms;
   
   procedure connect;
   
   function read_ch(unitno: integer; var ch: char): boolean;

   procedure read_str(unitno:integer; var s: string255);

   procedure echo(ch: char);

   procedure clear_buf(unitno:integer);

   function aand(x,y: integer): integer;

   function aor(x,y: integer): integer;

   function xor(x,y: integer): integer;

   procedure uppercase(var s: string255);

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

   procedure io_error(i: integer);

   procedure debugwrite(s: string255);

   procedure debugint(s: string255; i: integer);

   function min(x,y: integer): integer;

   function tochar(ch: char): char;

   function unchar(ch: char): char;

   function ctl(ch: char): char;

   function getch(var r: char): boolean;

   function getsoh: boolean;

   function getfil(filename: string255): boolean;

   procedure send_brk;

   function setup_comm : boolean;        {changed 31 Jul 88, RTC}
   
   procedure flush_comm;                {added 16 Apr 89, RTC}

   procedure write_bool(s: string255; b: boolean);
   
   procedure write_ch(unitno: integer; ch: char );

   procedure writescreen(s: string255);

   procedure refresh_screen(numtry, num: integer);
   
   procedure set_timer(t : integer);    {added 26 Apr 89, RTC}
   
   function timeout : boolean;          {added 26 Apr 89, RTC}
   
   procedure utl_version;

implementation

{>>>> FAKEUTIL.TEXT}

unit kermutil;

{ Change log:
30 Apr 89, V1.1: Created Fake version of KERMUTIL   RTC
}
   
{$I intfutil.text}

procedure fill_parity_array;
  begin end; (* fill_parity_array *)

procedure write_bool{s: string255; b: boolean};
  begin end; (* write_bool *)

procedure show_parms;
  begin end; (* show_sym *)

procedure set_parms;
  begin end; (* set_parms *)

procedure connect;
  begin (* connect *) end; (* connect *)

procedure uppercase(*var s: string255*);
  begin end; (* uppercase *)

function read_ch(*unitno:integer; var ch: char): boolean*);
  begin end; (* read_ch *)

procedure write_ch(*unitno: integer; ch: char*);
  begin end;

procedure read_str(*unitno:integer; var s: string255*);
  begin end; (* read_str *)

procedure clear_buf(*unitno:integer*);
  begin end;
  
procedure send_brk;
  begin end;

function setup_comm{ : boolean};
  begin end;
   
procedure flush_comm;                {added 16 Apr 89, RTC}
  begin {flush_comm} end {flush_comm};

function aand(*x,y: integer): integer*);
  begin end; (* aand *)

function aor(*x,y: integer): integer*);
  begin end; (* aor *)

function xor(*x,y: integer): integer*);
  begin end; (* xor *)

procedure error(*p: packettype; len: integer*);
  begin end; (* error *)

procedure io_error(*i: integer*);
  begin end; (* io_error *)

procedure debugwrite(*s: string255*);
  begin end; (* debugwrite *)

procedure debugint(*s: string255; i: integer*);
  begin end; (* debugint *)

function min(*x,y: integer): integer*);
  begin end; (* min *)

function tochar(*ch: char): char*);
  begin end; (* tochar *)

function unchar(*ch: char): char*);
  begin end; (* unchar *)

function ctl(*ch: char): char*);
  begin end; (* ctl *)

procedure echo(*ch: char*);
  begin end; (* echo *)

function getch(*var r: char): boolean*);
  begin end; (* getch *)

function getsoh(*: boolean*);
  begin end; (* getsoh *)

function getfil(*filename: string255): boolean*);
  begin end; (* getfil *)

procedure writescreen(*s: string255*);
  begin end; (* writescreen *)

procedure refresh_screen(*numtry, num: integer*);
  begin end; (* refresh_screen *)

procedure set_timer{t : integer};    {added 26 Apr 89, RTC}
  begin {set_timer} end {set_timer};

function timeout {: boolean};        {added 26 Apr 89, RTC}
  begin {timeout} end {timeout};

procedure utl_version;
  begin end {utl_version};

begin { body of unit kermutil }
   { initialization code }
   ***;
   { termination code }
end. { fakeutil }
{>>>> KERMUTIL.TEXT}
{$D OS_ERHDL+}    { indicates to compile to use Pecan's errorhandler unit }
{$D OS_TIMER+}    { indicates to compile to use TIME() for timeouts }

unit kermutil;

{ Change log:
13 May 89, V1.1: Eliminated "int_bool_rec" & misc cleanups   RTC
30 Apr 89, V1.1: Moved set/show & connect from kermit to here   RTC
26 Apr 89, V1.1: Added support for TIMEr controlled timeouts   RTC
16 Apr 89, V1.1: Added procedure flush_comm to Flush REMOTE:    RTC
13 Apr 89, V1.1: Added Version message          RTC
17 Aug 88: Fixed missing EOLN's problem in debf    RTC
14 Aug 88: Fixed the debug messages to all go to debf      RTC
31 Jul 88: Modified setup_comm to funct., updated io_error.    RTC
10 Jul 88: Converted to using screenops unit     RTC
02 Jul 88: Misc cleanup, eliminated char_int_rec, etc.   RTC
26 Jun 88 Patched Unitwrite problem in Echo   RTC
26 Jun 88 Modified read_ch to use cr_getkb    RTC

        13 May 84: Use KERNEL's syscom record for screen control -sp-
}
   
{$I intfutil.text}

uses {$U *system.library} screenops, {RTC, 10 Jul 88}
     {$U kermenus.code} kermenus,
     {$U kermpack.code} kermpack (pak_version),
     {$U helper.code} helper (hlp_version),
     {$U parser.code} parser (par_version),
     {$U sender.code} sender (sen_version),
     {$U receiver.code} receiver (rec_version),
     {$U client.code} client (cli_version),
     {$U remunit.code} remunit,  {SP, 1/14/84}
     {$U syslibr:kernel.code} kernel (syscom,version) {$B OS_ERHDL+},
     {$U syslibr:errorhandl.code} error_handling {$E OS_ERHDL+};
   
const
  my_version = '   Kermutil Unit V1.1, 13 May 89';

type
  time_value = integer[10];

var
  old_flush, old_stop: char;
  time_limit : time_value;

{$I setshow.text}

procedure connect;

(* connect to remote host and transceive *)

var ch: char;
    close: boolean;

  procedure read_esc;

  (* read character after esc char and interpret it *)

    begin
      repeat
      until read_ch(keyport,ch);       (* wait until they've typed something in *)
      if (ch in ['a'..'z']) then  (* uppercase it *)
          ch := chr(ord(ch) - ord('a') + ord('A'));
      if ch in ['B','C','S','?'] then
          case ch of
              'B': sendbrk;       (* B: send a break to the IBM *)
              'C': close := true; (* C: end connection *)
              'S': begin          (* S: show status *)
                      noun := allsym;
                      showparms
                   end; (* S *)
              '?': begin          (* ?: show options *)
                  writeln
('B    Send a BREAK signal.');
                  writeln
('C    Close Connection, return to KERMIT-UCSD command level.');
                  writeln
('S    Show Status of connection');
                  writeln
('?    Print this list');
                  writeln
('^',ctl(esc_char),'   send the escape character itself to the remote host.')
                end; (* ? *)
            end (* case *)
      else if ch = esc_char then  (* ESC-char: send it out *)
        begin
          if half_duplex then
              write(ch); { changed from echo() by SP }
          write_ch(oport,ch)
        end (* else if *)
      else                        (* anything else: ignore *)
          write(chr(bell))
    end; (* read_esc *)

  begin (* connect *)
    clear_buf(keyport);                    (* empty keyboard buffer *)
    clear_buf(inport);                    (* empty remote input buffer *)
    writeln('Connecting to host...type CTRL-',ctl(esc_char),' C to exit');
    close := false;
    repeat
        if read_ch(inport,ch) then        (* if char from host then *)
            echo(ch);                   (* echo it *)

        if read_ch(keyport,ch) then        (* if char from keyboard then *)
            if ch <> esc_char then      (* if not ESC-char then *)
              begin
                if half_duplex then       (* echo it if half-duplex *)
                    write(ch); { changed from echo() by sp }
                write_ch(oport,ch)     (* send it out the port *)
              end (* if *)
            else (* ch = esc_char *)    (* else is ESC-char so *)
              read_esc;                   (* interpret next char *)
    until close;                      (* if still connected, get more *)
    writeln('Disconnected')
  end; (* connect *)

procedure uppercase(*var s: string255*);

var i: integer;

  begin
    for i := 1 to length(s) do
        if s[i] in ['a'..'z'] then
            s[i] := chr(ord(s[i]) - ord('a') + ord('A'))
  end; (* uppercase *)


function read_ch(*unitno:integer; var ch: char): boolean*);

(* read a character from an input queue *)
var
   ready: boolean;

  begin
    if unitno=keyport then
       ready := cr_kbstat
    else if unitno=inport then
       ready := cr_remstat
    else
       ready := false;
    if ready then            (* if a char there *)
       if unitno=keyport then
          ch := cr_getkb
       else
          ch := cr_getrem;
    read_ch := ready
  end; (* read_ch *)

procedure write_ch(*unitno: integer; ch: char*);
begin
   if unitno=oport then
      cr_putrem( ch )
end;


procedure read_str(*unitno:integer; var s: string255*);

(* acts like readln(s) but takes input from input queue *)

var i: integer;

  begin
    i := 0;
    s := copy('',0,0);
    repeat
      repeat                              (* get a character *)
      until read_ch(unitno,ch);
      if (ord(ch) = backspace) then       (* if it's a backspace then *)
        begin
          if (i > 0) then                   (* if not at beginning of line *)
            begin
              write(ch);                      (* go back a space on screen *)
              write(' ');                     (* erase char on screen *)
              write(ch);                      (* go back a space again *)
              i := i - 1;                     (* adjust string counter *)
              s := copy(s,1,i)                (* adjust string *)
            end (* if *)
        end (* if *)
      else if (ord(ch) <> eoln_sym) then  (* otherwise if not at eoln  then *)
        begin
          write(ch);                        (* echo char on screen *)
          i := i + 1;                       (* inc string counter *)
          s := concat(s,' ');
          s[i] := ch;                       (* put char in string *)
        end; (* if *)
    until (ord(ch) = eoln_sym);           (* if not eoln, get another char *)
    s := copy(s,1,i);                     (* correct string length *)
    writeln                               (* write a line on the screen *)
  end; (* read_str *)


procedure clear_buf(*unitno:integer*);
{ modified by SP }
begin
   if unitno=keyport then
      unitclear( unitno )
end;


procedure send_brk;
begin
   cr_break
end;


function setup_comm{ : boolean};
{ SP, 14 Jan 84 }
var
   result: cr_baud_result;
begin
   setup_comm := false;
   cr_setcommunications(false,
                        false,
                        baud,
                        8,
                        1,
                        cr_orig,
                        system_id,
                        result );
   case result of
     CR_bad_parameter :
         writeln('Bad Parameter, # Bits or Parity wrong');
     CR_bad_rate :
         writeln('Bad Baud Rate selection');
     CR_set_OK :
         setup_comm := true;
     CR_select_not_supported :
         writeln('Hardware does not support Baud selection')
   end {case}
end;
   
procedure flush_comm;                {added 16 Apr 89, RTC}
  
  var
    ch : char;
  
  begin {flush_comm}
    while CR_remstat do
      ch := CR_getrem   {flush all characters in REMOTE port}
  end {flush_comm};

function aand(*x,y: integer): integer*);

(* arithmetic and--takes 2 integers and ands them, yeilding an integer *)

  begin
    aand := ord(odd(x) and odd(y));  (* use as booleans to 'and' them *)
  end; (* aand *)


function aor(*x,y: integer): integer*);

(* arithmetic or *)

  begin
    aor := ord(odd(x) or odd(y));   (* use as booleans to 'or' them *)
  end; (* aor *)

function xor(*x,y: integer): integer*);

(* exclusive or *)

  begin
    xor := ord( (odd(x) or odd(y)) and not(odd(x) and odd(y)) );
  end; (* xor *)

procedure error(*p: packettype; len: integer*);

(* writes error message sent by remote host *)

var i: integer;

  begin
    gotoxy(0,errorline);
    for i := 0 to len-1 do
        write(p[i]);
    gotoxy(0,promptline);
  end; (* error *)

procedure io_error(*i: integer*);
  
  var
    message : string;
    
  begin
    SC_erase_to_EOL( 0, errorline );
    {$B OS_ERHDL+}
    IOR_to_message(i,message);
    {$E OS_ERHDL+} {$B OS_ERHDL-}
    case i of
        0: message := 'No error';
        1: message := 'Bad Block, Parity error (CRC)';
        2: message := 'Bad Unit Number';
        3: message := 'Bad I/O request, Illegal operation';
        4: message := 'Undefined hardware error';
        5: message := 'Lost unit, Volume is no longer on-line';
        6: message := 'Lost file, File is no longer in directory';
        7: message := 'Bad Title, Illegal file name';
        8: message := 'No room, insufficient space';
        9: message := 'No unit, No such volume on line';
        10: message := 'No file, No such file on volume';
        11: message := 'Duplicate file';
        12: message := 'Not closed, attempt to open an open file';
        13: message := 'Not open, attempt to access a closed file';
        14: message := 'Bad format, error in reading real or integer';
        15: message := 'Queue overflow';
        16: message := 'Write Protected volume';
        17: message := 'Illegal Block';
        18: message := 'Illegal Buffer for low-level I/O';
        19: message := 'Illegal Size or Range of File Attribute';
        20: message := 'Attempted read past End of File';
      end; (* case *)
      if i >= 128 then
        begin
          i := i - 128; message := '0';
          while i > 0 do
            begin
              message[1] := chr(ord('0') + i mod 10);
              message := concat(' ',message);
              i := i div 10
            end;
          message := concat('Host Operating System Error #',message)
        end;
    {$E OS_ERHDL-}
    writeln(message);
    gotoxy(0,promptline)
  end; (* io_error *)

procedure debugwrite(*s: string255*);

(* writes a debugging message *)
var i: integer;

  begin
    if debug then
      begin
        SC_erase_to_EOL(0,debugline);
        gotoxy(0,pred(debugline)); writeln(debf);
        write(debf,s);
        for i := 1 to 2000 do ;                (* write debugging message *)
      end (* if debug *)
  end; (* debugwrite *)

procedure debugint(*s: string255; i: integer*);

(* write a debugging message and an integer *)

  begin
    if debug then
      begin
        debugwrite(s);
        write(debf,i)
      end (* if debug *)
  end; (* debugint *)

function min(*x,y: integer): integer*);

(* returns smaller of two integers *)

  begin
    if x < y then
        min := x
    else
        min := y
  end; (* min *)

function tochar(*ch: char): char*);

(* tochar converts a control character to a printable one by adding space *)

  begin
    tochar := chr(ord(ch) + ord(' '))
  end; (* tochar *)

function unchar(*ch: char): char*);

(* unchar undoes tochar *)

  begin
    unchar := chr(ord(ch) - ord(' '))
  end; (* unchar *)

function ctl(*ch: char): char*);

(* ctl toggles control bit: ^A becomes A, A becomes ^A *)

  begin
    ctl := chr(xor(ord(ch),64))
  end; (* ctl *)

procedure echo(*ch: char*);

(* echos a character on the screen *)

var cursorx, cursory:integer;
    ch_buf : packed array [0..1] of char;

{ The DataMedia emulation is by John Socha. }
begin
   ch := chr(aand(ord(ch),127)); (* mask off parity bit *)
   ch_buf[0] := ch;     {for unitwrite portability      RTC}
   
   if emulating and (ord(ch) in [30,25,28,31,29,11]) then
      case ord(ch) of
         { Datamedia 1520 emulation }
         { rs }30: begin
                      { allow timeout while waiting for coordinates
                        so computer doesn't freeze }
                      set_timer(2);
                      repeat
                      until read_ch( inport, ch ) or timeout;
                      if not timeout then begin
                         cursorx:=ord(ch)-32;
                         repeat
                         until read_ch( inport, ch ) or timeout;
                         if not timeout then begin
                            cursory:=ord(ch)-32;
                            gotoxy(cursorx,cursory)
                         end
                      end
                   end;
         { em }25: SC_home;
         { fs }28: SC_right;
         { us }31: SC_up;
         { gs }29: SC_erase_to_EOL(SC_find_X,SC_find_Y);
         { vt }11: SC_eras_eos(SC_find_X,SC_find_Y)
      end
    else
       unitwrite(1,ch_buf[0],1,,12)  { the 12 eliminates DLE & CR expansion }
  end; (* echo *)


function getch(*var r: char): boolean*);

(* gets a character, strips parity, returns true if it got a char which *)
(* isn't Kermit SOH, false if it gets SOH or nothing after timeout *)

  begin
    getch := false;
    repeat
    until (read_ch(inport,r)) or timeout;       (* wait for a character *)
    if timeout then                             (* if wait too long then *)
        exit(getch);                            (* get out of here *)
    if parity <> nopar
      then r := chr(aand(ord(r),127));          (* strip parity from char *)
    getch := (r <> chr(soh));                   (* return true if not SOH *)
  end; (* getch *)


function getsoh(*: boolean*);

(* reads characters until it finds an SOH; returns false if has timed out *)

var ch: char;

  begin
    getsoh := true;
    repeat
      repeat
      until (read_ch(inport,ch)) or timeout; (* wait for a character *)
      if timeout then
        begin
            getsoh := false;
            exit(getsoh)
          end; (* if *)
        ch := chr(aand(ord(ch),127));            (* strip parity of char *)
    until (ch = chr(SOH))                        (* if not SOH, get more *)
  end; (* getsoh *)


function getfil(*filename: string255): boolean*);

(* opens a file for writing *)

  begin
    (*$I-*) (* turn i/o checking off *)
    if f_is_binary
      then
        begin
          rewrite(b_file,filename);
          bufpos := 1           {new file... nothing in buffer}
        end
      else rewrite(t_file,filename);
    (*$I-*) (* turn i/o checking on *)
    getfil := (ioresult = 0)
  end; (* getfil *)


procedure writescreen(*s: string255*);

(* sets up the screen for receiving or sending files *)

begin
   page(output);
   gotoxy(0,titleline);
   write('            Kermit UCSD p-System, Version ', version );
   gotoxy(statuspos,statusline);
   write(s);
   gotoxy(0,packetline);
   write('Number of Packets: ');
   gotoxy(0,retryline);
   write('Number of Tries: ');
   gotoxy(0,fileline);
   write('File Name: ');
end; (* writescreen *)


procedure refresh_screen(*numtry, num: integer*);

(* keeps track of packet count on screen *)

begin
   gotoxy(retrypos,retryline);
   write(numtry: 5);
   gotoxy(packetpos,packetline);
   write(num: 5)
end; (* refresh_screen *)

{$B OS_TIMER+}
procedure long_time(var t : time_value);
  
  {this procedure converts the "dual integer" values returned by time()
   to a single "long integer" value, which it returns to the caller}
  
  var
    i : 0..1;
    hl : array [0..1] of integer;
  
  begin {long_time}
    t := 0; time(hl[0],hl[1]);
    for i := 0 to 1 do
      begin
        if hl[i] < 0 then t := t + 1;
        t := 65536*t + hl[i]
      end
  end {long_time};
{$E OS_TIMER+}

procedure set_timer{t : integer};    {added 26 Apr 89, RTC}
  
  {$B OS_TIMER-}
  const counts_per_second = 1000;        {WARNING!! implementation dependant}
  {$E OS_TIMER-}
  
  var long_t : time_value;
  
  begin {set_timer}
    long_t := t; {convert to long format}
    {$B OS_TIMER+}
    long_time(time_limit); time_limit := time_limit + 60*long_t
    {$E OS_TIMER+} {$B OS_TIMER-}
    time_limit := counts_per_second*long_t
    {$E OS_TIMER-}
  end {set_timer};

function timeout {: boolean};        {added 26 Apr 89, RTC}
  
  {$B OS_TIMER+}
  var this_time : time_value;
  {$E OS_TIMER+}
  
  begin {timeout}
    {$B OS_TIMER+}
    long_time(this_time);
    timeout := this_time > time_limit
    {$E OS_TIMER+} {$B OS_TIMER-}
    time_limit := time_limit - 1;
    timeout := time_limit <= 0
    {$E OS_TIMER-}
  end {timeout};

procedure utl_version;

begin
   write(my_version);
  {$B OS_TIMER+}
  write(' (with TIMER)');
  {$E OS_TIMER+}
  writeln
end {utl_version};


begin { body of unit kermutil }
   { initialization code }
   old_flush := syscom^.crtinfo.flush;
   old_stop := syscom^.crtinfo.stop;
   syscom^.crtinfo.flush := chr(255);  { effectively turning flush off }
   syscom^.crtinfo.stop := chr(254);   { effectively turning stop off }

   ***;

   { termination code }
   syscom^.crtinfo.flush := old_flush;  { turn flush back on }
   syscom^.crtinfo.stop := old_stop     { turn stop back on }
end. { kermutil }
{>>>> SETSHOW.TEXT}

{ Change log:
30 Apr 89, V1.1: moved into kermutil   RTC
30 Apr 89, V1.1: Added SET INTERFACE command    RTC
16 Apr 89, V1.1: Added Client Unit to SHOW VER command      RTC
14 Apr 89, V1.1: Added SHOW VERSION command   RTC
14 Aug 88: Added SYSTEM-ID and modified DEBUG      RTC
31 Jul 88: Modified to permit REMUNIT to accept/reject baud rate    RTC

}

procedure fill_parity_array;

(* parity value table for even parity...not(entry) = odd parity *)

const min = 0;
      max = 255;

var i, shifter, counter: integer;
    ch: char;

begin
   for ch := chr(min) to chr(max) do
     case parity of
        evenpar: begin
                    shifter := aand(ord(ch),255); (* mask off parity bit *)
                    counter := 0;
                    for i := 1 to 7 do begin       (* count the 1's *)
                       if odd(shifter) then
                          counter := counter + 1;
                       shifter := shifter div 2
                    end; (* for i *)
                    if odd(counter) then       (* stick a 1 on if necessary *)
                       parity_array[ch] := chr(aor(ord(ch),128))
                    else
                       parity_array[ch] := chr(aand(ord(ch),127))
                 end; (* for ch *) (* case even *)
        oddpar:  begin
                    shifter := aand(ord(ch),255);  (* mask off parity bit *)
                    counter := 0;
                    for i := 1 to 7 do begin        (* count the 1's *)
                       if odd(shifter) then
                           counter := counter + 1;
                       shifter := shifter div 2
                    end; (* for i *)
                    if odd(counter) then        (* stick a 1 on if necessary *)
                       parity_array[ch] := chr(aand(ord(ch),127))
                    else
                       parity_array[ch] := chr(aor(ord(ch),128))
                 end; (* for ch *) (* case odd *)
        markpar: parity_array[ch] := chr(aor(ord(ch),128));
        spacepar:parity_array[ch] := chr(aand(ord(ch),127));
        nopar:   parity_array[ch] := ch;
      end; (* case *)
  end; (* fill_parity_array *)

procedure write_bool{s: string255; b: boolean};

(* writes message & 'on' if b, 'off' if not b *)
  begin
    write(s);
    case b of
        true: writeln('on');
        false: writeln('off');
      end; (* case *)
  end; (* write_bool *)

procedure show_parms;

(* shows the various settable parameters *)
var
  i,first,last : vocab;

begin
   if noun = allsym then
     begin
       first := baudsym; last := systemsym
     end
   else
     begin
       first := noun; last := noun
     end;
   for i := first to last do
     case i of
       debugsym:    write_bool('Debugging is ',debug);
 
       escsym:      writeln('Escape character is ^',ctl(esc_char));
 
       filenamsym:  begin
                      write('File names are ');
                      if lit_names
                        then write('Literal')
                        else write('Converted');
                      writeln
                    end;
       
       filetypesym: begin
                      write('File type is ');
                      if f_is_binary
                        then write('Binary')
                        else write('Text');
                      writeln
                    end;
       
       filewarnsym: write_bool('File warning is ',fwarn);
 
       ibmsym:      write_bool('IBM is ',ibm);
 
       localsym:    write_bool('Local echo is ',halfduplex);
 
       emulatesym:  write_bool('Emulate DataMedia is ', emulating );
 
       baudsym:     writeln( 'Baud rate is ', baud:5 );
 
       paritysym:   begin
                       case parity of
                          evenpar: write('Even');
                          markpar: write('Mark');
                          nopar: write('No');
                          oddpar: write('Odd');
                          spacepar: write('Space');
                       end; (* case *)
                       writeln(' parity');
                    end; (* paritysym *)
     
       systemsym:   writeln('System ID is ',system_id);
       
     end; (* case *)
   if noun = versionsym then
     begin
       writeln(ker_version);
       rec_version; sen_version; cli_version;
       hlp_version; pak_version; utl_version; gbl_version;
       mnu_version; par_version;
     end
end; (* show_sym *)


procedure set_parms;

(* sets the parameters *)
  
  var
    oldbaud : integer;

  begin
    case noun of
        debugsym: debug := adj = onsym;
        escsym: escchar := newescchar;
        filenamsym : lit_names := adj = litsym;
        filetypesym : f_is_binary := adj = binsym;
        filewarnsym: fwarn := (adj = onsym);
        ibmsym: case adj of
                    onsym: begin
                        ibm := true;
                        parity := markpar;
                        half_duplex := true;
                        fillparityarray
                      end; (* onsym *)
                    offsym: begin
                        ibm := false;
                        parity := nopar;
                        half_duplex := false;
                        fillparityarray
                      end; (* onsym *)
                  end; (* case adj *)
        intsym: if adj = ucsdsym then menu_interface;
        localsym: halfduplex := (adj = onsym);
        emulatesym: emulating := (adj = onsym);
        paritysym: begin
              case adj of
                  evensym: parity := evenpar;
                  marksym: parity := markpar;
                  nonesym: parity := nopar;
                  oddsym: parity := oddpar;
                  spacesym: parity := spacepar;
                end; (* case *)
              fill_parity_array;
             end; (* paritysym *)
        baudsym: begin
            oldbaud := baud; baud := newbaud;
            if not setup_comm then baud := oldbaud
         end { baudsym };
        systemsym: system_id := line;
      end; (* case *)
  end; (* set_parms *)
{>>>> KERMENUS.TEXT}
unit kermenus;

interface
  
  {Change log:
  14 May 89, V1.1: Added Parameters menu   RTC
  02 May 89, V1.1: Added menu to control log files   RTC
  30 Apr 89, V1.1: Originally written   RTC
  }
  
  procedure menu_interface;
  
  procedure mnu_version;
  
implementation

  uses screenops,
       {$U kermglob.code} kermglob,
       {$U kermutil.code} kermutil,
       {$U sender.code}   sender,
       {$U receiver.code} receiver,
       {$U client.code}   client;
  
  const
    my_version = '   Kermenus Unit V1.1, 14 May 89';

  procedure transfer_files;
    
    var
      ch : char;
    
    begin {transfer_files}
      ch := SC_prompt(concat('Kermit-UCSD File Transfer: ',
                             'S(end, R(eceive, G(et, P(ut, A(bort'),
                      -1,-1,0,menu_line,
                      ['S','R','G','P','A',' '],
                      false,',');
      SC_clr_line(menu_line);
      case ch of
        'G', 'R' : begin
                if ch = 'G' then
                  begin
                    gotoxy(file_pos,file_line);
                    readln(xfilename); uppercase(xfilename)
                  end;
                recsw(rec_ok,ch = 'G');
                gotoxy(0,debugline);
                write(chr(bell));
                if rec_ok then
                    writeln('successful receive')
                else
                    writeln('unsuccessful receive');
                (*$I-*) (* set i/o checking off *)
                if f_is_binary
                  then close(b_file)
                  else close(t_file);
                (*$I+*) (* set i/o checking back on *)
              end; (* recsym *)
        'P', 'S' : begin
                gotoxy(file_pos,file_line);
                readln(xfilename); uppercase(xfilename);
                sendsw(send_ok);
                gotoxy(0,debugline);
                write(chr(bell));
                if send_ok then
                    writeln('successful send')
                else
                    writeln('unsuccessful send');
                (*$I-*) (* set i/o checking off *)
                if f_is_binary
                  then close(b_file)
                  else close(t_file);
                (*$I+*) (* set i/o checking back on *)
              end; (* sendsym *)
        'A', ' ' : begin
                gotoxy(0,debugline);
                write('file transfer aborted');
              end; {abort transfer}
      end {case ch}
    end {transfer_files};
    
  procedure logs;
    
    var
      ch_cmd,ch_log : char;
      log_message : string;
    
    begin {logs}
      ch_cmd := SC_prompt(concat('Kermit-UCSD Logs: ',
                             'O(pen, C(lose, A(bort'),
                      -1,-1,0,menu_line,
                      ['O','C','A',' '],
                      false,',');
      case ch_cmd of
        'O' : log_message := 'Open';
        'C' : log_message := 'Close';
        'A',' ' : exit(logs)
      end {case ch_cmd};
      ch_log := SC_prompt(concat('Kermit-UCSD ',log_message,' Log: ',
                             'D(ebug, A(bort'),
                      -1,-1,0,menu_line,
                      ['D','A',' '],
                      false,',');
      case ch_log of
        'D' : log_message := concat(log_message,' for Debug');
        'A',' ' : exit(logs)
      end {case ch_log};
      if ch_cmd = 'O' then {command was to open log}
        begin
          SC_clr_line(menu_line);
          write('File to ',log_message,' Logging>');
          readln(xfilename); uppercase(xfilename);
          {$I-}
          case ch_log of
            'D' :
              begin
                close(debf,lock);
                rewrite(debf,xfilename)
              end;
          end {case ch_log};
          if ioresult <> 0 then
            begin
              writeln('Unable to open ',xfilename);
              case ch_log of
                'D' :
                  begin
                    close(debf);
                    rewrite(debf,'CONSOLE:')
                  end;
              end {case ch_log};
            end
          else {$I+}
            case ch_log of
              'D' : write(debf,
                  ker_version,' -- Debug log...');
            end
        end
      else {command was to close log}
        begin
          {$I-}
          case ch_log of
            'D' : close(debf,lock);
          end {case ch_log};
          if ioresult <> 0 then
            begin
              writeln('Unable to close file');
            end;
          case ch_log of
            'D' : rewrite(debf,'CONSOLE:');
          end {case ch_log};
          {$I+}
        end;
    end {logs};
  
  procedure menu_interface;
    
    var
      done : boolean;
      ch : char;
    
    procedure write_bool(b: boolean);
      
      {writes 'True' or 'False'}
      
      begin {write_bool}
        if b
          then write('True ')
          else write('False')
      end {write_bool};
    
    procedure read_bool(var b: boolean);
      
      var ch : char;
      
      begin {read_bool}
        SC_getc_ch(ch,['T','F']);
        b := ch = 'T'
      end {read_bool};
    
    procedure parameters;
      
      const
        name_line = 9;
        type_line = 10;
        warn_line = 11;
        baud_line = 12;
        parity_line = 13;
        echo_line = 14;
        ibm_line = 15;
        em_line = 16;
        esc_line = 17;
        debug_line = 18;
        sys_line = 19;
        opt_pos = 4;
        val_pos = 25;
      
      begin {parameters}
        SC_eras_eos(0,pred(name_line));
        repeat
          gotoxy(opt_pos,name_line); write('File N(ames'); 
          gotoxy(val_pos,name_line);
          if lit_names
            then write('Literal  ')
            else write('Converted');
          gotoxy(opt_pos,type_line); write('File T(ype'); 
          gotoxy(val_pos,type_line);
          if f_is_binary
            then write('Binary')
            else write('Text  ');
          gotoxy(opt_pos,warn_line); write('File W(arning'); 
          gotoxy(val_pos,warn_line); write_bool(f_warn);
          gotoxy(opt_pos,baud_line); write('B(aud rate'); 
          gotoxy(val_pos,baud_line); write(baud);
          gotoxy(opt_pos,parity_line); write('P(arity'); 
          gotoxy(val_pos,parity_line);
          case parity of
            evenpar: write('Even');
            markpar: write('Mark');
            nopar: write('None');
            oddpar: write('Odd');
            spacepar: write('Space');
          end {case parity};
          gotoxy(opt_pos,echo_line); write('L(ocal echo'); 
          gotoxy(val_pos,echo_line); write_bool(half_duplex);
          gotoxy(opt_pos,ibm_line); write('I(BM mode'); 
          gotoxy(val_pos,ibm_line); write_bool(ibm);
          gotoxy(opt_pos,em_line); write('eM(ulate Datamedia'); 
          gotoxy(val_pos,em_line); write_bool(emulating);
          gotoxy(opt_pos,esc_line); write('E(scape Character'); 
          gotoxy(val_pos,esc_line); write('^',ctl(esc_char));
          gotoxy(opt_pos,debug_line); write('D(ebugging'); 
          gotoxy(val_pos,debug_line); write_bool(debug);
          gotoxy(opt_pos,sys_line); write('S(ystem ID'); 
          gotoxy(val_pos,sys_line); write(system_id);
          ch := SC_prompt(concat('Kermit Parameters: {options} ',
                               '<space> to leave, ',
                               'switch to K(ermit style interface, V(ersion'),
                          -1,-1,0,menu_line,
                ['D','E','N','T','W','I','L','M','B','P','S','K','V',' '],
                          false,',');
          case ch of
            'D' : begin
                    SC_erase_to_EOL(val_pos,debug_line); read_bool(debug)
                  end;
            'E' : repeat
                    SC_erase_to_EOL(val_pos,esc_line); 
                    read(keyboard,esc_char)
                  until esc_char in [chr(0)..chr(31)];
            'N' : begin
                    SC_erase_to_EOL(val_pos,name_line);
                    SC_getc_ch(ch,['L','C']);
                    lit_names := ch = 'L'
                  end;
            'T' : begin
                    SC_erase_to_EOL(val_pos,type_line);
                    SC_getc_ch(ch,['B','T']);
                    f_is_binary := ch = 'B'
                  end;
            'W' : begin
                    SC_erase_to_EOL(val_pos,warn_line); read_bool(f_warn)
                  end;
            'I' : begin
                    SC_erase_to_EOL(val_pos,ibm_line); read_bool(ibm);
                    if ibm then
                      begin
                        parity := markpar;
                        half_duplex := true
                      end
                    else
                      begin
                        parity := nopar;
                        half_duplex := false
                      end;
                    fill_parity_array
                  end;
            'L' : begin
                    SC_erase_to_EOL(val_pos,echo_line); read_bool(halfduplex)
                  end;
            'M' : begin
                    SC_erase_to_EOL(val_pos,em_line); read_bool(emulating)
                  end;
            'B' : repeat
                    SC_erase_to_EOL(val_pos,baud_line); {$I-} read(baud); {$I+}
                    SC_erase_to_EOL(0,menu_line)
                  until setup_comm;
            'P' : begin
                    SC_erase_to_EOL(val_pos,parity_line); 
                    SC_getc_ch(ch,['E','O','M','S','N']);
                    case ch of
                      'E' : parity := evenpar;
                      'M' : parity := markpar;
                      'N' : parity := nopar;
                      'O' : parity := oddpar;
                      'S' : parity := spacepar;
                    end {case ch};
                    fill_parity_array
                  end;
            'S' : begin
                    SC_erase_to_EOL(val_pos,sys_line); readln(system_id)
                  end;
            'K' : begin
                    done := true; {switch back to KERMIT style interface}
                    SC_clr_screen; exit(parameters)
                  end;
            'V' : begin
                    SC_eras_eos(0,name_line);
                    noun := versionsym; show_parms;
                    exit(parameters)
                  end;
            ' ' : exit(parameters);
          end {case ch}
        until false
      end {parameters};
    
    begin {menu_interface}
      done := false;
      writescreen('');
      repeat
        ch := SC_prompt(concat('Kermit-UCSD: ',
                               'C(onnect, T(ransfer Files, Q(uit, ',
                               'S(et Parameters, L(ogs, B(ye, F(inish'),
                        -1,-1,0,menu_line,
                        ['C','T','Q','S','L','B','F'],
                        false,',');
        SC_clr_line(status_line); SC_clr_line(debug_line);
        case ch of
          'C' : begin SC_clr_screen; connect; writescreen('') end;
          'T' : transfer_files;
          'L' : logs;
          'F', 'B' : begin
                  case ch of
                    'F' : line := 'F';
                    'B' : line := 'L';
                  end {case};
                  clientsw(send_ok,'G',line);
                  gotoxy(0,debugline);
                  write(chr(bell));
                  if send_ok then
                      writeln('successful transaction')
                  else
                      writeln('unsuccessful transaction');
                  (*$I-*) (* set i/o checking off *)
                  close(t_file);
                  (*$I+*) (* set i/o checking back on *)
                end; {generic server command}
          'S' : parameters;
          'Q' : begin done := true; verb := quitsym end;
        end {case ch}
      until done
    end {menu_interface};
  
  procedure mnu_version;
    
    begin {mnu_version}
      writeln(my_version)
    end {mnu_version};
  
end {kermenus}.
{>>>> KERMPACK.TEXT}
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 }
{>>>> KERMGLOB.TEXT}
unit kermglob;

interface
    
{Change log:
13 May 89, V1.1: Added COMMENT vocab. & Eliminated "int_bool_rec"   RTC
30 Apr 89, V1.1: Added vocabulary for SET INTERFACE command   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Added BYE & FINISH commands       RTC
13 Apr 89, V1.1: Added Version message      RTC
14 Aug 88: Added LOG, CLOSE, and SET SYSTEM commands    RTC
31 Jul 88: Added variable system_id string for REMUNIT    RTC
31 Jul 88: Added attributes packets & exact size bin. xfrs    RTC
10 Jul 88: Removed screen command definitions    RTC
30 Jun 88: Modified for binary files, "take", ^X & ^Z   RTC
}
   
   const
         blksize = 512;
         oport = 8;          (* output port # *)
         inport = 7;
         keyport = 2;
         bell = 7;           (* ASCII bell *)
         maxpack = 93;       (* maximum packet size minus 1 *)
         soh = 1;            (* start of header *)
         sp = 32;            (* ASCII space *)
         cr = 13;            (* ASCII CR *)
         lf = 10;            (* ASCII line feed *)
         del = 127;          (* delete *)
         can_cur = 24;       { cancel current file char ^X }
         can_all = 26;       { cancel all files char    ^Z }
         my_esc = 29;        (* default esc char for connect (^]) *)
         maxtry = 5;         (* number of times to retry sending packet *)
         my_quote = '#';     (* quote character I'll use *)
         my_qbin = '&';      { 8th bit quote character I want }
         my_pad = 0;         (* number of padding chars I need *)
         my_pchar = 0;       (* padding character I need *)
         my_eol = 13;        (* end of line character i need *)
         my_time = 5;        (* seconds after which I should be timed out *)
         maxtim = 20;        (* maximum timeout interval *)
         mintim = 2;         (* minimum time out interval *)
         at_eof = -1;        (* value to return if at eof *)
         at_badblk = -2;     { value to return if at bad block }
         {rqsize = 5000;      (* input queue size *)
         qsize1 = 5001;      (* qsize + 1 *)}
         eoln_sym = 13;      (* pascal eoln sym *)
         back_space = 8;     (* pascal backspace sym *)
         defaultbaud = 1200; (* default baud rate *)

   (* screen control information *)
     (* console line on which to put specified info *)
         menu_line = 0;
         title_line = 2;
         statusline = 3;
         packet_line = 4;
         retry_line = 5;
         file_line = 6;
         error_line = 7;
         debug_line = 8;
         prompt_line = 9;
     (* position on line to put info *)
         statuspos = 60;
         packet_pos = 19;
         retry_pos = 17;
         file_pos = 11;

   type packettype = packed array[0..maxpack] of char;
        parity_type = (evenpar, oddpar, markpar, spacepar, nopar);

        string255 = string[255];


        statustype = (null, at_eol, unconfirmed, parm_expected, ambiguous,
                      unrec, fn_expected, ch_expected, num_expected);
        vocab = (nullsym, allsym, baudsym, binsym, byesym, closesym, comsym,
                 consym, convsym, debugsym, emulatesym, escsym, evensym,
                 exitsym, filenamsym, filetypesym, filewarnsym, finsym,
                 getsym, helpsym, ibmsym, intsym, kermitsym, litsym,
                 localsym, logsym, marksym, nonesym, oddsym, offsym, onsym,
                 paritysym, putsym, quitsym, recsym, sendsym, setsym,
                 showsym, spacesym, systemsym, takesym, textsym, ucsdsym,
                 versionsym);

    var noun, verb, adj: vocab;
        status: statustype;
        vocablist: array[vocab] of string[13];
        xfilename, line: string255;
        newescchar: char;
        expected: set of vocab;
        newbaud: integer;

        currstate: char; (* current state *)
        xeol, quote, qbin, esc_char: char;
        lit_names, f_is_binary, fwarn, ibm, half_duplex,
        en_attr, en_qbin, debug: boolean;
        i, size, rpsiz, spsiz, pad, n, num_try, oldtry, timint: integer;
        recpkt, packet: packettype;
        padchar, ch: char;
        s: string255;
        debf: text; (* file for debug output *)
        parity: parity_type;
        xon: char;
        filebuf: packed array[1..blksize] of char;
        bufpos, bufend: integer;
        parity_array: packed array[char] of char;
        ctlset: set of char;
        rec_ok, send_ok: boolean;
        baud: integer;
        emulating: boolean;
        last_blksize : integer;  {size of last block of boolean file}
        t_file : text   {file for text file transfers};
        b_file : file   {file for binary file transfers};
        cmd_file : text {file of "take" commands};
        ker_version,            { version id for other units }
        system_id : string      {id string for REMUNIT};

   procedure gbl_version;

implementation
   
   const
     my_version = '   Kermglob Unit V1.1, 13 May 89';

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

end. { kermglob }
{>>>> UCPECAN.M.TEXT}
ckermglob


cfakeutil
kermutil

ckermpack


cparser


chelper


csender


creceiver


cclient


ckermenus


ckermutil


ckermit


{>>>>}
