Previous | Contents | Index |
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. |
To write a format routine, follow the modification procedure described in Section 18.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 18.3.5 for additional information about other function codes
used in the user-written formatting routine.
18.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:
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.
18.3.4 Writing an Output Routine
To write an output routine, follow the modification procedure described in Section 18.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 18.3.5 for additional information about other function codes.
18.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:
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.
18.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 |
Writing a symbiont initialization routine involves writing a program that calls the following:
Table 18-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.
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 |
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:
$ LINK/DEBUG your-symbiont |
$ SET TERMINAL/NODISCONNECT/PERMANENT _TTcu: |
$ DEFINE/GROUP DBG$INPUT _TTcu: $ DEFINE/GROUP DBG$OUTPUT _TTcu: |
$ INITIALIZE/QUEUE/PROCESSOR= your-symbiont /ON= printer_name |
$ PRINT/HEADER/QUEUE=queue-id |
$ START/QUEUE queue-name |
$ LINK/NOTRACEBACK/NODEBUG your-symbiont |
Example 18-1 shows how to use PSM routines to supply a page header routine in a VAX MACRO program.
Example 18-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 |