New help messages. Added suggestion for unknown/ambiguous options.

The help messages shown when argument error were redesigned and less
verbose now.  When unknown or ambiguous option is given, show
suggestions like "Did you mean...". Some constant values related to
levenstein distance are borrowed from git help.c.
pull/4/head
Tatsuhiro Tsujikawa 2011-12-03 16:30:00 +09:00
parent f77da0d112
commit 6bf696ca11
12 changed files with 392 additions and 100 deletions

View File

@ -5,6 +5,9 @@ src/OptionHandler.cc
src/OptionHandlerImpl.h
src/usage_text.h
src/version_usage.cc
src/option_processing.cc
src/OptionHandlerException.cc
src/UnknownOptionException.cc
src/BtSetup.cc
src/AbstractCommand.cc
src/AdaptiveURISelector.cc

View File

@ -224,7 +224,8 @@ SRCS = Socket.h\
a2iterator.h\
paramed_string.cc paramed_string.h\
rpc_helper.cc rpc_helper.h\
WatchProcessCommand.cc WatchProcessCommand.h
WatchProcessCommand.cc WatchProcessCommand.h\
UnknownOptionException.cc UnknownOptionException.h
if MINGW_BUILD
SRCS += WinConsoleFile.cc WinConsoleFile.h

View File

@ -64,4 +64,17 @@ std::ostream& operator<<(std::ostream& o, const OptionHandler& optionHandler)
return o;
}
void write(const Console& out, const OptionHandler& optionHandler)
{
out->printf("%s\n\n", optionHandler.getDescription());
std::string possibleValues = optionHandler.createPossibleValuesString();
if(!possibleValues.empty()) {
out->printf("%s%s\n", POSSIBLE_MSG, possibleValues.c_str());
}
if(!optionHandler.getDefaultValue().empty()) {
out->printf("%s%s\n", DEFAULT_MSG, optionHandler.getDefaultValue().c_str());
}
out->printf("%s%s\n", TAGS_MSG, optionHandler.toTagString().c_str());
}
} // namespace aria2

View File

@ -43,6 +43,7 @@
#include <functional>
#include "SharedHandle.h"
#include "console.h"
namespace aria2 {
@ -114,6 +115,8 @@ public:
std::ostream& operator<<(std::ostream& o, const OptionHandler& optionHandler);
void write(const Console& out, const OptionHandler& optionHandler);
} // namespace aria2
#endif // D_OPTION_HANDLER_H

View File

@ -38,15 +38,16 @@
namespace aria2 {
const std::string OptionHandlerException::MESSAGE
("We encountered a problem while processing the option '--%s'.");
namespace {
const char* MESSAGE =
_("We encountered a problem while processing the option '--%s'.");
} // namespace
OptionHandlerException::OptionHandlerException
(const char* file, int line,
const Pref* pref)
: RecoverableException
(file, line, fmt(MESSAGE.c_str(), pref->k),
error_code::OPTION_ERROR),
(file, line, fmt(MESSAGE, pref->k), error_code::OPTION_ERROR),
pref_(pref)
{}
@ -55,8 +56,7 @@ OptionHandlerException::OptionHandlerException
const Pref* pref,
const Exception& cause)
: RecoverableException
(file, line, fmt(MESSAGE.c_str(), pref->k),
error_code::OPTION_ERROR,
(file, line, fmt(MESSAGE, pref->k), error_code::OPTION_ERROR,
cause),
pref_(pref)
{}

View File

@ -43,8 +43,6 @@ class Pref;
class OptionHandlerException:public RecoverableException {
private:
const Pref* pref_;
static const std::string MESSAGE;
protected:
virtual SharedHandle<Exception> copy() const;
public:

View File

@ -51,6 +51,7 @@
#include "DlAbortEx.h"
#include "error_code.h"
#include "prefs.h"
#include "UnknownOptionException.h"
namespace aria2 {
@ -156,13 +157,52 @@ void OptionParser::parseArg
SharedHandle<OptionHandler> op;
if(c == 0) {
op = findById(lopt);
} else {
} else if(c != '?') {
op = findByShortName(c);
} else {
assert(c == '?');
if(optind == 1) {
throw DL_ABORT_EX2("Failed to parse command-line options.",
error_code::OPTION_ERROR);
}
int optlen = strlen(argv[optind-1]);
const char* optstr = argv[optind-1];
for(; *optstr == '-'; ++optstr);
int optstrlen = strlen(optstr);
if(optstrlen+1 >= optlen) {
// If this is short option form (1 '-' prefix), just throw
// error here.
throw DL_ABORT_EX2("Failed to parse command-line options.",
error_code::OPTION_ERROR);
}
// There are 3 situations: 1) completely unknown option 2)
// getopt_long() complained because too few arguments. 3)
// option is ambiguous.
int ambiguous = 0;
for(int i = 1, len = option::countOption(); i < len; ++i) {
const Pref* pref = option::i2p(i);
const SharedHandle<OptionHandler>& h = find(pref);
if(h && !h->isHidden()) {
if(strcmp(pref->k, optstr) == 0) {
// Exact match, this means getopt_long detected error
// while handling this option.
throw DL_ABORT_EX2("Failed to parse command-line options.",
error_code::OPTION_ERROR);
} else if(util::startsWith(pref->k, pref->k+strlen(pref->k),
optstr, optstr+optstrlen)) {
++ambiguous;
}
}
}
if(ambiguous == 1) {
// This is successfully abbreviated option. So it must be case
// 2).
throw DL_ABORT_EX2("Failed to parse command-line options.",
error_code::OPTION_ERROR);
}
throw UNKNOWN_OPTION_EXCEPTION(argv[optind-1]);
}
if(!op) {
throw DL_ABORT_EX2("Failed to parse command-line options.",
error_code::OPTION_ERROR);
}
assert(op);
out << op->getName() << "=";
if(optarg) {
out << optarg;
@ -189,8 +229,9 @@ void OptionParser::parse(Option& option, std::istream& is) const
if(nv.first.first == nv.first.second) {
continue;
}
const SharedHandle<OptionHandler>& handler =
find(option::k2p(std::string(nv.first.first, nv.first.second)));
const Pref* pref =
option::k2p(std::string(nv.first.first, nv.first.second));
const SharedHandle<OptionHandler>& handler = find(pref);
if(handler) {
handler->parse(option, std::string(nv.second.first, nv.second.second));
}

View File

@ -0,0 +1,68 @@
/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2011 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 "UnknownOptionException.h"
#include "fmt.h"
namespace aria2 {
namespace {
const char* MESSAGE = _("Unknown option '%s'");
} // namespace
UnknownOptionException::UnknownOptionException
(const char* file, int line, const std::string& unknownOption)
: RecoverableException
(file, line, fmt(MESSAGE, unknownOption.c_str()), error_code::OPTION_ERROR),
unknownOption_(unknownOption)
{}
UnknownOptionException::UnknownOptionException
(const char* file, int line, const std::string& unknownOption,
const Exception& cause)
: RecoverableException
(file, line, fmt(MESSAGE, unknownOption.c_str()), error_code::OPTION_ERROR,
cause),
unknownOption_(unknownOption)
{}
UnknownOptionException::~UnknownOptionException() throw() {}
SharedHandle<Exception> UnknownOptionException::copy() const
{
SharedHandle<Exception> e(new UnknownOptionException(*this));
return e;
}
} // namespace aria2

View File

@ -0,0 +1,69 @@
/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2011 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_UNKNOWN_OPTION_EXCEPTION_H
#define D_UNKNOWN_OPTION_EXCEPTION_H
#include "RecoverableException.h"
namespace aria2 {
class UnknownOptionException:public RecoverableException {
private:
std::string unknownOption_;
protected:
virtual SharedHandle<Exception> copy() const;
public:
UnknownOptionException(const char* file, int line,
const std::string& unknownOption);
UnknownOptionException(const char* file, int line,
const std::string& unknownOption,
const Exception& cause);
virtual ~UnknownOptionException() throw();
const std::string& getUnknownOption() const
{
return unknownOption_;
}
};
#define UNKNOWN_OPTION_EXCEPTION(arg) \
UnknownOptionException(__FILE__, __LINE__, arg)
#define UNKNOWN_OPTION_EXCEPTION2(arg1, arg2) \
UnknownOptionException(__FILE__, __LINE__, arg1, arg2)
} // namespace aria2
#endif // D_UNKNOWN_OPTION_EXCEPTION_EX_H

View File

@ -45,19 +45,16 @@
namespace aria2 {
#ifdef __MINGW32__
typedef SharedHandle<WinConsoleFile> Console;
#else // !__MINGW32__
typedef SharedHandle<BufferedFile> Console;
#endif // !__MINGW32__
namespace global {
#ifdef __MINGW32__
const SharedHandle<WinConsoleFile>& cout();
#else // !__MINGW32__
const SharedHandle<BufferedFile>& cout();
#endif // !__MINGW32__
#ifdef __MINGW32__
const SharedHandle<WinConsoleFile>& cerr();
#else // !__MINGW32__
const SharedHandle<BufferedFile>& cerr();
#endif // !__MINGW32__
const Console& cout();
const Console& cerr();
} // namespace global

View File

@ -37,7 +37,6 @@
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include "Option.h"
#include "prefs.h"
@ -52,6 +51,7 @@
#include "File.h"
#include "fmt.h"
#include "OptionHandlerException.h"
#include "UnknownOptionException.h"
#include "error_code.h"
#include "SimpleRandomizer.h"
#include "bittorrent_helper.h"
@ -66,7 +66,9 @@ namespace aria2 {
extern void showVersion();
extern void showUsage
(const std::string& keyword, const SharedHandle<OptionParser>& oparser);
(const std::string& keyword,
const SharedHandle<OptionParser>& oparser,
const Console& out);
namespace {
void overrideWithEnv
@ -81,14 +83,98 @@ void overrideWithEnv
optionParser->find(pref)->parse(op, value);
} catch(Exception& e) {
global::cerr()->printf
("Caught Error while parsing environment variable '%s'\n%s\n",
envName.c_str(),
e.stackTrace().c_str());
(_("Caught Error while parsing environment variable '%s'"),
envName.c_str());
global::cerr()->printf("\n%s\n", e.stackTrace().c_str());
}
}
}
} // namespace
namespace {
int levenstein
(const char* a,
const char* b,
int swapcost,
int subcost,
int addcost,
int delcost)
{
int alen = strlen(a);
int blen = strlen(b);
std::vector<std::vector<int> > dp(3, std::vector<int>(blen+1));
for(int i = 0; i <= blen; ++i) {
dp[1][i] = i;
}
for(int i = 1; i <= alen; ++i) {
dp[0][0] = i;
for(int j = 1; j <= blen; ++j) {
if(a[i-1] == b[j-1]) {
dp[0][j] = dp[1][j-1];
} else {
dp[0][j] = dp[1][j-1]+subcost;
}
if(i >= 2 && j >= 2 && a[i-1] != b[j-1] &&
a[i-2] == b[j-1] && a[i-1] == b[j-2]) {
dp[0][j] = std::min(dp[0][j], dp[2][j-2]+swapcost);
}
dp[0][j] = std::min(dp[0][j],
std::min(dp[1][j]+delcost, dp[0][j-1]+addcost));
}
std::rotate(dp.begin(), dp.begin()+2, dp.end());
}
return dp[1][blen];
}
} // namespace
namespace {
void showCandidates
(const std::string& unknownOption, const SharedHandle<OptionParser>& parser)
{
const char* optstr = unknownOption.c_str();
for(; *optstr == '-'; ++optstr);
if(*optstr == '\0') {
return;
}
int optstrlen = strlen(optstr);
std::vector<std::pair<int, const Pref*> > cands;
for(int i = 1, len = option::countOption(); i < len; ++i) {
const Pref* pref = option::i2p(i);
const SharedHandle<OptionHandler>& h = parser->find(pref);
if(!h || h->isHidden()) {
continue;
}
// Use cost 0 for prefix match
if(util::startsWith(pref->k, pref->k+strlen(pref->k),
optstr, optstr+optstrlen)) {
cands.push_back(std::make_pair(0, pref));
continue;
}
// cost values are borrowed from git, help.c.
int sim = levenstein(optstr, pref->k, 0, 2, 1, 4);
cands.push_back(std::make_pair(sim, pref));
}
if(cands.empty()) {
return;
}
std::sort(cands.begin(), cands.end());
int threshold = cands[0].first;
// threshold value 7 is borrowed from git, help.c.
if(threshold >= 7) {
return;
}
global::cerr()->printf("\n");
global::cerr()->printf(_("Did you mean:"));
global::cerr()->printf("\n");
for(std::vector<std::pair<int, const Pref*> >::iterator i = cands.begin(),
eoi = cands.end(); i != eoi && (*i).first <= threshold; ++i) {
global::cerr()->printf("\t--%s\n", (*i).second->k);
}
}
} // namespace
void option_processing(Option& op, std::vector<std::string>& uris,
int argc, char* argv[])
{
@ -125,7 +211,7 @@ void option_processing(Option& op, std::vector<std::string>& uris,
keyword.erase(keyword.begin()+eqpos, keyword.end());
}
}
showUsage(keyword, oparser);
showUsage(keyword, oparser, global::cout());
exit(error_code::FINISHED);
}
}
@ -148,24 +234,24 @@ void option_processing(Option& op, std::vector<std::string>& uris,
try {
oparser->parse(op, ss);
} catch(OptionHandlerException& e) {
global::cerr()->printf("Parse error in %s\n%s\n",
cfname.c_str(),
e.stackTrace().c_str());
global::cerr()->printf(_("Parse error in %s"), cfname.c_str());
global::cerr()->printf("\n%s", e.stackTrace().c_str());
const SharedHandle<OptionHandler>& h = oparser->find(e.getPref());
if(h) {
global::cerr()->printf("Usage:\n%s\n", h->getDescription());
global::cerr()->printf(_("Usage:"));
global::cerr()->printf("\n%s\n", h->getDescription());
}
exit(e.getErrorCode());
} catch(Exception& e) {
global::cerr()->printf("Parse error in %s\n%s\n",
cfname.c_str(),
e.stackTrace().c_str());
global::cerr()->printf(_("Parse error in %s"), cfname.c_str());
global::cerr()->printf("\n%s", e.stackTrace().c_str());
exit(e.getErrorCode());
}
} else if(!ucfname.empty()) {
global::cerr()->printf("Configuration file %s is not found.\n",
global::cerr()->printf(_("Configuration file %s is not found."),
cfname.c_str());
showUsage(TAG_HELP, oparser);
global::cerr()->printf("\n");
showUsage(TAG_HELP, oparser, global::cerr());
exit(error_code::UNKNOWN_ERROR);
}
}
@ -190,17 +276,21 @@ void option_processing(Option& op, std::vector<std::string>& uris,
}
#endif // __MINGW32__
} catch(OptionHandlerException& e) {
global::cerr()->printf("%s\n", e.stackTrace().c_str());
global::cerr()->printf("%s", e.stackTrace().c_str());
const SharedHandle<OptionHandler>& h = oparser->find(e.getPref());
if(h) {
std::ostringstream ss;
ss << *h;
global::cerr()->printf("Usage:\n%s\n", ss.str().c_str());
global::cerr()->printf(_("Usage:"));
global::cerr()->printf("\n");
write(global::cerr(), *h);
}
exit(e.getErrorCode());
} catch(UnknownOptionException& e) {
showUsage("", oparser, global::cerr());
showCandidates(e.getUnknownOption(), oparser);
exit(e.getErrorCode());
} catch(Exception& e) {
global::cerr()->printf("%s\n", e.stackTrace().c_str());
showUsage(TAG_HELP, oparser);
global::cerr()->printf("%s", e.stackTrace().c_str());
showUsage("", oparser, global::cerr());
exit(e.getErrorCode());
}
if(!op.getAsBool(PREF_ENABLE_RPC) &&
@ -212,8 +302,9 @@ void option_processing(Option& op, std::vector<std::string>& uris,
#endif // ENABLE_METALINK
op.blank(PREF_INPUT_FILE)) {
if(uris.empty()) {
global::cerr()->printf("%s\n", MSG_URI_REQUIRED);
showUsage(TAG_HELP, oparser);
global::cerr()->printf(MSG_URI_REQUIRED);
global::cerr()->printf("\n");
showUsage("", oparser, global::cerr());
exit(error_code::UNKNOWN_ERROR);
}
}

View File

@ -82,76 +82,84 @@ void showVersion() {
void showUsage
(const std::string& keyword,
const SharedHandle<OptionParser>& oparser) {
std::cout << _("Usage: aria2c [OPTIONS] [URI | MAGNET | TORRENT_FILE |"
" METALINK_FILE]...") << "\n"
<< "\n";
if(!keyword.empty() && keyword[0] == '#') {
const SharedHandle<OptionParser>& oparser,
const Console& out) {
out->printf(_("Usage: aria2c [OPTIONS] [URI | MAGNET | TORRENT_FILE |"
" METALINK_FILE]..."));
out->printf("\n");
if(keyword.empty()) {
// Very short version of usage.
out->printf(_("See 'aria2c -h'."));
out->printf("\n");
return;
} else if(keyword[0] == '#') {
std::vector<SharedHandle<OptionHandler> > handlers =
keyword == TAG_ALL ? oparser->findAll() : oparser->findByTag(keyword);
if(keyword == TAG_ALL) {
std::cout << _("Printing all options.");
out->printf(_("Printing all options."));
} else {
std::cout << fmt(_("Printing options tagged with '%s'."),
keyword.c_str());
std::cout << "\n";
const SharedHandle<OptionHandler>& help = oparser->find(PREF_HELP);
std::cout << fmt(_("See -h option to know other command-line"
" options(%s)."),
help->createPossibleValuesString().c_str());
out->printf(_("Printing options tagged with '%s'."),
keyword.c_str());
out->printf("\n");
out->printf(_("See 'aria2c -h#help' to know all available tags."));
}
std::cout << "\n"
<< _("Options:") << "\n";
out->printf("\n");
out->printf(_("Options:"));
out->printf("\n");
for(std::vector<SharedHandle<OptionHandler> >::const_iterator i =
handlers.begin(), eoi = handlers.end(); i != eoi; ++i) {
std::cout << *(*i) << "\n\n";
write(out, *(*i));
out->printf("\n");
}
} else {
std::vector<SharedHandle<OptionHandler> > handlers =
oparser->findByNameSubstring(keyword);
if(!handlers.empty()) {
std::cout << fmt(_("Printing options whose name includes '%s'."),
keyword.c_str())
<< "\n"
<< _("Options:") << "\n";
out->printf(_("Printing options whose name includes '%s'."),
keyword.c_str());
out->printf("\n");
out->printf(_("Options:"));
out->printf("\n");
for(std::vector<SharedHandle<OptionHandler> >::const_iterator i =
handlers.begin(), eoi = handlers.end(); i != eoi; ++i) {
std::cout << *(*i) << "\n\n";
write(out, *(*i));
out->printf("\n");
}
} else {
std::cout << fmt(_("No option matching with '%s'."),
keyword.c_str())
<< "\n" << *oparser->find(PREF_HELP) << "\n";
out->printf(_("No option matching with '%s'."),
keyword.c_str());
out->printf("\n");
write(out, *oparser->find(PREF_HELP));
}
}
if(keyword == TAG_BASIC) {
std::cout << "URI, MAGNET, TORRENT_FILE, METALINK_FILE:" << "\n"
<< _(" You can specify multiple HTTP(S)/FTP URIs. Unless you specify -Z option, all\n"
" URIs must point to the same file or downloading will fail.") << "\n"
<< _(" You can also specify arbitrary number of BitTorrent Magnet URIs, torrent/\n"
" metalink files stored in a local drive. Please note that they are always\n"
" treated as a separate download.") << "\n"
<< "\n"
<< _(" You can specify both torrent file with -T option and URIs. By doing this,\n"
" download a file from both torrent swarm and HTTP/FTP server at the same time,\n"
" while the data from HTTP/FTP are uploaded to the torrent swarm. For single file\n"
" torrents, URI can be a complete URI pointing to the resource or if URI ends\n"
" with '/', 'name' in torrent file is added. For multi-file torrents, 'name' and\n"
" 'path' in torrent are added to form a URI for each file.") << "\n"
<< "\n"
<< _(" Make sure that URI is quoted with single(\') or double(\") quotation if it\n"
" contains \"&\" or any characters that have special meaning in shell.") << "\n"
<< "\n";
std::cout << "About the number of connections\n"
<< " Since 1.10.0 release, aria2 uses 1 connection per host by default and has 20MiB\n"
<< " segment size restriction. So whatever value you specify using -s option, it\n"
<< " uses 1 connection per host. To make it behave like 1.9.x, use\n"
<< " --max-connection-per-server=4 --min-split-size=1M.\n"
<< "\n";
out->printf("URI, MAGNET, TORRENT_FILE, METALINK_FILE:\n");
out->printf(_(" You can specify multiple HTTP(S)/FTP URIs. Unless you specify -Z option, all\n"
" URIs must point to the same file or downloading will fail."));
out->printf("\n");
out->printf(_(" You can also specify arbitrary number of BitTorrent Magnet URIs, torrent/\n"
" metalink files stored in a local drive. Please note that they are always\n"
" treated as a separate download."));
out->printf("\n\n");
out->printf(_(" You can specify both torrent file with -T option and URIs. By doing this,\n"
" download a file from both torrent swarm and HTTP/FTP server at the same time,\n"
" while the data from HTTP/FTP are uploaded to the torrent swarm. For single file\n"
" torrents, URI can be a complete URI pointing to the resource or if URI ends\n"
" with '/', 'name' in torrent file is added. For multi-file torrents, 'name' and\n"
" 'path' in torrent are added to form a URI for each file."));
out->printf("\n\n");
out->printf(_(" Make sure that URI is quoted with single(\') or double(\") quotation if it\n"
" contains \"&\" or any characters that have special meaning in shell."));
out->printf("\n\n");
out->printf("About the number of connections\n"
" Since 1.10.0 release, aria2 uses 1 connection per host by default and has 20MiB\n"
" segment size restriction. So whatever value you specify using -s option, it\n"
" uses 1 connection per host. To make it behave like 1.9.x, use\n"
" --max-connection-per-server=4 --min-split-size=1M.\n"
"\n");
}
std::cout << _("Refer to man page for more information.") << std::endl;
out->printf(_("Refer to man page for more information."));
out->printf("\n");
}
} // namespace aria2