SIMH - 4.0BSDを動かすSIMH - テープ読込みコマンドの改良

2011年11月06日

SIMH - テープ作成コマンドの改良

SIMHのテープ形式でファイルにデータを書き込むコマンドmktapeを2月16日付記事に載せましたが、即席で作ったいい加減なコードなので、書き直すことにしました。具体的には、
  1. ブロックサイズを引数で指定できるようにする
  2. 大きなブロックサイズでの入力/出力の同期
    元のコードは、1回のread()呼び出しで1ブロック読みきれることを前提としており、ブロックサイズ(char buf[])を大きくしてread()した場合、標準入力がパイプだと一度で読みきれないことがありました。
    ※私の環境では、パイプからの読み出しは一度に最大2048バイトのようです。
  3. マルチファイルの書き込みサポート。つまり、1本のテープにこのように複数ファイル入れられるようにする:
    tar1<EOF>tar2<EOF>...tarN<EOF><EOF>
3.はBSD等のインストールテープイメージを作るスクリプトmkdisttap.pl に相当します。
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   10240
confファイルの書式は、
  • 先頭が#の行はコメント
  • レコードの形式は、
    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.tap
import.tapをsimhプロンプトからattach tsして、4.2BSD側からtar xvできます。

実装

以下、ソースコードです。


tapewrite.c:
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


corbie at 02:19│Comments(1)TrackBack(0) simh 

トラックバックURL

この記事へのコメント

1. Posted by zui   2014年02月22日 01:23
5 テープ読み込みツール使わせていただきました。ありがとうございます。

コメントする

名前
 
  絵文字
 
 
SIMH - 4.0BSDを動かすSIMH - テープ読込みコマンドの改良