//===- ToolOutputFile.cpp -------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/Support/ToolOutputFile.h>

#include <mcld/Support/Path.h>
#include <mcld/Support/FileHandle.h>
#include <mcld/Support/MemoryArea.h>

#include <mcld/Support/SystemUtils.h>
#include <mcld/Support/MsgHandling.h>

#include <llvm/Support/FileUtilities.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/FormattedStream.h>

using namespace mcld;

//===----------------------------------------------------------------------===//
// CleanupInstaller
//===----------------------------------------------------------------------===//
ToolOutputFile::CleanupInstaller::CleanupInstaller(const sys::fs::Path& pPath)
  : Keep(false), m_Path(pPath) {
  // Arrange for the file to be deleted if the process is killed.
  if ("-" != m_Path.native())
    llvm::sys::RemoveFileOnSignal(m_Path.native());
}

ToolOutputFile::CleanupInstaller::~CleanupInstaller()
{
  // Delete the file if the client hasn't told us not to.
  // FIXME: In Windows, some path in CJK characters can not be removed by LLVM
  // llvm::sys::Path
  if (!Keep && "_" != m_Path.native()) {
    bool Existed = false;
    llvm::sys::fs::remove(m_Path.native(), Existed);
  }

  // Ok, the file is successfully written and closed, or deleted. There's no
  // further need to clean it up on signals.
  if ("_" != m_Path.native())
    llvm::sys::DontRemoveFileOnSignal(m_Path.native());
}

//===----------------------------------------------------------------------===//
// ToolOutputFile
//===----------------------------------------------------------------------===//
ToolOutputFile::ToolOutputFile(const sys::fs::Path& pPath,
                               FileHandle::OpenMode pMode,
                               FileHandle::Permission pPermission)
  : m_Installer(pPath),
    m_pFdOstream(NULL),
    m_pFormattedOstream(NULL) {

  if (!m_FileHandle.open(pPath, pMode, pPermission)) {
    // If open fails, no clean-up is needed.
    m_Installer.Keep = true;
    fatal(diag::err_cannot_open_output_file)
      << pPath
      << sys::strerror(m_FileHandle.error());
    return;
  }
}

ToolOutputFile::~ToolOutputFile()
{
  if (m_pFormattedOstream != NULL)
    delete m_pFormattedOstream;
  if (m_pFdOstream != NULL)
    delete m_pFdOstream;
}

void ToolOutputFile::keep()
{
  m_Installer.Keep = true;
}

/// os - Return the containeed raw_fd_ostream.
/// Since os is rarely used, we lazily initialize it.
llvm::raw_fd_ostream& ToolOutputFile::os()
{
  if (m_pFdOstream == NULL) {
    assert(m_FileHandle.isOpened() &&
           m_FileHandle.isGood() &&
           m_FileHandle.isWritable());
    m_pFdOstream = new llvm::raw_fd_ostream(m_FileHandle.handler(), false);
  }
  return *m_pFdOstream;
}

/// formatted_os - Return the contained formatted_raw_ostream
llvm::formatted_raw_ostream& ToolOutputFile::formatted_os()
{
  if (m_pFormattedOstream == NULL) {
    m_pFormattedOstream = new llvm::formatted_raw_ostream(os());
  }
  return *m_pFormattedOstream;
}