hey mme pavoshko

This commit is contained in:
Gu://em_ 2026-04-06 21:12:22 +02:00
parent def6ecb013
commit d5a774a533
5 changed files with 205 additions and 91 deletions

View file

@ -1,9 +1,13 @@
#ifndef ARCHIVE_H #ifndef ARCHIVE_H
#define ARCHIVE_H #define ARCHIVE_H
#include "tar.h"
#include <stdbool.h>
/* @brief Archives the listed `files` into `archive_name` /* @brief Archives the listed `files` into `archive_name`
* @return 0 on success, the corresponding error code otherwise * @return 0 on success, the corresponding error code otherwise
*/ */
int archive(char *archive_name, char **files); int archive(char *archive_name, char **files, bool verbose);
#endif // ARCHIVE_H #endif // ARCHIVE_H

View file

@ -9,116 +9,179 @@
static void print_files_array(char **input_files) static void print_files_array(char **input_files)
{ {
if (input_files == NULL)
{
puts(" (none)");
return;
}
while (*input_files != NULL) while (*input_files != NULL)
{ {
printf(" - "); printf(" - %s\n", *input_files);
puts(*input_files);
input_files++; input_files++;
} }
} }
// Returns the corresponding ARG_* macro /**
static int handle_opt(char **argv, char opt, struct config *cfg) * Handle a single option flag
*/
static int handle_opt(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) switch (opt)
{ {
// Archive mode case 'h':
return ARG_HELP;
case 'c': case 'c':
if (cfg->mode != UNDEFINED) if (cfg->mode != UNDEFINED)
return ARG_INVALID; return ARG_INVALID;
cfg->mode = ARCHIVE; cfg->mode = ARCHIVE;
return ARG_ARCHIVE; return ARG_VALID;
// Extract mode
case 'x': case 'x':
if (cfg->mode != UNDEFINED) if (cfg->mode != UNDEFINED)
return ARG_INVALID; return ARG_INVALID;
cfg->mode = EXTRACT; cfg->mode = EXTRACT;
break; return ARG_VALID;
case 'v':
cfg->verbose = true;
return ARG_VALID;
default: default:
return ARG_INVALID; return ARG_INVALID;
} }
return ARG_VALID;
} }
// Returns true if config is valid, false otherwise /**
static bool check_config(struct config *cfg) *
*/
bool validate_config(struct config *cfg)
{ {
// TODO // Must have a valid mode
if (cfg->mode == UNDEFINED)
return false;
// Must have an archive file specified
if (cfg->archive_file == NULL)
return false;
return true; return true;
} }
int args_handler(int argc, char **argv, struct config *config) enum arg_error_code args_handler(int argc, char **argv, struct config *config)
{ {
struct option options[] = { // Global if (config == NULL || argv == NULL)
{ "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; return ARG_INVALID;
case ARG_HELP: // Init config
print_usage(); config->mode = UNDEFINED;
config_destroy(config); config->archive_file = NULL;
config->input_files = NULL;
config->verbose = false;
// Parse option flags
int opt;
while ((opt = getopt(argc, argv, "cxvh")) != -1)
{
int err = handle_opt(opt, config);
if (err == ARG_HELP)
{
return ARG_HELP; return ARG_HELP;
}
default: if (err != ARG_VALID)
print_usage(); {
config_destroy(config);
return ARG_INVALID; return ARG_INVALID;
} }
} }
int remaining_args = argc - optind;
// Need at least the archive file
if (remaining_args < 1)
{
return ARG_INVALID;
}
// First argument is the archive file
config->archive_file = argv[optind];
// Remaining arguments are input files (for archive mode)
int num_input_files = remaining_args - 1;
if (num_input_files > 0)
{
if (config->mode == EXTRACT)
return ARG_INVALID;
// Allocate input files array (+ NULL terminator)
config->input_files = malloc((num_input_files + 1) * sizeof(char *));
if (config->input_files == NULL)
return ARG_INVALID;
// SHallow copy names pointers
for (int i = 0; i < num_input_files; i++)
{
config->input_files[i] = argv[optind + 1 + i];
}
// NULL terminator
config->input_files[num_input_files] = NULL;
}
else
{
// For consistency
config->input_files = malloc(sizeof(char *));
if (config->input_files == NULL)
return ARG_INVALID;
config->input_files[0] = NULL;
}
// Validate config
if (!validate_config(config))
return ARG_INVALID;
return ARG_VALID;
} }
void print_config(struct config *config) void print_config(const struct config *config)
{ {
if (config == NULL)
{
puts("Config is NULL");
return;
}
printf("Mode: %s\n", printf("Mode: %s\n",
config->mode == EXTRACT ? "EXTRACT" config->mode == EXTRACT ? "EXTRACT"
: config->mode == ARCHIVE ? "ARCHIVE" : config->mode == ARCHIVE ? "ARCHIVE"
: "UNDEFINED"); : "UNDEFINED");
printf("Archive file: %s\n",
config->archive_file ? config->archive_file : "NULL");
printf("Input file(s):\n"); printf("Input file(s):\n");
if (config->input_files) if (config->input_files != NULL && config->input_files[0] != NULL)
print_files_array(config->input_files); print_files_array(config->input_files);
else else
puts("-> NULL"); puts(" (none)");
printf("Verbose: %B\n", config->verbose); printf("Verbose: %s\n", config->verbose ? "true" : "false");
printf("Show help message: %B\n", config->show_help);
} }
void print_usage(void) void print_usage(void)
{ {
puts("Usage: epitar -[xcvh] <file.tar> [<files>]"); puts("Usage: epitar -[xcvh] <file.tar> [<files>]");
// printf("\n");
// printf("Options:\n");
// printf(" -c Create an archive\n");
// printf(" -x Extract an archive\n");
// printf(" -v Verbose output\n");
// printf(" -h Show this help message\n");
// printf("\n");
// printf("Examples:\n");
// printf(" epitar -c archive.tar file1 file2 Create archive with
// files\n"); printf(" epitar -cv archive.tar file1 Create archive
// (verbose)\n"); printf(" epitar -x archive.tar Extract
// archive\n"); printf(" epitar -xv archive.tar Extract
// archive (verbose)\n");
} }
void print_invalid_option(void) void print_invalid_option(void)
@ -129,12 +192,16 @@ void print_invalid_option(void)
void config_destroy(struct config *config) void config_destroy(struct config *config)
{ {
char **current_str = config->input_files; if (config == NULL)
while (current_str != NULL && *current_str != NULL) return;
// Free input files array
if (config->input_files != NULL)
{ {
free(*current_str);
current_str++;
}
free(config->input_files); free(config->input_files);
}
// Note: archive_file points to argv, so we don't free it
free(config); free(config);
} }

View file

@ -1,15 +1,17 @@
#ifndef ARGS_H #ifndef CONFIG_H
#define ARGS_H #define CONFIG_H
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#define ARG_VALID 0 enum arg_error_code
#define ARG_INVALID 1 {
#define ARG_HELP 2 ARG_VALID = 0,
#define ARG_ARCHIVE 3 ARG_INVALID,
ARG_HELP,
};
enum tar_mode enum operation_mode
{ {
UNDEFINED, UNDEFINED,
EXTRACT, EXTRACT,
@ -19,15 +21,13 @@ enum tar_mode
struct config struct config
{ {
/* Defines what action the program will perform */ /* Defines what action the program will perform */
enum tar_mode mode; enum operation_mode mode;
/* File(s) to archive/extract */ /* Archive file name */
char *archive_file;
/* File(s) to archive (Archive mode only) */
char **input_files; char **input_files;
/* Program output after archiving */
char *output_file;
/* Enable or disable verbose mode */ /* Enable or disable verbose mode */
bool verbose; bool verbose;
/* Show the help message */
bool show_help;
}; };
/** /**
@ -38,19 +38,25 @@ struct config
* This function will handle the memory allocation. * This function will handle the memory allocation.
* @return 0 on success, non-zero on failure. * @return 0 on success, non-zero on failure.
*/ */
int args_handler(int argc, char **argv, struct config *config); enum arg_error_code args_handler(int argc, char **argv, struct config *config);
/** Prints the parsed arguments for debugging purposes. /** Prints the parsed arguments for debugging purposes.
* @param options Pointer to args_options structure containing parsed options. * @param options Pointer to args_options structure containing parsed options.
*/ */
void print_config(struct config *config); void print_config(const struct config *config);
/* Prints the usage information for the program. /* Prints the different information messages for the program.
*/ */
void print_usage(void); void print_usage(void);
void print_invalid_option(void);
/* @brief Check if the config is valid
* @return true if valid, false otherwise
*/
bool validate_config(struct config *cfg);
/* /*
*/ */
void config_destroy(struct config *config); void destroy_config(struct config *config);
#endif /* ARGS_H */ #endif /* CONFIG_H */

View file

@ -1,9 +1,13 @@
#ifndef EXTRACT_H #ifndef EXTRACT_H
#define EXTRACT_H #define EXTRACT_H
#include "tar.h"
#include <stdbool.h>
/* @brief Extract and write files from `archive_name` /* @brief Extract and write files from `archive_name`
* @return 0 on success, the corresponding error code otherwise * @return 0 on success, the corresponding error code otherwise
*/ */
int extract(char *archive_name); int extract(char *archive_name, bool verbose);
#endif // EXTRACT_H #endif // EXTRACT_H

View file

@ -1 +1,34 @@
#include "archive.h"
#include "config.h"
#include "extract.h"
int main(int argc, char **argv)
{
// Handle args
struct config config;
enum arg_error_code err = args_handler(argc, argv, &config);
switch (err)
{
case ARG_VALID:
break;
case ARG_INVALID:
print_invalid_option();
return 1;
case ARG_HELP:
print_usage();
return 0;
}
if (config.mode == EXTRACT)
{
extract(config.archive_file, config.verbose);
}
else if (config.mode == ARCHIVE)
{
archive(config.archive_file, config.input_files, config.verbose);
}
return 0;
}