Stock Update / Merge Program
Program Overview:
Before this program is used it is imperative that the stock transaction file used has been produced by the data entry program (494792P0.exe), and prior to this has been validated by the validation program (494792P1.exe). If this is not the case then the outcome of this program is not guaranteed to be in anyway reliable or useable, and the Stock Master file created can not be trusted in any shape or form.
The validated stock transaction file from the Validate Program (494792P1.exe) is used to update a Stock Master and Stock Index file, thus creating a new generation of the stock files. Stock transactions that can not be actioned are reported on the printer under suitable headings with an appropriate error message.
The text file which contains the validated stock transaction records must be named 494792SF.DAT. The stock master file must be named STCKMAST.DAT. The stock index file must be named STCKMAST.IDX, and all three must be located in the same directory from where the update program is located and executed from. This is unless the user changes these default file names by entering additional program parameters at the command line. Below describes how the default file names can be overridden: -
494792P5.exe <validated stock transaction file> <stock master file> <stock index file> <printer>
The default file names are as follows: -
Validated Transaction file – 494792SF.DAT
Stock Master file – STCKMAST.DAT
Stock Index file – STCKMAST.IDX
Printer – PRN
If the user wishes to override one of these files they must also enter the file names of all the files preceding it. For example to override the printer to a text file named test.txt, they must enter the following command: -
494792P5.exe 494792SF.DAT STCKMAST.DAT STCKMAST.IDX test.txt
However if the user only wishes to change the name of the file which contains the validated transactions, they may enter the following command: -
494792P5.exe TRANS.DAT
The overriding of the default file names is mainly for test purposes or where there is a need to do so. Such a need may arise when there is a need to test the outcome of a sock transaction file, but you do not wish to change the contents of the original stock master files. The everyday users of this program need not even have to be aware of this overriding feature.
Update:
When the program is first starting it checks to see if there was a command-line argument given. If there was then the argument is taken to be the name of the validated stock transaction data file, and an attempt at opening this file is made. If the attempt fails then the program reverts to using the default file name (494792SF.DAT). An attempt at opening the default named file is now made, if this also fails an error message informing the user that the file could not be opened is displayed and the program exits returning the user back to the operating system.
If there was another command-line argument given this is taken to be the name of the stock master file. An attempt at opening this file is made. If the attempt fails then the program reverts to using the default file name (STCKMAST.DAT). An attempt at opening the default named file is now made, if this also fails an error message informing the user that the file could not be opened is displayed and the program exits returning the user back to the operating system.
If there was another command-line argument given this is taken to be the name of the stock index file. An attempt at opening this file is made. If the attempt fails then the program reverts to using the default file name (STCKMAST.IDX). An attempt at opening the default named file is now made, if this also fails an error message informing the user that the file could not be opened is displayed and the program exits returning the user back to the operating system.
If there was another command-line argument given this is taken to be the name of the printer file (where the error report from this program is printed to). An attempt at opening this file is made. If the attempt fails then the program reverts to using the default file name (PRN). An attempt at opening the default named file is now made, if this also fails an error message informing the user that the file could not be opened is displayed and the program exits returning the user back to the operating system. If no command-line arguments were given then all default file names are used.
When all the required files have been successfully opened, a message is displayed on screen informing the user that the program is now updating the stock master file with the given validated stock transaction file. And all erroneous transactions will be written to the printer (or to the given file name).
The next stage of the program is to read the contents of the stock files into memory. The contents of the stock files are stored in a double linked list for easy and speed of processing. This linked list creation process is detailed below as it is a fundamental part of this program.
Once the linked list has been successfully created we now enter the main processing loop. A transaction is read from the validated stock transaction file and then it is determined what type of transaction was read. If the type of the transaction contains information then it is this type that has been read in. Depending on the type of transaction read either one of three functions is called, addStockRec, amendStockRec or zapStockRec. These three functions make changes to the contents of the linked list. All three of these functions are detailed below because they too are a central part to the correct functionality of this program.
Linked List Creation Process -
The linked list is structured as four elements. The first holds the contents of the index stock record. The second holds the contents of the master stock record. The third holds a pointer to the previous element in the linked list. And the forth element holds a pointer to the next element in the linked list.
/*– Double Linked List for file contents (index & master) –*/
typedef struct STOCK_LIST
{ STOCK_IDX index;
STOCK_REC rec;
struct STOCK_LIST *prev;
struct STOCK_LIST *next;
}STOCK_LIST;
The first thing to be done is to calculate how many stock records are held on the stock master file. This is accomplished by advancing to the end of the stock index file and then dividing the size of the file by the size of a stock index record.
/*– Calculate the number of record entries –*/
fseek(stkidx,0L,SEEK_END);
count = ftell(stkidx) / IDX_SIZE;
When the number of stock records has been determined, an attempt at allocating enough memory to hold the entire stock index file is made. If this memory allocation fails, an error message is printed to the screen before the program exits. The program will exit with a return value indicating to the operating system that it failed.
/*– Create a list to hold the contents of the index file –*/
if (!(idxList = (STOCK_IDX *) malloc(count * IDX_SIZE)))
{ printf(“Error allocating memory for INDEX LIST\n”);
exit(EXIT_FAILURE);
}
When the correct amount of memory has been allocated we go back to the start of the stock index file. We now read the entire contents of the stock index file and store it in the memory that has been allocated. This stock index list is then sorted into ascending part number order (index key) using a standard sort utility (qsort).
/*– Go back to the start of the index file –*/
rewind(stkidx);
/*– Read the complete list from the index file –*/
fread(idxList, IDX_SIZE * count, 1, stkidx);
/*– Sort the index list into ascending part number order –*/
qsort(idxList, count, IDX_SIZE, compare);
The main linked list creation loop is now entered. This loop uses the index list previously created to read the stock master file in a random fashion. This is because the stock master file will not always be in a sorted order, the reason it uses an index file is for this very reason.
Using the contents of the current index record we calculate where the corresponding record is stored on the master file. We then read this record into a temporary storage variable (of type STOCK_REC). If we were unable to read the record from the master stock record an error message is displayed on the screen informing the user of this fact. The program is then exited, and a value is returned to the operating system indicating that the program failed. The user is then returned to the operating system. The memory used to store the index list is also freed before the program exits.
/*– Go to the correct position in the master file –*/
fseek(master, STK_SIZE * idxPtr->rec_num, SEEK_SET);
/*– Read the required stock record from the master file –*/
if (fread(&stkRec, STK_SIZE, 1, master)!=1)
{ printf(“Error accessing STOCK MASTER FILE!\n”);
/*– Free the memory used by the index list –*/
free(idxList);
exit(EXIT_FAILURE);
}
When we have successfully read the correct stock record from the stock master file, we now allocate enough memory to store this record in the linked list.
/*– Allocate memory for the next element in the list –*/
recElement = (STOCK_LIST *)malloc(LST_SIZE);
If the memory was successfully allocated then we store the contents of the current index record, the key (part number) and record number (where the record is on the master file) in the relevant sections of the linked list element. We also store the contents of the stock record in the relevant sections of the link list element.
/*– Store the record in the correct position in the list –*/
/*– because the master stock file will not always be sorted –*/
if (recElement)
{
/*– Populate index list elements –*/
strcpy(recElement->index.key,idxPtr->key);
recElement->index.rec_num = idxPtr->rec_num;
/*– Populate stock list elements –*/
strcpy(recElement->rec.key,stkRec.key);
strcpy(recElement->rec.partDesc,stkRec.partDesc);
strcpy(recElement->rec.moveDate ,stkRec.moveDate);
recElement->rec.suppCode = stkRec.suppCode;
recElement->rec.freeStk = stkRec.freeStk;
recElement->rec.minStk = stkRec.minStk;
recElement->rec.price = stkRec.price;
recElement->next = NULL;
If this is the first element in the linked list we set the linked list pointers to reflect this fact. There are two global variables which keep track of the start and finish positions of the linked list (recFirst and recFinish respectively). If recFirst currently holds NULL, then the linked list is empty and the current element will be the start of the linked list. However if this is not the start of the list the current recFinish elements next pointer is set to point to this element, and this elements previous point is set to point to the current recFinish element. Finally the recFinish global variable is set to the current element.
/*– If start of list set this element as the start –*/
if (!recFirst)
{ recFirst = recElement;
recFirst->prev = NULL;
}else
{ recFinish->next = recElement;
recElement->prev = recFinish;
}
/*– Keep the end of list pointers up-to-date –*/
recFinish = recElement;
If the memory was not successfully allocated for the new list element then an error is displayed on screen informing the user of this fact. The program is then exited, and a value is returned to the operating system indicating that the program failed. The user is then returned to the operating system. The memory used to store the index list is also freed before the program exits.
}else
{ printf(“Error Creating Linked List – Memory allocation error!\n”);
/*– Free the memory used by the index list –*/
free(idxList);
exit(EXIT_FAILURE);
}
When the complete index list has been iterated through, and the linked list has been created the memory user by the index list is now freed and we return back to the calling function.
Add Stock Record
This function firstly searches the linked list for the given part number. If the part number was found within the linked list then an error is reported to the printer and this function returns to the main processing loop.
If the given part number was not found in the linked list then an attempt at allocating memory for the new list element is made. If the memory allocation for the new list element was unsuccessful then an error is displayed on screen informing the user of this fact. The program is then exited, and a value is returned to the operating system indicating that the program failed. The user is then returned to the operating system.
If the memory allocation was successful then we store the given part number in the elements index record (key) and record number (where the record is on the master file) is set to a dummy value of 9999. This value will be updated to the correct value within the main function as the stock files are being recreated. We also store the contents of the given validate stock transaction into the relevant sections of the link lists stock record element.
/*– Populate index record –*/
strcpy(newRec->index.key,nRec->partNum);
newRec->index.rec_num = 9999; /*– Dummy value –*/
/*– Populate stock record –*/
strcpy(newRec->rec.key,nRec->partNum);
strcpy(newRec->rec.partDesc,nRec->partDesc);
newRec->rec.suppCode=atoi(nRec->supCode);
newRec->rec.freeStk=atol(nRec->freeStk);
newRec->rec.minStk=atoi(nRec->minStk);
newRec->rec.price=atof(nRec->sellPrice) / 100;
We also get the current system date and convert this to the correct format required by the stock records last modified date.
/*– Get current date for stock records last movement date –*/
time(&secsNow);
dateNow = localtime(&secsNow);
if ((year = dateNow->tm_year) > 99)
year -= 100; /*– Ensure we have a two digit year format –*/
sprintf(newRec->rec.moveDate,”%02d%02d%02d”, year,
dateNow->tm_mon + 1,dateNow->tm_mday);
The six diagrams below show how the element pointers are manipulated in order to achieve the addition. The element to be added could be the first element of the linked list, in the middle of the linked list or be the last element of the linked list. The diagrams below show how the pointers would be updated for all of these situations. When the pointers have been updated the function returns to the main processing loop.
/*– Insert records in correct positions of the list –*/
if (recList == NULL) /*– Start of List –*/
{
newRec->next = recFirst;
newRec->prev = NULL;
recFirst->prev = newRec;
recFirst = newRec;
}
}else if (recList == recFinish) /*– End of list –*/
{
recFinish->next = newRec;
newRec->prev = recFinish;
newRec->next = NULL;
recFinish = newRec;
}
}else /*– Middle of list –*/
{
newRec->next = recList->next;
newRec->prev = recList;
recList->next = newRec;
newRec->next->prev = newRec;
}
Delete Stock Record
This function firstly searches the linked list for the given part number. If the part number was not found within the linked list then an error is reported to the printer and this function returns to the main processing loop.
If the given part number was found in the linked list but the part number has a non zero value free stock value then an error is reported to the printer and this function returns to the main processing loop.
If the given part number was found in the list and it has a zero value for it’s free stock then the record can be deleted. The record is removed from the link list by manipulating the prev and next pointers of the two elements previous and next to this element.
The six diagrams below show how the element pointers are manipulated in order to achieve the deletion. The element to be deleted could be the first element of the linked list, in the middle of the linked list or be the last element of the linked list. The diagrams below show how the pointers would be updated for all of these situations. When the pointers have been updated the memory used by the deleted element is now freed.
if (recList == recFirst) /*– Remove first element –*/
{
recFirst = recList->next;
recFirst->prev = NULL;
}else if (recList == recFinish) /*– Remove last element –*/
{
recFinish = recList->prev;
recFinish->next = NULL;
}else /*– Remove middle element –*/
{
recList->prev->next = recList->next;
recList->next->prev = recList->prev;
}
/*– Free deleted elements memory –*/
free(zapRec);
Amend Stock Record
This function firstly searches the linked list for the given part number. If the part number was not found within the linked list then an error is reported to the printer and this function returns to the main processing loop.
If the given part number was found in the linked list then we update the elements various data sections with the data that was provided within the validated stock transaction.
/*– Make the amendments to the given stock record –*/
strcpy(recList->rec.partDesc,aRec->partDesc);
recList->rec.suppCode=atoi(aRec->supCode);
recList->rec.freeStk=atol(aRec->freeStk);
recList->rec.minStk=atoi(aRec->minStk);
strcpy(recList->rec.moveDate,aRec->lastMove);
recList->rec.price=atof(aRec->sellPrice) / 100;
When the main processing loop has ended (all the transactions have been actioned). A final printout page is produced this page gives a breakdown of the error codes and their meanings.
We then go back to the start of the two stock files. The two stock files are now about to be updated (recreated), with the information held within the linked list. We iterate through the linked list writing the relevant information to the two files. The record number element of the index file is also update (replacing the 9999 from added records).
for(recList=recFirst, count=0; recList; count++)
{ /*– Update index numbers while looping through the list –*/
recList->index.rec_num = count;
/*– Index file creation –*/
fwrite(&recList->index, IDX_SIZE, 1, stkidx);
/*– Master file creation –*/
fwrite(&recList->rec, STK_SIZE, 1, master);
/*– Goto the next element in our list –*/
recList = recList->next;
}
When the two files have been updated, all opened files are closed and the user is returned back to the operating system.
Design decisions:
There were various design decisions made during the course of this program. The decision to use a double linked list was made as I thought with the relatively small size of the master file that using a linked list would be much faster than randomly reading and writing from the two files. It also served to further my experience of pointer manipulation. If the master file was larger then this method would be of no use due to the shear volume of memory that would be required. It is not expected that Zenith Paints will in fact rapidly extend their product range to such an extent that linked lists would cause a problem.
The decision to use a custom function to copy the relevant parts of the read in text line, rather than a standard c function was because I felt I needed more flexibility in the string copying. As this function is used in five of the seven programs it was a justified decision in my opinion.
The decision to print the breakdown of the error codes on a new page was made as it was felt it would be much easier to use it as a lookup table without keep trying to flip pages over because the codes were spread over multiple pages.
To start with the file names were hard coded within the program; if any of them were not found then the program would exit. This was changed quite late in the program development, as it would be more practical to allow the user to override the name of any file at the command line (as a command line argument). This way the file names are not hard coded into the program, which makes the program much more flexible. If however a file name is not provided on the command line then the default file names are used.
The way the program is structured should make it quite easy to modify, such as the time when the customer decides they require further action to be performed on the master files. These types of changes should be quite straight forward to implement as the program is divided into very distinct individual functions.
Limitations:
There are various limitations to this program. The first being that if Zenith Paints do rapidly extend their product range then the program will need modifications to move away from linked lists and update the files directly (a slightly slower method).
Another limitation that has now been removed; this was after thinking about the programs limitations. This was the names of the various required files being hard coded (may not be overridden) within the program. This would restrict the user to always using having to use these file names, and modifying the source code if they were to change. Now the user is able to override the default file names, by entering additional parameters at the command line when the program is executed. These parameters are detailed above and knowledge of them may even be restricted to certain persons.
All of the programs are limited to using a Hewlett Packard printer as all printouts use Hewlett Packard specific escape codes to format the output. If the printer is changed for a different make then this program will need modification and recompilation. The modifications are all contained within the printer.h header file and so should not cause any problems if this task is required.




