2 * This file is a part of QComicBook.
4 * Copyright (C) 2005-2006 Pawel Stolowski <yogin@linux.bydg.org>
6 * QComicBook is free software; you can redestribute it and/or modify it
7 * under terms of GNU General Public License by Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY. See GPL for more details.
13 #include "imgarchivesink.h"
16 #include <qstringlist.h>
18 #include <qfileinfo.h>
21 #include <qapplication.h>
24 #include <sys/types.h>
29 using namespace QComicBook;
32 QString ImgArchiveSink::openext;
33 QString ImgArchiveSink::saveext;
34 int ImgArchiveSink::suppopen;
35 int ImgArchiveSink::suppsave;
37 QValueList<ImgArchiveSink::ArchiveTypeInfo> ImgArchiveSink::archinfo;
39 ImgArchiveSink::ImgArchiveSink(): ImgDirSink()
44 ImgArchiveSink::ImgArchiveSink(const QString &path): ImgDirSink()
50 ImgArchiveSink::ImgArchiveSink(const ImgDirSink &sink): ImgDirSink(sink)
55 ImgArchiveSink::~ImgArchiveSink()
57 ImgArchiveSink::close();
60 void ImgArchiveSink::init()
62 pinf = new QProcess(this);
63 pext = new QProcess(this);
64 connect(pinf, SIGNAL(readyReadStdout()), this, SLOT(infoStdoutReady()));
65 connect(pinf, SIGNAL(processExited()), this, SLOT(infoExited()));
66 connect(pext, SIGNAL(readyReadStdout()), this, SLOT(extractStdoutReady()));
67 connect(pext, SIGNAL(processExited()), this, SLOT(extractExited()));
70 void ImgArchiveSink::doCleanup()
72 if (!tmppath.isEmpty())
76 // remove temporary files and dirs
77 for (QStringList::const_iterator it = archfiles.begin(); it != archfiles.end(); ++it)
79 for (QStringList::const_iterator it = archdirs.begin(); it != archdirs.end(); ++it)
85 ImgArchiveSink::ArchiveType ImgArchiveSink::archiveType(const QString &filename)
88 int offset; //the first byte to compare
89 int len; //number of bytes in pattern
90 const char *ptrn; //pattern
91 ArchiveType type; //archive type
93 {0, 4, "\x52\x61\x72\x21", RAR_ARCHIVE},
94 {0, 4, "\x50\x4b\x03\x04", ZIP_ARCHIVE},
95 {8, 4, "\x2a\x41\x43\x45", ACE_ARCHIVE}
99 if (file.open(IO_ReadOnly))
101 for (int i=0; i<3; i++)
104 if (!file.at(magic[i].offset))
106 for (j=0; j<magic[i].len; j++)
109 if ((c = file.getch()) < 0)
111 if (c != magic[i].ptrn[j])
114 if (j == magic[i].len)
117 return magic[i].type;
124 // try to match filename extension
125 for (QValueList<ArchiveTypeInfo>::const_iterator it = archinfo.begin(); it!=archinfo.end(); it++)
127 const ArchiveTypeInfo &inf = *it;
128 for (QStringList::const_iterator sit = inf.extensions.begin(); sit!=inf.extensions.end(); sit++)
130 if (filename.endsWith(*sit, false))
135 return UNKNOWN_ARCHIVE;
138 int ImgArchiveSink::extract(const QString &filename, const QString &destdir)
140 archivetype = archiveType(filename);
143 if (archivetype == UNKNOWN_ARCHIVE)
144 return SINKERR_UNKNOWNFILE;
145 if (!supportsOpen(archivetype))
146 return SINKERR_NOTSUPPORTED;
149 // match archive type, set subprocess extract and list options
150 for (QValueList<ArchiveTypeInfo>::const_iterator it = archinfo.begin(); it!=archinfo.end(); it++)
152 const ArchiveTypeInfo &inf = *it;
153 if (archivetype == inf.type)
155 pext->setArguments(inf.extractopts);
156 pinf->setArguments(inf.listopts);
161 pext->addArgument(filename);
162 pinf->addArgument(filename);
163 pext->setWorkingDirectory(destdir);
166 // extract archive file list first
168 return SINKERR_OTHER;
172 int ImgArchiveSink::open(const QString &path)
174 QFileInfo info(path);
176 archivename = info.fileName();
179 emit sinkError(SINKERR_NOTFOUND);
180 return SINKERR_NOTFOUND;
184 if (info.isReadable())
186 tmppath = makeTempDir();
187 int status = extract(path, tmppath);
190 emit sinkError(status);
197 emit sinkError(SINKERR_ACCESS);
198 return SINKERR_ACCESS;
201 emit sinkError(SINKERR_NOTFILE);
202 return SINKERR_NOTFILE;
205 void ImgArchiveSink::close()
209 archivename = QString::null;
212 QString ImgArchiveSink::getName(int maxlen)
214 if (archivename.length() < maxlen)
216 QString tmpname = archivename.left(maxlen-3) + "...";
220 QString ImgArchiveSink::getFullName() const
225 void ImgArchiveSink::infoExited()
229 emit sinkError(SINKERR_OTHER);
232 void ImgArchiveSink::extractExited()
235 // open temporary directory using ImgDirSink::open()
236 ImgDirSink::blockSignals(true);
237 int status = ImgDirSink::open(tmppath);
238 ImgDirSink::blockSignals(false);
240 archfiles = ImgDirSink::getAllfiles();
241 archdirs = ImgDirSink::getAlldirs();
242 setComicBookName(archivename);
244 if (!pext->normalExit())
246 emit sinkError(SINKERR_ARCHEXIT);
249 else if (status != 0)
251 emit sinkError(status);
258 // fix permissions of files; this is needed for ace archives as unace
259 // is buggy and sets empty permissions.
260 for (QStringList::const_iterator it = archfiles.begin(); it!=archfiles.end(); ++it)
262 QFileInfo finfo(*it);
263 if (!finfo.isReadable())
264 chmod(*it, S_IRUSR|S_IWUSR);
266 emit sinkReady(archivepath);
270 void ImgArchiveSink::infoStdoutReady()
272 QByteArray b = pinf->readStdout();
273 for (int i=0; i<b.size(); i++)
278 void ImgArchiveSink::extractStdoutReady()
280 QByteArray b = pext->readStdout();
281 for (int i=0; i<b.size(); i++)
282 if (b[i] == '\n' && extcnt < filesnum)
284 emit progress(extcnt, filesnum);
285 qApp->processEvents();
288 void ImgArchiveSink::autoconfRAR()
291 inf.type = RAR_ARCHIVE;
293 inf.extensions.append(".rar");
294 inf.extensions.append(".cbr");
295 inf.reading = inf.writing = false;
297 if (which("rar") != QString::null)
299 inf.extractopts.append("rar");
300 inf.extractopts.append("x");
301 inf.listopts.append("rar");
302 inf.listopts.append("lb");
303 inf.reading = inf.writing = true;
304 inf.compressopts.append("rar");
305 inf.compressopts.append("a");
307 else if (which("unrar") != QString::null)
310 bool nonfree_unrar = false;
311 inf.extractopts.append("unrar");
312 inf.listopts.append("unrar");
314 // now determine which unrar it is - free or non-free
315 if ((f = popen("unrar", "r")) != NULL)
317 QRegExp regexp("^UNRAR.+freeware");
318 for (QTextIStream s(f); !s.atEnd(); )
320 if (regexp.search(s.readLine()) >= 0)
322 nonfree_unrar = true;
329 inf.extractopts.append("x");
330 inf.listopts.append("lb");
334 inf.extractopts.append("-x");
335 inf.listopts.append("-t");
340 else if (which("unrar-free") != QString::null) //some distros rename free unrar like this
342 inf.extractopts.append("unrar-free");
343 inf.listopts.append("unrar-free");
344 inf.extractopts.append("-x");
345 inf.listopts.append("-t");
348 archinfo.append(inf);
351 void ImgArchiveSink::autoconfZIP()
354 inf.type = ZIP_ARCHIVE;
356 inf.extensions.append(".zip");
357 inf.extensions.append(".cbz");
358 inf.reading = inf.writing = false;
359 if (which("unzip") != QString::null)
361 inf.extractopts.append("unzip");
362 inf.listopts.append("unzip");
363 inf.listopts.append("-l");
366 if (which("zip") != QString::null)
369 inf.compressopts.append("zip");
371 archinfo.append(inf);
374 void ImgArchiveSink::autoconfACE()
377 inf.type = ACE_ARCHIVE;
379 inf.extensions.append(".ace");
380 inf.extensions.append(".cba");
381 inf.reading = inf.writing = false;
382 if (which("unace") != QString::null)
384 inf.extractopts.append("unace");
385 inf.extractopts.append("x");
386 inf.extractopts.append("-y");
387 inf.extractopts.append("-c-");
388 inf.listopts.append("unace");
389 inf.listopts.append("l");
390 inf.listopts.append("-y");
391 inf.listopts.append("-c-");
394 archinfo.append(inf);
397 void ImgArchiveSink::autoconfTARGZ()
400 inf.type = TARGZ_ARCHIVE;
402 inf.extensions.append(".tar.gz");
403 inf.extensions.append(".tgz");
404 inf.extensions.append(".cbg");
405 inf.reading = inf.writing = false;
406 if (which("tar") != QString::null)
408 inf.extractopts.append("tar");
409 inf.extractopts.append("-xvzf");
410 inf.listopts.append("tar");
411 inf.listopts.append("-tzf");
412 inf.reading = inf.writing = true;
413 inf.compressopts.append("tar");
414 inf.compressopts.append("-chzvf");
416 archinfo.append(inf);
419 void ImgArchiveSink::autoconfTARBZ2()
422 inf.type = TARBZ2_ARCHIVE;
423 inf.name = "tar.bz2";
424 inf.extensions.append(".tar.bz2");
425 inf.extensions.append(".cbb");
426 if (which("tar") != QString::null)
428 inf.extractopts.append("tar");
429 inf.extractopts.append("-xjvf");
430 inf.listopts.append("tar");
431 inf.listopts.append("-tjf");
432 inf.reading = inf.writing = true;
433 inf.compressopts.append("tar");
434 inf.compressopts.append("-chjvf");
436 archinfo.append(inf);
439 void ImgArchiveSink::autoconfTAR()
442 inf.type = TAR_ARCHIVE;
444 inf.extensions.append(".tar");
445 inf.extensions.append(".cbt");
446 if (which("tar") != QString::null)
448 inf.extractopts.append("tar");
449 inf.extractopts.append("-xvf");
450 inf.listopts.append("tar");
451 inf.listopts.append("-tf");
452 inf.reading = inf.writing = true;
453 inf.compressopts.append("tar");
454 inf.compressopts.append("-chvf");
456 archinfo.append(inf);
459 void ImgArchiveSink::autoconf7Z()
462 inf.type = SEVENZIP_ARCHIVE;
464 inf.extensions.append(".7z");
465 inf.extensions.append(".cb7");
466 if (which("7z") != QString::null)
468 inf.extractopts.append("7z");
469 inf.extractopts.append("x");
470 inf.extractopts.append("-y");
471 inf.listopts.append("7z");
472 inf.listopts.append("l");
473 inf.listopts.append("-y");
475 } else if (which("7za") != QString::null)
477 inf.extractopts.append("7za");
478 inf.extractopts.append("x");
479 inf.extractopts.append("-y");
480 inf.listopts.append("7za");
481 inf.listopts.append("l");
482 inf.listopts.append("-y");
485 archinfo.append(inf);
488 void ImgArchiveSink::autoconfArchivers()
498 openext = saveext = QString::null;
500 for (QValueList<ArchiveTypeInfo>::const_iterator it = archinfo.begin(); it!=archinfo.end(); it++)
502 const ArchiveTypeInfo &inf = *it;
504 suppopen |= inf.type;
506 suppsave |= inf.type;
507 for (QStringList::const_iterator sit = inf.extensions.begin(); sit!=inf.extensions.end(); sit++)
511 if (openext != QString::null)
513 openext += "*" + *sit;
517 if (saveext != QString::null)
519 saveext += "*" + *sit;
525 QString ImgArchiveSink::makeTempDir()
528 strcpy(tmpd, "/tmp/qcomic-XXXXXX");
530 return QString(tmpd);
533 QString ImgArchiveSink::supportedOpenExtensions()
538 QString ImgArchiveSink::supportedSaveExtensions()
543 int ImgArchiveSink::supportedArchives()
548 QValueList<ImgArchiveSink::ArchiveTypeInfo> ImgArchiveSink::supportedArchivesInfo()
553 bool ImgArchiveSink::supportsOpen(ArchiveType t)
555 return (suppopen & t) > 0;
558 bool ImgArchiveSink::supportsSave(ArchiveType t)
560 return (suppsave & t) > 0;
563 bool ImgArchiveSink::supportsNext() const
568 QString ImgArchiveSink::getNext() const
570 QFileInfo finfo(getFullName());
571 QDir dir(finfo.dirPath(true)); //get the full path of current cb
572 QStringList files = dir.entryList(ImgArchiveSink::supportedOpenExtensions(), QDir::Files|QDir::Readable, QDir::Name);
573 QStringList::iterator it = files.find(finfo.fileName()); //find current cb
574 if (it != files.end())
575 if (++it != files.end()) //get next file name
576 return dir.filePath(*it, true);
577 return QString::null;
580 QString ImgArchiveSink::getPrevious() const
582 QFileInfo finfo(getFullName());
583 QDir dir(finfo.dirPath(true)); //get the full path of current cb
584 QStringList files = dir.entryList(ImgArchiveSink::supportedOpenExtensions(), QDir::Files|QDir::Readable, QDir::Name);
585 QStringList::iterator it = files.find(finfo.fileName()); //find current cb
586 if (--it != files.begin())
587 return dir.filePath(*it, true);
588 return QString::null;