소스 검색

Use global::cout to output string to stdout.

In MinGW32 build, global::cout is WinConsoleFile. When outputing to
console, it uses WriteConsoleW, so Unicode characters are written.
Fixed memory leak in WChar<->MultiByte conversion functions.  The
portion of code which uses std::cout is rewritten to use global::cout.
Tatsuhiro Tsujikawa 14 년 전
부모
커밋
a10cda2f17
25개의 변경된 파일614개의 추가작업 그리고 185개의 파일을 삭제
  1. 2 0
      configure.ac
  2. 5 0
      src/BufferedFile.cc
  3. 6 5
      src/BufferedFile.h
  4. 24 20
      src/ConsoleStatCalc.cc
  5. 2 2
      src/FileEntry.h
  6. 19 17
      src/Logger.cc
  7. 4 3
      src/Logger.h
  8. 8 1
      src/Makefile.am
  9. 8 8
      src/MultiUrlRequestInfo.cc
  10. 3 3
      src/MultiUrlRequestInfo.h
  11. 52 0
      src/NullOutputFile.h
  12. 54 0
      src/OutputFile.h
  13. 3 0
      src/Platform.cc
  14. 19 17
      src/RequestGroupMan.cc
  15. 5 5
      src/RequestGroupMan.h
  16. 105 0
      src/WinConsoleFile.cc
  17. 58 0
      src/WinConsoleFile.h
  18. 0 48
      src/bittorrent_helper.cc
  19. 52 3
      src/bittorrent_helper.h
  20. 58 0
      src/console.cc
  21. 62 0
      src/console.h
  22. 12 13
      src/main.cc
  23. 32 28
      src/util.cc
  24. 14 10
      src/util.h
  25. 7 2
      test/UtilTest.cc

+ 2 - 0
configure.ac

@@ -435,6 +435,7 @@ AM_CONDITIONAL([HAVE_POLL], [test "x$have_poll" = "xyes"])
 
 case "$target" in
   *mingw*)
+    AM_CONDITIONAL([MINGW_BUILD], true)
     dnl defined in ws2tcpip.h, but only if _WIN32_WINNT >= 0x0501
     AM_CONDITIONAL([HAVE_GETADDRINFO], true)
     dnl defined in ws2tcpip.h, but missing in C:\mingw\lib\libws2_32.a
@@ -444,6 +445,7 @@ case "$target" in
     fi
     ;;
   *)
+    AM_CONDITIONAL([MINGW_BUILD], false)
     AM_CONDITIONAL([HAVE_TIMEGETTIME], false)
     ;;
 esac

+ 5 - 0
src/BufferedFile.cc

@@ -77,6 +77,11 @@ size_t BufferedFile::write(const void* ptr, size_t count)
   return fwrite(ptr, 1, count, fp_);
 }
 
+size_t BufferedFile::write(const char* str)
+{
+  return write(str, strlen(str));
+}
+
 char* BufferedFile::gets(char* s, int size)
 {
   return fgets(s, size, fp_);

+ 6 - 5
src/BufferedFile.h

@@ -35,7 +35,7 @@
 #ifndef D_BUFFERED_FILE_H
 #define D_BUFFERED_FILE_H
 
-#include "common.h"
+#include "OutputFile.h"
 
 #include <cstdio>
 #include <string>
@@ -44,14 +44,14 @@
 namespace aria2 {
 
 // This is a wrapper class for fopen/fclose/fread/fwrite/fgets.
-class BufferedFile {
+class BufferedFile:public OutputFile {
 private:
   typedef void (BufferedFile::*unspecified_bool_type)() const;
   void good_state() const {}
 public:
   BufferedFile(const std::string& filename, const std::string& mode);
   BufferedFile(FILE* fp);
-  ~BufferedFile();
+  virtual ~BufferedFile();
   // Returns true if file is opened and both ferror and feof returns
   // 0. Otherwise returns false.
   operator unspecified_bool_type() const;
@@ -59,6 +59,7 @@ public:
   size_t read(void* ptr, size_t count);
   // wrapper for fwrite. Using 1 for 2nd argument of fwrite.
   size_t write(const void* ptr, size_t count);
+  virtual size_t write(const char* str);
   // wrapper for fgets
   char* gets(char* s, int size);
   // wrapper for fgets, but trailing '\n' is replaced with '\0'.
@@ -71,9 +72,9 @@ public:
   // given stream. Returns written size.
   size_t transfer(std::ostream& out);
   // wrapper for fprintf
-  int printf(const char* format, ...);
+  virtual int printf(const char* format, ...);
   // wrapper for fflush
-  int flush();
+  virtual int flush();
   // Mode for reading
   static const std::string READ;
   // Mode for writing

+ 24 - 20
src/ConsoleStatCalc.cc

@@ -61,6 +61,7 @@
 #include "DownloadContext.h"
 #include "wallclock.h"
 #include "FileEntry.h"
+#include "console.h"
 #ifdef ENABLE_BITTORRENT
 # include "bittorrent_helper.h"
 # include "PeerStorage.h"
@@ -185,15 +186,17 @@ public:
   void operator()(const SharedHandle<RequestGroup>& rg)
   {
     const char SEP_CHAR = '-';
-    printProgress(std::cout, rg, e_, sizeFormatter_);
+    std::stringstream o;
+    printProgress(o, rg, e_, sizeFormatter_);
     const std::vector<SharedHandle<FileEntry> >& fileEntries =
       rg->getDownloadContext()->getFileEntries();
-    std::cout << "\n"
-              << "FILE: ";
+    o << "\n"
+      << "FILE: ";
     writeFilePath(fileEntries.begin(), fileEntries.end(),
-                  std::cout, rg->inMemoryDownload());
-    std::cout << "\n"
-              << std::setfill(SEP_CHAR) << std::setw(cols_) << SEP_CHAR << "\n";
+                  o, rg->inMemoryDownload());
+    o << "\n"
+      << std::setfill(SEP_CHAR) << std::setw(cols_) << SEP_CHAR << "\n";
+    global::cout->write(o.str().c_str());
   }
 };
 } // namespace
@@ -207,7 +210,8 @@ void printProgressSummary
   const char SEP_CHAR = '=';
   time_t now;
   time(&now);
-  std::cout << " *** Download Progress Summary";
+  std::stringstream o;
+  o << " *** Download Progress Summary";
   {
     time_t now;
     struct tm* staticNowtmPtr;
@@ -218,11 +222,12 @@ void printProgressSummary
       if(lfptr) {
         *lfptr = '\0';
       }
-      std::cout << " as of " << buf;
+      o << " as of " << buf;
     }
   }
-  std::cout << " *** " << "\n"
-            << std::setfill(SEP_CHAR) << std::setw(cols) << SEP_CHAR << "\n";
+  o << " *** " << "\n"
+    << std::setfill(SEP_CHAR) << std::setw(cols) << SEP_CHAR << "\n";
+  global::cout->write(o.str().c_str());
   std::for_each(groups.begin(), groups.end(),
                 PrintSummary(cols, e, sizeFormatter));
 }
@@ -267,7 +272,8 @@ ConsoleStatCalc::calculateStat(const DownloadEngine* e)
     }
 #endif // HAVE_TERMIOS_H
 #endif // !__MINGW32__
-    std::cout << '\r' << std::setfill(' ') << std::setw(cols) << ' ' << '\r';
+    std::string line(cols, ' ');
+    global::cout->printf("\r%s\r", line.c_str());
   }
   std::ostringstream o;
   if(e->getRequestGroupMan()->countRequestGroup() > 0) {
@@ -277,7 +283,8 @@ ConsoleStatCalc::calculateStat(const DownloadEngine* e)
       lastSummaryNotified_ = global::wallclock;
       printProgressSummary(e->getRequestGroupMan()->getRequestGroups(), cols, e,
                            sizeFormatter);
-      std::cout << "\n";
+      global::cout->write("\n");
+      global::cout->flush();
     }
   }
   if(!readoutVisibility_) {
@@ -362,17 +369,14 @@ ConsoleStatCalc::calculateStat(const DownloadEngine* e)
 #endif // ENABLE_MESSAGE_DIGEST
   std::string readout = o.str();
   if(isTTY) {
-    std::string::iterator last = readout.begin();
     if(truncate_ && readout.size() > cols) {
-      std::advance(last, cols);
-    } else {
-      last = readout.end();
+      readout[cols] = '\0';
     }
-    std::copy(readout.begin(), last, std::ostream_iterator<char>(std::cout));
-    std::cout << std::flush;
+    global::cout->write(readout.c_str());
+    global::cout->flush();
   } else {
-    std::copy(readout.begin(), readout.end(), std::ostream_iterator<char>(std::cout));
-    std::cout << std::endl;
+    global::cout->write(readout.c_str());
+    global::cout->write("\n");
   }
 }
 

+ 2 - 2
src/FileEntry.h

@@ -317,9 +317,9 @@ void writeFilePath
       }
     } else {
       if(memory) {
-        o << "[MEMORY]" << utf8ToNative(File(e->getPath()).getBasename());
+        o << "[MEMORY]" << File(e->getPath()).getBasename();
       } else {
-        o << utf8ToNative(e->getPath());
+        o << e->getPath();
       }
       size_t count = countRequestedFileEntry(first, last);
       if(count > 1) {

+ 19 - 17
src/Logger.cc

@@ -44,6 +44,7 @@
 #include "a2time.h"
 #include "BufferedFile.h"
 #include "util.h"
+#include "console.h"
 
 namespace aria2 {
 
@@ -61,31 +62,30 @@ static const std::string ERROR_LABEL("ERROR");
 
 Logger::Logger()
   : logLevel_(Logger::A2_DEBUG),
-    fpp_(0),
-    stdoutfpp_(new BufferedFile(stdout)),
     stdoutField_(0)
 {}
 
 Logger::~Logger()
 {
-  delete fpp_;
-  delete stdoutfpp_;
 }
 
 void Logger::openFile(const std::string& filename)
 {
   closeFile();
-  fpp_ = new BufferedFile(filename, BufferedFile::APPEND);
-  if(!fpp_) {
-    throw DL_ABORT_EX(fmt(EX_FILE_OPEN, filename.c_str(), "n/a"));
+  if(filename == DEV_STDOUT) {
+    fpp_ = global::cout;
+  } else {
+    fpp_.reset(new BufferedFile(filename, BufferedFile::APPEND));
+    if(!fpp_) {
+      throw DL_ABORT_EX(fmt(EX_FILE_OPEN, filename.c_str(), "n/a"));
+    }
   }
 }
 
 void Logger::closeFile()
 {
   if(fpp_) {
-    fpp_->close();
-    fpp_ = 0;
+    fpp_.reset();
   }
 }
 
@@ -124,8 +124,9 @@ const std::string& levelToString(Logger::LEVEL level)
 } // namespace
 
 namespace {
+template<typename Output>
 void writeHeader
-(BufferedFile& fp, Logger::LEVEL level, const char* sourceFile, int lineNum)
+(Output& fp, Logger::LEVEL level, const char* sourceFile, int lineNum)
 {
   struct timeval tv;
   gettimeofday(&tv, 0);
@@ -146,9 +147,10 @@ void writeHeader
 } // namespace
 
 namespace {
-void writeStackTrace(BufferedFile& fp, const std::string& stackTrace)
+template<typename Output>
+void writeStackTrace(Output& fp, const std::string& stackTrace)
 {
-  fp.write(stackTrace.data(), stackTrace.size());
+  fp.write(stackTrace.c_str());
 }
 } // namespace
 
@@ -168,11 +170,11 @@ void Logger::writeLog
     fpp_->flush();
   }
   if(toConsole) {
-    stdoutfpp_->write("\n", 1);
-    writeHeader(*stdoutfpp_, level, 0, 0);
-    stdoutfpp_->printf("%s\n", msg);
-    writeStackTrace(*stdoutfpp_, trace);
-    stdoutfpp_->flush();
+    global::cout->printf("\n");
+    writeHeader(*global::cout, level, 0, 0);
+    global::cout->printf("%s\n", msg);
+    writeStackTrace(*global::cout, trace);
+    global::cout->flush();
   }
 }
 

+ 4 - 3
src/Logger.h

@@ -39,10 +39,12 @@
 
 #include <string>
 
+#include "SharedHandle.h"
+
 namespace aria2 {
 
 class Exception;
-class BufferedFile;
+class OutputFile;
 
 class Logger {
 public:
@@ -55,8 +57,7 @@ public:
   };
 private:
   LEVEL logLevel_;
-  BufferedFile* fpp_;
-  BufferedFile* stdoutfpp_;
+  SharedHandle<OutputFile> fpp_;
   int stdoutField_;
   // Don't allow copying
   Logger(const Logger&);

+ 8 - 1
src/Makefile.am

@@ -225,7 +225,14 @@ SRCS =  Socket.h\
 	StreamPieceSelector.h\
 	DefaultStreamPieceSelector.cc DefaultStreamPieceSelector.h\
 	InorderStreamPieceSelector.cc InorderStreamPieceSelector.h\
-	MetalinkHttpEntry.cc MetalinkHttpEntry.h
+	MetalinkHttpEntry.cc MetalinkHttpEntry.h\
+	OutputFile.h\
+	NullOutputFile.h\
+	console.cc console.h
+
+if MINGW_BUILD
+SRCS += WinConsoleFile.cc WinConsoleFile.h
+endif # MINGW_BUILD
 
 if ENABLE_XML_RPC
 SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\

+ 8 - 8
src/MultiUrlRequestInfo.cc

@@ -59,6 +59,7 @@
 #include "TimeA2.h"
 #include "fmt.h"
 #include "SocketCore.h"
+#include "OutputFile.h"
 #ifdef ENABLE_SSL
 # include "TLSContext.h"
 #endif // ENABLE_SSL
@@ -142,7 +143,7 @@ MultiUrlRequestInfo::MultiUrlRequestInfo
 (const std::vector<SharedHandle<RequestGroup> >& requestGroups,
  const SharedHandle<Option>& op,
  const SharedHandle<StatCalc>& statCalc,
- std::ostream& summaryOut)
+ const SharedHandle<OutputFile>& summaryOut)
   : requestGroups_(requestGroups),
     option_(op),
     statCalc_(statCalc),
@@ -153,11 +154,10 @@ MultiUrlRequestInfo::~MultiUrlRequestInfo() {}
 
 void MultiUrlRequestInfo::printMessageForContinue()
 {
-  summaryOut_ << "\n"
-              << _("aria2 will resume download if the transfer is restarted.")
-              << "\n"
-              << _("If there are any errors, then see the log file. See '-l' option in help/man page for details.")
-              << "\n";
+  summaryOut_->printf
+    ("\n%s\n%s\n",
+     _("aria2 will resume download if the transfer is restarted."),
+     _("If there are any errors, then see the log file. See '-l' option in help/man page for details."));
 }
 
 error_code::Value MultiUrlRequestInfo::execute()
@@ -251,8 +251,8 @@ error_code::Value MultiUrlRequestInfo::execute()
     if(!serverStatOf.empty()) {
       e->getRequestGroupMan()->saveServerStat(serverStatOf);
     }
-    e->getRequestGroupMan()->showDownloadResults(summaryOut_);
-    summaryOut_ << std::flush;
+    e->getRequestGroupMan()->showDownloadResults(*summaryOut_);
+    summaryOut_->flush();
 
     RequestGroupMan::DownloadStat s =
       e->getRequestGroupMan()->getDownloadStat();

+ 3 - 3
src/MultiUrlRequestInfo.h

@@ -38,7 +38,6 @@
 #include "common.h"
 
 #include <vector>
-#include <iosfwd>
 
 #include "SharedHandle.h"
 #include "DownloadResult.h"
@@ -48,6 +47,7 @@ namespace aria2 {
 class RequestGroup;
 class Option;
 class StatCalc;
+class OutputFile;
 
 class MultiUrlRequestInfo {
 private:
@@ -57,7 +57,7 @@ private:
 
   SharedHandle<StatCalc> statCalc_;
 
-  std::ostream& summaryOut_;
+  SharedHandle<OutputFile> summaryOut_;
 
   void printMessageForContinue();
 public:
@@ -65,7 +65,7 @@ public:
   (const std::vector<SharedHandle<RequestGroup> >& requestGroups,
    const SharedHandle<Option>& op,
    const SharedHandle<StatCalc>& statCalc,
-   std::ostream& summaryOut);
+   const SharedHandle<OutputFile>& summaryOut);
   
   virtual ~MultiUrlRequestInfo();
 

+ 52 - 0
src/NullOutputFile.h

@@ -0,0 +1,52 @@
+/* <!-- 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_NULL_OUTPUT_FILE_H
+#define D_NULL_OUTPUT_FILE_H
+
+#include "OutputFile.h"
+
+namespace aria2 {
+
+class NullOutputFile:public OutputFile {
+public:
+  virtual ~NullOutputFile() {}
+  virtual size_t write(const char* str) { return 0; }
+  virtual int printf(const char* format, ...) { return 0; }
+  virtual int flush() { return 0; }
+};
+
+} // namespace aria2
+
+#endif // D_NULL_OUTPUT_FILE_H

+ 54 - 0
src/OutputFile.h

@@ -0,0 +1,54 @@
+/* <!-- 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_OUTPUT_FILE_H
+#define D_OUTPUT_FILE_H
+
+#include "common.h"
+
+#include <cstdlib>
+
+namespace aria2 {
+
+class OutputFile {
+public:
+  virtual ~OutputFile() {}
+  virtual size_t write(const char* str) = 0;
+  virtual int printf(const char* format, ...) = 0;
+  virtual int flush() = 0;
+};
+
+} // namespace aria2
+
+#endif // D_OUTPUT_FILE_H

+ 3 - 0
src/Platform.cc

@@ -72,6 +72,7 @@
 #include "DlAbortEx.h"
 #include "message.h"
 #include "fmt.h"
+#include "console.h"
 
 namespace aria2 {
 
@@ -94,6 +95,8 @@ bool Platform::setUp()
   }
   initialized_ = true;
 
+  global::initConsole();
+
 #ifdef ENABLE_NLS
   setlocale (LC_CTYPE, "");
   setlocale (LC_MESSAGES, "");

+ 19 - 17
src/RequestGroupMan.cc

@@ -38,7 +38,6 @@
 #include <cstring>
 #include <iomanip>
 #include <sstream>
-#include <ostream>
 #include <numeric>
 #include <algorithm>
 #include <utility>
@@ -74,6 +73,7 @@
 #include "uri.h"
 #include "Triplet.h"
 #include "Signature.h"
+#include "OutputFile.h"
 
 namespace aria2 {
 
@@ -588,7 +588,7 @@ RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const
                       lastError);
 }
 
-void RequestGroupMan::showDownloadResults(std::ostream& o) const
+void RequestGroupMan::showDownloadResults(OutputFile& o) const
 {
   static const std::string MARK_OK("OK");
   static const std::string MARK_ERR("ERR");
@@ -598,16 +598,17 @@ void RequestGroupMan::showDownloadResults(std::ostream& o) const
   // Download Results:
   // idx|stat|path/length
   // ===+====+=======================================================================
-  o << "\n"
-    <<_("Download Results:") << "\n"
-    << "gid|stat|avg speed  |path/URI" << "\n"
-    << "===+====+===========+";
+  o.printf("\n%s"
+           "\ngid|stat|avg speed  |path/URI"
+           "\n===+====+===========+",
+           _("Download Results:"));
 #ifdef __MINGW32__
   int pathRowSize = 58;
 #else // !__MINGW32__
   int pathRowSize = 59;
 #endif // !__MINGW32__
-  o << std::setfill('=') << std::setw(pathRowSize) << '=' << "\n";
+  std::string line(pathRowSize, '=');
+  o.printf("%s\n", line.c_str());
   int ok = 0;
   int err = 0;
   int inpr = 0;
@@ -632,29 +633,30 @@ void RequestGroupMan::showDownloadResults(std::ostream& o) const
       status = MARK_ERR;
       ++err;
     }
-    o << formatDownloadResult(status, *itr) << "\n";
+    o.write(formatDownloadResult(status, *itr).c_str());
+    o.write("\n");
   }
   if(ok > 0 || err > 0 || inpr > 0 || rm > 0) {
-    o << "\n"
-      << _("Status Legend:") << "\n";
-
+    o.printf("\n%s\n", _("Status Legend:"));
     if(ok > 0) {
-      o << " (OK):download completed.";
+      o.write(" (OK):download completed.");
     }
     if(err > 0) {
-      o << "(ERR):error occurred.";
+      o.write("(ERR):error occurred.");
     }
     if(inpr > 0) {
-      o << "(INPR):download in-progress.";
+      o.write("(INPR):download in-progress.");
     }
     if(rm > 0) {
-      o << "(RM):download removed.";
+      o.write("(RM):download removed.");
     }
-    o << "\n";
+    o.write("\n");
   }
 }
 
-std::string RequestGroupMan::formatDownloadResult(const std::string& status, const DownloadResultHandle& downloadResult) const
+std::string RequestGroupMan::formatDownloadResult
+(const std::string& status,
+ const DownloadResultHandle& downloadResult) const
 {
   std::stringstream o;
   o << std::setw(3) << downloadResult->gid << "|"

+ 5 - 5
src/RequestGroupMan.h

@@ -39,7 +39,6 @@
 
 #include <string>
 #include <deque>
-#include <iosfwd>
 #include <vector>
 
 #include "SharedHandle.h"
@@ -55,6 +54,7 @@ struct DownloadResult;
 class ServerStatMan;
 class ServerStat;
 class Option;
+class OutputFile;
 
 class RequestGroupMan {
 private:
@@ -85,9 +85,9 @@ private:
 
   size_t maxDownloadResult_;
 
-  std::string
-  formatDownloadResult(const std::string& status,
-                       const SharedHandle<DownloadResult>& downloadResult) const;
+  std::string formatDownloadResult
+  (const std::string& status,
+   const DownloadResultHandle& downloadResult) const;
 
   void configureRequestGroup
   (const SharedHandle<RequestGroup>& requestGroup) const;
@@ -160,7 +160,7 @@ public:
 
   bool removeReservedGroup(a2_gid_t gid);
 
-  void showDownloadResults(std::ostream& o) const;
+  void showDownloadResults(OutputFile& o) const;
 
   bool isSameFileBeingDownloaded(RequestGroup* requestGroup) const;
 

+ 105 - 0
src/WinConsoleFile.cc

@@ -0,0 +1,105 @@
+/* <!-- 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 "WinConsoleFile.h"
+
+#include <cstring>
+#include <cstdio>
+#include <cstdarg>
+#include <string>
+
+#ifdef __MINGW32__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif // __MINGW32__
+
+#include "a2io.h"
+#include "util.h"
+
+namespace aria2 {
+
+WinConsoleFile::WinConsoleFile() {}
+
+WinConsoleFile::~WinConsoleFile() {}
+
+namespace {
+bool console()
+{
+  DWORD mode;
+  return GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode);
+}
+} // namespace
+
+size_t WinConsoleFile::write(const char* str)
+{
+  DWORD written;
+  if(console()) {
+    std::wstring msg = utf8ToWChar(str);
+    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+                  msg.c_str(), msg.size(), &written, 0);
+  } else {
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+              str, strlen(str), &written, 0);
+  }
+  return written;
+}
+
+int WinConsoleFile::printf(const char* format, ...)
+{
+  char buf[1024];
+  va_list ap;
+  va_start(ap, format);
+  int r = vsnprintf(buf, sizeof(buf), format, ap);
+  va_end(ap);
+  if(r <= 0) {
+    return 0;
+  }
+  DWORD written;
+  if(console()) {
+    std::wstring msg = utf8ToWChar(buf);
+    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+                  msg.c_str(), msg.size(), &written, 0);
+  } else {
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+              buf, r, &written, 0);
+  }
+  return written;
+}
+
+int WinConsoleFile::flush()
+{
+  return 0;
+}
+
+} // namespace aria2

+ 58 - 0
src/WinConsoleFile.h

@@ -0,0 +1,58 @@
+/* <!-- 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_WIN_CONSOLE_FILE_H
+#define D_WIN_CONSOLE_FILE_H
+
+#include "OutputFile.h"
+
+namespace aria2 {
+
+// This is a wrapper class for WriteConsoleW
+class WinConsoleFile:public OutputFile {
+public:
+  WinConsoleFile();
+  virtual ~WinConsoleFile();
+  virtual size_t write(const char* str);
+  virtual int printf(const char* format, ...);
+  virtual int flush();
+private:
+  // Don't allow copying
+  WinConsoleFile(const WinConsoleFile&);
+  WinConsoleFile& operator=(const WinConsoleFile&);
+};
+
+} // namespace aria2
+
+#endif // D_WIN_CONSOLE_FILE_H

+ 0 - 48
src/bittorrent_helper.cc

@@ -611,54 +611,6 @@ getInfoHashString(const SharedHandle<DownloadContext>& dctx)
   return util::toHex(getTorrentAttrs(dctx)->infoHash);
 }
 
-void print(std::ostream& o, const SharedHandle<DownloadContext>& dctx)
-{
-  SharedHandle<TorrentAttribute> torrentAttrs = getTorrentAttrs(dctx);
-  o << "*** BitTorrent File Information ***" << "\n";
-  if(!torrentAttrs->comment.empty()) {
-    o << "Comment: " << torrentAttrs->comment << "\n";
-  }
-  if(torrentAttrs->creationDate) {
-    o << "Creation Date: " << Time(torrentAttrs->creationDate).toHTTPDate()
-      << std::endl;
-  }
-  if(!torrentAttrs->createdBy.empty()) {
-    o << "Created By: " << torrentAttrs->createdBy << "\n";
-  }
-  o << "Mode: " << torrentAttrs->mode << "\n";
-  o << "Announce:" << "\n";
-  for(std::vector<std::vector<std::string> >::const_iterator tierIter =
-        torrentAttrs->announceList.begin(),
-        eoi = torrentAttrs->announceList.end(); tierIter != eoi; ++tierIter) {
-    for(std::vector<std::string>::const_iterator i = (*tierIter).begin(),
-          eoi2 = (*tierIter).end(); i != eoi2; ++i) {
-      o << " " << *i;
-    }
-    o << "\n";
-  }
-  o << "Info Hash: "
-    << util::toHex(torrentAttrs->infoHash) << "\n"
-    << "Piece Length: "
-    << util::abbrevSize(dctx->getPieceLength()) << "B\n"
-    << "The Number of Pieces: "
-    << dctx->getNumPieces() << "\n"
-    << "Total Length: "
-    << util::abbrevSize(dctx->getTotalLength()) << "B ("
-    << util::uitos(dctx->getTotalLength(), true) << ")\n";
-  if(!torrentAttrs->urlList.empty()) {
-    o << "URL List: " << "\n";
-    for(std::vector<std::string>::const_iterator i =
-          torrentAttrs->urlList.begin(),
-          eoi = torrentAttrs->urlList.end(); i != eoi; ++i) {
-      o << " " << *i << "\n";
-    }
-  }
-  o << "Name: " << torrentAttrs->name << "\n"
-    << "Magnet URI: " << torrent2Magnet(torrentAttrs) << "\n";
-  util::toStream
-    (dctx->getFileEntries().begin(), dctx->getFileEntries().end(), o);
-}
-
 void computeFastSet
 (std::vector<size_t>& fastSet, const std::string& ipaddr,
  size_t numPieces, const unsigned char* infoHash, size_t fastSetSize)

+ 52 - 3
src/bittorrent_helper.h

@@ -47,6 +47,9 @@
 #include "a2netcompat.h"
 #include "Peer.h"
 #include "ValueBase.h"
+#include "util.h"
+#include "DownloadContext.h"
+#include "TimeA2.h"
 
 namespace aria2 {
 
@@ -142,9 +145,6 @@ void computeFastSet
 (std::vector<size_t>& fastSet, const std::string& ipaddr,
  size_t numPieces, const unsigned char* infoHash, size_t fastSetSize);
 
-// Writes the detailed information about torrent loaded in dctx.
-void print(std::ostream& o, const SharedHandle<DownloadContext>& dctx);
-
 SharedHandle<TorrentAttribute> getTorrentAttrs
 (const SharedHandle<DownloadContext>& dctx);
 
@@ -323,6 +323,55 @@ void extractPeer
 
 int getCompactLength(int family);
 
+// Writes the detailed information about torrent loaded in dctx.
+template<typename Output>
+void print(Output& o, const SharedHandle<DownloadContext>& dctx)
+{
+  SharedHandle<TorrentAttribute> torrentAttrs = getTorrentAttrs(dctx);
+  o.write("*** BitTorrent File Information ***\n");
+  if(!torrentAttrs->comment.empty()) {
+    o.printf("Comment: %s\n", torrentAttrs->comment.c_str());
+  }
+  if(torrentAttrs->creationDate) {
+    o.printf("Creation Date: %s\n",
+             Time(torrentAttrs->creationDate).toHTTPDate().c_str());
+  }
+  if(!torrentAttrs->createdBy.empty()) {
+    o.printf("Created By: %s\n", torrentAttrs->createdBy.c_str());
+  }
+  o.printf("Mode: %s\n", torrentAttrs->mode.c_str());
+  o.write("Announce:\n");
+  for(std::vector<std::vector<std::string> >::const_iterator tierIter =
+        torrentAttrs->announceList.begin(),
+        eoi = torrentAttrs->announceList.end(); tierIter != eoi; ++tierIter) {
+    for(std::vector<std::string>::const_iterator i = (*tierIter).begin(),
+          eoi2 = (*tierIter).end(); i != eoi2; ++i) {
+      o.printf(" %s", (*i).c_str());
+    }
+    o.write("\n");
+  }
+  o.printf("Info Hash: %s\n", util::toHex(torrentAttrs->infoHash).c_str());
+  o.printf("Piece Length: %sB\n",
+           util::abbrevSize(dctx->getPieceLength()).c_str());
+  o.printf("The Number of Pieces: %lu\n",
+           static_cast<unsigned long>(dctx->getNumPieces()));
+  o.printf("Total Length: %sB (%s)\n",
+           util::abbrevSize(dctx->getTotalLength()).c_str(),
+           util::uitos(dctx->getTotalLength(), true).c_str());
+  if(!torrentAttrs->urlList.empty()) {
+    o.write("URL List:\n");
+    for(std::vector<std::string>::const_iterator i =
+          torrentAttrs->urlList.begin(),
+          eoi = torrentAttrs->urlList.end(); i != eoi; ++i) {
+      o.printf(" %s\n", (*i).c_str());
+    }
+  }
+  o.printf("Name: %s\n", torrentAttrs->name.c_str());
+  o.printf("Magnet URI: %s\n", torrent2Magnet(torrentAttrs).c_str());
+  util::toStream
+    (dctx->getFileEntries().begin(), dctx->getFileEntries().end(), o);
+}
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 58 - 0
src/console.cc

@@ -0,0 +1,58 @@
+/* <!-- 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 "console.h"
+
+namespace aria2 {
+
+namespace global {
+
+#ifdef __MINGW32__
+SharedHandle<WinConsoleFile> cout;
+#else // !__MINGW32__
+SharedHandle<BufferedFile> cout;
+#endif // !__MINGW32__
+
+void initConsole()
+{
+#ifdef __MINGW32__
+  cout.reset(new WinConsoleFile());
+#else // !__MINGW32__
+  cout.reset(new BufferedFile(stdout));
+#endif // !__MINGW32__
+}
+
+} // namespace global
+
+} // namespace aria2

+ 62 - 0
src/console.h

@@ -0,0 +1,62 @@
+/* <!-- 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_CONSOLE_H
+#define D_CONSOLE_H
+
+#include "common.h"
+#include "SharedHandle.h"
+#ifdef __MINGW32__
+# include "WinConsoleFile.h"
+#else // !__MINGW32__
+# include "BufferedFile.h"
+#endif // !__MINGW32__
+
+namespace aria2 {
+
+namespace global {
+
+#ifdef __MINGW32__
+extern SharedHandle<WinConsoleFile> cout;
+#else // !__MINGW32__
+extern SharedHandle<BufferedFile> cout;
+#endif // !__MINGW32__
+
+void initConsole();
+
+} // namespace global
+
+} // namespace aria2
+
+#endif // D_CONSOLE_H

+ 12 - 13
src/main.cc

@@ -38,10 +38,9 @@
 #include <unistd.h>
 #include <getopt.h>
 
-#include <fstream>
-#include <iostream>
 #include <numeric>
 #include <vector>
+#include <iostream>
 
 #include "SharedHandle.h"
 #include "LogFactory.h"
@@ -69,6 +68,8 @@
 #include "SocketCore.h"
 #include "DownloadContext.h"
 #include "fmt.h"
+#include "NullOutputFile.h"
+#include "console.h"
 #ifdef ENABLE_BITTORRENT
 # include "bittorrent_helper.h"
 #endif // ENABLE_BITTORRENT
@@ -85,9 +86,6 @@ extern int optind, opterr, optopt;
 
 namespace aria2 {
 
-// output stream to /dev/null
-std::ofstream nullout(DEV_NULL);
-
 SharedHandle<StatCalc> getStatCalc(const SharedHandle<Option>& op)
 {
   SharedHandle<StatCalc> statCalc;
@@ -104,12 +102,12 @@ SharedHandle<StatCalc> getStatCalc(const SharedHandle<Option>& op)
   return statCalc;
 }
 
-std::ostream& getSummaryOut(const SharedHandle<Option>& op)
+SharedHandle<OutputFile> getSummaryOut(const SharedHandle<Option>& op)
 {
   if(op->getAsBool(PREF_QUIET)) {
-    return nullout;
+    return SharedHandle<OutputFile>(new NullOutputFile());
   } else {
-    return std::cout;
+    return global::cout;
   }
 }
 
@@ -120,7 +118,7 @@ void showTorrentFile(const std::string& uri)
   SharedHandle<Option> op(new Option());
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   bittorrent::load(uri, dctx, op);
-  bittorrent::print(std::cout, dctx);
+  bittorrent::print(*global::cout, dctx);
 }
 } // namespace
 #endif // ENABLE_BITTORRENT
@@ -135,8 +133,9 @@ void showMetalinkFile
                           op->get(PREF_METALINK_BASE_URI));
   std::vector<SharedHandle<FileEntry> > fileEntries;
   MetalinkEntry::toFileEntry(fileEntries, metalinkEntries);
-  util::toStream(fileEntries.begin(), fileEntries.end(), std::cout);
-  std::cout << std::endl;
+  util::toStream(fileEntries.begin(), fileEntries.end(), *global::cout);
+  global::cout->write("\n");
+  global::cout->flush();
 }
 } // namespace
 #endif // ENABLE_METALINK
@@ -168,7 +167,7 @@ void showFiles
             printf("\n\n");
           }
     } catch(RecoverableException& e) {
-      std::cout << e.stackTrace() << std::endl;
+      global::cout->printf("%s\n", e.stackTrace().c_str());
     }
   }
 }
@@ -269,7 +268,7 @@ error_code::Value main(int argc, char* argv[])
   op->remove(PREF_SELECT_FILE);
   op->remove(PREF_PAUSE);
   if(!op->getAsBool(PREF_ENABLE_RPC) && requestGroups.empty()) {
-    std::cout << MSG_NO_FILES_TO_DOWNLOAD << std::endl;
+    global::cout->printf("%s\n", MSG_NO_FILES_TO_DOWNLOAD);
   } else {
     exitStatus = MultiUrlRequestInfo(requestGroups, op, getStatCalc(op),
                                      getSummaryOut(op)).execute();

+ 32 - 28
src/util.cc

@@ -97,95 +97,99 @@ namespace aria2 {
 
 #ifdef __MINGW32__
 namespace {
-int utf8ToWChar(wchar_t* out, size_t outLength, const std::string& src)
+int utf8ToWChar(wchar_t* out, size_t outLength, const char* src)
 {
-  return MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1,
-                             out, outLength);
+  return MultiByteToWideChar(CP_UTF8, 0, src, -1, out, outLength);
 }
 } // namespace
 
 namespace {
-int ansiToWChar(wchar_t* out, size_t outLength, const std::string& src)
+int ansiToWChar(wchar_t* out, size_t outLength, const char* src)
 {
-  return MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1,
-                             out, outLength);
+  return MultiByteToWideChar(CP_ACP, 0, src, -1, out, outLength);
 }
 } // namespace
 
 namespace {
-int wCharToUtf8(char* out, size_t outLength, const std::wstring& src)
+int wCharToUtf8(char* out, size_t outLength, const wchar_t* src)
 {
-  return WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1,
-                             out, outLength, 0, 0);
+  return WideCharToMultiByte(CP_UTF8, 0, src, -1, out, outLength, 0, 0);
 }
 } // namespace
 
 namespace {
-int wCharToAnsi(char* out, size_t outLength, const std::wstring& src)
+int wCharToAnsi(char* out, size_t outLength, const wchar_t* src)
 {
-  return WideCharToMultiByte(CP_ACP, 0, src.c_str(), -1,
-                             out, outLength, 0, 0);
+  return WideCharToMultiByte(CP_ACP, 0, src, -1, out, outLength, 0, 0);
 }
 } // namespace
 
-std::wstring utf8ToWChar(const std::string& src)
+std::wstring utf8ToWChar(const char* src)
 {
   int len = utf8ToWChar(0, 0, src);
   if(len == 0) {
     abort();
   }
-  wchar_t* buf = new wchar_t[len];
+  array_ptr<wchar_t> buf(new wchar_t[len]);
   len = utf8ToWChar(buf, len, src);
   if(len == 0) {
     abort();
   } else {
-    return buf;
+    std::wstring dest(buf);
+    return dest;
   }
 }
 
-std::string const std::string& src
+std::wstring utf8ToWChar(const std::string& src)
+{
+  return utf8ToWChar(src.c_str());
+}
+
+std::string utf8ToNative(const std::string& src)
 {
   std::wstring wsrc = utf8ToWChar(src);
-  int len = wCharToAnsi(0, 0, wsrc);
+  int len = wCharToAnsi(0, 0, wsrc.c_str());
   if(len == 0) {
     abort();
   }
-  char* buf = new char[len];
-  len = wCharToAnsi(buf, len, wsrc);
+  array_ptr<char> buf(new char[len]);
+  len = wCharToAnsi(buf, len, wsrc.c_str());
   if(len == 0) {
     abort();
   } else {
-    return buf;
+    std::string dest(buf);
+    return dest;
   }
 }
 
 std::string wCharToUtf8(const std::wstring& wsrc)
 {
-  int len = wCharToUtf8(0, 0, wsrc);
+  int len = wCharToUtf8(0, 0, wsrc.c_str());
   if(len == 0) {
     abort();
   }
-  char* buf = new char[len];
-  len = wCharToUtf8(buf, len, wsrc);
+  array_ptr<char> buf(new char[len]);
+  len = wCharToUtf8(buf, len, wsrc.c_str());
   if(len == 0) {
     abort();
   } else {
-    return buf;
+    std::string dest(buf);
+    return dest;
   }
 }
 
 std::string nativeToUtf8(const std::string& src)
 {
-  int len = ansiToWChar(0, 0, src);
+  int len = ansiToWChar(0, 0, src.c_str());
   if(len == 0) {
     abort();
   }
-  wchar_t* buf = new wchar_t[len];
-  len = ansiToWChar(buf, len, src);
+  array_ptr<wchar_t> buf(new wchar_t[len]);
+  len = ansiToWChar(buf, len, src.c_str());
   if(len == 0) {
     abort();
   } else {
-    return wCharToUtf8(buf);
+    return wCharToUtf8(std::wstring(buf));
   }
 }
 #endif // __MINGW32__

+ 14 - 10
src/util.h

@@ -89,6 +89,8 @@ inline uint64_t hton64(uint64_t x) { return byteswap64(x); }
 #ifdef __MINGW32__
 std::wstring utf8ToWChar(const std::string& src);
 
+std::wstring utf8ToWChar(const char* str);
+
 std::string utf8ToNative(const std::string& src);
 
 std::string wCharToUtf8(const std::wstring& wsrc);
@@ -261,20 +263,22 @@ int64_t getRealSize(const std::string& sizeWithUnit);
 
 std::string abbrevSize(int64_t size);
 
-template<typename InputIterator>
+template<typename InputIterator, typename Output>
 void toStream
-(InputIterator first, InputIterator last, std::ostream& os)
+(InputIterator first, InputIterator last, Output& os)
 {
-  os << _("Files:") << "\n";
-  os << "idx|path/length" << "\n";
-  os << "===+===========================================================================" << "\n";
+  os.printf("%s\n"
+            "idx|path/length\n"
+            "===+===========================================================================\n", _("Files:"));
   int32_t count = 1;
   for(; first != last; ++first, ++count) {
-    os << std::setw(3) << count << "|"
-       << utf8ToNative((*first)->getPath()) << "\n";
-    os << "   |" << util::abbrevSize((*first)->getLength()) << "B ("
-       << util::uitos((*first)->getLength(), true) << ")\n";
-    os << "---+---------------------------------------------------------------------------" << "\n";
+    os.printf("%3d|%s\n"
+              "   |%sB (%s)\n"
+              "---+---------------------------------------------------------------------------\n",
+              count,
+              (*first)->getPath().c_str(),
+              util::abbrevSize((*first)->getLength()).c_str(),
+              util::uitos((*first)->getLength(), true).c_str());
   }
 }
 

+ 7 - 2
test/UtilTest.cc

@@ -13,6 +13,8 @@
 #include "FileEntry.h"
 #include "File.h"
 #include "array_fun.h"
+#include "BufferedFile.h"
+#include "TestUtil.h"
 
 namespace aria2 {
 
@@ -589,7 +591,10 @@ void UtilTest::testToStream()
   std::deque<SharedHandle<FileEntry> > entries;
   entries.push_back(f1);
   entries.push_back(f2);
-  util::toStream(entries.begin(), entries.end(), os);
+  std::string filename = A2_TEST_OUT_DIR"/aria2_UtilTest_testToStream";
+  BufferedFile fp(filename, BufferedFile::WRITE);
+  util::toStream(entries.begin(), entries.end(), fp);
+  fp.close();
   CPPUNIT_ASSERT_EQUAL(
                        std::string("Files:\n"
                                    "idx|path/length\n"
@@ -600,7 +605,7 @@ void UtilTest::testToStream()
                                    "  2|aria2.txt\n"
                                    "   |556B (556)\n"
                                    "---+---------------------------------------------------------------------------\n"),
-                       os.str());
+                       readFile(filename));
 }
 
 void UtilTest::testIsNumber()