Original Message:
Sent: 3/13/2026 12:19:00 PM
From: mark collins
Subject: RE: ancient Informix product - Perform
Sebastien (and everyone else),
Sorry, I thought I had put more context in the original message. What happened was I had started typing out the original post with lots of detail, and then something happened that reset my connection and I lost that post. When I created it again, I inadvertently left out some critical details as I was rushing to complete the post at the end of the day. In my mind, I had typed these details, but as I said, they got lost when the connection reset.
The actual Informix product is called Informix SQL. It contains the Ace report definition tool, Perform screen generator, a menu creation tool, and an interactive query tool similar to dbaccess. Ace and Perform have basic functionality for creating reports and screens, respectively. But Informix provided the ability to extend the functionality by creating a C module, compiling that program, and linking it with the Informix-provided runner programs sacego (for Ace) and sperform (Perform) to create a customized version of the runner, with its own name. The C module would contain your functions, and could be accessed via CALL statements in the 'instructions' section of the Perform source, such as:
before editadd editupdate of salary
begin
{ update }
let f301 = dsal
let f203 = f003
let f000 = 0.0
call get_acct()
{ end of update }
end
The get_acct() function is the module from which my previous C code excerpt was taken. The declaration of that function is 'valueptr get_acct()'. And yes, there is additional error checking after the SELECT:
if (sqlca.sqlcode != 0) {
pf_msg("invalid account, please add to cacct if you want posted %d"
,sqlca.sqlcode,0,0);
else
if (acctind >= 0)
pf_putval(&acct,CDECIMALTYPE,"f000");
}
The Perform screen opens up with a blank form (just the field labels). Users can choose Q to query, which allows them to tab through these empty fields and enter whatever information they have and hit Enter. Perform will then query the database and populate the screen with the first row that matches the information provided by the user. The user can choose N or P to move to the next or previous matching result, or U to update the data on the screen, E to exit, and so on.
In this case, the data comes from multiple tables which are specified in the 'tables' section of the Perform screen source. Normally, the database is queried at the beginning and the returned data is displayed on the screen, the user updates it or exits. But in this case, as they update information in certain screen fields (salary in this case), the CALL get_acct() will cause the function in the custom version of sperform to be executed. The get_acct() function pulls data from two of the fields on the screen form (prog and line) which may have been updated from their original values and does a SELECT to get acct_num. Assuming that SELECT was successful and the returned value is NOT NULL, it updates the field identified by tag "f000" with the acct_num that was just retrieved. This is b
In the Informix SQL Reference manual (hard to find online, I've saved a copy on my PC), there are five functions provided for interfacing with Perform forms:
- pf_gettype() - returns the type and length of the display field for a given field tag;
- pf_getval() - returns the value and length of the display field IF it is a character field;
- pf_putval() - puts a value in the field identified by the field tag, requires that the user have correct permission;
- pf_nxfield() - controls cursor placement on the Perform screen;
- pf_msg() - display message at bottom of the Perform screen.
The manual does not answer the questions that I posed in the original post. And the functions are documented in K&R style rather than ANSI C style, so I was guessing that if there was a return, it would be an integer in the way that functions normally return values in C.
The manual does indicate "return values" for each of these functions. It does not explicitly state whether those return values are done in the normal C manner or via sqlcode. The values that are listed for pf_getval() return codes are:
0 The operation was successful; display field was found.
3700 The user is not permitted to read the field.
3759 There is no such field tag in the form.
which are close to what finderr returns for those numbers as sqlcode values:
-3700 Permission not granted to allow reading of table-name.
-3759 The display field field-name does not exist in the form.
The problem with pf_msg() is that it takes three arguments:
pf_msg(msgstr, reverseflag, bellflag)
char* msgstr;
short reverseflag, bellflag;
The reverse flag causes the message to appear in inverse text (e.g., black text on white background), and the bell flag causes the terminal bell to ring.
In this case, the programmer attempted to combine the functionality of sprintf() and pf_msg(), passing a message format string with '%d' to display a numeric value, a value (sqlcode), the inverse flag, and the bell flag.
pf_msg("problem in prog %d",sqlca.sqlcode,0,0);
It runs as-is on both HP and RH, but it is not correct.
To compile the C program and create a customized version of sacego or sperform, you use the cace or cperf scripts that come with Informix SQL.
From his comments, I know Sebastien is familiar with Perform, but I included the detailed info above for those who have never heard of this tool. It dates back to at least Informix 5.x (back in the 1990s), and probably was around even earlier than that.
------------------------------
mark collins
------------------------------
Original Message:
Sent: Fri March 13, 2026 11:15 AM
From: Sebastien FLAESCH
Subject: ancient Informix product - Perform
Mark,
This piece of code looks to me like ESQL/C code...
Are you allowed to give more context?
What is the file extension of the source file?
How is this code compiled?
Are you sure this is used by the Informix perform tool?
I bet that the pf_getval() function sets the global sqlca[.scode] variable.
So the return value for such functions if probably irrelevant, expecting the caller to check sqlca.sqlcode.
Then, if this pf_getval() function ONLY sets sqlca.sqlcode to <0 in case of error, and leaves it unchanged (0) if no error occurs, it can work.
About indicator variables (acctind), from what I see here it would be set by the row fetching of the SELECT statement.
Just expecting some if( sqlca.sqlcode == 100 ) checking after the SELECT... but you probably did not give all the source code.
For pf_msg(), I would bet that the remaining params (0,0) are normally used for additional debug information, for error messages with more that just a single %d placeholder.
If it's using some ?printf() under the cover I see no issue here.
So all in all, that code does not look so bad to me ...
Seb
------------------------------
Sebastien FLAESCH
Original Message:
Sent: Fri March 13, 2026 10:24 AM
From: mark collins
Subject: ancient Informix product - Perform
Hi Sebastien,
Right now, we're focused on shifting this legacy application from HP to Red Hat, so migrating to Aubit4gl or Genero are way out of scope.
Thanks for confirming that pf_getval() simply returns an integer. I suspected as much, but could not prove it based on the documentation. I was about to try constructing a test application to prove it, but I like your method of disassembling.
The context is that as part of migrating to RH, we're looking at the existing code and seeing some truly atrocious coding. One example is:
$dec_t acct;$char prog[4], line[8];$short progind, lineind, acctind;sqlca.sqlcode = 0;pf_getval("a0",prog,CCHARTYPE,4);pf_getval("f301",line,CCHARTYPE,8);if (sqlca.sqlcode != 0) { flag=1; pf_msg("problem in part 1 %d",sqlca.sqlcode,0,0);}if (progind < 0) { pf_msg("problem in prog %d",sqlca.sqlcode,0,0);}if (lineind < 0) { pf_msg("problem in line %d",sqlca.sqlcode,0,0);}$select acctnum into $acct:acctind from accounts where accounts.program = $prog and accounts.line_item = $line;
There are so many flaws in this code, such as not checking the status of the first call to pf_getval() and improper number of arguments sent to pf_msg(). But since the original code was checking sqlca.sqlcode after the second call to pf_getval(), the question was raised as to whether the function used sqlca.sqlcode as its method of reporting errors. Based on your findings, the code should do 'rc = pf_getval(...)' and check the value of rc (assuming that rc is defined as an int).
The question about indicator variables came because the code defines indicators and appears to be testing them in the second and third 'if' statements, but there was never anything to initialize these variables, and they were not referenced with the host variables. As you can see in the SELECT statement, the original programmer understood how to use indicators in that context, but why they thought the other two indicators had been set is a bit of a mystery. I was just trying to confirm whether pf_getval() supported that host_var:indicator_var syntax. I strongly doubt it, since it expects a pointer rather than an actual host variable as its argument.
------------------------------
mark collins
Original Message:
Sent: Fri March 13, 2026 06:19 AM
From: Sebastien FLAESCH
Subject: ancient Informix product - Perform
Hello Mark,
If you download/try Aubit4gl, you also want to try FOURJ's Genero BDL.
Quick search to help you (needs more investigation):
Seems pf_getval() is implemented in e33lfunc.o provided in lib/tools/libsperf.a
After ar -x, doing some disassembling with:
objdump -d -M intel e33lfunc.o
Seems that the function return type is 32-bit integer (EAX op), typically used as status for C functions.
Maybe we could help more if you explain in which context this function is called and in which conditions you get an error.
Since "perform" is an old Informix product, you should also check 64-bit vs 32-bit integer concerns.
Seb
------------------------------
Sebastien FLAESCH
Original Message:
Sent: Thu March 12, 2026 06:29 PM
From: mark collins
Subject: ancient Informix product - Perform
This one is for the Informix dinosaurs who remember Ace and Perform.
The manual documents the pf_getval() as:
pf_getval(tagname, retvalue, valtype, vallen)
char* tagname, *retvalue;
short valtype, vallen;
and it lists the possible return values as:
0 The operation was successful; display field was found.
3700 The user is not permitted to read the field.
3759 There is no such field tag in the form.
First question - is the return value an int? Or does pf_getval() update sqlca.sqlcode (which is an int)? Or does it do something else? I have run finderr for the three possible return values, and they come back with messages similar to, but not exactly matching, those listed above. I can't find the function prototype documented anywhere, and I can't find the header file for it, so I'm not sure whether it uses an int for the return. I think I remember that K&R C defaulted all functions to an int return, but I've only actually written ANSI-style C, so I could have that wrong. But if it updates sqlca.sqlcode, then I need to know so the developers can check the correct thing.
Second question - does pf_getval() support the use of indicator variables like ESQL/C does for normal host variables? If 'retvalue' in the function definition about is a host variable, can we associate an indicator variable with it when calling pf_getval()? I strongly doubt it, given that 'retvalue' above actually expects a pointer, but I want confirmation so I can pass that info to the rest of our developers.
Thanks.
------------------------------
mark collins
------------------------------