1 WESLIB_$
  Westat's library of in-house functions is designed to supplement the
  normal complement of builtin functions available in VAX PL/I.
  External entry definitions may be included in any PL/I program through 
  the %INCLUDE preprocessor command.  The text library for these 
  definitions is assigned to the logical name PLI$LIBRARY.  The object
  module library is assigned to LNK$LIBRARY.  These libraries are
  searched by default at compile and link times, so users need no
  additional command language to use the library.  Source code
  is stored in the directory WES$LIBRARY.  For a printed copy of
  this help file type PRINT WES$LIBRARY:WESLIB.HLP.

  To suggest additions to this library or report bugs please contact
  Allan Jaworski at Westat (301)-251-1500 x8645.
2 Arguments
  Arguments and return values for the WESTAT library routines
  unless otherwise specified are:

         FIXED BINARY(31) for integers
         FLOAT BINARY(53) for real numbers
         CHAR(*) VARYING for strings
         BIT(1) for logical values

  Since some of the VAX languages do not support varying-length
  character or single bit data types, many functions may not be
  directly usable.  Source code is available to aid in conversion
  for special applications with other data types and other languages.
  It is stored in the directory WES$LIBRARY.
2 Cautions
  These routines have been thoroughly checked, but Westat makes
  no guarantees as to their reliability.  Damages due to incorrect
  algorithms or bugs are the responsibility of the user.

  To achieve generality many of these routines are less efficient
  than they could be.  If you are building a production system,
  check the source code of procedures in the library and make
  whatever modifications necessary to guarantee long-term efficiency
  and reliability.  

  One problem associated with the the string-processing functions
  is the use of RETURNS(CHAR(*) VARYING).  For generality the
  functions in this library use this as the argument return convention.
  Unfortunately the PL/I compiler does not have an adequate means
  to clear heap storage when this return is issued.  As a result
  programs using these functions can consume large amounts of
  virtual memory if a large number of calls are made.  For a
  frequently used production program change the code to
  RETURNS(CHAR(512) VARYING) or something similar to avoid
  piling up storage.
2 Installation
  For this library to be accessible to users the following
  logical name assignments should be included in SYSTARTUP.COM:

         $ ASSIGN/SYSTEM (this directory) WES$LIBRARY
         $ ASSIGN/SYSTEM WES$LIBRARY:WESLIB.TLB PLI$LIBRARY
         $ ASSIGN/SYSTEM WES$LIBRARY:WESLIB.OLB LNK$LIBRARY

  The help file should then be installed through

         $ LIBRARY/INS SYS$HELP:HELPLIB.HLB WES$LIBRARY:WESLIB.HLP
2 ALPHABETIC
  Function returns a BIT(1) value which is true if the argument
  string contains only upper and lower case letters and blanks.

  ALPHABETIC(string)

  Sample use:

  %INCLUDE ALPHABETIC;

  IF ^ALPHABETIC(STRING) THEN
    PUT SKIP LIST('String contains nonalphabetic characters.');
2 ALPHAMERIC
  Function returns a BIT(1) value which is true if the argument
  string contains only upper and lower case letters, numerics,
  and blanks.

  ALPHAMERIC(string)

  Sample use:

  %INCLUDE ALPHAMERIC;

  IF ^ALPHAMERIC(STRING) THEN
    PUT SKIP LIST('String contains nonalphameric characters.');
2 BATCH
  Function returns a BIT(1) value which is true when the program
  running batch and false when the program is running interactive.
  BATCH can be used to determine whether input should come from
  a file or from a terminal or what action should be taken in
  the event of an error condition.

  BATCH()

  Sample use:

  %INCLUDE BATCH;

  IF BATCH() THEN 
       GET FILE(PARAMETERS) EDIT(VALUE)(F(5));
  ELSE 
       GET EDIT(VALUE)(F(5)) 
           OPTIONS(PROMPT('Enter parameter value: '));   
2 CENTER
  Function returns a character string in which a passed string
  is centered.  CENTER can be used for centering headings on output.

  CENTER(input-string,output-string-length)

  Sample use:

  %INCLUDE CENTER;

  DCL HEADING CHAR(132) VARYING;
  HEADING=CENTER('PROJECT SUMMARY REPORT--WESTAT, INC.',132);

  IF END_PAGE_FLAG THEN PUT PAGE EDIT(HEADING)(A);
2 CHAIN
  Function allow chaining to a new program without argument passing.
  Argument is an executable image name.  A bad name may be trapped
  through an UNDEFINEDFILE(IMAGE) condition as illustrated below.

  CHAIN(image-name-string);

  Sample use:

  %INCLUDE CHAIN;
  %INCLUDE GETCHAR;

  DCL IMAGE FILE INPUT RECORD;
  ON UNDEFINEDFILE(IMAGE) BEGIN
    PUT SKIP LIST('Invalid program name');
    GO TO RESTART;
  END;
   
  RESTART:
    CALL CHAIN(GETCHAR('What program do you want to run?: ',20,''));
2 CHARC
  Function returns the ASCII complement of its argument, i.e.
  a string consisting of all ASCII characters which are not
  in the argument string.

  CHARC(string)

  Sample use:

  %INCLUDE CHARC;

  I=VERIFY(A,CHARC('0123456789'));
  IF I>0 THEN
    PUT EDIT(SUBSTR(A,I,1),' is the first numeric character.');
2 COMPRESS
  Procedure compresses multiple occurrences of a character in a
  string to a single occurrence followed by a count contained in
  one byte.  The effects of COMPRESS can be counteracted through 
  the EXPAND procedure.  The string to be compressed should be
  declared with a size at least 1.5 times its maximum compressed
  size to prevent data truncation due to degeneration of the
  compression algorithm.

  COMPRESS(compress-string,compression-character)

  Sample use:

  %INCLUDE COMPRESS;

  DCL A CHAR(200) VARYING;

  CALL COMPRESS(A,' '); /* Compresses blanks */
  WRITE FILE(OUT) FROM(A);
2 DATASECT
  Data sections are an alternative way to handle files, particularly
  files that do not have a strict rectangular structure or are 
  accessed randomly.  Examples of such files are text files and CATI 
  subject files.  Rather than reading and writing them record by
  record, the whole file is 'read in' to memory.  Actually the file
  is only mapped into the process's virtual address space.
  I/O is not done explicitly; rather the system's paging mechanism
  is used to transfer data from a disk file to memory.  Six routines
  are provided for manipulating data sections:

  DSOPENON -- to 'open' the file, i.e., to associate an area of virtual
           memory with the file.

  DSOPENRO -- to 'open' the file for read-only access.

  DSCLOSE  -- to 'close' the file, i.e., to release the area of virtual
           memory for other uses in the program. 

  DSCHKPTW -- to force a pageout, i.e., if the program crashes at a later
           point, the disk file will be at least as good as when this
           'checkpointing' routine is called.  Only those memory pages
           which have been modified since they were last paged out are
           actually written to disk.  Thus the file may be journalled
           very efficiently.  Do not use if the section was opened
           via DSOPENRO.

  DSGET    -- to 'get' a record from a sequential file.

  DSREWIND -- to 'rewind' the file, i.e. so that the next call to DSGET
           will return the first record in the file.
  
  If the records contain control information (e.g. relative files,
  or files with variable length records), you will have to take care 
  of this explicitly when you process the file.  However you can read
  sequentialfiles using DSGET and DSREWIND without worrying about the 
  format of the records.

  See HELP entries for the individual routines for details on their use.
  In the example below, a file is essentially opened, passed three times
  and updated each time, and closed:
  

  TDATSEC: PROC OPTIONS(MAIN);
    /*t dow 6/1/82 test datasec routines*/
    DCL DATA(32,128) FIXED BIN(31) BASED(P);
    DCL (P,VAP) PTR;
    DCL Q PTR BASED(VAP);
    DCL FNAME CHAR(63) VAR INIT('DSTEST.DAT');
    DCL (LL,PFC,ALQ,EFN,I,J,K) FIXED BIN(31) INIT(0);

    DCL DSOPENON EXTERNAL ENTRY(PTR,FIXED BIN(31),CHAR(*) VAR,
                                FIXED BIN(31));
    DCL DSCHKPTW EXTERNAL ENTRY(PTR,FIXED BIN(31));
    DCL DSCLOSE EXTERNAL ENTRY(PTR);

    PFC = 1;
    ALQ = 32;
    EFN = 0;
    CALL DSOPENON(VAP,ALQ,FNAME,PFC);
    P = Q;
    DO K = 1 TO 3;
      LL = 40144*K;
      DO I = 1 TO ALQ;
        DO J = 1 TO 128;
    	    DATA(I,J) = I+LL;
     	END;
    	CALL DSCHKPTW(VAP,EFN);
        /*PUT SKIP EDIT(I, ' SAFE    ')(F(12),A);*/
      END;
    END;
    CALL DSCLOSE(VAP);
  END TDATSEC;
2 DSOPENRO
  Procedure to 'open' a data section for read-only access.  You should 
  not try to modify the data, or you will get serious error messages, 
  since the system will refuse to page anything out.  This entry point
  is most useful for accessing large chunks of constant data on disk.
  The %INCLUDE file for this entry point declares it as a function, 
  where the rms error code "FNF" - file not found - is returned if
  the requested file is not on disk.  (Contrast this with DSOPENON
  which creates a new file when none exists).
  
  STS$VALUE = DSOPENRO(vap, alq, filespec, pfc);

  vap: (ptr) DSOPENRO will store the address of its 144-byte
       control block here.  The first longword in this control block
       is the address of the start of the data section in memory.

  alq: (fixed bin(31)) The actual number of whole blocks in the file is
       returned.  If the file is an ordinary sequential file, such
       as is output from a text editor, or a survey machine edit program,
       there may be a partially filled block at the end of the file.
       This block is not included in the alq count.  You may discover
       this situation by examining the word at vap+116, which contains
       the partial byte count.  Alternatively you may use DSGET and
       DSREWIND to access the data, as these routines take into
       account this problem.

  filespec: (char(*) var) The filespec for the disk file to be opened.

  pfc: (fixed bin(31)) Number of pages per page fault cluster.  Ideally
       this should be 1, but this may result in an excessively large
       list of pages, or excessive I/O overhead.

  Sample use:

    %INCLUDE DSOPENRO;
    DCL VAP PTR, P PTR BASED(VAP), A32 FIXED BIN(31);
    DCL DATA_PAGE(32) CHAR(512) BASED(P);
    CALL DSOPENRO(VAP,A32,'MYDATA.DAT',1);

2 DSOPENON
  Procedure to 'open' a data section.  If the file specified does not 
  already exist, it will be created with fixed length records whose size
  is 512*alq bytes; i.e., one record for the whole file.  If the file
  does already exist, the revision number will be incremented and the file
  will be updated in place. (The ON in the name refers to Old/New.)
  Use the entry point DSOPENRO to open a section for read-only access.
  Type HELP WESLIB DATASECT for a general discussion of data sections.

  CALL DSOPENON(vap, alq, filespec, pfc);

  vap: (ptr) DSOPENON will store the address of its 144-byte contol block
       here.  The first longword in this control block is the address
       of the start of the data section in memory.

  alq: (fixed bin(31)) The number of blocks to be allocated if the file
       doesn't exist.  If the file does exist, the actual number of 
       whole blocks in the file is returned.  (See the discussion of
       this parameter under DSOPENRO about partially filled blocks at
       the end of the file.)
  
  filespec: (char(*) var) The filespec for the disk file to be opened.

  pfc: (fixed bin(31)) Number of pages per page fault cluster.  Ideally
         this should be 1, but this may result in an excessively large
         list of pages.

  Sample use:

    %INCLUDE DSOPENON;
    DCL VAP PTR, P PTR BASED(VAP), A32 FIXED BIN(31) INIT(32);
    DCL DATA_PAGE(32) CHAR(512) BASED(P);
    CALL DSOPENON(VAP,A32,'MYDATA.DAT',1);
2 DSCHKPTW 
  Procedure to take a checkpoint of a data section.  All page fault
  clusters that have been modified but not yet paged out are written 
  back to the section file on disk.  Type HELP WESLIB DATASECT 
  for a general discussion of data sections.  Control is not returned
  to the caller until the I/O has completed.
  
  CALL DSCHKPTW(vap,efn);
  
  vap: (ptr) address of control block (returned by DSOPENON)

  efn: (fixed bin(31)) event flag number to use to test completion of
       I/O.  0 is suggested.  See the System Services manual for details.
       In general, a number between 0 and 23.

  Sample use:

    %INCLUDE DSOPENON;
    %INCLUDE DSCHKPTW;
    DCL VAP PTR, A32 FIXED BIN(31) INIT(32);
    CALL DSOPENON(VAP,A32,'MYFILE.DAT',1);
    ...
    CALL DSCHKPTW(VAP,0);
2 DSCLOSE
  Procedure to 'close' a data section.  A checkpoint is taken (as in
  DSCHKPTW) and the virtual address space is deleted.  The memory for
  the control block is freed and the pointer vap is set to null.

  Type HELP WESLIB DATASECT for a general discussion of data sections.

  CALL DSCLOSE (vap);

    vap: (ptr) address of control block, as returned by DSOPENON.
  
  Example:

    %INCLUDE DSOPENON;
    %INCLUDE DSCLOSE;
    DCL VAP PTR, A32 FIXED BIN(31) INIT(32);
    CALL DSOPENON(VAP,A32,'MYFILE.DAT',1);
    ...
    CALL DSCLOSE(VAP);
2 DSGET
  Procedure to 'get' a record from a sequential file accessed as a
  data section.  If the file contains fixed length records, a pointer
  to the start of the record is returned, along with the (constant)
  record length.  If the file consists of variable length records, 
  the pointer points to the start of the data, i.e. right after the
  the length word, and the returned length is the length of the data
  portion of the record (excluding the length word).  If the record
  format is VFC, variable length with a fixed control portion, the 
  pointer points to the start of the variable portion of the record
  (i.e. right after the fixed portion), and the returned length is the
  length of the variable portion.  If the record format is undefined, 
  fixed 512-byte records are assumed.  If the pointer is null, an
  end-of-file condition has been detected.  Type HELP WESLIB DATASECT
  for a general discussion of data sections.  

  CALL DSGET(vap, line_pointer, line_length);
    vap (PTR) -- address of control block, as returned by DSOPENRO or
                 DSOPENON.
    line_pointer (PTR) -- points to the record that was 'gotten'
    line_length (FIXED BIN(31)) -- length of the line
  
  Example:

  %INCLUDE DSOPENRO; %INCLUDE DSGET;
  DCL (VAP,LPT) PTR, (ALQ,LEN) FIXED BIN(31);
  DCL FIXREC CHAR(80) BASED(LPT);
  DCL VARREC CHAR(80) VARYING;
  CALL DSOPENRO(VAP,ALQ,'MYFILE.DAT',1);
  CALL DSGET(VAP,LPT,LEN);
  DO WHILE(LPT ^= NULL);
    VARREC = SUBSTR(FIXREC,1,LEN);
    PUT EDIT (VARREC)(A);
    CALL DSGET(VAP,LPT,LEN);
  END;
2 DSREWIND
  Procedure to 'rewind' a sequential file accessed as a data section.
  The next call to DSGET will return the first record in the file.
  (This routine does not cause a page fault for the data section.)
  Type HELP WESLIB DATASECT for a general discussion of data section.

  CALL DSREWIND(vap);
    vap (PTR) -- pointer to the control block, returned by DSOPENRO or
                 DSOPENON.
  
  Example:

  %INCLUDE DSOPENRO; %INCLUDE DSGET; %INCLUDE DSREWIND;
  CALL DSOPENRO(VAP,ALQ,'MYFILE.DAT',1);
  CALL DSGET (VAP,LPT,LEN) ;
  DO WHILE (LPT ^= NULL);
    ...
    CALL DSGET ...;
  END;
  CALL DSREWIND(VAP);
  CALL DSGET ...;
  DO WHILE(LPT ^= NULL);  etc...
2 DUPLICATE
  Function returns a logical value of true if a string argument has
  been previously passed to it.  DUPLICATE is useful for checking for
  duplicate values during the run of a program.  The second argument
  is a pointer to a storage area which should be initialized to
  NULL().  Use of several pointers allows multiple checks to be
  done simultaneously.  Values are stored in memory down a 
  balanced binary tree, so checks for duplicates among more than 
  a hundred thousand items may cause excessive page faulting. 

  DUPLICATE(string,pointer)
3 Sample_use
  /* Check records for duplicates of two different keys in one
     pass of a file */
  DCL 1 RECORD,
        2 KEY_A CHAR(6),
        2 KEY_B CHAR(4),
        2 DATA CHAR(70);
  DCL (LIST_A,LIST_B) POINTER;
  DCL EOF BIT(1) INIT('0'B);

  ON ENDFILE(IN) EOF='1'B;

  LIST_A=NULL();
  LIST_B=NULL();    

  READ FILE(IN) INTO(RECORD);

  DO WHILE(^EOF);
    IF DUPLICATE(KEY_A,LIST_A) THEN
       PUT SKIP LIST(KEY_A,'First key duplicated.');
    IF DUPLICATE(KEY_B,LIST_B) THEN
       PUT SKIP LIST(KEY_B,'Second key duplicated.');  
    READ FILE(IN) INTO(RECORD);
  END;
2 EXPAND
  Procedure counteracts the effects of the COMPRESS procedure
  reconstructing character string from its compressed form.

  EXPAND(decompress-string,compression-character)

  Sample use:

  %INCLUDE COMPRESS;
  %INCLUDE EXPAND;
  
  DCL A CHAR(80) VARYING;

  A='15        3        5        7         32';
  /* A is now 40 characters long */
  CALL COMPRESS(A,' ');
  /* A is now 15 characters long */
  CALL EXPAND(A,' ');
  /* A is now in its original 40 character form */  
2 FILTER
  Function removes all characters which do not appear in the
  second argument from the first argument.  If the second argument
  is null then only upper and lower alphabetics, numerics, blanks,
  and the special characters '-', '_', and '#' are let through.

  FILTER(input-string,allowable-string)

  Sample use:

  %INCLUDE FILTER;

  A='The # is <5.';
  A=FILTER(A,'');
  /* A='The # is 5' */
  A=FILTER(A,'0123456789');
  /* A='5' */  
2 FORMAT
  Procedure writes a formatted dump of a character string to
  a print file.  A ruler is also printed.  Arguments are the
  character string, width of segments to be printed, and
  the output file name.  Print segment width must be an
  integer between 10 and 120.

  FORMAT(string,width-value,print-file-name)

  Sample use:

  DCL A CHAR(300) VARYING;
  DCL OUT FILE PRINT;

  %INCLUDE FORMAT;

  CALL FORMAT(A,80,OUT);
  /* A is printed in four segments of 80, 80, 80, and 60 characters
     with an 80 column ruler. */
2 GETCHAR
  Function prompts for a character string.  If the third argument
  is a non-null character string GETCHAR verifies that the string
  entered contains only characters in this argument reprompting
  if necessary.  Since GETCHAR uses the QIOW system service, it
  is not necessary to enter a carriage return for the program
  to read the data.  This is useful for short prompts.  GETCHAR
  also performs automatic conversion to upper case.

  GETCHAR(prompt-string,chars-needed,verify-string)

  Sample use:

  %INCLUDE GETCHAR;
  
  A=GETCHAR('Enter YES or NO: ',1,'YN');
2 GETFILE
  Function prompts for the name of a file which must already 
  exist.  If the name of a file which cannot be opened is
  specified then an error message is printed and a reprompt
  occurs.  The full expanded name of the file including device
  name, directory, file name, and type suffix is returned.

  GETFILE(prompt-string)

  Sample use:

  %INCLUDE GETFILE;

  FILE_NAME=GETFILE('Enter input file name: ');
  OPEN FILE(IN) TITLE(FILE_NAME);
  /* An UNDEFINEDFILE condition will not occur. */
2 GETFIX
  Function prompts for a number checking range and reprompting
  when necessary.  

  GETFIX(prompt-string,range-string)

  Sample use:

  %INCLUDE GETFIX;

  DCL VALUE FIXED BINARY(31);
  VALUE=GETFIX('Enter a number: ','1-10,97,99');
2 GETWORD
  Function uses first argument to prompt for a response until
  user replies with one of the keywords listed in the second
  argument (or an unambiguous abbreviation).  The matching
  keyword is then returned.

  GETWORD (prompt-string,keyword-list)

  Sample use:

  %INCLUDE GETWORD;

  KEYWORD = GETWORD ('Enter type of sample: ',
     'SIMPLE EXACT SYSTEMATIC');
  IF KEYWORD = 'SIMPLE' THEN ...;
  ELSE IF KEYWORD = 'EXACT' THEN ...;
  ELSE IF KEYWORD = 'SYSTEMATIC' THEN ...;
3 Delimiters
  Any nonalphameric character except the underscore may be used
  to delimit the keywords, provided it does not occur in any
  keyword.  Leading and trailing delimiters are optional.  If 
  the answer contains a delimiter, it is considered invalid and
  the user is prompted again.  If the keyword list contains two
  consecutive delimiters, a null (or all blank) answer is 
  considered valid, and a null string is returned.
3 Matching
  Leading and trailing blanks are trimmed from the answer be-
  fore the keyword list is searched.  Lowercase characters are 
  treated as being equal to their uppercase equivalents.  The
  returned value will always be uppercase, even if the keyword
  list is partially or entirely in lowercase.
3 Null_answer
  GETWORD accepts a null (or all blank) answer as valid, and
  returns a null string, if and only if the keyword list con-
  tains two consecutive occurrences of the delimiter character.
  Otherwise, a null answer is considered invalid and GETWORD
  reprompts the user.
2 INDEXC
  Function searches left to right in first argument for the occurrence
  of a character contained in the second argument.  Returns position
  of character in first string if found.  If not found returns zero.

  Sample use:

  %INCLUDE INDEXC;       

  STRING='PUT FILE(SYSPRINT)(VALUE)(SKIP,F(5));';
  SEPARATORS=',;()-';

  I=INDEXC(STRING,SEPARATORS);
  /* I=9 */
2 INTEGERK
  Function returns a bit(1) value which is true when the argument 
  can be interpreted as an integer number, i.e. it can be
  converted by using a GET LIST statement.  Any embedded blanks will 
  fail.  Thus the text ' 123 junk ' will return '0'b, even though
  a GET LIST would yield '123'.  This function was designed for 
  checking bulk data.  The function NUMERIK should be used to check 
  the convertiblity of the argument to a FLOAT BIN value.
  
  INTEGERK(string)          (Source Code is in INTEGERK.MAR)

  Sample use:

  %INCLUDE INTEGERK;

  DCL TEXT CHAR(20) VAR;
  DCL X FIXED BIN(31);
  IF INTEGERK(TEXT) THEN GET STRING(TEXT) LIST(X);
  ELSE CALL ERROR_MESSAGE(343);
2 JIG
  JIG compares two input strings and returns a value of 1.0 if they
  are identical or if they would be identical but for the reversal
  of a pair of characters or the insertion or deletion of one
  duplicate character; values slightly less than 1.0 are returned by 
  other nearly identical input strings thus JIG('111234444','111324444')
  returns 1.0, and so does JIG('PHILLIPS','PHILIPS'), while a value
  of 0.88888 is returned by JIG('MCDONALD','MACDONALD').

  A JIG result of 1.0, or greater than some arbitrarily chosen
  threshold value (e.g., .83), might be used to determine
  "matches" in comparing data known not to be error free.

  JIG(string1,string2)

  Sample use:

  %INCLUDE JIG;
  DCL JIGRESULT FLOAT BINARY(53);
  JIGRESULT = JIG('KASPEROVICZ','KASPEROWICZ');
  /* JIGRESULT = 0.909090909 . . . */
2 JULDATE
  Function takes date character string in the form YYMMDD and
  returns number of days after December 31, 1899.  To reverse this 
  calculation use the function YYMMDD.  JULDATE also accepts partial 
  dates, separator characters, three letter month abbreviations,
  and the missing values '  ' and '99' in place of month and day.
  The string 'TODAY' evaluates to the current date.  All the
  following dates are interpreted as June 15, 1982:

  '820615', '820699', '829999', '82  15', '82', '8206', '82/6/15',
  '82/99/99', '82-6', '82//', '82JUN15', '82JUN'

  -1 is returned if the string cannot be evaluated as a date.
  Typical strings which will evaluate to -1 are:

  '82/13/15', '061582', '82/2/29', 'JUN15', '82/6-15', 'TODDAY' 

  JULDATE(date-string)

3 Sample_use

  %INCLUDE JULDATE;

  DCL BIRTHDATE CHAR(6) VARYING;
  DCL AGE FIXED BINARY(31);

  AGE=JULDATE(DATE())-JULDATE(BIRTHDATE));
  /* AGE IN DAYS CALCULATED */

  AGE=DIVIDE(AGE,365.25,31);
  /* AGE IN YEARS CALCULATED */
3 Parsing_algorithm
  JULDATE follows a fixed search pattern to interpret date-strings.
  In sequence it tests for the following configuations of characters:
  
  YYMMDD:   length=6, all numerics or blanks
  YYMM:     length=4, all numerics or blanks
  YY:       length=2, all numerics or blanks
  YYMONDD:  all numerics, alphabetics, or blanks,
            MON=standard 3-letter month abbreviation
  YRIMNIDY: YR, MN, DY strings of blanks or numerics evaluating
            to valid integers; I=blank, '/', or '-' 
  YRIMN:    YR, MN strings of blanks or numerics evaluating
            to valid integers; I=blank, '/', or '-' 
  YR:       YR a string of blanks or numerics evaluating
            to a valid integer

  If any of these date-string interpretations lead to a month or day
  having the values '  ' or '99' JULDATE substitutes '06' and '15'
  respectively.
2 LDIST
  Procedure requires two string input arguments
  and returns three values: the length of the longer string,
  the Levenshtein distance between the strings, and the
  ratio of the latter to the former.  The Levenshtein distance
  is defined as the minimum number of substitutions, insertions,
  and deletions that will translate one of the strings to
  the other.  LDIST can be used for matching names and other
  operations where some error in identifier data is expected.
  It should be used with some discretion since LDIST consumes 
  substantially more cpu time than a simple character
  equality check.

  LDIST(string1,string2,length-longer,Levensh-dist,Levensh_ratio)

  Sample use:

  %INCLUDE LDIST;

  CALL LDIST('ALLEN','ALAN',MAX_LENGTH,LEVENSH_DIST,LEVENSH_RATIO);

  /* MAX_LENGTH=5  LEVENSH_DIST=2  LEVENSH_RATIO=.4 */
2 LOP
  The LOP function returns the left-most chunk of a text string defined
  by the occurrence of any character specified in a string of delimiters.
  The original string is reduced to the remainder after the returned
  string and delimiter are lopped off.  The delimiter character is
  returned as a third argument.

  LOP(text-string,delimiter-string,delimiter-found)

  Sample use:

  %INCLUDE LOP;

  TEXT_STRING='FILE(SYSPRINT)'
  TOKEN=LOP(TEXT_STRING,'()/;',DELIMITER);

  /* TOKEN='FILE', TEXT_STRING='SYSPRINT)',  DELIMITER='(' */

  Blanks are treated somewhat differently from other delimit-
  ers.  Leading blanks in text-string are disregarded, consec-
  utive blanks are treated as a single delimiter, and blanks
  immediately preceding or following a nonblank delimiter are
  ignored.
2 LOWER
  The LOWER function translates all uppercase alphabetic
  characters in a character string to their lowercase equivalents 
  and returns the result.  Lowercase letters and nonalphabetic
  characters are unchanged.

  LOWER(string)

  Sample use:

  %INCLUDE LOWER;
  %INCLUDE UPPER;

  GET EDIT(FIRST_NAME)(A(15))
      OPTIONS(PROMPT('What is your first name?: '));
  FIRST_NAME=UPPER(SUBSTR(FIRST_NAME,1,1))||
             LOWER(SUBSTR(FIRST_NAME,2,14));
2 LTRIM
  The LTRIM function removes leading blanks, if any, from
  an input character string and returns the new string.  If the input
  string is all blanks, LTRIM returns a null string.

  LTRIM(string)

  Sample use:

  %INCLUDE LTRIM;

  STRING=LTRIM('   MARCH 3, 1982    ');

  /* STRING='MARCH 3, 1982    ' */
2 MATEXE
  Procedure multiplies two double precision floating point matrices.
  Entries returned in the first argument are the element by element
  products of respective elements of the second and third arguments.
  High and low bounds for all three matrices must be the same.

  MATEXE(product-matrix,factor-matrix1,factor-matrix2)

  Sample use:

  %INCLUDE MATEXE;

  DCL (A(3,5),B(3,5),C(3,5))

  CALL MATRXC(A,B,C);
  /* A CONTAINS THE DOT PRODUCT OF B AND C */  
2 MATRXC
  Procedure multiplies two double precision floating point matrices.
  Entries returned in the first argument are the dot products of
  respective rows and columns of the second and third arguments.
  High and low bounds for all three matrices must be compatible.

  MATRXC(product-matrix,row-factor-matrix,column-factor-matrix)

  Sample use:

  %INCLUDE MATRXC;

  DCL (A(3,5),B(3,10),C(10,5))

  CALL MATRXC(A,B,C);
  /* A CONTAINS THE MATRIX PRODUCT OF B AND C */  
2 MAXLENGTH
  Function returns a value which is the maximum length that the
  argument, a CHAR VARYING string, can have.  This is unlike the
  builtin LENGTH function which returns the current length.
  MAXLENGTH is of use in certain subroutines where it is important
  to detect truncation of strings.

  MAXLENGTH(string)       (Source code is in MAXLENGTH.MAR )

  Sample use:

  %INCLUDE MAXLENGTH;

  DCL TARGET CHAR(*) VARYING;
  DCL SOURCE CHAR(255) VARYING;

  TARGET=SOURCE;
  IF LENGTH(SOURCE) > MAXLENGTH(TARGET) THEN PUT EDIT
  	('String truncated')(SKIP,A);
2 NUMERIC
  Function returns a BIT(1) value which is true if the argument
  string contains only the digits 0-9.  Embedded blanks and
  signs are not allowed.

  NUMERIC(string)

  Sample use:

  %INCLUDE NUMERIC;

  IF NUMERIC(STRING) THEN I=FIXED(STRING,31);
2 NUMERIK
  Function returns a bit(1) value which is true when the argument 
  can be interpreted as a floating point number, i.e. it can be
  converted by using a GET LIST statement. 
  
  NUMERIK(string)          (Source Code is in NUMERIK.MAR)

  Sample use:

  %INCLUDE NUMERIK;

  DCL TEXT CHAR(20) VAR;
  DCL X FLOAT BIN(53);
  IF NUMERIK(TEXT) THEN GET STRING(TEXT) LIST(X);
  ELSE CALL ERROR_MESSAGE(33);
2 QIOWPUT
  Write a character string to the terminal bypassing the normal RMS
  route in favor of a direct QIOW.  QIOWPUT is somewhat more efficient
  than a PUT and is useful for certain CRT applications where a
  continuous stream of characters must be fed to the terminal without
  RMS intervention.

  QIOWPUT(string)

  Sample use:

  %INCLUDE QIOWPUT;

  CALL QIOWPUT('This clears the screen.'||BYTE(27)||'[2J');
2 REVERSE
  REVERSE returns a character string which is the mirror image
  of its argument.

  REVERSE(string)

  Sample use:

  %INCLUDE REVERSE;

  A='HELP!  I AM TRAPPED IN A MIRROR.';
  B=REVERSE(A);

  /* B='.RORRIM A NI DEPPART MA I  !PLEH' */
2 RTRIM
  The RTRIM function removes trailing blanks, if any, from
  an input character string and returns the new string.  If the input
  string is all blanks, RTRIM returns a null string.

  RTRIM(string)

  Sample use:

  %INCLUDE RTRIM;

  STRING=RTRIM('   MARCH 3, 1982    ');

  /* STRING='   MARCH 3, 1982' */
2 SCAN
  The SCAN function parses a string for the n-th word defined by
  a set of delimiters.  Unlike the SAS SCAN function, adjacent
  delimiters are treated as separate and a null string is returned.
  However, lead blanks are trimmed and blanks adjacent to 
  delimiters are disregarded.  


  SCAN(string,n,delimiter-string)

  Sample use:

  %INCLUDE SCAN;
  CITY=SCAN('43 Maple Road, Rockville,MD',4,' ,');

  /* CITY='Rockville' */
2 SOUNDEX
  Function returns a four character SOUNDEX code for its argument.
  SOUNDEX coding collapses similarly sounding words and names into
  a single code which may aid in matching operations.

  SOUNDEX(string)

  Sample use:

  %INCLUDE SOUNDEX;
  DCL (CODE1,CODE2,CODE2) CHAR(4);

  CODE1=SOUNDEX('McCloud');
  CODE2=SOUNDEX('MacCloud');
  CODE3=SOUNDEX('McLeod');
  /* CODE1, CODE2, and CODE3 all contain the value 'M253' */  
3 Algorithm
  Upper and lower case alphabetic characters are treated as the
  same.  Non-alphabetic characters and embedded blanks are ignored.
  The first character of the code is the first alphabetic character
  contained in the string.  Successive characters are collapsed to
  numerics or ignored as follows:

       B,F,P,V            -> 1
       C,G,J,K,Q,S,X,Z    -> 2
       D,T                -> 3
       L                  -> 4
       M,N                -> 5
       R                  -> 6
       A,E,I,O,U,Y,H,W    ignored

  Two or more adjacent characters with the same code are combined
  and the first three different digits form places two through four
  of the code.  If less then three digits are found then padding 
  blanks are included for the rightmost digits of the code.
2 SUM
  Function sums a double precision floating point array.

  SUM(floating-point-array)

  Sample use:

  %INCLUDE SUM;

  DCL VALUES(*) FLOAT BINARY(53);
  MEAN_OF_VALUES=SUM(VALUES) / 
              FLOAT(HBOUND(VALUES,1)-LBOUND(VALUES,1)+1,53);
2 SUMFIX
  Function sums a longword fixed point array.

  SUMFIX(fixed-point-array)

  Sample use:

  %INCLUDE SUMFIX;

  DCL VALUES(*) FIXED BINARY(31);
  TOTAL_VALUES=SUMFIX(VALUES);
2 TARGET
  Procedure prints target labels for centering line printer on 
  any rectangular label stock.  An output file named LABELS must
  be declared in the calling procedure with the attributes 
  OUTPUT PRINT.

  TARGET(value-up,value-down,value-across,value-between,
         value-lines,value-skip)

  Sample use:

  %INCLUDE TARGET;
  DCL LABELS FILE OUTPUT PRINT;

  CALL TARGET(3,8,38,3,8,1);
3 Arguments
  TARGET(value-up,value-down,value-across,value-between,
         value-lines,value-skip)

  Six arguments must be specified:

  value-up:       number of labels across the label stock

  value-down:     number of labels down one page of the label stock

  value-across:   maximum number of characters to be printed across
                  a single label

  value-between:  number of spaces to be left blank between labels 
                  horizontally

  value-lines:    maximum number of lines per label

  value-skip:     number of lines to be skipped between labels
                  vertically
2 TESTWORD
  Function determines whether the first argument matches (or is
  an unambiguous abbreviation of) any of the keywords listed in
  the second argument.  It returns the matching keyword, or a
  null string if no match is found.

  TESTWORD (token, keyword_list)

  Sample use:

  %INCLUDE TESTWORD;

  KEYWORD = TESTWORD (INPUT_STRING, 'SIMPLE EXACT SYSTEMATIC');
  IF KEYWORD = 'SIMPLE' THEN ...;
  ELSE IF KEYWORD = 'EXACT' THEN ...;
  ELSE IF KEYWORD = 'SYSTEMATIC' THEN ...;
  ELSE ...;  /*** NO MATCH FOUND ***/
3 delimiters
  Any nonalphameric character except the underscore may be used
  to delimit the keywords, provided it does not occur in any
  keyword.  Leading and trailing delimiters are optional.  If 
  the token contains a delimiter, a null string is returned.
3 matching
  Leading and trailing blanks are trimmed from the token before
  the keyword list is searched.  Lowercase characters are 
  treated as being equal to their uppercase equivalents.  The
  returned value will always be uppercase, even if the keyword
  list is partially or entirely in lowercase.
2 TREE
  Procedure does searches and insertions down a height-balanced
  binary tree.  For a comprehensive description of this data
  storage mechanism see Knuth's book The Art of Computer Programming,
  Volume III.  Briefly, the balancing mechanism is an efficient
  way of preventing trees from degenerating to list-like structures.
  This is especially useful when nodes are inserted in key-sort
  order.   Keys can be fixed-length character strings,
  longword integers, longword or double-precision floating point.
  The first nine bytes of any node to be inserted must be 
  reserved for left and right pointers and a single balancing byte.

  TREE(header-pointer,insert-node-pointer,key-type-length-code,
       key-offset,return-pointer)
3 Arguments
  TREE(header-pointer,insert-node-pointer,key-type-length-code,
       key-offset,return-pointer)

  header-pointer:  This is a pointer to the tree header node. 
    It should be initially set to NULL().

  insert-node-pointer:  This is a pointer to node to be inserted.
    The node should be a BASED variable with the first nine bytes
    reserved for use as left and right pointers and balancing byte.

  key-type-length-code:  This is a FIXED BIN(31) code for key type
    and length.

  key-offset:  This is the absolute offset of the key within the
    node.  The smallest possible value is 9, i.e. the key begins
    in byte 10 of the node.

  return-pointer:  This pointer is set to the address of any node
    found with the given key value.  If no node is found, the
    node passed to TREE is inserted in the tree and the address
    of the newly inserted node is returned.
3 Node_definition
  Nodes to be inserted should be of the form:
  
  DCL 1 NODE BASED(P),
        2 LEFT POINTER,
        2 RIGHT POINTER,
        2 BALANCE_BYTE CHAR(1),
        ...
        2 KEY ---,
        ...;
  
  The first three arguments should never be changed by user programs.
  Other arguments should be of fixed format or described by expressions
  in independent data structures since VAX PL/I does not support the 
  REFERS option.
3 Key-type-length-code
  This is a FIXED BINARY(31) code which determines the type of branching
  for the tree.  Different values of this code should not be mixed in 
  the same tree.  Available codes are:

  +n:  CHAR(n) (not VARYING)
   0:  FIXED BINARY(31)
  -1:  FLOAT BINARY(24)
  -2:  FLOAT BINARY(52)
3 Sample_use
  /* READ IN CHAR(5) KEYS.  IF KEY HAS PREVIOUSLY BEEN ENTERED THEN
     REPORT COUNT ELSE CREATE A NEW NODE */

  %INCLUDE TREE;

  DCL 1 NODE BASED(STORE_PTR),
        2 LEFT PTR,
        2 RIGHT PTR,
        2 BALANCE_BYTE CHAR(1),
        2 KEY CHAR(5),
        2 COUNT FIXED BINARY(31);

  DCL (KEY_TYPE_LENGTH,KEY_OFFSET) FIXED BINARY(31);
  DCL (HEADER,STORE_PTR,RETURN_PTR) POINTER;

  HEADER = NULL();
  KEY_TYPE_LENGTH=5;
  KEY_OFFSET=9;
  
  DO WHILE('1'B);
    ALLOCATE NODE SET(STORE_PTR);
    PUT SKIP;
    GET EDIT(KEY)(A(5)) OPTIONS(PROMPT('ENTER A KEY: '));
    CALL TREE(HEADER,STORE_PTR,KEY_TYPE_LENGTH,KEY_OFFSET,RETURN_PTR);
    IF RETURN_PTR=STORE_PTR THEN DO;
      COUNT=1;
      PUT SKIP LIST('FIRST KEY STORED');
    END;
    ELSE DO;
      FREE NODE;
      STORE_PTR=RETURN_PTR;
      COUNT=COUNT+1;
      PUT EDIT('RECORDS STORED=',COUNT)(SKIP,A,P'ZZ9');
    END;
  END;
3 Traversal
  Balancing only affects storage and deletion algorithms for trees.
  Standard methods can be used for dumping a balanced tree in sorted
  order.  A simple recursive routine for dumping the counts stored
  in the Sample_use example follows:

  CALL TREE_DUMP(HEADER);

  TREE_DUMP:  PROC(P);
  DCL P POINTER;
     
  IF P=NULL() THEN RETURN;
  ELSE DO;
    CALL TREE_DUMP(P->LEFT);         /* TRAVERSE LEFT TREE  */ 
    PUT SKIP LIST(P->KEY,P->COUNT);  /* PRINT CURRENT NODE  */
    CALL TREE_DUMP(P->RIGHT);        /* TRAVERSE RIGHT TREE */
  END;

  END TREE_DUMP;
2 TRIM
  The TRIM function removes leading and trailing blanks, if any, from
  an input character string and returns the new string.  If the input
  string is all blanks, TRIM returns a null string.

  TRIM(string)

  Sample use:

  STRING=TRIM('   MARCH 3, 1982    ');

  /* STRING='MARCH 3, 1982' */
2 UPPER
  The UPPER function translates all lowercase alphabetic
  characters in a character string to their uppercase equivalents 
  and returns the result.  Uppercase letters and nonalphabetic
  characters are unchanged.

  UPPER(string)

  Sample use:

  %INCLUDE UPPER;
  GET EDIT(ANSWER)(A(3))
      OPTIONS(PROMPT('Answer Yes or No: '));
  IF UPPER(ANSWER)='YES' THEN ...;
2 VTBLINK
  Sets cursor to writing blinking characters on a VT100-
  compatible terminal.  Use the VTRESET procedure to reset
  the cursor to writing normal characters.

  VTBLINK()

  Sample use:

  %INCLUDE VTBLINK;
  %INCLUDE VTBRIGHT;
  %INCLUDE VTRESET;

  CALL VTBLINK();
  CALL VTBRIGHT();
  CALL VTPUT(23,1,'Wrong Answer!');
  CALL VTRESET();
2 VTBOX
  Draws a box on the screen of a VT100-compatible terminal with a
  graphics character set.  The row and column of the upper left 
  corner of the box are given by the first two arguments respectively,
  and the lower right corner cordinates are similarly given by
  the second two arguments.

  VTBOX(row-value,column-value,row-value2,column-value2)

  Sample use:

  %INCLUDE VTBOX;

  CALL VTBOX(1,1,23,80);
  /* Draws a box around the periphery of the screen */
2 VTBRIGHT
  Sets cursor to writing high-intensity characters on a VT100-
  compatible terminal.  Use the VTRESET procedure to reset
  the cursor to writing normal characters.

  VTBRIGHT()

  Sample use:

  %INCLUDE VTBRIGHT;
  %INCLUDE VTPUT;
  %INCLUDE VTRESET;

  CALL VTBRIGHT();
  CALL VTPUT(23,1,'Wrong Answer!');
  CALL VTRESET();
2 VTCLEAR
  Clears the screen of a VT100-compatible terminal.

  VTCLEAR()

  Sample use:

  %INCLUDE VTCLEAR;

  PUT SKIP LIST('Watch everything vanish!');
  CALL VTCLEAR;
2 VTDEL
  Deletes characters on a VT100-compatible terminal from current
  cursor position to end of line.

  VTDEL()

  Sample use:

  %INCLUDE VTDEL;
  %INCLUDE VTPUT;

  CALL VTPUT(1,23,'This is the bottom of the screen.');
  CALL VTPUT(1,23,'');
  CALL VTDEL();
  /* Last line of screen is now blank. */
2 VTDPUT
  Positions the cursor of a VT100-compatible terminal to a row and
  column and writes a double-height double-width character string.
  Row and column specify upper left corner of string positions.

  VTDPUT(row-value,column-value,string)

  Sample use:

  %INCLUDE VTDPUT;

  CALL VTDPUT(1,20,'SPECIAL REPORT');
2 VTGETCHAR
  Function prompts for a character string by positioning cursor
  at row and column of a VT100-compatible terminal and prompting.
  If the verify string is non-null, VTGETCHAR checks to see that
  all characters entered are contained in the verify string.
  Automatic conversion of all letters to upper-case is done.
  Row 23 of terminal screen should be reserved for error messages.

  VTGETCHAR(row-value,column-value,prompt-string,
            chars-needed,verify-string)

  Sample use:

  %INCLUDE VTGETCHAR;

  A=VTGETCHAR(10,1,'Enter Yes or No: ',1,'YN');
2 VTPUT
  Positions the cursor of a VT100-compatible terminal to a row and
  column and writes a character string.  Pass a null string to position
  the cursor without writing anything.

  VTPUT(row-value,column-value,string)

  Sample use:

  %INCLUDE VTPUT;

  CALL VTPUT(23,20,'This is the bottom of the screen.');
2 VTRESET
  Resets the cursor on a VT100-compatible terminal to writing
  normal characters.  Counteracts the effects of VTBRIGHT,
  VTUNDER, VTBLINK, and VTREVERSE used singly or together.

  Sample use;

  %INCLUDE VTBRIGHT;
  %INCLUDE VTREVERSE;
  %INCLUDE VTRESET;

  CALL VTBRIGHT();
  CALL VTREVERSE();
  CALL VTDPUT(10,10,'WHAT A LIGHT SHOW!!');
  CALL VTRESET();
2 VTREVERSE
  Sets cursor to writing reversed characters on a VT100-
  compatible terminal.  Use the VTRESET procedure to reset
  the cursor to writing normal characters.

  VTREVERSE()

  Sample use:

  %INCLUDE VTREVERSE;
  %INCLUDE VTPUT;
  %INCLUDE VTRESET;

  CALL VTREVERSE();
  CALL VTPUT(23,1,'Wrong Answer!');
  CALL VTRESET();
2 VTUNDER
  Sets cursor to writing underlined characters on a VT100-
  compatible terminal.  Use the VTRESET procedure to reset
  the cursor to writing normal characters.

  VTUNDER()

  Sample use:

  %INCLUDE VTUNDER;
  %INCLUDE VTBRIGHT;
  %INCLUDE VTPUT;
  %INCLUDE VTRESET;

  CALL VTUNDER();
  CALL VTBRIGHT();
  CALL VTPUT(23,1,'Wrong Answer!');
  CALL VTRESET();
2 VT132
  Procedure sets the linesize of a VT100-compatible terminal to
  132 characters.

  VT132()

  Sample use:

  %INCLUDE VT132;
  %INCLUDE VTPUT;

  CALL VT132();
  CALL VTPUT(1,120,'Page 1');
2 VT80
  Procedure sets the linesize of a VT100-compatible terminal to
  80 characters.  VT80 is most commonly used to recover from the
  effect of VT132.

  VT80()

  Sample use:

  %INCLUDE VT80;
  %INCLUDE VT132;
  
  CALL VT132();
  ... /* 132 column terminal I/O operations here. */
  CALL VT80();
2 VXLNKCL
  Procedure to execute a dynamically callable subroutine that has
  been loaded by VXLOADR.  See VXLOADR for a general discussion and
  a sample usage.  VXLNKCL will work if the subroutine arguments are 
  all call-by-reference, i.e. arguments are not declared with the
  (*) or VALUE attributes, etc.  Minor  modifications are necessary 
  in other situations.  

  VXLNKCL(call_addr,subr_arg1,subr_arg2,...)

  call_addr is returned by VXLOADR.  It is the address of the code
  that was built to call the subroutine.  VXLNKCL pushes the subr_args
  onto the stack and the jumps to call_addr.

  subr_arg1,... are the arguments of the subroutine to be executed.
2 VXLOADR
  Subroutine to load a dynamically callable subroutine, i.e. to allow
  calls to subroutines determined at program run time.  The subroutine
  is then run with a call to VXLNKCL.

  CALL VXLOADR(exe_file_spec,calladdr);

  Sample use:

  %INCLUDE VXLOADR;
  %INCLUDE VXLNKCL;
  DCL CALLADDR PTR;
  DCL IA FIXED BIN(31),CH CHAR(8);

  CALL VXLOADR('XEX4.EXE',CALLADDR);
  ...
  CALL VXLNKCL(CALLADDR,IA,CH); 

  /* as if the program contained the lines
        DCL XEX4 ENTRY(FIXED BIN(31),CHAR(8));
        CALL XEX4(IA,CH);
     but subroutine name not specified at compile time */
3 Further_help
  VXLOADR does not load the image header or the symbol tables, 
  only the code and data.  The subroutine should first be compiled
  or assembled and linked.  (If it is a macro subroutine, be 
  sure to specify a transfer address in the .END directive.)  
  VXLOADR will copy the *.EXE file to a file with the same directory and
  filename, but with a file type of *.PVS.  This is the section file
  that VMS will use to page into and out of.  The PVS file is garbage
  when the image is finished.  You might want to put DEL *.PVS;* in
  your logoff exec.  Small MACRO programs will use a page or two.  
  Small PL/I programs without any I/O will use 35-40 pages, with I/O
  they gobble up 70-80 pages.  The subroutine ABSOLUTELY MUST be
  position-independent code (PIC).  Therefore NEVER use the .ADDRESS
  directive in MACRO, and beware of PL/I EXTERNALs, and FORTRAN COMMONs.
  VXLOADR is useful in designing general supervisory programs.  
  For example, you might want to write a general file 
  fixup/editing program, where the mainline handles the file I/O,
  record counts, etc., but reads a parameter to find out the 
  name of the editing routine and uses VXLOADR/VXLNKCL to execute
  the edit.  This is exactly the same scheme that CLMS's
  SAS PROC LMSDERV uses, and is very similar to the method that 
  SAS uses to execute PROC steps.
2 WAIT
  Procedure forces process to hibernate for a period of up to 86400 
  seconds.  Argument is in whole seconds.

  WAIT(second-value)

  Sample use:

  %INCLUDE WAIT;

  PUT EDIT('Program will now go to sleep for an hour.')(SKIP,A);
  CALL WAIT(3600);
  PUT EDIT('What a restful nap!')(SKIP,A);
2 WORDDATE
  Function takes an integer between 1 and 36524 and interprets it as
  a number of days after December 31, 1899 returning a character
  with the date fully expanded to words, e.g. April 1, 1982.

  WORDDATE(date-value)

  Sample use:

  %INCLUDE JULDATE;
  %INCLUDE WORDDATE;

  DCL I FIXED BINARY(31);

  I=JULDATE('82APR2');
  PUT EDIT(WORDDATE(I))(A);
  /* 'April 2, 1982' is printed */ 
2 WORKSET
  Procedure adjusts working set limit of a process from within a
  program.  Its argument is the requested working set size in pages.
  WORKSET cannot adjust the working set limit beyond the process
  quota or normal system minimums or maximums.  WORKSET is useful
  in optimizing response of programs when a sudden need for a large
  number of working set pages may arise or in surrendering working
  set pages after a large increase in working set size.

  WORKSET(page-value)

  Sample use:

  %INCLUDE WORKSET;

  CALL WORKSET(1024);
  /* A large memory intensive task could go here. */
  CALL WORKSET(80);
2 YYMMDD
  Inverse function of JULDATE.  Takes an integer between 1 and 36524 and
  interprets it as a number of days after December 31, 1899 returning
  a date character string in the form YYMMDD.
  
  YYMMDD(date-value)

  Sample use:

  
  %INCLUDE YYMMDD;
  %INCLUDE JULDATE;
  DCL LATER_DATE CHAR(6) VARYING;
  LATER_DATE=YYMMDD(JULDATE(DATE())+50);
  PUT EDIT(LATER_DATE,' is 50 days from today.')(SKIP,P'99/99/99',A);  
2 YYDDD
  Function reformats a JULDATE integer.  Takes an integer between 1
  and 36524 and interprets it as a number of days after December 31,
  1899 returning a date character string in the form YYDDD where YY
  is the year and DDD is the number of days after December 31 of the
  previous year.

  Sample use:

  %INCLUDE YYDDD;
  %INCLUDE JULDATE;

  DCL NEW_DATE CHAR(5);
  NEW_DATE=YYDDD(JULDATE('31DEC82'));
  /* NEW_DATE='82365' */
