<commit_before>/*
 * Copyright (C) 2015-2016 <NAME> <<EMAIL>>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 */

#include "pmem.h"
#include "pstring.h"
#include "pfile.h"
#include "pdir.h"
#include "plib-private.h"

#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#ifdef P_OS_SCO
#  include <limits.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>

#if defined(P_OS_SOLARIS) || defined(P_OS_QNX6) || defined(P_OS_UNIXWARE) || defined(P_OS_SCO) || \
    defined(P_OS_IRIX)
#  define P_DIR_NEED_BUF_ALLOC 1
#endif

#ifdef P_DIR_NEED_BUF_ALLOC
#  if defined(P_OS_SCO)
#    define P_DIR_NEED_SIMPLE_R 1
#  endif
#endif

struct _PDir {
	DIR *		dir;
	struct dirent	*dir_result;
	pchar		*path;
	pchar		*orig_path;
};

P_LIB_API PDir *
p_dir_new (const pchar	*path,
	   PError	**error)
{
	PDir	*ret;
	DIR	*dir;
	pchar	*pathp;

	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return NULL;
	}

	if ((dir = opendir (path)) == NULL) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call opendir() to open directory stream");
		return NULL;
	}

	if ((ret = p_malloc0 (sizeof (PDir))) == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for directory structure");
		closedir (dir);
		return NULL;
	}

	ret->dir	= dir;
	ret->path	= p_strdup (path);
	ret->orig_path	= p_strdup (path);

	pathp = ret->path + strlen (ret->path) - 1;

	if (*pathp == '/' || *pathp == '\\')
		*pathp = '\0';

	return ret;
}

P_LIB_API pboolean
p_dir_create (const pchar	*path,
	      pint		mode,
	      PError		**error)
{
	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return FALSE;
	}

	if (p_dir_is_exists (path))
		return TRUE;

	if (mkdir (path, mode) != 0) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call mkdir() to create directory");
		return FALSE;
	} else
		return TRUE;
}

P_LIB_API pboolean
p_dir_remove (const pchar	*path,
	      PError		**error)
{
	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return FALSE;
	}

	if (!p_dir_is_exists (path)) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NOT_EXISTS,
				     0,
				     "Specified directory doesn't exist");
		return FALSE;
	}

	if (rmdir (path) != 0) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call rmdir() to remove directory");
		return FALSE;
	} else
		return TRUE;
}

P_LIB_API pboolean
p_dir_is_exists (const pchar *path)
{
	struct stat sb;

	if (path == NULL)
		return FALSE;

	return (stat (path, &sb) == 0 && S_ISDIR (sb.st_mode)) ? TRUE : FALSE;
}

P_LIB_API pchar *
p_dir_get_path (const PDir *dir)
{
	if (dir == NULL)
		return NULL;

	return p_strdup (dir->orig_path);
}

P_LIB_API PDirEntry *
p_dir_get_next_entry (PDir	*dir,
		      PError	**error)
{
	PDirEntry	*ret;
#ifdef P_DIR_NEED_BUF_ALLOC
	struct dirent	*dirent_st;
#else
	struct dirent	dirent_st;
#endif
	struct stat	sb;
	pchar		*entry_path;
	pint		path_len;
#ifdef P_DIR_NEED_BUF_ALLOC
	pint		name_max;
#endif

	if (dir == NULL || dir->dir == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return NULL;
	}

#if defined(P_OS_SOLARIS)
	name_max = (pint) (FILENAME_MAX);
#elif defined(P_OS_SCO)
	name_max = (pint) pathconf (dir->orig_path, _PC_NAME_MAX);

	if (name_max == -1) {
		if (__p_error_get_last_error () == 0)
			name_max = _POSIX_PATH_MAX;
		else {
			p_error_set_error_p (error,
					     (pint) P_ERROR_IO_FAILED,
					     0,
					     "Failed to get NAME_MAX using pathconf()");
			return NULL;
		}
	}
#elif defined(P_OS_QNX6) || defined(P_OS_UNIXWARE) || defined(P_OS_IRIX)
	name_max = (pint) (NAME_MAX);
#endif

#ifdef P_DIR_NEED_BUF_ALLOC
	if ((dirent_st = p_malloc0 (sizeof (struct dirent) + name_max + 1)) == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for internal directory entry");
		return NULL;
	}

#  ifdef P_DIR_NEED_SIMPLE_R
	if ((dir->dir_result = readdir_r (dir->dir, dirent_st)) == NULL) {
		if (__p_error_get_last_error () != 0) {
			p_error_set_error_p (error,
					     (pint) __p_error_get_last_io (),
					     __p_error_get_last_error (),
					     "Failed to call readdir_r() to read directory stream");
			p_free (dirent_st);
			return NULL;
		}
	}
#  else<commit_msg>Use pathconf() on IRIX to obtain NAME_MAX value<commit_after>/*
 * Copyright (C) 2015-2016 <NAME> <<EMAIL>>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 */

#include "pmem.h"
#include "pstring.h"
#include "pfile.h"
#include "pdir.h"
#include "plib-private.h"

#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#ifdef P_OS_SCO
#  include <limits.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>

#if defined(P_OS_SOLARIS) || defined(P_OS_QNX6) || defined(P_OS_UNIXWARE) || defined(P_OS_SCO) || \
    defined(P_OS_IRIX)
#  define P_DIR_NEED_BUF_ALLOC 1
#endif

#ifdef P_DIR_NEED_BUF_ALLOC
#  if defined(P_OS_SCO)
#    define P_DIR_NEED_SIMPLE_R 1
#  endif
#endif

struct _PDir {
	DIR *		dir;
	struct dirent	*dir_result;
	pchar		*path;
	pchar		*orig_path;
};

P_LIB_API PDir *
p_dir_new (const pchar	*path,
	   PError	**error)
{
	PDir	*ret;
	DIR	*dir;
	pchar	*pathp;

	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return NULL;
	}

	if ((dir = opendir (path)) == NULL) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call opendir() to open directory stream");
		return NULL;
	}

	if ((ret = p_malloc0 (sizeof (PDir))) == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for directory structure");
		closedir (dir);
		return NULL;
	}

	ret->dir	= dir;
	ret->path	= p_strdup (path);
	ret->orig_path	= p_strdup (path);

	pathp = ret->path + strlen (ret->path) - 1;

	if (*pathp == '/' || *pathp == '\\')
		*pathp = '\0';

	return ret;
}

P_LIB_API pboolean
p_dir_create (const pchar	*path,
	      pint		mode,
	      PError		**error)
{
	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return FALSE;
	}

	if (p_dir_is_exists (path))
		return TRUE;

	if (mkdir (path, mode) != 0) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call mkdir() to create directory");
		return FALSE;
	} else
		return TRUE;
}

P_LIB_API pboolean
p_dir_remove (const pchar	*path,
	      PError		**error)
{
	if (path == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return FALSE;
	}

	if (!p_dir_is_exists (path)) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NOT_EXISTS,
				     0,
				     "Specified directory doesn't exist");
		return FALSE;
	}

	if (rmdir (path) != 0) {
		p_error_set_error_p (error,
				     (pint) __p_error_get_last_io (),
				     __p_error_get_last_error (),
				     "Failed to call rmdir() to remove directory");
		return FALSE;
	} else
		return TRUE;
}

P_LIB_API pboolean
p_dir_is_exists (const pchar *path)
{
	struct stat sb;

	if (path == NULL)
		return FALSE;

	return (stat (path, &sb) == 0 && S_ISDIR (sb.st_mode)) ? TRUE : FALSE;
}

P_LIB_API pchar *
p_dir_get_path (const PDir *dir)
{
	if (dir == NULL)
		return NULL;

	return p_strdup (dir->orig_path);
}

P_LIB_API PDirEntry *
p_dir_get_next_entry (PDir	*dir,
		      PError	**error)
{
	PDirEntry	*ret;
#ifdef P_DIR_NEED_BUF_ALLOC
	struct dirent	*dirent_st;
#else
	struct dirent	dirent_st;
#endif
	struct stat	sb;
	pchar		*entry_path;
	pint		path_len;
#ifdef P_DIR_NEED_BUF_ALLOC
	pint		name_max;
#endif

	if (dir == NULL || dir->dir == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return NULL;
	}

#if defined(P_OS_SOLARIS)
	name_max = (pint) (FILENAME_MAX);
#elif defined(P_OS_SCO) || defined(P_OS_IRIX)
	name_max = (pint) pathconf (dir->orig_path, _PC_NAME_MAX);

	if (name_max == -1) {
		if (__p_error_get_last_error () == 0)
			name_max = _POSIX_PATH_MAX;
		else {
			p_error_set_error_p (error,
					     (pint) P_ERROR_IO_FAILED,
					     0,
					     "Failed to get NAME_MAX using pathconf()");
			return NULL;
		}
	}
#elif defined(P_OS_QNX6) || defined(P_OS_UNIXWARE)
	name_max = (pint) (NAME_MAX);
#endif

#ifdef P_DIR_NEED_BUF_ALLOC
	if ((dirent_st = p_malloc0 (sizeof (struct dirent) + name_max + 1)) == NULL) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for internal directory entry");
		return NULL;
	}

#  ifdef P_DIR_NEED_SIMPLE_R
	if ((dir->dir_result = readdir_r (dir->dir, dirent_st)) == NULL) {
		if (__p_error_get_last_error () != 0) {
			p_error_set_error_p (error,
					     (pint) __p_error_get_last_io (),
					     __p_error_get_last_error (),
					     "Failed to call readdir_r() to read directory stream");
			p_free (dirent_st);
			return NULL;
		}
	}
#  else
{'fraction_non_alphanumeric': 0.11565483696631237, 'fraction_numerical': 0.007746352008647091, 'mean_word_length': 3.293503480278422, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5064530', 'n_tokens_mistral': 4822, 'n_tokens_neox': 4283, 'n_words': 1108}