Trailing-Edge
-
PDP-10 Archives
-
decuslib10-08
-
43,50512/page.b36
There are no other files named page.b36 in the archive.
MODULE PAGER ( !Interface to page manager and QUASAR
IDENT = '1'
) =
BEGIN
! COPYRIGHT (C) 1978
! DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS 01754
!
! THIS SOFTWARE IS FURNISHED UNDER A LICENSE FOR USE ONLY ON A SINGLE
! COMPUTER SYSTEM AND MAY BE COPIED ONLY WITH THE INCLUSION OF THE
! ABOVE COPYRIGHT NOTICE. THIS SOFTWARE, OR ANY OTHER COPISE THEREOF,
! MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON
! EXCEPT FOR USE ON SUCH SYSTEM AND TO ONE WHO AGREES TO THESE LICENSE
! TERMS. TITLE TO AND OWNERSHIP OF THE SOFTWARE SHALL AT ALL TIMES
! REMAIN IN DEC.
!
! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
! AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
! CORPORATION.
!
! DEC ASSUMES NO RESPONSIBLILTY FOR THE USE OR RELIABILITY OF ITS
! SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DEC.
!
!++
! FACILITY:
! RMCOPY for TOPS-10 only
!
! ABSTRACT:
! This module provides the system queue interface for RMCOPY.
! Specifically, it has hooks for doing Q entry creation, listing, and
! killing. Internally, it has a rudimentary memory manager, and
! an IPCF interface, and a QUASAR interface. The IPCF and QUASAR
! stuff is patterned after QMANGR and QUENCH. (QMANGR and QUENCH
! themselves are NOT used,, we talk DIRECTLY to QUASAR.
!
! ENVIRONMENT:
! TOPS-10 (TOPS-20 to come)
!
! AUTHOR: Dave Cornelius, CREATION DATE: January, 1978
!
! MODIFIED BY:
! , : VERSION
! 01 D Cornelius Mar 31, 1978 - Add /AFTER printer for LISTANSWER
!
! 02 D Cornelius May 12, 1978 - Clean up LISTON output.
!
! 03 D Cornelius May 15, 1978 - Add RELEASE calls to ADDFILE.
! Add debugging printout to ADDFILE.
! 04 Andy Nourse 15-Jun-78 Globalize QSRPID and use it if already set up,
! don't wait for QUASAR if FTNETSPL.
! 05 D Cornelius Jul 20, 1978 - Add PPN fields to QFUNC and
! KILLR
!--
EXTERNAL
G$NOW, !The current daytime (in universal form)
G$DBUG; !Poke non-zero w/DDT to get debug info
EXTERNAL ROUTINE
RELEASE; !To return an FB (+ its channel) (for ADDFILE)
!
! TABLE OF CONTENTS:
!
FORWARD ROUTINE
QUEFLS:NOVALUE, !Flush input queue
QUEQRY, !returns status of next message from QUASAR
QUEIGN:NOVALUE, !Receive and ignore the next message
QUEWAT:NOVALUE, !Wait for a msg from QUASAR
RCVACK:NOVALUE, !Receive a page or short message
MSGSND:NOVALUE, !Send a msg to QUASAR
GETPAG, !Returns number of a page not in adrs space
PAGCRE, !Includes a page in the adrs space
NEWPAG, !Creates a page in the adrs space, returns its adrs
PAGDES, !Destroys a page, removing it from the adrs space
LISTEM:NOVALUE, !Lists a specified Q
WRPPNA:NOVALUE, !Converts a ppn bit pattern to ASCII
WR2DEC:NOVALUE, !Like WRNUMA, but alwasy writes 2 digits
WRTIME:NOVALUE, !Converts universal date-time for output
LISTON, !Lists one entry from the Q
KILLR:NOVALUE, !Kills one or more request from the Q
PPN, !Converts ASCIZ ppn string to bit patttern
QFUNC, !Called by RMCOPY to do LIST or KILL
CREAT, !Called by RMCOPY to do a CREATE in the NET Q
ADDFILE, !Internal, adds files to CREATE's EQ
UNSIX6; !Internal, converts 6 SIXBIT chars
!
!Conditional Compilation
!
!IPC modifications are controlled by the LIBRARY file IPCF.REQ
COMPILETIME FTSIGNAL = %IF %VARIANT %THEN 1 %ELSE 0 %FI;
!If "on", use the signalling mechanism
COMPILETIME FTTRUST_PSI=0;
!If "on" then trust software interrupt system (see SPECIAL INSTRUCTIONS)
COMPILETIME FTNETSPL = %IF (%VARIANT AND 2) EQL 2 %THEN 1 %ELSE 0 %FI;
!If "on" then reformat error messages for NETSPL
COMPILETIME FTALLOC = ((%VARIANT AND %O'4000') NEQ 0);
!If "on" then compile for use with ALLOC & FREE (required for NETSPL)
%IF FTNETSPL %THEN
%ASSIGN(FTALLOC,1) !Force these on for NETSPL
%ASSIGN(FTSIGNAL,1)
%FI
COMPILETIME VAR=(%VARIANT AND 3); !Pull out interesting bits
COMPILETIME FTINFORM=(%VARIANT AND %O'400') EQL 0;
!If "on" (default), generate messages to tell about feature tests
!
! INCLUDE FILES:
!
%IF FTSIGNAL
%THEN
REQUIRE 'INTR.REQ'; !For MACROs DFF, DLIT, SFDMAX, CALLI, etc
%ELSE
LIBRARY 'RMCOPY'; !For interface to sys-independent portion
LIBRARY 'IPCF'; !For Packet fields, header fields
!LIBRARY 'RMCSND'; !Fields for RMC request block
%FI
!
! Version info
!
THIS_IS [PAGE] VERSION [1] EDIT [4] DATE [15,JUN,78]
!
! Tell User about conditionals
!
%IF FTINFORM
%THEN %IF (FTSIG OR FTNETSPL) EQL 0
%THEN %INFORM ('PAGE for RMCOPY')
%ELSE %IF (FTSIG AND FTNETSPL)
%THEN %INFORM ('PAGE for NETSPL')
%ELSE %IF FTSIG
%THEN %INFORM ('Interrupt-driven PAGE, but not for NETSPL')
%ELSE %WARN('This %VARIANT setting not supported')
%FI
%FI
%FI
%IF FTIPC
%THEN %INFORM('IPC-Modified version')
%FI
%IF FTALLOC AND (NOT FTNETSPL)
%THEN %INFORM('ALLOC/FREE support included')
%FI
%FI
!
! MACROS:
!
%IF NOT FTNETSPL
%THEN
UNDECLARE %QUOTE PPREFIX;
MACRO PPREFIX='RMC' %;
%FI
!
! EQUATED SYMBOLS:
!
LITERAL IPCF_INTERVAL = 10; !How long to wait for receive
LITERAL LSTLEN = 1; !One page in the request
LITERAL
IPCFR_ = %O'142',
IPCFS_ = %O'143',
IPCFQ_ = %O'144';
LITERAL
IPCDD = %O'7', !QUASAR disabled for receives
IPCRS = %O'10', !My send quota exhausted
IPCRR = %O'11', !QUASAR over quota
IPCRY = %O'12'; !System full
LITERAL
HIBER = %O'72',
HB_IPC = %O'200000000';
LITERAL
GETTAB = %O'41',
GETPPN = %O'24',
SLEEP = %O'31',
PJOB = %O'30';
LITERAL PAGE_ = %O'145';
LITERAL _PAGCD = 1; !Create/destroy a page
LITERAL PAG_DSK = %O'200000000000'; !The 'create-page-on-disk' bit for
!page creating
LITERAL PAG_DES = %O'400000000000'; !Don't create,,, destroy!
LITERAL JBFF = %O'121';
LITERAL JBREL = %O'44';
LITERAL NETQNAM = %SIXBIT 'NET';
!
! GLOBAL STORAGE
!
GLOBAL
QSRPID:INITIAL(0); !Holds ID over send...recv
!
! OWN STORAGE:
!
OWN
LISTARG, !Holds 0 or PPN for list function
LSTANY, !Flag... 0==> no jobs checked
LSTTJL; !Count of total # jobs listed
!
! EXTERNAL REFERENCES:
!
UNDECLARE ZERO;
EXTERNAL ROUTINE
ZERO, !in IO.B36
RDSIX,
RDNUMA,
WRSIXA,
WRNUMA,
FBINI,
FPARSE,
COPY, !does BLTs
MSGERROR; !In IO10.B36
%IF (NOT FTTRUST_PSI) AND FTSIGNAL %THEN
EXTERNAL ROUTINE
UDT, !Get time in UDT
TSIGNL, !Set alarm clock
TSICAN; !Clear ...
%FI
!
! POSSIBLE ERROR MESSAGES
!
%(
Prefix Text Severity
CFQ Cannot flush the IPCF receive queue (SEVERE)
AQF Acknowledgment receive from SYSTEM[QUASAR] failed (SEVERE)
LQF List answer receive from SYSTEM[QUASAR] failed (SEVERE)
MFQ <text of message from QUASAR> (determined by QUASAR)
NGS No GALAXY-10 Support in this monitor (SEVERE)
CQP Cannot find QUASARs PID (SEVERE)
WFQ RMCOPY Waiting for [SYSTEM]QUASAR to start (INFORMATIVE)
SQF Send to [SYSTEM]QUASAR failed (SEVERE)
SQR RMCOPY Send has failed, message being re-sent (WARN)
Note that if prefixes are being used, the prefix for the program
(i.e. RMC or NET) will be immediately before the prefix indicated here.
)%
!
! ROUTINES
!
GLOBAL ROUTINE QUEFLS:NOVALUE=
!++
! FUNCTIONAL DESCRIPTION:
! This routine flushes all outstanding messages from the IPCF receive
! queue for this job. It may be useful for initialization purposes
! to be sure that we are starting with a 'clean slate'. and
! not picking up garbage from some previous request.
!
! FORMAL PARAMETERS:
!
! NONE
!
! IMPLICIT INPUTS:
!
! The contents and state of the IPCF receive queue.
!
! IMPLICIT OUTPUTS:
!
! NONE
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! Any data in the IPCF receive queue is THROWN AWAY !!!
!
!--
BEGIN
LOCAL PACK:IPCF_HEADER;
WHILE QUEQRY(PACK) NEQ 0 DO QUEIGN(PACK);
END;
GLOBAL ROUTINE QUEQRY (HDRADR)= !Returns 1 if QSR mesg is waiting, 0 otherwise
!++
! FUNCTIONAL DESCRIPTION:
! This routine allows the caller to determine if there are any
! messages in the IPCF receive queue from QUASAR.
! If this routine returns 1, the caller may then do a receive
! and will not block.
!
! FORMAL PARAMETERS:
!
! An IPCF packet header (IPCF_HEADER structure
!
! IMPLICIT INPUTS:
!
! The state of the IPCF receive Q
!
! IMPLICIT OUTPUTS:
!
! A packet header describing the next packet to be received is returned
! in the passed packet header if the routine returns 1
!
! ROUTINE VALUE:
!
! 1 ==> a message from QUASAR is waiting,, 0 ==> none there
!
! SIDE EFFECTS:
!
! Any messages which are not from QUASAR are ignored (THROWN AWAY!!)
!
!--
BEGIN
MAP HDRADR:REF IPCF_HEADER;
REGISTER S2; !For the UUO
WHILE 1 DO
BEGIN
ZERO (.HDRADR, .HDRADR + IPCF$HEADER_LEN - 1);
S2<LH> = IPCF$HEADER_LEN;
S2<RH> = .HDRADR;
IF NOT CALLI (S2, IPCFQ_) THEN RETURN 0;
IF (.HDRADR [IPCF$MSG_LEN] EQL 0) AND
(.HDRADR [IPCF$MSG_NWAIT] EQL 0) THEN RETURN 0;
IF .HDRADR [IPCF$SEND_PID] EQL .QSRPID THEN RETURN 1;
QUEIGN (.HDRADR); !Flush the junk mail
END
END;
GLOBAL ROUTINE QUEIGN (HDRADR):NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
! This routine receives and throws away the next IPCF receive Q entry.
!
! FORMAL PARAMETERS:
!
! An IPCF header address
!
! IMPLICIT INPUTS:
!
! The IPCF receive Q
!
! IMPLICIT OUTPUTS:
!
! NONE
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! If there is no packet waiting, the job will block.
! The data that was in the message is completely lost.
!
!--
BEGIN
MAP HDRADR:REF IPCF_HEADER;
REGISTER S2;
S2 = .HDRADR [IPCF$PAGE_PACK];
ZERO (.HDRADR, .HDRADR + IPCF$HEADER_LEN - 1);
HDRADR [IPCF$PAGE_PACK] = .S2;
HDRADR [IPCF$TRUNC] = 1;
S2<LH> = IPCF$HEADER_LEN;
S2<RH> = .HDRADR;
IF NOT CALLI (S2, IPCFR_) THEN
MSGERROR (CH$PTR(PLIT (%ASCIZ
'Cannot flush the IPCF receive queue')),'CFQ' OR S$SEVERE);
END;
GLOBAL ROUTINE QUEWAT (HDRADR):NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
! This routine will put the job to sleep until there is a message from
! QUASAR in the IPCF receive Q. This routine does not do the receive,
! it merely sleeps until there is data.
! SPECIAL INSTRUCTIONS:
! Compile /VARIANT:1 to use software interrupt system and NETSPL
! condition-handling package. This requires an interrupt block to
! have already been set up and IPCF interrupts to have been enabled
! The interrupt block is assumed to be at IPCFINT (external)
!
! FORMAL PARAMETERS:
!
! The address of an IPCF header block
!
! IMPLICIT INPUTS:
!
! The state of the IPCF receive Q
! IPCFINT (if compiled /VARIANT:1, see SPECIAL INSTRUCTIONS)
!
! IMPLICIT OUTPUTS:
!
! The IPCF header will have some information about the packet to be received
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! See QUEQRY
!
!--
BEGIN
MAP HDRADR:REF IPCF_HEADER;
%IF FTSIGNAL AND (NOT FTTRUST_PSI)
%THEN
LOCAL IPCFRETRY: INT_BLOCK,
IR;
CLEARV(IPCFRETRY);
%FI
WHILE QUEQRY (.HDRADR) EQL 0
DO
%IF FTSIGNAL
%THEN BEGIN
EXTERNAL IPCFINT;
EXTERNAL ROUTINE WAIT;
%IF (NOT FTTRUST_PSI) %THEN
IR=TSIGNL(IPCFRETRY,UDT()+IPCF_INTERVAL); !Set timer for retry
%FI
WAIT(IPCFINT);
%IF (NOT FTTRUST_PSI) %THEN
TSICAN(.IR)
%FI; !Clear the timer
END
%ELSE BEGIN
REGISTER S2;
S2 = HB_IPC + 2000; !Sleep for 2 seconds
CALLI (S2, HIBER)
END
%FI
END;
GLOBAL ROUTINE RCVACK:NOVALUE=
!++
! FUNCTIONAL DESCRIPTION:
! This routine waits for an acknowledgment message from QUASAR.
! If the ack msg has a text message associated with it,
! the message is typed on the user's terminal.
! If the message spans several IPCF packets, then they will
! all be handled before control is returned.
!
! FORMAL PARAMETERS:
!
! NONE
!
! IMPLICIT INPUTS:
!
! The IPCF receive Q
!
! IMPLICIT OUTPUTS:
!
! Some data may be typed on the user's terminal
!
! ROUTINE VALUE:
!
! SS$_NORMAL: success, SS$_WARN or SS$_ERROR if QUASAR complains
! (any error message will have been printed (via MSGERROR)
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
LOCAL PACK:IPCF_HEADER; !Something to pass to QUEWAT
LOCAL SHORTMSG:BLOCK[20]; !For non-paged acks
LOCAL T; !A temporary
LOCAL M:REF QSR$MSG_TEXT; !Will point to the msg received
REGISTER S2; !For the UUO
WHILE 1 DO
BEGIN
QUEWAT (PACK); !Wait for the answer
T = .PACK [IPCF$PAGE_PACK]; !Save page mode flag
PACK [IPCF$FLAGS] =
PACK [IPCF$SEND_PID] =
PACK [IPCF$RECV_PID] = 0;
PACK [IPCF$MSG_ADR] = M = SHORTMSG; !Aim at short message
IF (PACK [IPCF$PAGE_PACK] = .T) !Restore the flag
NEQ 0 THEN
BEGIN
PACK [IPCF$MSG_LEN] = %O'1000';
PACK [IPCF$MSG_ADR] = GETPAG();
M = .PACK [IPCF$MSG_ADR] * %O'1000';
END;
S2<LH> = IPCF$HEADER_LEN;
S2<RH> = PACK;
IF NOT CALLI (S2, IPCFR_) THEN
MSGERROR (CH$PTR(PLIT (%ASCIZ
'Acknowledgment receive from SYSTEM[QUASAR] failed'
)),'AQF' OR S$SEVERE);
IF .M [QSR$TEXT_NOMESS] EQL 0 THEN
BEGIN
MSGERROR (CH$PTR (M[QSR$TEXT_MSG]),
((%IF FTNETSPL %THEN 'MFQ' %FI) OR
(IF .M[QSR$TEXT_FATAL] THEN S$SEVERE
ELSE IF .M[QSR$TEXT_WARN] THEN S$WARN
ELSE S$COMMENT)));
END;
IF .M [QSR$TEXT_MORE ] EQL 0
THEN RETURN (IF .M[QSR$TEXT_NOMESS]
THEN SS$_NORMAL !Good return
ELSE IF .M[QSR$TEXT_FATAL]
THEN SS$_ERROR !We lost
ELSE IF .M[QSR$TEXT_WARN]
THEN SS$_WARN !Warning
ELSE SS$_NORMAL); !Good return
!Convert QUASAR severity code to ours
END
END;
GLOBAL ROUTINE MSGSND(MSGADR, PAGEFLG):NOVALUE = !Send a message to QUASAR
!++
! FUNCTIONAL DESCRIPTION:
! This routine pulls the required UUOs to send a message to QUASAR
! If the message cannot be sent because of some temporary condition,
! The routine will sleep, and retry after a while.
! This routine does not wait for any acks on the message.
!
! FORMAL PARAMETERS:
!
! MSGADR - The address of a packet or page of information (ie the message)
! PAGEFLG - A number indicating whether the packet is a page of data or
! just a short message. (1==>page, 0==>packet).
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! A message is sent to QUASAR via IPCFS.
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
MAP MSGADR:REF QSR$HEADER; !The only thing we deal with is the header
!Whatever is tacked on to the header is the caller's
!Responsibility
LOCAL IPCFBLK: IPCF_HEADER;
REGISTER AC; !For the UUOS
LOCAL FLAG; !For first time-thru wait loop
LITERAL SECNDSTS = %O'106000011'; !Second configuration word GETTAB ptr
LITERAL STAT_GAL = %O'1000'; !Galaxy is built in
LITERAL SPIDQSR = %O'2000126'; !GETTAB for [SYSTEM]QUASAR's PID
ZERO (IPCFBLK, IPCFBLK + IPCF$HEADER_LEN - 1);
AC = SECNDSTS;
IF NOT CALLI (AC, GETTAB) THEN AC = 0;
IF (.AC AND STAT_GAL) EQL 0 THEN
MSGERROR (CH$PTR(PLIT (%ASCIZ
'No GALAXY-10 Support in this monitor')),'NGS' OR S$SEVERE);
FLAG = 0;
IF .QSRPID EQL 0
THEN WHILE 1 DO
BEGIN
AC = SPIDQSR;
IF NOT CALLI (AC, GETTAB) THEN
MSGERROR (CH$PTR(PLIT (%ASCIZ
'Cannot get QUASARs PID')),'CQP' OR S$SEVERE);
QSRPID = .AC; !Save QUASAR's ID
IF .AC NEQ 0 THEN EXITLOOP;
%IF FTNETSPL
%THEN ERROR(QSRNRN);
%ELSE
AC = 3;
CALLI (AC, SLEEP);
IF (FLAG = .FLAG + 1) NEQ 0 THEN
MSGERROR (CH$PTR(PLIT (%ASCIZ
'RMCOPY Waiting for [SYSTEM]QUASAR to start')),
'WFQ' OR S$WARN);
%FI
END;
!Now we know where to send messages so they will get to QUASAR.
IPCFBLK [IPCF$RECV_PID] = .QSRPID;
IPCFBLK [IPCF$MSG_ADR] = .MSGADR;
IPCFBLK [IPCF$MSG_LEN] = .MSGADR [QSR$MS_CNT]; !Assume non-paged length
IF .PAGEFLG NEQ 0 THEN
BEGIN
IPCFBLK [IPCF$PAGE_PACK] = 1;
IPCFBLK [IPCF$MSG_LEN] = %O'1000';
IPCFBLK [IPCF$MSG_ADR] = .IPCFBLK [IPCF$MSG_ADR] / %O'1000'
END;
WHILE 1 DO
BEGIN
AC<LH> = IPCF$HEADER_LEN;
AC<RH> = IPCFBLK;
IF CALLI (AC, IPCFS_) THEN RETURN;
IF (.AC NEQ IPCDD) AND
(.AC NEQ IPCRS) AND
(.AC NEQ IPCRR) AND
(.AC NEQ IPCRY) THEN
MSGERROR(CH$PTR(PLIT(%ASCIZ
'Send to [SYSTEM]QUASAR failed')),'SQF' OR S$SEVERE);
%IF FTNETSPL EQL 0
%THEN
AC = 2;
CALLI (AC, SLEEP);
MSGERROR (CH$PTR(PLIT(%ASCIZ
'RMCOPY Send has failed, message being re-sent')),
'SQR' OR S$WARN)
%FI
END
END;
GLOBAL ROUTINE GETPAG= !Returns number of next free
!page NOT in adress space
!This routine is NOT reentrant
!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
! NONE
!
! IMPLICIT INPUTS:
!
! The contents of .JBREL
!
! IMPLICIT OUTPUTS:
!
! If ALLOC & FREE are being used (FTALLOC, /Variant:#4000)
! .JBFF is pointed past the free page we are returning so
! the page will not end up multiply-allocated and
! any space between the old value of .JBFF and the page is FREE'ed
!
! ROUTINE VALUE:
!
! The number of the next free page which is not addressable
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
%IF FTALLOC %THEN
LOCAL F, !Address of hole we are about to make
L; !Length
EXTERNAL ROUTINE FREE;
F=.JBFF; !Save 'Old' value of .JBFF
L=(.JBREL-.JBFF)+1; !Length of hole
JBFF=.JBREL+%O'1001'; !Mark the page 'in-use'
IF .L GTR 0 THEN FREE(.F,.L); !Free the hole between old .JBFF and the page
%FI
(.JBREL + %O'777') / %O'1000'
END;
GLOBAL ROUTINE PAGCRE (PAGNUM) =
!++
! FUNCTIONAL DESCRIPTION:
! Places the page 'pagnum'
! in the job's address space
! This routine tries to create the page in core.
! If we can't create it in core, we try to create it on the swap space
!
! FORMAL PARAMETERS:
!
! PAGNUM - The number of the page which is to be included in the address space
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! The page is rendered addressable
!
! ROUTINE VALUE:
!
! 0==> The page cannot be had (ie core limits or CORMAX or VIRTAL exceeded)
! 1==> The page is now addressable
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
REGISTER AC; !For the UUO
LOCAL PAGEBLOCK:VECTOR [LSTLEN + 1]; !Space for the count, and the request
PAGEBLOCK [0] = LSTLEN;
PAGEBLOCK [1] = .PAGNUM; !Bits 0, 1 are zero...
!create page, in working set
DECR I FROM 1 TO 0 DO !Try in WS, then try on DSK, then quit
BEGIN
AC<LH> = _PAGCD; !Function create/destroy
AC<RH> = PAGEBLOCK; !Point to arg list
IF CALLI (AC, PAGE_) THEN RETURN 1;
PAGEBLOCK [1] = .PAGEBLOCK [1] OR PAG_DSK; !Set to try not in Working set
END;
RETURN 0; !Can't have a page
END;
GLOBAL ROUTINE NEWPAG= !Returns the adrs of a new, adressable page
!++
! FUNCTIONAL DESCRIPTION:
! This routine combines the functionality of GETPAG and PAGCRE
! Adds one page to the address space and returns its address.
! Note that 2 consecutive calls to this routine may not return
! 2 consecutive pages. This can happen if some calls to PAGDES have been made
!
! FORMAL PARAMETERS:
!
! NONE
!
! IMPLICIT INPUTS:
!
! See GETPAG
!
! IMPLICIT OUTPUTS:
!
! See PAGCRE
!
! ROUTINE VALUE:
!
! >=0, that's the address of the new page
! -1, No free pages can be had
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
LOCAL PAGNUM; !Holds data across routine calls
PAGNUM = GETPAG(); !Find a free one
IF PAGCRE(.PAGNUM) THEN .PAGNUM * %O'1000' ELSE -1
END;
GLOBAL ROUTINE PAGDES(ADRS) = !Destroy the page whose address is ADRS
!++
! FUNCTIONAL DESCRIPTION:
! This routine is used to return a page to the monitor. It is useful
! for cleanup after some series of IPCF receives has been done,
! and the data received has been processed.
!
! FORMAL PARAMETERS:
!
! The address of the first word of the page to be destroyed (ie nnn000)
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! NONE
!
! ROUTINE VALUE:
!
! 0==> the address is not a page bound, or the UUO failed.
! 1==> the page is gone. (all is ok)
!
! SIDE EFFECTS:
!
! The job's address space size is reduced by one page
!
!--
BEGIN
REGISTER AC; !For the UUO
LOCAL PAGBLK:VECTOR [LSTLEN + 1]; !a counted list of pages
IF (.ADRS AND %O'777') NEQ 0 THEN RETURN 0;
PAGBLK [0] = LSTLEN; !Count the args
PAGBLK [1] = PAG_DES + (.ADRS / %O'1000'); !Aim at the desired page
! ... and set the 'destroy page bit'
AC<LH> = _PAGCD; !Function create/destroy
AC<RH> = PAGBLK; !Point to arg list
IF CALLI (AC, PAGE_) THEN 1 ELSE 0
END;
GLOBAL ROUTINE LISTEM(QNAM, SERVROUT):NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
! This routine gets information from QUASAR about
! a specified Q. QUASAR returns information about the entries packed
! contiguously on a series of pages. This routine first requests
! QUASAR to list the Q by sending a LIST message to QUASAR.
! The routine then receives all the pages containing all the entries.
! The answer pages are sequence numbered by QUASAR in the
! count field of the header word. Since the pages may arrive in
! any order, QUASAR will set the ACK (sign) bit on the message
! with the highest sequence number.
! This routine builds a linked, ordered list of the answer pages
! by using the TYP field of the header word. (That field always
! contains the code for the LIST ANSWER message type).
! When all the answer pages have been retrieved, the routine
! flips thru all pages, examining all entries on each page.
! For each entry, the listing routine is called with a pointer
! to the entry.
!
! FORMAL PARAMETERS:
!
! QNAM - The SIXBIT name of the Q to list (0 ==> all Queues)
! SERVROUT - The address of the service output routine which must
! take one parameter (the address of a LIST ANSWER entry).
! This routine is responsible for decoding and listing each entry
!
! IMPLICIT INPUTS:
!
! The contents of the specified Q at the time of the call.
!
! IMPLICIT OUTPUTS:
!
! NONE
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! The Job's address space may temporarily increase if there are
! many entries in the Q.
!
!--
BEGIN
LOCAL LIST_REQ:QSR$MSG_LIST; !For the request
LOCAL NUMANS; !Will hold highest message number
LOCAL NUMREC; !Counts the number of messages received
LOCAL M:REF QSR$HEADER; !Points to the page just rec'd
LOCAL FSTMSG:REF QSR$HEADER; !Start of linked page answer list
LOCAL THISMSG:REF QSR$HEADER; !For chaining the pages
LOCAL PREVMSG:REF QSR$HEADER; !Also for linking entries
LOCAL THISENT:REF QSR$MSG_LANSW; !Used to ref each Q entry on an answer page
LOCAL IPCBLK:IPCF_HEADER; !For waiting, receiving
REGISTER S2; !For the receive UUO
CLEARV(LIST_REQ,IPCBLK); !Zero these first
LIST_REQ [QSR$MSTYP] = 0; !Start w/clean header
LIST_REQ [QSR$MS_ACK] = 1; !We want an ack
LIST_REQ [QSR$MS_CNT] = QSR$LIST_SIZE; !Our size
LIST_REQ [QSR$MS_TYP] = QSR$MS_LIST; !The code for LIST
LIST_REQ [QSR$LIST_QNAM] = .QNAM; !Set the Q we want to see
%IF FTIPC %THEN
BEGIN
BIND LRDB = LIST_REQ [QSR$LIST_RDB]: QSR$RDB;
LRDB [QSR$RDB_DEV] = .QNAM; !Twice for IPC
END %FI;
MSGSND (LIST_REQ, 0); !Send the request
RCVACK(); !Wait for ack (we asked for it...
!... we got it !)
NUMANS = 0; !No max is known yet
NUMREC = 0; !And none have been received yet, either
FSTMSG = 0; !Nothing on the list, either
DO
BEGIN
!Recevive a page
QUEWAT (IPCBLK); !Wait for a fish to come by...
ZERO (IPCBLK, IPCBLK + IPCF$HEADER_LEN - 1);
IPCBLK [IPCF$PAGE_PACK] = 1; !These messages are always pages
IPCBLK [IPCF$MSG_LEN] = %O'1000';
IPCBLK [IPCF$MSG_ADR] = GETPAG(); !Find a free slot.
! NOTE: If anyone else calls GETPAG before we do the IPCF receive,
! they, too, will get our page. Interrupts should be turned off
! over the interim.
S2<LH> = IPCF$HEADER_LEN;
S2<RH> = IPCBLK;
IF NOT CALLI (S2, IPCFR_) THEN
MSGERROR (CH$PTR(PLIT(%ASCIZ
'List answer receive from SYSTEM[QUASAR] failed'
)),'LQF' OR S$SEVERE);
NUMREC = .NUMREC + 1;
!Link it to list of answers
M = .IPCBLK [IPCF$MSG_ADR] * %O'1000'; !Aim at the page to be inserted
PREVMSG = 0; !No known predecessor
THISMSG = .FSTMSG; !Start looking at the head of the list
WHILE .THISMSG NEQ 0 DO
IF .M [QSR$MS_CNT] GEQ .THISMSG [QSR$MS_CNT]
THEN
BEGIN !Doesn't go here, try next entry
PREVMSG = .THISMSG;
THISMSG = .THISMSG [QSR$MS_TYP]
END
ELSE !Does go here, quit now
EXITLOOP;
M [QSR$MS_TYP] = .THISMSG; !Point us at the rest of the list
!Point the first part of the list at us
(IF .PREVMSG EQL 0 THEN
FSTMSG !Goes at the head of list
ELSE
PREVMSG [QSR$MS_TYP] !Goes in the middle of list
) = .M;
!check for last message
IF .M [QSR$MS_ACK] NEQ 0 THEN
BEGIN
M [QSR$MS_ACK] = 0;
NUMANS = .M [QSR$MS_CNT]
END;
END
UNTIL .NUMREC EQL .NUMANS;
!Now we have all the answer pages read in and linked in increasing order
!So we just aim at each Q entry, and give it to the output processor
THISMSG = .FSTMSG; !Start at the begining
!Do all pages
DECR I FROM .NUMANS TO 1 DO
BEGIN
!Do all entries on this page
THISENT = (.THISMSG) + QSR$LANSW_FIRST; !Point at the first on this page
DECR I FROM QSR$LANSW_APP TO 1 DO
BEGIN !List one entry
IF .THISENT [QSR$LANSW_JOB] EQL 0
THEN EXITLOOP; !Quit at end of list (0 JOBNAME)
(.SERVROUT)(.THISENT); !Call the output routine
THISENT = (.THISENT) + QSR$LANSW_SIZE; !Point to the next
END; !Of loop for all entries on a page
THISMSG = .THISMSG [QSR$MS_TYP]; !Link to the next page
END; !Of loop for all pages
!Now return the list pages to the monitor
WHILE .FSTMSG NEQ 0 DO
BEGIN
THISMSG = .FSTMSG [QSR$MS_TYP]; !Remember next page adrs
PAGDES (.FSTMSG); !Kill this page
FSTMSG = .THISMSG; !Link to next
END;
END; !Of the LISTEM routine
GLOBAL ROUTINE WRPPNA (VAL, PTR):NOVALUE=
!++
! FUNCTIONAL DESCRIPTION:
! This routine converts a 36 bit ppn pattern to ASCII
!
! FORMAL PARAMETERS:
!
! VAL - The left half has the project number, right half has programmer number
! PTR - The address of a char-seq-ptr.
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! The converted ppn is written into the space aimed at by the PTR
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! The pointer at the address PTR is advanced
!
!--
BEGIN
CH$WCHAR_A(%C'[',.PTR);
WRNUMA (.VAL<LH>, 8, .PTR);
CH$WCHAR_A(%C',', .PTR);
WRNUMA (.VAL<RH>, 8, .PTR);
CH$WCHAR_A (%C']', .PTR);
END;
ROUTINE WR2DEC (NUMBER, PTRADR) :NOVALUE = !
!++
! FUNCTIONAL DESCRIPTION:
! Writes at least 2 decimal digits
!
! FORMAL PARAMETERS:
!
! NUMBER - The bit pattern to be converted
! PTRADR - Adrs of a CH$PTR where the digits will be written
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! The number is written at ..PTRADR
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! The pointer at PTRADR is advanced
!
!--
BEGIN
IF .NUMBER LSS 10 THEN CH$WCHAR_A (%C'0', .PTRADR);
WRNUMA (.NUMBER, 10, .PTRADR);
END;
ROUTINE WRTIME (TIMWRD, PTRADR) :NOVALUE = !Universal date-time converter
!++
! FUNCTIONAL DESCRIPTION:
! This routine takes a universal date-time word (lh has days..
! rh has fractional days) and prints days (if any), hours, and
! minutes into the buffer pointed to by PTRADR
!
! FORMAL PARAMETERS:
!
! TIMWRD - the universal date-time word
! PTRADR - the address of a CH$PTR where the output will go
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! The characters are written using the PTRADR
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! The pointer in PTRADR is advanced
!
!--
BEGIN
LOCAL HOURS, !Eventually holds hours for printout
MINS; !Holds minutes
IF .TIMWRD <LH> NEQ 0 THEN
(WRNUMA (.TIMWRD <LH>, 10,.PTRADR);
CH$WCHAR_A (%C'd', .PTRADR));
TIMWRD = (.TIMWRD <RH> * 60 * 24) / %O'1000000';
HOURS = .TIMWRD / 60;
MINS = .TIMWRD MOD 60;
WR2DEC (.HOURS, .PTRADR);
CH$WCHAR_A (%C':', .PTRADR);
WR2DEC (.MINS, .PTRADR)
END;
GLOBAL ROUTINE LISTON(ADR)=
!++
! FUNCTIONAL DESCRIPTION:
! This routine is called once for each Q entry to list it on the
! user's terminal.
! It's address is passed by QFUNC to LISTEM, and LISTEM actually calls it.
!
! FORMAL PARAMETERS:
!
! ADR - The address of a LISTANSWER message from QUASAR
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! The converted data is written on the user's terminal
! The flags LSTANY and the counter LSTTJL (Total Jobs Listed)
! are modified by the routine.
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
MACRO SPACE(N) = PTR = CH$FILL(%C' ',N,.PTR) %;
MAP ADR:REF QSR$MSG_LANSW;
LOCAL LINE:BLOCK[CH$ALLOCATION(150)];
LOCAL PTR, !REF to LINE
PTIME; !Holds the time job will run
! OWN LISTARG; !Initialized by QFUNC
!Holds 0 (match any ppn)
!... or p,pn (match this ppn only)
! OWN LSTANY; !Flag that we considered any jobs
! OWN LSTTJL; !Count of total # listed
LSTANY = -1; !Note that we were given a job
IF (.LISTARG EQL 0) OR (.LISTARG EQL .ADR [QSR$LANSW_OWN]) THEN
BEGIN
IF .LSTTJL EQL 0 THEN !This is the first, add a header
MSGERROR (CH$PTR (UPLIT (%ASCIZ
'Node Job Function Seq Limit PPN Name After')), S$MSGCRLF);
LSTTJL = .LSTTJL + 1; !We got a job, and it matched spec, so count it
PTR = CH$PTR(LINE);
UNSIX6 (%REF (CH$PTR (ADR [QSR$LANSW_NODE], 0, 6)), PTR);
PTR = CH$MOVE (1,
CH$PTR ((IF .ADR [QSR$LANSW_PRDEV] NEQ 0
THEN UPLIT (%ASCII '*')
ELSE UPLIT (%ASCII ' '))),
.PTR);
SPACE (1);
UNSIX6 (%REF (CH$PTR (ADR [QSR$LANSW_JOB], 0, 6)), PTR);
SPACE(2);
PTR = CH$MOVE (9,
CH$PTR (SELECTONE .ADR [QSR$LANSW_FUNC] OF SET
[RMC$F_SEND]: PLIT(%ASCII 'Send ');
[RMC$F_GET]: PLIT(%ASCII 'Retrieve ');
[RMC$F_REN]: PLIT(%ASCII 'Rename ');
[RMC$F_DEL]: PLIT(%ASCII 'Delete ');
[RMC$F_DI]: PLIT(%ASCII 'Directory');
[OTHERWISE]: PLIT(%ASCII 'Illegal ');
TES),
.PTR);
SPACE (2);
WRNUMA (.ADR [QSR$LANSW_SEQ], 10, PTR);
SPACE (2);
WRNUMA (.ADR [QSR$LANSW_LIMIT], 10, PTR);
SPACE (2);
WRPPNA (.ADR [QSR$LANSW_OWN], PTR);
SPACE (2);
UNSIX6 (%REF (CH$PTR (ADR [QSR$LANSW_USER], 0, 6)), PTR);
UNSIX6 (%REF (CH$PTR (ADR [QSR$LANSW_USER2], 0, 6)), PTR);
SPACE (2);
IF (PTIME = .ADR [QSR$LANSW_AFTER] - .G$NOW) GTR 0
THEN WRTIME (.PTIME, PTR);
CH$WCHAR_A(0,PTR);
MSGERROR (CH$PTR(LINE),S$MSGCRLF);
IF .G$DBUG THEN
BEGIN
PTR = CH$PTR(LINE);
SPACE(2);
WRNUMA (.ADR [QSR$LANSW_PRO], 8, PTR);
SPACE(2);
WRSIXA (.ADR [QSR$LANSW_DEV], PTR);
SPACE (2);
WRSIXA (.ADR [QSR$LANSW_STR], PTR);
SPACE (2);
WRNUMA (.ADR [QSR$LANSW_PRIO], 10, PTR);
SPACE (2);
WRNUMA (.ADR [QSR$LANSW_OPT], 8, PTR);
SPACE(2);
SPACE (2);
IF .ADR [QSR$LANSW_RTS] NEQ 0 THEN
WRSIXA (.ADR [QSR$LANSW_RTS], PTR);
CH$WCHAR_A(0,PTR);
MSGERROR (CH$PTR(LINE),S$MSGCRLF);
END; !Of debugging stuff
END;
END;
GLOBAL ROUTINE KILLR (ARG, PPN, ARGTYPE):NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
! This routine is called to kill one or more jobs from the Q
! The routine may kill by jobname or by sequence number.
! A PPN may be specified if the request to be killed is not our own.
!
! FORMAL PARAMETERS:
!
! ARG - either a job number or a SIXBIT job name.
! PPN - 0==> kill for our ppn only (this is defaulted in QUASAR).
! .NE. 0 ==> this is a P,PN pair, try to kill for this user
! ARGTYPE - either QFN$JOB or QFN$SEQ. This arg describes the ARG
!
! IMPLICIT INPUTS:
!
! The contents of the Q.
!
! IMPLICIT OUTPUTS:
!
! The entry is removed from the Q, a message is typed on the user's terminal
!
! ROUTINE VALUE:
!
! NONE
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
LOCAL KILLBL: QSR$MSG_KILL;
LOCAL KRDB: REF QSR$RDB;
CLEARV(KILLBL); !Start out clean
KILLBL [QSR$MS_ACK] = 1; !We want an ack
KILLBL [QSR$MS_CNT] = QSR$KILL_SIZE; !Set up header w/size, type
KILLBL [QSR$MS_TYP] = QSR$MS_KILL;
KILLBL [QSR$KILL_QNAM] = NETQNAM; !We only kill things from the NET
KRDB = KILLBL + QSR$KILL_RDB; !Aim at the RDB part of the message
%IF FTIPC
%THEN KRDB [QSR$RDB_DEV] = NETQNAM; !Set twice for IPC
%FI
KRDB [QSR$RDB_PPN] = .PPN; !Supply the ppn
KRDB [QSR$RDB_PPNMSK] = -1; !Don't wildcard PPN
IF (.ARGTYPE EQL QFN$JOB) OR
(.ARGTYPE EQL QFN$JOBPPN)
THEN BEGIN !Wants to kill by jobname
KRDB [QSR$RDB_JOB] = .ARG; !Set the jobname
KRDB [QSR$RDB_JOBMSK] = -1; !And don't do any wildcarding
END
ELSE !Wants to kill by sequence
KRDB [QSR$RDB_SEQ] = .ARG; !Set the sequce number
MSGSND (KILLBL, 0); !Send the kill request (0==>non-paged)
RCVACK(); !Wait for the ack, print results
END;
GLOBAL ROUTINE PPN (CHPTR) =
!++
! FUNCTIONAL DESCRIPTION:
! This routine converts an ASCiz string to a LH,RH ppn word
!
! FORMAL PARAMETERS:
!
! PTR - a char-seq-ptr to the ASCIZ string
!
! IMPLICIT INPUTS:
!
! NONE
!
! IMPLICIT OUTPUTS:
!
! NONE
!
! ROUTINE VALUE:
!
! The 36 bit ppn word
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
LOCAL RESULT;
RESULT = 0;
RESULT<LH> = RDNUMA (CHPTR, 8);
IF CH$RCHAR_A(CHPTR) EQL %C',' THEN RESULT<RH> = RDNUMA (CHPTR, 8);
.RESULT
END;
GLOBAL ROUTINE QFUNC (FUNC, PARAMTYPE, PARAMPTR, PARAMPTR2) =
!++
! FUNCTIONAL DESCRIPTION:
! This is the entry point for the system-dependent Q lister and killer
! functions.
!
!
! FORMAL PARAMETERS:
!
! FUNC - either QFN$LIST or QFN$KILL
! PARMATYPE - describes the type of string aimed at by the PARAMPTR
! PARAMPTR - pointer to an ASCIZ string
! LIST can take QFN$NONE (list all entries) or QFN$PPN (list all
! entries for that ppn), or
! QFN$SELF (list all entries for my ppn).
! KILL can take QFN$SEQ (kill just that sequnce number)
! or QFN$JOB (kill all jobs with that jobname).
! QFN$JOBPPN or QFN$SEQPPN may also be specified, to
! kill by job or sequence for another user.
! PARAMPTR2 - a pointer to an ASCIZ p,pn string. This parameter is
! ignored if the PARAMTYPE is not QFN$JOBPPN or QFN$SEQPPN.
!
! IMPLICIT INPUTS:
!
! The contents of the Q
!
! IMPLICIT OUTPUTS:
!
! A message (or a list of entries) is typed on the user's terminal
! For KILL, one or several jobs may be removed from the Q
!
! ROUTINE VALUE:
!
! See RMCOPY.REQ for return values.
!
! SIDE EFFECTS:
!
! NONE
!
!--
SELECTONE .FUNC OF SET
[QFN$LIST]:
BEGIN
! OWN LISTARG; !Used by LISTON
! OWN LSTANY,LSTTJL; !Used by LISTON
QUEFLS(); !Make sure theres nothing in the Q
LSTANY = LSTTJL = 0; !Clear flag, and count
SELECTONE .PARAMTYPE OF SET
[QFN$PPN]: LISTARG = PPN (.PARAMPTR);
[QFN$NONE]: LISTARG = 0;
[QFN$SELF]: BEGIN
REGISTER AC;
CALLI (AC, GETPPN);
LISTARG = .AC
END;
[OTHERWISE]: RETURN QFN$ILP;
TES;
LISTEM (NETQNAM,LISTON); !List the NETQ entries via routine LISTON
IF .LSTANY EQL 0 THEN MSGERROR (CH$PTR (UPLIT
(%ASCIZ 'The Queue is empty')), S$COMMENT)
ELSE IF .LSTTJL EQL 0 THEN
MSGERROR (CH$PTR (PLIT (%ASCIZ
'No jobs match specification')),S$COMMENT)
ELSE BEGIN
LOCAL NUM:BLOCK[CH$ALLOCATION(12)];
LOCAL T;
MSGERROR (CH$PTR (PLIT (%ASCIZ
'Total of ')),S$CRLFMSG);
T = CH$PTR(NUM);
WRNUMA (.LSTTJL, 10, T);
CH$WCHAR_A(0,T);
MSGERROR (CH$PTR(NUM), S$MSG);
MSGERROR (CH$PTR (PLIT (%ASCIZ
' jobs in the Queue')),S$MSGCRLF);
END;
RETURN QFN$OK;
END;
[QFN$KILL]:
BEGIN
LOCAL KILLARG, KILLPPN;
QUEFLS(); !Make sure theres nothing in the Q
SELECTONE .PARAMTYPE OF SET
[QFN$JOB, QFN$JOBPPN]: KILLARG = RDSIX (PARAMPTR);
[QFN$SEQ, QFN$SEQPPN]: KILLARG = RDNUMA (PARAMPTR, 10);
[OTHERWISE]: RETURN QFN$ILP;
TES;
KILLPPN = (IF (.PARAMTYPE EQL QFN$JOBPPN) OR
(.PARAMTYPE EQL QFN$SEQPPN)
THEN PPN (.PARAMPTR2)
ELSE 0);
KILLR (.KILLARG, .KILLPPN, .PARAMTYPE);
RETURN QFN$OK;
END;
[OTHERWISE]: RETURN QFN$ILF;
TES;
GLOBAL ROUTINE CREAT (RB_PTR) = !Make an entry in QUASAR's Queue
!++
! FUNCTIONAL DESCRIPTION:
! This routine converts a 'transportable' RMC request block to
! an IPCF-able packet message for QUASAR's NET queue. It fills in
! some defaults, but generally is just a request translator
!
! FORMAL PARAMETERS:
!
! A pointer to an RMC request block (see XXXXXX.REQ for details)
!
! IMPLICIT INPUTS:
!
! The running job number, %%.QSR, RMC version
!
! IMPLICIT OUTPUTS:
!
! An entry in the NET queue
!
! ROUTINE VALUE:
!
! less than zero is an error
! See RMCOPY.REQ for details
!
! SIDE EFFECTS:
!
! Some of the data in the request block (eg pointers) may be modified
!
!--
BEGIN
MAP RB_PTR:REF SD_RMCBLK; !the kind of block we're given a ptr to
LITERAL _GTNM1 = %O'31', !GETTAB Table numbers for user names
_GTNM2 = %O'32';
LOCAL CRE_PTR:REF QSR_EQ; !Points to the page in which we build
!the EQ create message
LOCAL ERRCODE; !For passing errors back up to caller
REGISTER AC; !For the PJOB UUO
IF (CRE_PTR = NEWPAG()) LSS 0 THEN
RETURN CRE$NFP; !Lose quickly, no space left
ZERO (.CRE_PTR, %O'1000'-1); !Start with a clean slate
!Set up the header of the page
CRE_PTR [QSR$MS_ACK] = 1; !We will want an ack
CRE_PTR [QSR$MS_TYP] = QSR$MS_CREATE; !We are doing a create
CRE_PTR [QSR$MS_CNT] = QSR$EQ_SIZE; !Minimum length, files included
!later
!... header set up for now,
! ... start filling in the body
CRE_PTR [QSR$EQ_VERS] = QSRVER; !Fill in the version we know about
CRE_PTR [QSR$EQ_LENGTH] = QSR$EQ_SIZE; !Note size of EQ
CRE_PTR [QSR$EQ_DEVICE] = NETQNAM; !We make entries in the NET Q only
IF .RB_PTR [SD$CNT_JOB] NEQ 0 THEN !If there is a job name, then
CRE_PTR [QSR$EQ_JOBNAME] = RDSIX (RB_PTR [SD$PTR_JOB]); !Pick up job name
CRE_PTR [QSR$EQ_NUMFIL] = !Set the number of files which follow
(SELECTONE .RB_PTR [SD$RMCFUN] OF SET
[RMC$F_DEL]: 1;!Delete
[RMC$F_SEND TO RMC$F_DI]: 2;!Send, get, rename, dir
[OTHERWISE]: (PAGDES (.CRE_PTR);
RETURN CRE$ILF)!No others known
TES) + !... perhaps a logfile follows too
(IF .RB_PTR [RMC$O_CODE] NEQ 0 THEN 1 ELSE 0);
CRE_PTR [QSR$EQ_PRIOR] = .RB_PTR [SD$PRIOR]; !Copy the priority
CRE_PTR [QSR$EQ_AFTER] = .RB_PTR [SD$AFTER]; !include AFTER time
CRE_PTR [QSR$EQ_NODE] = RDSIX (RB_PTR [SD$PTR_RNOD]); !Remote node name
CRE_PTR [QSR$EQ_RJOB] = (CALLI (AC, PJOB); .AC); !Requstor's job #
CRE_PTR [QSR$EQ_FUNC] = .RB_PTR [SD$RMCFUN]; !Function code
CRE_PTR [QSR$EQ_RMCVER] = .RB_PTR [SD$RMCVER]; !RMCOPY version #
CRE_PTR [QSR$EQ_LIMIT] = .RB_PTR [SD$LIMIT]; !The limit (blocks)
CRE_PTR [QSR$EQ_OPTIONS] = .RB_PTR [SD$RMCOPT]; !Option bits
!And the 4 protection feilds
CRE_PTR [QSR$EQ_RPROT_SY] = .RB_PTR [SD$SYSPROTECT];
CRE_PTR [QSR$EQ_RPROT_OW] = .RB_PTR [SD$OWNPROTECT];
CRE_PTR [QSR$EQ_RPROT_GR] = .RB_PTR [SD$GRPPROTECT];
CRE_PTR [QSR$EQ_RPROT_WO] = .RB_PTR [SD$WORPROTECT];
IF .RB_PTR [RMC$O_RTS] NEQ 0 THEN
CRE_PTR [QSR$EQ_RUNSYS] = RDSIX (RB_PTR [SD$PTR_RTS]);
CALLI (AC, GETPPN); !Find our ID
CRE_PTR [QSR$EQ_OWNER] = .AC; !Store it
AC<LH> = -1; !Aim at our job
AC<RH> = _GTNM1;
CALLI (AC, GETTAB); !Get first half of name
CRE_PTR [QSR$EQ_USER] = .AC;
AC<LH> = -1; !Aim at our job
AC<RH> = _GTNM2;
CALLI (AC, GETTAB); !Get second half of name
CRE_PTR [QSR$EQ_USER2] = .AC;
!Now we have finished all the data that goes in the EQ
!So we have to tack on the 1, 2, or 3 file descriptor blocks
!Function Local file Remote file Log file Min # Max #
! files files
!SEND req'd * req'd optional * 2 3
!RETR req'd * req'd optional * 2 3
!REN req'd % req'd optional * 2 3
!DEL error req'd optional * 1 2
!DIR req'd * req'd optional * 2 3
!
!Notes: *) This file's structure must be online at schedule time
! %) This file has the remote DAP code (ie it is a remote file)
! -The local file is always added to the request block first. (except DEL)
! -The remote is always next,
! -the Log file (if requested) is last, with the FP.FLG bit lit
!First, the local file spec.
IF .RB_PTR [SD$RMCFUN] NEQ RMC$F_DEL THEN
IF (ERRCODE =
ADDFILE (.CRE_PTR,
.RB_PTR [SD$PTR_REN],
.RB_PTR [SD$CNT_REN],
(IF .RB_PTR [SD$RMCFUN] EQL RMC$F_REN THEN
.RB_PTR [SD$RDAP_NOD] ELSE
.RB_PTR [SD$LDAP_NOD]),
0, !This is not a log file
(IF .RB_PTR [SD$RMCFUN] EQL RMC$F_REN THEN 0 ELSE 1)))
NEQ CRE$OK THEN (PAGDES (.CRE_PTR); RETURN .ERRCODE);
!Then, always, add the remote file spec
IF (ERRCODE =
ADDFILE (.CRE_PTR,
.RB_PTR [SD$PTR_RMT],
.RB_PTR [SD$CNT_RMT],
.RB_PTR [SD$RDAP_NOD],
0,
0))
NEQ CRE$OK THEN (PAGDES (.CRE_PTR); RETURN .ERRCODE);
!And, if specified, add the log file
IF .RB_PTR [RMC$O_CODE] NEQ 0 THEN
IF (ERRCODE =
ADDFILE (.CRE_PTR,
.RB_PTR [SD$PTR_LOG],
.RB_PTR [SD$CNT_LOG],
.RB_PTR [SD$LDAP_NOD],
1,
1))
NEQ CRE$OK THEN (PAGDES (.CRE_PTR); RETURN .ERRCODE);
!The page message has been built, so let's send it off!
MSGSND (.CRE_PTR, 1); !Send to QUASAR
RCVACK (); !See what he has to say about it
RETURN CRE$OK;
END;
GLOBAL ROUTINE ADDFILE (PAGE, FILE_PTR, FILE_COUNT, DAP_CODE, LOG_FLAG, ONLINE_FLAG)=
!++
! FUNCTIONAL DESCRIPTION:
! This routine will add a file specification to the end of a
! QUASAR-bound create message. The QSR$MS_CNT field of the
! header must reflect the current length of the message.
! This routine adds an FP area (file parameters), including our
! DAP code, log file flag, and online flag.
! It also adds an FD area, which is parsed into a normal -10
! FD area, if the DAP code indicates it is a -10 spec.
! If it is not a -10 spec, then FD is added as
! an ASCIZ string, just like a -20 FD.
! In any case, both the FP and FD lengths are put in the
! length word of the FP header, and their sum is added to the
! QSR$MS_CNT field.
!
! FORMAL PARAMETERS:
!
! PAGE - The address of a page which has been partially
! set up as a QUASAR CREATE message
! FILE_PTR - a CH$PTR to an ASCIZ string describing the file
! FILE_COUNT - The number of characters in FILE_PTR excluding the null
! DAP_CODE - The DAP file system code (-10, RSX, -20, etc)
! LOG_FLAG - If non-zero, indicates this is the log file
! ONLINE_FLAG - If non-zero, this file's structure must be online
! in order to service this request (ie this file does or will live
! on the local system.
!
! IMPLICIT INPUTS:
!
! The QSR$MS_CNT field must be accurate at time of call
!
! IMPLICIT OUTPUTS:
!
! An FP and FD with DAP code, log file bit, online bit, are added
! to the page. QSR$MS_CNT is updated.
! If DEBUG, then the file is typed on the TTY.
!
! ROUTINE VALUE:
!
! If non-zero, there was an error (parsing, room in the block)
! See RMCOPY.REQ under CREAT for complete details
!
! SIDE EFFECTS:
!
! NONE
!
!--
BEGIN
MAP PAGE: REF QSR_EQ;
LOCAL FP: REF QSR$FP;
LOCAL FD: REF QSR$FD;
LOCAL FB: FILE_BLOCK; !Space into which we parse
!-10 filespecs
! First, build the FP area
FP = .PAGE + .PAGE [QSR$MS_CNT]; !Aim past the end
FP [QSR$FP_FPSZ] = QSR$FP_SIZE; !Mark our little header length
PAGE [QSR$MS_CNT] = .PAGE [QSR$MS_CNT] + QSR$FP_SIZE; !Note it up top too
FP [QSR$FP_DAP] = .DAP_CODE; !Note the system type
IF .LOG_FLAG NEQ 0 THEN !Is this the log file?
FP [QSR$FP_FLG] = 1; !Yes, mark the log bit
IF .ONLINE_FLAG NEQ 0 THEN !If this file should be online,
FP [QSR$FP_NFH] = 1; !Yes, mark it.
! Then add on the FD area
FD = .PAGE + .PAGE [QSR$MS_CNT]; !Aim at the FD to be added
IF .G$DBUG THEN
BEGIN
MSGERROR (CH$PTR (UPLIT (%ASCIZ 'Next file: ')), S$MSG);
MSGERROR (.FILE_PTR, S$MSG);
MSGERROR (CH$PTR ((IF .ONLINE_FLAG NEQ 0
THEN UPLIT (%ASCIZ ' On-line')
ELSE UPLIT (%ASCIZ ' Remote'))),
S$MSGCRLF);
END; !Of debugging stuff
IF .ONLINE_FLAG NEQ 0 THEN !If this file is local, then
BEGIN !... Parse it into SIXBIT
FBINI (FB);
FB [FILE$PF_NODE_A] = 0;
IF FPARSE (FB, FILE_PTR) NEQ WIN THEN
BEGIN
RELEASE (FB);
RETURN CRE$PERR
END;
IF ((FD [QSR$FD_STR] = .FB [FILE$DEVICE]) EQL 0 )
AND (.ONLINE_FLAG NEQ 0) THEN
BEGIN
RELEASE (FB);
RETURN CRE$SNR
END;
FD [QSR$FD_NAM] = .FB [FILE$NAME];
FD [QSR$FD_EXT] = .FB [FILE$EXTENSION];
FD [QSR$FD_PPN] = .FB [FILE$PPN];
COPY (FB [FILE$SFD], FD [QSR$FD_PATH], SFDMAX);
FP [QSR$FP_FDSZ] = QSR$FD_SIZE;
PAGE [QSR$MS_CNT] = .PAGE [QSR$MS_CNT] + QSR$FD_SIZE;
RELEASE (FB);
END
ELSE
BEGIN
CH$MOVE (.FILE_COUNT + 1, .FILE_PTR, CH$PTR (.FD));
FP [QSR$FP_FDSZ] = CH$ALLOCATION (.FILE_COUNT + 1);
PAGE [QSR$MS_CNT] = .PAGE [QSR$MS_CNT] + CH$ALLOCATION (.FILE_COUNT + 1);
END;
RETURN CRE$OK;
END;
ROUTINE UNSIX6 (SIXPTRADR, SEVENPTRADR)=
!++
!
! FUNCTIONAL DESCRIPTION:
!
! THIS ROUTINE CONVERTS 6 SIXBIT CHARACTERS TO 6 ASCII(7-BIT) CHARACTERS.
!
! FORMAL PARAMETERS:
!
! SIXPTRADR - The address of a pointer to the first char of the SIXBIT sequence
!
! SEVENPTRADR - The address of a pointer to the first character of the ASCII destination
! sequence.
!
!
! IMPLICIT INPUTS:
!
! THIS ROUTINE ALWAYS CONVERTS SIX CHARACTERS.
! The SIXBIT string to be converted is input via the SIXPTR parameter.
!
! IMPLICIT OUTPUTS:
!
! The ASCII string is written out using the SEVENPTR parameter.
! The pointer specified by SEVENPTRADR is updated to the character
! position following the last character written.
! The pointer specified by SIXPTRADR will point somewhere near the
! end of the SIXBIT sequence converted.
!
! ROUTINE VALUE:
!
! The number of characters converted (ALWAYS = 6).
!
! SIDE EFFECTS:
!
! none
!
!--
BEGIN
LOCAL CHAR; !HOLDS CHARACTER BEING MUNCHED
LOCAL CHARSDONE; !COUNTS HOW MANY CHARS WE CONVERTED
INCR CHARSDONE FROM 0 TO 5 DO
BEGIN
CHAR = CH$RCHAR_A(.SIXPTRADR);
CH$WCHAR_A (.CHAR + %O'40', .SEVENPTRADR);
END;
RETURN .CHARSDONE
END; !END OF ROUTINE
END ELUDOM !End of PAGE.B36