                              STRUCTURES

                            Matthew Probert
                           Servile  Software

C provides the means to group together variables under one name 
providing a convenient means of keeping related information together 
and providing a structured approach to data. 

The general form for a structure definition is;

typedef struct
{
    variable_type variable_name;
    variable_type variable_name;
}
structure_name;


When accessing data files, with a fixed record structure, the use of a 
structure variable becomes essential. The following example shows a 
record structure for a very simple name and address file. It declares 
a data structure called 'data', and comprised of six fields; 'name', 
'address', 'town', 'county' , 'post' and 'telephone'. 

    typedef struct
    {
        char name[30];
        char address[30];
        char town[30];
        char county[30];
        char post[12];
        char telephone[15];
    }
    data;

The structure 'data' may then be used in the program as a variable 
data type for declaring variables; 

    data record;

Thus declares a variable called 'record' to be of type 'data'.

The individual fields of the structure variable are accessed by the 
general form; 

    structure_variable.field_name;

Thus, the name field of the structure variable record is accessed 
with; 

    record.name

There is no limit to the number of fields that may comprise a 
structure, nor do the fields have to be of the same types, for 
example; 

    typedef struct
    {
        char name[30];
        int age;
        char *notes;
    }
    dp;

Declares a structure 'dp' that is comprised of a character array 
field, an integer field and a character pointer field. 

A structure variable may be passed as a parameter by passing the 
address of the variable as the parameter with the & operator thus; 

        func(&my_struct)


The following is an example program that makes use of a structure to 
provide the basic access to the data in a simple name and address 
file; 


/* A VERY simple address book written in ANSI C */

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <fcntl.h>
#include <sys\stat.h>

/* num_lines is the number of screen display lines */
#define num_lines    25


typedef struct
{
    char name[30];
    char address[30];
    char town[30];
    char county[30];
    char post[12];
    char telephone[15];
}
data;

data record;
int handle;

/* Function prototypes */

void ADD_REC(void);
void CLS(void);
void DISPDATA(void);
void FATAL(char *);
void GETDATA(void);
void MENU(void);
void OPENDATA(void);
int SEARCH(void);

void CLS()
{
    int n;

    for(n = 0; n < num_lines; n++)
        puts("");
}

void FATAL(char *error)
{
    printf("\nFATAL ERROR: %s",error);
    exit(0);
}

void OPENDATA()
{
    /* Check for existence of data file and if not create it */
    /* otherwise open it for reading/writing at end of file */

    handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);

    if (handle == -1)
    {
        handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);
        if (handle == -1)
            FATAL("Unable to create data file");
    }
}

void GETDATA()
{
    /* Get address data from operator */

    CLS();

    printf("Name ");
    gets(record.name);
    printf("\nAddress ");
    gets(record.address);
    printf("\nTown ");
    gets(record.town);
    printf("\nCounty ");
    gets(record.county);
    printf("\nPost Code ");
    gets(record.post);
    printf("\nTelephone ");
    gets(record.telephone);
}

void DISPDATA()
{
    /* Display address data */
    char text[5];

    CLS();

    printf("Name %s",record.name);
    printf("\nAddress %s",record.address);
    printf("\nTown %s",record.town);
    printf("\nCounty %s",record.county);
    printf("\nPost Code %s",record.post);
    printf("\nTelephone %s\n\n",record.telephone);

    puts("Press RETURN to continue");
    gets(text);
}

void ADD_REC()
{
    /* Insert or append a new record to the data file */
    int result;

    result = write(handle,&record,sizeof(data));

    if (result == -1)
        FATAL("Unable to write to data file");
}

int SEARCH()
{
    char text[100];
    int result;

    printf("Enter data to search for ");
    gets(text);
    if (*text == 0)
        return(-1);

    /* Locate start of file */
    lseek(handle,0,SEEK_SET);

    do
    {
        /* Read record into memory */
        result = read(handle,&record,sizeof(data));
        if (result > 0)
        {
            /* Scan record for matching data */
            if (strstr(record.name,text) != NULL)
                return(1);
            if (strstr(record.address,text) != NULL)
                return(1);
            if (strstr(record.town,text) != NULL)
                return(1);
            if (strstr(record.county,text) != NULL)
                return(1);
            if (strstr(record.post,text) != NULL)
                return(1);
            if (strstr(record.telephone,text) != NULL)
                return(1);
        }
    }
    while(result > 0);
    return(0);
}

void MENU()
{
    int option;
    char text[10];

    do
    {
        CLS();
        puts("\n\t\t\tSelect Option");
        puts("\n\n\t\t\t1 Add new record");
        puts("\n\n\t\t\t2 Search for data");
        puts("\n\n\t\t\t3 Exit");
        puts("\n\n\n\n\n");
        gets(text);
        option = atoi(text);

        switch(option)
        {
            case 1 : GETDATA();
                     /* Go to end of file to append new record */
                     lseek(handle,0,SEEK_END);
                     ADD_REC();
                     break;

            case 2 : if (SEARCH())
                         DISPDATA();
                     else
                     {
                         puts("NOT FOUND!");
                         puts("Press RETURN to continue");
                         gets(text);
                     }
                     break;

            case 3 : break;
        }
    }
    while(option != 3);
}

void main()
{
    CLS();
    OPENDATA();
    MENU();
}

A word of warning! When translating data structures from other 
languages, or creating data structures to access other people's data 
files you need to be aware of whether to use BYTE allignment or 
whether you can get away with the faster WORD allignment.

For example, the Remote Access user record (as used in USERS.BBS) must 
be accessed with BYTE allignment as some of the fields are an odd 
number of bytes wide. If you compile your structure with WORD 
allignment, the compiler will pad odd length fields and upset the 
offset location of subsequent fields. 

Here is the Remote Access user record data structure translated into C

/* BYTE allignment is essential */

typedef struct
{
    char x0;
    char Name[36];            
    char Location[25];        
    char Organisation[52];    
    char Address1[51];        
    char Address2[51];        
    char Address3[51];      
    char Handle[36];        
    char Comment[69];       
    char x1[16];            
    char DataPhone[16];        
    char VoicePhone[16];    
    char LastTime[6];        
    char LastDate[8];        
    unsigned char Attribute;
    unsigned char Attribute2;
    char x2[12];            
    int MsgsPosted;            
    int Security;            
    long HighMsgRead;        
    long NoCalls;            
    long Uploads;            
    long Downloads;            
    long UploadsK;            
    long DownloadsK;        
    long TodayK;            
    int TimeUsedToday;        
    char x3[2];
    int Group;                 
    char x4[402];
    char FirstDate[9];        
    char BirthDate[9];        
    char SubDate[9];        
    char ScreenWidth;
    char Language;
    char DateFormat;
    char ForwardTo[34];        
    char x5[5];
    char DefaultProtocol;    
    char x6[57];
}
DATA;

DATA user;

