Procházet zdrojové kódy

In MinGW32, open file with UNICODE filename and print them in ANSI.

Tatsuhiro Tsujikawa před 14 roky
rodič
revize
3879da592d

+ 11 - 10
src/AbstractDiskWriter.cc

@@ -95,15 +95,15 @@ void AbstractDiskWriter::openExistingFile(uint64_t totalLength)
   } else {
     flags |= O_RDWR;
   }
-
-  while((fd_ = open(filename_.c_str(), flags, OPEN_MODE)) == -1 &&
+  while((fd_ = a2open(utf8ToWChar(filename_).c_str(),
+                      flags, OPEN_MODE)) == -1 &&
         errno == EINTR);
   if(fd_ < 0) {
     int errNum = errno;
     throw DL_ABORT_EX3
       (errNum,
        fmt(EX_FILE_OPEN,
-           filename_.c_str(),
+           utf8ToNative(filename_).c_str(),
            util::safeStrerror(errNum).c_str()),
        error_code::FILE_OPEN_ERROR);
   }
@@ -114,14 +114,15 @@ void AbstractDiskWriter::createFile(int addFlags)
   assert(!filename_.empty());
   util::mkdirs(File(filename_).getDirname());
 
-  while((fd_ = open(filename_.c_str(), O_CREAT|O_RDWR|O_TRUNC|O_BINARY|addFlags,
-                    OPEN_MODE)) == -1 && errno == EINTR);
+  while((fd_ = a2open(utf8ToWChar(filename_).c_str(),
+                      O_CREAT|O_RDWR|O_TRUNC|O_BINARY|addFlags,
+                      OPEN_MODE)) == -1 && errno == EINTR);
   if(fd_ < 0) {
     int errNum = errno;
     throw DL_ABORT_EX3
       (errNum,
        fmt(EX_FILE_OPEN,
-           filename_.c_str(),
+           utf8ToNative(filename_).c_str(),
            util::safeStrerror(errNum).c_str()),
        error_code::FILE_CREATE_ERROR);
   }  
@@ -153,7 +154,7 @@ void AbstractDiskWriter::seek(off_t offset)
   if(a2lseek(fd_, offset, SEEK_SET) == (off_t)-1) {
     int errNum = errno;
     throw DL_ABORT_EX2(fmt(EX_FILE_SEEK,
-                           filename_.c_str(),
+                           utf8ToNative(filename_).c_str(),
                            util::safeStrerror(errNum).c_str()),
                        error_code::FILE_IO_ERROR);
   }
@@ -170,14 +171,14 @@ void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, off_t
       throw DOWNLOAD_FAILURE_EXCEPTION3
         (errNum,
          fmt(EX_FILE_WRITE,
-             filename_.c_str(),
+             utf8ToNative(filename_).c_str(),
              util::safeStrerror(errNum).c_str()),
          error_code::NOT_ENOUGH_DISK_SPACE);
     } else {
       throw DL_ABORT_EX3
         (errNum,
          fmt(EX_FILE_WRITE,
-             filename_.c_str(),
+             utf8ToNative(filename_).c_str(),
              util::safeStrerror(errNum).c_str()),
          error_code::FILE_IO_ERROR);
     }
@@ -193,7 +194,7 @@ ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, off_t offs
     throw DL_ABORT_EX3
       (errNum,
        fmt(EX_FILE_READ,
-           filename_.c_str(),
+           utf8ToNative(filename_).c_str(),
            util::safeStrerror(errNum).c_str()),
        error_code::FILE_IO_ERROR);
   }

+ 1 - 1
src/BtDependency.cc

@@ -135,7 +135,7 @@ bool BtDependency::resolve()
           if(d == ctxFilesEnd) {
             throw DL_ABORT_EX
               (fmt("No entry %s in torrent file",
-                   (*s)->getOriginalName().c_str()));
+                   utf8ToNative((*s)->getOriginalName()).c_str()));
           }
           copyValues(*d, *s);
         }

+ 1 - 1
src/BtPostDownloadHandler.cc

@@ -69,7 +69,7 @@ void BtPostDownloadHandler::getNextRequestGroups
  RequestGroup* requestGroup)
 {
   A2_LOG_INFO(fmt("Generating RequestGroups for Torrent file %s",
-                  requestGroup->getFirstFilePath().c_str()));
+                  utf8ToNative(requestGroup->getFirstFilePath()).c_str()));
   std::string content;
   try {
     requestGroup->getPieceStorage()->getDiskAdaptor()->openExistingFile();

+ 6 - 3
src/CheckIntegrityCommand.cc

@@ -73,7 +73,8 @@ bool CheckIntegrityCommand::executeInternal()
     if(getRequestGroup()->downloadFinished()) {
       A2_LOG_NOTICE
         (fmt(MSG_VERIFICATION_SUCCESSFUL,
-             getRequestGroup()->getDownloadContext()->getBasePath().c_str()));
+             utf8ToNative(getRequestGroup()->getDownloadContext()
+                          ->getBasePath()).c_str()));
       std::vector<Command*>* commands = new std::vector<Command*>();
       auto_delete_container<std::vector<Command*> > commandsDel(commands);
       entry_->onDownloadFinished(*commands, getDownloadEngine());
@@ -82,7 +83,8 @@ bool CheckIntegrityCommand::executeInternal()
     } else {
       A2_LOG_ERROR
         (fmt(MSG_VERIFICATION_FAILED,
-             getRequestGroup()->getDownloadContext()->getBasePath().c_str()));
+             utf8ToNative(getRequestGroup()->getDownloadContext()
+                          ->getBasePath()).c_str()));
       std::vector<Command*>* commands = new std::vector<Command*>();
       auto_delete_container<std::vector<Command*> > commandsDel(commands);
       entry_->onDownloadIncomplete(*commands, getDownloadEngine());
@@ -106,7 +108,8 @@ bool CheckIntegrityCommand::handleException(Exception& e)
   A2_LOG_ERROR
     (fmt(MSG_DOWNLOAD_NOT_COMPLETE,
          getCuid(),
-         getRequestGroup()->getDownloadContext()->getBasePath().c_str()));
+         utf8ToNative(getRequestGroup()->getDownloadContext()
+                      ->getBasePath()).c_str()));
   return true;
 }
 

+ 23 - 12
src/File.cc

@@ -72,7 +72,7 @@ File& File::operator=(const File& c)
 }
 
 int File::fillStat(a2_struct_stat& fstat) {
-  return a2stat(name_.c_str(), &fstat);
+  return a2stat(utf8ToWChar(name_).c_str(), &fstat);
 }
 
 bool File::exists() {
@@ -98,9 +98,9 @@ bool File::isDir() {
 
 bool File::remove() {
   if(isFile()) {
-    return unlink(name_.c_str()) == 0;
+    return a2unlink(utf8ToWChar(name_).c_str()) == 0;
   } else if(isDir()) {
-    return rmdir(name_.c_str()) == 0;
+    return a2rmdir(utf8ToWChar(name_).c_str()) == 0;
   } else {
     return false;
   }
@@ -148,13 +148,14 @@ bool File::mkdirs() {
     }
 #endif // __MINGW32__
     std::string dir = std::string(begin, j);
-    A2_LOG_DEBUG(fmt("Making directory %s", dir.c_str()));
+    A2_LOG_DEBUG(fmt("Making directory %s", utf8ToNative(dir).c_str()));
     if(File(dir).isDir()) {
-      A2_LOG_DEBUG(fmt("%s exists and is a directory.", dir.c_str()));
+      A2_LOG_DEBUG(fmt("%s exists and is a directory.",
+                       utf8ToNative(dir).c_str()));
       continue;
     }
-    if(a2mkdir(dir.c_str(), DIR_OPEN_MODE) == -1) {
-      A2_LOG_DEBUG(fmt("Failed to create %s", dir.c_str()));
+    if(a2mkdir(utf8ToWChar(dir).c_str(), DIR_OPEN_MODE) == -1) {
+      A2_LOG_DEBUG(fmt("Failed to create %s", utf8ToNative(dir).c_str()));
       return false;
     }
   }
@@ -205,13 +206,13 @@ bool File::renameTo(const std::string& dest)
 {
 #ifdef __MINGW32__
   /* MinGW's rename() doesn't delete an existing destination */
-  if (_access(dest.c_str(), 0) == 0) {
-    if (_unlink(dest.c_str()) != 0) {
+  if (_waccess(utf8ToWChar(dest).c_str(), 0) == 0) {
+    if (a2unlink(utf8ToWChar(dest).c_str()) != 0) {
       return false;
     }
   }
 #endif // __MINGW32__
-  if(rename(name_.c_str(), dest.c_str()) == 0) {
+  if(a2rename(utf8ToWChar(name_).c_str(), utf8ToWChar(dest).c_str()) == 0) {
     name_ = dest;
     return true;
   } else {
@@ -221,10 +222,10 @@ bool File::renameTo(const std::string& dest)
 
 bool File::utime(const Time& actime, const Time& modtime) const
 {
-  struct utimbuf ub;
+  a2utimbuf ub;
   ub.actime = actime.getTime();
   ub.modtime = modtime.getTime();
-  return ::utime(name_.c_str(), &ub) == 0;
+  return a2utime(utf8ToWChar(name_).c_str(), &ub) == 0;
 }
 
 Time File::getModifiedTime()
@@ -238,6 +239,15 @@ Time File::getModifiedTime()
 
 std::string File::getCurrentDir()
 {
+#ifdef __MINGW32__
+  const size_t buflen = 2048;
+  wchar_t buf[buflen];
+  if(_wgetcwd(buf, buflen)) {
+    return wCharToUtf8(buf);
+  } else {
+    return A2STR::DOT_C;
+  }
+#else // !__MINGW32__
   const size_t buflen = 2048;
   char buf[buflen];
   if(getcwd(buf, buflen)) {
@@ -245,6 +255,7 @@ std::string File::getCurrentDir()
   } else {
     return A2STR::DOT_C;
   }
+#endif // !__MINGW32__
 }
 
 } // namespace aria2

+ 2 - 1
src/FileAllocationCommand.cc

@@ -96,7 +96,8 @@ bool FileAllocationCommand::handleException(Exception& e)
   A2_LOG_ERROR
     (fmt(MSG_DOWNLOAD_NOT_COMPLETE,
          getCuid(),
-         getRequestGroup()->getDownloadContext()->getBasePath().c_str()));
+         utf8ToNative(getRequestGroup()->getDownloadContext()
+                      ->getBasePath()).c_str()));
   return true;
 }
 

+ 1 - 1
src/FileEntry.cc

@@ -355,7 +355,7 @@ void FileEntry::removeURIWhoseHostnameIs(const std::string& hostname)
   }
   A2_LOG_DEBUG(fmt("Removed %lu duplicate hostname URIs for path=%s",
                    static_cast<unsigned long>(uris_.size()-newURIs.size()),
-                   getPath().c_str()));
+                   utf8ToNative(getPath()).c_str()));
   uris_.swap(newURIs);
 }
 

+ 3 - 2
src/FileEntry.h

@@ -49,6 +49,7 @@
 #include "error_code.h"
 #include "A2STR.h"
 #include "TimerA2.h"
+#include "util.h"
 
 namespace aria2 {
 
@@ -316,9 +317,9 @@ void writeFilePath
       }
     } else {
       if(memory) {
-        o << "[MEMORY]" << File(e->getPath()).getBasename();
+        o << "[MEMORY]" << utf8ToNative(File(e->getPath()).getBasename());
       } else {
-        o << e->getPath();
+        o << utf8ToNative(e->getPath());
       }
       size_t count = countRequestedFileEntry(first, last);
       if(count > 1) {

+ 5 - 4
src/FtpNegotiationCommand.cc

@@ -376,7 +376,7 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength)
      isSameFileBeingDownloaded(getRequestGroup())) {
     throw DOWNLOAD_FAILURE_EXCEPTION2
       (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
-           getRequestGroup()->getFirstFilePath().c_str()),
+           utf8ToNative(getRequestGroup()->getFirstFilePath()).c_str()),
        error_code::DUPLICATE_DOWNLOAD);
   }
   if(totalLength == 0) {
@@ -404,9 +404,10 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength)
       getPieceStorage()->markAllPiecesDone();
       getDownloadContext()->setChecksumVerified(true);
       sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
-      A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
-                        util::itos(getRequestGroup()->getGID()).c_str(),
-                        getRequestGroup()->getFirstFilePath().c_str()));
+      A2_LOG_NOTICE
+        (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+             util::itos(getRequestGroup()->getGID()).c_str(),
+             utf8ToNative(getRequestGroup()->getFirstFilePath()).c_str()));
       poolConnection();
 
       return false;

+ 9 - 7
src/HttpResponseCommand.cc

@@ -186,9 +186,10 @@ bool HttpResponseCommand::executeInternal()
     getPieceStorage()->markAllPiecesDone();
     // Just set checksum verification done.
     getDownloadContext()->setChecksumVerified(true);
-    A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
-                      util::itos(getRequestGroup()->getGID()).c_str(),
-                      getRequestGroup()->getFirstFilePath().c_str()));
+    A2_LOG_NOTICE
+      (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+           util::itos(getRequestGroup()->getGID()).c_str(),
+           utf8ToNative(getRequestGroup()->getFirstFilePath()).c_str()));
     poolConnection();
     getFileEntry()->poolRequest(getRequest());
     return true;
@@ -257,7 +258,7 @@ bool HttpResponseCommand::executeInternal()
        isSameFileBeingDownloaded(getRequestGroup())) {
       throw DOWNLOAD_FAILURE_EXCEPTION2
         (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
-             getRequestGroup()->getFirstFilePath().c_str()),
+             utf8ToNative(getRequestGroup()->getFirstFilePath()).c_str()),
          error_code::DUPLICATE_DOWNLOAD);
     }
     // update last modified time
@@ -438,9 +439,10 @@ bool HttpResponseCommand::handleOtherEncoding
     getRequestGroup()->initPieceStorage();
     getPieceStorage()->markAllPiecesDone();
     getDownloadContext()->setChecksumVerified(true);
-    A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
-                      util::itos(getRequestGroup()->getGID()).c_str(),
-                      getRequestGroup()->getFirstFilePath().c_str()));
+    A2_LOG_NOTICE
+      (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+           util::itos(getRequestGroup()->getGID()).c_str(),
+           utf8ToNative(getRequestGroup()->getFirstFilePath()).c_str()));
     poolConnection();
     return true;
   }

+ 1 - 1
src/IteratableChunkChecksumValidator.cc

@@ -143,7 +143,7 @@ std::string IteratableChunkChecksumValidator::digest(off_t offset, size_t length
                                                          curoffset);
     if(r == 0 || r < static_cast<size_t>(woffset)) {
       throw DL_ABORT_EX
-        (fmt(EX_FILE_READ, dctx_->getBasePath().c_str(),
+        (fmt(EX_FILE_READ, utf8ToNative(dctx_->getBasePath()).c_str(),
              "data is too short"));
     }
     size_t wlength;

+ 3 - 2
src/Metalink2RequestGroup.cc

@@ -233,7 +233,8 @@ Metalink2RequestGroup::createRequestGroup
     SharedHandle<DownloadContext> dctx;
     if(mes.size() == 1) {
       SharedHandle<MetalinkEntry> entry = mes[0];
-      A2_LOG_INFO(fmt(MSG_METALINK_QUEUEING, entry->getPath().c_str()));
+      A2_LOG_INFO(fmt(MSG_METALINK_QUEUEING,
+                      utf8ToNative(entry->getPath()).c_str()));
       entry->reorderResourcesByPriority();
       std::vector<std::string> uris;
       std::for_each(entry->resources.begin(), entry->resources.end(),
@@ -287,7 +288,7 @@ Metalink2RequestGroup::createRequestGroup
       for(std::vector<SharedHandle<MetalinkEntry> >::const_iterator i =
             mes.begin(), eoi = mes.end(); i != eoi; ++i) {
         A2_LOG_INFO(fmt("Metalink: Queueing %s for download as a member.",
-                        (*i)->getPath().c_str()));
+                        utf8ToNative((*i)->getPath()).c_str()));
         A2_LOG_DEBUG(fmt("originalName = %s", (*i)->metaurls[0]->name.c_str()));
         (*i)->reorderResourcesByPriority();
         std::vector<std::string> uris;

+ 1 - 1
src/MetalinkPostDownloadHandler.cc

@@ -97,7 +97,7 @@ void MetalinkPostDownloadHandler::getNextRequestGroups
  RequestGroup* requestGroup)
 {
   A2_LOG_DEBUG(fmt("Generating RequestGroups for Metalink file %s",
-                   requestGroup->getFirstFilePath().c_str()));
+                   utf8ToNative(requestGroup->getFirstFilePath()).c_str()));
   SharedHandle<DiskAdaptor> diskAdaptor =
     requestGroup->getPieceStorage()->getDiskAdaptor();
   try {

+ 10 - 8
src/MultiDiskAdaptor.cc

@@ -187,7 +187,7 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
           pieceLength_*pieceLength_;
         A2_LOG_DEBUG(fmt("Checking adjacent backward file to %s"
                          " whose lastPieceStartOffset+pieceLength_=%lld",
-                         fileEntry->getPath().c_str(),
+                         utf8ToNative(fileEntry->getPath()).c_str(),
                          static_cast<long long int>
                          (lastPieceStartOffset+pieceLength_)));
         ++itr;
@@ -196,14 +196,16 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
         for(; itr != eoi &&
               (!(*itr)->getFileEntry()->isRequested() ||
                (*itr)->getFileEntry()->getLength() == 0); ++itr) {
-          A2_LOG_DEBUG(fmt("file=%s, offset=%lld",
-                           (*itr)->getFileEntry()->getPath().c_str(),
-                           static_cast<long long int>
-                           ((*itr)->getFileEntry()->getOffset())));
+          A2_LOG_DEBUG
+            (fmt("file=%s, offset=%lld",
+                 utf8ToNative((*itr)->getFileEntry()->getPath()).c_str(),
+                 static_cast<long long int>
+                 ((*itr)->getFileEntry()->getOffset())));
           if((*itr)->getFileEntry()->getOffset() <
              static_cast<off_t>(lastPieceStartOffset+pieceLength_)) {
-            A2_LOG_DEBUG(fmt("%s needs diskwriter",
-                             (*itr)->getFileEntry()->getPath().c_str()));
+            A2_LOG_DEBUG
+              (fmt("%s needs diskwriter",
+                   utf8ToNative((*itr)->getFileEntry()->getPath()).c_str()));
             dwreq[(*itr)->getFileEntry()->getPath()] = true;
           } else {
             break;
@@ -224,7 +226,7 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
        dwreq.find((*i)->getFileEntry()->getPath()) != dwreq.end() ||
        (*i)->fileExists()) {
       A2_LOG_DEBUG(fmt("Creating DiskWriter for filename=%s",
-                       (*i)->getFilePath().c_str()));
+                       utf8ToNative((*i)->getFilePath()).c_str()));
       (*i)->setDiskWriter(dwFactory.newDiskWriter((*i)->getFilePath()));
       if(readOnly_) {
         (*i)->getDiskWriter()->enableReadOnly();

+ 10 - 0
src/Option.cc

@@ -122,4 +122,14 @@ std::map<std::string, std::string>::const_iterator Option::end() const
   return table_.end();
 }
 
+std::map<std::string, std::string>::iterator Option::begin()
+{
+  return table_.begin();
+}
+
+std::map<std::string, std::string>::iterator Option::end()
+{
+  return table_.end();
+}
+
 } // namespace aria2

+ 4 - 0
src/Option.h

@@ -68,6 +68,10 @@ public:
   std::map<std::string, std::string>::const_iterator begin() const;
 
   std::map<std::string, std::string>::const_iterator end() const;
+
+  std::map<std::string, std::string>::iterator begin();
+
+  std::map<std::string, std::string>::iterator end();
 };
 
 } // namespace aria2

+ 21 - 18
src/RequestGroup.cc

@@ -246,9 +246,10 @@ SharedHandle<CheckIntegrityEntry> RequestGroup::createCheckIntegrityEntry()
 #endif // ENABLE_MESSAGE_DIGEST
         {
           downloadContext_->setChecksumVerified(true);
-          A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
-                            util::itos(gid_).c_str(),
-                            downloadContext_->getBasePath().c_str()));
+          A2_LOG_NOTICE
+            (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+                 util::itos(gid_).c_str(),
+                 utf8ToNative(downloadContext_->getBasePath()).c_str()));
         }
     } else {
       checkEntry.reset(new StreamCheckIntegrityEntry(this));
@@ -267,9 +268,10 @@ SharedHandle<CheckIntegrityEntry> RequestGroup::createCheckIntegrityEntry()
 #endif // ENABLE_MESSAGE_DIGEST
       {
         downloadContext_->setChecksumVerified(true);
-        A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
-                          util::itos(gid_).c_str(),
-                          downloadContext_->getBasePath().c_str()));
+        A2_LOG_NOTICE
+          (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+               util::itos(gid_).c_str(),
+               utf8ToNative(downloadContext_->getBasePath()).c_str()));
       }
   } else {
     loadAndOpenFile(infoFile);
@@ -312,7 +314,7 @@ void RequestGroup::createInitialCommand
         if(e->getRequestGroupMan()->isSameFileBeingDownloaded(this)) {
           throw DOWNLOAD_FAILURE_EXCEPTION2
             (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
-                 downloadContext_->getBasePath().c_str()),
+                 utf8ToNative(downloadContext_->getBasePath()).c_str()),
              error_code::DUPLICATE_DOWNLOAD);
         }
         initPieceStorage();
@@ -419,7 +421,7 @@ void RequestGroup::createInitialCommand
             // TODO we need this->haltRequested = true?
             throw DOWNLOAD_FAILURE_EXCEPTION2
               (fmt(MSG_FILE_ALREADY_EXISTS,
-                   downloadContext_->getBasePath().c_str()),
+                   utf8ToNative(downloadContext_->getBasePath()).c_str()),
                error_code::FILE_ALREADY_EXISTS);
           } else {
             pieceStorage_->getDiskAdaptor()->openFile();
@@ -484,7 +486,7 @@ void RequestGroup::createInitialCommand
       if(e->getRequestGroupMan()->isSameFileBeingDownloaded(this)) {
         throw DOWNLOAD_FAILURE_EXCEPTION2
           (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
-               downloadContext_->getBasePath().c_str()),
+               utf8ToNative(downloadContext_->getBasePath()).c_str()),
            error_code::DUPLICATE_DOWNLOAD);
       }
       SharedHandle<BtProgressInfoFile> progressInfoFile
@@ -511,7 +513,7 @@ void RequestGroup::createInitialCommand
     if(e->getRequestGroupMan()->isSameFileBeingDownloaded(this)) {
       throw DOWNLOAD_FAILURE_EXCEPTION2
         (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
-             downloadContext_->getBasePath().c_str()),
+             utf8ToNative(downloadContext_->getBasePath()).c_str()),
          error_code::DUPLICATE_DOWNLOAD);
     }
     initPieceStorage();
@@ -535,7 +537,7 @@ void RequestGroup::createInitialCommand
           // TODO we need this->haltRequested = true?
           throw DOWNLOAD_FAILURE_EXCEPTION2
             (fmt(MSG_FILE_ALREADY_EXISTS,
-                 downloadContext_->getBasePath().c_str()),
+                 utf8ToNative(downloadContext_->getBasePath()).c_str()),
              error_code::FILE_ALREADY_EXISTS);
         } else {
           pieceStorage_->getDiskAdaptor()->openFile();
@@ -720,7 +722,7 @@ void RequestGroup::removeDefunctControlFile
     progressInfoFile->removeFile();
     A2_LOG_NOTICE(fmt(MSG_REMOVED_DEFUNCT_CONTROL_FILE,
                       progressInfoFile->getFilename().c_str(),
-                      downloadContext_->getBasePath().c_str()));
+                      utf8ToNative(downloadContext_->getBasePath()).c_str()));
   }
 }
 
@@ -769,17 +771,18 @@ void RequestGroup::shouldCancelDownloadForSafety()
   if(outfile.exists()) {
     if(option_->getAsBool(PREF_AUTO_FILE_RENAMING)) {
       if(tryAutoFileRenaming()) {
-        A2_LOG_NOTICE(fmt(MSG_FILE_RENAMED, getFirstFilePath().c_str()));
+        A2_LOG_NOTICE(fmt(MSG_FILE_RENAMED,
+                          utf8ToNative(getFirstFilePath()).c_str()));
       } else {
         throw DOWNLOAD_FAILURE_EXCEPTION2
           (fmt("File renaming failed: %s",
-               getFirstFilePath().c_str()),
+               utf8ToNative(getFirstFilePath()).c_str()),
            error_code::FILE_RENAMING_FAILED);
       }
     } else {
       throw DOWNLOAD_FAILURE_EXCEPTION2
         (fmt(MSG_FILE_ALREADY_EXISTS,
-             getFirstFilePath().c_str()),
+             utf8ToNative(getFirstFilePath()).c_str()),
          error_code::FILE_ALREADY_EXISTS);
     }
   }
@@ -1036,7 +1039,7 @@ void RequestGroup::releaseRuntimeResource(DownloadEngine* e)
 void RequestGroup::preDownloadProcessing()
 {
   A2_LOG_DEBUG(fmt("Finding PreDownloadHandler for path %s.",
-                   getFirstFilePath().c_str()));
+                   utf8ToNative(getFirstFilePath()).c_str()));
   try {
     for(std::vector<SharedHandle<PreDownloadHandler> >::const_iterator itr =
           preDownloadHandlers_.begin(), eoi = preDownloadHandlers_.end();
@@ -1058,7 +1061,7 @@ void RequestGroup::postDownloadProcessing
 (std::vector<SharedHandle<RequestGroup> >& groups)
 {
   A2_LOG_DEBUG(fmt("Finding PostDownloadHandler for path %s.",
-                   getFirstFilePath().c_str()));
+                   utf8ToNative(getFirstFilePath()).c_str()));
   try {
     for(std::vector<SharedHandle<PostDownloadHandler> >::const_iterator itr =
           postDownloadHandlers_.begin(), eoi = postDownloadHandlers_.end();
@@ -1200,7 +1203,7 @@ DownloadResultHandle RequestGroup::createDownloadResult() const
 void RequestGroup::reportDownloadFinished()
 {
   A2_LOG_NOTICE(fmt(MSG_FILE_DOWNLOAD_COMPLETED,
-                    downloadContext_->getBasePath().c_str()));
+                    utf8ToNative(downloadContext_->getBasePath()).c_str()));
   uriSelector_->resetCounters();
 #ifdef ENABLE_BITTORRENT
   if(downloadContext_->hasAttribute(bittorrent::BITTORRENT)) {

+ 2 - 1
src/RequestGroupMan.cc

@@ -344,7 +344,8 @@ public:
           A2_LOG_NOTICE
             (fmt("Download GID#%s not complete: %s",
                  util::itos(group->getGID()).c_str(),
-                 group->getDownloadContext()->getBasePath().c_str()));
+                 utf8ToNative(group->getDownloadContext()
+                              ->getBasePath()).c_str()));
           group->saveControlFile();
         }
       } catch(RecoverableException& ex) {

+ 1 - 1
src/SegmentMan.cc

@@ -471,7 +471,7 @@ size_t SegmentMan::countFreePieceFrom(size_t index) const
 void SegmentMan::ignoreSegmentFor(const SharedHandle<FileEntry>& fileEntry)
 {
   A2_LOG_DEBUG(fmt("ignoring segment for path=%s, offset=%s, length=%s",
-                   fileEntry->getPath().c_str(),
+                   utf8ToNative(fileEntry->getPath()).c_str(),
                    util::itos(fileEntry->getOffset()).c_str(),
                    util::uitos(fileEntry->getLength()).c_str()));
   ignoreBitfield_.addFilter(fileEntry->getOffset(), fileEntry->getLength());

+ 14 - 2
src/a2io.h

@@ -124,9 +124,15 @@
 #  undef stat
 # endif // stat
 # define a2_struct_stat struct _stati64
-# define a2stat(path, buf)  _stati64(path, buf)
+# define a2stat(path, buf) _wstati64(path, buf)
 # define a2tell(handle) _telli64(handle)
-# define a2mkdir(path, openMode) mkdir(path)
+# define a2mkdir(path, openMode) _wmkdir(path)
+# define a2utimbuf _utimbuf
+# define a2utime(path, times) _wutime(path, times)
+# define a2unlink(path) _wunlink(path)
+# define a2rmdir(path) _wrmdir(path)
+# define a2rename(src, dest) _wrename(src, dest)
+# define a2open(path, flags, mode) _wopen(path, flags, mode)
 #else // !__MINGW32__
 # define a2lseek(fd, offset, origin) lseek(fd, offset, origin)
 # define a2fseek(fp, offset, origin) fseek(fp, offset, origin)
@@ -135,6 +141,12 @@
 # define a2_struct_stat struct stat
 # define a2stat(path, buf) stat(path, buf)
 # define a2mkdir(path, openMode) mkdir(path, openMode)
+# define a2utimbuf utimbuf
+# define a2utime(path, times) ::utime(path, times)
+# define a2unlink(path) unlink(path)
+# define a2rmdir(path) rmdir(path)
+# define a2rename(src, dest) rename(src, dest)
+# define a2open(path, flags, mode) open(path, flags, mode)
 #endif // !__MINGW32__
 
 #define OPEN_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH

+ 8 - 0
src/option_processing.cc

@@ -170,6 +170,14 @@ void option_processing(Option& op, std::vector<std::string>& uris,
     cmdstream.seekg(0, std::ios::beg);
     // finaly let's parse and store command-iine options.
     oparser.parse(op, cmdstream);
+#ifdef __MINGW32__
+    for(std::map<std::string, std::string>::iterator i = op.begin();
+        i != op.end(); ++i) {
+      if(!util::isUtf8((*i).second)) {
+        (*i).second = nativeToUtf8((*i).second);
+      }
+    }
+#endif // __MINGW32__
   } catch(OptionHandlerException& e) {
     std::cerr << e.stackTrace() << "\n";
     SharedHandle<OptionHandler> h = oparser.findByName(e.getOptionName());

+ 96 - 4
src/util.cc

@@ -94,6 +94,101 @@
 
 namespace aria2 {
 
+#ifdef __MINGW32__
+namespace {
+int utf8ToWChar(wchar_t* out, size_t outLength, const std::string& src)
+{
+  return MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1,
+                             out, outLength);
+}
+} // namespace
+
+namespace {
+int ansiToWChar(wchar_t* out, size_t outLength, const std::string& src)
+{
+  return MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1,
+                             out, outLength);
+}
+} // namespace
+
+namespace {
+int wCharToUtf8(char* out, size_t outLength, const std::wstring& src)
+{
+  return WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1,
+                             out, outLength, 0, 0);
+}
+} // namespace
+
+namespace {
+int wCharToAnsi(char* out, size_t outLength, const std::wstring& src)
+{
+  return WideCharToMultiByte(CP_ACP, 0, src.c_str(), -1,
+                             out, outLength, 0, 0);
+}
+} // namespace
+
+std::wstring utf8ToWChar(const std::string& src)
+{
+  int len = utf8ToWChar(0, 0, src);
+  if(len == 0) {
+    abort();
+  }
+  wchar_t* buf = new wchar_t[len];
+  len = utf8ToWChar(buf, len, src);
+  if(len == 0) {
+    abort();
+  } else {
+    return buf;
+  }
+}
+
+std::string utf8ToNative(const std::string& src)
+{
+  std::wstring wsrc = utf8ToWChar(src);
+  int len = wCharToAnsi(0, 0, wsrc);
+  if(len == 0) {
+    abort();
+  }
+  char* buf = new char[len];
+  len = wCharToAnsi(buf, len, wsrc);
+  if(len == 0) {
+    abort();
+  } else {
+    return buf;
+  }
+}
+
+std::string wCharToUtf8(const std::wstring& wsrc)
+{
+  int len = wCharToUtf8(0, 0, wsrc);
+  if(len == 0) {
+    abort();
+  }
+  char* buf = new char[len];
+  len = wCharToUtf8(buf, len, wsrc);
+  if(len == 0) {
+    abort();
+  } else {
+    return buf;
+  }
+}
+
+std::string nativeToUtf8(const std::string& src)
+{
+  int len = ansiToWChar(0, 0, src);
+  if(len == 0) {
+    abort();
+  }
+  wchar_t* buf = new wchar_t[len];
+  len = ansiToWChar(buf, len, src);
+  if(len == 0) {
+    abort();
+  } else {
+    return wCharToUtf8(buf);
+  }
+}
+#endif // __MINGW32__
+
 namespace util {
 
 const std::string DEFAULT_STRIP_CHARSET("\r\n\t ");
@@ -1171,7 +1266,7 @@ void mkdirs(const std::string& dirpath)
     if(!dir.isDir()) {
       throw DL_ABORT_EX3
         (errNum,
-         fmt(EX_MAKE_DIR, dir.getPath().c_str(),
+         fmt(EX_MAKE_DIR, utf8ToNative(dir.getPath()).c_str(),
              safeStrerror(errNum).c_str()),
          error_code::DIR_CREATE_ERROR);
     }
@@ -1412,9 +1507,6 @@ std::string escapePath(const std::string& s)
        || std::find(vbegin(WIN_INVALID_PATH_CHARS),
                     vend(WIN_INVALID_PATH_CHARS),
                     c) != vend(WIN_INVALID_PATH_CHARS)
-       // Since Windows does not understand UTF-8 correctly, we
-       // percent-encode character other than ASCII.
-       || c > 0x7fu
 #endif // __MINGW32__
        ){
       d += fmt("%%%02X", c);

+ 15 - 1
src/util.h

@@ -86,6 +86,19 @@ inline uint64_t ntoh64(uint64_t x) { return byteswap64(x); }
 inline uint64_t hton64(uint64_t x) { return byteswap64(x); }
 #endif // !WORDS_BIGENDIAN
 
+#ifdef __MINGW32__
+std::wstring utf8ToWChar(const std::string& src);
+
+std::string utf8ToNative(const std::string& src);
+
+std::string wCharToUtf8(const std::wstring& wsrc);
+
+std::string nativeToUtf8(const std::string& src);
+#else // !__MINGW32__
+# define utf8ToWChar(src) src
+# define utf8ToNative(src) src
+#endif // !__MINGW32__
+
 namespace util {
 
 void divide
@@ -255,7 +268,8 @@ void toStream
   os << "===+===========================================================================" << "\n";
   int32_t count = 1;
   for(; first != last; ++first, ++count) {
-    os << std::setw(3) << count << "|" << (*first)->getPath() << "\n";
+    os << std::setw(3) << count << "|"
+       << utf8ToNative((*first)->getPath()) << "\n";
     os << "   |" << util::abbrevSize((*first)->getLength()) << "B ("
        << util::uitos((*first)->getLength(), true) << ")\n";
     os << "---+---------------------------------------------------------------------------" << "\n";

+ 3 - 1
test/FileTest.cc

@@ -7,6 +7,8 @@
 #include <fstream>
 #include <cppunit/extensions/HelperMacros.h>
 
+#include "util.h"
+
 namespace aria2 {
 
 class FileTest:public CppUnit::TestFixture {
@@ -241,7 +243,7 @@ void FileTest::testUtime()
   CPPUNIT_ASSERT(f.utime(Time(atime), Time(mtime)));
   
   a2_struct_stat buf;
-  CPPUNIT_ASSERT(0 == a2stat(f.getPath().c_str(), &buf));
+  CPPUNIT_ASSERT(0 == a2stat(utf8ToWChar(f.getPath()).c_str(), &buf));
   CPPUNIT_ASSERT_EQUAL((time_t)atime, (time_t)buf.st_atime);
   CPPUNIT_ASSERT_EQUAL((time_t)mtime, f.getModifiedTime().getTime());