Customizing DES

Introduction

This chapter deals with

Writing Functions for the Data Entry System

UFORM specifications offer four places where you can call in functions of your own to customize the validation and default-value capability of mio. Three places are for validation functions, one for default functions:

Validation Functions

Validation functions can be used to check the input in a single field, to check values across fields, to change the attributes of fields, or even to change the form that is displayed on the terminal. This last operation would normally be based on an evaluation performed in the function; if the objective was only to change forms it would be simpler to use a UFORM MENU or FORMEXIT statement.

The Declaration for Validation Functions

The declaration of the firstval(), lastval() and field validation functions has the same format. It is:


int usrfunc(val, err, usrarg, flag, fldid, occno)
char *val;      /* pointer to value being validated */
char **err;     /* pointer to error message or 0 */
char *usrarg;   /* user argument passed to function */
int flag;       /* identifies type of function */
FLDID *fldid;   /* pointer to the buffer field id of this field */
int *occno;     /* pointer to buffer occurrence number.*/

where:

usrfunc is the name of the function.

val points to the value of the field being validated. In calls to firstval() and lastval() functions val should be null (0), since no particular field is being validated.

err is a pointer to an error pointer. If an error is detected, err may be used to change the error pointer to point to an error message.

usrarg points to an argument string if one is needed. Its length can not exceed 11 characters.

flag is 2 for firstval(), 1 for lastval(), and 0 for field validation functions.

fldid and occno are pointers to the field id and occurrence number of the field being validated.

firstval() and lastval() Return Codes

firstval() is the generic name of a validation function called by the form handler program when the users exits the form, but before any other validation functions are called. It is often used to change field attributes or otherwise initialize the form. The nature and timing of the actions taken by mio depends on the value returned by the function.

lastval() is the generic name of a validation function called after the user has left the form. It is useful for checking for agreement among fields. For example, if a form has a field for State and another for Zip_Code, a user can enter values in each that would be accepted by field validation functions, but that don't agree with each other. A State of NY and Zip_Code of 94117 are contradictory. Either State should be CA or Zip_Code should start with digits in the 100-149 range.

firstval() Return Codes

firstval() should be written to return a 1, 2, 3 or 4 on success and a -1 on failure. The action taken by mio on receipt of the return code is shown in Table 1

Table 1: firstval Return Codes and mio Actions

if firstval returns -1 1 2 3 4
then mio:          
displays an error message Y - - - -
makes attribute changes immediately - Y N Y N
defers attribute changes to next form - N Y N Y
does field validations - Y Y N N
does lastval validations - Y Y N N
visits server before returning to user - Y Y N N
returns to user without visiting server - N N Y Y

Here is further explanation of the actions listed in Table 1:

If the validation criteria are not met, the function returns a -1. When a -1 is returned, no dynamic attribute changes are applied, no field validations are performed and lastval() is not called.

The firstval() function flag is always 2.

lastval() Return Codes

lastval() should be written to return a 1 or 2 on success and a -1 on failure. The action taken by mio is shown in Table 2.

Table 2: lastval Return Codes and mio Actions

if lastval returns -1 1 2
then mio:      
displays an error message- -    
makes attribute changes immediately -N    
defers attribute changes to next form - N Y

The assumptions listed above following Table 1 are also in effect for lastval().

Field Validation Functions

The function must return a -1 if the field is in error and a 1 if the field is acceptable.

Table 3: Field Validation Return Codes and mio Actions

if the function returns -1 1
then mio:    
displays an error message-    
allows user to go on to next field N Y

If the field is found to be invalid, the function can set its err argument to point to an error message. If this is done, it overrides any value provided by the UFORM ERR specification (Error message: line on page 2 of the VARIABLE FIELD PARAMETERS form in vuform illustrated in Figure 3-12). Field validation functions are declared as shown above, except that flag is always 0.

val is a character pointer to the value in the field. To access the values in other fields of the form the get_val() routine can be called, or the external FML buffer pointer _gfbfr can be referenced. These are described below.

Getting the Value of Fields in Validation Functions

In standard field validation functions val points to the value of the field. However for firstval() and lastval(), since no particular field is being validated, it would be inappropriate to make val point to the value in an arbitrary field. For these functions, val is always passed as null (0). Instead of accessing field values via a pointer, the firstval() and lastval() functions can call the get_val() routine, to find the value of any field on the screen. get_val() expects two arguments:

get_val() returns a read-only pointer to the field value or a (char *)0 if no value exists for the field. The pointer returned by get_val() is only valid until the next call of get_val().

In the TUXEDO System/T software get_val() is declared as follows:


char *get_val(fldid, occno)
FLDID fldid;
int occno;

In your firstval() or lastval() function it should be declared as:


extern char *get_val();

Another way to look at the data entered in the form is via the external variable _gfbfr. _gfbfr should be declared as follows:


#include "fml.h";	
extern FBFR *_gfbfr;

_gfbfr can be read and modified with the standard FML buffer functions of the Field Manipulation Language (see the TUXEDO FML Guide). If necessary, _gfbfr can even be made larger by a call to tprealloc(). tprealloc() routine tprealloc() Modifications made to _gfbfr are reflected on the terminal on return from firstval() or lastval(). For read-only access, get_val() is preferred, since it is not FML specific.

By changing some system reserved fields in _gfbfr it is possible to change forms and the destination service in your firstval() or lastval() routine. (See System Fields later in this chapter.) It is also possible to alter attributes of fields dynamically. The techniques for doing these things in the firstval() and lastval() functions are the same as those used in a service. They are described in Changing Field Attributes below. For the form change to take effect, lastval() must return a 1 or a 2. When dynamically changing attributes a successful return value can be 1 or 2. The difference between a return of 1 and 2 is as follows: the return of a 1 implies that dynamic attribute changes should take effect immediately; the return of 2 implies that the dynamic attribute changes should be deferred.

_gfbfr can be modified and attributes can be dynamically altered from firstval(), lastval() and standard field validation functions. However, there is some risk involved in changing system fields in the buffer in validation functions. It is easy to create confusion for the service that processes the data from the form.

There is another global variable called _gformnam, it should be declared as follows :


extern char _gformnam[];

It contains the name of the form currently shown on the screen. This global variable should be considered read-only; it should never be altered by the user program.

In the firstval() function flag is always 2, in the lastval() function it is always 1, and in standard field validation functions it is always 0. This difference makes it possible to use the same function for all three validations. You need only check flag. In all validation functions fldid and occno are passed as pointers. In standard field validation functions, changing their values accomplishes nothing. However, if lastval() returns failure, the cursor is positioned at the field designated by *fldid and *occno. err keeps the same meaning with all validations. That is, it should be set to point to an error message, which is displayed if the function returns failure. Before lastval() is called, mio places the function key used to exit the form in the LEVKEY system field.

Changing Field Attributes

The attributes associated with a field on a form can be dynamically altered by the firstval() function, the lastval() function, by a standard field validation function, or by a service. The alteration is made via a call to change_atts(). The format of the call is:


int change_atts(fbfr, fldid, occno, atts)
FBFR *fbfr; 	/* FML buffer with screen representation*/
Fldid fldid;	/* FML buffer field id */
int occno;	/* occurrence number */
char *atts;	/* attributes */

change_atts() adds a reserved field to the FML buffer for the screen. Depending on the return code of the function that calls change_atts(), the changes appear on the terminal immediately or are deferred to the next form.

Attributes that can be changed are shown in Table 4. The full table of attribute flag values is shown in Appendix A, Figure 4.

Table 4: Attribute Flags That Can Be Changed with change_atts()

TYPE Can be changed from U (unprotected) to P (protected), or vice versa. Changing the type of an L (literal) field is not allowed.
CASE Any of the legal flag values can be used.
DISPLAY FEATURES Any of the legal flag values can be used. A frequently used strategy is to highlight a field that is in error by changing the flag value to 2 (reverse video)

Attributes other than those listed above may not be changed.

atts points to any of the FLAGS values to be changed.

For example, to change occurrence zero of LAST_NAME from an unprotected field accepting mixed case input displayed with normal intensity to unprotected, upper case, bold the function would be called as follows:


change_atts(fbfr,LAST_NAME,0,"Uu3")

atts can also point to the string "RESTORE". In this case the field will be restored to its initial attributes. Continuing with the above example, LAST_NAME could be restored to its initial attributes by:


change_atts(fbfr,LAST_NAME,0,"RESTORE")

The calls shown above also affect other occurrences of LAST_NAME on the same form that are tied to occurrence zero. change_atts() affects all fields on a form with the given fldid and occno. In the above example, if field LAST_NAME, occurrence number zero, appeared on two pages of a form, both fields would be affected by the change_atts() calls.

change_atts() has three return codes.

1
- means success
0
- means an FML buffer operation failed.
-1
- means there is an error in an atts value.

With the 0 return, the Ferror variable shows the cause of failure. Since change_atts() adds a field to the FML buffer, an out-of-space error is a possibility. In this case, tprealloc() should be called to enlarge the FML buffer.

If a firstval() function is changing the attributes of the form currently on the screen, then on success firstval() should return a 1 or a 3. If it is changing the attributes of a form it is about to display (by changing the FORMNAM system field), it should return a 2 or a 4.

If a lastval() function is changing the attributes of the form currently on the screen, it should return a 1 on success. If it is changing the attributes of a form it is about to display (by changing the FORMNAM system field) it should return a 2.

Default-Value Functions

Default-value functions are named in a UFORM extended descriptor statement for a variable field. Using a function to furnish the default value for a field allows you to vary the default value depending on a value entered by the user in some other field. Once control has been passed to the function, the techniques described above under Getting the Value of Fields in Validation Functions, can be used to inspect other fields in the buffer.

The Declaration for Default-Value Functions

For default-value functions (specified in vuform under Default type: FUNC on page 2 of the VARIABLE FIELD PARAMETERS form illustrated in Figure 3-12), the declaration is as follows:


char *usrfunc(fldid, occno, usrarg)
FLDID fldid;	/* buffer field id of this field.*/
int occno;	/* occurrence number of this field in buffer*/
char *usrarg;	/* user argument passed to function */

The function must return a pointer to a string, which is taken to be the default value.

Link Editing Functions with mio

User validation and default functions should be compiled with the -c option to suppress the link edit phase (see cc(1) in a UNIX programmer's reference manual). The resulting object files can then be stored in archive libraries. In general it is best to store validation functions and default-value functions in separate libraries, and to store no other external functions with them. If default functions and validation functions are stored together in one archive library or compiled together into one object file, the following naming convention must be followed:

This isn't such a bad idea even when the functions are stored in separate archive libraries; it makes them self-documenting.

The formerly-supported buildmio(1) was used to build a version of mio with user functions linked in. The -o option of buildmio can be used to assign a name to the executable file. The default name is a.out. The fact that your new version of mio has user functions linked in does not prevent you from naming it mio. However, if you do, it would be wise to save the original version under another name in case you have to backtrack.

In addition to linking user functions into mio, buildmio also gives you the capability of replacing all the literals (including error messages) output by mio, with literals of your own choosing.

You can run mio in test mode (-t option) after you use buildmio if you want to check how the compiled form looks and whether the validation functions perform correctly. See Using mio in Chapter 4.

Undisplayed Fields

In addition to fields normally displayed on the user's screen, there are two types of fields that are not displayed.

Secret Fields

These are fields with a UFORM display flag of S, for secret. Secret fields are just like other fields as far as the application is concerned. Their distinguishing feature is that their contents are never displayed on the screen. Screen space is reserved for them, but characters entered into the field by the user are not echoed. If a value for a secret field is sent from the application service (presumably for use in some later validation function), the field is displayed as blank. In all other respects, the use of secret fields by an application service is just like non-secret fields.

It is worth remembering that the secret character of these fields applies only to their display. When stored in a data base a secret field is no more or less immune from unauthorized viewing than an ordinary field. It is not encrypted unless your application does it.

Undefined Fields

In addition to secret fields, the application service may send fields to the client process, mio, in the output buffer. Fields not mentioned in the UFORM definition of the form are not displayed on the screen. They are retained by mio in the screen buffer, and are subsequently input with the next mask. Such fields are commonly used to store information to be carried from one service request to another by re-entrant application functions. (The fields are, of course, defined in a field table file, as are all FML fields used in a TUXEDO System/T application.)

The use of such fields is totally application-specific, but the possible need for their existence is based on these characteristics of the TUXEDO System/T software:

This leads to a the need to be able to store information from one visit and make it available on subsequent visits. In the paragraphs that follow we will describe two ways of doing this.

Hiding Data in the Form Buffer

This is the simpler of the two methods. Here is how you might go about it:

Step 1:

Include in the application field tables the name of the field you intend to use.
Step 2:

Set the field to any value that carries your intended meaning.
Step 3:

Include in the logic of the service some code that checks the value of the field.
Step 4:

Use UPDTMOD mode 1 or 2 the first time the field is added to the buffer, so the field does not get discarded.

Hiding Data in a Database Record

This method also assumes a field in the form buffer to carry information about a record key. Beyond that the strategy is to define a file of scratch pad records that can be created in one visit to the service and referenced in a subsequent visit.

An advantage of this method is that the amount of data stored could easily be more extensive than you might want to carry in a field in the buffer.

A disadvantage is that you need to include in the application design some process for cleaning the file out as required.

Conversational Clients

It may be preferable to write conversational clients and servers rather than using either of the above two methods for carrying information from one request/response call to another. In the conversational mode a connection between the client and server is maintained until it is brought down, so the client can be assured of working with a dedicated server until the task is complete. Refer to Chapter 4 "Writing Conversational Clients and Servers" in the BEA TUXEDO Programmer's Guide.

Accessing Forms from a C Client Program

The TUXEDO System/T software includes a function called do_form(3c). do_form() acts like a simplified version of mio. The first of its two arguments is the name of a compiled form; the second is a pointer to a pointer to a fielded buffer. Use of the function is described in the BEA TUXEDO Reference Manual: Section 3c. The formal declaration is shown in Figure 1.

Fig. 1: do_form() Declaration

#include "fml.h"
do_form(formname,fbfr)
char *formname;
FBFR *fbfr;

Services that Interface with the Data Entry System

This section describes a variety of techniques you may need in order to write services that interface with DES. Refer to the BEA TUXEDO Programmer's Guide where the basic template of a service is described in detail.

The function declaration for service routines called by the standard main() is


usrfunc(svcinfo)
TPSVCINFO *svcinfo;

where usrfunc is the name of the function and svcinfo points to the service information structure being passed to the function. This declaration should be used in all service functions. The service information structure, TPSVCINFO, is declared in atmi.h and includes the following members:


char	name[32];	/* service name being invoked */
char	*data;	/* request data */
long	len;	/* request data length */
long	flags;	/* describes service attributes */
int	cd;	/* connection descriptor if (flags & TPCONV) true */
int	appkey;	/* application authentication client key */
CLIENTID	cltid;	/* client identifier for originating client */

The TPSVCINFO structure consists of a name member that is populated with the name that the calling process used to invoke the service, a data member that points to a data buffer that was allocated by tpalloc(), a len member that is set to the length of that buffer, a flags member whose value indicates attributes that the service may want to note, a cd member that contains a unique connection descriptor for conversational connections, an appkey member for authentication, and a cltid member that identifies the originating client. The most important characteristic of services coded to interface with the TUXEDO System/T data entry system is that the data portion of the message is an FML buffer. You must use FML functions in dealing with the buffer and the fields it contains.

Getting a Larger Buffer

tprealloc() routine getting a larger buffer As is shown in code fragments in BEA TUXEDO Programmer's Guide, and as you will see in examples in this chapter, you can modify the input buffer that your service receives and return it to the terminal as the output buffer. However, there are bound to be times when there is not enough space in the input buffer to take all the output data.

The code shown in Figure 2 is an example of how you might deal with this situation.

Fig. 2: Getting More Buffer Space

static FBFR *fbfr
	.
	.
	.
FBFR *newbuf = NULL	/*Temporary buffer used to ensure there is
			  enough data space left in the screen buffer*/
	.
	.
	.
fbfr = (FBFR *)svcinfo->data
	.
	.
	.
/*At this point, assuming that at least 1000 bytes of unused data
  space is needed, allocate 1000 more bytes.  Checking for that
  amount of unused space means having enough space in the buffer.*/
	/*If the buffer has less than 1000 free bytes of data space left*/
	if(Funused(fbfr) < 1000) {
	/*Allocate enough space for 50 additional fields and 1000
	  bytes of data space.                                    */
		newbuf = (FBFR*)tprealloc((char*)fbfr, svcinfo->len+1000)
			/* Check that return is not null. Code omitted.  */
			/*Copy the current data from the screen buffer to the
	  		temporary buffer.                                  */
		Fcpy(newbuf,fbfr);
			/* Check the return to make sure it is not < 0 */
			/*Set the screen buffer to point to the temporary buffer*/
		fbfr = newbuf;
}

Checking for Modified Fields

It may be useful for a TUXEDO System/T service to know which fields on a data entry form were modified by a user at a terminal. Four routines are provided for this purpose. The first, get_mods(), places in an array provided by the caller, the field ids and occurrence numbers of all modified fields in the specified buffer. Only fields that were changed through the standard input since the display of a new form are counted in the modified list. The format of the call to get_mods() is:


get_mods(fbfr,mod_array,size_mod_array)
FBFR *fbfr;
struct track_mods *mod_array;
int size_mod_array;

fbfr points to an FML buffer that contains the data entered on the form by the user. This pointer to the FML buffer is the first argument in all four routines that provide information about modified fields. The reserved MODS field in the buffer carries the information about modified fields in an encoded format.

The second argument, mod_array, is a pointer to an array of track_mods structures. Space for this array must be allocated by the caller. The track_mods structure is defined in mods.h. Its format appears below:


#include "Fld.h"
struct track_mods {
	FLDID fldid;	/* field id of field */
	int occno;	/* occurrence number of field */
	short flags;
	};

get_mods() places the field id of the modified field in fldid and the occurrence number in occno. The flags field does not contain any information of interest to the caller.

The third argument, size_mod_array, should contain the number of track_mods structures that will fit into mod_array. get_mods() will not place more than size_mod_array entries in mod_array. get_mods() returns a -1 on an error and the number of entries placed in mod_array on success. On an error, Ferror should be checked for the cause of the error. For this routine, a return code of zero indicates that no fields were modified.

Calculating Space for mod_arrays

Before calling get_mods() you may want to find out the mod_array size needed to hold all the information that get_mods can return. For this purpose the mods_needed() routine is provided. Its format is shown below:


mods_needed(fbfr)
FBFR *fbfr;

mods_needed() returns the number of entries needed in mod_array to hold all modified field information returned by get_mods(). It returns a -1 on an internal failure, in which case Ferror contains the reason for failure. The value returned by mods_needed() may be passed directly to get_mods. If get_mods finds a -1 in its size_mod_array parameter it will return a 0.

Checking Whether a Specific Field Was Modified

While get_mods() returns all modified fields in an array, fld_mod() provides modification information about a specific field. It is called as follows:


fld_mod(fbfr, fldid, occno)
FBFR *fbfr;
FLDID fldid;
int occno;

fld_mod returns a 1 if the field on the form specified by fldid and occno was modified. It returns a 0 if there was no modification and a -1 on an internal error.

Force a Field to Be Modified or Unmodified

Another routine, set_mods(), enables you to force a field on the form to be considered as either modified or unmodified, regardless of its modification history. Its format is shown below:


set_mods(fbfr,fldid,occno,cmd)
FBFR *fbfr;
FLDID fldid;
int occno;
char *cmd;

set_mods() sets the modified status of all fields on a form with field identifier fldid and occurrence number occno based on cmd. cmd may be either of the strings MOD_SET or MOD_RESET. If cmd is MOD_RESET, the indicated fields will not be returned in the modified list until they are changed again. If cmd is MOD_SET, the indicated fields will always appear in the modified list until a new form is displayed or the modified field is reset with a call to set_mods(). If fldid is zero, cmd applies to all protected and unprotected fields on the form. set_mods() returns a 0 on an invalid cmd, a 1 on success and a -1 on an &F ;error (in which case the reason for the error is in Ferror). If the Ferror is FNOSPACE the caller should tprealloc() the FML buffer and try again. The FML function, Frealloc(), may be used to resize the buffer if the FML buffer is not being used as a message buffer in ATMI communication calls. tprealloc() tprealloc() routine Frealloc() (See tpalloc(3c) and Frealloc(3fml).)

A sample server that uses the above routines is in Figure 3. This server prints modified fields on the standard output, and resets all fields on the form to be unmodified.

Fig. 3: Sample mods Routine

#include   <stdio.h>
#include   "fml.h"
#include   "atmi.h"
#include   "Usysflds.h"
#include   "mods.h"
extern char *Fname();
extern char *malloc();
#define ERROR { sprintf(s,"FATAL ERROR");\
                Fchgs(f,STATLIN, 0, s);	\
                tpreturn(TPFAIL,-1,NULL,0,0);\
               }
MOD(sinfo)
TPSVCINFO *sinfo;
{
         struct track_mods *mod_array;
         int i,cnt;
         FBFR *f;
         char s[100];
         f = (FBFR *)sinfo->data;	/* read in message from mio */
         cnt=mods_needed(f);	/* get the number of entries needed */
         if (cnt < 0) ERROR;
         /* allocate space for the array */
         mod_array=(struct track_mods *)malloc(cnt * sizeof(struct track_mods));
         if (!mod_array) ERROR;
         if (get_mods(f,mod_array,cnt) < 0) ERROR;
         fprintf(stderr,"\n%d field(s) modified\n",cnt);
         for (i=0; i<cnt; i++)
            fprintf(stderr,"%s %d\n",
             Fname(mod_array[i].fldid),mod_array[i].occno);
        /* reset all the mods on the form */
        if (set_mods(f,(FLDID)0,0,"MOD_RESET") < 1) {
             ERROR;
        }
	if (mod_array) free(mod_array);
	tpreturn(TPSUCCESS,1,f,0,0)
}

The server can be built as follows:


$ buildserver -o MOD -s MOD -f MOD.c  -f $TUXDIR/lib/formlib.a

System Fields

As was described in Chapter 4, under After the Leave Key is Pressed, when a user enters values on a form mio stores the values in an FML buffer. When the user enters a leave key, mio sends the buffer as the data portion of a message to a server that advertises the service associated with the form.

In addition to the fields explicitly described on a form, mio exchanges information with a service through system reserved fields. The list of fields is shown in Table 5.

Table 5: DES System Fields

#NAME ID TYPE FLAG COMMENT
CURSID 2 long - field id of cursor
CURSOC 3 long - field occ no of cursor
LEVKEY 4 string - function key used to leave form
STATLIN 5 string - message for terminal status line
FORMNAM 6 string - form that is (to be) displayed
UPDTMOD 7 short - method of buffer refresh
SRVCNM 8 string - destination service for input buffer
NEWFORM 9 string - tells if FORMNAM is significant
DESTSRVC 17 string - service to send the buffer to
VALONENTRY 19 string - call validation functions when form is received

Some of the system fields were described in Chapter 4. Those that have their greatest significance on output from a server are described here.

Like any fields used in the TUXEDO System/T software, system fields have identifying numbers. To prepare for possible expanded use of system fields in the future, the first 100 field numbers are reserved.

FORMNAM

On input, FORMNAM contains the name of the form used to generate the current input buffer. On output, FORMNAM names the form that should be used to display the output buffer. If FORMNAM contains the name of the form currently being displayed, NEWFORM is checked.
NEWFORM

If there is no occurrence of NEWFORM, no action is taken. If there is at least one occurrence of NEWFORM in the buffer, a fresh copy of the current form is displayed. This copy is displayed with the default attributes specified in the form definition, plus any data that may reside in the current output buffer. The value in NEWFORM doesn't matter; only the fact that it exists. In addition to being altered by a service, FORMNAM and NEWFORM can also be altered by a firstval() or lastval() function. In these cases, a new form may be displayed without any interactions with a service.
DESTSRVC

On output from an application service or from firstval() or lastval(), DESTSRVC may contain the name of the service to which the fielded buffer should be sent the next time a leave key is pressed. DESTSRVC can also appear as a field on the form. In all cases, when a leave key is pressed, mio first consults the DESTSRVC field. If it finds a non-null value, it uses that as the name of the destination service.
SRVCNM

contains the name of the service to which the input buffer was dispatched. If the service changes SRVCNM, nothing is affected, because the field is not examined on output. If the service changes DESTSRVC on the other hand, the future destination service may be affected.
STATLIN

contains a value to appear on the status line of the form. Typically, this is some indication of the success or failure of the process done by the service. It is also used by DES validation functions and by mio as a place to display error or help messages.
UPDTMOD

The UPDTMOD system field is used to control the method of screen buffer update. Three methods are available:

1. Replace the screen buffer entirely with the output buffer. 2. Modify the screen buffer using Fupdate(). 3. Modify the screen buffer using Fojoin(). mio holds the original screen buffer and sends a copy of it along to the server as the input buffer to the service function. When the service is ready to send out the results of its processing, it makes a call to Fchg() (see Fchg(3fml)) naming UPDTMOD as the field and specifying a 1, 2 or 3 for the val argument. UPDTMOD is a short, so the val argument needs to be a pointer to a short. The code might look like this:


short s;
s = 1;
Fchg(fbfr,UPDTMOD,0,&s,0);

The service then calls tpreturn() to send the output buffer back to mio. mio consults the val argument to determine whether the output buffer should replace the screen buffer, or update it with Fupdate() or Fojoin(). Replacing the buffer entirely (val = 1) can include changing to a different form. Fupdate() (val = 2) updates screen buffer fields with corresponding output buffer fields. Fields in the screen buffer that do not have counterparts in the output buffer are left alone. Fields in the output buffer that do not appear in the screen buffer are added. Fojoin() (val = 3) acts like Fupdate(), except that fields in the output buffer that are not in the screen buffer are discarded. Fupdate() and Fojoin() are FML functions. See Fupdate(3fml) and Fojoin(3fml) for details on how they operate.

VALONENTRY

If VALONENTRY is present in a form buffer returned from a service, mio will call the form's validation functions. This feature can be used to format fields for display on the screen, e.g. change numeric values into dollar amounts in string format.

In order for these changes to appear on the display, the validation function must return 1 (see the earlier description of firstval() and lastval()).

Working with System Fields

Since the system reserved fields reside in the FML buffer portion of the message that mio sends to a service, you use the same FML subroutines to access them as you would use for the data fields in your application service routines, or in a firstval() or lastval() function.

Gaining Access

We have seen earlier in this chapter how you can use


#include "fml.h";
extern FBFR *_gfbfr;

to gain access to the FML buffer from a DES function. From a service, the way to get at the data that has been passed to a service as the data portion of the TPSVCINFO structure is illustrated in the following fragment.


TPSVCINFO  *svcinfo;
FBFR *fbfr;
	{
	.
	.
	.
	fbfr = (FBFR *)svcinfo->data;
	.
	.
	.
	}

Adding a System Field

The FML function to call to add a field is Fadd(). The code might look like this:


	/* See if an occurrence zero of DESTSRVC exists */
if(Fpres(fbfr,DESTSRVC,0){
	process the error;
	}
	/* No? Then okay to add one */
if(Fadd(fbfr,DESTSRVC,"OPEN_ACCT",0) == -1){
	process the error;
	}
 .
 .
 .

Fpres() returns TRUE if the occurrence number of the Fpres() specified field exists in the buffer. Checking for the existence of an occurrence zero of DESTSRVC is one way to see if the field exists in the buffer at all. The arguments for the Fadd() call are as follows: Fadd()

fbfr -the pointer to the buffer.
DESTSRVC -the FLDID of the field to be added (if an occurrence exists another is added.
"OPEN_ACCT" -a pointer to the value of the new field. (In this case, we're providing the literal value)
0 -the length because the field DESTSRVC is of type STRING.

Fadd() returns a -1 on failure, so that is used as a test for success in the example above.

Changing a System Field

The FML function you use to change a value in a system field is Fchg(). The code might look like this: Fchg()


if(Fchg(fbfr,DESTSRVC,0,"CLOSE_ACCT",0) == -1){
	process the error;
	}

which says, ``In the FML buffer I'm pointing at, change occurrence zero of the field called DESTSRVC to the value CLOSE_ACCT.'' Again, where we used the literal CLOSE_ACCT, we could have provided a pointer to a value. If the data type of the value had been a CARRAY rather than a STRING, the last argument would have given the actual length of the CARRAY.

Inspecting a System Field

The most frequently used function for checking the value of a system field is likely to be Fvals(), which returns Fvals() a character pointer to the string value of a field. (All the system fields except UPDTMOD, CURSID and CURSOC have a data type of string.)

Figure 4 shows how the code might look for checking what key was used to leave the form:

Fig. 4: Checking the Value of LEVKEY

/*The system field LEVKEY always contains the value of the transmission
  key; that is, it records the value of the key sequence that was
  depressed when the form was completed and sent into the mio()
  process.  The possible values of LEVKEY are <ESC> 0 (interpreted
  by the system as "F0") through <ESC> 9 (interpreted as "F9") and
  <CTRL> v (interpreted by the system as "F11").  With LEVKEY,
  meaning can be assigned to certain key sequences.                 */
char  *levkval;		/*Points to the value contained in the system
		  	  field LEVKEY                             */	
levkval = Fvals(fbfr,LEVKEY,0);
/*In this example the possible values of LEVKEY are "F1"
  through "F3".  Therefore, the switch can be done just on
  a value of 1, 2 or 3 in levkval[1] since levkval[0] == 'F'
  for each of the cases.                                           */
	switch(levkval[1]) {
	case '1':
	 	add();
		break;
	case '2':
		display();
		break;
	case '3':
		modify();
		break;
	}