/****************************************
 *   Program up-date manager : update   *
 *       Program made by  K,Ajima       *
 *     Copyright ajiyan soft l.t.d.     *
 ****************************************/



#if 0
/*** update **************************************************************

  プログラムのバージョン管理を自動的に行い、プログラマの無駄な労力を削減し
ます。

**************************************************************************/
#endif



#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <errno.h>

#include <findf.h>
#include <str.h>
#include <err.h>

#include "update.dtv"



static int upd_hd;									/* ファイルハンドル */

static char src_name[128],							/* ファイル名 */
			upd_name[128],
			dtv_name[128];

static unsigned long src_ts,						/* タイムスタンプ */
					 upd_ts;

static char version,								/* バージョン番号 */
			major,									/* メジャー番号 */
			minor,									/* マイナー番号 */
			alpha;									/* タイプアルファベット */



					/* エラーコード */
#define ERR_CANT_OPEN		1
#define ERR_CANT_CREAT		2
#define ERR_CANT_WRITE		3
#define ERR_CANT_READ		4



										/* エラーメッセージ */
static const char * MSG_CANT_OPEN		= "fatal error : Can\'t open : ";
static const char * MSG_CANT_CREAT		= "fatal error : Can\'t creat : ";
static const char * MSG_CANT_WRITE		= "fatal error : Can\'t write : ";
static const char * MSG_CANT_READ		= "fatal error : Can\'t read : ";



#define DTV_STR(x)			#x
#define DTV_ADD(x,y,z,a,b)	DTV_STR(x) DTV_STR(y) DTV_STR(z) DTV_STR(a) DTV_STR(b)
#define DTV_ADDVER()		DTV_ADD(DTV_VERSION,.,DTV_MAJOR,DTV_MINOR,DTV_ALPHA)
#define DTV_ADDTS()			DTV_ADD(DTV_YEAR,/,DTV_MANTH,/,DTV_DAY)



/********************************************************************
 *	初期化処理
 ********************************************************************/
void _cdecl start_up(void)
{
}



/********************************************************************
 *	オープニングメッセージ
 ********************************************************************/
void _cdecl opn_msg(void)
{
	static const char * msg[] = {
		"Program up-date manager : update ver. " DTV_ADDVER(),
		"Program made by K,Ajima",
		"Copyright ajiyan soft l.t.d.   " DTV_ADDTS(),
		"",
		NULL,
	};

	int i;

	for(i=0;msg[i]!=NULL;i++) {
		cputs(msg[i]); cputs("\x0d\x0a");
	}
}



/********************************************************************
 *	ヘルプ
 ********************************************************************/
void _cdecl help(void)
{
	static const char * msg[] = {
		"Syntax : update < src file > ...",
		"  you can set wild card at < src file >",
		"",
		NULL
	};

	int i;

	for(i=0;msg[i]!=NULL;i++) {
		cputs(msg[i]); cputs("\x0d\x0a");
	}
}



/********************************************************************
 *	タイムスタンプ設定
 *		1. 設定するポインタ
 *		2. 時刻(DOS form)
 *		3. 日時(DOS form)
 ********************************************************************/
void _fastcall set_ts(unsigned long * ts,unsigned int time,unsigned int date)
{
	*ts=time | ((unsigned long)(date) << 16L);
}



/********************************************************************
 *	upd ファイルへ書き込み
 ********************************************************************/
void _cdecl write_upd(void)
{
	char wr_data[4];

	wr_data[0]=version;
	wr_data[1]=major;
	wr_data[2]=minor;
	wr_data[3]=alpha;

	if(lseek(upd_hd,0L,SEEK_SET)==-1) {				/* シーク */
		out_err(ERR_CANT_WRITE,MSG_CANT_WRITE,upd_name,NULL);
	}

	if(write(upd_hd,wr_data,4)==-1) {				/* ライト */
		out_err(ERR_CANT_WRITE,MSG_CANT_WRITE,upd_name,NULL);
	}
}



/********************************************************************
 *	エラーチェック付き write()
 *		1. ハンドル
 *		2. バッファ
 *		3. 長さ
 ********************************************************************/
void _fastcall as_write(int hd,char * buff,unsigned int len)
{
	if(write(hd,buff,len)==-1) {
		out_err(ERR_CANT_WRITE,MSG_CANT_WRITE,dtv_name,NULL);
	}
}



/********************************************************************
 *	dtv ファイルへ書き込み
 ********************************************************************/
void _cdecl write_dtv(void)
{
	char wr_buff[128];
	int dtv_hd;

	cprintf("Writing %s and %s : %d.%d%d%c\x0d\x0a",
					upd_name,
					dtv_name,
					version,
					major,
					minor,
					alpha);

	if((dtv_hd=open(dtv_name,O_BINARY | O_CREAT | O_RDWR | O_TRUNC,S_IREAD | S_IWRITE))==-1) {
		out_err(ERR_CANT_OPEN,MSG_CANT_OPEN,dtv_name,NULL);
	}

													/* コメント */
	sprintf(wr_buff,"/* %s writen by update */\x0d\x0a\x0d\x0a",upd_name);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* バージョン */
	sprintf(wr_buff,"#define DTV_VERSION\t\t%u\x0d\x0a",version);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* メジャー */
	sprintf(wr_buff,"#define DTV_MAJOR\t\t%u\x0d\x0a",major);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* マイナー */
	sprintf(wr_buff,"#define DTV_MINOR\t\t%u\x0d\x0a",minor);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* アルファ */
	sprintf(wr_buff,"#define DTV_ALPHA\t\t%c\x0d\x0a\x0d\x0a",alpha);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 年 */
	sprintf(wr_buff,"#define DTV_YEAR\t\t%d\x0d\x0a",(src_ts>>25)+1980);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 月 */
	sprintf(wr_buff,"#define DTV_MANTH\t\t%d\x0d\x0a",((src_ts>>21)&15));
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 日 */
	sprintf(wr_buff,"#define DTV_DAY\t\t\t%d\x0d\x0a\x0d\x0a",((src_ts>>16)&31));
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 時 */
	sprintf(wr_buff,"#define DTV_HOUR\t\t%d\x0d\x0a",((src_ts>>11)&31));
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 分 */
	sprintf(wr_buff,"#define DTV_MINUTE\t\t%d\x0d\x0a",((src_ts>>5)&63));
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 秒 */
	sprintf(wr_buff,"#define DTV_SECOND\t\t%d\x0d\x0a\x0d\x0a",(src_ts&31)<<1);
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 専用マクロ 1 */
	strcpy(wr_buff,"#define DTV_STR(x)\t\t\t#x\x0d\x0a");
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 専用マクロ 2 */
	strcpy(wr_buff,"#define DTV_ADD(x,y,z,a,b)\tDTV_STR(x) DTV_STR(y) DTV_STR(z) DTV_STR(a) DTV_STR(b)\x0d\x0a");
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 専用マクロ 3 */
	strcpy(wr_buff,"#define DTV_ADDVER()\t\tDTV_ADD(DTV_VERSION,.,DTV_MAJOR,DTV_MINOR,DTV_ALPHA)\x0d\x0a");
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

													/* 専用マクロ 4 */
	strcpy(wr_buff,"#define DTV_ADDTS()\t\t\tDTV_ADD(DTV_YEAR,/,DTV_MANTH,/,DTV_DAY)\x0d\x0a");
	as_write(dtv_hd,wr_buff,strlen(wr_buff));

	close(dtv_hd);
}



/********************************************************************
 *	新しい upd ファイル作成
 ********************************************************************/
void _cdecl make_new_upd(void)
{
	int c,flag=0;

	cputs("File not found : "); cputs(upd_name); cputs("\x0d\x0a");
	cputs("Creat ? [y/n] : ");

	while(!flag) {
		c=getch();
		switch(tolower(c)) {
			case 'y': cputs("y\x0d\x0a"); flag=1; break;
			case 'n': cputs("n\x0d\x0a"); return;
			default : putch('\x07'); break;
		}
	}

	if((upd_hd=open(upd_name,O_BINARY | O_RDWR | O_CREAT | O_EXCL,S_IREAD | S_IWRITE))==-1) {
		out_err(ERR_CANT_CREAT,MSG_CANT_CREAT,upd_name,NULL);
	}

	version	=1;
	major	=0;
	minor	=0;
	alpha	='a';

	write_upd(); write_dtv();

	close(upd_hd);
}



/********************************************************************
 *	upd ファイルステータス読み込み
 ********************************************************************/
void _cdecl get_upd_stat(void)
{
	char rd_data[4];
	unsigned int date,time;

	if((upd_hd=open(upd_name,O_BINARY | O_RDWR))==-1) {
		out_err(ERR_CANT_OPEN,MSG_CANT_OPEN,upd_name,NULL);
	}

	if(lseek(upd_hd,0L,SEEK_SET)==-1L) {
		out_err(ERR_CANT_READ,MSG_CANT_READ,upd_name,NULL);
	}

	if(read(upd_hd,rd_data,4)==-1) {
		out_err(ERR_CANT_READ,MSG_CANT_READ,upd_name,NULL);
	}

	version	=rd_data[0];
	major	=rd_data[1];
	minor	=rd_data[2];
	alpha	=rd_data[3];

	cprintf("Reading %s : %d.%d%d%c\x0d\x0a",
					upd_name,
					version,
					major,
					minor,
					alpha);

	_dos_getftime(upd_hd,& date,& time);
	set_ts(& upd_ts,time,date);
}



/********************************************************************
 *	新しい dtv ファイル作成
 ********************************************************************/
void _cdecl make_new_dtv(void)
{
	int c,flag=0;
	long temp;

	cputs("Input up date mode [ 0:No 1:version 2:major 3:minor 4:alpha] : ");

	while(!flag) {
		c=getch();
		switch(c) {
			case '0':
				flag=1;
				break;

			case '1':
				version++;
				major=0;
				minor=0;
				alpha='a';
				flag=1;
				break;

			case '2':
				major++;
				minor=0;
				alpha='a';
				flag=1;
				break;

			case '3':
				minor++;
				alpha='a';
				flag=1;
				break;

			case '4':
				alpha++;
				flag=1;
				break;

			default:
				putch('\x07');
				break;
		}
	}
	putch(c); cputs("\x0d\x0a");

	temp=
		version	*10L	*10L	*6L	+
		major			*10L	*6L	+
		minor					*6L	+
		alpha-'a'			;

	version	=(char)(temp/(10L*10L*6L));
	major	=(char)(temp/(10L*6L)%10L);
	minor	=(char)(temp/6L%10L);
	alpha	=(char)(temp%6L+'a');

	write_upd(); write_dtv();
}



/********************************************************************
 *	メイクアップ
 ********************************************************************/
void _cdecl upd_make(void)
{
	cprintf("===== %s =====\x0d\x0a",src_name);

	if(access(upd_name,6)) {
		make_new_upd();									/* 新しい upd ファイル作成 */
	}
	else {
		get_upd_stat();									/* upd ステータス読み込み */
		if(src_ts>upd_ts) {
			make_new_dtv();									/* 新しい dtv ファイル作成 */
		}
		else {
			cputs("This file is no changed\x0d\x0a");
		}
		close(upd_hd);
	}
	cputs("\x0d\x0a");
}



/********************************************************************
 *	メイン処理
 *		1. argc
 *		2. argv
 ********************************************************************/
void _fastcall upd_main(int argc,char ** argv)
{
	FND_FILE_LIST * root,							/* ファイル検索データ */
				  * list;
	int i;											/* argv[] 添字 */
	unsigned int j,num;								/* カウント */

	for(i=1;i<argc;i++) {
														/* ワイルドカード展開 */
		root=fnd_get_file_list(argv[i],_A_NORMAL | _A_RDONLY | _A_ARCH,& num);
		if(!num) {
															/* ファイルがない */
			out_err(ERR_CANT_OPEN,MSG_CANT_OPEN,argv[i],NULL);
		}

		list=root;
		for(j=0;j<num;j++) {
			str_get_path(src_name,argv[i]);					/* ファイル名作成 */
			strcat(src_name,list->name);
			str_set_extname(upd_name,list->name,".upd");
			str_set_extname(dtv_name,list->name,".dtv");

			set_ts(& src_ts,list->wr_time,list->wr_date);	/* ソースファイルタイムスタンプ取得 */

			upd_make();

			list=list->next;
		}

	}
}



/********************************************************************
 *	main
 ********************************************************************/
void _cdecl main(int argc,char **argv)
{
	start_up();										/* 初期化処理 */

	opn_msg();										/* メッセージ */

	if(argc==2 && !strcmp(argv[1],"/?")) {
		help();											/* ヘルプ */
		exit(0);
	}

	upd_main(argc,argv);							/* メイン処理 */

	exit(0);
}

