Le code source de la démo se trouve dans le répertoire zlib-1.2.11\contrib\minizip. Voici une analyse simplifiée des fonctions clés :
/* Extrait de miniunz.c - Démonstration de la bibliothèque MiniZip */
/* Modification des noms et structure pour la clarté */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include "unzip.h"
#define SENSIBILITE_CAS (0)
#define TAILLE_TAMPON_ECRITURE (8192)
#define NOM_FICHIER_MAX (256)
/* Met à jour l'horodatage d'un fichier */
void modifier_horodatage_fichier(const char *nom_fichier, uLong date_dos, tm_unz date_tm) {
#ifdef _WIN32
HANDLE hFichier;
FILETIME ftm, ftLocal, ftCreation, ftDernierAcces, ftDerniereEcriture;
hFichier = CreateFileA(nom_fichier, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
GetFileTime(hFichier, &ftCreation, &ftDernierAcces, &ftDerniereEcriture);
DosDateTimeToFileTime((WORD)(date_dos >> 16), (WORD)date_dos, &ftLocal);
LocalFileTimeToFileTime(&ftLocal, &ftm);
SetFileTime(hFichier, &ftm, &ftDernierAcces, &ftm);
CloseHandle(hFichier);
#else
struct utimbuf ut;
struct tm nouvelle_date;
nouvelle_date.tm_sec = date_tm.tm_sec;
nouvelle_date.tm_min = date_tm.tm_min;
nouvelle_date.tm_hour = date_tm.tm_hour;
nouvelle_date.tm_mday = date_tm.tm_mday;
nouvelle_date.tm_mon = date_tm.tm_mon;
if (date_tm.tm_year > 1900) {
nouvelle_date.tm_year = date_tm.tm_year - 1900;
} else {
nouvelle_date.tm_year = date_tm.tm_year;
}
nouvelle_date.tm_isdst = -1;
ut.actime = ut.modtime = mktime(&nouvelle_date);
utime(nom_fichier, &ut);
#endif
}
/* Crée un répertoire */
int creer_repertoire(const char *nom_rep) {
int resultat = 0;
#ifdef _WIN32
resultat = _mkdir(nom_rep);
#elif unix || __APPLE__
resultat = mkdir(nom_rep, 0775);
#endif
return resultat;
}
/* Crée des répertoires imbriqués si nécessaire */
int creer_chemin_repertoire(char *nouveau_chemin) {
char *tampon;
char *pointeur;
int longueur = (int)strlen(nouveau_chemin);
if (longueur <= 0) return 0;
tampon = (char*)malloc(longueur + 1);
if (tampon == NULL) {
printf("Erreur d'allocation mémoire\n");
return UNZ_INTERNALERROR;
}
strcpy(tampon, nouveau_chemin);
if (tampon[longueur - 1] == '/') {
tampon[longueur - 1] = '\0';
}
if (creer_repertoire(tampon) == 0) {
free(tampon);
return 1;
}
pointeur = tampon + 1;
while (1) {
char temporaire;
while (*pointeur && *pointeur != '\\' && *pointeur != '/') pointeur++;
temporaire = *pointeur;
*pointeur = 0;
if ((creer_repertoire(tampon) == -1) && (errno == ENOENT)) {
printf("Impossible de créer le répertoire %s\n", tampon);
free(tampon);
return 0;
}
if (temporaire == 0) break;
*pointeur++ = temporaire;
}
free(tampon);
return 1;
}
/* Liste les fichiers dans une archive ZIP */
int lister_fichiers(unzFile handle_archive) {
uLong compteur;
unz_global_info64 info_globale;
int erreur;
erreur = unzGetGlobalInfo64(handle_archive, &info_globale);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGetGlobalInfo\n", erreur);
}
printf(" Longueur Méthode Taille Ratio Date Heure CRC-32 Nom\n");
for (compteur = 0; compteur < info_globale.number_entry; compteur++) {
char nom_fichier_archive[256];
unz_file_info64 info_fichier;
uLong taux = 0;
const char *methode_texte;
char indicateur_chiffrement = ' ';
erreur = unzGetCurrentFileInfo64(handle_archive, &info_fichier, nom_fichier_archive, sizeof(nom_fichier_archive), NULL, 0, NULL, 0);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGetCurrentFileInfo\n", erreur);
break;
}
if (info_fichier.uncompressed_size > 0) {
taux = (uLong)((info_fichier.compressed_size * 100) / info_fichier.uncompressed_size);
}
if ((info_fichier.flag & 1) != 0) {
indicateur_chiffrement = '*';
}
if (info_fichier.compression_method == 0) {
methode_texte = "Stocké";
} else if (info_fichier.compression_method == Z_DEFLATED) {
uInt niveau = (uInt)((info_fichier.flag & 0x6) / 2);
if (niveau == 0) methode_texte = "Defl:N";
else if (niveau == 1) methode_texte = "Defl:X";
else methode_texte = "Defl:F";
} else if (info_fichier.compression_method == Z_BZIP2ED) {
methode_texte = "BZip2 ";
} else {
methode_texte = "Inconnu";
}
// Affichage des détails - code simplifié pour la démonstration
if ((compteur + 1) < info_globale.number_entry) {
erreur = unzGoToNextFile(handle_archive);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGoToNextFile\n", erreur);
break;
}
}
}
return 0;
}
Une implémentation personnalisée en C++ peut encapsuler ces fonctionnalités. Voici une classe réécrite avec des noms et une logique modifiés :
// DecompressionOutil.h - En-tête de la classe de décompression
#pragma once
#include <direct.h>
#include "unzip.h"
#define TAILLE_TAMPON 8192
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#ifdef __APPLE__
#define FONCTION_OUVRIR(nom, mode) fopen(nom, mode)
#define FONCTION_POSITION(stream) ftello(stream)
#define FONCTION_DEPLACER(stream, offset, origine) fseeko(stream, offset, origine)
#else
#define FONCTION_OUVRIR(nom, mode) fopen(nom, mode)
#define FONCTION_POSITION(stream) ftello(stream)
#define FONCTION_DEPLACER(stream, offset, origine) fseeko(stream, offset, origine)
#endif
class DecompressionOutil {
public:
DecompressionOutil();
~DecompressionOutil();
int Extraire(char* chemin_archive, char* mot_de_passe);
int Extraire(char* chemin_archive, char* mot_de_passe, char* dossier_sortie);
int Compresser(); // Fonctionnalité à implémenter
private:
unzFile handle_archive = NULL;
int executer_extraction(unzFile handle, int ignorer_chemin, int ecraser, const char *mot_de_passe);
int extraire_fichier_courant(unzFile handle, const int *ignorer_chemin, int *ecraser, const char *mot_de_passe);
void modifier_horodatage(const char *nom_fichier, uLong date_dos, tm_unz date_tm);
int creer_repertoire(const char *nom_rep);
int creer_chemin_repertoire(char *nouveau_chemin);
char* dossier_extrait;
};
// DecompressionOutil.cpp - Implémentation des méthodes
#include "stdafx.h"
#include "DecompressionOutil.h"
DecompressionOutil::DecompressionOutil() {
dossier_extrait = NULL;
}
DecompressionOutil::~DecompressionOutil() {
}
int DecompressionOutil::Extraire(char *chemin_archive, char *mot_de_passe) {
dossier_extrait = NULL;
if (chemin_archive != NULL) {
handle_archive = unzOpen64(chemin_archive);
}
if (handle_archive == NULL) {
return 1;
}
int resultat = executer_extraction(handle_archive, 0, 1, mot_de_passe);
unzClose(handle_archive);
return resultat;
}
int DecompressionOutil::Extraire(char *chemin_archive, char *mot_de_passe, char *dossier_sortie) {
if (dossier_sortie != NULL && !PathFileExists(CString(dossier_sortie))) {
creer_chemin_repertoire(dossier_sortie);
}
if (chemin_archive != NULL) {
handle_archive = unzOpen64(chemin_archive);
}
if (handle_archive == NULL) {
return 1;
}
dossier_extrait = dossier_sortie;
int resultat = executer_extraction(handle_archive, 0, 1, mot_de_passe);
unzClose(handle_archive);
dossier_extrait = NULL;
return resultat;
}
int DecompressionOutil::executer_extraction(unzFile handle, int ignorer_chemin, int ecraser, const char *mot_de_passe) {
uLong compteur;
unz_global_info64 info_globale;
int erreur;
FILE* fichier_sortie = NULL;
erreur = unzGetGlobalInfo64(handle, &info_globale);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGetGlobalInfo\n", erreur);
}
for (compteur = 0; compteur < info_globale.number_entry; compteur++) {
if (extraire_fichier_courant(handle, &ignorer_chemin, &ecraser, mot_de_passe) != UNZ_OK) {
break;
}
if ((compteur + 1) < info_globale.number_entry) {
erreur = unzGoToNextFile(handle);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGoToNextFile\n", erreur);
break;
}
}
}
return 0;
}
int DecompressionOutil::extraire_fichier_courant(unzFile handle, const int *ignorer_chemin, int *ecraser, const char *mot_de_passe) {
char nom_fichier_archive[256];
char *nom_sans_chemin;
char *pointeur;
int erreur = UNZ_OK;
FILE *fichier_sortie = NULL;
void *tampon;
uInt taille_tampon;
unz_file_info64 info_fichier;
erreur = unzGetCurrentFileInfo64(handle, &info_fichier, nom_fichier_archive, sizeof(nom_fichier_archive), NULL, 0, NULL, 0);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzGetCurrentFileInfo\n", erreur);
return erreur;
}
if (dossier_extrait) {
char chemin_temporaire[256];
sprintf(chemin_temporaire, "%s%s", dossier_extrait, nom_fichier_archive);
strncpy(nom_fichier_archive, chemin_temporaire, 256);
}
taille_tampon = TAILLE_TAMPON;
tampon = (void*)malloc(taille_tampon);
if (tampon == NULL) {
printf("Erreur d'allocation mémoire\n");
return UNZ_INTERNALERROR;
}
pointeur = nom_sans_chemin = nom_fichier_archive;
while ((*pointeur) != '\0') {
if (((*pointeur) == '/') || ((*pointeur) == '\\')) {
nom_sans_chemin = pointeur + 1;
}
pointeur++;
}
if ((*nom_sans_chemin) == '\0') {
if ((*ignorer_chemin) == 0) {
printf("Création du répertoire: %s\n", nom_fichier_archive);
creer_repertoire(nom_fichier_archive);
}
} else {
const char* nom_ecriture;
int ignorer = 0;
if ((*ignorer_chemin) == 0) {
nom_ecriture = nom_fichier_archive;
} else {
nom_ecriture = nom_sans_chemin;
}
erreur = unzOpenCurrentFilePassword(handle, mot_de_passe);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzOpenCurrentFilePassword\n", erreur);
}
if (((*ecraser) == 0) && (erreur == UNZ_OK)) {
char reponse = 0;
FILE* test_existence;
test_existence = FONCTION_OUVRIR(nom_ecriture, "rb");
if (test_existence != NULL) {
fclose(test_existence);
do {
char saisie[128];
int lecture;
printf("Le fichier %s existe. Écraser ? [o]ui, [n]on, [T]ous: ", nom_ecriture);
lecture = scanf("%1s", saisie);
if (lecture != 1) {
exit(EXIT_FAILURE);
}
reponse = saisie[0];
if ((reponse >= 'a') && (reponse <= 'z')) {
reponse -= 0x20;
}
} while ((reponse != 'O') && (reponse != 'N') && (reponse != 'T'));
}
if (reponse == 'N') {
ignorer = 1;
}
if (reponse == 'T') {
*ecraser = 1;
}
}
if ((ignorer == 0) && (erreur == UNZ_OK)) {
fichier_sortie = FONCTION_OUVRIR(nom_ecriture, "wb");
if ((fichier_sortie == NULL) && ((*ignorer_chemin) == 0) && (nom_sans_chemin != (char*)nom_fichier_archive)) {
char caractere = *(nom_sans_chemin - 1);
*(nom_sans_chemin - 1) = '\0';
creer_chemin_repertoire((char*)nom_ecriture);
*(nom_sans_chemin - 1) = caractere;
fichier_sortie = FONCTION_OUVRIR(nom_ecriture, "wb");
}
if (fichier_sortie == NULL) {
printf("Erreur lors de l'ouverture de %s\n", nom_ecriture);
}
}
if (fichier_sortie != NULL) {
printf(" Extraction: %s\n", nom_ecriture);
do {
erreur = unzReadCurrentFile(handle, tampon, taille_tampon);
if (erreur < 0) {
printf("Erreur %d avec l'archive dans unzReadCurrentFile\n", erreur);
break;
}
if (erreur > 0) {
if (fwrite(tampon, erreur, 1, fichier_sortie) != 1) {
printf("Erreur lors de l'écriture du fichier extrait\n");
erreur = UNZ_ERRNO;
break;
}
}
} while (erreur > 0);
if (fichier_sortie) {
fclose(fichier_sortie);
}
if (erreur == 0) {
modifier_horodatage(nom_ecriture, info_fichier.dosDate, info_fichier.tmu_date);
}
}
if (erreur == UNZ_OK) {
erreur = unzCloseCurrentFile(handle);
if (erreur != UNZ_OK) {
printf("Erreur %d avec l'archive dans unzCloseCurrentFile\n", erreur);
}
} else {
unzCloseCurrentFile(handle);
}
}
free(tampon);
return erreur;
}
void DecompressionOutil::modifier_horodatage(const char *nom_fichier, uLong date_dos, tm_unz date_tm) {
// Implémentation similaire à celle montrée précédemment, avec adaptations
}
int DecompressionOutil::creer_repertoire(const char *nom_rep) {
// Implémentation adaptée
return 0;
}
int DecompressionOutil::creer_chemin_repertoire(char *nouveau_chemin) {
// Implémentation adaptée
return 1;
}
int DecompressionOutil::Compresser() {
return 0;
}
Pour utiliser cette classe, voici un exemple :
// Exemple d'utilisation
DecompressionOutil* outil = new DecompressionOutil();
// Extraction avec le répertoire par défaut
// outil->Extraire("chemin/vers/archive.zip", NULL);
// Extraction vers un dossier spécifique
outil->Extraire("chemin/vers/archive.zip", NULL, "./dossier_sortie/");
Notez que la bibliothèque zlibwapi.dll fournie est en 32 bits, ce qui peut causer des problèmes. Pour une compatibilité 64 bits, il est nécessaire de compiler la bibliothèque à partir des sources, disponibles dans le répertoire zlib-1.2.11\contrib\vstudio. Lors de la décompression de fichiers protégés par mot de passe, des erreurs comme UNZ_PARAMERROR ou UNZ_BADZIPFILE peuvent survenir, indiquant des problèmes avec l'archive ou le mot de passe.