UWSCでcab圧縮ファイルを扱う

多分こういう需要はあるんだろうな、、、と思いつつ、ネットの片隅に書いてみる。
標準のExpandコマンドとTempフォルダーを使って、UWSCスクリプトに内包したcabファイルを扱う。



スクリプト

cab.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "cab.uws" THEN
	// テスト用cabファイル生成

	// cabファイル
	//	.┬sub1
	//	 | └sub.txt (sub1)
	//	 ├sub2
	//	 | └sub.txt (sub2)
	//	 └root.txt   (root)
	TEXTBLOCK CAB_DATA
		TVNDRgAAAACVAAAAAAAAACwAAAAAAAAAAwEBAAMAAACxDAAAfwAAAAEAAQAEAAAAAAAAAAAA
		L0KwTSAAcm9vdC50eHQABAAAAAQAAAAAAC9CvE0gAHN1YjFcc3ViLnR4dAAEAAAACAAAAAAA
		L0LCTSAAc3ViMlxzdWIudHh0AM91CoYOAAwAQ0srys8vKS5NMgRiIwA=
	ENDTEXTBLOCK


	// 設定する
	DIM res = Cab.SetCabData(CAB_DATA), ret = !CHKNUM(res)
	IF ret THEN MSGBOX("Cabモジュール管理下に一時ファイルが作成されました " + res)

	// 全て展開する
	res = 0
	IF ret THEN res = Cab.Expand()
	IF !CHKNUM(res) THEN MSGBOX("全てを一時展開しました " + res)

	// root.txtを展開する
	DIM tar = "root.txt"
	IFB ret AND MSGBOX(tar + "を一時展開しますか?", BTN_YES OR BTN_NO) = BTN_YES THEN
		res = Cab.Expand(tar)
		IFB !CHKNUM(res) THEN
			MSGBOX(res + " に展開しました")
			IFB MSGBOX("展開した" + tar + "を、先に削除しますか?", BTN_YES OR BTN_NO) = BTN_YES THEN
				Cab.Dispose(res)
			ENDIF
		ELSE
			MSGBOX("展開に失敗しました")
		ENDIF
	ENDIF
	// sub.txtを展開する
	tar = "sub.txt"
	IFB ret AND MSGBOX(tar + "を一時展開しますか?", BTN_YES OR BTN_NO) = BTN_YES THEN
		res = Cab.Expand(tar)
		IFB !CHKNUM(res) THEN
			MSGBOX(res + " に展開しました")
		ELSE
			MSGBOX("展開に失敗しました")
		ENDIF
	ENDIF

	// 片付ける
	MSGBOX("一時展開したものを片付けます。path指定の展開は対象外です")
	Cab.Dispose()

ENDIF


MODULE Cab

	PUBLIC _fso
	DIM _path
	HASHTBL _tmps

	PROCEDURE Cab
		_fso = CreateOleObj("Scripting.FileSystemObject")
		_path = EMPTY
	FEND

	FUNCTION Dispose(tar=EMPTY, num=1)
		RESULT = TRUE
		IFB tar = EMPTY THEN
			WHILE LENGTH(_tmps)
				RESULT = RESULT AND Dispose(_tmps[0, HASH_KEY], _tmps[0, HASH_VAL])
			WEND
		ELSE
			IFB _tmps[tar, HASH_EXISTS] THEN
				_tmps[tar] = _tmps[tar] - num
				IFB _tmps[tar] <= 0 THEN
					TRY
						RESULT = _tmps[tar, HASH_REMOVE]
						IFB _fso.FolderExists(tar) THEN
							_fso.DeleteFolder(tar)
						ELSEIF _fso.FileExists(tar) THEN
							_fso.DeleteFile(tar)
						ENDIF
					EXCEPT
						RESULT = FALSE
					ENDTRY
				ENDIF
			ENDIF
		ENDIF
	FEND

	FUNCTION GetTempPath()
		RESULT = _fso.GetSpecialFolder(2) + "\" + _fso.GetTempName()
	FEND

	FUNCTION SetCabFile(tar, temp=TRUE)
		RESULT = _fso.FileExists(tar)
		IFB RESULT THEN
			_path = tar
			IFB temp THEN
				_path = GetTempPath()
				TRY
					_fso.CopyFile(tar, _path)
					_tmps[_path] = 1
					RESULT = _path
				EXCEPT
					RESULT = 1
				ENDTRY
			ELSE
				RESULT = tar
			ENDIF
		ENDIF
	FEND

	FUNCTION SetCabData(data, path=EMPTY)
		IFB path = EMPTY THEN
			path = GetTempPath()
			_tmps[path] = 1
		ENDIF
		DIM xml=CreateOleObj("Microsoft.XMLDOM"), t=xml.createElement("t"), s=CreateOleObj("ADODB.Stream")
		t.dataType = "bin.base64"
		t.text = data
		s.Open()
		s.Type=1
		s.Write(t.nodeTypedValue)
		s.SaveToFile(path, 2)
		s.Close()
		RESULT = SetCabFile(path, FALSE)
	FEND

	FUNCTION Expand(tar="*", path=EMPTY, cab=EMPTY)
		IFB path = EMPTY THEN
			path = GetTempPath()
			_fso.CreateFolder(path)
			_tmps[path] = 1
		ENDIF
		IF cab = EMPTY THEN cab = _path
		RESULT = EXEC("EXPAND " + cab + " -F:" + tar + " " + path, TRUE, ERR_VALUE, ERR_VALUE)
		IFB RESULT = 0 THEN
			RESULT = path
		ELSE
			Dispose(path)
		ENDIF
	FEND

ENDMODULE

テストコードには、コメントで書いたようなcabファイルが書かれています。
このcabファイルの作り方は後述します。


Cab.SetCabDataでcabファイルを設定(一時フォルダーに作成)
(既存cabファイルを扱う場合、Cab.SetCabFileで)
Cab.Expandで一時フォルダーに全てを展開(戻りは展開先フォルダーかエラーコード)
Cab.Expandにファイル名を指定すると、そのファイルだけを展開。
展開先は、サブフォルダーの絡みがあるので、存在するフォルダーのみ指定可能です。
Cab.Disposeで、一時フォルダーの片付け。
指定した一時フォルダーのみ片付けることも可能。


他の操作をしたい場合、EXPANDコマンドのヘルプでも見て、がんばってくださいな。

  • ファイル一覧はDオプションで取れるけど、パスが不明
  • Rオプションでファイルを直接取り出せる、、、かも
  • EXECだとコンソールウインドウが出てイマイチかも

cabファイルの作り方

makecabコマンドを使います。
大抵の環境には入っていると思います。
ただ使いにくいため、以下のスクリプトを用意しました。
が、さらに下のスクリプトの方が便利かもしれません。
mcab.js

// makecab utility
WScript.Quit((function() {
	var ret = 0;

	// 引数チェック
	if(WScript.Arguments.Length < 2) {
		WScript.Echo('usage: (cab file path) (target dir)');
		return 1;
	}
	var fso = WScript.CreateObject('Scripting.FileSystemObject');
	var pathCab = WScript.Arguments.Item(0);
	if(fso.GetExtensionName(pathCab).toLowerCase() != 'cab') pathCab += '.cab';
	var pathCabD = fso.GetSpecialFolder(2) + '\\' + fso.GetTempName();
	var pathTemp = fso.GetSpecialFolder(2) + '\\' + fso.GetTempName();

	// 出力先ファイルの有無確認
	var preDel = false;
	if(fso.FileExists(pathCab)) {
		// 上書き確認して変更する
		//preDel = true;
	}

	// makecab用ファイルの作成
	var tmp = fso.CreateTextFile(pathTemp, true);
	// 固定出力
	tmp.WriteLine('.Set MaxCabinetSize=1073741824');	// 最大サイズは1GB
	tmp.WriteLine('.Set MaxDiskSize=1073741824');
	tmp.WriteLine('.Set RptFilename=nul');
	tmp.WriteLine('.Set InfFilename=nul');
	tmp.WriteLine('.Set DiskDirectoryTemplate=' + pathCabD);
	// サブフォルダーの再帰処理用関数
	var subFunc = function(tmp, fso, tar, s) {
		tmp.WriteLine('.Set DestinationDir="' + s + '"');
		var f = fso.GetFolder(tar), fc;
		for(fc = new Enumerator(f.files); !fc.atEnd(); fc.moveNext()) {
			tmp.WriteLine('"' + fc.item() + '"');
		}
		for(fc = new Enumerator(f.SubFolders); !fc.atEnd(); fc.moveNext()) {
			if(s.length > 0 && s.charAt(s.length-1) != '\\') s += '\\';
			subFunc(tmp, fso, fc.item(), s + fso.GetFileName(fc.item()));
		}
	};
	// 指定フォルダー内を列挙
	for(var i=1; i<WScript.Arguments.Length; i++) {
		var tar = WScript.Arguments.Item(i);
		if(fso.FileExists(tar)) {
			tmp.WriteLine('.Set DestinationDir=""');
			tmp.WriteLine('"' + tar + '"');
		} else if(fso.FolderExists(tar)) {
			// フォルダーの場合は、内容を列挙
			subFunc(tmp, fso, tar, '');
		} else {
			WScript.Echo('File or Folder not found. ' + tar);
		}
	}
	tmp.Close();

	// makecab実行
	var wsh = WScript.CreateObject('WScript.Shell');
	var oExec = wsh.Exec('makecab /F ' + pathTemp);
	WScript.Echo(oExec.StdOut.ReadAll());
	while(oExec.Status == 0) {
		WScript.Sleep(100);
	}

	// tempファイル削除
	try {
		fso.DeleteFile(pathTemp);
	} catch(e) {
		WScript.Echo('Delete ' + pathTemp + ' ' + e.name + '! ' + e.number + ' : ' + e.message);
	}

	// ファイルを移動
	if(preDel) {
		try {
			fso.DeleteFile(pathCab);
		} catch(e) {
			WScript.Echo('Delete ' + pathCab + ' ' + e.name + '! ' + e.number + ' : ' + e.message);
		}
	}
	try {
		fso.MoveFile(pathCabD + '\\1.cab', pathCab);
	} catch(e) {
		switch(e.number) {
		case -2146828235:
			WScript.Echo('makecab output not found.');
			break;
		case -2146828230:
			WScript.Echo('File already exists! ' + pathCab);
			break;
		default:
			WScript.Echo('MoveFile ' + pathCabD + '\\1.cab to ' + pathCab + ' ' + e.name + '! ' + e.number + ' : ' + e.message);
			break;
		}
	}
	try {
		fso.DeleteFolder(pathCabD);
	} catch(e) {
		WScript.Echo('Delete ' + pathCabD + ' ' + e.name + '! ' + e.number + ' : ' + e.message);
	}

	return ret;
})());

「cscript //nologo mcab.js (cabファイル名) (対象フォルダー)」
対象フォルダーの中身をcabファイルに圧縮します。

cab操作スクリプト

ファイルをUWSCスクリプトにするスクリプト - じゅんじゅんのきまぐれ
とmcab.jsと最初のスクリプトを合成してみました。
これで、makecabコマンドのある環境なら、cab圧縮・展開ができますね。
cabUtil.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "cabUtil.uws" THEN

	// cabファイル
	//	.┬sub1
	//	 | └sub.txt (sub1)
	//	 ├sub2
	//	 | └sub.txt (sub2)
	//	 └root.txt   (root)
	TEXTBLOCK CAB_DATA
		TVNDRgAAAACVAAAAAAAAACwAAAAAAAAAAwEBAAMAAACxDAAAfwAAAAEAAQAEAAAAAAAAAAAA
		L0KwTSAAcm9vdC50eHQABAAAAAQAAAAAAC9CvE0gAHN1YjFcc3ViLnR4dAAEAAAACAAAAAAA
		L0LCTSAAc3ViMlxzdWIudHh0AM91CoYOAAwAQ0srys8vKS5NMgRiIwA=
	ENDTEXTBLOCK

	// cabファイル作成
	DIM res = CabUtil.SetCabData(CAB_DATA), ret = !CHKNUM(res)
	IF ret THEN MSGBOX("Cabモジュール管理下に一時ファイルが作成されました " + res)

	// 全て展開する
	res = 0
	IF ret THEN res = CabUtil.Expand()
	IF !CHKNUM(res) THEN MSGBOX("全てを一時展開しました " + res)

	// ファイル追加
	IFB ret THEN
		DIM addFile = CabUtil._fso.CreateTextFile(res + "\add.txt")
		addFile.WriteLine("add file")
		addFile.Close()
	ENDIF

	// フォルダーcab圧縮
	IFB ret THEN
		res = CabUtil.Contract(res)
		IF !CHKNUM(res) THEN MSGBOX("圧縮しました " + res)
	ENDIF

	// Base64表示
	IFB ret AND !CHKNUM(res) THEN
		CabUtil.Log("<#CR><#CR>Base64")
		CabUtil.Log(CabUtil.ConvText(res))
	ENDIF

	// 片付け
	CabUtil.Dispose()
ENDIF


MODULE CabUtil

	PUBLIC _fso
	DIM _path
	HASHTBL _tmps

	PROCEDURE CabUtil
		_fso = CreateOleObj("Scripting.FileSystemObject")
		_path = EMPTY
	FEND

	FUNCTION Dispose(tar=EMPTY, num=1)
		RESULT = TRUE
		IFB tar = EMPTY THEN
			WHILE LENGTH(_tmps)
				RESULT = RESULT AND Dispose(_tmps[0, HASH_KEY], _tmps[0, HASH_VAL])
			WEND
		ELSE
			IFB _tmps[tar, HASH_EXISTS] THEN
				_tmps[tar] = _tmps[tar] - num
				IFB _tmps[tar] <= 0 THEN
					TRY
						RESULT = _tmps[tar, HASH_REMOVE]
						IFB _fso.FolderExists(tar) THEN
							_fso.DeleteFolder(tar)
						ELSEIF _fso.FileExists(tar) THEN
							_fso.DeleteFile(tar)
						ENDIF
					EXCEPT
						RESULT = FALSE
					ENDTRY
				ENDIF
			ENDIF
		ENDIF
	FEND

	FUNCTION GetTempPath()
		RESULT = _fso.GetSpecialFolder(2) + "\" + _fso.GetTempName()
	FEND

	FUNCTION SetCabFile(tar, temp=TRUE)
		RESULT = _fso.FileExists(tar)
		IFB RESULT THEN
			_path = tar
			IFB temp THEN
				_path = GetTempPath()
				TRY
					_fso.CopyFile(tar, _path)
					_tmps[_path] = 1
					RESULT = _path
				EXCEPT
					RESULT = 1
				ENDTRY
			ELSE
				RESULT = tar
			ENDIF
		ENDIF
	FEND

	FUNCTION SetCabData(data, path=EMPTY)
		IFB path = EMPTY THEN
			path = GetTempPath()
			_tmps[path] = 1
		ENDIF
		DIM xml=CreateOleObj("Microsoft.XMLDOM"), t=xml.createElement("t"), s=CreateOleObj("ADODB.Stream")
		t.dataType = "bin.base64"
		t.text = data
		s.Open()
		s.Type=1
		s.Write(t.nodeTypedValue)
		s.SaveToFile(path, 2)
		s.Close()
		RESULT = SetCabFile(path, FALSE)
	FEND

	FUNCTION Expand(tar="*", path=EMPTY, cab=EMPTY)
		IFB path = EMPTY THEN
			path = GetTempPath()
			_fso.CreateFolder(path)
			_tmps[path] = 1
		ENDIF
		IF cab = EMPTY THEN cab = _path
		RESULT = EXEC("EXPAND " + cab + " -F:" + tar + " " + path, TRUE, ERR_VALUE, ERR_VALUE)
		IFB RESULT = 0 THEN
			RESULT = path
		ELSE
			Dispose(path)
		ENDIF
	FEND

	PROCEDURE Log(msg)
		PRINT msg
	FEND

	PROCEDURE ContractSub(tmp, fso, tar, s)
		tmp.WriteLine(".Set DestinationDir=<#DBL>" + s + "<#DBL>")
		DIM f = fso.GetFolder(tar), i
		FOR i = 0 TO GETOLEITEM(f.files) - 1
			tmp.WriteLine("<#DBL>" + ALL_OLE_ITEM[i].Path + "<#DBL>");
		NEXT
		FOR i = 0 TO GETOLEITEM(f.SubFolders) - 1
			IFB LENGTH(s) > 0 THEN
				IF COPY(s, LENGTH(s), 1) <> "\" THEN s = s + "\"
			ENDIF
			ContractSub(tmp, fso, ALL_OLE_ITEM[i].Path, s + ALL_OLE_ITEM[i].Name)
		NEXT
	FEND

	FUNCTION Contract(path, cab=EMPTY, cap=1073741824)
		IFB cab = EMPTY THEN
			cab = GetTempPath()
			_tmps[cab] = 1
		ENDIF

		DIM preDel = FALSE

		// makecab用ファイルの作成
		DIM pathCabD = GetTempPath(), pathTemp = GetTempPath()
		DIM tmp = _fso.CreateTextFile(pathTemp, TRUE)
		// 固定出力
		tmp.WriteLine(".Set MaxCabinetSize=" + cap)
		tmp.WriteLine(".Set MaxDiskSize=1073741824" + cap)
		tmp.WriteLine(".Set RptFilename=nul")
		tmp.WriteLine(".Set InfFilename=nul")
		tmp.WriteLine(".Set DiskDirectoryTemplate=" + pathCabD)

		// 指定ディレクトリー内を列挙
		DIM i
		IFB VARTYPE(path) < VAR_ARRAY THEN
			i = path
			path = SAFEARRAY(0, 0)
			path[0] = i
		ENDIF
		FOR i = 0 TO LENGTH(path) - 1
			IFB _fso.FileExists(path[i]) THEN
				tmp.WriteLine(".Set DestinationDir=<#DBL><#DBL>")
				tmp.WriteLine("<#DBL>" + path[i] + "<#DBL>")
			ELSEIF _fso.FolderExists(path[i]) THEN
				ContractSub(tmp, _fso, path[i], "")
			ELSE
				Log("File or Folder not found. " + path[i])
			ENDIF
		NEXT
		tmp.Close()

		// makecab実行
		Log(DOSCMD("makecab /F " + pathTemp))

		// tempファイル削除
		_fso.DeleteFile(pathTemp)

		// ファイルを移動
		IF preDel THEN _fso.DeleteFile(cab)
		_fso.MoveFile(pathCabD + "\1.cab", cab)
		_fso.DeleteFolder(pathCabD);

		RESULT = cab
	FEND

	FUNCTION ConvText(path)
		RESULT = ""

		// ファイル読み込み
		DIM ins = CreateOleObj("ADODB.Stream")
		ins.Open()
		ins.Charset = "iso-8859-1"
		ins.Type = 1	// Binary
		ins.LoadFromFile(path)

		// バイト配列操作用オブジェクト
		DIM dom = CreateOleObj("Microsoft.XMLDOM"), binMan = dom.createElement("tmp")
		binMan.dataType = "bin.base64"
		DIM loop = TRUE
		WHILE loop
			binMan.nodeTypedValue = ins.Read(54)
			loop = (LENGTH(binMan.text) > 0)
			IF loop THEN RESULT = RESULT + "<#CR>" + binMan.text
		WEND
		ins.Close()
		ins = Nothing
		binMan = Nothing
		dom = Nothing
	FEND

ENDMODULE

あまり大きなcabファイルになると、メモリーがきついことになりそう、、、。


ファイルを暗号化したい?
一時ファイルを作成してしまうつくりですからねぇ、、、あまり意味ないかと。
それでもしたいなら、
UWSCでWin32 Crypto APIを使う - じゅんじゅんのきまぐれ
を参照してがんばってくださいな。