Created
November 20, 2024 13:33
-
-
Save hbobenicio/9f23d100604fd2baa65d3f1a279aa819 to your computer and use it in GitHub Desktop.
File Copy in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <assert.h> | |
enum result { RESULT_OK, RESULT_ERR }; | |
/** | |
* @brief Calculates the hash of a array of bytes. | |
*/ | |
uint32_t hash_fnv_1a_32(const unsigned char* key, size_t key_len) | |
{ | |
#define FNV_1A_32_OFFSET_BASIS 2166136261u | |
#define FNV_1A_32_PRIME 16777619 | |
uint32_t hash = FNV_1A_32_OFFSET_BASIS; | |
static_assert(sizeof(key[0]) == sizeof(uint8_t), "incompatible sizes"); | |
for (size_t i = 0; i < key_len; i++) { | |
hash ^= (uint8_t) key[i]; | |
hash *= FNV_1A_32_PRIME; | |
} | |
return hash; | |
#undef FNV_1A_32_PRIME | |
#undef FNV_1A_32_OFFSET_BASIS | |
} | |
enum result file_checksum(const char* input_file_path, const char* output_file_path) | |
{ | |
assert(input_file_path != NULL); | |
assert(output_file_path != NULL); | |
fprintf(stderr, "info: integrity check... f1=\"%s\" f2=\"%s\"\n", input_file_path, output_file_path); | |
FILE* input_file = fopen(input_file_path, "rb"); | |
if (input_file == NULL) { | |
const char* posix_error_cause = strerror(errno); | |
fprintf(stderr, "error: failed to open file for reading: %s. file=\"%s\"\n", posix_error_cause, input_file_path); | |
return RESULT_ERR; | |
} | |
FILE* output_file = fopen(output_file_path, "rb"); | |
if (output_file == NULL) { | |
const char* posix_error_cause = strerror(errno); | |
fprintf(stderr, "error: failed to open file for writing: %s. file=\"%s\"\n", posix_error_cause, output_file_path); | |
//goto err_fclose_input; | |
} | |
//TODO continue... | |
fclose(output_file); | |
fclose(input_file); | |
return RESULT_OK; | |
} | |
enum result file_copy(const char* input_file_path, const char* output_file_path) | |
{ | |
assert(input_file_path != NULL); | |
assert(output_file_path != NULL); | |
fprintf(stderr, "info: copying... \"%s\" -> \"%s\"\n", input_file_path, output_file_path); | |
FILE* input_file = fopen(input_file_path, "rb"); | |
if (input_file == NULL) { | |
const char* posix_error_cause = strerror(errno); | |
fprintf(stderr, "error: failed to open file for reading: %s. file=\"%s\"\n", posix_error_cause, input_file_path); | |
return RESULT_ERR; | |
} | |
FILE* output_file = fopen(output_file_path, "wb"); | |
if (output_file == NULL) { | |
const char* posix_error_cause = strerror(errno); | |
fprintf(stderr, "error: failed to open file for writing: %s. file=\"%s\"\n", posix_error_cause, output_file_path); | |
goto err_fclose_input; | |
} | |
#define BUFFER_CAPACITY (4 * 1024) | |
unsigned char buffer[BUFFER_CAPACITY]; | |
// the copying loop | |
while (true) { | |
// fread() does not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to determine which occurred | |
size_t read_count = fread(buffer, sizeof(buffer[0]), BUFFER_CAPACITY, input_file); | |
if (ferror(input_file) != 0) { | |
// does fread set errno accordingly? | |
fprintf(stderr, "error: failed to read from file. file=\"%s\"\n", input_file_path); | |
goto err_fclose_files; | |
} | |
if (read_count == 0) { | |
// done reading (end-of-file) | |
break; | |
} | |
size_t write_count = fwrite(buffer, sizeof(buffer[0]), read_count, output_file); | |
if (ferror(output_file) != 0 || write_count != read_count) { | |
fprintf(stderr, "error: failed to write to file. file=\"%s\"\n", output_file_path); | |
goto err_fclose_files; | |
} | |
} | |
fprintf(stderr, "info: copying: done.\n"); | |
fclose(output_file); | |
fclose(input_file); | |
if (file_checksum(input_file_path, output_file_path) != RESULT_OK) { | |
fprintf(stderr, "error: integrity check failed\n"); | |
return RESULT_ERR; | |
} | |
return RESULT_OK; | |
err_fclose_files: | |
fclose(output_file); | |
err_fclose_input: | |
fclose(input_file); | |
return RESULT_ERR; | |
#undef BUFFER_SIZE | |
} | |
int main(void) | |
{ | |
if (file_copy("file-copy.c", "/tmp/file-copy.c") != RESULT_OK) { | |
fprintf(stderr, "error: failed copying files\n"); | |
return RESULT_ERR; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment