
// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//
// 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., 59 Temple Place, Suite 330, Boston,
// MA  02111-1307  USA

#include "ACModelFile.h"

#include <algorithm>
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#ifdef _MSC_VER
# include <direct.h>
# include <string>
#else
# include <unistd.h>
# include <dirent.h>
#endif // _MSC_VER

#ifdef WIN32
# include <io.h>
# include <windows.h>
#endif // WIN32

ACModelFile::ACModelFile(const std::string &filename) : filename_(filename), filename_lock(), fd_(-1), fd_lock(-1) {
#if defined(WIN32)
  std::replace(filename_.begin(), filename_.end(), '/', '\\');
#endif
  filename_lock = filename;
  filename_lock.append(".lock");
}

bool ACModelFile::lock() {
  assert(fd_lock != -1);
#if defined(__GLIBC__)
  flock lock;
  memset(&lock, 0, sizeof lock);
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  if (fcntl(fd_lock, F_SETLKW, &lock) == -1)
    return false;
#endif 
  return true;
}

bool ACModelFile::is_empty() const {
  struct stat s;
  int r = fstat(fd_, &s);
  return r != -1 && s.st_size == 0;
}

// Create and lock the repository file if it does not exist.
// If it exists, open it.
bool ACModelFile::open_or_create(int mode) {
  assert(fd_ == -1);
  int cflags = O_CREAT | O_RDWR | O_EXCL;
#if defined(WIN32)
  cflags |= O_BINARY;
#endif
  fd_ = ::open(filename_.c_str(), cflags, mode);

  if(fd_ == -1 && errno == EEXIST) {
    int oflags = O_RDWR;
#if defined(WIN32)
    oflags |= O_BINARY;
#endif
    fd_ = ::open(filename_.c_str(), oflags, mode);
  }

  fd_lock = ::open(filename_lock.c_str(), O_CREAT | O_RDWR, mode);
  return fd_ != -1 && fd_lock != -1 && lock();
}

// Open and lock the file for exclusive read-only access
bool ACModelFile::open() {
  assert(fd_ == -1);
  int flags = O_RDWR;
#if defined(WIN32)
  flags |= O_BINARY;
#endif
  fd_ = ::open(filename_.c_str(), flags);
  fd_lock = ::open(filename_lock.c_str(), O_CREAT | O_RDWR, 0600);
  return fd_ != -1 && lock();
}

// Unlock and close the file
bool ACModelFile::close() {
  assert(fd_ != -1 && fd_lock != -1);
  bool result = true;
#if defined(__GLIBC__)
  flock lock;
  memset(&lock, 0, sizeof lock);
  lock.l_type = F_UNLCK;
  lock.l_whence = SEEK_SET;
  if (fcntl(fd_lock, F_SETLKW, &lock) == -1) {
    result = false;
  }
  if (::close(fd_lock) == -1)
    result = false;
  //if (remove(filename_lock.c_str()) == -1)
  //  result = false;
#endif
  if (::close(fd_) == -1)
    result = false;
  fd_ = -1;
  fd_lock = -1;
  return result;
}

// Set the file pointer back to the start for another read operation
bool ACModelFile::seek_start() {
  assert(fd_ != -1);
  if (::lseek(fd_, 0, SEEK_SET) == -1)
    return false;
  return true;
}
