#//
#//	base64.awk - Awk用Base64エンコード/デコード関数
#//
#//	* Sat Jun 23 23:59:59 JST 2018 Naoyuki Sawa
#//	- 1st リリース。
#//	- 動作確認に使用したGawkのバージョンは、以下の通りです。
#//	  □http://www.kt.rim.or.jp/~kbk/gawk-3.1/gawk-mbcs-win32-20091124.zip
#//	  ＞igawk.exe --version
#//	  │igawk: C version 1.1
#//	  │GNU Awk 3.1.7(windows special Nov 24 2009)
#//	  │Copyright (C) 1989, 1991-2009 Free Software Foundation.
#//	- 当ファイル単体では動作しません。
#//	  アプリケーションスクリプトからインクルードして使用して下さい。
#//	  使用例は、「C:/Home/Share/Piece/clip/tool/dbase64enc.awk」を参照して下さい。
#//
#//================================================================================================
#//	グローバル関数
#//================================================================================================
#//「C:/Home/Share/Piece/clip/clipcode.c」の「base64_encode()」に互換な処理を行います。
function base64_encode(src,
	#//隠し引数
	bVerify,
	#//ローカル変数
	i, c, aSrc, src_len, dst) {
	#//共通処理を呼び出す。
	src_len = _base64_common(src, aSrc)
	#//以下は「\Home\Share\Piece\clip\clipcode.c」の「base64_encode()」と同じアルゴリズムです。
	dst = ""	#//何故か必要。これを行わないと、以下のwhileループが一度も実行されない時に(即ちsrc_len==0の時に)、誤った結果になる事が有った。「ローカル変数は空文字列に初期化される。」(http://www.kt.rim.or.jp/~kbk/gawk-30/gawk_14.html)はずなのだが…。「GNU Awk 3.1.7(windows special Nov 24 2009)」のバグだろうか？
	i = 0
	i = 0
	while(i < src_len) {
		switch(src_len - i) {
		case 1:
			#//xxxxxx xx0000 000000 000000
			c =       lshift(aSrc[i++], 16)
			dst = dst substr(_base64_sBase, and(rshift(c, 18), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c, 12), 63) + 1, 1)
			dst = dst "="
			dst = dst "="
			break
		case 2:
			#//xxxxxx xxyyyy yyyy00 000000
			c =       lshift(aSrc[i++], 16)
			c = or(c, lshift(aSrc[i++],  8))
			dst = dst substr(_base64_sBase, and(rshift(c, 18), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c, 12), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c,  6), 63) + 1, 1)
			dst = dst "="
			break
		default:#//3以上
			#//xxxxxx xxyyyy yyyyzz zzzzzz
			c =       lshift(aSrc[i++], 16)
			c = or(c, lshift(aSrc[i++],  8))
			c = or(c, lshift(aSrc[i++],  0))
			dst = dst substr(_base64_sBase, and(rshift(c, 18), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c, 12), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c,  6), 63) + 1, 1)
			dst = dst substr(_base64_sBase, and(rshift(c,  0), 63) + 1, 1)
			break
		}
	}
	#//ベリファイを行う。(必須ではない)
	if(!bVerify) {
		if(base64_decode(dst, 1) != src) {
			printf "base64_encode: verify error '%s'", src > "/dev/stderr"
			exit 1
		}
	}
	return dst
}
#//------------------------------------------------------------------------------------------------
#//「C:/Home/Share/Piece/clip/clipcode.c」の「base64_decode()」に互換な処理を行います。
function base64_decode(src,
	#//隠し引数
	bVerify,
	#//ローカル変数
	i, c, aSrc, src_len, dst) {
	#//共通処理を呼び出す。
	src_len = _base64_common(src, aSrc)
	#//以下は「\Home\Share\Piece\clip\clipcode.c」の「base64_decode()」と同じアルゴリズムです。
	if(src_len && (aSrc[src_len - 1] == _base64_aOrd["="])) { src_len-- }
	if(src_len && (aSrc[src_len - 1] == _base64_aOrd["="])) { src_len-- }
	dst = ""	#//何故か必要。これを行わないと、以下のwhileループが一度も実行されない時に(即ちsrc_len==0の時に)、誤った結果になる事が有った。「ローカル変数は空文字列に初期化される。」(http://www.kt.rim.or.jp/~kbk/gawk-30/gawk_14.html)はずなのだが…。「GNU Awk 3.1.7(windows special Nov 24 2009)」のバグだろうか？
	i = 0
	while(i < src_len) {
		switch(src_len - i) {
		#//case 1: → 有り得ません
		case 2:
			c =       lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 18)
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 12))
			dst = dst _base64_aChr[and(rshift(c, 16), 255)]
			break
		case 3:
			c =       lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 18)
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 12))
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1,  6))
			dst = dst _base64_aChr[and(rshift(c, 16), 255)]
			dst = dst _base64_aChr[and(rshift(c,  8), 255)]
			break
		default:#//4以上
			c =       lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 18)
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1, 12))
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1,  6))
			c = or(c, lshift(index(_base64_sBase, _base64_aChr[aSrc[i++]]) - 1,  0))
			dst = dst _base64_aChr[and(rshift(c, 16), 255)]
			dst = dst _base64_aChr[and(rshift(c,  8), 255)]
			dst = dst _base64_aChr[and(rshift(c,  0), 255)]
			break
		}
	}
	#//ベリファイを行う。(必須ではない)
	if(!bVerify) {
		if(base64_encode(dst, 1) != src) {
			printf "base64_decode: verify error '%s'", src > "/dev/stderr"
			exit 1
		}
	}
	return dst
}
#//================================================================================================
#//	ローカル関数
#//================================================================================================
#//base64_encode()とbase64_decode()の共通処理です。
#// - base64_encode(),及び,base64_decode()が内部で利用します。
#//   アプリケーションからは当関数を直接呼び出さないで下さい。
function _base64_common(src, aSrc,
	#//ローカル変数
	i, c, src_len) {
	#//当関数の初回呼び出し時に…
	if(!_base64_sBase) {
		#//Base64文字列を初期化する。
		_base64_sBase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
				"abcdefghijklmnopqrstuvwxyz" \
				"0123456789+/"
		#//シフトJIS文字⇔コード変換配列を初期化する。
		delete _base64_aChr	#//念のため
		delete _base64_aOrd	#//念のため
		for(i = 0; i <= 65535; i++) {
			c = sprintf("%c", and(i, 255))
			if(i > 255) { c = sprintf("%c", rshift(i, 8)) c }
			_base64_aChr[i] = c	#//シフトJIS文字→コード変換配列
			_base64_aOrd[c] = i	#//シフトJIS文字←コード変換配列
		}
	}
	#//入力文字列をバイト配列に変換する。
	#// - Gawkのsubstr()は、マルチバイト文字を1バイト単位で切り出す事が出来ず、文字単位でしか取得出来ません。
	#//   仕方が無いので、文字単位で取得して、マルチバイト文字ならば明示的に2バイトに分割する必要が有ります。
	#//   尚、--ctype=ASCIIを指定すればsubstr()を1バイト単位で取得する動作に変更する事も出来るのですが、
	#//   スクリプト全体に影響してしまうため、Base64の処理だけのために--ctype=ASCIIを指定する事は出来ません。
	delete aSrc	#//念のため
	src_len = 0
	for(i = 1; i <= length(src); i++) {
		c = _base64_aOrd[substr(src, i, 1)]	#//【注意】文字列の中に'\xA0'が含まれていると誤った結果になります。例えば「gawk.exe -f dbase64enc.awk C:\Home\Share\Piece\clip\clippce.c」がベリファイエラーになります(2018/06/21現在、左記のソースが'\xA0'を含んでいるため)。'\xA0'は一般的には使用されない半角文字の一つで、「GNU Awk 3.1.7(windows special Nov 24 2009)」は'\xA0'を上手く扱えないようです。
		if(c > 255) { aSrc[src_len++] = rshift(c, 8) }
		aSrc[src_len++] = and(c, 255)
	}
	return src_len
}
#//------------------------------------------------------------------------------------------------
