Artifact Content

Not logged in

Artifact 764020530952f7eeb5a79f8e11ade0a8407ab589


// NoahAM.cpp
//-- control many archiver routines --

#include "stdafx.h"
#include "resource.h"
#include "NoahApp.h"
#include "NoahAM.h"
#include "ArcDLL.h"
#include "ArcAce.h"
#include "ArcMsc.h"
#include "ArcB2e.h"
#include "ArcCpt.h"

//----------------------------------------------//
//------ 実働部隊のデータで初期化しておく ------//
//----------------------------------------------//

void CNoahArchiverManager::init()
{
	const char* kl = mycnf().kill();
	static int dead[128];
	while( *kl ) dead[ 0x7f & (*(kl++)) ] = 1;

	// 初期対応形式
	if( !dead['L'] )	m_AList.add( new CArcLzh );
	if( !dead['7'] )	m_AList.add( new CArc7z ),
						m_AList.add( new CArc7zZip );
	if( !dead['Z'] )	m_AList.add( new CArcUnZip );
	if( !dead['z'] )	m_AList.add( new CArcZip );
	if( !dead['T'] )	m_AList.add( new CArcTar );
	if( !dead['C'] )	m_AList.add( new CArcCab );
	if( !dead['R'] )	m_AList.add( new CArcRar );
	if( !dead['A'] )	m_AList.add( new CArcArj );
	if( !dead['B'] )	m_AList.add( new CArcBga );
	if( !dead['Y'] )	m_AList.add( new CArcYz1 );
	if( !dead['G'] )	m_AList.add( new CArcGca );
	if( !dead['a'] )	m_AList.add( new CArcAce );
	if( !dead['M'] )	m_AList.add( new CArcMsc );
	if( !dead['c'] )	m_AList.add( new CArcCpt );

	// 拡張スクリプトロード
	char prev_cur[MAX_PATH];
	::GetCurrentDirectory(MAX_PATH, prev_cur);
	::SetCurrentDirectory( CArcB2e::init_b2e_path() );
	kiFindFile find;
	find.begin( "*.b2e" );
	WIN32_FIND_DATA fd;
	for( int t=0; find.next(&fd); t++ )
		m_AList.add( new CArcB2e(fd.cFileName) );
	m_b2e = (t>1);
	::SetCurrentDirectory(prev_cur);
}

//----------------------------------------------//
//------------ ファイルリストを記憶 ------------//
//----------------------------------------------//

unsigned long CNoahArchiverManager::set_files( const cCharArray& files )
{
	//-- クリア
	m_FName.empty();
	m_BasePathList.empty();

	//-- 基底パスを取得( 出来るだけ利用範囲を広げるため、8.3形式で )
	if( files.len() != 0 )
	{
		char spath[MAX_PATH];
		m_BasePath =
			( 0!=::GetShortPathName( files[0], spath, MAX_PATH ) )
			? spath : "";
		if( !m_BasePath.beDirOnly() )
		{
			m_BasePath.beSpecialPath( kiPath::Cur );
			m_BasePath.beBackSlash( true );
		}
	}

	//-- 短いファイル名と長いのを両方取得しておく
	m_FName.alloc( files.len() );
	m_BasePathList.alloc( files.len() );
	for( unsigned int i=0,c=0; i!=files.len(); i++ )
		if( kiFindFile::findfirst( files[i], &m_FName[c] ) )
		{
			if( m_FName[c].cAlternateFileName[0] == '\0' )
				::lstrcpy(m_FName[c].cAlternateFileName,m_FName[c].cFileName);
			m_BasePathList[c] = files[i];
			if( !m_BasePathList[c].beDirOnly() )
			{
				m_BasePathList[c].beSpecialPath( kiPath::Cur );
				m_BasePathList[c].beBackSlash( true );
			}
			++c;
		}
	m_FName.forcelen( c );
	m_BasePathList.forcelen( c );
	return c;
}

//----------------------------------------------//
//--- ファイルリストに解凍ルーチンを割り当て ---//
//----------------------------------------------//

// 指定された拡張子に対応しているルーチンを線形探索
CArchiver* CNoahArchiverManager::fromExt( const char* ext )
{
	kiStr tmp = ext;
	tmp.lower();

	for( unsigned int i=0; i!=m_AList.len(); i++ )
		if( m_AList[i]->extCheck( tmp ) 
		 && (m_AList[i]->ability() & aMelt) )
			return m_AList[i];
	return NULL;
}

bool CNoahArchiverManager::map_melters( int mode ) // 1:cmp 2:mlt 3:must_mlt
{
	// クリア
	m_Melters.empty();

#define attrb (m_FName[ct].dwFileAttributes)
#define lname (m_FName[ct].cFileName)
#define sname (m_FName[ct].cAlternateFileName[0]==0 ? m_FName[ct].cFileName : m_FName[ct].cAlternateFileName)

	kiPath fnm;
	const char* ext;
	for( unsigned int ct=0, bad=0; ct!=file_num(); ct++ )
	{
//		fnm = m_BasePath, fnm += sname;
		fnm = m_BasePathList[ct], fnm += sname;

		//-- 0byteファイル / ディレクトリは弾く
		if( !(attrb & FILE_ATTRIBUTE_DIRECTORY) && 0!=kiFile::getSize( fnm, 0 ) )
		{
			//-- まず対応拡張子かどうかで候補Aを一つ選出
			CArchiver* x = fromExt( ext=kiPath::ext(lname) );

			//-- 候補Aで、ファイル内容によるチェック
			if( x && x->check( fnm ) )
			{
				m_Melters.add( x );
				continue;
			}

			//-- 候補Aが内容チェック不可なものだったらそれを使う
			if( x && !(x->ability() & aCheck) )
			{
				m_Melters.add( x );
				continue;
			}

			//-- 候補Aがダメなら、その他の内容チェック可能なルーチン全てで試す
			if( mode!=1 || 0==ki_strcmpi( "exe", ext ) )
			{
				for( unsigned long j=0; j!=m_AList.len(); j++ )
					if( m_AList[j]!=x && m_AList[j]->check( fnm ) )
					{
						m_Melters.add( m_AList[j] );
						break;
					}
				if( m_Melters.len() == ct+1 )
					continue;
			}
		}

		//-- チェックの結果、解凍不能でしたとさ
		if( mode!=3 )
			return false; //-- 解凍専用モードでなければ終了
		m_Melters.add( NULL ), bad++;
	}
#undef sname
#undef lname
#undef attrb

	return (ct!=bad);
}

//----------------------------------------------//
//--- ファイルリストに圧縮ルーチンを割り当て ---//
//----------------------------------------------//

bool CNoahArchiverManager::map_compressor( const char* ext, const char* method, bool sfx )
{
	int m;
	m_Method = -1;
	m_Sfx    = sfx;

	for( unsigned int i=0; i!=m_AList.len(); i++ )
		if( -1 != (m=m_AList[i]->cancompressby(ext,method,sfx)) )
			if( m!=-2 ) // 完全一致
			{
				m_Compressor = m_AList[i];
				m_Method = m;
				break;
			}
			else if( m_Method == -1 ) // 形式名のみ一致した最初のモノ
			{
				m_Compressor = m_AList[i];
				m_Method = m_AList[i]->cmp_mhd_default();
			}
	return (m_Method != -1);
}

//----------------------------------------------//
//------------ バージョン情報文字列 ------------//
//----------------------------------------------//

void CNoahArchiverManager::get_version( kiStr& str )
{
	kiStr tmp;
	for( unsigned int i=0; i!=m_AList.len(); i++ )
		if( m_AList[i]->ver( tmp ) )
			str+=tmp, str+="\r\n";
}

//----------------------------------------------//
//--------------- 圧縮形式リスト ---------------//
//----------------------------------------------//

static unsigned int find( const cCharArray& x, const char* o )
{
	for( unsigned int i=0; i!=x.len(); i++ )
		if( 0==ki_strcmp( x[i], o ) )
			return i;
	return 0xffffffff;
}

static unsigned int find( const StrArray& x, const char* o )
{
	for( unsigned int i=0; i!=x.len(); i++ )
		if( x[i]==o )
			return i;
	return 0xffffffff;
}

void CNoahArchiverManager::get_cmpmethod(
		const char* set,
		int& def_mhd,
		StrArray& mhd_list,
		bool need_ext,
		cCharArray* ext_list )
{
	def_mhd = -1;

	const char* x;
	for( unsigned int i=0; i!=m_AList.len(); i++ )
	{
		if( *(x = m_AList[i]->cmp_ext())=='\0' )
			continue;
		if( need_ext )
		{
			if( -1 == find( *ext_list, x ) )
				ext_list->add( x );
		}
		if( 0 == ki_strcmp( set, x ) )
		{
			if( mhd_list.len()==0 )
			{
				def_mhd = m_AList[i]->cmp_mhd_default();
				for( unsigned int j=0; j!=m_AList[i]->cmp_mhd_list().len(); j++ )
					mhd_list.add( (m_AList[i]->cmp_mhd_list())[j] );
			}
			else
			{
				for( unsigned int j=0; j!=m_AList[i]->cmp_mhd_list().len(); j++ )
					if( -1 == find( mhd_list, (m_AList[i]->cmp_mhd_list())[j] ) )
						mhd_list.add( (m_AList[i]->cmp_mhd_list())[j] );
			}
		}
	}

	if( def_mhd == -1 )
		def_mhd = 0;
}

//----------------------------------------------//
//--------------- 書庫一覧モード ---------------//
//----------------------------------------------//

#include "SubDlg.h"

void CNoahArchiverManager::do_listing( kiPath& destdir )
{
	kiWindow* mptr = app()->mainwnd();
	kiPath ddir;
	int    mdf = mycnf().mkdir();
	bool   rmn = mycnf().mnonum();
	destdir.beBackSlash( true );

	//-- ダイアログの個数カウンタをクリア
	kiArray<CArcViewDlg*> views;
	CArcViewDlg::clear();

	//-- ダイアログ起動
	for( unsigned int i=0; i!=m_FName.len(); i++ )
	{
		if( !m_Melters[i] )
			continue;

		arcname an(
			m_BasePathList[i],
//			m_BasePath,
			m_FName[i].cAlternateFileName[0]==0 ? m_FName[i].cFileName : m_FName[i].cAlternateFileName,
			m_FName[i].cFileName );
		ddir = destdir;

		if( mdf )
			generate_dirname( m_FName[i].cFileName, ddir, rmn );

		CArcViewDlg* x = new CArcViewDlg( m_Melters[i],an,ddir );
		views.add( x );
		x->createModeless( NULL );
	}

	//-- 全部終了するまで待機
	kiWindow::msgLoop( kiWindow::GET );

	//-- お終い
	app()->setMainWnd( mptr );
	for( i=0; i!=views.len(); i++ )
		delete views[i];
}

//----------------------------------------------//
//----------------- 解凍作業 -------------------//
//----------------------------------------------//

void CNoahArchiverManager::do_melting( kiPath& destdir )
{
	//-- 設定ロード
	const int  mdf = mycnf().mkdir();  // Make Directory Flag( 0:no 1:no1file 2: noddir 3:yes )
	const bool rmn = mycnf().mnonum(); // Remove NuMber ?

	//-- 出力先
	destdir.beBackSlash( true );
	destdir.mkdir(), destdir.beShortPath();

	for( unsigned int i=0; i!=m_FName.len(); i++ )
		if( m_Melters[i] )
		{
			//-- 出力先

			int mk=2; // 0:no 1:yes 2:???
			kiPath ddir( destdir ), dnm;
			if( mdf==0 )
				mk=0;
			else if( mdf==3 )
				mk=1;
			else
			{
				kiPath anm(m_BasePathList[i]);
//				kiPath anm(m_BasePath);
				anm+=m_FName[i].cFileName;
				int c = m_Melters[i]->contents( anm, dnm );
				if( c==aSingleDir || (c==aSingleFile && mdf==1) )
					mk=0; // 2重フォルダ防止処理(強)
				else if( c==aMulti )
					mk=1;
			}
			if( mk )
			{
				generate_dirname( m_FName[i].cFileName, ddir, rmn );
				if( mk==2 && kiSUtil::exist(ddir) )
					mk=1;
				ddir+='\\';
				ddir.mkdir();
				ddir.beShortPath();
			}

			//-- 解凍!

			arcname an( m_BasePathList[i],
//			arcname an( m_BasePath,
				m_FName[i].cAlternateFileName[0]==0 ? m_FName[i].cFileName : m_FName[i].cAlternateFileName,
				m_FName[i].cFileName );
			int result = m_Melters[i]->melt( an, ddir );
			if( result<0x8000 )
			{
				if( mk==2 ) // 2重フォルダ防止処理(弱)
					break_ddir( ddir, mdf==2 );
				else if( mk==0 && dnm.len() ) // 2重フォルダ防止処理(強)
					if( dnm.len()<=1 || dnm[1]!=':' ) // 絶対パスは開かない
						ddir+=dnm, ddir+='\\';
				// 出力先を開くかも
				myapp().open_folder( ddir, 1 );
			}
			else if( result!=0x8020 )
			{
				//エラー!
				char str[255];
				wsprintf( str, "%s\nError No: [%x]",
					(const char*)kiStr().loadRsrc( IDS_M_ERROR ), result );
				app()->msgBox( str );
			}
		}
}

void CNoahArchiverManager::generate_dirname( const char* src, kiPath& dst, bool rmn )
{
	// srcで示された書庫名からディレクトリ名を生成し、
	// dstへ足す。rmn==trueなら末尾の数字も削除

	// 一番左の . と左から二番目の . を探す
	const char *fdot=NULL, *sdot=NULL, *tail;
	for( tail=src; *tail; tail=kiStr::next(tail) )
		if( *tail=='.' )
			sdot=fdot, fdot=tail;

	// .tar.xxx か、.xxx.gz/.xxx.z/.xxx.bz2 なら二つ削除
	if( fdot )
	{
		tail = fdot;
		if( sdot )
			if( 0==::lstrcmpi(fdot,".gz")
			 || 0==::lstrcmpi(fdot,".z")
			 || 0==::lstrcmpi(fdot,".bz2")
			 || (sdot+4==fdot
			 && (sdot[1]=='t'||sdot[1]=='T')
			 && (sdot[2]=='a'||sdot[2]=='A')
			 && (sdot[3]=='r'||sdot[3]=='R')
			))
				tail = sdot;
	}

	// 末尾の数字と'-'と'_'と'.'削除。半角スペースも。
	bool del[256];
	ki_memzero( del, sizeof(del) );
	if( rmn )
	{
		del['-'] = del['_'] = del['.'] = true;
		for( char c='0'; c<='9'; ++c )
			del[c] = true;
	}
	del[' '] = true;

	const char* mjs=NULL;
	for( const char *x=src; x<tail; x=kiStr::next(x) )
		if( !del[(unsigned char)(*x)] )
			mjs = NULL;
		else if( !mjs )
			mjs = x;
	if( mjs && mjs!=src )
		tail = mjs;

	// 空になってしまったら "noahmelt" という名前にしてしまう。
	if( src==tail )
		dst += "noahmelt";
	else
		while( src!=tail )
			dst += *src++;
}

bool CNoahArchiverManager::break_ddir( kiPath& dir, bool onlydir )
{
// 2重フォルダ or 単一ファイル 状態を解消
//
// 素直に格納ファイル名を一度走査するのが本当なんですが、
// 特に巨大書庫の時速度低下が激しいのと、FindFirst系を
// サポートしたDLLや内蔵エンジン以外に対応できないという
// 欠点があるため、相変わらず Noah 2.xx と同じ手法です。

//-- 中に1個しか入ってないことを確認 -----------------
	char wild[MAX_PATH];
	ki_strcpy( wild, dir );
	ki_strcat( wild, "*.*" );
	kiFindFile find;
	if( !find.begin( wild ) )
		return false;
	WIN32_FIND_DATA fd,fd2,fd3;
	find.next( &fd );
	if( find.next( &fd2 ) )
		return false;
	find.close();
//----------------------------------------------------

//-- to:最終移動先ファイル名。ついでに、カレントDirは消せない問題の回避策 -----
	kiPath to(dir); to.beBackSlash( false ), to.beDirOnly();
	::SetCurrentDirectory( to );
	to += fd.cFileName;
//-------------------------------------------------------------------------

//-- ファイルだった場合 --------------------------------------
	if( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
	{
		if( !onlydir )
		{
			// now 現在のファイル名
			kiStr now=dir; now+=fd.cFileName;

			// now -> to 移動
			if( ::MoveFile( now, to ) )
			{
				dir.remove();
				dir.beBackSlash( false ), dir.beDirOnly();
				return true;
			}
		}
	}
//-- フォルダだった場合 ----------------------------------------
	else
	{
		// 'base/aaa/aaa/' だと中のaaaを外にmoveできない。
		// よって、回避策。-> 'base/aaa_noah_tmp_178116/aaa/'

		dir.beBackSlash( false );
		kiFindFile::findfirst( dir, &fd3 );
		kiPath dirx( dir ); dirx+="_noah_tmp_178116";

		if( ::MoveFile( dir, dirx ) )
		{
			// now 現在のファイル名
			kiStr now( dirx ); now+='\\', now+=fd.cFileName;

			// ディレクトリを移動
			if( ::MoveFile( now, to ) )
			{
				dirx.remove();
				dir=to, dir.beBackSlash( true );
				return true;
			}
			else
			{
				// 'base/aaa_noah_tmp_178116/aaa/' -> 'base/aaa/aaa/'
				dir.beDirOnly(), dir+=fd3.cFileName;
				::MoveFile( dirx, dir );
			}
		}

		dir.beBackSlash( true );
	}
//------------------------------------------------------------
	return false;
}

//----------------------------------------------//
//----------------- 圧縮作業 -------------------//
//----------------------------------------------//

void CNoahArchiverManager::do_compressing( kiPath& destdir, bool each )
{
	int result = 0xffff, tr;

	// 出力先を確実に作っておく
	destdir.beBackSlash( true );
	destdir.mkdir();
	destdir.beShortPath();

	// 個別圧縮モードか、Archiving不可の形式なら一個ずつ
	if( each || !(m_Compressor->ability() & aArchive) )
	{
		wfdArray templist;

		for( unsigned int i=0; i!=m_FName.len(); i++ )
		{
			templist.empty();
			templist.add( m_FName[i] );
			tr = m_Compressor->compress( m_BasePath,templist,destdir,m_Method,m_Sfx );
			if( tr<0x8000 || tr==0x8020 )
				result = tr;
		}
	}
	else
		result = m_Compressor->compress( m_BasePath,m_FName,destdir,m_Method,m_Sfx );

	// 開くかも
	if( result<0x8000 )
		myapp().open_folder( destdir, 2 );
	else if( result!=0x8020 )
	{
		//エラー!
		char str[255];
		wsprintf( str, "%s\nError No: [%x]", "Compression Error", result );
		app()->msgBox( str );
	}
}