-
Notifications
You must be signed in to change notification settings - Fork 0
/
file_utils.c
133 lines (107 loc) · 3.5 KB
/
file_utils.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
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "file_utils.h"
#include "string_utils.h"
struct return_buf {
char* buf;
int len;
} collect;
/*
* Simply reads a file in chunks of bytes and sends each chunk to a given
* function to deal with.
*/
void read_file (const char* filename, read_file_cb chunk_handler, void* cb_data) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Error opening %s for reading.\n", filename);
}
// Create a buffer to read chunks of a file at a time.
char buffer[FILE_READ_CHUNK_SIZE];
int bytes_read;
while (!feof(file)) {
bytes_read = fread(buffer, sizeof(char), FILE_READ_CHUNK_SIZE, file);
chunk_handler(buffer, bytes_read, feof(file), cb_data);
}
fclose(file);
}
/*
* We take a chunk of data and then prepare it so that it's in the right form
* to send to string_split. Since it's very likely that the chunk of data we
* get sent does not end on a newline character we have to chop off the end
* and store it for later to be placed onto the front of the next chunk. We
* use string_split_cb so that each line can be passed to a callback.
*/
void file_split_lines (const char* chunk, int len, int eof, void* line_handler) {
static char buffer[FILE_MAX_LINE_LEN];
static int n = 0;
char* data = calloc(n + len + 1, sizeof(char));
if (n > 0) {
// If there is some data in the buffer from the last chunk, prepend it
// to the data we send to string_split.
memmove(data, buffer, n * sizeof(char));
}
memmove(data + n, chunk, len * sizeof(char));
data[n + len] = '\0';
if (!eof && chunk[len - 1] != '\n') {
// If it's not the end of the file and the last character is not a new
// line then the chunk of the file that has been read is in the middle
// of a line so we must save it in the buffer and send only the string
// from the beginning and up to the last newline character.
int count = 0;
while (chunk[len - count - 1] != '\n' && len - count - 1 >= 0) {
count++;
}
int offset = len - count;
if (count > FILE_MAX_LINE_LEN) {
fprintf(stderr, "Trying to buffer too much data.");
} else {
n = count;
memmove(buffer, chunk + offset, count * sizeof(char));
}
// Lastly, place a null character to effectively truncate the data
// before sending it off to string_split.
data[offset] = '\0';
} else {
n = 0;
}
string_split_cb((strsplit_cb) line_handler, NULL, data, "\n");
free(data);
}
/*
* We use read_file to read in a file one chunk at a time and send each chunk
* to file_split_lines.
*/
void file_get_lines (const char* filename, strsplit_cb line_handler) {
read_file(filename, &file_split_lines, line_handler);
}
void file_collect (const char* chunk, int len, int eof, void* data) {
struct return_buf* collect = data;
int extra = 0;
char* alloc;
if (eof) {
// Allocate an extra byte if this is the last chunk so we can add the
// null character to terminate the string.
extra = 1;
}
if (collect->len == 0) {
collect->buf = calloc(len + extra, sizeof(char));
} else {
alloc = realloc(collect->buf, (collect->len + len + extra) * sizeof(char));
if (alloc == NULL) {
fprintf(stderr, "Error allocating memory in file_collect.");
} else {
collect->buf = alloc;
}
}
memmove(collect->buf + collect->len, chunk, len * sizeof(char));
collect->len += len;
}
char* file_get_contents (const char* filename) {
struct return_buf collect;
collect.len = 0;
read_file(filename, &file_collect, &collect);
collect.buf[collect.len] = '\0';
return collect.buf;
}