Line | Count | Source |
1 | | #include <sys/stat.h> |
2 | | |
3 | | #include <errno.h> |
4 | | #include <fcntl.h> |
5 | | #include <stdio.h> |
6 | | #include <stdlib.h> |
7 | | #include <string.h> |
8 | | #include <unistd.h> |
9 | | #include "jv.h" |
10 | | #include "jv_unicode.h" |
11 | | |
12 | 0 | jv jv_load_file(const char* filename, int raw) { |
13 | 0 | struct stat sb; |
14 | 0 | int fd = open(filename, O_RDONLY); |
15 | 0 | if (fd == -1) { |
16 | 0 | return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", |
17 | 0 | filename, |
18 | 0 | strerror(errno))); |
19 | 0 | } |
20 | 0 | if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode)) { |
21 | 0 | close(fd); |
22 | 0 | return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", |
23 | 0 | filename, |
24 | 0 | "It's a directory")); |
25 | 0 | } |
26 | 0 | FILE* file = fdopen(fd, "r"); |
27 | 0 | struct jv_parser* parser = NULL; |
28 | 0 | jv data; |
29 | 0 | if (!file) { |
30 | 0 | close(fd); |
31 | 0 | return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", |
32 | 0 | filename, |
33 | 0 | strerror(errno))); |
34 | 0 | } |
35 | 0 | if (raw) { |
36 | 0 | data = jv_string(""); |
37 | 0 | } else { |
38 | 0 | data = jv_array(); |
39 | 0 | parser = jv_parser_new(0); |
40 | 0 | } |
41 | | |
42 | | // To avoid mangling UTF-8 multi-byte sequences that cross the end of our read |
43 | | // buffer, we need to be able to read the remainder of a sequence and add that |
44 | | // before appending. |
45 | 0 | char buf[4096 + 4]; |
46 | 0 | while (!feof(file) && !ferror(file)) { |
47 | 0 | size_t n = fread(buf, 1, sizeof(buf) - 4, file); |
48 | 0 | if (n == 0) |
49 | 0 | continue; |
50 | | |
51 | 0 | char *end = buf + n; |
52 | 0 | int len = 0; |
53 | 0 | if (jvp_utf8_backtrack(end - 1, buf, &len) && len > 0 && |
54 | 0 | !feof(file) && !ferror(file)) { |
55 | 0 | n += fread(end, 1, len, file); |
56 | 0 | } |
57 | |
|
58 | 0 | if (raw) { |
59 | 0 | data = jv_string_append_buf(data, buf, n); |
60 | 0 | } else { |
61 | 0 | jv_parser_set_buf(parser, buf, n, !feof(file)); |
62 | 0 | jv value; |
63 | 0 | while (jv_is_valid((value = jv_parser_next(parser)))) |
64 | 0 | data = jv_array_append(data, value); |
65 | 0 | if (jv_invalid_has_msg(jv_copy(value))) { |
66 | 0 | jv_free(data); |
67 | 0 | data = value; |
68 | 0 | break; |
69 | 0 | } |
70 | 0 | } |
71 | 0 | } |
72 | 0 | if (!raw) |
73 | 0 | jv_parser_free(parser); |
74 | 0 | int badread = ferror(file); |
75 | 0 | if (fclose(file) != 0 || badread) { |
76 | 0 | jv_free(data); |
77 | 0 | return jv_invalid_with_msg(jv_string_fmt("Error reading from %s", |
78 | 0 | filename)); |
79 | 0 | } |
80 | 0 | return data; |
81 | 0 | } |