/*	
 *	clipio.c
 *
 *	S1C33 Family Cコンパイラパッケージの io.lib 関数を置き換えます。
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Mon Nov 25 20:37:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Nov 30 08:06:00 JST 2002 Naoyuki Sawa
 *	- stderr_open()/close()/write()を追加。
 *	* Wed Dec 11 12:30:00 JST 2002 Naoyuki Sawa
 *	- キャッシュ用メンバ変数(cache_sct/len/ptr)と、キャッシュを介したシーク関数(io_seek())を追加。
 *	  連続したfread()呼び出しでセクタが変化しない場合は、pceFileReadSct()を使わずキャッシュ情報を使います。
 *	  この改良により、fgetc()やfgets()のような細かな単位で読み込みを行う処理が、2倍以上速くなりました。
 *	* Sat Nov 22 06:00:00 JST 2003 Naoyuki Sawa
 *	- stderr_open()はアプリケーションから明示的に呼ぶようにしたので、(→clippce.c参照)
 *	  stderr_open()が呼ばれた場合は常に出力モニタの接続を待つように変更しました。
 *	* Wed Jun 26 20:03:00 JST 2005 Naoyuki Sawa
 *	- stderr_open()内の不要なpceLCDDispStop()呼び出しを削除しました。
 *	* Tue Dec 13 04:35:00 JST 2005 Naoyuki Sawa
 *	- 標準出力への書き込みも、標準エラー出力と同様に、標準エラー出力モニタへ出力するように変更しました。
 *	  これまでは、標準出力(stdout)への書き込みは、エラー終了としていました。
 *	  この変更により、fprintf(stderr, fmt, ...)だけでなく、printf(fmt, ...)もモニタできるようになりました。
 *	* Sun Jan 15 04:02:00 JST 2005 Naoyuki Sawa
 *	- ungetc()にて、EOFが指定されたら、エラーとして扱い、何もせずにEOFを返す処理を追加しました。
 *	  これまでは、EOFが指定された場合にも、エラーとせずに、ストリームへ戻す処理を行っていました。
 *	* Tue Oct 30 00:38:57 JST 2012 Naoyuki Sawa
 *	- fileno(),isatty()を追加しました。宣言は、clippce.h にあります。	//{{2015/01/07追記:fileno(),isatty()の宣言を、clippce.h⇒include/stdio.hへ移動しました。実装はclipio.cにあります。isatty()は本当はio.hかunistd.hで宣言すべきなのですが、P/ECEにはio.hもunistd.hも無いのでinclude/stdio.hで宣言することにしました。}}
 *	* Tue Oct 30 22:43:07 JST 2012 Naoyuki Sawa
 *	- isatty()の生成コードが(僅かながら)小さくなるよう、条件式を変更しました。
 *	* Sat Mar 23 17:43:37 JST 2013 Naoyuki Sawa
 *	- stat(),fstat()を追加しました。宣言は、include/sys/stat.h にあります。
 *	* Sun Mar 09 14:07:14 JST 2014 Naoyuki Sawa
 *	- stdin_read()を追加しました。
 *	  この機能追加に伴い、tool/dstderr にも機能追加を行いました。
 *	  詳細は、tool/dstderr/dstderr.c の同日のコメントを参照してください。
 *	* Mon Mar 10 17:00:24 JST 2014 Naoyuki Sawa
 *	- puts()が改行を出力していなかったバグを修正しました。
 *	- getchar,getc,putchar,putcをマクロ化し、関数を削除しました。
 *	  /clip/include/stdio.h を参照してください。
 *	* Thu Mar 13 01:48:55 JST 2014 Naoyuki Sawa
 *	- 接続完了時のメッセージが解析の邪魔になる事が有るため削除しました。
 *	- tool/dstderr/dstdpipe を作成しました。
 *	  これまでの dstderr と同等の機能を持ち、より柔軟です。
 *	  キーボード以外からの入力も受け付けるので、ファイルの流し込みや、他プログラムとP/ECEの中継パイプとしても動作します。
 *	  今後は、dstderr よりも dstdpipe を使用するようにしてください。
 *	* Thu Aug 07 23:44:53 JST 2014 Naoyuki Sawa
 *	- fcloseall()を追加しました。
 *	* Sun Sep 07 20:09:27 JST 2014 Naoyuki Sawa
 *	- stat.st_blksize,stat.st_blocksを追加しました。
 *	- statvfs(),fstatvfs()を追加しました。宣言は、include/sys/statvfs.h にあります。
 *	* Thu Oct 23 18:26:54 JST 2014 Naoyuki Sawa
 *	- 関数プロトタイプのfscanf(),scanf()を削除し、clipscan.cにて実装しました。
 *	* Wed Dec 31 17:37:43 JST 2014 Naoyuki Sawa
 *	- cstart.cのpceAppInit00()でpceLCDDispStart()を呼び出し、液晶画面の表示を開始するようにしました。
 *	  この変更に伴い、stderr_open()内のpceLCDDispStart()呼び出しは不要になり、削除しました。
 */
#include "clip.h"

/* ※2002/11/26現在の実装では、いずれの関数もエラー時にerrnoを設定しません。
 *   errnoを見てエラーを判断しているプログラムは、正しく動かない可能性があります。
 * →TODO:errnoを設定するよう修正せよ。
 */

/****************************************************************************
 *	関数プロトタイプ
 ****************************************************************************/

/*  『S5U1C33000C Manual (S1C33 Family Cコンパイラパッケージ) (Ver.4)』(S5U1C33000C_J.pdf)
 * p.97 8.2.1 入出力関数(io.lib) 表8.2.1.1 入出力関数一覧 の並びと同じ順番です。
 * 各々の関数を実装したら、コメントアウトして行ってください。
 */
//FILE* fopen(const char* filename, const char* mode) { DIE(); return 0; }
//FILE* freopen(const char* filename, const char* mode, FILE* stream) { DIE(); return 0; }
//int fclose(FILE* stream) { DIE(); return 0; }
//int fflush(FILE* stream) { DIE(); return 0; }
//int fseek(FILE* stream, long offset, int origin) { DIE(); return 0; }
//long ftell(FILE* stream) { DIE(); return 0; }
//void rewind(FILE* stream) { DIE(); }
//int fgetpos(FILE* stream, fpos_t* pos) { DIE(); return 0; }
//int fsetpos(FILE* stream, const fpos_t* pos) { DIE(); return 0; }
//size_t fread(void* buffer, size_t size, size_t count, FILE* stream) { DIE(); return 0; }
//size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream) { DIE(); return 0; }
//int fgetc(FILE* stream) { DIE(); return 0; }
//int getc(FILE* stream) { DIE(); return 0; }
//int getchar() { DIE(); return 0; }
//int ungetc(int c, FILE* stream) { DIE(); return 0; }
//char* fgets(char* string, int n, FILE* stream) { DIE(); return 0; }
//char* gets(char* buffer) { DIE(); return 0; }
//int fputc(int c, FILE* stream) { DIE(); return 0; }
//int putc(int c, FILE* stream) { DIE(); return 0; }
//int putchar(int c) { DIE(); return 0; }
//int fputs(const char* string, FILE* stream) { DIE(); return 0; }
//int puts(const char* string) { DIE(); return 0; }
//int remove(const char* filename) { DIE(); return 0; }
int rename(const char* oldname, const char* newname) { DIE(); return 0; }
void setbuf(FILE* stream, char* buffer) { DIE(); }
int setvbuf(FILE* stream, char* buffer, int mode, size_t size) { DIE(); return 0; }
FILE* tmpfile() { DIE(); return 0; }
char* tmpnam(char* string) { DIE(); return 0; }
//int feof(FILE* stream) { DIE(); return 0; }
//int ferror(FILE* stream) { DIE(); return 0; }
//void clearerr(FILE* stream) { DIE(); }
//void perror(const char* string) { DIE(); }
//int fscanf(FILE* stream, const char* format, ...) { DIE(); return 0; }	-> 2014/10/23 clipscan.c
//int scanf(const char* format, ...) { DIE(); return 0; }			-> 2014/10/23 clipscan.c
//int sscanf(char* buffer, const char* format, ...); -> io.lib(そのまま使用)
//int fprintf(FILE* stream, const char* format, ...) { DIE(); return 0; }
//int printf(const char* format, ...) { DIE(); return 0; }
//int sprintf(char* buffer, const char* format, ...); -> P/ECE Kernel(pcesprintf) -> 2003/05/20 clipprnt.c
//int vfprintf(FILE* stream, const char* format, va_list argptr) { DIE(); return 0; }
//int vprintf(const char* format, va_list argptr) { DIE(); return 0; }
//int vsprintf(char* buffer, const char* format, va_list argptr); -> P/ECE Kernel(pcevsprintf) -> 2003/05/20 clipprnt.c

/****************************************************************************
 *	ローカル変数・関数
 ****************************************************************************/

#define MAX_IO		4		/* 同時に開ける最大ファイル数 */
#define IO_SIG		((unsigned short)(('I'<<0)|('O'<<8)))	/* ->IO.sig */
#define IO_EOF		(1 << 0)	/* -> IO.flags */
#define IO_ERROR	(1 << 1)	/* -> IO.flags */
typedef struct _IO {
	unsigned short sig;		/* 0:空き/IO_SIG:使用中 */
	unsigned short flags;		/* IO_EOF,IO_ERRORの組み合わせ */
	int pos;			/* 次回の読み込み/書き込み位置 */
	FILEACC fa;			/* ファイルアクセス構造体 */
	/*{{キャッシュ用メンバ変数:io_seek()にて更新*/
	int cache_sct;			/* 最後にアクセスしたセクタ(-1:キャッシュ無効) */
	int cache_len;			/* そのセクタの有効バイト数 */
	const void* cache_ptr;		/* そのセクタの先頭アドレス */
	/*}}キャッシュ用メンバ変数:io_seek()にて更新*/
} IO;
static IO ios[MAX_IO];			/* ファイルI/O構造体バッファ */

/* 一時バッファとして、システム用仮想画面を利用。
 * システム用仮想画面は128x88=11KBあるので、同時に使用する領域が重ならないよう割り当てます。
 */
#define sctbuf (&_def_vbuff[        0])	/* 0〜BLOCKSIZE(=4KB) : fwrite()用セクタバッファ */
#define strbuf (&_def_vbuff[BLOCKSIZE])	/* BLOCKSIZE〜最後まで: vsprintf用文字列バッファ */

/* ダミーの標準入出力用アドレス定義。
 * 偶数アドレスにしておかないと、io_file()でエラーを検出する前にアドレスエラーで落ちるので注意。
 */
#define FILE_STDIN	(FILE*)(- 4)	/* ダミー。io_file()でエラー扱い。 */
#define FILE_STDOUT	(FILE*)(- 8)	/* ダミー。io_file()でエラー扱い。fwrite()のみ特別処理 */
#define FILE_STDERR	(FILE*)(-12)	/* ダミー。io_file()でエラー扱い。fwrite()のみ特別処理 */

static IO*
io_file(FILE* stream)
{
	IO* io = (IO*)stream;

	/* ファイルが開かれていることを確認します。 */
	if(io->sig != IO_SIG) DIE();

	return io;
}

static IO*
io_open(IO* io, const char* filename, const char* mode)
{
	/* まずクリアしておきます。 */
	memset(io, 0, sizeof(IO));

	/* ファイルを開きます。 */
	switch(*mode) { /* 一文字目だけで判定 */
	case 'r':
		if(pceFileOpen(&io->fa, filename, FOMD_RD) != 0) return NULL;
		break;
	case 'w':
		if(pceFileOpen(&io->fa, filename, FOMD_WR) != 0) return NULL;
		break;
	default:
		return NULL;
	}

	/* このエントリが使用中であることをマークします。 */
	io->sig = IO_SIG;

	/* キャッシュを無効化しておきます。 */
	io->cache_sct = -1;

	return io;
}

static int
io_seek(IO* io, int sct)
{
	/* 前回と違うセクタなら、有効バイト数とセクタアドレスを取得し、キャッシュします。 */
	if(sct != io->cache_sct) {
		io->cache_sct = sct;
		io->cache_len = pceFileReadSct(&io->fa, NULL, sct, BLOCKSIZE);
		io->cache_ptr = io->fa.aptr;
	}

	return io->cache_len; /* pceFileReadSct()互換の戻り値として、有効バイト数を返します */
}

/****************************************************************************
 *	グローバル変数・関数
 ****************************************************************************/

FILE* stdin = FILE_STDIN;
FILE* stdout = FILE_STDOUT;
FILE* stderr = FILE_STDERR;

FILE*
fopen(const char* filename, const char* mode)
{
	IO* io;
	int i;

	/* ファイルI/O構造体バッファから、空きエントリを探します。 */
	for(io = ios, i = 0; i < MAX_IO; i++, io++) {
		if(!io->sig) break; /* 空きエントリ発見？ */
	}
	if(i == MAX_IO) return NULL;

	/* ファイルを開きます。 */
	return (FILE*)io_open(io, filename, mode);
}

FILE*
freopen(const char* filename, const char* mode, FILE* stream)
{
	IO* io = io_file(stream);

	/* いったん閉じます。 */
	pceFileClose(&io->fa);

	/* ファイルを開きます。 */
	return (FILE*)io_open(io, filename, mode); /* この中でキャッシュも無効化されます */
}

int
fclose(FILE* stream)
{
	IO* io = io_file(stream);

	/* ファイルを閉じます。 */
	pceFileClose(&io->fa);

	/* このエントリが空きであることをマークします。 */
	memset(io, 0, sizeof(IO));

	return 0;
}

int
fcloseall()
{
	/* 正しくは以下のように実装すべきであるが、
	 * │int fcloseall(){
	 * │  int i,result=0;
	 * │  for(i=0;i<MAX_IO;i++){
	 * │    if(ios[i].sig==IO_SIG){
	 * │      if(fclose((FILE*)&ios[i])){result=EOF;}
	 * │    }
	 * │  }
	 * │  return 0;
	 * │}
	 * P/ECEでファイルを閉じる処理は何もしないダミーなので簡略化できる。
	 * 本モジュール内のファイルI/O構造体バッファをクリアするだけで良い。 */
	memset(ios, 0, sizeof ios);
	/* fcloseall()の戻り値には、二つの流儀があります。
	 * │GNU C: 全てのファイルが正常に閉じられた場合(0)を返す。エラーの場合、EOFを返す。
	 * │Visual C++: クローズしたストリームの総数を返す。エラーが発生すると、EOFを返す。
	 * 本モジュールの実装は、GNU Cの流儀に従いました。 */
	return 0;
}

int
fflush(FILE* stream)
{
	return 0; /* 何もしません */
}

int
fseek(FILE* stream, long offset, int origin)
{
	IO* io = io_file(stream);

	clearerr(stream); /* EOF・エラーインジケータをクリアします */

	switch(origin) {
	case SEEK_SET:
		io->pos = offset;
		break;
	case SEEK_CUR:
		io->pos += offset;
		break;
	case SEEK_END:
		io->pos = io->fa.fsize - offset;
		break;
	default:
		return -1;
	}

	return 0;
}

long
ftell(FILE* stream)
{
	IO* io = io_file(stream);

	return io->pos;
}

void
rewind(FILE* stream)
{
	fseek(stream, 0, SEEK_SET); /* fseek()の中でEOF・エラーインジケータがクリアされます */
}

int
fgetpos(FILE* stream, fpos_t* pos)
{
	*pos = ftell(stream);

	return 0;
}

int
fsetpos(FILE* stream, const fpos_t* pos)
{
	return fseek(stream, *pos, SEEK_SET); /* fseek()の中でEOF・エラーインジケータがクリアされます */
}

size_t
fread(void* buffer, size_t size, size_t count, FILE* stream)
{
	if(stream == FILE_STDIN) { /* 標準入力は特別処理で、標準エラー出力モニタから入力します。 */
		/* * 厳密には、この処理は誤りである。
		 *   本当は、標準入力に文字が残っていたとしても、sizeの倍数に満たないブロックは読み込んではいけない。
		 *   しかし、P/ECEプログラムではほぼsize=1で使用すると思うので、この誤りは実質問題にはならないと判断し、このままにすることにした。
		 */
		return stdin_read(buffer, size * count) / size;
	} else {
		IO* io = io_file(stream);
		char* p = (char*)buffer;
		int len = size * count;
		int ofs, sct, sof, sln;

		/* * 現在の実装では、最後の項目が中途半端に読み込める場合、その分もバッファに読み込んでしまいます。
		 *   例えば、150バイトのファイルの前半100バイトをバッファに読み込む場合、本当のfread()ならば：
		 *	char buf[100];
		 *	retval = fread(buf, 100, 2, fp); -> 戻り値は1で、buf[0〜99]までにデータが読み込まれます
		 *   となりますが、この実装では：
		 *	char buf[100];
		 *	retval = fread(buf, 100, 2, fp); -> 戻り値は1ですが、buf[0〜149]までデータが読み込まれます
		 *   となり、バッファオーバーランを起こしてしまいます。
		 *   そもそもこのような危険な呼び出し方をすることはあまりないと思いますが、念のためご注意ください。
		 */

		ofs = 0;
		while(ofs < len) {
			/* セクタのフラッシュメモリアドレスを取得します。 */
			sct = (io->pos + ofs) / BLOCKSIZE;			/* sct = 現在の読み込みオフセットを含むセクタ番号                         */
			sln = io_seek(io, sct);					/* sln = このセクタのデータサイズ(最終セクタのみsln<=BLOCKSIZEになります) */
			if(sln == 0) break;					/* たぶんオフセット超過                                                   */

			/* このセクタから読み込むサイズを決定します。 */
			sof = (io->pos + ofs) % BLOCKSIZE;			/* セクタ内オフセット                                                       */
			sln -= sof;						/* セクタ内オフセット以降から読み込み可能な最大データサイズ                 */
			if(sln > len - ofs) sln = len - ofs;			/* 読み込み可能な最大データサイズが残り読み込みサイズ以上なら、切り詰めます */
			if(sln <= 0) break;					/* たぶんオフセット超過、または読み込みサイズに0以下が指定された(len<=0)    */

			/* セクタからバッファへ読み込みます。 */
			memcpy(p + ofs, io->cache_ptr + sof, sln);
			ofs += sln;
		}
		count = ofs / size;						/* 完全に読み込めた項目数                                             */
		io->pos += count * size;					/* 完全に読み込めた項目の分だけ、ファイルポインタの現在位置を進めます */
		if(count * size < len) io->flags |= IO_EOF;			/* 完全に読み込めたバイト数が、要求サイズ*未満*だったら、EOFをマーク  */

		return count;							/* 完全に読み込めた項目数を返します */
	}
}

size_t
fwrite(const void* buffer, size_t size, size_t count, FILE* stream)
{
	if((stream == FILE_STDOUT) || (stream == FILE_STDERR)) { /* 標準出力と標準エラー出力は特別処理で、標準エラー出力モニタへ出力します。 */
		stderr_write(buffer, size * count);
		return count;
	} else {
#if 0
//		IO* io = io_file(stream);
//		char* p = (char*)buffer;
//		int len = size * count;
//		int ofs, sct, sof, sln;
//
//		/* * fread()と同じく、最後の項目を中途半端に書き込んでしまう可能性があります。ご注意ください。 */
//		/* * v1.18現在のP/ECEカーネルは、pceFileOpen()に指定されたmodeを無視します。
//		 *   FOMD_RDで開いたファイルに対してもpceFileWriteSct()できますし、FOMD_WRで開いたファイルに対してもpceFileReadSct()できます。
//		 *   fwrite()関数に渡されるファイルはFOMD_WRで開かれているはずですが、上記の特性を利用してpceFileReadSct()を使ってしまいます。
//		/* * 書き込み処理では、キャッシュ用メンバ変数(cache_sct/ptr/len)を参照せず、変更もしません。 */
//
//		ofs = 0;
//		while(ofs < len) {
//			/* セクタのフラッシュメモリアドレスを取得します。 */
//			sct = (io->pos + ofs) / BLOCKSIZE;			/* sct = 現在の書き込みオフセットを含むセクタ番号                             */
//			sln = pceFileReadSct(&io->fa, sctbuf, sct, BLOCKSIZE);	/* このセクタの全データを取得します。(最終セクタのみsln<=BLOCKSIZEになります) */
//			if(sln == 0) break;					/* たぶんオフセット超過                                                       */
//
//			/* このセクタに書き込むサイズを決定します。 */
//			sof = (io->pos + ofs) % BLOCKSIZE;			/* セクタ内オフセット                                                       */
//			sln -= sof;						/* セクタ内オフセット以降に書き込み可能な最大データサイズ                   */
//			if(sln > len - ofs) sln = len - ofs;			/* 書き込み可能な最大データサイズが残り書き込みサイズ以上なら、切り詰めます */
//			if(sln <= 0) break;					/* たぶんオフセット超過、または書き込みサイズに0以下が指定された(len<=0)    */
//
//			/* セクタの全データの一部を書き換えます。 */
//			memcpy(sctbuf + sof, p + ofs, sln);
//			ofs += sln;
//
//			/* セクタの全データをフラッシュメモリに書き戻します。 */
//			pceFileWriteSct(&io->fa, sctbuf, sct, BLOCKSIZE);	/* 最終セクタの末尾超過分は、pceFileWriteSct()によって切り詰められます  */
//										/* ここで失敗することはありえないはずなので、エラーチェックは行いません */
//		}
//		count = ofs / size;						/* 完全に書き込めた項目数                                             */
//		io->pos += count * size;					/* 完全に書き込めた項目の分だけ、ファイルポインタの現在位置を進めます */
//		if(count * size < len) io->flags |= IO_EOF;			/* 完全に書き込めたバイト数が、要求サイズ*未満*だったら、EOFをマーク  */
//
//		return count;							/* 完全に書き込めた項目数を返します */
#else
		/* フラッシュメモリ上の通常ファイルへの細かな書き込みは、フラッシュメモリを痛める恐れがあるので、現在のところエラー扱いとしておきます。 */
		DIE(); return 0;
#endif
	}
}

int
fgetc(FILE* stream)
{
	char c;

	if(fread(&c, 1, 1, stream) != 1) return -1;

	return c & 0xff; /* '\x80'〜'\xff'を符号拡張してはいけない */
}

//{{2014/03/10削除:getchar,getc,putchar,putcをマクロ化し、関数を削除しました。/clip/include/stdio.h を参照してください。
//int
//getc(FILE* stream)
//{
//	return fgetc(stream);
//}
//
//int
//getchar()
//{
//	return getc(stdin);
//}
//}}2014/03/10削除:getchar,getc,putchar,putcをマクロ化し、関数を削除しました。/clip/include/stdio.h を参照してください。

int
ungetc(int c, FILE* stream)
{
	IO* io = io_file(stream);

	/* EOFを返そうとしたら、ストリームへ戻す処理を行わずに、エラーを示すEOFを返します。
	 * それ以外ならば、ストリームへ戻す処理を行い、戻した文字コードをそのまま返します。
	 */
	if(c != EOF) {

		io->pos--; /* ※適当な実装！本当のungetc()とはぜんぜん違います */

		/* もしも呼び出し元が、'\x80'〜'\xff'を符号拡張して、'\xff80'〜'\xffff'を渡した場合でも、
		 * ungetc()の戻り値は、'\x80'〜'\xff'となります。
		 * (実際には、上位8bitが\xffかどうかにかかわらず、常に上位8bitをクリアします。)
		 * この仕様は、Visual C++ 6.0 SP6の、ランタイムライブラリのソースの実装に合わせませした。
		 */
		return c & 0xff; /* '\x80'〜'\xff'を符号拡張してはいけない */
	}

	return c;
}

char*
fgets(char* string, int n, FILE* stream)
{
	char* p = string;

	if(n <= 0) return NULL;

	while(--n) { /* n-1文字までしか読み込まないので、プリデクリメント */
		if(fread(p, 1, 1, stream) != 1) {
			if(p == string) return NULL; /* 一文字も読めなかった */
			break;
		}
		if(*p++ == '\n') break; /* 改行を\0に置き換えないので、必ずp++を行う */
	}
	*p = '\0';

	return string;
}

char*
gets(char* buffer)
{
	char* p = buffer;

	for(;;) {
		if(fread(p, 1, 1, stdin) != 1) {
			if(p == buffer) return NULL; /* 一文字も読めなかった */
			break;
		}
		if(*p == '\n') break; /* 改行を\0に置き換えるので、\nなら++を行わない */
		p++;
	}
	*p = '\0';

	return buffer;
}

int
fputc(int c, FILE* stream)
{
	if(fwrite(&c, 1, 1, stream) != 1) return -1;

	return c & 0xff; /* '\x80'〜'\xff'を符号拡張してはいけない */
}

//{{2014/03/10削除:getchar,getc,putchar,putcをマクロ化し、関数を削除しました。/clip/include/stdio.h を参照してください。
//int
//putc(int c, FILE* stream)
//{
//	return fputc(c, stream);
//}
//
//int
//putchar(int c)
//{
//	return putc(c, stdout);
//}
//}}2014/03/10削除:getchar,getc,putchar,putcをマクロ化し、関数を削除しました。/clip/include/stdio.h を参照してください。

int
fputs(const char* string, FILE* stream)
{
	const char* p = string;

	while(*p != '\0') {
		if(fwrite(p++, 1, 1, stream) != 1) return -1;
	}

	return 0; /* 成功時は書き込んだバイト数ではなく0を返す */
}

int
puts(const char* string)
{
	const char* p = string;

	while(*p != '\0') {
		if(fwrite(p++, 1, 1, stdout) != 1) return -1;
	}
	if(fwrite("\n", 1, 1, stdout) != 1) return -1; /* 改行 */

	return 0; /* 成功時は書き込んだバイト数ではなく0を返す */
}

int
remove(const char* filename)
{
	if(pceFileDelete(filename) != 0) return -1;

	return 0;
}

int
feof(FILE* stream)
{
	IO* io = io_file(stream);

	return io->flags & IO_EOF;
}

int
ferror(FILE* stream)
{
	IO* io = io_file(stream);

	return io->flags & IO_ERROR;
}

void
clearerr(FILE* stream)
{
	IO* io = io_file(stream);

	io->flags = 0;
}

void
perror(const char* string)
{
	if(string && *string) { /* 空文字列時は": "を表示しない */
		fprintf(stderr, "%s", string);
		fprintf(stderr, ": ");
	}
	fprintf(stderr, "%s", strerror(errno));
	fprintf(stderr, "\n"); /* 要改行 */
}

int
fprintf(FILE* stream, const char* format, ...)
{
	int retval;
	va_list argptr;

	va_start(argptr, format);
	retval = vfprintf(stream, format, argptr);
	va_end(argptr);

	return retval;
}

int
printf(const char* format, ...)
{
	int retval;
	va_list argptr;

	va_start(argptr, format);
	retval = vprintf(format, argptr);
	va_end(argptr);

	return retval;
}

int
vfprintf(FILE* stream, const char* format, va_list argptr)
{
	return fwrite(strbuf, 1, vsprintf(strbuf, format, argptr), stream);
}

int
vprintf(const char* format, va_list argptr)
{
	return vfprintf(stdout, format, argptr);
}

/****************************************************************************
 *	標準Ｃライブラリの補助 -> clippce.h					//{{2015/01/07追記:fileno(),isatty()の宣言を、clippce.h⇒include/stdio.hへ移動しました。実装はclipio.cにあります。isatty()は本当はio.hかunistd.hで宣言すべきなのですが、P/ECEにはio.hもunistd.hも無いのでinclude/stdio.hで宣言することにしました。}}
 ****************************************************************************/

int
fileno(FILE* stream)
{
	IO* io;
	int i;

	/* 指定されたストリームが、標準入力,標準出力,標準エラー出力ならば、そのファイル記述子を返します。 */
	if(stream == FILE_STDIN ) return STDIN_FILENO;	/* 標準入力 */
	if(stream == FILE_STDOUT) return STDOUT_FILENO;	/* 標準出力 */
	if(stream == FILE_STDERR) return STDERR_FILENO;	/* 標準エラー出力 */

	/* 指定されたストリームが、標準入力,標準出力,標準エラー出力でなければ、ファイルI/O構造体バッファを検索して… */
	for(io = ios, i = 0; i < MAX_IO; i++, io++) {
		/* このファイルI/O構造体が、オープンされていたら… */
		if(io->sig == IO_SIG) {
			/* このファイルI/O構造体が、指定されたストリームに一致したら… */
			if(io == (IO*)stream) {
				/* このファイルI/O構造体に対応する、ファイル記述子を返します。
				 * ファイルI/O構造体に対応するファイル記述子は、(標準エラー出力+1) の値から始まります。
				 */
				return (STDERR_FILENO + 1) + i;
			}
		}
	}

	/* 指定されたストリームが、有効なストリームでなければ、-1 を返します。 */
	return -1;
}

int
isatty(int fd)
{
	/* 指定されたファイル記述子が、端末を参照するファイル記述子ならば、1 を返します。
	 * そうでなければ、0 を返します。
	 */
//{{2012/10/30:生成コードが(僅かながら)小さくなるよう、条件式を変更しました。
//	return (fd == STDIN_FILENO ) ||	/* 標準入力 */
//	       (fd == STDOUT_FILENO) ||	/* 標準出力 */
//	       (fd == STDERR_FILENO);	/* 標準エラー出力 */
//↓2012/10/30:生成コードが(僅かながら)小さくなるよう、条件式を変更しました。
	return (unsigned)fd <= STDERR_FILENO;	/* 標準入力,標準出力,標準エラー出力 */
//}}2012/10/30:生成コードが(僅かながら)小さくなるよう、条件式を変更しました。
}

/****************************************************************************
 *	標準Ｃライブラリの補助 -> include/sys/stat.h
 ****************************************************************************/

int
stat(const char* path, struct stat* buf)
{
	FILEACC fa;
	if(pceFileOpen(&fa, path, FOMD_RD) == 0) {
		memset(buf, 0, sizeof(struct stat));
		buf->st_mode    = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
		buf->st_nlink   = 1;
		buf->st_size    = fa.fsize;
		buf->st_blksize = BLOCKSIZE;			/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/
		buf->st_blocks  = (buf->st_size + 511) >> 9;	/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/
		pceFileClose(&fa);
		return 0;
	}
	errno = ENOENT; /* No such file or directory */
	return -1;
}

int
fstat(int fd, struct stat* buf)
{
	if((unsigned)fd <= STDERR_FILENO) { /* STDIN_FILENO or STDOUT_FILENO or STDERR_FILENO */
		memset(buf, 0, sizeof(struct stat));
		buf->st_mode  = S_IFCHR;
		buf->st_nlink = 1;
		buf->st_blksize = BLOCKSIZE;			/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/	//標準入出力もst_blksizeを格納する仕様のようです。(Web上のLinuxの情報より)
	//不要	buf->st_blocks  = (buf->st_size + 511) >> 9;	/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/	//標準入出力はst_blocks=0。既にクリア済みなので明示的な格納処理は不要です。
		return 0;
	}
	if((unsigned)(fd - (STDERR_FILENO + 1)) < MAX_IO) {
		IO* io = &ios[fd - (STDERR_FILENO + 1)];
		if(io->sig == IO_SIG) {
			memset(buf, 0, sizeof(struct stat));
			buf->st_mode  = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
			buf->st_nlink = 1;
			buf->st_size  = io->fa.fsize;
			buf->st_blksize = BLOCKSIZE;			/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/
			buf->st_blocks  = (buf->st_size + 511) >> 9;	/*{{2014/09/07変更:stat.st_blksize,stat.st_blocksを追加しました。}}*/
			return 0;
		}
	}
	errno = EBADF; /* Bad file number */
	return -1;
}

/****************************************************************************
 *	標準Ｃライブラリの補助 -> include/sys/statvfs.h
 ****************************************************************************/

int statvfs(const char* path, struct statvfs* buf) {
	memset(buf, 0, sizeof(struct statvfs));
	{
		const pffsMASTERBLOCK* msb = (pffsMASTERBLOCK*)pceSystemGetInfo()->pffs_top;
		{
			const DIRECTORY *pdir = msb->dir, *pdir_end = msb->dir + MAXDIR;
			do {
				if((pdir->name[0] ==  0) ||	//┐\usr\PIECE\sysdev\pcekn\file.c
				   (pdir->name[0] == -1)) {	//┘Search() 参照
					buf->f_ffree++;
				}
			} while(++pdir < pdir_end);
		}
		{
			const FAT *pfat = msb->fat, *pfat_end = msb->fat + MAXFAT;
			do {
				switch(pfat->chain) {
				case FAT_INVALID:		//無効セクタ(512KB版と2MB版の差分)
				case FAT_SYSTEM:		//マスターブロック
					/** no job **/
					break;
				case FAT_FREE:			//空きセクタ
					buf->f_bfree++;
					/*FALLTHRU*/
				case FAT_END:			//チェインの終端
				default:			//チェインの途中
					buf->f_blocks++;
					break;
				}
			} while(++pfat < pfat_end);
		}
	}
	buf->f_bsize	= BLOCKSIZE;
	buf->f_frsize	= BLOCKSIZE;
//	buf->f_blocks	= 上の処理で設定済み
//	buf->f_bfree	= 上の処理で設定済み
	buf->f_bavail	= buf->f_bfree;
	buf->f_files	= MAXDIR;
//	buf->f_ffree	= 上の処理で設定済み
	buf->f_favail	= buf->f_ffree;
//不要	buf->f_fsid	= 0;
//不要	buf->f_flag	= 0;
	buf->f_namemax	= FIELD_SIZEOF(DIRECTORY, name) - 1;
	return 0;
}

int fstatvfs(int fd, struct statvfs* buf) {
	return statvfs("/", buf);
}

/****************************************************************************
 *	標準エラー出力,標準入力
 ****************************************************************************/

/* UCS_RXDONEとUCS_TXDONEは、pceUSBCOMGetStat()を呼ぶとクリアされてしまうので、蓄積するための変数が必要である。
 * 別の目的で呼び出したpceUSBCOMGetStat()によって、UCS_RXDONEとUCS_TXDONEがクリアされてしまうのを回避するため、
 * pceUSBCOMGetStat()を呼び出すたびに、ucs変数にUCS_RX*とUCS_TX*を蓄積するようにした。
 */
static int ucs;	/* UCS_PCSTATとUCS_PISTATは即反映、UCS_RX*とUCS_TX*は蓄積 */

//void stderr_open() {
//	USBCOMINFO uci = { "stderr" };
//	/* 標準エラー出力を開きます。 */
//	pceUSBCOMSetup(&uci);
//	/* Ｂボタンが押されていたら、標準エラー出力モニタの接続を待ちます。 */
//	if(pcePadGetDirect() & PAD_B) {
//		unsigned char* old_vbuff;
//		pceLCDDispStop();
//		old_vbuff = pceLCDSetBuffer(_def_vbuff);
//		pceLCDDispStart();
//		pceFontSetType(0x80);
//		pceFontSetTxColor(3);
//		pceFontSetBkColor(0);
//		pceFontSetPos(0, (DISP_Y - 10 * 5) / 2);
//		/*             ０１２３４５６７８９０１２ */
//		pceFontPutStr(" +----------------------+\n"
//		              " |標準エラー出力モニタの|\n"
//		              " | 接続を待っています… |\n"
//		              " |(Ａボタンでキャンセル)|\n"
//		              " +----------------------+");
//		pceLCDTrans();
//		while(pcePadGetDirect() & PAD_A) { /* 開放待ち*/ }
//		for(;;) {
//			if(pceUSBCOMGetStat() & UCS_PCSTAT) break; /* 接続 */
//			if(pcePadGetDirect() & PAD_A) break; /* キャンセル */
//		}
//		while(pcePadGetDirect() & PAD_A) { /* 開放待ち*/ }
//		pceLCDSetBuffer(old_vbuff);
//		stderr_write("接続完了\n", 9); /* 標準エラー出力モニタに表示 */
//	}
//}
//↓2003/11/22 stderr_open()はアプリケーションから明示的に呼ぶようにしたので、
//             stderr_open()が呼ばれた場合は常に出力モニタの接続を待つように変更しました。
void stderr_open(int timeout) {
	USBCOMINFO uci = { "stderr" };
	unsigned char* old_vbuff;
	int T0, T1;
	/* 標準エラー出力を開きます。 */
	pceUSBCOMSetup(&uci);
	/* 標準エラー出力モニタの接続待ち。 */
//不要	pceLCDDispStop(); {{2005/06/26 Naoyuki Sawa 不要なので削除しました}}
	old_vbuff = pceLCDSetBuffer(_def_vbuff);
//不要	pceLCDDispStart();	//{{2014/12/31削除:cstart.cのpceAppInit00()でpceLCDDispStart()を呼び出し、液晶画面の表示を開始するようにしました。}}
	pceFontSetType(0x80);
	pceFontSetTxColor(3);
	pceFontSetBkColor(0);
	pceFontSetPos(0, (DISP_Y - 10 * 5) / 2);
	/*             ０１２３４５６７８９０１２ */
	pceFontPutStr(" +----------------------+\n"
	              " |標準エラー出力モニタの|\n"
	              " | 接続を待っています… |\n"
	              " |(Ａボタンでキャンセル)|\n"
	              " +----------------------+");
	pceLCDTrans();
	while(pcePadGetDirect() & PAD_A) { /* 開放待ち*/ }
	T0 = pceTimerGetCount();
	for(;;) {
		if(pceUSBCOMGetStat() & UCS_PCSTAT) break; /* 接続 */
		if(pcePadGetDirect() & PAD_A) break; /* キャンセル */
		if(timeout >= 0) {
			T1 = timeout * 1000 - (pceTimerGetCount() - T0);
			if(T1 <= 0) break; /* タイムアウト */
			pceFontSetPos((DISP_X - 5 * 9) / 2, DISP_Y - 16);
			/*             012345678 */
			pceFontPrintf("あと%3d秒", (T1 + 999) / 1000);
			pceLCDTrans();
		}
	}
	while(pcePadGetDirect() & PAD_A) { /* 開放待ち*/ }
	pceLCDSetBuffer(old_vbuff);
//	stderr_write("接続完了\n", 9); /* 標準エラー出力モニタに表示(キャンセル時は/dev/nulへ) */	⇒{{2014/03/13削除:接続完了時のメッセージが解析の邪魔になる事が有るため削除しました。}}
	/* 標準入力の、初回の受信要求を発行します。 */
	stdin_read(NULL, 0);
}

void stderr_close() {
	USBCOMINFO uci = { "" };
	/* 標準エラー出力を閉じます。
	 * * 単にpceUSBCOMStop()を呼んだだけでは、USBCOMが切断できません。
	 *   PCとの接続が確立していた場合、pceUSBCOMStop()は切断を行わないからです。
	 *   また、シグネチャもクリアされません。(以上、BIOS 1.18で確認)
	 * * この問題を回避するには、pceUSBCOMSetup()を使ってUSBCOMを切断します。
	 *   pceUSBCOMSetup()ならば、無条件にPC側の接続フラグをクリアするので、
	 *   いったんpceUSBCOMSetup()を使って：
	 *	mystat(P/ECE側の接続フラグ) -> 1
	 *	pcstat(PC側の接続フラグ)    -> 0
	 *	シグネチャ                  -> ""
	 *   としておき、あらためてpceUSBCOMStop()を呼んで：
	 *	mystat(P/ECE側の接続フラグ) -> 0
	 *   とします。
	 */
	pceUSBCOMSetup(&uci);
	pceUSBCOMStop();
	ucs = 0;	/* 蓄積変数をクリア */
}

void stderr_write(const void* ptr, int len) {
	/* 0バイトで送信要求を発行すると、絶対にUCS_TXDONEが立たないので、(usbcom.c: txsub() 参照)
	 * あらかじめ除外しておきます。
	 */
	if(len <= 0) return;
	/* PCとの接続が確立していたら…
	 * * PISTATが1でなければPCSTATも1になりません。(usbcom.c: usbcom() case13: (USBCOM Open) 参照)
	 *   PCSTATが1である間PISTATは0に戻りません。(usbcom.c: pceUSBCOMStop() 参照)
	 *   従って、PCSTATが1ならPISTATも必ず1です。PISTATを検査する必要はありません。
	 */
	ucs = pceUSBCOMGetStat() | (ucs & (UCS_RXSTAT|UCS_TXSTAT));	/* UCS_PCSTATとUCS_PISTATは即反映、UCS_RX*とUCS_TX*は蓄積 */
	if(ucs & UCS_PCSTAT) {
		/* 送信要求を発行します。 */
		pceUSBCOMStartTx(ptr, len);
		ucs &= ~UCS_TXSTAT;	/* UCS_TX*をクリア */
		/* 送信が完了するまで待ちます。 */
		for(;;) {
			ucs = pceUSBCOMGetStat() | (ucs & (UCS_RXSTAT|UCS_TXSTAT));	/* UCS_PCSTATとUCS_PISTATは即反映、UCS_RX*とUCS_TX*は蓄積 */
			if(!(ucs & UCS_PCSTAT)) { break; }	/* PCとの接続が切れた */
			if(  ucs & UCS_TXDONE ) { break; }	/* 送信が完了した */
		}
	}
}

int stdin_read(void* _buf, int bufsize) {
#define STDIN_RXSIZE (127/*調整可*/ + 1/*nul*/)
	static char*   rxbuf/*[STDIN_RXSIZE]*/;
	static char* keepbuf/*[STDIN_RXSIZE]*/;
	static int i_keepbuf;
	//
	char* buf = _buf;
	int i_buf = 0, c;
	/* PCとの接続が確立していたら…
	 * * PISTATが1でなければPCSTATも1になりません。(usbcom.c: usbcom() case13: (USBCOM Open) 参照)
	 *   PCSTATが1である間PISTATは0に戻りません。(usbcom.c: pceUSBCOMStop() 参照)
	 *   従って、PCSTATが1ならPISTATも必ず1です。PISTATを検査する必要はありません。
	 */
	ucs = pceUSBCOMGetStat() | (ucs & (UCS_RXSTAT|UCS_TXSTAT));	/* UCS_PCSTATとUCS_PISTATは即反映、UCS_RX*とUCS_TX*は蓄積 */
	if(ucs & UCS_PCSTAT) {
		/* 初回に受信バッファと保持バッファを確保します。こうすれば、stdin_read()が呼ばれなければ余分なメモリを消費しません。 */
		if(!rxbuf) {
			rxbuf = calloc(2, STDIN_RXSIZE);
			if(!rxbuf) { DIE(); }	/* メモリ不足 */
			keepbuf = rxbuf + STDIN_RXSIZE;
		}
		/* 書き込みポインタが、書き込みバッファのバイト数に達するまで… */
		while(i_buf < bufsize) {
			/* 保持バッファの文字が、まだ残っていたら… */
			if((c = keepbuf[i_keepbuf])) {
				/* 保持バッファから読み出した文字を、書き込みバッファに格納します。 */
				buf[i_buf++] = c;
				/* 保持バッファの読み出し位置を進めます。 */
				i_keepbuf++;
			/* 保持バッファの文字が、もう残っていなければ… */
			} else {
				/* 前回の受信要求が完了していなければ… */
				if(!(ucs & UCS_RXDONE)) { break; }	/* 受信要求が未発行ならば、この下で初回の受信要求を発行する。 */
				/* 前回の受信要求が完了していたら… */
				ucs &= ~UCS_RXSTAT;	/* UCS_RX*をクリア */			/* この下で次回の受信要求を発行する。 */
				/* 受信内容を保持バッファに保持します。受信バッファは、この後の受信要求発行で破壊されるからです。 */
				strcpy(keepbuf, rxbuf);
				/* 保持バッファの読み出し位置を、先頭に戻します。 */
				i_keepbuf = 0;
			}
		}
		/* 受信要求が発行されていなければ… */
		if(!(ucs & (UCS_RXWAIT|UCS_RXING))) { /* 受信待機中,又は,受信中 */
			/* 次回のために、受信バッファへの受信要求を発行しておく。 */
			pceUSBCOMStartRx(rxbuf, STDIN_RXSIZE - 1/*nul*/);
		}
	}
	/* 書き込みバッファに格納したバイト数を返します。 */
	return i_buf;
#undef  STDIN_RXSIZE
}

