From def6ecb013b0dcc3a5f43838e03bb1c40d548bd3 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Mon, 6 Apr 2026 20:07:49 +0200 Subject: [PATCH] heloooo, youuhouuu --- epitar/Makefile | 34 +++++++++++ epitar/src/archive.h | 9 +++ epitar/src/config.c | 140 +++++++++++++++++++++++++++++++++++++++++++ epitar/src/config.h | 56 +++++++++++++++++ epitar/src/extract.h | 9 +++ epitar/src/main.c | 1 + epitar/src/tar.c | 59 ++++++++++++++++++ epitar/src/tar.h | 117 ++++++++++++++++++++++++++++++++++++ epitar/src/utils.c | 3 + epitar/src/utils.h | 6 ++ 10 files changed, 434 insertions(+) create mode 100644 epitar/Makefile create mode 100644 epitar/src/archive.h create mode 100644 epitar/src/config.c create mode 100644 epitar/src/config.h create mode 100644 epitar/src/extract.h create mode 100644 epitar/src/main.c create mode 100644 epitar/src/tar.c create mode 100644 epitar/src/tar.h create mode 100644 epitar/src/utils.c create mode 100644 epitar/src/utils.h diff --git a/epitar/Makefile b/epitar/Makefile new file mode 100644 index 0000000..4c5166d --- /dev/null +++ b/epitar/Makefile @@ -0,0 +1,34 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla +LDFLAGS= + +DBG_CFLAGS = -fsanitize=address -g +DBG_LDFLAGS= -fsanitize=address + + +SRC_DIR = src +LIB_SRCS = +MAIN_SRCS = main.c + +# SRCS = $(patsubst %,$(SRC_DIR)/%, $(MAIN_SRCS)) +SRCS = $(MAIN_SRCS:%=$(SRC_DIR)/%) $(LIB_SRCS:%=$(SRC_DIR)/%) +OBJS = $(SRCS:.c=.o) + +TARGET= epitar +# DBG_TARGET = epitar-dbg + +$(TARGET): $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) + @echo $(OBJS) + +debug: CFLAGS += $(DBG_CFLAGS) +debug: LDFLAGS += $(DBG_LDFLAGS) +debug: $(OBJS) + $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) + +check: + dash ./tests/run.sh + +clean: + $(RM) $(TARGET) + $(RM) $(OBJS) diff --git a/epitar/src/archive.h b/epitar/src/archive.h new file mode 100644 index 0000000..f1cdeaa --- /dev/null +++ b/epitar/src/archive.h @@ -0,0 +1,9 @@ +#ifndef ARCHIVE_H +#define ARCHIVE_H + +/* @brief Archives the listed `files` into `archive_name` + * @return 0 on success, the corresponding error code otherwise + */ +int archive(char *archive_name, char **files); + +#endif // ARCHIVE_H diff --git a/epitar/src/config.c b/epitar/src/config.c new file mode 100644 index 0000000..9b50f94 --- /dev/null +++ b/epitar/src/config.c @@ -0,0 +1,140 @@ +#define _POSIX_C_SOURCE 200809L +#include "config.h" + +#include +#include +#include +#include +#include + +static void print_files_array(char **input_files) +{ + while (*input_files != NULL) + { + printf(" - "); + puts(*input_files); + input_files++; + } +} + +// Returns the corresponding ARG_* macro +static int handle_opt(char **argv, char opt, struct config *cfg) +{ + // Options without value + // Help + if (opt == 'h') + return ARG_HELP; + + // Options with value + + if (optarg == NULL) + return ARG_INVALID; + + switch (opt) + { + // Archive mode + case 'c': + if (cfg->mode != UNDEFINED) + return ARG_INVALID; + cfg->mode = ARCHIVE; + return ARG_ARCHIVE; + + // Extract mode + case 'x': + if (cfg->mode != UNDEFINED) + return ARG_INVALID; + cfg->mode = EXTRACT; + break; + + default: + return ARG_INVALID; + } + + return ARG_VALID; +} + +// Returns true if config is valid, false otherwise +static bool check_config(struct config *cfg) +{ + // TODO + return true; +} + +int args_handler(int argc, char **argv, struct config *config) +{ + struct option options[] = { // Global + { "archive", required_argument, NULL, 'c' }, + { "extract", required_argument, NULL, 'x' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + // End + { NULL, 0, NULL, 0 } + }; + + config = calloc(1, sizeof(struct config)); + + char opt; + int optindex = 0; + while ((opt = getopt_long(argc, argv, "cxvh", options, &optindex)) != -1) + { + int err = handle_opt(argv, opt, config); + switch (err) + { + case ARG_VALID: + if (check_config(config)) + return ARG_VALID; + config_destroy(config); + return ARG_INVALID; + + case ARG_HELP: + print_usage(); + config_destroy(config); + return ARG_HELP; + + default: + print_usage(); + config_destroy(config); + return ARG_INVALID; + } + } +} + +void print_config(struct config *config) +{ + printf("Mode: %s\n", + config->mode == EXTRACT ? "EXTRACT" + : config->mode == ARCHIVE ? "ARCHIVE" + : "UNDEFINED"); + + printf("Input file(s):\n"); + if (config->input_files) + print_files_array(config->input_files); + else + puts("-> NULL"); + + printf("Verbose: %B\n", config->verbose); + printf("Show help message: %B\n", config->show_help); +} + +void print_usage(void) +{ + puts("Usage: epitar -[xcvh] []"); +} + +void print_invalid_option(void) +{ + puts("epitar: invalid or missing option"); + puts("Try './epitar -h' for more information"); +} + +void config_destroy(struct config *config) +{ + char **current_str = config->input_files; + while (current_str != NULL && *current_str != NULL) + { + free(*current_str); + current_str++; + } + free(config->input_files); + free(config); +} diff --git a/epitar/src/config.h b/epitar/src/config.h new file mode 100644 index 0000000..b898358 --- /dev/null +++ b/epitar/src/config.h @@ -0,0 +1,56 @@ +#ifndef ARGS_H +#define ARGS_H + +#include +#include + +#define ARG_VALID 0 +#define ARG_INVALID 1 +#define ARG_HELP 2 +#define ARG_ARCHIVE 3 + +enum tar_mode +{ + UNDEFINED, + EXTRACT, + ARCHIVE +}; + +struct config +{ + /* Defines what action the program will perform */ + enum tar_mode mode; + /* File(s) to archive/extract */ + char **input_files; + /* Program output after archiving */ + char *output_file; + /* Enable or disable verbose mode */ + bool verbose; + /* Show the help message */ + bool show_help; +}; + +/** + * Handles command-line arguments and populates the args_options structure. + * @param argc The argument count. + * @param argv The argument vector. + * @param config Pointer to config structure populate. + * This function will handle the memory allocation. + * @return 0 on success, non-zero on failure. + */ +int args_handler(int argc, char **argv, struct config *config); + +/** Prints the parsed arguments for debugging purposes. + * @param options Pointer to args_options structure containing parsed options. + */ +void print_config(struct config *config); + +/* Prints the usage information for the program. + */ +void print_usage(void); + +/* + */ +void config_destroy(struct config *config); + +#endif /* ARGS_H */ diff --git a/epitar/src/extract.h b/epitar/src/extract.h new file mode 100644 index 0000000..ad6f6df --- /dev/null +++ b/epitar/src/extract.h @@ -0,0 +1,9 @@ +#ifndef EXTRACT_H +#define EXTRACT_H + +/* @brief Extract and write files from `archive_name` + * @return 0 on success, the corresponding error code otherwise + */ +int extract(char *archive_name); + +#endif // EXTRACT_H diff --git a/epitar/src/main.c b/epitar/src/main.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/epitar/src/main.c @@ -0,0 +1 @@ + diff --git a/epitar/src/tar.c b/epitar/src/tar.c new file mode 100644 index 0000000..5defa56 --- /dev/null +++ b/epitar/src/tar.c @@ -0,0 +1,59 @@ +#include "tar.h" + +#include +#include + +struct unixtar_header *get_unixtar_header(union tar_header *header) +{ + if (header == NULL) + return NULL; + char empty_magic[6] = { 0 }; + if (memcmp(empty_magic, header->ustar.magic, 6) != 0) + return &header->unixtar; + return NULL; +} + +struct ustar_header *get_ustar_header(union tar_header *header) +{ + if (header == NULL) + return NULL; + if (strcmp(header->ustar.magic, USTAR_MAGIC) == 0) + return &header->ustar; + return NULL; +} + +bool check_checksum(union tar_header *header) +{ + struct ustar_header *ustar_header; + struct unixtar_header *unixtar_header; + int actual_checksum; + + // USTAR + if ((ustar_header = get_ustar_header(header)) != NULL) + { + actual_checksum = atoi(ustar_header->chksum); + } + // UNIXTAR + else if ((unixtar_header = get_unixtar_header(header)) != NULL) + { + actual_checksum = atoi(unixtar_header->chksum); + } + // Unsupported format + else + { + return false; + } + + int computed_checksum = compute_checksum(header); + return computed_checksum == actual_checksum; +} + +int compute_checksum(union tar_header *header) +{ + char *block = header->raw_block; + int sum = 0; + for (int i = 0; i < BLOCK_SIZE; i++) + sum += block[i]; + + return sum; +} diff --git a/epitar/src/tar.h b/epitar/src/tar.h new file mode 100644 index 0000000..5ac0be5 --- /dev/null +++ b/epitar/src/tar.h @@ -0,0 +1,117 @@ +#ifndef TAR_H +#define TAR_H + +#include + +// === Definitions + +#define BLOCK_SIZE 512 + +#define USTAR_MAGIC "ustar" /* ustar and a null */ +#define UNIXTAR_MAGIC "" +#define TMAGLEN 6 + +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +// Values for the typeflag field +#define REGTYPE '0' /* regular file */ +#define AREGTYPE '\0' /* regular file */ +#define LNKTYPE '1' /* link */ +#define SYMTYPE '2' /* reserved */ +#define CHRTYPE '3' /* character special */ +#define BLKTYPE '4' /* block special */ +#define DIRTYPE '5' /* directory */ +#define FIFOTYPE '6' /* FIFO special */ +#define CONTTYPE '7' /* reserved */ + +// Bit masks for the mpde field +#define TSUID 04000 /* set UID on execution */ +#define TSGID 02000 /* set GID on execution */ +#define TSVTX 01000 /* reserved */ +// File permissions +#define TUREAD 00400 /* read by owner */ +#define TUWRITE 00200 /* write by owner */ +#define TUEXEC 00100 /* execute/search by owner */ +#define TGREAD 00040 /* read by group */ +#define TGWRITE 00020 /* write by group */ +#define TGEXEC 00010 /* execute/search by group */ +#define TOREAD 00004 /* read by other */ +#define TOWRITE 00002 /* write by other */ +#define TOEXEC 00001 /* execute/search by other */ + +// === Structures + +enum error_codes +{ + SUCCESS = 0, + UNKNOWN_ERROR, + FILE_NOT_FOUND, + BAD_CHECKSUM, +}; + +struct ustar_header +{ + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; +}; + +struct unixtar_header +{ + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char link[1]; + char linkname[100]; + char padding[255]; // Should be empty +}; + +union tar_header +{ + struct unixtar_header unixtar; + struct ustar_header ustar; + char raw_block[512]; +}; + +// === Functions + +/* @brief Returns the header in the unix tar format + * @return the header if header is indeed in the unix tar format, NULL otherwise + */ +struct unixtar_header *get_unixtar_header(union tar_header *header); + +/* @brief Returns the header in the posix (ustar) tar format + * @return the header if header is indeed in the posix tar format, NULL + * otherwise + */ +struct ustar_header *get_ustar_header(union tar_header *header); + +/* @brief checks if file has not been corrupted via checksum + * @return true if member file is valid, false otherwise + */ +bool check_checksum(union tar_header *header); + +/* @brief computes the checksum of the given file + * @return the checksum + */ +int compute_checksum(union tar_header *header); + +#endif // TAR_H diff --git a/epitar/src/utils.c b/epitar/src/utils.c new file mode 100644 index 0000000..0271150 --- /dev/null +++ b/epitar/src/utils.c @@ -0,0 +1,3 @@ +#include "utils.h" + +#include diff --git a/epitar/src/utils.h b/epitar/src/utils.h new file mode 100644 index 0000000..8bc97b2 --- /dev/null +++ b/epitar/src/utils.h @@ -0,0 +1,6 @@ +#ifndef UTILS_H +#define UTILS_H + +void print_str_array(const char **input_files); + +#endif /* UTILS_H */