diff --git a/inc/compress.h b/inc/compress.h index 70bfebd..554c8a4 100644 --- a/inc/compress.h +++ b/inc/compress.h @@ -3,6 +3,12 @@ #include "huftree.h" -void compress(const char* filepath); +#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*); #endif diff --git a/src/compress.c b/src/compress.c index 448d020..fd7df18 100644 --- a/src/compress.c +++ b/src/compress.c @@ -1,6 +1,8 @@ #include "compress.h" #include "common.h" +#include "buffer.h" #include "display.h" +#include "assert.h" #include #include #include @@ -8,13 +10,21 @@ /** * Calculer un tableau de fréquences d'apparition des - * caractères ASCII dans le fichier de chemin donné + * caractères ASCII dans le fichier donné. Le fichier + * donné doit être ouvert au moins en lecture * * (résultat à libérer avec `free`) */ -static double* _createFrequencies(const char*); +static double* _createFrequencies(FILE*); -double* _createFrequencies(const char* filepath) { +/** + * Encoder le fichier d'entrée vers le fichier de sortie + * en suivant les étiquettes passées. Les étiquettes + * peuvent être calculées avec labelTree() + */ +void _encodeFromTable(char**, FILE*, FILE*); + +double* _createFrequencies(FILE* file) { double* frequencies = malloc(NUM_CHARS * sizeof(*frequencies)); int totalChars = 0; @@ -22,29 +32,15 @@ double* _createFrequencies(const char* filepath) { frequencies[i] = 0; } - // Ouverture du fichier en lecture seule, et comptage - // des occurences de chaque caractère ASCII ainsi que - // du nombre total de caractères - FILE* file = fopen(filepath, "r"); - - if (file == NULL) { - fprintf( - stderr, "Impossible d'ouvrir '%s' : %s\n", - filepath, strerror(errno) - ); - - exit(1); - } - - char current; + int current; + // Lecture du fichier caractère par caractère et comptage while ((current = fgetc(file)) != EOF) { - frequencies[(size_t) current]++; + assert(current >= 0 && current < NUM_CHARS); + frequencies[current]++; totalChars++; } - fclose(file); - // Conversion des effectifs des caractères en fréquences for (size_t i = 0; i < NUM_CHARS; i++) { frequencies[i] /= totalChars; @@ -53,11 +49,53 @@ double* _createFrequencies(const char* filepath) { return frequencies; } -void compress(const char* filepath) { - printVerbose("Compression du fichier '%s'...\n", filepath); - printVerbose("Calcul des fréquences d'apparition des caractères.\n"); +void _encodeFromTable(char** labels, FILE* input, FILE* output) { + Buffer buffer = createBuffer(output); + int current; - double* frequencies = _createFrequencies(filepath); + // Lecture du fichier d'entrée caractère par caractère + while ((current = fgetc(input)) != EOF) { + assert(current >= 0 && current < NUM_CHARS); + char* label = labels[current]; + assert(label != NULL); + + // Ajout du label dans le buffer, caractère par caractère + // vidant progressivement le buffer + while (*label != '\0') { + pushToBuffer(*label == '1', &buffer); + label++; + } + } + + // Écriture du dernier octet dans le fichier + // (pas toujours plein) + flushBuffer(&buffer); +} + +int compress(const char* inputpath, const char* outputpath) { + // FIXME: gérer le fichier vide + // FIXME: gérer les fichiers avec un seul type de caractère + + 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 + printVerbose("Calcul des fréquences d'apparition des caractères.\n"); + double* frequencies = _createFrequencies(input); if (isVerbose()) { printFrequenciesTable(frequencies, NUM_CHARS); @@ -76,12 +114,23 @@ void compress(const char* filepath) { printVerbose("\nÉtiquetage des feuilles de l'arbre.\n"); char** labels = createTreeLabels(tree); - freeTree(tree); - if (isVerbose()) { printLabelsTable(labels, NUM_CHARS); } + printVerbose("\nÉcriture de l'arbre dans la sortie.\n"); + writeTree(tree, output); + + printVerbose("Écriture des données compressées du fichier.\n"); + rewind(input); + _encodeFromTable(labels, input, output); + + freeTree(tree); freeTreeLabels(labels); labels = NULL; + + fclose(input); + fclose(output); + + return COMPRESS_OK; } diff --git a/src/main.c b/src/main.c index 75ce12c..b1ad4af 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include "compress.h" #include #include +#include /** * Définition de la configuration de Argp, qui affichera @@ -75,16 +76,60 @@ int main(int argc, char** argv) { // Passage en mode verbeux si demandé setVerbose(args.verbose); - // Compression de chaque fichier en argument - char** filepath = args.files; + // Compression des fichiers en argument + char** files = args.files; - while (*filepath != NULL) { - compress(*filepath); - filepath++; + while (*files != NULL) { + char* input = *files; - // Ligne de séparation entre les différents fichier - if (*filepath != NULL) { + // 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"); } } + + return EXIT_SUCCESS; }