Stock Control System
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1#include <errno.h>
2#include <limits.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "Pool.h"
9#include "Product.h"
10
18
20{
21 enum MenuOption option = MENU_INVALID;
22
23 while (option == MENU_INVALID) {
24 fputs("1. Manage Stock Levels\n"
25 "2. Generate Reports\n"
26 "3. Quit the Program\n\n"
27 "Please enter your selection: ",
28 stdout);
29
30 fflush(stdout);
31
32 switch (getchar()) {
33 case '1':
34 option = MENU_MANAGE_STOCK;
35 break;
36 case '2':
37 option = MENU_GENERATE_REPORT;
38 break;
39 case '3':
40 option = MENU_QUIT;
41 break;
42 default:
43 fputs("Invalid option.\n\n", stderr);
44 }
45
46 // Flush remaining characters from the input stream.
47 char ch;
48 while ((ch = getchar()) != '\n' && ch != EOF)
49 ;
50 }
51
52 return option;
53}
54
55static size_t get_input_line(char *const buffer, const size_t buffer_size, FILE *const stream)
56{
57 errno = 0;
58 if (!fgets(buffer, buffer_size - 1, stream))
59 return 0;
60
61 if (errno != 0) {
62 fputs("Could not get line of input: ", stderr);
63 fputs(strerror(errno), stderr);
64 return 0;
65 }
66
67 const size_t input_length = strlen(buffer);
68 if (input_length < 2)
69 return 0;
70
71 if (buffer[input_length - 1] != '\n') {
72 fputs("Could not get line of input: too long.\n", stderr);
73 return buffer_size;
74 }
75
76 buffer[input_length - 1] = '\0';
77 return input_length;
78}
79
80static unsigned int convert_to_uint(const char *const buffer, bool *error)
81{
82 errno = 0;
83 char *end = NULL;
84 const unsigned long value = strtoul(buffer, &end, 10);
85
86 if (errno != 0 || value >= UINT_MAX || *end != '\0') {
87 *error = true;
88 return 0;
89 }
90
91 *error = false;
92 return (unsigned int) value;
93}
94
95static int convert_to_sint(const char *const buffer, bool *error)
96{
97 errno = 0;
98 char *end = NULL;
99 const long value = strtol(buffer, &end, 10);
100
101 if (errno != 0 || value <= INT_MIN || value >= INT_MAX || *end != '\0') {
102 *error = true;
103 return 0;
104 }
105
106 *error = false;
107 return (int) value;
108}
109
110static void manage_stock(const struct Pool *const pool)
111{
112 bool valid = false;
113 char buffer[MAX_NAME_LENGTH + 1];
114 struct Product *product;
115 int quantity_modifier;
116
117 while (!valid) {
118 fputs("Please Enter a Unique Product Number : ", stdout);
119 fflush(stdout);
120
121 unsigned int product_id;
122 bool error_flag = get_input_line(buffer, sizeof(buffer) / sizeof(buffer[0]), stdin) < 1;
123
124 if (!error_flag)
125 product_id = convert_to_uint(buffer, &error_flag);
126
127 if (error_flag) {
128 fputs("That is not a valid product ID.\n", stderr);
129 continue;
130 }
131
132 // ReSharper disable once CppLocalVariableMightNotBeInitialized
133 if ((product = pool_get_product_by_index(pool, product_id)) == NULL) {
134 fputs("A product with that number does not exist!\n", stderr);
135 continue;
136 }
137
138 valid = true;
139 }
140
141 valid = false;
142 while (!valid) {
143 fputs("Please Enter A Stock Level Adjustment Value : ", stdout);
144 fflush(stdout);
145
146 bool error_flag = get_input_line(buffer, sizeof(buffer) / sizeof(buffer[0]), stdin) < 1;
147 if (!error_flag)
148 quantity_modifier = convert_to_sint(buffer, &error_flag);
149
150 if (error_flag) {
151 fputs("That is not a valid quantity modifier.\n", stderr);
152 continue;
153 }
154
155 // ReSharper disable CppLocalVariableMightNotBeInitialized
156 if (!product_modify_quantity(product, quantity_modifier)) {
157 // ReSharper restore CppLocalVariableMightNotBeInitialized
158 fputs("There is insufficient stock to allow that.\n", stderr);
159 continue;
160 }
161
162 valid = true;
163 }
164}
165
166static void generate_reports(const struct Pool *const pool)
167{
168 char path_buffer[PATH_MAX + 1];
169
170 fputs("Enter a destination filename for the report : ", stdout);
171 fflush(stdout);
172
173 FILE *const destination = get_input_line(path_buffer, sizeof(path_buffer) / sizeof(path_buffer[0]), stdin)
174 ? fopen(path_buffer, "w+")
175 : stdout;
176
177 if (pool_serialise_report(pool, destination) < 0)
178 fprintf(stderr, "Error writing to report file: file error %d.\n", ferror(destination));
179
180 if (destination != stdout)
181 fclose(destination);
182}
183
184static int load_inventory(FILE *const stock_file, struct Pool *const pool)
185{
186 char name_buffer[MAX_NAME_LENGTH + 1];
187 char quantity_buffer[MAX_NAME_LENGTH + 1];
188 unsigned int product_id = 0;
189
190 while (get_input_line(name_buffer, sizeof(name_buffer) / sizeof(name_buffer[0]), stock_file)) {
191 if (!get_input_line(quantity_buffer, sizeof(name_buffer) / sizeof(name_buffer[0]), stock_file)) {
192 fprintf(stderr, "Missing quantity for product \"%s\"\n", name_buffer);
193 break;
194 }
195
196 bool error_flag;
197 const unsigned int quantity = convert_to_uint(quantity_buffer, &error_flag);
198
199 if (error_flag) {
200 fprintf(stderr, "Quantity for product \"%s\" is invalid.\n", name_buffer);
201 return -1;
202 }
203
204 struct Product *new_product = product_create(product_id++, name_buffer, quantity);
205 if (new_product == NULL) {
206 fprintf(stderr, "Could not create new product \"%s\": %s\n", name_buffer, strerror(errno));
207 return -1;
208 }
209
210 if (!pool_insert_element(pool, new_product)) {
211 product_delete(&new_product);
212 fprintf(stderr, "Could not create new product \"%s\": %s\n", name_buffer, strerror(errno));
213 return -1;
214 }
215 }
216
217 return 0;
218}
219
220static FILE *open_stock_file(const char *const filename)
221{
222 errno = 0;
223 FILE *const fp = fopen(filename, "r+");
224
225 if (fp == NULL) {
226 if (errno == 0) {
227 fputs("could not open stock data file : ", stdout);
228 puts(filename);
229 } else {
230 fputs(strerror(errno), stdout);
231 fputs(" : ", stdout);
232 puts(filename);
233 }
234 } else {
235 fputs("using stock data file : ", stdout);
236 puts(filename);
237 putchar('\n');
238 }
239
240 return fp;
241}
242
243static void save_inventory(FILE *const datafile, const struct Pool *const pool)
244{
245 if (pool_serialise_stock_file(pool, datafile) < 0)
246 fprintf(stderr, "Error writing to stock data file: file error %d.\n", ferror(datafile));
247}
248
249int main(const int argc, const char **const argv)
250{
251 fputs("Stock Control Program Started - ", stdout);
252 FILE *stock_file = open_stock_file(argc > 1 ? argv[1] : "stock_data.txt");
253 if (stock_file == NULL)
254 return EXIT_FAILURE;
255
256 struct Pool *pool = pool_create(16, 2.0f);
257 if (load_inventory(stock_file, pool) != 0) {
258 pool_delete(&pool);
259 fclose(stock_file);
260 return EXIT_FAILURE;
261 }
262
263 bool continue_running = true;
264 while (continue_running) {
265 switch (get_menu_selection()) {
267 manage_stock(pool);
268 break;
270 generate_reports(pool);
271 break;
272 default:
273 save_inventory(stock_file, pool);
274 continue_running = false;
275 break;
276 }
277 }
278
279 pool_delete(&pool);
280 fclose(stock_file);
281 return EXIT_SUCCESS;
282}
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:52
bool product_modify_quantity(struct Product *this, const int modifier)
Definition Product.c:68
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)
Definition main.c:243
static FILE * open_stock_file(const char *const filename)
Definition main.c:220
static void manage_stock(const struct Pool *const pool)
Definition main.c:110
MenuOption
Definition main.c:12
@ MENU_QUIT
Definition main.c:16
@ MENU_MANAGE_STOCK
Definition main.c:14
@ MENU_GENERATE_REPORT
Definition main.c:15
@ MENU_INVALID
Definition main.c:13
static int load_inventory(FILE *const stock_file, struct Pool *const pool)
Definition main.c:184
static void generate_reports(const struct Pool *const pool)
Definition main.c:166
static enum MenuOption get_menu_selection()
Definition main.c:19
static unsigned int convert_to_uint(const char *const buffer, bool *error)
Definition main.c:80
int main(const int argc, const char **const argv)
Definition main.c:249
static size_t get_input_line(char *const buffer, const size_t buffer_size, FILE *const stream)
Definition main.c:55
static int convert_to_sint(const char *const buffer, bool *error)
Definition main.c:95
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