ISPF Library services provide programmatic access to ISPF libraries, enabling programs to initialize library access, open libraries, list members, search for members, and manage library resources. Shared lists allow multiple programs to share library member information, improving efficiency and enabling coordination. Understanding Library services and shared lists is essential for building ISPF applications that work with panel libraries, message libraries, and other ISPF library types. This tutorial covers Library service functions, library access patterns, member enumeration, shared list operations, and best practices.
Library services extend REXX and other programming languages with the ability to work with ISPF libraries programmatically. Whether you need to enumerate panel library members, search for specific messages, or coordinate library access between multiple programs, Library services provide the necessary functions. Learning to use Library services and shared lists enables you to build sophisticated ISPF applications. This tutorial provides comprehensive guidance for using Library services effectively.
ISPF libraries are partitioned datasets (PDS or PDSE) that contain ISPF components.
ISPF libraries:
Common ISPF library types:
Libraries can be concatenated:
Library concatenation allows you to override system components with user-specific versions.
Library services provide functions for library management.
LMINIT initializes access to a library:
Example:
1234567891011121314/*REXX*/ parse arg libname address ispexec /* Initialize library access */ 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then do say 'Error initializing library:' libname say 'Return code:' rc exit 8 end say 'Library initialized, ID:' id /* Use library */ 'LMFREE DATASETID(id)' exit 0
This example:
LMINIT parameters:
The DATASETID parameter receives a unique identifier used for subsequent library operations.
LMOPEN opens a library for access:
Example:
1234567891011121314151617/*REXX*/ parse arg libname address ispexec /* Initialize */ 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then exit 8 /* Open for input */ 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do say 'Error opening library' 'LMFREE DATASETID(id)' exit 8 end /* Library is now open for operations */ 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
LMOPEN must be called after LMINIT and before member operations.
LMOPEN open modes:
Choose the appropriate mode based on your operations - use INPUT for read-only operations to prevent accidental modifications.
LMMLIST lists library members:
Example:
123456789101112131415161718192021222324/*REXX*/ parse arg libname address ispexec /* Initialize and open */ 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* List all members */ member_count = 0 do forever 'LMMLIST DATASETID(id) MEMBER(member) STATS(YES)' if rc <> 0 then leave member_count = member_count + 1 say 'Member:' member end say 'Total members:' member_count /* Cleanup */ 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
This example:
LMMLIST can filter members by name pattern:
12345678910111213141516171819/*REXX*/ parse arg libname pattern address ispexec 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* List members matching pattern */ do forever 'LMMLIST DATASETID(id) MEMBER(member) PATTERN('"'"pattern"'"')' if rc <> 0 then leave say 'Found member:' member end 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
Pattern matching allows you to find specific members, such as all panels starting with "MY" or all messages ending with "ERR".
LMMFIND searches for a specific member:
Example:
123456789101112131415161718192021/*REXX*/ parse arg libname membername address ispexec 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* Search for member */ 'LMMFIND DATASETID(id) MEMBER('"'"membername"'"')' if rc = 0 then do say 'Member' membername 'found' end else do say 'Member' membername 'not found' end 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
LMMFIND is more efficient than LMMLIST when you only need to check if a specific member exists.
LMCLOSE closes an open library:
Example:
12345678/*REXX*/ address ispexec 'LMINIT DATASETID(id) DATASET(MY.LIBRARY)' 'LMOPEN DATASETID(id) OPTION(INPUT)' /* Use library */ 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
Always close libraries before freeing them to ensure proper cleanup.
LMFREE frees library resources:
Example:
1234567/*REXX*/ address ispexec 'LMINIT DATASETID(id) DATASET(MY.LIBRARY)' if rc <> 0 then exit 8 /* Use library */ 'LMFREE DATASETID(id)' exit 0
Always call LMFREE to free resources, even if errors occur.
Common patterns for library access.
Standard pattern for library operations:
123456789101112131415161718/*REXX*/ address ispexec /* 1. Initialize */ 'LMINIT DATASETID(id) DATASET(libname)' if rc <> 0 then exit 8 /* 2. Open */ 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* 3. Use library */ /* ... library operations ... */ /* 4. Close */ 'LMCLOSE DATASETID(id)' /* 5. Free */ 'LMFREE DATASETID(id)' exit 0
This pattern ensures proper resource management and cleanup.
Pattern with comprehensive error handling:
123456789101112131415161718192021222324252627282930313233/*REXX*/ address ispexec 'CONTROL ERRORS RETURN' lib_id = '' /* Initialize */ 'LMINIT DATASETID(lib_id) DATASET(libname)' if rc <> 0 then do say 'LMINIT failed, RC=' rc exit 8 end /* Open */ 'LMOPEN DATASETID(lib_id) OPTION(INPUT)' if rc <> 0 then do say 'LMOPEN failed, RC=' rc 'LMFREE DATASETID(lib_id)' exit 8 end /* Use library */ signal on error /* ... library operations ... */ signal off error /* Cleanup */ 'LMCLOSE DATASETID(lib_id)' 'LMFREE DATASETID(lib_id)' exit 0 error: say 'Error during library operations' if lib_id <> '' then do 'LMCLOSE DATASETID(lib_id)' 'LMFREE DATASETID(lib_id)' end exit 8
This pattern ensures cleanup even if errors occur during operations.
Working with multiple libraries:
1234567891011121314151617181920/*REXX*/ address ispexec /* Initialize multiple libraries */ 'LMINIT DATASETID(id1) DATASET(LIB1)' if rc <> 0 then exit 8 'LMINIT DATASETID(id2) DATASET(LIB2)' if rc <> 0 then do 'LMFREE DATASETID(id1)' exit 8 end /* Open both */ 'LMOPEN DATASETID(id1) OPTION(INPUT)' 'LMOPEN DATASETID(id2) OPTION(INPUT)' /* Use libraries */ /* Cleanup */ 'LMCLOSE DATASETID(id1)' 'LMCLOSE DATASETID(id2)' 'LMFREE DATASETID(id1)' 'LMFREE DATASETID(id2)' exit 0
Each library requires its own initialization, opening, and cleanup sequence.
Shared lists enable multiple programs to share library member information.
Shared lists:
Shared lists are created through library operations:
123456789101112131415161718192021/*REXX*/ address ispexec /* Initialize library */ 'LMINIT DATASETID(id) DATASET(MY.LIBRARY)' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* Build shared list */ list_name = 'MYLIST' do forever 'LMMLIST DATASETID(id) MEMBER(member) STATS(YES)' if rc <> 0 then leave /* Add to shared list */ 'LMMADD LIST('"'"list_name"'"') MEMBER('"'"member"'"')' end 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
This creates a shared list containing all library members that can be accessed by other programs.
Other programs can access shared lists:
12345678910/*REXX*/ address ispexec list_name = 'MYLIST' /* Access shared list */ do forever 'LMMFIND LIST('"'"list_name"'"') MEMBER(member)' if rc <> 0 then leave say 'Member from shared list:' member end exit 0
Shared lists allow programs to coordinate without each building the list separately.
Shared lists enable coordination:
This is useful when multiple programs need to work with the same set of library members.
Real-world examples of Library services usage.
List all panels in a panel library:
12345678910111213141516171819202122/*REXX*/ parse arg panellib address ispexec 'LMINIT DATASETID(id) DATASET('"'"panellib"'"')' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end say 'Panels in' panellib':' panel_count = 0 do forever 'LMMLIST DATASETID(id) MEMBER(panel) STATS(YES)' if rc <> 0 then leave panel_count = panel_count + 1 say panel_count'.' panel end say 'Total panels:' panel_count 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
This script enumerates all panels in a panel library, useful for documentation or analysis.
Search for a specific panel across multiple libraries:
12345678910111213141516171819202122232425262728293031323334353637/*REXX*/ parse arg panelname address ispexec /* Search in user library */ 'LMINIT DATASETID(id) DATASET(USER.PANELS)' if rc = 0 then do 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc = 0 then do 'LMMFIND DATASETID(id) MEMBER('"'"panelname"'"')' if rc = 0 then do say 'Panel found in USER.PANELS' 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0 end 'LMCLOSE DATASETID(id)' end 'LMFREE DATASETID(id)' end /* Search in system library */ 'LMINIT DATASETID(id) DATASET(SYS1.ISPPLIB)' if rc = 0 then do 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc = 0 then do 'LMMFIND DATASETID(id) MEMBER('"'"panelname"'"')' if rc = 0 then do say 'Panel found in SYS1.ISPPLIB' 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0 end 'LMCLOSE DATASETID(id)' end 'LMFREE DATASETID(id)' end say 'Panel' panelname 'not found' exit 8
This searches for a panel in multiple libraries, simulating library concatenation behavior.
Build a list of members matching a pattern:
123456789101112131415161718192021222324/*REXX*/ parse arg libname pattern address ispexec 'LMINIT DATASETID(id) DATASET('"'"libname"'"')' if rc <> 0 then exit 8 'LMOPEN DATASETID(id) OPTION(INPUT)' if rc <> 0 then do 'LMFREE DATASETID(id)' exit 8 end /* Build list */ member_list = '' do forever 'LMMLIST DATASETID(id) MEMBER(member) PATTERN('"'"pattern"'"')' if rc <> 0 then leave if member_list = '' then member_list = member else member_list = member_list member end say 'Matching members:' member_list 'LMCLOSE DATASETID(id)' 'LMFREE DATASETID(id)' exit 0
This builds a space-separated list of members matching a pattern, useful for further processing.
Follow these best practices when using Library services:
Avoid these common mistakes:
Imagine ISPF Library services like a library card system:
Just like you need to get a library card before using the library, you need to initialize library access before using Library services. And just like you return your card when done, you free library resources when finished!
Practice using Library services:
1. What service initializes library access?
2. What service lists library members?
3. What service opens a library for access?
4. What service frees library resources?
5. What are shared lists used for?