Download as pdf or txt
Download as pdf or txt
You are on page 1of 28

PL/I V3R5M0 - Getting Started

• Introduction to the PL/I V3R5M0 Compiler.


• The limitations of the PL/I V3R5M0 compiler.
• The highlights of the PL/I V3R5M0 compiler.
• Useful new functions of the PL/I V3R5M0 compiler.
• Current experience and how to's of the PL/I V3R5M0 compiler.
• Future Exploitation of the PL/I V3R5M0 compiler.

ePL/I V3R5M0 Compiler Introduction.


Instead of using the name PL/I V3R5M0 for the compiler all references will use the
name ePL/I throughout this document. Tne new ePL/I has many features
including 100 more functions than the previous PL/I compilers. Fortunately, or
unfortunately, it also has much more rigid enforcement of PL/I rules. This can and
will cause runtime problems unless the source code is clean. To force clean compila-
tions all the LAX compiler options have been disabled in our environment. To
complete the requirement for clean compilations, any compile time message from W
and up will result in an RC8 and the compile and link process will terminate until
the messages are cleared. Compile time oprions/rules are contained in a dataset
which is input to SCLM.

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.

PL/I V3R5M0 - Getting Started 1


Compile time Diagnostic Rules.
1. RULES(NOLAXDCL)
• RULES(NOLAXDCL) causes the compiler to emit an E-level message for
each undeclared variable.
2. RULES(NOLAXIF)
• NOLAXIF allows IF,WHILE,UNTIL,and WHEN clauses to evaluate to
only BIT(1) NONVARYING.
3. RULES(NOLAXLINK)
• Under RULES(NOLAXLINK),the following errors will also be flagged
with E level messages
- Using RETURN without an expression in a procedure with the
RETURNS option.
- Using RETURN with an expression in a procedure without the
RETURNS option.
- Assigning or comparing ENTRYs with parameter and returns
description that do not match.
4. RULES(NOLAXCTL)
• NOLAXCTL requires that if a CONTROLLED variable is to be allocated
with a varying extent, then that extent must be specified as an asterisk or as
a non-constant expression.
5. RULES(NOMULTICLOSE)
• NOMULTICLOSE causes the compiler to flag all statements that force the
closure of multiple groups of statement with an E-level message.
Note:All level W&I compiler messages must be resolved before SCLM will Link
(Exception is W level for unused Declared Procedure)

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.

New Features of PL/I V3R5M0.


The ePL/I compiler has all the functions of the previous PL/I V2R3 plus the fol-
lowing new functions.
• Easier Java interoperability
• An integrated XML parser
• Integrated SQL preprocessor
• OS/390 Unix? System Services
• C/C++ interoperability.
• More than 100 new built-in functions
It is not the intention of this document to go into all the new functions in detail at
this time but some of the highlights are:

PL/I V3R5M0 - Getting Started 3


ePL/I new Functions.
• Easier Java Interoperability.
- Supports ASCII and UNICODE.
- WIDECHAR attribute to declare that string holds UTF-16 data.
- Can store data in 390 format or Windows format.
- etc.
• An integrated XML parser
- Event-based API reports events to the application through callbacks.
- Tree-based API translates XML into internal tree-based structure.
- Supports XML files encoded in either Unicode UTF-16 or any of
several single byte code pages.
• Integrated SQL preprocessor
- Program can contain EXEC SQL statements.
- Handled same was as MACRO Pre-Processor.
• C/C++ interoperability.
- Refer to Language Reference Manual for ePL/I functions.
• OS/390 Unix? System Services
- New ePL/I functions for HFS processing.
• More than 100 new built-in functions
- Refer to Language Reference Manual.

Compiler Limitations
Compiler Limitations.

The ePL/I compiler is significantly different PL/I V2R3 in that:


• The compiler rules are more rigidly enforced,
• The resulting object decks are smaller than previous V3R? releases
• The memory requirements smaller than previous V3R? releases
• The runtime is significantly decreased than previous V3R? releases
• The execution still uses LE but substantially less C code.
• The Hardware Instruction Set used requires that LE 2.10 and higher are
mandatory for successful execution.
• Structures as arguments are not supported unless both the following are
true:
- There is a parameter expression
- The parameter description specifies all constant extents.
• An array expression passed as an argument to a user function may cause
problems when using varying length strings.
• EXTERNAL names must not begin with @@,CEE,IBM, OR PLI.

Useful New Functions.

4
COPY
COPY is the ANSI version of REPEAT
Format: COPY(string,number_of_repetitions);

Example of Use.
REPEAT( 'ha', 2 ) returns 'hahaha'

COPY( 'ha', 2 ) returns 'haha'

To to fill a nonvarying string C, you could use

C = REPEAT('*',LENGTH(C)-1);/*-1 to avoid STRZ*/


- OR -
C = COPY( '*', LENGTH(C) ); /* much nicer */

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.

• YYYY Four-digit year


• YY Two-digit year
• MM Two-digit month
• MMM Three-letter month (Ex: DEC)
• Mmm Three-letter month (Ex: Dec)
• DD Two-digit day within a given month
• DDD Number of days within a given year
• HH Number of hours within a given day
• MI Number of minutes within a given hour
• SS Number of seconds within a given minute
• 999 Number of milliseconds within a given second
Note: For the three-letter month patterns, the uppercase/lowercase char
must correspond exactly.

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');

2. DCL PATTRN CHAR(6) VALUE('MMDDYY');


A = DATETIME(PATTRN);

PL/I V3R5M0 - Getting Started 5


DAYS.
Returns the number of days corresponding to a date/time pattern or the number of
days for today's date
Format: DAYS(date,pattern,century window)

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.

Y4DATE('990101',1950); /* returns '19990101' */


Y4DATE('030325',1950); /* returns '20030325' */

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.

Y4JULIAN('99001',1950) returns '1999001'


Y4JULIAN('03001',1950) returns '2003001'.

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.

Y4YEAR('99',1950) returns '1999'


Y4YEAR('00',1950) returns '2000'

VALIDDATE (Note the 2 D's).


Indicates if a string holds a valid date

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);

DFLAG=VALIDDATE(TDATE,'YYYYMMDD' ); /*DATE_FLAG='1'B MATCH*/


DFLAG=VALIDDATE(TDATE,'MMDDYYYY' ); /*DATE_FLAG='0'B NOMATCH*/

PL/I V3R5M0 - Getting Started 7


REPATTERN
Takes a value holding a date in one pattern and returns that value converted to a
date in a second pattern.
Format: REPATTERN(date,target_pattern,source_pattern,w);

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(' ');

<date> <target> <source>


TDATE=REPATTERN('030315','YYYYMMDD','YYMMDD',1985)
*returns '20030315'*

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;

PL/I V3R5M0 - Getting Started 9


String Handling.
EDIT.
EDIT saves you having to declare temporary PIC variables or having to use PUT
STRING with P format: EDIT returns a character string of length LENGTH(y).
Its value is equivalent to what would result if x were assigned to a variable declared
with the picture specification given by y. This may be used in a PUT SKIP or any
assginment expression.
Format: EDIT(Expression,String containing PICTURE characters).

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.

DCL SOURCE CHAR(10) INIT('HELLO');


DCL TARG CHAR(20) INIT(' ');

TARG = LEFT (SOURCE, LENGTH(TARG), '*');


TARG will be 'HELLO***************'

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);

* WORK_VAR will contain 'abcdefg'

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) );

PL/I V3R5M0 - Getting Started 11


SEARCH.
SEARCH returns the first position in one string at which any character, or widechar
of another string appears. It also allows you to specify the location at which to start
searching. This function is useful to parse strings with delimiters.
Format: SEARCH(target string,arg string,start position);

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);

/* extract the three-digit numbers from the source string */


/* by searching for the delimiters */

START=VERIFY(SOURCE,' '); /*start of first number*/


END=SEARCH(SOURCE,',;.',START+1); /*end of first number*/
NUMBER(1)=SUBSTR(SOURCE,START+1,3); /* 368 */
START=VERIFY(SOURCE,DELIMS,END); /*start of second number*/
END = SEARCH (SOURCE, DELIMS, START+1);
NUMBER(2) = SUBSTR (SOURCE, START + 1, 3); /* 475 */

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

TRIM(INPUT_FLD,'',' '); Remove trailing blanks only


TRIM(INPUT_FLD,' ',''); Remove leading blanks only
TRIM(INPUT_FLD,' ','*P'); Remove trailing P then *
Not*P combination!

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);

* WORK_VAR will contain 'ABCDEFG'

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 */

PL/I V3R5M0 - Getting Started 13


Parameter Functions.
PRESENT
PRESENT(parmx) returns a BIT(1) value that is '1'B if the parameter parmx was
present in the invocation of its containing procedure.
Format: PRESENT(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);

IF PRESENT(this_parm) THEN DO;


....
END;

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);

IF OMITTED(this_parm) THEN DO;


....
END;

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);

PL/I V3R5M0 - Getting Started 15


POINTERADD
POINTERADD returns a pointer value that is the sum of its arguments.
Format: POINTERADD(Pointer,Binary expression);

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;

P1=POINTERVALUE(0); *INITIALIZE POINTER TO 0.

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

IF CHECKSTG(pointer) THEN DO;

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);

IF UNALLOCATED(PROD_PTR) THEN DO;


ALLOCATE PROD_STACK SET(PROD_PTR);
.......
IF ¬UNALLOCATED(PROD_PTR) THEN DO;
FREE PROD_STACK;

PL/I V3R5M0 - Getting Started 17


Tracing/debugging Helpful functions.
PROCNAME / SOURCELINE.
• PROCNAME returns the name of the current proc
• SOURCELINE returns the current line number
These are very useful in debugging:
Format: PROCNAME()
Format: SOURCELINE()

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

Incompletely Initialized arrays.


Incompletely initialized arrays can and will cause runtime problems when the
program references a variable which contains unknown value.
DCL WPPXS_TAB(15) CHAR(3500) INIT((15)' ');
• Older PL/I compilers will issue no messages for this dcl
• New PL/I compiler issues the following message if the variable is
used in any way

IBM1208I.
IBM1208I W INITIAL list for the array WPPXS_TAB
contains only one item.

DCL WPPXS_TAB(15) CHAR(3500) INIT((15)' ');

The INIT( (15)' ' ) does not specify 15 instances of a string


consisting of one blank The 15 is a string repetition factor, and
so this INIT clauses specifies only one string (of 15 blanks)

This message would also be produced for these dcls

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)'');

PL/I V3R5M0 - Getting Started 19


Another Example.
Instead of

DCL A(8) FIXED BIN INIT( 13 );

Use this

DCL B(8) FIXED BIN INIT( (*) 13 ); <===== ***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.

PL/I V3R5M0 - Getting Started 21


Example Programming Practices.
The following is an example of some coding techniques we have either found to be
necessary or to be a useful tip to developers.

DCL & ALLOC for Structures.


In many cases a CONTROLLed Structure is DeCLared and then ALLOCATED
and FREED during the execution of the program. A sample of the way to properly
do it follows:

Sample Controlled Structure use.


1. DeCLare the structure.

DCL 1 FMID_B_RECORD CONTROLLED,


2 FMID_B_BASE_PART,
/* 3 FMID_B_LEN FIXED BIN ( 15),
3 FMID_B_PFX CHAR( 1),
3 FMID_B_ID CHAR( 8),
3 FMID_B_TYPE CHAR( 1),
3 FMID_B_SEQ1 FIXED BIN ( 15),
3 FMID_B_SEQ2 FIXED BIN ( 15),
3 FMID_B_LEVEL CHAR( 1),
3 FMID_B_EXT CHAR( 1),
3 FMID_B_FLAG UNALIGNED,
4 FMID_B_LAST BIT ( 1),
3 FMID_B_LIST_COUNT FIXED BIN ( 15),
2 FMID_B_LIST(*), <===note the *. This is var attribute
3 FMID_B_SYSMOD CHAR(7);
/*set up an initialization variable */
DCL FMID_B_RECORD_INIT FIXED BIN(31) INIT(1);

This is how to allocate it.


ALLOC 1 FMID_B_RECORD,
2 FMID_B_BASE_PART,
3 FMID_B_PFX,
3 FMID_B_ID,
3 FMID_B_TYPE,
3 FMID_B_SEQ1,
3 FMID_B_SEQ2,
3 FMID_B_LEVEL,
3 FMID_B_EXT,
3 FMID_B_FLAG,
4 FMID_B_LAST,
3 FMID_B_LIST_COUNT,
2 FMID_B_LIST(FMID_B_RECORD_INIT),
3 FMID_B_SYSMOD;

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.

CHAR text to Binary.


To convert the JOBNUM from the CHAR work field to BINARY We first check
that the incoming variable contains proper numeric characters by using the
VERIFY. After that we trim leading and trailing blanks and shift the values to the
right in the receiving field. The leading fill character is 0. The final operation is using
the redefined numeric variable (redefines the CHAR) and using the BIN builtin
function to avoid library calls.

Example.
DCL WORK_04 CHAR(32500) INIT('') VARYING;
DCL #WORK_CHAR CHAR(6) INIT('0');
DCL #WORK_NUM PIC'999999' DEF #WORK_CHAR;

/* check that chars are valid numerics */


IF VERIFY(SUBSTR(WORK_04,1,A-1),' 0123456789')=0 THEN DO;

/* Following is to avoid lib conversion */


#WORK_CHAR =
RIGHT(TRIM(SUBSTR(WORK_04,1,A-1)),LENGTH(#WORK_CHAR),'0');
PJOBNBR=BIN(#WORK_NUM);

PL/I V3R5M0 - Getting Started 23


Calling subroutines.
Variables must match arrtibutes.
When calling subroutines or using a function, the variable passed must match the
attributes of the called subroutine.

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

Looking at Declare for these fields


TS_USER_NAME CHAR( 8 ) INIT(''),
TS_RECORD_TYPE CHAR( 2 ) INIT(''),

Looking at Declare for the CPPCL09


DCL CPPCL09 ENTRY
(CHAR(8) VAR,CHAR(*) VAR,CHAR(*) VAR,
CHAR(*) VAR,CHAR(*) VAR,CHAR(*) VAR,
CHAR(*) VAR);

It is obvious that the declare for the parms have to be VAR.


You can do the following.

DCL #TS_USER_NAME CHAR( 8 ) VARYING INIT('');


DCL #TS_RECORD_TYPE CHAR( 2 ) VARYING INIT('');

#TS_USER_NAME =TS_USER_NAME;
#TS_RECORD_TYPE=TS_RECORD_TYPE;

CALL CPPCLOG( 'LOG'


,'UPDVAR'
,#TS_USER_NAME <===== argument 3 replaced
,#TS_RECORD_TYPE <===== argument 4 replaced
,TS_CUR_GROUP_ID
,'*' <=== must be used to indicate variable.
,VAR_NAME
,OLD_DATA
,VAR_DATA

24
Another example.

Example 2.
X: PROCEDURE(A,B,C);
END X;

If you only want to use


CALL X(C,D);

Instead use the following.


CALL(C,D,*);

Expressions passed as Parameters.


The compiler does not easily accept expressions passed as parameters. Check the
Language reference manual for more details. Here is an example where a compiler
diagnostic is generated.

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:

Example of Correct way.


1. Declare work variables as follows:

DCL #NEXT_POINTER POINTER INIT(NULL);

2. Prior to the if statement do the following

#NEXT_POINTER = NEXT_PTR -> ISP1_NEXT_PTR; <-- set value


RETURN (DELETE_ISP_AREA(NEXT_PTR,#NEXT_POINTER));

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.

PL/I V3R5M0 - Getting Started 25


Declared Variables not used.
If a variable is declared and not referenced in the program then it is discarded. For
example:

Example.
%DCL EYEBALL_COMPILETIME CHAR;
%EYEBALL_COMPILETIME =
'''** COMPILED (' || COMPILETIME || ')''';

DCL EYEBALL CHAR( 32)


INIT(EYEBALL_COMPILETIME);

The EYEBALL variable is null where is should really contain


** COMPILED (24.JAN.03 11.15.17)

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 || ')''';

DCL EYEBALL CHAR( 32)


INIT(EYEBALL_COMPILETIME) STATIC ABNORMAL;

Variable declared as static.


A variable declared EXTERNAL STATIC cannot be set by an INIT

Wrong Way.
DCL COMPILE STATIC EXTERNAL CHAR( 40) INIT('CPP????');

Instead use:

Right Way.
DCL COMPILE STATIC EXTERNAL CHAR( 40);
COMPILE = 'CPP......'; /* program name*/

Print file does not appear in SDSF


If a print file is declared in the program and is never used then the FILENAME will
not appear in SDSF output.

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:

PUT FILE(TEMP) EDIT(BUFFER) (COL(1),A);

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.

PL/I V3R5M0 - Getting Started 27


RETURNS Satement.
A function, for example, a PROCEDURE or ENTRY statement with the
RETURNS attribute, has been invoked in a CALL statement. The value that is
returned by the function will be discarded, but the OPTIONAL attribute should be
used to indicate that this is valid.

*** Calling statement ***


CALL UPDATE_NEWMTBL; *No use of the returned value*

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.

Improper Procedure definition statement


UPDATE_NEWMTBL: PROC RETURNS(FIXED BIN ( 31));

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);

IBM1221I - Statement uses nnnnn bytes for temporaries


This message is produced if a statement uses more bytes for temporaries than
allowed by the MAXTEMP compiler option.

To declare more storage for Temporariesuse :


%PROCESS MAXTEMP(nnnnn); (n in bytes)

28

You might also like