///////////////////////////////////////////////////////////////////////////// // Rev 11/12/2003 // // // UNGZIP.C // ------- // // // Sylvain MARECHAL - sylvain.marechal1@libertysurf.fr ///////////////////////////////////////////////////////////////////////////// /* ** Encapsulation de la zlib pour compresser et decompresser des donnees ** au format gzip ** ** Necessite la zlib */ #include #include #include #include #include #include #include "ungzip.h" /* Programme de test : Ce programme decompresse un fichier a la volee */ /* Proto pour le test */ static int ReadGzipFile(LPARAM lParam, char ** ppBuffer, int * pcbBuffer ); /* Petite structure pour decompresser un fichier */ typedef struct _tagTestUngzipData { UngzipData * pZip; int hFile; char * pBuffer; int cbBuffer; } TestUngzipData; /* Fonction de lecture des donnees */ int ReadGzipFile(LPARAM lParam, char ** ppBuffer, int * pcbBuffer ) { TestUngzipData *ptuzd = (TestUngzipData *)lParam; int nRet; nRet = read( ptuzd->hFile, ptuzd->pBuffer, ptuzd->cbBuffer ); *ppBuffer = (nRet <= 0 ? NULL : ptuzd->pBuffer); *pcbBuffer = (nRet <= 0 ? 0 : nRet); if( nRet < ptuzd->cbBuffer ) { nRet = -1; } return nRet; } /* Decompression a la volee */ void TestUngzip( char * pszFileSource, char * pszFileDest ) { TestUngzipData tuzd; int hFileDest; /* Init structure + ouverture fichiers */ tuzd.pZip = UngzipInit( FALSE, 20000, ReadGzipFile, (LPARAM )&tuzd ); if( tuzd.pZip == NULL ) return; tuzd.cbBuffer = 4096; tuzd.pBuffer = (char *)malloc( tuzd.cbBuffer ); if( tuzd.pBuffer == NULL ) { UngzipDestroy( tuzd.pZip ); return; } /* Fichier source */ tuzd.hFile = open( pszFileSource, O_RDONLY | O_BINARY, 0666 ); if( tuzd.hFile == -1 ) { UngzipDestroy( tuzd.pZip ); return; } /* Fichier de destination */ hFileDest = open( pszFileDest, O_CREAT | O_TRUNC | O_BINARY | O_RDWR , 0666 ); if( hFileDest == -1 ) { close( tuzd.hFile ); UngzipDestroy( tuzd.pZip ); return; } /* Ecriture ds fichier de destination */ for(;;) { int err; char *pDecompress; int cbDecompress; err = UngzipDecode( tuzd.pZip, &pDecompress, &cbDecompress ); if( cbDecompress > 0 ) write( hFileDest, pDecompress, cbDecompress ); if( err < 0 ) break; } /* Liberation memoire et fermeture fichiers */ close( hFileDest ); close( tuzd.hFile ); free( tuzd.pBuffer ); UngzipDestroy( tuzd.pZip ); } ///////////////////////////////////////////////////////////////////////////// // // UNGZIPINIT ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --UngzipInit-- // // Initilisation du decompresseur avec passage de la fonction // qui va chercher les donnees // // ARGUMENTS // Argument1: BOOL fDeflate // Argument2: int cbBuffer Taille du buffer interne (si 0, 20000) // Argument3: (*pfGetData)(LPARAM,char **) // Argument4: LPARAM lParam // RETOUR/RESULTAT // UngzipData * // REMARQUE // Rev 01/03/2000 ////////////////////////////////////////////////////////////////////////////// UngzipData * UngzipInit( BOOL fDeflate, int cbBuffer, int (*pfGetData)(LPARAM, char **, int *), LPARAM lParam ) { UngzipData * pZip; /* malloc structure */ pZip = (UngzipData *)malloc( sizeof(UngzipData) ); if( pZip == NULL ) return pZip; /* init decompresseur */ memset( pZip, 0, sizeof(UngzipData) ); pZip->Z.zalloc = Z_NULL ; pZip->Z.zfree = Z_NULL ; pZip->Z.opaque = NULL ; pZip->Z.next_in = NULL ; pZip->Z.next_out = NULL ; pZip->Z.data_type = Z_BINARY ; if (Z_OK != inflateInit2(&pZip->Z, -MAX_WBITS)) { free(pZip); return NULL; } /* Init reste de la structure */ pZip->fDeflate = fDeflate; pZip->GetData = pfGetData; pZip->lParam = lParam; pZip->cbBuffer = (cbBuffer == 0 ? 20000 : cbBuffer); pZip->cbMalloc = pZip->cbBuffer; pZip->pBuffer = (char *)malloc( pZip->cbBuffer ); if( pZip->pBuffer == NULL ) { free(pZip); return NULL; } return pZip; } ///////////////////////////////////////////////////////////////////////////// // // UNGZIPDESTROY ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --UngzipDestroy-- // // liberation de la structure // // ARGUMENTS // Argument1: UngzipData * pZip // RETOUR/RESULTAT // void // REMARQUE // Rev 01/03/2000 ////////////////////////////////////////////////////////////////////////////// void UngzipDestroy( UngzipData * pZip ) { if( ! pZip ) return; inflateEnd (&pZip->Z); if( pZip->pBuffer ) free( pZip->pBuffer ); free( pZip ); } ///////////////////////////////////////////////////////////////////////////// // // UNGZIPDECODE ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --UngzipDecode-- // // Decompression des donnees extraites par GetData() // Renvoie la qtite decompresse // // ARGUMENTS // Argument1: UngzipData * pZip (IN) // Argument2: char **ppData (OUT) Pointeur sur donnees decompressees // Argument2: int * pcbDecompress(OUT) Quantite // RETOUR/RESULTAT // int (OUT) Qtite, -1 = fin, -2 = erreur // REMARQUE // Rev 11/12/2003 ////////////////////////////////////////////////////////////////////////////// int UngzipDecode( UngzipData * pZip, char **ppDecompress, int * pcbDecompress ) { int err, nRet, nFlush; char * pData; int cbData; /* Init */ *ppDecompress = NULL; *pcbDecompress = 0; /* On va chercher les donnees */ err = (*pZip->GetData)( pZip->lParam, &pData, &cbData ); /* Gestion de l'entete en gzip */ if( ! pZip->fDeflate && ! pZip->fHeaderRemoved ) { UngzipRemoveHeader( pZip, &pData, &cbData ); } /* Gestion du cas ou on a rien lu*/ if( cbData <= 0 ) return cbData; /* Buffer en entree */ pZip->Z.next_in = pData; pZip->Z.avail_in = (uInt)cbData; /* Buffer en sortie */ pZip->Z.next_out = pZip->pBuffer; pZip->Z.avail_out = (uInt)pZip->cbBuffer; /* Gestion de la fin */ nFlush = ( err < 0 ? Z_FINISH : Z_NO_FLUSH ); /* Decompression (pas la peine de passer qd il reste plus que la fin)*/ nRet = -1; for (; ! pZip->fTrailerRemoved ;) { err = inflate( &pZip->Z, nFlush ); if( err == Z_STREAM_END ) { /* C'est fini */ nRet = -1; *ppDecompress = pZip->pBuffer; *pcbDecompress = max(pZip->cbBuffer - pZip->Z.avail_out, 0); /* On met a jour pour l'extraction du trailer */ pData = pZip->Z.next_in; cbData = pZip->Z.avail_in; /* On peut sortir */ nRet = -1; break; } else if( err == Z_OK ) { /* Soit le buffer d'entree est vide, soit celui de sortie est plein */ if( pZip->Z.avail_in == 0 ) { /* Buffer d'entree completement comsomme */ *ppDecompress = pZip->pBuffer; *pcbDecompress = max(pZip->cbBuffer - pZip->Z.avail_out, 0); /* On peut sortir */ nRet = *pcbDecompress; break; } else if( pZip->Z.avail_out == 0 ) { char * pSaveBuffer = pZip->pBuffer; /* On doit refaire de la place dans le buffer de sortie . on relloc donc */ pZip->pBuffer = realloc( pZip->pBuffer, pZip->cbBuffer + pZip->cbMalloc ); if( pZip->pBuffer == NULL ) { pZip->pBuffer = pSaveBuffer; return -2; /*kk*/ } pZip->Z.next_out = pZip->pBuffer + pZip->cbBuffer; pZip->Z.avail_out = (uInt)pZip->cbMalloc; pZip->cbBuffer += pZip->cbMalloc; /* On doit refaire un tour dans inflate() */ } } else { /* kk */ nRet = -2; break; } } /* Gestion de la fin en gzip */ if( nRet == -1 && ! pZip->fDeflate && ! pZip->fTrailerRemoved ) { nRet = UngzipRemoveTrailer( pZip, &pData, &cbData ); } return nRet; } ///////////////////////////////////////////////////////////////////////////// // // UNGZIPREMOVEHEADER ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --UngzipRemoveHeader-- // // Suppression de l'entete gzip // // ARGUMENTS // Argument1: UngzipData * pZip // Argument2: char ** ppData // Argument3: int * pcbData // RETOUR/RESULTAT // void // REMARQUE // Rev 01/03/2000 ////////////////////////////////////////////////////////////////////////////// void UngzipRemoveHeader( UngzipData * pZip, char ** ppData, int * pcbData ) { int cbCopy; /* Normalement inutile ... */ if( pZip->fHeaderRemoved ) return; /* On commence par copier l'entete de 10 octets */ if( pZip->cbHeaderRead < 10 ) { /* On veut juste copier 10 octets */ cbCopy = (*pcbData > (10-pZip->cbHeaderRead) ? (10-pZip->cbHeaderRead) : *pcbData ); if( cbCopy <= 0 ) return; memcpy( pZip->aHeader + pZip->cbHeaderRead, *ppData, cbCopy ); pZip->cbHeaderRead += cbCopy; /* On peut analyser l'entete */ pZip->fExtra = (pZip->aHeader[3] & 0x04 ? 1 : 0 ); pZip->fName = (pZip->aHeader[3] & 0x08 ? 1 : 0 ); pZip->fComment = (pZip->aHeader[3] & 0x10 ? 1 : 0 ); /* Mise a jour buffer */ (*ppData) += cbCopy; (*pcbData) -= cbCopy; } /* Lecture du 1° octet SI1 */ if( pZip->fExtra == 1 && *pcbData ) { (*ppData) ++; (*pcbData) --; pZip->fExtra = 2; } /* Lecture du 2° octet SI2 */ if( pZip->fExtra == 2 && *pcbData ) { (*ppData) ++; (*pcbData) --; pZip->fExtra = 3; } /* Lecture du 1° octet contenant la taille */ if( pZip->fExtra == 3 && *pcbData ) { pZip->szExtra[0] = **ppData; (*ppData) ++; (*pcbData) --; pZip->fExtra = 4; } /* Lecture du 2° octet contenant la taille */ if( pZip->fExtra == 4 && *pcbData ) { pZip->szExtra[1] = **ppData; (*ppData) ++; (*pcbData) --; pZip->fExtra = 5; } /* Lecture du champ extra maintenant qu'on connait sa taille */ if( pZip->fExtra == 5 && *pcbData ) { int cbExtra = (int)( (unsigned char)pZip->szExtra[0] + ( unsigned char)(pZip->szExtra[1] << 8) ); cbCopy = ( *pcbData > (cbExtra - pZip->cbExtraRead) ? (cbExtra - pZip->cbExtraRead) : *pcbData); pZip->cbExtraRead += cbCopy; (*pcbData) -= cbCopy; (*ppData) += cbCopy; if( pZip->cbExtraRead == cbExtra ) pZip->fExtra = 0; } /* On s'occupe du champ name qui se termine par 0 */ if( pZip->fName == 1 && *pcbData ) { while( *pcbData && **ppData ) { (*ppData) ++; (*pcbData) --; } if( *pcbData && **ppData == 0 ) { (*ppData) ++; (*pcbData) --; pZip->fName = 0; } } /* On s'occupe du champ fComment qui se termine par 0 */ if( pZip->fComment == 1 && *pcbData ) { while( *pcbData && **ppData ) { (*ppData) ++; (*pcbData) --; } if( *pcbData && **ppData == 0 ) { (*ppData) ++; (*pcbData) --; pZip->fComment = 0; } } /* Si tous sont 0, on a fini */ if( ! pZip->fExtra && ! pZip->fName && ! pZip->fComment ) pZip->fHeaderRemoved = TRUE; } ///////////////////////////////////////////////////////////////////////////// // // UNGZIPREMOVETRAILER ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --UngzipRemoveTrailer-- // // Suppression du triler gzip // // ARGUMENTS // Argument1: UngzipData * pZip // Argument2: char ** ppData // Argument3: int * pcbData // RETOUR/RESULTAT // int (-1) si l'entete a ete retiree, 0 sinon // REMARQUE // Rev 11/12/2003 ////////////////////////////////////////////////////////////////////////////// int UngzipRemoveTrailer( UngzipData * pZip, char ** ppData, int * pcbData ) { int cbCopy; /* Normalement inutile ... */ if( pZip->fTrailerRemoved ) return -1; /* On commence par copier la fin de 8 octets */ if( pZip->cbTrailerRead < 8 ) { /* On veut juste copier 10 octets */ cbCopy = (*pcbData > (8-pZip->cbTrailerRead) ? (8-pZip->cbTrailerRead) : *pcbData ); if( cbCopy < 0 ) return 0; memcpy( pZip->aTrailer + pZip->cbTrailerRead, *ppData, cbCopy ); pZip->cbTrailerRead += cbCopy; /* On a donc fini */ if( pZip->cbTrailerRead == 8 ) { pZip->fTrailerRemoved = TRUE; return -1; } } return 0; }