/* Directory entry code for Window platforms.
Copyright (C) 1996-2013 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make 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 3 of the License, or (at your option) any later
version.

GNU Make 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, see <http://www.gnu.org/licenses/>.  */


#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "dirent.h"


DIR*
opendir(const char* pDirName)
{
	struct stat sb;
	DIR*	pDir;
	char*	pEndDirName;
	int	nBufferLen;

	/* sanity checks */
	if (!pDirName) {
		errno = EINVAL;
		return NULL;
	}
	if (stat(pDirName, &sb) != 0) {
		errno = ENOENT;
		return NULL;
	}
	if ((sb.st_mode & S_IFMT) != S_IFDIR) {
		errno = ENOTDIR;
		return NULL;
	}

	/* allocate a DIR structure to return */
	pDir = (DIR *) malloc(sizeof (DIR));

	if (!pDir)
		return NULL;

	/* input directory name length */
	nBufferLen = strlen(pDirName);

	/* copy input directory name to DIR buffer */
	strcpy(pDir->dir_pDirectoryName, pDirName);

	/* point to end of the copied directory name */
	pEndDirName = &pDir->dir_pDirectoryName[nBufferLen - 1];

	/* if directory name did not end in '/' or '\', add '/' */
	if ((*pEndDirName != '/') && (*pEndDirName != '\\')) {
		pEndDirName++;
		*pEndDirName = '/';
	}

	/* now append the wildcard character to the buffer */
	pEndDirName++;
	*pEndDirName = '*';
	pEndDirName++;
	*pEndDirName = '\0';

	/* other values defaulted */
	pDir->dir_nNumFiles = 0;
	pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
	pDir->dir_ulCookie = __DIRENT_COOKIE;

	return pDir;
}

void
closedir(DIR *pDir)
{
	/* got a valid pointer? */
	if (!pDir) {
		errno = EINVAL;
		return;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return;
	}

	/* close the WINDOWS32 directory handle */
	if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
		FindClose(pDir->dir_hDirHandle);

	free(pDir);

	return;
}

struct dirent *
readdir(DIR* pDir)
{
	WIN32_FIND_DATA wfdFindData;

	if (!pDir) {
		errno = EINVAL;
		return NULL;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return NULL;
	}

	if (pDir->dir_nNumFiles == 0) {
		pDir->dir_hDirHandle = FindFirstFile(pDir->dir_pDirectoryName, &wfdFindData);
		if (pDir->dir_hDirHandle == INVALID_HANDLE_VALUE)
			return NULL;
	} else if (!FindNextFile(pDir->dir_hDirHandle, &wfdFindData))
			return NULL;

	/* bump count for next call to readdir() or telldir() */
	pDir->dir_nNumFiles++;

	/* fill in struct dirent values */
	pDir->dir_sdReturn.d_ino = (ino_t)-1;
	strcpy(pDir->dir_sdReturn.d_name, wfdFindData.cFileName);

	return &pDir->dir_sdReturn;
}

void
rewinddir(DIR* pDir)
{
	if (!pDir) {
		errno = EINVAL;
		return;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return;
	}

	/* close the WINDOWS32 directory handle */
	if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
		if (!FindClose(pDir->dir_hDirHandle))
			errno = EBADF;

	/* reset members which control readdir() */
	pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
	pDir->dir_nNumFiles = 0;

	return;
}

int
telldir(DIR* pDir)
{
	if (!pDir) {
		errno = EINVAL;
		return -1;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return -1;
	}

	/* return number of times readdir() called */
	return pDir->dir_nNumFiles;
}

void
seekdir(DIR* pDir, long nPosition)
{
	if (!pDir)
		return;

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE)
		return;

	/* go back to beginning of directory */
	rewinddir(pDir);

	/* loop until we have found position we care about */
	for (--nPosition; nPosition && readdir(pDir); nPosition--);

	/* flag invalid nPosition value */
	if (nPosition)
		errno = EINVAL;

	return;
}
