HP OpenVMS Systems Documentation

Content starts here

OpenVMS Utility Routines Manual


Previous Contents Index

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:

  1. Allocates the print device
  2. Assigns a channel to the device
  3. Obtains the device characteristics
  4. 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)
  5. 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:

  1. PSM$REPLACE once for each routine (input, output, or format) that you have written. PSM$REPLACE identifies your routines to the symbiont.
  2. 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:

  1. Compile or assemble the user routine and the symbiont initialization routine into an object module.
  2. 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.
  3. Place the resulting executable symbiont image in SYS$SYSTEM.
  4. Locate two unallocated terminals: one at which to issue DCL commands and one at which to debug the symbiont image.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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:


    $ START/QUEUE queue-name
    
  10. After you debug your symbiont, relink the symbiont by entering the following DCL command:


    $ LINK/NOTRACEBACK/NODEBUG your-symbiont
    
  11. 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



Previous Next Contents Index