- Commit
- b67e1f24b89415d04c261aaf194251292103d1dd
- Parent
- 8e80513468b619ca5c4593736deb50705525cbe9
- Author
- Pablo <pablo-pie@riseup.net>
- Date
Implemented buffered IO
NOTE: substantially changed the API
Minimalist header-only library for generating TAR files
Implemented buffered IO
NOTE: substantially changed the API
2 files changed, 100 insertions, 24 deletions
| Status | Name | Changes | Insertions | Deletions |
| Modified | example.c | 2 files changed | 12 | 7 |
| Modified | tarw.h | 2 files changed | 88 | 17 |
diff --git a/example.c b/example.c @@ -7,29 +7,34 @@ int main(void) { const char *output_path = "./test.tar"; - FILE *sink = fopen(output_path, "wb"); + FILE *output = fopen(output_path, "wb"); - if (sink == NULL) { + if (output == NULL) { fprintf(stderr, "ERROR: could not open \"%s\"\n", output_path); return EXIT_FAILURE; } TarWriter w = { - .sink = sink, .owner = "user", .group = "group", .owner_id = 1000, .group_id = 1000, }; - if (!tarw_write_directory(w, "test/", 0755)) goto error; + if (!tarw_add_directory(&w, "test/", 0755)) goto not_enought_memmory; const char *msg = "Hello from C!\n"; - if (!tarw_write_file(w, "test/a", 0644, msg, strlen(msg))) goto error; + if (!tarw_add_file(&w, "test/a", 0644, msg, strlen(msg))) + goto not_enought_memmory; + + if (!tarw_write(w, output)) { + fprintf(stderr, "ERROR: could write all bytes to \"%s\"\n", output_path); + return EXIT_FAILURE; + } return EXIT_SUCCESS; -error: - fprintf(stderr, "ERROR: could write all bytes to \"%s\"\n", output_path); +not_enought_memmory: + fprintf(stderr, "ERROR: could not allocate enough memmory\n"); return EXIT_FAILURE; }
diff --git a/tarw.h b/tarw.h @@ -1,6 +1,7 @@ #ifndef TARW_H_ #define TARW_H_ +#include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <stdbool.h> @@ -12,17 +13,22 @@ extern "C" { #endif // __cplusplus typedef struct { - FILE *sink; - const char *owner; const char *group; size_t owner_id; size_t group_id; + + char *data; + size_t cursor; + size_t capacity; } TarWriter; -bool tarw_write_directory(TarWriter w, const char *path, uint16_t mode); -bool tarw_write_file(TarWriter w, const char *path, uint16_t mode, - const char *buff, size_t buff_size); +bool tarw_add_directory(TarWriter *w, const char *path, uint16_t mode); +bool tarw_add_file(TarWriter *w, const char *path, uint16_t mode, + const char *buff, size_t buff_size); +bool tarw_write(TarWriter w, FILE *f); + +void tarw_free(TarWriter *w); #define TARW_CHUNK_SIZE 512 @@ -68,9 +74,19 @@ enum { TARW_PIPE = '6', // TODO: unsupported }; +// = Implementation =========================================================== #ifdef TARW_IMPLEMENTATION + #define TARW_USTARW_INDICATOR "ustar" +#ifdef TARW_EMIT_ASSERTS +#ifndef TARW_ASSERT +#define TARW_ASSERT(cond, msg) assert((cond) && (msg)) +#endif // TARW_ASSERT +#else +#define TARW_ASSERT(cond, msg) +#endif // TARW_EMIT_ASSERTS + void tarw_header_set_checksum(TarHeader *h) { uint8_t *data = (uint8_t*)h; @@ -110,33 +126,88 @@ TarHeader tarw_make_header(const char *path, uint16_t mode, return h; } -bool tarw_write_directory(TarWriter w, const char *path, uint16_t mode) +bool tarw__reserve_bytes(TarWriter *w, size_t bytes) +{ + if (w->capacity - w->cursor >= bytes) return true; + + size_t new_capacity = 2 * w->capacity; + if (new_capacity - w->cursor < bytes) new_capacity = w->capacity + bytes; + + void *new_data = realloc(w->data, new_capacity); + if (new_data == NULL) return false; + w->data = new_data; + w->capacity = new_capacity; + + return true; +} + +/* + * NOTE: it is only safe to call this function when buff fits inside w->data! + */ +void tarw__write_bytes(TarWriter *w, const char *buff, size_t buff_size) { + TARW_ASSERT(w->capactiry - w->cursor >= buff_size, "buffer overflow"); + memcpy(w->data + w->cursor, buff, buff_size); + w->cursor += buff_size; +} + +/* + * NOTE: it is only safe to call this function when buff fits inside w->data! + */ +void tarw__write_zeros(TarWriter *w, size_t n) +{ + TARW_ASSERT(w->capactiry - w->cursor >= buff_size, "buffer overflow"); + memset(w->data + w->cursor, 0, n); + w->cursor += n; +} + +bool tarw_add_directory(TarWriter *w, const char *path, uint16_t mode) +{ + if (!tarw__reserve_bytes(w, TARW_CHUNK_SIZE)) return false; + TarHeader h = tarw_make_header(path, mode, - w.owner, w.group, w.owner_id, w.group_id, + w->owner, w->group, w->owner_id, w->group_id, TARW_DIRECTORY, 0); - if (fwrite((char*)&h, 1, sizeof(h), w.sink) != sizeof(h)) return false; + tarw__write_bytes(w, (char*)&h, TARW_CHUNK_SIZE); return true; } -bool tarw_write_file(TarWriter w, const char *path, uint16_t mode, - const char *buff, size_t buff_size) +bool tarw_add_file(TarWriter *w, const char *path, uint16_t mode, + const char *buff, size_t buff_size) { + size_t chunks_ceil = (buff_size + (TARW_CHUNK_SIZE-1))/TARW_CHUNK_SIZE; + size_t padding = chunks_ceil*TARW_CHUNK_SIZE - buff_size; + size_t capacity_needed = TARW_CHUNK_SIZE + buff_size + padding; + if (!tarw__reserve_bytes(w, capacity_needed)) return false; + TarHeader h = tarw_make_header(path, mode, - w.owner, w.group, w.owner_id, w.group_id, + w->owner, w->group, w->owner_id, w->group_id, TARW_NORMAL, buff_size); - if (fwrite((char*)&h, 1, sizeof(h), w.sink) != sizeof(h)) return false; - if (fwrite(buff, 1, buff_size, w.sink) != buff_size) return false; + tarw__write_bytes(w, (char*)&h, sizeof(h)); + tarw__write_bytes(w, buff, buff_size); + tarw__write_zeros(w, padding); - static const char zeros[TARW_CHUNK_SIZE] = {0}; - size_t chunks_ceil = (buff_size + (TARW_CHUNK_SIZE-1))/TARW_CHUNK_SIZE; - size_t padding = chunks_ceil*TARW_CHUNK_SIZE - buff_size; - if (fwrite(zeros, 1, padding, w.sink) != padding) return false; return true; } +bool tarw_write(TarWriter w, FILE *f) +{ + if (fwrite(w.data, 1, w.cursor, f) != w.cursor) return false; + return true; +} + +void tarw_free(TarWriter *w) +{ + if (w->data) { + free(w->data); + w->data = NULL; + } + w->capacity = 0; + w->cursor = 0; +} + #endif // TARW_IMPLEMENTATION #ifdef __cplusplus }