4. Common Questions about tools:


4.1 Informix-4GL

4.1.1 I keep getting -4518 errors! How can I fix this problem?

The -4518 error is usually caused by running out of a resource known as temporary string space. Solutions include:

This error can be caused by any of the following coding structures:

LET l_str = "Hello ",myfunc("World"))
CASE statements without an OTHERWISE # Pre-4.1
CASE statements that look like this: # Pre-4.11 (maybe 4.10)
   CASE dummy_var 
        WHEN 0 
     ..... 
        WHEN 1 
     ..... 
        WHEN 2 
     ..... 
    END CASE 
       instead of this: 
    CASE 
        WHEN dummy_var = 0 
     ..... 
        WHEN dummy_var = 1 
     ..... 
        WHEN dummy_var = 2 
     ..... 
    END CASE 
Reports with excessive string usage (ie. USING, or other literals). One thing you can do to reduce the contents of string storage is to assign redundant strings associated with USING to variables and then refer to the variable:

let l_str1 = '$$$$$$$$.$$'

PRINT l_number USING l_str1

RETURNing large strings from functions.
Use of SPACE, SPACES, COLUMN, UPSHIFT, DOWNSHIFT, or WORDWRAP
Assignment of NULL to a string subscript:
      DEFINE x, y CHAR(10) 
      LET y = NULL 
      LET x[3,5] = y 
A return statement in a report function.
Passing an argument to a function which has no parameters defined:
   CALL something(my_variable)
...
   FUNCTION something()

From: heiker@quietsch.ping.de (Christian Heiker)

There is an Informix-Lib *(undocumented function)* called "acdealloc()" which job it is to clear the stack.

From a 4gl-program you can call this function with "CALL acdealloc()". With RDS you have to hook this function into your fguser.c module.

Here are the steps do do it with RDS:

edit file "fgiusr.c" (copy $INFORMIXDIR/etc/fgiusr.c)
int   acdealloc();
#include "fgicfunc.h"

cfunc_t usrcfuncs[] =
    {
    "acdealloc", acdealloc, 0,
    0, 0, 0
    };
create a new runner (fglgo)
cfglgo fgiusr.c -o newrun
Call the c-function in the RDS-program
CALL acdealloc()
Start the program with the new runner

You should pay attention that you make the call in an upper hierachy, for example after a FINISH REPORT.

There is no guarantee that other important data may be deleted from the stack (because nobody nows what is at which time on the stack).

BEWARE: There seems to be a bug in 4GL/ID (4.1 at least) which causes TSS problems to occur after the interrupt key has been pressed, which isn't very helpful if you're trying to find TSS problems in your program!!!

4.1.2 How can I write to a flat ASCII file?

Generate a REPORT with 0 TOP, LEFT, RIGHT & BOTTOM MARGINs and 1 line PAGE LENGTH.

4.1.3 How can I read from a flat ASCII file?

If you're talking about loading data which has been exported from another system you should just use the LOAD command.

If you want to do something sexier like suck in the output from a Unix command use either the routine in Appendix B (100% 4GL code) or Appendix C (a 'C' routine).

4.1.4 How can I force INPUT ARRAY onto the next line?

From: dennisp@informix.com (Dennis Pimple)

Here's a short article that addresses this and other INPUT ARRAY issues:

Emulating "NEXT ROW" syntax in INPUT ARRAY until version 5.0.

Create a screen record in which the last field in a NOENTRY field. This field can be one that actually stores data for display or a CHAR(1) designed just for this purpose. Remember that the last field in the screen record doesn't have to be the right-most field on the screen, but it usually is
SCREEN 
{ 
[a  ][b   ][c  ][n] 
... 
} 
 
ATTRIBUTES 
a = ... 
b = ... 
c = ... 
n = FORMONLY.ne TYPE CHAR, NOENTRY; 
 
INSTRUCTIONS 
... 
SCREEN RECORD s_array[##] (...,FORMONLY.ne) 
 
END
Define your array with the last field that matches the NOENTRY in the screen record:
DEFINE ma_array ARRAY[##] OF RECORD 
        ... 
        ne      CHAR(1) 
END RECORD 
... 
In your INPUT ARRAY function, you need counters for arr_curr, scr_line, arr_count, and one to mark if we want to jump rows:
DEFINE arr  SMALLINT 
DEFINE scr  SMALLINT 
DEFINE act  SMALLINT 
DEFINE arrg SMALLINT 
We surround the entire INPUT ARRAY with a WHILE loop, because the trick to moving backward is to mark the row to go to, then exit the input and loop back around to begin input again.
 
LET arrg = 1 
LET act = number of rows filled in the array already 
WHILE arrg > 0 
    CALL set_count(act) 
    INPUT ARRAY ma_array WITHOUT DEFAULTS FROM s_array.* 
        BEFORE ROW 
            LET arr = arr_curr() 
            LET scr = scr_line() 
            IF arrg > arr THEN 
                # this means we want to go to a row below us in the 
                # array, so NEXT FIELD the NOENTRY field, which means 
                # jump to the next row 
                NEXT FIELD ne 
            END IF 
            # reset arrg to 0 if we get to this point, which allows 
            # us to leave the WHILE loop around the INPUT ARRAY if 
            # the user hits the ACCEPT or INTERRUPT key 
            LET arrg = 0 
        ... 
        AFTER ROW 
            # I have found that AFTER ROW is the best place to track 
            # the last filled in row. 
            LET act = arr_count() 
        ... 
        # below is an example of how we can implement a function key 
        # to act as a HOME key. When the user types the function key 
        # designated as HOME, the cursor will move to array row 1 
        ON KEY (F##) 
            LET arrg = 1 
            EXIT INPUT 
        ... 
        # below is an example of how we can implement a function key 
        # to act as an END key. When the user types the function key 
        # designated as END, the cursor will move to the last filled 
        # in row. 
        ON KEY (F##) 
            IF arr .lt. act THEN 
                # if we're not on the last row now, go there 
                LET arrg = act 
                NEXT FIELD ne 
            END IF 
        ... 
    END INPUT 
    # in case we're looping we need to track arr_count here 
    # because an EXIT INPUT call avoids the AFTER ROW block 
    LET act = arr_count() 
END WHILE 
 
IF int_flag THEN 
... 

The only drawback to this approach is that for very large arrays the looping can take some time. But for arrays that are 50 rows or less, this is suitable.

BUG #11364 WORK AROUND

The only problem, now, is that with version 4.10 there is a bug that causes exiting from an AUTONEXT field into the NOENTRY field (and thus to the next row) to put the cursor into an infinite loop. This bug (#11364) is scheduled for a fix in version 4.10.UE1, which is currently shipping on some platforms. If you can't get the fixed version, there are two approaches to fixing the problem.

The first approach is to remove the AUTONEXT from the field just before the NOENTRY field. This is the simplest, and thus recommended, approach. If your user can't live with hitting the RETURN key after this field, though, you can use some of the new 4.10 library functions as a work around:

 
... 
AFTER FIELD before_ne 
    IF fgl_lastkey() = fgl_keyval("autonext") THEN 
        IF arr < maximum array size THEN 
            # go to the next row, avoiding bug 11364 
            LET arrg = arr + 1 
        ELSE 
            # we're on the last row already, loop back to the top 
            LET arrg = 1 
        END IF 
        EXIT INPUT 
    END IF 
... 

"before_ne" in the example above is the name of the AUTONEXT field that is listed just before the NOENTRY field in the screen record, and "maximum array size" is the size of array ma_array.

This work around has the same problem as the jumping solution in that for very large arrays a lot of screen repainting is done to get back down to the desired row.

4.1.5 How can I make a screen dump?

There is an undocumented environment variable called DBSCREENOUT which you need to set to the name of the file you wish you to use for your screendumps.

eg: DBSCREENOUT=screen.out

Now press CONTROL-P when the screen you want is displayed and it will be appended to your output file (screen.out in the above example).

NOTE: There is a similar environment variable called DBSCREENDUMP which includes some ESCape sequencing, so isn't as readable.

4.1.6 Any undocumented features that I could play with?

4.1.6.1 fgl_drawbox(depth, width, y, x, color)

A 4GL function which, surprize, surprize, draws boxes. It has now been documented in one of the 4GL for Windows manuals, but does exist on other platforms too.

The optional "color" parameter uses an integer code where 0=white, 7=black, and miscellaneous other colors in between.

4.1.6.2 FREE <statement-id|cursor-name>

This command wasn't made "official" until 4.0, but it does exist in earlier releases. It frees memory used by a cursor, and should be used only once you've completely finished with a cursor (ie: after a CLOSE)

statement-id is the name of a statement that has been PREPAREd

cursor-name is the name of a cursor whose DECLARE statement includes the keywords SELECT or INSERT

In 6.00, you can free a statement-id after you have declared the cursor. Also, in 6.00 you should free both the statement-id and the cursor.

4.1.6.3 Undocumented math functions from SELECT

        SELECT  ACOS(col1), 
                ASIN(col1), 
                ATAN(col1), 
                ATAN2(col1,col2), 
                COS(col1), 
                HEX(col1), 
                LOG10(col1), 
                LOGN(col1), 
                ROUND(col1), 
                SIN(col1), 
                SQRT(col1), 
                TAN(col1), 
                TRUNC(col1)       FROM foo_table; 

4.1.6.4 Is there a "get_key" function, or similar

In 4GL 4.1 and above you can use the fgl_lastkey function, but June Tong has a solution for pre-4.1 users:

Here is some information about the undocumented/unsupported/ replaced-by-fgl_lastkey() feature called eflastkey:

There is a global variable called eflastkey which is set in effin.c which holds the value of the last key typed. eflastkey is a short integer which will contain the ascii value of 'regular' keys typed and special values for keys for which 4GL does a 'mapping'. The ACCEPT KEY, for example is defined as 2016, LEFT arrow is defined as 2002 and RIGHT arrow is defined as 2003. If your linker accepts multiple global definitions of the same variable (treating one as the primary declaration and all others as extern) then you can simply include the variable eflastkey in the globals section of your 4GL program:

        GLOBALS
                DEFINE  eflastkey       SMALLINT
        END GLOBALS

        Then, during your INPUT statement, you can test the value:

        CASE eflastkey
        WHEN 2000               # UP ARROW
        WHEN 2001               # DOWN ARROW
        WHEN 2002               # LEFT ARROW
        WHEN 2003               # RIGHT ARROW
        OTHERWISE               # Some other key
        END CASE

If your linker gives you a problem with the definition of eflastkey, you can always call a C function such as CALL lastkey from your INPUT statement as:

lastkey()
{
        extern short eflastkey;

        switch (eflastkey)
        {
        case 2000:
                ...
        case 2001:
                ...
        case 2002:
                ...
        case 2003:
                ...
        default:
                ...
        }
}

You can test for other special mapped values such as INTERRUPT KEY which will set eflastkey to 2011 (assuming it has been DEFERed of course).