#include #include #include #include #include #include #include #include "jv.h" #include "jv_unicode.h" jv jv_load_file(const char* filename, int raw) { struct stat sb; int fd = open(filename, O_RDONLY); if (fd == -1) { return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", filename, strerror(errno))); } if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode)) { close(fd); return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", filename, "It's a directory")); } FILE* file = fdopen(fd, "r"); struct jv_parser* parser = NULL; jv data; if (!file) { close(fd); return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", filename, strerror(errno))); } if (raw) { data = jv_string(""); } else { data = jv_array(); parser = jv_parser_new(0); } // To avoid mangling UTF-8 multi-byte sequences that cross the end of our read // buffer, we need to be able to read the remainder of a sequence and add that // before appending. char buf[4096 + 4]; int at_file_start = 1; while (!feof(file) && !ferror(file)) { size_t n = fread(buf, 1, sizeof(buf) - 4, file); if (n == 0) continue; char *end = buf + n; int len = 0; if (jvp_utf8_backtrack(end - 1, buf, &len) && len > 0 && !feof(file) && !ferror(file)) { n += fread(end, 1, len, file); } char *p = buf; if (at_file_start) { // Strip UTF-8 BOM at the start of the file if (n >= 3 && !memcmp(buf, "\xEF\xBB\xBF", 3)) { p += 3; n -= 3; } at_file_start = 0; } if (raw) { data = jv_string_append_buf(data, p, n); if (!jv_is_valid(data)) break; } else { jv_parser_set_buf(parser, p, n, !feof(file)); jv value; while (jv_is_valid((value = jv_parser_next(parser)))) data = jv_array_append(data, value); if (jv_invalid_has_msg(jv_copy(value))) { jv_free(data); data = value; break; } jv_free(value); } } if (!raw) jv_parser_free(parser); int badread = ferror(file); if (fclose(file) != 0 || badread) { jv_free(data); return jv_invalid_with_msg(jv_string_fmt("Error reading from %s", filename)); } return data; }