Utilisation de la LibZip

Loïc BARTOLETTI

Tutoriel sur l'utilisation de la bibliothèque LibZip.
Commentez

- Titre : Utilisation de la LibZip

- Auteur : Loïc BARTOLETTI

- Parution : 10 décembre 2011

- Licence : Copyright ® 2011 Loïc BARTOLETTI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 2. Exemple de Code

- 2-A. Lister le contenu d'une archive zip

- 2-A-1. Algorithme

- 2-A-2. Code source

- 2-B. Effacer un fichier/dossier d'une archive zip

- 2-B-1. Algorithme

- 2-B-2. Code source

- 2-C. Renommer un fichier/dossier d'une archive zip

- 2-C-1. Algorithme

- 2-C-2. Code source

- 2-D. Remplacer un fichier d'une archive zip

- 2-D-1. Algorithme

- 2-D-2. Code source

- 2-E. Extraire un fichier/dossier d'une archive zip

- 2-E-1. Algorithme

- 2-E-2. Code source

- 2-F. Décompresser une archive zip

- 2-F-1. Algorithme

- 2-F-2. Code source

- 2-G. Créer une archive zip

- 2-G-1. Algorithme

- 2-G-2. Code source

- 3. Conclusion

- 4. Remerciements

1 - Introduction

Pour utiliser les archives zip plusieurs bibliothèques sont disponibles. Dans ce tutoriel nous allons nous intéresser à la libzip. Cette bibliothèque est écrite en C et permet de lire, créer ou modifier les archives zip. À ce jour, la dernière version est la 0.10 qui date du 18/03/2010. C'est cette version qui a été utilisée pour ce tutoriel. Le manuel de la bibliothèque est très clair et quasiment toutes les fonctions sont détaillées. Il se trouve en ligne ou via man libzip pour la bibliothèque en général et man "nom de la commande".
Ce document présente comment :

- lister le contenu d'une archive zip ;

- effacer un fichier/dossier d'une archive zip ;

- renommer un fichier/dossier d'une archive zip ;

- remplacer un fichier d'une archive zip  ;

- extraire un fichier/dossier d'une archive zip ;

- décompresser une archive zip ;

- créer une archive zip.

Tous les codes sont en C.
Les codes ci-dessous peuvent faire appel à des fonctions non présentes dans cette page mais le tout est disponible dans le code source C présent à la fin de la page. Le tutoriel n'est disponible que pour les systèmes UNIX/POSIX. Il ne devrait pas être difficile à adapter pour la plateforme Windows. Le code présente des appels particuliers POSIX et considère que les chemins sont de type UNIX '/' et non de type Windows '\'. Tout a été testé sur FreeBSD, si des modifications sont à apporter pour d'autres systèmes n'hésitez pas à me le faire savoir.

2 - Exemple de Code

2-A - Lister le contenu d'une archive zip

2-A-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Compter le nombre d'éléments dans l'archive (Fonction zip_get_num_files).
Pour chaque élément, afficher son nom (Fonction zip_get_name).
Fermer l'archive (Fonction zip_close).

2-A-2 - Code source

Afficher le contenu d'une archive Zip

/**
* \fn static int afficherFichierZip(const char* fichierZip)
* \brief Affiche les fichiers à l'intérieur de l'archive Zip.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int afficherFichierZip(const char* fichierZip)
{

if(fichierZip == NULL)
return ZIPZAP_FAILURE;

int err = 0;
struct zip *f_zip=NULL;

f_zip = zip_open(fichierZip, ZIP_CHECKCONS, &err); /* on ouvre l'archive zip */

/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}

/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}

/* on récupère le nombre de fichiers dans l'archive zip */
int count = zip_get_num_files(f_zip);
if(count==-1)
{

printf("Erreur à la lecture du fichier %s\n", fichierZip);
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}

int i = 0;
printf("Nombre de fichiers dans l'archive : %d\n",count);

for(i=0; i<count; i++)
{
/* on utilise la position "i" pour récupérer le nom des fichiers */
printf("%s\n", zip_get_name(f_zip, i, ZIP_FL_UNCHANGED));
}

zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_SUCCESS;

}

2-B - Effacer un fichier/dossier d'une archive zip

2-B-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Rechercher le fichier/dossier dans l'archive (Fonction zip_name_locate).
Si c'est un dossier
pour chaque élément du dossier, le supprimer (Fonction zip_delete).
Sinon
le supprimer (Fonction zip_delete).
Fermer l'archive (Fonction zip_close).

2-B-2 - Code source

Effacer un fichier/dossier d'une archive zip

/**
* \fn static int effacerFichierZip(const char* fichierZip, const char* fichier)
* \brief Efface le fichier/dossier "fichier" de l'archive zip "fichierZip".
*
* \param fichierZip Archive zip contenant le fichier à effacer.
* \param fichier Nom du fichier/dossier à effacer.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int effacerFichierZip(const char* fichierZip, const char* fichier)
{
int visu = 0;
struct zip * f_zip=NULL;
int err = 0;

f_zip=zip_open(fichierZip,ZIP_CREATE,&err);
/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}

visu=zip_name_locate(f_zip,fichier,0);
if (visu==-1) /* recherche de l'emplacement du fichier dans le zip */
{
printf("Le fichier %s n'existe pas dans %s\n", fichier, fichierZip);
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;

}

/* si on demande la suppression d'un répertoire */
if(fichier[strlen(fichier)-1]=='/')
{
char *ptr = NULL;
int i = 0;
int count = zip_get_num_files(f_zip);
if(count==-1)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
zip_close(f_zip);
f_zip = NULL;
ptr = NULL;
return ZIPZAP_FAILURE;
}

for(i=0; i<count; i++)
{

/* on parcourt tous les fichiers du zip pour savoir si un fichier est dans le dossier à supprimer */
if( (ptr=strstr(zip_get_name(f_zip, i, ZIP_FL_UNCHANGED), fichier)) != NULL)
{
/* on efface le fichier positionné à l'index i */
if(zip_delete(f_zip,i) == -1)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
ptr = NULL;
return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été supprimé dans %s\n", zip_get_name(f_zip, i, ZIP_FL_UNCHANGED), fichierZip);

}
}
ptr = NULL;
}
else
{
/* sinon on supprime simplement le fichier trouvé par zip_name_locate à la position visu */
if(zip_delete(f_zip,visu) == -1)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été supprimé dans %s\n", fichier, fichierZip);
}

/* lecture terminée, fermeture de l'archive */
zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_SUCCESS;
}

2-C - Renommer un fichier/dossier d'une archive zip

2-C-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Rechercher le fichier/dossier dans l'archive (Fonction zip_name_locate).
Si c'est un dossier
pour chaque élément du dossier, le renommer (Fonction zip_rename).
Sinon
le renommer (Fonction zip_rename).
Fermer l'archive (Fonction zip_close).

2-C-2 - Code source

Renommer un fichier/dossier d'une archive

/**
* \fn static int renommerFichierZip(const char* fichierZip, const char* fichierFrom, const char* fichierTo)
* \brief Efface le fichier/dossier "fichierFrom" en "FichierTo" de l'archive zip "fichierZip".
*
* \param fichierZip Archive zip contenant le fichier à effacer.
* \param fichierFrom Nom du fichier/dossier en entrée.
* \param fichierTo Nom du fichier/dossier en sortie.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int renommerFichierZip(const char* fichierZip, const char* fichierFrom, const char* fichierTo)
{
int visu = 0;
struct zip * f_zip=NULL;
int err = 0;

f_zip=zip_open(fichierZip,ZIP_CREATE,&err);
/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}

/* recherche de l'emplacement du fichier dans le zip */
visu=zip_name_locate(f_zip,fichierFrom,0);
if (visu==-1)
{
printf("Le fichier %s n'existe pas dans %s\n", fichierFrom, fichierZip);
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;

}

/* si on demande de renommer un répertoire */
if(fichierFrom[strlen(fichierFrom)-1]=='/')
{
char *ptr = NULL;
int i = 0;
int count = zip_get_num_files(f_zip);
if(count==-1)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}
for(i=0; i<count; i++)
{

/* on parcourt tous les fichiers du zip pour savoir si un fichier est dans le dossier à supprimer */
if( (ptr=strstr(zip_get_name(f_zip, i, ZIP_FL_UNCHANGED), fichierFrom)) != NULL)
{
/* si c'est le répertoire on lui applique un traitement particulier */
if(strlen(fichierFrom)==strlen(zip_get_name(f_zip, i, ZIP_FL_UNCHANGED)))
{
if(zip_rename(f_zip,i,fichierTo) == -1)
{

printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
ptr = NULL;
return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été renommé en %s dans %s\n", fichierFrom, fichierTo, fichierZip);
}
else
{
/* si c'est un fichier de ce répertoire */
char *buf = NULL;
buf = malloc(FILENAME_MAX*sizeof(char));
if(buf==NULL)
{

printf("Erreur d'allocation mémoire.\n");
zip_close(f_zip);
f_zip = NULL;
FREE(buf);
ptr = NULL;
return ZIPZAP_FAILURE;
}

strcpy(buf,fichierTo);

strcat(buf,zip_get_name(f_zip, i, ZIP_FL_UNCHANGED)+strlen(fichierFrom));
printf("%s\n",buf);
if(zip_rename(f_zip,i,buf) == -1)
{

printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
FREE(buf);
ptr = NULL;

return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été renommé en %s dans %s\n", fichierFrom, fichierTo, fichierZip);
FREE(buf);
}
}
}
ptr = NULL;
}
else
{
/* sinon on renomme simplement le fichier trouvé par zip_name_locate à la position visu */
if(zip_rename(f_zip,visu,fichierTo) == -1)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été renommé en %s dans %s\n", fichierFrom, fichierTo, fichierZip);
}

/* lecture terminée, fermeture de l'archive */

zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_SUCCESS;
}

2-D - Remplacer un fichier d'une archive zip

2-D-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Rechercher le fichier dans l'archive (Fonction zip_name_locate).
Si le fichier n'existe pas et si on a demandé de l'ajouter dans ce cas-là
ajouter le fichier (Fonction zip_add).
Sinon
le remplacer (Fonction zip_replace).
Fermer l'archive (Fonction zip_close).

2-D-2 - Code source

Remplacer le fichier d'une archive

/**
* \fn static int remplacerFichierZip(const char* fichierZip, const char* fichier, int ajout)
* \brief Remplace le fichier/dossier "fichier" dans l'archive zip "fichierZip".
*
* \param fichierZip Archive zip contenant le fichier à effacer.
* \param fichier Nom du fichier/dossier à remplacer.
* \param ajout Si le fichier n'existe pas dans l'archive, on l'ajoute ou non suivant la valeur de ajout (REPLACE_ADD ou REPLACE_NOT_ADD).
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int remplacerFichierZip(const char* fichierZip, const char* fichier, int ajout)
{

/* Source de Troumad original : http://www.developpez.net/forums/d1012322/c-cpp/c/contribuez/faq-modifier-fichier-zip/ */
/* modifié pour ajouter la gestion des erreurs et quelques fonctionnalités */

int visu = 0;
struct zip * f_zip=NULL;
struct zip_source * n_zip=NULL;
int err = 0;

f_zip=zip_open(fichierZip,ZIP_CREATE,&err);
/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}
/* on met dans le zip_source le fichier que l'on veut remplacer */
if((n_zip=zip_source_file(f_zip,fichier, (off_t)0, (off_t)0)) == NULL)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}

/* recherche de l'emplacement du fichier dans le zip */
visu=zip_name_locate(f_zip,fichier,0);
if (visu==-1)
{
printf("Le fichier %s n'existe pas dans %s\n", fichier, fichierZip);
if(ajout==REPLACE_ADD)
{

/* nouveau document dans le fichier zip  : le fichier n'y est pas */
/* c'est là qu'on fixe le nom qu'aura le nouveau document dans le fichier zip */
if(zip_add(f_zip,fichier,n_zip) == -1)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
zip_source_free(n_zip);
n_zip = NULL;
return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été ajouté dans %s\n", fichier, fichierZip);
}
else if(ajout==REPLACE_NOT_ADD)
{

printf("Le fichier %s n'a pas été ajouté ou remplacé dans %s\n", fichier, fichierZip);

}
else
{
printf("Erreur d'utilisation de la fonction %s\n", __FUNCTION__);

}
}
else
{
/* modification d'un document dans le fichier zip : le fichier est déjà dedans */
/* notre document remplace le document qui se trouve à l'emplacement visu */
if(zip_replace(f_zip,visu,n_zip) == -1)
{
printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
zip_source_free(n_zip);
n_zip = NULL;
return ZIPZAP_FAILURE;
}
printf("Le fichier %s a été remplacé dans %s\n", fichier, fichierZip);

}

zip_close(f_zip);
f_zip = NULL;
zip_source_free(n_zip);
n_zip = NULL;

return ZIPZAP_SUCCESS;

}

2-E - Extraire un fichier/dossier d'une archive zip

2-E-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Rechercher le fichier/dossier dans l'archive (Fonction zip_name_locate).
Si c'est un dossier
pour chaque élément du dossier, l'extraire (Fonction extraireFichier -> Fonctions zip_stat, zip_fopen, zip_fread, zip_fclose).
Sinon
l'extraire (Fonction extraireFichier -> Fonctions zip_stat, zip_fopen, zip_fread, zip_fclose).
Fermer l'archive (Fonction zip_close).

2-E-2 - Code source

Extraire un fichier/dossier d'une archive

/**
* \fn static int extraireFichierZip(const char* fichierZip, const char* fichier, const char* repSortie)
* \brief Fonction préparant l'extraction du fichier de l'archive zip.
*
* \param fichierZip Archive zip contenant le fichier à extraire.
* \param fichier Nom du fichier/dossier à extraire.
* \param repSortie Le répertoire où doit être extrait le fichier de l'archive. Si repSortie vaut NULL, le répertoire est l'endroit d'où est lancée la commande.
* \param motdePasse Le mot de passe de l'archive protégée.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int extraireFichierZip(const char* fichierZip, const char* fichier, const char* repSortie, const char* motDePasse)
{

int visu = 0;
struct zip *f_zip=NULL;
int err=0;

f_zip=zip_open(fichierZip, ZIP_CHECKCONS, &err);
/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}

visu=zip_name_locate(f_zip,fichier,0);
if (visu==-1) /* recherche de l'emplacement du fichier dans le zip */
{
printf("Le fichier %s n'existe pas dans %s\n", fichier, fichierZip);
printf("Ajouter le caractère '/' à la fin du nom s'il s'agit d'un dossier\n");
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}

/* si on demande l'extraction d'un répertoire */
/* le dernier caractère est le symbole du répertoire '/' */
if(fichier[strlen(fichier)-1]=='/') /* TODO : ajouter le test pour Windows. Un simple '\' ? */
{
char *ptr = NULL;
int i = 0;
int count = zip_get_num_files(f_zip);
if(count==-1)
{
zip_close(f_zip);
f_zip = NULL;
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}
for(i=0; i<count; i++)
{

if( (ptr=strstr(zip_get_name(f_zip, i, ZIP_FL_UNCHANGED), fichier)) != NULL)
{
/* si le fichier rencontré dans l'archive est le dossier lui-même, on ne l'extrait pas */
if( (strlen(fichier) == strlen(ptr)) && (strcmp(ptr,fichier)==0))
{
printf("Extraction des fichiers du dossier %s\n",ptr);
}
else /* si ce sont ces fichiers, oui */
{

if(extraireFichier(i, f_zip, repSortie, motDePasse)==ZIPZAP_FAILURE)
{

printf("Erreur à l'extraction du fichier %s\n", zip_get_name(f_zip, i, ZIP_FL_UNCHANGED));
zip_close(f_zip);
f_zip = NULL;
ptr = NULL;
return ZIPZAP_FAILURE;
}

}

}
}
ptr = NULL;
}
else
{

if(extraireFichier(visu, f_zip, repSortie, motDePasse)==ZIPZAP_FAILURE)
{
printf("Erreur à l'extraction du fichier %s\n", zip_get_name(f_zip, visu, ZIP_FL_UNCHANGED));
zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_FAILURE;
}

}

zip_close(f_zip);

return ZIPZAP_SUCCESS;
}

Fonction extraireFichier

/**
* \fn static int extraireFichier(int id, struct zip *f_zip, const char *repSortie)
* \brief Fonction ne s'occupant exclusivement que d'extraire le fichier n° "id" de l'archive "f_zip" dans "repSortie". \
Fonction appelée par extraireFichierZip et decompresserFichierZip.
*
* \param id Position du fichier à extraire de l'archive.
* \param f_zip Archive zip contenant le fichier à extraire.
* \param repSortie Le répertoire où doit être extrait le fichier de l'archive. Si repSortie vaut NULL, le répertoire est l'endroit d'où est lancée la commande.
* \param motdePasse Le mot de passe de l'archive protégée.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int extraireFichier(int id, struct zip *f_zip, const char *repSortie, const char* motDePasse)
{

/* Code d'origine : */
/* http://forum.ubuntu-fr.org/viewtopic.php?pid=2596893#p2596893 Visité le 02/01/2011 */
/* http://www.siteduzero.com/forum-83-372372-p1-c-zip-et-unzip.html#r3519929 */
/* modifié pour ajouter la gestion des erreurs et quelques fonctionnalités */

struct zip_stat file_stat;
struct zip_file *file_zip=NULL;
zip_stat_index(f_zip, id, 0, &file_stat);

/* 4. pour connaître la taille du fichier et ainsi pouvoir le lire en entier*/
if(zip_stat(f_zip, file_stat.name, 0, &file_stat) == -1)
{
printf("%s\n", zip_strerror(f_zip));
return ZIPZAP_FAILURE;
}

printf("taille du fichier %s : %d \n", file_stat.name, (int)(file_stat.size*sizeof(char)+1));

/* 5. on ouvre le fichier archivé */
if(motDePasse==NULL)
{
/* sans mot de passe */
file_zip=zip_fopen(f_zip, file_stat.name, ZIP_FL_UNCHANGED);
}
else
{
/* avec mot de passe */
file_zip=zip_fopen_encrypted(f_zip, file_stat.name, ZIP_FL_UNCHANGED, motDePasse);
}
if(!file_zip)
{
printf("%s\n", zip_strerror(f_zip));
return ZIPZAP_FAILURE;
}

char *str=NULL;
str = malloc((size_t)(file_stat.size+1));
memset(str, 0, (size_t)(file_stat.size+1));
if(str == NULL)
{
printf("Erreur d'allocation mémoire\n");
return ZIPZAP_FAILURE;
}

/* 6. on lit le fichier archivé */
if(zip_fread(file_zip, str, (size_t)(file_stat.size)) != file_stat.size)
{
printf("%s\n", zip_strerror(f_zip));
FREE(str);

zip_fclose(file_zip);
file_zip = NULL;
return ZIPZAP_FAILURE;
}

/* 7. on l'écrit en sortie en mode binaire */
FILE *fOut = NULL;

if(repSortie != NULL) /* si on a indiqué un répertoire de sortie */
{

char *repOut= malloc(FILENAME_MAX * sizeof(char));
if(repOut == NULL)
{
printf("Erreur d'allocation mémoire\n");
FREE(str);

zip_fclose(file_zip);
file_zip = NULL;
return ZIPZAP_FAILURE;
}

/* on test les '/' pour ne pas avoir affaire avec des "foo//bar/fichier" ou autres */
/* TODO : ajouter le test pour windows. Un simple '\' ? */
memset(repOut, 0, FILENAME_MAX * sizeof(char));
if( (repSortie[strlen(repSortie)-1]=='/') && (file_stat.name[0]=='/'))
{
strncpy(repOut, repSortie, strlen(repSortie)-2);
strcat(repOut, file_stat.name);
}
else if( (repSortie[strlen(repSortie)-1]=='/') || (file_stat.name[0]=='/'))
sprintf(repOut, "%s%s", repSortie, file_stat.name);
else
sprintf(repOut, "%s/%s", repSortie, file_stat.name);

/* Avant d'extraire le fichier il faut créer les répertoires nécessaires le cas échéant */
mkpath(repOut, 0777);

/* si c'est autre chose qu'un dossier on l'écrit */
if(repOut[strlen(repOut)-1]!='/')
{
if((fOut = fopen(repOut, "wb"))==NULL)
{
printf("Erreur à la création du fichier %s\n", repOut);
FREE(str);

FREE(repOut);

zip_fclose(file_zip);
file_zip = NULL;
return ZIPZAP_FAILURE;
}

/* on écrit le fichier */
fwrite(str, sizeof(char), (size_t)file_stat.size, fOut);
fclose(fOut);
fOut = NULL;
}

zip_fclose(file_zip);
file_zip = NULL;
FREE(str);
FREE(repOut);

}
else /* sinon on utilise le répertoire d'où est lancée la commande */
{

/* Avant d'extraire le fichier il faut créer les répertoires nécessaires le cas échéant */
mkpath(file_stat.name, 0777);

/* si c'est autre chose qu'un dossier on l'écrit */
if(file_stat.name[strlen(file_stat.name)-1] != '/' )
{

if((fOut = fopen(file_stat.name, "wb"))==NULL)
{
printf("Erreur à la création du fichier %s\n", file_stat.name);

zip_fclose(file_zip);
file_zip = NULL;
FREE(str);

return ZIPZAP_FAILURE;

}
/* on écrit le fichier */
fwrite(str, sizeof(char), (size_t)file_stat.size, fOut);

FREE(str);
fclose(fOut);
fOut = NULL;
zip_fclose(file_zip);
file_zip = NULL;

}
}

FREE(str);
file_zip = NULL;
return ZIPZAP_SUCCESS;
}

2-F - Décompresser une archive zip

2-F-1 - Algorithme

Ouvrir l'archive (Fonction zip_open).
Compter le nombre d'éléments dans l'archive (Fonction zip_get_num_files).
Extraire chaque élément (Fonction extraireFichier -> Fonctions zip_stat, zip_fopen, zip_fread, zip_fclose).
Fermer l'archive (Fonction zip_close).

2-F-2 - Code source

Décompresser une archive

static int decompresserFichierZip(const char* fichierZip, const char* repSortie, const char* motDePasse)
{

/* Code d'origine : */
/* http://www.siteduzero.com/forum-83-372372-p1-c-zip-et-unzip.html#r3519929 */
/* http://forum.ubuntu-fr.org/viewtopic.php?pid=2596893#p2596893 Visité le 02/01/2011 */
/* modifié pour ajouter la gestion des erreurs et quelques fonctionnalités */

int err=0;
struct zip *f_zip=NULL;

/* 1. Ouverture de l'archive */
f_zip = zip_open(fichierZip, ZIP_CHECKCONS, &err);
/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
return ZIPZAP_FAILURE;
}

/* 2. on récupère le nombre de fichiers dans l'archive zip */
int count = zip_get_num_files(f_zip);
if(count==-1)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZip);
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}

printf("Nombre de fichiers dans l'archive : %d\n", count);

int i;

/* 3. on lit tous les fichiers */
for(i=0; i<count; i++)
{
/* on lance la fonction qui extraira le fichier en position "i" dans l'archive Zip */
if(extraireFichier(i, f_zip, repSortie, motDePasse)==ZIPZAP_FAILURE)
{
printf("Erreur à l'extraction du fichier %s\n", zip_get_name(f_zip, i, ZIP_FL_UNCHANGED));
zip_close(f_zip);
f_zip = NULL;
return ZIPZAP_FAILURE;
}
}

/* lecture terminée, fermeture de l'archive */
zip_close(f_zip);
f_zip = NULL;

return ZIPZAP_SUCCESS;
}

2-G - Créer une archive zip

2-G-1 - Algorithme

Créer l'archive suivant le mode choisi (Fonction zip_open).
Vérifier que le fichier/dossier n'est pas déjà présent dans l'archive (Fonction verfiPresenceFichier).
Si on ajoute un dossier
on l'ajoute à l'archive (Fonction zip_add_dir),
on ajoute tous ses éléments dans l'archive (Fonction ajouteDossier ->Fonctions zip_source_file, zip_add_dir, zip_add).
Sinon
on ajoute le fichier à l'archive (Fonctions zip_source_file, zip_add).
Fermer l'archive (Fonction zip_close).

2-G-2 - Code source

Créer une archive

/* Ajoute les fichiers tels qu'ils sont envoyés dans la ligne de commande */
/* Pose problème avec les fichiers du style "../../fichiers" */
/**
* \fn static int creerFichierZip(const char* fichierZipEnSortie, int modeZip, int argc, char *arg[])
* \brief Créer l'archive zip "fichierZipEnSortie" suivant le mode "modeZip" avec les fichiers contenus dans *arg[].
*
* \param fichierZipEnSortie Le nom de l'archive zip à créer en sortie.
* \param modeZip Mode de création de l'archive ADD ; CREATE ou EXIST. (voir les définitions).
* \param argc Nombre de fichiers reçus par le programme et transmis à la fonction.
* \param argv Chaînes de caractères reçues par le programme et transmises à la fonction. Ces chaînes contiennent les fichiers/répertoires à ajouter.
*
* \return ZIPZAP_SUCCESS : Retour OK ; ZIPZAP_FAILURE : Retour d'erreur.
*/
static int creerFichierZip(const char* fichierZipEnSortie, int modeZip, int argc, char *arg[])
{

struct zip *f_zip=NULL;
struct zip_source *doc = NULL;
int err = 0;

/* Suivant le mode choisi pour la création de l'archive on définit les flags */
if(modeZip==CREATE)
{
/* on supprime l'archive pour en créer une nouvelle */
remove(fichierZipEnSortie);
f_zip=zip_open(fichierZipEnSortie, ZIP_CREATE, &err); /* on crée l'archive */
}
else if(modeZip==EXIST)
{
f_zip=zip_open(fichierZipEnSortie, ZIP_EXCL|ZIP_CREATE, &err); /* on ne crée pas l'archive si elle existe, Jacques THERY2011-12-06T12:28:12.39Vous ne pensez pas que la phrase qui suit n'a pas d'utilité ?mais si elle n'existe pas on la crée */
}
else if(modeZip==ADD)
{
f_zip=zip_open(fichierZipEnSortie, ZIP_CREATE, &err); /* on crée l'archive si elle n'existe pas */
}
else
{
printf("Erreur du choix du mode\n");
usage(arg[0]);
return ZIPZAP_FAILURE;
}

/* s'il y a des erreurs */
if(err != ZIP_ER_OK)
{
zip_error_to_str(buf_erreur, sizeof buf_erreur, err, errno);
printf("Error %d : %s\n",err, buf_erreur);
return ZIPZAP_FAILURE;
}
/* si le fichier zip n'est pas ouvert */
if(f_zip==NULL)
{
printf("Erreur à l'ouverture du fichier %s\n", fichierZipEnSortie);
return ZIPZAP_FAILURE;
}

/* on positionne i à 3 car on a dans le tableau : "0. Le nom du programme" "1. Le mode de création" "2. Le nom de l'archive" */
int i = 3;
for(; i<argc; i++)
{

if(verifPresenceFichier(f_zip, arg[i])==ZIPZAP_FAILURE)
{
printf("Erreur lors de l'ajout du fichier/dossier \"%s\" à l'archive %s\n", arg[i], fichierZipEnSortie);
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;
return ZIPZAP_FAILURE;
}

#if defined(__POSIX_VISIBLE) || defined(_POSIX_C_SOURCE)
/* on teste si le fichier demandé est un dossier ou non */
struct stat f_stat;
if(lstat(arg[i], &f_stat) != -1)
{
if(S_ISDIR(f_stat.st_mode))
{
/* Si cela en est un, on l'ajoute à l'archive */
if(zip_add_dir(f_zip, arg[i]) == -1)
{

printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;

return ZIPZAP_FAILURE;
}

/* Puis on ajoute tous les fichiers du dossier et de ses sous-dossiers */
if(ajouteDossier(arg[i], f_zip)==ZIPZAP_FAILURE)
{
printf("Erreur à l'ajout du dossier %s\n", arg[i]);
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;
return ZIPZAP_FAILURE;
}

}
else /* Sinon on le traite comme un fichier normal */
{

/* que l'on récupère dans un zip_source */
if((doc=zip_source_file(f_zip,arg[i],(off_t)0,(off_t)0))==NULL)
{

printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;
return ZIPZAP_FAILURE;
}

/* pour l'inclure dans l'archive */
if(zip_add(f_zip, arg[i], doc)==-1)
{

printf("%s\n", zip_strerror(f_zip));
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;
return ZIPZAP_FAILURE;
}

}
}
else
{
printf("Le fichier %s n'existe pas\n", arg[i]);
zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;
return ZIPZAP_FAILURE;
}

#endif /* posix */
/* TODO : la même chose pour Windows */

}

zip_close(f_zip);
f_zip = NULL;
zip_source_free(doc);
doc = NULL;

return ZIPZAP_SUCCESS;
}

3 - Conclusion

Les codes sources présentés ci-dessus montrent comment il est facile avec la LibZip de réaliser les opérations classiques sur des archives. Je vous recommande l'utilisation de cette bibliothèque très puissante et très simple d'utilisation. Elle est d'ailleurs utilisée par de nombreux gros projets et ce n'est sans doute pas pour rien.
Vous pouvez télécharger le code source complet et la Makefile dans cette archive zip créée avec les exemples du code.
Télécharger ZipZap(miroir).

4 - Remerciements

Ce document et les codes sources n'auraient pas pu exister sans la présence de codes source sur la toile. D'autres exemples d'utilisations :
- J. Leffler pour son code sur la création des dossiers avec mkdir ;
- Bernard SIAUD aka Troumad pour son exemple de modification d'un fichier zip ;
- Seldok pour l'extraction d'un fichier dans une archive zip avec libzip ;
- Claude LELOUP et jacques_jeanpour la relecture orthographique de cet article ;
et bien sûr, Dieter Baron et Thomas Klausner, auteurs de la libzip.