Category : C Source Code
Archive   : C-STRUCT.ZIP
Filename : STRUCTS.C

 
Output of file : STRUCTS.C contained in archive : C-STRUCT.ZIP

/*************************************************************************
This program is a demonstration of using arrays of structures
for such tasks as screen painting, field definition, and data file
searching.

This program is submitted as public domain. It's basic concept is
to communicate some uses of data structures and re-useable coding.
This program (along with other BTree and similiar structure based
programs) is the result of a C programming part II class I taught
at a local university. Several of my students have stated how much
easier project developement was with routines similiar to these.

Mario Giannini
Compuserve #76276,1576
January 25, 1991
*************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include /* alloc.h for TurboC */
#include
/*************************************************************************/
/* Key macros to make life easier */
#define ESC 0x1B
#define F10 0x4400
#define CURUP 0x4800
#define CURDN 0x5000
#define BACKSPACE 0x0008
#define ENTER 0x000D

#define TextColor 7
#define DataColor 0xF

#define INDEXFLD 0x0001
/* Here are our basic data structure definitions */
typedef struct {
int H, V, Len, Option, Type, FieldNum;
char *Text, *Data;
} FIELDS;

typedef struct {
int Handle, RecSize;
FIELDS *EditFields;
char *Filename, *RecSpace;
long Recpos;
} RECORD;

/*************************************************************************/
/* Here are data declarations, it should be noted that the record types
** can be changed here, and upon re-compile all of the code should
** still work as always, interpreting the structures themselves
**/

FIELDS PersonFields[]={
0, 0, 10, 0, 0, 0, "First", NULL,
25, 0, 10, 0, INDEXFLD, 0, "Last", NULL,
0, 2, 35, 0, 0, 0, "Company", NULL,
0, 3, 35, 0, 0, 0, "Comment", NULL,
0, 0, 0, 0, 0, 0, NULL, NULL}; /* NULL for Text is terminator */

RECORD Person={0, 0, PersonFields, "PERSON.DAT", NULL, 0L};

FIELDS TransFields[]={
0, 0, 8, 0, 0, 0, "Date", NULL,
25, 0, 10, 0, 0, 0, "Amount", NULL,
0, 0, 0, 0, 0, 0, NULL, NULL}; /* NULL for Text is terminator */
RECORD Transaction={0, 0, TransFields, "TRANS.DAT", NULL,0L};

/*************************************************************************/
/* Sort of a 'main menu' of tables we can edit */
RECORD *AllRecords[]={&Person, &Transaction, NULL};
/*************************************************************************/
/**** Function prototypes ***/
void main(void);
void AddItem(RECORD *Record);
void EditItem(RECORD *Record);
int EditRecord(RECORD *Record);
void ShowScreen(RECORD *Record);
void ShowData(RECORD *Record);
void Cls(int Color);
void Locate(int H,int V);
void PrintStr(char *Str,int Color);
void Message(char *Str,int Wait);
int MyGets(char *Dest,int Len);
int GetKey(void);
int AppendRecord(RECORD *Record);
int LocateRecord(RECORD *Rec);
int ReadRecord(RECORD *Record);
int WriteRecord(RECORD *Record);
void BlankRecord(RECORD *Record);
int OpenFile(RECORD *Record);
void CloseFile(RECORD *Record);
/*************************************************************************/
/*** Start of code section ***/
/*** ***/
/*************************************************************************/
void main(void)
{
int i, NumOfRecs, Ch=0, Ch2;

while(Ch!=ESC)
{
Cls(7);
for(NumOfRecs=0;AllRecords[NumOfRecs];NumOfRecs++)
printf("%d: %s\n", NumOfRecs+1, AllRecords[NumOfRecs]->Filename);
printf("\nESC to quit.\n\nPlease press a key..");
do {
Ch=GetKey();
} while(Ch!=ESC && (Ch<'0'||Ch-'1'>NumOfRecs));
if(Ch==ESC)
continue;
Cls(7);
printf("You Selected %s\n\n", AllRecords[Ch-'1']->Filename);
printf("A) Add\nE) Edit\n\nESC for previous menu\n\nMake selection:");
do {
Ch2=getch();
Ch2=toupper(Ch2);
} while( Ch2!=ESC && Ch2!='A' && Ch2!='E') ;

/*
** Note how the AllRecord array is used to process table based on user
** selection.
*/
if(Ch2=='A')
AddItem(AllRecords[Ch-'1']);
if(Ch2=='E')
EditItem(AllRecords[Ch-'1']);
}

}
/*************************************************************************/
/*** Higher-level functions ***/
/*** ***/
/*************************************************************************/
void AddItem(RECORD *Record)
{
Cls(7);
OpenFile(Record);
ShowScreen(Record);
BlankRecord(Record);
Message("Press ESC to Abort, F10 to Add",0);
if(EditRecord(Record)==F10)
AppendRecord(Record);
CloseFile(Record);
Message("", 0);
}
/*************************************************************************/
void EditItem(RECORD *Record)
{
Cls(7);
OpenFile(Record);
ShowScreen(Record);
BlankRecord(Record);
Message("Press ESC to Abort, F10 to start search",0);
if(EditRecord(Record)==F10)
{
if(LocateRecord(Record))
{
Message("Press ESC to abort, F10 to save updates", 0);
ReadRecord(Record);
ShowData(Record);
if(EditRecord(Record)==F10)
WriteRecord(Record);
}
else
Message("Criteria not found. Press any key to continue",1);
}
CloseFile(Record);
Message("",0);
}
/*************************************************************************/
/*** ScreenI/O section follows ***/
/*** ***/
/*************************************************************************/
int EditRecord(RECORD *Record) /* Edits a Record */
{
int i=0, Ch=0, LastEntry=0;

/*
** Note how this founction interprets whatever structures are passed
** to it at run-time. Not much is hard-coded.
*/
for(LastEntry=0;Record->EditFields[LastEntry].Text;LastEntry++)
;
while(Ch!=ESC && Ch!=F10)
{
Locate(Record->EditFields[i].H+strlen(Record->EditFields[i].Text)+1,
Record->EditFields[i].V);
Ch=MyGets(Record->EditFields[i].Data, Record->EditFields[i].Len);
if( (Ch==ENTER||Ch==CURDN) && ++i>=LastEntry)
i=0;
if(Ch==CURUP && --i<0)
i=LastEntry-1;
}
return(Ch);
}
/****************************************************************/
void ShowScreen(RECORD *Record) /* Show Screen layout Data */
{
int i=0;

for(i=0;Record->EditFields[i].Text;i++)
{
Locate(Record->EditFields[i].H, Record->EditFields[i].V);
PrintStr(Record->EditFields[i].Text, TextColor);
}
}
/****************************************************************/
void ShowData(RECORD *Record) /* Show Record Data */
{
int i=0;

for(i=0;Record->EditFields[i].Text;i++)
{
Locate(Record->EditFields[i].H+strlen(Record->EditFields[i].Text)+1,
Record->EditFields[i].V);
PrintStr(Record->EditFields[i].Data, DataColor);
}
}
/****************************************************************/
void Cls(int Color) /* Clears the screen to any color */
{
union REGS Regin;

Regin.x.ax=0x0600;
Regin.h.bh=(char)Color;
Regin.x.cx=0;
Regin.x.dx=0x1950;
int86(0x10, &Regin, &Regin);
Locate(0,0);
}
/****************************************************************/
void Locate(int H, int V) /* Locate cursor at an Horiz, Vert coordinate */
{
union REGS Regin;
Regin.h.ah=2;
Regin.h.bh=0;
Regin.h.dh=(char)V;
Regin.h.dl=(char)H;
int86(0x10, &Regin, &Regin);
}
/****************************************************************/
void PrintStr(char *Str, int Color) /* Generic color print string routine */
{
union REGS Regin;

Regin.h.ah=9;
Regin.h.al=*Str;
Regin.x.cx=strlen(Str);
Regin.h.bl=(char)Color;
Regin.h.bh=0;
int86(0x10, &Regin, &Regin);
while(*Str)
{
Regin.h.ah=0xE;
Regin.h.al=*Str++;
int86(0x10, &Regin, &Regin);
}
}
/****************************************************************/
void Message(char *Str, int Wait) /* Simple message display routine */
{
Locate(0,24);
printf(" ");
Locate(0,24);
printf("%s", Str);
if(Wait)
GetKey();
}
/****************************************************************/
int MyGets(char *Dest, int Len) /* Simple line input routine */
{
int i, Ch=0;
char ThisChar[2];

ThisChar[1]=*(Dest+Len)='\0'; /* Insure NULL Terminate */
PrintStr(Dest, DataColor);
i=strlen(Dest);
while( (Ch=GetKey())!=ESC && Ch!=ENTER && Ch!=CURUP && Ch!=CURDN&&Ch!=F10)
{
if(Ch==BACKSPACE && i)
{
PrintStr("\x08 \x08",DataColor);
i--;
continue;
}
if(i {
ThisChar[0]= *(Dest+i)=(char)Ch;
PrintStr(ThisChar, DataColor);
i++;
}
}
return(Ch);
}
/****************************************************************/
int GetKey() /* like getch, but handles function keys OK */
{
int i;

if( (i=getch()) )
return(i);
return(getch()*0x100);
}
/*************************************************************************/
/*** File I/O section follows ***/
/*** ***/
/*** Note that this section is where the DataBase Engine interface ***/
/*** would appear to API's like ORACLE, dbVista, or Paradox. ***/
/*** It may be useful to add new members to the FIELDS structure so ***/
/*** that the field number is also present (as used by some APIs). ***/
/*** ***/
/*************************************************************************/
int AppendRecord(RECORD *Record) /* Add record to End of file */
{
Record->Recpos=lseek(Record->Handle, 0L, SEEK_END);
return(WriteRecord(Record));
}
/*************************************************************************/
int LocateRecord(RECORD *Rec) /* Locate record */
{
char *LocalRec, *Easier;
static char Mess[64];
int i, Found=0, j;

if( (LocalRec=calloc(Rec->RecSize, sizeof(char)))==NULL)
return(-1);

/*
** Just a note, since this is an all-field search we would
** first locate the index or key fields by which to search. But
** since this program has no index mechanism, this is just a comment.
*/
for(i=0;Rec->EditFields[i].Text;i++) /* Locate index field */
if(Rec->EditFields[i].Type&INDEXFLD)
break;
if(Rec->EditFields[i].Text)
{
Locate(0,17);
printf("%s is an index (or key) field, I would search there\n",
Rec->EditFields[i].Text);
printf("first, if I had a DataBase Engine.\n");
GetKey();
Locate(0,17);
printf(" \n");
printf(" ");

}
Rec->Recpos=lseek(Rec->Handle, 0L, SEEK_SET);
/*
** Now the search begins
*/
while(!Found && read(Rec->Handle, LocalRec, Rec->RecSize)==Rec->RecSize)
{
Found=1;
for(i=0;Rec->EditFields[i].Text&&Found;i++)
{
if( ! *Rec->EditFields[i].Data ) /* Don't test blank search fields */
continue;
Easier=LocalRec+(Rec->EditFields[i].Data-Rec->RecSpace);
j=strlen(Rec->EditFields[i].Data);
if(strnicmp(Rec->EditFields[i].Data, Easier, j))
Found=0;
}
if(!Found)
Rec->Recpos=lseek(Rec->Handle, 0L, SEEK_CUR);
}
free(LocalRec);
return(Found);
}
/*************************************************************************/
int ReadRecord(RECORD *Record) /* Read current record */
{
lseek(Record->Handle, Record->Recpos, SEEK_SET);
return(read(Record->Handle, Record->RecSpace, Record->RecSize));
}
/*************************************************************************/
int WriteRecord(RECORD *Record) /* Write current record */
{
lseek(Record->Handle, Record->Recpos, SEEK_SET);
return(write(Record->Handle, Record->RecSpace, Record->RecSize));
}
/*************************************************************************/
void BlankRecord(RECORD *Record) /* Empty out all fields */
{
int i;
for(i=0;Record->EditFields[i].Text;i++)
if(Record->EditFields[i].Data)
*(Record->EditFields[i].Data)='\0';
}
/*************************************************************************/
int OpenFile(RECORD *Record) /* Open file and create Edit Space for buffer */
{
int i, Memory=0;

if( (Record->Handle=open(Record->Filename, O_RDWR|O_BINARY|O_CREAT,S_IWRITE))==-1)
return(-2);

/* Determine and allocate needed record block size */
for(i=0;Record->EditFields[i].Text;i++)
Memory=Memory+Record->EditFields[i].Len+1;
if( (Record->RecSpace=calloc(Memory,sizeof(char)))==NULL)
return(-1);

/* Assign field pointers into out Data Record block */
for(Memory=0, i=0;Record->EditFields[i].Text;i++)
{
Record->EditFields[i].Data=(Record->RecSpace+Memory);
Memory=Memory+Record->EditFields[i].Len+1;
}
Record->RecSize=Memory;
return(0);
}
/*************************************************************************/
void CloseFile(RECORD *Record) /* Close File, free buffer */
{
int i;
free(Record->RecSpace);
close(Record->Handle);
}


/** End of File **/