3.6 Sequence Number Example



The last example implements a sequential number field that is incremented each time the operator adds a record. This could be used, for example, with the subscriber invoice form as described in the Advanced Form Features chapter.

The user edit routine must perform the following actions:

cbautil20000000.gif During initialization, open a file containing the sequence number

cbautil20000000.gif When in ADD mode and after the blank form has been displayed (U_BEGINFORM), perform the following

cbautil20000000.gif Lock the sequence number file

cbautil20000000.gif Read the old sequence number

cbautil20000000.gif Increment the sequence number

cbautil20000000.gif Update the sequence number in the sequence number file

cbautil20000000.gif Write the sequence number onto the screen

cbautil20000000.gif Close the sequence file when form quits (U_EXIT or U_DIE)

The user edit routine assumes that the first record in the file contains the sequence number. Since there is only one record in the file, it can be a sequential file. The user edit routine assumes the seqno field contains the sequence number. Since the seqno field cannot be the primary key (you cannot change the value of the primary key field with dupdate), there must be a dummy field in the file that is the primary key.

//*

* cbase\demo\src\seqform.c 4.3 Delta date: 2/13/86

*

* A simple user edit routine to implement a sequential serial number.

* SEQFILE is a file containing one record that contains the sequence

* number. The serial number is a string - an embedded number is

* incremented (xyz123-a increments to xyz124-a, xyz999-a increments

* to xyz000-a).

*

* The sequence number field in your form must have an edit name of

* seqno (defined below). When the operator ADDs a record in a form

* containing the seqno field, the sequence number in SEQFILE is

* incremented, and the incremented sequence number is written into

* the seqno field.

*

* This is a very simple-minded implementation - you can only have

* one sequence number file per form, and it generates "holes" in

* the sequence of numbers whenever an ADD is not stored (by pressing

* CANCEL or ADD while adding a record).

*

*/

#include <stdio.h>

#include <ctype.h>

#include <cbase/dtypes.h>

#include <cbase/dirio.h>

#include <cbase/form.h>

static char sccs_id[] = "%W"

#define SEQFILE "seqfile" /* name of sequence number file */

#define SEQFIELD "seqno" /* seq field name in form */

#define SEQSIZE 16 /* field size in SEQFILE */

static DFILE *seq_file; /* sequence file pointer */

static char *seq_list[] = { /* sequence file field list */

"seqno",

NULL,

};

static struct sb { /* sequence file record structure */

char sb_seqno[SEQSIZE];

};

main (argc, argv)

int argc;

char *argv[];

{

form (argc, argv);

exit (0);

}

char *

user_edit (type, name, old_value, new_value, exit_char)

int type;

char name[];

char old_value[];

char new_value[];

int exit_char;

{

struct sb seq_buf;

if (type == U_INITIALIZE) {

/* open sequence file */

if ((seq_file = dlopen (SEQFILE, "u")) == NULL)

return (derrmsg ());

if (dblist (seq_list, seq_file) == BAD)

return (derrmsg ());

}

if ( type == U_BEGINFORM

&& smode() == F_ADD

&& sread(SEQFIELD) != NULL ) {

/* increment sequence file and display it */

if (dflockw (seq_file) == 0)

return ("cannot lock sequence file");

if (dfind (&seq_buf, seq_file) == BAD)

return ("cannot find sequence file record");

increment (seq_buf.sb_seqno);

if (dupdate (&seq_buf, seq_file) == BAD)

return ("cannot update sequence file record");

if (dfunlock (seq_file) == 0)

return ("cannot unlock sequence file");

if (swrite (SEQFIELD, seq_buf.sb_seqno) == 0)

return ("cannot swrite sequence number");

}

if (type == U_EXIT || type == U_DIE)

/* close sequence file */

dclose (seq_file);

return (NULL);

}

increment (string) /* increments number embedded in string */

char string[];

{

register char *s;

s = string;

while (*s)

s++;

while (s > string) {

if (*s == '9')

*s = '0';

else if (isdigit (*s)) {

*s = *s + 1;

break;

}

}

}

A limitation with this user edit routine is that it only deals with one series of sequence numbers. One method for extending this limitation would be to include on each screen that you want a sequence number an invisible field that has an initial value that is a key value of the sequence number record in the sequence number file. Then, the user edit routine would sread this field and do a dfindk in the sequence number file.

Since the user edit routine updates the sequence number on every U_BEGINFORM while in ADD mode, there will be 'holes' in the sequence of serial numbers if the operator leaves ADD mode without storing the record (by pressing CANCEL or ADD). This problem is much more difficult to solve. If you do not require the serial number to be visible on the form, you could instead increment the serial number during U_PREPARE; then holes only occur if the file is full and the record is not stored. Another approach would be to lock and display the sequence file record during U_BEGINFORM, and update and unlock the sequence file record during U_STORED. This approach has several serious drawbacks, however; it breaks the rule that a user edit routine should not leave a record locked across user edit routine calls; also, no one else can add a record between the time the operator presses ADD and STORE. It is possible under abnormal conditions that your user edit routine may not get a chance to unlock the file, and on some systems, a record remains locked even when the program that locked it has terminated. If the operator presses ADD, the sequence file stays locked until the operator presses STORE. No one else can add a record that uses that sequence number record for an indefinite period.

Currently, the user edit routine locks the entire sequence number file while it updates the sequence number file. Normally, this is not a problem since the file is never locked more than a fraction of a second during the update, and there is only one sequence number in the file. If you implement the above suggestions, it may be more efficient to lock the record rather than the entire file.

This concludes the section on writing user edit routines. The examples above give some idea of what can be done inside a user edit routine. Many other things are possible using the form functions that are described at the end of the chapter. A general set of rules to follow when writing a user edit routine is as follows:

1. Make sure your user edit routine always returns either NULL or a pointer to a valid error message. If you break this rule, for example by just "falling off" the end of your user edit routine, form will usually core dump.

2. Use the sread function to read data from a form.

3. Use the swrite function to display data on the screen rather than writing on the screen directly. By using swrite, form knows where data has been written and erases it when a new record or form is displayed.

4. Use swrite to place data into the data record. If the screen field is associated with a field in the data record, then swrite also stores the value into the data field.

5. Always use record or file locking when adding or updating a record in another file. This guarantees that the edit routine will work in a multi-user environment.

6. Never leave a record or file locked between one user edit call and the next. There is no guarantee that the user edit routine will be called again, and thus the RMSfile would stay locked.

7. For any RMSfiles your user edit routine uses, make sure the data types and lengths of each member of your user record structure match the corresponding fields in the RMSfile dictionary.