diff options
| -rw-r--r-- | environment.c | 2 | ||||
| -rw-r--r-- | stdlib/paths.c | 25 | ||||
| -rw-r--r-- | stdlib/paths.h | 3 |
3 files changed, 22 insertions, 8 deletions
diff --git a/environment.c b/environment.c index 7eb13685..5b829b1e 100644 --- a/environment.c +++ b/environment.c @@ -326,7 +326,7 @@ env_t *new_compilation_unit(CORD libname) {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, {"parent", "Path$parent", "func(path:Path -> Path)"}, {"read", "Path$read", "func(path:Path -> Text?)"}, - {"read_bytes", "Path$read_bytes", "func(path:Path -> [Byte]?)"}, + {"read_bytes", "Path$read_bytes", "func(path:Path, limit=!Int -> [Byte]?)"}, {"relative", "Path$relative", "func(path:Path, relative_to=(./) -> Path)"}, {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, diff --git a/stdlib/paths.c b/stdlib/paths.c index 2453394d..ac4d54bc 100644 --- a/stdlib/paths.c +++ b/stdlib/paths.c @@ -266,7 +266,7 @@ public void Path$append_bytes(Path_t path, Array_t bytes, int permissions) _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } -public OptionalArray_t Path$read_bytes(Path_t path) +public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$_expand_home(path); int fd = open(Text$as_c_string(path), O_RDONLY); @@ -277,26 +277,37 @@ public OptionalArray_t Path$read_bytes(Path_t path) if (fstat(fd, &sb) != 0) return NULL_ARRAY; + int64_t const target_count = count.small ? Int_to_Int64(count, false) : INT64_MAX; + if (target_count < 0) + fail("Cannot read a negative number of bytes!"); + if ((sb.st_mode & S_IFMT) == S_IFREG) { // Use memory mapping if it's a real file: const char *mem = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); char *content = GC_MALLOC_ATOMIC((size_t)sb.st_size+1); memcpy(content, mem, (size_t)sb.st_size); content[sb.st_size] = '\0'; close(fd); - return (Array_t){.data=content, .atomic=1, .stride=1, .length=(int64_t)sb.st_size}; + if (count.small && (int64_t)sb.st_size < target_count) + fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, sb.st_size); + int64_t len = count.small ? target_count : (int64_t)sb.st_size; + return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; } else { size_t capacity = 256, len = 0; char *content = GC_MALLOC_ATOMIC(capacity); + int64_t count_remaining = target_count; for (;;) { char chunk[256]; - ssize_t just_read = read(fd, chunk, sizeof(chunk)); - if (just_read < 0) + size_t to_read = count_remaining < (int64_t)sizeof(chunk) ? (size_t)count_remaining : sizeof(chunk); + ssize_t just_read = read(fd, chunk, to_read); + if (just_read < 0) { + close(fd); return NULL_ARRAY; - else if (just_read == 0) { + } else if (just_read == 0) { if (errno == EAGAIN || errno == EINTR) continue; break; } + count_remaining -= (int64_t)just_read; if (len + (size_t)just_read >= capacity) { content = GC_REALLOC(content, (capacity *= 2)); @@ -309,13 +320,15 @@ public OptionalArray_t Path$read_bytes(Path_t path) break; } close(fd); + if (count.small != 0 && (int64_t)len < target_count) + fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, len); return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; } } public OptionalText_t Path$read(Path_t path) { - Array_t bytes = Path$read_bytes(path); + Array_t bytes = Path$read_bytes(path, NULL_INT); if (bytes.length < 0) return NULL_TEXT; return Text$from_bytes(bytes); } diff --git a/stdlib/paths.h b/stdlib/paths.h index 551467e9..989e5d84 100644 --- a/stdlib/paths.h +++ b/stdlib/paths.h @@ -7,6 +7,7 @@ #include "types.h" #include "datatypes.h" +#include "optionals.h" #define Path_t Text_t #define OptionalPath_t Text_t @@ -34,7 +35,7 @@ void Path$write_bytes(Path_t path, Array_t bytes, int permissions); void Path$append(Path_t path, Text_t text, int permissions); void Path$append_bytes(Path_t path, Array_t bytes, int permissions); OptionalText_t Path$read(Path_t path); -OptionalArray_t Path$read_bytes(Path_t path); +OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t limit); void Path$remove(Path_t path, bool ignore_missing); void Path$create_directory(Path_t path, int permissions); Array_t Path$children(Path_t path, bool include_hidden); |
