Chapter 8
Using Sort Subroutines (IBM Only)
The ibm dialect of Endless PL/I for POSIX Systems provides the IBM sort subroutines,
PLISRTA, PLISRTB, PLISRTC and PLISRTD. These routines rely on the POSIX-compatible sort utility that ships as
part of the local system.
To use the sort routines, add calls to your program to one of the Sort program's entry points. The call passes information on the fields to be sorted. You must specify information about the data to be sorted, the type of sort to perform, the format and destination of the output, and so on.
The Sort program called by Endless PL/I for POSIX Systems provides a flexible interface. You define the sorting fields in each record and the order of the sort. You can also specify whether the data originates and/or is output to a file or a user-written routine.
The following sections describe how to use the sort routines.
Note
Do not name any of your routines "SORT" if you are using the sort subroutines. To be safe, do not use any upper or lower case variants of the word "sort" for routine names.
8.1. General Information
Before writing the call to the Sort program, you must determine the following:
Whether you want to sort by the entire record or by specific fields in the record.
If you are sorting by specific fields, how many, and where in the record they are located.
What the data types and sizes of the sort fields are.
The type of sort you want to do, ascending or descending.
This section contains information on the PL/I sorting interface. Understanding the interface will help you decide which routine to use and what to pass to the routine. The sections following Section 8.2, “Calling the Sort Subroutines” give details on each portion of the call. They contain information on what specifying sort fields, the available sort types, how to determine storage needs, and so on.
8.1.1. How the Sort Program Interface Works
The Sort program is part of the
Endless PL/I for POSIX Systems run-time library. It copies each record to a temporary file, then has the system's
sort command order the records by their sort fields, and finally reads the records back in sorted order.
Records whose sort fields are equal stay in the order in which they were read. The temporary files are created in
/tmp and removed when the sort ends; see Section 8.2.4, “Specifying Storage”.
The Endless PL/I for POSIX Systems Sort interface provides four entry points to the Sort program. Each entry point specifies a different combination of data source and destination, either from and to a file or from and to a subroutine, or a combination of the two. The entry points are described in Section 8.1.2, “Determining Which Entry Point to Use”.
The Sort program handles the complete sort operation. It communicates with the calling program, and any specified user-written input or output routines, by using return codes.
To use the Sort program, insert a CALL statement in your program. The CALL statement specifies one of the four Sort entry points. It must contain the correct arguments for the specified entry point. Once the Sort program is called, it does the following:
Gets records from a file or calls the specified user-written input subroutine to get records, using the selection criteria specified in the call.
Sorts the records according to the sorting criteria specified in the call.
Places the sorted records in a file or calls the specified user-written output routine to handle the records.
Passes a return code, indicating success or failure, back to the calling program.
Note that user-written input or output routines become, for all purposes, part of the Sort program for the duration of the sort. The Sort program calls the routines at the appropriate point in processing. Return codes control the flow of control between the Sort program and the input or output routines, telling the Sort program whether it should call the routine again or continue to the next stage in processing.
8.1.2. Determining Which Entry Point to Use
When sorting in PL/I, the data can originate in a file and be output to another file, or it can originate and/or be
output to a user-written PL/I subroutine. The simplest use of the Sort program is to sort a file and direct the results into
another file. The input file must be in the current directory, and the output file is created in the current directory. By
default, the input file is sortin and the output file is sortout; see Section 8.2.7, “Specifying a File Prefix”.
Using subroutines lets you manipulate the data before and/or after the sort. For example, you could read a file and sort only certain records, or you could print the resulting sorted data. The format of input and output routines is described more fully in Section 8.3, “Input and Output Routines”.
The Sort program interface provides four entry points. The following table lists the entry points to the Sort program and specifies the source and destination of data in each case:
| Sort Subroutine Entry Point | Data Source | Data Destination |
|---|---|---|
| PLISRTA | File | File |
| PLISRTB | Subroutine | File |
| PLISRTC | File | Subroutine |
| PLISRTD | Subroutine | Subroutine |
8.2. Calling the Sort Subroutines
Once you know your sort requirements and have decided on a sort routine, you can write the call to the PLISRTx routine. The following sections give the general syntax of the call and details on each required or optional argument.
8.2.1. General Format of the Sort Interface Call
The format of the call to any of the four sort entry points is:
CALL PLISRTx (sort-specification,record-specification,maximum-storage,return-code[,input-routine] [,output-routine] [,file-prefix,message-level] );
sort-specificationA character-string expression of the form:
'bSORTbFIELDS=(
start1,length1,format1,order1[,startn,lengthn,formatn,ordern] ) [,OPTION1[...,OPTIONn]]b'This is a required argument.
record-specificationA character-string expression of the form:
'bRECORDbTYPE=RECTYPE[,LENGTH=(
rec-length[,,min-length,modal-length)]b'This is a required argument.
maximum-storageA fixed binary (31) expression giving the amount of storage the Sort program may use. It is accepted for compatibility and has no effect; see Section 8.2.4, “Specifying Storage”.
This is a required argument.
return-codeFixed binary (31) user-supplied variable in which Sort returns a code indicating success or failure. This is a required argument.
input-routineName of the user-written PL/I routine that supplies records to the Sort program. Required for PLISRTB and PLISRTD; invalid otherwise.
output-routineName of the user-written PL/I routine that receives sorted records from the Sort program. Required for PLISRTC and PLISRTD; invalid otherwise.
file-prefixOptional. A character-string expression of up to four characters that replaces the default prefix
sortin the names of the input and output files.message-levelOptional. A character-string expression of two characters, NO, AP or CP. It is accepted for compatibility and has no effect; see Section 8.2.8, “Specifying Message Level”.
The following sections discuss each of these arguments in detail.
8.2.2. Specifying the Sort Fields
The sort-specification is the first argument to PLISRTx. It has the following
syntax:
'bSORTbFIELDS=(start1,length1,format1,order1,...startn,lengthn,formatn,ordern)[OPTION1[...,OPTIONn]]b'
b
A single blank space, before SORT and between SORT and FIELDS. Exactly one blank is required in each place, and no other blank spaces are allowed before the field list.
start,length,format,order
The sorting information specifies the location of each sort field and how to sort by each field. You can specify several sets of sorting information. Together the fields must not exceed 2,000 bytes, counting each ZD, PD, FI and BI field as 8 bytes.
If you specify more than one sort field, the Sort program sorts on each field in order. In other words, it sorts on the first field, then sorts groups of records that have the same first field according to the second field, and so on.
Sort fields can overlap each other.
The following list gives details on each element of the sorting information.
OPTION1...,OPTIONn
Additional options to the Sort program. Use a comma to separate any options from the preceding arguments and from each other. Do not put blank spaces between options. Any option not listed here causes the sort to fail.
The following list gives details on each additional option.
| FILSZ=y | Gives the number of records in the sort, exactly or, preceded by the letter E, approximately. Accepted for compatibility; it has no effect. |
| SKIPREC=y | Accepted for compatibility, but it has no effect: no records are skipped. To leave records out of a sort, use PLISRTB or PLISRTD with an input routine that does not pass them on. |
| EQUALS|NOEQUALS | Accepted for compatibility. Records with equal sort fields always stay in the order in which they were read, whichever is given. |
8.2.3. Specifying Record Information
The record-specification is the second argument to PLISRTx. It has the following
syntax:
'bRECORDbTYPE=RECTYPE[,LENGTH=(rec-length[,,min-length,modal-length)]b'
b
One or more blank spaces. These blank spaces are mandatory; no other blank spaces are allowed.
RECTYPE
The type of the records. Only fixed-length records are supported:
| F | fixed length |
The types V (varying length EBCDIC), D (varying length ASCII) and S (stream) are recognized, but a sort with any of them fails. Records of varying length can still be sorted through an input routine; see Section 8.3.2, “Writing Input Routines”.
LENGTH
The LENGTH argument is required.
Every record is exactly rec-length bytes long. The maximum length you can specify is 32767
bytes.
rec-length | Length of the records to be sorted, in bytes. Required. |
| ,, | two sort arguments not used in the PL/I interface. Required if you specify any of the following arguments. |
min-length | The minimum length of varying-length records. Accepted for compatibility; it has no effect. Optional. Must be
specified if modal-length is specified. |
modal-length | The most common length of varying-length records. Accepted for compatibility; it has no effect. Optional. |
8.2.4. Specifying Storage
You can specify storage either by declaring a fixed binary (31) variable and passing the variable to the Sort routine, or by passing a constant value. For example, either one of the following calls will invoke the Sort program PLISRTA to sort a 40-byte character record using 512,000 bytes of storage:
DECLARE RETURN_CODE FIXED BIN(31); CALL PLISRTA(' SORT FIELDS=(1,40,CH,D) ', ' RECORD TYPE=F,LENGTH=(40) ', 512000, RETURN_CODE);
or:
DECLARE RETURN_CODE FIXED BIN(31); DECLARE MAXSTORAGE FIXED BIN(31) INITIAL(512000); CALL PLISRTA(' SORT FIELDS=(1,40,CH,D) ', ' RECORD TYPE=F,LENGTH=(40) ',MAXSTORAGE,RETURN_CODE);
You must specify a value for storage, but it has no effect: the sort command manages its own memory,
and uses temporary files when the data does not fit.
The Sort program needs space in /tmp for a copy of every record being sorted, plus a line of about
twice the length of the sort fields for each record. The sort command may need further temporary space of
its own (in /tmp, unless the environment variable TMPDIR names another
directory).
The sort command is run through the shell with the environment variable LC_ALL set to
C, so that the records are compared byte by byte and not according to the locale. It must be in the
search path (PATH).
Only one sort can run at a time in a program. Do not call PLISRTA, PLISRTB, PLISRTC or PLISRTD from an input or output routine.
8.2.5. Using the Return Code
The required return code argument is a variable to contain the fixed binary(31) value returned by the sort program. The sort program returns one of the following:
0—success
16—failure
In your calling program, declare a fixed binary(31) variable to contain return code.
To determine success or failure of the sort, check the return code after the call to the Sort program. If the
sort-specification or record-specification is not valid, the Sort
program also writes a message ending in “in SORT statement **** Execution aborted” to standard output and
raises the ERROR condition; if an ON-unit for ERROR returns, the return code is 16.
For example, assume that the variable RETURN_CODE was declared for use with the Sort program. The following code calls PLISRTD to sort 80-byte records on a single 10-character field at the start of the record. The input and output routines are SORT_IN and SORT_OUT. After returning from the call, the code checks the success of the sort:
CALL PLISRTD (' SORT FIELDS=(1,10,CH,A) ', ' RECORD TYPE=F,LENGTH=(80) ', 256000,RETURN_CODE,SORT_IN,SORT_OUT); IF RETURN_CODE ^= 0 THEN DO; PUT SKIP LIST ('Error! Return code = '||RETURN_CODE); SIGNAL ERROR; END;
8.2.6. Specifying Input and Output Routines
The input-routine argument is used with PLISRTB and PLISRTD. The
output-routine argument is used with PLISRTC and PLISRTD. These arguments are required for the
specified routines. They give the name of the user-written PL/I routine that supplies records to or accepts records from the
Sort program. See Section 8.3, “Input and Output Routines” for more information on writing routines for use with the
Sort program. See the preceding section for an example of calling PLISRTD, specifying the user-written input routine SORT_IN
and the user-written output routine SORT_OUT.
8.2.7. Specifying a File Prefix
The optional file prefix argument lets you replace
the default prefix sort in the names of the input and output files the Sort program uses. Using a file
prefix lets you sort several files in turn without renaming them. For example, you can specify 'TEMP' for
the file prefix, as shown in the following example:
CALL PLISRTA(' SORT FIELDS=(1,40,CH,D) ', ' RECORD TYPE=F,LENGTH=(40) ',STOR,RETURN_CODE,'TEMP');
In this case, PLISRTA reads TEMPin and writes TEMPout. The prefix is used
exactly as given, followed by in or out in lower case; by default the files are
sortin and sortout. The temporary files in /tmp are named
after the prefix too.
The file prefix is at most four characters; a blank ends it early. A blank or null string selects the default prefix,
sort.
If you omit this argument but require the message-level argument, specify a null string ('')
in place of the file prefix argument.
8.2.8. Specifying Message Level
The message-level argument is accepted for
compatibility with other implementations of PL/I, where it controls the Sort program's diagnostic messages. Here the Sort
program writes no such messages, so it has no effect. It must still be one of the following, or the sort fails; AP is
assumed if it is omitted:
NO—no messages.
AP—all messages.
CP—critical messages only.
If you omit the file-prefix argument but require the message-level
argument, specify a null string ('') in place of the file prefix argument. For example, the following call invokes
PLISRTB to sort on the first 12 characters of
a 30 character record in ascending order. Input is passed to the Sort program by a user-written routine called READ_DATA,
the file prefix is defaulted to sort, and no messages are passed back:
CALL PLISRTB(' SORT FIELDS=(1,12,CH,A) ', ' RECORD TYPE=F,LENGTH=(30) ',MAXSTORAGE,RETURN_CODE, READ_DATA,' ','NO');
8.3. Input and Output Routines
When sorting in PL/I, the data can originate in a file and be output to another file. This is done using PLISRTA, the simplest interface.
Input and output routines give you additional flexibility. When using routines PLISRTB, PLISRTC, and PLISRTD, your sort data can originate and/or be output to a user-written routine. These input and output routines are written in PL/I. They can be either internal or external procedures. If they are internal to the calling program (the program routine with the call to the Sort program), the input and output routines follow the usual scoping rules. Whether or not the routines are internal to the calling program, the input and output routine names must be known within the scope of the block containing the call.
The Sort program calls the input and output routines once for each record being sorted. Therefore, write each routine to handle one record at a time. Note that this means automatic variables will not retain their value between calls. If a variable, such as a counter, must retain its value between calls, either declare it STATIC or declare it in the containing block.
8.3.1. Using the PLIRETC Built-In Subroutine
The PLIRETC built-in subroutine sets the PL/I return code. Use PLIRETC in your input and output routines to return a value to the Sort program. The PLIRETV built-in function returns the current value of the PL/I return code, so a program can also use the two for its own purposes, to pass a value from one procedure to another. Both are available in the ibm dialect only.
The format of the subroutine call is:
CALL PLIRETC(return-code)return-codeAn expression, converted to fixed binary(15). It becomes the PL/I return code.
The PL/I return code is zero when the program starts, and keeps the value of the most recent call to PLIRETC, in any procedure. The Sort program sets it as well: to 4 before it passes the first record to an output routine, so an output routine that does not call PLIRETC goes on receiving records. The PL/I return code is not passed to the operating system when the program ends; it is not the program's exit status.
For example, the following procedure records whether it found any errors, and its caller tests the result:
CHECK_INPUT: PROCEDURE; ... IF ERRORS_FOUND THEN CALL PLIRETC(8); ELSE CALL PLIRETC(0); END CHECK_INPUT; CALL CHECK_INPUT; IF PLIRETV ^= 0 THEN PUT SKIP LIST ('Errors in input.');
8.3.2. Writing Input Routines
Use an input routine to manipulate the data in some way before sorting it. For example, you might want to print the data or generate or modify the sorting fields.
The input routine and the Sort program communicate using calls to PLIRETC. The Sort program recognizes two PLIRETC return codes from an input routine:
8—All records have been sent. The Sort program ignores any record returned by your input routine when the return code is 8.
12—Include this record in the sort.
Any other return code is treated as 12: the record is included.
Calls made to PLISRTB or PLISRTD require an input routine. When using an input handling routine, the sort processing is as follows:
Your program calls PLISRTB or PLISRTD.
The Sort program assumes control and calls the input routine.
The input routine assumes control and does the following:
The input routine checks to see if there are any more records to sort. If not, it calls PLIRETC with a
return-codeof 8.If there are records, the input routine does any processing required, then calls PLIRETC with a
return-codeof 12, and RETURNs, passing back a record in character-string format.
The Sort program assumes control again and processes the record. It continues to call the input routine and receive records until it receives a return code of 8.
The Sort program sorts all the records.
The Sort program either writes the sorted records to the output file or calls the output routine, described in the next section.
The Sort program sets up the
return-codeand returns to the statement after the CALL PLISRTx statement.
The input routine passes records back to the Sort program in the RETURN statement. Therefore you must specify the RETURNS attribute in the input routine PROCEDURE statement.
Returned records must be in character-string format. They can be fixed or varying. A varying string is padded with blanks to the record length given in the LENGTH argument, so sort-field positions count from the first character of the string. A varying string longer than the record length makes the sort fail.
Example
SORT_INPUT_ROUTINE: PROCEDURE RETURNS(CHAR(80)); /* you must specify the returns attribute. For varying strings, the length must be the maximum length. DECLARE CUST_RECORD CHARACTER(80); DECLARE CUSTOMER_FILE FILE RECORD INPUT; ON ENDFILE(CUSTOMER_FILE) BEGIN; PUT SKIP(2) LIST('End of customer file input.'); CALL PLIRETC(8); /* signal end of input to Sort */ GOTO END_ROUTINE; END; /* Do any processing you require to get a record. */ /* For example, you could get the record from a file */ /* and then display the record. */ READ FILE (CUSTOMER_FILE) INTO (CUST_RECORD); PUT SKIP LIST(CUST_RECORD); /* signal Sort to include this record */ CALL PLIRETC(12); RETURN(CUST_RECORD); /* return the record to Sort */ END_ROUTINE: END SORT_INPUT_ROUTINE;
8.3.3. Writing Output Routines
Use an output routine to do any post-sorting processing. For example, you might want to print the data or use the sorted data in a calculation.
The output routine and the Sort program communicate using calls to PLIRETC. The Sort program recognizes one PLIRETC return code from an output routine:
4—Pass the next record.
Any other return code stops the Sort program passing records to the output routine; the remaining records are not passed.
Calls made to PLISRTC or PLISRTD require an output routine. When using an output handling routine, the sort processing is as follows:
Your program calls PLISRTC or PLISRTD.
The Sort program assumes control. When it requires a record, the Sort program gets it from the input file or the input routine, as described in the preceding section.
The Sort program processes all of the records.
The Sort program calls the output routine, passing it one record.
The output routine assumes control and does the following:
Processes the record as required.
Calls PLIRETC with a
return-codeof 4.
The Sort program assumes control and continues to pass records to the output routine until all the records have been processed.
The Sort program sets up the
return-codeand returns to the statement after the CALL PLISRTx statement.
Note that the Sort program does not pass an end-of-data code to your output routine. Do any end-of-data processing after the Sort program returns to the calling program. A record of a CHARACTER VARYING output routine parameter is always passed at the full record length.
Example
SORT_OUTPUT_ROUTINE: PROCEDURE(CUST_RECORD); DECLARE CUST_RECORD CHARACTER(80); DECLARE NEWCUST_FILE FILE RECORD OUTPUT; /* Do any processing you require. */ /* For example, you could display the record. */ PUT SKIP LIST(CUST_RECORD); CALL PLIRETC(4); /* request the next record */ RETURN; END SORT_OUTPUT_ROUTINE;
8.4. Programming Examples
This section contains an example of calling PLISRTA, which uses a file for both input and output, and PLISRTD, which uses input and output routines. PLISRTB and PLISRTC use a combination of files and routines. If you want to use either of those entry points, look at both of the following programs for examples.
8.4.1. Calling the PLISRTA Entry Point
SORT_EXAMPLE1: PROCEDURE OPTIONS(MAIN); /* declare a variable to hold the code returned by the Sort /* program */ DECLARE RETURN_CODE FIXED BIN(31); /* call the PLISRTA entry point of the sort routine. */ CALL PLISRTA (' SORT FIELDS=(5,20,CH,A,25,20,CH,A,45,1,CH,A) ', ' RECORD TYPE=F,LENGTH=(120) ', 4000000, RETURN_CODE, 'CUST'); SELECT (RETURN_CODE); WHEN(0) PUT SKIP LIST ('Sort completed successfully.'); WHEN(16) PUT SKIP LIST ('Sort failed.'); OTHERWISE PUT SKIP LIST('Invalid Sort return code'||RETURN_CODE); END; /*select*/ END SORT_EXAMPLE1;
The example above sorts a customer file by name. The arguments of the call to PLISRTA are:
SORT FIELDS—There are three sort fields, which are in this case a last name, first name, and middle initial. The fields are all character strings. The sort order is ascending.
RECORD TYPE—Records are fixed length and 120 bytes long.
STORAGE—4 Mb, accepted for compatibility; it has no effect.
RETURN CODE—The variable RETURN_CODE will contain the return code from the Sort program.
FILE PREFIX—The Sort program reads the file
CUSTinand writesCUSTout, instead of using the defaultsortinandsortout.MESSAGE LEVEL—This call does not specify a message level, so AP is assumed.
8.4.2. Calling the PLISRTD Entry Point
SORT_EXAMPLE2: PROCEDURE OPTIONS(MAIN); /* declare a variable to hold the code returned by the Sort /* program */ DECLARE RETURN_CODE FIXED BIN(31); /* call the PLISRTD entry point of the sort routine. */ CALL PLISRTD (' SORT FIELDS=(5,20,CH,A,25,20,CH,A,45,1,CH,A) ', ' RECORD TYPE=F,LENGTH=(120) ', 4000000, RETURN_CODE, SORT_INPUT, SORT_OUTPUT, 'CUST'); SELECT (RETURN_CODE); WHEN(0) PUT SKIP LIST ('Sort completed successfully.'); WHEN(16) PUT SKIP LIST ('Sort failed.'); OTHERWISE PUT SKIP LIST('Invalid Sort return code'||RETURN_CODE); END; /*select*/ SORT_INPUT: PROCEDURE RETURNS(CHAR(120) VARYING); /* you must specify the returns attribute. For varying strings, the length must be the maximum length. */ DECLARE CUST_RECORD CHARACTER(120) VARYING; DECLARE CUSTOMER_FILE FILE RECORD INPUT; ON ENDFILE(CUSTOMER_FILE) BEGIN; PUT SKIP(2) LIST('End of customer file input.'); CALL PLIRETC(8); /* signal end of input to Sort */ GOTO END_ROUTINE; END; /* Do any processing you require to get a record. */ /* For example, you could get the record from a file */ /* and then display the record. */ READ FILE (CUSTOMER_FILE) INTO (CUST_RECORD); PUT SKIP LIST(CUST_RECORD); /* signal Sort to include this record */ CALL PLIRETC(12); RETURN(CUST_RECORD); /* return the record to Sort */ END_ROUTINE: END SORT_INPUT; SORT_OUTPUT: PROCEDURE(CUST_RECORD); DECLARE CUST_RECORD CHARACTER(*); DECLARE NEWCUST_FILE FILE RECORD OUTPUT; /* Do any processing you require. */ /* For example, you could display the record. */ PUT SKIP LIST(CUST_RECORD); CALL PLIRETC(4); /* request the next record */ RETURN; END SORT_OUTPUT; END SORT_EXAMPLE2;
The example sorts the same customer records by customer name, but the records pass through an input and an output routine. The elements of the call to PLISRTD are similar to the ones in the call to PLISRTA. The differences are:
Varying length records.
The input routine returns CHARACTER(120) VARYING strings. The Sort program pads each one with blanks to the record length, 120, so the sort fields are at the same positions as in the previous example, and the output routine receives each record as a 120-character string.
The input and output routine names.
The two routines are internal to the main program.