Professional Documents
Culture Documents
PL - I Tips
PL - I Tips
The principal reason for this enforcement the new ePL/I's dependency on the L/E
runtime environment which is not as forgiving as the previous PL/I runtime library
and its components was. Warning: L/E runtime option ERRCOUNT must be set
to 0. This is because PL/I ON conditions such as ENDPAGE, will be considered an
ERROR condition by LE and the program will terminate prematurely.
2
PL/I Reference Manuals.
For More information on the compiler and its features the following manuals are
referenced.
ePL/I References.
Enterprise PL/I for z/OS and OS/390 V3R5 Compiler and Run-Time
Migration Guide
GC27-1458-01
Enterprise PL/I for z/OS and OS/390 V3R5 Language Reference
SC27-1460-02
Enterprise PL/I for z/OS and OS/390 V3R5 Programming Guide
SC27-1457-02.
Enterprise PL/I for z/OS and OS/390 V3R5 Messages and Codes
SC27-1461-02.
Compiler Limitations
Compiler Limitations.
4
COPY
COPY is the ANSI version of REPEAT
Format: COPY(string,number_of_repetitions);
Example of Use.
REPEAT( 'ha', 2 ) returns 'hahaha'
DATE Functions.
DATETIME PATTERNS.
Following is a partial list of DATETIME patterns. For a full list please refer to the
PL/I Language Reference Manual.
DATETIME
Returns the current date and time in the user-specified pattern or in
default pattern YYYYMMDDHHMISS999. The patterns are:
Format: DATETIME(pattern);
Example of use.
1. A = DATETIME('MmmDDYYYY');
Example of Use.
DCL Days_of_July4_1993 fixed bin(31);
Days_of_July4_1993 = days('07041993','MMDDYYYY');
DAYSTODATE
Converts a number of days to a date/time pattern string
Format: DAYSTODATE(number_of_days,Pattern);
Example of Use.
DCL DATE_FORMAT VALUE ('MMDDYYYY') CHAR;
DCL TODAYS_DATE CHAR(LENGTH(DATE_FORMAT));
DCL SEP2_1993 CHAR(LENGTH(DATE_FORMAT));
DCL DAYS_OF_JULY4_1993 FIXED BIN(31);
DCL DATE_DUE CHAR(LENGTH(DATE_FORMAT));
TODAYS_DATE = DATETIME(DATE_FORMAT); /* E.G. 06161993 */
DAYS_OF_JULY4_1993 = DAYS('07041993','MMDDYYYY');
SEP2_1993=DAYSTODATE(DAYS_OF_JULY4_1993 + 60,DATE_FORMAT);
/* 09021993 */
DATE_DUE = DAYSTODATE(DAYS() + 60, DATE_FORMAT);
*ASSUMING TODAY IS JULY 4,1993, THIS WOULD BE SEPT 2, 1993 *
Y4DATE.
Takes a date value with the pattern 'YYMMDD' and returns the date value with
the two-digit year widened to a four-digit year
Format: Y4DATE(Date,Century year);
Example of Use.
6
Y4JULIAN.
Takes a date value with the pattern 'YYDDD' and returns the date with the two-
digit year widened to a four-digit year
Format: Y4JULIAN(Date,Century year);
Example of Use.
Y4YEAR.
Takes a date value with the pattern 'YY' and returns the date value w the two-digit
year widened to a four-digit year.
Format: Y4YEAR(Year,Century year);
Example of Use.
VALIDDATE returns a '1'B if the string d holds a date/time value that matches a
valid DATE pettern.
Format: VALIDATE (d,p,w);
d = DATE
P = DATE pattern (Ref ePL/I Language Reference Manual)
w = offset that can be subtracted from the year. This is equivalent
to our base year in CPAC/SPAC of 1985.
Example of Use.
DCL TDATE CHAR(8) INIT('20030326');
DCL DFLAG BIT(1) INIT(0);
Example of Use.
DCL TDATE CHAR(8) INIT(' ');
8
Loop Contol.
DO FOREVER.
This, in effect, replaces our DO WHILE(1 = 1) coding technique. A LEAVE must
be used to exit the loop. It is recommended that DO loops be labelled so the
LEAVE statement will exit the proper DO loop if there is nesting.
Format: DO FOREVER, (or DO LOOP)
Example of use.
#DO_LOOP1: DO FOREVER;
... code ....
#DO_LOOP2: DO FOREVER;
... code ....
LEAVE #DO_LOOP2; *This leaves nested loop #DO_LOOP2
IF (...) THEN LEAVE #DO_LOOP1; *Exits main loop.
END;
IF (...) THEN LEAVE #DO_LOOP1; *Also exits main loop.
END;
ITERATE.
ITERATE branches to a loop's end statement
Format: ITERATE(label);
Example of Use.
#DO_LOOP1: DO I = 1 TO HBOUND(A);
IF A(I) < 0 THEN ITERATE;
...
END;
Example of Use.
Instead of Coding
DCL TP PIC'ZZZ,ZZ9';
TP = REC_COUNT;
PUT SKIP LIST( 'REC COUNT IS ' || TP );
Simply Use
PUT SKIP LIST('RECORD COUNT IS '||EDIT(REC_COUNT,'ZZZ,ZZ9');
String Justifying.
Justify LEFT, Justify RIGHT.: Basically it is a "left adjust" where string S with a
length L and a fill character F is inserted into a target string. RIGHT is the same
but is a right adjust.
Format: LEFT(S,L,F);
S = input string,
L = Length of string,
F = the fill Character. Must be single character.
Example of use.
RIGHT(S,L,F);
TARG = RIGHT(SOURCE, LENGTH(TARG), '*');
TARG will be '***************HELLO'
10
Justify CENTERLEFT, Justify CENTERRIGHT.: CENTERLEFT returns a string
that is the result of inserting string S in the center (or one position to the left of
center) of a string with length L and padded on the left and on the right with the
character F as needed.
Format: CENTERLEFT(String,Length,Fill);
Example of Use.
DCL SOURCE CHAR VALUE('FEEL THE POWER');
DCL TARGET20 CHAR(20);
DCL TARGET21 CHAR(21);
TARGET20 = CENTERLEFT(SOURCE, LENGTH(TARGET20), '*');
/* '***FEEL THE POWER***' - EXACTLY CENTERED */
TARGET21 = CENTERLEFT(SOURCE, LENGTH(TARGET21), '*');
/* '***FEEL THE POWER****' - LEANING LEFT! */
LOWERCASE
LOWERCASE returns a string with all the alphabetic characters converted to their
lowercase equivalent.
Format: LOWERCASE(string)
Example of Use.
DCL LOWER_VAR CHAR(7) INIT('ABCDEFG');
DCL WORKVAR CHAR(7) INIT(' ');
WORK_VAR = LOWERCASE(LOWER_VAR);
MAXLENGTH.
LENGTH(c) gives you the current length of a string, but what do you do to get the
maximum length of the string STG(c)-2 works for varying, but not for nonvarying
or varyingz. MAXLENGTH solves this problem So to fill any string c, you could
use the example below.
Format: MAXLENGTH(var);
Example of Use.
C = COPY( '*', MAXLENGTH(C) );
Example of Use.
DCL SOURCE CHAR VALUE (' 368,475;121.,856,478')
DCL DELIMS CHAR(3) INIT (',;.'); /* String of delimiters */
DCL NUMBER(5) CHAR(3);
DCL START FIXED BIN(31);
DCL END FIXED BIN(31);
TRIM.
The in-house (CPP0LTR) function may be replaced by the new PL/I TRIM func-
tion. The syntax is TRIM(x,y,z), where x, y and z are CHARACTER type. Y and
z can be one or more characters. EACH of these characters will be trimmed one or
the other end as specified by y and z. Please note that the TRIM TRIM built-in
does NOT concatenate characters specified in y and z. Using two or more charac-
ters (like '*p') will NOT trim the string '*p' it will trim all occurrences of '*' and/or
'p' Please further note that trimming will only be performed if the string x begins or
ends with one of the characters in y or z. It will remove leading and trailing chars
as follows:
Format: TRIM(fld,x,y)
fld is the field you wish to trim
x is the leading char(s) that you wish to trim
y is the training char(s) that you wish to trim
Example of Use.
TRIM(INPUT_FLD,' ',' '); Remove leading and trailing
TRIM(INPUT_FLD); Remove leading and trailing
12
UPPERCASE
UPPERCASE returns a string with all the alphabetic characters converted to their
uppercase equivalent.
Format: UPPERCASE(string)
Example of Use.
DCL LOWER_VAR CHAR(7) INIT('abcdefg');
DCL WORKVAR CHAR(7) INIT(' ');
WORK_VAR = UPPERCASE(LOWER_VAR);
UNION.
UNION is a better, more powerful DEFINED attribute. The UNION attribute
allows you to specify that a variable is a union and that its members are those that
follow it and are at the next logically higher level.
Example of Use.
DCL
1 * UNION,
2 B1 BIT(32),
2 B2 BIT(16);
DECLARE 1 CLIENT,
2 NUMBER PIC '999999',
2 TYPE BIT(1),
2 * BIT(7),
2 NAME UNION,
3 INDIVIDUAL,
5 LAST_NAME CHAR(20),
5 FIRST_NAME UNION,
7 FIRST CHAR(15),
7 INITIAL CHAR(1), (redef first)
3 COMPANY CHAR(35), (redef individual)
2 * CHAR(0);
Any data type is permitted in a union. The purpose of a union is easier to under-
stand
VERIFYR.
VERIFYR performs exactly the same as VERIFY but goes from right to left
instead. (Returns pos in string of value that is NOT in arg).
Format: VERIFYR(string,arg,len);
Example of Use.
DCL I FIXED BINARY(31) INIT(0);
X = 'A B '; /* TWO BLANKS IN EACH SPACE */
Y = ' '; /* ONE BLANK */
N = LENGTH(X); /* N = 6 , default anyway*/
I = VERIFYR(X,Y,N); /* I = 4 */
Example of use.
TESTSUBR: PROC(parm1,this_parm,parmn);
DCL parm1 CHAR(10);
DCL this_parm CHAR(10); /* we will check this one */
DCL parm3 CHAR(10);
OMITTED(parmx)
OMITTED returns a BIT(1) value that is '1'B if the parameter named parmx was
omitted in the invocation to its containing procedure.
Format: OMITTED(parmx);
Example of use.
TESTSUBR: PROC(parm1,this_parm,parmn);
DCL parm1 CHAR(10);
DCL this_parm CHAR(10); /* we will check this one */
DCL parm3 CHAR(10);
Pagination Functions.
LINENO.
LINENO returns a real fixed-point binary value specifying the current line number
of a PRINT directed file. The file must have been declared as PRINT and be open.
If neither is true then the value returned is 0.
Format: LINENO(file);
Example of use.
DCL SYSPRINT FILE OUTPUT PRINT;
DCL LINE_POS FIXED BINARY(31) INIT(0);
LINE_POS = LINENO(SYSPRINT);
14
PAGENO.
PAGENO returns a FIXED BINARY(31,0) value that is the current page number
associated with a PRINT directed file. If the file is not a print file, the ERROR
condition is raised.
Format: PAGENO(file);
Example of use.
DCL SYSPRINT FILE OUTPUT PRINT;
DCL PAGE_NUM FIXED BINARY(31) INIT(0);
DCL LINE_POS FIXED BINARY(31) INIT(0);
LINE_POS = LINENO(SYSPRINT);
PAGENUM = PAGENO(SYSPRINT);
Pointers.
ADDRDATA
ADDRDATA behaves the same as the ADDR built-in function except in the fol-
lowing instance: When applied to a varying string, ADDRDATA returns the
address of the first data byte of the string (rather than of the length field).
Format: ADDRDATA(string ref);
Example of use.
X = ADDRDATA(string);
AUTOMATIC
AUTOMATIC allocates storage of size n automatic storage and returns the pointer
to the allocated storage. This is a type of getmain. Note that the storage obtained
cannot be FREED explicitly. It is automatically freed when the block terminates.
Format: AUTOMATIC(size);
Example of use.
DCL WORK_POINTER POINTER INIT(NULL());
DCL GET_MAIN_32 FIXED BINARY(31,0) INIT(32768);
WORK_POINTER = AUTOMATIC(GET_MAIN_32);
OR
WORK_POINTER = AUTOMATIC(32768);
Example of use.
DCL P POINTER;
DCL X FIXED BIN(31), B BASED FIXED BIN(31);
set p........
X = POINTERADD(P,2)->B;
POINTERDIFF
POINTERDIFF returns a FIXED BINARY(31,0) result that is the difference
between the two pointers x and y.
Format: POINTERDIFF(pointer1,pointer2);
Example of use.
DCL P1 POINTER;
DCL P2 POINTER;
DCL X FIXED BIN(31), B BASED FIXED BIN(31);
set p1........
set p2........
X = POINTERDIFF(P1,P2)->B;
POINTERVALUE
POINTERVALUE returns a pointer value that is the converted value of expression
which is either a HANDLE or computational type.
Format: POINTERVALUE(x);
Example of use.
DCL P1 POINTER;
16
CHECKSTG
CHECKSTG returns a bit(1) value which indicates whether a specified pointer is
the start of a piece of uncorrupted allocated storage. If no pointer is supplied,
CHECKSTG determines whether all allocated storage is uncorrupt uncorrupted. To
use this built-in function, you must also specify the CHECK(STORAGE) compile-
time option.
Format: CHECKSTG(pointer);
Example of use.
DCL CHKPTR BIT(1);
CHKPTR = CHECKSTG(pointer);
or
UNALLOCATED
UNALLOCATED returns the bit(1) value '1'b if the specified pointer value is the
start of a piece of storage obtained via the ALLOCATE statement or the ALLO-
CATE built-in function. CHECK(STORAGE) compile time option must be
selected.
Format: UNALLOCATED(pointer)
Example of use.
IF UNALLOCATED(pointer) THEN ALLOCATE(xxx) SET(pointer);
or
IF ¬UNALLOCATED(pointer) THEN FREE(xxx);
Example of Use.
PUT SKIP LIST( PROCNAME()||SOURCELINE());
PUT SKIP LIST( '>> ' || PROCNAME());
...
PUT SKIP LIST( '<< ' || PROCNAME());
REM - (REMAINDER).
REM returns the remainder of x divided by y.
Format: REM(number,divisor);
Example of Use.
A = REM(+10,+8); /* REM = 2 */
18
Our Experience.
Factors that may cause trouble
• User programs may be illegal
• Subtle differences between new PL/I and OS PL/I
• Code produced by new PL/I may be not correct
• Code produced by new PL/I is slow
• New PL/I compiler needs too much time or region
• Load module requires more Region
• L/E run time Options
IBM1208I.
IBM1208I W INITIAL list for the array WPPXS_TAB
contains only one item.
Wrong Way.
DCL LIST(4,60:73) CHAR(50) INIT('');
DCL XTAB_TAB(4) CHAR(15) INIT('');
Use:
Correct Way.
DCL LIST(4,60:73) CHAR(50) INIT((*)(50)'');
DCL XTAB_TAB(4) CHAR(15) INIT((*)(15)'');
Use this
20
S0C4'S.
The following outlines the common causes of abend S0C4's we have experienced.
1. Variables not Initialized.
Un-initialized Variables will cause unpredictable Runtime Results. In some
cases it will cause an S0C4 abend but in others you may work wih invalid data
and the program will not abend but complete with an RC0. Only later will you
discover that the data passed in files or tables is wrong. You may have to search
several jobsteps back before you find which program was the culprit. This hap-
pened on the conversion and is very time consuming. Please ensure all variables
are properly initialized.
One good example is a problem we found in a program as follows:
The UTAPE_STACK was not initialized.
UTAPE_CNT= -405282106 <=== Wrong.
A variable(A) from the stack used by a DO A = 1 TO UTAPE_CNT caused
the problem
2. Variable Declared as pointer.
• Pointer not initialized
• Pointer is 0
• Pointer out of range
3. Controlled Stack.
• Not ALLOCATED
• Allocated using bad pointer
• Freed then attempt to use Variables mapped to it
4. File Buffers.
• File Closed then Buffer accessed
• Get/Put Locate mode and using pointers to locate data.
(Restoring a pointer thinking you will get original data)
5. Invalid Length Table Variables
The variable CPPPROD in dialog table is defined as 40 bytes in length. The abend
was caused by the variable being longer than the 40 bytes.
6. Storage Overlay
A RECORD field was inaccessible and would abend the program upon any
refernece to it. Investigation showed that the based file record Storage was never
allocated. This caused the abend and storage overlay.
7. Bad Linkage Edit
CEESTART must always be the entry point for the load module. If it is not then
the results are unpredictable. Check the LINKDEF to be sure that SINC is the first
statement. Always check the LINKMAP to ensure proper linkage editing was done.
8. Invalid Pointer Use.
• In some programs Buffers were ALLOCated then freed. An attempt was
made to re-read the same buffer. This causes an S0C4.
• A program ALLOCATed a BUFFER then FREED it. The program then
used the same pointer to the FREE area to check its data content. This
will cause unpredictable results. Ensure that you clear or initialize areas or
pointers after FREEing them.
22
CHAR text to Numeric.
The Following is an example of getting a value passed in a general parameter field,
extracting the numeric value and using a picture.
example.
DCL VAR_FIELD CHAR(25) VAR;
DCL #NUMBER_OF_WE_CHAR CHAR(3) INIT(' ');
DCL NUMBER_OF_WE PIC'999' DEF #NUMBER_OF_WE_CHAR;
#NUMBER_OF_WE_CHAR =
RIGHT(TRIM(VAR_FIELD),LENGTH(#NUMBER_OF_WE_CHAR),'0');
The above will trim blanks from both sides of the incoming variable and right adjust
the remainder. The length of 3 is determined by the LENGTH(..). We right adjusted
because this is numeric data we wish to operate on. If it was CHAR then we may
elect to do a LEFT.
Example.
DCL WORK_04 CHAR(32500) INIT('') VARYING;
DCL #WORK_CHAR CHAR(6) INIT('0');
DCL #WORK_NUM PIC'999999' DEF #WORK_CHAR;
Example 1.
Description:
CALL CPPCLOG( 'LOG'
,'UPDVAR'
,TS_USER_NAME <===== argument 3
,TS_RECORD_TYPE <===== argument 4
,TS_CUR_GROUP_ID
,'*'
,VAR_NAME
,OLD_DATA
,VAR_DATA
#TS_USER_NAME =TS_USER_NAME;
#TS_RECORD_TYPE=TS_RECORD_TYPE;
24
Another example.
Example 2.
X: PROCEDURE(A,B,C);
END X;
Incorrect.
IF DELETE_ISP_AREA(TS_ISPF_ANCHOR_PTR,
TS_ISPF_ANCHOR_PTR -> ISP1_NEXT_PTR) .....
All the variables are DECLARED as pointers. Obviously the compiler does not like
using an expression as a variable as is stated in the migration guide and will generate
an IBM1214I compile time message. Instead code the following:
Comments /* and */
The compiler will now complain if a comment spans multiple lines. Also, If there
is a /* comment..... with no */ up the line. What happened to us was that no
diagnostic was generated but the compiler turned a DCL for a FILE into a default
DEC FLOAT.
Example.
%DCL EYEBALL_COMPILETIME CHAR;
%EYEBALL_COMPILETIME =
'''** COMPILED (' || COMPILETIME || ')''';
The variable isn't used and is thrown away. If you want to keep it around in the
object, you would have to add the attributes ABNORMAL and STATIC to the
declare.
Correct use.
%DCL EYEBALL_COMPILETIME CHAR;
%EYEBALL_COMPILETIME =
'''** COMPILED (' || COMPILETIME || ')''';
Wrong Way.
DCL COMPILE STATIC EXTERNAL CHAR( 40) INIT('CPP????');
Instead use:
Right Way.
DCL COMPILE STATIC EXTERNAL CHAR( 40);
COMPILE = 'CPP......'; /* program name*/
26
Stream I/O Consideration:
Warning: This flash is intended to emphasize the importance of investigating the
use of PUT I/O statements within PL/I source code with the ePL/I Within the
Enterprise compiler, the use of PUT statements as a method of writing STREAM
data is strictly enforced. What does 'strictly enforced' mean to the applications pro-
grammer? The following is an example of a scenario that worked in previous com-
pilers, but produces unexpected results in the Enterprise PL/I compiler:
In this example, Buffer contains HEX data in columns 11 & 12, which is the length
of valid data in the character(80) record. Logically the hex value is 0030 or 48 in
decimal. What occurs under the Enterprise PL/I compiler is the value 0030 is con-
verted to 4B30 or 19248. This could cause any number of unexpected results to
occur. This type of conversion occurs for all values of HEX'20' or less as described
in the Enterprise PL/I for z/OS and OS/390 Compiler and Run-Time Migration
Guide for Version 3 Release 2 under the topic STREAM I/O with unprintable char-
acters on page 88 in the document.
The above program call for the subroutine fails because the returns attribute was
specified on the declaration of the subroutine. A compiler error is raised.
Following is the correct way to specify that the returned attribute may either be
used or discarded.
Correct way
UPDATE_NEWMTBL: PROC RETURNS(FIXED BIN ( 31) OPTIONAL);
28