From f5522c4036a55e565b596225ed936924bb453514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre=20=E2=9C=8F?= Date: Fri, 28 Oct 2016 16:27:25 +0200 Subject: [PATCH] =?UTF-8?q?Am=C3=A9lioration=20de=20l'interface=20de=20la?= =?UTF-8?q?=20ligne=20de=20commande?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inc/compress.h | 9 +-- inc/huftree.h | 7 +- src/compress.c | 55 +++++---------- src/huftree.c | 6 +- src/main.c | 182 +++++++++++++++++++++++++------------------------ 5 files changed, 119 insertions(+), 140 deletions(-) diff --git a/inc/compress.h b/inc/compress.h index 554c8a4..c58fcea 100644 --- a/inc/compress.h +++ b/inc/compress.h @@ -2,13 +2,8 @@ #define __IN303_COMPRESS_H__ #include "huftree.h" +#include -#define COMPRESS_OK 0 -#define COMPRESS_READ_ERROR 1 -#define COMPRESS_WRITE_ERROR 2 -#define COMPRESS_OPEN_INPUT_ERROR 3 -#define COMPRESS_OPEN_OUTPUT_ERROR 4 - -int compress(const char*, const char*); +void compress(FILE* source, FILE* dest); #endif diff --git a/inc/huftree.h b/inc/huftree.h index bff35e6..ca579f3 100644 --- a/inc/huftree.h +++ b/inc/huftree.h @@ -2,6 +2,7 @@ #define __IN303_HUFTREE_H__ #include +#include "buffer.h" #include /** @@ -47,10 +48,10 @@ struct HufTree { HufTree createTree(double* frequencies); /** - * Écrit une représentation binaire de l'arbre dans le fichier - * passé en paramètre + * Écrit une représentation binaire de l'arbre dans le + * tampon passé en paramètre */ -void writeTree(HufTree, FILE*); +void writeTree(HufTree, Buffer*); /** * Reconstruit un arbre de Huffman à partir du fichier passé diff --git a/src/compress.c b/src/compress.c index fd7df18..adf46e3 100644 --- a/src/compress.c +++ b/src/compress.c @@ -22,7 +22,7 @@ static double* _createFrequencies(FILE*); * en suivant les étiquettes passées. Les étiquettes * peuvent être calculées avec labelTree() */ -void _encodeFromTable(char**, FILE*, FILE*); +void _encodeFromTable(char**, FILE*, Buffer*); double* _createFrequencies(FILE* file) { double* frequencies = malloc(NUM_CHARS * sizeof(*frequencies)); @@ -49,12 +49,11 @@ double* _createFrequencies(FILE* file) { return frequencies; } -void _encodeFromTable(char** labels, FILE* input, FILE* output) { - Buffer buffer = createBuffer(output); +void _encodeFromTable(char** labels, FILE* source, Buffer* output) { int current; // Lecture du fichier d'entrée caractère par caractère - while ((current = fgetc(input)) != EOF) { + while ((current = fgetc(source)) != EOF) { assert(current >= 0 && current < NUM_CHARS); char* label = labels[current]; assert(label != NULL); @@ -62,40 +61,20 @@ void _encodeFromTable(char** labels, FILE* input, FILE* output) { // Ajout du label dans le buffer, caractère par caractère // vidant progressivement le buffer while (*label != '\0') { - pushToBuffer(*label == '1', &buffer); + pushToBuffer(*label == '1', output); label++; } } - - // Écriture du dernier octet dans le fichier - // (pas toujours plein) - flushBuffer(&buffer); } -int compress(const char* inputpath, const char* outputpath) { +void compress(FILE* source, FILE* dest) { // FIXME: gérer le fichier vide - // FIXME: gérer les fichiers avec un seul type de caractère + // FIXME: gérer les fichiers ne contenant qu'un seul type de caractère + // FIXME: gérer l'entrée depuis stdin (pas de double lecture) - printVerbose( - "Compression du fichier '%s' en '%s'...\n", - inputpath, outputpath - ); - - // Ouverture de l'entrée en lecture et la sortie en écriture - FILE* input; - FILE* output; - - if ((input = fopen(inputpath, "r")) == NULL) { - return COMPRESS_OPEN_INPUT_ERROR; - } - - if ((output = fopen(outputpath, "w")) == NULL) { - return COMPRESS_OPEN_OUTPUT_ERROR; - } - - // Comptage des caractères et calcul des fréquences d'apparition + // Calcul de la clef de codage à partir de la source printVerbose("Calcul des fréquences d'apparition des caractères.\n"); - double* frequencies = _createFrequencies(input); + double* frequencies = _createFrequencies(source); if (isVerbose()) { printFrequenciesTable(frequencies, NUM_CHARS); @@ -118,19 +97,19 @@ int compress(const char* inputpath, const char* outputpath) { printLabelsTable(labels, NUM_CHARS); } + // Écriture des données compressées vers la sortie + Buffer output = createBuffer(dest); + printVerbose("\nÉcriture de l'arbre dans la sortie.\n"); - writeTree(tree, output); + writeTree(tree, &output); printVerbose("Écriture des données compressées du fichier.\n"); - rewind(input); - _encodeFromTable(labels, input, output); + rewind(source); + _encodeFromTable(labels, source, &output); + // Vidage du dernier octet du tampon et nettoyage + flushBuffer(&output); freeTree(tree); freeTreeLabels(labels); labels = NULL; - - fclose(input); - fclose(output); - - return COMPRESS_OK; } diff --git a/src/huftree.c b/src/huftree.c index 0d769e1..da6d789 100644 --- a/src/huftree.c +++ b/src/huftree.c @@ -146,10 +146,8 @@ void _findMinimalVertices( } } -void writeTree(HufTree tree, FILE* output) { - Buffer buffer = createBuffer(output); - _writeVertex(*tree.root, &buffer); - flushBuffer(&buffer); +void writeTree(HufTree tree, Buffer* buffer) { + _writeVertex(*tree.root, buffer); } void _writeVertex(HufVertex vertex, Buffer* buffer) { diff --git a/src/main.c b/src/main.c index b1ad4af..2703cc2 100644 --- a/src/main.c +++ b/src/main.c @@ -1,31 +1,19 @@ #include "common.h" #include "compress.h" #include +#include #include #include -/** - * Définition de la configuration de Argp, qui affichera - * ces informations dans la page d'aide entre autres - */ -static char doc[] = "Compresse ou décompresse des fichiers en " - "utilisant l'algorithme de Huffman"; -static char args_doc[] = "FICHIER..."; -static struct argp_option options[] = { - { - "verbose", 'V', 0, 0, - "Afficher des informations de débogage sur la compression" - }, - {0} -}; - /** * Contient les valeurs des arguments (options et opérandes) * passées au programme et interprétées via Argp */ typedef struct Args { int verbose; - char** files; + int compress; + FILE* source; + FILE* dest; } Args; /** @@ -36,100 +24,118 @@ static error_t parse_opt(int key, char* arg, struct argp_state* state) { Args* args = state->input; switch (key) { - case 'V': + case 'v': args->verbose = TRUE; break; - case ARGP_KEY_ARG: - // Consommation de tous les fichiers en argument - // et arrêt du parsage - args->files = &state->argv[state->next - 1]; - state->next = state->argc; + case 'd': + args->compress = FALSE; break; - case ARGP_KEY_NO_ARGS: - argp_usage(state); - break; + case ARGP_KEY_ARG: + switch (state->arg_num) { + case 0: + // Premier argument : fichier d'entrée. Si omis, + // utilisation de l'entrée standard + args->source = fopen(arg, "r"); + + if (args->source == NULL) { + argp_error( + state, "%s: %s", + arg, strerror(errno) + ); + } + break; + + case 1: + // Second argument : fichier de sortie. Si omis, + // utilisation de la sortie standard + args->dest = fopen(arg, "w"); + + if (args->dest == NULL) { + argp_error( + state, "%s: %s", + arg, strerror(errno) + ); + } + break; default: + argp_error( + state, "Trop d'arguments - L'argument %s est superflu", + arg + ); + } + + break; + + default: return ARGP_ERR_UNKNOWN; } return 0; } -static struct argp argp = {options, parse_opt, args_doc, doc}; +/** + * Définition de la configuration de Argp, qui affichera + * ces informations dans la page d'aide entre autres + */ +static struct argp_option options[] = { + { + .name = "decompress", + .key = 'd', + .doc = "Décompresse SOURCE vers DEST" + }, + { + .name = "verbose", + .key = 'v', + .doc = "Affiche des informations de débogage" + }, + {0} +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "[SOURCE [DEST]]", + .doc = "Compresse ou décompresse SOURCE vers DEST en utilisant " + "l'algorithme de Huffman (par défaut, compresse SOURCE vers DEST)." +}; int main(int argc, char** argv) { - // Pour pouvoir afficher des caractères larges dans le terminal - setlocale(LC_CTYPE, ""); + // Paramètres de langue. Utilisation de la locale de l'utilisateur, + // et des messages traduits dans libc (pour pallier à un bug de argp) + setlocale(LC_ALL, ""); + textdomain("libc"); - // Interprétation des arguments passés au programme par Argv, - // suivant la configuration définie au dessus - Args args; + // Valeurs par défaut des arguments optionnels + Args args = { + .verbose = FALSE, + .compress = TRUE, + .source = stdin, + .dest = stdout + }; - args.verbose = FALSE; - args.files = NULL; - - argp_parse(&argp, argc, argv, 0, 0, &args); + // Interprétation des arguments passés au programme, résultat + // dans `args`. Arrêt en cas d'erreur + if (argp_parse(&argp, argc, argv, 0, 0, &args)) { + return EXIT_FAILURE; + } // Passage en mode verbeux si demandé setVerbose(args.verbose); - // Compression des fichiers en argument - char** files = args.files; - - while (*files != NULL) { - char* input = *files; - - // Compression du fichier xx en xx.huf - // TODO: rendre le nom du fichier de sortie configurable - char* output = malloc((strlen(input) + 5) * sizeof(*output)); - strcpy(output, input); - strcat(output, ".huf"); - - switch (compress(input, output)) { - case COMPRESS_READ_ERROR: - fprintf( - stderr, - "Erreur : impossible de lire le fichier '%s' - %s\n", - input, strerror(errno) - ); - return COMPRESS_READ_ERROR; - - case COMPRESS_WRITE_ERROR: - fprintf( - stderr, - "Erreur : impossible d'écrire dans le fichier '%s' - %s\n", - output, strerror(errno) - ); - return COMPRESS_WRITE_ERROR; - - case COMPRESS_OPEN_INPUT_ERROR: - fprintf( - stderr, - "Erreur : impossible d'ouvrir le fichier '%s' - %s\n", - input, strerror(errno) - ); - return COMPRESS_OPEN_INPUT_ERROR; - - case COMPRESS_OPEN_OUTPUT_ERROR: - fprintf( - stderr, - "Erreur : impossible d'ouvrir le fichier '%s' - %s\n", - output, strerror(errno) - ); - return COMPRESS_OPEN_OUTPUT_ERROR; - } - - free(output); - files++; - - // Ligne de séparation entre les différents fichiers - if (*files != NULL) { - printVerbose("\n"); - } + // Compression ou décompression du flux d'entrée vers le flux de sortie + if (args.compress) { + compress(args.source, args.dest); + } else { + fprintf(stderr, "Décompression non-implémentée.\n"); + return EXIT_FAILURE; } + // Fermeture des flux et sortie + fclose(args.source); + fclose(args.dest); + return EXIT_SUCCESS; }