2011年11月06日
SIMH - テープ作成コマンドの改良
SIMHのテープ形式でファイルにデータを書き込むコマンドmktapeを2月16日付記事に載せましたが、即席で作ったいい加減なコードなので、書き直すことにしました。具体的には、
mkdisttap.pl は、1つのスクリプト中に書き込み対象のファイル名とファイルの入出力がコーディングされているため、UNIXのバージョンごとにスクリプトを選ぶ必要がありました。
そこで、データと処理ロジックを分離させて、テープのファイル構成を別ファイルで指定できるようにシェルスクリプトを書くことにしました。
まず、作業ディレクトリの内容:
tape-bsd42.confは、4.2BSDのテープの構成を記述したファイルです。あらかじめエディタで作成しておきます。内容は
maketape tape.conf 出力ファイル
結果、4.2BSDのインストールテープファイルができます。
- ブロックサイズを引数で指定できるようにする
- 大きなブロックサイズでの入力/出力の同期
元のコードは、1回のread()呼び出しで1ブロック読みきれることを前提としており、ブロックサイズ(char buf[])を大きくしてread()した場合、標準入力がパイプだと一度で読みきれないことがありました。
※私の環境では、パイプからの読み出しは一度に最大2048バイトのようです。 - マルチファイルの書き込みサポート。つまり、1本のテープにこのように複数ファイル入れられるようにする:
tar1<EOF>tar2<EOF>...tarN<EOF><EOF>
mkdisttap.pl は、1つのスクリプト中に書き込み対象のファイル名とファイルの入出力がコーディングされているため、UNIXのバージョンごとにスクリプトを選ぶ必要がありました。
そこで、データと処理ロジックを分離させて、テープのファイル構成を別ファイルで指定できるようにシェルスクリプトを書くことにしました。
コマンドの構成
今回作ったプログラムは次の2つです。以下の説明で「テープ」とはSIMHのテープ形式のファイルの意味です。- tapewrite
1ファイルをテープに書き込むCプログラム。mktapeコマンドの置き換え。 - maketape
マルチファイルをテープに書き込むシェルスクリプト。tapewriteのフロントエンド
使い方
こんな感じで使います。マルチファイルをテープに書き込みます。4.2BSDのアーカイブ(tar.gz)から、インストールテープを作成する例です。まず、作業ディレクトリの内容:
$ ls archive maketape tape-bsd42.conf tapewriteディレクトリarchive/の下に4.2BSDのアーカイブ(stand.gz, miniroot.gzなど)が置いてあるものとします。(10月10日付記事参照)
tape-bsd42.confは、4.2BSDのテープの構成を記述したファイルです。あらかじめエディタで作成しておきます。内容は
# 4.2bsd VAX UNIX System 8/23/83 # tape 1 archive/stand.gz 512 archive/miniroot.gz 10240 archive/rootdump.gz 10240 archive/srcsys.tar.gz 10240 archive/usr.tar.gz 10240 archive/vfont.tar.gz 10240 # tape 2 archive/src.tar.gz 10240 archive/new.tar.gz 10240 archive/ingres.tar.gz 10240confファイルの書式は、
- 先頭が#の行はコメント
- レコードの形式は、
path bsize
ここで、pathは、テープに書き込むファイルのパス名、bsizeはブロックサイズ(バイト)
bsizeを指定しないと、デフォルト値10240が使われる。 - pathで指定したファイルの拡張子は.gz, .bz2をサポート。拡張子に応じてgunzip, bunzip2により展開した後のデータをテープに書き込む。ただし、元のファイルは変更しない。これら以外の拡張子および拡張子がないファイルは、そのままデータをテープに書き込む。
$ ./maketape tape-bsd42.conf bsd42.tap archive/stand.gz bsize=512 archive/miniroot.gz bsize=10240 archive/rootdump.gz bsize=10240 ......コマンドの形式は
maketape tape.conf 出力ファイル
結果、4.2BSDのインストールテープファイルができます。
$ ls -lhn bsd42.tap -rw-r--r-- 1 1000 1000 67M 2011-11-03 18:20 bsd42.tap
tapewriteコマンド
tapewriteコマンドは標準入力からデータを読み込んで、SIMHテープ形式でファイルに書き出します。オプションは次のとおり。- -b bsize
- --bsize bsize
- 書き込みのブロックサイズ(バイト)を指定。デフォルトは512バイト。
- -n
- --notend
- テープの終端マーカを書き込まない。続けてtapewriteを呼び出せば、テープにファイルを追記できる。
- -e
- --end
- テープの終端マーカのみ書き込む。
$ tar cf - src |./tapewrite -b 10240 > import.tapimport.tapをsimhプロンプトからattach tsして、4.2BSD側からtar xvできます。
実装
以下、ソースコードです。tapewrite.c:
read/writeを直接呼ぶのをやめて、fread/fwriteを使うようにしました。
read/writeを直接呼ぶのをやめて、fread/fwriteを使うようにしました。
/* tapewrite.c */ #include <unistd.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #define TP_BSIZE 512 #define TP_ENDMARK 0 int next_ok = 0; int end_of_tape = 0; static void tape_endmark(void) { uint32_t endmark = TP_ENDMARK; fwrite(&endmark, 4, 1, stdout); fflush(stdout); } static void tape_block_write(const char *buf, size_t n) { uint32_t length; length = (uint32_t)n; fwrite(&length, 4, 1, stdout); fwrite(buf, 1, n, stdout); fwrite(&length, 4, 1, stdout); } static void write_file(size_t bsize) { ssize_t n; char *buf; if ((buf = malloc(bsize)) == NULL) { perror("malloc"); exit(1); } while ((n = fread(buf, 1, bsize, stdin)) > 0) { if (n < bsize) { memset(&buf[n], 0, bsize - n); n = bsize; } tape_block_write(buf, n); } if (!feof(stdin)) { perror("fread"); exit(1); } free(buf); tape_endmark(); } static struct option long_options[] = { { "bsize", required_argument, NULL, 'b' }, { "notend", no_argument, NULL, 'n' }, { "end", no_argument, NULL, 'e' }, { 0, 0, 0, 0 } }; static void usage(void) { fprintf(stderr, "usage: tapewrite [-b bsize][-n]\n" " tapewrite -e\n"); exit(1); } int main(int argc, char **argv) { int c; int idx; size_t bsize = TP_BSIZE; for (;;) { c = getopt_long(argc, argv, "b:en", long_options, &idx); if (c == -1) break; switch (c) { case 0: /* long option */ break; case 'b': bsize = atoi(optarg); break; case 'e': end_of_tape = 1; break; case 'n': next_ok = 1; break; case '?': default: usage(); break; } } if (bsize < 512) bsize = 512; /* data is rounded to an even number of bytes */ bsize = (bsize + 1) & ~1; if (end_of_tape && next_ok) usage(); if (end_of_tape) { tape_endmark(); return 0; } write_file(bsize); if (!next_ok) tape_endmark(); return 0; }maketape:
#! /bin/bash # maketape ver.0.1 if [ $# -lt 2 ]; then echo "usage: maketape conf outfile" exit 1 fi outfile=$2 list=$(awk '/^#/ { next } NF > 0 { print $2 ":" $1 }' $1) for p in $list; do f=${p#*:} b=${p%%:*} bsize=${b:-10240} echo "$f bsize=$bsize" case $f in *.gz) cmd="gunzip -c $f" ;; *.bz2) cmd="bunzip2 -c $f" ;; *) cmd="cat $f" ;; esac $cmd | ./tapewrite -b $bsize -n >> $outfile done ./tapewrite -e >> $outfile
トラックバックURL
この記事へのコメント
1. Posted by zui 2014年02月22日 01:23
