/** * @file bzip2.c bzip2 module * * $Id: bzip2.c,v 1.25 2003/01/01 06:22:31 chipx86 Exp $ * * @Copyright (C) 2001-2003 The GNUpdate Project. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include typedef struct { FILE *fp; char *path; } CxBZ2Data; static size_t __readFunc(void *ptr, size_t size, size_t nmemb, CxFP *fp) { return fread(ptr, size, nmemb, (FILE *)fp->moduleData); } static size_t __writeFunc(const void *ptr, size_t size, size_t nmemb, CxFP *fp) { return fwrite(ptr, size, nmemb, (FILE *)fp->moduleData); } static void __seekFunc(CxFP *fp, long pos, int whence) { fseek((FILE *)fp->moduleData, pos, whence); } static void __closeFunc(CxFP *fp) { fp->moduleData = NULL; } static char * __makeOutputFilename(const char *filename) { char *newFilename; char *c; if (filename == NULL) return strdup("unknown"); newFilename = strdup(filename); /* TODO: This has some BAD potential problems. Rewrite this. */ c = strrchr(newFilename, '.'); if (c == NULL) return newFilename; *c = '\0'; return newFilename; } static char * __extractFp(CxFP *fp) { size_t s; char buffer[BUFSIZ]; FILE *outFp; char *filename; filename = cxMakeTempFilename(); outFp = fopen(filename, "w"); if (outFp == NULL) { free(filename); return NULL; } while ((s = cxRead(buffer, sizeof(char), BUFSIZ, fp)) != 0) { fwrite(buffer, sizeof(char), s, outFp); } fclose(outFp); return filename; } static CxStatus readArchive(CxArchive *archive, CxFP *fp) { BZFILE *bzfp; FILE *tempFp; CxFile *file; char *temp; int bzerr, nread; char buffer[BUFSIZ]; char *path1, *path2; CxBZ2Data *data; /* Necessary hack. */ path1 = __extractFp(fp); if (path1 == NULL) return CX_ERROR; /* Open the file. */ bzfp = BZ2_bzopen(path1, "rb"); if (bzfp == NULL) { unlink(path1); free(path1); return CX_FILE_NOT_FOUND; /* ? */ } /* Do an initial read to determine if it's a bzip2 file. */ nread = BZ2_bzRead(&bzerr, bzfp, buffer, BUFSIZ); if (bzerr == BZ_DATA_ERROR_MAGIC) { /* It's not a bzip2 file. */ BZ2_bzclose(bzfp); unlink(path1); free(path1); return CX_INVALID_FORMAT; } else if ((bzerr != BZ_OK && bzerr != BZ_STREAM_END) || nread <= 0) { /* An error occurred. */ BZ2_bzclose(bzfp); unlink(path1); free(path1); return CX_ERROR; } /* * That passed. * * Now, because of the lack of bzip2's seek function, we must extract * to a plain file and open with that. :/ */ path2 = cxMakeTempFilename(); tempFp = fopen(path2, "wb"); do { fwrite(buffer, sizeof(char), nread, tempFp); } while ((nread = BZ2_bzread(bzfp, buffer, BUFSIZ)) > 0); BZ2_bzclose(bzfp); unlink(path1); free(path1); /* Re-open. */ tempFp = freopen(path2, "rb", tempFp); /* That passed. Let's setup everything. */ file = cxNewFile(); /* Remove the ".bz2" */ temp = __makeOutputFilename(cxGetArchiveFileName(archive)); cxSetFileName(file, temp); free(temp); cxDirAddFile(cxGetArchiveRoot(archive), file); /* This is always a single-file archive. */ cxSetArchiveType(archive, CX_ARCHIVE_SINGLE); /* Store the fp and path in moduleData */ MEM_CHECK(data = (CxBZ2Data *)malloc(sizeof(CxBZ2Data))); data->fp = tempFp; data->path = path2; archive->moduleData = data; return CX_SUCCESS; } static CxStatus saveArchive(CxArchive *archive, CxFP *fp) { CxFile *file; CxFP *inFp; char outbuf[BUFSIZ]; char inbuf[BUFSIZ]; bz_stream stream; size_t s; int r; int finished = 0; file = cxGetFirstFile(cxGetArchiveRoot(archive)); /* Open the file. */ inFp = cxOpenFile(cxGetFilePhysicalPath(file), CX_MODE_READ_ONLY | CX_MODE_RAW); if (inFp == NULL) return CX_ERROR; /* Create the input buffer. */ r = BZ2_bzCompressInit(&stream, 5, 0, 0); if (r != BZ_OK) return CX_ERROR; while ((s = cxRead(inbuf, 1, BUFSIZ, inFp)) > 0) { stream.next_in = inbuf; stream.avail_in = s; while (stream.avail_in) { stream.next_out = outbuf; stream.avail_out = BUFSIZ; BZ2_bzCompress(&stream, BZ_RUN); cxWrite(outbuf, 1, BUFSIZ - stream.avail_out, fp); } } cxClose(inFp); /* Flush it. */ stream.next_in = NULL; stream.avail_in = 0; do { stream.next_out = outbuf; stream.avail_out = BUFSIZ; finished = (BZ2_bzCompress(&stream, BZ_FINISH) == BZ_STREAM_END); cxWrite(outbuf, 1, BUFSIZ - stream.avail_out, fp); } while (!finished); BZ2_bzCompressEnd(&stream); return CX_SUCCESS; } static void closeArchive(CxArchive *archive) { if (archive->moduleData != NULL) { CxBZ2Data *data = (CxBZ2Data *)archive->moduleData; fclose(data->fp); unlink(data->path); free(data->path); free(data); archive->moduleData = NULL; } } static CxFP * openFile(CxFile *file, CxAccessMode mode) { CxArchive *archive = NULL; CxFP *fp; if (!CX_IS_MODE_READ_ONLY(mode)) return NULL; /* Open the file. */ archive = cxGetFileArchive(file); fp = cxNewFp(); fp->moduleData = ((CxBZ2Data *)archive->moduleData)->fp; fseek((FILE *)fp->moduleData, 0, SEEK_SET); cxSetReadFunc(fp, __readFunc); cxSetWriteFunc(fp, __writeFunc); cxSetSeekFunc(fp, __seekFunc); cxSetCloseFunc(fp, __closeFunc); return fp; } static void destroyFile(CxFile *file) { file->moduleData = NULL; } static char supportsExtension(const char *ext) { if (!strcasecmp(ext, "bz2")) { return 1; } return 0; } static CxArchiveOps ops = { readArchive, /* openArchive */ saveArchive, /* saveArchive */ closeArchive, /* closeArchive */ openFile, /* openFile */ destroyFile, /* destroyFile */ supportsExtension /* supportsExtension */ }; static void __moduleInit(CxModuleType type) { } CX_INIT_ARCHIVE_MODULE(bzip2, __moduleInit, ops)