Stock Control System
Loading...
Searching...
No Matches
StockControlSystem.c
Go to the documentation of this file.
1/*
2 * Copyright (c) All Rights Reserved
3 * 2025 Oliver Dixon <od641@york.ac.uk>
4 */
5
14#include <errno.h>
15#include <limits.h>
16#include <stdbool.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "Pool.h"
22#include "Product.h"
23
36
42{
43 enum MenuOption option = MENU_INVALID;
44
45 while (option == MENU_INVALID) {
46 fputs("1. Manage Stock Levels\n"
47 "2. Generate Reports\n"
48 "3. Quit the Program\n\n"
49 "Please enter your selection: ",
50 stdout);
51
52 fflush(stdout);
53
54 switch (getchar()) {
55 case '1':
56 option = MENU_MANAGE_STOCK;
57 break;
58 case '2':
59 option = MENU_GENERATE_REPORT;
60 break;
61 case '3':
62 option = MENU_QUIT;
63 break;
64 default:
65 fputs("Invalid option.\n\n", stderr);
66 }
67
68 // Flush remaining characters from the input stream.
69 char ch;
70 while ((ch = getchar()) != '\n' && ch != EOF)
71 ;
72 }
73
74 return option;
75}
76
84static size_t get_input_line(char *const buffer, const size_t buffer_size, FILE *const stream)
85{
86 errno = 0;
87 if (!fgets(buffer, buffer_size - 1, stream))
88 return 0;
89
90 if (errno != 0) {
91 fputs("Could not get line of input: ", stderr);
92 fputs(strerror(errno), stderr);
93 return 0;
94 }
95
96 const size_t input_length = strlen(buffer);
97 if (input_length < 2)
98 return 0;
99
100 if (buffer[input_length - 1] == '\n')
101 buffer[input_length - 1] = '\0';
102
103 if (buffer[input_length] != '\0') {
104 fputs("Could not get line of input: too long.\n", stderr);
105 return buffer_size;
106 }
107
108 return input_length;
109}
110
117static unsigned int convert_to_uint(const char *const buffer, bool *error)
118{
119 errno = 0;
120 char *end = NULL;
121 const unsigned long value = strtoul(buffer, &end, 10);
122
123 if (errno != 0 || value >= UINT_MAX || *end != '\0') {
124 *error = true;
125 return 0;
126 }
127
128 *error = false;
129 return (unsigned int) value;
130}
131
138static int convert_to_sint(const char *const buffer, bool *error)
139{
140 errno = 0;
141 char *end = NULL;
142 const long value = strtol(buffer, &end, 10);
143
144 if (errno != 0 || value <= INT_MIN || value >= INT_MAX || *end != '\0') {
145 *error = true;
146 return 0;
147 }
148
149 *error = false;
150 return (int) value;
151}
152
157static void manage_stock(const struct Pool *const pool)
158{
159 bool valid = false;
160 char buffer[MAX_NAME_LENGTH + 1];
161 struct Product *product;
162 int quantity_modifier;
163
164 while (!valid) {
165 fputs("Please Enter a Unique Product Number : ", stdout);
166 fflush(stdout);
167
168 unsigned int product_id;
169 bool error_flag = get_input_line(buffer, sizeof(buffer) / sizeof(buffer[0]), stdin) < 1;
170
171 if (!error_flag)
172 product_id = convert_to_uint(buffer, &error_flag);
173
174 if (error_flag) {
175 fputs("That is not a valid product ID.\n", stderr);
176 continue;
177 }
178
179 // ReSharper disable once CppLocalVariableMightNotBeInitialized
180 if ((product = pool_get_product_by_index(pool, product_id)) == NULL) {
181 fputs("A product with that number does not exist!\n", stderr);
182 continue;
183 }
184
185 valid = true;
186 }
187
188 valid = false;
189 while (!valid) {
190 fputs("Please Enter A Stock Level Adjustment Value : ", stdout);
191 fflush(stdout);
192
193 bool error_flag = get_input_line(buffer, sizeof(buffer) / sizeof(buffer[0]), stdin) < 1;
194 if (!error_flag)
195 quantity_modifier = convert_to_sint(buffer, &error_flag);
196
197 if (error_flag) {
198 fputs("That is not a valid quantity modifier.\n", stderr);
199 continue;
200 }
201
202 // ReSharper disable CppLocalVariableMightNotBeInitialized
203 if (!product_modify_quantity(product, quantity_modifier)) {
204 // ReSharper restore CppLocalVariableMightNotBeInitialized
205 fputs("There is insufficient stock to allow that.\n", stderr);
206 continue;
207 }
208
209 valid = true;
210 }
211}
212
217static void generate_reports(const struct Pool *const pool)
218{
219 char path_buffer[PATH_MAX + 1];
220
221 fputs("Enter a destination filename for the report : ", stdout);
222 fflush(stdout);
223
224 FILE *const destination = get_input_line(path_buffer, sizeof(path_buffer) / sizeof(path_buffer[0]), stdin)
225 ? fopen(path_buffer, "w+")
226 : stdout;
227
228 if (pool_serialise_report(pool, destination) < 0)
229 fprintf(stderr, "Error writing to report file: file error %d.\n", ferror(destination));
230
231 if (destination != stdout)
232 fclose(destination);
233}
234
241static bool load_inventory(FILE *const stock_file, struct Pool *const pool)
242{
243 char name_buffer[MAX_NAME_LENGTH + 1];
244 char quantity_buffer[MAX_NAME_LENGTH + 1];
245 unsigned int product_id = 0;
246
247 while (get_input_line(name_buffer, sizeof(name_buffer) / sizeof(name_buffer[0]), stock_file)) {
248 if (!get_input_line(quantity_buffer, sizeof(name_buffer) / sizeof(name_buffer[0]), stock_file)) {
249 fprintf(stderr, "Missing quantity for product \"%s\"\n", name_buffer);
250 break;
251 }
252
253 bool error_flag;
254 const unsigned int quantity = convert_to_uint(quantity_buffer, &error_flag);
255
256 if (error_flag) {
257 fprintf(stderr, "Quantity for product \"%s\" is invalid.\n", name_buffer);
258 return false;
259 }
260
261 struct Product *new_product = product_create(product_id++, name_buffer, quantity);
262 if (new_product == NULL) {
263 fprintf(stderr, "Could not create new product \"%s\": %s\n", name_buffer, strerror(errno));
264 return false;
265 }
266
267 if (!pool_insert_element(pool, new_product)) {
268 product_delete(&new_product);
269 fprintf(stderr, "Could not create new product \"%s\": %s\n", name_buffer, strerror(errno));
270 return false;
271 }
272 }
273
274 return true;
275}
276
282static FILE *open_stock_file(const char *const filename)
283{
284 errno = 0;
285 FILE *const fp = fopen(filename, "r+");
286
287 if (fp == NULL) {
288 if (errno == 0) {
289 fputs("could not open stock data file : ", stdout);
290 puts(filename);
291 } else {
292 fputs(strerror(errno), stdout);
293 fputs(" : ", stdout);
294 puts(filename);
295 }
296 } else {
297 fputs("using stock data file : ", stdout);
298 puts(filename);
299 putchar('\n');
300 }
301
302 return fp;
303}
304
310static void save_inventory(FILE *const datafile, const struct Pool *const pool)
311{
312 if (datafile == NULL || pool_serialise_stock_file(pool, datafile) < 0)
313 fprintf(stderr, "Error writing to stock data file: file error %d.\n", ferror(datafile));
314}
315
322int main(const int argc, const char **const argv)
323{
324 fputs("Stock Control Program Started - ", stdout);
325 FILE *stock_file = open_stock_file(argc > 1 ? argv[1] : "stock_data.txt");
326 if (stock_file == NULL)
327 return EXIT_FAILURE;
328
329 struct Pool *pool = pool_create(16, 2.0f);
330 if (!load_inventory(stock_file, pool)) {
331 pool_delete(&pool);
332 fclose(stock_file);
333 return EXIT_FAILURE;
334 }
335
336 bool continue_running = true;
337 while (continue_running) {
338 switch (get_menu_selection()) {
340 manage_stock(pool);
341 break;
343 generate_reports(pool);
344 break;
345 default:
346 save_inventory(freopen(NULL, "w", stock_file), pool);
347 continue_running = false;
348 break;
349 }
350 }
351
352 pool_delete(&pool);
353 fclose(stock_file);
354 return EXIT_SUCCESS;
355}
int pool_serialise_report(const struct Pool *const this, FILE *const buffer)
Definition Pool.c:102
bool pool_insert_element(struct Pool *const this, struct Product *const product)
Definition Pool.c:78
struct Product * pool_get_product_by_index(const struct Pool *const this, const unsigned int index)
Definition Pool.c:140
void pool_delete(struct Pool **const this)
Definition Pool.c:67
struct Pool * pool_create(const unsigned int initial_capacity, const float growth_factor)
Definition Pool.c:46
int pool_serialise_stock_file(const struct Pool *const this, FILE *const buffer)
Definition Pool.c:128
Pool class specification.
struct Product * product_create(const unsigned int id, const char *const name, const unsigned int quantity)
Definition Product.c:28
void product_delete(struct Product **this)
Definition Product.c:53
bool product_modify_quantity(struct Product *this, const int modifier)
Definition Product.c:69
Product class specification.
#define MAX_NAME_LENGTH
Maximum number of bytes for a Product name, excluding the NULL-terminator.
Definition Product.h:26
static void save_inventory(FILE *const datafile, const struct Pool *const pool)
Save the inventory in the stock file format to the given file buffer.
static FILE * open_stock_file(const char *const filename)
Open the stock file with the given file name for reading, reporting errors as encountered.
static void manage_stock(const struct Pool *const pool)
Interactively queries the user for a Product ID and an amount by which to modify the stock level.
MenuOption
Represents all menu options on the interactive menu.
@ MENU_QUIT
Quit the Program option.
@ MENU_MANAGE_STOCK
Manage Stock option.
@ MENU_GENERATE_REPORT
Generate Report option.
@ MENU_INVALID
Placeholder invalid menu state.
static void generate_reports(const struct Pool *const pool)
Interactively query the user for an output file, and write a report to the chosen destination.
static enum MenuOption get_menu_selection()
Presents the user with a menu and interactively retrieves the selection.
static bool load_inventory(FILE *const stock_file, struct Pool *const pool)
Parse the inventory from the given stock file into the given Pool.
static unsigned int convert_to_uint(const char *const buffer, bool *error)
Converts the string in the given buffer to an unsigned integer.
int main(const int argc, const char **const argv)
The application entry point for interactive control of the Stock Control System.
static size_t get_input_line(char *const buffer, const size_t buffer_size, FILE *const stream)
Gets a single line of input from the given input and writes to the given buffer.
static int convert_to_sint(const char *const buffer, bool *error)
Converts the string in the given buffer to a signed integer.
An ordered expandable collection of dynamically allocated Product records.
Definition Pool.c:25
The Product represents a single product with a unique numerical identifier, human-readable name,...
Definition Product.c:22
unsigned int quantity
Non-negative number of Product items in stock.
Definition Product.c:24