|
OpenVMS Utility Routines Manual
16.3.2.2 Symbiont Processing of Carriage Control
Each input record can be thought of as consisting of three parts:
leading carriage control, data, and trailing carriage control. Taken
together, these three parts are called the composite data record.
Leading and trailing carriage control are determined by the type of
carriage control used in the file and explicit carriage-control
information returned with each record. For embedded carriage control,
however, leading and trailing carriage control is always null.
The type of carriage control returned by the main input routine on the
PSM$K_OPEN request code determines, for that invocation of the input
routine, how the symbiont applies carriage control to each record that
the main input routine returns on the PSM$K_READ request code.
Note that, for all four carriage control types, the first character
returned on the first PSM$K_READ call to an input routine receives
special processing. If that character is a line feed or a form feed and
if the symbiont is currently at line 1, column 1 of the current page,
then the symbiont discards that line feed or form feed.
The Four Types of Carriage Control
The following table briefly describes each type of carriage control and
how the symbiont's main input routine processes it. For a detailed
explanation of each type of carriage control, refer to the description
of the FAB$B_RAT field of the FAB block in the OpenVMS Record Management Services Reference Manual.
Type of Carriage Control |
Symbiont Processing |
Embedded
|
Leading and trailing carriage control are embedded in the data portion
of the input record. Therefore, the symbiont supplies no special
carriage control processing; it assumes that leading and trailing
carriage control are null.
|
Fortran
|
The first byte of each data record contains a Fortran carriage-control
character. This character specifies both the leading and trailing
carriage control for the data record. The symbiont extracts the first
byte of each data record and interprets that byte as a Fortran
carriage-control character. If the data record is empty, the symbiont
generates a leading carriage control of line feed and a trailing
carriage control of carriage return.
|
PRN
|
Each data record contains a 2-byte header that contains the
carriage-control specifier. The first byte specifies the carriage
control to apply before printing the data portion of the record. The
second byte specifies the carriage control to apply after printing the
data portion. The abbreviation PRN stands for print-file format.
Unlike other types of carriage control, PRN carriage control
information is returned through the
funcarg argument of the main input routine; this
occurs with the PSM$K_READ request. The
funcarg argument specifies a longword; your routine
writes the 2-byte PRN carriage control specifier into the first two
bytes of this longword.
|
Implied
|
The symbiont provides a leading line feed and a trailing carriage
return. But if the data record consists of a single form feed, the
symbiont sets to null the leading and trailing carriage control for
that record, and the leading carriage control for the record that
follows it.
|
16.3.3 Writing a Format Routine
To write a format routine, follow the modification procedure described
in Section 16.3. Do not replace the symbiont's main format routine.
Instead, modify its action by writing input and output filter routines.
These execute immediately before and after the main format routine,
respectively. The main formatting routine uses an undocumented and
nonpublic interface; you cannot replace the main formatting routine.
The DCL command PRINT/PASSALL bypasses the main format routine of the
print symbiont.
See Section 16.3.5 for additional information about other function codes
used in the user-written formatting routine.
16.3.3.1 Internal Logic of the Symbiont's Main Format Routine
The main format routine contains all the logic necessary to convert
composite data records to a data stream for output. Actions taken by
the format routine include the following:
- Tracking the current column and line
- Implementing the special processing of the first character of the
first record
- Implementing the alignment data mask specified by the DCL command
START/QUEUE/ALIGN=MASK
- Handling margins as specified by the forms definition
- Initiating processing of page headers when specified by the DCL
command PRINT/HEADER
- Expanding leading and trailing carriage control
- Handling line overflow
- Handling page overflow
- Expanding tab characters to spaces for some devices
- Handling escape sequences
- Accumulating accounting information
- Implementing double-spacing when specified by the DCL command
PRINT/SPACE
- Implementing automatic page ejection when specified by the DCL
command PRINT/FEED
The symbiont's main format routine uses a special rule when processing
the first character of the first composite data record returned by an
input routine. (A composite data record is the input data record and a
longword that contains carriage-control information for the input data
record.) This rule is that if the first character is a vertical format
effector (form feed or line feed) and if the symbiont has processed no
printable characters on the current page (that is, the current position
is column 1, line 1), then that vertical format effector is discarded.
16.3.4 Writing an Output Routine
To write an output routine, follow the modification procedure described
in Section 16.3.
The print symbiont calls your output routine. Input arguments are
supplied by the print symbiont; output arguments and status values are
returned by your routine to the print symbiont. For this reason, your
output routine must have the call interface that is described in the
USER-OUTPUT-ROUTINE routine.
When the print symbiont calls your routine, it specifies in one of the
input arguments---the func argument---the reason for
the call. Each reason has a corresponding function code.
There are several function codes that the print symbiont can supply
when it calls your output routine. Your routine must contain the logic
to respond to the following function codes: PSM$K_OPEN, PSM$K_WRITE,
PSM$K_WRITE_NOFORMAT, and PSM$K_CLOSE.
It is not required that your output routine contain the logic to
respond to the other function codes, but you can provide this logic if
you want to.
A complete list and description of all relevant function codes for
output routines is provided in the description of the
func argument of the USER-OUTPUT-ROUTINE routine.
See Section 16.3.5 for additional information about other function codes.
16.3.4.1 Internal Logic of the Symbiont's Main Output Routine
When the symbiont calls the main output routine with the PSM$K_OPEN
function code, the main output routine takes the following steps:
- Allocates the print device
- Assigns a channel to the device
- Obtains the device characteristics
- Returns the device-status longword in the funcarg
argument (for more information, see the description of the
SMBMSG$K_DEVICE_STATUS message item in Chapter 17)
- Returns an error if the device is not a terminal or a printer
When this routine receives a PSM$K_WRITE service request code, it sends
the contents of the symbiont output buffer to the device for printing.
When this routine receives a PSM$K_WRITE_NOFORMAT service request code,
it sends the contents of the symbiont output buffer to the device for
printing and suppresses device drive formatting as appropriate for the
device in use.
When this routine receives a PSM$K_CANCEL service request code, it
requests the device driver to cancel any outstanding output operations.
When this routine receives a PSM$K_CLOSE service request code, it
deassigns the channel to the device and deallocates the device.
16.3.5 Other Function Codes
A status PSM$_PENDING might not be returned whenever the symbiont
notifies user-written input, output, and format routines using the
following message function codes:
Function Code |
Description |
PSM$K_START_STREAM
|
Job controller sends a message to the symbiont to start a queue
|
PSM$K_START_TASK
|
Symbiont parses a message from job controller directing it to start a
queue
|
PSM$K_PAUSE_TASK
|
Job controller sends a message to the symbiont to suspend processing of
the current task
|
PSM$K_STOP_STREAM
|
Job controller sends a message to the symbiont to stop the queue
|
PSM$K_STOP_TASK
|
Job controller sends a message to the symbiont to stop the task
|
PSM$K_RESUME_TASK
|
Job controller sends a message to the symbiont to resume processing of
the current task
|
PSM$K_RESET_STREAM
|
Same as PSM$K_STOP_STREAM
|
16.3.6 Writing a Symbiont Initialization Routine
Writing a symbiont initialization routine involves writing a program
that calls the following:
- PSM$REPLACE once for each routine (input, output, or format) that
you have written. PSM$REPLACE identifies your routines to the symbiont.
- PSM$PRINT exactly once after you have identified all your service
routines using PSM$REPLACE.
Table 16-1 lists all routine codes that you can specify in the
PSM$REPLACE routine. Choosing the correct routine code is important
because the code specifies when the symbiont will call your routine.
The functions of these routines are described further in the
description of the PSM$REPLACE routine.
For those input routines that execute in a predefined sequence, the
second column contains a number showing the order in which that input
routine is called relative to the other input routines for a single
file job. If the routine does not execute in a predefined sequence, the
second column contains the character x.
Column three specifies whether the routine is an input, format, or
output routine; this information directs you to the section describing
how to write a routine of that type.
Column four specifies whether there is a symbiont-supplied routine
corresponding to that routine code. The codes for the input-filter and
output-filter routines, which have no corresponding routines in the
symbiont, allow you to specify new routines for inclusion in the
symbiont.
Table 16-1 Routine Codes for Specification to PSM$REPLACE
Routine Code |
Sequence |
Function |
Supplied |
PSM$K_JOB_SETUP
|
1
|
Input
|
Yes
|
PSM$K_FORM_SETUP
|
2
|
Input
|
Yes
|
PSM$K_JOB_FLAG
|
3
|
Input
|
Yes
|
PSM$K_JOB_BURST
|
4
|
Input
|
Yes
|
PSM$K_FILE_SETUP
|
5
|
Input
|
Yes
|
PSM$K_FILE_FLAG
|
6
|
Input
|
Yes
|
PSM$K_FILE_BURST
|
7
|
Input
|
Yes
|
PSM$K_FILE_SETUP_2
|
8
|
Input
|
Yes
|
PSM$K_MAIN_INPUT
|
9
|
Input
|
Yes
|
PSM$K_FILE_INFORMATION
|
10
|
Input
|
Yes
|
PSM$K_FILE_ERRORS
|
11
|
Input
|
Yes
|
PSM$K_FILE_TRAILER
|
12
|
Input
|
Yes
|
PSM$K_JOB_RESET
|
13
|
Input
|
Yes
|
PSM$K_JOB_TRAILER
|
14
|
Input
|
Yes
|
PSM$K_JOB_COMPLETION
1
|
15
|
Input
|
Yes
|
PSM$K_PAGE_SETUP
|
x
|
Input
|
Yes
|
PSM$K_PAGE_HEADER
|
x
|
Input
|
Yes
|
PSM$K_LIBRARY_INPUT
|
x
|
Input
|
Yes
|
PSM$K_INPUT_FILTER
|
x
|
Formatting
|
No
|
PSM$K_MAIN_FORMAT
|
x
|
Formatting
|
Yes
|
PSM$K_OUTPUT_FILTER
|
x
|
Formatting
|
No
|
PSM$K_OUTPUT
1
|
x
|
Output
|
Yes
|
1The job completion (PSM$K_JOB_COMPLETION) and output
(PSM$K_OUTPUT) routines are not replaceable when using the LAT protocol
option.
16.3.7 Integrating a Modified Symbiont
To integrate your user routine and the symbiont initialization routine,
perform the following steps; note that the sequence of steps described
here assumes that you will be debugging the modified symbiont:
- Compile or assemble the user routine and the symbiont
initialization routine into an object module.
- Enter the following DCL command:
$ LINK/DEBUG your-symbiont
|
The file name your-symbiont is the object module built in
Step 1. Symbols necessary for this link operation are located in the
shareable images SYS$SHARE:SMBSRVSHR.EXE and SYS$LIBRARY:IMAGELIB.EXE.
The linker automatically searches these shareable images and extracts
the necessary information.
- Place the resulting executable symbiont image in SYS$SYSTEM.
- Locate two unallocated terminals: one at which to issue DCL
commands and one at which to debug the symbiont image.
- Log in on one of the terminals under UIC [1,4], which is the system
manager's account. This terminal is the one at which you enter DCL
commands. Do not log in at the other terminal.
- Enter the following DCL command:
$ SET TERMINAL/NODISCONNECT/PERMANENT _TTcu:
|
The variable _TTcu: is the physical terminal name of the
terminal at which you want to debug (the terminal at which you are not
logged in). You must specify the underscore (_) and colon (:)
characters.
- Enter the following DCL commands:
$ DEFINE/GROUP DBG$INPUT _TTcu:
$ DEFINE/GROUP DBG$OUTPUT _TTcu:
|
The variable _TTcu: specifies the physical terminal name
of the terminal at which you will be debugging. Note that other users
having a UIC with group number 1 should not use the debugger at the
same time.
- Initialize the queue by entering the following DCL command:
$ INITIALIZE/QUEUE/PROCESSOR= your-symbiont /ON= printer_name
|
The symbiont image specified by the file name
your-symbiont must reside in SYS$SYSTEM. Note too that the
/PROCESSOR qualifier accepts only a file name; the device, directory,
and file type default to SYS$SYSTEM:.EXE. The /ON qualifier
specifies the device that will be served by the symbiont while you
debug the symbiont.
- Enter the following DCL command to execute the modified symbiont
routine:
$ PRINT/HEADER/QUEUE=queue-id
|
Enter the following DCL command to start the queue and invoke the
debugger:
- After you debug your symbiont, relink the symbiont by entering the
following DCL command:
$ LINK/NOTRACEBACK/NODEBUG your-symbiont
|
- Deassign the logical names DBG$INPUT and DBG$OUTPUT so that they
will not interfere with other users in UIC group 1.
16.4 Using the PSM Routines: An Example
Example 16-1 shows how to use PSM routines to supply a page header
routine in a VAX MACRO program.
Example 16-1 Using PSM Routines to Supply a
Page Header Routine in a VAX MACRO Program |
.TITLE EXAMPLE - Example user modified symbiont
.IDENT 'V03-000'
;++
; THIS PROGRAM SUPPLIES A USER WRITTEN PAGE HEADER
; ROUTINE TO THE STANDARD SYMBIONT. THE PAGE HEADER
; INCLUDES THE SUBMITTER'S ACCOUNT NAME AND USER NAME,
; THE FULL FILE SPECIFICATION, AND THE PAGE NUMBER.
; THE HEADER LINE IS UNDERLINED BY A ROW OF DASHES
; PRINTED ON A SECOND HEADER LINE.
;--
.LIBRARY /SYS$LIBRARY:LIB.MLB/
;
; System definitions
;
$PSMDEF ; Symbiont definitions
$SMBDEF ; Message item definitions
$DSCDEF ; Descriptor definitions
;
; Define argument offsets for user supplied services called by symbiont
;
CONTEXT = 04 ; symbiont context
WORK_AREA = 08 ; user context
FUNC = 12 ; function code
FUNC_DESC = 16 ; function dependent descriptor
FUNC_ARG = 20 ; function dependent argument
;
; Macro to create dynamic descriptors
;
.MACRO D_DESC
.WORD 0 ; DSC$W_LENGTH = 0
.BYTE DSC$K_DTYPE_T ; DSC$B_DTYPE = STRING
.BYTE DSC$K_CLASS_D ; DSC$B_CLASS = DYNAMIC
.LONG 0 ; DSC$A_POINTER = 0
.ENDM
;
; Storage for page header information
;
FILE: D_DESC ; file name descriptor
USER: D_DESC ; user name descriptor
ACCOUNT: D_DESC ; account name descriptor
PAGE: .LONG 0 ; page number
LINE: .LONG 0 ; line number
;
; FAO control string and work buffer. Header format:
; "[account,name] filename ........ Page 9999"
;
FAO_Ctrl: .ASCID /!71<[!AS, !AS] !AS!>Page 9999/
FAO_Ctrl_2: .ASCID /!4UL/
FAO_DESC: .LONG 80 ; work buffer descriptor
.ADDRESS FAO_BUFF
FAO_BUFF: .BLKB 80 ; work buffer
;
; Own storage for values passed by reference
;
CODE: .LONG 0 ; service or item code
STREAMS: .LONG 1 ; number of simultaneous streams
BUFSIZ: .LONG 2048 ; output buffer size
LINSIZ: .WORD 81 ; line size for underlines
;
; Main routine -- invoked at image startup
;
START: .WORD 0 ; save nothing because this routine uses only R0 and R1
;
; Supply private page header routine
;
MOVZBL #PSM$K_PAGE_HEADER,CODE ; set the service code
PUSHAL HEADER ; address of modified routine
PUSHAL CODE ; address of service code
CALLS #2,G^PSM$REPLACE ; replace the routine
BLBC R0,10$ ; exit if any errors
;
; Transfer control to the standard symbiont
;
PUSHAL BUFSIZ ; address of output buffer size
PUSHAL STREAMS ; address of number of streams
CALLS #2,G^PSM$PRINT ; invoke standard symbiont
10$: RET
;
; Page header routine
;
HEADER: .WORD 0 ; save nothing
;
; Check function code
;
CMPL #PSM$K_START_TASK,@FUNC(AP) ; new task?
BEQL 20$ ; branch if so
CMPL #PSM$K_READ,@FUNC(AP) ; READ function?
BNEQ 15$
BRW 50$ ; branch if so
15$: CMPL #PSM$K_OPEN, @FUNC(AP) ; OPEN function?
BNEQ 16$
BRW 66$ ; branch if so
16$: MOVL #PSM$_FUNNOTSUP,R0 ; unsupported function
RET ; return to symbiont
;
; Starting a new file
;
20$:
CLRL PAGE ; reset the page number
MOVZBL #2,LINE ; and the line number
;
; Get the account name
;
MOVZBL #SMBMSG$K_ACCOUNT_NAME,CODE ; set item code
PUSHAL ACCOUNT ; address of descriptor
PUSHAL CODE ; address of item code
PUSHAL @CONTEXT(AP) ; address of symbiont ctx value
CALLS #3,G^PSM$READ_ITEM_DX ; read it
BLBC R0,40$ ; branch if any errors
;
; Get the file name
;
MOVZBL #SMBMSG$K_FILE_SPECIFICATION,CODE ; set item code
PUSHAL FILE ; address of descriptor
PUSHAL CODE ; address of item code
PUSHAL @CONTEXT(AP) ; address of symbiont ctx value
CALLS #3,G^PSM$READ_ITEM_DX ; read it
BLBC R0,40$ ; branch if any errors
;
; Get the user name
;
MOVZBL #SMBMSG$K_USER_NAME,CODE ; set item code
PUSHAL USER ; address of descriptor
PUSHAL CODE ; address of item code
PUSHAL @CONTEXT(AP) ; address of symbiont ctx value
CALLS #3,G^PSM$READ_ITEM_DX ; read it
BLBC R0,40$ ; branch if any errors
;
; Set up the static header information that is constant for the task
;
$FAO_S CTRSTR = FAO_Ctrl, - ; FAO control string desc
OUTBUF = FAO_DESC, - ; output buffer descriptor
P1 = #ACCOUNT, - ; account name descriptor
P2 = #USER, - ; user name descriptor
P3 = #FILE ; file name descriptor
BLBC R0,40$ ; branch if any errors
MOVL #PSM$_FUNNOTSUP,R0 ; unsupported function
40$: RET ; return usupported status or error
;
; Read a page header
;
50$:
DECL LINE ; decrement the line number
BEQL 60$ ; branch if second read
BLSS 70$ ; branch if third read
;
; Insert the page number into the header
;
INCL PAGE ; increment the page number
MOVAB FAO_BUFF+76,FAO_DESC+4 ; point to page number buffer
$FAO_S CTRSTR = FAO_Ctrl_2, - ; FAO control string desc
OUTBUF = FAO_DESC, - ; output buffer descriptor
P1 = PAGE ; page number
MOVAB FAO_BUFF,FAO_DESC+4 ; point to work buffer
BLBC R0,55$ ; return if error
;
; Copy the line to the symbiont's buffer
;
PUSHAB FAO_DESC ; work buffer descriptor
PUSHL FUNC_DESC(AP) ; symbiont descriptor
CALLS #2,G^STR$COPY_DX ; copy to symbiont buffer
55$: RET ; return success or any error
;
; Second line -- underline header
;
60$:
PUSHL FUNC_DESC(AP) ; symbiont descriptor
PUSHAL LINSIZ ; number of bytes to reserve
CALLS #2,G^STR$GET1_DX ; reserve the space
BLBC R0,67$ ; exit if error
MOVL FUNC_DESC(AP),R1 ; get address of descriptor
MOVL 4(R1),R1 ; get address of buffer
MOVAB 80(R1),R0 ; set up transfer limit
65$: MOVB #^A/-/,(R1)+ ; fill with dashes
CMPL R0,R1 ; reached limit?
BGTRU 65$ ; branch if not
MOVB #10,(R1)+ ; extra line feed
66$: MOVZBL #SS$_NORMAL,R0 ; set success
67$: RET ; return
;
; Done with this page header
;
70$:
MOVL #PSM$_EOF,R0 ; return end of input
MOVZBL #2,LINE ; reset line counter
RET ; return
.END START
|
|