/*
 *	clipdire.c
 *
 *	POSIX準拠のディレクトリ制御ルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2011 Naoyuki Sawa
 *
 *	* Wed Jan 26 00:13:38 JST 2011 Naoyuki Sawa
 *	- 1st リリース。
 *	- P/ECE開発環境、および、EPSONのS1C33開発環境には、POSIX準拠のdirent.hヘッダファイルが含まれていません。
 *	  P/ECE APIのpceFileFindOpen(),pceFileFindNext(),pceFileFindClose()を直接使えば充分なので、無くても困らないのですが、
 *	  Webや書籍のサンプルプログラム等をコンパイルする時に必要になる場合に備えて、用意しておくことにしました。
 *	  具体的には、「プログラミング言語Lua公式解説書」のサンプルプログラムをコンパイルするために用意しました。
 *	- P/ECEファイルシステムには、サブディレクトリが有りません。
 *	  opendir()関数の引数 dirname は無視し、常にルートディレクトリを開く実装としました。
 *	* Thu Jan 27 00:39:00 JST 2011 Naoyuki Sawa
 *	- rewinddir(),telldir(),seekdir()を追加しました。
 *	- scandir(),alphasort(),versionsort()の宣言だけを追加しました。未実装です。今後、必要になった際に、実装する予定です。
 *	* Sat Feb 05 01:53:09 JST 2011 Naoyuki Sawa
 *	- scandir(),alphasort()を実装しました。
 */
#include "clip.h"

DIR*
opendir(const char* dirname)
{
	FILEINFO* pfi = malloc(sizeof(FILEINFO));
	if(!pfi) DIE();
	pceFileFindOpen(pfi);	/* 常に0(正常終了) */
	return (DIR*)pfi;
}

struct dirent*
readdir(DIR* dirp)
{
	FILEINFO* pfi = (FILEINFO*)dirp;
	if(!pceFileFindNext(pfi)) return NULL;	/* 0(ファイルが見つからない) */
	return (struct dirent*)pfi->filename;	/* 1(ファイルが見つかった) */
}

int
closedir(DIR* dirp)
{
	FILEINFO* pfi = (FILEINFO*)dirp;
	pceFileFindClose(pfi);	/* 常に0(正常終了) */
	free(pfi);
	return 0;
}

void
rewinddir(DIR* dirp)
{
	FILEINFO* pfi = (FILEINFO*)dirp;
	pceFileFindClose(pfi);	/* 常に0(正常終了) */
	pceFileFindOpen(pfi);	/* 常に0(正常終了) */
}

/*{{C:\usr\PIECE\sysdev\pcekn\file.cより*/
typedef struct tagWORKS {
	DIRECTORY *pdir;
} WORKS;
/*}}C:\usr\PIECE\sysdev\pcekn\file.cより*/

long
telldir(DIR* dirp)
{
	FILEINFO* pfi = (FILEINFO*)dirp;
	WORKS* pw = (WORKS*)pfi->works;
	return (long)pw->pdir;
}

void
seekdir(DIR* dirp, long offset)
{
	FILEINFO* pfi = (FILEINFO*)dirp;
	WORKS* pw = (WORKS*)pfi->works;
	pw->pdir = (DIRECTORY*)offset;
}

int
scandir(const char* dirname, struct dirent*** namelist,
	int (*filter)(const struct dirent*),
	int (*compar)(const void* /* const struct dirent** */, const void* /* const struct dirent** */))
{
	int i = 0;
	//
	DIR* d;
	struct dirent* e;

	/* エントリ配列を初期化します。 */
	*namelist = NULL;
	/* ディレクトリを開きます。 */
	if((d = opendir(dirname)) == NULL) {
		DIE();
	}
	/* ディレクトリの各エントリについて… */
	while((e = readdir(d)) != NULL) {
		/* フィルタ関数が指定されていないか、又は、フィルタ関数によって選択されたら… */
		if((filter == NULL) || (filter(e) != 0)) {
			/* エントリ配列を拡張します。 */
			if((*namelist = realloc(*namelist, sizeof(struct dirent*) * (i + 1))) == NULL) {
				DIE();
			}
			/* エントリを複製するための、メモリを確保します。 */
			if(((*namelist)[i] = malloc(sizeof(struct dirent))) == NULL) {
				DIE();
			}
			memcpy((*namelist)[i++], e, sizeof(struct dirent));
		}
	}
	/* ディレクトリを閉じます。 */
	if(closedir(d) != 0) {
		DIE();
	}
	/* エントリ比較関数が指定されていたら、エントリ比較関数を使ってソートします。 */
	if(compar != NULL) {
		qsort(*namelist, i, sizeof(struct dirent*), compar);
	}
	/* 選択されたエントリ数を返します。
	 * - 選択されたエントリ数が 0 の場合は、*namelist=NULL のままです。
	 *   呼び出し側が、free(*namelist) を行いますが、free(NULL) は安全なので問題ありません。
	 */
	return i;
}

int
alphasort(const void* /* const struct dirent** */ a, const void* /* const struct dirent** */ b)
{
	return strcmp((*(const struct dirent**)a)->d_name, (*(const struct dirent**)b)->d_name);
}

