Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Objectives: - Structures and Structure Pointers - Linked Lists - Function Pointe

ID: 3889054 • Letter: O

Question

Objectives:

- Structures and Structure Pointers

- Linked Lists

- Function Pointers

- Passing Command Line Arguments to main()

- Header files and Make files

LAB DESCRIPTION

PART 1. CLASSIC BOOKSTORE INVENTORY SYSTEM:

Mandatory file names: lab3main.c GetDataAndBuildList.c

createNodeAndGetData.c createNodeAndGetStdinData.c insert.c
delete.c calculateAverageProfit.c calculateTotalProfit.c calculateCapitalInvestment.c calculateTotalBooksSold.c calculateTotalRevenue.c calculateWholeSaleCost.c freeAllNodes.c getUserOption.c printInStock.c printOutOfStock.c writeInventoryToDisk.c

You will write a program to store and process data in order to manage inventory and to keep business records for a bookstore which deals in (mostly) classic book titles.

The program will execute in the following format: lab3 filename1 filename2

where lab3 is the name of the executable
filename1 is the data file that contains the inventory to input
filename2 is a data file that your program will create with the output inventory

This means that you will have to read two parameters from the command line.

You will be supplied with a “test” inventory file called book_inventory. All of the data for the book titles to be stored in the inventory system will be in this file. Once the program has read in the book data, a user will be able to select options related to what to do with the data. You do not know the number of different book titles which will be in the file, so your code must be able to deal with an indeterminate number of different books (up to the limits of memory).

First, your program should open the file specified by the filename1 parameter and read in the initial inventory. There will be options for adding or deleting books provided to the user later by the program after the initial data is read in and used to build the linked list. The data will be entered in the following format, with each item of data listed entered on a different line:

- Book title (We will assume the title may possibly contain white spaces; check the initial book_inventory file on Piazza)

- Author's name (We will assume there will be a single author line for each book, with the

name(s) possibly containing white spaces; see the sample data posted on Piazza)

- Bookstocknumber(1-10000)

- Wholesalepriceofthebookindollars(afloatingpointvalue)

- Retailpriceofthebookindollars(afloatingpointvalue)

- Wholesalequantitypurchasedbybookstore(awholenumbergreaterthanorequaltozero)

- Retailquantitypurchasedbycustomers(awholenumbergreaterthanorequaltozero)

Data about each book will not be separated from the data for the following book; that is, the first line of input for the next book will immediately follow the book which precedes it on the following line. The end of the input for the books will be indicated when an fread() function returns EOF. There will be data about at least one book in the file. The linked list which you program builds for the books should store them in order of increasing book stock number, but the books will not necessarily appear in the input in this order. After reading the input data about the books, and constructing the linked list, your program should

a) Tell the user that the inventory of books has been read in from the file and how many book titles were read in.

b) Prompt the user to select from the following options for processing of the data.(getUserOptions.c) Use an Array of Function Pointers to call the User’s Choice of Function for options 1 through 8.

Determineandprinttotalrevenue(fromallbooks):Thisisthesumof(retailprice*retail quantity) for all books (calculateTotalRevenue.c);

Determineandprinttotalwholesalecost(forallbooks):Thisisthesumof(wholesaleprice* wholesale quantity) for all books (calculateWholeSaleCost.c);

Determineandprintthecurrentinvestmentinbooks:Thisisthesumof(wholesaleprice* (wholesale quantity – retail quantity)) (calculateCapitalInvestment.c);

Determineandprinttotalprofit(forallbooks):Thisistotalrevenue(#1)minustotalwholesale cost(#2) plus cost of current inventory (#3) (calculateTotalProfit.c);

Determineandprinttotalnumberofsales(totalnumberofbookssold):Thisisthesumofthe retail quantities for all books(calculateTotalBooksSold.c);

Determineandprintaverageprofitpersale:Thisistotalprofit(#4)dividedbytotalnumberofsales (#5)( calculateAverageProfit.c);

Printbooksinstock:Thisfunctionshouldprinteachbookon“Booklist”where(Wholesale quantity – Retail quantity >0). The output would be a “Title Line” that says something like “Books in Stock” then on subsequent lines, the number of books currently in stock (Wholesale – Retail) followed by a tab character then the title of the book followed by the tab character then the author of the book.( printInStock.c);

Printbooksoutofstock:Thisfunctionshouldprinteachbookon“Booklist”where(Wholesale quantity – Retail quantity == 0). The output would be a “Title Line” that says something like

“Books out of Stock” then on subsequent lines, the title of the book followed by a tab character then the author of the book. (printOutOfStock.c);

9. Addasinglebook(prompttheusertoenterthebookdataforasinglebook,intheformat given above from the keyboard (createNodeAndGetStdinData.c/insert()));

10.Delete book (prompt the user to enter the book stock number: if the book is not found in the linked list, print a message indicating an error, or if the list is empty, print an error indicatingthat) (delete.c);

11.Exit the program. (This option would call writeInventoryToDisk.c and freeAllNodes.c)

The user will enter a choice of one of these eleven options by entering 1 11 immediately followed by enter (new line). You can assume that all user input will be correct, except that the user may inadvertently attempt to delete a book which has not been added to the list of books. You should write a separate function to do the necessary computations and print output for each of these options. Some of these functions may call some of the other functions. The goal is to make main() as small and succinct as possible, while creating functions that make it as easy as possible to monitor, modify and execute the code. You should also write a separate function to read the data for a single book from stdin (rather than from a file on disk (i.e. createNodeAndGetStdinData.c)) into a node after allocating storage for the node.

Be sure, for each of the functions above, which is required to print output, to print a label which describes the output, along with the appropriate output on the same line (except for the function to print the books in stock list or books out of stock list, where each title/author pair should be printed on separate line).

GUIDANCE
Declare the struct Node shown below at file scope:

struct Data {
char title[50];

char author[50];
int StockNumber;
float WholesalePrice; float RetailPrice;
int WholesaleQuantity; int RetailQuantity;

};

typedef struct Node { struct Data book;

struct Node *next; } Node;

You should write and debug the following functions first:

main
the program to read in the inventory data from disk (getDataAndBuildList.c which would

call createNodeAndGetData.c)
the functions to print the nodes in the list (i.e. in-stock list and out-of-stock list); find and

carefully follow the sketch of the algorithm in the class slides on linked lists.
a function insert (insert.c), to add a node to the list; find and carefully follow the sketch of

the algorithm in the classslides.

DONOTWRITETHECODEFOROTHERFUNCTIONSUNTILTHEFUNCTIONS ABOVE ARE WORKING!!!

If the output for either print statement is empty (e.g. the print out-of-stock list has no books to print, you should print a statement to that effect. Similarly, if you are asked to delete a book with a stock number that is not found in the list, print a statement that says that.

Until you are ready to write the code for the other functions, you can write “stub” functions with empty parameters and empty blocks for the other functions in the program; if these functions are called in the program, they will do nothing, so this will create no problems for testing and debugging.

After the functions above are working, write a function called delete (delete.c), to delete a single node from the list; find and carefully follow the sketch of the algorithm in the class slides.

Then, write the remaining functions (they are similar to the function to print in or out of stock books in the list.)

CONSTRAINTS

The book data must be stored in a singly-linked list, where each node in the list contains the data for one booktitle.

You are not permitted to declare any variables at file scope; you should, however, declare the Node type used to store the data for the linked list at file scope (but no variables can be declared as part of this declaration). See the description of the declaration of the Node type above.

All floating point data will be entered with two digits of precision and should also be output with two digits ofprecision and using monetary symbols and commas.

You must allocate the storage for each book node structure dynamically.

The book name and author's name should each be stored in strings declared to be of type

array of char of size 50. (See the description of the declaration of the Node type above.)

If a book is deleted, you should call free() to free the storage for the node for the book.

You must write the inventory list to disk (filename2) and then free the space for the individual nodes that still contain data before the program terminates when the user selects option 11.

See the text file posted on Piazza with sample input for Part 1.

You must create a Makefile that creates your executable lab3. Insure that file contains all appropriate dependencies.

Explanation / Answer

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Data {
   char title[40];
   char author[40];
   int stockNumber;
   float wholesalePrice;
   float retailPrice;
   int wholesaleQuantity;
   int retailQuantity;
};
typedef struct Node {
   struct Data book;
   struct Node *next;
} Node;

void getDataAndBuildList(Node **headAdr, int *size);

void displayOptions(void);

Node *createNodeAndGetData();

void insert(Node **headAdr, Node *newNodePtr);

void delete(Node **headAdr, int stockNum);

void getUserOption(Node **head, int *size);

double calculateTotalRevenue(Node **head);

double calculateTotalWholesaleCost(Node **head);

int calculateTotalBooksSold(Node **head);

double calculateAverageProfit(Node **head);

double calculateTotalProfit(Node **head);

void printInStock(Node **head);

void printOutOfStock(Node **head);

void freeAllNodes(Node **headAdr);

void callFunction(Node **head, int *size, int userSelection);

int main(void) {
   /* The number of books in the list */
   int *sizeOfList, size;

   /* Pointer to the memory address where the pointer to list
    * head is stored
    */
   Node **head;

   /* Pointer to the current head of the list */
   Node *listHead;

   /* Initialize pointer to the size of the list */
   size = 0;
   sizeOfList = &size;

   /* Try to read in list data to store in the form of Nodes
    * (see above for the definition of a Node type).
    * If a node is read in, continue to read in Nodes for the
    * list until the end has been reached.
    */
   listHead = createNodeAndGetData();
   *head = listHead;
   if (listHead != NULL) {
      getDataAndBuildList(head, sizeOfList);
   }

   /* Continuously ask the user to execute list operations
    * until they elect to quit.
    */
   getUserOption(head, sizeOfList);

   /* The user has elected to quit. Print a closing message
    * and free any remaining allocated memory.
    */
   printf(" Session Terminated. ");
   freeAllNodes(head);
   return(0);
}
/* Attempt to create a node for the given formatted data */
Node *createNodeAndGetData() {
   Node *newNodePtr;
   newNodePtr = malloc (sizeof(Node));
   if (newNodePtr == NULL) {
      printf("Error: memory could not be allocated for enough nodes. ");
      printf("Terminating program! ");
      exit (0);
   }
   else {
      scanf("%[^ ]s", &newNodePtr->book.title);
      if (strcmp(newNodePtr->book.title, "END_DATA") == 0) {
         /* free Node if end of book data detected */
         free(newNodePtr);
         return NULL;
      }
      else {
         /* consume newline before author string */
         getchar();
         scanf("%[^ ]s", &newNodePtr->book.author);
         scanf("%i", &newNodePtr->book.stockNumber);
         scanf("%f", &newNodePtr->book.wholesalePrice);
         scanf("%f", &newNodePtr->book.retailPrice);
         scanf("%i", &newNodePtr->book.wholesaleQuantity);
         scanf("%i", &newNodePtr->book.retailQuantity);
         /* consume newline before next title string */
         getchar();
      }
      return (newNodePtr);
   }
}
/* Read in nodes and insert them into the list until the end
* of the input has been reached.
*/
void getDataAndBuildList(Node **headAdr, int *size) {
   /* Pointers to the current position in the list as well
    * as the new node that was created.
    */
   Node *traversePtr, *newNode;

   /* Read in and insert Nodes into the list until the end
    * of the input. Increase the size each time a new
    * node is added.
    */
   newNode = createNodeAndGetData();
   while (newNode != NULL) {
      insert(headAdr, newNode);
      (*size)++;
      newNode = createNodeAndGetData();
   }
}

/* Display the list operations the user can choose to perform */
void displayOptions(void) {
   printf("Select one of the following options: ");
   printf("[1] Calculate total revenue ");
   printf("[2] Calculate total wholesale cost ");
   printf("[3] Calculate total profit ");
   printf("[4] Calculate total book sales numbers ");
   printf("[5] Calculate average profit per sale ");
   printf("[6] Print list of books in stock ");
   printf("[7] Print list of books out of stock ");
   printf("[8] Add a book to the list ");
   printf("[9] Delete a book from the list ");
   printf("[10] Exit the program ");
   printf("Option: ");
}
/* Based on the user input, execute the appropriate list op */
void callFunction(Node **head, int *size, int userSelection) {

   /* Store the number too search for when deleting and the
    * total number of books sold, respectively
    */
   int deleteStockNum, soldTotal;

   /* Names correspond with their values directly here */
   double averageProfit, totalProfit, totalCost, totalRev;

   /* Pointer to a new node if the user elects too add a
    * book to the list
    */
   Node *newNodePtr;

   /* Based on userSelection, execute the appropriate list op */
   switch(userSelection) {
      case 1:
         /* Total Revenue */
         totalRev = calculateTotalRevenue(head);
         printf(" The total revenue is: $%.2f ", totalRev);
         break;
      case 2:
         /* Total Wholesale cost */
         totalCost = calculateTotalWholesaleCost(head);
         printf(" The total wholesale cost is: $%.2f ", totalCost);
         break;
      case 3:
         /* Total profit */
         totalProfit = calculateTotalProfit(head);
         printf(" The total profit is: $%.2f ", totalProfit);
         break;
      case 4:
         /* Total book numbers */
         soldTotal = calculateTotalBooksSold(head);
         printf(" The total number of books sold is: %d ", soldTotal);
         break;
      case 5:
         /* Average profit */
         averageProfit = calculateAverageProfit(head);
         printf(" The average book profit is: $%.2f ", averageProfit);
         break;
      case 6:
         /* Print in stock books if any exist */
         if ((*size) > 0) {
            printInStock(head);
         } else {
            printf(" No books in stock. ");
         }
         break;
      case 7:
         /* Print out of stock books if any exist */
         if ((*size) > 0) {
            printOutOfStock(head);
         } else {
            printf(" No books out of stock. ");
         }
         break;
      case 8:
         /* Add book and increment size */
         getchar();
         newNodePtr = createNodeAndGetData();
         insert(head, newNodePtr);
         (*size)++;
         break;
      case 9:
         /* Delete book and decrement size */
         deleteStockNum;
         printf("Enter the stock number of the book you want to delete: ");
         scanf("%d", &deleteStockNum);
         delete(head, deleteStockNum);
         (*size)--;
         break;
      case 10:
         /* Continue to the top of the loop to quit */
         break;
      default:
         printf(" Invalid option. Try again. ");
         break;
   }
}

/* Continuously execute row ops until the user quits */
void getUserOption(Node **head, int *size) {
   /* The operation number selected by the user */
   int userSelection;

   /* Display the user's options */
   displayOptions();

   /* Read in the user's choice and execute that op.
    * Keep doing so until they elect to quit.
    */
   scanf("%d", &userSelection);
   while (userSelection != 10) {
      callFunction(head, size, userSelection);
      displayOptions();
      scanf("%d", &userSelection);
   }
}

double calculateTotalRevenue(Node **head){
   /* total revenue, revenue for the current book, and
    * the book's retail price, respectively.
    */
   double revenue = 0.0, currentRevenue, retail;

   /* Retail quantity of the current book */
   int quantity = 0;

   /* Pointer to the current node in the list */
   Node *current = *(head);

   /* Add up the revenue earned by each book in the
    * list (retail price * quantity).
    */
   while(current != NULL){
      retail = current->book.retailPrice;
      quantity = current->book.retailQuantity;
      currentRevenue = retail * quantity;
      revenue += currentRevenue;
      current = current->next;
   }
   current = *(head);
   return revenue;
}

double calculateTotalWholesaleCost(Node **head){
   /* total wholesale cost, current book wholesale cost,
    * and the wholesale price of the current book
    */
   double wholesale = 0.0, currentWholesale, cost;

   /* Wholesale quantity of the current book */
   int quantity = 0;

   /* Pointer to the current node in the list */
   Node *current = *(head);

   /* For each book in the list, calculate the wholesale cost
    * and total them. (Wholesale cost = wholesale price * quantity)
    */
   while(current != NULL){
      cost = current->book.wholesalePrice;
      quantity = current->book.wholesaleQuantity;
      currentWholesale = cost * quantity;
      wholesale += currentWholesale;
      current = current->next;
   }
   return wholesale;
}

int calculateTotalBooksSold(Node **head){

   /* total number of books sold */
   int sold = 0;

   /* Pointer to the current node in the list */
   Node *current = *(head);

   /* add up the retail quantity of each book
    * in the list.
    */
   while (current != NULL) {
      sold += current->book.retailQuantity;
      current = current->next;
   }
   return sold;
}

double calculateTotalProfit(Node **head) {
   double profit = 0.0, revenue, cost;
   revenue = calculateTotalRevenue(head);
   cost = calculateTotalWholesaleCost(head);
   profit = revenue - cost;
   return profit;
}

double calculateAverageProfit(Node **head){

   /* Total retail quantity */
   int totalSold = calculateTotalBooksSold(head);

   /* Total profit earned from the books in the list */
   double totalProfit = calculateTotalRevenue(head);
   totalProfit -= calculateTotalWholesaleCost(head);

   /* Return the profit earned per book sold (average profit) */
   if (totalSold > 0) {
      return totalProfit / totalSold;
   } else {
      return 0.0;
   }
}

void printInStock(Node **head){

   /* Current stock and count of the number of in
    * stock books, respectively
    */
   int currentStock, counter;

   /* Pointer to the current node in the list */
   Node *current = *(head);

   /* If there are any books in stock, print a list of them */
   counter = 0;
   while (current != NULL) {
      currentStock = current->book.wholesaleQuantity;
      currentStock -= current->book.retailQuantity;
      if (currentStock > 0) {
         counter++;
         if (counter == 1) {
            printf(" Books in Stock: ");
         }
         printf("%d %s ", currentStock, current->book.title);
      }
      current = current->next;
   }
   if (counter == 0) {
      printf(" No books in stock. ");
   }
   printf(" ");
}

void printOutOfStock(Node **head){

   /* Current stock and count of the number of in
    * stock books, respectively
    */
   int currentStock, counter;

   /* Pointer to the current node in the list */
   Node *current = *(head);

   /* If there are any books out of stock, print a list of them */
   counter = 0;
   while (current != NULL) {
      currentStock = current->book.wholesaleQuantity;
      currentStock -= current->book.retailQuantity;
      if (currentStock == 0) {
         counter++;
         if (counter == 1) {
            printf(" Books Out of Stock: ");
         }
         printf("%s ", current->book.title);

      }
      current = current->next;
   }
   if (counter == 0) {
      printf(" No books out of stock. ");
   }
   printf(" ");
}

/* Clear all remaining allocated memory at the end of the program */
void freeAllNodes(Node **headAdr){

   /* Pointer to the current node in the list */
   Node *currentPtr;

   /* Delete each node from the head to the tail */
   while (*(headAdr) != NULL) {
      currentPtr = *(headAdr);
      *(headAdr) = (*headAdr)->next;
      free(currentPtr);
   }
}

/* Inserts a new book into the list */
void insert(Node **headAdr, Node *newNodePtr) {

   /* Pointer to the current node in the list */
   Node *traversePtr;

   /* If the new node comes before the head of the list in stock number */
   if (*(headAdr) == NULL
      || newNodePtr->book.stockNumber < (*headAdr)->book.stockNumber) {
      newNodePtr->next = (*headAdr);
      *headAdr = newNodePtr;
   } else {
      /* Find where the new node should be inserted and insert it */
      traversePtr = *(headAdr);
      while (traversePtr->next != NULL
            && newNodePtr->book.stockNumber > traversePtr->next->book.stockNumber) {
         traversePtr = traversePtr->next;
      }

      /* Adjust the next pointers so the list is complete */
      newNodePtr->next = traversePtr->next;
      traversePtr->next = newNodePtr;
   }
}

/* Deletes a book from the list */
void delete(Node **headAdr, int stockNum){

   /* Pointer to the current node and the node to be deleted,
    * respectively.
    */
   Node *traversePtr, *nodeToDelete;

   /* Initialize traversePtr to the head of the list */
   traversePtr = *(headAdr);

   /* If the list is empty, display an error message and return to the
    * input prompt.
    */
   if(traversePtr == NULL){
      printf(" Error: cannot delete from an empty list. ");
   } /* If the list head must be deleted, adjust the list head pointer
      * and delete the node. */
   else if (traversePtr->book.stockNumber == stockNum) {
      *(headAdr) = traversePtr->next;
      free(traversePtr);
   }
   else { /* Otherwise, find the node in the list and delete it */
      while (traversePtr->next != NULL
            && traversePtr->next->book.stockNumber != stockNum) {
         traversePtr = traversePtr->next;
      }
      /* If the node doesn't exist, display an error message. */
      if (traversePtr->next == NULL) {
         printf(" Error: That book doesn't exist in the stock list. ");
      } else {
         /* Adjust pointers to keep the list complete and
          * connected.
          */
         nodeToDelete = traversePtr->next;
         traversePtr->next = traversePtr->next->next;
         free(nodeToDelete);
      }
   }
}