DEC社 TOPS-20を使う方法 ディレクトリのツリーウォーク編

ディレクトリのツリーウォーク

TOPS-20 ディレクトリ構造の調査

2018年1月、歴史的に貴重なDEC社 TOPS-20を使用しています。

Panda TOPS-20 distributionには、どんなプログラムをインストールしているのでしょうか?  ディレクトリ構造は、どのような構成になっているのでしょうか?  ディレクトリの構造を調べてみたいと思います。

ディレクトリ構造ツリーを出力するようなコマンドが見当たりません。Unixのtreeコマンドなどがあれば良いのですけど。

DIRECTORYコマンドは、ディレクトリ名とそのファイル名の一覧を表示します。

@DIRECTORY (OF FILES)

   TOPS20:<PI>
 DIR.TXT.1
 HELLOX.C.1
 PROJ.DIRECTORY.1

 Total of 3 pages in 4 files
@

 

ディレクトリ名にワイルドカード(*)を指定するとすべてのディレクトリ一覧が得られそうです。

大量のディレクトリ一覧が出力されました。その中のディレクトリ名の出力部分を加工すると、ディレクトリの構造をツリー表示できそうです。

TOPS20:<BBOARD>

TOPS20:<CHIVES>

TOPS20:<CHIVES.V1>
・・・

Unixなら標準出力をリダイレクトしてファイルに保存するか、パイプを通してteeコマンドでファイルに保存します。
何か方法はないかと、カンマ(,)を入力してサブコマンドに入ります。クエスチョンマーク(?)を入力してヘルプ表示してみると、OUTPUTサブコマンドがありました。もう一度クエスチョンマーク(?)を入力して、OUTPUTサブコマンドのオプションを調べるとファイル名を指定することがわかりました。

本当に、TOPS-20のシェルはユーザーインターフェイスが優れていますね。

@DIRECTORY (OF FILES) ,
@@? confirm with carriage return
  or one of the following:
 ACCOUNT                      ALPHABETICALLY
 ARCHIVE                      BEFORE
 CHECKSUM                     CHRONOLOGICAL
 COMPLETE                     CRAM
 DATES                        DELETED
 DOUBLESPACE                  EVERYTHING
 FIND                         GENERATION-RETENTION-COUNT
 HEADING                      INVISIBLE
 LARGER                       LENGTH
 LPT                          NO
 OFFLINE                      ONLINE
 OUTPUT                       PROHIBIT-MIGRATION
 PROTECTION                   RESIST-MIGRATION
 REVERSE                      SEPARATE
 SINCE                        SIZE
 SMALLER                      TIMES
 USER
@@OUTPUT (TO FILE) ? FILE NAME
@@OUTPUT (TO FILE) ^C
@

システムのディレクトリ一覧も取得するので、ENABLEコマンドで特権モードになります。
DIRECTORYコマンドで、PS:(プライマリーストレージ)のすべてのディレクトリ一覧を取得して、テキストファイルに出力します。

@ENABLE (CAPABILITIES)
$
$DIRECTORY (OF FILES) PS:<*>,
$$OUTPUT (TO FILE) DIR.TXT
$$
$
$DISABLE (CAPABILITIES)
@

ディレクトリ一覧をAWKスクリプトで加工してディレクトリ構造をツリー表示すれば、ディレクトリ構造を調べることができます。

TOPS-20 ディレクトリ構造

AWKスクリプトで、TOPS-20のディレクトリをツリーウォークするスクリプトを作成しました。

AWKスクリプトによるTOPS-20ディレクトリ構造を以下に示します。

@AWK -f DIR_TREE.AWK DIR.TXT
TOPS20:
 |-- ROOT-DIRECTORY
 |-- ACCOUNTS
 |-- ALGOL
 |-- BBOARD
 |-- BLISS
 |-- BLISS-SOURCES
 |-- CHIVES
 |   `-- V1
 |       |-- CONFIG
 |       |-- DOCUMENTATION
 |       `-- SOURCE
 |-- CLISP
 |-- DECNET-SOURCES
 |-- DECNET-TOOLS
 |-- DOCUMENTATION
 |-- DOMAIN
 |-- EMACS
 |-- EXEC-SOURCES
 |-- FAIL
 |-- FINGER
 |-- FORTRAN
 |   |-- COMPILER
 |   |-- DOCUMENTATION
 |   |-- OTS-DEBUGGER
 |   |-- SYSTEM
 |   |-- TEST
 |   `-- TOOLS
 |-- FTP
 |   `-- OLD
 |-- GALAXY-SOURCES
 |-- GAMES
 |-- GIGI
 |-- HELP
 |-- KCC-6
 |   |-- BUILD
 |   |-- INCLUDE
 |   |   |-- ARPA
 |   |   |-- NET
 |   |   |-- NETINET
 |   |   `-- SYS
 |   |-- KCC
 |   |-- LIB
 |   |   |-- GEN
 |   |   |-- INET
 |   |   |-- MATH
 |   |   |-- SOCKET
 |   |   |-- STDIO
 |   |   |-- TEST
 |   |   |-- USER
 |   |   `-- USYS
 |   `-- T20
 |-- KERMIT
 |-- LANGUAGE-SOURCES
 |-- LISP
 |   `-- DCROSS
 |-- LISPUSERS
 |-- LOGO
 |   |-- HELP
 |   |-- LIBRARY
 |   `-- SOURCES
 |-- MACLISP
 |-- MAIL
 |-- MAKE
 |-- MM
 |   |-- BINARIES
 |   |-- DOCUMENTATION
 |   |-- LOCAL
 |   |-- SPELL
 |   `-- VAX
 |       `-- CAFARD
 |-- MONITOR-SOURCES
 |-- NETSRV
 |-- NFS
 |-- OLD-SYSTEM
 |-- OPERATOR
 |-- PDP-8
 |   `-- MAINDEC
 |-- PHONE
 |-- PI
 |   `-- PROJ
 |-- ROOT
 |   |-- BIN
 |   |-- ETC
 |   `-- USR
 |       |-- BIN
 |       |-- DICT
 |       |-- LIB
 |       `-- LOCAL
 |           |-- EMACS
 |           |   |-- ETC
 |           |   |-- INFO
 |           |   `-- LISP
 |           |       `-- TERM
 |           `-- LIB
 |-- SAIL
 |-- SCRIBE
 |-- SOURCES
 |-- SPOOL
 |-- SUBSYS
 |-- SWSKIT
 |-- SYSTEM
 |-- SYSTEM-ERROR
 |-- TELNET
 |-- TOOLS
 |   |-- ALU
 |   |-- BLIS10
 |   |-- CHANS
 |   |-- CISTS
 |   |-- DCNSPY
 |   |-- DOBOPR
 |   |-- DOCUMENTATION
 |   |-- ENQTST
 |   |-- EXEC-MODS
 |   |-- FORMAT
 |   |-- KRYPTN
 |   |-- MAIL
 |   |-- MAILER
 |   |-- MONRD
 |   |-- QUETST
 |   |-- RDMAIL
 |   |-- REDIT
 |   |-- REV
 |   |-- RMTCON
 |   |-- SCM
 |   |-- SED
 |   |-- SSU
 |   |-- SYSDPY
 |   |-- TSTATS
 |   |-- TTYINI
 |   |-- TYPVFU
 |   |-- UNITS
 |   |-- USAG20
 |   |-- USAH20
 |   `-- VNP36
 |-- TSU
 |-- UNSUPPORTED
 |-- UTF9
 |-- UTILITIES
 `-- WINDOW
@

ALGOL、LOGO、FORTRAN、LISPというディレクトリ名がありますので、いろんな言語処理系がインストールされているようです。

AWKでディレクトリツリーウォーク

AWKスクリプト

ディレクトリをツリーウォークするAWKスクリプトを以下に示します。

@TYPE  dir_tree.awk
BEGIN {   }

$1 ~ /^([A-Z][A-Z0-9]*):<([A-Z0-9\-\.]+)>$/ {
    fDev = match($1,/([A-Z][A-Z0-9]*):/)
    if (fDev > 0) { sDev = substr($1, RSTART , RLENGTH)     }
    fDir = match($1,/<(.+)>/)
    if (fDir > 0) { sDir = substr($1, RSTART+1 , RLENGTH-2) }
    #
    if (fDev > 0 && fDir > 0) {
        sDirF = sDev "." sDir
        num = split(sDirF ,aryDir , ".")
        ix = 1
        sDirP = aryDir[ix]
        while (num-- >1) {
            sDirC = aryDir[++ix]
            if (!(sDirP in Dic))  { Dic[sDirP] = sDirC }
            else
            {
                if (","Dic[sDirP]"," !~  ","sDirC"," )  {
                    Dic[sDirP] = Dic[sDirP] "," sDirC
                }
            }
            sDirP = sDirP "." sDirC
        }
    }
}
END {
    treeout("",sDev )
}
# Tree Outout
function treeout(Tab ,sDirP    ,_sDirC ,_sDirN ,_num ,_ix ,_aryDir ) {
    if (Tab=="") { printf("%s\n", sDirP) }
    _num = split(Dic[sDirP] ,_aryDir ,",")
    _ix = 1
    while (_num--> 0) {
        _sDirC = _aryDir[_ix++]
        printf("%s %s\n",Tab "" ((_num!=0)?" |--":" `--"), _sDirC)
        _sDirN = sDirP "." _sDirC
        if (_sDirN in Dic) {
            treeout(Tab "" ((_num!=0)?" |  ":"    "),_sDirN)
        }
    }
}
@

AWKスクリプト説明

ディレクトリ構造をツリー表示するAWKスクリプトのアルゴリズムは、思ったより簡単ではありませんでした。プログラミングする前に、PAD図でアルゴリズムを書きます。

PAD図はプログラム論理を記述する方法で、Problem Analysis Diagram(問題分析図)と呼ぶ二次元木構造の図面です。

素晴らしいPAD図

TOPS-20のディレクトリは、「デバイス名:<ディレクトリ名.サブディレクトリ名・・・>」です。

TOPS-20ディレクトリ名

TOPS-20

パターンにマッチする正規表現「^([A-Z][A-Z0-9]*):<([A-Z0-9\-\.]+)>$」で、ディレクトリ一覧の行を抽出します。

メイン処理部のPAD図を下記に示します。

AWK ディレクトリ登録

AWK ディレクトリ登録

辞書 Dic[]は、親ディレクトリに対応する子ディレクトリをカンマで区切って格納します。

DirA
DirA,DirB
DirA,DirB,DirC・・・

入力フィールド $1からmatch()関数でデバイス名(sDev)とディレクトリ名(sDir)を取り出します。
変数 sdirFにデバイス名(sDev)とディレクトリ名(sDir)をドットで連結します。

sdirFをドットで分解して配列 aryDir[]に設定します。ドットの数がデバイス名から始まるディレクトリ階層の深さになります。

階層のインデックス(ix)←1とします。
aryDirの1番目を親ディレクトリ(sDirP)に設定します。
階層の深さだけ、以下を繰り返します。

  • aryDirの(++ix)番目を子ディレクトリ(sDirC)に設定します。
  • 親ディレクトリ(sDirP)が辞書 Dicに未登録の場合は、辞書 Dic[sDirP]に子ディレクトリ(sDirC)を格納します。
  • 辞書に登録済みの場合は、辞書 Dic[sDirP]にカンマ(,)で子ディレクトリ(sDirC)を連結します。この場合、子ディレクトリが連結内にあれば二重登録にならないように子ディレクトリは捨てます。
  • 親ディレクトリに子ディレクトリをドット連結したものを親ディレクトリにします。

子ディレクトリの二重登録防止のチェックは、前後にカンマを付加したパターンでマッチするか調べます。

「”,”Dic[sDirP]”,”」 と 「”,”sDirC”,”」 のパターンマッチ

このメイン処理は、下記入力から次のように辞書登録します。

【入力】
TOPS20:<BBOARD>
TOPS20:<CHIVES>
TOPS20:<CHIVES.V1>
TOPS20:<CHIVES.V1.CONFIG>
TOPS20:<CHIVES.V1.SOURCE>

【辞書登録】
親ディレクトリ 子ディレクトリ
TOPS20: BBOARD,CHIVES
TOPS20:.CHIVES V1
TOPS20:.CHIVES.V1 CONFIG,SOURCE

入力ファイルがEOFになるとENDパターンで関数 treeout(“” ,sDev)を呼出してツリー出力します。変数 sDevは、デバイス名で「TOPS20:」を格納しています。


次にツリー出力する関数treeout()のPAD図を下記に示します。

AWK ツリー出力

AWK ツリー出力

関数treeout()仮引数
仮引数 内容
Tab 左部のTreeタブ
sDirP 親ディレクトリ

Treeタブが空文字のときツリーのトップなので、親ディレクトリを出力します。
辞書 Dic[sDirP]は、親ディレクトリに対応する子ディレクトリをカンマで区切った情報を格納しています。カンマで分解して配列 _aryDir[]に設定します。子ディレクトリの数は _num です。
以下を_num回繰り返します。

  • 子ディレクトリ(_sDirC)を取り出します。
  • ツリーを書きます。Treeタブと”├─”と子ディレクトリ(_sDirC)を出力します。
  • 子ディレクトリに孫がいるか調べるために、親と子ディレクトリをドットで連結したフルパス(_sDirN)を生成します。
  • 辞書 Dic[]に_sDirNが存在した場合は、関数treeout()をリカーシブコールして孫以下のツリーを書きます。

まとめ

Panda TOPS-20 distributionのディレクトリ構造を調べるために、AWKでディレクトリをツリーウォークするスクリプトを作成しました。

ディレクトリ名を見ると、なかなか興味深いプログラムがインストールされているみたいです。そのうち、調べてみようと思います。

TOPS-20に関するブログ

歴史的に貴重なDEC社 TOPS-20を使うためにKLH10エミュレーターをLinuxにインストールして、TOPS-20を実行することができました。

TOPS-20に関するブログを以下に示します。

DEC社 TOPS-20を使う方法 KLH10エミュレーター導入編

DEC社 TOPS-20を使う方法 KLH10エミュレーター実行編

DEC社 TOPS-20を使う方法 TOPS-20コマンド使用編

DEC社 TOPS-20を使う方法 エミュレーター簡単起動/停止編

DEC社 TOPS-20を使う方法 TELNETでログイン編

DEC社 TOPS-20を使う方法 タイムゾーンの設定編