ホストプログラムmmmのTALKデータを表示

往年のアスキーネットに似た操作系のパソコン通信のホストプログラム「mmm(トライエム)」のtalkルームの内容をテキストで取り出したくて解析して、プログラムも作ってみた。
mmmのTALKディレクトリにはルームの使用状況を表すROOMS.DATと、ルーム番号に応じてR1.USR〜R1500.USRの座席状況と、R1.IDX〜R1500.IDXの発言内容を格納したファイルが存在する。使用していないルーム番号のUSRファイル、IDXファイルは存在しない。
ROOMS.DATは1500バイト固定のファイルで、ルーム番号に対応したバイト位置が00なら未使用、01なら使用中を示している。
USRファイルは1レコード31バイトのファイルで、各レコードの最初の1バイト目が次に続く文字列の長さを、2バイト目から8バイト分が文字列、10バイト目から4バイト分がFAT形式の日時、14バイト目が次に続く文字列の長さ、15バイト目から16バイト分が文字列、31バイト目がフラグとなっている。
1レコード目は、最初の文字列がルームの状況(Open、Closed、Locked)を表しており、続いてルームの状況が更新された日時、2つ目の文字列やフラグは未使用となっている。
2レコード目以降はルーム内に座席のあるユーザーを示しており、最初の文字列がユーザーIDを、続いてユーザーが最後に入室した日時、2つ目の文字列がハンドル名を表し、フラグは座席がある場合は1、無い場合が0、ノック中である場合は2が加えられ、リーダーである場合は4が加えられる。
ちなみにFAT形式での日付は2バイトで、上位7ビット分が1980年からの年、次の4ビット分が月、下位5ビット分が日を表す。時間も2バイトで、上位5ビット分が時、次の6ビット分が分、下位5ビット分が2秒ごとの秒を表す。
以下、LSI-C86試食版で作ったUSRファイル及びIDXファイルのテキスト化表示プログラムのソース。いつものごとく標準関数しか使ってないので、SLザウルスとかでも動くと思う。使い方はUSRファイルもしくはIDXファイルを指定して起動するだけ。

#include <stdio.h>
#include <string.h>

int strcpyn(char *sd,char *ss,int sl,int lm) {
    int i;

    i=0;
    while(i<sl && i<lm && sl<=lm) {
        sd[i]=ss[i];
        i++;
    }
    sd[i]='\0';
}

int main(int argc,char *argv[]) {
    FILE *fp;
    unsigned char buf[94];
    char idx[9],msg[81];
    int dy,dm,dd,th,tm,ts,bs,ms;

    if(argc!=2) {
        printf("usage: TALKTXT filename\n",argv[0]);
        return(1);
    }
    fp=fopen(argv[1],"rb");
    if(fp==NULL) {
        printf("'%s' is not open.\n",argv[1]);
        return(1);
    }
    if(strstr(argv[1],".IDX")!=NULL) {
        bs=94;
        ms=80;
    }
    else {
        bs=31;
        ms=16;
    }
    while(fread(buf,1,bs,fp)==bs) {
        strcpyn(idx,buf+1,buf[0],8);
        ts=(buf[9] & 0x1f)*2;
        tm=((buf[10] & 0x07) << 3)+((buf[9] & 0xe0) >> 5);
        th=(buf[10] & 0xf8) >> 3;
        dd=buf[11] & 0x1f;
        dm=((buf[12] & 0x01) << 3)+((buf[11] & 0xe0) >> 5);
        dy=1980+((buf[12] & 0xfe) >> 1);
        strcpyn(msg,buf+14,buf[13],ms);
        printf("%3d %-8s %02d/%02d/%02d %02d:%02d:%02d %3d ",buf[0],idx,dy,dm,dd,th,tm,ts,buf[13]);
        if(bs==94) {
            printf("%s\n",msg);
        }
        else {
            printf("%-16s %3d\n",msg,buf[30]);
        }
    }
    fclose(fp);
    return(0);
}