-
Notifications
You must be signed in to change notification settings - Fork 1
/
assignment5.c
297 lines (258 loc) · 8.19 KB
/
assignment5.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/* Assignment 5
Authors: Eric Hebert, Tyler Dallwig, Kendal Reed, Zain Syed
Usage:
make
./assignment5 filename
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#define HEADER_SIZE 4
// Valid values for the "type" field
#define TYPE_INT16 0
#define TYPE_INT32 1
#define TYPE_FLOAT32 2
#define TYPE_FLOAT64 3
#define TYPE_ASCII 7
#define TYPE_JUNK 8
#define CONTROL_SKIP 9
#define CONTROL_BURN 10
#define CONTROL_STOP 11
// Return values for handle_datagram
#define STATUS_CONTINUE 0
#define STATUS_STOP -1
#define STATUS_FAIL -2
// Macro for printing different types of data
#define PRINT_DATA(data, data_length, type, format) { \
size_t num_elements = data_length / sizeof(type); \
for (size_t i = 0; i < num_elements; ++i) { \
printf(format, *((type*) data + i)); \
} \
}
// Holds any version of a 32-bit datagram
typedef struct {
uint8_t version: 4;
uint8_t type: 4;
uint8_t length;
union {
// The skip bit exists in every version
uint16_t skip_bit: 1;
// Datagram version 2
struct {
// Including skip bit to make sure alignment is correct
uint8_t skip_bit: 1;
uint8_t dupe_bit: 1;
uint8_t checksum;
} version2;
// Datagram version 3
struct {
uint8_t skip_bit: 1;
uint8_t id: 7;
uint8_t checksum;
} version3;
} data;
} datagram;
// Reads a binary file, and prints its data
int read_file(const char *filename);
// Reads the data of a datagram, or handles the control instruction
int handle_datagram(datagram *dptr, FILE *fp, uint32_t *skips);
// Skips to the next datagram in the file, and returns the new status
int skip_datagram(FILE *fp, size_t data_length);
// Returns true if checksum is valid
bool valid_checksum(void *data);
// Prints data based on the numeric type value from the binary file
void print_data(uint8_t type, void *data, uint8_t data_length);
// BURN instruction
int handle_burn();
int main(int argc, char **argv)
{
int status = 0;
// Check number of command line arguments
if (argc == 2) {
// Read and process the file
status = read_file(argv[1]);
if (status < 0) {
printf("Error reading file '%s'\n", argv[1]);
}
}
else {
printf("Usage: %s filename\n", argv[0]);
status = 1;
}
return status;
}
int read_file(const char *filename)
{
int file_status = -1;
// Open the file in read-only binary mode
FILE *fp = fopen(filename, "rb");
if (fp) {
datagram temp_datagram;
int status = STATUS_CONTINUE;
uint32_t skips = 0;
// Read each datagram in the file, until STOP or EOF is reached
while (status == STATUS_CONTINUE) {
size_t bytes_read = fread(&temp_datagram, 1, sizeof(datagram), fp);
if (bytes_read == sizeof(datagram)) {
// Process the datagram if its header was successfully read
status = handle_datagram(&temp_datagram, fp, &skips);
}
else if (feof(fp)) {
printf("Reached the end of the file.\n");
status = STATUS_STOP;
}
else {
printf("There was an error reading the file.\n");
status = STATUS_FAIL;
}
}
file_status = 0;
fclose(fp);
if (status == STATUS_FAIL) {
printf("An error occurred while processing \"%s\".\n", filename);
}
}
return file_status;
}
int handle_datagram(datagram *dptr, FILE *fp, uint32_t *skips)
{
int status = STATUS_CONTINUE;
// Get the length of the data portion in bytes (not including the header)
uint8_t data_length = dptr->length - sizeof(datagram);
// Get the datagram type
uint8_t type = dptr->type;
if (*skips > 0 || dptr->data.skip_bit) {
// Handle skipping N datagrams and the skip bit
if (*skips > 0) {
--(*skips);
}
return skip_datagram(fp, data_length);
}
// Make sure the version is valid
if (dptr->version < 1 || dptr->version > 3) {
printf("Invalid version: %u\n", dptr->version);
return status;
}
// Normally, we only run things once
int run_count = 1;
// When the dupe bit is set, run things twice
if (dptr->version == 2 && dptr->data.version2.dupe_bit) {
run_count = 2;
}
if (dptr->version == 2 || dptr->version == 3) {
// Skip the datagram if the checksum is invalid (ignoring the length)
if (!valid_checksum(dptr)) {
while (run_count--) {
printf("Checksum is invalid!!!\n");
}
return status;
}
}
if (dptr->length < sizeof(datagram)) {
// Make sure the length is valid
while (run_count--) {
printf("Length is invalid: %u\n", dptr->length);
}
status = STATUS_FAIL;
}
else if ((type >= 0 && type <= 3) || type == 7) {
// Handle datagram containing data
// Allocate memory to read the data
void *data = malloc(data_length);
// Read the data from the file into the buffer
size_t bytes_read = fread(data, 1, data_length, fp);
if (bytes_read == data_length) {
// Print the data read as the correct type
while (run_count--) {
print_data(type, data, data_length);
}
}
else {
while (run_count--) {
printf("Read %lu/%u bytes of the datagram.\n", bytes_read, data_length);
}
status = STATUS_FAIL;
}
// Free the memory allocated for the data
free(data);
}
else if (type == CONTROL_SKIP) {
// Handle datagram containing SKIP instruction
// Read the number of datagrams to skip from the file (store this number in skips)
size_t bytes_read = fread(skips, 1, sizeof(*skips), fp);
// Modify the skip value according to the run count.
// (This doubles the skips if the dupe bit is set.)
(*skips) *= run_count;
// If the number could not be read, then fail
if (bytes_read != sizeof(*skips)) {
status = STATUS_FAIL;
}
}
else if (type == CONTROL_BURN) {
// Handle datagram containing BURN instruction
while (run_count--) {
status = handle_burn();
}
}
else if (type == CONTROL_STOP) {
// Handle datagram containing STOP instruction
// Stop reading the file
status = STATUS_STOP;
}
else if (type == TYPE_JUNK) {
// Handle datagram containing junk data
// Skip junk data
status = skip_datagram(fp, data_length);
}
else {
// Handle datagram with unrecognized type value
printf("Unknown datagram type: %u\n", type);
status = STATUS_FAIL;
}
return status;
}
int skip_datagram(FILE *fp, size_t data_length)
{
// Go to the next datagram without reading any data
if (fseek(fp, data_length, SEEK_CUR)) {
return STATUS_FAIL;
}
return STATUS_CONTINUE;
}
bool valid_checksum(void *data)
{
uint8_t total = 0;
// Add up all of the bytes of the header
for (unsigned i = 0; i < sizeof(datagram); ++i) {
total += ((uint8_t *) data)[i];
}
// Total needs to be 0 for the checksum to be valid
return (total == 0);
}
void print_data(uint8_t type, void *data, uint8_t data_length)
{
// Depending on the type ID stored, print the data as a different type
switch (type) {
case TYPE_INT16:
PRINT_DATA(data, data_length, int16_t, "%d");
break;
case TYPE_INT32:
PRINT_DATA(data, data_length, int32_t, "%d");
break;
case TYPE_FLOAT32:
PRINT_DATA(data, data_length, float, "%.7f");
break;
case TYPE_FLOAT64:
PRINT_DATA(data, data_length, double, "%.15f");
break;
case TYPE_ASCII:
PRINT_DATA(data, data_length, char, "%c");
break;
}
}
int handle_burn()
{
printf("All system fans have been disabled, your CPU is now melting...\n");
return STATUS_STOP;
}