/*
 * Copyright 2002-2009 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Tyler Dauwalder
 *		Ingo Weinhold, ingo_weinhold@gmx.de
 */


#include <new>
#include <string.h>

#include <SymLink.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>

#include <syscalls.h>

#include "storage_support.h"


using namespace std;


// Creates an uninitialized BSymLink object.
BSymLink::BSymLink()
{
}


// Creates a copy of the supplied BSymLink object.
BSymLink::BSymLink(const BSymLink& other)
	:
	BNode(other)
{
}


// Creates a BSymLink object and initializes it to the symbolic link referred
// to by the supplied entry_ref.
BSymLink::BSymLink(const entry_ref* ref)
	:
	BNode(ref)
{
}


// Creates a BSymLink object and initializes it to the symbolic link referred
// to by the supplied BEntry.
BSymLink::BSymLink(const BEntry* entry)
		: BNode(entry)
{
}


// Creates a BSymLink object and initializes it to the symbolic link referred
// to by the supplied path name.
BSymLink::BSymLink(const char* path)
	:
	BNode(path)
{
}


// Creates a BSymLink object and initializes it to the symbolic link referred
// to by the supplied path name relative to the specified BDirectory.
BSymLink::BSymLink(const BDirectory* dir, const char* path)
	:
	BNode(dir, path)
{
}


// Destroys the object and frees all allocated resources.
BSymLink::~BSymLink()
{
}


// Reads the contents of the symbolic link into a buffer.
ssize_t
BSymLink::ReadLink(char* buffer, size_t size)
{
	if (buffer == NULL)
		return B_BAD_VALUE;

	if (InitCheck() != B_OK)
		return B_FILE_ERROR;

	size_t linkLen = size;
	status_t result = _kern_read_link(get_fd(), NULL, buffer, &linkLen);
	if (result < B_OK)
		return result;

	// null-terminate
	if (linkLen >= size)
		return B_BUFFER_OVERFLOW;

	buffer[linkLen] = '\0';

	return linkLen;
}


// Combines a directory path and the contents of this symbolic link to form an
// absolute path.
ssize_t
BSymLink::MakeLinkedPath(const char* dirPath, BPath* path)
{
	// BeOS seems to convert the dirPath to a BDirectory, which causes links
	// to be resolved. This means that the dirPath must exist!
	if (dirPath == NULL || path == NULL)
		return B_BAD_VALUE;

	BDirectory dir(dirPath);
	ssize_t result = dir.InitCheck();
	if (result == B_OK)
		result = MakeLinkedPath(&dir, path);

	return result;
}


// Combines a directory path and the contents of this symbolic link to form an
// absolute path.
ssize_t
BSymLink::MakeLinkedPath(const BDirectory* dir, BPath* path)
{
	if (dir == NULL || path == NULL)
		return B_BAD_VALUE;

	char contents[B_PATH_NAME_LENGTH];
	ssize_t result = ReadLink(contents, sizeof(contents));
	if (result >= 0) {
		if (BPrivate::Storage::is_absolute_path(contents))
			result = path->SetTo(contents);
		else
			result = path->SetTo(dir, contents);

		if (result == B_OK)
			result = strlen(path->Path());
	}

	return result;
}


// Returns whether or not the object refers to an absolute path.
bool
BSymLink::IsAbsolute()
{
	char contents[B_PATH_NAME_LENGTH];
	bool result = (ReadLink(contents, sizeof(contents)) >= 0);
	if (result)
		result = BPrivate::Storage::is_absolute_path(contents);

	return result;
}


void BSymLink::_MissingSymLink1() {}
void BSymLink::_MissingSymLink2() {}
void BSymLink::_MissingSymLink3() {}
void BSymLink::_MissingSymLink4() {}
void BSymLink::_MissingSymLink5() {}
void BSymLink::_MissingSymLink6() {}


/*!	Returns the file descriptor of the BSymLink.

	This method should be used instead of accessing the private \c fFd member
	of the BNode directly.

	\return The object's file descriptor, or -1 if not properly initialized.
*/
int
BSymLink::get_fd() const
{
	return fFd;
}
