Line | Count | Source (jump to first uncovered line) |
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 | 6.29k | jv jv_load_file(const char* filename, int raw) { |
13 | 6.29k | struct stat sb; |
14 | 6.29k | int fd = open(filename, O_RDONLY); |
15 | 6.29k | 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 | 6.29k | 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 | 6.29k | FILE* file = fdopen(fd, "r"); |
27 | 6.29k | struct jv_parser* parser = NULL; |
28 | 6.29k | jv data; |
29 | 6.29k | 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 | 6.29k | if (raw) { |
36 | 3.14k | data = jv_string(""); |
37 | 3.14k | } else { |
38 | 3.14k | data = jv_array(); |
39 | 3.14k | parser = jv_parser_new(0); |
40 | 3.14k | } |
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 | 6.29k | const int max_utf8_len = 4; |
46 | 6.29k | char buf[4096+max_utf8_len]; |
47 | 36.6k | while (!feof(file) && !ferror(file)) { |
48 | 31.8k | size_t n = fread(buf, 1, sizeof(buf)-max_utf8_len, file); |
49 | 31.8k | int len = 0; |
50 | | |
51 | 31.8k | if (n == 0) |
52 | 32 | continue; |
53 | 31.8k | if (jvp_utf8_backtrack(buf+(n-1), buf, &len) && len > 0 && |
54 | 31.8k | !feof(file) && !ferror(file)) { |
55 | 1.10k | n += fread(buf+n, 1, len, file); |
56 | 1.10k | } |
57 | | |
58 | 31.8k | if (raw) { |
59 | 16.7k | data = jv_string_append_buf(data, buf, n); |
60 | 16.7k | } else { |
61 | 15.0k | jv_parser_set_buf(parser, buf, n, !feof(file)); |
62 | 15.0k | jv value; |
63 | 3.64M | while (jv_is_valid((value = jv_parser_next(parser)))) |
64 | 3.63M | data = jv_array_append(data, value); |
65 | 15.0k | if (jv_invalid_has_msg(jv_copy(value))) { |
66 | 1.46k | jv_free(data); |
67 | 1.46k | data = value; |
68 | 1.46k | break; |
69 | 1.46k | } |
70 | 15.0k | } |
71 | 31.8k | } |
72 | 6.29k | if (!raw) |
73 | 3.14k | jv_parser_free(parser); |
74 | 6.29k | int badread = ferror(file); |
75 | 6.29k | 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 | 6.29k | return data; |
81 | 6.29k | } |