/* * vendorInfo.c * * SEN-102 Intro to Programming - Pre-Assessment * Program that simulates a market containing vendors. * * Created by Win (Thanawin Pattanaphol), 28th August 2024 */ #include #include #include #include #define MAX_VENDORS 20 #define MAX_FILENAME_LENGTH 4096 /* STRUCTURE DECLARATION */ typedef struct vendor { char vendor_name[20]; char merchandise[15]; int inventory_count; int item_price_baht; int revenue; int duplicate; } vendor; // Return type for search function typedef struct search_r { int status; int item_id; } search_r; // Return type for purchase function typedef struct purchase_r { int status; char vendor_name[20]; int price_owe_baht; } purchase_r; // The global vendors array vendor vendors[MAX_VENDORS]; /* END OF STRUCTURE DECLARATION*/ /* FUNCTION DECLARATION */ int getstr(char str[], size_t n); char *strnlower(char *s, size_t n); search_r search ( char query[], vendor vendors[MAX_VENDORS], int total_vendors ); purchase_r purchase ( int item_id, int amount, vendor vendors[MAX_VENDORS] ); int check_vendors(vendor vendors[MAX_VENDORS], int total_vendors); /* END OF FUNCTION DECLARATION */ int main() { char file_name[MAX_FILENAME_LENGTH]; printf("Enter name of input file: "); getstr(file_name, MAX_FILENAME_LENGTH); FILE *fp; if ((fp = fopen(file_name, "r")) == NULL) { perror("Error opening file"); return 1; } // Calculate the file size fseek(fp, 0, SEEK_END); unsigned long file_size = ftell(fp); fseek(fp, 0, SEEK_SET); // Dynamically allocate buffer for file content char *file_buffer = (char *) malloc(file_size); int index = 0; // The index for the while loop below. // Reading the file with fgets and formatting them using strtok while ( (fgets(file_buffer, 50, fp) != NULL) && (index <= MAX_VENDORS - 1) ) { char *token = strtok(file_buffer, " "); strncpy(vendors[index].vendor_name, token, strlen(token) + 1); token = strtok(NULL, " "); strncpy(vendors[index].merchandise, token, strlen(token) + 1); token = strtok(NULL, " "); vendors[index].inventory_count = atoi(token); token = strtok(NULL, " "); vendors[index].item_price_baht = atoi(token); index++; } // Error handling if (ferror(fp)) { perror("An error occured while reading the file.\n"); fclose(fp); free(file_buffer); return 1; } int total_vendors = index; printf("\n>>> Read %d vendors from file %s\n", total_vendors, file_name); // Print out each vendor's info - excluding duplicates. for (int i = 0; i <= (total_vendors - 1); i++) { /* This is similar to a linear search algorithm i = the current item j = the item next to i i is being compared to j: - if i == j then mark both i and j as duplicates - if not, move on to the next two items */ for (int j = i + 1; j < total_vendors; j++) { if ( (strcmp(vendors[i].merchandise, vendors[j].merchandise) ) == 0) { vendors[j].duplicate = 1; } if ((strcmp(vendors[i].vendor_name, vendors[j].vendor_name)) == 0) { vendors[j].duplicate = 1; } } if (vendors[i].duplicate == 0) { char vendor_info[200]; sprintf(vendor_info, "%s sells %s for %d each", vendors[i].vendor_name, vendors[i].merchandise, vendors[i].item_price_baht ); char vendor_stock[50]; sprintf(vendor_stock, "Inventory %d items", vendors[i].inventory_count ); printf("%-50s %s\n", vendor_info, vendor_stock); } else { printf("\e[31m%s\e[m\n", "Duplicate record detected!"); } } printf("\n"); while (1) { if(check_vendors(vendors, total_vendors) == 0) { break; }; char user_query[100]; printf("What do you want to buy? (DONE to end) "); getstr(user_query, 100); if ( (strcmp(user_query, "DONE") == 0) || (strcmp(user_query, "done") == 0) ) { break; } if (strcmp(user_query, "") == 0) { printf("Please enter an item name\n"); } else { int user_query_length = strlen(user_query); strnlower(user_query, user_query_length); struct search_r item = search(user_query, vendors, total_vendors); if(item.status == 0) { char input[10]; printf("How many do you want to buy? "); getstr(input, 10); if(atoi(input) > 0) // atoi returns 0 if character was sent { // atoi returns -x if -x was provided int item_amount = atoi(input); purchase_r response = purchase(item.item_id, item_amount, vendors); if (response.status == 0) { printf("You owe %s a total of %d baht.\n", response.vendor_name, response.price_owe_baht ); } else { printf("\e[31m%s -- %s %s\e[m\n", "Sorry", response.vendor_name, "does not have enough items available" ); } } else { printf("\e[31m%s\e[m\n", "You must enter a positive whole number" " for the number of items" ); } } else if (item.status == 2) { printf("\e[31m%s\e[m\n", "This item has duplicate records," " please choose another item" ); } else { printf("\e[31m%s %s\e[m\n", "No vendor in this market sells", user_query ); } } } printf("\n>>> End of day details\n"); // Print out remaining inventory for (int i = 0; i <= (total_vendors - 1); i++) { if (vendors[i].duplicate == 0) { char vendor_info[200]; sprintf(vendor_info, "%s made %d baht today", vendors[i].vendor_name, vendors[i].revenue ); char vendor_stock[50]; sprintf(vendor_stock, "Remaining inventory %d items", vendors[i].inventory_count ); printf("%-50s %s\n", vendor_info, vendor_stock); } } fclose(fp); free(file_buffer); return 0; }; /* getstr: Custom read input function Parameters: - str = string itself - n = max amount of characters that the function is able to read into buffer Reads one character at a time into buffer and adds a null terminator when finish reading Return values: - (int) i: length of string */ int getstr(char str[], size_t n) { int ch, i = 0; while ((ch = getchar()) != '\n') { if (i < (int)n) { str[i++] = ch; } } str[i] = '\0'; return i; }; /* strnlower: Convert string to lowercase Parameters: - s: String - n: Length of string Return values: - (char *): Lowercased version of the string. */ char *strnlower(char *s, size_t n) { for (int i = 0; i < (int) n; i++) { s[i] = tolower(s[i]); }; } /* Search function Parameters: - (string) query: search query - (vendor) vendors: the vendor table - (int) total_vendors: the amount of vendors Return values: (search_r) response: - status: 0 (Found record) + id of record - status: 1 (No results) - status: 2 (Found duplicate record) */ search_r search(char query[], vendor vendors[MAX_VENDORS], int total_vendors) { int i; for (i = 0; i < total_vendors; i++) { // First, check if duplicate if (strcmp(query, vendors[i].merchandise) == 0) { if (vendors[i].duplicate == 0) { search_r response; response.status = 0; response.item_id = i; return (response); break; } else { search_r response; response.status = 2; return (response); } } }; if (i == total_vendors) { search_r response; response.status = 1; response.item_id = 0; return (response); } }; /* purchase: Purchase function Parameters: - item_id: The item id that the user is purchasing - amount: How many items that the user is purchasing - vendors: vendor table Return values: (purchase_r) response: - status: 0 (Purchase successful) Also includes the vendor_name, price owe and revenue - status: 1 (Vendor does not have enough items) */ purchase_r purchase(int item_id, int amount, vendor vendors[MAX_VENDORS]) { if(amount <= vendors[item_id].inventory_count) { // Decrease vendor inventory vendors[item_id].inventory_count = vendors[item_id].inventory_count - amount; purchase_r response; response.status = 0; strcpy(response.vendor_name, vendors[item_id].vendor_name); response.price_owe_baht = vendors[item_id].item_price_baht * amount; vendors[item_id].revenue = vendors[item_id].item_price_baht * amount; return response; } else { purchase_r response; response.status = 1; strcpy(response.vendor_name, vendors[item_id].vendor_name); return response; } }; /* check_vendors: Custom function to check if all vendors are empty. Parameters: - vendors[MAX_VENDORS]: (vendor) All of the vendors. - total_vendors: (integer) Total vendors in market. Return values: - (int) 0: All vendors are empty. - (int) 1: There are still items left. */ int check_vendors(vendor vendors[MAX_VENDORS], int total_vendors) { int i; for (i = 0; i < total_vendors; i++) { // Condition: Vendors that are not empty // (only ones that are not duplicates) if (vendors[i].inventory_count != 0 && vendors[i].duplicate == 0) { return 1; } } if (i == total_vendors) { return 0; } }; ;