Impl. basic functionality "MVP"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 48s
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 48s
"MinimumViableProduct", resolves #2 . TODO: Support de/compressor cmds when creating archive. Support symbolic links when creating archive. Support extracting archive.
This commit is contained in:
parent
4670f0f3c1
commit
41fde43eed
8 changed files with 372 additions and 18 deletions
|
@ -7,6 +7,7 @@ set(SimpleArchiver_SOURCES
|
|||
src/main.c
|
||||
src/parser.c
|
||||
src/helpers.c
|
||||
src/archiver.c
|
||||
src/data_structures/linked_list.c
|
||||
src/data_structures/hash_map.c
|
||||
src/data_structures/priority_heap.c
|
||||
|
|
|
@ -7,6 +7,7 @@ SOURCES = \
|
|||
../src/main.c \
|
||||
../src/parser.c \
|
||||
../src/helpers.c \
|
||||
../src/archiver.c \
|
||||
../src/algorithms/linear_congruential_gen.c \
|
||||
../src/data_structures/linked_list.c \
|
||||
../src/data_structures/hash_map.c \
|
||||
|
@ -16,6 +17,7 @@ HEADERS = \
|
|||
../src/parser.h \
|
||||
../src/parser_internal.h \
|
||||
../src/helpers.h \
|
||||
../src/archiver.h \
|
||||
../src/algorithms/linear_congruential_gen.h \
|
||||
../src/data_structures/linked_list.h \
|
||||
../src/data_structures/hash_map.h \
|
||||
|
|
271
src/archiver.c
Normal file
271
src/archiver.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright 2024 Stephen Seo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* `archiver.c` is the source for an interface to creating an archive file.
|
||||
*/
|
||||
|
||||
#include "archiver.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
typedef struct SDArchiverInternalToWrite {
|
||||
void *buf;
|
||||
uint64_t size;
|
||||
} SDArchiverInternalToWrite;
|
||||
|
||||
void free_FILE_helper(FILE **fd) {
|
||||
if (fd && *fd) {
|
||||
fclose(*fd);
|
||||
*fd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void free_internal_to_write(void *data) {
|
||||
SDArchiverInternalToWrite *to_write = data;
|
||||
free(to_write->buf);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int write_list_datas_fn(void *data, void *ud) {
|
||||
SDArchiverInternalToWrite *to_write = data;
|
||||
FILE *out_f = ud;
|
||||
|
||||
fwrite(to_write->buf, 1, to_write->size, out_f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_files_fn(void *data, void *ud) {
|
||||
const SDArchiverFileInfo *file_info = data;
|
||||
const SDArchiverState *state = ud;
|
||||
|
||||
__attribute__((cleanup(simple_archiver_list_free)))
|
||||
SDArchiverLinkedList *to_write = simple_archiver_list_init();
|
||||
SDArchiverInternalToWrite *temp_to_write;
|
||||
|
||||
if (!file_info->filename) {
|
||||
// Invalid entry, no filename.
|
||||
return 1;
|
||||
} else if (!state->out_f) {
|
||||
// Invalid out-ptr.
|
||||
return 1;
|
||||
} else if (!file_info->link_dest) {
|
||||
// Regular file, not a symbolic link.
|
||||
if (state->parsed->compressor && state->parsed->decompressor) {
|
||||
// De/compressor specified.
|
||||
// TODO
|
||||
} else {
|
||||
uint16_t u16;
|
||||
uint64_t u64;
|
||||
|
||||
u16 = strlen(file_info->filename);
|
||||
|
||||
// Write filename length.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
|
||||
temp_to_write->buf = malloc(2);
|
||||
temp_to_write->size = 2;
|
||||
memcpy(temp_to_write->buf, &u16, 2);
|
||||
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
|
||||
|
||||
// Write filename.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
|
||||
temp_to_write->buf = malloc(u16 + 1);
|
||||
temp_to_write->size = u16 + 1;
|
||||
memcpy(temp_to_write->buf, file_info->filename, u16 + 1);
|
||||
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
|
||||
|
||||
// Write flags.
|
||||
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
|
||||
temp_to_write->buf = malloc(4);
|
||||
temp_to_write->size = 4;
|
||||
for (unsigned int idx = 0; idx < temp_to_write->size; ++idx) {
|
||||
((unsigned char *)temp_to_write->buf)[idx] = 0;
|
||||
}
|
||||
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
|
||||
|
||||
// Write file length.
|
||||
__attribute__((cleanup(free_FILE_helper))) FILE *fd =
|
||||
fopen(file_info->filename, "rb");
|
||||
if (!fd) {
|
||||
// Error.
|
||||
return 1;
|
||||
} else if (fseek(fd, 0, SEEK_END) != 0) {
|
||||
// Error.
|
||||
return 1;
|
||||
}
|
||||
long end = ftell(fd);
|
||||
if (end == -1L) {
|
||||
// Error.
|
||||
return 1;
|
||||
}
|
||||
u64 = end;
|
||||
simple_archiver_helper_64_bit_be(&u64);
|
||||
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
|
||||
temp_to_write->buf = malloc(8);
|
||||
temp_to_write->size = 8;
|
||||
memcpy(temp_to_write->buf, &u64, 8);
|
||||
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
|
||||
|
||||
if (fseek(fd, 0, SEEK_SET) != 0) {
|
||||
// Error.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write all previuosly set data before writing file.
|
||||
simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
|
||||
|
||||
simple_archiver_list_free(&to_write);
|
||||
|
||||
// Write file.
|
||||
char buf[1024];
|
||||
size_t ret;
|
||||
do {
|
||||
ret = fread(buf, 1, 1024, fd);
|
||||
if (ret == 1024) {
|
||||
fwrite(buf, 1, 1024, state->out_f);
|
||||
} else if (ret > 0) {
|
||||
fwrite(buf, 1, ret, state->out_f);
|
||||
}
|
||||
if (feof(fd)) {
|
||||
break;
|
||||
} else if (ferror(fd)) {
|
||||
// Error.
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
} else {
|
||||
// A symblic link.
|
||||
// TODO
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed) {
|
||||
if (!parsed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDArchiverState *state = malloc(sizeof(SDArchiverState));
|
||||
state->flags = 0;
|
||||
state->parsed = parsed;
|
||||
state->out_f = NULL;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void simple_archiver_free_state(SDArchiverState **state) {
|
||||
if (state && *state) {
|
||||
free(*state);
|
||||
*state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
|
||||
const SDArchiverLinkedList *filenames) {
|
||||
if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
|
||||
uint16_t u16 = 0;
|
||||
|
||||
// No need to convert to big-endian for version 0.
|
||||
// simple_archiver_helper_16_bit_be(&u16);
|
||||
|
||||
if (fwrite(&u16, 2, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
|
||||
if (state->parsed->compressor && !state->parsed->decompressor) {
|
||||
return SDAS_NO_DECOMPRESSOR;
|
||||
} else if (!state->parsed->compressor && state->parsed->decompressor) {
|
||||
return SDAS_NO_COMPRESSOR;
|
||||
} else if (state->parsed->compressor && state->parsed->decompressor) {
|
||||
// Write the four flag bytes with first bit set.
|
||||
unsigned char c = 1;
|
||||
if (fwrite(&c, 1, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
c = 0;
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
if (fwrite(&c, 1, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
// De/compressor bytes.
|
||||
u16 = strlen(state->parsed->compressor);
|
||||
// To big-endian.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
// Write the size in big-endian.
|
||||
if (fwrite(&u16, 2, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
// From big-endian.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
// Write the compressor cmd including the NULL at the end of the string.
|
||||
if (fwrite(state->parsed->compressor, 1, u16 + 1, out_f) !=
|
||||
(size_t)u16 + 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
|
||||
u16 = strlen(state->parsed->decompressor);
|
||||
// To big-endian.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
// Write the size in big-endian.
|
||||
if (fwrite(&u16, 2, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
// From big-endian.
|
||||
simple_archiver_helper_16_bit_be(&u16);
|
||||
// Write the decompressor cmd including the NULL at the end of the string.
|
||||
if (fwrite(state->parsed->decompressor, 1, u16 + 1, out_f) !=
|
||||
(size_t)u16 + 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
} else {
|
||||
// Write the four flag bytes with first bit NOT set.
|
||||
unsigned char c = 0;
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (fwrite(&c, 1, 1, out_f) != 1) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write file count.
|
||||
{
|
||||
uint32_t u32 = filenames->count;
|
||||
simple_archiver_helper_32_bit_be(&u32);
|
||||
if (fwrite(&u32, 1, 4, out_f) != 4) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over files in list to write.
|
||||
state->out_f = out_f;
|
||||
if (simple_archiver_list_get(filenames, write_files_fn, state)) {
|
||||
// Error occurred.
|
||||
}
|
||||
state->out_f = NULL;
|
||||
|
||||
return SDAS_SUCCESS;
|
||||
}
|
52
src/archiver.h
Normal file
52
src/archiver.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2024 Stephen Seo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* `archiver.h` is the header for an interface to creating an archive file.
|
||||
*/
|
||||
|
||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
|
||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "data_structures/linked_list.h"
|
||||
#include "parser.h"
|
||||
|
||||
typedef struct SDArchiverState {
|
||||
/*
|
||||
*/
|
||||
unsigned int flags;
|
||||
const SDArchiverParsed *parsed;
|
||||
FILE *out_f;
|
||||
} SDArchiverState;
|
||||
|
||||
enum SDArchiverStateReturns {
|
||||
SDAS_SUCCESS = 0,
|
||||
SDAS_HEADER_ALREADY_WRITTEN = 1,
|
||||
SDAS_FAILED_TO_WRITE,
|
||||
SDAS_NO_COMPRESSOR,
|
||||
SDAS_NO_DECOMPRESSOR,
|
||||
SDAS_INVALID_PARSED_STATE
|
||||
};
|
||||
|
||||
SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed);
|
||||
void simple_archiver_free_state(SDArchiverState **state);
|
||||
|
||||
/// Returns zero on success. Otherwise one value from SDArchiverStateReturns
|
||||
/// enum.
|
||||
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
|
||||
const SDArchiverLinkedList *filenames);
|
||||
|
||||
#endif
|
|
@ -178,7 +178,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void *simple_archiver_list_get(SDArchiverLinkedList *list,
|
||||
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
|
||||
int (*data_check_fn)(void *, void *),
|
||||
void *user_data) {
|
||||
if (!list) {
|
||||
|
|
|
@ -62,7 +62,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
|
|||
/// Returns non-null on success.
|
||||
/// data_check_fn must return non-zero if the data passed to it is to be
|
||||
/// returned.
|
||||
void *simple_archiver_list_get(SDArchiverLinkedList *list,
|
||||
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
|
||||
int (*data_check_fn)(void *, void *),
|
||||
void *user_data);
|
||||
|
||||
|
|
26
src/main.c
26
src/main.c
|
@ -18,15 +18,16 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "archiver.h"
|
||||
#include "parser.h"
|
||||
|
||||
int print_list_fn(void *data, __attribute__((unused)) void *ud) {
|
||||
const SDArchiverFileInfo *file_info = data;
|
||||
if (file_info->link_dest == NULL) {
|
||||
printf(" REGULAR FILE: %s\n", file_info->filename);
|
||||
fprintf(stderr, " REGULAR FILE: %s\n", file_info->filename);
|
||||
} else {
|
||||
printf(" SYMBOLIC LINK: %s -> %s\n", file_info->filename,
|
||||
file_info->link_dest);
|
||||
fprintf(stderr, " SYMBOLIC LINK: %s -> %s\n", file_info->filename,
|
||||
file_info->link_dest);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,8 +55,25 @@ int main(int argc, const char **argv) {
|
|||
SDArchiverLinkedList *filenames =
|
||||
simple_archiver_parsed_to_filenames(&parsed);
|
||||
|
||||
puts("Filenames:");
|
||||
fprintf(stderr, "Filenames:\n");
|
||||
simple_archiver_list_get(filenames, print_list_fn, NULL);
|
||||
|
||||
if ((parsed.flags & 1) == 0) {
|
||||
FILE *file = fopen(parsed.filename, "wb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "ERROR: Failed to open \"%s\" for writing!\n",
|
||||
parsed.filename);
|
||||
return 2;
|
||||
}
|
||||
|
||||
__attribute__((cleanup(simple_archiver_free_state)))
|
||||
SDArchiverState *state = simple_archiver_init_state(&parsed);
|
||||
|
||||
if (simple_archiver_write_all(file, state, filenames) != SDAS_SUCCESS) {
|
||||
fprintf(stderr, "Error during writing.");
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
34
src/parser.c
34
src/parser.c
|
@ -134,16 +134,23 @@ int list_remove_same_str_fn(void *data, void *ud) {
|
|||
}
|
||||
|
||||
void simple_archiver_print_usage(void) {
|
||||
puts("Usage flags:");
|
||||
puts("-c : create archive file");
|
||||
puts("-x : extract archive file");
|
||||
puts("-f <filename> : filename to work on");
|
||||
puts("--compressor <full_compress_cmd> : requires --decompressor");
|
||||
puts("--decompressor <full_decompress_cmd> : requires --compressor");
|
||||
puts("--overwrite-create : allows overwriting an archive file");
|
||||
puts("-- : specifies remaining arguments are files to archive/extract");
|
||||
puts("If creating archive file, remaining args specify files to archive.");
|
||||
puts("If extracting archive file, remaining args specify files to extract.");
|
||||
fprintf(stderr, "Usage flags:\n");
|
||||
fprintf(stderr, "-c : create archive file\n");
|
||||
fprintf(stderr, "-x : extract archive file\n");
|
||||
fprintf(stderr, "-f <filename> : filename to work on\n");
|
||||
fprintf(stderr,
|
||||
"--compressor <full_compress_cmd> : requires --decompressor\n");
|
||||
fprintf(stderr,
|
||||
"--decompressor <full_decompress_cmd> : requires --compressor\n");
|
||||
fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
|
||||
fprintf(stderr,
|
||||
"-- : specifies remaining arguments are files to archive/extract\n");
|
||||
fprintf(
|
||||
stderr,
|
||||
"If creating archive file, remaining args specify files to archive.\n");
|
||||
fprintf(
|
||||
stderr,
|
||||
"If extracting archive file, remaining args specify files to extract.\n");
|
||||
}
|
||||
|
||||
SDArchiverParsed simple_archiver_create_parsed(void) {
|
||||
|
@ -181,7 +188,10 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
|||
|
||||
while (argc > 0) {
|
||||
if (!is_remaining_args) {
|
||||
if (strcmp(argv[0], "-c") == 0) {
|
||||
if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
|
||||
simple_archiver_print_usage();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[0], "-c") == 0) {
|
||||
// unset first bit.
|
||||
out->flags &= 0xFFFFFFFE;
|
||||
} else if (strcmp(argv[0], "-x") == 0) {
|
||||
|
@ -348,7 +358,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
|||
strcmp(dir_entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
printf("dir entry in %s is %s\n", next, dir_entry->d_name);
|
||||
fprintf(stderr, "dir entry in %s is %s\n", next, dir_entry->d_name);
|
||||
int combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
|
||||
char *combined_path = malloc(combined_size);
|
||||
snprintf(combined_path, combined_size, "%s/%s", next,
|
||||
|
|
Loading…
Reference in a new issue