// FAT32でファイルのFATつながりを表示する

#include <stdio.h>
#include <stdlib.h>	// exit
#include "defines.h"
#include "astdio.h"
#include "aparam.h"
#include "fat32.h"

char prog[]="@@> FATCHK32.C : 2004/09/20 MON : COMMAND : FATCHK32 : X680x0 : GCC";
/*
2000/09/27	初版
2000/10/02	/NODISP 表示しないでチェックだけするモード
		ＦＡＴチェック強化
		ボリューム名もチェック対象になっていたのを修正
2000/10/03	ロングファイル名対応
2000/10/19	ファイル名長定数をfat32.hのそれに合わせて変更
		ロングファイル名関数差し替えに対応（実行ファイル大幅サイズ縮小）
2004/09/20	/SECT=先頭セクターのみ表示追加
*/

unlong Fat2Sec0(FILE32 *fp)
// FAT番号からセクター番号に変換する
{
	// この変換式はcluread.cにある
	return(
		fp->secdata
			+((fp->fat-fp->fatroot)*fp->clu1sec)
	);
}

int FatChain(FILE32 *fp,int fsec,int fnodisp)
// １対象のチェイン
{
unlong fat,fatlast=0L;
unlong size=0;	// サイズチェック
int fren=1;	// 連続性チェック
unchar ftop=0;
	fat=fp->topfat;
	goto FAT1;
	
	// つながり
	while ((fat=NextFat(fp,fp->fat))!=ENDofFATs) {
		if (fat==0L) {
			printf2("ＦＡＴ領域が読めません\n"); // FAT読み込みに失敗した
			return(1);
		}
		if (fat!=fp->fat+1) fren=0; // 連続でない
	    FAT1:;
	    	fp->fat=fat;
	    	if (fnodisp!=1) {
			if (ftop==0) {
				printf2("$%08x",fat);
				if (fnodisp==2) fnodisp=1; // 先頭のみ
			}
			if (fsec) printf2("($%08x)",Fat2Sec0(fp));
		}
		// チェック用集計
		size+=(unlong)fp->clu1byte;
		if (fatlast<fat) fatlast=fat; // 最大FATの検知
		else if (fat==fatlast) { // ループしている
			printf2(";ループ異常");
		    	if (!fnodisp) { // ,は次の\b対策
				printf2(",");
			}
			break; // ループを続けても仕方がないので切る
		}
		if (fat>=fp->allsec/fp->clu1sec) { // 有効セクターより大きい
			printf2(";ＦＡＴ異常（大きい）");
		    	if (!fnodisp) { // ,は次の\b対策
				printf2(",");
			}
			break; // 異常なＦＡＴを続けても仕方がないので切る
		}
	    	if (!fnodisp) {
			printf2(",");
		}
	}
	if (!fnodisp) {
		printf2("\b \b"); // 最後の,を消す
	}
	if (fren) printf2(";連続");
	if (!fp->atr&FA_DIREC) { // ファイルのみ
		if (size<fp->filelen) printf2(";サイズ不足異常");
		if (size>((fp->filelen+fp->clu1byte-1)/fp->clu1byte)*fp->clu1byte) {
			printf2(";サイズ過剰異常");
		}
	}
	printf2("\n");
	return(0);
}

int ChkFat32(unchar *path32,int fsize,int fsec,int flong,int fnodisp)
// 全対象のチェイン
{
FILE32 fp;
int ret,ret2=1;
	// 対象はファイルとディレクトリ
	ret=VFILES32(&fp,path32,FA_ARCH|FA_DIREC);
	if (ret<0) {
		printf2("ファイル／ディレクトリが見つかりません\n");
		return(1);
	}
	do {
		if (fp.atr&FA_VOL) continue; // volumeなのにFA_ARCHが立っていることがあるので
		
		// ファイル名
		pr_spcleft(fp.name,MAXSHORTFILEEXT32);
		if (flong) printf2("\t%s",fp.lname);
		
		// ファイル長さ表示（ファイルのみ）
		if (fsize) {
			if (fp.atr&FA_ARCH) {
				printf2("\t%11d",fp.filelen);
			} else { // ディレクトリ
				printf2("\t-----------");
			}
		}
	    	if (!fnodisp) printf2("\n");
		
		// １つのチェインチェック
		ret2=FatChain(&fp,fsec,fnodisp);
		if (ret2) break; // エラー
		
		// 次の対象へ
	} while ((ret=NVFILES32(&fp))>=0);
	return(ret2);
}

void main(int argc,unchar *argv[])
{
int i,j,hflag=0,ret;
int fsize,fsec,flong,fnodisp;
	/* check parameter */
	/* オプションチェック初期化 */
	clear_param();
    HELP:
	if (helpcom(argc,argv,hflag,"FATCHK32","[Options] FAT32フルパス名")) {
		exit(hflag? hflag:255);
	}
	
	fsize  =check_param2("SIZE"  ,argv,argc,0); // ファイルサイズ表示
	flong  =check_param2("LONG"  ,argv,argc,0); // ロングファイル名も表示
	fsec   =check_param2("SEC"   ,argv,argc,0); // セクター番号も表示
	// 表示
	fnodisp=0;
	if (check_param2("NODISP",argv,argc,0)) fnodisp=1; // 表示無し（チェックのみ）
	if (check_param2("TDISP" ,argv,argc,0)) fnodisp=2; // 先頭のみ表示
	
	/* オプションチェック */
	i=check_param_error(argv,argc);
	if (i) {
		printf2("オプションエラーです：%s\n",argv[i]);
		exit(3);
	}
	
	// パス名指定
	i=getfname(argc,argv,1,NULL);
	if (i==0) { // パス指定が１つもない
		hflag=100;
		goto HELP;
	}
	
	if (OpenUni2SJisF(argv[0])) {
		printf2("Unicode関数が初期化出来ない\n");
		exit(100);
	}
	j=2; // 次は２番目
	do {
		ret=ChkFat32(argv[i],fsize,fsec,flong,fnodisp);
		if (ret) break;
		i=getfname(argc,argv,j++,NULL);
	} while (i!=0);
	
	CloseUni2SJisF();
	exit(ret==0);
}
