#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
#include "ckw.h"			/* MAX_ansi, MAX_msc */

#define FINISHED	-1
#define IDENTIFIER	1
#define BLOCK		2
#define PROG_SIZE	65500
#define MAX_length	40		/* 40バイト以上の長さの識別子は扱わない.*/
#define MAX_Icount	1450	/* 内部関数の累積数 */
#define MAX_Ecount	100		/* 外部関数の個数 */
#define MAX_Buff	300		/* 1個の外部関数の中の内部関数の個数 */

enum	Int_func_spec{ETC,USER,ANSI,MSC,EGB,GDS,MOS,KYB};
											/*ユーザー,ANSI,MSC,その他関数*/

char	C_prog[PROG_SIZE];					/* 64k byte ファイル格納域*/
char 	Ext_func[MAX_Ecount][MAX_length];	/*  4k byte 外部関数の名前*/

struct {
		unsigned Efunc_no;					/*  3k byte 外部関数の番号*/
		char name[MAX_length];				/* 57k byte 内部関数の名前*/
		char spec;							/*  3k byte 内部関数の種類*/
		} Int_func[MAX_Icount];

typedef struct {							/* 内部関数用作業域 */
		char name[MAX_length];				/* 12k byte 内部関数の名前*/
		char spec;							/*  1k byte 内部関数の種類*/
		} BUFF;
		
BUFF	Buff[MAX_Buff];

char	token[80];
char	*prog;

char	keyword[]=
	 "case do for goto if else return sizeof switch while main defined char int void short long signed unsigned float double";
	 				/* 予約語のうち後ろにカッコが来る可能性があるもの*/

extern char ansi_func[MAX_ansi][10];
extern char msc_func[MAX_msc][17];

void file_read(char *prog, char *name);
void sorting(BUFF *, int , int );
void swap(BUFF *, int , int );
int get_token(void);
int isdelim(char c);
int iswhite(char c);
void putback(void);

void main(int argc, char **argv)
{
	static char temp[80];	/* 80バイト以上の長さの識別子はないと仮定した. */
	static char abc[512];
	int 	file,ret;
	unsigned i,j,k,l,p,q,r;
	unsigned len,Icount,Ecount,brace;
	char	*ppp;

	if(argc==1){
		printf("Usage: ckw.exe <filename or wild card>\n");
		exit(0);
	}

	printf("\t======== Ｃプログラム関数解析結果 ========<by yagioya '95.6.19>===");
    for(file=1; file<argc; file++){		/* 該当するファィルについて */

		prog=C_prog;					/* 初期化 */
		Icount=0;
		Ecount=0;
		brace=0;
		token[0]='\0';
		
		file_read(prog, argv[file]);	/* ファイルの読み込みと前処理 */

		while((ret=get_token())!=FINISHED){
			if(*token=='{')	brace++;	/* 大括弧の外か内か */
			if(*token=='}')	brace--;

			if(ret==IDENTIFIER){		/* 関数か変数 あるいはキーワード */
				strcpy(temp, token);
				get_token();
				if(*token=='('){		/* キーワードか関数 */
					if(strlen(temp)<MAX_length){/* MAX_length以上は採用せず */
						if(brace==0){	/* 外部関数か関数宣言かのいずれか */
							ppp=prog;
							for(i=0; *prog !=';' && i<512; i++) abc[i]=*prog++;
							abc[i]='\0';
							if( strchr(abc, '{') )	/* 関数と関数宣言の区別 */
								strcpy(Ext_func[Ecount++], temp);
							prog=ppp;
						}
						else {
							strcpy(Int_func[Icount].name, temp);/* 内部関数 */
							Int_func[Icount].Efunc_no=Ecount-1;
							Icount++;
						}
					}
					if(Icount>=MAX_Icount || Ecount>=MAX_Ecount){
					 	printf("抽出した関数(内部=%u,外部=%u)",Icount,Ecount);
					 	printf("が多すぎるのでファイルを分割してください.\n");
						exit(0);
					}
				}
				else putback();
			}
		}

		for(i=0,j=0; i<Icount; i++){	/* キーワードと一致するものを除く */
			if( strstr(keyword,Int_func[i].name)==NULL){
				strcpy(Int_func[j].name,Int_func[i].name);
				Int_func[j].Efunc_no=Int_func[i].Efunc_no;
				j++;
			}
		}
		Icount=j;

		for(j=0; j<Icount; j++){
			Int_func[j].spec=ETC;
			for(i=0; i<Ecount; i++){/* 同じソースの中で定義された関数=USER */
				if(strcmp(Ext_func[i],Int_func[j].name)==0){
					Int_func[j].spec=USER;
					goto END;
				}
			}
			for(i=0; i<MAX_ansi; i++){/* ANSI-C関数なら=ANSI */
				if(strcmp(ansi_func[i],Int_func[j].name)==0){
					Int_func[j].spec=ANSI;
					goto END;
				}
			}
			for(i=0; i<MAX_msc; i++){/* MS-C関数(V6.00)なら=MSC */
				if(strcmp(msc_func[i],Int_func[j].name)==0){
					Int_func[j].spec=MSC;
					goto END;
				}
			}
			END:;

			if(strstr(Int_func[j].name, "EGB_")!=NULL)
				Int_func[j].spec=EGB;

			if(strstr(Int_func[j].name, "GDS_")!=NULL)
				Int_func[j].spec=GDS;

			if(strstr(Int_func[j].name, "MOS_")!=NULL)
				Int_func[j].spec=MOS;

			if(strstr(Int_func[j].name, "KYB_")!=NULL)
				Int_func[j].spec=KYB;

		}
		
		for(i=0; i<Ecount; i++){		/* 結果の出力 */
			printf("\n%s",Ext_func[i]);/* 外部関数の名前を呼び出して印字 */
			len=strlen(Ext_func[i]);/* 画面の化粧. 75文字まで----------- */
			for(j=0; j<(unsigned)(76-len); j++) printf("-");
			printf("\n");
			for(k=0,l=0; k<Icount; k++){
				if(Int_func[k].Efunc_no==i){	/* 同じ外部関数番号であれば */
					strcpy(Buff[l].name,Int_func[k].name);	/* 全て登録 */
					Buff[l].spec=Int_func[k].spec;
					l++;
				}
			}

			sorting(Buff, 0, l-1 );		/* 内部関数の名前の若い順番に整列 */
			for(p=0, q=1, r=0; p<l; ){	/* 重複しているものを整理 */
				while( strcmp(Buff[p].name,Buff[p+q].name )==0)	q++;
				strcpy(Buff[r].name,Buff[p+q-1].name);
				Buff[r].spec=Buff[p+q-1].spec;
				r++;
				p=p+q;
				q=1;
			}	

			for(p=0; p<r; p++){		/*印刷 */
				if(Buff[p].spec==USER) 
					strcpy(temp,"本ソース中での定義関数");
				else if(Buff[p].spec==ANSI) 
					strcpy(temp,"ANSI-Cの標準関数");
				else if(Buff[p].spec==MSC) 
					strcpy(temp,"MS-Cの標準関数");
				else if(Buff[p].spec==EGB || Buff[p].spec==GDS) 
					strcpy(temp,"グラフィックライブラリ");
				else if(Buff[p].spec==MOS) 
					strcpy(temp,"マウスライブラリ");
				else if(Buff[p].spec==KYB) 
					strcpy(temp,"キーボードライブラリ");
				else strcpy(temp,"他ソースでの定義関数");
				printf("\t%-40s:%s\n",Buff[p].name,temp);
			}
		}
	}								/* wild card の終わり */
}

/***** ファイルの読み込みと注釈文､ コメントの排除 *******/
void file_read(char *prog, char *name)
{
	FILE 	*fp;
	char 	c;
	unsigned i,j;
	unsigned size;
	
	
   	if ((fp = fopen(name, "rb")) == NULL){	/* ファイルオープン*/
   		printf("\n%s file open error\n",name );
   		exit(0);
   	}

	/* 読み込み*/
   	printf("\n\n<< %s >>\n",name);
	for(size=0; (c=getc(fp))!=EOF;	) {
		if( size>=PROG_SIZE ){
			printf("ファイルが大きすぎて、全てを読み込んでおりません.");
			printf("%u,%uバイト分処理しました.\n" ,size/1000,size%1000);
			break;
		}
		*(prog+size)=c;
		size++;
	}
	*(prog+size)='\0';
	fclose(fp);

	for(i=0,j=0; i<size ;i++){	/* コメントの排除 */
		if(*(prog+i)=='/' && *(prog+i+1)=='*' ){ 
			while(*(prog+i+2)!='*' || *(prog+i+3)!='/')	i++;
			i+=3;
		}
		else 
		{
			*(prog+j)=*(prog+i);
			j++;
		}
	}
	*(prog+j)='\0';

	size=j;
	for(i=0,j=0; i<size; i++){	/* プリブロセス文字列の排除 */
		if(*(prog+i)=='\'' && *(prog+i+1)=='#' && *(prog+i+2)=='\'')
			i+=2;
		else if(*(prog+i)=='#' ){ 
			while(*(prog+i+1)!='\r') i++;
			i+=2;
		}
		else
		{	*(prog+j)=*(prog+i);
			j++;
		}
	}
	*(prog+j)='\0';

	size=j;
	for(i=0,j=0; i<size; i++){	/* 文字列の排除 */
		if(*(prog+i)=='\'' && *(prog+i+1)=='"' && *(prog+i+2)=='\'')
			i+=2;
		else if(*(prog+i)=='"' ){ 
			while(*(prog+i+1)!='"') i++;
			i++;
		}
		else
		{	*(prog+j)=*(prog+i);
			j++;
		}
	}
	*(prog+j)='\0';

	size=j;
	for(i=0,j=0; i<size; i++){	/* ｢'{','}'｣を取り除く */
		if(*(prog+i)=='\'' && *(prog+i+1)=='{' && *(prog+i+2)=='\'')
			i+=2;
		else if(*(prog+i)=='\'' && *(prog+i+1)=='}' && *(prog+i+2)=='\'')
			i+=2;
		else 
		{	*(prog+j)=*(prog+i);
			j++;
		}
	}
	*(prog+j)='\0';

/*	printf("%s\n",prog); exit(0);	*/
}

/******** トークンの取り出し *********/
int get_token(void)
{
	char *temp;

	temp=token;
	*temp='\0';
	
	/* 空白文字の読み飛ばし */
	while(iswhite(*prog)&& *prog) ++prog;

	if(*prog=='\0'){		/* ファイルの終わり */
		*token='\0';
		return(FINISHED);
	}
	
	if(strchr("{}", *prog)){/* ブロックのデリミタ */
		*temp=*prog;
		temp++;
		*temp='\0';
		prog++;				/* 必ず次の位置へポインタを進めておく */
		return(BLOCK);
	}

	if(isalpha(*prog) || *prog=='_'){	/* 変数かコマンド =IDENTIFIER */
		while( !isdelim(*prog)) *temp++ =*prog++;
		*temp='\0';
		return (IDENTIFIER);
	} else {
		*temp=*prog;
		prog++;	
		temp++;
		*temp='\0';
		return(0);
	}
}

int isdelim(char c)
{
	if(strchr(" !;,+-<>'/%*^=()&|[]{}@?",c) || c=='\t' || c=='\r' || c=='\0') 
		return 1;
	else return 0;
}

int iswhite(char c)
{
	if(c==0x20 || c=='\t') return 1;
	else return 0;
}

/* トークンを入力ストリームへ戻す */
void putback(void)
{
	char *t;
	
	t=token;
	for(; *t; t++) prog--;
}

/***** 石田晴久 訳 ｢プログラミング言語Ｃ｣(共立出版) を参照 *****/
void sorting(BUFF *Buff, int left, int right)
{
	int	i,last;
	
	if(left >= right)	return;		
	swap(Buff, left, (left+right)/2);
	last = left;					
	for(i=left+1; i<=right; i++)
		if( strcmp(Buff[i].name, Buff[left].name)<0 )	swap(Buff, ++last, i);
	swap(Buff, left, last);
	sorting(Buff, left, last-1);
	sorting(Buff, last+1, right);
}

void swap(BUFF *Buff, int i, int j)
{
	static char	temp[MAX_length];
	char	k;
	
	strcpy(temp , Buff[i].name);
	k=Buff[i].spec;
	strcpy(Buff[i].name , Buff[j].name);
	Buff[i].spec=Buff[j].spec;
	strcpy(Buff[j].name , temp);
	Buff[j].spec=k;
}
