///###////////////////////////////////////////////////////////////////////////
//
// Burton Computer Corporation
// http://www.burton-computer.com
// http://www.cooldevtools.com
// $Id: File.cc 272 2007-01-06 19:37:27Z brian $
//
// Copyright (C) 2007 Burton Computer Corporation
// ALL RIGHTS RESERVED
//
// This program is open source software; you can redistribute it
// and/or modify it under the terms of the Q Public License (QPL)
// version 1.0. Use of this software in whole or in part, including
// linking it (modified or unmodified) into other programs is
// subject to the terms of the QPL.
//
// 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
// Q Public License for more details.
//
// You should have received a copy of the Q Public License
// along with this program; see the file LICENSE.txt.  If not, visit
// the Burton Computer Corporation or CoolDevTools web site
// QPL pages at:
//
//    http://www.burton-computer.com/qpl.html
//    http://www.cooldevtools.com/qpl.html
//

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <stdexcept>
#include "File.h"

const char SEPARATOR_CHAR('/');
const string SEPARATOR("/");
const string SUFFIX_START(".");

const string File::DOT(".");
const string File::DOT_DOT("..");

File::File()
{
}

File::File(const string &path)
{
  setPath(path);
}

File::File(const File &other)
  : m_path(other.m_path)
{
}

File::File(const File &other,
           const string &name)
  : m_path(other.m_path)
{
  addComponent(name);
}

File::~File()
{
}

File &File::operator=(const File &other)
{
  m_path = other.m_path;
  return *this;
}

void File::addComponent(const string &name)
{
  if (m_path != SEPARATOR) {
    m_path += SEPARATOR;
  }
  string::size_type last = name.length() - 1;
  if (last >= 1 && name[last] == SEPARATOR_CHAR) {
    m_path.append(name, 0, last);
  } else {
    m_path += name;
  }
}

void File::setPath(const string &path)
{
  string::size_type last = path.length() - 1;
  if (last >= 1 && path[last] == SEPARATOR_CHAR) {
    m_path.assign(path, 0, last);
  } else {
    m_path = path;
  }
}

bool File::exists() const
{
  struct stat buf;
  return stat(m_path.c_str(), &buf) == 0;
}

bool File::isFile() const
{
  struct stat buf;
  return stat(m_path.c_str(), &buf) == 0 && S_ISREG(buf.st_mode);
}

bool File::isDirectory() const
{
  struct stat buf;
  return stat(m_path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode);
}

bool File::isHidden() const
{
  return starts_with(getName(), DOT);
}

bool File::isSelfOrParent() const
{
  string name = getName();
  return name == DOT || name == DOT_DOT;
}

bool File::hasChild(const string &path) const
{
  return File(*this, path).exists();
}

unsigned long File::getSize() const
{
  struct stat buf;
  return stat(m_path.c_str(), &buf) == 0 ? buf.st_size : 0;
}

File File::parent() const
{
  string::size_type pos = m_path.rfind(SEPARATOR);
  if (pos == string::npos) {
    return isDirectory() ? File(DOT_DOT) : File(DOT);
  } else {
    return File(m_path.substr(0, pos));
  }
}

string File::getName() const
{
  string::size_type pos = m_path.rfind(SEPARATOR);
  if (pos == string::npos) {
    return m_path;
  } else {
    return m_path.substr(pos + 1);
  }
}

void File::makeDirectory(int mode) const
{
  if (mkdir(m_path.c_str(), (mode_t)mode) != 0) {
    throw runtime_error(strerror(errno));
  }
}

void File::rename(const string &new_path)
{
  if (::rename(m_path.c_str(), new_path.c_str()) != 0) {
    throw runtime_error(strerror(errno));
  }
  m_path = new_path;
}

void File::remove()
{
  if (unlink(m_path.c_str()) != 0) {
    throw runtime_error(strerror(errno));
  }
}

File File::getHomeDir()
{
  char *home = getenv("HOME");
  if (home == NULL) {
    return File();
  } else {
    return File(home);
  }
}

bool File::hasSuffix(const string &suffix) const
{
  string::size_type pos = m_path.rfind(SUFFIX_START);
  return (pos != string::npos) && (strcmp(m_path.c_str() + pos + 1, suffix.c_str()) == 0);
}

void File::setSuffix(const string &suffix)
{
  string::size_type pos = m_path.rfind(SUFFIX_START);
  if (pos != string::npos && m_path.find(SEPARATOR, pos + 1) == string::npos) {
    m_path.erase(pos, string::npos);
  }
  m_path += SUFFIX_START;
  m_path += suffix;
}

bool operator<(const File &a, const File &b)
{
  return a.getPath() < b.getPath();
}

#ifdef HAVE_DIRENT_H
struct DirHelper
{
  DirHelper(const char *path) : dir(opendir(path)) {}
  ~DirHelper() { if (dir) closedir(dir); }

  DIR *dir;
};
#endif

void File::children(vector<File> &files)
{
#ifdef HAVE_DIRENT_H
  DirHelper d(m_path.c_str());
  if (d.dir) {
    for (struct dirent *ent = readdir(d.dir); ent ; ent = readdir(d.dir)) {
      files.push_back(File(*this, ent->d_name));
    }
  }
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1