UWSCのヘルプを開くモジュール

以前、.chmファイルを解析してみた。
UWSCで.chmファイルを解析する - じゅんじゅんのきまぐれ
手元にあるUWSCインタープリターは、解析したキーワードを登録しておいて、hh.exeでヘルプを開くものでした。
が、これだと、キーワードを更新しないといけない。
それは面倒。
ということで、バージョンが上がる(または下がる)と、自動的にキーワードを更新して、ヘルプを開くモジュールを作成してみた。



使い方

モジュールをCALLして、Help.Open関数にキーワードを渡すだけ。
キーワードは前方一致(正規表現)なので、「A」を渡すと「A」で始まるキーワードの一覧を表示するので、選択するとそこを開く。(キーワードは大文字小文字無視)
前方一致ですが正規表現なので、「read」を含むにしたい場合「.*read」とかでOKです。

制約

CALLして使う場合、本体と同じフォルダーに存在すること。(キーワード更新の都合上)
テンポラリーフォルダーのパスに空白が含まれると、更新時におかしなことになる、、、かな。
特殊文字の記載は面倒になってやめました、、、。

スクリプト

help.uws

OPTION EXPLICIT, OPTFINALLY

IFB GET_UWSC_NAME = "help.uws" THEN
	Help.UpdateKeywords()

	Help.Open(INPUT("keyword?"))
ENDIF


MODULE Help

	FUNCTION GetTempFolder(fso, sub=FALSE)
		RESULT = fso.GetSpecialFolder(2)
		IFB sub THEN
			DIM subp = RESULT + "\" + fso.GetTempName()
			WHILE fso.FolderExists(subp) OR fso.FileExists(subp)
				subp = RESULT + "\" + fso.GetTempName()
			WEND
			RESULT = subp
			fso.CreateFolder(RESULT)
		ENDIF
	FEND

	FUNCTION DeleteFolder(fso, path)
		RESULT = fso.DeleteFolder(path, TRUE)
	FEND

	FUNCTION Decompile(fso, chmFile, outPath=NULL)
		RESULT = outPath
		IF RESULT = NULL THEN RESULT = GetTempFolder(fso, TRUE)
		EXEC("hh.exe -decompile " + RESULT + " " + chmFile, TRUE)
		IFB fso.GetFolder(RESULT).Files.Count = 0 THEN
			IF outPath = NULL THEN DeleteFolder(fso, RESULT)
			RESULT = NULL
		ENDIF
	FEND

	FUNCTION GetKeywordsFromFile(fso, path)
		DIM i, j, k, l, ie, fid = -1, tar, n, r
		HASHTBL res
		TRY
			ie = CREATEOLEOBJ("InternetExplorer.Application")
			ie.Navigate("about:blank")
			fid = FOPEN(path)
			ie.Document.body.innerHTML = FGET(fid, F_ALLTEXT)
			FCLOSE(fid)
			fid = -1
			FOR l = 0 TO ie.Document.body.childNodes.length - 1
				IF ie.Document.body.childNodes.item(l).nodeName <> "UL" THEN CONTINUE
				tar = ie.Document.body.childNodes.item(l).childNodes
				FOR i = 0 TO tar.length - 1
					n = tar.Item(i)
					IF n.nodeName <> "LI" THEN CONTINUE
					FOR j = 0 TO n.childNodes.length - 1
						IF n.childNodes.item(j).nodeName <> "OBJECT" THEN CONTINUE
						n = n.childNodes.item(j)
						BREAK
					NEXT
					// 特殊文字系がサポートできないけど、ま、あきらめる
					r = SAFEARRAY(0, 1)
					k = 0
					FOR j = 0 TO n.childNodes.length - 1
						IF n.childNodes.item(j).nodeName <> "param" THEN CONTINUE
						r[k] = n.childNodes.item(j).value
						k = k + 1
						IF k > 1 THEN BREAK
					NEXT
					res[LENGTH(res)] = r
				NEXT
			NEXT
		FINALLY
			ie.Quit()
			IF fid <> -1 THEN FCLOSE(fid)
		ENDTRY
		RESULT = SAFEARRAY(0, LENGTH(res) - 1)
		FOR i = 0 TO LENGTH(res) - 1
			RESULT[i] = res[i]
		NEXT
	FEND


	FUNCTION UpdateKeywords(iniPath=NULL, chmPath=NULL, ext=".hhk")
		IF chmPath = NULL THEN chmPath = GET_UWSC_DIR + "\uwsc.chm"
		DIM fso = CREATEOLEOBJ("Scripting.FileSystemObject"), path = NULL, f = -1
		IF iniPath = NULL THEN iniPath = ".\help.uws"
		DIM d = fso.GetFile(chmPath).DateLastModified, ds = READINI("ChmInfo", "LastModified", iniPath)
		IFB d = ds THEN
			RESULT = TRUE
			EXIT
		ENDIF

		TRY
			// UWSC.chmを展開
			path = Decompile(fso, chmPath)

			// キーワードを読み込む
			DIM i, j, tmp
			HASHTBL res
			FOR i = 0 TO GETOLEITEM(fso.GetFolder(path).Files) - 1
				tmp = ALL_OLE_ITEM[i].Path
				IFB COPY(tmp, LENGTH(tmp) - LENGTH(ext) + 1) = ext THEN
					tmp = GetKeywordsFromFile(fso, tmp)
					FOR j = 0 TO LENGTH(tmp) - 1
						res[LENGTH(res)] = tmp[j]
					NEXT
				ENDIF
			NEXT

			// 一旦空にする
			f = FOPEN(iniPath, F_READ OR F_WRITE)
			j = FGET(f, F_LINECOUNT)
			i = 1
			tmp = TRUE
			WHILE i <= j
				IFB tmp THEN
					IF POS("[Keywords]", FGET(f, i)) = 1 THEN tmp = FALSE
					i = i + 1
				ELSE
					IFB POS("[", FGET(f, i)) = 1 THEN
						FPUT(f, "", i, F_INSERT)
						BREAK
					ELSE
						FDELLINE(f, i)
						j = j - 1
					ENDIF
				ENDIF
			WEND
			FCLOSE(f)
			f = -1
			_keywords = "[Keywords]<#CR>"

			RESULT = LENGTH(res)
			FOR i = 0 TO RESULT - 1
				WRITEINI("Keywords", res[i][0], res[i][1], iniPath)
				_keywords = _keywords + res[i][0] + "=" + res[i][1] + "<#CR>"
			NEXT
			WRITEINI("ChmInfo", "LastModified", d, iniPath)
			_keywords = _keywords + "[EndIni]"
		FINALLY
			IF path <> NULL THEN DeleteFolder(fso, path)
			IF f <> -1 THEN FCLOSE(f)
		ENDTRY
	FEND


	FUNCTION Open(keyword=EMPTY, chmPath=NULL)
		IF chmPath = NULL THEN chmPath = GET_UWSC_DIR + "\uwsc.chm"
		IF _keywords = NULL THEN _keywords = _ini_area
		DIM url = EMPTY
		IFB LENGTH(keyword) THEN
			DIM regexp = CREATEOLEOBJ("VBScript.RegExp")
			regexp.Global = TRUE
			regexp.IgnoreCase = TRUE
			regexp.Multiline = TRUE
			regexp.Pattern = "[\s\S]*\n\[Keywords\]([\s\S]*?\n)\[[\s\S]*"
			DIM keywords = regexp.Replace(_keywords, "$1")
			regexp.Pattern = "^(" + keyword + "[^=\r\n]*)=([^#\r\n]*(?:#(.*))?)$"
			DIM ms = regexp.Execute(keywords), i
			HASHTBL urls
			FOR i = 0 TO ms.Count - 1
				IFB urls[ms.Item(i).SubMatches.Item(1), HASH_EXISTS] THEN
					urls[ms.Item(i).SubMatches.Item(1)] = urls[ms.Item(i).SubMatches.Item(1)] + "/" + ms.Item(i).SubMatches.Item(0)
				ELSE
					urls[ms.Item(i).SubMatches.Item(1)] = ms.Item(i).SubMatches.Item(0)
				ENDIF
			NEXT
			url = " ::/"
			IFB LENGTH(urls) = 1 THEN
				url = url + urls[0, HASH_KEY]
			ELSEIF LENGTH(urls) THEN
				i = SLCTBOX(SLCT_NUM, 0, "開くキーワードを指定してください", urls)
				IF i >= 0 THEN url = url + urls[i, HASH_KEY]
			ENDIF
		ENDIF
		RESULT = (LENGTH(url) = 0) OR (LENGTH(url) > 4)
		IF RESULT THEN EXEC("hh.exe " + chmPath + url)
	FEND


DIM _keywords = NULL

TEXTBLOCK _ini_area
[StartIni]

[Keywords]

[ChmInfo]

[EndIni]
ENDTEXTBLOCK

ENDMODULE

解説

TEXTBLOCKにiniを持つ方式です。
[Keywords]セクションに、キーワードを持ちます。
[ChmInfo]セクションに、キーワードを抽出したchmファイルの情報(最終更新日時)を持ちます。
最終更新日時がこれと異なった場合、更新がかかることになります。
開くのは、Help.Open関数。他の関数は更新用です。
Help.UpdateKeywords関数でキーワードを更新する想定です。
Help.Open関数に組み込んで使っても良い気がしますが、、、一応独立させています。