/**
 * @file file_manip.cpp
 * Useful file management helpers
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Philippe Elie
 * @author John Levon
 */

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
#include <limits.h>
#include <stdlib.h>

#include <cstdio>
#include <cerrno>
#include <iostream>
#include <fstream>
#include <vector>

#include "op_file.h"

#include "file_manip.h"
#include "string_manip.h"

using namespace std;


bool copy_file(string const & source, string const & destination)
{
	int retval;
	struct stat buf;
	if (stat(source.c_str(), &buf))
		return false;

	if (!op_file_readable(source))
		return false;

	ifstream in(source.c_str());
	if (!in)
		return false;

	mode_t mode = buf.st_mode & ~S_IFMT;
	if (!(mode & S_IWUSR))
		mode |= S_IWUSR;

	int fd = open(destination.c_str(), O_RDWR|O_CREAT, mode);
	if (fd < 0)
		return false;
	close(fd);


	// ignore error here: a simple user can copy a root.root 744 file
	// but can't chown the copied file to root.
	retval = chown(destination.c_str(), buf.st_uid, buf.st_gid);

	// a scope to ensure out is closed before changing is mtime/atime
	{
	ofstream out(destination.c_str(), ios::trunc);
	if (!out)
		return false;
	out << in.rdbuf();
	}

	struct utimbuf utim;
	utim.actime = buf.st_atime;
	utim.modtime = buf.st_mtime;
	if (utime(destination.c_str(), &utim))
		return false;

	return true;
}


bool is_directory(string const & dirname)
{
	struct stat st;
	return !stat(dirname.c_str(), &st) && S_ISDIR(st.st_mode);
}


bool is_files_identical(string const & file1, string const & file2)
{
	struct stat st1;
	struct stat st2;

	if (stat(file1.c_str(), &st1) == 0 && stat(file2.c_str(), &st2) == 0) {
		if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
			return true;
	}

	return false;
}


string const op_realpath(string const & name)
{
	static char tmp[PATH_MAX];
	if (!realpath(name.c_str(), tmp))
		return name;
	return string(tmp);
}


bool op_file_readable(string const & file)
{
	return op_file_readable(file.c_str());
}

static void get_pathname(const char * pathname, void * name_list)
{
	list<string> * file_list = (list<string> *)name_list;
	file_list->push_back(pathname);
}

bool create_file_list(list<string> & file_list, string const & base_dir,
		      string const & filter, bool recursive)
{
	return !get_matching_pathnames(&file_list, get_pathname,
				       base_dir.c_str(), filter.c_str(),
				       recursive ? MATCH_ANY_ENTRY_RECURSION :
				       NO_RECURSION) ? true : false;
 
}


/**
 * @param path_name the path where we remove trailing '/'
 *
 * erase all trailing '/' in path_name except if the last '/' is at pos 0
 */
static string erase_trailing_path_separator(string const & path_name)
{
	string result(path_name);

	while (result.length() > 1) {
		if (result[result.length() - 1] != '/')
			break;
		result.erase(result.length() - 1, 1);
	}

	return result;
}

string op_dirname(string const & file_name)
{
	string result = erase_trailing_path_separator(file_name);
	if (result.find_first_of('/') == string::npos)
		return "."; 	 
  	 
	// catch result == "/" 	 
	if (result.length() == 1) 	 
		return result;

	size_t pos = result.find_last_of('/'); 	 

	// "/usr" must return "/" 	 
	if (pos == 0) 	 
		pos = 1; 	 

	result.erase(pos, result.length() - pos); 	 

	// "////usr" must return "/"
	return erase_trailing_path_separator(result);
}


string op_basename(string const & path_name)
{
	string result = erase_trailing_path_separator(path_name);

	// catch result == "/"
	if (result.length() == 1)
		return result;

	return erase_to_last_of(result, '/');
}