-// ExecCompressor - Open a de/compressor pipe /*{{{*/
-// ---------------------------------------------------------------------
-/* This opens the compressor, either in compress mode or decompress
- mode. FileFd is always the compressor input/output file,
- OutFd is the created pipe, Input for Compress, Output for Decompress. */
-bool ExecCompressor(APT::Configuration::Compressor const &Prog,
- pid_t *Pid, int const FileFd, int &OutFd, bool const Comp)
-{
- if (Pid != NULL)
- *Pid = -1;
-
- // No compression
- if (Prog.Binary.empty() == true)
- {
- OutFd = dup(FileFd);
- return true;
- }
-
- // Handle 'decompression' of empty files
- if (Comp == false)
- {
- struct stat Buf;
- fstat(FileFd, &Buf);
- if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
- {
- OutFd = FileFd;
- return true;
- }
- }
-
- // Create a data pipe
- int Pipe[2] = {-1,-1};
- if (pipe(Pipe) != 0)
- return _error->Errno("pipe",_("Failed to create subprocess IPC"));
- for (int J = 0; J != 2; J++)
- SetCloseExec(Pipe[J],true);
-
- if (Comp == true)
- OutFd = Pipe[1];
- else
- OutFd = Pipe[0];
-
- // The child..
- pid_t child = ExecFork();
- if (Pid != NULL)
- *Pid = child;
- if (child == 0)
- {
- if (Comp == true)
- {
- dup2(FileFd,STDOUT_FILENO);
- dup2(Pipe[0],STDIN_FILENO);
- }
- else
- {
- dup2(FileFd,STDIN_FILENO);
- dup2(Pipe[1],STDOUT_FILENO);
- }
-
- SetCloseExec(STDOUT_FILENO,false);
- SetCloseExec(STDIN_FILENO,false);
-
- std::vector<char const*> Args;
- Args.push_back(Prog.Binary.c_str());
- std::vector<std::string> const * const addArgs =
- (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs);
- for (std::vector<std::string>::const_iterator a = addArgs->begin();
- a != addArgs->end(); ++a)
- Args.push_back(a->c_str());
- Args.push_back(NULL);
-
- execvp(Args[0],(char **)&Args[0]);
- cerr << _("Failed to exec compressor ") << Args[0] << endl;
- _exit(100);
- }
- if (Comp == true)
- close(Pipe[0]);
- else
- close(Pipe[1]);
-
- if (Pid == NULL)
- ExecWait(child, Prog.Binary.c_str(), true);
-
- return true;
-}
-bool ExecCompressor(APT::Configuration::Compressor const &Prog,
- pid_t *Pid, std::string const &FileName, int &OutFd, bool const Comp)
-{
- if (Pid != NULL)
- *Pid = -1;
-
- // No compression
- if (Prog.Binary.empty() == true)
- {
- if (Comp == true)
- OutFd = open(FileName.c_str(), O_WRONLY, 0666);
- else
- OutFd = open(FileName.c_str(), O_RDONLY);
- return true;
- }
-
- // Handle 'decompression' of empty files
- if (Comp == false)
- {
- struct stat Buf;
- stat(FileName.c_str(), &Buf);
- if (Buf.st_size == 0)
- {
- OutFd = open(FileName.c_str(), O_RDONLY);
- return true;
- }
- }
-
- // Create a data pipe
- int Pipe[2] = {-1,-1};
- if (pipe(Pipe) != 0)
- return _error->Errno("pipe",_("Failed to create subprocess IPC"));
- for (int J = 0; J != 2; J++)
- SetCloseExec(Pipe[J],true);
-
- if (Comp == true)
- OutFd = Pipe[1];
- else
- OutFd = Pipe[0];
-
- // The child..
- pid_t child = ExecFork();
- if (Pid != NULL)
- *Pid = child;
- if (child == 0)
- {
- if (Comp == true)
- {
- dup2(Pipe[0],STDIN_FILENO);
- SetCloseExec(STDIN_FILENO,false);
- }
- else
- {
- dup2(Pipe[1],STDOUT_FILENO);
- SetCloseExec(STDOUT_FILENO,false);
- }
-
- std::vector<char const*> Args;
- Args.push_back(Prog.Binary.c_str());
- std::vector<std::string> const * const addArgs =
- (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs);
- for (std::vector<std::string>::const_iterator a = addArgs->begin();
- a != addArgs->end(); ++a)
- Args.push_back(a->c_str());
- Args.push_back("--stdout");
- Args.push_back(FileName.c_str());
- Args.push_back(NULL);
-
- execvp(Args[0],(char **)&Args[0]);
- cerr << _("Failed to exec compressor ") << Args[0] << endl;
- _exit(100);
- }
- if (Comp == true)
- close(Pipe[0]);
- else
- close(Pipe[1]);
+class FileFdPrivate { /*{{{*/
+ public:
+#ifdef HAVE_ZLIB
+ gzFile gz;
+#endif
+#ifdef HAVE_BZ2
+ BZFILE* bz2;
+#endif
+#ifdef HAVE_LZMA
+ struct LZMAFILE {
+ FILE* file;
+ uint8_t buffer[4096];
+ lzma_stream stream;
+ lzma_ret err;
+ bool eof;
+ bool compressing;
+
+ LZMAFILE() : file(NULL), eof(false), compressing(false) {}
+ ~LZMAFILE() {
+ if (compressing == true)
+ {
+ for (;;) {
+ stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
+ stream.next_out = buffer;
+ err = lzma_code(&stream, LZMA_FINISH);
+ if (err != LZMA_OK && err != LZMA_STREAM_END)
+ {
+ _error->Error("~LZMAFILE: Compress finalisation failed");
+ break;
+ }
+ size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
+ if (n && fwrite(buffer, 1, n, file) != n)
+ {
+ _error->Errno("~LZMAFILE",_("Write error"));
+ break;
+ }
+ if (err == LZMA_STREAM_END)
+ break;
+ }
+ }
+ lzma_end(&stream);
+ fclose(file);
+ }
+ };
+ LZMAFILE* lzma;
+#endif
+ int compressed_fd;
+ pid_t compressor_pid;
+ bool pipe;
+ APT::Configuration::Compressor compressor;
+ unsigned int openmode;
+ unsigned long long seekpos;
+ FileFdPrivate() :
+#ifdef HAVE_ZLIB
+ gz(NULL),
+#endif
+#ifdef HAVE_BZ2
+ bz2(NULL),
+#endif
+#ifdef HAVE_LZMA
+ lzma(NULL),
+#endif
+ compressed_fd(-1), compressor_pid(-1), pipe(false),
+ openmode(0), seekpos(0) {};
+ bool InternalClose(std::string const &FileName)
+ {
+ if (false)
+ /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+ else if (gz != NULL) {
+ int const e = gzclose(gz);
+ gz = NULL;
+ // gzdclose() on empty files always fails with "buffer error" here, ignore that
+ if (e != 0 && e != Z_BUF_ERROR)
+ return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
+ }
+#endif
+#ifdef HAVE_BZ2
+ else if (bz2 != NULL) {
+ BZ2_bzclose(bz2);
+ bz2 = NULL;
+ }
+#endif
+#ifdef HAVE_LZMA
+ else if (lzma != NULL) {
+ delete lzma;
+ lzma = NULL;
+ }
+#endif
+ return true;
+ }
+ bool CloseDown(std::string const &FileName)
+ {
+ bool const Res = InternalClose(FileName);
+
+ if (compressor_pid > 0)
+ ExecWait(compressor_pid, "FileFdCompressor", true);
+ compressor_pid = -1;
+
+ return Res;
+ }
+ bool InternalStream() const {
+ return false
+#ifdef HAVE_BZ2
+ || bz2 != NULL
+#endif
+#ifdef HAVE_LZMA
+ || lzma != NULL
+#endif
+ ;
+ }