From C20211@UK.AC.PLYMOUTH.PRIME-A Wed Apr 7 14:08 GMT 1993 Via: uk.ac.plymouth; Wed, 7 Apr 93 14:08:25 GMT Date: Wed, 07 Apr 93 15:06:12 From: John Horne Subject: Prime Kermit 8.15 - file 2 To: syspds In-Reply-To: Your message of Fri, 19 Mar 1993 16:21:30 +0000 (GMT) Status: RO From : John Horne, Computing Service, Polytechnic South West. Email : C20211 @ UK.AC.PSW.PA The following is a list of known bugs and potential problems in the current version of Prime Kermit. It is possible for some of the problems to be resolved at each individual site by the person responsible for installing Kermit, although this may require some minor code changes. This version has been tested at PRIMOS revisions 21.0.5q and 22.1.1b. (It has also been run, but not 'rigorously' tested, at Primos rev 23.2.0b). 1) Odd file lengths are indicated by setting the read/write lock of the file to NONE. This will fail (with no warning) if the user does not have P or O rights to the directory. The consequence of this is simply a final control-Z in the file. Also if the file initially has a read/write lock of NONE, then the final character may be lost. I know of no other way of "marking" the file as being of odd length. Any ideas? 2) TAKE files try to dynamically obtain a file unit to use. However, it is not known what range of file units a user is allowed. So the range from 7 to 127 is used. Some sites may have limited this range, and so a change to the code may be necessary if this is a problem. 3) The Date/Time file created (DTC) attribute can only be set if the user has P or O rights. No warning is given if this cannot be set from the received attribute packet. 4) Some of the code uses the Primos subroutine T$AMLC to transfer data along an AMLC line. Unfortunately this subroutine only returns a zero for success, or a one for failure. In the case of a failure Kermit will abort the operation, since it cannot correct the problem without knowing what it is! It will then display a brief, although possibly vague, message. 5) The end-of-line characters expected in text files must be either a single line-feed (LF), a single carriage-return (CR), or a carriage-return (CR) followed by a line-feed (CRLF). However, the sequence LFCR will not be handled correctly. It is not expected that this will cause any problems! 6) The command "SET BAUD baud_rate" allows only 8 speeds to be specified. The first four are 110, 134.5, 300, and 1200, these are fixed within Primos. The next value is the programmable clock speed specified by the CONFIG directive AMLCLK for the computer. Its default value is 9600, but may be changed by the system administrator. The final three values are set by hardware jumpers within the computer, the default values are 75, 150, and 1800. These may also have been changed at the request of the system administrator by Prime. It would be nice to be able to ask Primos what these values are, but this is not possible. So it is up to the user to ask the system administrator if none of the other values are suitable. Also note that 110 baud will use 2 stop bits, but 75 baud will only use 1. This is because we cannot guarantee that JUMPER_1 is actually 75 baud! HOWEVER, at PRIMOS revision 22.1 it is possible to ask the computer what baud rates are supported on AMLC lines. It is also possible to set any of about 20 speeds for ICS lines. So Kermit version 8.14 now only checks on the validity of the speed, and lets Primos sort out whether the hardware actually supports it. The supported baud rates are not shown at all by Kermit, except for those computers using pre-rev 22 Primos when the default values of CLOCK, JUMPER_1, JUMPER_2, and JUMPER_3 will be shown. 7) The "SET TIMEOUT" command can only set the local send packet timeout, the receive packet timeout has to be set from the "other" Kermit program. The "SHOW TIMEOUT" command will show a value for the receive packet timeout, but this will be either an initial default value supplied by the local Kermit program or the last value received by Kermit from a file transfer. 8) The MS-DOS pound conversion facility may seem to switch from OFF to ON occasionally. This occurs because the conversion is turned ON or OFF depending on the information Kermit receives - either from the user or from the remote Kermit during a transfer. E.g. Setting the file type to binary will set it OFF, a SHOW command will verify this; if a file is now received from an MS-DOS machine (and the attributes packet is sent) then the conversion is set ON since Prime Kermit detects that it is coming from a DOS machine. Again a SHOW command will verify this. This is not harmful since the pound conversion is only actually performed when files are sent, not received. At that time the deciding factors are whether the conversion has been explicitly set by the user (either ON or OFF), or whether it is a binary file (setting it OFF). The Prime Kermit code has been written to assume that the pound conversion is always ON unless either of the two deciding factors above is true. So after sending a file (or receiving one from an MS-DOS machine), the pound conversion is set back to the default of ON; hence the conversion seems to switch from OFF to ON. 9) The acknowledgment received to the file name may have the file name encoded with repeat characters. E.g. the file "X0000001" may be acknowledged as "X~$01". This will be treated by Prime Kermit as a different file name and reported as such. The code for repeat character processing is somewhat long-winded, and so has not been included in the file name acknowledgment section. This should not give users any real problems since it is possible to still work out the correct file name. 10) Bug fix 44 in the PRIME.HLP file should be corrected by having a SET SERVER TIMEOUT n command. This will be done later. 11) Sliding windows do not work when the Prime is dialing into a C-Kermit or MS-Kermit machine. 12) The code for sizing ASCII files is at the moment inefficient, due to the first part of the file being scanned twice. The first scan determines the file type, and the second then actually sizes it if it is an ASCII file. This could be recoded to only parse the file once, and if a binary file then only the first part needs to be examined. 13) Some commands, e.g. SERVER, SEND, and RECEIVE, do not work from within a TAKE file. These commands expect to receive packets from the current 'input stream' which would normally be the 'other side' e.g. a PC, but TAKE files get their input from the file itself. ------------------------------ Date: 25 Oct 93 12:12:48-0400 From: ABDUL-JAMIL.KHAN@dialcom.tymnet.com Subject: Fix for Prime Kermit filename handling I have modified KERMIT module REC_SWITCH.PLP to handle filenames which have the '#' character. In other words I am properly handling the quoting and repeat processing. This means our DOS application can now send such files to PRIME. I have not done the converse change i.e. sending files to DOS; however, this is not so important for our application, but I will try and get this done to keep the code consistent. I am enclosing, below, the modified code for your use/comments. Regards, Jamil Khan ------------REC_SWITCH.PLP------- /* REC_SWITCH -- Handle Kermit file receive protocol. */ /* Modified by Jamil Khan, BT Development Oct 22 93 to handling quoting in filename packet */ Rec_switch : proc; $Insert *>insert>common.ins.plp $Insert *>insert>kermit.ins.plp $Insert *>insert>primos.ins.plp $Insert *>insert>constants.ins.plp $Insert *>insert>rev21keys.ins.plp $Insert syscom>errd.ins.pl1 Dcl (temp, i, fs_attr_type, rep_count, eof_rec_seq, pathlen) fixed bin, new_path char (128) var, chr char (1), (single_file_rec, test_flag, discard) bit (1) aligned; /* ************************************************************************* */ do_flush = true; discard = false; num_retries = 0; /* Initialize the number of retries. */ eof_rec_seq = -1; single_file_rec = (length (path_name) ^= 0); if packet_log_opened then do; if single_file_rec then errmsg = space_8bit_asc || path_name; else errmsg = ''; call log_info (packet_log, ''); call log_info (packet_log, kversion || ' receiving' || errmsg || '.'); end; do while (true); select (state); when (state_r) state = rec_init (); when (state_rf) state = rec_file (); when (state_ra) state = rec_attrib (); when (state_rdw) state = rec_windowing (); when (state_c) do; call sleep$ (3000); return; end; otherwise /* This includes state_a. */ do; do_flush = true; call discard_output (i); if i ^= 0 then do; call get_error_msg (i); snd_msg = 'Error trying to discard the output file. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); end; call sleep$ (3000); return; end; end; /* select */ end; /* do while ... */ /* ******************************** Rec_init ******************************* */ Rec_init : proc returns (fixed bin); /* ************************************************************************* */ msg_number = 0; /* Initialize sequence numbering. */ if ^rec_message () then /* Get a packet. */ return (state_a); if rec_pkt_type = msg_snd_init then do; call ack_send_init; num_retries = 0; msg_number = mod (msg_number + 1, 64); return (state_rf); /* Ready to receive file info. */ end; else do; call send_packet (msg_nak, 0, rec_seq); return (state_a); end; end; /* Rec_init */ /* ******************************* Rec_file ******************************** */ Rec_file : proc returns (fixed bin); /* ************************************************************************* */ if ^rec_message () then /* Get a packet. */ return (state_a); discard = false; /* Initialise these just in case. */ eof_rec_seq = -1; do i = 0 to 63; msg_table.slot(i).acked = false; msg_table.slot(i).retries = 0; end; select (rec_pkt_type); when (msg_file) do; if rec_seq ^= msg_number then do; snd_msg = 'Protocol error detected.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; if length (path_name) = 0 then /* Get pathname from the packet. */ do; if single_file_rec then do; snd_msg = 'Error : only ONE file upload allowed.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; path_name = substr (rec_msg, pkt_msg, length (rec_msg) - pkt_msg); path_name = trim (set8str (path_name), '11'b); /* The pathname may have repeat character processing in it, so we must handle this. 8-bit quoting and control quoting are not allowed in path names, and so will be caught later on. NOT TRUE, CONTROL QUOTING NEEDS TO BE HANDLED. KERMIT ON DOS QUOTES CONTROL QUOTE CHAR - JAMIL KHAN */ pathlen = length (path_name); if (do_repeats & index (path_name, loc_rep_chr) ^= 0) | (index (path_name, loc_quote_chr) ^=0) then do; new_path = ''; do i = 1 to pathlen; chr = substr (path_name, i, 1); if chr = loc_quote_chr then /* skip quote */ do; if (i < pathlen) then i = i + 1; chr = substr (path_name, i, 1); rep_count = 1; end; else if do_repeats & chr = loc_rep_chr then do; if (i < pathlen) then i = i + 1; rep_count = knum (substr (path_name, i, 1)); if (i < pathlen) then i = i + 1; chr = substr (path_name, i, 1); if chr = loc_quote_chr then /* skip */ do; if (i < pathlen) then i = i + 1; chr = substr (path_name, i, 1); end; end; else rep_count = 1; do temp = 1 to rep_count; new_path = new_path || chr; end; end; path_name = new_path; call log_info(packet_log,'Unquoted Path '||new_path); end; call set_path (path_name); end; i = open_output (); /* Open the file for writing. */ select (i); when (0) snd_msg = ''; when (e$exst) do; /* Acknowldege with our new file name. */ snd_msg = file_name; call log_info (packet_log, 'File already exists. New file name is ' || file_name || '.'); end; when (e$bnam) do; snd_msg = file_name; call log_info (packet_log, 'The file name is illegal, ' || file_name || ' will be used instead.'); end; when (e$ialn) do; snd_msg = 'File already exists. Unable to generate a new file name!'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; otherwise do; call get_error_msg (i); snd_msg = 'Error opening file on remote system. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; end; if explicit_ft_set then do; rec_file_type = file_type; if packet_log_opened then do; errmsg = 'The receiving file type has been explicitly set to '; select (file_type); when (ascii_ft) errmsg = errmsg || 'ASCII.'; when (binary_ft) errmsg = errmsg || 'BINARY.'; when (automatic_ft) /* ? - This can't be! */ errmsg = errmsg || 'AUTOMATIC.'; otherwise /* And what's this ? */ errmsg = errmsg || 'ILLEGAL.'; end; call log_info (packet_log, (errmsg)); end; end; else do; rec_file_type = automatic_ft; file_type = ascii_ft; /* Assume this to start with. */ if packet_log_opened then do; call log_info (packet_log, 'The receiving file type will be automatically detected.'); call log_info (packet_log, 'But ASCII file type will initially be assumed.'); end; end; /* Acknowledge the file header packet. */ num_retries = 0; do_flush = false; msg_number = mod (msg_number + 1, 64); call send_packet (msg_ack, length (snd_msg), rec_seq); if loc_file_attrib then /* Get the file attributes if we can. */ return (state_ra); else do; tab_first = msg_number; return (state_rdw); end; end; when (msg_eof, msg_snd_init) if rec_seq = mod (msg_number - 1, 64) then do; if bump_retry () then if rec_pkt_type = msg_eof then call send_packet (msg_ack, 0, rec_seq); else call ack_send_init; return (state); end; else do; snd_msg = 'Protocol error detected.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; when (msg_break) do; call send_packet (msg_ack, 0, rec_seq); return (state_c); end; when (msg_error) return (state_a); otherwise do; snd_msg = 'Unexpected packet type "' || rec_pkt_type || '" received on remote system.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; end; /* Select */ end; /* Rec_file */ /* ****************************** Rec_attrib ******************************* */ Rec_attrib : proc returns (fixed bin); Dcl avail_disk_space fixed bin (31), code fixed bin, 1 quota_info, 2 (record_size, dir_used, max_quota, quota_used) fixed bin (31), 2 (duff1, duff2, duff3, duff4) fixed bin (31), inf_array (8) fixed bin (31) based; /* ************************************************************************* */ if ^rec_message () then /* Get a packet. */ return (state_a); select (rec_pkt_type); when (msg_attrib) do; call q$read (dir_name, addr (quota_info) -> inf_array, 4, temp, code); if code ^= 0 | temp = 1 then avail_disk_space = -1; else do; avail_disk_space = quota_info.max_quota - quota_info.quota_used; if quota_info.record_size ^= 1024 then avail_disk_space = divide ((avail_disk_space * quota_info.record_size) + 1023, 1024, 31); end; call decode_attrs; if avail_disk_space = -1 | rec_file_size <= 0 | rec_file_size <= avail_disk_space then snd_msg = 'Y'; else /* ONLY reject the file if we run out of room. */ do; call discard_output (temp); if fs_attr_type = 0 then snd_msg = 'N!'; else snd_msg = 'N1'; end; if rec_file_dtc = 0 then snd_msg = snd_msg || '#'; if file_type = illegal_ft then do; rec_file_type = automatic_ft; file_type = ascii_ft; /* Reset this, but let the */ snd_msg = snd_msg || '"'; /* other side know. */ end; num_retries = 0; msg_number = mod (msg_number + 1, 64); call send_packet (msg_ack, length (snd_msg), rec_seq); if substr (snd_msg, 1, 1) = 'N' then call log_info (packet_log, 'Unable to receive the file ' || file_name || '. File too big.'); return (state); end; when (msg_data) do; if rec_seq ^= msg_number then /* Out of sequence messages. */ if rec_seq = mod (msg_number - 1, 64) then do; if bump_retry () then call send_packet (msg_ack, 0, rec_seq); return (state); end; else do; snd_msg = 'Protocol error detected.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; temp = write_output (); if temp ^= 0 then do; call get_error_msg (temp); snd_msg = 'Unable to write to output file. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; num_retries = 0; msg_number = mod (msg_number + 1, 64); call send_packet (msg_ack, 0, rec_seq); tab_first = msg_number; return (state_rdw); end; when (msg_file) if rec_seq = mod (msg_number - 1, 64) then do; if bump_retry () then call send_packet (msg_ack, 0, rec_seq); return (state); end; else do; snd_msg = 'Protocol error detected.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; when (msg_eof) if rec_seq = msg_number then do; i = close_output (); call set_path (''); /* Knock out the file_name for later. */ if i ^= 0 then do; call get_error_msg (i); snd_msg = 'Unable to close output file on remote system. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; num_retries = 0; msg_number = mod (msg_number + 1, 64); call send_packet (msg_ack, 0, rec_seq); return (state_rf); end; else do; snd_msg = 'Protocol error detected.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; when (msg_error) return (state_a); otherwise do; snd_msg = 'Unexpected packet type "' || rec_pkt_type || '" received on remote system.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; end; /* select */ end; /* Rec_attrib */ /* ***************************** Rec_windowing ***************************** */ Rec_windowing : proc returns (fixed bin); /* ************************************************************************* */ call rec_packet; /* Get input. */ select (rec_pkt_type); when (msg_data) do; call update_table; if tab_first = eof_rec_seq then do; rec_seq = eof_rec_seq; goto eof; end; else return (state); end; when (msg_eof) do; eof_rec_seq = rec_seq; if length (rec_msg) > pkt_msg then rec_msg = substr (rec_msg, pkt_msg, 1); else rec_msg = ''; discard = (rec_msg = 'D'); if discard then call discard_output (i); else do; if tab_first ^= eof_rec_seq then do; call nak_all; return (state); end; Eof : i = close_output (); end; do_flush = true; /* Okay, we can do this now, */ call set_path (''); /* and do this for later. */ if i ^= 0 then do; call get_error_msg (i); if discard then snd_msg = 'Unable to discard the output file on remote system. ' || errmsg; else snd_msg = 'Unable to close output file on remote system. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; num_retries = 0; msg_number = mod (rec_seq + 1, 64); call send_packet (msg_ack, 0, rec_seq); return (state_rf); end; when (msg_error) return (state_a); when (msg_timeout) do; if bump_retry () then do; num_retries = num_retries - 1; /* Don't increase this. */ call log_info (packet_log, 'Timeout : NAK for most desired packet.'); call nak_oldest (true); end; return (state); end; when (msg_check_err) do; if bump_retry () then do; num_retries = num_retries - 1; /* Don't increase this. */ call log_info (packet_log, 'Checksum error : NAK for oldest unACKed packet.'); call nak_oldest (false); end; return (state); end; otherwise do; snd_msg = 'Unexpected packet type "' || rec_pkt_type || '" received on remote system.'; call send_packet (msg_error, length (snd_msg), msg_number); return (state_a); end; end; /* Select */ end; /* Rec_windowing */ /* ****************************** Rec_message ****************************** */ Rec_message : proc returns (bit (1) aligned); /* ************************************************************************* */ test_flag = false; do until (test_flag); call rec_packet; if rec_pkt_type = msg_timeout | rec_pkt_type = msg_check_err then if bump_retry () then call send_packet (msg_nak, 0, msg_number); else return (false); else test_flag = true; end; return (true); end; /* Rec_message */ /* ***************************** Update_table ****************************** */ Update_table : proc; /* ************************************************************************* */ if ^between (rec_seq, tab_first, mod (tab_first + window_size - 1, 64)) then do; if between (rec_seq, mod (tab_first - window_size, 64), mod (tab_first - 1, 64)) then call send_packet (msg_ack, 0, rec_seq); return; end; /* Add the new data packet to the table. */ if rec_seq ^= eof_rec_seq then /* Don't mark the EOF packet as ACKed. */ do; msg_table.slot(rec_seq).msg = rec_msg; msg_table.slot(rec_seq).acked = true; end; if msg_table.slot(tab_first).acked then do; i = tab_first; do until (^msg_table.slot(i).acked); rec_msg = msg_table.slot(i).msg; temp = write_output (); if temp ^= 0 then do; call get_error_msg (temp); snd_msg = 'Unable to write to output file. ' || errmsg; call send_packet (msg_error, length (snd_msg), msg_number); state = state_a; return; end; else msg_table.slot(i).acked = false; i = mod (i + 1, 64); end; tab_first = i; end; num_retries = 0; msg_number = mod (rec_seq + 1, 64); call send_packet (msg_ack, 0, rec_seq); /* Acknowledge the packet. */ return; end; /* Update_table */ /* ****************************** Nak_oldest ******************************* */ Nak_oldest : proc (desire); Dcl desire bit (1) aligned; /* ************************************************************************* */ i = tab_first; temp = mod (tab_first + window_size, 64); do until (i = temp); if ^msg_table.slot(i).acked then do; call send_packet (msg_nak, 0, i); return; end; i = mod (i + 1, 64); end; /* No packets to NAK, so NAK for next in hope of unblocking sender if a NAK for the most desired packet is required. */ if desire then call send_packet (msg_nak, 0, temp); return; end; /* Nak_oldest */ /* ******************************* Nak_all ********************************* */ Nak_all : proc; /* ************************************************************************* */ i = tab_first; do until (i = eof_rec_seq); if ^msg_table.slot(i).acked then call send_packet (msg_nak, 0, i); i = mod (i + 1, 64); end; return; end; /* Nak_all */ /* ******************************* Bump_retry ****************************** */ Bump_retry : proc returns (bit (1) aligned); /* ************************************************************************* */ if num_retries > max_retries then do; snd_msg = 'Retry limit exceeded on remote system.'; call send_packet (msg_error, length (snd_msg), msg_number); state = state_a; return (false); end; num_retries = num_retries + 1; return (true); end; /* Bump_retry */ /* ****************************** Decode_attrs ***************************** */ Decode_attrs : proc; Dcl (str, data) char (max_msg) var, attr char (1), (len, found, code) fixed bin; /* ************************************************************************* */ rec_file_size = -1; /* -1 = Unknown, 0 = Illegal, > 0 = Legal value. */ rec_file_dtc = -1; found = 0; str = substr (rec_msg, pkt_msg, length (rec_msg) - pkt_msg); str = set8str (str); do while (length (str) > 0 & found < 5); attr = substr (str, 1, 1); len = knum (substr (str, 2, 1)); data = substr (str, 3, len); str = substr (str, len + 3); select (attr); when ('!') /* File size in Kbytes. */ do; fs_attr_type = 0; rec_file_size = bin (trim (data, '11'b), 31); end; when ('1') /* File size in bytes. */ do; fs_attr_type = 1; rec_file_size = bin (trim (data, '11'b), 31); rec_file_size = divide (rec_file_size + 1023, 1024, 31); end; when ('#') /* Date/Time file created (DTC). */ do; if substr (data, 1, 2) = '19' then data = substr (data, 3); /* Knock off the century. */ data = substr (data, 1, 2) || '-' || substr (data, 3, 2) || '-' || substr (data, 5, 2) || '.' || after (data, space_8bit_asc); call cv$dtb (data, rec_file_dtc, code); if code ^= 0 then rec_file_dtc = 0; end; when ('.') /* Machine and OS. */ if ^explicit_pound_set & (data = 'U8' | substr (data, 1, 1) = 'K') then pound_conversion = true; /* U8 = MS-DOS, K = Atari. */ when ('"') /* Indication of file type. */ if ^explicit_ft_set then /* Might as well use this if we can. */ do; select (substr (data, 1, 1)); when ('A') do; rec_file_type = ascii_ft; /* ASCII file. */ call log_info (packet_log, 'The received file type attribute is ASCII, this file type will be used.'); end; when ('B') do; rec_file_type = binary_ft; /* BINARY file. */ call log_info (packet_log, 'The received file type attribute is BINARY, this file type will be used.'); end; when ('I') do; rec_file_type = binary_ft; /* IMAGE file (BINARY). */ call log_info (packet_log, 'The received file type attribute is IMAGE, but BINARY file type will be used.' ); end; otherwise do; rec_file_type = illegal_ft; /* ILLEGAL file type. */ call log_info (packet_log, 'The received file type attribute is ILLEGAL.'); call log_info (packet_log, 'The file type will be ' || 'automatically detected, but ASCII will initially be used.'); end; end; file_type = rec_file_type; end; otherwise found = found - 1; /* Didn't find one we wanted. */ end; found = found + 1; /* Assume that we did find one. */ end; return; end; /* Decode_attrs */ end; /* Rec_switch */ ------------END OF CODE-----------