Skip to content

Instantly share code, notes, and snippets.

@hbobenicio
Created November 20, 2024 13:33
Show Gist options
  • Save hbobenicio/9f23d100604fd2baa65d3f1a279aa819 to your computer and use it in GitHub Desktop.
Save hbobenicio/9f23d100604fd2baa65d3f1a279aa819 to your computer and use it in GitHub Desktop.
File Copy in C
#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