diff options
| author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-03-07 03:10:53 +0000 |
|---|---|---|
| committer | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-03-07 03:10:53 +0000 |
| commit | f44554a6346605468f5fbc221746e32735db44ca (patch) | |
| tree | 308ddd0b6173cc1aa72c291fa64fe6a204ef1de7 /src | |
| parent | 4aae48c9b2e9b1280f724df50ac71dd64fe1c0df (diff) | |
2009-03-07 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added --index-out option to specify each file path for torrent.
Here index shown in --show-files option is used to specify which
file path should be altered. For example, to change the file
path with index=2, use --index-out=2=aria2.tar.bz2. You can use
this option multiple times: --index-out=1=aria2.tar.bz2
--index-out=2=aria2-opt.tar.bz2. The short hand form -O is also
available. This option can be specified in -i list.
* src/DefaultBtContext.cc
* src/DefaultBtContext.h
* src/MultiDiskAdaptor.cc
* src/MultiDiskAdaptor.h
* src/MultiFileAllocationIterator.cc
* src/OptionHandlerFactory.cc
* src/OptionHandlerImpl.h
* src/Util.cc
* src/Util.h
* src/download_helper.cc
* src/prefs.cc
* src/prefs.h
* src/usage_text.h
* test/DefaultBtContextTest.cc
* test/MultiDiskAdaptorTest.cc
* test/MultiFileAllocationIteratorTest.cc
* test/UtilTest.cc
Diffstat (limited to 'src')
| -rw-r--r-- | src/DefaultBtContext.cc | 25 | ||||
| -rw-r--r-- | src/DefaultBtContext.h | 6 | ||||
| -rw-r--r-- | src/MultiDiskAdaptor.cc | 73 | ||||
| -rw-r--r-- | src/MultiDiskAdaptor.h | 18 | ||||
| -rw-r--r-- | src/MultiFileAllocationIterator.cc | 2 | ||||
| -rw-r--r-- | src/OptionHandlerFactory.cc | 8 | ||||
| -rw-r--r-- | src/OptionHandlerImpl.h | 26 | ||||
| -rw-r--r-- | src/Util.cc | 22 | ||||
| -rw-r--r-- | src/Util.h | 8 | ||||
| -rw-r--r-- | src/download_helper.cc | 12 | ||||
| -rw-r--r-- | src/prefs.cc | 2 | ||||
| -rw-r--r-- | src/prefs.h | 2 | ||||
| -rw-r--r-- | src/usage_text.h | 6 |
13 files changed, 160 insertions, 50 deletions
diff --git a/src/DefaultBtContext.cc b/src/DefaultBtContext.cc index aede7d07..79976621 100644 --- a/src/DefaultBtContext.cc +++ b/src/DefaultBtContext.cc @@ -151,14 +151,20 @@ void DefaultBtContext::extractFileEntries(const bencode::BDE& infoDict, throw DlAbortEx("Path is empty."); } - std::vector<std::string> elements(pathList.size()); - std::transform(pathList.listBegin(), pathList.listEnd(), elements.begin(), + std::vector<std::string> pathelem(pathList.size()); + std::transform(pathList.listBegin(), pathList.listEnd(), pathelem.begin(), std::mem_fun_ref(&bencode::BDE::s)); - std::string path = Util::joinPath(elements.begin(), elements.end()); + std::string path = Util::joinPath(pathelem.begin(), pathelem.end()); + // Split path with '/' again because each pathList element can + // contain "/" inside. + std::deque<std::string> elements; + Util::slice(elements, path, '/'); + elements.push_front(name); + path = Util::joinPath(elements.begin(), elements.end()); std::deque<std::string> uris; std::transform(urlList.begin(), urlList.end(), std::back_inserter(uris), - std::bind2nd(std::plus<std::string>(), "/"+name+"/"+path)); + std::bind2nd(std::plus<std::string>(), "/"+path)); FileEntryHandle fileEntry(new FileEntry(path, fileLengthData.i(), offset, uris)); fileEntries.push_back(fileEntry); @@ -475,4 +481,15 @@ void DefaultBtContext::setInfoHash(const unsigned char* infoHash) memcpy(this->infoHash, infoHash, sizeof(this->infoHash)); } +void DefaultBtContext::setFilePathWithIndex +(size_t index, const std::string& path) +{ + if(0 < index && index <= fileEntries.size()) { + fileEntries[index-1]->setPath(path); + } else { + throw DlAbortEx(StringFormat("No such file with index=%u", + static_cast<unsigned int>(index)).str()); + } +} + } // namespace aria2 diff --git a/src/DefaultBtContext.h b/src/DefaultBtContext.h index d736edb7..3a1bec86 100644 --- a/src/DefaultBtContext.h +++ b/src/DefaultBtContext.h @@ -182,6 +182,12 @@ private: void setRandomizer(const SharedHandle<Randomizer>& randomizer); + // Sets file path for spcified index. index starts from 1. The index + // is the same used in BtContext::setFileFilter(). Please note that + // path is not the actual file path. The actual file path is + // getDir()+"/"+path. + void setFilePathWithIndex(size_t index, const std::string& path); + friend std::ostream& operator<<(std::ostream& o, const DefaultBtContext& ctx); static const std::string DEFAULT_PEER_ID_PREFIX; diff --git a/src/MultiDiskAdaptor.cc b/src/MultiDiskAdaptor.cc index b7ab9b53..c7da07ff 100644 --- a/src/MultiDiskAdaptor.cc +++ b/src/MultiDiskAdaptor.cc @@ -58,15 +58,15 @@ DiskWriterEntry::DiskWriterEntry(const SharedHandle<FileEntry>& fileEntry): DiskWriterEntry::~DiskWriterEntry() {} -std::string DiskWriterEntry::getFilePath(const std::string& topDir) const +std::string DiskWriterEntry::getFilePath(const std::string& storeDir) const { - return topDir+"/"+fileEntry->getPath(); + return storeDir+"/"+fileEntry->getPath(); } -void DiskWriterEntry::initAndOpenFile(const std::string& topDir) +void DiskWriterEntry::initAndOpenFile(const std::string& storeDir) { if(!diskWriter.isNull()) { - diskWriter->initAndOpenFile(getFilePath(topDir), fileEntry->getLength()); + diskWriter->initAndOpenFile(getFilePath(storeDir), fileEntry->getLength()); if(_directIO) { diskWriter->enableDirectIO(); } @@ -74,10 +74,10 @@ void DiskWriterEntry::initAndOpenFile(const std::string& topDir) } } -void DiskWriterEntry::openFile(const std::string& topDir) +void DiskWriterEntry::openFile(const std::string& storeDir) { if(!diskWriter.isNull()) { - diskWriter->openFile(getFilePath(topDir), fileEntry->getLength()); + diskWriter->openFile(getFilePath(storeDir), fileEntry->getLength()); if(_directIO) { diskWriter->enableDirectIO(); } @@ -85,10 +85,10 @@ void DiskWriterEntry::openFile(const std::string& topDir) } } -void DiskWriterEntry::openExistingFile(const std::string& topDir) +void DiskWriterEntry::openExistingFile(const std::string& storeDir) { if(!diskWriter.isNull()) { - diskWriter->openExistingFile(getFilePath(topDir), fileEntry->getLength()); + diskWriter->openExistingFile(getFilePath(storeDir), fileEntry->getLength()); if(_directIO) { diskWriter->enableDirectIO(); } @@ -109,9 +109,9 @@ void DiskWriterEntry::closeFile() } } -bool DiskWriterEntry::fileExists(const std::string& topDir) +bool DiskWriterEntry::fileExists(const std::string& storeDir) { - return File(getFilePath(topDir)).exists(); + return File(getFilePath(storeDir)).exists(); } uint64_t DiskWriterEntry::size() const @@ -270,9 +270,9 @@ void MultiDiskAdaptor::resetDiskWriterEntries() diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) { if((*i)->needsFileAllocation() || dwreq.find((*i)->getFileEntry()->getPath()) != dwreq.end() || - (*i)->fileExists(getTopDirPath())) { + (*i)->fileExists(storeDir)) { logger->debug("Creating DiskWriter for filename=%s", - (*i)->getFilePath(getTopDirPath()).c_str()); + (*i)->getFilePath(storeDir).c_str()); (*i)->setDiskWriter(dwFactory.newDiskWriter()); (*i)->getDiskWriter()->setDirectIOAllowed(_directIOAllowed); if(_readOnly) { @@ -287,25 +287,25 @@ std::string MultiDiskAdaptor::getTopDirPath() const return storeDir+"/"+topDir; } -void MultiDiskAdaptor::mkdir(const std::string& topDirPath) const +void MultiDiskAdaptor::mkdir(const std::string& storeDir) const { for(std::deque<SharedHandle<DiskWriterEntry> >::const_iterator i = diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) { - (*i)->getFileEntry()->setupDir(topDirPath); + (*i)->getFileEntry()->setupDir(storeDir); } } void MultiDiskAdaptor::openIfNot (const SharedHandle<DiskWriterEntry>& entry, void (DiskWriterEntry::*open)(const std::string&), - const std::string& topDirPath) + const std::string& storeDir) { if(!entry->isOpen()) { // logger->debug("DiskWriterEntry: Cache MISS. offset=%s", // Util::itos(entry->getFileEntry()->getOffset()).c_str()); size_t numOpened = _openedDiskWriterEntries.size(); - (entry.get()->*open)(topDirPath); + (entry.get()->*open)(storeDir); if(numOpened >= _maxOpenFiles) { // Cache is full. // Choose one DiskWriterEntry randomly and close it. @@ -326,32 +326,29 @@ void MultiDiskAdaptor::openIfNot void MultiDiskAdaptor::openFile() { - _cachedTopDirPath = getTopDirPath(); resetDiskWriterEntries(); - mkdir(_cachedTopDirPath); + mkdir(storeDir); // Call DiskWriterEntry::openFile to make sure that zero-length files are // created. for(DiskWriterEntries::iterator itr = diskWriterEntries.begin(); itr != diskWriterEntries.end(); ++itr) { - openIfNot(*itr, &DiskWriterEntry::openFile, _cachedTopDirPath); + openIfNot(*itr, &DiskWriterEntry::openFile, storeDir); } } void MultiDiskAdaptor::initAndOpenFile() { - _cachedTopDirPath = getTopDirPath(); resetDiskWriterEntries(); - mkdir(_cachedTopDirPath); + mkdir(storeDir); // Call DiskWriterEntry::initAndOpenFile to make files truncated. for(DiskWriterEntries::iterator itr = diskWriterEntries.begin(); itr != diskWriterEntries.end(); ++itr) { - openIfNot(*itr, &DiskWriterEntry::initAndOpenFile, _cachedTopDirPath); + openIfNot(*itr, &DiskWriterEntry::initAndOpenFile, storeDir); } } void MultiDiskAdaptor::openExistingFile() { - _cachedTopDirPath = getTopDirPath(); resetDiskWriterEntries(); // Not need to call openIfNot here. } @@ -416,12 +413,12 @@ findFirstDiskWriterEntry(const DiskWriterEntries& diskWriterEntries, off_t offse static void throwOnDiskWriterNotOpened(const SharedHandle<DiskWriterEntry>& e, off_t offset, - const std::string& topDirPath) + const std::string& storeDir) { throw DlAbortEx (StringFormat("DiskWriter for offset=%s, filename=%s is not opened.", Util::itos(offset).c_str(), - e->getFilePath(topDirPath).c_str()).str()); + e->getFilePath(storeDir).c_str()).str()); } void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len, @@ -434,10 +431,10 @@ void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len, for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) { size_t writeLength = calculateLength(*i, fileOffset, rem); - openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath); + openIfNot(*i, &DiskWriterEntry::openFile, storeDir); if(!(*i)->isOpen()) { - throwOnDiskWriterNotOpened(*i, offset+(len-rem), _cachedTopDirPath); + throwOnDiskWriterNotOpened(*i, offset+(len-rem), storeDir); } (*i)->getDiskWriter()->writeData(data+(len-rem), writeLength, fileOffset); @@ -459,10 +456,10 @@ ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len, off_t offset for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) { size_t readLength = calculateLength(*i, fileOffset, rem); - openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath); + openIfNot(*i, &DiskWriterEntry::openFile, storeDir); if(!(*i)->isOpen()) { - throwOnDiskWriterNotOpened(*i, offset+(len-rem), _cachedTopDirPath); + throwOnDiskWriterNotOpened(*i, offset+(len-rem), storeDir); } totalReadLength += @@ -478,12 +475,13 @@ ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len, off_t offset bool MultiDiskAdaptor::fileExists() { + // TODO Use FileEntry::fileExists() here. + // Don't use _cachedTopDirPath because they are initialized after opening files. // This method could be called before opening files. - std::string topDirPath = getTopDirPath(); for(std::deque<SharedHandle<FileEntry> >::iterator i = fileEntries.begin(); i != fileEntries.end(); ++i) { - if(File(topDirPath+"/"+(*i)->getPath()).exists()) { + if(File(storeDir+"/"+(*i)->getPath()).exists()) { return true; } } @@ -496,7 +494,7 @@ uint64_t MultiDiskAdaptor::size() uint64_t size = 0; for(DiskWriterEntries::const_iterator itr = diskWriterEntries.begin(); itr != diskWriterEntries.end(); ++itr) { - openIfNot(*itr, &DiskWriterEntry::openFile, _cachedTopDirPath); + openIfNot(*itr, &DiskWriterEntry::openFile, storeDir); size += (*itr)->size(); } return size; @@ -538,9 +536,9 @@ void MultiDiskAdaptor::cutTrailingGarbage() for(std::deque<SharedHandle<DiskWriterEntry> >::const_iterator i = diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) { uint64_t length = (*i)->getFileEntry()->getLength(); - if(File((*i)->getFilePath(_cachedTopDirPath)).size() > length) { + if(File((*i)->getFilePath(storeDir)).size() > length) { // We need open file before calling DiskWriter::truncate(uint64_t) - openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath); + openIfNot(*i, &DiskWriterEntry::openFile, storeDir); (*i)->getDiskWriter()->truncate(length); } } @@ -557,7 +555,7 @@ size_t MultiDiskAdaptor::utime(const Time& actime, const Time& modtime) for(std::deque<SharedHandle<FileEntry> >::const_iterator i = fileEntries.begin(); i != fileEntries.end(); ++i) { if((*i)->isRequested()) { - File f(getTopDirPath()+"/"+(*i)->getPath()); + File f(storeDir+"/"+(*i)->getPath()); if(f.isFile() && f.utime(actime, modtime)) { ++numOK; } @@ -572,4 +570,9 @@ MultiDiskAdaptor::getDiskWriterEntries() const return diskWriterEntries; } +const std::string& MultiDiskAdaptor::getStoreDir() const +{ + return storeDir; +} + } // namespace aria2 diff --git a/src/MultiDiskAdaptor.h b/src/MultiDiskAdaptor.h index a52e8b1e..686ef694 100644 --- a/src/MultiDiskAdaptor.h +++ b/src/MultiDiskAdaptor.h @@ -55,19 +55,19 @@ public: ~DiskWriterEntry(); - std::string getFilePath(const std::string& topDir) const; + std::string getFilePath(const std::string& storeDir) const; - void initAndOpenFile(const std::string& topDir); + void initAndOpenFile(const std::string& storeDir); - void openFile(const std::string& topDir); + void openFile(const std::string& storeDir); - void openExistingFile(const std::string& topDir); + void openExistingFile(const std::string& storeDir); void closeFile(); bool isOpen() const; - bool fileExists(const std::string& topDir); + bool fileExists(const std::string& storeDir); uint64_t size() const; @@ -105,8 +105,6 @@ private: size_t pieceLength; DiskWriterEntries diskWriterEntries; - std::string _cachedTopDirPath; - std::deque<SharedHandle<DiskWriterEntry> > _openedDiskWriterEntries; size_t _maxOpenFiles; @@ -117,13 +115,13 @@ private: void resetDiskWriterEntries(); - void mkdir(const std::string& topDirPath) const; + void mkdir(const std::string& storeDir) const; std::string getTopDirPath() const; void openIfNot(const SharedHandle<DiskWriterEntry>& entry, void (DiskWriterEntry::*f)(const std::string&), - const std::string& topDirPath); + const std::string& storeDir); static const size_t DEFAULT_MAX_OPEN_FILES = 100; @@ -200,6 +198,8 @@ public: const std::deque<SharedHandle<DiskWriterEntry> >& getDiskWriterEntries() const; + + const std::string& getStoreDir() const; }; typedef SharedHandle<MultiDiskAdaptor> MultiDiskAdaptorHandle; diff --git a/src/MultiFileAllocationIterator.cc b/src/MultiFileAllocationIterator.cc index 7fcb7b3e..7ab33832 100644 --- a/src/MultiFileAllocationIterator.cc +++ b/src/MultiFileAllocationIterator.cc @@ -59,7 +59,7 @@ void MultiFileAllocationIterator::allocateChunk() FileEntryHandle fileEntry = entry->getFileEntry(); // Open file before calling DiskWriterEntry::size() _diskAdaptor->openIfNot(entry, &DiskWriterEntry::openFile, - _diskAdaptor->getTopDirPath()); + _diskAdaptor->getStoreDir()); entry->enableDirectIO(); if(entry->needsFileAllocation() && entry->size() < fileEntry->getLength()) { // Calling private function of MultiDiskAdaptor. diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 9387881c..6b6ef14a 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -969,6 +969,14 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() handlers.push_back(op); } { + SharedHandle<OptionHandler> op(new IndexOutOptionHandler + (PREF_INDEX_OUT, + TEXT_INDEX_OUT, + 'O')); + op->addTag(TAG_BITTORRENT); + handlers.push_back(op); + } + { SharedHandle<OptionHandler> op(new IntegerRangeOptionHandler (PREF_LISTEN_PORT, TEXT_LISTEN_PORT, diff --git a/src/OptionHandlerImpl.h b/src/OptionHandlerImpl.h index d20ddbba..2475d4a9 100644 --- a/src/OptionHandlerImpl.h +++ b/src/OptionHandlerImpl.h @@ -366,6 +366,32 @@ public: } }; +class IndexOutOptionHandler : public NameMatchOptionHandler { +private: +public: + IndexOutOptionHandler(const std::string& optName, + const std::string& description, + char shortName = 0): + NameMatchOptionHandler(optName, description, NO_DEFAULT_VALUE, + OptionHandler::REQ_ARG, shortName) {} + + virtual ~IndexOutOptionHandler() {} + + virtual void parseArg(Option& option, const std::string& optarg) + { + // See optarg is in the fomrat of "INDEX=PATH" + Util::parseIndexPath(optarg); + std::string value = option.get(_optName); + value += optarg+"\n"; + option.put(_optName, value); + } + + virtual std::string createPossibleValuesString() const + { + return "INDEX=PATH"; + } +}; + class ParameterOptionHandler : public NameMatchOptionHandler { private: std::deque<std::string> _validParamValues; diff --git a/src/Util.cc b/src/Util.cc index c8227779..21ce6d37 100644 --- a/src/Util.cc +++ b/src/Util.cc @@ -941,4 +941,26 @@ std::string Util::htmlEscape(const std::string& src) return dest; } +std::map<size_t, std::string>::value_type +Util::parseIndexPath(const std::string& line) +{ + std::pair<std::string, std::string> p = Util::split(line, "="); + size_t index = parseUInt(p.first); + if(p.second.empty()) { + throw DlAbortEx(StringFormat("Path with index=%u is empty.", + static_cast<unsigned int>(index)).str()); + } + return std::map<size_t, std::string>::value_type(index, p.second); +} + +std::map<size_t, std::string> Util::createIndexPathMap(std::istream& i) +{ + std::map<size_t, std::string> indexPathMap; + std::string line; + while(getline(i, line)) { + indexPathMap.insert(indexPathMap.begin(), parseIndexPath(line)); + } + return indexPathMap; +} + } // namespace aria2 @@ -44,6 +44,7 @@ #include <deque> #include <iosfwd> #include <numeric> +#include <map> #include "SharedHandle.h" #include "IntSequence.h" @@ -306,6 +307,13 @@ public: return std::accumulate(elements.begin()+1, elements.end(), elements[0], Concat("/")); } + + // Parses INDEX=PATH format string. INDEX must be an unsigned + // integer. + static std::map<size_t, std::string>::value_type + parseIndexPath(const std::string& line); + + static std::map<size_t, std::string> createIndexPathMap(std::istream& i); }; } // namespace aria2 diff --git a/src/download_helper.cc b/src/download_helper.cc index 67988b6e..490c2215 100644 --- a/src/download_helper.cc +++ b/src/download_helper.cc @@ -37,6 +37,7 @@ #include <iostream> #include <fstream> #include <algorithm> +#include <sstream> #include "RequestGroup.h" #include "Option.h" @@ -124,6 +125,13 @@ createBtRequestGroup(const std::string& torrentFilePath, btContext->setDir(requestOption.get(PREF_DIR)); btContext->setFileFilter (Util::parseIntRange(requestOption.get(PREF_SELECT_FILE))); + std::istringstream indexOutIn(requestOption.get(PREF_INDEX_OUT)); + std::map<size_t, std::string> indexPathMap = + Util::createIndexPathMap(indexOutIn); + for(std::map<size_t, std::string>::const_iterator i = indexPathMap.begin(); + i != indexPathMap.end(); ++i) { + btContext->setFilePathWithIndex((*i).first, (*i).second); + } rg->setDownloadContext(btContext); btContext->setOwnerRequestGroup(rg.get()); return rg; @@ -295,11 +303,13 @@ static void createRequestGroupForUriList if(uris.empty()) { continue; } + // TODO use OptionParser to validate the value in the options. // These options can be specified in input list(-i list). static const std::string REQUEST_OPTIONS[] = { PREF_DIR, PREF_OUT, - PREF_SELECT_FILE + PREF_SELECT_FILE, + PREF_INDEX_OUT }; foreachCopyIfndef(&REQUEST_OPTIONS[0], &REQUEST_OPTIONS[arrayLength(REQUEST_OPTIONS)], diff --git a/src/prefs.cc b/src/prefs.cc index 765fd801..9306c127 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -283,6 +283,8 @@ const std::string PREF_BT_HASH_CHECK_SEED("bt-hash-check-seed"); const std::string PREF_BT_MAX_PEERS("bt-max-peers"); // values: a string (IP address) const std::string PREF_BT_EXTERNAL_IP("bt-external-ip"); +// values: 1*digit '=' a string that your file system recognizes as a file name. +const std::string PREF_INDEX_OUT("index-out"); /** * Metalink related preferences diff --git a/src/prefs.h b/src/prefs.h index dbaa1f2c..4a95b821 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -287,6 +287,8 @@ extern const std::string PREF_BT_HASH_CHECK_SEED; extern const std::string PREF_BT_MAX_PEERS; // values: a string (IP address) extern const std::string PREF_BT_EXTERNAL_IP; +// values: 1*digit '=' a string that your file system recognizes as a file name. +extern const std::string PREF_INDEX_OUT; /** * Metalink related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 92ae0bc0..ffce426c 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -493,3 +493,9 @@ _(" --http-auth-challenge[=true|false] Send HTTP authorization header only when " are embedded in URI, authorization header is\n"\ " always sent to the server regardless of this\n"\ " option.") +#define TEXT_INDEX_OUT \ +_(" -O, --index-out=INDEX=PATH Set file path for file with index=INDEX. You can\n"\ + " find the file index using the --show-files option.\n"\ + " PATH is a relative path to the path specified in\n"\ + " --dir option. You can use this option multiple\n"\ + " times.") |
