/*
 *	clipdire.c
 *
 *	POSIX準拠のディレクトリ制御ルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 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()を実装しました。
 *	* Wed May 21 15:29:53 JST 2014 Naoyuki Sawa
 *	- Windows版のopendir(),readdir(),closedir()を実装しました。
 */
#include "clip.h"

/****************************************************************************
 *	P/ECE版
 ****************************************************************************/
#ifdef  PIECE
/*--------------------------------------------------------------------------*/
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;
}
/*--------------------------------------------------------------------------*/
#endif/*PIECE*/

/****************************************************************************
 *	Windows版
 ****************************************************************************/
#ifdef  WIN32
/*--------------------------------------------------------------------------*/
struct __dirstream {
	int count;			/* readdir()が呼ばれた回数 */
	int handle;			/* find系関数の検索ハンドル */
	struct _finddata_t fileinfo;	/* find系関数のファイル情報バッファ */
};
/*--------------------------------------------------------------------------*/
DIR* opendir(const char* dirname) {
	char filespec[_MAX_PATH];
	DIR* dirp = calloc(1, sizeof(DIR));
	if(!dirp) { DIE(); }
	_makepath(filespec, NULL, dirname, "*", NULL);	//┬どちらでも良い。「*」でも「*.*」でも、全てのファイル名に一致する。
//	_makepath(filespec, NULL, dirname, "*", "*");	//┘具体的には、「*」も拡張子の有るファイル名に一致するし、「*.*」も拡張子の無いファイル名に一致する。
	dirp->handle = _findfirst(filespec, &dirp->fileinfo);			/* 1個目の一致ファイル */
	if(dirp->handle == -1) { DIE(); }
	return dirp;
}
/*--------------------------------------------------------------------------*/
struct dirent* readdir(DIR* dirp) {
	if(dirp->count++) {
		if(_findnext(dirp->handle, &dirp->fileinfo)) { return NULL; }	/* 2個目以降の一致ファイル */
	}
	return (struct dirent*)dirp->fileinfo.name;
}
/*--------------------------------------------------------------------------*/
int closedir(DIR* dirp) {
	if(_findclose(dirp->handle)) { DIE(); }
	free(dirp);
	return 0;
}
/*--------------------------------------------------------------------------*/
//void rewinddir(DIR* dirp);		/* Windows版は未実装 */
/*--------------------------------------------------------------------------*/
//long telldir(DIR* dirp);		/* Windows版は未実装 */
/*--------------------------------------------------------------------------*/
//void seekdir(DIR* dirp, long offset);	/* Windows版は未実装 */
/*--------------------------------------------------------------------------*/
#endif/*WIN32*/

/****************************************************************************
 *	P/ECE版,Windows版(共通)
 ****************************************************************************/
int scandir(const char* dirname, struct dirent*** namelist, int (*filter)(const struct dirent*), int (*compar)(const struct dirent**, 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*), (int (*)(const void*, const void*))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);
}

