21
Sat, Dec
3 New Articles

C and APIs -> What's the Point?

General
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

APIs have been a hot topic among enthusiastic AS/400 programmers since Release 3.0 of OS/400. Even though IBM has given us only a taste of Application Programming Interfaces with Release 3.0, these 23 APIs are powerful enough to prompt us into trying them out for size. After scouring the System Programmer's Interface Reference manual for information on the application programming interfaces (APIs), we come face-to-face with a totally new concept of programming for us RPG and COBOL coders -- pointer-based languages.

Most of the Release 3.0 APIs work fine with RPG or COBOL, and even CL, but the inference in the System Programmer Interface manual is to use pointer-based languages. Also, in a recent interview with News 3X/400, Ron Fess, project manager for IBM's Management System Openness on the AS/400, said, "The handwriting is on the wall.... It's a question of how fast we move in that direction..." in response to questions about IBM's move to C as the systems programming language of choice. C, along with Pascal, PL/I and MI, is one of the pointer-based languages available on the AS/400.

Fess also suggested in the News 3X/400 (March 1991) interview that, although many future APIs will be compatible with RPG and COBOL, certain APIs will only be usable through C. This is already true for three of the Release 3.0 APIs -- user queues, user indexes and change pool attributes.

The List APIs all use a new Release 3.0 object type -- user space -- to hold the retrieved information. For more information on user spaces, see the article, "Anatomy of a User Space," in this issue of Midrange Computing. Once the List API stores the retrieved job, spool or object information to the user space, the user space must be processed. This is where the pointer-based languages are highly efficient. RPG and COBOL were designed to work with standard record formats -- not the variable data typically retrieved by the List APIs. C, and the other pointer languages, can directly reference a user space's variable data, whereas RPG and COBOL must use the QUSRTVUS API to access this data.

If the handwriting is on the wall, and if future APIs will be geared to C, and if APIs work more efficiently with C, then those of us who wish to exploit the AS/400 to its fullest potential need to learn about pointer languages. I will be describing C pointers in detail, along with their use with the APIs. We will compare RPG and C coding techniques, that is, a non-pointer language against a pointer language, by looking at fragments of the example programs found in IBM's System Programmer's Interface Reference. You also will be introduced to C's data types and its powerful data structure definitions.

The Point Is

Kernighan and Ritchie, in their book, The C Programming Language, (the de facto standard of C books) state: "A pointer is a variable that contains the address of another variable." Think about this definition while you look over 1. Notice that the pointer variable, prt_2_Char, contains the memory address of the variable Char_Var and thus points to the value A. This seemingly simple concept yields great programming power and -- complexity.

Kernighan and Ritchie, in their book, The C Programming Language, (the de facto standard of C books) state: "A pointer is a variable that contains the address of another variable." Think about this definition while you look over Figure 1. Notice that the pointer variable, prt_2_Char, contains the memory address of the variable Char_Var and thus points to the value A. This seemingly simple concept yields great programming power and -- complexity.

Some of the basic issues of pointer use include: definition, initialization, indirection and pointer arithmetic. In C, pointers are defined explicitly by:

type *variable;

'type' is any valid C data type and 'variable' is the name of the pointer variable. 2 contains a list of valid C data types. To manipulate and use data referred to by pointers we need to use the pointer operators '*' and '&'. The pointer operators are commonly referred to as: "at address" or "actual value of" for the asterisk '*' and "address of" for the ampersand '&'. In regard to our example in 1, these would be the variable definitions:

'type' is any valid C data type and 'variable' is the name of the pointer variable. Figure 2 contains a list of valid C data types. To manipulate and use data referred to by pointers we need to use the pointer operators '*' and '&'. The pointer operators are commonly referred to as: "at address" or "actual value of" for the asterisk '*' and "address of" for the ampersand '&'. In regard to our example in Figure 1, these would be the variable definitions:

char *ptr_2_Char;

char Char_Var;

and then: char_Var = 'A';

ptr_2_Char = &Char_Var;

would have initialized Char_Var to the EBCDIC value for 'A' and set ptr_2_Char to the address of the variable Char_Var. To refer to the value ptr_2_Char points to we need to use the actual value of operator:

*ptr_2_Char = 'B';

The asterisk (*) indicates that we want to access the data at the memory location referred to by ptr_2_Char, not the address itself. Restated another way: move the value 'B' into the memory location to which ptr_2_Char refers. Now both *ptr_2_Char and Char_Var are equal to 'B' since the pointer value in the variable ptr_2_Char is the same as the address of Char_Var. This doesn't mean that ptr_2_char always has to point tp Char_Var, we can easily change it to point elsewhere by setting the address of another character variable to the pointer variable ptr_2_Char:

ptr_2_Char = &Another_Char_Var;

RPG/400 does not allow pointers to be defined, but RPG parameters are a good example of how pointers work. In RPG, when a variable is passed to another program, the address of the data is passed. The calling program then receives the address of the data to an entry parameter for manipulation. When control returns to the calling program, the passed variable retains any modifications made to it by the called program.

C, on the other hand, passes parameters to other C functions by value. Passing parameters by value means that the variable's value in the calling program is copied into temporary storage; it is the temporary storage that is received by the called program. In C, the calling program must pass the address of the variables to the called program in order to have the parameter's modified value returned. The program being called must have its entry parameters defined as pointers.

Pointers can become quite complex. For instance, a pointer variable can be defined to point to a pointer:

char **ptr_2_ptr;

Which is basically a two-level array. In fact, the standard C entry parameter list for a program's Main() function uses a two-level array. In 5, line 8, IBM has described the second parameter of the main function to be an array of pointers to characters. There is a close parallel between C pointers and arrays since an array variable, without an index, is a pointer to the address of the first byte of data in the array. The only significant difference is that with pointers, you use pointer arithmetic to traverse contiguous bytes of data, whereas arrays use indexing. Line 7 of this example, and all Main() functions that need a parameter list, describe an integer variable to hold the count of the actual parameters.

Which is basically a two-level array. In fact, the standard C entry parameter list for a program's Main() function uses a two-level array. In Figure 5, line 8, IBM has described the second parameter of the main function to be an array of pointers to characters. There is a close parallel between C pointers and arrays since an array variable, without an index, is a pointer to the address of the first byte of data in the array. The only significant difference is that with pointers, you use pointer arithmetic to traverse contiguous bytes of data, whereas arrays use indexing. Line 7 of this example, and all Main() functions that need a parameter list, describe an integer variable to hold the count of the actual parameters.

You might ask, "Is pointer arithmetic anything like 'New Math'?" It's pretty simple, really. A pointer variable can be added to and subtracted from, as in:

ptr_2_Char = prt_2_Char + 1;

or ptr_2_Char += 1;

or ptr_2_Char++;

All three of these operations are equivalent in that they increment the address in the pointer variable by the length of the pointer's data type. All pointer arithmetic is relative to its base type. So, in the above examples, the address would be incremented by one whereas a pointer to an integer would increment by four. I must mention here that there is no bounds checking in C. You can loop though pointer arithmetic indefinitely...or until, on an AS/400, your program exceeds its work space and aborts. On a PC, it crashes. Seatbelts not provided with C; proceed with caution!

But pointer arithmetic is why C handles strings so well. C programs can quickly and easily traverse a string, copy strings, or return the address of the subset of a string. These and many other pointer-based techniques are why so many word processors were written in C.

Pointers can also point to data structures, an explanation of which brings us back to the APIs. The user spaces, created to hold the information (about jobs, spool file members, data file field record descriptions, etc.) all have a particular data structure associated with them. These structures are fairly lengthy, but they are all defined for us in the QUSRTOOL library -- for RPG and COBOL, as well as for C. 3 contains three of QUSRTOOL's C and the corresponding RPG data structures that the System Programmer's Interface Reference uses in its delete spool file example programs -- SPOOLINFO.

Pointers can also point to data structures, an explanation of which brings us back to the APIs. The user spaces, created to hold the information (about jobs, spool file members, data file field record descriptions, etc.) all have a particular data structure associated with them. These structures are fairly lengthy, but they are all defined for us in the QUSRTOOL library -- for RPG and COBOL, as well as for C. Figure 3 contains three of QUSRTOOL's C and the corresponding RPG data structures that the System Programmer's Interface Reference uses in its delete spool file example programs -- SPOOLINFO.

The first token in 3, type def, is an example of how C allows a programmer to create unique type definitions. This definition can then be used to declare other variables. The type definition header_struct (3) is used in IBM's example program in the System Programmer's Interface Reference. The SPOOLINFO code fragments in 5 uses:

The first token in Figure 3, type def, is an example of how C allows a programmer to create unique type definitions. This definition can then be used to declare other variables. The type definition header_struct (Figure 3) is used in IBM's example program in the System Programmer's Interface Reference. The SPOOLINFO code fragments in Figure 5 uses:

header_struct *space;

to define a pointer to a memory location that contains data formatted like the structure definition header_struct.

To refer to an individual variable within the data structure, header_struct, that space points to; we need to introduce two new pointer operators: the . (dot) and the -> (arrow). The dot operator is used when working with the actual structure. The arrow operator is used when a pointer to a structure is used. The operators reference individual elements of structures and in C, you must fully qualify variables (unlike COBOL, where qualification is an option). RPG definitely does not let you qualify a DS structure variable. This inability to qualify a variable is why an RPG program can't use the same variable name in different structures or different files. Incidentally, IBM's C/400 and ASNA's Diploma/C both use data-structures for record formats. In the SPOOLINFO C program, to reference the header structures, number-of-entries element, pointed to by the variable, space, IBM uses:

space->number_of_entries.

Had the variable, space, not been defined as a pointer but as variable to hold the actual data as in:

header_struct space;

then we would have referenced number-of-entries by:

space.number_of_entries.

But that would have completely changed the logic of the whole program by doing away with the use of pointers and pointer arithmetic. The resulting C code would have been no better than IBM's other delete spool file API RPG example. IBM's C SPOOLINFO example uses a combination of pointer arithmetic and data structures to more efficiently traverse the user space -- resulting in a faster program. In the RPG example, the API call, QUSRTVUS, must be used multiple times to access each spool file entry in the user space. 4 uses action diagrams to describe the program logic for both versions of the SPOOLINFO program.

But that would have completely changed the logic of the whole program by doing away with the use of pointers and pointer arithmetic. The resulting C code would have been no better than IBM's other delete spool file API RPG example. IBM's C SPOOLINFO example uses a combination of pointer arithmetic and data structures to more efficiently traverse the user space -- resulting in a faster program. In the RPG example, the API call, QUSRTVUS, must be used multiple times to access each spool file entry in the user space. Figure 4 uses action diagrams to describe the program logic for both versions of the SPOOLINFO program.

In both examples shown in 5, a user space is first created to hold spool file data retrieved from the single call to the QUSLSPL API. Both the RPG and C programs also fill the user space with QUSLSPL and retrieve spool file attributes with QUSSPLA. The difference lies in how the two programs access data from the user space. In RPG, the QUSRTVUS call is used with a hard-coded offset to pull the list starting position, list size, and the number of entries variables from the header portion of the user space (lines 7-13). The list starting position and size are then used for subsequent calls to QUSRTVUS (lines 14-19) to process each entry. The list starting position is incremented after each call to QUSRTVUS by the list size, making it equal to the offset of the next entry (line 21). The number of entries variable is used to control the loop (line 14).

In both examples shown in Figure 5, a user space is first created to hold spool file data retrieved from the single call to the QUSLSPL API. Both the RPG and C programs also fill the user space with QUSLSPL and retrieve spool file attributes with QUSSPLA. The difference lies in how the two programs access data from the user space. In RPG, the QUSRTVUS call is used with a hard-coded offset to pull the list starting position, list size, and the number of entries variables from the header portion of the user space (lines 7-13). The list starting position and size are then used for subsequent calls to QUSRTVUS (lines 14-19) to process each entry. The list starting position is incremented after each call to QUSRTVUS by the list size, making it equal to the offset of the next entry (line 21). The number of entries variable is used to control the loop (line 14).

The C example directly references user space information through the pointer returned from the QUSPTRUS API (line 10). With the pointer variable, space, now holding the address of the first byte of the user space, we need to simply add the list section offset found in the header_struct data-structure to point to the first spool file entry (line 13). The C program initializes the pointer variable, list_section, to hold the address of the first byte of the list portion of the user space by setting it equal to the pointer, space, plus the list section offset (lines 12-13). The pointer to the list section is then moved into the pointer variable, entry_list. This pointer variable is used to hold the address of the current entry being processed. There are then three pointer variables used:

o space -- address of the first byte of the user space

o list_section -- address of the first byte of the list section within the user space

o entry_list -- address of the current list entry being processed within the user space

Using pointer arithmetic, it is simple to jump from one section of the user space to the next. The double pluses (++) after entry_list increment the address by the length of the data structure -- splf0100. The variable, entry_list, then, effectively points to the next list entry.

On lines 12 and 14, the (char *) and the (splf0100 *) may seem somewhat obscure to you. In C, this is know as casting, and is used to ensure that the value being assigned is of the same type, or cast, of the receiving variable. This is important, for if we had not cast list_section as the data structure type splf0100, the pointer arithmetic in line 22, would not have incremented entry_list relative to the appropriate base type. There is no telling where entry_list would have pointed to then.

If you still don't see the "point," consider this: user spaces can be directly manipulated by pointer-based languages. "So what? You've already covered that anyway," you say. Well, this capability may seem trivial to you, but it allows C programmers to code on the AS/400 using some highly sophisticated techniques previously not directly supported.

I've already mentioned word processors, but consider cross reference tools, sorts, spreadsheets and even compilers. C on the PC has been used to develop many sophisticated and complicated products including: Lotus 1-2-3, Microsoft Word and Microsoft Excel. With C on the AS/400 coming closer and closer to its PC counterpart, hopefully, many of these products will be ported to the AS/400. C isn't for everyone by a long shot, but it's nice to know it's there if you need it.


C and APIs -> What's the Point?

Figure 1 Pointers

 Figure 1: Pointers Memory Variable Variable Address in Memory Name 1000 1003 || *ptr_2_Cha 1001 | 1002 | 1003 A <--/ Char_Var 1004 X 1005 E Another_Char_Var 
C and APIs -> What's the Point?

Figure 2 Valid C types

 Figure 2: Valid C Types TYPE DESCRIPTION RPG EQUIVALENT char - unsigned byte holding EBCDIC chaaracter CHAR int - signed value 4 bytes long BIN(4) float - single precision floating point 4 bytes long double - double precision floating point 8 bytes long short int - signed value 2 bytes BIN(2) long int - same as int, added for compatibility BIN(4) pointer to any - 16 bytes *none * arrays and data structures are also fully supported in C * int and short int are comparable to RPG's Binary 2 and 4 * packed decimal data are not directly supported by C and must be manipulated through C arrays and external calls to conversion routines 
C and APIs -> What's the Point?

Figure 3 API data structures

 Figure 3: API Data Structures C structure RPG structure typedef struct header_struct { char user_data 64 ; int generic_header_size; char header_version 4 ; char format_name 8 ; char program_name 10 ; char time_generated 13 ; char informations_status; int usrspc_used; IRCVVAR DS int parm_section_offset; I B 1 40OFFSET int parm_section_size; I B 9 120NOENTR int header_section_offset; I B 13 160LSTSIZ int header_section_size; int list_section_offset; int list_section_size; int number_of_entries; int size_of_entry; } header_struct; typedef struct splf0100 { ISPLF DS char usr_name 10 ; I 1 10 USRNM1 char outq_name 10 ; I 11 20 OUTQNA char outq_lib 10 ; I 21 30 OUTQLI char form_type 10 ; I 31 40 FRMTY1 char usr_data 10 ; I 41 50 USRDT1 char int_job_ID 16 ; I 51 66 IJOBID char int_splf_ID 16 ; I 67 82 ISPLID } splf0100; typedef struct spla0100 { ISPLA DS int bytes_return; I B 1 40BYTRTN int bytes_avail; I B 5 80BYTVAL char int_job_ID 16 ; I 9 24 JOBID /* various spool file information */ I* etc. int nmbr_disk_rcdrs; I B 721 7240TOTRCD } spla0100; 
C and APIs -> What's the Point?

Figure 4 C vs RPG

 Figure 4: C vs. RPG Delete Old Spool Files Using C -> and Pointers +-- || create a user space using QUSRCRTU || retreive the pointer to the user space with QUSCRTU || receive spool file information into user space with QUSLSP || initialize the list section offset pointe || initialize the current entry pointer of data structure type splf010 +-- +== DO WHILE entries processed are < number of entries || retrieve more information using QUSRSPL || +--if spool file is too ol || || delete spooled fi || +- || increment current entry pointer by the list siz || increment entries processed counte +-- +-- || delete user spac +-- Delete Old Spool Files Using RPG +-- || create a user space using QUSRCRTU || receive spool files information into user space with QUSLSP || initialize list offset, entry size, number of entries fro || the user space using QUSRTVU +-- +== DO WHILE entries processed are < number of entries || retrieve one spool file entry from the user spac || to the SPLF DS with QUSRTVU || retrieve spool entry attributes to SPLA DS with QUSRSPL || +--if spool file is too ol || || delete spooled fi || +- || increment entries processed counte +-- +-- || delete user spac +-- 
C and APIs -> What's the Point?

Figure 5 IBM's Spoolinfo Program

 Figure 5: IBM's Spoolinfo Program Important Sections of IBM's Spoolinfo C Program . . . 1 #incluse "OPENAPI.h" /* pull in API's data structures */ . . . 2 header_struct *space; /* ptr to US of type header_struct */ 3 char *list_section; /* ptr to 1st char in list section of US */ 4 splf0100 *entry_list; /* ptr to current list entry of US */ 5 spla0100 *Rcv_Spl_Var; /* ptr to Spool Attribute from QUSSPLA */ . . . 6 main(argc,argv) /* program entry parameter list */ 7 int argc; /* count of arguments/parameters */ 8 char *argv ; /* array of pointers to char */ . . . 9 QUSCRTUS(spc_name,ext_atr,initial_size,auth,desc); /* create US */ 10 QUSPTRUS(spc_name,&space); /* set space to 1st byte of US */ 11 QUSLSPL(spc_name,frmt,usr,OutQ_Nm,ls_frm_typ,Usr_dat); 12 list_section = (char *) space; /* ptr to 1st char of list in US */ 13 list_section = list_section + space->list_section_offset; 14 entry_list = (splf0100 *) list_section; /* set 1st entry ptr */ . . . 15 count = 1; 16 while (count <= space->number_of_entries) { 17 QUSRSPLA(Rcv_Var,Rcv_lgth,Rtv_Fmt,Qal_Jb-Nam, 18 entry_list->int_job_ID,entry_list->int_splf_ID, 19 Splf_Name,Splf_Number); 20 Rcv_Spl_Var = (spla0100 *) Rcv_Var; /* if spool file is old, delete it */ . 21 count++; 22 entry_list++; /* ptr arithmetic, add the base value of 1 */ /* list entry, which is the length of the */ /* data structure splf0100, so entry_list */ /* points to the next list entry */ } . . . } Important Sections of IBM's Spoolinfo RPG Program 1 IRCVVAR DS 2 I B 1 40OFFSET 3 I B 9 120NOENTR 4 I B 13 160LSTSIZ . . . 5 C CALL 'QUSCRTUS' . . . 6 C CALL 'QUSLSPL' . . . 7 C Z-ADD16 LENDTA 8 C Z-ADD125 STRPOS 9 C CALL 'QUSRTVUS' 10 C PARM USRSPC 11 C PARM STRPOS 12 C PARM LENDTA 13 C PARM RCVDTA . . . 14 C COUNT DOWLENOENTR 15 C CALL 'QUSRTVUS' 16 C PARM USRSPC 17 C PARM STRPOS 18 C PARM LENDTA 19 C PARM SPLF fig. 2 DS . . . 20 C CALL 'QUSRSPLA' * receiver parms . . . * delete spool file logic . . . 21 C ADD LSTSIZ STRPOS 22 C ADD 1 COUNT 23 C END enddo 
BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: