MODULE KERSYS (IDENT = '3.3.113', ADDRESSING_MODE(EXTERNAL = GENERAL, NONEXTERNAL = GENERAL) ) = BEGIN !++ ! FACILITY: ! KERMIT-32 ! ! ABSTRACT: ! KERMIT-32 is an implementation of the KERMIT protocal to allow the ! transfer of files from micro computers to the DECsystem-10, DECSYSTEM-20 ! and now the VAX/VMS systems. ! ! ENVIRONMENT: ! User mode ! ! AUTHOR: Robert C. McQueen, Nick Bush, CREATION DATE: 24-January-1983 ! ! MODIFIED BY: ! !-- %SBTTL 'Table of Contents' %SBTTL 'Revision History' !++ ! ! 2.0.032 By: Nick Bush On: 25-Feb-1984 ! Add code for LOCAL and REMOTE commands. These depend ! upon support in KERMSG and KERSYS. ! ! 3.0.045 Start of version 3. ! ! 3.1.057 By: Nick Bush On: 21-Feb-1985 ! Determine VMS version on startup and remember for later ! use. Use it in KERSYS to determine whether we will need ! to force an end-of-file on the mailbox when the subprocess ! on the other end goes away. ! ! 3.1.064 By: Nick Bush On: 30-March-1985 ! Fix LIB$SPAWN call to set SYS$INPUT for the subprocess ! to be NLA0: so that it doesn't try to input from the ! terminal. ! ! 3.1.066 By: Nick Bush On: 22-April-1985 ! Don't use NLA0: as SYS$INPUT when spawning things under VMS 3. ! ! Start of version 3.3 ! ! 3.3.101 By: Robert McQueen On: 2-July-1986 ! Change from $TRNLOG system service calls to LIB$SYS_TRNLOG ! library routine calls. ! ! 3.3.108 By: Antonino N. Mione On: 8-Sep-1986 ! Make KERMIT-32 close the terminal (so the terminal ! parameters are appropriately reset) upon reciept of ! a GENERIC LOGOUT packet. ! ! 3.3.113 JHW0002 Jonathan Welch, 5-May-1988 11:48 ! Modified SY_TIME to use $GETTIM as opposed to the LIB$timer ! routines (which broke when their method of calculating ! time differences changed in V4.4?). ! ! Removed the call to LIB$INIT_TIMER in SY_INIT. !-- %SBTTL 'Include files' ! ! INCLUDE FILES: ! LIBRARY 'SYS$LIBRARY:STARLET'; LIBRARY 'SYS$LIBRARY:TPAMAC'; REQUIRE 'KERCOM'; ! Common definitions REQUIRE 'KERERR'; ! Error message symbol definitions %SBTTL 'Storage -- Local' ! ! OWN STORAGE: ! OWN VMS_VERSION, ! Major version number of VMS ORG_DEFAULT_DIR_TEXT : VECTOR [MAX_FILE_NAME, BYTE], ! Text of default dir ORG_DEFAULT_DIR : BLOCK [8, BYTE], ! Original default directory ORG_DEFAULT_DEV_TEXT : VECTOR [MAX_FILE_NAME, BYTE], ! Text of default device ORG_DEFAULT_DEV : BLOCK [8, BYTE], ! Descriptor for orginal default device Subtrahend : VECTOR [2, LONG]; ! Constant to subtract from system time. ! %SBTTL 'External routines' ! ! EXTERNAL REFERENCES: ! EXTERNAL ROUTINE ! ! Library routines ! LIB$EDIV : ADDRESSING_MODE (GENERAL), LIB$SET_LOGICAL : ADDRESSING_MODE (GENERAL), LIB$SIGNAL : ADDRESSING_MODE (GENERAL) NOVALUE, LIB$SPAWN : ADDRESSING_MODE (GENERAL), LIB$SUBX : ADDRESSING_MODE (GENERAL), OTS$CVT_L_TZ : ADDRESSING_MODE (GENERAL) NOVALUE, SYS$SETDDIR : ADDRESSING_MODE (GENERAL), SYS$GETTIM : ADDRESSING_MODE (GENERAL), ! ! KERTRM - Terminal handling routines ! TERM_CLOSE, ! Close terminal and restore characteristics ! ! KERTT - Text processing ! TT_INIT : NOVALUE, ! Initialization routine TT_TEXT : NOVALUE, ! Output a text string TT_NUMBER : NOVALUE, ! Output a number TT_CHAR : NOVALUE, ! Output a single character TT_OUTPUT : NOVALUE, ! Routine to dump the current ! text line. TT_CRLF : NOVALUE; ! Output the line %SBTTL 'External storage' ! ! EXTERNAL Storage: ! EXTERNAL ! ! KERMSG storage ! GEN_1DATA : VECTOR [CH$ALLOCATION (MAX_MSG)], ! Data for generic command GEN_1SIZE, ! Size of data in GEN_1DATA GEN_2DATA : VECTOR [CH$ALLOCATION (MAX_MSG)], ! Second argument for generic command GEN_2SIZE, ! Size of data in GEN_2DATA GEN_3DATA : VECTOR [CH$ALLOCATION (MAX_MSG)], ! Third arg for generic command GEN_3SIZE, ! Size of data in GEN_3DATA ! ! Misc constants. ! FILE_SIZE, ! Number of characters in FILE_NAME FILE_NAME : VECTOR [CH$ALLOCATION (MAX_FILE_NAME)]; %SBTTL 'SY_INIT - Initialize KERSYS' GLOBAL ROUTINE SY_INIT : NOVALUE = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine will initialize the module KERSYS. ! ! CALLING SEQUENCE: ! ! SY_INIT (); ! ! INPUT PARAMETERS: ! ! None. ! ! IMPLICIT INPUTS: ! ! None. ! ! OUPTUT PARAMETERS: ! ! None. ! ! IMPLICIT OUTPUTS: ! ! None. ! ! COMPLETION CODES: ! ! None. ! ! SIDE EFFECTS: ! ! KERSYS storage initialized ! !-- BEGIN LITERAL SYI_EFN = 10; ! EFN to use for $GETSYI LOCAL VERSION_STRING : VECTOR [8, BYTE], ! Return version string here VERSION_LENGTH, ! And length here SYI_ITEM_LIST : BLOCK [16, BYTE], ! Argument list for $GETSYI LENGTH, ! Length of default dir returned STATUS; EXTERNAL ROUTINE LIB$SYS_TRNLOG : ADDRESSING_MODE(GENERAL); ! ! Set up arg list for $GETSYI ! SYI_ITEM_LIST [0, 0, 16, 0] = 8; ! We expect an 8-byte string SYI_ITEM_LIST [2, 0, 16, 0] = SYI$_VERSION; ! Want the VMS version SYI_ITEM_LIST [4, 0, 32, 0] = VERSION_STRING; ! Put it here SYI_ITEM_LIST [8, 0, 32, 0] = VERSION_LENGTH; ! Length goes here SYI_ITEM_LIST [12, 0, 32, 0] = 0; ! End the list STATUS = $GETSYI (EFN=SYI_EFN, ITMLST=SYI_ITEM_LIST); ! Get the data IF NOT .STATUS ! If we can't get the version THEN VMS_VERSION = 0 ! Assume very old VMS? ELSE BEGIN STATUS = $WAITFR (EFN=SYI_EFN); ! Wait for completion IF .STATUS ! If we got it THEN BEGIN IF .VERSION_STRING [0] GEQ %C'0' AND .VERSION_STRING [0] LEQ %C'9' ! If first character is numeric THEN VMS_VERSION = (.VERSION_STRING[0] - %C'0')*10 ! save first digit ELSE VMS_VERSION = 0; ! No first digit, store 0 VMS_VERSION = .VMS_VERSION + .VERSION_STRING[1] - %C'0' ! Get rest of version END ELSE VMS_VERSION = 0; ! Can't get version? END; ! ! Set up original default directory ! ORG_DEFAULT_DIR [DSC$B_CLASS] = DSC$K_CLASS_S; ORG_DEFAULT_DIR [DSC$B_DTYPE] = DSC$K_DTYPE_T; ORG_DEFAULT_DIR [DSC$W_LENGTH] = MAX_FILE_NAME; ORG_DEFAULT_DIR [DSC$A_POINTER] = ORG_DEFAULT_DIR_TEXT; STATUS = SYS$SETDDIR (0, LENGTH, ORG_DEFAULT_DIR); IF .STATUS THEN ORG_DEFAULT_DIR [DSC$W_LENGTH] = .LENGTH ELSE ORG_DEFAULT_DIR [DSC$W_LENGTH] = 0; ! ! Get original default device ! ORG_DEFAULT_DEV [DSC$B_CLASS] = DSC$K_CLASS_S; ORG_DEFAULT_DEV [DSC$B_DTYPE] = DSC$K_DTYPE_T; ORG_DEFAULT_DEV [DSC$W_LENGTH] = MAX_FILE_NAME; ORG_DEFAULT_DEV [DSC$A_POINTER] = ORG_DEFAULT_DEV_TEXT; STATUS = LIB$SYS_TRNLOG (%ASCID'SYS$DISK', LENGTH, ORG_DEFAULT_DEV); IF .STATUS EQL SS$_NOTRAN ! No translation? THEN LENGTH = 0; ! Yes, set the length to zero IF .STATUS THEN ORG_DEFAULT_DEV [DSC$W_LENGTH] = .LENGTH ELSE ORG_DEFAULT_DEV [DSC$W_LENGTH] = 0; END; ! End of SY_INIT %SBTTL 'SY_LOGOUT - delete the process.' GLOBAL ROUTINE SY_LOGOUT : NOVALUE = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine will delete this process. ! ! CALLING SEQUENCE: ! ! SY_LOGOUT (); ! ! INPUT PARAMETERS: ! ! None. ! ! IMPLICIT INPUTS: ! ! None. ! ! OUTPUT PARAMETERS: ! ! None. ! ! IMPLICIT OUTPUTS: ! ! None. ! ! COMPLETION CODES: ! ! None. ! ! SIDE EFFECTS: ! ! None. ! !-- BEGIN TERM_CLOSE(); ![108] Close the terminal early $DELPRC (); END; ! End of SY_LOGOUT %SBTTL 'SY_GENERIC - Perform a generic command' GLOBAL ROUTINE SY_GENERIC (GCMD_TYPE, STRING_ADDRESS, STRING_LENGTH, GET_CHR_RTN) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine will perform a generic command. ! ! CALLING SEQUENCE: ! ! SY_GENERIC (GCMD_TYPE, STRING_ADDRESS, STRING_LENGTH, GET_CHR_RTN); ! ! INPUT PARAMETERS: ! ! GCMD_TYPE - GC_xxx value for command to be performed ! STRING_ADDRESS - Place to return address of string result ! STRING_LENGTH - Place to return length of string result ! GET_CHR_RTN - Place to return address of a get a character routine ! ! IMPLICIT INPUTS: ! ! None. ! ! OUTPUT PARAMETERS: ! ! Returns KER_xxx status ! ! IMPLICIT OUTPUTS: ! ! None. ! ! COMPLETION CODES: ! ! None. ! ! SIDE EFFECTS: ! ! None. ! !-- BEGIN LITERAL MAX_CMD_LEN = 2*MAX_MSG, ! Max command length MAX_MBX_LEN = 20; ! Max mailbox name length OWN RSP_TEXT : VECTOR [MAX_CMD_LEN, BYTE], ! Return text RSP_LEN; ! Length of return text LOCAL STATUS, ! Status results FLAGS, ! Flag word for LIB$SPAWN OUR_PID, ! Our PID value ITMLST : VECTOR [4, LONG], ! GETJPI argument POINTER, ! Character pointer MBX_CHAN, ! Channel for mail box COMMAND_LENGTH, ! Length of command string COMMAND_DESC : BLOCK [8, BYTE], ! Descriptor for command string COMMAND_STR : VECTOR [MAX_CMD_LEN, BYTE], ! Actual command string MBX_DESC : BLOCK [8, BYTE], ! Mailbox equivalence name MBX_NAME : VECTOR [MAX_MBX_LEN, BYTE]; ! Storage for MBX name ROUTINE PROCESS_COMPLETION_AST (MBX_CHAN) = ! ! This routine is called upon process completion (of the process we spawned ! to perform the command). It will ensure that the mailbox gets an end-of-file. ! BEGIN RETURN $QIO (CHAN = .MBX_CHAN, FUNC = IO$_WRITEOF); ! Write the EOF END; ROUTINE CONCAT (SRC_ADR, SRC_LEN, DST_PTR, DST_LEN) : NOVALUE = ! ! This routine is called to concatenate a string onto the current string ! BEGIN LOCAL LENGTH; ! Length we will actually move LENGTH = .SRC_LEN; ! Get total length IF .LENGTH GTR MAX_CMD_LEN - ..DST_LEN THEN LENGTH = MAX_CMD_LEN - ..DST_LEN; CH$MOVE (.LENGTH, CH$PTR (.SRC_ADR), ..DST_PTR); .DST_PTR = CH$PLUS (.LENGTH, ..DST_PTR); .DST_LEN = ..DST_LEN + .LENGTH; ! Update length END; ! ! Initialize the command descriptor ! COMMAND_DESC [DSC$B_CLASS] = DSC$K_CLASS_S; COMMAND_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T; COMMAND_LENGTH = 0; ! Nothing here yet COMMAND_DESC [DSC$A_POINTER] = COMMAND_STR; ! Point at string storage POINTER = CH$PTR (COMMAND_STR); ! ! Determine what to do with the command ! CASE .GCMD_TYPE FROM GC_MIN TO GC_MAX OF SET [GC_COPY] : BEGIN EXTERNAL GEN_COPY_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_COPY_CMD [DSC$A_POINTER], .GEN_COPY_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); CONCAT (UPLIT (%ASCII' '), 1, POINTER, COMMAND_LENGTH); CONCAT (GEN_2DATA, .GEN_2SIZE, POINTER, COMMAND_LENGTH); END; [GC_CONNECT] : BEGIN LOCAL LENGTH, DIR_FAB : $FAB_DECL, ! FAB for $PARSE DIR_NAM : $NAM_DECL, ! NAM for $PARSE EXP_STR : VECTOR [NAM$C_MAXRSS, BYTE], ! Expanded file spec DEV_DESC : BLOCK [8, BYTE], ! Descriptor for device name DIR_DESC : BLOCK [8, BYTE]; DIR_DESC [DSC$B_CLASS] = DSC$K_CLASS_S; DIR_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T; DEV_DESC [DSC$B_CLASS] = DSC$K_CLASS_S; DEV_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T; IF .GEN_1SIZE GTR 0 THEN BEGIN $FAB_INIT (FAB = DIR_FAB, FOP = NAM, NAM = DIR_NAM, FNA = GEN_1DATA, FNS = .GEN_1SIZE); $NAM_INIT (NAM = DIR_NAM, ESA = EXP_STR, ESS = NAM$C_MAXRSS); STATUS = $PARSE (FAB = DIR_FAB); IF NOT .STATUS THEN BEGIN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; IF .DIR_NAM [NAM$B_NODE] GTR 0 THEN BEGIN DEV_DESC [DSC$A_POINTER] = .DIR_NAM [NAM$L_NODE]; DEV_DESC [DSC$W_LENGTH] = .DIR_NAM [NAM$B_NODE] + .DIR_NAM [NAM$B_DEV]; END ELSE BEGIN DEV_DESC [DSC$W_LENGTH] = .DIR_NAM [NAM$B_DEV]; DEV_DESC [DSC$A_POINTER] = .DIR_NAM [NAM$L_DEV]; END; DIR_DESC [DSC$W_LENGTH] = .DIR_NAM [NAM$B_DIR]; DIR_DESC [DSC$A_POINTER] = .DIR_NAM [NAM$L_DIR]; END ELSE BEGIN DIR_DESC [DSC$W_LENGTH] = .ORG_DEFAULT_DIR [DSC$W_LENGTH]; DIR_DESC [DSC$A_POINTER] = .ORG_DEFAULT_DIR [DSC$A_POINTER]; DEV_DESC [DSC$W_LENGTH] = .ORG_DEFAULT_DEV [DSC$W_LENGTH]; DEV_DESC [DSC$A_POINTER] = .ORG_DEFAULT_DEV [DSC$A_POINTER]; END; STATUS = LIB$SET_LOGICAL (%ASCID'SYS$DISK', DEV_DESC); IF NOT .STATUS THEN BEGIN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; STATUS = SYS$SETDDIR (DIR_DESC, 0, 0); IF NOT .STATUS THEN BEGIN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; DIR_DESC [DSC$A_POINTER] = GEN_1DATA; DIR_DESC [DSC$W_LENGTH] = MAX_MSG; STATUS = SYS$SETDDIR (0, DIR_DESC [DSC$W_LENGTH], DIR_DESC); IF NOT .STATUS THEN BEGIN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; POINTER = CH$PTR (RSP_TEXT); RSP_LEN = 0; CONCAT (UPLIT (%ASCII'Default directory set to '), 25, POINTER, RSP_LEN); CONCAT (.DEV_DESC [DSC$A_POINTER], .DEV_DESC [DSC$W_LENGTH], POINTER, RSP_LEN); CONCAT (.DIR_DESC [DSC$A_POINTER], .DIR_DESC [DSC$W_LENGTH], POINTER, RSP_LEN); .STRING_ADDRESS = RSP_TEXT; .STRING_LENGTH = .RSP_LEN; RETURN KER_NORMAL; END; [GC_DELETE] : BEGIN EXTERNAL GEN_DELETE_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_DELETE_CMD [DSC$A_POINTER], .GEN_DELETE_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); END; [GC_DIRECTORY] : BEGIN EXTERNAL GEN_DIR_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_DIR_CMD [DSC$A_POINTER], .GEN_DIR_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); END; [GC_DISK_USAGE] : BEGIN EXTERNAL GEN_USG_CMD : BLOCK [8, BYTE], ! Command without arg GEN_USG_ARG_CMD : BLOCK [8, BYTE]; ! Command with arg IF .GEN_1SIZE LEQ 0 THEN BEGIN CONCAT (.GEN_USG_CMD [DSC$A_POINTER], .GEN_USG_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); END ELSE BEGIN CONCAT (.GEN_USG_ARG_CMD [DSC$A_POINTER], .GEN_USG_ARG_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); END; END; [GC_HELP] : BEGIN EXTERNAL GEN_HELP_TEXT : BLOCK [8, BYTE]; .STRING_ADDRESS = .GEN_HELP_TEXT [DSC$A_POINTER]; .STRING_LENGTH = .GEN_HELP_TEXT [DSC$W_LENGTH]; RETURN KER_NORMAL; END; [GC_RENAME] : BEGIN EXTERNAL GEN_REN_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_REN_CMD [DSC$A_POINTER], .GEN_REN_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); CONCAT (UPLIT (%ASCII' '), 1, POINTER, COMMAND_LENGTH); CONCAT (GEN_2DATA, .GEN_2SIZE, POINTER, COMMAND_LENGTH); END; [GC_SEND_MSG] : BEGIN EXTERNAL GEN_SEND_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_SEND_CMD [DSC$A_POINTER], .GEN_SEND_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); CONCAT (UPLIT (%ASCII' "'), 2, POINTER, COMMAND_LENGTH); CONCAT (GEN_2DATA, .GEN_2SIZE, POINTER, COMMAND_LENGTH); CONCAT (UPLIT (%ASCII'"'), 1, POINTER, COMMAND_LENGTH); END; [GC_TYPE] : ! ! While KERMSG handles this for server requests, COMND_LOCAL in KERMIT does ! not. Therefore, set up the request to open the correct file. ! BEGIN CH$COPY (.GEN_1SIZE, GEN_1DATA, CHR_NUL, MAX_FILE_NAME, FILE_NAME); FILE_SIZE = .GEN_1SIZE; RETURN KER_NORMAL; END; [GC_WHO] : BEGIN EXTERNAL GEN_WHO_CMD : BLOCK [8, BYTE]; CONCAT (.GEN_WHO_CMD [DSC$A_POINTER], .GEN_WHO_CMD [DSC$W_LENGTH], POINTER, COMMAND_LENGTH); CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); CONCAT (GEN_2DATA, .GEN_2SIZE, POINTER, COMMAND_LENGTH); END; [GC_COMMAND] : ! Host command. Just pass it to the process CONCAT (GEN_1DATA, .GEN_1SIZE, POINTER, COMMAND_LENGTH); [INRANGE, OUTRANGE] : BEGIN LIB$SIGNAL (KER_UNIMPLGEN); RETURN KER_UNIMPLGEN; ! We don't do any END; TES; ! ! If we fall out of the case statement, we need to create a mailbox and ! spawn a process to perform the command with its output going to the ! mailbox ! COMMAND_DESC [DSC$W_LENGTH] = .COMMAND_LENGTH; ! Copy command length ITMLST [0] = JPI$_PID^16 + 4; ! Get PID ITMLST [1] = OUR_PID; ! Into OUR_PID ITMLST [2] = ITMLST [2]; ! Get length here ITMLST [3] = 0; ! End of list $GETJPI (ITMLST = ITMLST); ! Get info for us CH$COPY (11, CH$PTR (UPLIT (%ASCII'KERMIT$MBX_')), CHR_NUL, ! Build name MAX_MBX_LEN, CH$PTR (MBX_NAME)); ! for mailbox MBX_DESC [DSC$B_CLASS] = DSC$K_CLASS_S; MBX_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T; MBX_DESC [DSC$W_LENGTH] = MAX_MBX_LEN - 12; ! MBX name length MBX_DESC [DSC$A_POINTER] = MBX_NAME + 11; ! Where to build rest of name OTS$CVT_L_TZ (OUR_PID, MBX_DESC, MAX_MBX_LEN - 12); ! Generate rest of name MBX_DESC [DSC$W_LENGTH] = MAX_MBX_LEN - 1; ! Set total length for create MBX_DESC [DSC$A_POINTER] = MBX_NAME; ! Point at start of name STATUS = $CREMBX (CHAN = MBX_CHAN, LOGNAM = MBX_DESC); IF NOT .STATUS THEN BEGIN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; MBX_NAME [MAX_MBX_LEN - 1] = %C':'; ! Terminate with colon MBX_DESC [DSC$W_LENGTH] = MAX_MBX_LEN; ! Set total length including colon CH$COPY (MAX_MBX_LEN - 1, CH$PTR (MBX_NAME), CHR_NUL, MAX_FILE_NAME, CH$PTR (FILE_NAME)); FILE_SIZE = MAX_MBX_LEN - 1; ! Set up FILE_NAME FLAGS = 1; ! Don't wait for process STATUS = LIB$SPAWN ( ! Spawn a DCL subprocess COMMAND_DESC, ! to do this command (IF .VMS_VERSION LEQ 3 ! If old VMS THEN 0 ! Then no SYS$INPUT arg ELSE %ASCID'NLA0:'), ! no SYS$INPUT MBX_DESC, ! set SYS$OUTPUT to mailbox FLAGS, ! don't wait for process to complete 0, ! Process name 0, ! process id 0, ! completion status 0, ! ? (IF .VMS_VERSION LEQ 3 ! If VMS 3 or earlier THEN PROCESS_COMPLETION_AST ! We need to force eof ELSE ! when process finishes 0), ! 4.0 and on we get one free .MBX_CHAN); ! feed ast routine this value IF NOT .STATUS THEN LIB$SIGNAL (.STATUS); RETURN .STATUS; END; ! End of SY_GENERIC %SBTTL 'SY_DISMISS - Sleep for N seconds' GLOBAL ROUTINE SY_DISMISS (SECONDS) : NOVALUE = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine is called to cause KERMIT to sleep for the ! specified number of seconds. ! ! CALLING SEQUENCE: ! ! SY_DISMISS(Number of seconds); ! ! INPUT PARAMETERS: ! ! Number of seconds to sleep. ! ! IMPLICIT INPUTS: ! ! None. ! ! OUTPUT PARAMETERS: ! ! None. ! ! IMPLICIT OUTPUTS: ! ! None. ! ! COMPLETION CODES: ! ! None. ! ! SIDE EFFECTS: ! ! None. ! !-- BEGIN LOCAL STATUS, TOTAL_TIME : VECTOR [2, LONG]; ! Quad word for length of time to sleep IF .SECONDS EQL 0 THEN RETURN KER_NORMAL; TOTAL_TIME [0] = -.SECONDS*10*1000*1000; TOTAL_TIME [1] = -1; STATUS = $SETIMR (EFN = 1, DAYTIM = TOTAL_TIME); IF NOT .STATUS THEN LIB$SIGNAL (.STATUS); STATUS = $WAITFR (EFN = 1); IF NOT .STATUS THEN LIB$SIGNAL (.STATUS); END; ! End of SY_DISMISS(time) %SBTTL 'SY_TIME - Return abbreviated system time' GLOBAL ROUTINE SY_TIME = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine will return the system time to the calling routine. ! This will allow for the calculation of the effective baud rate. ! ! CALLING SEQUENCE: ! ! TIME = SY_TIME (); ! ! INPUT PARAMETERS: ! ! None. ! ! IMPLICIT INPUTS: ! ! None. ! ! OUTPUT PARAMETERS: ! ! Time in milliseconds. ! ! IMPLICIT OUTPUTS: ! ! None. ! ! COMPLETION CODES: ! ! None. ! ! SIDE EFFECTS: ! ! None. ! !-- BEGIN ! ! Local storage ! LOCAL Adjusted_Time : VECTOR [2, LONG], ! System time - a constant. MILLI_SECONDS, ! Time in milliseconds REMAINDER, ! Remainder on EDIV STATUS, ! Status returned by lower level Time : VECTOR [2, LONG], ! Quadword to hold system time. TEN_FOURTH : VECTOR [2, LONG]; ! to hold 10**4 ! ! LIB$EDIV will fail if the system time is too large, so we need ! to subtract some large constant from it - might as well use ! the current time. ! IF .Subtrahend [0] EQL 0 AND .Subtrahend [1] EQL 0 THEN BEGIN STATUS = SYS$GETTIM(Subtrahend); IF NOT .STATUS THEN RETURN 0; END; ! ! Get the VMS system time. ! STATUS = SYS$GETTIM(Time); IF NOT .STATUS THEN RETURN 0; ! ! Compute the longword value from the quadword returned. ! Status = LIB$SUBX(Time, Subtrahend, Adjusted_Time); IF NOT .STATUS THEN RETURN 0; TEN_FOURTH [0] = 1000*10; TEN_FOURTH [1] = 0; STATUS = LIB$EDIV (TEN_FOURTH, Adjusted_Time, MILLI_SECONDS, REMAINDER); IF NOT .STATUS AND .Status NEQ SS$_INTOVF THEN RETURN 0; RETURN .MILLI_SECONDS; END; ! End of SY_TIME %SBTTL 'End of KERSYS.BLI' END ! End of module ELUDOM