/*
 *  emul.c -- ファイラーのフリをする
 *
 *  revision history
 *      0.0: Jun.  8, 1996 by Dai ISHIJIMA (bsdfiler.c)
 *      0.1: Jul. 27, 1996
 *      0.2: Aug. 10, 1996
 *	0.3: Sep. 27, 1996
 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

#ifndef SEEK_SET	/* NEWS-OS, SunOS */
#include <unistd.h>
#endif

#include "bsdfiler.h"

#define FILEMODE 0777   /* ファイルのパーミッション */

static unsigned char sbuf[MAXPACKET];
static unsigned char rbuf[MAXPACKET];
static int rlen, slen;
static int finish;
static int cnt;
static int lastcnt;      /* ひとつまえのパケットのカウンタ */
static long lastfilpos;
static FILE *fp = NULL;
static char curdir[MAXLINE];
static char currentfile[MAXLINE];
static int depth = -1;
static char filnams[MAXDEPTH][MAXDIRENT * FILNAMSIZ];
static char *entries[MAXDEPTH][MAXDIRENT + 1];
static char **dirlst[MAXDEPTH];


/*
 * 接続と解除
 */
/* 接続 */
lxconnect()
{
    slen = clrparm(sbuf, slen, 2);
    strcpy(curdir, "./");
    depth = -1;
}


/* 接続解除 */
discon()
{
    slen = clrparm(sbuf, slen, 6);
    finish = YES;
}


/*
 * ドライブ・ディレクトリ操作
 */
/* カレントドライブ変更 */
logdisk()
{
    /* curdrv = rbuf[4]; */          /* UNIX版では何もしない */
    slen = clrparm(sbuf, slen, 6);   /* 常にドライブは C: */
    depth = -1;
}


/* 利用可能ドライブ通知 */
availdrv()
{
    slen = clrparm(sbuf, slen, 4);
    slen = setshort(sbuf, slen, 2);
    slen = setbyte(sbuf, slen, 'C'); /* 使えるドライブは C: のみ */
    slen = clrparm(sbuf, slen, 5);
}


/* カレントディレクトリ設定 */
changedir()
{
    int len;
    char path[MAXLINE];
    
    len = getmesg(rbuf, 4, path);
    slen = clrparm(sbuf, slen, 6);
    if (movdir(curdir, path) == NO) {
	sbuf[6] = CANTMOVEDIR;
    }
}


/* カレントディレクトリ通知 */
lxgetcwd()
{
    char *s;

    s = dospath(curdir);
    if ((s[strlen(s) - 1] == DOSPATHSEP) && (strlen(s) > 3)) {
	s[strlen(s) - 1] = EOS; /* ルートディレクトリ以外なら */
    }                           /* パスの最後の '\' をとる */
    slen = clrparm(sbuf, slen, 4);
    slen = setmesg(sbuf, slen, strlen(s), s);
    slen = clrparm(sbuf, slen, 4);
}


/* ディレクトリ作成 */
lxmkdir()
{
    int len;
    char path[MAXLINE];

    len = getmesg(rbuf, 4, path);
    mkdir(serverpath(curdir, path), FILEMODE);
    slen = clrparm(sbuf, slen, 6);
}


/*
 * ディレクトリリスト関連
 */
/* ディレクトリ検索ワイルドカード設定 */
setwild()
{
    int len;
    char wildcard[MAXLINE];

    ++depth;
    if (depth < 0) {
	depth = 0;
    }
    else if (depth >= MAXDEPTH) {
	depth = MAXDEPTH - 1;
    }
    len = getmesg(rbuf, 4, wildcard);
    slen = clrparm(sbuf, slen, 6);
    dirlst[depth] = searchdir(wildcard, entries[depth], filnams[depth]);
    /* そのワイルドカードを使って検索 */
}


/* 検索したディレクトリエントリを通知 */
getdirent()
{
    lxdirent *entry;
    
    slen = clrparm(sbuf, slen, 4);
    if (*dirlst[depth] != NULL) {
	entry = direntry(*dirlst[depth]);
	slen = setshort(sbuf, slen, 0x16);
	slen = setbyte(sbuf, slen, entry->type);
	slen = setlong(sbuf, slen, entry->timestamp);
	slen = setlong(sbuf, slen, entry->size);
	slen = setparm(sbuf, slen, FILNAMSIZ, entry->name);
	slen = clrparm(sbuf, slen, 4);
	sbuf[slen - 4] = 1; /* まだ続きがある */
    }
    else {
	/* すべてのエントリを通知したら... */
	--dirlst[depth];
	entry = direntry(*dirlst[depth]);
	slen = setshort(sbuf, slen, 0x16);
	slen = setbyte(sbuf, slen, entry->type);
	slen = setlong(sbuf, slen, entry->timestamp);
	slen = setlong(sbuf, slen, entry->size);
	slen = setparm(sbuf, slen, FILNAMSIZ, entry->name);
	slen = clrparm(sbuf, slen, 4);
    }
    ++dirlst[depth];
}


/* ディスク空き容量通知 */
lxdfree()
{
    slen = clrparm(sbuf, slen, 4);
    slen = setmesg(sbuf, slen, LABELSIZ, VOLUMELABEL);
    slen = setlong(sbuf, slen, diskfree());
}


/* ディレクトリかどうかを調べる */
getfiltype()
{
    int len;
    char path[MAXLINE];

    len = getmesg(rbuf, 4, path);
    slen = clrparm(sbuf, slen, 6);
    if (filetype(serverpath(curdir, path)) == ISDIR) {
	sbuf[slen - 2] = ISDIR;
    }
}


/*
 * ファイルの転送
 */
/* ファイルを開く (クライアント->サーバ) */
writeopen()
{
    int len;
    char path[MAXLINE];

    len = getmesg(rbuf, 4, path);
    if (fp != NULL) {
	fclose(fp);
    }
    strcpy(currentfile, serverpath(curdir, path));
    fp = efopen(currentfile, "w");
}


/* ファイル書き込み (クライアント->サーバ) */
lxfwrite()
{
    int len;
    char transbuf[DATBLKSIZ];

    len = getmesg(rbuf, 6, transbuf);
    fwrite(transbuf, 1, len, fp);
    slen = clrparm(sbuf, slen, 4);
    setshort(sbuf, slen, len);
}


/* ファイルのタイムスタンプ設定 */
setstamp()
{
    long stamp;
    
    stamp = getlong(rbuf, 6);
    slen = clrparm(sbuf, slen, 6);
    if (fp != NULL) {
	fclose(fp);
	fp = NULL;
        /* setmtime(currentfile, stamp); */ /* 準備中 :-) */
    }
}


/* ファイルを閉じる */
lxfclose()
{
    slen = clrparm(sbuf, slen, 6);
    if (fp != NULL) {
	fclose(fp);
	fp = NULL;
    }
}


/* ファイルを開く (サーバ->クライアント) */
readopen()
{
    int len;
    char path[MAXLINE];

    len = getmesg(rbuf, 4, path);
    if (fp != NULL) {
	fclose(fp);
    }
    fp = efopen(serverpath(curdir, path), "r");
    lastfilpos = 0;
    slen = clrparm(sbuf, slen, 6);
}


/* ファイル転送準備完了 (?) */
transrdy()
{
    slen = clrparm(sbuf, slen, 6);
    --depth;
    if (depth < 0) {
	depth = 0;
    }
}


/* ファイル読み込み (サーバ->クライアント) */
lxfread()
{
    int len;
    char transbuf[DATBLKSIZ];

    len = getshort(rbuf, 6);
    if (cnt == lastcnt) {	/* リトライ */
	fprintf(stderr, "retry sending packet (%02x)\n", cnt);
	fseek(fp, lastfilpos, SEEK_SET);
    }
    lastfilpos = ftell(fp);
    len = fread(transbuf, 1, len, fp);
    slen = clrparm(sbuf, slen, 4);
    slen = setmesg(sbuf, slen, len, transbuf);
}


/* ファイルのタイムスタンプ通知 */
lxgetstamp()
{
    slen = clrparm(sbuf, slen, 4);
    slen = setlong(sbuf, slen, tmstamp(currentfile));
}


/* 指定位置にシーク */
lxfseek()
{
    int whence;
    long offset;

    whence = getshort(rbuf, 6);
    offset = getlong(rbuf, 8);
    fseek(fp, offset, whence);
    slen = clrparm(sbuf, slen, 6);
}


/* ファイルポインタ通知 */
lxftell()
{
    slen = clrparm(sbuf, slen, 4);
    slen = setlong(sbuf, slen, ftell(fp));
}


/*
 * 各種ファイル操作
 */
/* ファイル名の変更 */
lxrename()
{
    int olen, nlen;
    char opath[MAXLINE], npath[MAXLINE];

    olen = getmesg(rbuf, 4, opath);
    nlen = getmesg(rbuf, 4 + olen + 2 + 2, npath);
    strcpy(opath, serverpath(curdir, opath));
    strcpy(npath, serverpath(curdir, npath));
    if (rename(opath, npath) == -1) {
	fprintf(stderr, "can't rename %s to %s\n", opath, npath);
    }
    slen = clrparm(sbuf, slen, 6);
}


/* ファイルの削除 */
lxdelfile()
{
    int olen;
    char opath[MAXLINE], npath[MAXLINE];

    olen = getmesg(rbuf, 4, opath);
    strcpy(opath, serverpath(curdir, opath));
    strcpy(npath, opath);
    strcat(npath, ".BAK");     /* 実はクライアントから見えない名前に変更 */
    if (rename(opath, npath) == -1) {
	fprintf(stderr, "can't rename %s to %s\n", opath, npath);
    }
    slen = clrparm(sbuf, slen, 6);
}


typedef int (*op)();

typedef struct {
    int code;
    op  action;
} commandlist;

/* コマンドと関数の対応表 */
static commandlist command[] = {
    CONNECT,	lxconnect,
    DISCON,	discon,
    LOGDSK,	logdisk,
    AVAILDRV,	availdrv,
    CHDIR,	changedir,
    GETCWD,	lxgetcwd,
    MKDIR,	lxmkdir,
    WILDCARD,	setwild,
    GETDIRENT,	getdirent,
    GETDFREE,	lxdfree,
    GETFTYPE,	getfiltype,
    WRITEOPEN,	writeopen,
    FWRITE,	lxfwrite,
    SETSTAMP,	setstamp,
    FCLOSE,	lxfclose,
    READOPEN,	readopen,
    TRANSRDY,	transrdy,
    FREAD,	lxfread,
    GETSTAMP,	lxgetstamp,
    FSEEK,	lxfseek,
    FTELL,	lxftell,
    RENAME,	lxrename,
    DELFILE,	lxdelfile,
    -1,		NULL,
};


/* サーバの "フリ" をする */
int emulserver(int fd)
{
    int i;
    int cmd;

    finish = NO;
    rlen = recvpacket(fd, rbuf);
    cmd = sbuf[0] = rbuf[0];
    cnt = sbuf[1] = rbuf[1];
    if (rlen < 0) {
	fprintf(stderr, "packet #%02x crc error\n", cnt);
	return(finish);
    }
    sbuf[2] = REPLYSOH;
    sbuf[3] = rbuf[3];
    slen = 4;
    for (i = 0; command[i].action != NULL; i++) {
	if (command[i].code == cmd) {
	    break;
	}
    }
    if (command[i].action != NULL) {
	(*command[i].action)();
	lastcnt = cnt;
	sendpacket(fd, slen, sbuf);
    }
    else {
	fprintf(stderr, "Unknown command: 0x%02x\n", cmd);
    }
    return(finish);
}
