/* Emulate readlink on win32 - finds real name (i.e. correct case) of
   a file. UNC servers and shares are lower-cased. Directories must be
   given without trailing '/'. One day, this could read Win2K's
   reparse points. */

#include "stdafx.h"
#include <assert.h>

#define IS_DIRECTORY_SEP(X) (X == '\\')
#define IS_DEVICE_SEP(ch) ((ch) == ':')

#define ABS_LENGTH(name) (win32_abs_start (name))
static int win32_abs_start (const char * name);

int readlink (const char * name, char * buf, int size)
{
	WIN32_FIND_DATA find_data;
	HANDLE dir_handle = NULL;
	int len = 0;
	int err = 0;
	const char* lastname;
	int count = 0;
	const char* tmp;
	char* res = NULL;

	assert (*name);

	/* Sort of check we have a valid filename. */
	if (strpbrk (name, "*?|<>\"") || strlen (name) >= MAX_PATH)
	{
		errno = EIO;
		return -1;
	}
  
	/* Find start of filename */
	lastname = name + strlen (name);
	while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
		--lastname;

	/* Count slashes in unc path */
	if (ABS_LENGTH (name) == 2)
		for (tmp = name; *tmp; tmp++)
			if (IS_DIRECTORY_SEP (*tmp))
				count++;

	if (count >= 2 && count < 4)
	{
		/* UNC server or share name: just copy lowercased name. */
		res = find_data.cFileName;
		for (tmp = lastname; *tmp; tmp++)
			*res++ = tolower (*tmp);
		*res = '\0';
	}
	else
		dir_handle = FindFirstFile (name, &find_data);

	if (res || dir_handle != INVALID_HANDLE_VALUE)
	{
		if ((len = strlen (find_data.cFileName)) < size)
		{
			if (strcmp (lastname, find_data.cFileName) == 0)
				/* Signal that the name is already OK. */
				err = EINVAL;
			else
				memcpy (buf, find_data.cFileName, len + 1);
		}
		else
			err = ENAMETOOLONG;
		if (!res) FindClose (dir_handle);
	}
	else
		err = ENOENT;

	errno = err;
	return err ? -1 : len;
}

/* Length of start of absolute filename. */
static int win32_abs_start (const char * name)
{
	if (isalpha (*name) && IS_DEVICE_SEP (name[1])
	   && IS_DIRECTORY_SEP (name[2]))
		return 3;
	else if (IS_DIRECTORY_SEP (*name))
		return IS_DIRECTORY_SEP (name[1]) ? 2 : 1;
	else 
		return 0;
}
