UWSCで.chmファイルを解析する
UWSCのCUIインタープリターを作っていて、ヘルプを載せようかと思ったのだけど、天から
「ばーじょんあっぷ」
という声がして、面倒になってやめた。
しかし、、、バージョンアップはそう頻繁じゃないし、ヘルプを生成する補助モジュールがあっても良いんじゃないか?
と思ったので、作ってみた。
その前にhh.exeについて
hh.exeに.chmファイルを渡すと開いてくれるのですが、その際、開くところも指定可能です。
といってもURLがわかっているケースのみ。
hh.exe uwsc.chm ::/_RESOURCE/function.htm#getactiveoleobj
さらに、デコンパイルはこんな感じ。
hh.exe -decompile (出力先フォルダー) uwsc.chm
補助スクリプト
chm.uws
OPTION EXPLICIT, OPTFINALLY IFB GET_UWSC_NAME = "chm.uws" THEN DIM path = NULL, keys, i TRY // UWSC.chmを展開 path = CHM.Decompile(GET_UWSC_DIR + "\uwsc.chm") // キーワードを読み込む keys = CHM.GetKeyWords(path, TRUE) FOR i = 0 TO LENGTH(keys) - 1 PRINT "key:" + keys[i][0] PRINT keys[i][1] PRINT "--------------------------------------------------------------" NEXT FINALLY IF path <> NULL THEN CHM.DeleteFolder(path) ENDTRY ENDIF MODULE CHM DIM _fso = NULL, _regexp = NULL FUNCTION GetTempFolder(sub=FALSE) IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") 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(path) IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") RESULT = _fso.DeleteFolder(path, TRUE) FEND FUNCTION Decompile(chmFile, outPath=NULL) IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") RESULT = outPath IF RESULT = NULL THEN RESULT = GetTempFolder(TRUE) EXEC("hh.exe -decompile " + RESULT + " " + chmFile, TRUE) // パスを""で括るとダメ。cmdではOKなのに IFB _fso.GetFolder(RESULT).Files.Count = 0 THEN IF outPath = NULL THEN DeleteFolder(RESULT) RESULT = NULL ENDIF FEND FUNCTION GetKeyWords(path, getc=FALSE, ext=".hhk") IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") 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(tmp, getc) FOR j = 0 TO LENGTH(tmp) - 1 res[LENGTH(res)] = tmp[j] NEXT ENDIF NEXT RESULT = SAFEARRAY(0, LENGTH(res) - 1) FOR i = 0 TO LENGTH(res) - 1 RESULT[i] = res[i] NEXT FEND FUNCTION GetKeyWordsFromFile(path, getc=FALSE) IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") DIM i, 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 IFB ie.Document.body.childNodes.length THEN tar = ie.Document.body.childNodes.item(0).childNodes FOR i = 0 TO tar.length - 1 n = tar.Item(i) WHILE n.childNodes.length = 1 n = n.childNodes.item(0) WEND r = SAFEARRAY(0, 1) r[0] = n.childNodes.item(0).value r[1] = n.childNodes.item(1).value IF getc THEN r[1] = GetContent(_fso.GetParentFolderName(path), r[1]) res[LENGTH(res)] = r NEXT ENDIF 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 GetContent(path, url) IF _fso = NULL THEN _fso = CREATEOLEOBJ("Scripting.FileSystemObject") IF _regexp = NULL THEN _regexp = CREATEOLEOBJ("VBScript.RegExp") DIM fid, fname = url, tar = POS("#", url) IFB tar THEN fname = COPY(url, 1, tar - 1) tar = COPY(url, tar + 1) ENDIF fid = FOPEN(_fso.BuildPath(path, fname)) RESULT = FGET(fid, F_ALLTEXT) FCLOSE(fid) IFB tar <> 0 THEN _regexp.IgnoreCase = TRUE _regexp.Multiline = FALSE _regexp.Pattern = "<a\s[^>]*name=(['<#DBL>]?)" + tar + "\1[^>]*>[^<]*</a>([\s\S]*?)(?:<a\s[^>]*name=|$)" fid = _regexp.Execute(RESULT) IFB fid.Count THEN IF fid.Item(0).SubMatches.Count > 1 THEN RESULT = fid.Item(0).SubMatches.Item(1) ENDIF ENDIF TRY fid = CREATEOLEOBJ("InternetExplorer.Application") fid.Navigate("about:blank") fid.Document.body.innerHTML = RESULT RESULT = fid.Document.body.innerText IF tar <> 0 THEN RESULT = TRIM(RESULT) FINALLY fid.Quit() ENDTRY FEND ENDMODULE
.chmファイルのルールを知らないので、適当な部分が多いですが、UWSCのヘルプはこれで解析可能。
Decompile関数
第一引数は展開したい.chmファイル。(フルパス可。ただしスペース含むパスは不可)
第二引数を省略すると、テンポラリーフォルダーに適当なフォルダーを作って、そこに展開します。
テンポラリーフォルダーがスペースを含むパスだとおかしなことになるので、その場合は第二引数を指定してやってください。
リターンは、成功でパス。失敗はNULL
GetKeyWords関数
第一引数は、.chmファイルの展開先フォルダー
第二引数は、GetKeyWordsFromFile関数にそのまま渡す。
第一引数のフォルダー内の末尾が第三引数のファイルを探して、GetKeyWordsFromFile関数に渡します。
リターンは、GetKeyWordsFromFile関数の結果集合。
備考
GetContentまではしないで、GetKeyWordsで取得したURLに「::/」を付与して「hh.exe」呼んだ方が良い気もする、、、。