ファイラーが起動されるのは,コマンド#O
(ファイルのオープン)等のファイルオープン系コマンドを実行して開いた入力窓で何も入力せずに[Enter]だけを入力したときです。この動作を,そのままマクロにすればよいのです。強く推奨(笑)するキーアサインは,コマンド#98
(コマンドラインからファイラー起動)の初期設定と同じ\[CR]です。コマンド#n
(行挿入=#53
)は[N]ew lineと覚えやすい^Nがあれば十分でしょう。なお,コマンドに割り当てている\[CR]を削除する必要はありません。編集モードではマクロが優先され,コマンドへのキー割当を残しておくことにより,メニューでのコマンドキー入力という場面でシフトキー分岐を使って処理を分けることもできます。
1 \[CR] "" ?. #O #m
基本的に複数のモードをまたいで表示のオン・オフをすることはできません。特にファイラーの表示はファイラープールという一時的にフォーマットしたものを使っているので,ファイル・ディレクトリの移動が発生すると再表示がうまくいかず,表示がとんでもなく崩れることがあります。
ファイラーを起動している状態の視覚効果に必然性がなければ,ファイラーを起動してから関数&s
(サイレントモード)で表示の更新を止めるのではなく,ファイラーを起動する前の段階から画面表示を停止しておきましょう。
1 ^^ "" ?.
&s ;編集モードでサイレントにする
#O #m ;ファイラー起動
; ;何か処理をする
(-1){ #[ (s)??{} } ;編集モードに戻る
&d ;再表示する
なお,製品版ではこのようにしてもファイラーカーソルのようなものが表示されたりするという問題がありましたが,高橋版では修正されて大変寡黙に動作するようになっています。
ファイル名入力窓に,ファイラーで表示したいパスを入力して[Enter]で確定すると,そのパスでファイラーを起動できます。架空のディレクトリを指定することはできませんので,パスが見当たらない場合はファイラーに移行せず,再度入力を促します。
1 ^^ "" ?.
#O "c:\doc\vzmacro.faq" #m ;パス末尾の'\'は有っても無くてもよい
これは拡張子のあるディレクトリが指定されている例です。もしも,同名のファイルが存在すれば,ファイル名が指定されたものと見なされ,該当のファイルがオープンされます。
ファイル名入力窓に,ファイラーで指定したいマスクを入力して[Enter]で確定すると,そのマスクの設定でファイラーを起動できます。
1 ^^ "" ?.
#O "*.def" #m ;VZマクロ以外をマスク
前項で述べた指定パスと組み合わせることもできます。ただし,ディレクトリをマスクすることはできません。
1 ^^ "" ?.
#O "c:\vz\mac\*.def" #m ;マクロのあるディレクトリでマクロだけ表示
ファイル名入力窓で,実在のファイル名を入力した場合は,該当のファイルがオープンされます。しかし,指定したファイルをオープンしたくないときもあります。例えば次のような場合です。
このような場合は,フルパス(あるいはカレントディレクトリのファイル)のファイル名の末尾に'*'を付加してファイラーを起動します。
1 ^^ "" ?.
d="c:\vz\lzh", ;末尾に'\'を付けない
&Sh(pa,"hoge.lzh")
mx+, ;ファイラーイベントを起動させない
#O &?("%s\%s*",d,pa) #m ;パスの末尾に'*'を付加
mx-,
;;マクロを停止させれば手動で処理が可能になる
すみません。FAQではないですね。
古典的とも言える上村氏のワザが,今なお有効です。ファイル名変更窓を出して削除しキャンセルで元に戻します。これだけでファイル名が削除バッファとpr(リネーム用バッファ)に取得されています。出力はカーソル位置なら編集テキストでも入力窓でも大丈夫ですし,バッファ間のコピーや&m()
/&?()
による書式出力もできます。
1 ^^ "" (s==3)??.
#R #H #[
特筆すべきことは,高橋版でlf(長いファイル名を扱う)を有効にした環境であれば,長いファイル名で取得できる点です。コマンド#P
(ファイル名の複写)やwa+146
の出力では,こうはいきません。まさに温故知新のワザと言えるでしょう。
v1.6で追加されたファイラー関数を使います。
まず,ファイラー関数&Fi
は選択ファイルの先頭へ選択ポインタを移動する機能を持ちます。複数選択されている場合だけに必要な処理ではなく,プールされたファイル群の初期化に類する処理ですので必ず実行しなければなりません。実行しないと,ツバを付けた手を宙に浮かせて次にツバを付ける饅頭をまさに選択しようとしている状態ですから,饅頭は確保したことになりません。
次に,ファイラー関数&Fg()
でファイル名を取得します。引数はひとつ乃至ふたつで,ひとつ目の引数には格納先を,必要があればふたつ目の引数として1を与えることにより常にフルパスで取得できます。
1 ^\ "" (s!=3)?.
&Fi &Fg(pr,1) ;ファイル名をprへ格納
&m("%s",pr) ;取得されたファイル名を表示
なお,ディレクトリ上で実行した場合はそのディレクトリ名が,上位ディレクトリ記号(..\)上で実行した場合はそのままの表示が取得されますので注意してください。このような実行を許可しないためには,&Fg()
の返り値としてファイルの属性が返されるのを利用します。
&Fg()
の返り値は,取得されたファイル名を解析して得られたビットフラグの加算値です。次の表を参照してください。
値 | 属性 |
---|---|
+1 | readonly |
+2 | hidden |
+4 | system |
+8 | binary or label |
+16 | directory |
+32 | archive |
+128 | select |
正常な位置で実行されなかった場合は,次の例のように警告を出すということができます。
&Fg(pr,1)
(r&16)?{ &m("ディレクトリが選択されました") . }
ファイルのタイムスタンプを取得するには,ファイルのDTA領域にアクセスすることが必要です。このためファンクションコールを用いて,ファイルハンドルを操作しなければなりません。このようなことを本当に理解するには,DOSシステムに関する大変難解な知識を必要とすることなのですが,そんなことは知らなくても手順さえ確かに踏めば誰でもできます。
手順としては,次のようになります。
要するに,すべてファンクションコールによって「楽ちん」に行えます。なにしろ,こちらは作業用の箱と値を用意するだけで,面倒な作業自体はすべてシステムがやってくれるのです。何の見返りも要求しない純粋なボランティアのような,ありがたいシステム様さまではありませんか。
ファイルハンドルをオープンするというのは,システムがファイルにアクセスするための整理券を受け取ってアレコレできる権利を得た状態です。ファイルをひとつの部屋と見なせば,鍵をもらって部屋の扉をあけたところでしょうか。整理番号はファイルハンドル番号です。部屋には「梅の間」とか「松の間」とか名前がありますが,鍵には番号しかありません。なかなかセキュリティの高いハイテクなシステムが導入されていますな。わははは。
ファイル名を伝えるには,フルパスのファイル名が格納されているアドレスを指定します。本来はセグメントとオフセットの両方を用意しなければなりませんが,セグメントの方は賢いVZ君が気を利かせて設定してくれますので,オフセットだけを設定すればよいのです。何て楽ちんなんでしょう。
(注意)この部品だけで実行させてはいけません。
&Fi &Fg(pr,1) ;ファイル名をprに格納
ax=$3D00, dx=pr, ;設定値とファイル名のアドレスをレジスタ変数へ
&i($21) ;ファンクションコール
h=ax, ;axにファイルハンドル番号が返る
ファイルハンドル番号は,後々使いますので変数に保存しておきました。
ファイルという部屋の鍵をあけて中に入ると,その部屋の中にかぎり,いろいろな資料を自由に見たり書き直したりできるようになります。タイムスタンプの取得は「見る」行為に属します。しかし,べたで書いてないんです,これが。日付と時刻はそれぞれ2バイトずつのデータで,16ビットに納めています。日付は年に7ビット,月に4ビット,日に5ビットを割り当て,時刻は時に5ビット,分に6ビット,秒に5ビットを割り当てています。これらの値をビット演算を使って引き出すのです。
要素 | ビット数 | 表現可能数 | 使用数 | 範囲 | 備考 |
---|---|---|---|---|---|
年 | 7ビット | 128 | 100 | 1980〜2079 | |
月 | 4ビット | 16 | 12 | 1〜12 | |
日 | 5ビット | 32 | 31 | 1〜31 | |
時 | 5ビット | 32 | 24 | 0〜23 | |
分 | 6ビット | 64 | 60 | 0〜59 | |
秒 | 5ビット | 32 | 30 | 0〜29 | 2倍して0〜58として扱う |
注目すべきは年と秒です。DOSの寿命は最長でも2079年までということになりますね。7ビットなので2107年まで可能だったはずですが,設定範囲は2079年までしかありません。人類存亡危機時代の到来の方が早かったりするかもしれませんが,ともかく打ち止めで2080年になったら100年時間をサバ読んで使わざるをえません。秒の方は,偶数しかなかったなんて,言われなければ気付かない人がほとんどだと思います。あまり支障はありませんが,秒を取り出した時は2倍するのを忘れないようにしましょう。
閑話休題。
さて,ファイルの更新日時を取得する設定値は5700hです。そのほかにハンドルでオープンしたファイルハンドル番号をマスタキーとして渡す必要がありますので,オープンしたときの返り値を保存した変数fを使います。
(注意)この部品だけで実行させてはいけません。
&Fi &Fg(pr,1) ;ファイル名をprに格納
ax=$3D00, dx=pr, ;設定値とファイル名のアドレスをレジスタ変数へ
&i($21) ;ファンクションコール
h=ax, ;axにファイルハンドル番号が返る
ax=$5700, ;axの設定値は5700h
bx=h, ;bxはファイルハンドル番号
&i($21) ;ファンクションコール
a=1980+dx>>9, ;年
b=dx>>5&$F, ;月
c=dx&$1F, ;日
d=cx>>11&$1F, ;時
e=cx>>5&$3F, ;分
f=(cx&$1f)*2, ;秒
取得できなかった場合は,AXにエラーコードが返ることになっていますが,万が一にもないと思われるのでエラー処理は省略します。どうしても心配な人は調べてみてください。正常終了すればALにゼロが,それ以外はエラーです。
本当は,これを先に説明すべきところですが,進行上の都合で。
資料を参照し終えたら,部屋の鍵を返さなければいけませんね。ファイルハンドルをクローズする設定値は,AXに3E00h,BXにファイルハンドル番号です。直前に実行したファイルの更新日時の取得ではBXに値は格納されませんので,そのまま値は残っています。
それでは,すべての手順をまとめてみましょう。
1 ^^ ""
(s-3)?. ;ファイラーのみ実行
&Fi &Fg(pr,1) ;ファイル名をprに格納
; === Open handle ===
ax=$3D00, dx=pr, ;設定値とファイル名のアドレスをレジスタ変数へ
&i($21) ;ファンクションコール
bx=ax, ;axに返ったファイルハンドル番号をbxへ
; === Get Time Stamp ===
ax=$5700, ;axの設定値は5700h
;ファイルハンドル番号はbxに設定済
&i($21) ;ファンクションコール
a=1980+dx>>9, ;年
b=dx>>5&$F, ;月
c=dx&$1F, ;日
d=cx>>11&$1F, ;時
e=cx>>5&$3F, ;分
f=(cx&$1f)*2, ;秒
; === Close handle ===
ax=$3E00, ;ファイルハンドル番号はbxに残っている
&i($21)
&m("%4d/%02d/%02d %02d:%02d:%02d",a,b,c,d,e,f)
前項ではファイルのタイムスタンプを取得したわけですが,同じファンクションで任意のタイムスタンプを設定することができます。原稿が〆切に間に合わなかったときに,「いや〜,昨日完成してたんだけど持ってくるのを忘れちゃってさぁ〜」なんて言い訳のためにある機能ではありません,念の為。
AXへの設定値5700hはタイムスタンプの取得でしたが,タイムスタンプの設定は5701hをAXへの設定値とし,そのほか設定日付をDXに,設定時刻をCXに与えます。設定日付・時刻はそれぞれ2バイトにパックされた値でなければなりません。
次のマクロは,ファイラーカーソルの置かれたファイルのタイムスタンプを「2001年1月1日午前零時」に変更します。
1 ^^ ""
(s-3)?.
a=2001, b=c=1, d=e=f=0, ;2001/01/01 00:00:00
&Fi &Fg(pr,1) ;ファイル名をprに格納
; === Open handle ===
ax=$3D00, dx=pr, ;設定値とファイル名のアドレスをレジスタ変数へ
&i($21) ;ファンクションコール
bx=ax, ;axに返ったファイルハンドル番号をbxへ
; === Set Time Stamp ===
dx=(a-1980)<<9|b<<5|c, ;dxに日付のパックデータをセット
cx=d<<11|e<<5|(f/2), ;cxに時刻のパックデータをセット
ax=$5701, &i($21) ;axの設定値は5701h
; === Close handle ===
ax=$3E00, ;ファイルハンドル番号はbxに残っている
&i($21)
#U ;ファイラー表示の更新
#N &Sy(pr) &?("%s",bx) #m ;ファイラーカーソル位置の更新
前項では,単一のファイルに対して処理を行いましたが,複数選択して処理させたい場合があると思います。このような場合に利用できるファイラー関数が&Fn
です。ファイラー関数&Fn
は,次の選択ファイルに処理ポインタを移動する機能があります。次の選択ファイルがもうない場合は,ゼロを返します。したがって,返り値が真(0以外)であればループの先頭から繰り返すという処理にすればよいのです。
次のマクロは選択ファイルを連番のファイル名にするサンプルです。
1 ^^ "" (s!=3)?. ;ファイラーのみ
j=e=i-, ;[i/e]:成功数/失敗数 [j]:連番用
(fl..5)??{ ;マークしていなければ
FM[-, ' ' FM], ;カーソル位置のファイルを選択
}
&Fi (r)??. ;選択ファイルの先頭へ
:a &Fg(pa,1) ;ファイル名を取得
fv^=4, #V ;再表示
&Sy(pa) ;パス名取得・解析
(si>cx)?{ ;puに拡張子抽出
&Sc(pu,cx) } ;パスの末尾が拡張子の開始位置と一致なら
?pu.0-, ;拡張子がないので先頭にヌルを埋める
&Sp(pr,"%05u%s",j++,pu) ;新しいファイル名 00000.ext
dx=pa, ;old path / pa
di=pr, ;new path / pr
ax=$5600, &i($21) ;Rename
(r)?e++, ?i++, ;カウントする
&Fn (r)?>a ;★r:選択ファイル数があればループ
#U ;再表示
&m("success: %d file(s). / failure: %d file(s).",i,e) .
すみません。FAQではありませんね。
テキスト構造体wa内に持つ現編集ファイルのフルパスは,編集モード以外のモードでも参照が可能ですので,ファイラー専用マクロでも利用できます。
1 ^^ "" (s==3 && wc)??. ;編集ファイルがあり,かつファイラーで起動
&Sc(pa,wa+146) ;paに現編集ファイルのパスを複写
&Sy(pa) bx.0-, ;ファイル名の開始位置でチョッキン
#P &?("%s",pa) #m ;パス/マスク入力窓に出力
編集モードで起動した場合は#P
でなく#O
を使うように分岐を工夫すれば,水陸両用車のように便利になりますね。
マクロローダーの機能の一部として,ファイラーで選択したマクロをロードするという場合があります。このようなケースにおいてはファイラーのソートオプションの初期値FSを1(ファイル名でソート)にすると使い易いですね。
次の例は,上村氏のOUTLAW.DEFにおいて外部マクロをコールする場合に,ファイル名でソートしたファイラーを起動するというものです。fl..6
はファイラーのカーソル位置で,通常に起動したファイラーに影響を与えないために保存・復帰しています。
10 [ESC]^ "Exec Macro"
(s)?.
&Sp(pr,"%s*.def",pi)
((fl..6)[, fs[=1, ;★H_ear sort filename
&Fl(pr) &Cr fg.1=1,
fs], fl..6=0], ;★
(r)? { &g("【 Ext Mac】",20) (r==-2)? >^ (r<0)?. &Sh(pr,pw) (0)}
? &Fg(pr)
pm=pr,
>88
「"文字列"」でイベントマクロ(!Filer や !Edit など)を起動すると、イベントマクロが終わった時点で呼出元のマクロがエラーになったり、終わってしまったりすることがあるのは,VZのバグによります。
対策としては,次のような方法があります。
v1.6以降に新設されたコマンドがあり,例えば #P でパスマスク窓が出ます。文字や文字列と違って,シフトキーやキャップスロックが押されている場合を考慮しなくても済みます。
v1.57以前のバージョンにも対応するなら,「"文字列"」ではなく「'文字'」を使用すれば,マクロの停止を回避できます。'文字'であれば,大文字/小文字対策も簡単な条件式で対応可能です。
例:(文字を変数cで受け取ったとします)
(c & 95 == 'P') または (c | 32 == 'p')
また,他のコマンドにも対応したいなら,次のようなフィルタを通します。
(c >= 'A' && c <= 'Z')?{ c += $20, }
この項は,fgalpk/mes/14/03449/03475(1994/01/29) における榊原知氏の発言を参考としました。