Przeglądaj źródła

2007-11-20 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	Preallocate non-requested file which is adjacent forward to 
requested
	file('requested' files means the files given in --select-file 
option)
	if they share a same piece.
	This fixes long pause in the file system which doesn't support 
sparse
	files like FAT32 while downloading.
	* src/MultiFileAllocationIterator.{h, cc}
	* test/MultiFileAllocationIteratorTest.cc
	* src/FileEntry.{h, cc}

	Removed unused _option.
	* src/MultiDiskAdaptor.h
	* test/MultiDiskAdaptorTest.cc
	* src/DefaultPieceStorage.cc

	Set the default value of --seed-ratio to 1.0.
	If 0.0 is given, then seeding continues regardless of share 
ratio.
	* src/version_usage.cc
	* src/option_processing.cc
	* src/BtSetup.cc
	* doc/aria2c.1.txt
	* doc/aria2c.1

	Fixed: Selective download is not working in BitTorrent
	* src/RequestGroup.cc

	Introduced Sequence class. Use this instead of 
Util::unfoldRange()
	* src/PieceStorage.h
	* test/MockPieceStorage.h
	* src/UnknownLengthPieceStorage.h
	* src/DefaultPieceStorage.{h, cc}
	* src/Metalink2RequestGroup.cc
	* src/RequestGroup.cc
	* src/Sequence.h
	* test/SequenceTest.cc
	* src/IntSequence.h
	* src/message.h
	* src/Util.{h, cc}
	* test/UtilTest.cc

	Added new function 'parse' to catch exception thrown by 
subclass's
	parseArg
	* src/OptionHandler.h
	* src/OptionParser.cc
	* src/NameMatchOptionHandler.h
	* src/OptionHandlerImpl.h
	* test/OptionHandlerTest.cc

	Added IntegerRangeOptionHandler. Used for --listen-port and
	--select-file. Now --listen-port accepts range of port.
	* src/OptionHandlerFactory.cc
	* src/version_usage.cc
	* src/OptionHandlerImpl.h
	* src/option_processing.cc
	* src/BtSetup.cc
	* src/PeerListenCommand.{h, cc}
	* doc/aria2c.1.txt
	* doc/aria2c.1
	
	Implemented operator< for Exception class to provide easy way to 
print
	exception stack trace.
	* src/Exception.{h, cc}
	* src/main.cc
	* src/option_processing.cc
Tatsuhiro Tsujikawa 18 lat temu
rodzic
commit
c9e3d51054

+ 66 - 0
ChangeLog

@@ -1,3 +1,69 @@
+2007-11-20  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	Preallocate non-requested file which is adjacent forward to requested
+	file('requested' files means the files given in --select-file option)
+	if they share a same piece.
+	This fixes long pause in the file system which doesn't support sparse
+	files like FAT32 while downloading.
+	* src/MultiFileAllocationIterator.{h, cc}
+	* test/MultiFileAllocationIteratorTest.cc
+	* src/FileEntry.{h, cc}
+
+	Removed unused _option.
+	* src/MultiDiskAdaptor.h
+	* test/MultiDiskAdaptorTest.cc
+	* src/DefaultPieceStorage.cc
+
+	Set the default value of --seed-ratio to 1.0.
+	If 0.0 is given, then seeding continues regardless of share ratio.
+	* src/version_usage.cc
+	* src/option_processing.cc
+	* src/BtSetup.cc
+	* doc/aria2c.1.txt
+	* doc/aria2c.1
+
+	Fixed: Selective download is not working in BitTorrent
+	* src/RequestGroup.cc
+
+	Introduced Sequence class. Use this instead of Util::unfoldRange()
+	* src/PieceStorage.h
+	* test/MockPieceStorage.h
+	* src/UnknownLengthPieceStorage.h
+	* src/DefaultPieceStorage.{h, cc}
+	* src/Metalink2RequestGroup.cc
+	* src/RequestGroup.cc
+	* src/Sequence.h
+	* test/SequenceTest.cc
+	* src/IntSequence.h
+	* src/message.h
+	* src/Util.{h, cc}
+	* test/UtilTest.cc
+
+	Added new function 'parse' to catch exception thrown by subclass's
+	parseArg
+	* src/OptionHandler.h
+	* src/OptionParser.cc
+	* src/NameMatchOptionHandler.h
+	* src/OptionHandlerImpl.h
+	* test/OptionHandlerTest.cc
+
+	Added IntegerRangeOptionHandler. Used for --listen-port and
+	--select-file. Now --listen-port accepts range of port.
+	* src/OptionHandlerFactory.cc
+	* src/version_usage.cc
+	* src/OptionHandlerImpl.h
+	* src/option_processing.cc
+	* src/BtSetup.cc
+	* src/PeerListenCommand.{h, cc}
+	* doc/aria2c.1.txt
+	* doc/aria2c.1
+	
+	Implemented operator< for Exception class to provide easy way to print
+	exception stack trace.
+	* src/Exception.{h, cc}
+	* src/main.cc
+	* src/option_processing.cc
+	
 2007-11-18  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	* src/version_usage.cc

+ 2 - 1
TODO

@@ -4,7 +4,6 @@
 * Add SSL client cert support
 * Better HTTP status handling
 * Add the feature which adds or removes URLs on-the-fly.
-* Add port range command-line option
 * Add max peers command-line option
 * Refacturing HttpConnection and FtpConnection
 * Query resource by location
@@ -57,3 +56,5 @@
 
 -- remaining issues to be implemented for 0.12.0 release
 * Update translation
+* replace strtol with Util::parseInt
+* precision of share ratio

+ 23 - 7
doc/aria2c.1

@@ -1,11 +1,11 @@
 .\"     Title: aria2c
 .\"    Author: 
 .\" Generator: DocBook XSL Stylesheets v1.73.1 <http://docbook.sf.net/>
-.\"      Date: 11/18/2007
+.\"      Date: 11/22/2007
 .\"    Manual: 
 .\"    Source: 
 .\"
-.TH "ARIA2C" "1" "11/18/2007" "" ""
+.TH "ARIA2C" "1" "11/22/2007" "" ""
 .\" disable hyphenation
 .nh
 .\" disable justification (adjust text to left margin only)
@@ -319,6 +319,15 @@ to specify a range: "1\-5"\.
 and
 \fI\-\fR
 can be used together\. When used with the \-M option, index may vary depending on the query(see \-\-metalink\-* options)\.
+.sp
+.RS 4
+.nf
+Note: In multi file torrent, it seems that adjacent files specified
+by this option are downloaded\. This is by design, not a bug\.
+A single piece may include several files or part of files,
+and aria2 writes the piece to the appropriate files\.
+.fi
+.RE
 .RE
 .PP
 \-T, \-\-torrent\-file=TORRENT_FILE
@@ -335,11 +344,18 @@ Set to false to prevent aria2 from entering BitTorrent mode even if the filename
 .RS 4
 Directly read from and write to each file mentioned in \.torrent file\. Default:
 \fItrue\fR
-.RE
-.PP
-\-\-listen\-port=PORT
+.sp
 .RS 4
-Set TCP port number for BitTorrent downloads\. Default: 6881\-6999
+.nf
+\-\-listen\-port=PORT\.\.\.
+       Set TCP port number for BitTorrent downloads\.
+       Multiple values can be specified by using \',\',
+       for example: "6881,6885"\.
+       You can also use \'\-\' to specify a range: "6881\-6999"\.
+       \',\' and \'\-\' can be used together\.
+       Default: 6881\-6999
+.fi
+.RE
 .RE
 .PP
 \-\-max\-upload\-limit=SPEED
@@ -360,7 +376,7 @@ Specify seeding time in minutes\. Also see the \-\-seed\-ratio option\.
 .PP
 \-\-seed\-ratio=RATIO
 .RS 4
-Specify share ratio\. Seed completed torrents until share ratio reaches RATIO\. 1\.0 is encouraged\. If \-\-seed\-time option is specified along with this option, seeding ends when at least one of the conditions is satisfied\.
+Specify share ratio\. Seed completed torrents until share ratio reaches RATIO\. 1\.0 is encouraged\. Specify 0\.0 if you intend to do seeding regardless of share ratio\. If \-\-seed\-time option is specified along with this option, seeding ends when at least one of the conditions is satisfied\. Default: 1\.0
 .RE
 .PP
 \-\-peer\-id\-prefix=PEERI_ID_PREFIX

+ 28 - 16
doc/aria2c.1.txt

@@ -229,14 +229,19 @@ http://host/image[000-100:2].img
      and exit.
 
  --select-file=INDEX...::
-       Set file to download by specifing its index.
-                              You can find the file index using the
-                              --show-files option. Multiple indexes can be
-                              specified by using ',', for example: "3,6".
-                              You can also use '-' to specify a range: "1-5".
-                              ',' and '-' can be used together.
-                              When used with the -M option, index may vary
-                              depending on the query(see --metalink-* options).
+	Set file to download by specifing its index.
+	You can find the file index using the
+	--show-files option. Multiple indexes can be
+	specified by using ',', for example: "3,6".
+        You can also use '-' to specify a range: "1-5".
+        ',' and '-' can be used together.
+        When used with the -M option, index may vary
+        depending on the query(see --metalink-* options).
+
+	Note: In multi file torrent, it seems that adjacent files specified
+	by this option are downloaded. This is by design, not a bug.
+	A single piece may include several files or part of files,
+	and aria2 writes the piece to the appropriate files.
 
  -T, --torrent-file=TORRENT_FILE::
   The path to the .torrent file.
@@ -252,9 +257,13 @@ http://host/image[000-100:2].img
                               mentioned in .torrent file.
                               Default: 'true'
 
- --listen-port=PORT::
-         Set TCP port number for BitTorrent downloads.
-                              Default: 6881-6999
+ --listen-port=PORT...
+	Set TCP port number for BitTorrent downloads.
+        Multiple values can be specified by using ',',
+        for example: "6881,6885".
+        You can also use '-' to specify a range: "6881-6999".
+        ',' and '-' can be used together.
+        Default: 6881-6999
 
  --max-upload-limit=SPEED::
      Set max upload speed in bytes per sec.
@@ -267,11 +276,14 @@ http://host/image[000-100:2].img
                               --seed-ratio option.
 
  --seed-ratio=RATIO::
-           Specify share ratio. Seed completed torrents
-                              until share ratio reaches RATIO. 1.0 is
-                              encouraged. If --seed-time option is specified
-                              along with this option, seeding ends when at
-                              least one of the conditions is satisfied.
+	Specify share ratio. Seed completed torrents
+	until share ratio reaches RATIO. 1.0 is
+	encouraged. Specify 0.0 if you intend to do
+	seeding regardless of share ratio.
+	If --seed-time option is specified along with
+	this option, seeding ends when at least one of
+	the conditions is satisfied.
+	Default: 1.0
 
  --peer-id-prefix=PEERI_ID_PREFIX::
              Specify the prefix of peer ID. The peer ID in

+ 9 - 10
src/BtSetup.cc

@@ -53,6 +53,7 @@
 #include "LogFactory.h"
 #include "Logger.h"
 #include "Util.h"
+#include "IntSequence.h"
 
 BtSetup::BtSetup():_logger(LogFactory::getInstance()) {}
 
@@ -85,9 +86,12 @@ Commands BtSetup::setup(RequestGroup* requestGroup,
   if(option->defined(PREF_SEED_TIME)) {
     unionCri->addSeedCriteria(new TimeSeedCriteria(option->getAsInt(PREF_SEED_TIME)*60));
   }
-  if(option->defined(PREF_SEED_RATIO)) {
-    unionCri->addSeedCriteria(new ShareRatioSeedCriteria(option->getAsDouble(PREF_SEED_RATIO), btContext));
-      }
+  {
+    double ratio = option->getAsDouble(PREF_SEED_RATIO);
+    if(ratio > 0.0) {
+      unionCri->addSeedCriteria(new ShareRatioSeedCriteria(option->getAsDouble(PREF_SEED_RATIO), btContext));
+    }
+  }
   if(unionCri->getSeedCriterion().size() > 0) {
     commands.push_back(new SeedCheckCommand(CUIDCounterSingletonHolder::instance()->newID(),
 					    requestGroup,
@@ -98,13 +102,8 @@ Commands BtSetup::setup(RequestGroup* requestGroup,
 
   if(PeerListenCommand::getNumInstance() == 0) {
     PeerListenCommand* listenCommand = PeerListenCommand::getInstance(e);
-    int32_t port;
-    int32_t listenPort = option->getAsInt(PREF_LISTEN_PORT);
-    if(listenPort == -1) {
-      port = listenCommand->bindPort(6881, 6999);
-    } else {
-      port = listenCommand->bindPort(listenPort, listenPort);
-    }
+    IntSequence seq = Util::parseIntRange(option->get(PREF_LISTEN_PORT));
+    int32_t port = listenCommand->bindPort(seq);
     if(port == -1) {
       _logger->error(_("Errors occurred while binding port.\n"));
       delete listenCommand;

+ 0 - 5
src/DefaultDiskWriter.cc

@@ -33,11 +33,6 @@
  */
 /* copyright --> */
 #include "DefaultDiskWriter.h"
-#include "message.h"
-#include "prefs.h"
-#include "Util.h"
-#include <errno.h>
-#include <unistd.h>
 
 DefaultDiskWriter::DefaultDiskWriter():AbstractDiskWriter() {}
 

+ 5 - 7
src/DefaultPieceStorage.cc

@@ -391,8 +391,11 @@ void DefaultPieceStorage::setFileFilter(const Strings& filePaths)
   bitfieldMan->enableFilter();
 }
 
-void DefaultPieceStorage::setFileFilter(const Integers& fileIndexes)
+void DefaultPieceStorage::setFileFilter(IntSequence seq)
 {
+  Integers fileIndexes = seq.flush();
+  sort(fileIndexes.begin(), fileIndexes.end());
+  fileIndexes.erase(unique(fileIndexes.begin(), fileIndexes.end()), fileIndexes.end());
   Strings filePaths;
   const FileEntries& entries = diskAdaptor->getFileEntries();
   for(int32_t i = 0; i < (int32_t)entries.size(); i++) {
@@ -441,7 +444,6 @@ void DefaultPieceStorage::initStorage()
       MultiDiskAdaptorHandle multiDiskAdaptor = new MultiDiskAdaptor();
       multiDiskAdaptor->setPieceLength(downloadContext->getPieceLength());
       multiDiskAdaptor->setTopDir(downloadContext->getName());
-      multiDiskAdaptor->setOption(option);
       this->diskAdaptor = multiDiskAdaptor;
     } else {
       logger->debug("Instantiating CopyDiskAdaptor");
@@ -456,11 +458,7 @@ void DefaultPieceStorage::initStorage()
       this->diskAdaptor = copyDiskAdaptor;
     }
   }
-  string storeDir = downloadContext->getDir();//option->get(PREF_DIR);
-//   if(storeDir == "") {
-//     storeDir = ".";
-//   }
-  diskAdaptor->setStoreDir(storeDir);
+  diskAdaptor->setStoreDir(downloadContext->getDir());
   diskAdaptor->setFileEntries(downloadContext->getFileEntries());
 }
 

+ 1 - 1
src/DefaultPieceStorage.h

@@ -128,7 +128,7 @@ public:
 
   virtual void setFileFilter(const Strings& filePaths);
 
-  virtual void setFileFilter(const Integers& fileIndexes);
+  virtual void setFileFilter(IntSequence seq);
 
   virtual void clearFileFilter();
 

+ 0 - 2
src/DownloadEngine.cc

@@ -47,9 +47,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <signal.h>
-#include <algorithm>
 
 volatile sig_atomic_t globalHaltRequested;
 

+ 44 - 0
src/Exception.cc

@@ -0,0 +1,44 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "Exception.h"
+
+ostream& operator<<(ostream& o, const Exception& e)
+{
+  o << e.getMsg() << "\n";
+  for(Exception* cause = e.getCause(); cause; cause = cause->getCause()) {
+    o << "Cause: " << cause->getMsg() << "\n";
+  }
+  return o;
+}

+ 2 - 0
src/Exception.h

@@ -63,6 +63,8 @@ public:
   const string& getMsg() const { return msg; }
 
   Exception* getCause() const { return cause; }
+
+  friend ostream& operator<<(ostream& o, const Exception& e);
 };
 
 #endif // _D_EXCEPTION_H_

+ 5 - 0
src/FileEntry.cc

@@ -62,3 +62,8 @@ FileEntry& FileEntry::operator=(const FileEntry& entry)
   }
   return *this;
 }
+
+bool FileEntry::operator<(const FileEntry& fileEntry) const
+{
+  return offset < fileEntry.offset;
+}

+ 2 - 0
src/FileEntry.h

@@ -91,6 +91,8 @@ public:
   {
     return _uris;
   }
+
+  bool operator<(const FileEntry& fileEntry) const;
 };
 
 typedef SharedHandle<FileEntry> FileEntryHandle;

+ 42 - 0
src/IntSequence.h

@@ -0,0 +1,42 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_INT_SEQUENCE_H_
+#define _D_INT_SEQUENCE_H_
+
+#include "Sequence.h"
+
+typedef Sequence<int32_t> IntSequence;
+
+#endif // _D_INT_SEQUENCE_H_

+ 1 - 1
src/Makefile.am

@@ -32,7 +32,7 @@ SRCS =  Socket.h\
 	Request.cc Request.h\
 	common.h\
 	message.h\
-	Exception.h\
+	Exception.cc Exception.h\
 	FatalException.h\
 	RecoverableException.h\
 	DlAbortEx.h\

+ 48 - 47
src/Makefile.in

@@ -202,29 +202,29 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	DownloadEngine.cc DownloadEngine.h Segment.h GrowSegment.cc \
 	GrowSegment.h PiecedSegment.cc PiecedSegment.h SegmentMan.cc \
 	SegmentMan.h Util.cc Util.h Request.cc Request.h common.h \
-	message.h Exception.h FatalException.h RecoverableException.h \
-	DlAbortEx.h DlRetryEx.h Logger.h SimpleLogger.cc \
-	SimpleLogger.h TransferEncoding.h ChunkedEncoding.cc \
-	ChunkedEncoding.h DiskWriter.h AbstractDiskWriter.cc \
-	AbstractDiskWriter.h DefaultDiskWriter.cc DefaultDiskWriter.h \
-	File.cc File.h Option.cc Option.h Base64.cc Base64.h \
-	CookieBox.cc CookieBox.h LogFactory.cc LogFactory.h \
-	NullLogger.h TimeA2.cc TimeA2.h SharedHandle.h \
-	HandleRegistry.h FeatureConfig.cc FeatureConfig.h \
-	DownloadEngineFactory.cc DownloadEngineFactory.h RequestInfo.h \
-	SpeedCalc.cc SpeedCalc.h PeerStat.h BitfieldMan.cc \
-	BitfieldMan.h BitfieldManFactory.cc BitfieldManFactory.h \
-	Randomizer.h SimpleRandomizer.cc SimpleRandomizer.h \
-	FileAllocator.h HttpResponse.cc HttpResponse.h HttpRequest.cc \
-	HttpRequest.h Range.h AbstractProxyRequestCommand.cc \
-	AbstractProxyRequestCommand.h AbstractProxyResponseCommand.cc \
-	AbstractProxyResponseCommand.h Netrc.cc Netrc.h AuthConfig.cc \
-	AuthConfig.h AuthResolver.h AbstractAuthResolver.h \
-	DefaultAuthResolver.cc DefaultAuthResolver.h \
-	NetrcAuthResolver.cc NetrcAuthResolver.h AuthConfigFactory.cc \
-	AuthConfigFactory.h OptionParser.cc OptionParser.h \
-	OptionHandlerFactory.cc OptionHandlerFactory.h NameResolver.cc \
-	NameResolver.h RequestGroup.cc RequestGroup.h \
+	message.h Exception.cc Exception.h FatalException.h \
+	RecoverableException.h DlAbortEx.h DlRetryEx.h Logger.h \
+	SimpleLogger.cc SimpleLogger.h TransferEncoding.h \
+	ChunkedEncoding.cc ChunkedEncoding.h DiskWriter.h \
+	AbstractDiskWriter.cc AbstractDiskWriter.h \
+	DefaultDiskWriter.cc DefaultDiskWriter.h File.cc File.h \
+	Option.cc Option.h Base64.cc Base64.h CookieBox.cc CookieBox.h \
+	LogFactory.cc LogFactory.h NullLogger.h TimeA2.cc TimeA2.h \
+	SharedHandle.h HandleRegistry.h FeatureConfig.cc \
+	FeatureConfig.h DownloadEngineFactory.cc \
+	DownloadEngineFactory.h RequestInfo.h SpeedCalc.cc SpeedCalc.h \
+	PeerStat.h BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
+	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
+	SimpleRandomizer.h FileAllocator.h HttpResponse.cc \
+	HttpResponse.h HttpRequest.cc HttpRequest.h Range.h \
+	AbstractProxyRequestCommand.cc AbstractProxyRequestCommand.h \
+	AbstractProxyResponseCommand.cc AbstractProxyResponseCommand.h \
+	Netrc.cc Netrc.h AuthConfig.cc AuthConfig.h AuthResolver.h \
+	AbstractAuthResolver.h DefaultAuthResolver.cc \
+	DefaultAuthResolver.h NetrcAuthResolver.cc NetrcAuthResolver.h \
+	AuthConfigFactory.cc AuthConfigFactory.h OptionParser.cc \
+	OptionParser.h OptionHandlerFactory.cc OptionHandlerFactory.h \
+	NameResolver.cc NameResolver.h RequestGroup.cc RequestGroup.h \
 	RequestGroupAware.cc RequestGroupAware.h RequestGroupMan.cc \
 	RequestGroupMan.h FileAllocationMan.cc FileAllocationMan.h \
 	FileAllocationCommand.cc FileAllocationCommand.h \
@@ -433,7 +433,7 @@ am__objects_12 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	FtpTunnelResponseCommand.$(OBJEXT) SleepCommand.$(OBJEXT) \
 	DownloadEngine.$(OBJEXT) GrowSegment.$(OBJEXT) \
 	PiecedSegment.$(OBJEXT) SegmentMan.$(OBJEXT) Util.$(OBJEXT) \
-	Request.$(OBJEXT) SimpleLogger.$(OBJEXT) \
+	Request.$(OBJEXT) Exception.$(OBJEXT) SimpleLogger.$(OBJEXT) \
 	ChunkedEncoding.$(OBJEXT) AbstractDiskWriter.$(OBJEXT) \
 	DefaultDiskWriter.$(OBJEXT) File.$(OBJEXT) Option.$(OBJEXT) \
 	Base64.$(OBJEXT) CookieBox.$(OBJEXT) LogFactory.$(OBJEXT) \
@@ -694,29 +694,29 @@ SRCS = Socket.h SocketCore.cc SocketCore.h Command.cc Command.h \
 	DownloadEngine.cc DownloadEngine.h Segment.h GrowSegment.cc \
 	GrowSegment.h PiecedSegment.cc PiecedSegment.h SegmentMan.cc \
 	SegmentMan.h Util.cc Util.h Request.cc Request.h common.h \
-	message.h Exception.h FatalException.h RecoverableException.h \
-	DlAbortEx.h DlRetryEx.h Logger.h SimpleLogger.cc \
-	SimpleLogger.h TransferEncoding.h ChunkedEncoding.cc \
-	ChunkedEncoding.h DiskWriter.h AbstractDiskWriter.cc \
-	AbstractDiskWriter.h DefaultDiskWriter.cc DefaultDiskWriter.h \
-	File.cc File.h Option.cc Option.h Base64.cc Base64.h \
-	CookieBox.cc CookieBox.h LogFactory.cc LogFactory.h \
-	NullLogger.h TimeA2.cc TimeA2.h SharedHandle.h \
-	HandleRegistry.h FeatureConfig.cc FeatureConfig.h \
-	DownloadEngineFactory.cc DownloadEngineFactory.h RequestInfo.h \
-	SpeedCalc.cc SpeedCalc.h PeerStat.h BitfieldMan.cc \
-	BitfieldMan.h BitfieldManFactory.cc BitfieldManFactory.h \
-	Randomizer.h SimpleRandomizer.cc SimpleRandomizer.h \
-	FileAllocator.h HttpResponse.cc HttpResponse.h HttpRequest.cc \
-	HttpRequest.h Range.h AbstractProxyRequestCommand.cc \
-	AbstractProxyRequestCommand.h AbstractProxyResponseCommand.cc \
-	AbstractProxyResponseCommand.h Netrc.cc Netrc.h AuthConfig.cc \
-	AuthConfig.h AuthResolver.h AbstractAuthResolver.h \
-	DefaultAuthResolver.cc DefaultAuthResolver.h \
-	NetrcAuthResolver.cc NetrcAuthResolver.h AuthConfigFactory.cc \
-	AuthConfigFactory.h OptionParser.cc OptionParser.h \
-	OptionHandlerFactory.cc OptionHandlerFactory.h NameResolver.cc \
-	NameResolver.h RequestGroup.cc RequestGroup.h \
+	message.h Exception.cc Exception.h FatalException.h \
+	RecoverableException.h DlAbortEx.h DlRetryEx.h Logger.h \
+	SimpleLogger.cc SimpleLogger.h TransferEncoding.h \
+	ChunkedEncoding.cc ChunkedEncoding.h DiskWriter.h \
+	AbstractDiskWriter.cc AbstractDiskWriter.h \
+	DefaultDiskWriter.cc DefaultDiskWriter.h File.cc File.h \
+	Option.cc Option.h Base64.cc Base64.h CookieBox.cc CookieBox.h \
+	LogFactory.cc LogFactory.h NullLogger.h TimeA2.cc TimeA2.h \
+	SharedHandle.h HandleRegistry.h FeatureConfig.cc \
+	FeatureConfig.h DownloadEngineFactory.cc \
+	DownloadEngineFactory.h RequestInfo.h SpeedCalc.cc SpeedCalc.h \
+	PeerStat.h BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
+	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
+	SimpleRandomizer.h FileAllocator.h HttpResponse.cc \
+	HttpResponse.h HttpRequest.cc HttpRequest.h Range.h \
+	AbstractProxyRequestCommand.cc AbstractProxyRequestCommand.h \
+	AbstractProxyResponseCommand.cc AbstractProxyResponseCommand.h \
+	Netrc.cc Netrc.h AuthConfig.cc AuthConfig.h AuthResolver.h \
+	AbstractAuthResolver.h DefaultAuthResolver.cc \
+	DefaultAuthResolver.h NetrcAuthResolver.cc NetrcAuthResolver.h \
+	AuthConfigFactory.cc AuthConfigFactory.h OptionParser.cc \
+	OptionParser.h OptionHandlerFactory.cc OptionHandlerFactory.h \
+	NameResolver.cc NameResolver.h RequestGroup.cc RequestGroup.h \
 	RequestGroupAware.cc RequestGroupAware.h RequestGroupMan.cc \
 	RequestGroupMan.h FileAllocationMan.cc FileAllocationMan.h \
 	FileAllocationCommand.cc FileAllocationCommand.h \
@@ -925,6 +925,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadEngine.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadEngineFactory.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Exception.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeatureConfig.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/File.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileAllocationCommand.Po@am__quote@

+ 1 - 2
src/Metalink2RequestGroup.cc

@@ -101,9 +101,8 @@ RequestGroups Metalink2RequestGroup::generate(const string& metalinkFile)
     _logger->notice(EX_NO_RESULT_WITH_YOUR_PREFS);
     return RequestGroups();
   }
+  Integers selectIndexes = Util::parseIntRange(_option->get(PREF_SELECT_FILE)).flush();
   bool useIndex;
-  Integers selectIndexes;
-  Util::unfoldRange(_option->get(PREF_SELECT_FILE), selectIndexes);
   if(selectIndexes.size()) {
     useIndex = true;
   } else {

+ 1 - 12
src/MultiDiskAdaptor.h

@@ -36,7 +36,6 @@
 #define _D_MULTI_DISK_ADAPTOR_H_
 
 #include "DiskAdaptor.h"
-#include "Option.h"
 #include "DiskWriter.h"
 #include "File.h"
 
@@ -108,7 +107,6 @@ private:
   string topDir;
   int32_t pieceLength;
   DiskWriterEntries diskWriterEntries;
-  const Option* option;
 
   void resetDiskWriterEntries();
 
@@ -122,8 +120,7 @@ private:
 
   string getTopDirPath() const;
 public:
-  MultiDiskAdaptor():pieceLength(0),
-		     option(0)
+  MultiDiskAdaptor():pieceLength(0)
   {}
 
   virtual ~MultiDiskAdaptor() {}
@@ -169,14 +166,6 @@ public:
   int32_t getPieceLength() const {
     return pieceLength;
   }
-
-  void setOption(const Option* option) {
-    this->option = option;
-  }
-
-  const Option* getOption() const {
-    return option;
-  }
 };
 
 typedef SharedHandle<MultiDiskAdaptor> MultiDiskAdaptorHandle;

+ 45 - 9
src/MultiFileAllocationIterator.cc

@@ -40,7 +40,7 @@
 
 MultiFileAllocationIterator::MultiFileAllocationIterator(MultiDiskAdaptor* diskAdaptor):
   _diskAdaptor(diskAdaptor),
-  _entries(diskAdaptor->getFileEntries()),
+  _entries(makeFileEntries(diskAdaptor->getFileEntries(), diskAdaptor->getPieceLength())),
   _currentEntry(0),
   _offset(0)
 {}
@@ -51,16 +51,14 @@ void MultiFileAllocationIterator::prepareNextEntry()
 {
   _currentEntry = 0;
   _offset = 0;
-  while(!_entries.empty()) {
+  if(!_entries.empty()) {
     FileEntryHandle entry = _entries.front();
     _entries.pop_front();
-    if(entry->isRequested()) {
-      _currentEntry = entry;
-      _offset = File(_diskAdaptor->getStoreDir()+"/"+
-		     _diskAdaptor->getTopDir()+"/"+
-		     _currentEntry->getPath()).size();
-      break;
-    }
+
+    _currentEntry = entry;
+    _offset = File(_diskAdaptor->getStoreDir()+"/"+
+		   _diskAdaptor->getTopDir()+"/"+
+		   _currentEntry->getPath()).size();
   }
 }
 
@@ -99,3 +97,41 @@ int64_t MultiFileAllocationIterator::getTotalLength()
     return _currentEntry->getLength();
   }
 }
+
+const FileEntries& MultiFileAllocationIterator::getFileEntries() const
+{
+  return _entries;
+}
+
+FileEntries MultiFileAllocationIterator::makeFileEntries(const FileEntries& srcEntries, int32_t pieceLength) const
+{
+  if(pieceLength == 0) {
+    FileEntries entries;
+    for(FileEntries::const_iterator itr = srcEntries.begin(); itr != srcEntries.end(); ++itr) {
+      if((*itr)->isRequested()) {
+	entries.push_back(*itr);
+      }
+    }
+    return entries;
+  }
+  FileEntries temp(srcEntries);
+  temp.push_front(new FileEntry());
+  FileEntries entries;
+  FileEntries::const_iterator done = temp.begin();
+  for(FileEntries::const_iterator itr = temp.begin()+1; itr != temp.end(); ++itr) {
+    if(!(*itr)->isRequested()) {
+      continue;
+    }
+    int64_t pieceStartOffset = ((*itr)->getOffset()/pieceLength)*pieceLength;
+    for(FileEntries::const_iterator i = itr-1; i != done; --i) {
+      if(pieceStartOffset < (*i)->getOffset()+(*i)->getLength()) {
+	entries.push_back(*i);
+      } else {
+	break;
+      }
+    }
+    entries.push_back(*itr);
+    done = itr;
+  }
+  return entries;
+}

+ 4 - 0
src/MultiFileAllocationIterator.h

@@ -49,6 +49,8 @@ private:
   FileEntries _entries;
   FileEntryHandle _currentEntry;
   int64_t _offset;
+
+  FileEntries makeFileEntries(const FileEntries& srcEntries, int32_t pieceLength) const;
 public:
   MultiFileAllocationIterator(MultiDiskAdaptor* diskAdaptor);
 
@@ -66,6 +68,8 @@ public:
   }
 
   virtual int64_t getTotalLength();
+
+  const FileEntries& getFileEntries() const;
 };
 
 typedef SharedHandle<MultiFileAllocationIterator> MultiFileAllocationIteratorHandle;

+ 12 - 0
src/NameMatchOptionHandler.h

@@ -36,10 +36,13 @@
 #define _D_NAME_MATCH_OPTION_HANDLER_H_
 
 #include "OptionHandler.h"
+#include "DlAbortEx.h"
 
 class NameMatchOptionHandler : public OptionHandler {
 protected:
   string _optName;
+
+  virtual void parseArg(Option* option, const string& arg) = 0;
 public:
   NameMatchOptionHandler(const string& optName):_optName(optName) {}
 
@@ -49,6 +52,15 @@ public:
   {
     return strcasecmp(_optName.c_str(), optName.c_str()) == 0;
   }
+
+  virtual void parse(Option* option, const string& arg)
+  {
+    try {
+      parseArg(option, arg);
+    } catch(Exception* e) {
+      throw new DlAbortEx(e, "Exception occurred while processing option %s", _optName.c_str());
+    }
+  }
 };
 
 typedef SharedHandle<NameMatchOptionHandler> NameMatchOptionHandlerHandle;

+ 1 - 1
src/OptionHandler.h

@@ -43,7 +43,7 @@ public:
   virtual ~OptionHandler() {}
   
   virtual bool canHandle(const string& optName) = 0;
-  virtual void parseArg(Option* option, const string& arg) = 0;
+  virtual void parse(Option* option, const string& arg) = 0;
 };
 
 typedef SharedHandle<OptionHandler> OptionHandlerHandle;

+ 2 - 2
src/OptionHandlerFactory.cc

@@ -55,11 +55,11 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
   handlers.push_back(new UnitNumberOptionHandler(PREF_MIN_SEGMENT_SIZE, 1024));
   handlers.push_back(new ParameterOptionHandler(PREF_HTTP_PROXY_METHOD,
 						V_GET, V_TUNNEL));
-  handlers.push_back(new NumberOptionHandler(PREF_LISTEN_PORT, 1024, UINT16_MAX));
+  handlers.push_back(new IntegerRangeOptionHandler(PREF_LISTEN_PORT, 1024, UINT16_MAX));
   handlers.push_back(new BooleanOptionHandler(PREF_FOLLOW_TORRENT));
   handlers.push_back(new BooleanOptionHandler(PREF_NO_PREALLOCATION));
   handlers.push_back(new BooleanOptionHandler(PREF_DIRECT_FILE_MAPPING));
-  handlers.push_back(new DefaultOptionHandler(PREF_SELECT_FILE));
+  handlers.push_back(new IntegerRangeOptionHandler(PREF_SELECT_FILE, 1, INT32_MAX));
   handlers.push_back(new NumberOptionHandler(PREF_SEED_TIME, 0));
   handlers.push_back(new FloatNumberOptionHandler(PREF_SEED_RATIO, 0.0));
   handlers.push_back(new UnitNumberOptionHandler(PREF_MAX_UPLOAD_LIMIT, 0));

+ 27 - 3
src/OptionHandlerImpl.h

@@ -38,6 +38,7 @@
 #include "OptionHandler.h"
 #include "NameMatchOptionHandler.h"
 #include "Util.h"
+#include "DlAbortEx.h"
 #include "FatalException.h"
 #include "prefs.h"
 
@@ -47,7 +48,7 @@ public:
 
   virtual bool canHandle(const string& optName) { return true; }
 
-  virtual void parseArg(Option* option, const string& arg) {}
+  virtual void parse(Option* option, const string& arg) {}
 };
 
 class BooleanOptionHandler : public NameMatchOptionHandler {
@@ -68,6 +69,29 @@ public:
   }
 };
 
+class IntegerRangeOptionHandler : public NameMatchOptionHandler {
+private:
+  int32_t _min;
+  int32_t _max;
+public:
+  IntegerRangeOptionHandler(const string& optName, int32_t min, int32_t max):NameMatchOptionHandler(optName), _min(min), _max(max) {}
+
+  virtual ~IntegerRangeOptionHandler() {}
+
+  virtual void parseArg(Option* option, const string& optarg)
+  {
+    IntSequence seq = Util::parseIntRange(optarg);
+    while(seq.hasNext()) {
+      int32_t v = seq.next();
+      if(v < _min || _max < v) {
+	string msg = _optName+" "+_("must be between %s and %s.");
+	throw new DlAbortEx(msg.c_str(), Util::llitos(_min).c_str(), Util::llitos(_max).c_str());
+      }
+      option->put(_optName, optarg);
+    }
+  }
+};
+
 class NumberOptionHandler : public NameMatchOptionHandler {
 private:
   int64_t _min;
@@ -79,7 +103,7 @@ public:
 
   virtual void parseArg(Option* option, const string& optarg)
   {
-    int64_t num = strtoll(optarg.c_str(), 0, 10);
+    int64_t num = Util::parseLLInt(optarg);
     parseArg(option, num);
   }
 
@@ -217,7 +241,7 @@ public:
   virtual void parseArg(Option* option, const string& optarg)
   {
     pair<string, string> proxy = Util::split(optarg, ":");
-    int32_t port = strtol(proxy.second.c_str(), 0, 10);
+    int32_t port = Util::parseInt(proxy.second);
     if(proxy.first.empty() || proxy.second.empty() ||
        port <= 0 || 65535 < port) {
       throw new FatalException(_("unrecognized proxy format"));

+ 1 - 1
src/OptionParser.cc

@@ -47,7 +47,7 @@ void OptionParser::parse(Option* option, istream& is)
     }
     pair<string, string> nv = Util::split(line, "=");
     OptionHandlerHandle handler = getOptionHandlerByName(nv.first);
-    handler->parseArg(option, nv.second);
+    handler->parse(option, nv.second);
   }
 }
 

+ 3 - 6
src/PeerListenCommand.cc

@@ -58,13 +58,10 @@ PeerListenCommand::~PeerListenCommand()
   --__numInstance;
 }
 
-int32_t PeerListenCommand::bindPort(int32_t portRangeStart,
-				    int32_t portRangeEnd)
+int32_t PeerListenCommand::bindPort(IntSequence& seq)
 {
-  if(portRangeStart > portRangeEnd) {
-    return -1;
-  }
-  for(int32_t port = portRangeStart; port <= portRangeEnd; port++) {
+  while(seq.hasNext()) {
+    int32_t port = seq.next();
     try {
       socket->beginListen(port);
       logger->info(MSG_LISTENING_PORT,

+ 2 - 1
src/PeerListenCommand.h

@@ -37,6 +37,7 @@
 
 #include "Command.h"
 #include "Socket.h"
+#include "IntSequence.h"
 
 class DownloadEngine;
 
@@ -57,7 +58,7 @@ public:
   
   virtual bool execute();
 
-  int32_t bindPort(int32_t portRangeStart, int32_t portRangeEnd);
+  int32_t bindPort(IntSequence& seq);
 
   void setLowestSpeedLimit(int32_t speed)
   {

+ 2 - 1
src/PieceStorage.h

@@ -37,6 +37,7 @@
 
 #include "common.h"
 #include "TimeA2.h"
+#include "IntSequence.h"
 
 class Piece;
 extern typedef SharedHandle<Piece> PieceHandle;
@@ -124,7 +125,7 @@ public:
   
   virtual void setFileFilter(const Strings& filePaths) = 0;
 
-  virtual void setFileFilter(const Integers& fileIndexes) = 0;
+  virtual void setFileFilter(IntSequence seq) = 0;
 
   virtual void clearFileFilter() = 0;
   

+ 4 - 4
src/RequestGroup.cc

@@ -73,7 +73,6 @@
 #ifdef ENABLE_METALINK
 # include "MetalinkPostDownloadHandler.h"
 #endif // ENABLE_METALINK
-#include <cerrno>
 
 int32_t RequestGroup::_gidCounter = 0;
 
@@ -148,16 +147,17 @@ Commands RequestGroup::createInitialCommand(DownloadEngine* e)
 	throw new DownloadFailureException(EX_DUPLICATE_FILE_DOWNLOAD,
 					   getFilePath().c_str());
       }
+      initPieceStorage();
       if(btContext->getFileEntries().size() > 1) {
 	// this is really multi file torrent.
 	// clear http/ftp uris because the current implementation does not
 	// allow integrating multi-file torrent and http/ftp.
 	_logger->debug("Clearing http/ftp URIs because the current implementation does not allow integrating multi-file torrent and http/ftp.");
 	_uris.clear();
-      }
-
-      initPieceStorage();
 
+	_pieceStorage->setFileFilter(Util::parseIntRange(_option->get(PREF_SELECT_FILE)));
+      }
+      
       BtProgressInfoFileHandle progressInfoFile =
 	new DefaultBtProgressInfoFile(_downloadContext,
 				      _pieceStorage,

+ 0 - 1
src/SegmentMan.cc

@@ -48,7 +48,6 @@
 #include "DownloadContext.h"
 #include "Piece.h"
 #include "a2io.h"
-#include <errno.h>
 
 SegmentEntry::SegmentEntry(int32_t cuid, const SegmentHandle& segment):
   cuid(cuid), segment(segment) {}

+ 99 - 0
src/Sequence.h

@@ -0,0 +1,99 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_SEQUENCE_H_
+#define _D_SEQUENCE_H_
+
+#include <deque>
+
+using namespace std;
+
+template<typename T>
+class Sequence
+{
+public:
+  // Generates value in [_first, _last). _last is not included.
+  class Value {
+  private:
+    T _first;
+    T _last;
+  public:
+    Value(const T& first, const T& last):_first(first), _last(last) {}
+
+    T next()
+    {
+      return _first++;
+    }
+    
+    bool hasNext() const
+    {
+      return _first != _last;
+    }
+  };
+
+  typedef deque<Value> Values;
+private:
+  Values _values;
+public:
+  Sequence(const Values& values):
+    _values(values) {}
+
+  T next()
+  {
+    if(_values.empty()) {
+      return T();
+    }
+    T t = _values.front().next();
+    if(!_values.front().hasNext()) {
+      _values.pop_front();
+    }
+    return t;
+  }
+
+  bool hasNext()
+  {
+    return !_values.empty();
+  }
+
+  deque<T> flush()
+  {
+    deque<T> r;
+    while(hasNext()) {
+      r.push_back(next());
+    }
+    return r;
+  }
+};
+
+#endif // _D_SEQUENCE_H_

+ 1 - 1
src/UnknownLengthPieceStorage.h

@@ -154,7 +154,7 @@ public:
   
   virtual void setFileFilter(const Strings& filePaths) {}
 
-  virtual void setFileFilter(const Integers& fileIndexes) {}
+  virtual void setFileFilter(IntSequence seq) {}
 
   virtual void clearFileFilter() {}
   

+ 52 - 0
src/Util.cc

@@ -464,6 +464,58 @@ void Util::unfoldRange(const string& src, Integers& range) {
   range.erase(unique(range.begin(), range.end()), range.end());
 }
 
+int32_t Util::parseInt(const string& s, int32_t base)
+{
+  char* stop;
+  errno = 0;
+  long int v = strtol(s.c_str(), &stop, base);
+  if(*stop != '\0') {
+    throw new DlAbortEx(MSG_ILLEGAL_CHARACTER, stop);
+  } else if((v == LONG_MIN || v == LONG_MAX) && errno == ERANGE || v > INT32_MAX || v < INT32_MIN) {
+    throw new DlAbortEx(MSG_OVERFLOW_UNDERFLOW_DETECTED, s.c_str());
+  }
+  return v;
+}
+
+int64_t Util::parseLLInt(const string& s, int32_t base)
+{
+  char* stop;
+  errno = 0;
+  int64_t v = strtoll(s.c_str(), &stop, base);
+  if(*stop != '\0') {
+    throw new DlAbortEx(MSG_ILLEGAL_CHARACTER, stop);
+  } else if((v == INT64_MIN || v == INT64_MAX) && errno == ERANGE) {
+    throw new DlAbortEx(MSG_OVERFLOW_UNDERFLOW_DETECTED, s.c_str());
+  }
+  return v;
+}
+
+IntSequence Util::parseIntRange(const string& src)
+{
+  IntSequence::Values values;
+  string temp = src;
+  while(temp.size()) {
+    pair<string, string> p = Util::split(temp, ",");
+    temp = p.second;
+    if(p.first.empty()) {
+      continue;
+    }
+    if(p.first.find("-") == string::npos) {
+      int32_t v = Util::parseInt(p.first.c_str());
+      values.push_back(IntSequence::Value(v, v+1));
+    } else {
+      pair<string, string> vp = Util::split(p.first.c_str(), "-");
+      if(vp.first.empty() || vp.second.empty()) {
+	throw new DlAbortEx(MSG_INCOMPLETE_RANGE, p.first.c_str());
+      }
+      int32_t v1 = Util::parseInt(vp.first.c_str());
+      int32_t v2 = Util::parseInt(vp.second.c_str());
+      values.push_back(IntSequence::Value(v1, v2+1));
+    } 
+  }
+  return values;
+}
+
 string Util::getContentDispositionFilename(const string& header) {
   string keyName = "filename=";
   string::size_type attributesp = header.find(keyName);

+ 7 - 0
src/Util.h

@@ -38,6 +38,7 @@
 #include "common.h"
 #include "a2time.h"
 #include "FileEntry.h"
+#include "IntSequence.h"
 #include <utility>
 #include <deque>
 #include <ostream>
@@ -112,6 +113,12 @@ public:
 
   static void unfoldRange(const string& src, Integers& range);
 
+  static int32_t parseInt(const string& s, int32_t base = 10);
+
+  static int64_t parseLLInt(const string& s, int32_t base = 10);
+
+  static IntSequence parseIntRange(const string& src);
+
   // this function temporarily put here
   static string getContentDispositionFilename(const string& header);
 

+ 1 - 1
src/main.cc

@@ -319,7 +319,7 @@ int main(int argc, char* argv[]) {
 	  downloadUri(op, args);
 	}
   } catch(Exception* ex) {
-    cerr << EX_EXCEPTION_CAUGHT << "\n" << ex->getMsg() << endl;
+    cerr << EX_EXCEPTION_CAUGHT << "\n" << *ex << endl;
     delete ex;
     exit(EXIT_FAILURE);
   }

+ 3 - 0
src/message.h

@@ -126,6 +126,9 @@
 #define MSG_DAEMON_FAILED _("daemon failed.")
 #define MSG_VERIFICATION_SUCCESSFUL _("Verification finished successfully. file=%s")
 #define MSG_VERIFICATION_FAILED _("Checksum error detected. file=%s")
+#define MSG_ILLEGAL_CHARACTER _("Illegal character detected: %s")
+#define MSG_INCOMPLETE_RANGE _("Incomplete range specified. %s")
+#define MSG_OVERFLOW_UNDERFLOW_DETECTED _("Overflow/underflow detected: %s")
 
 #define EX_TIME_OUT _("Timeout.")
 #define EX_INVALID_CHUNK_SIZE _("Invalid chunk size.")

+ 4 - 3
src/option_processing.cc

@@ -71,7 +71,7 @@ Option* option_processing(int argc, char* const argv[])
   op->put(PREF_SPLIT, "1");
   op->put(PREF_DAEMON, V_FALSE);
   op->put(PREF_SEGMENT_SIZE, Util::itos((int32_t)(1024*1024)));
-  op->put(PREF_LISTEN_PORT, "-1");
+  op->put(PREF_LISTEN_PORT, "6881-6999");
   op->put(PREF_METALINK_SERVERS, "5");
   op->put(PREF_FOLLOW_TORRENT,
 #ifdef ENABLE_BITTORRENT
@@ -124,6 +124,7 @@ Option* option_processing(int argc, char* const argv[])
   op->put(PREF_ENABLE_HTTP_KEEP_ALIVE, V_FALSE);
   op->put(PREF_ENABLE_HTTP_PIPELINING, V_FALSE);
   op->put(PREF_MAX_HTTP_PIPELINING, "2");
+  op->put(PREF_SEED_RATIO, "1.0");
   while(1) {
     int optIndex = 0;
     int lopt;
@@ -407,14 +408,14 @@ Option* option_processing(int argc, char* const argv[])
       oparser.parse(op, cfstream);
     } catch(Exception* e) {
       cerr << "Parse error in " << cfname << endl;
-      cerr << e->getMsg() << endl;
+      cerr << *e << endl;
       delete e;
       exit(EXIT_FAILURE);
     }
     try {
       oparser.parse(op, cmdstream);
     } catch(Exception* e) {
-      cerr << e->getMsg() << endl;
+      cerr << *e << endl;
       delete e;
       exit(EXIT_FAILURE);
     }

+ 11 - 4
src/version_usage.cc

@@ -242,7 +242,11 @@ void showUsage() {
   cout << _(" --direct-file-mapping=true|false Directly read from and write to each file\n"
 	    "                              mentioned in .torrent file.\n"
 	    "                              Default: true") << endl;
-  cout << _(" --listen-port=PORT           Set TCP port number for BitTorrent downloads.\n"
+  cout << _(" --listen-port=PORT...        Set TCP port number for BitTorrent downloads.\n"
+	    "                              Multiple values can be specified by using ',',\n"
+	    "                              for example: \"6881,6885\".\n"
+	    "                              You can also use '-' to specify a range: \"6881-6999\".\n"
+	    "                              ',' and '-' can be used together.\n"
 	    "                              Default: 6881-6999") << endl;
   cout << _(" --max-upload-limit=SPEED     Set max upload speed in bytes per sec.\n"
 	    "                              0 means unrestricted.\n"
@@ -252,9 +256,12 @@ void showUsage() {
 	    "                              --seed-ratio option.") << endl;
   cout << _(" --seed-ratio=RATIO           Specify share ratio. Seed completed torrents\n"
 	    "                              until share ratio reaches RATIO. 1.0 is\n"
-	    "                              encouraged. If --seed-time option is specified\n"
-	    "                              along with this option, seeding ends when at\n"
-	    "                              least one of the conditions is satisfied.") << endl;
+	    "                              encouraged. Specify 0.0 if you intend to do\n"
+	    "                              seeding regardless of share ratio.\n"
+	    "                              If --seed-time option is specified along with\n"
+	    "                              this option, seeding ends when at least one of\n"
+	    "                              the conditions is satisfied.\n"
+	    "                              Default: 1.0") << endl;
   cout << _(" --peer-id-prefix=PEERI_ID_PREFIX Specify the prefix of peer ID. The peer ID in\n"
 	    "                              in BitTorrent is 20 byte length. If more than 20\n"
 	    "                              bytes are specified, only first 20\n"

+ 1 - 0
test/Makefile.am

@@ -1,6 +1,7 @@
 TESTS = aria2c
 check_PROGRAMS = $(TESTS)
 aria2c_SOURCES = AllTest.cc\
+	SequenceTest.cc\
 	a2functionalTest.cc\
 	FileEntryTest.cc\
 	PieceTest.cc\

+ 12 - 9
test/Makefile.in

@@ -112,9 +112,10 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 am__EXEEXT_1 = aria2c$(EXEEXT)
-am__aria2c_SOURCES_DIST = AllTest.cc a2functionalTest.cc \
-	FileEntryTest.cc PieceTest.cc SegmentTest.cc \
-	GrowSegmentTest.cc SingleFileAllocationIteratorTest.cc \
+am__aria2c_SOURCES_DIST = AllTest.cc SequenceTest.cc \
+	a2functionalTest.cc FileEntryTest.cc PieceTest.cc \
+	SegmentTest.cc GrowSegmentTest.cc \
+	SingleFileAllocationIteratorTest.cc \
 	DefaultBtProgressInfoFileTest.cc \
 	SingleFileDownloadContextTest.cc RequestGroupTest.cc \
 	PStringBuildVisitorTest.cc ParameterizedStringParserTest.cc \
@@ -204,9 +205,10 @@ am__aria2c_SOURCES_DIST = AllTest.cc a2functionalTest.cc \
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroupTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkPostDownloadHandlerTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkHelperTest.$(OBJEXT)
-am_aria2c_OBJECTS = AllTest.$(OBJEXT) a2functionalTest.$(OBJEXT) \
-	FileEntryTest.$(OBJEXT) PieceTest.$(OBJEXT) \
-	SegmentTest.$(OBJEXT) GrowSegmentTest.$(OBJEXT) \
+am_aria2c_OBJECTS = AllTest.$(OBJEXT) SequenceTest.$(OBJEXT) \
+	a2functionalTest.$(OBJEXT) FileEntryTest.$(OBJEXT) \
+	PieceTest.$(OBJEXT) SegmentTest.$(OBJEXT) \
+	GrowSegmentTest.$(OBJEXT) \
 	SingleFileAllocationIteratorTest.$(OBJEXT) \
 	DefaultBtProgressInfoFileTest.$(OBJEXT) \
 	SingleFileDownloadContextTest.$(OBJEXT) \
@@ -416,9 +418,9 @@ target_cpu = @target_cpu@
 target_os = @target_os@
 target_vendor = @target_vendor@
 TESTS = aria2c
-aria2c_SOURCES = AllTest.cc a2functionalTest.cc FileEntryTest.cc \
-	PieceTest.cc SegmentTest.cc GrowSegmentTest.cc \
-	SingleFileAllocationIteratorTest.cc \
+aria2c_SOURCES = AllTest.cc SequenceTest.cc a2functionalTest.cc \
+	FileEntryTest.cc PieceTest.cc SegmentTest.cc \
+	GrowSegmentTest.cc SingleFileAllocationIteratorTest.cc \
 	DefaultBtProgressInfoFileTest.cc \
 	SingleFileDownloadContextTest.cc RequestGroupTest.cc \
 	PStringBuildVisitorTest.cc ParameterizedStringParserTest.cc \
@@ -577,6 +579,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SegmentManTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SegmentTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SequenceTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ShaVisitorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ShareRatioSeedCriteriaTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SharedHandleTest.Po@am__quote@

+ 1 - 1
test/MockPieceStorage.h

@@ -109,7 +109,7 @@ public:
   
   virtual void setFileFilter(const Strings& filePaths) {}
 
-  virtual void setFileFilter(const Integers& fileIndexes) {}
+  virtual void setFileFilter(IntSequence seq) {}
 
   virtual void clearFileFilter() {}
 

+ 1 - 6
test/MultiDiskAdaptorTest.cc

@@ -11,18 +11,13 @@ class MultiDiskAdaptorTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testReadData);
   CPPUNIT_TEST_SUITE_END();
 private:
-  Option* option;
   MultiDiskAdaptorHandle adaptor;
 public:
-  MultiDiskAdaptorTest():option(0), adaptor(0) {}
+  MultiDiskAdaptorTest():adaptor(0) {}
 
   void setUp() {
-    delete option;
-    option = new Option();
-
     adaptor = new MultiDiskAdaptor();
     adaptor->setPieceLength(2);
-    adaptor->setOption(new Option());
     adaptor->setStoreDir(".");
     adaptor->setTopDir(".");
   }

+ 44 - 0
test/MultiFileAllocationIteratorTest.cc

@@ -7,6 +7,7 @@ class MultiFileAllocationIteratorTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(MultiFileAllocationIteratorTest);
   CPPUNIT_TEST(testAllocate);
+  CPPUNIT_TEST(testMakeFileEntries);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -14,11 +15,54 @@ public:
   void setUp() {}
 
   void testAllocate();
+  void testMakeFileEntries();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION( MultiFileAllocationIteratorTest );
 
+void MultiFileAllocationIteratorTest::testMakeFileEntries()
+{
+  FileEntryHandle fs[] = {
+    new FileEntry("file1", 1536, 0),
+    new FileEntry("file2", 2048, 1536),
+    new FileEntry("file3", 1024, 3584),
+    new FileEntry("file4", 1024, 4608),
+    new FileEntry("file5", 1024, 5632),
+    new FileEntry("file6", 1024, 6656),
+    new FileEntry("file7",  256, 7680),
+    new FileEntry("file8",  768, 7936),
+    new FileEntry("file9",  256, 8704),
+    new FileEntry("fileA",  256, 8960),
+  };
+  fs[1]->setRequested(false);
+  fs[3]->setRequested(false);
+  fs[4]->setRequested(false);
+  fs[5]->setRequested(false);
+  fs[6]->setRequested(false);
+  fs[8]->setRequested(false);
+  fs[9]->setRequested(false);
+  
+  MultiDiskAdaptorHandle diskAdaptor = new MultiDiskAdaptor();
+  diskAdaptor->setFileEntries(FileEntries(&fs[0], &fs[10]));
+  diskAdaptor->setPieceLength(1024);
+
+  MultiFileAllocationIteratorHandle itr = diskAdaptor->fileAllocationIterator();
+
+  FileEntries entries = itr->getFileEntries();
+
+  sort(entries.begin(), entries.end());
+
+  CPPUNIT_ASSERT_EQUAL((size_t)6, entries.size());
+
+  CPPUNIT_ASSERT_EQUAL(string("file1"), entries[0]->getPath());
+  CPPUNIT_ASSERT_EQUAL(string("file2"), entries[1]->getPath());
+  CPPUNIT_ASSERT_EQUAL(string("file3"), entries[2]->getPath());
+  CPPUNIT_ASSERT_EQUAL(string("file6"), entries[3]->getPath());
+  CPPUNIT_ASSERT_EQUAL(string("file7"), entries[4]->getPath());
+  CPPUNIT_ASSERT_EQUAL(string("file8"), entries[5]->getPath());
+}
+
 void MultiFileAllocationIteratorTest::testAllocate()
 {
   string dir = "/tmp";

+ 46 - 46
test/OptionHandlerTest.cc

@@ -52,7 +52,7 @@ void OptionHandlerTest::testNullOptionHandler()
 {
   NullOptionHandler handler;
   CPPUNIT_ASSERT(handler.canHandle("foo"));
-  handler.parseArg(0, "bar");
+  handler.parse(0, "bar");
 }
 
 void OptionHandlerTest::testBooleanOptionHandler()
@@ -61,12 +61,12 @@ void OptionHandlerTest::testBooleanOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, V_TRUE);
+  handler.parse(&option, V_TRUE);
   CPPUNIT_ASSERT_EQUAL(string(V_TRUE), option.get("foo"));
-  handler.parseArg(&option, V_FALSE);
+  handler.parse(&option, V_FALSE);
   CPPUNIT_ASSERT_EQUAL(string(V_FALSE), option.get("foo"));
   try {
-    handler.parseArg(&option, "hello");
+    handler.parse(&option, "hello");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -80,7 +80,7 @@ void OptionHandlerTest::testNumberOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "0");
+  handler.parse(&option, "0");
   CPPUNIT_ASSERT_EQUAL(string("0"), option.get("foo"));
 }
 
@@ -88,10 +88,10 @@ void OptionHandlerTest::testNumberOptionHandler_min()
 {
   NumberOptionHandler handler("foo", 1);
   Option option;
-  handler.parseArg(&option, "1");
+  handler.parse(&option, "1");
   CPPUNIT_ASSERT_EQUAL(string("1"), option.get("foo"));
   try {
-    handler.parseArg(&option, "0");
+    handler.parse(&option, "0");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -104,10 +104,10 @@ void OptionHandlerTest::testNumberOptionHandler_max()
 {
   NumberOptionHandler handler("foo", -1, 100);
   Option option;
-  handler.parseArg(&option, "100");
+  handler.parse(&option, "100");
   CPPUNIT_ASSERT_EQUAL(string("100"), option.get("foo"));
   try {
-    handler.parseArg(&option, "101");
+    handler.parse(&option, "101");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -119,19 +119,19 @@ void OptionHandlerTest::testNumberOptionHandler_min_max()
 {
   NumberOptionHandler handler("foo", 1, 100);
   Option option;
-  handler.parseArg(&option, "1");
+  handler.parse(&option, "1");
   CPPUNIT_ASSERT_EQUAL(string("1"), option.get("foo"));
-  handler.parseArg(&option, "100");
+  handler.parse(&option, "100");
   CPPUNIT_ASSERT_EQUAL(string("100"), option.get("foo"));
   try {
-    handler.parseArg(&option, "0");
+    handler.parse(&option, "0");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, "101");
+    handler.parse(&option, "101");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -145,17 +145,17 @@ void OptionHandlerTest::testUnitNumberOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "4294967296");
+  handler.parse(&option, "4294967296");
   CPPUNIT_ASSERT_EQUAL(string("4294967296"), option.get("foo"));
-  handler.parseArg(&option, "4096M");
+  handler.parse(&option, "4096M");
   CPPUNIT_ASSERT_EQUAL(string("4294967296"), option.get("foo"));
-  handler.parseArg(&option, "4096K");
+  handler.parse(&option, "4096K");
   CPPUNIT_ASSERT_EQUAL(string("4194304"), option.get("foo"));
-  handler.parseArg(&option, "K");
+  handler.parse(&option, "K");
   CPPUNIT_ASSERT_EQUAL(string("0"), option.get("foo"));
-  handler.parseArg(&option, "M");
+  handler.parse(&option, "M");
   CPPUNIT_ASSERT_EQUAL(string("0"), option.get("foo"));
-  handler.parseArg(&option, "");
+  handler.parse(&option, "");
   CPPUNIT_ASSERT_EQUAL(string("0"), option.get("foo"));
 }
 
@@ -165,10 +165,10 @@ void OptionHandlerTest::testParameterOptionHandler_1argInit()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "value1");
+  handler.parse(&option, "value1");
   CPPUNIT_ASSERT_EQUAL(string("value1"), option.get("foo"));
   try {
-    handler.parseArg(&option, "value3");
+    handler.parse(&option, "value3");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -182,12 +182,12 @@ void OptionHandlerTest::testParameterOptionHandler_2argsInit()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "value1");
+  handler.parse(&option, "value1");
   CPPUNIT_ASSERT_EQUAL(string("value1"), option.get("foo"));
-  handler.parseArg(&option, "value2");
+  handler.parse(&option, "value2");
   CPPUNIT_ASSERT_EQUAL(string("value2"), option.get("foo"));
   try {
-    handler.parseArg(&option, "value3");
+    handler.parse(&option, "value3");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -205,12 +205,12 @@ void OptionHandlerTest::testParameterOptionHandler_listInit()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "value1");
+  handler.parse(&option, "value1");
   CPPUNIT_ASSERT_EQUAL(string("value1"), option.get("foo"));
-  handler.parseArg(&option, "value2");
+  handler.parse(&option, "value2");
   CPPUNIT_ASSERT_EQUAL(string("value2"), option.get("foo"));
   try {
-    handler.parseArg(&option, "value3");
+    handler.parse(&option, "value3");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -224,9 +224,9 @@ void OptionHandlerTest::testDefaultOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "bar");
+  handler.parse(&option, "bar");
   CPPUNIT_ASSERT_EQUAL(string("bar"), option.get("foo"));
-  handler.parseArg(&option, "");
+  handler.parse(&option, "");
   CPPUNIT_ASSERT_EQUAL(string(""), option.get("foo"));
 }
 
@@ -236,7 +236,7 @@ void OptionHandlerTest::testFloatNumberOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "1.0");
+  handler.parse(&option, "1.0");
   CPPUNIT_ASSERT_EQUAL(string("1.0"), option.get("foo"));
 }
 
@@ -244,10 +244,10 @@ void OptionHandlerTest::testFloatNumberOptionHandler_min()
 {
   FloatNumberOptionHandler handler("foo", 0.0);
   Option option;
-  handler.parseArg(&option, "0.0");
+  handler.parse(&option, "0.0");
   CPPUNIT_ASSERT_EQUAL(string("0.0"), option.get("foo"));
   try {
-    handler.parseArg(&option, "-0.1");
+    handler.parse(&option, "-0.1");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -259,10 +259,10 @@ void OptionHandlerTest::testFloatNumberOptionHandler_max()
 {
   FloatNumberOptionHandler handler("foo", -1, 10.0);
   Option option;
-  handler.parseArg(&option, "10.0");
+  handler.parse(&option, "10.0");
   CPPUNIT_ASSERT_EQUAL(string("10.0"), option.get("foo"));
   try {
-    handler.parseArg(&option, "10.1");
+    handler.parse(&option, "10.1");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -274,19 +274,19 @@ void OptionHandlerTest::testFloatNumberOptionHandler_min_max()
 {
   FloatNumberOptionHandler handler("foo", 0.0, 10.0);
   Option option;
-  handler.parseArg(&option, "0.0");
+  handler.parse(&option, "0.0");
   CPPUNIT_ASSERT_EQUAL(string("0.0"), option.get("foo"));
-  handler.parseArg(&option, "10.0");
+  handler.parse(&option, "10.0");
   CPPUNIT_ASSERT_EQUAL(string("10.0"), option.get("foo"));
   try {
-    handler.parseArg(&option, "-0.1");
+    handler.parse(&option, "-0.1");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, "10.1");
+    handler.parse(&option, "10.1");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
@@ -300,12 +300,12 @@ void OptionHandlerTest::testLogOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "/tmp/log.txt");
+  handler.parse(&option, "/tmp/log.txt");
   CPPUNIT_ASSERT_EQUAL(string("/tmp/log.txt"), option.get(PREF_LOG));
   CPPUNIT_ASSERT_EQUAL(string(""), option.get(PREF_STDOUT_LOG));
 
   option.clear();
-  handler.parseArg(&option, "-");
+  handler.parse(&option, "-");
   CPPUNIT_ASSERT_EQUAL(string(""), option.get(PREF_LOG));
   CPPUNIT_ASSERT_EQUAL(string(V_TRUE), option.get(PREF_STDOUT_LOG));
 }
@@ -316,42 +316,42 @@ void OptionHandlerTest::testHttpProxyOptionHandler()
   CPPUNIT_ASSERT(handler.canHandle("foo"));
   CPPUNIT_ASSERT(!handler.canHandle("foobar"));
   Option option;
-  handler.parseArg(&option, "bar:80");
+  handler.parse(&option, "bar:80");
   CPPUNIT_ASSERT_EQUAL(string("bar:80"), option.get(PREF_HTTP_PROXY));
   CPPUNIT_ASSERT_EQUAL(string("bar"), option.get(PREF_HTTP_PROXY_HOST));
   CPPUNIT_ASSERT_EQUAL(string("80"), option.get(PREF_HTTP_PROXY_PORT));
   CPPUNIT_ASSERT_EQUAL(string(V_TRUE), option.get(PREF_HTTP_PROXY_ENABLED));
 
   try {
-    handler.parseArg(&option, "bar");
+    handler.parse(&option, "bar");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, "bar:");
+    handler.parse(&option, "bar:");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, ":");
+    handler.parse(&option, ":");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, ":80");
+    handler.parse(&option, ":80");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;
     delete e;
   }
   try {
-    handler.parseArg(&option, "foo:bar");
+    handler.parse(&option, "foo:bar");
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(Exception* e) {
     cerr << e->getMsg() << endl;

+ 79 - 0
test/SequenceTest.cc

@@ -0,0 +1,79 @@
+#include "Sequence.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace std;
+
+class SequenceTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(SequenceTest);
+  CPPUNIT_TEST(testParseAndNext);
+  CPPUNIT_TEST(testParseAndNext2);
+  CPPUNIT_TEST(testFlush);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testParseAndNext();
+  void testParseAndNext2();
+  void testFlush();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SequenceTest);
+
+typedef Sequence<int32_t> IntSequence;
+
+void SequenceTest::testParseAndNext()
+{
+  IntSequence::Value params[] = {
+    IntSequence::Value(1, 2),
+    IntSequence::Value(3, 9),
+    IntSequence::Value(10, 11),
+  };
+  IntSequence seq(IntSequence::Values(&params[0], &params[3]));
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)1, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)3, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)4, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)5, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)6, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)7, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)8, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)10, seq.next());
+  CPPUNIT_ASSERT(!seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)0, seq.next());
+ 
+}
+
+void SequenceTest::testParseAndNext2()
+{
+  IntSequence::Value params[] = {
+    IntSequence::Value(1, 2),
+  };
+  IntSequence seq(IntSequence::Values(&params[0], &params[1]));
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)1, seq.next());
+  CPPUNIT_ASSERT(!seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)0, seq.next());
+ 
+}
+
+void SequenceTest::testFlush()
+{
+  IntSequence::Value params[] = {
+    IntSequence::Value(1, 2),
+    IntSequence::Value(3, 9),
+    IntSequence::Value(10, 11),
+  };
+  IntSequence seq(IntSequence::Values(&params[0], &params[3]));
+  deque<int32_t> r = seq.flush();
+
+  int32_t answers[] = { 1, 3, 4, 5, 6, 7, 8, 10 };
+
+  CPPUNIT_ASSERT(equal(r.begin(), r.end(), &answers[0])); 
+}

+ 128 - 0
test/UtilTest.cc

@@ -32,6 +32,10 @@ class UtilTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testAlphaToNum);
   CPPUNIT_TEST(testMkdirs);
   CPPUNIT_TEST(testConvertBitfield);
+  CPPUNIT_TEST(testParseIntRange);
+  CPPUNIT_TEST(testParseIntRange_invalidRange);
+  CPPUNIT_TEST(testParseInt);
+  CPPUNIT_TEST(testParseLLInt);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -61,6 +65,10 @@ public:
   void testAlphaToNum();
   void testMkdirs();
   void testConvertBitfield();
+  void testParseIntRange();
+  void testParseIntRange_invalidRange();
+  void testParseInt();
+  void testParseLLInt();
 };
 
 
@@ -407,3 +415,123 @@ void UtilTest::testConvertBitfield()
 		       Util::toHex(destBitfield.getBitfield(),
 				   destBitfield.getBitfieldLength()));
 }
+
+void UtilTest::testParseIntRange()
+{
+  IntSequence seq = Util::parseIntRange("1,3-8,10");
+
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)1, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)3, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)4, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)5, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)6, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)7, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)8, seq.next());
+  CPPUNIT_ASSERT(seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)10, seq.next());
+  CPPUNIT_ASSERT(!seq.hasNext());
+  CPPUNIT_ASSERT_EQUAL((int32_t)0, seq.next()); 
+}
+
+void UtilTest::testParseIntRange_invalidRange()
+{
+  try {
+    IntSequence seq = Util::parseIntRange("-1");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    IntSequence seq = Util::parseIntRange("2147483648");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    IntSequence seq = Util::parseIntRange("2147483647-2147483648");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    IntSequence seq = Util::parseIntRange("1-2x");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    IntSequence seq = Util::parseIntRange("3x-4");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+}
+
+void UtilTest::testParseInt()
+{
+  CPPUNIT_ASSERT_EQUAL((int32_t)-1, Util::parseInt("-1"));
+  CPPUNIT_ASSERT_EQUAL((int32_t)2147483647, Util::parseInt("2147483647"));
+  try {
+    Util::parseInt("2147483648");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    Util::parseInt("-2147483649");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    Util::parseInt("12x");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+
+}
+
+void UtilTest::testParseLLInt()
+{
+  CPPUNIT_ASSERT_EQUAL((int64_t)-1, Util::parseLLInt("-1"));
+  CPPUNIT_ASSERT_EQUAL((int64_t)9223372036854775807LL,
+		       Util::parseLLInt("9223372036854775807"));
+  try {
+    Util::parseLLInt("9223372036854775808");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    Util::parseLLInt("-9223372036854775809");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+  try {
+    Util::parseLLInt("12x");
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e;
+    delete e;
+  }
+
+}