|
|
There are two sets of I/O routines available for reading and writing G2 records from a C program. One is for applications which deal with a specific set of G2 record types and the other is for programs that work with all G2 records, regardless of type. The first set of routines map G2 data records of known type directly to or from C variables. This is called the compiled interface. The second set of I/O routines map G2 data records of arbitrary type to or from a general-purpose data structure which can be subsequently navigated through a set of pointers. This is called the interpreted interface. Of course, both the compiled and interpreted interfaces can be mixed within a single program and at opposite ends of a communications stream.
The routines of the compiled interface are constructed from G2 record definitions. The construction process has four steps:
We will use an example to show the compiled interface in more detail.
Let's assume that we simply want to read and write G2 records with the definition
usr log 10 id 8 gid LONG name 40
We start by typing the above definition into a file named usr.g (the name of the file is arbitrary, but the .g suffix is mandatory). This file is then compiled with the following UNIX command:
$ g2comp usr.g
Assuming this step goes well, we now have two new files: usr.h which contains a typedef and usr.c which contains the typedef descriptor. The typedef in usr.h will look like
typedef struct { char log[10+1]; char id[8+1]; long gid; char name[40+1]; } USR;
(the extra character added to each field is for the null byte). Note that the typename (USR) is the uppercase version of the record name (usr).
Also in usr.h is an external declaration for the typedef descriptor in usr.c:
extern G2DESC *usr;
Now we can write a simple C program to read and write G2 usr records:
#include <stdio.h> #include "usr.h" main(){ USR buf; while(getrec(&buf, usr, stdin)){ putrec(&buf, usr, stdout); } exit(0); }
Assuming this program resides in a file named simple.c, we compile and link an executable program with the command:
$ cc simple.c usr.c -lg2
In a more realistic program, there would be something useful happening between the getrec and putrec function calls. Such code would be in native C and independent of the G2 I/O functions, for example:
USR buf; while(getrec(&buf, usr, stdin)){ buf.gid++; putrec(&buf, usr, stdout); }
Some applications must be prepared to deal with a collection of possible record types in the communications stream. In this case, function getname() can be called to get the name of the next G2 record, and function getbody() can then be used to map the remainder of the G2 record into a C variable of the appropriate type. The following example illustrates this usage.
#include <stdio.h> #include <string.h> #include "usr.h" #include "sys.h" main(){ char name[20]; USR usrbuf; SYS sysbuf;
while(getname(name, stdin)){ if(strcmp(name,"usr")==0){ getbody(&usrbuf,usr,stdin); ... }else if(strcmp(name,"sys")==0){ getbody(&sysbuf,sys,stdin); ... } } ... exit(0); }
Here, two G2 record types, usr and sys, can be handled. In general, any number of G2 record types can be dealt with in this way. Note, however, that a priori knowledge of the record types was required. Unknown record types must be dealt with using the interpreted interface.
The compiled interface requires that each G2 record type be known at compile time. For programs which need to handle the conceptually infinite class of G2 records in a general way, an interpretive interface is provided.
The functions getbuf() and putbuf() map G2 records between a stream and an internal data structure which can be ``navigated'' by following pointers. The data structure, of type G2BUF, is defined as follows in g2.h:
struct G2NODE; struct G2BUF{ char* name; char* val; G2NODE* child; G2NODE* next; }; struct G2NODE{ G2NODE* root; };
The following program uses the interpreted interface to produce a report of the contents of each G2 record in its input stream:
#include <g2.h> main(){ G2BUF buf[1]; while(getbuf(buf,stdin)){ walk(buf->root,0); } }
walk(np, level) G2NODE* np; int level; { G2NODE* cp; printf("%d: %s",level,np->name); if(np->val){ printf("\ t%s\ n",np->val); }else{ printf("\ n"); } for(cp=np->child;cp;cp=cp->next){ walk(cp,level+1); } }