From ae31e19f92e717919ac8e3db9039eb38d2b89aae Mon Sep 17 00:00:00 2001
From: in2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>
Date: Thu, 7 Mar 2002 15:13:44 +0000
Subject: Initial revision

git-svn-id: http://opensvn.csie.org/pttbbs/pttbbs/trunk/pttbbs@1 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
---
 mbbsd/.cvsignore  |    2 +
 mbbsd/Makefile    |   45 +
 mbbsd/admin.c     | 1105 ++++++++++++++++++++++
 mbbsd/announce.c  | 1590 ++++++++++++++++++++++++++++++++
 mbbsd/args.c      |   62 ++
 mbbsd/bbcall.c    |  268 ++++++
 mbbsd/bbs.c       | 1904 ++++++++++++++++++++++++++++++++++++++
 mbbsd/board.c     | 1098 ++++++++++++++++++++++
 mbbsd/cache.c     | 1053 +++++++++++++++++++++
 mbbsd/cal.c       |  512 ++++++++++
 mbbsd/calendar.c  |  284 ++++++
 mbbsd/card.c      |  625 +++++++++++++
 mbbsd/chat.c      |  623 +++++++++++++
 mbbsd/chc_draw.c  |  187 ++++
 mbbsd/chc_net.c   |   28 +
 mbbsd/chc_play.c  |  277 ++++++
 mbbsd/chc_rule.c  |  186 ++++
 mbbsd/chicken.c   |  989 ++++++++++++++++++++
 mbbsd/dark.c      |  456 +++++++++
 mbbsd/descrypt.c  |  616 +++++++++++++
 mbbsd/dice.c      |  447 +++++++++
 mbbsd/edit.c      | 2256 +++++++++++++++++++++++++++++++++++++++++++++
 mbbsd/friend.c    |  509 ++++++++++
 mbbsd/gamble.c    |  361 ++++++++
 mbbsd/gomo.c      |  417 +++++++++
 mbbsd/gomo1.c     |  136 +++
 mbbsd/guess.c     |  364 ++++++++
 mbbsd/indict.c    |  184 ++++
 mbbsd/io.c        |  611 ++++++++++++
 mbbsd/kaede.c     |   95 ++
 mbbsd/lovepaper.c |  120 +++
 mbbsd/mail.c      | 1675 +++++++++++++++++++++++++++++++++
 mbbsd/mbbsd.c     | 1465 +++++++++++++++++++++++++++++
 mbbsd/menu.c      |  596 ++++++++++++
 mbbsd/more.c      |  931 +++++++++++++++++++
 mbbsd/name.c      |  473 ++++++++++
 mbbsd/osdep.c     |   79 ++
 mbbsd/othello.c   |  541 +++++++++++
 mbbsd/page.c      |  130 +++
 mbbsd/passwd.c    |  138 +++
 mbbsd/read.c      |  998 ++++++++++++++++++++
 mbbsd/record.c    |  536 +++++++++++
 mbbsd/register.c  |  339 +++++++
 mbbsd/screen.c    |  559 +++++++++++
 mbbsd/stuff.c     |  524 +++++++++++
 mbbsd/syspost.c   |  102 ++
 mbbsd/talk.c      | 2663 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 mbbsd/term.c      |  144 +++
 mbbsd/toolkit.c   |   14 +
 mbbsd/topsong.c   |   79 ++
 mbbsd/uptime      |   23 +
 mbbsd/user.c      |  980 ++++++++++++++++++++
 mbbsd/var.c       |  268 ++++++
 mbbsd/vice.c      |  151 +++
 mbbsd/vote.c      | 1068 +++++++++++++++++++++
 mbbsd/voteboard.c |  447 +++++++++
 mbbsd/xyz.c       |  448 +++++++++
 57 files changed, 32781 insertions(+)
 create mode 100644 mbbsd/.cvsignore
 create mode 100644 mbbsd/Makefile
 create mode 100644 mbbsd/admin.c
 create mode 100644 mbbsd/announce.c
 create mode 100644 mbbsd/args.c
 create mode 100644 mbbsd/bbcall.c
 create mode 100644 mbbsd/bbs.c
 create mode 100644 mbbsd/board.c
 create mode 100644 mbbsd/cache.c
 create mode 100644 mbbsd/cal.c
 create mode 100644 mbbsd/calendar.c
 create mode 100644 mbbsd/card.c
 create mode 100644 mbbsd/chat.c
 create mode 100644 mbbsd/chc_draw.c
 create mode 100644 mbbsd/chc_net.c
 create mode 100644 mbbsd/chc_play.c
 create mode 100644 mbbsd/chc_rule.c
 create mode 100644 mbbsd/chicken.c
 create mode 100644 mbbsd/dark.c
 create mode 100644 mbbsd/descrypt.c
 create mode 100644 mbbsd/dice.c
 create mode 100644 mbbsd/edit.c
 create mode 100644 mbbsd/friend.c
 create mode 100644 mbbsd/gamble.c
 create mode 100644 mbbsd/gomo.c
 create mode 100644 mbbsd/gomo1.c
 create mode 100644 mbbsd/guess.c
 create mode 100644 mbbsd/indict.c
 create mode 100644 mbbsd/io.c
 create mode 100644 mbbsd/kaede.c
 create mode 100644 mbbsd/lovepaper.c
 create mode 100644 mbbsd/mail.c
 create mode 100644 mbbsd/mbbsd.c
 create mode 100644 mbbsd/menu.c
 create mode 100644 mbbsd/more.c
 create mode 100644 mbbsd/name.c
 create mode 100644 mbbsd/osdep.c
 create mode 100644 mbbsd/othello.c
 create mode 100644 mbbsd/page.c
 create mode 100644 mbbsd/passwd.c
 create mode 100644 mbbsd/read.c
 create mode 100644 mbbsd/record.c
 create mode 100644 mbbsd/register.c
 create mode 100644 mbbsd/screen.c
 create mode 100644 mbbsd/stuff.c
 create mode 100644 mbbsd/syspost.c
 create mode 100644 mbbsd/talk.c
 create mode 100644 mbbsd/term.c
 create mode 100644 mbbsd/toolkit.c
 create mode 100644 mbbsd/topsong.c
 create mode 100644 mbbsd/uptime
 create mode 100644 mbbsd/user.c
 create mode 100644 mbbsd/var.c
 create mode 100644 mbbsd/vice.c
 create mode 100644 mbbsd/vote.c
 create mode 100644 mbbsd/voteboard.c
 create mode 100644 mbbsd/xyz.c

(limited to 'mbbsd')

diff --git a/mbbsd/.cvsignore b/mbbsd/.cvsignore
new file mode 100644
index 00000000..35f9f2d1
--- /dev/null
+++ b/mbbsd/.cvsignore
@@ -0,0 +1,2 @@
+*.o
+mbbsd
diff --git a/mbbsd/Makefile b/mbbsd/Makefile
new file mode 100644
index 00000000..886944f3
--- /dev/null
+++ b/mbbsd/Makefile
@@ -0,0 +1,45 @@
+# $Id: Makefile,v 1.1 2002/03/07 15:13:48 in2 Exp $
+
+BBSHOME?=$(HOME)
+OSTYPE=FreeBSD
+
+# FreeBSD
+CFLAGS_FreeBSD=	-pipe -Wall -g -O3 -DHAVE_SETPROCTITLE -DBBSHOME='"$(BBSHOME)"' -DFreeBSD -I../include
+LDFLAGS_FreeBSD=-pipe -Wall -g -O3
+LIBS_FreeBSD=	-lutil -lkvm
+
+# Linux
+CFLAGS_linux=	-pipe -Wall -g -O3 -DHAVE_DES_CRYPT -DBBSHOME='"$(BBSHOME)"' -DLinux -I../include -s
+LDFLAGS_linux=	-pipe -Wall -g -O3
+LIBS_linux=	-lcrypt
+
+CFLAGS=	$(CFLAGS_$(OSTYPE))
+LDFLAGS=$(LDFLAGS_$(OSTYPE))
+LIBS=	$(LIBS_$(OSTYPE))
+
+CC=	gcc
+PROG=	mbbsd
+OBJS=	admin.o announce.o args.o bbcall.o bbs.o board.o cache.o cal.o card.o\
+	chat.o chc_draw.o chc_net.o chc_play.o chc_rule.o chicken.o dark.o\
+	dice.o edit.o friend.o gamble.o gomo.o gomo1.o guess.o indict.o io.o\
+	kaede.o lovepaper.o mail.o mbbsd.o menu.o more.o name.o osdep.o\
+	othello.o page.o read.o record.o register.o screen.o stuff.o\
+	talk.o term.o topsong.o user.o vice.o vote.o xyz.o\
+	voteboard.o syspost.o var.o descrypt.o toolkit.o passwd.o\
+	calendar.o
+
+.SUFFIXES: .c .o
+.c.o:
+	$(CC) $(CFLAGS) -c $*.c
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install: $(PROG)
+	install -d $(BBSHOME)/bin/
+	install -c -m 755 $(PROG) $(BBSHOME)/bin/
+
+clean:
+	rm -f $(OBJS) $(PROG)
diff --git a/mbbsd/admin.c b/mbbsd/admin.c
new file mode 100644
index 00000000..14bc9721
--- /dev/null
+++ b/mbbsd/admin.c
@@ -0,0 +1,1105 @@
+/* $Id: admin.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *msg_uid;
+extern userec_t xuser;
+extern char *err_uid;
+extern boardheader_t *bcache;
+
+/* �ϥΪ̺޲z */
+int m_user() {
+    userec_t muser;
+    int id;
+    char genbuf[200];
+    
+    stand_title("�ϥΪ̳]�w");
+    usercomplete(msg_uid, genbuf);
+    if(*genbuf) {
+	move(2, 0);
+	if((id = getuser(genbuf))) {
+	    memcpy(&muser, &xuser, sizeof(muser));
+	    user_display(&muser, 1);
+	    uinfo_query(&muser, 1, id);
+	} else {
+	    outs(err_uid);
+	    clrtoeol();
+	    pressanykey();
+	}
+    }
+    return 0;
+}
+
+extern int b_lines;
+
+static int search_key_user(char *passwdfile, int mode) {
+    userec_t user;
+    int ch;
+    int coun = 0;
+    FILE *fp1 = fopen(passwdfile, "r");
+    char buf[128], key[22], genbuf[8];
+    
+    clear();
+    getdata(0, 0, mode ? "�п�J�ϥΪ�����r[�q��|�a�}|�m�W|�W���a�I|"
+	    "email|�p��id] :" : "�п�Jid :", key, 21, DOECHO);
+    while((fread(&user, sizeof(user), 1, fp1)) > 0 && coun < MAX_USERS) {
+	if(!(++coun & 15)) {
+	    move(1, 0);
+	    sprintf(buf, "�� [%d] �����\n", coun);
+	    outs(buf);
+	    refresh();
+	}
+	if(!strcasecmp(user.userid, key) ||
+	   (mode && (
+	       strstr(user.realname, key) || strstr(user.username, key) ||
+	       strstr(user.lasthost, key) || strstr(user.email, key) ||
+	       strstr(user.address, key) || strstr(user.justify, key) ||
+	       strstr(user.mychicken.name, key)))) {
+	    move(1, 0);
+	    sprintf(buf, "�� [%d] �����\n", coun);
+	    outs(buf);
+	    refresh();
+	    
+	    user_display(&user, 1);
+	    uinfo_query(&user, 1, coun);
+	    outs("\033[44m               �ť���\033[37m:�j�M�U�@��"
+		 "          \033[33m Q\033[37m: ���}");
+	    outs(mode ? "                        \033[m " :
+		 "      S: ���γƥ����    \033[m ");
+	    while(1) {
+	    	while((ch = igetch()) == 0);
+	    	if(ch == ' ')
+	    	    break;
+		if(ch == 'q' || ch == 'Q')
+		    return 0;
+		if(ch == 's' && !mode) {
+		    if((ch = searchuser(user.userid))) {
+                        setumoney(ch,user.money);
+			passwd_update(ch, &user);
+			return 0;
+		    } else {
+			move(b_lines - 1, 0);
+			genbuf[0] = 'n';
+			getdata(0, 0,
+				"�ثe�� PASSWD �ɨS���� ID�A�s�W�ܡH[y/N]",
+				genbuf, 3, LCECHO);
+			if(genbuf[0] == 'n') {
+			    outs("�ثe��PASSWDS�ɨS����id "
+				 "�Х�new�@�ӳo��id���b��");
+			} else {
+			    int allocid = getnewuserid();
+			    
+			    if(allocid > MAX_USERS || allocid <= 0) {
+				fprintf(stderr, "�����H�f�w�F���M�I\n");
+				exit(1);
+			    }
+			    
+			    if(passwd_update(allocid, &user) == -1) {
+				fprintf(stderr, "�Ⱥ��F�A�A���I\n");
+				exit(1);
+			    }
+			    setuserid(allocid, user.userid);
+			    if(!searchuser(user.userid)) {
+				fprintf(stderr, "�L�k�إ߱b��\n");
+				exit(1);
+			    }
+			    return 0;
+			}
+		    }
+		}
+	    }
+	}
+    }
+    
+    fclose(fp1);
+    return 0;
+}
+
+/* �H���N key �M��ϥΪ� */
+int search_user_bypwd() {
+    search_key_user(FN_PASSWD, 1);
+    return 0;
+}
+
+/* �M��ƥ����ϥΪ̸�� */
+int search_user_bybakpwd() {
+    char *choice[] = {
+	"PASSWDS.NEW1", "PASSWDS.NEW2", "PASSWDS.NEW3",
+	"PASSWDS.NEW4", "PASSWDS.NEW5", "PASSWDS.NEW6",
+	"PASSWDS.BAK"
+    };
+    int ch;
+    
+    clear();
+    move(1, 1);
+    outs("�п�J�A�n�ΨӴM��ƥ����ɮ� �Ϋ� 'q' ���}\n");
+    outs(" [\033[1;31m1\033[m]�@�ѫe, [\033[1;31m2\033[m]��ѫe, "
+	 "[\033[1;31m3\033[m]�T�ѫe\n");
+    outs(" [\033[1;31m4\033[m]�|�ѫe, [\033[1;31m5\033[m]���ѫe, "
+	 "[\033[1;31m6\033[m]���ѫe\n");
+    outs(" [7]�ƥ���\n");
+    do {
+	move(5, 1);
+	outs("��� => ");
+	ch = igetch();
+	if(ch == 'q' || ch == 'Q')
+	    return 0;
+    } while (ch < '1' || ch > '8');
+    ch -= '1';
+    search_key_user(choice[ch], 0);
+    return 0;
+}
+
+static void bperm_msg(boardheader_t *board) {
+    prints("\n�]�w [%s] �ݪO��(%s)�v���G", board->brdname,
+	   board->brdattr & BRD_POSTMASK ? "�o��" : "�\\Ū");
+}
+
+extern char* str_permboard[];
+
+unsigned int setperms(unsigned int pbits, char *pstring[]) {
+    register int i;
+    char choice[4];
+    
+    move(4, 0);
+    for(i = 0; i < NUMPERMS / 2; i++) {
+	prints("%c. %-20s %-15s %c. %-20s %s\n",
+	       'A' + i, pstring[i],
+	       ((pbits >> i) & 1 ? "��" : "��"),
+	       i < 10 ? 'Q' + i : '0' + i - 10,
+	       pstring[i + 16],
+	       ((pbits >> (i + 16)) & 1 ? "��" : "��"));
+    }
+    clrtobot();
+    while(getdata(b_lines - 1, 0, "�� [A-5] �����]�w�A�� [Return] �����G",
+		  choice, 3, LCECHO)) {
+	i = choice[0] - 'a';
+	if(i < 0)
+	    i = choice[0] - '0' + 26;
+	if(i >= NUMPERMS)
+	    bell();
+	else {
+	    pbits ^= (1 << i);
+	    move(i % 16 + 4, i <= 15 ? 24 : 64);
+	    prints((pbits >> i) & 1 ? "��" : "��");
+	}
+    }
+    return pbits;
+}
+
+/* �۰ʳ]�ߺ�ذ� */
+void setup_man(boardheader_t * board) {
+    char genbuf[200];
+    
+    setapath(genbuf, board->brdname);
+    mkdir(genbuf, 0755);
+}
+
+extern char *fn_board;
+extern char *err_bid;
+extern userec_t cuser;
+extern char *msg_sure_ny;
+extern char* str_permid[];
+
+int m_mod_board(char *bname) {
+    boardheader_t bh, newbh;
+    int bid;
+    char genbuf[256], ans[4];
+    
+    bid = getbnum(bname);
+    if(!bid || !bname[0] || get_record(fn_board, &bh, sizeof(bh), bid) == -1) {
+	outs(err_bid);
+	pressanykey();
+	return -1;
+    }
+    
+    prints("�ݪO�W�١G%s\n�ݪO�����G%s\n�ݪObid�G%d\n�ݪOGID�G%d\n"
+	   "�O�D�W��G%s", bh.brdname, bh.title, bid, bh.gid, bh.BM);
+    bperm_msg(&bh);
+    
+    /* Ptt �o���_��|�ɨ�U�� */
+    move(9, 0);
+    sprintf(genbuf, "�ݪO (E)�]�w (V)�H�k/�Ѱ� %s (D)�R�� [Q]�����H",
+	    HAS_PERM(PERM_SYSOP) ?
+	    " (B)BVote (S)�Ϧ^�峹" : "");
+    getdata(10, 0, genbuf, ans, 3, LCECHO);
+    
+    switch(*ans) {
+    case 's':
+	if(HAS_PERM(PERM_SYSOP) ) {
+	    char actionbuf[512];
+	    
+	    sprintf(actionbuf, BBSHOME "/bin/buildir boards/%s &",
+		    bh.brdname);
+	    system(actionbuf);
+	}
+	break;
+    case 'b':
+	if(HAS_PERM(PERM_SYSOP)) {
+	    char bvotebuf[10];
+	    
+	    memcpy(&newbh, &bh, sizeof(bh));
+	    sprintf(bvotebuf, "%d", newbh.bvote);
+	    move(20, 0);
+	    prints("�ݪ� %s ��Ӫ� BVote�G%d", bh.brdname, bh.bvote);
+	    getdata_str(21, 0, "�s�� Bvote�G", genbuf, 5, LCECHO, bvotebuf);
+	    newbh.bvote = atoi(genbuf);
+	    substitute_record(fn_board, &newbh, sizeof(newbh), bid);
+	    reset_board(bid);
+	    log_usies("SetBoardBvote", newbh.brdname);
+	    break;
+	} else
+	    break;
+    case 'v':
+	memcpy(&newbh, &bh, sizeof(bh));
+	outs("�ݪ��ثe��");
+	outs((bh.brdattr & BRD_BAD) ? "�H�k" : "���`");
+	getdata(21, 0, "�T�w���H", genbuf, 5, LCECHO);
+	if(genbuf[0] == 'y') {
+	    if(newbh.brdattr & BRD_BAD)
+		newbh.brdattr = newbh.brdattr & (!BRD_BAD);
+	    else
+		newbh.brdattr = newbh.brdattr | BRD_BAD;
+	    substitute_record(fn_board, &newbh, sizeof(newbh), bid);
+	    reset_board(bid);
+	    log_usies("ViolateLawSet", newbh.brdname);
+	}
+	break;
+    case 'd':
+	    getdata_str(9, 0, msg_sure_ny, genbuf, 3, LCECHO, "N");
+	    if(genbuf[0] != 'y' || !bname[0])
+		outs(MSG_DEL_CANCEL);
+	    else {
+		strcpy(bname, bh.brdname);
+		sprintf(genbuf,
+			"/bin/tar zcvf tmp/board_%s.tgz boards/%s man/%s >/dev/null 2>&1;"
+			"/bin/rm -fr boards/%s man/%s", 
+			bname, bname, bname, bname, bname);
+		system(genbuf);
+		memset(&bh, 0, sizeof(bh));
+		sprintf(bh.title, "%s �ݪO %s �R��", bname, cuser.userid);
+		post_msg("Security", bh.title,"�Ъ`�N�R�����X�k��","[�t�Φw����]");
+		substitute_record(fn_board, &bh, sizeof(bh), bid);
+		reset_board(bid);
+		log_usies("DelBoard", bh.title);
+		outs("�R�O����");
+	    }
+	break;
+    case 'e':
+	move(8, 0);
+	outs("������ [Return] ���ק�Ӷ��]�w");
+	memcpy(&newbh, &bh, sizeof(bh));
+
+	while(getdata(9, 0, "�s�ݪO�W�١G", genbuf, IDLEN + 1, DOECHO)) {
+	    if(getbnum(genbuf)) {
+		move(3, 0);
+		outs("���~! �O�W�p�P");
+	    } else if(!invalid_brdname(genbuf)) {
+		strcpy(newbh.brdname, genbuf);
+		break;
+	    }
+	}
+	
+	do {
+	    getdata_str(12, 0, "�ݪO���O�G", genbuf, 5, DOECHO, bh.title);
+	    if(strlen(genbuf) == 4)
+		break;
+	} while (1);
+
+	if(strlen(genbuf) >= 4)
+	    strncpy(newbh.title, genbuf, 4);
+	
+	newbh.title[4] = ' ';
+
+	getdata_str(14, 0, "�ݪO�D�D�G", genbuf, BTLEN + 1, DOECHO, 
+		    bh.title + 7);
+	if(genbuf[0])
+	    strcpy(newbh.title + 7, genbuf);
+	if(getdata_str(15, 0, "�s�O�D�W��G", genbuf, IDLEN * 3 + 3, DOECHO,
+		       bh.BM)) {
+	    trim(genbuf);
+	    strcpy(newbh.BM, genbuf);
+	}
+	
+	if(HAS_PERM(PERM_SYSOP)) {
+	    move(1, 0);
+	    clrtobot();
+	    newbh.brdattr = setperms(newbh.brdattr, str_permboard);
+	    move(1, 0);
+	    clrtobot();
+	}
+	
+	if(newbh.brdattr & BRD_GROUPBOARD)
+	    strncpy(newbh.title + 5, "�U", 2);
+	else if(newbh.brdattr & BRD_NOTRAN)
+	    strncpy(newbh.title + 5, "��", 2);
+	else
+	    strncpy(newbh.title + 5, "��", 2);
+	
+	if(HAS_PERM(PERM_SYSOP) && !(newbh.brdattr & BRD_HIDE)) {
+	    getdata_str(14, 0, "�]�wŪ�g�v��(Y/N)�H", ans, 4, LCECHO, "N");
+	    if(*ans == 'y') {
+		getdata_str(15, 0, "���� [R]�\\Ū (P)�o���H", ans, 4, LCECHO,
+			    "R");
+		if(*ans == 'p')
+		    newbh.brdattr |= BRD_POSTMASK;
+		else
+		    newbh.brdattr &= ~BRD_POSTMASK;
+		
+		move(1, 0);
+		clrtobot();
+		bperm_msg(&newbh);
+		newbh.level = setperms(newbh.level, str_permid);
+		clear();
+	    }
+	}
+	
+	getdata_str(b_lines - 1, 0, msg_sure_ny, genbuf, 4, LCECHO, "Y");
+
+	if((*genbuf == 'y') && memcmp(&newbh, &bh, sizeof(bh))) {
+	    if(strcmp(bh.brdname, newbh.brdname)) {
+		char src[60], tar[60];
+		
+		setbpath(src, bh.brdname);
+		setbpath(tar, newbh.brdname);
+		Rename(src, tar);
+		
+		setapath(src, bh.brdname);
+		setapath(tar, newbh.brdname);
+		Rename(src, tar);
+	    }
+	    setup_man(&newbh);
+	    substitute_record(fn_board, &newbh, sizeof(newbh), bid);
+	    reset_board(bid);
+	    log_usies("SetBoard", newbh.brdname);
+	}
+    }
+    return 0;
+}
+
+extern char *msg_bid;
+
+/* �]�w�ݪ� */
+int m_board() {
+    char bname[32];
+    
+    stand_title("�ݪO�]�w");
+    make_blist();
+    namecomplete(msg_bid, bname);
+    if(!*bname)
+	return 0;
+    m_mod_board(bname);
+    return 0;
+}
+
+/* �]�w�t���ɮ� */
+int x_file() {
+    int aborted;
+    char ans[4], *fpath;
+    
+    move(b_lines - 4, 0);
+    /* Ptt */
+    outs("�]�w (1)�����T�{�H (4)post�`�N�ƶ� (5)���~�n�J�T�� (6)���U�d�� (7)�q�L�T�{�q��\n");
+    outs("     (8)email post�q�� (9)�t�Υ\\����F (A)���� (B)�����W�� (C)email�q�L�T�{\n");
+    outs("     (D)�s�ϥΪ̻ݪ� (E)�����T�{��k (F)�w��e�� (G)�i���e�� "
+#ifdef MULTI_WELCOME_LOGIN
+"(X)�R���i���e��"
+#endif
+"\n");
+    getdata(b_lines - 1, 0, "      (H)�ݪO���� (I)�G�m (J)�X���e�� (K)�ͤ�d (L)�`�� [Q]�����H", ans, 3, LCECHO);
+    
+    switch(ans[0]) {
+    case '1':
+	fpath = "etc/confirm";
+	break;
+    case '4':
+	fpath = "etc/post.note";
+	break;
+    case '5':
+	fpath = "etc/goodbye";
+	break;
+    case '6':
+	fpath = "etc/register";
+	break;
+    case '7':
+	fpath = "etc/registered";
+	break;
+    case '8':
+	fpath = "etc/emailpost";
+	break;
+    case '9':
+	fpath = "etc/hint";
+	break;
+    case 'a':
+	fpath = "etc/teashop";
+	break;
+    case 'b':
+	fpath = "etc/sysop";
+	break;
+    case 'c':
+	fpath = "etc/bademail";
+	break;
+    case 'd':
+	fpath = "etc/newuser";
+	break;
+    case 'e':
+	fpath = "etc/justify";
+	break;
+    case 'f':
+	fpath = "etc/Welcome";
+	break;
+    case 'g':
+#ifdef MULTI_WELCOME_LOGIN
+	getdata(b_lines - 1, 0, "�ĴX�Ӷi���e��[0-4]", ans, 3, LCECHO);
+	if( ans[0] == '1' )     { fpath = "etc/Welcome_login.1"; }
+	else if( ans[0] == '2' ){ fpath = "etc/Welcome_login.2"; }
+	else if( ans[0] == '3' ){ fpath = "etc/Welcome_login.3"; }
+	else if( ans[0] == '4' ){ fpath = "etc/Welcome_login.4"; }
+	else                    { fpath = "etc/Welcome_login.0"; }
+#else
+	fpath = "etc/Welcome_login";
+#endif
+	break;
+
+#ifdef MULTI_WELCOME_LOGIN
+    case 'x':
+	getdata(b_lines - 1, 0, "�ĴX�Ӷi���e��[1-4]", ans, 3, LCECHO);
+	if( ans[0] == '1' )     { unlink("etc/Welcome_login.1"); outs("ok"); }
+	else if( ans[0] == '2' ){ unlink("etc/Welcome_login.2"); outs("ok"); }
+	else if( ans[0] == '3' ){ unlink("etc/Welcome_login.3"); outs("ok"); }
+	else if( ans[0] == '4' ){ unlink("etc/Welcome_login.4"); outs("ok"); }
+	else {outs("�ҫ��w���i���e���L�k�R��"); }
+	pressanykey();
+	return FULLUPDATE;
+
+#endif
+
+    case 'h':
+	fpath = "etc/expire.conf";
+	break;
+    case 'i':
+	fpath = "etc/domain_name_query";
+	break;
+    case 'j':
+	fpath = "etc/Logout";
+	break;
+    case 'k':
+	fpath = "etc/Welcome_birth";
+	break;
+    case 'l':
+	fpath = "etc/feast";
+	break;
+    default:
+	return FULLUPDATE;
+    }
+    aborted = vedit(fpath, NA, NULL);
+    prints("\n\n�t���ɮ�[%s]�G%s", fpath,
+	   (aborted == -1) ? "������" : "��s����");
+    pressanykey();
+    return FULLUPDATE;
+}
+
+extern int numboards;
+extern int class_bid;
+
+int m_newbrd(int recover) {
+    boardheader_t newboard;
+    char ans[4];
+    int bid;
+    char genbuf[200];
+
+    stand_title("�إ߷s�O");
+    memset(&newboard, 0, sizeof(newboard));
+    
+    newboard.gid = class_bid;
+    if (newboard.gid == 0) {
+	move(6, 0);
+	outs("�Х���ܤ@�����O�A�}�O!");
+	pressanykey();
+	return -1;
+    }
+    
+    do {
+	if(!getdata(3, 0, msg_bid, newboard.brdname, IDLEN + 1, DOECHO))
+	    return -1;
+    } while(invalid_brdname(newboard.brdname));
+
+    do {
+	getdata(6, 0, "�ݪO���O�G", genbuf, 5, DOECHO);
+	if(strlen(genbuf) == 4)
+	    break;
+    } while (1);
+    
+    if(strlen(genbuf) >= 4)
+	strncpy(newboard.title, genbuf, 4);
+    
+    newboard.title[4] = ' ';
+    
+    getdata(8, 0, "�ݪO�D�D�G", genbuf, BTLEN + 1, DOECHO);
+    if(genbuf[0])
+	strcpy(newboard.title + 7, genbuf);
+    setbpath(genbuf, newboard.brdname);
+    
+    if(recover) {
+	struct stat sb;
+	
+	if(stat(genbuf, &sb) == -1 || !(sb.st_mode & S_IFDIR)) {
+	    outs("���ݪO�w�g�s�b! �Ш����P�^��O�W");
+	    pressanykey();
+	    return -1;
+	}
+    } else if(getbnum(newboard.brdname) > 0 || mkdir(genbuf, 0755) == -1) {
+	outs("���ݪO�w�g�s�b! �Ш����P�^��O�W");
+	pressanykey();
+	return -1;
+    }
+    
+    newboard.brdattr = BRD_NOTRAN;
+    
+    if(HAS_PERM(PERM_SYSOP)) {
+	move(1, 0);
+	clrtobot();
+	newboard.brdattr = setperms(newboard.brdattr, str_permboard);
+	move(1, 0);
+	clrtobot();
+    }
+    getdata(9, 0, "�O�ݪO? (N:�ؿ�) (Y/n)�G", genbuf, 3, LCECHO);
+    if(genbuf[0]=='n')
+	newboard.brdattr |= BRD_GROUPBOARD;
+
+    if(newboard.brdattr & BRD_GROUPBOARD)
+	strncpy(newboard.title + 5, "�U", 2);
+    else if(newboard.brdattr & BRD_NOTRAN)
+	strncpy(newboard.title + 5, "��", 2);
+    else
+	strncpy(newboard.title + 5, "��", 2);
+    
+    newboard.level = 0;
+    getdata(11, 0, "�O�D�W��G", newboard.BM, IDLEN * 3 + 3, DOECHO);
+
+    if(HAS_PERM(PERM_SYSOP) && !(newboard.brdattr & BRD_HIDE)) {
+	getdata_str(14, 0, "�]�wŪ�g�v��(Y/N)�H", ans, 3, LCECHO, "N");
+	if(*ans == 'y') {
+	    getdata_str(15, 0, "���� [R]�\\Ū (P)�o���H", ans, 4, LCECHO, "R");
+	    if(*ans == 'p')
+		newboard.brdattr |= BRD_POSTMASK;
+	    else
+		newboard.brdattr &= (~BRD_POSTMASK);
+	    
+	    move(1, 0);
+	    clrtobot();
+	    bperm_msg(&newboard);
+	    newboard.level = setperms(newboard.level, str_permid);
+	    clear();
+	}
+    }
+    
+    if((bid = getbnum("")) > 0)
+     {
+	substitute_record(fn_board, &newboard, sizeof(newboard), bid);
+        reset_board(bid);
+     }
+    else if(append_record(fn_board, (fileheader_t *) & newboard,
+			  sizeof(newboard)) == -1) {
+	pressanykey();
+	return -1;
+    }
+    else
+    {
+      addbrd_touchcache();
+    }
+    setup_man(&newboard);
+
+    outs("\n�s�O����");
+    post_newboard(newboard.title, newboard.brdname, newboard.BM);
+    log_usies("NewBoard", newboard.title);
+    pressanykey();
+    return 0;
+}
+
+static int auto_scan(char fdata[][STRLEN], char ans[]) {
+    int good = 0;
+    int count = 0;
+    int i;
+    char temp[10];
+    
+    if(!strncmp(fdata[2], "�p", 2) || strstr(fdata[2], "�X")
+	|| strstr(fdata[2], "��") || strstr(fdata[2], "��")) {
+	ans[0] = '0';
+	return 1;
+    }
+    
+    strncpy(temp, fdata[2], 2);
+    temp[2] = '\0';
+    
+    /* �|�r */
+    if(!strncmp(temp, &(fdata[2][2]), 2)) {
+	ans[0] = '0';
+	return 1;
+    }
+
+    if(strlen(fdata[2]) >= 6) {
+	if(strstr(fdata[2], "������")) {
+	    ans[0] = '0';
+	    return 1;
+	}
+	
+	if(strstr("�����]���P�d�G��", temp))
+	    good++;
+	else if(strstr("���C���L���x�E���B", temp))
+	    good++;
+	else if(strstr("Ĭ��d�f����i����Ĭ", temp))
+	    good++;
+	else if(strstr("�}�¥ۿc�I���έ�", temp))
+	    good++;
+    }
+    
+    if(!good)
+	return 0;
+
+    if(!strcmp(fdata[3], fdata[4]) ||
+       !strcmp(fdata[3], fdata[5]) ||
+       !strcmp(fdata[4], fdata[5])) {
+	ans[0] = '4';
+	return 5;
+    }
+    
+    if(strstr(fdata[3], "�j")) {
+	if (strstr(fdata[3], "�x") || strstr(fdata[3], "�H") ||
+	    strstr(fdata[3], "��") || strstr(fdata[3], "�F") ||
+	    strstr(fdata[3], "�M") || strstr(fdata[3], "ĵ") ||
+	    strstr(fdata[3], "�v") || strstr(fdata[3], "�ʶ�") ||
+	    strstr(fdata[3], "����") || strstr(fdata[3], "��") ||
+	    strstr(fdata[3], "��") || strstr(fdata[3], "�F�d"))
+	    good++;
+    } else if(strstr(fdata[3], "�k��"))
+	good++;
+    
+    if(strstr(fdata[4], "�a�y") || strstr(fdata[4], "�t�z") ||
+       strstr(fdata[4], "�H�c")) {
+	ans[0] = '2';
+	return 3;
+    }
+    
+    if(strstr(fdata[4], "��") || strstr(fdata[4], "��")) {
+	if(strstr(fdata[4], "��") || strstr(fdata[4], "��")) {
+	    if(strstr(fdata[4], "��"))
+		good++;
+	}
+    }
+    
+    for(i = 0; fdata[5][i]; i++) {
+	if(isdigit(fdata[5][i]))
+	    count++;
+    }
+    
+    if(count <= 4) {
+	ans[0] = '3';
+	return 4;
+    } else if(count >= 7)
+	good++;
+
+    if(good >= 3) {
+	ans[0] = 'y';
+	return -1;
+    } else
+	return 0;
+}
+
+/* �B�z Register Form */
+int scan_register_form(char *regfile, int automode, int neednum) {
+    char genbuf[200];
+    static char *logfile = "register.log";
+    static char *field[] = {
+	"uid", "ident", "name", "career", "addr", "phone", "email", NULL
+    };
+    static char *finfo[] = {
+	"�b��", "�����Ҹ�", "�u��m�W", "�A�ȳ��", "�ثe���}",
+	"�s���q��", "�q�l�l��H�c", NULL
+    };
+    static char *reason[] = {
+	"��J�u��m�W", "�Զ�Ǯլ�t�P�~��", "��g���㪺���}���",
+	"�Զ�s���q��", "�T���g���U�ӽЪ�", "�Τ����g�ӽг�", NULL
+    };
+    static char *autoid = "AutoScan";
+    userec_t muser;
+    FILE *fn, *fout, *freg;
+    char fdata[7][STRLEN];
+    char fname[STRLEN], buf[STRLEN];
+    char ans[4], *ptr, *uid;
+    int n, unum;
+    int nSelf = 0, nAuto = 0;
+    
+    uid = cuser.userid;
+    sprintf(fname, "%s.tmp", regfile);
+    move(2, 0);
+    if(dashf(fname)) {
+	if(neednum == 0) { /* �ۤv�i Admin �Ӽf�� */
+	    outs("��L SYSOP �]�b�f�ֵ��U�ӽг�");
+	    pressanykey();
+	}
+	return -1;
+    }
+    Rename(regfile, fname);
+    if((fn = fopen(fname, "r")) == NULL) {
+	prints("�t�ο��~�A�L�kŪ�����U�����: %s", fname);
+	pressanykey();
+	return -1;
+    }
+    if(neednum) { /* �Q�j���f�� */
+	move(1, 0);
+	clrtobot();
+	prints("�U��㦳�����v�����H�A���U��ֿn�W�L�@�ʥ��F�A�·бz�����f %d ��\n", neednum);
+	prints("�]�N�O�j���G�Q�����@���ƶq�A���M�A�z�]�i�H�h�f\n�S�f�����e�A�t�Τ��|���A���X��I����");
+	pressanykey();
+    }
+    memset(fdata, 0, sizeof(fdata));
+    while(fgets(genbuf, STRLEN, fn)) {
+	if ((ptr = (char *) strstr(genbuf, ": "))) {
+	    *ptr = '\0';
+	    for(n = 0; field[n]; n++) {
+		if(strcmp(genbuf, field[n]) == 0) {
+		    strcpy(fdata[n], ptr + 2);
+		    if((ptr = (char *) strchr(fdata[n], '\n')))
+			*ptr = '\0';
+		}
+	    }
+	} else if ((unum = getuser(fdata[0])) == 0) {
+	    move(2, 0);
+	    clrtobot();
+	    outs("�t��~�A�d�L���H\n\n");
+	    for (n = 0; field[n]; n++)
+		prints("%s     : %s\n", finfo[n], fdata[n]);
+	    pressanykey();
+	    neednum--;
+	} else {
+	    neednum--;
+	    memcpy(&muser, &xuser, sizeof(muser));
+	    if(automode)
+		uid = autoid;
+	    
+	    if(!automode || !auto_scan(fdata, ans)) {
+		uid = cuser.userid;
+		
+		move(1, 0);
+		prints("�b����m    �G%d\n", unum);
+		user_display(&muser, 1);
+		move(14, 0);
+		prints("\033[1;32m------------- �Я����Y��f�֨ϥΪ̸�ơA�z�٦� %d ��---------------\033[m\n", neednum);
+		for(n = 0; field[n]; n++) {
+		    if(n >= 2 && n <= 5)
+			prints("%d.", n - 2);
+		    else
+			prints("  ");
+		    prints("%-12s�G%s\n", finfo[n], fdata[n]);
+		}
+		if(muser.userlevel & PERM_LOGINOK) {
+		    getdata(b_lines - 1, 0, "\033[1;32m���b���w�g�������U, "
+			    "��s(Y/N/Skip)�H\033[m[N] ", ans, 3, LCECHO);
+		    if(ans[0] != 'y' && ans[0] != 's')
+			ans[0] = 'd';
+		} else {
+		    getdata(b_lines - 1, 0,
+			    "�O�_���������(Y/N/Q/Del/Skip)�H[Y] ",
+			    ans, 3, LCECHO);
+		}
+		nSelf++;
+	    } else
+		nAuto++;
+	    if(neednum > 0 && ans[0] == 'q') {
+		move(2, 0);
+		clrtobot();
+		prints("�S�f������h�X");
+		pressanykey();
+		ans[0] = 's';
+	    }
+	    
+	    switch(ans[0]) {
+	    case 'q':
+		if((freg = fopen(regfile, "a"))) {
+		    for(n = 0; field[n]; n++)
+			fprintf(freg, "%s: %s\n", field[n], fdata[n]);
+		    fprintf(freg, "----\n");
+		    while(fgets(genbuf, STRLEN, fn))
+			fputs(genbuf, freg);
+		    fclose(freg);
+		}
+	    case 'd':
+		break;
+	    case '0':
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case 'n':
+		if(ans[0] == 'n') {
+		    for(n = 0; field[n]; n++)
+			prints("%s: %s\n", finfo[n], fdata[n]);
+		    move(9, 0);
+		    prints("�д��X�h�^�ӽЪ���]�A�� <enter> ����\n");
+		    for (n = 0; reason[n]; n++)
+			prints("%d) ��%s\n", n, reason[n]);
+		} else
+		    buf[0] = ans[0];
+		if(ans[0] != 'n' ||
+		    getdata(10 + n, 0, "�h�^��]�G", buf, 60, DOECHO))
+		    if((buf[0] - '0') >= 0 && (buf[0] - '0') < n) {
+			int i;
+			fileheader_t mhdr;
+			char title[128], buf1[80];
+			FILE *fp;
+			
+			i = buf[0] - '0';
+			strcpy(buf, reason[i]);
+			sprintf(genbuf, "[�h�^��]] ��%s", buf);
+			
+			sethomepath(buf1, muser.userid);
+			stampfile(buf1, &mhdr);
+			strcpy(mhdr.owner, cuser.userid);
+			strncpy(mhdr.title, "[���U����]", TTLEN);
+			mhdr.savemode = 0;
+			mhdr.filemode = 0;
+			sethomedir(title, muser.userid);
+			if(append_record(title, &mhdr, sizeof(mhdr)) != -1) {
+			    fp = fopen(buf1, "w");
+			    fprintf(fp, "%s\n", genbuf);
+			    fclose(fp);
+			}
+			if((fout = fopen(logfile, "a"))) {
+			    for(n = 0; field[n]; n++)
+				fprintf(fout, "%s: %s\n", field[n], fdata[n]);
+			    n = time(NULL);
+			    fprintf(fout, "Date: %s\n", Cdate((time_t *) & n));
+			    fprintf(fout, "Rejected: %s [%s]\n----\n",
+				    uid, buf);
+			    fclose(fout);
+			}
+			break;
+		    }
+		move(10, 0);
+		clrtobot();
+		prints("�����h�^�����U�ӽЪ�");
+	    case 's':
+		if((freg = fopen(regfile, "a"))) {
+		    for(n = 0; field[n]; n++)
+			fprintf(freg, "%s: %s\n", field[n], fdata[n]);
+		    fprintf(freg, "----\n");
+		    fclose(freg);
+		}
+		break;
+	    default:
+		prints("�H�U�ϥΪ̸�Ƥw�g��s:\n");
+		mail_muser(muser, "[���U���\\�o]", "etc/registered");
+		muser.userlevel |= (PERM_LOGINOK | PERM_POST);
+		strcpy(muser.realname, fdata[2]);
+		strcpy(muser.address, fdata[4]);
+		strcpy(muser.email, fdata[6]);
+		sprintf(genbuf, "%s:%s:%s", fdata[5], fdata[3], uid);
+		strncpy(muser.justify, genbuf, REGLEN);
+		sethomefile(buf, muser.userid, "justify");
+		log_file(buf, genbuf);
+		passwd_update(unum, &muser);
+		
+		if((fout = fopen(logfile, "a"))) {
+		    for(n = 0; field[n]; n++)
+			fprintf(fout, "%s: %s\n", field[n], fdata[n]);
+		    n = time(NULL);
+		    fprintf(fout, "Date: %s\n", Cdate((time_t *) & n));
+		    fprintf(fout, "Approved: %s\n", uid);
+		    fprintf(fout, "----\n");
+		    fclose(fout);
+		}
+		break;
+	    }
+	}
+    }
+    fclose(fn);
+    unlink(fname);
+
+    move(0, 0);
+    clrtobot();
+
+    move(5, 0);
+    prints("�z�f�F %d �����U��AAutoScan �f�F %d ��", nSelf, nAuto);
+
+/**	DickG: �N�f�F�X����������� post �� Security �O�W	***********/
+/*    DickG: �]���s�������W���ݼf�֤�סA�O�G�S�����n�d�U record ������
+      strftime(buf, 200, "%Y/%m/%d/%H:%M", pt);
+
+      strcpy(xboard, "Security");
+      setbpath(xfpath, xboard);
+      stampfile(xfpath, &xfile);
+      strcpy(xfile.owner, "�t��");
+      strcpy(xfile.title, "[���i] �f�ְO��");
+      xfile.savemode = 'S';
+      xptr = fopen(xfpath, "w");
+      fprintf(xptr, "\n�ɶ��G%s 
+      %s �f�F %d �����U��\n
+      AutoScan �f�F %d �����U��\n
+      �@�p %d ���C", 
+      buf, cuser.userid, nSelf, nAuto, nSelf+nAuto);
+      fclose(xptr);
+      setbdir(fname, xboard);
+      append_record(fname, &xfile, sizeof(xfile));
+      outgo_post(&xfile, xboard);
+      touchbtotal(getbnum(xboard));
+      cuser.numposts++;        
+*/
+/*********************************************/
+    pressanykey();
+    return (0);
+}
+
+extern char* fn_register;
+extern int t_lines;
+
+int m_register() {
+    FILE *fn;
+    int x, y, wid, len;
+    char ans[4];
+    char genbuf[200];
+    
+    if((fn = fopen(fn_register, "r")) == NULL) {
+	outs("�ثe�õL�s���U���");
+	return XEASY;
+    }
+    
+    stand_title("�f�֨ϥΪ̵��U���");
+    y = 2;
+    x = wid = 0;
+    
+    while(fgets(genbuf, STRLEN, fn) && x < 65) {
+	if(strncmp(genbuf, "uid: ", 5) == 0) {
+	    move(y++, x);
+	    outs(genbuf + 5);
+	    len = strlen(genbuf + 5);
+	    if(len > wid)
+		wid = len;
+	    if(y >= t_lines - 3) {
+		y = 2;
+		x += wid + 2;
+	    }
+	}
+    }
+    fclose(fn);
+    getdata(b_lines - 1, 0, "�}�l�f�ֶ�(Auto/Yes/No)�H[N] ", ans, 3, LCECHO);
+    if(ans[0] == 'a')
+	scan_register_form(fn_register, 1, 0);
+    else if(ans[0] == 'y')
+	scan_register_form(fn_register, 0, 0);
+    
+    return 0;
+}
+
+int cat_register() {
+    if(system("cat register.new.tmp >> register.new") == 0 &&
+       system("rm -f register.new.tmp") == 0)
+	mprints(22, 0, "OK �P~~ �~��h���a!!                                                ");
+    else
+	mprints(22, 0, "�S��kCAT�L�h�O �h�ˬd�@�U�t�Χa!!                                    ");
+    pressanykey();
+    return 0;
+}
+
+static void give_id_money(char *user_id, int money, FILE *log_fp, char *mail_title, time_t t) {
+    char tt[TTLEN + 1] = {0};
+    
+    if(deumoney(searchuser(user_id), money) < 0) {
+        move(12, 0);
+        clrtoeol();
+        prints("id:%s money:%d ����a!!", user_id, money);
+        pressanykey();
+    } else {
+        fprintf(log_fp, "%ld %s %d", t, user_id, money);
+        sprintf(tt, "%s : %d ptt ��", mail_title, money);
+        mail_id(user_id, tt, "~bbs/etc/givemoney.why", "[PTT �Ȧ�]");
+    }
+}            
+
+int give_money() {
+    FILE *fp, *fp2;
+    char *ptr, *id, *mn;
+    char buf[200] = {0}, tt[TTLEN + 1] = {0}; 
+    time_t t = time(NULL);
+    struct tm *pt = localtime(&t);
+    int to_all = 0, money = 0;
+    
+    getdata(0, 0, "���w�ϥΪ�(S) �����ϥΪ�(A) ����(Q)�H[S]", buf, 3, LCECHO);
+    if(buf[0] == 'q')
+	return 1;
+    else if( buf[0] == 'a') {
+	to_all = 1;
+	getdata(1, 0, "�o�h�ֿ��O?", buf, 20, DOECHO);
+	money = atoi(buf);
+	if(money <= 0) {
+	    move(2, 0);
+	    prints("��J���~!!");
+	    pressanykey();
+	    return 1;
+	}
+    } else {
+	if(vedit("etc/givemoney.txt", NA, NULL) < 0)
+	    return 1;
+    }
+    
+    clear();
+    getdata(0, 0, "�n�o���F��(Y/N)[N]", buf, 3, LCECHO);
+    if(buf[0] != 'y')
+	return 1;
+
+    if(!(fp2 = fopen("etc/givemoney.log", "a")))
+	return 1;
+    strftime(buf, 200, "%Y/%m/%d/%H:%M", pt);
+    fprintf(fp2, "%s\n", buf);
+    
+    getdata(1, 0, "���]�U���D �G", tt, TTLEN, DOECHO);
+    move(2, 0);
+    
+    prints("�s���]�U���e");
+    pressanykey();
+    if(vedit("etc/givemoney.why", NA, NULL) < 0)
+	return 1;
+    
+    stand_title("�o����...");
+    if(to_all) {
+	extern struct uhash_t *uhash;
+	int i, unum;
+	for(unum = uhash->number, i=0; i<unum; i++) {
+	    if(bad_user_id(uhash->userid[i]))
+		continue;
+	    id = uhash->userid[i];
+	    give_id_money(id, money, fp2, tt, t);
+	}
+    } else {
+	if(!(fp = fopen("etc/givemoney.txt", "r+"))) {
+	    fclose(fp2);
+	    return 1;
+	}
+	while(fgets(buf, 255, fp)) {
+//  	    clear();
+	    if (!(ptr = strchr(buf, ':')))
+		continue;
+	    *ptr = '\0';
+	    id = buf;
+	    mn = ptr + 1;
+	    give_id_money(id, atoi(mn), fp2, tt, t);
+	}
+	fclose(fp);
+    }
+    
+    fclose(fp2);
+    pressanykey();
+    return FULLUPDATE;
+}
diff --git a/mbbsd/announce.c b/mbbsd/announce.c
new file mode 100644
index 00000000..960a45aa
--- /dev/null
+++ b/mbbsd/announce.c
@@ -0,0 +1,1590 @@
+/* $Id: announce.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern int b_lines;
+extern int p_lines;
+extern int TagNum;
+extern int currbid;
+static void g_showmenu(gmenu_t *pm) {
+    static char *mytype = "�s    ��     ��������";
+    char *title, ch;
+    int n, max;
+    item_t *item;
+
+    showtitle("��ؤ峹", pm->mtitle);
+    prints("  \033[1;36m�s��    ��      �D%56s\033[m", mytype);
+    
+    if(pm->num) {
+	n = pm->page;
+	max = n + p_lines;
+	if(max > pm->num)
+	    max = pm->num;
+	while(n < max) {
+	    item = pm->item[n++];
+	    title = item->title;
+	    ch = title[1];
+	    prints("\n%5d. %-72.71s", n, title);
+	}
+    } else
+	outs("\n  �m��ذϡn�|�b�l���Ѧa��������� :)");
+
+    move(b_lines, 1);
+    outs(pm->level ?
+	 "\033[34;46m �i�O  �D�j \033[31;47m  (h)\033[30m����  "
+	 "\033[31m(q/��)\033[30m���}  \033[31m(n)\033[30m�s�W�峹  "
+	 "\033[31m(g)\033[30m�s�W�ؿ�  \033[31m(e)\033[30m�s���ɮ�  \033[m" :
+	 "\033[34;46m �i�\\����j \033[31;47m  (h)\033[30m����  "
+	 "\033[31m(q/��)\033[30m���}  \033[31m(k��j��)\033[30m���ʴ��  "
+	 "\033[31m(enter/��)\033[30mŪ�����  \033[m");
+}
+
+static FILE *go_cmd(item_t *node, int *sock) {
+    struct sockaddr_in sin;
+    struct hostent *host;
+    char *site;
+    FILE *fp;
+    
+    *sock = socket(AF_INET, SOCK_STREAM, 0);
+    
+    if(*sock < 0) {
+	syslog(LOG_ERR, "socket(): %m");
+	return NULL;
+    }
+    memset((char *)&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(node->X.G.port);
+    
+    host = gethostbyname(site = node->X.G.server);
+    if(host == NULL)
+	sin.sin_addr.s_addr = inet_addr(site);
+    else
+	memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
+    
+    if(connect(*sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+	syslog(LOG_ERR, "connect(): %m");
+	return NULL;
+    }
+    fp = fdopen(*sock, "r+");
+    if(fp != NULL) {
+	setbuf(fp, (char *) 0);
+	fprintf(fp, "%s\r\n", node->X.G.path);
+	fflush(fp);
+    } else
+	close(*sock);
+    return fp;
+}
+
+static char *nextfield(char *data, char *field) {
+    register int ch;
+    
+    while((ch = *data)) {
+	data++;
+	if((ch == '\t') || (ch == '\r' && *data == '\n'))
+	    break;
+	*field++ = ch;
+    }
+    *field = '\0';
+    return data;
+}
+
+extern char *str_author1;
+
+static FILE* my_open(char* path) {
+    FILE* ans = 0;
+    char buf[80];
+    struct stat st;
+    time_t now = time(0);
+
+    if(stat(path, &st) == 0 && st.st_mtime < now - 3600 * 24 * 7) {
+	return fopen(path, "w");
+    }
+    
+    if((ans = fopen(path, "r+"))) {
+	fclose(ans);
+	return 0;
+	/*
+	  return directly due to currutmp->pager > 1 mode (real copy)
+	*/
+	fgets(buf, 80, ans);
+	if(!strncmp(buf, str_author1, strlen(str_author1)) ||
+	   *buf == '0' || *buf == '1') {
+	    fclose(ans);
+	    return 0;
+	}
+
+	rewind(ans);
+    } else
+	ans = fopen(path, "w");
+    return ans;
+}
+
+static jmp_buf jbuf;
+
+static void isig(int sig) {
+    longjmp(jbuf, 1);
+}
+
+#define PROXY_HOME      "proxy/"
+extern userec_t cuser;
+extern userinfo_t *currutmp;
+
+static void go_proxy(char* fpath, item_t *node, int update) {
+    char *ptr, *str, *server;
+    int ch;
+    static FILE *fo;
+    
+    strcpy(fpath, PROXY_HOME);
+    ptr = fpath + sizeof(PROXY_HOME) - 1;
+    str = server = node->X.G.server;
+    while((ch = *str)) {
+	str++;
+	if(ch == '.') {
+	    if(!strcmp(str, "edu.tw"))
+		break;
+	} else if(ch >= 'A' && ch <= 'Z') {
+	    ch |= 0x20;
+	}
+	*ptr++ = ch;
+    }
+    *ptr = '\0';
+    mkdir(fpath, 0755);
+    
+    *ptr++ = '/';
+    str = node->X.G.path;
+    while((ch = *str)) {
+	str++;
+	if(ch == '/') {
+	    ch = '.';
+	}
+	*ptr++ = ch;
+    }
+    *ptr = '\0';
+    
+    /* expire proxy data */
+    
+    if((fo = update ? fopen(fpath, "w") : my_open(fpath))) {
+	FILE *fp;
+	char buf[512];
+	int sock;
+	
+	if(fo == NULL)
+	    return;
+	
+	outmsg("�� �إ� proxy ��Ƴs�u�� ... ");
+	refresh();
+	
+	sock = -1;
+	if(setjmp(jbuf)) {
+	    if(sock != -1)
+		close(sock);
+	    fseek(fo, 0, SEEK_SET);
+	    fwrite("", 0, 0, fo);
+	    fclose(fo);
+	    alarm(0);
+	    return;
+	}
+
+	signal(SIGALRM, isig);
+	alarm(5);
+	fp = go_cmd(node, &sock);
+	alarm(0);
+
+	str = node->title;
+	ch = str[1];
+	if(ch == (char) 0xbc &&
+	   !(HAS_PERM(PERM_SYSOP) && currutmp->pager > 1)) {
+	    time_t now;
+	    
+	    time(&now);
+	    fprintf(fo, "�@��: %s (�s�u��ذ�)\n���D: %s\n�ɶ�: %s\n",
+		    server, str + 3, ctime(&now)
+		);
+	}
+
+	while(fgets(buf, 511, fp)) {
+	    if(!strcmp(buf, ".\r\n"))
+		break;
+	    if((ptr = strstr(buf, "\r\n")))
+		strcpy(ptr, "\n");
+	    fputs(buf, fo);
+	}
+	fclose(fo);
+	fclose(fp);
+    }
+}
+
+static void g_additem(gmenu_t *pm, item_t *myitem) {
+    if(pm->num < MAX_ITEMS) {
+	item_t *newitem = (item_t *)malloc(sizeof(item_t));
+	
+	memcpy(newitem, myitem, sizeof(item_t));
+	pm->item[(pm->num)++] = newitem;
+    }
+}
+
+static void go_menu(gmenu_t *pm, item_t *node, int update) {
+    FILE *fp;
+    char buf[512], *ptr, *title;
+    item_t item;
+    int ch;
+    
+    go_proxy(buf, node, update);
+    pm->num = 0;
+    if((fp = fopen(buf, "r"))) {
+	title = item.title;
+	while(fgets(buf, 511, fp)) {
+	    ptr = buf;
+	    ch = *ptr++;
+	    if(ch != '0' && ch != '1')
+		continue;
+	    
+	    strcpy(title, "�� ");
+	    if(ch == '1')
+		title[1] = (char) 0xbd;
+
+	    ptr = nextfield(ptr, title + 3);
+	    if(!*ptr)
+		continue;
+	    title[sizeof(item.title) - 1] = '\0';
+
+	    ptr = nextfield(ptr, item.X.G.path);
+	    if(!*ptr)
+		continue;
+
+	    ptr = nextfield(ptr, item.X.G.server);
+	    if(!*ptr)
+		continue;
+
+	    nextfield(ptr, buf);
+	    item.X.G.port = atoi(buf);
+
+	    g_additem(pm, &item);
+	}
+	fclose(fp);
+    }
+}
+
+static int g_searchtitle(gmenu_t* pm, int rev) {
+    static char search_str[30] = "";
+    int pos;
+    
+    if(getdata(b_lines - 1, 1,"[�j�M]����r:", search_str, 40, DOECHO))
+	if(!*search_str)
+	    return pm->now;
+
+    str_lower(search_str, search_str);
+
+    rev = rev ? -1 : 1;
+    pos = pm->now;
+    do {
+	pos += rev;
+	if(pos == pm->num)
+	    pos = 0;
+	else if(pos < 0)
+	    pos = pm->num - 1;
+	if(strstr_lower(pm->item[pos]->title, search_str))
+	    return pos;
+    } while(pos != pm->now);
+    return pm->now;
+}
+
+static void g_showhelp() {
+    clear();
+    outs("\033[36m�i " BBSNAME "�s�u��ذϨϥλ��� �j\033[m\n\n"
+	 "[��][q]         ���}��W�@�h�ؿ�\n"
+	 "[��][k]         �W�@�ӿﶵ\n"
+	 "[��][j]         �U�@�ӿﶵ\n"
+	 "[��][r][enter]  �i�J�ؿ���Ū���峹\n"
+	 "[b][PgUp]       �W�����\n"
+	 "[^F][PgDn][Spc] �U�����\n"
+	 "[##]            ����ӿﶵ\n"
+	 "[^S]            �N�峹�s��H�c\n"
+	 "[R]             ��s���\n"
+	 "[N]             �d���ɦW\n"
+	 "[c][C][^C]      �����峹/�ø��ܤW���K�峹���a��/"
+	 "�����K��W���K���a��\n");
+    pressanykey();
+}
+
+#define PATHLEN     256
+
+static char paste_fname[200];
+extern time_t paste_time;
+extern char paste_path[];
+extern char paste_title[];
+extern int paste_level;
+
+static void load_paste() {
+    struct stat st;
+    FILE *fp;
+    
+    if(!*paste_fname)
+	setuserfile(paste_fname, "paste_path");
+    if(stat(paste_fname, &st) == 0 && st.st_mtime > paste_time &&
+       (fp = fopen(paste_fname, "r"))) {
+	int i;
+	fgets(paste_path, PATHLEN, fp);
+	i = strlen(paste_path) - 1;
+	if(paste_path[i] == '\n')
+	    paste_path[i] = 0;
+	fgets(paste_title, STRLEN, fp);
+	i = strlen(paste_title) - 1;
+	if(paste_title[i] == '\n')
+	    paste_title[i] = 0;
+	fscanf(fp, "%d", &paste_level);
+	paste_time = st.st_mtime;
+	fclose(fp);
+    }
+}
+
+static char copyfile[PATHLEN];
+static char copytitle[TTLEN+1];
+static char copyowner[IDLEN + 2];
+
+void a_copyitem(char* fpath, char* title, char* owner, int mode) {
+    strcpy(copyfile, fpath);
+    strcpy(copytitle, title);
+    if(owner)
+	strcpy(copyowner, owner);
+    else
+	*copyowner = 0;
+    if(mode) {
+	outmsg("�ɮ׼аO�����C[�`�N] ������~��R�����!");
+	igetch();
+    }
+}
+
+#define FHSZ            sizeof(fileheader_t)
+
+static void a_loadname(menu_t *pm) {
+    char buf[PATHLEN];
+    int len;
+    
+    setadir(buf, pm->path);
+    len = get_records(buf, pm->header, FHSZ, pm->page+1, p_lines);
+    if(len < p_lines)
+	bzero(&pm->header[len], FHSZ*(p_lines-len));
+}
+
+static void a_timestamp(char *buf, time_t *time) {
+    struct tm *pt = localtime(time);
+    
+    sprintf(buf, "%02d/%02d/%02d", pt->tm_mon + 1, pt->tm_mday, (pt->tm_year + 1900) % 100);
+}
+
+static void a_showmenu(menu_t *pm) {
+    char *title, *editor;
+    int n;
+    fileheader_t *item;
+    char buf[PATHLEN];
+    time_t dtime;
+
+    showtitle("��ؤ峹", pm->mtitle);
+    prints("   \033[1;36m�s��    ��      �D%56s\033[0m",
+	   "�s    ��      ��    ��");
+
+    if(pm->num) {
+	setadir(buf, pm->path);
+	a_loadname(pm);
+	for(n = 0; n < p_lines && pm->page + n < pm->num; n++) {
+	    item = &pm->header[n];
+	    title = item->title;
+	    editor = item->owner;
+	    /*  Ptt ��ɶ��אּ���ɮ׮ɶ�
+		dtime = atoi(&item->filename[2]);
+	    */
+	    sprintf(buf,"%s/%s",pm->path,item->filename);
+	    dtime = dasht(buf);
+	    a_timestamp(buf, &dtime);
+	    prints("\n%6d%c %-47.46s%-13s[%s]", pm->page+n+1, 
+			    (item->filemode & FILE_BM) ?'X':
+			    (item->filemode & FILE_HIDE) ?')':'.',
+			    title, editor,
+		   buf);
+	}
+    } else
+	outs("\n  �m��ذϡn�|�b�l���Ѧa��������ؤ�... :)");
+
+    move(b_lines, 1);
+    outs(pm->level ?
+	 "\033[34;46m �i�O  �D�j \033[31;47m  (h)\033[30m����  "
+	 "\033[31m(q/��)\033[30m���}  \033[31m(n)\033[30m�s�W�峹  "
+	 "\033[31m(g)\033[30m�s�W�ؿ�  \033[31m(e)\033[30m�s���ɮ�  \033[m" :
+	 "\033[34;46m �i�\\����j \033[31;47m  (h)\033[30m����  "
+	 "\033[31m(q/��)\033[30m���}  \033[31m(k��j��)\033[30m���ʴ��  "
+	 "\033[31m(enter/��)\033[30mŪ�����  \033[m");
+}
+
+static int a_searchtitle(menu_t *pm, int rev) {
+    static char search_str[40] = "";
+    int pos;
+    
+    getdata(b_lines - 1, 1, "[�j�M]����r:", search_str, 40, DOECHO);
+    
+    if(!*search_str)
+	return pm->now;
+    
+    str_lower(search_str, search_str);
+    
+    rev = rev ? -1 : 1;
+    pos = pm->now;
+    do {
+	pos += rev;
+	if(pos == pm->num)
+	    pos = 0;
+	else if(pos < 0)
+	    pos = pm->num - 1;
+	if(pos < pm->page || pos >= pm->page + p_lines) {
+	    pm->page = pos - pos % p_lines;
+	    a_loadname(pm);
+	}
+	if(strstr_lower(pm->header[pos - pm->page].title, search_str))
+	    return pos;
+    } while(pos != pm->now);
+    return pm->now;
+}
+
+enum {NOBODY, MANAGER, SYSOP};
+
+static void a_showhelp(int level) {
+    clear();
+    outs("\033[36m�i " BBSNAME "���G��ϥλ��� �j\033[m\n\n"
+	 "[��][q]         ���}��W�@�h�ؿ�\n"
+	 "[��][k]         �W�@�ӿﶵ\n"
+	 "[��][j]         �U�@�ӿﶵ\n"
+	 "[��][r][enter]  �i�J�ؿ���Ū���峹\n"
+	 "[^B][PgUp]      �W�����\n"
+	 "[^F][PgDn][Spc] �U�����\n"
+	 "[##]            ����ӿﶵ\n"
+	 "[F][U]          �N�峹�H�^ Internet �l�c/"
+	 "�N�峹 uuencode ��H�^�l�c\n");
+    if(level >= MANAGER) {
+	outs("\n\033[36m�i �O�D�M���� �j\033[m\n"
+             "[H]             ������ ���}/�|��/���D �~��\\Ū\n"
+	     "[n/g/G]         ������ؤ峹/�}�P�ؿ�/�إ߳s�u\n"
+	     "[m/d/D]         ����/�R���峹/�R���@�ӽd�򪺤峹\n"
+	     "[f/T/e]         �s����D�Ÿ�/�ק�峹���D/���e\n"
+	     "[c/p/a]         ����/�߶K/���[�峹\n"
+	     "[^P/^A]         �߶K/���[�w��'t'�аO�峹\n");
+    }
+
+    if(level >= SYSOP) {
+	outs("\n\033[36m�i �����M���� �j\033[m\n"
+	     "[l]             �� symbolic link\n"
+	     "[N]             �d���ɦW\n");
+    }
+    pressanykey();
+}
+
+static int AnnounceSelect() {
+    static char xboard[20];
+    char buf[20];
+    char fpath[256];
+    boardheader_t *bp;
+
+    move(2, 0);
+    clrtoeol();
+    move(3, 0);
+    clrtoeol();
+    move(1, 0);
+    make_blist();
+    namecomplete("��ܺ�ذϬݪO�G", buf);
+    if(*buf)
+	strcpy(xboard, buf);
+    if(*xboard && (bp = getbcache(getbnum(xboard)))) {
+	setapath(fpath, xboard);
+	setutmpmode(ANNOUNCE);
+	a_menu(xboard, fpath,
+	       (HAS_PERM(PERM_ALLBOARD) ||
+		(HAS_PERM(PERM_BM) && is_BM(bp->BM))) ? 1 : 0);
+    }
+    return FULLUPDATE;
+}
+
+extern char vetitle[];
+
+void gem(char* maintitle, item_t* path, int update) {
+    gmenu_t me;
+    int ch;
+    char fname[PATHLEN];
+    
+    strncpy(me.mtitle, maintitle, 40);
+    me.mtitle[40] = 0;
+    go_menu(&me, path, update);
+    
+    /* ��ذ�-tree ���������c�ݩ� cuser ==> BM */
+    
+    me.level = 0;
+    me.page = 9999;
+    me.now = 0;
+    for(;;) {
+	if(me.now >= me.num && me.num > 0)
+	    me.now = me.num - 1;
+	else if(me.now < 0)
+	    me.now = 0;
+	
+	if(me.now < me.page || me.now >= me.page + p_lines) {
+	    me.page = me.now - (me.now % p_lines);
+	    g_showmenu(&me);
+	}
+	ch = cursor_key(2 + me.now - me.page, 0);
+	if(ch == 'q' || ch == 'Q' || ch == KEY_LEFT)
+	    break;
+	
+	if(ch >= '0' && ch <= '9') {
+	    if((ch = search_num(ch, me.num)) != -1)
+		me.now = ch;
+	    me.page = 9999;
+	    continue;
+	}
+	
+	switch(ch) {
+	case KEY_UP:
+	case 'k':
+	    if(--me.now < 0)
+		me.now = me.num - 1;
+	    break;
+	case KEY_DOWN:
+	case 'j':
+	    if(++me.now >= me.num)
+		me.now = 0;
+	    break;
+	case KEY_PGUP:
+	case 'b':
+	    if(me.now >= p_lines)
+		me.now -= p_lines;
+	    else if(me.now > 0)
+		me.now = 0;
+	    else
+		me.now = me.num - 1;
+	    break;
+	case ' ':
+	case KEY_PGDN:
+	case Ctrl('F'):
+	    if(me.now < me.num - p_lines)
+		me.now += p_lines;
+	    else if(me.now < me.num - 1)
+		me.now = me.num - 1;
+	    else
+		me.now = 0;
+	    break;
+	case 'h':
+	    g_showhelp();
+	    me.page = 9999;
+	    break;
+	case '?':
+	case '/':
+	    me.now = g_searchtitle(&me, ch == '?');
+	    me.page = 9999;
+	    break;
+	case 'N':
+	    if(HAS_PERM(PERM_SYSOP)) {
+		go_proxy(fname, me.item[me.now], 0);
+		move(b_lines - 1, 0);
+		outs(fname);
+		pressanykey();
+		me.page = 9999;
+	    }
+	    break;
+	case 'c':
+	case 'C':
+	case Ctrl('C'):
+	    if(me.now < me.num) {
+		item_t *node = me.item[me.now];
+		char *title = node->title;
+		int mode = title[1];
+
+		load_paste();
+		if(mode == (char) 0xbc || ch == Ctrl('C')) {
+		    if(mode == (char) 0xbc)
+			go_proxy(fname, node, 0);
+		    if(ch == Ctrl('C') && *paste_path && paste_level) {
+			char newpath[PATHLEN];
+			fileheader_t item;
+
+			strcpy(newpath, paste_path);
+			if(mode == (char) 0xbc) {
+			    stampfile(newpath, &item);
+			    unlink(newpath);
+			    Link(fname, newpath);
+			} else
+			    stampdir(newpath, &item);
+			strcpy(item.owner, cuser.userid);
+			sprintf(item.title, "%s%.72s",
+				(currutmp->pager > 1) ? "" :
+				(mode == (char) 0xbc) ? "�� " : "�� ",
+				title + 3);
+			strcpy(strrchr(newpath, '/') + 1, ".DIR");
+			append_record(newpath, &item, FHSZ);
+			if(++me.now >= me.num)
+			    me.now = 0;
+			break;
+		    }
+		    if(mode == (char) 0xbc) {
+			a_copyitem(fname,
+				   title + ((currutmp->pager > 1) ? 3 : 0),
+				   0, 1);
+			if(ch == 'C' && *paste_path) {
+			    setutmpmode(ANNOUNCE);
+			    a_menu(paste_title, paste_path, paste_level);
+			}
+			me.page = 9999;
+		    } else
+			bell();
+		}
+	    }
+	    break;
+	case Ctrl('B'):
+	    m_read();
+	    me.page = 9999;
+	    break;
+	case Ctrl('I'):
+	    t_idle();
+	    me.page = 9999;
+	    break;
+	case 's':
+	    AnnounceSelect();
+	    me.page = 9999;
+	    break;
+	case '\n':
+	case '\r':
+	case KEY_RIGHT:
+	case 'r':
+	case 'R':
+	    if(me.now < me.num) {
+		item_t *node = me.item[me.now];
+		char *title = node->title;
+		int mode = title[1];
+		int update = (ch == 'R') ? 1 : 0;
+
+		title += 3;
+
+		if(mode == (char) 0xbc) {
+		    int more_result;
+
+		    go_proxy(fname, node, update);
+		    strcpy(vetitle, title);
+		    while((more_result = more(fname, YEA))) {
+			if(more_result == 1) {
+			    if(--me.now < 0) {
+				me.now = 0;
+				break;
+			    }
+			} else if(more_result == 3) {
+			    if(++me.now >= me.num) {
+				me.now = me.num - 1;
+				break;
+			    }
+			} else
+			    break;
+			node = me.item[me.now];
+			if(node->title[1] != (char) 0xbc)
+			    break;
+			go_proxy(fname, node, update);
+			strcpy(vetitle, title);
+		    }
+		} else if(mode == (char) 0xbd) {
+		    gem(title, node, update);
+		}
+		me.page = 9999;
+	    }
+	    break;
+	}
+    }
+    for(ch = 0; ch < me.num; ch++)
+	free(me.item[ch]);
+}
+
+extern char *msg_fwd_ok;
+extern char *msg_fwd_err1;
+extern char *msg_fwd_err2;
+
+static void a_forward(char *path, fileheader_t *pitem, int mode) {
+    fileheader_t fhdr;
+    
+    strcpy(fhdr.filename, pitem->filename);
+    strcpy(fhdr.title, pitem->title);
+    switch(doforward(path, &fhdr, mode)) {
+    case 0:
+	outmsg(msg_fwd_ok);
+	break;
+    case -1:
+	outmsg(msg_fwd_err1);
+	break;
+    case -2:
+	outmsg(msg_fwd_err2);
+	break;
+    }
+}
+
+static void a_additem(menu_t *pm, fileheader_t *myheader) {
+    char buf[PATHLEN];
+    
+    setadir(buf, pm->path);
+    if(append_record(buf, myheader, FHSZ) == -1)
+	return;
+    pm->now = pm->num++;
+    
+    if(pm->now >= pm->page + p_lines) {
+	pm->page = pm->now - ((pm->page == 10000 && pm->now > p_lines / 2) ?
+			      (p_lines / 2) : (pm->now % p_lines));
+    }      
+    
+    /*Ptt*/  
+    strcpy(pm->header[pm->now - pm->page].filename, myheader->filename);
+}
+
+#define ADDITEM         0
+#define ADDGROUP        1
+#define ADDGOPHER       2
+#define ADDLINK         3
+extern char currboard[];
+
+static void a_newitem(menu_t *pm, int mode) {
+    static char *mesg[4] = {
+	"[�s�W�峹] �п�J���D�G",      /* ADDITEM */
+	"[�s�W�ؿ�] �п�J���D�G",      /* ADDGROUP */
+	"[�s�W�s�u] �п�J���D�G",      /* ADDGOPHER */
+	"�п�J���D�G"                  /* ADDLINK */
+    };
+
+    char fpath[PATHLEN], buf[PATHLEN], lpath[PATHLEN];
+    fileheader_t item;
+    int d;
+
+    strcpy(fpath, pm->path);
+
+    switch(mode) {
+    case ADDITEM:
+	stampfile(fpath, &item);
+	strcpy(item.title, "�� ");  /* A1BA */
+	break;
+	
+    case ADDGROUP:
+	stampdir(fpath, &item);
+	strcpy(item.title, "�� ");  /* A1BB */
+	break;
+    case ADDGOPHER:
+	bzero(&item, sizeof(item));
+	strcpy(item.title, "�� ");  /* A1BB */
+	if(!getdata(b_lines - 2, 1, "��JURL��}�G",
+		    item.filename+2,61, DOECHO))
+	    return;
+	break;
+    case ADDLINK:
+	stamplink(fpath, &item);
+	if (!getdata(b_lines - 2, 1, "�s�W�s�u�G", buf, 61, DOECHO))
+	    return;
+	if(invalid_pname(buf)) {
+	    unlink(fpath);
+	    outs("�ت��a���|���X�k�I");
+	    igetch();
+	    return;
+	}
+
+	item.title[0] = 0;
+	for(d = 0; d <= 3; d++) {
+	    switch(d) {
+	    case 0:
+		sprintf(lpath, "%s%s%s/%s",
+			BBSHOME, "/man/boards/",currboard , buf);
+		break;
+	    case 1:
+		sprintf(lpath, "%s%s%s",
+			BBSHOME, "/man/boards/" , buf);
+		break;
+	    case 2:
+		sprintf(lpath, "%s%s%s",
+			BBSHOME, "/" , buf);
+		break;
+	    case 3:
+		sprintf(lpath, "%s%s%s",
+			BBSHOME, "/etc/" , buf);
+		break;
+	    }
+	    if(dashf(lpath)) {
+		strcpy(item.title, "�� ");        /* A1B3 */
+		break;
+	    } else if (dashd(lpath)) {
+		strcpy(item.title, "�� ");        /* A1B4 */
+		break;
+	    }
+	    if(!HAS_PERM(PERM_BBSADM) && d==1)
+		break;
+	}
+	
+	if(!item.title[0]) {
+	    unlink(fpath);
+	    outs("�ت��a���|���X�k�I");
+	    igetch();
+	    return;
+	}
+    }
+
+    if(!getdata(b_lines - 1, 1, mesg[mode], &item.title[3], 55, DOECHO)) {
+	if(mode == ADDGROUP)
+	    rmdir(fpath);
+	else if(mode != ADDGOPHER)
+	    unlink(fpath);
+	return;
+    }
+
+    switch(mode) {
+    case ADDITEM:
+	if(vedit(fpath, 0, NULL) == -1) {
+	    unlink(fpath);
+	    pressanykey();
+	    return;
+	}
+	break;
+    case ADDLINK:
+	unlink(fpath);
+	if(symlink(lpath, fpath) == -1) {
+	    outs("�L�k�إ� symbolic link");
+	    igetch();
+	    return;
+	}
+	break;
+    case ADDGOPHER:
+	strcpy(item.date, "70");
+	strncpy(item.filename, "H.",2);
+	break;
+    }
+
+    strcpy(item.owner, cuser.userid);
+    a_additem(pm, &item);
+}
+
+static void a_pasteitem(menu_t *pm, int mode) {
+    char newpath[PATHLEN];
+    char buf[PATHLEN];
+    char ans[2];
+    int i;
+    fileheader_t item;
+
+    move(b_lines - 1, 1);
+    if(copyfile[0]) {
+	if(dashd(copyfile)) {
+	    for(i = 0; copyfile[i] && copyfile[i] == pm->path[i]; i++);
+	    if(!copyfile[i]) {
+		outs("�N�ؿ����i�ۤv���l�ؿ����A�|�y���L�a�j��I");
+		igetch();
+		return;
+	    }
+	}
+	if(mode) {
+	    sprintf(buf, "�T�w�n����[%s]��(Y/N)�H[N] ", copytitle);
+	    getdata(b_lines - 1, 1, buf, ans, 3, LCECHO);
+	} else
+	    ans[0]='y';
+	if(ans[0] == 'y') {
+	    strcpy(newpath, pm->path);
+
+	    if(*copyowner) {
+		char* fname = strrchr(copyfile, '/');
+
+		if(fname)
+		    strcat(newpath, fname);
+		else
+		    return;
+		if(access(pm->path, X_OK | R_OK | W_OK))
+		    mkdir(pm->path, 0755);
+		memset(&item, 0, sizeof(fileheader_t));
+		strcpy(item.filename, fname + 1);
+		memcpy(copytitle, "��", 2);
+		if(HAS_PERM(PERM_BBSADM))
+		    Link(copyfile, newpath);
+		else {
+		    sprintf(buf, "/bin/cp %s %s", copyfile, newpath);
+		    system(buf);
+		}
+	    } else if(dashf(copyfile)) {
+		stampfile(newpath, &item);
+		memcpy(copytitle, "��", 2);
+		sprintf(buf, "/bin/cp %s %s", copyfile, newpath);
+	    } else if(dashd(copyfile)) {
+		stampdir(newpath, &item);
+		memcpy(copytitle, "��", 2);
+		sprintf(buf, "/bin/cp -r %s/* %s/.D* %s", copyfile, copyfile,
+			newpath);
+	    } else {
+		outs("�L�k�����I");
+		igetch();
+		return;
+	    }
+	    strcpy(item.owner, *copyowner ? copyowner : cuser.userid);
+	    strcpy(item.title, copytitle);
+	    if(!*copyowner)
+		system(buf);
+	    a_additem(pm, &item);
+	    copyfile[0] = '\0';
+	}
+    } else {
+	outs("������ copy �R�O��A paste");
+	igetch();
+    }
+}
+
+static void a_appenditem(menu_t *pm, int isask) {
+    char fname[PATHLEN];
+    char buf[ANSILINELEN];
+    char ans[2] = "y";
+    FILE *fp, *fin;
+
+    move(b_lines - 1, 1);
+    if(copyfile[0]) {
+	if(dashf(copyfile)) {
+	    sprintf(fname, "%s/%s", pm->path,
+		    pm->header[pm->now-pm->page].filename);
+	    if(dashf(fname)) {
+		if(isask) {
+		    sprintf(buf, "�T�w�n�N[%s]���[�󦹶�(Y/N)�H[N] ",
+			    copytitle);
+		    getdata(b_lines - 2, 1, buf, ans, 3, LCECHO);
+		}
+		if(ans[0] == 'y') {
+		    if((fp = fopen(fname, "a+"))) {
+			if((fin = fopen(copyfile, "r"))) {
+			    memset(buf, '-', 74);
+			    buf[74] = '\0';
+			    fprintf(fp, "\n> %s <\n\n", buf);
+			    if(isask)
+				getdata(b_lines - 1, 1,
+					"�O�_����ñ�W�ɳ���(Y/N)�H[Y] ",
+					ans, 3, LCECHO);
+			    while(fgets(buf, sizeof(buf), fin)) {
+				if((ans[0] == 'n' ) &&
+				   !strcmp(buf, "--\n"))
+				    break;
+				fputs(buf, fp);
+			    }
+			    fclose(fin);
+			    copyfile[0] = '\0';
+			}
+			fclose(fp);
+		    }
+		}
+	    } else {
+		outs("�ɮפ��o���[�󦹡I");
+		igetch();
+	    }
+	} else {
+	    outs("���o���[��ӥؿ����ɮ׫�I");
+	    igetch();
+	}
+    } else {
+	outs("������ copy �R�O��A append");
+	igetch();
+    }
+}
+
+static int a_pastetagpost(menu_t *pm, int mode) {
+    extern int TagNum;
+    extern void EnumTagFhdr();
+    extern void UnTagger(int locus);
+    fileheader_t fhdr;
+    int ans = 0, ent=0, tagnum;
+    char title[TTLEN + 1]=  "��  ";
+    char dirname[200],buf[200];
+
+    setbdir(dirname, currboard);
+    tagnum = TagNum;
+
+    if (!tagnum) return ans;
+
+    while (tagnum--)
+    {
+      EnumTagFhdr (&fhdr, dirname, ent++);
+      setbfile (buf, currboard, fhdr.filename);
+
+      if (dashf (buf))
+      {
+        strncpy(title+3, fhdr.title, TTLEN-3);
+        title[TTLEN] = '\0';
+        a_copyitem(buf, title, 0, 0);
+        if(mode) 
+        {
+  	  mode--;     
+	  a_pasteitem(pm,0);
+        } 
+        else a_appenditem(pm, 0);
+        ++ans;
+        UnTagger (tagnum);
+      }
+
+    };
+
+    return ans;
+}          
+
+static void a_moveitem(menu_t *pm) {
+    fileheader_t *tmp;
+    char newnum[4];
+    int num, max, min;
+    char buf[PATHLEN];
+    int fail;
+
+    sprintf(buf, "�п�J�� %d �ﶵ���s���ǡG", pm->now + 1);
+    if(!getdata(b_lines - 1, 1, buf, newnum, 6, DOECHO))
+	return;
+    num = (newnum[0] == '$') ? 9999 : atoi(newnum) - 1;
+    if(num >= pm->num)
+	num = pm->num - 1;
+    else if(num < 0)
+	num = 0;
+    setadir(buf, pm->path);
+    min = num < pm->now ? num : pm->now;
+    max = num > pm->now ? num : pm->now;
+    tmp = (fileheader_t *) calloc(max + 1, FHSZ);
+
+    fail = 0;
+    if(get_records(buf, tmp, FHSZ, 1, min) != min)
+	fail = 1;
+    if(num > pm->now) {
+	if(get_records(buf, &tmp[min], FHSZ, pm->now+2, max-min) != max-min)
+	    fail = 1;
+	if(get_records(buf, &tmp[max], FHSZ, pm->now+1, 1) != 1)
+	    fail = 1;
+    } else {
+	if(get_records(buf, &tmp[min], FHSZ, pm->now+1, 1) != 1)
+	    fail = 1;
+	if(get_records(buf, &tmp[min+1], FHSZ, num+1, max-min) != max-min)
+	    fail = 1;
+    }
+    if(!fail)
+	substitute_record(buf, tmp, FHSZ * (max + 1), 1);
+    pm->now = num;
+    free(tmp);
+}
+
+static void a_delrange(menu_t *pm) {
+    char fname[256];
+
+    sprintf(fname,"%s/.DIR",pm->path);
+    del_range(0, NULL, fname);
+    pm->num = get_num_records(fname, FHSZ);
+}
+
+static void a_delete(menu_t *pm) {
+    char fpath[PATHLEN], buf[PATHLEN], cmd[PATHLEN];
+    char ans[4];
+    fileheader_t backup;
+
+    sprintf(fpath, "%s/%s", pm->path, pm->header[pm->now - pm->page].filename);
+    setadir(buf, pm->path);
+        
+    if(pm->header[pm->now - pm->page].filename[0] == 'H' && 
+       pm->header[pm->now - pm->page].filename[1] == '.') {
+	getdata(b_lines - 1, 1, "�z�T�w�n�R������ذϳs�u��(Y/N)�H[N] ",
+		ans, 3, LCECHO);
+	if(ans[0] != 'y')
+	    return;
+	if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+	    return;
+    } else if (dashl(fpath)) {
+	getdata(b_lines - 1, 1, "�z�T�w�n�R���� symbolic link ��(Y/N)�H[N] ",
+		ans, 3, LCECHO);
+	if(ans[0] != 'y')
+	    return;
+	if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+	    return;
+	unlink(fpath);
+    } else if(dashf(fpath)) {
+	getdata(b_lines - 1, 1, "�z�T�w�n�R�����ɮ׶�(Y/N)�H[N] ", ans, 3,
+		LCECHO);
+	if(ans[0] != 'y')
+	    return;
+	if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+	    return; 
+
+	setbpath(buf, "deleted");
+	stampfile(buf, &backup);
+	strcpy(backup.owner, cuser.userid);
+	strcpy(backup.title,pm->header[pm->now - pm->page].title + 2);
+	backup.savemode = 'D';
+
+	sprintf(cmd, "mv -f %s %s", fpath,buf);
+	system(cmd);
+	setbdir(buf, "deleted"); 
+	append_record(buf, &backup, sizeof(backup)); 
+    } else if (dashd(fpath)) {
+	getdata(b_lines - 1, 1, "�z�T�w�n�R����ӥؿ���(Y/N)�H[N] ", ans, 3, 
+		LCECHO);
+	if(ans[0] != 'y')
+	    return;
+	if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+	    return;
+
+	setapath(buf, "deleted"); 
+	stampdir(buf, &backup); 
+
+	sprintf(cmd, "rm -rf %s;/bin/mv -f %s %s",buf,fpath,buf); 
+	system(cmd); 
+
+	strcpy(backup.owner, cuser.userid); 
+	strcpy(backup.title,pm->header[pm->now - pm->page].title +2); 
+	setapath(buf, "deleted");  
+	setadir(buf,buf);
+	append_record(buf, &backup, sizeof(backup));  
+    } else { /* Ptt �l�������� */
+	getdata(b_lines - 1, 1, "�z�T�w�n�R�����l�������ض�(Y/N)�H[N] ",
+		ans, 3, LCECHO);
+	if(ans[0] != 'y')
+	    return;
+	if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+	    return;
+    }
+    pm->num--;
+}
+
+static void a_newtitle(menu_t *pm) {
+    char buf[PATHLEN];
+    fileheader_t item;
+
+    memcpy(&item, &pm->header[pm->now - pm->page], FHSZ);
+    strcpy(buf,item.title + 3);
+    if(getdata_buf(b_lines - 1, 1, "�s���D�G", buf, 60, DOECHO)) {
+	strcpy(item.title + 3, buf);
+	setadir(buf, pm->path);
+	substitute_record(buf, &item, FHSZ, pm->now + 1);
+    }
+}
+static void a_hideitem(menu_t *pm) {
+    fileheader_t *item=&pm->header[pm->now - pm->page];
+    char buf[PATHLEN];
+    if(item->filemode&FILE_BM)
+	{
+          item->filemode &= ~FILE_BM;
+	  item->filemode &= ~FILE_HIDE;
+        }
+    else if(item->filemode&FILE_HIDE)
+          item->filemode |= FILE_BM;
+    else item->filemode |= FILE_HIDE;
+    setadir(buf, pm->path);
+    substitute_record(buf, item, FHSZ, pm->now + 1);
+}
+static void a_editsign(menu_t *pm) {
+    char buf[PATHLEN];
+    fileheader_t item;
+
+    memcpy(&item, &pm->header[pm->now - pm->page], FHSZ);
+    sprintf(buf, "%c%c", item.title[0], item.title[1]);
+    if(getdata_buf(b_lines - 1, 1, "�Ÿ�", buf, 5, DOECHO)) {
+	item.title[0] = buf[0] ? buf[0] : ' ';
+	item.title[1] = buf[1] ? buf[1] : ' ';
+	item.title[2] = buf[2] ? buf[2] : ' ';
+	setadir(buf, pm->path);
+	substitute_record(buf, &item, FHSZ, pm->now + 1);
+    }
+}
+
+static void a_showname(menu_t *pm) {
+    char buf[PATHLEN];
+    int len;
+    int i;
+    int sym;
+
+    move(b_lines - 1, 1);
+    sprintf(buf, "%s/%s", pm->path, pm->header[pm->now - pm->page].filename);
+    if(dashl(buf)) {
+	prints("�� symbolic link �W�٬� %s\n",
+	       pm->header[pm->now - pm->page].filename);
+	if((len = readlink(buf, buf, PATHLEN-1)) >= 0) {
+	    buf[len] = '\0';
+	    for(i = 0; BBSHOME[i] && buf[i] == BBSHOME[i]; i++);
+	    if(!BBSHOME[i] && buf[i] == '/') {
+		if(HAS_PERM(PERM_BBSADM))
+		    sym = 1;
+		else {
+		    sym = 0;
+		    for(i++; BBSHOME "/man"[i] && buf[i] == BBSHOME "/man"[i];
+			i++);
+		    if(!BBSHOME "/man"[i] && buf[i] == '/')
+			sym = 1;
+		}
+		if(sym) {
+		    pressanykey();
+		    move(b_lines - 1, 1);
+		    prints("�� symbolic link ���V %s\n", &buf[i+1]);
+		}
+	    }
+	}
+    } else if(dashf(buf))
+	prints("���峹�W�٬� %s", pm->header[pm->now - pm->page].filename);
+    else if(dashd(buf))
+	prints("���ؿ��W�٬� %s", pm->header[pm->now - pm->page].filename);
+    else
+	outs("�����ؤw�l��, ��ij�N��R���I");
+    pressanykey();
+}
+
+static char *a_title;
+
+static void atitle() {
+    showtitle("��ؤ峹", a_title);
+    outs("[��]���} [��]�\\Ū [^P]�o���峹 [b]�Ƨѿ� [d]�R�� [q]��ذ� "
+	 "[TAB]��K [h]elp\n\033[7m  �s��   �� ��  �@  ��       "
+	 "��  ��  ��  �D\033[m");
+}
+
+extern char currtitle[];
+
+char trans_buffer[256];
+extern char quote_file[];
+extern unsigned int currstat;
+
+static int isvisible_man(menu_t  *me)
+{
+  fileheader_t *fhdr = &me->header[me->now-me->page];
+  if( me->level<MANAGER && ((fhdr->filemode & FILE_BM) ||
+        ((fhdr->filemode & FILE_HIDE) && 
+        hbflcheck(currbid, currutmp->uid)))) 
+	   return 0;
+  return 1;
+}
+int a_menu(char *maintitle, char *path, int lastlevel) {
+    static char Fexit;
+    menu_t me;
+    char fname[PATHLEN];
+    int ch, returnvalue = FULLUPDATE;
+    
+    trans_buffer[0] = 0;
+    
+    Fexit = 0;
+    me.header = (fileheader_t *)calloc(p_lines, FHSZ);
+    me.path = path;
+    strcpy(me.mtitle, maintitle);
+    setadir(fname, me.path);
+    me.num = get_num_records(fname, FHSZ);
+    
+    /* ��ذ�-tree ���������c�ݩ� cuser ==> BM */
+    
+    if(!(me.level = lastlevel)) {
+	char *ptr;
+	
+	if((ptr = strrchr(me.mtitle, '[')))
+	    me.level = is_BM(ptr + 1);
+    }
+    
+    me.page = 9999;
+    me.now = 0;
+    for(;;) {
+	if(me.now >= me.num)
+	    me.now = me.num - 1;
+	if(me.now < 0)
+	    me.now = 0;
+
+	if(me.now < me.page || me.now >= me.page + p_lines) {
+	    me.page = me.now - ((me.page == 10000 && me.now > p_lines / 2) ?
+				(p_lines / 2) : (me.now % p_lines));
+	    a_showmenu(&me);
+	}
+	
+	ch = cursor_key(2 + me.now - me.page, 0);
+	
+	if(ch == 'q' || ch == 'Q' || ch == KEY_LEFT)
+	    break;
+	
+	if(ch >= '1' && ch <= '9') {
+	    if((ch = search_num(ch, me.num)) != -1)
+		me.now = ch;
+	    me.page = 10000;
+	    continue;
+	}
+	
+	switch(ch) {
+	case KEY_UP:
+	case 'k':
+	    if(--me.now < 0)
+		me.now = me.num - 1;
+	    break;
+	    
+	case KEY_DOWN:
+	case 'j':
+	    if(++me.now >= me.num)
+		me.now = 0;
+	    break;
+
+	case KEY_PGUP:
+	case Ctrl('B'):
+	    if(me.now >= p_lines)
+		me.now -= p_lines;
+	    else if (me.now > 0)
+		me.now = 0;
+	    else
+		me.now = me.num - 1;
+	    break;
+
+	case ' ':
+	case KEY_PGDN:
+	case Ctrl('F'):
+	    if(me.now < me.num - p_lines)
+		me.now += p_lines;
+	    else if(me.now < me.num - 1)
+		me.now = me.num - 1;
+	    else
+		me.now = 0;
+	    break;
+
+	case '0':
+	    me.now = 0;
+	    break;
+	case '?':
+	case '/':
+	    me.now = a_searchtitle(&me, ch == '?');
+	    me.page = 9999;
+	    break;
+	case '$':
+	    me.now = me.num -1;
+	    break;
+	case 'h':
+	    a_showhelp(me.level);
+	    me.page = 9999;
+	    break;
+	case Ctrl('C'):
+	    cal();
+	    me.page = 9999;
+	    break;
+
+	case Ctrl('I'):
+	    t_idle();
+	    me.page = 9999;
+	    break;
+
+	case 's':
+	    AnnounceSelect();
+	    me.page = 9999;
+	    break;
+
+	case 'e':
+	case 'E':
+	    sprintf(fname, "%s/%s", path, me.header[me.now-me.page].filename);
+	    if(dashf(fname) && me.level >= MANAGER) {
+		*quote_file = 0;
+		if(vedit(fname, NA, NULL) != -1) {
+		    char fpath[200];
+		    fileheader_t fhdr;
+
+		    strcpy(fpath, path);
+		    stampfile(fpath, &fhdr);
+		    unlink(fpath);
+		    Rename(fname, fpath);
+		    strcpy(me.header[me.now-me.page].filename, fhdr.filename);
+		    strcpy(me.header[me.now-me.page].owner, cuser.userid);
+		    setadir(fpath, path);
+		    substitute_record(fpath, me.header+me.now-me.page,
+				      sizeof(fhdr), me.now  + 1);
+		}
+		me.page = 9999;
+	    }
+	    break;
+
+	case 'c':
+	    if(me.now < me.num) {
+		if(!isvisible_man(&me)) break;
+		sprintf(fname, "%s/%s", path,
+			me.header[me.now-me.page].filename);
+		a_copyitem(fname, me.header[me.now-me.page].title, 0, 1);
+		me.page = 9999;
+		break;
+	    }
+
+	case '\n':
+	case '\r':
+	case KEY_RIGHT:
+	case 'r':
+	    if(me.now < me.num) {
+		fileheader_t *fhdr = &me.header[me.now-me.page];
+		if(!isvisible_man(&me)) break;
+		sprintf(fname, "%s/%s", path, fhdr->filename);
+		if(*fhdr->filename == 'H' && fhdr->filename[1] == '.') {
+		    item_t item;
+		    strcpy(item.X.G.server, fhdr->filename + 2);
+		    strcpy(item.X.G.path, "1/");
+		    item.X.G.port = 70;
+		    gem(fhdr->title, &item, (ch == 'R') ? 1 : 0);
+		} else if (dashf(fname)) {
+		    int more_result;
+
+		    while((more_result = more(fname, YEA))) {
+			/* Ptt �d�����F plugin */
+			if(currstat == EDITEXP || currstat == OSONG) {
+			    char ans[4];
+
+			    move(22, 0);
+			    clrtoeol();
+			    getdata(22, 1, 
+				    currstat == EDITEXP ? 
+				    "�n��d�� Plugin ��峹��?[y/N]":
+				    "�T�w�n�I�o���q��?[y/N]",
+				    ans, 3, LCECHO);
+			    if(ans[0]=='y') {
+				strcpy(trans_buffer,fname);
+				Fexit = 1;
+				free(me.header);
+				if(currstat == OSONG){				
+				    log_file(FN_USSONG,fhdr->title);
+				}
+				return FULLUPDATE;
+			    }
+			}
+			if(more_result == 1) {
+			    if(--me.now < 0) {
+				me.now = 0;
+				break;
+			    }
+			} else if(more_result == 3) {
+			    if(++me.now >= me.num) {
+				me.now = me.num - 1;
+				break;
+			    }
+			} else
+			    break;
+			if(!isvisible_man(&me)) break;
+			sprintf(fname, "%s/%s", path,
+				me.header[me.now-me.page].filename);
+			if(!dashf(fname))
+			    break;
+		    }
+		} else if(dashd(fname)) {
+		    a_menu(me.header[me.now-me.page].title, fname, me.level);
+		    /* Ptt  �j�O���Xrecursive */
+		    if(Fexit) {
+			free(me.header);
+			return FULLUPDATE;
+		    }
+		}
+		me.page = 9999;
+	    }
+	    break;
+
+	case 'F':
+	case 'U':
+	    sprintf(fname, "%s/%s", path, me.header[me.now-me.page].filename);
+	    if(me.now < me.num && HAS_PERM(PERM_BASIC) && dashf(fname)) {
+		    a_forward(path, &me.header[me.now-me.page], ch /*== 'U'*/);
+		/*By CharlieL*/
+	    } else
+		outmsg("�L�k��H������");
+
+	    me.page = 9999;
+	    refresh();
+	    sleep(1);
+	    break;
+	}
+
+	if(me.level >= MANAGER) {
+	    int page0 = me.page;
+
+	    switch(ch) {
+	    case 'n':
+		a_newitem(&me, ADDITEM);
+		me.page = 9999;
+		break;
+	    case 'g':
+		a_newitem(&me, ADDGROUP);
+		me.page = 9999;
+		break;
+	    case 'G':
+		a_newitem(&me, ADDGOPHER);
+		me.page = 9999;
+		break;
+	    case 'p':
+		a_pasteitem(&me,1);
+		me.page = 9999;
+		break;
+	    case 'f':
+		a_editsign(&me);
+		me.page = 9999;
+		break;
+	    case Ctrl('P'):
+		a_pastetagpost(&me, -1);
+		returnvalue = DIRCHANGED;  
+		me.page = 9999;
+		break;
+	    case Ctrl('A'):
+		a_pastetagpost(&me, 1);
+		returnvalue = DIRCHANGED;
+		me.page = 9999;
+		break;
+	    case 'a':
+		a_appenditem(&me, 1);
+		me.page = 9999;
+		break;
+	    default:
+		me.page = page0;
+		break;
+	    }
+	    
+	    if(me.num)
+		switch(ch) {
+		case 'm':
+		    a_moveitem(&me);
+		    me.page = 9999;
+		    break;
+
+		case 'D':
+		    /* Ptt me.page = -1;*/
+		    a_delrange(&me);
+		    me.page = 9999;
+		    break;
+		case 'd':
+		    a_delete(&me);
+		    me.page = 9999;
+		    break;
+                case 'H':
+		    a_hideitem(&me);
+		    me.page = 9999;
+		    break;
+		case 'T':
+		    a_newtitle(&me);
+		    me.page = 9999;
+		    break;
+		}
+	}
+
+	if(me.level == SYSOP) {
+	    switch(ch) {
+	    case 'l':
+		a_newitem(&me, ADDLINK);
+		me.page = 9999;
+		break;
+	    case 'N':
+		a_showname(&me);
+		me.page = 9999;
+		break;
+	    }
+	}
+    }
+    free(me.header);
+    return returnvalue;
+}
+
+static char *mytitle = BBSNAME "�G�i��";
+
+int Announce() {
+    setutmpmode(ANNOUNCE);
+    a_menu(mytitle, "man",
+	   ((HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_ANNOUNCE)) ? SYSOP :
+	    NOBODY));
+    return 0;
+}
diff --git a/mbbsd/args.c b/mbbsd/args.c
new file mode 100644
index 00000000..4d1d6ceb
--- /dev/null
+++ b/mbbsd/args.c
@@ -0,0 +1,62 @@
+/* $Id: args.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#ifdef HAVE_SETPROCTITLE
+
+void initsetproctitle(int argc, char **argv, char **envp) {
+}
+
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+char **Argv = NULL;          /* pointer to argument vector */
+char *LastArgv = NULL;       /* end of argv */
+extern char **environ;
+
+void initsetproctitle(int argc, char **argv, char **envp) {
+    register int i;
+    
+    /* Move the environment so setproctitle can use the space at
+       the top of memory. */
+    for(i = 0; envp[i]; i++);
+    environ = malloc(sizeof(char *) * (i + 1));
+    for(i = 0; envp[i]; i++)
+	environ[i] = strdup(envp[i]);
+    environ[i] = NULL;
+    
+    /* Save start and extent of argv for setproctitle. */
+    Argv = argv;
+    if(i > 0)
+	LastArgv = envp[i - 1] + strlen(envp[i - 1]);
+    else
+	LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
+}
+
+static void do_setproctitle(const char *cmdline) {
+    char buf[256], *p;
+    int i;
+    
+    strncpy(buf, cmdline, 256);
+    buf[255] = '\0';
+    i = strlen(buf);
+    if(i > LastArgv - Argv[0] - 2) {
+	i = LastArgv - Argv[0] - 2;
+    }
+    strcpy(Argv[0], buf);
+    p = &Argv[0][i];
+    while(p < LastArgv)
+	*p++='\0';
+    Argv[1] = NULL;
+}
+
+void setproctitle(const char* format, ...) {
+    char buf[256];
+    va_list args;
+    va_start(args, format);
+    vsprintf(buf, format,args);
+    do_setproctitle(buf);
+    va_end(args);
+}
+#endif
diff --git a/mbbsd/bbcall.c b/mbbsd/bbcall.c
new file mode 100644
index 00000000..d7b2d33b
--- /dev/null
+++ b/mbbsd/bbcall.c
@@ -0,0 +1,268 @@
+/* $Id: bbcall.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+#define SERVER_0941     "www.chips.com.tw"
+#define SERVER_0943     "www.pager.com.tw"
+#define SERVER_0948     "www.fitel.net.tw"
+#define SERVER_0947     "www.hoyard.com.tw"
+#define SERVER_0945     "203.73.181.254"
+
+#define CGI_0948        "/cgi-bin/Webpage.dll"
+#define CGI_0941        "/cgi-bin/paging1.pl"
+#define CGI_0947        "/scripts/fp_page1.dll"
+#define CGI_0945        "/Scripts/fiss/PageForm.exe"
+#define CGI_0943        "/tpn/tpnasp/dowebcall.asp"
+
+#define CGI_RAILWAY     "http://www.railway.gov.tw/cgi-bin/timetk.cgi"
+
+#define REFER_0943      "http://www.pager.com.tw/tpn/webcall/webcall.asp"
+#define REFER_0948      "http://www.fitel.net.tw/html/svc03.htm"
+#define REFER_0941      "http://www.chips.com.tw:9100/WEB2P/page_1.htm"
+#define REFER_0947      "http://web1.hoyard.com.tw/freeway/freewayi.html"
+#define REFER_0945      "http://203.73.181.254/call.HTM"
+
+static void pager_msg_encode(char *field, char *buf) {
+    char *cc = field;
+    unsigned char *p;
+    
+    for(p = (unsigned char *)buf; *p; p++) {
+        if((*p >= '0' && *p <= '9') ||
+           (*p >= 'A' && *p <= 'Z') ||
+           (*p >= 'a' && *p <= 'z') ||
+           *p == ' ')
+            *cc++ = *p == ' ' ? '+' : (char)*p;
+        else {
+            sprintf(cc, "%%%02X", (int)*p);
+            cc += 3;
+        }
+    }
+    *cc = 0;
+}
+
+static void gettime(int flag, int *Year, int *Month, int *Day, int *Hour,
+             int *Minute) {
+    char ans[5];
+
+    do {
+        getdata(10, 0, "�~[20-]:", ans, 3, LCECHO);
+        *Year = atoi(ans);
+    } while(*Year < 00 || *Year > 02);
+    do {
+        getdata(10, 15, "��[1-12]:", ans, 3, LCECHO);
+    } while(!IsSNum(ans) || (*Month = atoi(ans)) > 12 || *Month < 1);
+    do {
+        getdata(10,30, "��[1-31]:", ans, 3, LCECHO);
+    } while(!IsSNum(ans) || (*Day = atoi(ans)) > 31 || *Day < 1);
+    do {
+        getdata(10,45, "��[0-23]:", ans, 3, LCECHO);
+    } while(!IsSNum(ans) || (*Hour = atoi(ans)) > 23 || *Hour < 0);
+    do {
+        getdata(10,60, "��[0-59]:", ans, 3, LCECHO);
+    } while(!IsSNum(ans) || (*Minute=atoi(ans))>59 || *Minute<0);
+    if(flag == 1)
+        *Year-=11;
+}
+
+#define hpressanykey(a) {move(22, 0); prints(a); pressanykey();}
+
+static int Connect(char *s, char *server) {
+    FILE *fp = fopen(BBSHOME "/log/bbcall.log", "a");
+    int sockfd;
+    char result[2048];
+    struct sockaddr_in serv_addr;
+    struct hostent *hp;
+
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if(sockfd < 0)
+        return 0;
+
+    memset((char *)&serv_addr, 0, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+
+    if((hp = gethostbyname(server)) == NULL)
+        return 0;
+
+    memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length);
+
+    if(!strcmp(server, SERVER_0941))
+        serv_addr.sin_port = htons(9100);
+    else
+        serv_addr.sin_port = htons(80);
+
+    if(connect(sockfd, (struct sockaddr *) &serv_addr, sizeof serv_addr)) {
+        hpressanykey("�L�k�P���A�����o�s���A�ǩI����");
+        return 0;
+    } else {
+        mprints(20, 0, "\033[1;33m���A���w�g�s���W�A�еy��"
+                ".....................\033[m");
+        refresh();
+    }
+
+    write(sockfd, s, strlen(s));
+    shutdown(sockfd, 1);
+
+    while(read(sockfd, result, sizeof(result)) > 0) {
+        fprintf(fp, "%s\n", result);
+        fflush(fp);
+        if(strstr(result, "���T") ||
+           strstr(result,"����") ||
+           strstr(result,"�A��") ||
+           strstr(result,"�� �� �^ ��") ||
+           strstr(result, "�w����") ||
+           strstr(result,"�ǰe��")) {
+            close(sockfd);
+            hpressanykey("���Q�e�X�ǩI");
+            return 0;
+        }
+        memset(result, 0, sizeof(result));
+    }
+    fclose(fp);
+    close(sockfd);
+    hpressanykey("�L�k���Q�e�X�ǩI");
+    return 0;
+}
+
+#define PARA \
+"Connection: Keep-Alive\r\n"\
+"User-Agent: Lynx/2.6  libwww-FM/2.14\r\n"\
+"Content-type: application/x-www-form-urlencoded\r\n"\
+"Accept: text/html, text/plain, application/x-wais-source, "\
+"application/html, */*;q=0.001\r\n"\
+"Accept-Encoding: gzip\r\n"\
+"Accept-Language: en\r\n"\
+"Accept-Charset: iso-8859-1,*,utf-8\r\n"
+
+static void halpha0943(char* CoId) {
+    char tmpbuf[64],ans[2];
+    char ID[8];
+    char Msg[64], atrn[512], sendform[1024];
+    int Year = 99, Month = 1, Day = 15, Hour = 13, Minute = 8;
+
+    sprintf(tmpbuf, "\033[1;37m�п�J�z�n�ǩI�����X\033[m : %s-", CoId);
+    if(!getdata(7,0, tmpbuf, ID, 7, LCECHO) ||
+       !getdata(8,0, "\033[1;37m�п�J�ǩI�T��\033[m�G", tmpbuf, 63, LCECHO)) {
+        hpressanykey("���ǩI");
+        return;
+    }
+    pager_msg_encode(Msg,tmpbuf);
+    getdata(9, 0, "\033[1;37m�p�G�A�n���W�e�� '1' "
+            "�p�G�n�w�ɰe�Ы� '2': \033[m", ans, 2, LCECHO);
+
+    if(ans[0] != '1')
+        gettime(0, &Year, &Month, &Day, &Hour, &Minute);
+
+    sprintf(atrn, "CoId=%s&ID=%s&Year=19%02d&Month=%02d&Day=%02d"
+            "&Hour=%02d&Minute=%02d&Msg=%s",
+            CoId, ID,Year,Month,Day,Hour,Minute,Msg);
+    sprintf(sendform, "POST %s HTTP/1.0\nReferer: "
+            "%s\n%sContent-length:%d\n\n%s",
+            CGI_0943, REFER_0943, PARA, strlen(atrn), atrn);
+    Connect(sendform, SERVER_0943);
+    return ;
+}
+
+static void hcall0941() {
+    char ans[2];
+    char PAGER_NO[8], TRAN_MSG[18], TIME[8];
+    char trn[512], sendform[512];
+    int  year = 98, month = 12, day = 4, hour = 13, min = 8;
+
+    if(!getdata(7, 0, "\033[1;37 �бz��J�z�n�ǩI�����X : 0941- \033[m",
+                PAGER_NO, 7, LCECHO)  ||
+       !getdata(8, 0, "\033[1;37m�п�J�ǩI�T��\033[m�G", trn, 17, LCECHO)) {
+        hpressanykey("���ǩI");
+        return;
+    }
+    pager_msg_encode(TRAN_MSG,trn);
+    getdata(9,0, "\033[1;37m�p�G�A�n���W�e�� '1' "
+            "�p�G�n�w�ɰe�Ы� '2': \033[m", ans, 2, LCECHO);
+    if(ans[0] != '1') {
+        strcpy(TIME,"DELAY");
+        gettime(0, &year, &month, &day, &hour, &min);
+    } else
+        strcpy(TIME,"NOW");
+    sprintf(trn,"PAGER_NO=%s&TRAN_MSG=%s&MSG_TYPE=NUMERIC&%s=1"
+            "&year=19%02d&month=%02d&day=%02d&hour=%02d&min=%02d",
+            PAGER_NO, TRAN_MSG, TIME,year,month,day,hour,min);
+
+    sprintf(sendform, "POST %s HTTP/1.0\nReferer: %s\n%s"
+            "Content-length:%d\n\n%s",
+            CGI_0941, REFER_0941, PARA, strlen(trn), trn);
+
+    Connect(sendform, SERVER_0941);
+    return ;
+}
+
+static void hcall0948() {
+    int  year = 87, month = 12, day = 19, hour = 12, min = 0, ya = 0;
+    char svc_no[8], message[64], trn[256], sendform[512], ans[3];
+
+    move(7,0);
+    clrtoeol();
+
+    if(!getdata(7, 0, "\033[1;37m�п�J�z�n�ǩI�����X\033[m�G0948-",
+                svc_no, 7, LCECHO) ||
+       !getdata(8, 0, "\033[1;37m�п�J�ǩI�T��\033[m�G", trn, 61, LCECHO)) {
+        hpressanykey("���ǩI");
+        return;
+    }
+    pager_msg_encode(message, trn);
+    getdata(9, 0, "\033[1;37m�p�G�A�n���W�e�� '1' "
+            "�p�G�n�w�ɰe�Ы� '2'\033[m: ", ans, 2, LCECHO);
+    if(ans[0] != '1') {
+        gettime(1, &year, &month, &day, &hour, &min);
+        ya = 1;
+    }
+
+    sprintf(trn, "MfcISAPICommand=SinglePage&svc_no=%s&reminder=%d"
+            "&year=%02d&month=%02d&day=%02d&hour=%02d&min=%02d&message=%s",
+            svc_no, ya, year, month, day, hour, min, message);
+
+    sprintf(sendform, "GET %s?%s Http/1.0\n\n", CGI_0948, trn);
+
+    Connect(sendform, SERVER_0948);
+    return;
+}
+
+int main_bbcall() {
+    char ch[2];
+
+    clear();
+    move(0, 30);
+    prints("\033[1;37;45m �G�G��� \033[m");
+    move(3, 0);
+    prints("\033[1;31m     �z�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+           "�w�w�w�w�w�w�w�w�w�{\033[m\n");
+    prints("\033[1;33m        (1)0941        (2)0943        (3)0946  "
+           "       (4)0948     \033[m\n");
+    prints("\033[1;31m     �|�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+           "�w�w�w�w�w�w�w�w�w�}\033[m\n");
+    getdata(7, 8, "\033[1;37m�A�����? [1-4]\033[m", ch, 2, LCECHO);
+
+    switch(ch[0]) {
+    case '1':
+        hcall0941();
+        break;
+    case '2':
+        halpha0943("0943");
+        break;
+    case '3':
+        halpha0943("0946");
+        break;
+    case '4':
+        hcall0948();
+        break;
+    }
+    return 0;
+}
diff --git a/mbbsd/bbs.c b/mbbsd/bbs.c
new file mode 100644
index 00000000..15db1c91
--- /dev/null
+++ b/mbbsd/bbs.c
@@ -0,0 +1,1904 @@
+/* $Id: bbs.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "modes.h"
+#include "common.h"
+#include "proto.h"
+
+static int g_board_names(boardheader_t *fhdr) {
+    AddNameList(fhdr->brdname);
+    return 0;
+}
+
+extern userec_t cuser;
+extern void touchdircache(int bid);
+extern int TagNum;
+
+static void mail_by_link(char* owner, char* title, char* path) {
+    char genbuf[200];
+    fileheader_t mymail;     
+    
+    sprintf(genbuf,BBSHOME"/home/%c/%s", cuser.userid[0], cuser.userid);
+    stampfile(genbuf, &mymail);
+    strcpy(mymail.owner, owner);
+    sprintf(mymail.title, title); 
+    mymail.savemode = 0;
+    unlink(genbuf);
+    Link(path, genbuf);
+    sprintf(genbuf,BBSHOME"/home/%c/%s/.DIR",cuser.userid[0],cuser.userid); 
+                                                                  
+    append_record(genbuf, &mymail, sizeof(mymail));      
+}
+
+extern int usernum;
+
+void anticrosspost() {
+    char buf[200];
+    time_t now = time(NULL);
+    
+    sprintf(buf,
+	    "\033[1;33;46m%s \033[37;45mcross post �峹 \033[37m %s\033[m",
+	    cuser.userid, ctime(&now));
+    log_file("etc/illegal_money", buf);
+    
+    post_violatelaw(cuser.userid, "Ptt�t��ĵ��", "Cross-post", "�@��B��");    
+    cuser.userlevel |= PERM_VIOLATELAW;
+    cuser.vl_count ++;
+    mail_by_link("Pttĵ���", "Cross-Post�@��",
+		 BBSHOME "/etc/crosspost.txt");
+    passwd_update(usernum, &cuser);
+    exit(0);
+}
+
+/* Heat CharlieL*/
+int save_violatelaw() {
+    char buf[128], ok[3];
+    
+    setutmpmode(VIOLATELAW); 
+    clear();
+    stand_title("ú�@�椤��");
+
+    if(!(cuser.userlevel & PERM_VIOLATELAW)) {
+	mprints(22, 0, "\033[1;31m�A�L���? �A�S�S���Q�}�@��~~\033[m");
+	pressanykey();
+	return 0;
+    }
+  
+    reload_money();
+    if(cuser.money < (int)cuser.vl_count*1000) {
+	sprintf(buf, "\033[1;31m�o�O�A�� %d ���H�ϥ����k�W"
+		"����ú�X %d $Ptt ,�A�u�� %d ��, ��������!!\033[m",
+		(int)cuser.vl_count, (int)cuser.vl_count * 1000, cuser.money);
+	mprints(22, 0, buf);
+	pressanykey();
+	return 0;
+    }
+                            
+    move(5,0);
+    prints("\033[1;37m�A���D��? �]���A���H�k "
+	   "�w�g�y���ܦh�H�����K\033[m\n");
+    prints("\033[1;37m�A�O�_�T�w�H�ᤣ�|�A�ǤF�H\033[m\n");
+    
+    if(!getdata(10,0,"�T�w�ܡH[y/n]:", ok, 2, LCECHO) ||
+       ok[0] == 'n' || ok[0] == 'N') {
+	mprints(22,0,"\033[1;31m���A�Q�q�F�A�ӧa!! "
+		"�ڬ۫H�A���|�������諸~~~\033[m");
+	pressanykey();
+	return 0;
+    }
+
+    sprintf(buf, "�o�O�A�� %d ���H�k ����ú�X %d $Ptt",
+	    cuser.vl_count, cuser.vl_count*1000);
+    mprints(11,0,buf);
+
+    if(!getdata(10, 0, "�n�I��[y/n]:", ok, 2, LCECHO) || 
+       ok[0] == 'N' || ok[0] == 'n') {
+
+	mprints(22,0, "\033[1;31m �� �s���� �A�ӧa!!!\033[m");
+	pressanykey();
+	return 0;
+    }
+    
+    demoney(-1000*cuser.vl_count);
+    cuser.userlevel &= (~PERM_VIOLATELAW);
+    passwd_update(usernum, &cuser);
+    return 0;
+}
+
+void make_blist() {
+    CreateNameList();
+    apply_boards(g_board_names);
+}
+
+extern int currbid;
+extern char currBM[];
+extern int currmode;
+extern char currboard[];
+static time_t board_note_time;
+static char *brd_title;
+
+void set_board() {
+    boardheader_t *bp;
+
+    bp = getbcache(currbid);
+    board_note_time = bp->bupdate;
+    brd_title = bp->BM;
+    if(brd_title[0] <= ' ')
+	brd_title = "�x�D��";
+    sprintf(currBM, "�O�D�G%s", brd_title);
+    brd_title = ((bp->bvote != 2 && bp->bvote) ? "���ݪO�i��벼��" :
+		 bp->title + 7);
+    currmode = (currmode & (MODE_DIRTY | MODE_MENU)) | MODE_STARTED ;
+		 
+    if (HAS_PERM(PERM_ALLBOARD) || is_BM(bp->BM))
+	currmode = currmode | MODE_BOARD | MODE_POST;
+    else if(haspostperm(currboard))
+    	currmode |= MODE_POST;
+}
+
+static void readtitle() {
+    showtitle(currBM, brd_title);
+    outs("[��]���} [��]�\\Ū [^P]�o���峹 [b]�Ƨѿ� [d]�R�� [z]��ذ� "
+	 "[TAB]��K [h]elp\n\033[7m  �s��   �� ��  �@  ��       ��  ��  ��  �D"
+	 "                                   \033[m");
+}
+
+extern int brc_num;
+extern int brc_list[];
+extern char currtitle[];
+
+extern int Tagger();
+
+static void readdoent(int num, fileheader_t *ent) {
+    int type;
+    char *mark, *title, color;
+    
+    type = brc_unread(ent->filename,brc_num,brc_list) ? '+' : ' ';
+    
+    if((currmode & MODE_BOARD) && (ent->filemode & FILE_DIGEST))
+	type = (type == ' ') ? '*' : '#';
+    else if(currmode & MODE_BOARD || HAS_PERM(PERM_LOGINOK)) {
+	if(ent->filemode & FILE_MARKED)
+	    type = (type == ' ') ? 'm' : 'M';
+
+        else if (TagNum && !Tagger(atoi(ent->filename + 2), 0, TAG_NIN))
+            type = 'D';
+
+	else if (ent->filemode & FILE_SOLVED)
+	    type = 's';
+    }
+    
+    title = subject(mark = ent->title);
+    if(title == mark)
+	color = '1', mark = "��";
+    else
+	color = '3', mark = "R:";
+    
+    if(title[47])
+	strcpy(title + 44, " �K");  /* ��h�l�� string �屼 */
+
+    if(strncmp(currtitle, title, 40))
+	prints("%6d %c %-7s%-13.12s%s %s\n", num, type,
+	       ent->date, ent->owner, mark, title);
+    else
+	prints("%6d %c %-7s%-13.12s\033[1;3%cm%s %s\033[m\n", num, type,
+	       ent->date, ent->owner, color, mark, title);
+}
+
+extern char currfile[];
+
+int cmpfilename(fileheader_t *fhdr) {
+    return (!strcmp(fhdr->filename, currfile));
+}
+
+extern unsigned char currfmode;
+
+int cmpfmode(fileheader_t *fhdr) {
+    return (fhdr->filemode & currfmode);
+}
+
+extern char currowner[];
+
+int cmpfowner(fileheader_t *fhdr) {
+    return !strcasecmp(fhdr->owner, currowner);
+}
+
+extern char *err_bid;
+extern userinfo_t *currutmp;
+
+int whereami(int ent, fileheader_t *fhdr, char *direct) {
+    boardheader_t *bh, *p[32], *root;
+    int i,j;
+
+    if(!currutmp->brc_id) return 0;
+
+    move(1,0);
+    clrtobot();
+    bh=getbcache(currutmp->brc_id); 
+    root=getbcache(1);
+    p[0]=bh;
+    for(i=0;i<31 && p[i]->parent!=root && p[i]->parent;i++)
+         	p[i+1]=p[i]->parent;
+    j=i;
+    prints("�ڦb��?\n%-40.40s %.13s\n", p[j]->title+7, p[j]->BM);
+    for(j--;j>=0;j--)
+	   prints("%*s %-13.13s %-37.37s %.13s\n", (i-j)*2, "",
+			   p[j]->brdname, p[j]->title, 
+			    p[j]->BM);
+
+    pressanykey();
+    return FULLUPDATE;
+}
+static int do_select(int ent, fileheader_t *fhdr, char *direct) {
+    char bname[20];
+    char bpath[60];
+    boardheader_t *bh;
+    struct stat st;
+    int i; 
+
+    move(0, 0);
+    clrtoeol();
+    make_blist();
+    namecomplete(MSG_SELECT_BOARD, bname);
+    if(bname[0]=='\0' || !(i = getbnum(bname)))
+       return FULLUPDATE;
+    bh = getbcache(i);
+    if(!Ben_Perm(bh))  return FULLUPDATE; 
+    strcpy(bname, bh->brdname);
+    currbid=i;
+    
+    setbpath(bpath, bname);
+    if((*bname == '\0') || (stat(bpath, &st) == -1)) {
+	move(2, 0);
+	clrtoeol();
+	outs(err_bid);
+	return FULLUPDATE;
+    }
+    
+    currutmp->brc_id = currbid;
+
+    brc_initial(bname);
+    set_board();
+    setbdir(direct, currboard);
+
+    move(1, 0);
+    clrtoeol();
+    return NEWDIRECT;
+}
+
+/* ----------------------------------------------------- */
+/* ��} innbbsd ��X�H��B�s�u��H���B�z�{��             */
+/* ----------------------------------------------------- */
+void outgo_post(fileheader_t *fh, char *board) {
+    FILE *foo;
+    
+    if((foo = fopen("innd/out.bntp", "a"))) {
+	fprintf(foo, "%s\t%s\t%s\t%s\t%s\n", board,
+		fh->filename, cuser.userid, cuser.username, fh->title);
+	fclose(foo);
+    }
+}
+
+extern char *str_author1;
+extern char *str_author2;
+
+static void cancelpost(fileheader_t *fh, int by_BM) {
+    FILE *fin, *fout;
+    char *ptr, *brd;
+    fileheader_t postfile;
+    char genbuf[200];
+    char nick[STRLEN], fn1[STRLEN], fn2[STRLEN];
+    
+    setbfile(fn1, currboard, fh->filename);
+    if((fin = fopen(fn1, "r"))) {
+	brd = by_BM ? "deleted" : "junk";
+
+	setbpath(fn2, brd);
+	stampfile(fn2, &postfile);
+	memcpy(postfile.owner, fh->owner, IDLEN + TTLEN + 10);
+	postfile.savemode = 'D';
+
+	if(fh->savemode == 'S') {
+	    nick[0] = '\0';
+	    while(fgets(genbuf, sizeof(genbuf), fin)) {
+		if (!strncmp(genbuf, str_author1, LEN_AUTHOR1) ||
+		    !strncmp(genbuf, str_author2, LEN_AUTHOR2)) {
+		    if((ptr = strrchr(genbuf, ')')))
+			*ptr = '\0';
+		    if((ptr = (char *)strchr(genbuf, '(')))
+			strcpy(nick, ptr + 1);
+		    break;
+		}
+	    }
+
+	    if((fout = fopen("innd/cancel.bntp", "a"))) {
+		fprintf(fout, "%s\t%s\t%s\t%s\t%s\n", currboard, fh->filename,
+			cuser.userid, nick, fh->title);
+		fclose(fout);
+	    }
+	}
+	
+	fclose(fin);
+	Rename(fn1, fn2);
+	setbdir(genbuf, brd);
+	append_record(genbuf, &postfile, sizeof(postfile));
+    }
+}
+
+extern char *str_reply;
+extern char save_title[];
+
+/* ----------------------------------------------------- */
+/* �o���B�^���B�s��B����峹                            */
+/* ----------------------------------------------------- */
+void do_reply_title(int row, char *title) {
+    char genbuf[200];
+    char genbuf2[4];
+
+    if(strncasecmp(title, str_reply, 4))
+	sprintf(save_title, "Re: %s", title);
+    else
+	strcpy(save_title, title);
+    save_title[TTLEN - 1] = '\0';
+    sprintf(genbuf, "�ĥέ���D�m%.60s�n��?[Y] ", save_title);
+    getdata(row, 0, genbuf, genbuf2, 4, LCECHO);
+    if(genbuf2[0] == 'n' || genbuf2[0] == 'N')
+	getdata(++row, 0, "���D�G", save_title, TTLEN, DOECHO);
+}
+
+static void do_unanonymous_post(char* fpath) {
+    fileheader_t mhdr;
+    char title[128];
+    char genbuf[200];
+
+    setbpath(genbuf, "UnAnonymous");
+    if(dashd(genbuf)) {
+	stampfile(genbuf, &mhdr);
+	unlink(genbuf);
+	Link(fpath, genbuf);
+	strcpy(mhdr.owner, cuser.userid);
+	strcpy(mhdr.title, save_title);
+	mhdr.savemode = 0;
+	mhdr.filemode = 0;
+	setbdir(title, "UnAnonymous");
+	append_record(title, &mhdr, sizeof(mhdr));
+    }
+}
+
+extern char quote_file[];
+extern char quote_user[];
+extern int curredit;
+extern unsigned int currbrdattr;
+extern char currdirect[];
+extern char *err_uid;
+
+#ifdef NO_WATER_POST
+static time_t last_post_time = 0;
+static time_t water_counts = 0;
+#endif
+int local_article;
+char real_name[20];
+
+static int do_general() {
+    fileheader_t postfile;
+    char fpath[80], buf[80];
+    int aborted, defanony, ifuseanony;
+    char genbuf[200],*owner;
+    boardheader_t *bp;
+    int islocal;
+    
+    ifuseanony = 0;
+    bp = getbcache(currbid);
+    
+    clear();
+    if(!(currmode & MODE_POST)) {
+	move(5, 10);
+	outs("�藍�_�A�z�ثe�L�k�b���o���峹�I");
+	pressanykey();
+	return FULLUPDATE;
+    }
+
+#ifdef NO_WATER_POST
+    /* �T�������̦h�o�����g�峹 */
+    if(currutmp->lastact - last_post_time < 60 * 3) {
+	if(water_counts >= 5) {
+	    move(5, 10);
+	    outs("�藍�_�A�z���峹�Ӥ��o�A�h��Ҥ@�U�A�ݷ|�Apost�a�I");
+	    pressanykey();
+	    return FULLUPDATE;
+        }
+    } else {
+	last_post_time = currutmp->lastact;
+	water_counts = 0;
+    }
+#endif
+    
+    setbfile(genbuf, currboard, FN_POST_NOTE );
+    
+    if(more(genbuf,NA) == -1)
+	more("etc/"FN_POST_NOTE , NA);
+    
+    move(19,0);
+    prints("�o���峹��i\033[33m %s\033[m �j \033[32m%s\033[m �ݪO\n\n",
+	   currboard, bp->title + 7);
+    
+    if(quote_file[0])
+	do_reply_title(20, currtitle);
+    else {
+	getdata(21, 0, "���D�G", save_title, TTLEN, DOECHO);
+	strip_ansi(save_title,save_title,0);
+    }
+    if(save_title[0] == '\0')
+	return FULLUPDATE;
+    
+    curredit &= ~EDIT_MAIL;
+    curredit &= ~EDIT_ITEM;
+    setutmpmode(POSTING);
+    
+    /* ����� Internet �v���̡A�u��b�����o���峹 */
+    if(HAS_PERM(PERM_INTERNET))
+	local_article = 0;
+    else
+	local_article = 1;
+
+    /* build filename */
+    setbpath(fpath, currboard);
+    stampfile(fpath, &postfile);
+    
+    aborted = vedit(fpath, YEA, &islocal);
+    if(aborted == -1) {
+	unlink(fpath);
+	pressanykey();
+	return FULLUPDATE;
+    }
+    water_counts++;  /* po���\ */
+
+    /* set owner to Anonymous , for Anonymous board */
+
+#ifdef HAVE_ANONYMOUS
+    /* Ptt and Jaky */
+    defanony=currbrdattr & BRD_DEFAULTANONYMOUS;
+    if((currbrdattr & BRD_ANONYMOUS) && 
+       ((strcmp(real_name,"r") && defanony) || (real_name[0] && !defanony)) 
+	) {
+	strcat(real_name,".");
+	owner = real_name;
+	ifuseanony=1;
+    } else
+	owner = cuser.userid;
+#else
+    owner = cuser.userid;
+#endif
+    /* �� */
+    aborted = (aborted > MAX_POST_MONEY * 2) ? MAX_POST_MONEY : aborted / 2;
+    postfile.money = aborted;
+    strcpy(postfile.owner, owner);
+    strcpy(postfile.title, save_title);
+    if(islocal) {            /* local save */
+	postfile.savemode = 'L';
+	postfile.filemode = FILE_LOCAL;
+    } else
+	postfile.savemode = 'S';
+    
+    setbdir(buf, currboard);
+    if(append_record(buf, &postfile, sizeof(postfile)) != -1) {
+	setbtotal(currbid);
+
+	if(currmode & MODE_SELECT)
+	    append_record(currdirect,&postfile,sizeof(postfile));
+	if(!islocal && !(bp->brdattr & BRD_NOTRAN))
+	    outgo_post(&postfile, currboard);
+	brc_addlist(postfile.filename);
+
+	if(!(currbrdattr  & BRD_HIDE) &&
+	   (!bp->level || (currbrdattr  & BRD_POSTMASK))) {
+	    setbpath(genbuf, ALLPOST);
+	    stampfile(genbuf, &postfile);
+	    unlink(genbuf);
+
+	    /* jochang: boards may spread across many disk */
+	    /* link doesn't work across device,
+	       Link doesn't work if we have same-time-across-device posts,
+	       we try symlink now */
+	    {
+	    	/* we need absolute path for symlink */
+	    	char abspath[256]=BBSHOME"/";
+	    	strcat(abspath,fpath);
+	    	symlink(abspath,genbuf);
+	    }
+	    strcpy(postfile.owner, owner);
+	    strcpy(postfile.title, save_title);
+	    postfile.savemode = 'L';
+	    setbdir(genbuf, ALLPOST);
+	    if(append_record(genbuf, &postfile, sizeof(postfile)) != -1) {
+		setbtotal(getbnum(ALLPOST));
+	    }
+	}
+	
+	outs("���Q�K�X�G�i�A");
+	
+#ifdef MAX_POST_MONEY
+	aborted = (aborted > MAX_POST_MONEY) ? MAX_POST_MONEY : aborted;
+#endif
+	if(strcmp(currboard, "Test") && !ifuseanony) {
+	    prints("�o�O�z���� %d �g�峹�C �Z�S %d �ȡC",
+		   ++cuser.numposts, aborted );
+	    demoney(aborted);
+            passwd_update(usernum, &cuser); /* post �� */
+	} else
+	    outs("���իH�󤣦C�J�����A�q�Х]�[�C");
+	
+	/* �^�����@�̫H�c */
+	
+	if(curredit & EDIT_BOTH) {
+	    char *str, *msg = "�^���ܧ@�̫H�c";
+
+	    if((str = strchr(quote_user, '.'))) {
+		if(
+#ifndef USE_BSMTP
+		    bbs_sendmail(fpath, save_title, str + 1)
+#else
+		    bsmtp(fpath, save_title, str + 1 ,0)
+#endif
+		    < 0)
+		    msg = "�@�̵L�k���H";
+	    } else {
+		sethomepath(genbuf, quote_user);
+		stampfile(genbuf, &postfile);
+		unlink(genbuf);
+		Link(fpath, genbuf);
+
+		strcpy(postfile.owner, cuser.userid);
+		strcpy(postfile.title, save_title);
+		postfile.savemode = 'B';/* both-reply flag */
+		sethomedir(genbuf, quote_user);
+		if(append_record(genbuf, &postfile, sizeof(postfile)) == -1)
+		    msg = err_uid;
+	    }
+	    outs(msg);
+	    curredit ^= EDIT_BOTH;
+	}
+	if(currbrdattr & BRD_ANONYMOUS)
+	    do_unanonymous_post(fpath);
+    }
+    pressanykey();
+    return FULLUPDATE;
+}
+
+int do_post() {
+    boardheader_t *bp;
+    bp = getbcache(currbid);
+    if(bp->brdattr & BRD_VOTEBOARD)
+        return do_voteboard();
+    else if(!(bp->brdattr & BRD_GROUPBOARD))
+        return do_general();
+    touchdircache(currbid);
+    return 0;
+}
+
+extern int b_lines;
+extern int curredit;
+
+static void do_generalboardreply(fileheader_t *fhdr){            
+    char genbuf[200];
+    getdata(b_lines - 1, 0,
+	    "�� �^���� (F)�ݪO (M)�@�̫H�c (B)�G�̬ҬO (Q)�����H[F] ",
+	    genbuf, 3, LCECHO);
+    switch(genbuf[0]) {
+    case 'm':
+	mail_reply(0, fhdr, 0);
+    case 'q':
+	break;
+
+    case 'b':
+	curredit = EDIT_BOTH;
+    default:
+	strcpy(currtitle, fhdr->title);
+	strcpy(quote_user, fhdr->owner);
+	quote_file[79] = fhdr->savemode;
+	do_post();
+    }
+    *quote_file = 0;
+}
+
+int getindex(char *fpath, char *fname, int size) {
+    int fd, now=0;
+    fileheader_t fhdr;
+    
+    if((fd = open(fpath, O_RDONLY, 0)) != -1) {
+	while((read(fd, &fhdr, size) == size)) {
+	    now++;
+	    if(!strcmp(fhdr.filename,fname)) {
+		close(fd);
+		return now;
+	    }
+	}
+	close(fd);
+    }
+    return 0;
+}
+
+int invalid_brdname(char *brd) {
+    register char ch;
+    
+    ch = *brd++;
+    if(not_alnum(ch))
+	return 1;
+    while((ch = *brd++)) {
+	if(not_alnum(ch) && ch != '_' && ch != '-' && ch != '.')
+	    return 1;
+    }
+    return 0;
+}       
+
+static void do_reply(fileheader_t *fhdr) {
+    boardheader_t *bp;
+    bp = getbcache(currbid);
+    if (bp->brdattr & BRD_VOTEBOARD)
+        do_voteboardreply(fhdr);
+    else
+        do_generalboardreply(fhdr);
+}
+
+static int reply_post(int ent, fileheader_t *fhdr, char *direct) {
+    if(!(currmode & MODE_POST))
+	return DONOTHING;
+
+    setdirpath(quote_file, direct, fhdr->filename);
+    do_reply(fhdr);
+    *quote_file = 0;
+    return FULLUPDATE;
+}
+
+static int edit_post(int ent, fileheader_t *fhdr, char *direct) {
+    char fpath[80], fpath0[80];
+    char genbuf[200];
+    fileheader_t postfile;
+    boardheader_t *bp;
+    bp = getbcache(currbid);
+    if (!HAS_PERM(PERM_SYSOP) && (bp->brdattr & BRD_VOTEBOARD))
+	return DONOTHING;
+
+    if ((!HAS_PERM(PERM_SYSOP)) && 
+        strcmp(fhdr->owner, cuser.userid))
+	return DONOTHING;
+    setutmpmode(REEDIT); 
+    setdirpath(genbuf, direct, fhdr->filename);
+    local_article = fhdr->filemode & FILE_LOCAL;
+    strcpy(save_title, fhdr->title);
+
+/* rocker.011018: �o�̬O���O���ˬd�@�U�ק�峹�᪺money�M�즳�����? */
+    if(vedit(genbuf, 0, NULL) != -1) {
+	setbpath(fpath, currboard);
+	stampfile(fpath, &postfile);
+	unlink(fpath);
+	setbfile(fpath0, currboard, fhdr->filename);
+
+	Rename(fpath0, fpath);
+
+/* rocker.011018: fix �걵�Ҧ���峹��峹�N������bug */
+	if ((currmode & MODE_SELECT) && (fhdr->money & FHR_REFERENCE))
+	{
+	  fileheader_t hdr;
+	  int num;
+
+	  num = fhdr->money & ~FHR_REFERENCE;
+	  setbdir(fpath0, currboard);
+          get_record(fpath0, &hdr, sizeof (hdr), num);
+
+	  /* �A�o�̭ncheck�@�U��Ӫ�dir�̭��O���O���Q�H�ʹL... */
+	  if (!strcmp (hdr.filename, fhdr->filename))
+	  {
+	    strcpy(hdr.filename, postfile.filename);
+	    strcpy(hdr.title, save_title);
+	    substitute_record(fpath0, &hdr, sizeof(hdr), num);
+	  }
+	}
+
+	strcpy(fhdr->filename, postfile.filename);
+	strcpy(fhdr->title, save_title);
+	brc_addlist(postfile.filename);
+	substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+/* rocker.011018: ���K��s�@�Ucache */
+        touchdircache(currbid); 
+    }
+    return FULLUPDATE;
+}
+
+extern crosspost_t postrecord;
+#define UPDATE_USEREC   (currmode |= MODE_DIRTY)
+
+static int cross_post(int ent, fileheader_t *fhdr, char *direct) {
+    char xboard[20], fname[80], xfpath[80], xtitle[80], inputbuf[10];
+    fileheader_t xfile;
+    FILE *xptr;
+    int author = 0;
+    char genbuf[200];
+    char genbuf2[4];
+    boardheader_t *bp;
+    make_blist();
+    move(2, 0);
+    clrtoeol();
+    move(3, 0);
+    clrtoeol();
+    move(1, 0);
+    bp = getbcache(currbid);
+    if (bp && (bp->brdattr & BRD_VOTEBOARD))
+	return FULLUPDATE;
+    namecomplete("������峹��ݪO�G", xboard);
+    if(*xboard == '\0' || !haspostperm(xboard))
+	return FULLUPDATE;
+	
+    if((ent = str_checksum(fhdr->title)) != 0 &&
+       ent == postrecord.checksum[0]) {
+	/* �ˬd cross post ���� */
+	if(postrecord.times++ > MAX_CROSSNUM)
+	    anticrosspost();
+    } else {
+	postrecord.times = 0;
+	postrecord.checksum[0] = ent;
+    }
+
+    ent = 1;
+    if(HAS_PERM(PERM_SYSOP) || !strcmp(fhdr->owner, cuser.userid)) {
+	getdata(2, 0, "(1)������ (2)������榡�H[1] ",
+		genbuf, 3, DOECHO);
+	if(genbuf[0] != '2') {
+	    ent = 0;
+	    getdata(2, 0, "�O�d��@�̦W�ٶ�?[Y] ", inputbuf, 3, DOECHO);
+	    if (inputbuf[0] != 'n' && inputbuf[0] != 'N') author = 1;
+	}
+    }
+
+    if(ent)
+	sprintf(xtitle, "[���]%.66s", fhdr->title);
+    else
+	strcpy(xtitle, fhdr->title);
+
+    sprintf(genbuf, "�ĥέ���D�m%.60s�n��?[Y] ", xtitle);
+    getdata(2, 0, genbuf, genbuf2, 4, LCECHO);
+    if(genbuf2[0] == 'n' || genbuf2[0] == 'N') {
+	if(getdata_str(2, 0, "���D�G", genbuf, TTLEN, DOECHO,xtitle))
+	    strcpy(xtitle, genbuf);
+    }
+    
+    getdata(2, 0, "(S)�s�� (L)���� (Q)�����H[Q] ", genbuf, 3, LCECHO);
+    if(genbuf[0] == 'l' || genbuf[0] == 's') {
+	int currmode0 = currmode;
+
+	currmode = 0;
+	setbpath(xfpath, xboard);
+	stampfile(xfpath, &xfile);
+	if(author)
+	    strcpy(xfile.owner, fhdr->owner);
+	else
+	    strcpy(xfile.owner, cuser.userid);
+	strcpy(xfile.title, xtitle);
+	if(genbuf[0] == 'l') {
+	    xfile.savemode = 'L';
+	    xfile.filemode = FILE_LOCAL;
+	} else
+	    xfile.savemode = 'S';
+
+	setbfile(fname, currboard, fhdr->filename);
+//	if(ent)	{
+	xptr = fopen(xfpath, "w");
+
+	strcpy(save_title, xfile.title);
+	strcpy(xfpath, currboard);
+	strcpy(currboard, xboard);
+	write_header(xptr);
+	strcpy(currboard, xfpath);
+
+	fprintf(xptr, "�� [��������� %s �ݪO]\n\n", currboard);
+
+	b_suckinfile(xptr, fname);
+	addsignature(xptr,0);
+	fclose(xptr);
+/* Cross fs�����D
+   } else {
+   unlink(xfpath);
+   link(fname, xfpath);
+   }
+*/
+	setbdir(fname, xboard);
+	append_record(fname, &xfile, sizeof(xfile));
+	bp = getbcache(getbnum(xboard));
+	if(!xfile.filemode && !(bp->brdattr && BRD_NOTRAN))
+	    outgo_post(&xfile, xboard);
+	setbtotal(getbnum(xboard));
+	cuser.numposts++;
+	UPDATE_USEREC;
+	outs("�峹�������");
+	pressanykey();
+	currmode = currmode0;
+    }
+    return FULLUPDATE;
+}
+
+static int read_post(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[200];
+    int more_result;
+
+    if(fhdr->owner[0] == '-')
+	return DONOTHING;
+
+    setdirpath(genbuf, direct, fhdr->filename);
+
+    if((more_result = more(genbuf, YEA)) == -1)
+	return DONOTHING;
+
+    brc_addlist(fhdr->filename);
+    strncpy(currtitle, subject(fhdr->title), 40);
+    strncpy(currowner, subject(fhdr->owner), IDLEN + 2);
+
+    switch (more_result) {
+    case 1:
+	return READ_PREV;
+    case 2:
+	return RELATE_PREV;
+    case 3:
+	return READ_NEXT;
+    case 4:
+	return RELATE_NEXT;
+    case 5:
+	return RELATE_FIRST;
+    case 6:
+	return FULLUPDATE;
+    case 7:
+    case 8:
+	if((currmode & MODE_POST)) {
+	    strcpy(quote_file, genbuf);
+	    do_reply(fhdr);
+	    *quote_file = 0;
+	}
+	return FULLUPDATE;
+    case 9:
+	return 'A';
+    case 10:
+	return 'a';
+    case 11:
+	return '/';
+    case 12:
+	return '?';
+    }
+
+
+    outmsg("\033[34;46m  �\\Ū�峹  \033[31;47m  (R/Y)\033[30m�^�H \033[31m"
+	   "(=[]<>)\033[30m�����D�D \033[31m(����)\033[30m�W�U�� \033[31m(��)"
+	   "\033[30m���}  \033[m");
+
+    switch(egetch()) {
+    case 'q':
+    case 'Q':
+    case KEY_LEFT:
+	break;
+
+    case ' ':
+    case KEY_RIGHT:
+    case KEY_DOWN:
+    case KEY_PGDN:
+    case 'n':
+    case Ctrl('N'):
+	return READ_NEXT;
+
+    case KEY_UP:
+    case 'p':
+    case Ctrl('P'):
+    case KEY_PGUP:
+	return READ_PREV;
+
+    case '=':
+	return RELATE_FIRST;
+
+    case ']':
+    case 't':
+	return RELATE_NEXT;
+
+    case '[':
+	return RELATE_PREV;
+
+    case '.':
+    case '>':
+	return THREAD_NEXT;
+
+    case ',':
+    case '<':
+	return THREAD_PREV;
+
+    case Ctrl('C'):
+	cal();
+	return FULLUPDATE;
+	break;
+
+    case Ctrl('I'):
+	t_idle();
+	return FULLUPDATE;
+    case 'y':
+    case 'r':
+    case 'R':
+    case 'Y':
+	if((currmode & MODE_POST)) {
+	    strcpy(quote_file, genbuf);
+	    do_reply(fhdr);
+	    *quote_file = 0;
+	}
+    }
+    return FULLUPDATE;
+}
+
+/* ----------------------------------------------------- */
+/* �Ķ���ذ�                                            */
+/* ----------------------------------------------------- */
+static int b_man() {
+    char buf[64];
+    
+    setapath(buf, currboard);
+    if( (currmode & MODE_BOARD) || HAS_PERM(PERM_SYSOP) ){
+        char    genbuf[128];
+        int     fd;
+        sprintf(genbuf, "%s/.rebuild", buf);
+        if( (fd = open(genbuf, O_CREAT, 0640)) > 0 )
+            close(fd);
+    }
+    return a_menu(currboard, buf, HAS_PERM(PERM_ALLBOARD) ? 2 :
+		  (currmode & MODE_BOARD ? 1 : 0));
+}
+
+#ifndef NO_GAMBLE
+static int join_gamble(int ent, fileheader_t *fhdr, char *direct) {
+   ticket(currbid);
+   return FULLUPDATE; 
+}
+static int hold_gamble(int ent, fileheader_t *fhdr, char *direct) { 
+   char fn_ticket[128],fn_ticket_end[128],genbuf[128],
+        msg[256]="",yn[10]="";
+   int i;
+   FILE *fp=NULL;
+
+   if(!(currmode & MODE_BOARD)) return 0;
+   setbfile(fn_ticket, currboard, FN_TICKET);
+   setbfile(fn_ticket_end, currboard, FN_TICKET_END);
+   if(dashf(fn_ticket))
+    {
+      getdata(b_lines - 1, 0, "�w�g���|���L, "
+            "�O�_�n [����U�`]?(N/y)�G", yn, 3, LCECHO);
+      if(yn[0]!='y') return FULLUPDATE;        
+      rename(fn_ticket, fn_ticket_end);
+      return FULLUPDATE;
+    }
+   
+   if(dashf(fn_ticket_end))
+    {
+      getdata(b_lines - 1, 0, "�w�g���|���L, "
+            "�O�_�n [�}��]?(N/y)�G", yn, 3, LCECHO);
+      if(yn[0]!='y') return FULLUPDATE;
+      openticket(currbid);
+      return FULLUPDATE; 
+    }
+   getdata(b_lines - 2, 0, "�n�|���L (N/y):", yn, 3, LCECHO);
+   if(yn[0]!='y') return FULLUPDATE;
+   getdata(b_lines - 1, 0, "�䤰��? �п�J�D�D (��J��s�褺�e):",
+            msg, 20, DOECHO);
+   if(msg[0]==0 ||
+      vedit(fn_ticket_end, NA, NULL)<0)
+	    return FULLUPDATE; 
+      
+   clear();
+   showtitle("�|���L",BBSNAME);
+   setbfile(genbuf, currboard, FN_TICKET_ITEMS);
+
+//    sprintf(genbuf, "%s/"FN_TICKET_ITEMS, direct);
+    
+   if(!(fp=fopen(genbuf,"w"))) return FULLUPDATE;
+   do
+    {
+        getdata(2, 0, "��J�m������ (����:10-10000):",yn,6, LCECHO);
+        i=atoi(yn);
+    } while( i<10 || i>10000);
+   fprintf(fp,"%d\n",i);
+   move(3,0);
+   sprintf(genbuf,"�Ш� %s �� ��'f'�ѻP���!\n\n�@�i %d Ptt��, �o�O%s�����\n", 
+            currboard,
+            i, i<100 ? "�p�䦡" : i<500 ? "������": 
+            i<1000 ?"�Q�گ�" : i<5000 ?"�I����" : "�ɮa����");
+   strcat(msg, genbuf);
+   prints("�Ш̦���J�m���W��, �ݴ���2~8��. (�����K��, ��J������enter)\n");
+   for(i=0; i<8; i++)
+	{
+           sprintf(yn, " %d)",i+1);
+           getdata(6+i, 0, yn, genbuf, 9, DOECHO); 
+           if(!genbuf[0] && i>1)
+	       break;
+	   fprintf(fp,"%s\n",genbuf);
+	}	
+   fclose(fp);
+   move(8+i,0);
+   prints("��L�]�w����");
+   sprintf(genbuf,"[���i] %s �� �}�l���!", currboard);  
+   post_msg(currboard, genbuf, msg, cuser.userid);
+   post_msg("Record", genbuf+7, msg, "[�������l]"); 
+      /* Tim ����CS, �H�K���b����user���Ƥw�g�g�i�� */
+   rename(fn_ticket_end, fn_ticket); // �]�w���~���ɦW��L��
+
+   return FULLUPDATE;
+}
+#endif
+
+static int cite_post(int ent, fileheader_t *fhdr, char *direct) {
+    char fpath[256];
+    char title[TTLEN + 1];
+    
+    setbfile(fpath, currboard, fhdr->filename);
+    strcpy(title, "�� ");
+    strncpy(title+3, fhdr->title, TTLEN-3);
+    title[TTLEN] = '\0';
+    a_copyitem(fpath, title, 0, 1);
+    b_man();
+    return FULLUPDATE;
+}
+
+int edit_title(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[200];
+    fileheader_t tmpfhdr = *fhdr;
+    int dirty = 0;
+
+    if(currmode & MODE_BOARD || !strcmp(cuser.userid,fhdr->owner)) {
+        if(getdata(b_lines - 1, 0, "���D�G", genbuf, TTLEN, DOECHO)) {
+	    strcpy(tmpfhdr.title, genbuf);
+	    dirty++;
+        }
+    }
+
+    if(HAS_PERM(PERM_SYSOP)) {
+        if(getdata(b_lines - 1, 0, "�@�̡G", genbuf, IDLEN + 2, DOECHO)) {
+	    strcpy(tmpfhdr.owner, genbuf);
+	    dirty++;
+        }
+	
+        if(getdata(b_lines - 1, 0, "����G", genbuf, 6, DOECHO)) {
+	    sprintf(tmpfhdr.date, "%.5s", genbuf);
+	    dirty++;
+        }
+    }
+
+    if(currmode & MODE_BOARD || !strcmp(cuser.userid,fhdr->owner)) {
+        getdata(b_lines-1, 0, "�T�w(Y/N)?[n] ", genbuf, 3, DOECHO);
+	if((genbuf[0] == 'y' || genbuf[0] == 'Y') && dirty) {
+	    *fhdr = tmpfhdr;
+	    substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+/* rocker.011018: �o�����ӧ令��reference���覡���o��Ӫ��ɮ� */
+#if 0
+	    if((currmode & MODE_SELECT)) {
+		int now;
+
+		setbdir(genbuf, currboard);
+		now = getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+		substitute_record(genbuf, fhdr, sizeof(*fhdr), now);
+	    }
+#else
+	if ((currmode & MODE_SELECT) && (fhdr->money & FHR_REFERENCE))
+	{
+	  fileheader_t hdr;
+	  int num;
+
+	  num = fhdr->money & ~FHR_REFERENCE;
+	  setbdir(genbuf, currboard);
+          get_record(genbuf, &hdr, sizeof (hdr), num);
+
+	  /* �A�o�̭ncheck�@�U��Ӫ�dir�̭��O���O���Q�H�ʹL... */
+	  if (strcmp (hdr.filename, fhdr->filename))
+	    num = getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+
+	  substitute_record(genbuf, fhdr, sizeof(*fhdr), num);
+	}
+#endif
+            touchdircache(currbid); 
+	}
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+extern unsigned int currstat;
+
+static int solve_post(int ent, fileheader_t * fhdr, char *direct){
+    if (HAS_PERM(PERM_SYSOP)) {
+	fhdr->filemode ^= FILE_SOLVED;
+	substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+        touchdircache(currbid);
+	return PART_REDRAW;
+    }
+    return DONOTHING;
+}
+
+static int mark_post(int ent, fileheader_t *fhdr, char *direct) {
+
+    if(!(currmode & MODE_BOARD)) return DONOTHING;
+
+    fhdr->filemode ^= FILE_MARKED;
+    substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+
+    /* rocker.011018: �걵�Ҧ���reference�W�i�IJv */
+    if ((currmode & MODE_SELECT) && (fhdr->money & FHR_REFERENCE))
+    {
+      fileheader_t hdr;
+      char genbuf[100];
+      int num;
+
+      num = fhdr->money & ~FHR_REFERENCE;
+      setbdir(genbuf, currboard);
+      get_record(genbuf, &hdr, sizeof (hdr), num);
+
+      /* �A�o�̭ncheck�@�U��Ӫ�dir�̭��O���O���Q�H�ʹL... */
+      if (strcmp (hdr.filename, fhdr->filename))
+	num = getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+
+      substitute_record(genbuf, fhdr, sizeof(*fhdr), num);
+    }
+    touchdircache(currbid);
+    return PART_REDRAW;
+}
+
+extern char *msg_sure_ny;
+
+int del_range(int ent, fileheader_t *fhdr, char *direct) {
+    char num1[8], num2[8];
+    int inum1, inum2;
+
+/* rocker.011018: �걵�Ҧ��U�٬O�����\�R������n */
+    if(currmode & MODE_SELECT) {
+	    outmsg("�Х��^�쥿�`�Ҧ���A�i��R��...");
+	    refresh();
+	    /*safe_sleep(1);*/
+	    return FULLUPDATE;
+    }
+
+    if((currstat != READING) || (currmode & MODE_BOARD)) {
+	getdata(1, 0, "[�]�w�R���d��] �_�I�G", num1, 5, DOECHO);
+	inum1 = atoi(num1);
+	if(inum1 <= 0) {
+	    outmsg("�_�I���~");
+	    refresh();
+	    /*safe_sleep(1);*/
+	    return FULLUPDATE;
+	}
+	getdata(1, 28, "���I�G", num2, 5, DOECHO);
+	inum2 = atoi(num2);
+	if(inum2 < inum1) {
+	    outmsg("���I���~");
+	    refresh();
+	    /*safe_sleep(1);*/
+	    return FULLUPDATE;
+	}
+	getdata(1, 48, msg_sure_ny, num1, 3, LCECHO);
+	if(*num1 == 'y') {
+	    outmsg("�B�z��,�еy��...");
+	    refresh();
+	    if(currmode & MODE_SELECT) {
+		int fd,size = sizeof(fileheader_t);
+		char genbuf[100];
+		fileheader_t rsfh;
+		int i = inum1,now;
+		if(currstat == RMAIL)
+		    sethomedir(genbuf, cuser.userid);
+		else
+		    setbdir(genbuf,currboard);
+		if((fd = (open(direct, O_RDONLY, 0))) != -1) {
+		    if(lseek(fd, (off_t)(size * (inum1 - 1)), SEEK_SET) !=
+		       -1) {
+			while(read(fd,&rsfh,size) == size) {
+			    if(i > inum2)
+				break;
+			    now = getindex(genbuf, rsfh.filename, size);
+			    strcpy(currfile, rsfh.filename);
+			    delete_file(genbuf, sizeof(fileheader_t), now,
+					cmpfilename);
+			    i++;
+			}
+		    }
+		    close(fd);
+		}
+	    }
+	    
+	    delete_range(direct, inum1, inum2);
+	    fixkeep(direct, inum1);
+
+	    if(currmode & MODE_BOARD)
+		setbtotal(currbid);
+	    
+	    return DIRCHANGED;
+	}
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+extern char *msg_del_ny;
+extern char *msg_del_ok;
+
+static int del_post(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[100];
+    int not_owned;
+    boardheader_t *bp;
+    
+    bp = getbcache(currbid);
+
+    if((fhdr->filemode & FILE_MARKED) || (fhdr->filemode & FILE_DIGEST) ||
+       (fhdr->owner[0] == '-'))
+	return DONOTHING;
+
+    not_owned = strcmp(fhdr->owner, cuser.userid);
+    if((!(currmode & MODE_BOARD) && not_owned) ||
+       ((bp->brdattr & BRD_VOTEBOARD) && !HAS_PERM(PERM_SYSOP)) ||
+       !strcmp(cuser.userid, STR_GUEST))
+	return DONOTHING;
+
+    getdata(1, 0, msg_del_ny, genbuf, 3, LCECHO);
+    if(genbuf[0] == 'y' || genbuf[0] == 'Y') {
+	strcpy(currfile, fhdr->filename);
+	
+	setbfile(genbuf,currboard,fhdr->filename);
+	if(!delete_file (direct, sizeof(fileheader_t), ent, cmpfilename)) {
+
+	    if(currmode & MODE_SELECT) 
+	    {
+		/* rocker.011018: �Q��reference��Cloading */
+	  	fileheader_t hdr;
+	  	int num;
+
+	  	num = fhdr->money & ~FHR_REFERENCE;
+	  	setbdir(genbuf, currboard);
+          	get_record(genbuf, &hdr, sizeof (hdr), num);
+
+	  	/* �A�o�̭ncheck�@�U��Ӫ�dir�̭��O���O���Q�H�ʹL... */
+	  	if (strcmp (hdr.filename, fhdr->filename))
+		{
+		  num=getindex(genbuf,fhdr->filename,sizeof(fileheader_t));
+          	  get_record(genbuf, &hdr, sizeof (hdr), num);
+		}
+
+		/* rocker.011018: �o�̭n�٭�Q�}�a��money */
+		fhdr->money = hdr.money;
+		delete_file (genbuf, sizeof(fileheader_t), num, cmpfilename);
+	    }
+
+#if 0
+	    {
+		setbdir(genbuf,currboard);
+		now=getindex(genbuf,fhdr->filename,sizeof(fileheader_t));
+		delete_file (genbuf, sizeof(fileheader_t),now,cmpfilename);
+	    }
+#endif
+	    cancelpost(fhdr, not_owned);
+
+	    setbtotal(currbid);
+	    if (fhdr->money < 0)
+		fhdr->money = 0;
+	    if (not_owned && strcmp(currboard, "Test")){		
+		deumoney(searchuser(fhdr->owner), -fhdr->money);
+	    }
+	    if(!not_owned && strcmp(currboard, "Test")) {
+		if(cuser.numposts)
+		    cuser.numposts--;
+		move(b_lines - 1, 0);
+		clrtoeol();
+		demoney(-fhdr->money);
+                passwd_update(usernum, &cuser); /* post �� */
+		prints("%s�A�z���峹� %d �g�A��I�M��O %d ��", msg_del_ok,
+		       cuser.numposts,fhdr->money);
+		refresh();
+		pressanykey();
+	    }
+	    return DIRCHANGED;
+	}
+    }
+    return FULLUPDATE;
+}
+
+static int view_postmoney(int ent, fileheader_t *fhdr, char *direct) {
+    move(b_lines - 1, 0);
+    clrtoeol();
+    prints("�o�@�g�峹�� %d ��", fhdr->money);
+    refresh();
+    pressanykey();
+    return FULLUPDATE;
+}
+
+#ifdef OUTJOBSPOOL
+/* �ݪ��ƥ� */
+static int tar_addqueue(int ent, fileheader_t *fhdr, char *direct) {
+    char    email[60], qfn[80], ans[2];
+    FILE    *fp;
+    char    bakboard, bakman;
+    clear();
+    showtitle("�ݪ��ƥ�", BBSNAME);
+    move(2, 0);
+    if( !((currmode & MODE_BOARD) || HAS_PERM(PERM_SYSOP)) ) {
+        move(5, 10);
+        outs("�p�n�O���D�άO�����~������� -.-\"\"");
+        pressanykey();
+        return FULLUPDATE;
+    }
+
+    sprintf(qfn, BBSHOME "/jobspool/tarqueue.%s", currboard);
+    if( access(qfn, 0) == 0 ){
+        outs("�w�g�Ʃw��{, �y��|�i��ƥ�");
+        pressanykey();
+        return FULLUPDATE;
+    }
+    if( !getdata(4, 0, "�п�J�ت��H�c�G", email, sizeof(email), DOECHO) )
+        return FULLUPDATE;
+
+    /* check email -.-"" */
+    if( strstr(email, "@") == NULL || strstr(email, ".bbs@") != NULL ){
+        move(6, 0);
+        outs("�z���w���H�c�����T! ");
+        pressanykey();
+        return FULLUPDATE;
+    }
+
+    getdata(6, 0, "�n�ƥ��ݪ����e��(Y/N)?[Y]", ans, 2, LCECHO);
+    bakboard = (ans[0] == 'n' || ans[0] =='N') ? 0 : 1;
+    getdata(7, 0, "�n�ƥ���ذϤ��e��(Y/N)?[N]", ans, 2, LCECHO);
+    bakman = (ans[0] == 'y' || ans[0] =='Y') ? 1 : 0;
+    if( !bakboard && !bakman ){
+        move(8, 0);
+        outs("�i�O�ڭ̥u��ƥ��ݪ��κ�ذϪ��C ^^\"\"\"");
+        pressanykey();
+        return FULLUPDATE;
+    }
+
+    fp = fopen(qfn, "w");
+    fprintf(fp, "%s\n", cuser.userid);
+    fprintf(fp, "%s\n", email);
+    fprintf(fp, "%d,%d\n", bakboard, bakman);
+    fclose(fp);
+
+    move(10, 0);
+    outs("�t�Τw�g�N�z���ƥ��ƤJ��{, \n");
+    outs("�y��N�|�b�t�έt�����C���ɭԱN��ƱH���z~ :) ");
+    pressanykey();
+    return FULLUPDATE;
+}
+#endif
+
+static int sequent_ent;
+static int continue_flag;
+
+/* ----------------------------------------------------- */
+/* �̧�Ū�s�峹                                          */
+/* ----------------------------------------------------- */
+static int sequent_messages(fileheader_t *fptr) {
+    static int idc;
+    char genbuf[200];
+
+    if(fptr == NULL)
+	return (idc = 0);
+
+    if(++idc < sequent_ent)
+	return 0;
+
+    if(!brc_unread(fptr->filename,brc_num,brc_list))
+	return 0;
+
+    if(continue_flag)
+	genbuf[0] = 'y';
+    else {
+	prints("Ū���峹��G[%s] �@�̡G[%s]\n���D�G[%s]",
+	       currboard, fptr->owner, fptr->title);
+	getdata(3, 0, "(Y/N/Quit) [Y]: ", genbuf, 3, LCECHO);
+    }
+
+    if(genbuf[0] != 'y' && genbuf[0]) {
+	clear();
+	return (genbuf[0] == 'q' ? QUIT : 0);
+    }
+
+    setbfile(genbuf, currboard, fptr->filename);
+    brc_addlist(fptr->filename);
+
+    if(more(genbuf, YEA) == 0)
+	outmsg("\033[31;47m  \033[31m(R)\033[30m�^�H  \033[31m(��,n)"
+	       "\033[30m�U�@��  \033[31m(��,q)\033[30m���}  \033[m");
+    continue_flag = 0;
+
+    switch(egetch()) {
+    case KEY_LEFT:
+    case 'e':
+    case 'q':
+    case 'Q':
+	break;
+	
+    case 'y':
+    case 'r':
+    case 'Y':
+    case 'R':
+	if(currmode & MODE_POST) {
+	    strcpy(quote_file, genbuf);
+	    do_reply(fptr);
+	    *quote_file = 0;
+	}
+	break;
+
+    case ' ':
+    case KEY_DOWN:
+    case '\n':
+    case 'n':
+	continue_flag = 1;
+    }
+
+    clear();
+    return 0;
+}
+
+static int sequential_read(int ent, fileheader_t *fhdr, char *direct) {
+    char buf[40];
+
+    clear();
+    sequent_messages((fileheader_t *) NULL);
+    sequent_ent = ent;
+    continue_flag = 0;
+    setbdir(buf, currboard);
+    apply_record(buf, sequent_messages, sizeof(fileheader_t));
+    return FULLUPDATE;
+}
+
+extern char *fn_notes;
+extern char *msg_cancel;
+extern char *fn_board;
+
+/* ----------------------------------------------------- */
+/* �ݪO�Ƨѿ��B��K�B��ذ�                              */
+/* ----------------------------------------------------- */
+int b_note_edit_bname(int bid) {
+    char buf[64];
+    int aborted;
+    boardheader_t *fh=getbcache(bid);
+
+    setbfile(buf, fh->brdname, fn_notes);
+    aborted = vedit(buf, NA, NULL);
+    if(aborted == -1) {
+	clear();
+	outs(msg_cancel);
+	pressanykey();
+    } else {
+	aborted = (fh->bupdate - time(0)) / 86400 + 1;
+	sprintf(buf,"%d", aborted > 0 ? aborted : 0);
+	getdata_buf(3, 0, "�г]�w���Ĵ���(0 - 9999)�ѡH", buf, 5, DOECHO);
+	aborted = atoi(buf);
+	fh->bupdate = aborted ? time(0) + aborted * 86400 : 0;
+	substitute_record(fn_board, fh, sizeof(boardheader_t), bid);
+    }
+    return 0;
+}
+
+static int b_notes_edit() {
+    if(currmode & MODE_BOARD) {
+        b_note_edit_bname(currbid);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int b_water_edit() {
+    if(currmode & MODE_BOARD) {
+	friend_edit(BOARD_WATER);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int visable_list_edit() {
+    if(currmode & MODE_BOARD) {
+	friend_edit(BOARD_VISABLE);
+	hbflreload(currbid);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int b_post_note() {
+    char buf[200], yn[3];
+  
+    if(currmode & MODE_BOARD) {
+	setbfile(buf, currboard,  FN_POST_NOTE );
+	if(more(buf,NA) == -1)  more("etc/"FN_POST_NOTE , NA);
+	getdata(b_lines - 2, 0, "�O�_�n�Φۭqpost�`�N�ƶ�?", yn, 3, LCECHO);
+	if(yn[0] == 'y')
+	    vedit(buf, NA, NULL);
+	else
+	    unlink(buf);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int b_application() {
+    char buf[200];
+
+    if(currmode & MODE_BOARD) {
+	setbfile(buf, currboard,  FN_APPLICATION);
+	vedit(buf, NA, NULL);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int can_vote_edit() {
+    if(currmode & MODE_BOARD) {
+	friend_edit(FRIEND_CANVOTE);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int bh_title_edit() {
+    boardheader_t *bp;
+
+    if(currmode & MODE_BOARD) {
+	char genbuf[BTLEN];
+
+	bp = getbcache(currbid);
+	move(1,0);
+	clrtoeol();
+	getdata_str(1,0,"�п�J�ݪO�s����ԭz:", genbuf,BTLEN -
+		    16,DOECHO, bp->title + 7);
+
+	if(!genbuf[0])
+	    return 0;
+	strip_ansi( genbuf,genbuf,0);
+	strcpy(bp->title + 7,genbuf);
+	substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
+	log_usies("SetBoard", currboard);
+	return FULLUPDATE;
+    }
+    return 0;
+}
+
+static int b_notes() {
+    char buf[64];
+    
+    setbfile(buf, currboard, fn_notes);
+    if(more(buf, NA) == -1) {
+	clear();
+	move(4, 20);
+	outs("���ݪO�|�L�u�Ƨѿ��v�C");
+    }
+    pressanykey();
+    return FULLUPDATE;
+}
+
+int board_select() {
+    char fpath[80];
+    char genbuf[100];
+
+    currmode &= ~MODE_SELECT;
+    sprintf(fpath, "SR.%s", cuser.userid);
+    setbfile(genbuf, currboard, fpath);
+    unlink(genbuf);
+    if(currstat == RMAIL)
+	sethomedir(currdirect, cuser.userid);
+    else
+	setbdir(currdirect, currboard);
+    return NEWDIRECT;
+}
+
+int board_digest() {
+    if(currmode & MODE_SELECT)
+	board_select();
+    currmode ^= MODE_DIGEST;
+    if(currmode & MODE_DIGEST)
+	currmode &= ~MODE_POST;
+    else if (haspostperm(currboard))
+	currmode |= MODE_POST;
+    
+    setbdir(currdirect, currboard);
+    return NEWDIRECT;
+}
+
+int board_etc() {
+    if(!HAS_PERM(PERM_SYSOP))
+	return DONOTHING;
+    currmode ^= MODE_ETC;
+    if(currmode & MODE_ETC)
+	currmode &= ~MODE_POST;
+    else if(haspostperm(currboard))
+	currmode |= MODE_POST;
+    
+    setbdir(currdirect, currboard);
+    return NEWDIRECT;
+}
+
+extern char *fn_mandex;
+
+static int good_post(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[200];
+    char genbuf2[200];
+    int delta = 0;
+
+    if((currmode & MODE_DIGEST) || !(currmode & MODE_BOARD))
+	return DONOTHING;
+
+    if(fhdr->filemode & FILE_DIGEST) {
+	fhdr->filemode = (fhdr->filemode & ~FILE_DIGEST);
+	if(!strcmp(currboard,"Note") || !strcmp(currboard,"PttBug") ||
+	   !strcmp(currboard,"Artdsn") || !strcmp(currboard, "PttLaw")) {
+	    deumoney(searchuser(fhdr->owner),-1000);
+	    if(!(currmode & MODE_SELECT))
+	      fhdr->money -= 1000;
+	    else
+	      delta = -1000;
+	}
+    } else {
+	fileheader_t digest;
+	char *ptr, buf[64];
+
+	memcpy(&digest, fhdr, sizeof(digest));
+	digest.filename[0] = 'G';
+	strcpy(buf, direct);
+	ptr = strrchr(buf, '/') + 1;
+	ptr[0] = '\0';
+	sprintf(genbuf, "%s%s", buf, digest.filename);
+
+	if(dashf(genbuf)) unlink (genbuf);
+
+	digest.savemode = digest.filemode = 0;
+	sprintf(genbuf2, "%s%s", buf, fhdr->filename);
+	Link(genbuf2, genbuf);
+	strcpy(ptr, fn_mandex);
+	append_record(buf, &digest, sizeof(digest));
+
+	fhdr->filemode = (fhdr->filemode & ~FILE_MARKED) | FILE_DIGEST;
+	if(!strcmp(currboard, "Note") || !strcmp(currboard, "PttBug") ||
+	   !strcmp(currboard,"Artdsn") || !strcmp(currboard, "PttLaw")) {
+	    deumoney(searchuser(fhdr->owner), 1000);
+	    if(!(currmode & MODE_SELECT)) fhdr->money += 1000;
+	    else delta = 1000;
+	}
+    }
+    substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+    touchdircache(currbid); 
+/* rocker.011018: �걵�Ҧ���reference�W�i�IJv */
+    if ((currmode & MODE_SELECT) && (fhdr->money & FHR_REFERENCE))
+    {
+      fileheader_t hdr;
+      char genbuf[100];
+      int num;
+
+      num = fhdr->money & ~FHR_REFERENCE;
+      setbdir(genbuf, currboard);
+      get_record(genbuf, &hdr, sizeof (hdr), num);
+
+      /* �A�o�̭ncheck�@�U��Ӫ�dir�̭��O���O���Q�H�ʹL... */
+      if (strcmp (hdr.filename, fhdr->filename))
+      {
+	num = getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+        get_record(genbuf, &hdr, sizeof (hdr), num);
+      }
+      fhdr->money = hdr.money + delta;
+
+      substitute_record(genbuf, fhdr, sizeof(*fhdr), num);
+    }
+#if 0
+    if(currmode & MODE_SELECT) {
+	int now;
+	char genbuf[100];
+
+	setbdir(genbuf, currboard);
+	now=getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+	substitute_record(genbuf, fhdr, sizeof(*fhdr), now);
+    }
+#endif
+    return PART_REDRAW;
+}
+
+/* help for board reading */
+static char *board_help[] = {
+    "\0���\\��ݪO�ާ@����",
+    "\01�򥻩R�O",
+    "(p)(��)   �W���@�g�峹         (^P)     �o���峹",
+    "(n)(��)   �U���@�g�峹         (d)      �R���峹",
+    "(P)(PgUp) �W���@��             (S)      ��s�����峹",
+    "(N)(PgDn) �U���@��             (##)     ���� ## ���峹",
+    "(r)(��)   �\\Ū���g�峹         ($)      ����̫�@�g�峹",
+    "\01�i���R�O",
+    "(tab)/z   ��K�Ҧ�/��ذ�      (a)(A)   ��M�@��",
+    "(b/f)     �iŪ�Ƨѿ�/�ѻP��L  (?)(/)   ��M���D",
+    "(V/R)     �벼/�d�ߧ벼���G    (^W)     �ڦb���̥i�ݨ�ݪO������",
+    "(x)       ����峹���L�ݪO   (=)/([]<>-+) ��M���g�峹/�D�D���\\Ū",
+#ifdef INTERNET_EMAIL
+    "(F)       �峹�H�^Internet�l�c (U)      �N�峹 uuencode ��H�^�l�c",
+#endif
+    "(E)       ���s�峹             (^H)     �C�X�Ҧ��� New Post(s)",
+    "\01�O�D�R�O",
+    "(G)       �|���L/����U�`/�}��(W/w/v) �s��Ƨѿ�/�����W��/�i�ݨ��W��",
+    "(M/o)     �|��벼/�s�p�벼�W�� (m/c/g) �O�d�峹/������/��K",
+    "(D)       �R���@�q�d�򪺤峹    (T/B)   ���s�峹���D/���s�ݪ����D",
+    "(i)       �s��ӽФJ�|����      (t/^D)  �аO�峹/�尣�аO���峹",
+    "(O)       �s��Post�`�N�ƶ�      (H)     �ݪO����/�{��",
+    NULL
+};
+
+static int b_help() {
+    show_help(board_help);
+    return FULLUPDATE;
+}
+
+/* ----------------------------------------------------- */
+/* �O�D�]�w����/ ������                                  */
+/* ----------------------------------------------------- */
+char board_hidden_status;
+#ifdef  BMCHS
+extern char *fn_board;
+static int change_hidden(int ent, fileheader_t *fhdr, char *direct)
+{
+    boardheader_t bh;
+    int    bid;
+    char   ans[4];
+
+    if( !((currmode & MODE_BOARD) || HAS_PERM(PERM_SYSOP)) ||
+	currboard[0] == 0 ||
+	(bid = getbnum(currboard)) < 0 ||
+	get_record(fn_board, &bh, sizeof(bh), bid) == -1 )
+	return DONOTHING;
+
+    if( ((bh.brdattr & BRD_HIDE) && (bh.brdattr & BRD_POSTMASK)) ){
+	getdata(1, 0, "�ثe�O�b���Ϊ��A, �n�����ι�(Y/N)?[N]", ans, 2, LCECHO);
+	if( ans[0] != 'y' && ans[0] != 'Y' )
+	    return FULLUPDATE;
+	getdata(2, 0, "�A�T�{�@��, �u���n��O�O���}�� @____@(Y/N)?[N]",
+		ans, 2, LCECHO);
+	if( ans[0] != 'y' && ans[0] != 'Y' )
+	    return FULLUPDATE;
+	if( bh.brdattr & BRD_HIDE     ) bh.brdattr -= BRD_HIDE;
+	if( bh.brdattr & BRD_POSTMASK ) bh.brdattr -= BRD_POSTMASK;
+	log_usies("OpenBoard", bh.brdname);
+	outs("�g�ߤ��Dz��H�A�L�B���D���q�C\n");
+	board_hidden_status = 0;
+	hbflreload(bid);
+    }
+    else{
+	getdata(1, 0, "�ثe�O�b�{�Ϊ��A, �n���ι�(Y/N)?[N]", ans, 2, LCECHO);
+	if( ans[0] != 'y' && ans[0] != 'Y' )
+	    return FULLUPDATE;
+	bh.brdattr |= BRD_HIDE;
+	bh.brdattr |= BRD_POSTMASK;
+	log_usies("CloseBoard", bh.brdname);
+	outs("�g�ߤ��w�����A���ߵ��۬í��C\n");
+	board_hidden_status = 1;
+    }
+    setup_man(&bh);
+    substitute_record(fn_board, &bh, sizeof(bh), bid);
+    reset_board(bid);
+    log_usies("SetBoard", bh.brdname);
+    pressanykey();
+    return FULLUPDATE;
+}
+#endif
+
+/* ----------------------------------------------------- */
+/* �ݪO�\���                                            */
+/* ----------------------------------------------------- */
+struct onekey_t read_comms[] = {
+    {KEY_TAB, board_digest},
+    {'C', board_etc},
+    {'b', b_notes},
+    {'c', cite_post},
+    {'r', read_post},
+    {'z', b_man},
+    {'D', del_range},
+    {'S', sequential_read},
+    {'E', edit_post},
+    {'T', edit_title},
+    {'s', do_select},
+    {'R', b_results},
+    {'V', b_vote},
+    {'M', b_vote_maintain},
+    {'B', bh_title_edit},
+    {'W', b_notes_edit},
+    {'O', b_post_note},
+    {'w', b_water_edit},
+    {'v', visable_list_edit},
+    {'i', b_application},
+    {'o', can_vote_edit},
+    {'x', cross_post},
+    {'h', b_help},
+#ifndef NO_GAMBLE
+    {'f', join_gamble},
+    {'G', hold_gamble},
+#endif
+    {'g', good_post},
+    {'y', reply_post},
+    {'d', del_post},
+    {'m', mark_post},
+    {'L', solve_post},
+    {Ctrl('P'), do_post},
+    {Ctrl('W'), whereami},
+    {'Q', view_postmoney},
+#ifdef OUTJOBSPOOL
+    {'u', tar_addqueue},
+#endif
+#ifdef BMCHS
+    {'H', change_hidden},
+#endif
+    {'\0', NULL}
+};
+
+time_t board_visit_time;
+
+int Read() {
+    int mode0 = currutmp->mode;
+    int stat0 = currstat, tmpbid=currutmp->brc_id;
+    char buf[40];
+#ifdef LOG_BOARD
+    time_t usetime = time(0);
+#endif 
+
+    setutmpmode(READING);
+    set_board();
+
+    if(board_visit_time < board_note_time) {
+	setbfile(buf, currboard, fn_notes);
+	more(buf, NA);
+	pressanykey();
+    }
+    currutmp->brc_id = currbid;
+    setbdir(buf, currboard);
+    curredit &= ~EDIT_MAIL;
+    i_read(READING, buf, readtitle, readdoent, read_comms,
+	    currbid);
+#ifdef LOG_BOARD
+    log_board(currboard, time(0) - usetime);
+#endif
+    brc_update();
+
+    currutmp->brc_id =tmpbid;
+    currutmp->mode = mode0;
+    currstat = stat0;
+    return 0;
+}
+
+void ReadSelect() {
+    int mode0 = currutmp->mode;
+    int stat0 = currstat;
+    char genbuf[200];
+
+    currstat = XMODE;
+    if(do_select(0, 0, genbuf) == NEWDIRECT)
+	Read();
+    currutmp->brc_id=0;
+    currutmp->mode = mode0;
+    currstat = stat0;
+}
+
+#ifdef LOG_BOARD 
+static void log_board(char *mode, time_t usetime) {
+    time_t now;
+    char buf[ 256 ];
+
+    if(usetime > 30) {
+	now = time(0);
+	sprintf(buf, "USE %-20.20s Stay: %5ld (%s) %s",
+		mode, usetime ,cuser.userid ,ctime(&now));
+	log_file(FN_USEBOARD,buf);
+    }
+}
+#endif
+
+int Select() {
+    char genbuf[200];
+
+    setutmpmode(SELECT);
+    do_select(0, NULL, genbuf);
+    return 0;
+}
diff --git a/mbbsd/board.c b/mbbsd/board.c
new file mode 100644
index 00000000..44b4b842
--- /dev/null
+++ b/mbbsd/board.c
@@ -0,0 +1,1098 @@
+/* $Id: board.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "modes.h"
+#include "common.h"
+#include "proto.h"
+
+#define BRC_STRLEN 15             /* Length of board name */
+#define BRC_MAXSIZE     24576
+#define BRC_ITEMSIZE    (BRC_STRLEN + 1 + BRC_MAXNUM * sizeof( int ))
+#define BRC_MAXNUM      80
+
+extern userinfo_t *currutmp;
+static char *brc_getrecord(char *ptr, char *name, int *pnum, int *list) {
+    int num;
+    char *tmp;
+
+    strncpy(name, ptr, BRC_STRLEN);
+    ptr += BRC_STRLEN;
+    num = (*ptr++) & 0xff;
+    tmp = ptr + num * sizeof(int);
+    if (num > BRC_MAXNUM)
+	num = BRC_MAXNUM;
+    *pnum = num;
+    memcpy(list, ptr, num * sizeof(int));
+    return tmp;
+}
+
+static time_t brc_expire_time;
+
+static char *brc_putrecord(char *ptr, char *name, int num, int *list) {
+    if(num > 0 && list[0] > brc_expire_time) {
+	if (num > BRC_MAXNUM)
+	    num = BRC_MAXNUM;
+
+	while(num > 1 && list[num - 1] < brc_expire_time)
+	    num--;
+
+	strncpy(ptr, name, BRC_STRLEN);
+	ptr += BRC_STRLEN;
+	*ptr++ = num;
+	memcpy(ptr, list, num * sizeof(int));
+	ptr += num * sizeof(int);
+    }
+    return ptr;
+}
+
+extern userec_t cuser;
+extern char currboard[];        /* name of currently selected board */
+static int brc_changed = 0;
+static char brc_buf[BRC_MAXSIZE];
+int brc_num;
+static char brc_name[BRC_STRLEN];
+int brc_list[BRC_MAXNUM];
+static char *fn_boardrc = ".boardrc";
+static int brc_size;
+
+void brc_update() {
+    if(brc_changed && cuser.userlevel) {
+	char dirfile[STRLEN], *ptr;
+	char tmp_buf[BRC_MAXSIZE - BRC_ITEMSIZE], *tmp;
+	char tmp_name[BRC_STRLEN];
+	int tmp_list[BRC_MAXNUM], tmp_num;
+	int fd, tmp_size;
+
+	ptr = brc_buf;
+	if(brc_num > 0)
+	    ptr = brc_putrecord(ptr, brc_name, brc_num, brc_list);
+
+	setuserfile(dirfile, fn_boardrc);
+	if((fd = open(dirfile, O_RDONLY)) != -1) {
+	    tmp_size = read(fd, tmp_buf, sizeof(tmp_buf));
+	    close(fd);
+	} else {
+	    tmp_size = 0;
+	}
+
+	tmp = tmp_buf;
+	while(tmp < &tmp_buf[tmp_size] && (*tmp >= ' ' && *tmp <= 'z')) {
+	    tmp = brc_getrecord(tmp, tmp_name, &tmp_num, tmp_list);
+	    if(strncmp(tmp_name, currboard, BRC_STRLEN))
+		ptr = brc_putrecord(ptr, tmp_name, tmp_num, tmp_list);
+	}
+	brc_size = (int)(ptr - brc_buf);
+
+	if((fd = open(dirfile, O_WRONLY | O_CREAT, 0644)) != -1) {
+	    ftruncate(fd, 0);
+	    write(fd, brc_buf, brc_size);
+	    close(fd);
+	}
+	brc_changed = 0;
+    }
+}
+
+static void read_brc_buf() {
+    char dirfile[STRLEN];
+    int fd;
+
+    if(brc_buf[0] == '\0') {
+	setuserfile(dirfile, fn_boardrc);
+	if((fd = open(dirfile, O_RDONLY)) != -1) {
+	    brc_size = read(fd, brc_buf, sizeof(brc_buf));
+	    close(fd);
+	} else {
+	    brc_size = 0;
+	}
+    }
+}
+
+extern int currbid;
+extern unsigned int currbrdattr;
+extern boardheader_t *bcache;
+
+int brc_initial(char *boardname) {
+    char *ptr;
+    if(strcmp(currboard, boardname) == 0) {
+	return brc_num;
+    }
+    brc_update();
+    strcpy(currboard, boardname);
+    currbid = getbnum(currboard);
+    currbrdattr = bcache[currbid - 1].brdattr;
+    read_brc_buf();
+
+    ptr = brc_buf;
+    while(ptr < &brc_buf[brc_size] && (*ptr >= ' ' && *ptr <= 'z')) {
+	ptr = brc_getrecord(ptr, brc_name, &brc_num, brc_list);
+	if (strncmp(brc_name, currboard, BRC_STRLEN) == 0)
+	    return brc_num;
+    }
+    strncpy(brc_name, boardname, BRC_STRLEN);
+    brc_num = brc_list[0] = 1;
+    return 0;
+}
+
+void brc_addlist(char *fname) {
+    int ftime, n, i;
+
+    if(!cuser.userlevel)
+	return;
+
+    ftime = atoi(&fname[2]);
+    if(ftime <= brc_expire_time
+	/* || fname[0] != 'M' || fname[1] != '.' */ ) {
+	return;
+    }
+    if(brc_num <= 0) {
+	brc_list[brc_num++] = ftime;
+	brc_changed = 1;
+	return;
+    }
+    if((brc_num == 1) && (ftime < brc_list[0]))
+	return;
+    for(n = 0; n < brc_num; n++) {
+	if(ftime == brc_list[n]) {
+	    return;
+	} else if(ftime > brc_list[n]) {
+	    if(brc_num < BRC_MAXNUM)
+		brc_num++;
+	    for(i = brc_num - 1; --i >= n; brc_list[i + 1] = brc_list[i]);
+	    brc_list[n] = ftime;
+	    brc_changed = 1;
+	    return;
+	}
+    }
+    if(brc_num < BRC_MAXNUM) {
+	brc_list[brc_num++] = ftime;
+	brc_changed = 1;
+    }
+}
+
+static int brc_unread_time(time_t ftime, int bnum, int *blist) {
+    int n;
+
+    if(ftime <= brc_expire_time )
+	return 0;
+
+    if(brc_num <= 0)
+	return 1;
+    for(n = 0; n < bnum; n++) {
+	if(ftime > blist[n])
+	    return 1;
+	else if(ftime == blist[n])
+	    return 0;
+    }
+    return 0;
+}
+
+int brc_unread(char *fname, int bnum, int *blist) {
+    int ftime, n;
+
+    ftime = atoi(&fname[2]);
+
+    if(ftime <= brc_expire_time )
+	return 0;
+
+    if(brc_num <= 0)
+	return 1;
+    for(n = 0; n < bnum; n++) {
+	if(ftime > blist[n])
+	    return 1;
+	else if(ftime == blist[n])
+	    return 0;
+    }
+    return 0;
+}
+
+#define BRD_UNREAD 1
+#define BRD_FAV  2
+#define BRD_ZAP  4
+#define BRD_TAG  8
+
+typedef struct {
+    int bid, *total;
+    time_t *lastposttime;
+    boardheader_t *bh;
+    unsigned int myattr;
+} boardstat_t;
+
+extern time_t login_start_time;
+extern int numboards;
+static int *zapbuf=NULL,*favbuf;
+static boardstat_t *nbrd;
+
+#define STR_BBSRC ".bbsrc"
+#define STR_FAV   ".fav"
+
+void init_brdbuf() {
+    register int n, size;
+    char fname[60];
+
+    /* MAXBOARDS ==> �ܦh�ݱo�� 4 �ӷs�O */
+    n = numboards + 4;
+    size = n * sizeof(int);
+    zapbuf = (int *) malloc(size);
+    favbuf = (int *) malloc(size);
+
+    memset(favbuf,0,size);
+
+    while(n)
+	zapbuf[--n] = login_start_time;
+    setuserfile(fname, STR_BBSRC);
+    if((n = open(fname, O_RDONLY, 0600)) != -1) {
+	read(n, zapbuf, size);
+	close(n);
+    }
+    setuserfile(fname, STR_FAV);
+    if((n = open(fname, O_RDONLY, 0600)) != -1) {
+	read(n, favbuf, size);
+	close(n);
+    }
+    
+    if(!nbrd)
+	nbrd = (boardstat_t *)malloc(MAX_BOARD * sizeof(boardstat_t));
+    brc_expire_time = login_start_time - 365 * 86400;
+}
+
+void save_brdbuf() {
+    int fd, size;
+    char fname[60];
+
+    if(!zapbuf) return;
+    setuserfile(fname, STR_BBSRC);
+    if((fd = open(fname, O_WRONLY | O_CREAT, 0600)) != -1) {
+	size = numboards * sizeof(int);
+	write(fd, zapbuf, size);
+	close(fd);
+    }
+    setuserfile(fname, STR_FAV);
+    if((fd = open(fname, O_WRONLY | O_CREAT, 0600)) != -1) {
+	size = numboards * sizeof(int);
+	write(fd, favbuf, size);
+	close(fd);
+    }
+}
+
+extern char *fn_visable;
+
+int Ben_Perm(boardheader_t *bptr) {
+    register int level,brdattr;
+    register char *ptr;
+
+    level = bptr->level;
+    brdattr = bptr->brdattr;
+
+    if(HAS_PERM(PERM_SYSOP))
+	return 1;
+
+    ptr = bptr->BM;
+    if(is_BM(ptr))
+	return 1;
+
+    /* ���K�ݪO�G�ֹﭺ�u�O�D���n�ͦW�� */
+
+    if(brdattr & BRD_HIDE) { /* ���� */
+	if( hbflcheck((int)(bptr-bcache) + 1, currutmp->uid) ){
+	    if(brdattr & BRD_POSTMASK)
+		return 0;
+	    else
+		return 2;
+	} else
+	    return 1;
+    }
+    /* ����\Ū�v�� */
+    if(level && !(brdattr & BRD_POSTMASK) && !HAS_PERM(level))
+	return 0;
+
+    return 1;
+}
+
+extern char currauthor[];
+extern int b_lines;
+extern char currowner[];
+
+static int have_author(char* brdname) {
+    char dirname[100];
+
+    sprintf(dirname, "���b�j�M�@��%s �ݪO:%s.....",
+	    currauthor,brdname);
+    move(b_lines, 0);
+    clrtoeol();
+    outs(dirname);
+    refresh();
+
+    setbdir(dirname, brdname);
+    str_lower(currowner, currauthor);
+
+    return search_rec(dirname, cmpfowner);
+}
+
+static int check_newpost(boardstat_t *ptr) { /* Ptt �� */
+    int tbrc_list[BRC_MAXNUM], tbrc_num;
+    char bname[BRC_STRLEN];
+    char *po;
+    time_t ftime;
+
+    ptr->myattr &= ~BRD_UNREAD;
+    if(ptr->bh->brdattr & BRD_GROUPBOARD)
+	return 0;
+
+    if(*(ptr->total) == 0) 
+      setbtotal(ptr->bid);
+    if(*(ptr->total) == 0) return 0;
+    ftime = *(ptr->lastposttime);
+    read_brc_buf();
+    po = brc_buf;
+    while(po < &brc_buf[brc_size] && (*po >= ' ' && *po <= 'z')) {
+	po = brc_getrecord(po, bname, &tbrc_num, tbrc_list);
+	if(strncmp(bname, ptr->bh->brdname, BRC_STRLEN) == 0) {
+	    if(brc_unread_time(ftime,tbrc_num,tbrc_list)) {
+		ptr->myattr |= BRD_UNREAD;
+            }
+	    return 1;
+	}
+    }
+
+    ptr->myattr |= BRD_UNREAD;
+    return 1;
+}
+
+extern int currmode;
+extern struct bcache_t *brdshm;
+static int brdnum;
+int  class_bid = 0;
+static int yank_flag = 1;
+static void load_uidofgid(const int gid, const int type){
+   boardheader_t *bptr,*currbptr;
+   int n;
+   currbptr = &bcache[gid-1];
+   for(n=0;n<numboards;n++)
+    {
+     bptr = brdshm->sorted[type][n];            
+     if(bptr->brdname[0]=='\0') continue; 
+     if(bptr->gid == gid)
+	{ 
+	  if(currbptr == &bcache[gid-1])
+	     currbptr->firstchild[type]=bptr;
+	  else
+           {
+	     currbptr->next[type]=bptr;
+             currbptr->parent=&bcache[gid-1];
+           }
+	  currbptr=bptr;
+	}
+    }
+   if(currbptr == &bcache[gid-1])
+       currbptr->firstchild[type]=(boardheader_t *) ~0;
+   else
+       currbptr->next[type]=(boardheader_t *) ~0;   
+}
+static boardstat_t * addnewbrdstat(int n, int state)
+{
+  boardstat_t *ptr=&nbrd[brdnum++];
+  boardheader_t *bptr = &bcache[n];
+  ptr->total = &(brdshm->total[n]);
+  ptr->lastposttime = &(brdshm->lastposttime[n]);
+  ptr->bid = n+1;
+  ptr->myattr=0;
+  ptr->myattr=(favbuf[n]&~BRD_ZAP);
+  if(zapbuf[n] == 0)
+      ptr->myattr|=BRD_ZAP;
+  ptr->bh = bptr;
+  if((bptr->brdattr & BRD_HIDE) && state == 1)
+         bptr->brdattr |= BRD_POSTMASK;
+  check_newpost(ptr);
+  return ptr;
+}
+
+static void load_boards(char *key) {
+    boardheader_t *bptr = NULL;
+    int type=cuser.uflag & BRDSORT_FLAG?1:0;
+    register int i,n;
+    register int state ;
+
+    if(class_bid>0)
+     {
+        bptr = &bcache[class_bid-1];
+        if(bptr->firstchild[type]==NULL)
+		load_uidofgid(class_bid,type);
+     }
+    brdnum = 0;
+    if(class_bid==0)
+      for(i=0 ; i < numboards; i++)
+	{
+          
+	  if( (bptr = brdshm->sorted[type][i]) == NULL )
+	    continue;
+	  n = (int)( bptr - bcache);
+	  if(!bptr->brdname[0] || bptr->brdattr & BRD_GROUPBOARD ||
+             !((state = Ben_Perm(bptr)) || (currmode & MODE_MENU)) ||
+             (yank_flag == 0 && !(favbuf[n]&BRD_FAV)) ||
+             (yank_flag == 1 && !zapbuf[n]) ||
+	     (key[0] && !strcasestr(bptr->title, key)) 
+            ) continue;	
+           addnewbrdstat(n, state);
+	}
+   else
+     for(bptr=bptr->firstchild[type]; bptr!=(boardheader_t *)~0;
+	   bptr=bptr->next[type]) 
+      {
+        n = (int)( bptr - bcache);
+        if(!((state = Ben_Perm(bptr)) || (currmode & MODE_MENU))
+            ||(yank_flag == 0 && !(favbuf[n]&BRD_FAV)) 
+            ||(yank_flag == 1 && !zapbuf[n]) ||
+	    (key[0] && !strcasestr(bptr->title, key))) continue;
+        addnewbrdstat(n, state);
+      }
+}
+
+static int search_board() {
+    int num;
+    char genbuf[IDLEN + 2];
+    move(0, 0);
+    clrtoeol();
+    CreateNameList();
+    for(num = 0; num < brdnum; num++)
+	AddNameList(nbrd[num].bh->brdname);
+    namecomplete(MSG_SELECT_BOARD, genbuf);
+
+    for (num = 0; num < brdnum; num++)
+        if (!strcasecmp(nbrd[num].bh->brdname, genbuf))
+            return num;
+    return -1;                
+}
+
+static int unread_position(char *dirfile, boardstat_t *ptr) {
+    fileheader_t fh;
+    char fname[FNLEN];
+    register int num, fd, step, total;
+
+    total = *(ptr->total);
+    num = total + 1;
+    if((ptr->myattr&BRD_UNREAD) &&(fd = open(dirfile, O_RDWR)) > 0) {
+	if(!brc_initial(ptr->bh->brdname)) {
+	    num = 1;
+	} else {
+	    num = total - 1;
+	    step = 4;
+	    while(num > 0) {
+		lseek(fd, (off_t)(num * sizeof(fh)), SEEK_SET);
+		if(read(fd, fname, FNLEN) <= 0 ||
+		   !brc_unread(fname,brc_num,brc_list))
+		    break;
+		num -= step;
+		if(step < 32)
+		    step += step >> 1;
+	    }
+	    if(num < 0)
+		num = 0;
+	    while(num < total) {
+		lseek(fd, (off_t)(num * sizeof(fh)), SEEK_SET);
+		if(read(fd, fname, FNLEN) <= 0 ||
+		   brc_unread(fname,brc_num,brc_list))
+		    break;
+		num++;
+	    }
+	}
+	close(fd);
+    }
+    if(num < 0)
+	num = 0;
+    return num;
+}
+
+static void brdlist_foot() {
+    prints("\033[34;46m  ��ܬݪO  \033[31;47m  (c)\033[30m�s�峹�Ҧ�  "
+	   "\033[31m(v/V)\033[30m�аO�wŪ/��Ū  \033[31m(y)\033[30m�z��%s"
+	   "  \033[31m(z)\033[30m�������  \033[m",
+	   yank_flag==0 ? "�̷R" : yank_flag==1 ? "����" : "����");
+}
+
+extern unsigned int currstat;
+extern char *BBSName;
+
+static void show_brdlist(int head, int clsflag, int newflag) {
+    int myrow = 2;
+    if(class_bid == 1) {
+	currstat = CLASS;
+	myrow = 6;
+	showtitle("�����ݪO", BBSName);
+	movie(0);
+	move(1, 0);
+	prints(
+	    "                                                              "
+	    "��  �~�X\033[33m��\n"
+	    "                                                    ��X  \033[m "
+	    "���i\033[47m��\033[40m�i�i����\n");
+	prints(
+	    "  \033[44m   �s�s�s�s�s�s�s�s                               "
+	    "\033[33m��\033[m\033[44m �����i�i�i�������� \033[m\n"
+	    "  \033[44m                                                  "
+	    "\033[33m  \033[m\033[44m �����i�i�i������ ��\033[m\n"
+	    "                                  �s�s�s�s�s�s�s�s    \033[33m"
+	    "�x\033[m   ���i�i�i�i�� ��\n"
+	    "                                                      \033[33m��"
+	    "�X�X\033[m  ��      �X��\033[m");
+    } else if (clsflag) {
+	showtitle("�ݪO�C��", BBSName);
+	prints("[��]�D��� [��]�\\Ū [����]��� [y]���J [S]�Ƨ� [/]�j�M "
+	       "[TAB]��K�E�ݪO [h]�D�U\n"
+	       "\033[7m%-20s ���O ��H%-31s�벼 �O    �D     \033[m",
+	       newflag ? "�`�� ��Ū ��  �O" : "  �s��  ��  �O",
+	       "  ��   ��   ��   �z");
+	move(b_lines, 0);
+	brdlist_foot();
+    }
+
+    if(brdnum > 0) {
+	boardstat_t *ptr;
+	static char *color[8]={"","\033[32m",
+			       "\033[33m","\033[36m","\033[34m","\033[1m",
+			       "\033[1;32m","\033[1;33m"};
+	static char *unread[2]={"\33[37m  \033[m","\033[1;31m��\033[m"};
+	
+	while(++myrow < b_lines) {
+	    move(myrow, 0);
+	    clrtoeol();
+	    if(head < brdnum) {
+		ptr = &nbrd[head++];
+		if(class_bid == 1)
+		    prints("          ");
+		if(!newflag) {
+		    prints("%5d%c%s", head,
+			   !(ptr->bh->brdattr & BRD_HIDE) ? ' ':
+			   (ptr->bh->brdattr & BRD_POSTMASK) ? ')' : '-',
+			   (ptr->myattr & BRD_TAG) ? "D " :
+			   (ptr->myattr & BRD_ZAP) ? "- " :
+			   (ptr->bh->brdattr & BRD_GROUPBOARD) ? "  " :
+			   unread[ptr->myattr&BRD_UNREAD]);
+		} else if(ptr->myattr&BRD_ZAP) {
+		    ptr->myattr &= ~BRD_UNREAD;
+		    prints("   �� ��");
+		} else {
+		    if(newflag) {
+		    	if((ptr->bh->brdattr & BRD_GROUPBOARD))
+		    	    prints("        ");
+		    	else
+			    prints("%6d%s", (int)(*(ptr->total)),
+                                    unread[ptr->myattr&BRD_UNREAD]);
+		    }
+		}
+		if(class_bid != 1) {
+                     prints("%s%-13s\033[m%s%5.5s\033[0;37m%2.2s\033[m"
+                           "%-35.35s%c %.13s",
+                           (ptr->myattr & BRD_FAV)?"\033[1;36m":"",
+			   ptr->bh->brdname,
+			   color[(unsigned int)
+				(ptr->bh->title[1] + ptr->bh->title[2] +
+				 ptr->bh->title[3] + ptr->bh->title[0]) & 07],
+			   ptr->bh->title, ptr->bh->title+5, ptr->bh->title+7,
+			   (ptr->bh->brdattr & BRD_BAD) ? 'X' :
+						 " ARBCDEFGHI"[ptr->bh->bvote],
+			   ptr->bh->BM);
+		    refresh();
+		} else {
+		    prints("%-40.40s %.13s", ptr->bh->title + 7, ptr->bh->BM);
+		}
+	    }
+	    clrtoeol();
+	}
+    }
+}
+
+static char *choosebrdhelp[] = {
+    "\0�ݪO��滲�U����",
+    "\01�򥻫��O",
+    "(p)(��)/(n)(��)�W�@�ӬݪO / �U�@�ӬݪO",
+    "(P)(^B)(PgUp)  �W�@���ݪO",
+    "(N)(^F)(PgDn)  �U�@���ݪO",
+    "($)/(s)/(/)    �̫�@�ӬݪO / �j�M�ݪO / �H����j�M�ݪO����r",
+    "(�Ʀr)         ���ܸӶ���",
+    "\01�i�����O",
+    "(^W)           �g���F �ڦb����",
+    "(r)(��)(Rtn)   �i�J�h�\\��\\Ū���",
+    "(q)(��)        �^��D���",
+    "(z)(Z)         �q�\\/�ϭq�\\�ݪO �q�\\/�ϭq�\\�Ҧ��ݪO",
+    "(y)            �ڪ��̷R/�q�\\�ݪO/�X�Ҧ��ݪO",
+    "(v/V)          �q�q�ݧ�/������Ū",
+    "(S)            ���Ӧr��/�����Ƨ�",
+    "(t/^T/^A/^D)   �аO�ݪO/�����Ҧ��аO/�w�аO���[�J�ڪ��̷R/�����ڪ��̷R",
+    "(m)            ��ݪO�[�J�ڪ��̷R",
+    "\01�p�ժ����O",
+    "(E/W/B)        �]�w�ݪO/�]�w�p�ճƧ�/�}�s�ݪO",
+    "(^P)           ���ʤw�аO�ݪO�즹����",
+    NULL
+};
+
+
+static void set_menu_BM(char *BM) {
+    if(HAS_PERM(PERM_ALLBOARD) || is_BM(BM)) {
+	currmode |= MODE_MENU;
+	cuser.userlevel |= PERM_SYSSUBOP;
+    }
+}
+
+extern int p_lines;             /* a Page of Screen line numbers: tlines-4 */
+extern int t_lines;
+extern char *fn_notes;
+static char *privateboard =
+"\n\n\n\n         �藍�_ ���O�ثe�u��ݪO�n�Ͷi�J  �Х��V�O�D�ӽФJ�ҳ\\�i";
+static void dozap(int num){
+        boardstat_t *ptr;
+	ptr = &nbrd[num];
+	ptr->myattr ^= BRD_ZAP;
+	if(ptr->bh->brdattr & BRD_NOZAP) ptr->myattr &= ~BRD_ZAP;
+	if(!(ptr->myattr & BRD_ZAP) ) check_newpost(ptr);
+	zapbuf[ptr->bid-1] = (ptr->myattr&BRD_ZAP?0:login_start_time);
+}
+static void choose_board(int newflag) {
+    static int num = 0;
+    boardstat_t *ptr;
+    int head = -1, ch = 0, currmodetmp, tmp,tmp1, bidtmp;
+    char keyword[13]="";
+#if HAVE_SEARCH_ALL
+    char genbuf[200];
+#endif
+    extern time_t board_visit_time;
+    
+    setutmpmode(newflag ? READNEW : READBRD);
+    brdnum = 0;
+    if(!cuser.userlevel)         /* guest yank all boards */
+	yank_flag = 2;
+    
+    do {
+	if(brdnum <= 0) {
+	    load_boards(keyword);
+	    if(brdnum <= 0) {
+		if(keyword[0]!=0)
+		  {
+		   mprints(b_lines-1,0,"�S������ݪO���D��������r "
+				   "(���D���`�N�ݪO���D�R�W)");
+		   pressanykey();
+	           keyword[0]=0;
+		   brdnum = -1;
+		   continue;
+	          }
+                if(yank_flag<2)
+                  {
+		   brdnum = -1;
+                   yank_flag++;
+                   continue;
+                  } 
+		if(HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU)) {
+		    if(m_newbrd(0) == -1)
+			break;
+		    brdnum = -1;
+		    continue;
+		} else
+		    break;
+	    }
+	    head = -1;
+	}
+	
+	if(num < 0)
+	    num = 0;
+	else if(num >= brdnum)
+	    num = brdnum - 1;
+	
+	if(head < 0) {
+	    if(newflag) {
+		tmp = num;
+		while(num < brdnum) {
+		    ptr = &nbrd[num];
+		    if(ptr->myattr&BRD_UNREAD)
+			break;
+		    num++;
+		}
+		if(num >= brdnum)
+		    num = tmp;
+	    }
+	    head = (num / p_lines) * p_lines;
+	    show_brdlist(head, 1, newflag);
+	} else if(num < head || num >= head + p_lines) {
+	    head = (num / p_lines) * p_lines;
+	    show_brdlist(head, 0, newflag);
+	}
+	if(class_bid == 1)
+	    ch = cursor_key(7 + num - head, 10);
+	else
+	    ch = cursor_key(3 + num - head, 0);
+	
+	switch(ch) {
+	case Ctrl('W'):
+	    whereami(0,NULL,NULL);
+	    head=-1;
+	    break;
+	case 'e':
+	case KEY_LEFT:
+	case EOF:
+	    ch = 'q';
+	case 'q':
+	    if(keyword[0])
+		{
+		  keyword[0]=0;
+		  brdnum=-1;
+		  ch=' ';
+		}
+	    break;
+	case 'c':
+	    show_brdlist(head, 1, newflag ^= 1);
+	    break;
+	case KEY_PGUP:
+	case 'P':
+	case 'b':
+	case Ctrl('B'):
+	    if(num) {
+		num -= p_lines;
+		break;
+	    }
+	case KEY_END:
+	case '$':
+	    num = brdnum - 1;
+	    break;
+	case ' ':
+	case KEY_PGDN:
+	case 'N':
+	case Ctrl('F'):
+	    if(num == brdnum - 1)
+		num = 0;
+	    else
+		num += p_lines;
+	    break;
+	case Ctrl('C'):
+	    cal();
+	    show_brdlist(head, 1, newflag);
+	    break;
+	case Ctrl('I'):
+	    t_idle();
+	    show_brdlist(head, 1, newflag);
+	    break;
+	case KEY_UP:
+	case 'p':
+	case 'k':
+	    if (num-- <= 0)
+		num = brdnum - 1;
+	    break;
+	case 't':
+		ptr = &nbrd[num];
+                ptr->myattr ^= BRD_TAG;
+                favbuf[ptr->bid-1]=ptr->myattr;
+		head = 9999;
+	case KEY_DOWN:
+	case 'n':
+	case 'j':
+	    if (++num < brdnum)
+		break;
+	case '0':
+	case KEY_HOME:
+	    num = 0;
+	    break;
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	    if((tmp = search_num(ch, brdnum)) >= 0)
+		num = tmp;
+	    brdlist_foot();
+	    break;
+        case 'F':
+        case 'f':
+	    if(class_bid && HAS_PERM(PERM_SYSOP))
+	     {
+	      bcache[class_bid-1].firstchild[cuser.uflag&BRDSORT_FLAG?1:0]
+									=NULL; 
+	      brdnum = -1;
+	     }
+	    break;
+	case 'h':
+	    show_help(choosebrdhelp);
+	    show_brdlist(head, 1, newflag);
+	    break;
+	case '/':
+	    getdata_buf(b_lines-1,0,"�п�J�ݪO��������r:",keyword, 12, DOECHO);
+	    brdnum=-1;
+	    break;
+	case 'S':
+	    cuser.uflag ^= BRDSORT_FLAG;
+	    brdnum = -1;
+	    break;
+	case 'y':
+            if(class_bid==0)
+	      yank_flag = (yank_flag+1)%3; 
+            else
+              yank_flag = yank_flag%2+1;
+	    brdnum = -1;
+	    break;
+	case Ctrl('D'):
+	      for(tmp = 0; tmp < numboards; tmp++)
+	      {
+		     if(favbuf[tmp] & BRD_TAG)
+	             {
+		       favbuf[tmp] &= ~BRD_FAV;
+		       favbuf[tmp] &= ~BRD_TAG; 
+		     }
+	      }
+              brdnum = -1;
+              break;
+	case Ctrl('A'):
+	      for(tmp = 0; tmp < numboards; tmp++)
+	      {
+		     if(favbuf[tmp] & BRD_TAG)
+	             {
+		       favbuf[tmp] |= BRD_FAV;
+		       favbuf[tmp] &= ~BRD_TAG; 
+		     }
+	      } 
+              brdnum = -1;
+	      break;
+	case Ctrl('T'):
+              for(tmp = 0; tmp < numboards; tmp++)
+		       favbuf[tmp] &= ~BRD_TAG;
+              brdnum = -1;
+	      break;
+        case Ctrl('P'):
+	    if(class_bid!=0 &&
+               (HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU))) {
+              for(tmp = 0; tmp < numboards; tmp++) {
+                   boardheader_t *bh=&bcache[tmp];
+                   if(!(favbuf[tmp]&BRD_TAG) || bh->gid==class_bid)
+                          continue;
+                   favbuf[tmp] &= ~BRD_TAG;
+                   if(bh->gid != class_bid)
+			{
+                          bh->gid = class_bid;
+                          substitute_record(FN_BOARD, bh,
+                                 sizeof(boardheader_t), tmp+1);
+                          reset_board(tmp+1);
+                          log_usies("SetBoardGID", bh->brdname);
+			}
+                 }
+               brdnum = -1;
+            }
+            break;
+	case 'm':
+	    if(HAS_PERM(PERM_BASIC)) {
+		ptr = &nbrd[num];
+                ptr->myattr ^= BRD_FAV;
+                favbuf[ptr->bid-1]=ptr->myattr;
+		head = 9999;
+	    }
+	    break;
+	case 'z':
+	    if(HAS_PERM(PERM_BASIC)) {
+                dozap(num);
+		head = 9999;
+	    }
+	    break;
+	case 'Z':                   /* Ptt */
+	    if(HAS_PERM(PERM_BASIC)) {
+		for(tmp = 0; tmp < brdnum; tmp++) {
+                    dozap(tmp);
+		}
+		head = 9999;
+	    }
+	    break;
+	case 'v':
+	case 'V':
+	    ptr = &nbrd[num];
+	    brc_initial(ptr->bh->brdname);
+	    if(ch == 'v') {
+		ptr->myattr &= ~BRD_UNREAD;
+		zapbuf[ptr->bid-1] = time((time_t *) &brc_list[0]);
+	    } else
+            {
+		zapbuf[ptr->bid-1] = brc_list[0] = 1;
+                ptr->myattr |= BRD_UNREAD;
+            } 
+	    brc_num = brc_changed = 1;
+	    brc_update();
+	    show_brdlist(head, 0, newflag);
+	    break;
+	case 's':
+	    if((tmp = search_board()) == -1) {
+		show_brdlist(head, 1, newflag);
+		break;
+	    }
+	    num = tmp;
+	case 'E':
+	    if(HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU)) {
+		ptr = &nbrd[num];
+		move(1,1);
+		clrtobot();
+		m_mod_board(ptr->bh->brdname);
+		brdnum = -1;
+	    }
+	    break;
+	case 'R':
+	    if(HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU)) {
+		m_newbrd(1);
+		brdnum = -1;
+	    }
+	    break;
+	case 'B':
+	    if(HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU)) {
+		m_newbrd(0);
+		brdnum = -1;
+	    }
+	    break;
+	case 'W':
+	    if(class_bid > 0 && 
+	       (HAS_PERM(PERM_SYSOP) || (currmode & MODE_MENU))) {
+		b_note_edit_bname(class_bid);
+		brdnum = -1;
+	    }
+	    break;
+	case KEY_RIGHT:
+	case '\n':
+	case '\r':
+	case 'r':
+	{
+	    char buf[STRLEN];
+	    
+	    ptr = &nbrd[num];
+	    
+	    if(!(ptr->bh->brdattr & BRD_GROUPBOARD)) {    /* �Dsub class */
+		if(!(ptr->bh->brdattr & BRD_HIDE) ||
+		   (ptr->bh->brdattr & BRD_POSTMASK)) {
+		    brc_initial(ptr->bh->brdname);
+
+		    if(newflag) {
+			setbdir(buf, currboard);
+			tmp = unread_position(buf, ptr);
+			head = tmp - t_lines / 2;
+			getkeep(buf, head > 1 ? head : 1, tmp + 1);
+		    }
+		    board_visit_time = zapbuf[ptr->bid-1];
+		    if(!(ptr->myattr&BRD_ZAP))
+			time((time_t *) &zapbuf[ptr->bid-1]);
+		    Read();
+		    check_newpost(ptr);
+		    head = -1;
+		    setutmpmode(newflag ? READNEW : READBRD);
+		} else {
+		    setbfile(buf, ptr->bh->brdname, FN_APPLICATION);
+		    if(more(buf,YEA)==-1) {
+			move(1,0);
+			clrtobot();
+			outs(privateboard);
+			pressanykey();
+		    }
+		    head = -1;
+		}
+	    } else {                                  /* sub class */
+		move(12,1);
+                bidtmp = class_bid;
+                currmodetmp =currmode;
+		tmp1=num; 
+		num=0;
+                class_bid = ptr->bid;
+		if (!(currmode & MODE_MENU))/*�p�G�٨S���p�ժ��v�� */
+		   set_menu_BM(ptr->bh->BM);
+
+		if(time(NULL) < ptr->bh->bupdate) {
+			setbfile(buf, ptr->bh->brdname, fn_notes);
+			if(more(buf, NA) != -1)
+ 	 		   pressanykey();
+		}              
+		tmp=currutmp->brc_id;
+		currutmp->brc_id=ptr->bid;
+		choose_board(0);
+		currmode  = currmodetmp;	/* ���}������N���v�������� */
+		num=tmp1;
+		class_bid = bidtmp;
+		currutmp->brc_id=tmp;
+		brdnum = -1;
+	    }
+	}
+	}
+    } while(ch != 'q');
+    save_brdbuf();
+}
+
+int root_board() {
+    class_bid = 1;
+    yank_flag = 1;
+    choose_board(0);
+    return 0;
+}
+
+int Boards() {
+    class_bid = 0;
+    yank_flag = 0; 
+    choose_board(0);
+    return 0;
+}
+
+
+int New() {
+    int mode0 = currutmp->mode;
+    int stat0 = currstat;
+
+    class_bid = 0;
+    choose_board(1);
+    currutmp->mode = mode0;
+    currstat = stat0;
+    return 0;
+}
+
+/*
+int v_favorite(){
+    char fname[256];
+    char inbuf[2048];
+    FILE* fp;
+    int nGroup;
+    char* strtmp;
+    
+    setuserfile(fname,str_favorite);
+    
+    if (!(fp=fopen(fname,"r")))
+        return -1;
+    move(0,0);
+    clrtobot();
+    fgets(inbuf,sizeof(inbuf),fp);
+    nGroup=atoi(inbuf);
+    
+    currutmp->nGroup=0;
+    currutmp->ninRoot=0;
+    
+    while(nGroup!=currutmp->nGroup+1){
+        fgets(inbuf,sizeof(inbuf),fp);
+        prints("%s\n",strtmp=strtok(inbuf," \n"));
+        strcpy(currutmp->gfavorite[currutmp->nGroup++],strtmp);
+        while((strtmp=strtok(NULL, " \n"))){
+            prints("     %s %d\n",strtmp,getbnum(strtmp));
+        }
+        currutmp->nGroup++;
+    }
+    prints("+++%d+++\n",currutmp->nGroup);
+    
+    fgets(inbuf,sizeof(inbuf),fp);
+    
+    for(strtmp=strtok(inbuf, " \n");strtmp;strtmp=strtok(NULL, " \n")){
+        if (strtmp[0]!='#')
+            prints("*** %s %d\n",strtmp, getbnum(strtmp));
+        else
+            prints("*** %s %d\n",strtmp+1, -1);
+        currutmp->ninRoot++;
+    }
+    
+    fclose(fp);
+    pressanykey();
+    return 0;
+} 
+*/
diff --git a/mbbsd/cache.c b/mbbsd/cache.c
new file mode 100644
index 00000000..73f8ac5b
--- /dev/null
+++ b/mbbsd/cache.c
@@ -0,0 +1,1053 @@
+/* $Id: cache.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+
+#ifdef __FreeBSD__
+#include <machine/param.h>
+#endif
+
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+
+#ifndef __FreeBSD__
+union semun {
+    int val;                    /* value for SETVAL */
+    struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
+    unsigned short int *array;  /* array for GETALL, SETALL */
+    struct seminfo *__buf;      /* buffer for IPC_INFO */
+};
+#endif
+
+int fcache_semid;
+
+/* the reason for "safe_sleep" is that we may call sleep during
+   SIGALRM handler routine, while SIGALRM is blocked.
+   if we use the original sleep, we'll never wake up. */
+unsigned int safe_sleep(unsigned int seconds) {
+    /* jochang  sleep�����D�ɥ�*/
+    sigset_t set,oldset;
+    
+    sigemptyset(&set);
+    sigprocmask(SIG_BLOCK, &set, &oldset);
+    if(sigismember(&oldset, SIGALRM)) {
+	unsigned long retv;
+#if !defined(_BBS_UTIL_C_)
+	log_usies("SAFE_SLEEP ", "avoid hang");
+#endif
+	sigemptyset(&set);
+	sigaddset(&set,SIGALRM);
+	sigprocmask(SIG_UNBLOCK,&set,NULL);
+	retv=sleep(seconds);
+	sigprocmask(SIG_BLOCK,&set,NULL);
+	return retv;
+    }
+    return sleep(seconds);
+}
+
+#if defined(_BBS_UTIL_C_)
+static void setapath(char *buf, char *boardname) {
+    sprintf(buf, "man/boards/%s", boardname);
+}
+
+static char *str_dotdir = ".DIR";
+
+static void setadir(char *buf, char *path) {
+    sprintf(buf, "%s/%s", path, str_dotdir);
+}
+#endif
+
+static void attach_err(int shmkey, char *name) {
+    fprintf(stderr, "[%s error] key = %x\n", name, shmkey);
+    fprintf(stderr, "errno = %d: %s\n", errno, strerror(errno));
+    exit(1);
+}
+
+static void *attach_shm(int shmkey, int shmsize) {
+    void *shmptr;
+    int shmid;
+
+    char *empty_addr;
+    /* set up one page in-accessible -- jochang */
+    {
+	int fd = open("/dev/zero",O_RDONLY);
+	int size = ((shmsize + 4095) / 4096) * 4096;
+	
+	munmap(
+	    (empty_addr=mmap(0,4096+size,PROT_NONE,MAP_PRIVATE,fd,0))+4096
+	    ,size);
+	
+	close(fd);
+    }
+    
+    shmid = shmget(shmkey, shmsize, 0);
+    if(shmid < 0) {
+	shmid = shmget(shmkey, shmsize, IPC_CREAT | 0600);
+	if(shmid < 0)
+	    attach_err(shmkey, "shmget");
+	shmptr = (void *)shmat(shmid, NULL, 0);
+	if(shmptr == (void *)-1)
+	    attach_err(shmkey, "shmat");
+    } else {
+	shmptr = (void *)shmat(shmid, NULL, 0);
+	if(shmptr == (void *)-1)
+	    attach_err(shmkey, "shmat");
+    }
+    
+    /* unmap the page -- jochang */
+    {
+	munmap(empty_addr,4096);		
+    }
+    return shmptr;
+}
+
+
+#define SEM_FLG        0600    /* semaphore mode */
+
+/* ----------------------------------------------------- */
+/* semaphore : for critical section                      */
+/* ----------------------------------------------------- */
+void sem_init(int semkey,int *semid) {
+    union semun s;
+
+    s.val=1;
+    *semid = semget(semkey, 1, 0);
+    if(*semid == -1) {
+	*semid = semget(semkey, 1, IPC_CREAT | SEM_FLG);
+	if(*semid == -1)
+	    attach_err(semkey, "semget");
+	semctl(*semid, 0, SETVAL, s);
+    }
+}
+
+void sem_lock(int op,int semid) {
+    struct sembuf sops;
+
+    sops.sem_num = 0;
+    sops.sem_flg = SEM_UNDO;
+    sops.sem_op = op;
+    semop(semid, &sops, 1);
+}
+
+/* uhash *******************************************/
+/* the design is this:
+   we use another stand-alone program to create and load data into the hash.
+   (that program could be run in rc-scripts or something like that)
+   after loading completes, the stand-alone program sets loaded to 1 and exits.
+   
+   the bbs exits if it can't attach to the shared memory or 
+   the hash is not loaded yet.
+*/
+uhash_t *uhash;
+
+/* attach_uhash should be called before using uhash */
+void attach_uhash() {
+    uhash = attach_shm(UHASH_KEY, sizeof(*uhash));
+    if(!uhash->loaded)	/* assume fresh shared memory is zeroed */
+	exit(1);
+}
+
+void add_to_uhash(int n, char *id) {
+    int *p, h = StringHash(id);
+    strcpy(uhash->userid[n], id);
+    
+    p = &(uhash->hash_head[h]);
+			
+    while(*p != -1)
+	p = &(uhash->next_in_hash[*p]);
+				
+    uhash->next_in_hash[*p = n] = -1;
+}
+
+/* note: after remove_from_uhash(), you should add_to_uhash()
+   (likely with a different name) */
+void remove_from_uhash(int n) {
+    int h = StringHash(uhash->userid[n]);
+    int *p = &(uhash->hash_head[h]);
+    
+    while(*p != -1 && *p != n)
+	p = &(uhash->next_in_hash[*p]);
+    if(*p == n)
+	*p = uhash->next_in_hash[n];
+}
+
+int setumoney(int uid, int money) {
+   uhash->money[uid-1]=money;
+   passwd_update_money(uid);
+   return uhash->money[uid-1];
+}
+
+int deumoney(int uid, int money) {
+   if(money<0 && uhash->money[uid-1]<-money)
+       return setumoney(uid,0); 
+   else
+       return setumoney(uid,uhash->money[uid-1]+money);
+}
+int demoney(int money) {
+  extern int usernum;
+  return deumoney(usernum,money);
+} 
+int moneyof(int uid){   /* ptt ��i�����B�z�IJv */
+   return uhash->money[uid-1]; 
+}
+int searchuser(char *userid) {
+    int h,p;
+    
+    h = StringHash(userid);
+    p = uhash->hash_head[h];
+	
+    while(p != -1) {
+	if(strcasecmp(uhash->userid[p],userid) == 0) {
+	    strcpy(userid,uhash->userid[p]);
+	    return p + 1;
+	}
+	p = uhash->next_in_hash[p];
+    }
+    return 0;
+}
+
+#if !defined(_BBS_UTIL_C_)
+extern userec_t xuser;
+
+int getuser(char *userid) {
+    int uid;
+    
+    if((uid = searchuser(userid)))
+	passwd_query(uid, &xuser);
+    return uid;
+}
+
+char *getuserid(int num) {
+    if(--num >= 0 && num < MAX_USERS)
+	return ((char *) uhash->userid[num]);
+    return NULL;
+}
+
+void setuserid(int num, char *userid) {
+    if(num > 0 && num <= MAX_USERS) {
+	if(num > uhash->number)
+	    uhash->number = num;
+	else
+	    remove_from_uhash(num-1);
+	add_to_uhash(num-1,userid);
+    }
+}
+
+/* 0 ==> ��L���b�� */
+/* 1 ==> �إ߷s�b�� */
+/* should do it by searching "" in the hash */
+int searchnewuser(int mode) {
+    register int i, num;
+
+    num = uhash->number;
+    i = 0;
+
+    /* ������o�䤣�� hash table �h��ӭn�� linear search? */
+    while(i < num) {
+	if(!uhash->userid[i++][0])
+	    return i;
+    }
+    if(mode && (num < MAX_USERS))
+	return num + 1;
+    return 0;
+}
+
+char *u_namearray(char buf[][IDLEN + 1], int *pnum, char *tag) {
+    register struct uhash_t *reg_ushm = uhash;
+    register char *ptr, tmp;
+    register int n, total;
+    char tagbuf[STRLEN];
+    int ch, ch2, num;
+
+    if(*tag == '\0') {
+	*pnum = reg_ushm->number;
+	return reg_ushm->userid[0];
+    }
+    for(n = 0; tag[n]; n++)
+	tagbuf[n] = chartoupper(tag[n]);
+    tagbuf[n] = '\0';
+    ch = tagbuf[0];
+    ch2 = ch - 'A' + 'a';
+    total = reg_ushm->number;
+    for(n = num = 0; n < total; n++) {
+	ptr = reg_ushm->userid[n];
+	tmp = *ptr;
+	if(tmp == ch || tmp == ch2) {
+	    if(chkstr(tag, tagbuf, ptr))
+		strcpy(buf[num++], ptr);
+	}
+    }
+    *pnum = num;
+    return buf[0];
+}
+#endif
+
+/*-------------------------------------------------------*/
+/* .UTMP cache                                           */
+/*-------------------------------------------------------*/
+struct utmpfile_t *utmpshm=NULL;
+
+void resolve_utmp() {
+    if(utmpshm == NULL) {
+	utmpshm = attach_shm(UTMPSHM_KEY, sizeof(*utmpshm));
+    }
+}
+
+userinfo_t *currutmp = NULL;
+
+#if !defined(_BBS_UTIL_C_)
+extern unsigned int currstat;
+extern userec_t cuser;
+
+void setutmpmode(int mode) {
+    if(currstat != mode)
+	currutmp->mode = currstat = mode;
+    
+    /* �l�ܨϥΪ� */
+    if(HAS_PERM(PERM_LOGUSER)) {
+	time_t now = time(NULL);
+	char msg[200];
+	sprintf(msg, "%s setutmpmode to %s(%d) at %s",
+		cuser.userid, modestring(currutmp, 0), mode, Cdate(&now));
+	log_user(msg);
+    }
+}
+#endif
+/*
+static int cmputmpuserid(userinfo_t ** i, userinfo_t ** j) {
+ return strcasecmp((*i)->userid, (*j)->userid);
+}
+
+static int cmputmpmode(userinfo_t ** i, userinfo_t ** j) {
+ return (*i)->mode-(*j)->mode;
+} 
+
+static int cmputmpidle(userinfo_t ** i, userinfo_t ** j) {
+ return (*i)->lastact-(*j)->lastact;
+}
+
+static int cmputmpfrom(userinfo_t ** i, userinfo_t ** j) {
+ return strcasecmp((*i)->from, (*j)->from);
+} 
+
+static int cmputmpfive(userinfo_t ** i, userinfo_t ** j) {
+ int type;
+ if((type=(*j)->five_win - (*i)->five_win))
+     return type;
+ if((type=(*i)->five_lose - (*j)->five_lose))
+     return type;
+ return (*i)->five_tie-(*j)->five_tie;
+} 
+static int cmputmpsex(userinfo_t ** i, userinfo_t ** j) {
+ static int ladyfirst[]={1,0,1,0,1,0,3,3};
+ return ladyfirst[(*i)->sex]-ladyfirst[(*j)->sex];
+}
+static int cmputmppid(userinfo_t ** i, userinfo_t ** j) {
+ return (*i)->pid-(*j)->pid;
+}
+static int cmputmpuid(userinfo_t ** i, userinfo_t ** j) {
+ return (*i)->uid-(*j)->uid;
+}
+*/
+static int cmputmpuserid(const void *i, const void *j){
+  return strcasecmp((*((userinfo_t**)i))->userid, (*((userinfo_t**)j))->userid);
+}
+
+static int cmputmpmode(const void *i, const void *j){
+  return (*((userinfo_t**)i))->mode-(*((userinfo_t**)j))->mode;
+} 
+
+static int cmputmpidle(const void *i, const void *j){
+  return (*((userinfo_t**)i))->lastact-(*((userinfo_t**)j))->lastact;
+} 
+
+static int cmputmpfrom(const void *i, const void *j){
+  return strcasecmp((*((userinfo_t**)i))->from, (*((userinfo_t**)j))->from);
+} 
+
+static int cmputmpfive(const void *i, const void *j){
+  int type;
+  if((type=(*((userinfo_t**)j))->five_win - (*((userinfo_t**)i))->five_win))
+    return type;
+  if((type=(*((userinfo_t**)i))->five_lose - (*((userinfo_t**)j))->five_lose))
+     return type;
+  return (*((userinfo_t**)i))->five_tie-(*((userinfo_t**)j))->five_tie;
+} 
+
+static int cmputmpsex(const void *i, const void *j){
+ static int ladyfirst[]={1,0,1,0,1,0,3,3};
+ return ladyfirst[(*((userinfo_t**)i))->sex]-ladyfirst[(*((userinfo_t**)j))->sex];
+}
+static int cmputmppid(const void *i, const void *j){
+ return (*((userinfo_t**)i))->pid-(*((userinfo_t**)j))->pid;
+}
+static int cmputmpuid(const void *i, const void *j){
+ return (*((userinfo_t**)i))->uid-(*((userinfo_t**)j))->uid;
+}
+void sort_utmp()
+{
+    time_t now=time(NULL);
+    int count, i, ns;
+    userinfo_t *uentp;
+
+    if(now-utmpshm->uptime<60 && (now==utmpshm->uptime || utmpshm->busystate))
+           return; /* lazy sort */
+    utmpshm->busystate=1;
+    utmpshm->uptime = now;
+    ns=(utmpshm->currsorted?0:1);
+
+    for(uentp = &utmpshm->uinfo[0], count=0, i=0;
+            i< USHM_SIZE; i++,uentp = &utmpshm->uinfo[i])
+      if(uentp->pid) 
+        {
+         utmpshm->sorted[ns][0][count++]= uentp;
+        }
+    utmpshm->number = count;
+    qsort(utmpshm->sorted[ns][0],count,sizeof(userinfo_t*),cmputmpuserid);
+    memcpy(utmpshm->sorted[ns][1],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][2],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][3],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][4],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][5],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][6],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    memcpy(utmpshm->sorted[ns][7],utmpshm->sorted[ns][0],
+						 sizeof(userinfo_t *)*count);
+    qsort(utmpshm->sorted[ns][1], count, sizeof(userinfo_t *), cmputmpmode );
+    qsort(utmpshm->sorted[ns][2], count, sizeof(userinfo_t *), cmputmpidle );
+    qsort(utmpshm->sorted[ns][3], count, sizeof(userinfo_t *), cmputmpfrom );
+    qsort(utmpshm->sorted[ns][4], count, sizeof(userinfo_t *), cmputmpfive );
+    qsort(utmpshm->sorted[ns][5], count, sizeof(userinfo_t *), cmputmpsex );
+    qsort(utmpshm->sorted[ns][6], count, sizeof(userinfo_t *), cmputmpuid );
+    qsort(utmpshm->sorted[ns][7], count, sizeof(userinfo_t *), cmputmppid );
+    utmpshm->currsorted=ns;
+    utmpshm->busystate=0;
+}
+// Ptt:�o��[�Jhash�[�� ��Ū�utmp
+void getnewutmpent(userinfo_t *up) {
+    extern int errno;
+    register int i, p ;
+    register userinfo_t *uentp;
+    for(i = 0, p=StringHash(up->userid)%USHM_SIZE; i < USHM_SIZE; i++, p++) {
+	if(p==USHM_SIZE) p=0;
+	uentp = &(utmpshm->uinfo[p]);
+	if(!(uentp->pid)) {
+	    memcpy(uentp, up, sizeof(userinfo_t));
+	    currutmp = uentp;
+	    sort_utmp();
+	    return;
+	}
+    }
+    exit(1);
+}
+
+int apply_ulist(int (*fptr)(userinfo_t *)) {
+    register userinfo_t *uentp;
+    register int i, state;
+
+    for(i = 0; i < USHM_SIZE; i++) {
+	uentp = &(utmpshm->uinfo[i]);
+	if(uentp->pid && (PERM_HIDE(currutmp) || !PERM_HIDE(uentp)))
+	    if((state = (*fptr) (uentp)))
+		return state;
+    }
+    return 0;
+}
+
+userinfo_t *search_ulist(int uid) {
+    return search_ulistn(uid,1);
+}
+
+#if !defined(_BBS_UTIL_C_)           
+extern int usernum;
+
+userinfo_t *search_ulist_pid(int pid) {
+    register int i=0, j, start = 0, end = utmpshm->number - 1;
+    register userinfo_t **ulist;
+    ulist=utmpshm->sorted[utmpshm->currsorted][7];
+    for(i=((start+end)/2);  ;i=(start+end)/2)
+     {
+         j=pid-ulist[i]->pid;
+         if(!j)
+           {
+              return (userinfo_t *) (ulist[i]);
+           }
+         if(end==start)
+           {
+               break;
+           }
+	else if(i==start)
+	   {
+	     i=end;
+	     start=end;
+	   }
+	else if(j>0) start = i;
+	else    end   = i;
+     }
+    return 0;
+}
+userinfo_t *search_ulistn(int uid, int unum) {
+    register int i=0, j, start = 0, end = utmpshm->number - 1;
+    register userinfo_t **ulist;
+    ulist=utmpshm->sorted[utmpshm->currsorted][6];
+    for(i=((start+end)/2);  ;i=(start+end)/2)
+     {
+         j= uid - ulist[i]->uid;
+         if(!j)
+           {
+                 for(;i>0 && uid==ulist[i-1]->uid;i--);/* ����Ĥ@�� */
+                 if(uid==ulist[i+unum-1]->uid) 
+                    return (userinfo_t *) (ulist[i+unum-1]);
+                 break; /* �W�L�d�� */
+           }
+         if(end==start)
+           {
+               break;
+           }       
+	else if(i==start)
+	   {
+	     i=end;
+	     start=end;
+	   }
+	else if(j>0) start = i;
+	else    end   = i;
+     }
+    return 0;
+}
+
+int count_logins(int uid, int show) {
+    register int i=0, j, start = 0, end = utmpshm->number - 1, count;
+    register userinfo_t **ulist;
+    ulist=utmpshm->sorted[utmpshm->currsorted][6];
+    for(i=((start+end)/2);  ;i=(start+end)/2)
+     {
+         j = uid-ulist[i]->uid;
+         if(!j)
+           {
+                 for(;i>0 && uid==ulist[i-1]->uid;i--);/* ����Ĥ@�� */
+                 for(count=0;uid==ulist[i+count]->uid;count++)
+                     {
+			if(show)
+			 prints("(%d) �ثe���A��: %-17.16s(�Ӧ� %s)\n",
+			         count+1, modestring(ulist[i+count], 0),
+                                 ulist[i+count]->from);
+		     }  
+                 return count;
+           }
+         if(end==start)
+           {
+               break;
+           }       
+	else if(i==start)
+	   {
+	     i=end;
+	     start=end;
+	   }
+	else if(j>0) start = i;
+	else    end   = i;
+     }
+    return 0;
+}
+
+
+void purge_utmp(userinfo_t *uentp) {
+    logout_friend_online();
+    memset(uentp, 0, sizeof(userinfo_t));
+}
+
+#endif
+
+/*-------------------------------------------------------*/
+/* .BOARDS cache                                         */
+/*-------------------------------------------------------*/
+extern char *fn_board;
+extern int currbid;
+bcache_t *brdshm;
+boardheader_t *bcache;
+
+void touchdircache(int bid)
+{
+    int *i= (int *)&brdshm->dircache[bid - 1][0].filename[0];
+    *i=0;
+}       
+ 
+void load_fileheader_cache(int bid, char *direct)
+{
+    int num=getbtotal(bid);
+    int n = num-DIRCACHESIZE+1;
+    if (brdshm->busystate!=1)
+     {
+       brdshm->busystate = 1;
+       get_records(direct, brdshm->dircache[bid - 1] ,
+                sizeof(fileheader_t),n<1?1:n, DIRCACHESIZE);
+       brdshm->cachetotal[bid-1]=num; // cachetotal���] �H��A�藍�Υ���load
+       brdshm->busystate = 0;
+     }         
+    else 
+     {safe_sleep(1);}
+}
+
+int get_fileheader_cache(int bid,  char *direct, fileheader_t *headers, 
+			 int recbase, int nlines)
+{
+    int ret, n,num;
+
+    num=getbtotal(bid);
+
+    ret = num-recbase+1,
+    n = (num - DIRCACHESIZE+1); 
+
+
+    if(brdshm->dircache[bid - 1][0].filename[0]=='\0')
+	load_fileheader_cache(bid, direct);	
+    if (n<1)
+	n=recbase-1;
+    else
+	n=recbase-n;
+    if(n<0) n=0;
+    if (ret>nlines) ret=nlines; 
+    memcpy(headers, &(brdshm->dircache[bid - 1][n]),sizeof(fileheader_t)*ret);
+    return ret;
+}
+static int cmpboardname(boardheader_t **brd, boardheader_t **tmp) {
+    return strcasecmp((*brd)->brdname, (*tmp)->brdname);
+}              
+static int cmpboardclass(boardheader_t **brd, boardheader_t **tmp) {
+     return (strncmp((*brd)->title, (*tmp)->title, 4)<<8)+
+           strcasecmp((*brd)->brdname, (*tmp)->brdname); 
+}
+static void sort_bcache(){
+       int i;/*critical section �����W�I�s �I�sreload_bcache or reset_board */
+       for(i=0;i<brdshm->number;i++)
+         {
+           brdshm->sorted[1][i]=brdshm->sorted[0][i]=&bcache[i];
+         }
+       qsort(brdshm->sorted[0], brdshm->number, sizeof(boardheader_t *),
+             (QCAST)cmpboardname);   
+       qsort(brdshm->sorted[1], brdshm->number, sizeof(boardheader_t *),
+             (QCAST)cmpboardclass);
+}
+static void reload_bcache() {
+    if(brdshm->busystate) {
+	safe_sleep(1);
+    }
+#if !defined(_BBS_UTIL_C_)
+    else {
+	int fd,i;
+
+	brdshm->busystate = 1;
+	if((fd = open(fn_board, O_RDONLY)) > 0) {
+	    brdshm->number =
+		read(fd, bcache, MAX_BOARD * sizeof(boardheader_t)) /
+		sizeof(boardheader_t);
+	    close(fd);
+	}
+	memset(brdshm->lastposttime, 0, MAX_BOARD * sizeof(time_t));
+	/* ���Ҧ� boards ��Ƨ�s��A�]�w uptime */
+	brdshm->uptime = brdshm->touchtime;
+	log_usies("CACHE", "reload bcache");
+        sort_bcache();
+        for(i=0;i<brdshm->number;i++)
+           {
+             bcache[i].firstchild[0]=NULL;
+             bcache[i].firstchild[1]=NULL;
+           }
+	brdshm->busystate = 0;
+    }
+#endif
+}
+
+int numboards = -1;
+
+void resolve_boards() {
+    if(brdshm == NULL) {
+	brdshm = attach_shm(BRDSHM_KEY, sizeof(*brdshm));
+	if(brdshm->touchtime == 0)
+	    brdshm->touchtime = 1;
+	bcache = brdshm->bcache;
+    }
+
+    while(brdshm->uptime < brdshm->touchtime)
+	{reload_bcache();}
+    numboards = brdshm->number;
+}
+
+void touch_boards() {
+    time(&(brdshm->touchtime));
+    numboards = -1;
+    resolve_boards();  
+}
+void addbrd_touchcache()
+{
+      brdshm->number++;
+      numboards=brdshm->number;     
+      reset_board(numboards); 
+}
+#if !defined(_BBS_UTIL_C_)
+void reset_board(int bid) { /* Ptt: �o�˴N���ΦѬOtouch board�F */
+    int fd,i;
+    boardheader_t *bhdr;
+    
+    if(--bid < 0)
+	return;
+    if(brdshm->busystate) {
+	safe_sleep(1);
+    } else {
+	brdshm->busystate = 1;
+	bhdr = bcache;
+	bhdr += bid;       
+	if((fd = open(fn_board, O_RDONLY)) > 0) {
+	    lseek(fd, (off_t)(bid *  sizeof(boardheader_t)), SEEK_SET);
+	    read(fd, bhdr, sizeof(boardheader_t));
+	    close(fd);
+	}
+        sort_bcache();
+        for(i=0;i<brdshm->number;i++)
+           {
+             bcache[i].firstchild[0]=NULL;
+             bcache[i].firstchild[1]=NULL;
+           }
+	brdshm->busystate = 0;
+    }
+}   
+
+int apply_boards(int (*func)(boardheader_t *)) {
+    register int i;
+    register boardheader_t *bhdr;
+    
+    for(i = 0, bhdr = bcache; i < numboards; i++, bhdr++) {
+	if(!(bhdr->brdattr & BRD_GROUPBOARD) && Ben_Perm(bhdr) && 
+	   (*func)(bhdr) == QUIT)
+	    return QUIT;
+    }
+    return 0;
+}
+#endif
+
+boardheader_t *getbcache(int bid) { /* Ptt��g */
+    return bcache + bid - 1;
+}
+int getbtotal(int bid)
+{
+  return brdshm->total[bid - 1];
+}
+void setbtotal(int bid) {
+    boardheader_t *bh = getbcache(bid);
+    struct stat st;
+    char genbuf[256];
+    int num,fd;
+
+    sprintf(genbuf, "boards/%s/.DIR", bh->brdname);
+
+    if((fd = open(genbuf, O_RDWR)) < 0)
+            return; /* .DIR���F */
+    fstat(fd, &st); 
+    num =  st.st_size / sizeof(fileheader_t);
+    brdshm->total[bid - 1] = num;
+
+    if(num>0)
+     {
+       lseek(fd, (off_t) (num - 1) * sizeof(fileheader_t), SEEK_SET);
+       if(read(fd, genbuf, FNLEN)>=0)
+	{
+	  brdshm->lastposttime[bid - 1]=(time_t) atoi(&genbuf[2]);
+	}
+     }
+    else
+        brdshm->lastposttime[bid - 1] = 0; 
+    close(fd);
+    if(num)
+       touchdircache(bid);
+}
+
+void
+touchbpostnum(int bid, int delta) {
+  int *total = &brdshm->total[bid - 1];
+  if (*total) *total += delta;
+}
+
+
+int getbnum(char *bname) {
+    register int i=0, j, start = 0, end = brdshm->number - 1;
+    register boardheader_t **bhdr;
+    bhdr=brdshm->sorted[0]; 
+    for(i=((start+end)/2);  ;i=(start+end)/2)
+     {
+         if(! (j=strcasecmp(bname,bhdr[i]->brdname)))
+		 return (int) (bhdr[i] - bcache +1);
+         if(end==start)
+           {
+               break;
+           }
+         else if(i==start)
+           {
+             i=end;
+             start=end;
+           }
+         else if(j>0) start = i;
+         else    end   = i;
+     } 
+    return 0;
+}
+
+#if !defined(_BBS_UTIL_C_)
+extern char *fn_water;
+extern char *str_sysop;
+
+int haspostperm(char *bname) {
+    register int i;
+    char buf[200];
+
+    setbfile(buf, bname, fn_water);
+    if(belong(buf, cuser.userid))
+	return 0;
+
+    if(!strcasecmp(bname, DEFAULT_BOARD))
+	return 1;
+
+    if (!strcasecmp(bname, "PttLaw"))
+        return 1;
+
+    if(!HAS_PERM(PERM_POST))
+	return 0;
+    
+    if(!(i = getbnum(bname)))
+	return 0;
+
+    /* ���K�ݪO�S�O�B�z */
+    if(bcache[i - 1].brdattr & BRD_HIDE)
+	return 1;
+
+    i = bcache[i - 1].level;
+
+    if (HAS_PERM(PERM_VIOLATELAW) && (i & PERM_VIOLATELAW))
+        return 1;
+    else if (HAS_PERM(PERM_VIOLATELAW))
+        return 0;
+
+    return HAS_PERM(i & ~PERM_POST);
+}
+#endif
+
+/*-------------------------------------------------------*/
+/* PTT  cache                                            */
+/*-------------------------------------------------------*/
+/* cachefor �ʺA�ݪ� */
+struct pttcache_t *ptt;
+
+static void reload_pttcache() {
+    if(ptt->busystate)
+	safe_sleep(1);
+    else {				/* jochang: temporary workaround */
+	fileheader_t item, subitem;
+	char pbuf[256], buf[256], *chr;
+	FILE *fp, *fp1, *fp2;
+	int id, section = 0;
+
+	ptt->busystate = 1;
+	ptt->max_film = 0;
+	bzero(ptt->notes, sizeof ptt->notes);
+	setapath(pbuf, "Note");
+	setadir(buf, pbuf);
+	id = 0;
+	if((fp = fopen(buf, "r"))) {
+	    while(fread(&item, sizeof(item), 1, fp)) {
+		if(item.title[3]=='<' && item.title[8]=='>') {
+		    sprintf(buf,"%s/%s", pbuf, item.filename);
+		    setadir(buf, buf);
+		    if(!(fp1 = fopen(buf, "r")))
+			continue;
+		    ptt->next_refresh[section] = ptt->n_notes[section] = id;
+		    section ++;
+		    while(fread(&subitem, sizeof(subitem), 1, fp1)) {
+			sprintf(buf,"%s/%s/%s", pbuf, item.filename ,
+				subitem.filename);
+			if(!(fp2=fopen(buf,"r")))
+			    continue;
+			fread(ptt->notes[id],sizeof(char), 200*11, fp2);
+			ptt->notes[id][200*11 - 1]=0;
+			id++;
+			fclose(fp2);
+			if(id >= MAX_MOVIE)
+			    break;  
+		    }
+		    fclose(fp1);	   
+		    if(id >= MAX_MOVIE || section >= MAX_MOVIE_SECTION)
+			break;	  
+		}
+	    }
+	    fclose(fp);
+	}
+	ptt->next_refresh[section] = -1;
+	ptt->n_notes[section] = ptt->max_film = id-1;
+	ptt->max_history = ptt->max_film - 2;
+	if(ptt->max_history > MAX_HISTORY - 1)
+	    ptt->max_history = MAX_HISTORY - 1;
+	if(ptt->max_history <0) ptt->max_history=0;
+
+	fp = fopen("etc/today_is","r");
+	if(fp) {
+	    fgets(ptt->today_is,15,fp);
+	    if((chr = strchr(ptt->today_is,'\n')))
+		*chr = 0;
+	    ptt->today_is[15] = 0;
+	    fclose(fp);
+	}
+     
+	/* ���Ҧ���Ƨ�s��A�]�w uptime */
+
+	ptt->uptime = ptt->touchtime ;
+#if !defined(_BBS_UTIL_C_)
+	log_usies("CACHE", "reload pttcache");
+#endif
+	ptt->busystate = 0;
+    }
+}
+
+void resolve_garbage() {
+    int count=0;
+    
+    if(ptt == NULL) {
+	ptt = attach_shm(PTTSHM_KEY, sizeof(*ptt));
+	if(ptt->touchtime == 0)
+	    ptt->touchtime = 1;
+    }
+    while(ptt->uptime < ptt->touchtime) { /* ����while�� */
+	reload_pttcache();
+	if(count ++ > 10 && ptt->busystate) {
+/* Ptt: �o��|�����D  load�W�L10 ���|�Ҧ��iloop��process���� busystate = 0
+   �o�˷|�Ҧ�prcosee���|�bload �ʺA�ݪO �|�y��load�j�W
+   ���S���γo��function���� �U�@load passwd�ɪ�process���F �S�S���H��L
+   �Ѷ}  �P�˪����D�o�ͦbreload passwd
+*/    
+	    ptt->busystate = 0;
+#ifndef _BBS_UTIL_C_
+	    log_usies("CACHE", "refork Ptt dead lock");
+#endif
+	}
+    }
+}
+
+/*-------------------------------------------------------*/
+/* PTT's cache                                           */
+/*-------------------------------------------------------*/
+/* cachefor from host �P�̦h�W�u�H�� */
+struct fromcache_t *fcache;
+
+static void reload_fcache() {
+    if(fcache->busystate)
+	safe_sleep(1);
+    else {
+	FILE *fp;
+
+	fcache->busystate = 1;
+	bzero(fcache->domain, sizeof fcache->domain);
+	if((fp = fopen("etc/domain_name_query","r"))) {
+	    char buf[256],*po;
+
+	    fcache->top=0;
+	    while(fgets(buf, sizeof(buf),fp)) {
+		if(buf[0] && buf[0] != '#' && buf[0] != ' ' &&
+		   buf[0] != '\n') {
+		    sscanf(buf,"%s",fcache->domain[fcache->top]);
+		    po = buf + strlen(fcache->domain[fcache->top]);
+		    while(*po == ' ')
+			po++;
+		    strncpy(fcache->replace[fcache->top],po,49);
+		    fcache->replace[fcache->top]
+			[strlen(fcache->replace[fcache->top])-1] = 0;
+		    (fcache->top)++;
+		    if(fcache->top == MAX_FROM)
+			break;
+		}	
+	    }
+	}
+
+	fcache->max_user=0;
+
+	/* ���Ҧ���Ƨ�s��A�]�w uptime */
+	fcache->uptime = fcache->touchtime;
+#if !defined(_BBS_UTIL_C_)
+	log_usies("CACHE", "reload fcache");
+#endif
+	fcache->busystate = 0;
+    }
+}
+
+void resolve_fcache() {
+    if(fcache == NULL) {
+	fcache = attach_shm(FROMSHM_KEY, sizeof(*fcache));
+	if(fcache->touchtime == 0)
+	    fcache->touchtime = 1;
+    }
+    while(fcache->uptime < fcache->touchtime)
+	reload_fcache();
+}
+
+extern time_t login_start_time;
+extern char *fn_visable;
+FILE   *DEBUG = NULL;
+
+void hbflreload(int bid)
+{
+    int     hbfl[MAX_FRIEND + 1], i, num, uid;
+    char    buf[128];
+    FILE    *fp;
+
+    memset(hbfl, 0, sizeof(hbfl));
+    setbfile(buf, bcache[bid-1].brdname, fn_visable);
+    if( (fp = fopen(buf, "r")) != NULL ){
+	for( num = 1 ; num <= MAX_FRIEND ; ++num ){
+	    if( fgets(buf, sizeof(buf), fp) == NULL )
+		break;
+	    for( i = 0 ; buf[i] != 0 ; ++i )
+		if( buf[i] == ' ' ){
+		    buf[i] = 0;
+		    break;
+		}
+	    if( strcasecmp("guest", buf) == 0 ||
+		(uid = searchuser(buf))  == 0     ) {
+		--num;
+		continue;
+	    }
+	    hbfl[num] = uid;
+	}
+	fclose(fp);
+    }
+    hbfl[0] = time(NULL);
+    memcpy(uhash->hbfl[bid], hbfl, sizeof(hbfl));
+}
+
+int hbflcheck(int bid, int uid)
+{
+    int     i;
+
+    if( uhash->hbfl[bid][0] < login_start_time - HBFLexpire )
+	hbflreload(bid);
+    for( i = 1 ; uhash->hbfl[bid][i] != 0 && i <= MAX_FRIEND ; ++i ){
+	if( uhash->hbfl[bid][i] == uid )
+	    return 0;
+    }
+    return 1;
+}
diff --git a/mbbsd/cal.c b/mbbsd/cal.c
new file mode 100644
index 00000000..680ee9d6
--- /dev/null
+++ b/mbbsd/cal.c
@@ -0,0 +1,512 @@
+/* $Id: cal.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "perm.h"
+#include "proto.h"
+
+extern struct utmpfile_t *utmpshm;
+extern int usernum;
+
+/* ���� Multi play */
+static int count_multiplay(int unmode) {
+    register int i, j;
+    register userinfo_t *uentp;
+    extern struct utmpfile_t *utmpshm;
+
+    for(i = j = 0; i < USHM_SIZE; i++) {
+	uentp = &(utmpshm->uinfo[i]);
+	if(uentp->uid == usernum)
+	    if(uentp->lockmode == unmode)
+		j++;
+    }
+    return j;
+}
+
+extern userinfo_t *currutmp;
+extern char *ModeTypeTable[];
+
+int lockutmpmode(int unmode, int state) {
+    int errorno = 0;
+    
+    if(currutmp->lockmode)
+	errorno = 1;
+    else if(count_multiplay(unmode))
+	errorno = 2;
+    
+    if(errorno && !(state == LOCK_THIS && errorno == LOCK_MULTI)) {
+	clear();
+	move(10,20);
+	if(errorno == 1)
+	    prints("�����} %s �~��A %s ",
+		   ModeTypeTable[currutmp->lockmode],
+		   ModeTypeTable[unmode]);
+	else 
+	    prints("��p! �z�w����L�u�ۦP��ID���b%s",
+		   ModeTypeTable[unmode]);
+	pressanykey();
+	return errorno;
+    }
+    
+    setutmpmode(unmode);
+    currutmp->lockmode = unmode;
+    return 0;
+}
+
+int unlockutmpmode() {
+    currutmp->lockmode = 0;
+    return 0;
+}
+
+extern userec_t cuser;
+extern userec_t xuser;
+
+/* �ϥο������ */
+#define VICE_NEW   "vice.new"
+
+/* Heat:�o�� */
+int vice(int money, char* item) {
+    char buf[128];
+    unsigned int viceserial=(currutmp->lastact%1000000)*100+rand()%100;
+    FILE *fp;
+    demoney(-money);
+    sprintf(buf, BBSHOME"/home/%c/%s/%s",
+	    cuser.userid[0], cuser.userid, VICE_NEW);
+    fp = fopen(buf, "a");
+    if(!fp )
+	{return 0;}
+   
+    fprintf(fp, "%08d\n", viceserial);
+    fclose(fp);
+    sprintf(buf, "%s ��F%d$ �s��[%08d]",item, money, viceserial);
+    mail_id(cuser.userid, buf, "etc/vice.txt", "Ptt�g�ٳ�"); 
+    return 0;
+}
+
+#define lockreturn(unmode, state) if(lockutmpmode(unmode, state)) return 
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+#define lockbreak(unmode, state) if(lockutmpmode(unmode, state)) break
+#define SONGBOOK  "etc/SONGBOOK"
+#define OSONGPATH "etc/SONGO"
+extern char trans_buffer[];
+extern char save_title[];
+
+static int osong(char *defaultid) {
+    char destid[IDLEN + 1],buf[200],genbuf[200],filename[256],say[51];
+    char receiver[60],ano[2];
+    FILE *fp,*fp1;// *fp2;
+    fileheader_t mail;
+    time_t now;
+    int nsongs;
+    
+    now = time(NULL);
+    strcpy(buf, Cdatedate(&now));
+    
+    lockreturn0(OSONG, LOCK_MULTI);
+    
+    /* Jaky �@�H�@���I�@�� */
+    if(!strcmp(buf, Cdatedate(&cuser.lastsong)) && !HAS_PERM(PERM_SYSOP)) {
+	move(22,0);
+	outs("�A���Ѥw�g�I�L�o�A���ѦA�I�a....");
+	refresh();
+	pressanykey();
+
+	unlockutmpmode();
+	return 0;
+    }
+
+    if(cuser.money < 200) {
+	move(22, 0);
+	outs("�I�q�n200�ȭ�!....");
+	refresh();
+	pressanykey();
+	unlockutmpmode();
+	return 0;
+    }
+    move(12, 0);
+    clrtobot();
+    sprintf(buf, "�˷R�� %s �w��Ө�ڮ�۰��I�q�t��\n", cuser.userid);
+    outs(buf);
+    trans_buffer[0] = 0;
+    if(!defaultid){
+	  getdata(13, 0, "�n�I���֩O:[�i������ Enter ����q]", destid, IDLEN + 1, DOECHO);
+	  while (!destid[0]){
+	     a_menu("�I�q�q��", SONGBOOK,0 );
+	     clear();
+	     getdata(13, 0, "�n�I���֩O:[�i�� Enter ���s��q]", destid, IDLEN + 1, DOECHO);
+	  }
+	}
+    else
+	strcpy(destid,defaultid);
+
+    /* Heat:�I�q�̰ΦW�\�� */    
+    getdata(14,0, "�n�ΦW��?[y/n]:", ano, 2, DOECHO);
+     
+    if(!destid[0]) {
+	unlockutmpmode();
+	return 0;
+    }
+    
+    getdata_str(14, 0, "�Q�n�n��L(�o)��..:", say, 51, DOECHO, "�ڷR�p..");
+    sprintf(save_title, "%s:%s", (ano[0]=='y')?"�ΦW��":cuser.userid, say);
+    getdata_str(16, 0, "�H��֪��H�c(�i��E-mail)?", receiver, 45,
+		LCECHO, destid);
+    
+
+    
+    if (!trans_buffer[0]){
+       outs("\n���ۭn��q�o..�i�J�q���n�n����@���q�a..^o^");
+       pressanykey();    
+       a_menu("�I�q�q��", SONGBOOK,0 );
+    }
+    if(!trans_buffer[0] ||  strstr(trans_buffer, "home") ||
+       strstr(trans_buffer, "boards") || !(fp = fopen(trans_buffer, "r"))) {
+	unlockutmpmode();
+	return 0;
+    }
+    
+    strcpy(filename, OSONGPATH);
+    
+    stampfile(filename, &mail);
+    
+    unlink(filename);
+    
+    if(!(fp1 = fopen(filename, "w"))) {
+	fclose(fp);
+	unlockutmpmode();
+	return 0;
+    }
+    
+    strcpy(mail.owner, "�I�q��");
+    sprintf(mail.title, "�� %s �I�� %s ", (ano[0]=='y')?"�ΦW��":cuser.userid, destid);
+    mail.savemode = 0;
+    
+    while(fgets(buf, 200, fp)) {
+	char *po;
+	if(!strncmp(buf, "���D: ", 6)) {
+	    clear();
+	    move(10,10);prints("%s", buf);
+	    pressanykey();
+	    fclose(fp);
+	    unlockutmpmode();
+	    return 0;
+	}
+	while((po = strstr(buf, "<~Src~>"))) {
+	    po[0] = 0;
+	    sprintf(genbuf,"%s%s%s",buf,(ano[0]=='y')?"�ΦW��":cuser.userid,po+7);
+	    strcpy(buf,genbuf);
+        }
+	while((po = strstr(buf, "<~Des~>"))) {
+	    po[0] = 0;
+	    sprintf(genbuf,"%s%s%s",buf,destid,po+7);
+	    strcpy(buf,genbuf);
+        }
+	while((po = strstr(buf, "<~Say~>"))) {
+	    po[0] = 0;
+	    sprintf(genbuf,"%s%s%s",buf,say,po+7);
+	    strcpy(buf,genbuf);
+        }
+	fputs(buf,fp1);
+    }
+    fclose(fp1);
+    fclose(fp);
+
+//    do_append(OSONGMAIL "/.DIR", &mail2, sizeof(mail2));
+    
+    if(do_append(OSONGPATH "/.DIR", &mail, sizeof(mail)) != -1) {
+	  cuser.lastsong = time(NULL);
+	/* Jaky �W�L 500 ���q�N�}�l�� */
+          nsongs=get_num_records(OSONGPATH "/.DIR", sizeof(mail));
+	  if (nsongs > 500){
+	    delete_range(OSONGPATH "/.DIR", 1, nsongs-500);
+	  }
+	/* ��Ĥ@������ */
+       vice(200, "�I�q");
+    }
+    sprintf(save_title, "%s:%s", (ano[0]=='y')?"�ΦW��":cuser.userid, say);
+    hold_mail(filename, destid);
+
+    if(receiver[0]) {
+#ifndef USE_BSMTP
+	bbs_sendmail(filename, save_title, receiver);
+#else
+	bsmtp(filename, save_title, receiver,0);
+#endif
+    }
+    clear();
+    outs(
+	"\n\n  ���߱z�I�q�����o..\n"
+	"  �@�p�ɤ��ʺA�ݪO�|�۰ʭ��s��s\n"
+	"  �j�a�N�i�H�ݨ�z�I���q�o\n\n"
+	"  �I�q��������D�i�H��Note�O����ذϧ䵪��\n"
+	"  �]�i�bNote�O��ذϬݨ�ۤv���I�q�O��\n"
+	"  ������O�Q���N���]�w���Note�O�d��\n"
+	"  ���ˤ����O�D���z�A��\n");
+    pressanykey();
+    sortsong();
+    topsong();
+
+    unlockutmpmode();
+    return 1;
+}
+
+int ordersong() {
+    osong(NULL);
+    return 0;
+}
+
+static int inmailbox(int m) {
+    passwd_query(usernum, &xuser);
+    cuser.exmailbox = xuser.exmailbox + m;
+    passwd_update(usernum, &cuser);
+    return cuser.exmailbox;
+}
+
+
+extern int b_lines;
+
+#if !HAVE_FREECLOAK
+/* ������ */
+int p_cloak() {
+    char buf[4];
+    getdata(b_lines-1, 0,
+	    currutmp->invisible ? "�T�w�n�{��?[y/N]" : "�T�w�n����?[y/N]",
+	    buf, 3, LCECHO);
+    if(buf[0] != 'y')
+	return 0;
+    if(cuser.money >= 19) {
+	vice(19, "cloak");
+	currutmp->invisible %= 2;
+	outs((currutmp->invisible ^= 1) ? MSG_CLOAKED : MSG_UNCLOAK);
+	refresh();
+	safe_sleep(1);
+    }
+    return 0;
+}
+#endif
+
+int p_from() {
+    char ans[4];
+
+    getdata(b_lines-2, 0, "�T�w�n��G�m?[y/N]", ans, 3, LCECHO);
+    if(ans[0] != 'y')
+	return 0;
+    reload_money();
+    if(cuser.money < 49)
+	return 0;
+    if(getdata_buf(b_lines-1, 0, "�п�J�s�G�m:",
+		   currutmp->from, 17, DOECHO)) {
+	vice(49,"home");
+	currutmp->from_alias=0;
+    }
+    return 0;
+}
+
+int p_exmail() {
+    char ans[4],buf[200];
+    int n;
+
+    if(cuser.exmailbox >= MAX_EXKEEPMAIL) {
+	sprintf(buf,"�e�q�̦h�W�[ %d �ʡA����A�R�F�C", MAX_EXKEEPMAIL);
+	outs(buf);
+	refresh();
+	return 0;
+    }
+    sprintf(buf,"�z���W�� %d �ʮe�q�A�٭n�A�R�h��?",
+	    cuser.exmailbox);
+    
+    getdata_str(b_lines-2, 0, buf, ans, 3, LCECHO, "10");
+    
+    n = atoi(ans);
+    if(!ans[0] || !n)
+	return 0;
+    if(n + cuser.exmailbox > MAX_EXKEEPMAIL)
+	n = MAX_EXKEEPMAIL - cuser.exmailbox;
+    reload_money();
+    if(cuser.money < n * 1000)
+	return 0;
+    vice(n * 1000, "mail");
+    inmailbox(n);
+    return 0;
+}
+
+void mail_redenvelop(char* from, char* to, int money, char mode){
+    char genbuf[200];
+    fileheader_t fhdr;
+    time_t now;
+    FILE* fp;
+    sprintf(genbuf, "home/%c/%s", to[0], to);
+    stampfile(genbuf, &fhdr);
+    if (!(fp = fopen(genbuf, "w")))
+        return;
+    now = time(NULL);
+    fprintf(fp, "�@��: %s\n"
+                "���D: �۰]�i�_\n"
+                "�ɶ�: %s\n"
+                "\033[1;33m�˷R�� %s �G\n\n\033[m"
+                "\033[1;31m    �ڥ]���A�@�� %d �����j���]�� ^_^\n\n"
+                "    §�����N���A�Я���...... ^_^\033[m\n"
+                , from, ctime(&now), to, money);
+    fclose(fp);
+    sprintf(fhdr.title, "�۰]�i�_");
+    strcpy(fhdr.owner, from);
+
+    if (mode == 'y')
+       vedit(genbuf, NA, NULL);
+    sprintf(genbuf, "home/%c/%s/.DIR", to[0], to);       
+    append_record(genbuf, &fhdr, sizeof(fhdr));
+}
+
+int p_give() {
+    int money;
+    char id[IDLEN + 1], genbuf[90];
+    time_t now = time(0);
+    
+    move(1,0);
+    usercomplete("�o�쩯�B�઺id:", id);
+    if(!id[0] || !strcmp(cuser.userid,id) ||
+       !getdata(2, 0, "�n���h�ֿ�:", genbuf, 7, LCECHO))
+	return 0;
+    money = atoi(genbuf);
+    reload_money();
+    if(money > 0 && cuser.money >= money ) {
+        deumoney(searchuser(id), money);
+	demoney(-money);
+	now = time(NULL);
+	sprintf(genbuf,"%s\t��%s\t%d\t%s", cuser.userid, id, money,
+		ctime(&now));
+	log_file(FN_MONEY, genbuf);
+	genbuf[0] = 'n';
+	getdata(3, 0, "�n�ۦ�Ѽg���]�U�ܡH[y/N]", genbuf, 2, LCECHO);
+	mail_redenvelop(cuser.userid, id, money, genbuf[0]);
+    }
+    return 0;
+}
+
+int p_sysinfo() {
+    char buf[100];
+    long int total,used;
+    float p;
+    
+    move(b_lines-1,0);
+    clrtoeol();
+    cpuload(buf);
+    outs("CPU �t�� : ");
+    outs(buf);
+    
+    p = swapused(&total,&used);
+    sprintf(buf, " �����O����: %.1f%%(����:%ldMB �α�:%ldMB)\n",
+	    p*100, total >> 20, used >> 20);
+    outs(buf);
+    pressanykey();
+    return 0;
+}
+
+/* �p�p��� */
+static void ccount(float *a, float b, int cmode) {
+    switch(cmode) {
+    case 0:
+    case 1:
+    case 2:
+        *a += b;
+        break;
+    case 3:
+        *a -= b;
+        break;
+    case 4:
+        *a *= b;
+        break;
+    case 5:
+        *a /= b;
+        break;
+    }
+}
+
+int cal() {
+    float a = 0;
+    char flo = 0, ch = 0;
+    char mode[6] = {' ','=','+','-','*','/'} , cmode = 0;
+    char buf[100] = "[            0] [ ] ", b[20] = "0";
+    
+    move(b_lines - 1, 0);
+    clrtoeol();
+    outs(buf);
+    move(b_lines, 0);
+    clrtoeol();
+    outs("\033[44m �p�p���  \033[31;47m      (0123456789+-*/=) "
+	 "\033[30m��J     \033[31m  "
+	 "(Q)\033[30m ���}                   \033[m");
+    while(1) {
+	ch = igetch();
+	switch(ch) {
+	case '\r':
+            ch = '=';
+	case '=':
+	case '+':
+	case '-':
+	case '*':
+	case '/':
+            ccount(&a, atof(b), cmode);
+            flo = 0;
+            b[0] = '0';
+            b[1] = 0;
+            move(b_lines - 1, 0);
+            sprintf(buf, "[%13.2f] [%c] ", a, ch);
+            outs(buf);
+            break;
+	case '.':
+            if(!flo)
+		flo = 1;
+            else
+		break;
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	case '0':
+	    if(strlen(b) > 13)
+		break;
+	    if(flo || b[0] != '0')
+		sprintf(b,"%s%c",b,ch);
+	    else
+		b[0]=ch;
+	    move(b_lines - 1, 0);
+	    sprintf(buf, "[%13s] [%c]", b, mode[(int)cmode]);
+	    outs(buf);
+	    break;
+	case 'q':
+	    return 0;
+	}
+	
+	switch(ch) {
+	case '=':
+	    a = 0;
+	    cmode = 0;
+	    break;
+	case '+':
+	    cmode = 2;
+	    break;
+	case '-':
+	    cmode = 3;
+	    break;
+	case '*':
+	    cmode = 4;
+	    break;
+	case '/':
+	    cmode = 5;
+	    break;
+	}
+    }
+}
diff --git a/mbbsd/calendar.c b/mbbsd/calendar.c
new file mode 100644
index 00000000..78ca46cf
--- /dev/null
+++ b/mbbsd/calendar.c
@@ -0,0 +1,284 @@
+/* $Id: calendar.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "proto.h"
+#include "modes.h"
+
+typedef struct event_t {
+    int year, month, day, days;
+    int color;
+    char *content;
+    struct event_t *next;
+} event_t;
+
+static int MonthDay(int m, int leap) {
+    static int day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+    
+    return leap && m == 2 ? 29 : day[m - 1];
+}
+
+static int IsLeap(int y) {
+    if(y % 400 == 0 || (y % 4 == 0 && y % 100 != 0))
+	return 1;
+    else
+	return 0;
+}
+
+static int Days(int y, int m, int d) {
+    int i, w;
+    
+    w = 1 + 365 * (y - 1)
+	+ ((y - 1) / 4) - ((y - 1) / 100) + ((y - 1) / 400)
+	+ d - 1;
+    for(i = 1; i < m; i++)
+	w += MonthDay(i, IsLeap(y));
+    return w;
+}
+
+static int ParseDate(char *date, event_t *t) {
+    char *y, *m, *d;
+    
+    y = strtok(date, "/");
+    m = strtok(NULL, "/");
+    d = strtok(NULL, "");
+    if(!y || !m || !d)
+	return 1;
+    
+    t->year = atoi(y);
+    t->month = atoi(m);
+    t->day = atoi(d);
+    if(t->year < 1 || t->month < 1 || t->month > 12 || 
+       t->day < 1 || t->day > 31)
+	return 1;
+    t->days = Days(t->year, t->month, t->day);
+    return 0;
+}
+
+static int ParseColor(char *color) {
+    struct {
+	char *str;
+	int val;
+    } c[] = {
+	{"black", 0},
+	{"red", 1},
+	{"green", 2},
+	{"yellow", 3},
+	{"blue", 4},
+	{"magenta", 5},
+	{"cyan", 6},
+	{"white", 7}
+    };
+    int i;
+    
+    for(i = 0; i < sizeof(c) / sizeof(c[0]); i++)
+	if(strcasecmp(color, c[i].str) == 0)
+	    return c[i].val;
+    return 7;
+}
+
+static void InsertEvent(event_t *head, event_t *t) {
+    event_t *p;
+    
+    for(p = head; p->next && p->next->days < t->days; p = p->next);
+    t->next = p->next;
+    p->next = t;
+}
+
+static void FreeEvent(event_t *e) {
+    event_t *n;
+    
+    while(e) {
+	n = e->next;
+	free(e);
+	e = n;
+    }
+}
+
+extern userec_t cuser;
+
+static event_t *ReadEvent(int today) {
+    FILE *fp;
+    char buf[256];
+    static event_t head;
+    
+    head.next = NULL;
+    setcalfile(buf, cuser.userid);
+    fp = fopen(buf, "r");
+    if(fp) {
+	while(fgets(buf, sizeof(buf), fp)) {
+	    char *date, *color, *content;
+	    event_t *t;
+	    
+	    if(buf[0] == '#')
+		continue;
+	    
+	    date = strtok(buf, " \t\n");
+	    color = strtok(NULL, " \t\n");
+	    content = strtok(NULL, "\n");
+	    if(!date || !color || !content)
+		continue;
+	    
+	    t = malloc(sizeof(event_t));
+	    if(ParseDate(date, t) || t->days < today) {
+		free(t);
+		continue;
+	    }
+	    t->color = ParseColor(color) + 30;
+	    for(; *content == ' ' || *content == '\t'; content++);
+	    t->content = strdup(content);
+	    InsertEvent(&head, t);
+	}
+	fclose(fp);
+    } 
+    return head.next;
+}
+
+static char **AllocCalBuffer(int line, int len) {
+    int i;
+    char **p;
+    
+    p = malloc(sizeof(char *) * line);
+    p[0] = malloc(sizeof(char) * line * len);
+    for(i = 1; i < line; i++)
+	p[i] = p[i - 1] + len;
+    return p;
+}
+
+static void FreeCalBuffer(char **buf) {
+    free(buf[0]);
+    free(buf);
+}
+
+#define CALENDAR_COLOR  "\33[0;30;47m"
+#define HEADER_COLOR    "\33[1;44m"
+#define HEADER_SUNDAY_COLOR    "\33[31m"
+#define HEADER_DAY_COLOR       "\33[33m"
+
+static int GenerateCalendar(char **buf, int y, int m, int today, event_t *e) {
+    static char *week_str[7] = {"��", "�@", "�G", "�T", "�|", "��", "��"};    
+    static char *month_color[12] = {
+	"\33[1;32m", "\33[1;33m", "\33[1;35m", "\33[1;36m",
+	"\33[1;32m", "\33[1;33m", "\33[1;35m", "\33[1;36m",
+	"\33[1;32m", "\33[1;33m", "\33[1;35m", "\33[1;36m"
+    };
+    static char *month_str[12] = {
+	"�@��  ", "�G��  ", "�T��  ", "�|��  ", "����  ", "����  ",
+	"�C��  ", "�K��  ", "�E��  ", "�Q��  ", "�Q�@��", "�Q�G��"
+    };
+    
+    char *p, attr1[16], *attr2;
+    int i, d, w, line = 0, first_day = Days(y, m, 1);
+
+    
+    /* week day banner */
+    p = buf[line];
+    p += sprintf(p, "    %s%s%s%s", HEADER_COLOR, HEADER_SUNDAY_COLOR,
+		 week_str[0], HEADER_DAY_COLOR);
+    for(i = 1; i < 7; i++)
+	p += sprintf(p, " %s", week_str[i]);
+    p += sprintf(p, "\33[m");
+    
+    /* indent for first line */
+    p = buf[++line];
+    p += sprintf(p, "    %s", CALENDAR_COLOR);
+    for(i = 0, w = first_day % 7; i < w; i++)
+	p += sprintf(p, "   ");
+    
+    /* initial event */
+    for(; e && e->days < first_day; e = e->next);
+    
+    d = MonthDay(m, IsLeap(y));
+    for(i = 1; i <= d; i++, w = (w + 1) % 7) {
+        attr1[0] = 0;
+        attr2 = "";
+	while(e && e->days == first_day + i - 1) {
+	    sprintf(attr1, "\33[1;%dm", e->color);
+	    attr2 = CALENDAR_COLOR;
+	    e = e->next;
+	}
+	if(today == first_day + i - 1) {
+	    strcpy(attr1, "\33[1;37;42m");
+	    attr2 = CALENDAR_COLOR;
+	}
+	p += sprintf(p, "%s%2d%s", attr1, i, attr2);
+	
+	if(w == 6) {
+	    p += sprintf(p, "\33[m");
+	    p = buf[++line];
+	    /* show month */
+	    if(line >= 2 && line <= 4)
+		p += sprintf(p, "%s%2.2s\33[m  %s", month_color[m - 1],
+			     month_str[m - 1] + (line - 2) * 2,
+			     CALENDAR_COLOR);
+	    else if(i < d)
+		p += sprintf(p, "    %s", CALENDAR_COLOR);
+	} else
+	    *p++ = ' ';
+    }
+    
+    /* fill up the last line */
+    if(w) {
+	for(w = 7 - w; w; w--)
+	    p += sprintf(p, w == 1 ? "  " : "   ");
+	p += sprintf(p, "\33[m");
+    }
+    return line + 1;
+}
+
+int calendar() {
+    char **buf;
+    time_t t;
+    struct tm now;
+    int i, y, m, today, lines = 0;
+    event_t *head = NULL, *e = NULL;
+    
+    /* initialize date */
+    time(&t);
+    memcpy(&now, localtime(&t), sizeof(struct tm));
+    today = Days(now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
+    y = now.tm_year + 1900, m = now.tm_mon + 1;
+    
+    /* read event */
+    head = e = ReadEvent(today);
+    
+    /* generate calendar */
+    buf = AllocCalBuffer(22, 256);
+    for(i = 0; i < 22; i++)
+	sprintf(buf[i], "%24s", "");
+    for(i = 0; i < 3; i++) {
+	lines += GenerateCalendar(buf + lines, y, m, today, e) + 1;
+	if(m == 12)
+	    y++, m = 1;
+	else
+	    m++;
+    }
+    
+    /* output */
+    clear();
+    outc('\n');
+    for(i = 0; i < 22; i++) {
+	outs(buf[i]);
+	if(i == 0) {
+	    prints("\t\33[1;37m�{�b�O %d.%02d.%02d %2d:%02d:%02d%cm\33[m",
+		   now.tm_year + 1900, now.tm_mon + 1, now.tm_mday,
+		   (now.tm_hour == 0 || now.tm_hour == 12) ? 
+		   12 : now.tm_hour % 12, now.tm_min, now.tm_sec,
+		   now.tm_hour >= 12 ? 'p' : 'a');
+	} else if(i >= 2 && e) {
+	    prints("\t\33[1;37m(\33[%dm%3d\33[37m)\33[m %02d/%02d %s",
+		   e->color, e->days - today,
+		   e->month, e->day, e->content);
+	    e = e->next;
+	}
+	outc('\n');
+    }
+    FreeEvent(head);
+    FreeCalBuffer(buf);
+    pressanykey();
+    return 0;
+}
diff --git a/mbbsd/card.c b/mbbsd/card.c
new file mode 100644
index 00000000..cfa10b0c
--- /dev/null
+++ b/mbbsd/card.c
@@ -0,0 +1,625 @@
+/* $Id: card.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+extern int usernum;
+
+static int card_remain(int cards[]) {
+    int i, temp = 0;
+    
+    for(i = 0; i < 52; i++)
+        temp += cards[i];
+    if(temp == 52)
+        return 1;
+    return 0;
+}
+
+/* 0 Spare ,  1 heart , ...3 dimon */
+static int card_flower(int card) {
+    return (card / 13);
+}
+
+/* 1...13 */
+static int card_number(int card) {
+    return (card % 13 + 1);
+}
+
+static int card_select(int *now) {
+    char *cc[2] = {"\033[44m            \033[m",
+                   "\033[1;33;41m     ��     \033[m"};
+
+    while(1) {
+        move(20, 0);
+        clrtoeol();
+        prints("%s%s%s%s%s", (*now == 0) ? cc[1] : cc[0],
+               (*now == 1) ? cc[1] : cc[0],
+               (*now == 2) ? cc[1] : cc[0],
+               (*now == 3) ? cc[1] : cc[0],
+               (*now == 4) ? cc[1] : cc[0]);
+        switch(egetch()) {
+        case 'Q':
+        case 'q':
+            return 0;
+        case '+':
+        case ',':
+            return 1;
+        case '\r':
+            return -1;
+        case KEY_LEFT:
+            *now = (*now + 4) % 5;
+            break;
+        case KEY_RIGHT:
+            *now = (*now + 1) % 5;
+            break;
+        case '1':
+            *now = 0;
+            break;
+        case '2':
+            *now = 1;
+            break;
+        case '3':
+            *now = 2;
+            break;
+        case '4':
+            *now = 3;
+            break;
+        case '5':
+            *now = 4;
+            break;
+        }
+    }
+}
+
+static void card_display(int cline, int number, int flower, int show) {
+    int color = 31;
+    char *cn[13] = {"��", "��", "��", "��", "��", "��",
+                    "��", "��", "��", "10", "��", "��", "��"};
+    if(flower == 0 || flower == 3)
+        color = 36;
+    if((show < 0) && (cline > 1 && cline < 8))
+        prints("�x\033[1;33;42m��������\033[m�x");
+    else
+        switch(cline) {
+        case 1:
+            prints("�~�w�w�w�w��");
+            break;
+        case 2:
+            prints("�x\033[1;%dm%s\033[m      �x", color, cn[number - 1]);
+            break;
+        case 3:
+            if(flower == 1)
+                prints("�x\033[1;%dm��������\033[m�x", color);
+            else
+                prints("�x\033[1;%dm  ����  \033[m�x", color);
+            break;
+        case 4:
+            if(flower == 1)
+                prints("�x\033[1;%dm�i�i�i�i\033[m�x", color);
+            else if (flower == 3)
+                prints("�x\033[1;%dm���i�i��\033[m�x", color);
+            else
+                prints("�x\033[1;%dm���i�i��\033[m�x", color);
+            break;
+        case 5:
+            if(flower == 0)
+                prints("�x\033[1;%dm�i�i�i�i\033[m�x", color);
+            else if (flower == 3)
+                prints("�x\033[1;%dm�i�����i\033[m�x", color);
+            else
+                prints("�x\033[1;%dm���i�i��\033[m�x", color);
+            break;
+        case 6:
+            if(flower == 0)
+                prints("�x\033[1;%dm  ����  \033[m�x", color);
+            else if (flower == 3)
+                prints("�x\033[1;%dm��������\033[m�x", color);
+            else
+                prints("�x\033[1;%dm  ����  \033[m�x", color);
+            break;
+        case 7:
+            prints("�x      \033[1;%dm%s\033[m�x", color, cn[number - 1]);
+            break;
+        case 8:
+            prints("���w�w�w�w��");
+            break;
+        }
+}
+
+static void card_show(int cpu[], int c[], int me[], int m[]) {
+    int i, j;
+
+    for(j = 0; j < 8; j++) {
+        move(2 + j, 0);
+        clrtoeol();
+        for(i = 0; i < 5 && cpu[i] >= 0; i++)
+            card_display(j + 1, card_number(cpu[i]),
+                         card_flower(cpu[i]), c[i]);
+    }
+
+    for(j = 0; j < 8; j++) {
+        move(11 + j, 0);
+        clrtoeol();
+        for(i = 0; i < 5 && me[i] >= 0; i++)
+            card_display(j + 1, card_number(me[i]), card_flower(me[i]), m[i]);
+    }
+}
+static void card_new(int cards[]) {
+	    memset(cards, 0, sizeof(int)*52);
+}  
+
+static int card_give(int cards[]) {
+    int i, error;
+    for(error=0, i=rand()%52; cards[i] == 1 && error<52; error++, i=rand()%52);
+    if(error==52) card_new(cards); // Ptt:�o�䦳dead lock���D
+    cards[i] = 1;
+    return i;
+}
+
+static void card_start(char name[]) {
+    clear();
+    stand_title(name);
+    move(1, 0);
+    prints("    \033[1;33;41m   �q  ��   \033[m");
+    move(10, 0);
+    prints("\033[1;34;44m���㡻�㡻�㡻�㡻�㡻�㡻�㡻�㡻�㡻�㡻�㡻��"
+           "���㡻�㡻�㡻�㡻�㡻�㡻�㡻\033[m");
+    move(19, 0);
+    prints("    \033[1;37;42m   ��  �v   \033[m");
+}
+
+static int card_99_add(int i, int aom, int count) {
+    if (i == 4 || i == 5 || i == 11)
+        return count;
+    else if(i == 12)
+        return count + 20 * aom;
+    else if(i == 10)
+        return count + 10 * aom;
+    else if(i == 13)
+        return 99;
+    else
+        return count + i;
+}
+
+static int card_99_cpu(int cpu[], int *count) {
+    int stop = -1;
+    int twenty = -1;
+    int ten = -1;
+    int kill = -1;
+    int temp, num[10];
+    int other = -1;
+    int think = 99-(*count);
+    int i, j;
+
+    for(i = 0; i < 10; i++)
+        num[i] = -1;
+    for(i = 0; i < 5; i++) {
+        temp = card_number(cpu[i]);
+        if(temp == 4 || temp == 5 || temp == 11)
+            stop = i;
+        else if(temp == 12)
+            twenty = i;
+        else if (temp == 10)
+            ten = i;
+        else if (temp == 13)
+            kill = i;
+        else {
+            other = i;
+            num[temp] = i;
+        }
+    }
+    for(j = 9; j > 0; j--)
+        if(num[j] >= 0 && j != 4 && j != 5 && think >= j) {
+            (*count) += j;
+            return num[j];
+        }
+    if((think >= 20) && (twenty >= 0)) {
+        (*count) += 20;
+        return twenty;
+    } else if((think >= 10) && (ten >= 0)) {
+        (*count) += 10;
+        return ten;
+    } else if(stop >= 0)
+        return stop;
+    else if (kill >= 0) {
+        (*count) = 99;
+        return kill;
+    } else if(ten  >= 0) {
+        (*count) -= 10;
+        return ten;
+    } else if(twenty >= 0) {
+        (*count) -= 20;
+        return twenty;
+    } else {
+        (*count) += card_number(cpu[0]);
+        return 0;
+    }
+}
+
+int card_99() {
+    int i, j, turn;
+    int cpu[5], c[5], me[5], m[5];
+    int cards[52];
+    int count = 0;
+    char *ff[4] = {"\033[1;36m�®�", "\033[1;31m����",
+                   "\033[1;31m���", "\033[1;36m�ª�"};
+    char *cn[13] = {"��", "��", "��", "��", "��", "��",
+                    "��", "��", "��", "10", "��", "��", "��"};
+    for(i = 0; i < 5; i++)
+        cpu[i] = c[i] = me[i] = m[i] = -1;
+    setutmpmode(CARD_99);
+    card_start("�Ѫ��a�[");
+    card_new(cards);
+    for(i = 0; i < 5; i++) {
+        cpu[i] = card_give(cards);
+        me[i] = card_give(cards);
+        m[i] = 1;
+    }
+    card_show(cpu, c, me, m);
+    j = 0;
+    turn = 1;
+    move(21, 0);
+    clrtoeol();
+    prints("[0]�ثe %d , �� %d �I\n", count, 99 - count);
+    prints("���k�䲾�ʴ��, [Enter]�T�w, [ + ]���[�G�Q(�[�Q), [Q/q]���C��");
+    while(1) {
+        i = card_select(&j);
+        if(i == 0) /*���C��*/
+            return 0;
+        count = card_99_add(card_number(me[j]), i, count);
+        move(21 + (turn / 2) % 2, 0);
+        clrtoeol();
+        prints("[%d]�z�X %s%s\033[m �ثe \033[1;31m%d/\033[34m%d\033[m �I",
+               turn, ff[card_flower(me[j])],
+               cn[card_number(me[j]) - 1], count, 99 - count);
+        me[j] = card_give(cards);
+        turn++;
+        if(count < 0)
+            count = 0;
+        card_show(cpu, c, me, m);
+        pressanykey();
+        if(count > 99) {
+            move(22, 0);
+            clrtoeol();
+            prints("[%d]���G..YOU LOSS..�ثe \033[1;31m%d/\033[34m%d\033[m �I",
+                   turn, count, 99 - count);
+            pressanykey();
+            return 0;
+        }
+        i = card_99_cpu(cpu, &count);
+        move(21 + (turn / 2 + 1) % 2, 40);
+        prints("[%d]�q���X %s%s\033[m �ثe \033[1;31m%d/\033[34m%d\033[m �I",
+               turn, ff[card_flower(cpu[i])],
+               cn[card_number(cpu[i]) - 1], count, 99 - count);
+        cpu[i] = card_give(cards);
+        turn++;
+        if(count < 0)
+            count = 0;
+        if(count > 99) {
+            move(22, 0);
+            clrtoeol();
+            prints("[%d]���G..YOU WIN!..�ثe \033[1;31m%d/\033[34m%d\033[m �I",
+                   turn, count, 99 - count);
+            pressanykey();
+            return 0;
+        }
+        if(!card_remain(cards)) {
+            card_new(cards);
+            for(i = 0; i < 5; i++) {
+                cards[me[i]] = 1;
+                cards[cpu[i]] = 1;
+            }
+        }
+    }
+}
+
+#define PMONEY     (10)
+#define TEN_HALF   (5)        /*�Q�I�b��Ticket*/
+#define JACK      (10)        /*�³ǧJ��Ticket*/
+#define NINE99    (99)        /*99    ��Ticket*/
+
+extern userec_t cuser;
+
+static int game_log(int type, int money) {
+    FILE *fp;
+
+    if(money > 0)
+	demoney(money);
+
+    switch(type) {
+    case JACK:
+        fp = fopen(BBSHOME "/etc/card/jack.log", "a");
+        if(!fp)
+            return 0;
+        fprintf(fp, "%s win:%d\n", cuser.userid, money);
+        fclose(fp);
+        break;
+    case TEN_HALF:
+        fp = fopen(BBSHOME "/etc/card/tenhalf.log",  "a");
+        if(!fp)
+            return 0;
+        fprintf(fp, "%s win:%d\n", cuser.userid, money);
+        fclose(fp);
+        break;
+    }
+    return 0;
+}
+
+static int card_double_ask() {
+    char buf[100], buf2[3];
+
+    sprintf(buf, "[ %s ]�z�{�b�@�� %d P��,  �{�b�n����(�[�� %d ��)��? [y/N]",
+            cuser.userid, cuser.money, JACK);
+    reload_money();
+    if(cuser.money < JACK)
+        return 0;
+    getdata(20, 0, buf, buf2, 2, LCECHO);
+    if(buf2[0] == 'y' || buf2[0] == 'Y')
+        return 1;
+    return 0;
+}
+
+static int card_ask() {
+    char buf[100], buf2[3];
+
+    sprintf(buf, "[ %s ]�z�{�b�@�� %d P��,  �٭n�[�P��? [y/N]",
+            cuser.userid, cuser.money);
+    getdata(20, 0 , buf, buf2, 2,  LCECHO);
+    if(buf2[0] == 'y' || buf2[0] == 'Y')
+        return 1;
+    return 0;
+}
+
+static int card_alls_lower(int all[]) {
+    int i, count = 0;
+    for (i = 0; i < 5 && all[i] >= 0; i++)
+        if(card_number(all[i]) <= 10)
+            count += card_number(all[i]);
+        else
+            count += 10;
+    return count;
+}
+
+static int card_alls_upper(int all[]) {
+    int i, count;
+
+    count = card_alls_lower(all);
+    for (i = 0; i < 5 && all[i] >= 0 && count <= 11; i++)
+        if (card_number(all[i]) == 1)
+            count += 10;
+    return count;
+}
+
+extern int b_lines;
+
+static int card_jack(int *db) {
+    int i, j;
+    int cpu[5], c[5], me[5], m[5];
+    int cards[52];
+
+    for(i = 0;i<5;i++)
+        cpu[i] = c[i] = me[i] = m[i] = -1;
+
+    if((*db)<0) {
+        card_new(cards);
+        card_start("�³ǧJ");
+        for(i = 0; i < 2; i++) {
+            cpu[i] = card_give(cards);
+            me[i] = card_give(cards);
+        }
+    } else {
+        card_start("�³ǧJDOUBLE�l�[��");
+        cpu[0] = card_give(cards);
+        cpu[1] = card_give(cards);
+        me[0] = *db;
+        me[1] = card_give(cards);
+    }
+    c[1] = m[0] = m[1] = 1;
+    card_show(cpu, c, me, m);
+    if((card_number(me[0]) == 0 && card_number(me[1]) == 12) ||
+       (card_number(me[1]) == 0 && card_number(me[0]) == 12)) {
+        if(card_flower(me[0]) == 0 && card_flower(me[1]) == 0) {
+            move(b_lines - 1, 0);
+            prints("�D�`������! (�W�Ŷ³ǧJ!! �[ %d ��)", JACK * 10);
+            game_log(JACK, JACK * 10);
+            pressanykey();
+            return 0;
+        } else {
+            move(b_lines - 1, 0);
+            prints("�ܤ�����! (�³ǧJ!! �[ %d ��)", JACK * 5);
+            game_log(JACK, JACK * 5);
+            pressanykey();
+            return 0;
+        }
+    }
+    if((card_number(cpu[0]) == 0 && card_number(cpu[1]) == 12) ||
+       (card_number(cpu[1]) == 0 && card_number(cpu[0]) == 12)) {
+        c[0] = 1;
+        card_show(cpu, c, me, m);
+        move(b_lines - 1, 0);
+        prints("�K�K...���n�N��....�³ǧJ!!");
+        game_log(JACK, 0);
+        pressanykey();
+        return 0;
+    }
+    if((*db < 0)&& (card_number(me[0]) == card_number(me[1])) &&
+       (card_double_ask())) {
+        *db = me[1];
+        me[1] = card_give(cards);
+        card_show(cpu, c, me, m);
+    }
+    i = 2;
+    while(i < 5 && card_ask()) {
+        me[i] = card_give(cards);
+        m[i] = 1;
+        card_show(cpu, c, me, m);
+        if(card_alls_lower(me) > 21) {
+            move(b_lines - 1, 0);
+            prints("���...�z���F!");
+            game_log(JACK, 0);
+            pressanykey();
+            return 0;
+        }
+        i++;
+        if((i == 3) && (card_number(me[0]) == 7) &&
+           (card_number(me[1]) == 7) && (card_number(me[2]) == 7)) {
+            move(b_lines - 1, 0);
+            prints("�ܤ�����! (���B�C��!! �[ %d ��)", JACK * 7);
+            game_log(JACK, JACK * 7);
+            pressanykey();
+            return 0;
+        }
+    }
+    if(i == 5) {/*�L����*/
+        move(b_lines - 1, 0);
+        prints("�n�F�`��! �L������! �[P�� %d ��!", 5 * JACK);
+        game_log(JACK, JACK * 5);
+        pressanykey();
+        return 0;
+    }
+    j = 2;
+    c[0] = 1;
+    while((card_alls_upper(cpu) < card_alls_upper(me))||
+          ((card_alls_upper(cpu) == card_alls_upper(me) && j < i ) && j < 5)) {
+        cpu[j] = card_give(cards);
+        c[j] = 1;
+        if(card_alls_lower(cpu) > 21) {
+            card_show(cpu, c, me, m);
+            move(b_lines - 1, 0);
+            prints("����...�q���z���F! �A�F! �i�oP�� %d ��", JACK * 2);
+            game_log(JACK, JACK * 2);
+            pressanykey();
+            return 0;
+        }
+        j++;
+    }
+    card_show(cpu, c, me, m);
+    move(b_lines - 1, 0);
+    prints("�z�z...�q���F!");
+    game_log(JACK, 0);
+    pressanykey();
+    return 0;
+}
+
+int g_card_jack() {
+    int db;
+    char buf[3];
+
+    setutmpmode(JACK_CARD);
+    while(1) {
+        reload_money();
+        if(cuser.money < JACK) {
+            outs("�z����������!�h�h�o���Ǧ��N�q���峹�A��~~~");
+            return 0;
+        }
+        getdata(b_lines - 1, 0, "�T�w�n���³ǧJ�� �@���Q����?(Y/N)?[N]",
+                buf, 3, LCECHO);
+        if((*buf != 'y') && (*buf != 'Y'))
+            break;
+        else {
+            db = -1;
+	    vice(PMONEY, "�³ǧJ");
+            card_jack(&db);
+            if(db >= 0)
+                card_jack(&db);
+        }
+    }
+    return 0;
+}
+
+static int card_all(int all[]) {
+    int i, count = 0;
+
+    for (i = 0; i < 5 && all[i] >= 0; i++)
+        if(card_number(all[i]) <= 10)
+            count += 2 * card_number(all[i]);
+        else
+            count += 1;
+    return count;
+}
+
+static int ten_helf() {
+    int i, j;
+    int cpu[5], c[5], me[5], m[5];
+    int cards[52];
+
+    card_start("�Q�I�b");
+    card_new(cards);
+    for (i = 0; i < 5; i++)
+        cpu[i] = c[i] = me[i] = m[i] = -1;
+
+    cpu[0] = card_give(cards);
+    me[0] = card_give(cards);
+    m[0] = 1;
+    card_show(cpu, c, me, m);
+    i = 1;
+    while(i < 5 && card_ask()) {
+        me[i] = card_give(cards);
+        m[i] = 1;
+        card_show(cpu, c, me, m);
+        if(card_all(me) > 21) {
+            move(b_lines - 1, 0);
+            prints("���...�z���F!");
+            game_log(TEN_HALF, 0);
+            pressanykey();
+            return 0;
+        }
+        i++;
+    }
+    if(i == 5) {/*�L����*/
+        move(b_lines - 1, 0);
+        prints("�n�F�`��! �L������! �[P�� %d ��!", 5 * PMONEY);
+        game_log(TEN_HALF, PMONEY * 5);
+        pressanykey();
+        return 0;
+    }
+    j = 1;
+    c[0] = 1;
+    while( j<5 && ((card_all(cpu) < card_all(me)) ||
+                   (card_all(cpu) == card_all(me) && j < i))) {
+        cpu[j] = card_give(cards);
+        c[j] = 1;
+        if(card_all(cpu) > 21) {
+            card_show(cpu, c, me, m);
+            move(b_lines - 1, 0);
+            prints("����...�q���z���F! �A�F! �i�oP�� %d ��", PMONEY * 2);
+            game_log(TEN_HALF, PMONEY * 2);
+            pressanykey();
+            return 0;
+        }
+        j++;
+    }
+    card_show(cpu, c, me, m);
+    move(b_lines - 1, 0);
+    prints("�z�z...�q���F!");
+    game_log(TEN_HALF, 0);
+    pressanykey();
+    return 0;
+}
+
+int g_ten_helf() {
+    char buf[3];
+
+    setutmpmode(TENHALF);
+    while(1) {
+        reload_money();
+        if(cuser.money < TEN_HALF) {
+            outs("�z����������!�h�h�o���Ǧ��N�q���峹�A��~~~");
+            return 0;
+        }
+        getdata(b_lines - 1, 0,
+                "\033[1;37m�T�w�n���Q�I�b�� �@���Q����?(Y/N)?[N]\033[m",
+                buf, 3, LCECHO);
+        if(buf[0] !=  'y' && buf[0] !=  'Y')
+            return 0;
+        else {
+            vice(PMONEY, "�Q�I�b");
+            ten_helf();
+        }
+    }
+    return 0;
+}
diff --git a/mbbsd/chat.c b/mbbsd/chat.c
new file mode 100644
index 00000000..f75383a8
--- /dev/null
+++ b/mbbsd/chat.c
@@ -0,0 +1,623 @@
+/* $Id: chat.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern userinfo_t *currutmp;
+static int chatline;
+static int stop_line;    /* next line of bottom of message window area */
+static FILE *flog;
+
+static void printchatline(char *str) {
+    move(chatline, 0);
+    if(*str == '>' && !PERM_HIDE(currutmp))
+	return;
+    else if(chatline < stop_line - 1)
+	chatline++;
+    else {
+	region_scroll_up(2, stop_line - 2);
+	move(stop_line - 2, 0);
+    }
+    outs(str);
+    outc('\n');
+    outs("��");
+    
+    if(flog)
+	fprintf(flog, "%s\n", str);
+}
+
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+
+static void chat_clear() {
+    for(chatline = 2; chatline < stop_line; chatline++) {
+	move(chatline, 0);
+	clrtoeol();
+    }
+    move(b_lines, 0);
+    clrtoeol();
+    move(chatline = 2, 0);
+    outs("��");
+}
+
+static void print_chatid(char *chatid) {
+    move(b_lines - 1, 0);
+    clrtoeol();
+    outs(chatid);
+    outc(':');
+}
+
+static int chat_send(int fd, char *buf) {
+    int len;
+    char genbuf[200];
+
+    sprintf(genbuf, "%s\n", buf);
+    len = strlen(genbuf);
+    return (send(fd, genbuf, len, 0) == len);
+}
+
+static char chatroom[IDLEN];         /* Chat-Room Name */
+
+static int chat_recv(int fd, char *chatid) {
+    static char buf[512];
+    static int bufstart = 0;
+    char genbuf[200];
+    int c, len;
+    char *bptr;
+
+    len = sizeof(buf) - bufstart - 1;
+    if((c = recv(fd, buf + bufstart, len, 0)) <= 0)
+	return -1;
+    c += bufstart;
+
+    bptr = buf;
+    while(c > 0) {
+	len = strlen(bptr) + 1;
+	if(len > c && len < (sizeof buf / 2))
+	    break;
+
+	if(*bptr == '/') {
+	    switch(bptr[1]) {
+	    case 'c':
+		chat_clear();
+		break;
+	    case 'n':
+		strncpy(chatid, bptr + 2, 8);
+		print_chatid(chatid);
+		clrtoeol();
+		break;
+	    case 'r':
+		strncpy(chatroom, bptr + 2, IDLEN - 1);
+		break;
+	    case 't':
+		move(0, 0);
+		clrtoeol();
+		sprintf(genbuf, "�ͤѫ� [%s]", chatroom);
+		prints("\033[1;37;46m %-21s \033[45m ���D�G%-48s\033[m",
+		       genbuf, bptr + 2);
+	    }
+	} else
+	    printchatline(bptr);
+
+	c -= len;
+	bptr += len;
+    }
+
+    if(c > 0) {
+	strcpy(genbuf, bptr);
+	strcpy(buf, genbuf);
+	bufstart = len - 1;
+    } else
+	bufstart = 0;
+    return 0;
+}
+
+extern userec_t cuser;
+
+static int printuserent(userinfo_t *uentp) {
+    static char uline[80];
+    static int cnt;
+    char pline[30];
+
+    if(!uentp) {
+	if(cnt)
+	    printchatline(uline);
+	bzero(uline, 80);
+	cnt = 0;
+	return 0;
+    }
+    if(!HAS_PERM(PERM_SYSOP) && !HAS_PERM(PERM_SEECLOAK) && uentp->invisible)
+	return 0;
+
+    sprintf(pline, "%-13s%c%-10s ", uentp->userid,
+	    uentp->invisible ? '#' : ' ',
+	    modestring(uentp, 1));
+    if(cnt < 2)
+	strcat(pline, "�x");
+    strcat(uline, pline);
+    if(++cnt == 3) {
+	printchatline(uline);
+	memset(uline, 0, 80);
+	cnt = 0;
+    }
+    return 0;
+}
+
+static void chathelp(char *cmd, char *desc) {
+    char buf[STRLEN];
+    
+    sprintf(buf, "  %-20s- %s", cmd, desc);
+    printchatline(buf);
+}
+
+static void chat_help(char *arg) {
+    if(strstr(arg, " op")) {
+	printchatline("�ͤѫǺ޲z���M�Ϋ��O");
+	chathelp("[/f]lag [+-][ls]", "�]�w��w�B���K���A");
+	chathelp("[/i]nvite <id>", "�ܽ� <id> �[�J�ͤѫ�");
+	chathelp("[/k]ick <id>", "�N <id> ��X�ͤѫ�");
+	chathelp("[/o]p <id>", "�N Op ���v�O�ಾ�� <id>");
+	chathelp("[/t]opic <text>", "���Ӹ��D");
+	chathelp("[/w]all", "�s�� (�����M��)");
+    } else {
+	chathelp("[//]help", "MUD-like ����ʵ�");
+	chathelp("[/.]help", "chicken �������O");
+	chathelp("[/h]elp op", "�ͤѫǺ޲z���M�Ϋ��O");
+	chathelp("[/a]ct <msg>", "���@�Ӱʧ@");
+	chathelp("[/b]ye [msg]", "�D�O");
+	chathelp("[/c]lear", "�M���ù�");
+	chathelp("[/j]oin <room>", "�إߩΥ[�J�ͤѫ�");
+	chathelp("[/l]ist [room]", "�C�X�ͤѫǨϥΪ�");
+	chathelp("[/m]sg <id> <msg>", "�� <id> ��������");
+	chathelp("[/n]ick <id>", "�N�ͤѥN������ <id>");
+	chathelp("[/p]ager", "�����I�s��");
+	chathelp("[/q]uery", "�d�ߺ���");
+	chathelp("[/r]oom", "�C�X�@��ͤѫ�");
+	chathelp("[/u]sers", "�C�X���W�ϥΪ�");
+	chathelp("[/w]ho", "�C�X���ͤѫǨϥΪ�");
+	chathelp("[/w]hoin <room>", "�C�X�ͤѫ�<room> ���ϥΪ�");
+    }
+}
+
+static void chat_date() {
+    time_t thetime;
+    char genbuf[200];
+
+    time(&thetime);
+    sprintf(genbuf, "�� " BBSNAME "�зǮɶ�: %s", Cdate(&thetime));
+    printchatline(genbuf);
+}
+
+static void chat_pager() {
+    char genbuf[200];
+
+    char *msgs[] = {"����", "���}", "�ޱ�", "����","�n��"};
+    sprintf(genbuf, "�� �z���I�s��:[%s]",
+	    msgs[currutmp->pager = (currutmp->pager+1)%5]);
+    printchatline(genbuf);
+}
+
+extern char *str_space;
+extern userec_t xuser;
+extern char *fn_plans;
+extern char *err_uid;
+
+static void chat_query(char *arg) {
+    char *uid;
+    int tuid;
+    
+    printchatline("");
+    strtok(arg, str_space);
+    if((uid = strtok(NULL, str_space)) && (tuid = getuser(uid))) {
+	char buf[128], *ptr;
+	FILE *fp;
+	
+	sprintf(buf, "%s(%s) �@�W�� %d ���A�o���L %d �g�峹",
+		xuser.userid, xuser.username, xuser.numlogins, xuser.numposts);
+	printchatline(buf);
+	
+	sprintf(buf, "�̪�(%s)�q[%s]�W��", Cdate(&xuser.lastlogin),
+		(xuser.lasthost[0] ? xuser.lasthost : "(����)"));
+	printchatline(buf);
+	
+	sethomefile(buf, xuser.userid, fn_plans);
+	if((fp = fopen(buf, "r"))) {
+	    tuid = 0;
+	    while(tuid++ < MAX_QUERYLINES && fgets(buf, 128, fp)) {
+		if((ptr = strchr(buf, '\n')))
+		    ptr[0] = '\0';
+		printchatline(buf);
+	    }
+	    fclose(fp);
+	}
+    } else
+	printchatline(err_uid);
+}
+
+extern char *msg_shortulist;
+
+static void chat_users() {
+    printchatline("");
+    printchatline("�i " BBSNAME "���C�ȦC�� �j");
+    printchatline(msg_shortulist);
+
+    if(apply_ulist(printuserent) == -1)
+	printchatline("�ŵL�@�H");
+    printuserent(NULL);
+}
+
+typedef struct chat_command_t {
+    char *cmdname;                /* Chatroom command length */
+    void (*cmdfunc) ();           /* Pointer to function */
+} chat_command_t;
+
+static chat_command_t chat_cmdtbl[] = {
+    {"help", chat_help},
+    {"clear", chat_clear},
+    {"date", chat_date},
+    {"pager", chat_pager},
+    {"query", chat_query},
+    {"users", chat_users},
+    {NULL, NULL}
+};
+
+static int chat_cmd_match(char *buf, char *str) {
+    while(*str && *buf && !isspace(*buf))
+	if(tolower(*buf++) != *str++)
+	    return 0;
+    return 1;
+}
+
+static int chat_cmd(char *buf, int fd) {
+    int i;
+
+    if(*buf++ != '/')
+	return 0;
+
+    for(i = 0; chat_cmdtbl[i].cmdname; i++) {
+	if(chat_cmd_match(buf, chat_cmdtbl[i].cmdname)) {
+	    chat_cmdtbl[i].cmdfunc(buf);
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+extern char trans_buffer[256];         /* �@��ǻ��ܼ� add by Ptt */
+
+#if 0
+static char *select_address() {
+    int c;
+    FILE *fp;
+    char nametab[25][90];
+    char iptab[25][18], buf[80];
+
+    move(1, 0);
+    clrtobot();
+    outs("\n          \033[36m�i��Ӧa����b�a!�j\033[m "
+	 "��  �i�H�U�������n�O���ת����ӡj          \n");
+    trans_buffer[0]=0;
+    if((fp = fopen("etc/teashop", "r"))) {
+	for(c = 0; fscanf(fp, "%s%s", iptab[c], nametab[c]) != EOF; c++) {
+            sprintf(buf,"\n            (\033[36m%d\033[0m) %-30s     [%s]",
+		    c + 1, nametab[c], iptab[c]);
+            outs(buf);
+	}
+	getdata(20, 10, "��\033[32m �п�ܡA[0]���}�G\033[0m", buf, 3, 
+		LCECHO);
+	if(buf[1])
+	    buf[0] = (buf[0] + 1) * 10 + (buf[1] - '1');
+	else
+	    buf[0] -= '1';
+	if(buf[0] >= 0 && buf[0] < c)
+	    strcpy(trans_buffer,iptab[(int)buf[0]]);
+    } else {
+	outs("�����S���n�O����X�����");
+	pressanykey();
+    }
+    return trans_buffer;
+}
+#endif
+
+extern int usernum;
+extern int t_lines;
+extern char *msg_seperator;
+#define MAXLASTCMD 6
+static int chatid_len = 10;
+
+int t_chat() {
+    char inbuf[80], chatid[20], lastcmd[MAXLASTCMD][80], *ptr = "";
+    struct sockaddr_in sin;
+    struct hostent *h;
+    int cfd, cmdpos, ch;
+    int currchar;
+    int newmail;
+    int chatting = YEA;
+    char fpath[80];
+    char genbuf[200];
+    char roomtype; 
+
+    if(inbuf[0] == 0)
+	return -1;
+
+    outs("                     �X���e�� ��........         ");
+    if(!(h = gethostbyname("localhost"))) {
+	perror("gethostbyname");
+	return -1;
+    }
+    memset(&sin, 0, sizeof sin);
+#ifdef FreeBSD
+    sin.sin_len = sizeof(sin);
+#endif
+    sin.sin_family = PF_INET;
+    memcpy(&sin.sin_addr, h->h_addr, h->h_length);
+    sin.sin_port = htons(NEW_CHATPORT);
+    cfd = socket(sin.sin_family, SOCK_STREAM, 0);
+    if(!(connect(cfd, (struct sockaddr *) & sin, sizeof sin)))
+	roomtype = 1;
+    else {
+	sin.sin_port = CHATPORT;
+	cfd = socket(sin.sin_family, SOCK_STREAM, 0);
+	if(!(connect(cfd, (struct sockaddr *) & sin, sizeof sin)))
+	    roomtype = 2;
+	else {
+	    outs("\n              "
+		 "�z! �S�H�b����C...�n�����a�誺�H���h�}����!...");
+            system("bin/xchatd");
+	    pressanykey();
+	    return -1;
+	}
+    }
+  
+    while(1) {
+	getdata(b_lines - 1, 0, "�п�J��ѥN���G", inbuf, 9, DOECHO);
+	sprintf(chatid, "%s", (inbuf[0] ? inbuf : cuser.userid));
+	chatid[8] = '\0';
+/*
+  �®榡:    /! �ϥΪ̽s�� �ϥΪ̵��� UserID ChatID
+  �s�榡:    /! UserID ChatID Password
+*/
+	if(roomtype == 1)
+	    sprintf(inbuf, "/! %s %s %s",
+		    cuser.userid, chatid, cuser.passwd);
+	else
+	    sprintf(inbuf, "/! %d %d %s %s",
+		    usernum, cuser.userlevel, cuser.userid, chatid);
+	chat_send(cfd, inbuf);
+	if(recv(cfd, inbuf, 3, 0) != 3)
+	    return 0;
+	if(!strcmp(inbuf, CHAT_LOGIN_OK))
+	    break;
+	else if(!strcmp(inbuf, CHAT_LOGIN_EXISTS))
+	    ptr = "�o�ӥN���w�g���H�ΤF";
+	else if(!strcmp(inbuf, CHAT_LOGIN_INVALID))
+	    ptr = "�o�ӥN���O���~��";
+	else if(!strcmp(inbuf, CHAT_LOGIN_BOGUS))
+	    ptr = "�ФŬ��������i�J��ѫ� !!";
+	
+	move(b_lines - 2, 0);
+	outs(ptr);
+	clrtoeol();
+	bell();
+    }
+    
+    add_io(cfd, 0);
+    
+    newmail = currchar = 0;
+    cmdpos = -1;
+    memset(lastcmd, 0, MAXLASTCMD * 80);
+    
+    setutmpmode(CHATING);
+    currutmp->in_chat = YEA;
+    strcpy(currutmp->chatid, chatid);
+    
+    clear();
+    chatline = 2;
+    strcpy(inbuf, chatid);
+    stop_line = t_lines - 3;
+    
+    move(stop_line, 0);
+    outs(msg_seperator);
+    move(1, 0);
+    outs(msg_seperator);
+    print_chatid(chatid);
+    memset(inbuf, 0, 80);
+
+    sethomepath(fpath, cuser.userid);
+    strcpy(fpath, tempnam(fpath, "chat_"));
+    flog = fopen(fpath, "w");
+
+    while(chatting) {
+	move(b_lines - 1, currchar + chatid_len);
+	ch = igetkey();
+
+	switch(ch) {
+	case KEY_DOWN:
+	    cmdpos += MAXLASTCMD - 2;
+	case KEY_UP:
+	    cmdpos++;
+	    cmdpos %= MAXLASTCMD;
+	    strcpy(inbuf, lastcmd[cmdpos]);
+	    move(b_lines - 1, chatid_len);
+	    clrtoeol();
+	    outs(inbuf);
+	    currchar = strlen(inbuf);
+	    continue;
+	case KEY_LEFT:
+	    if(currchar)
+		--currchar;
+	    continue;
+	case KEY_RIGHT:
+	    if(inbuf[currchar])
+		++currchar;
+	    continue;
+	}
+
+	if(!newmail && currutmp->mailalert ) {
+	    newmail = 1;
+	    printchatline("�� ���I�l�t�S�ӤF...");
+	}
+	
+	if(ch == I_OTHERDATA) {     /* incoming */
+	    if(chat_recv(cfd, chatid) == -1) {
+		chatting = chat_send(cfd, "/b");
+		break;
+	    }
+	    continue;
+	}
+	if(isprint2(ch)) {
+	    if(currchar < 68) {
+		if(inbuf[currchar]) {   /* insert */
+		    int i;
+		    
+		    for(i = currchar; inbuf[i] && i < 68; i++);
+		    inbuf[i + 1 ] = '\0';
+		    for(; i > currchar; i--)
+			inbuf[i] = inbuf[i - 1];
+		} else                  /* append */
+		    inbuf[currchar + 1] = '\0';
+		inbuf[currchar] = ch;
+		move(b_lines - 1, currchar + chatid_len);
+		outs(&inbuf[currchar++]);
+	    }
+	    continue;
+	}
+
+	if(ch == '\n' || ch == '\r') {
+	    if(*inbuf) {
+		chatting = chat_cmd(inbuf, cfd);
+		if(chatting == 0)
+		    chatting = chat_send(cfd, inbuf);
+		if(!strncmp(inbuf, "/b", 2))
+		    break;
+		
+		for(cmdpos = MAXLASTCMD - 1; cmdpos; cmdpos--)
+		    strcpy(lastcmd[cmdpos], lastcmd[cmdpos - 1]);
+		strcpy(lastcmd[0], inbuf);
+		
+		inbuf[0] = '\0';
+		currchar = 0;
+		cmdpos = -1;
+	    }
+	    print_chatid(chatid);
+	    move(b_lines - 1, chatid_len);
+	    continue;
+	}
+
+	if(ch == Ctrl('H') || ch == '\177') {
+	    if(currchar) {
+		currchar--;
+		inbuf[69] = '\0';
+		memcpy(&inbuf[currchar], &inbuf[currchar + 1], 69 - currchar);
+		move(b_lines - 1, currchar + chatid_len);
+		clrtoeol();
+		outs(&inbuf[currchar]);
+	    }
+	    continue;
+	}
+	if(ch == Ctrl('Z') || ch == Ctrl('Y')) {
+	    inbuf[0] = '\0';
+	    currchar = 0;
+	    print_chatid(chatid);
+	    move(b_lines - 1, chatid_len);
+	    continue;
+	}
+	
+	if(ch == Ctrl('C')) {
+	    chat_send(cfd, "/b");
+	    break;
+	}
+	if(ch == Ctrl('D')) {
+	    if(currchar < strlen(inbuf)) {
+		inbuf[69] = '\0';
+		memcpy(&inbuf[currchar], &inbuf[currchar + 1], 69 - currchar);
+		move(b_lines - 1, currchar + chatid_len);
+		clrtoeol();
+		outs(&inbuf[currchar]);
+	    }
+	    continue;
+	}
+	if(ch == Ctrl('K')) {
+	    inbuf[currchar] = 0;
+	    move(b_lines - 1, currchar + chatid_len);
+	    clrtoeol();
+	    continue;
+	}
+	if(ch == Ctrl('A')) {
+	    currchar = 0;
+	    continue;
+	}
+	if(ch == Ctrl('E')) {
+	    currchar = strlen(inbuf);
+	    continue;
+	}
+	if(ch == Ctrl('I')) {
+	    extern screenline_t *big_picture;
+	    screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t));
+
+	    memcpy(screen0, big_picture, t_lines * sizeof(screenline_t));
+	    add_io(0, 0);
+	    t_idle();
+	    memcpy(big_picture, screen0, t_lines * sizeof(screenline_t));
+	    free(screen0);
+	    redoscr();
+	    add_io(cfd, 0);
+	    continue;
+	}
+	if(ch == Ctrl('Q')) {
+	    print_chatid(chatid);
+	    move(b_lines - 1, chatid_len);
+	    outs(inbuf);
+	    continue;
+	}
+    }
+    
+    close(cfd);
+    add_io(0, 0);
+    currutmp->in_chat = currutmp->chatid[0] = 0;
+    
+    if(flog) {
+	char ans[4];
+	
+	fclose(flog);
+	more(fpath, NA);
+	getdata(b_lines - 1, 0, "�M��(C) ���ܳƧѿ�(M) (C/M)?[C]",
+		ans, 4, LCECHO);
+	if (*ans == 'm') {
+	    fileheader_t mymail;
+	    char title[128];
+	    
+	    sethomepath(genbuf, cuser.userid);
+	    stampfile(genbuf, &mymail);
+	    mymail.savemode = 'H';        /* hold-mail flag */
+	    mymail.filemode = FILE_READ;
+	    strcpy(mymail.owner, "[��.��.��]");
+	    strcpy(mymail.title, "�|ij\033[1;33m�O��\033[m");
+	    sethomedir(title, cuser.userid);
+	    append_record(title, &mymail, sizeof(mymail));
+	    Rename(fpath, genbuf);
+	} else
+	    unlink(fpath);
+    }
+    
+    return 0;
+}
+/* -------------------------------------------------- */
+#if 0
+
+extern char page_requestor[];
+extern userinfo_t *currutmp;
+
+#endif
diff --git a/mbbsd/chc_draw.c b/mbbsd/chc_draw.c
new file mode 100644
index 00000000..b3bd216f
--- /dev/null
+++ b/mbbsd/chc_draw.c
@@ -0,0 +1,187 @@
+/* $Id: chc_draw.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+#define SIDE_ROW          10
+#define TURN_ROW          11
+#define STEP_ROW          12
+#define TIME_ROW          13
+#define WARN_ROW          15
+#define MYWIN_ROW         17
+#define HISWIN_ROW        18
+
+extern char chc_warnmsg[64], *chc_mateid;
+extern rc_t chc_from, chc_to, chc_select;
+extern int chc_selected, chc_my, chc_turn, chc_firststep;
+extern int chc_lefttime;
+extern int chc_hiswin, chc_hislose, chc_histie;
+
+static char *turn_str[2] = {"�ª�", "����"};
+
+static char *num_str[10] = {
+    "", "�@", "�G", "�T", "�|", "��", "��", "�C", "�K", "�E"
+};
+
+static char *chess_str[2][8] = {
+    /* 0     1     2     3     4     5     6     7 */
+    {"  ", "�N", "�h", "�H", "��", "��", "�]", "��"},
+    {"  ", "��", "�K", "��", "��", "�X", "��", "�L"}
+};
+
+static char *chess_brd[BRD_ROW * 2 - 1] = {
+    /*0   1   2   3   4   5   6   7   8*/
+    "�z�w�s�w�s�w�s�w�s�w�s�w�s�w�s�w�{", /* 0 */
+    "�x  �x  �x  �x�@�x���x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 1 */
+    "�x  �x  �x  �x���x�@�x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 2 */
+    "�x  �x  �x  �x  �x  �x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 3 */
+    "�x  �x  �x  �x  �x  �x  �x  �x  �x",
+    "�u�w�r�w�r�w�r�w�r�w�r�w�r�w�r�w�t", /* 4 */
+    "�x  ��    �e          �~    ��  �x",
+    "�u�w�s�w�s�w�s�w�s�w�s�w�s�w�s�w�t", /* 5 */
+    "�x  �x  �x  �x  �x  �x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 6 */
+    "�x  �x  �x  �x  �x  �x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 7 */
+    "�x  �x  �x  �x�@�x���x  �x  �x  �x",
+    "�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t", /* 8 */
+    "�x  �x  �x  �x���x�@�x  �x  �x  �x",
+    "�|�w�r�w�r�w�r�w�r�w�r�w�r�w�r�w�}"  /* 9 */
+};
+
+static char *hint_str[] = {
+    "  q      �{�����}",
+    "  p      �n�D�M��",
+    "��V��   ���ʹC��",
+    "Enter    ���/����"
+};
+
+void chc_movecur(int r, int c) {
+    move(r * 2 + 3, c * 4 + 4);
+}
+
+#define BLACK_COLOR       "\033[1;36m"
+#define RED_COLOR         "\033[1;31m"
+#define BLACK_REVERSE     "\033[1;37;46m"
+#define RED_REVERSE       "\033[1;37;41m"
+#define TURN_COLOR        "\033[1;33m"
+
+static void showstep(board_t board) {
+    int turn, fc, tc, eatten;
+    char *dir;
+    
+    turn = CHE_O(board[chc_from.r][chc_from.c]);
+    fc = (turn == (chc_my ^ 1) ? chc_from.c + 1 : 9 - chc_from.c);
+    tc = (turn == (chc_my ^ 1) ? chc_to.c + 1 : 9 - chc_to.c);
+    if(chc_from.r == chc_to.r)
+	dir = "��";
+    else {
+	if(chc_from.c == chc_to.c)
+	    tc = chc_from.r - chc_to.r;
+	if(tc < 0) tc = -tc;
+	
+	if((turn == (chc_my ^ 1) && chc_to.r > chc_from.r) ||
+	   (turn == chc_my && chc_to.r < chc_from.r))
+	    dir = "�i";
+	else
+	    dir = "�h";
+    }
+    prints("%s%s%s%s%s",
+	   turn == 0 ? BLACK_COLOR : RED_COLOR,
+	   chess_str[turn][CHE_P(board[chc_from.r][chc_from.c])],
+	   num_str[fc], dir, num_str[tc]);
+    eatten = board[chc_to.r][chc_to.c];
+    if(eatten)
+	prints("�G %s%s",
+	       CHE_O(eatten) == 0 ? BLACK_COLOR : RED_COLOR,
+	       chess_str[CHE_O(eatten)][CHE_P(eatten)]);
+    prints("\033[m");
+}
+
+extern userec_t cuser;
+
+void chc_drawline(board_t board, int line) {
+    int i, j;
+    
+    move(line, 0);
+    clrtoeol();
+    if(line == 0) {
+	prints("\033[1;46m   �H�ѹ��   \033[45m%30s VS %-30s\033[m",
+	       cuser.userid, chc_mateid);
+    } else if(line >= 3 && line <= 21) {
+	outs("   ");
+	for(i = 0; i < 9; i++) {
+	    j = board[RTL(line)][i];
+	    if((line & 1) == 1 && j) {
+		if(chc_selected &&
+		   chc_select.r == RTL(line) && chc_select.c == i)
+		    prints("%s%s\033[m",
+			   CHE_O(j) == 0 ? BLACK_REVERSE : RED_REVERSE,
+			   chess_str[CHE_O(j)][CHE_P(j)]);
+		else
+		    prints("%s%s\033[m",
+			   CHE_O(j) == 0 ? BLACK_COLOR : RED_COLOR,
+			   chess_str[CHE_O(j)][CHE_P(j)]);
+	    } else
+		prints("%c%c", chess_brd[line - 3][i * 4],
+		       chess_brd[line - 3][i * 4 + 1]);
+	    if(i != 8)
+		prints("%c%c", chess_brd[line - 3][i * 4 + 2],
+		       chess_brd[line - 3][i * 4 + 3]);
+	}
+	outs("        ");
+
+	if(line >= 3 && line < 3 + dim(hint_str)) {
+	    outs(hint_str[line - 3]);
+	} else if(line == SIDE_ROW) {
+	    prints("\033[1m�A�O%s%s\033[m",
+		   chc_my == 0 ? BLACK_COLOR : RED_COLOR,
+		   turn_str[chc_my]);
+	} else if(line == TURN_ROW) {
+	    prints("%s%s\033[m",
+		   TURN_COLOR,
+		   chc_my == chc_turn ? "����A�U�ѤF" : "���ݹ��U��");
+	} else if(line == STEP_ROW && !chc_firststep) {
+	    showstep(board);
+	} else if(line == TIME_ROW) {
+	    prints("�Ѿl�ɶ� %d:%02d", chc_lefttime / 60, chc_lefttime % 60);
+	} else if(line == WARN_ROW) {
+	    outs(chc_warnmsg);
+	} else if(line == MYWIN_ROW) {
+	    prints("\033[1;33m%12.12s    "
+		   "\033[1;31m%2d\033[37m�� "
+		   "\033[34m%2d\033[37m�� "
+		   "\033[36m%2d\033[37m�M\033[m",
+		   cuser.userid,
+		   cuser.chc_win, cuser.chc_lose - 1, cuser.chc_tie);
+	} else if(line == HISWIN_ROW) {
+	    prints("\033[1;33m%12.12s    "
+		   "\033[1;31m%2d\033[37m�� "
+		   "\033[34m%2d\033[37m�� "
+		   "\033[36m%2d\033[37m�M\033[m",
+		   chc_mateid,
+		   chc_hiswin, chc_hislose - 1, chc_histie);
+	}
+    } else if(line == 2 || line == 22) {
+	outs("   ");
+	if(line == 2)
+	    for(i = 1; i <= 9; i++)
+		prints("%s  ", num_str[i]);
+	else
+	    for(i = 9; i >= 1; i--)
+		prints("%s  ", num_str[i]);
+    }
+}
+
+void chc_redraw(board_t board) {
+    int i;
+    
+    for(i = 0; i <= 22; i++)
+	chc_drawline(board, i);
+}
diff --git a/mbbsd/chc_net.c b/mbbsd/chc_net.c
new file mode 100644
index 00000000..d094d5f2
--- /dev/null
+++ b/mbbsd/chc_net.c
@@ -0,0 +1,28 @@
+/* $Id: chc_net.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "config.h"
+#include "pttstruct.h"
+
+extern rc_t chc_from, chc_to;
+
+typedef struct drc_t {
+    rc_t from, to;
+} drc_t;
+
+int chc_recvmove(int s) {
+    drc_t buf;
+
+    if(read(s, &buf, sizeof(buf)) != sizeof(buf))
+	return 1;
+    chc_from = buf.from, chc_to = buf.to;
+    return 0;
+}
+
+void chc_sendmove(int s) {
+    drc_t buf;
+    
+    buf.from = chc_from, buf.to = chc_to;
+    write(s, &buf, sizeof(buf));
+}
diff --git a/mbbsd/chc_play.c b/mbbsd/chc_play.c
new file mode 100644
index 00000000..86c17fbc
--- /dev/null
+++ b/mbbsd/chc_play.c
@@ -0,0 +1,277 @@
+/* $Id: chc_play.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern userinfo_t *currutmp;
+extern int usernum;
+
+typedef int (*play_func_t)(int, board_t, board_t);
+
+static int chc_ipass = 0, chc_hepass = 0;
+int chc_lefttime;
+int chc_my, chc_turn, chc_selected, chc_firststep;
+char chc_warnmsg[64], *chc_mateid;
+rc_t chc_from, chc_to, chc_select, chc_cursor;
+int chc_hiswin, chc_hislose, chc_histie;
+
+#define CHC_TIMEOUT           300
+#define SIDE_ROW          10
+#define TURN_ROW          11
+#define STEP_ROW          12
+#define TIME_ROW          13
+#define WARN_ROW          15
+#define MYWIN_ROW         17
+#define HISWIN_ROW        18
+
+static int hisplay(int s, board_t board, board_t tmpbrd) {
+    int start_time;
+    int endgame = 0, endturn = 0;
+    
+    start_time = time(NULL);
+    while(!endturn) {
+	chc_lefttime = CHC_TIMEOUT - (time(NULL) - start_time);
+	if(chc_lefttime < 0) {
+	    chc_lefttime = 0;
+
+	    /* to make him break out igetkey() */
+	    chc_from.r = -2;
+	    chc_sendmove(s);
+	}
+	chc_drawline(board, TIME_ROW);
+	move(1, 0);
+	oflush();
+	switch(igetkey()) {
+	case 'q':
+	    endgame = 2;
+	    endturn = 1;
+	    break;
+	case 'p':
+	    if(chc_hepass) {
+		chc_from.r = -1;
+		chc_sendmove(s);
+		endgame = 3;
+		endturn = 1;
+	    }
+	    break;
+	case I_OTHERDATA:
+	    if(chc_recvmove(s)) { /* disconnect */
+		endturn = 1;
+		endgame = 1;
+	    } else {
+		if(chc_from.r == -1) {
+		    chc_hepass = 1;
+		    strcpy(chc_warnmsg, "\033[1;33m�n�D�M��!\033[m");
+		    chc_drawline(board, WARN_ROW);
+		} else {
+		    chc_from.r = 9 - chc_from.r, chc_from.c = 8 - chc_from.c;
+		    chc_to.r = 9 - chc_to.r, chc_to.c = 8 - chc_to.c;
+		    chc_cursor = chc_to;
+		    if(CHE_P(board[chc_to.r][chc_to.c]) == 1)
+			endgame = 2;
+		    endturn = 1;
+		    chc_hepass = 0;
+		    chc_drawline(board, STEP_ROW);
+		    chc_movechess(board);
+		    chc_drawline(board, LTR(chc_from.r));
+		    chc_drawline(board, LTR(chc_to.r));
+		}
+	    }
+	    break;
+	}
+    }
+    return endgame;
+}
+
+static int myplay(int s, board_t board, board_t tmpbrd) {
+    int ch, start_time;
+    int endgame = 0, endturn = 0;
+    
+    chc_ipass = 0, chc_selected = 0;
+    start_time = time(NULL);
+    chc_lefttime = CHC_TIMEOUT - (time(NULL) - start_time);
+    bell();
+    while(!endturn) {
+	chc_drawline(board, TIME_ROW);
+	chc_movecur(chc_cursor.r, chc_cursor.c);
+	oflush();
+	ch = igetkey();
+       	chc_lefttime = CHC_TIMEOUT - (time(NULL) - start_time);
+	if(chc_lefttime < 0)
+	    ch = 'q';
+	switch(ch) {
+	case I_OTHERDATA:
+	    if(chc_recvmove(s)) { /* disconnect */
+		endgame = 1;
+		endturn = 1;
+	    } else if(chc_from.r == -1 && chc_ipass) {
+		endgame = 3;
+		endturn = 1;
+	    }
+	    break;
+	case KEY_UP:
+	    chc_cursor.r--;
+	    if(chc_cursor.r < 0)
+		chc_cursor.r = BRD_ROW - 1;
+	    break;
+	case KEY_DOWN:
+	    chc_cursor.r++;
+	    if(chc_cursor.r >= BRD_ROW)
+		chc_cursor.r = 0;
+	    break;
+	case KEY_LEFT:
+	    chc_cursor.c--;
+	    if(chc_cursor.c < 0)
+		chc_cursor.c = BRD_COL - 1;
+	    break;
+	case KEY_RIGHT:
+	    chc_cursor.c++;
+	    if(chc_cursor.c >= BRD_COL)
+		chc_cursor.c = 0;
+	    break;
+	case 'q':
+	    endgame = 2;
+	    endturn = 1;
+	    break;
+	case 'p':
+	    chc_ipass = 1;
+	    chc_from.r = -1;
+	    chc_sendmove(s);
+	    strcpy(chc_warnmsg, "\033[1;33m�n�D�M��!\033[m");
+	    chc_drawline(board, WARN_ROW);
+	    bell();
+	    break;
+	case '\r':
+	case '\n':
+	case ' ':
+	    if(chc_selected) {
+		if(chc_cursor.r == chc_select.r &&
+		   chc_cursor.c == chc_select.c) {
+		    chc_selected = 0;
+		    chc_drawline(board, LTR(chc_cursor.r));
+		} else if(chc_canmove(board, chc_select, chc_cursor)) {
+		    if(CHE_P(board[chc_cursor.r][chc_cursor.c]) == 1)
+			endgame = 1;
+		    chc_from = chc_select;
+		    chc_to = chc_cursor;
+		    if(!endgame) {
+			memcpy(tmpbrd, board, sizeof(board_t));
+			chc_movechess(tmpbrd);
+		    }
+		    if(endgame || !chc_iskfk(tmpbrd)) {
+			chc_drawline(board, STEP_ROW);
+			chc_movechess(board);
+			chc_sendmove(s);
+			chc_selected = 0;
+			chc_drawline(board, LTR(chc_from.r));
+			chc_drawline(board, LTR(chc_to.r));
+			endturn = 1;
+		    } else {
+			strcpy(chc_warnmsg, "\033[1;33m���i�H������\033[m");
+			bell();
+			chc_drawline(board, WARN_ROW);
+		    }
+		}
+	    } else if(board[chc_cursor.r][chc_cursor.c] &&
+		      CHE_O(board[chc_cursor.r][chc_cursor.c]) == chc_turn) {
+		chc_selected = 1;
+		chc_select = chc_cursor;
+		chc_drawline(board, LTR(chc_cursor.r));
+	    }
+	    break;
+	}
+    }
+    return endgame;
+}
+
+extern userec_t cuser;
+
+static void mainloop(int s, board_t board) {
+    int endgame;
+    board_t tmpbrd;
+    play_func_t play_func[2];
+    
+    play_func[chc_my] = myplay;
+    play_func[chc_my ^ 1] = hisplay;
+    for(chc_turn = 1, endgame = 0; !endgame; chc_turn ^= 1) {
+	chc_firststep = 0;
+	chc_drawline(board, TURN_ROW);
+	if(chc_ischeck(board, chc_turn)) {
+	    strcpy(chc_warnmsg, "\033[1;31m�N�x!\033[m");
+	    bell();
+	} else
+	    chc_warnmsg[0] = 0;
+	chc_drawline(board, WARN_ROW);
+	endgame = play_func[chc_turn](s, board, tmpbrd);
+    }
+    
+    if(endgame == 1) {
+	strcpy(chc_warnmsg, "���{��F!");
+	cuser.chc_win++;
+    } else if(endgame == 2) {
+	strcpy(chc_warnmsg, "�A�{��F!");
+	cuser.chc_lose++;
+    } else {
+	strcpy(chc_warnmsg, "�M��");
+	cuser.chc_tie++;
+    }
+    cuser.chc_lose--;
+    passwd_update(usernum, &cuser);
+    chc_drawline(board, WARN_ROW);
+    bell();
+    oflush();
+}
+
+extern userec_t xuser;
+
+static void chc_init(int s, board_t board) {
+    userinfo_t *my = currutmp;
+
+    setutmpmode(CHC);
+    clear();
+    chc_warnmsg[0] = 0;
+    chc_my = my->turn;
+    chc_mateid = my->mateid;
+    chc_firststep = 1;
+    chc_init_board(board);
+    chc_redraw(board);
+    chc_cursor.r = 9, chc_cursor.c = 0;
+    add_io(s, 0);
+    
+    if(my->turn) chc_recvmove(s);
+    passwd_query(usernum, &xuser);
+    cuser.chc_win = xuser.chc_win;
+    cuser.chc_lose = xuser.chc_lose + 1;
+    cuser.chc_tie = xuser.chc_tie;
+    cuser.money   = xuser.money;
+    passwd_update(usernum, &cuser);
+    
+    getuser(chc_mateid);
+    chc_hiswin = xuser.chc_win;
+    chc_hislose = xuser.chc_lose;
+    chc_histie = xuser.chc_tie;
+    
+    if(!my->turn) {
+	chc_sendmove(s);
+	chc_hislose++;
+    }
+    
+    chc_redraw(board);
+}
+
+void chc(int s) {
+    board_t board;
+    
+    chc_init(s, board);
+    mainloop(s, board);
+    close(s);
+    add_io(0, 0);
+    if(chc_my) pressanykey();
+}
diff --git a/mbbsd/chc_rule.c b/mbbsd/chc_rule.c
new file mode 100644
index 00000000..35d8fe6a
--- /dev/null
+++ b/mbbsd/chc_rule.c
@@ -0,0 +1,186 @@
+/* $Id: chc_rule.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+extern rc_t chc_from, chc_to;
+extern int chc_my;
+
+#define CENTER(a, b)      (((a) + (b)) >> 1)
+
+void chc_init_board(board_t board) {
+    memset(board, 0, sizeof(board_t));
+    board[0][4] = CHE(1, chc_my ^ 1);                    /* �N */
+    board[0][3] = board[0][5] = CHE(2, chc_my ^ 1);      /* �h */
+    board[0][2] = board[0][6] = CHE(3, chc_my ^ 1);      /* �H */
+    board[0][0] = board[0][8] = CHE(4, chc_my ^ 1);      /* �� */
+    board[0][1] = board[0][7] = CHE(5, chc_my ^ 1);      /* �� */
+    board[2][1] = board[2][7] = CHE(6, chc_my ^ 1);      /* �] */
+    board[3][0] = board[3][2] = board[3][4] =
+	board[3][6] = board[3][8] = CHE(7, chc_my ^ 1);  /* �� */
+
+    board[9][4] = CHE(1, chc_my);                    /* �� */
+    board[9][3] = board[9][5] = CHE(2, chc_my);      /* �K */
+    board[9][2] = board[9][6] = CHE(3, chc_my);      /* �� */
+    board[9][0] = board[9][8] = CHE(4, chc_my);      /* �� */
+    board[9][1] = board[9][7] = CHE(5, chc_my);      /* �X */
+    board[7][1] = board[7][7] = CHE(6, chc_my);      /* �� */
+    board[6][0] = board[6][2] = board[6][4] =
+	board[6][6] = board[6][8] = CHE(7, chc_my);  /* �L */
+}
+
+void chc_movechess(board_t board) {
+    board[chc_to.r][chc_to.c] = board[chc_from.r][chc_from.c];
+    board[chc_from.r][chc_from.c] = 0;
+}
+
+static int dist(rc_t from, rc_t to, int rowcol) {
+    int d;
+    
+    d = rowcol ? from.c - to.c : from.r - to.r;
+    return d > 0 ? d : -d;
+}
+
+static int between(board_t board, rc_t from, rc_t to, int rowcol) {
+    int i, rtv = 0;
+
+    if(rowcol) {
+	if(from.c > to.c)
+	    i = from.c, from.c = to.c, to.c = i;
+	for(i = from.c + 1; i < to.c; i++)
+	    if(board[to.r][i]) rtv++;
+    } else {
+	if(from.r > to.r)
+	    i = from.r, from.r = to.r, to.r = i;
+	for(i = from.r + 1; i < to.r; i++)
+	    if(board[i][to.c]) rtv++;
+    }
+    return rtv;
+}
+
+int chc_canmove(board_t board, rc_t from, rc_t to) {
+    int i;
+    int rd, cd, turn;
+
+    rd = dist(from, to, 0);
+    cd = dist(from, to, 1);
+    turn = CHE_O(board[from.r][from.c]);
+    
+    /* general check */
+    if(board[to.r][to.c] && CHE_O(board[to.r][to.c]) == turn)
+	return 0;
+
+    /* individual check */
+    switch(CHE_P(board[from.r][from.c])) {
+    case 1: /* �N �� */
+	if(!(rd == 1 && cd == 0) &&
+	   !(rd == 0 && cd == 1))
+	    return 0;
+	if((turn == (chc_my ^ 1) && to.r > 2) ||
+	   (turn == chc_my && to.r < 7) ||
+	    to.c < 3 || to.c > 5)
+		return 0;
+	break;
+    case 2: /* �h �K */
+	if(!(rd == 1 && cd == 1))
+	    return 0;
+	if((turn == (chc_my ^ 1) && to.r > 2) ||
+	   (turn == chc_my && to.r < 7) ||
+	    to.c < 3 || to.c > 5)
+		return 0;
+       	break;
+    case 3: /* �H �� */
+	if(!(rd == 2 && cd == 2))
+	    return 0;
+	if((turn == (chc_my ^ 1) && to.r > 4) ||
+	   (turn == chc_my && to.r < 5))
+	    return 0;
+	/* ��H�L */
+	if(board[CENTER(from.r, to.r)][CENTER(from.c, to.c)])
+	    return 0;
+	break;
+    case 4: /* �� */
+	if(!(rd > 0 && cd == 0) &&
+	   !(rd == 0 && cd > 0))
+	    return 0;
+	if(between(board, from, to, rd == 0))
+	    return 0;
+	break;
+    case 5: /* �� �X */
+	if(!(rd == 2 && cd == 1) &&
+	   !(rd == 1 && cd == 2))
+	    return 0;
+	/* �䰨�} */
+	if(rd == 2) {
+	    if(board[CENTER(from.r, to.r)][from.c])
+		return 0;
+	} else {
+	    if(board[from.r][CENTER(from.c, to.c)])
+		return 0;
+	} 
+	break;
+    case 6: /* �] �� */
+	if(!(rd > 0 && cd == 0) &&
+	   !(rd == 0 && cd > 0))
+	    return 0;
+	i = between(board, from, to, rd == 0);
+	if((i > 1) ||
+	   (i == 1 && !board[to.r][to.c]) ||
+	   (i == 0 && board[to.r][to.c]))
+	   return 0;
+	break;
+    case 7: /* �� �L */
+	if(!(rd == 1 && cd == 0) &&
+	   !(rd == 0 && cd == 1))
+	    return 0;
+	if(((turn == (chc_my ^ 1) && to.r < 5) ||
+	    (turn == chc_my && to.r > 4)) &&
+	    cd != 0)
+	    return 0;
+	if((turn == (chc_my ^ 1) && to.r < from.r) ||
+	   (turn == chc_my && to.r > from.r))
+	    return 0;
+	break;
+    }
+    return 1;
+}
+
+static void findking(board_t board, int turn, rc_t *buf) {
+    int i, r, c;
+
+    r = (turn == (chc_my ^ 1)) ? 0 : 7;
+    for(i = 0; i < 3; r++, i++)
+	for(c = 3; c < 6; c++)
+	    if(CHE_P(board[r][c]) == 1 &&
+	       CHE_O(board[r][c]) == turn) {
+		buf->r = r, buf->c = c;
+		return ;
+	    }
+}
+
+int chc_iskfk(board_t board) {
+    rc_t from, to;
+    
+    findking(board, 0, &to);
+    findking(board, 1, &from);
+    if(from.c == to.c && between(board, from, to, 0) == 0)
+	return 1;
+    return 0;
+}
+
+int chc_ischeck(board_t board, int turn) {
+    rc_t from, to;
+    
+    findking(board, turn, &to);
+    for(from.r = 0;from.r < BRD_ROW; from.r++)
+	for(from.c = 0; from.c < BRD_COL; from.c++)
+	    if(board[from.r][from.c] &&
+	       CHE_O(board[from.r][from.c]) != turn)
+		if(chc_canmove(board, from, to))
+		    return 1;
+    return 0;
+}
diff --git a/mbbsd/chicken.c b/mbbsd/chicken.c
new file mode 100644
index 00000000..f789925f
--- /dev/null
+++ b/mbbsd/chicken.c
@@ -0,0 +1,989 @@
+/* $Id: chicken.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+#define NUM_KINDS   13                   /* ���h�ֺذʪ� */
+
+static const char *cage[17] = {
+    "�ϥ�", "�g��", "���~", "�֦~", "�C�K", "�C�~",
+    "�C�~", "���O", "���~", "���~", "���~", "���~",
+    "���~", "�Ѧ~", "�Ѧ~", "������", "�j��"};
+static const char *chicken_type[NUM_KINDS] = {
+    "�p��", "���֤k", "�i�h", "�j��",
+    "���s", "���N", "��", "�����p�s",
+    "����", "�c�]", "�Ԫ�", "����",
+    "���^�E"};
+static const char *chicken_food[NUM_KINDS] = {
+    "���}��", "��i�p��", "���ƫK��", "������",
+    "����", "�p��", "�߻氮", "�p���氮",
+    "�_��", "�F��", "����", "�K��",
+    "���L"};
+static const int egg_price[NUM_KINDS] = {
+    5, 25, 30, 40,
+    80, 50, 15, 35,
+    17, 100, 85, 200,
+    200};
+static const int food_price[NUM_KINDS] = {
+    4, 6, 8, 10,
+    12, 12, 5, 6,
+    5, 20, 15, 23,
+    23};
+static const char *attack_type[NUM_KINDS] = {
+    "��", "�@��", "�l", "�r",
+    "����", "��", "��", "��",
+    "�r","�U�N","�t��","�ҥ�",
+    "�C��"};
+
+static const char *damage_degree[] = {
+    "�A�l����", "���o����", "�p�O��", "���L��",
+    "���I�k��", "�ϤO��", "�ˤH��", "������",
+    "�ϥ��O��", "�c������", "�M�I��", "�ƨg��",
+    "�r�P��", "�g���ɫB����", "��Ѱʦa��",
+    "�P�R��", NULL};
+
+enum {
+    OO, FOOD, WEIGHT, CLEAN, RUN, ATTACK, BOOK, HAPPY, SATIS,
+    TEMPERAMENT, TIREDSTRONG, SICK, HP_MAX, MM_MAX
+};
+
+extern userec_t cuser;
+
+static chicken_t *mychicken = &cuser.mychicken;
+static int age;
+
+static const int time_change[NUM_KINDS][14] =
+/*  �ɫ~ ���� �魫 ���b �ӱ� �����O ���� �ּ� ���N ��� �h�� �f�� ���� ���k*/
+{
+/*��*/
+    { 1,  1,  30,    3,   8,    3,    3,  40,  9,   1,   7,   3,   30,   1},
+/*���֤k*/
+    { 1,  1, 110,    1,   4,    7,   41,  20,  9,  25,  25,   7,  110,  15},
+/*�i�h*/
+    { 1,  1, 200,    5,   4,   10,   33,  20, 15,  10,  27,   1,  200,   9},
+/*�j��*/
+    { 1,  1,  10,    5,   8,    1,    1,   5,  3,   1,   4,   1,   10,  30},
+/*���s*/
+    { 1,  1,1000,    9,   1,   13,    4,  12,  3,   1, 200,   1, 1000,   3},
+/*���N*/
+    { 1,  1,  90,    7,  10,    7,    4,  12,  3,  30,  20,   5,   90,  20},
+/*��*/
+    { 1,  1,  30,    5,   5,    6,    4,   8,  3,  15,   7,   4,   30,  21},
+/*�����p�s*/
+    { 1,  1, 100,    9,   7,    7,   20,  50, 10,   8,  24,   4,  100,   9},
+/*��*/
+    { 1,  1,  45,    8,   7,    9,    3,  40, 20,   3,   9,   5,   45,   1},
+/* �c�] */
+    { 1,  1,  45,   10,  11,   11,    5,  21, 11,   1,   9,   5,   45,  25},
+/* �Ԫ� */
+    { 1,  1,  45,    2,  12,   10,   25,   1,  1,  10,   9,   5,   45,  26},
+/* ���� */
+    { 1,  1, 150,    4,   8,   13,   95,  25,  7,  10,  25,   5,  175,  85},
+/* ���^�E */
+    { 1,  1, 147,    2,  10,   10,   85,  20,  4,  25,  25,   5,  145,  95}
+};
+
+extern userec_t xuser;
+extern int usernum;
+
+int reload_chicken() {
+    passwd_query(usernum, &xuser);
+    memcpy(mychicken, &xuser.mychicken, sizeof(chicken_t));
+    if(!mychicken->name[0])
+	return 0;
+    else return 1;
+}
+
+#define CHICKENLOG  "etc/chicken"
+
+static int new_chicken() {
+    char buf[150];    
+    int  price;
+    time_t now;
+    
+    clear();
+    move(2,0);
+    outs("�w���[�{ \033[33m��\033[37;44m Ptt�d������ \033[33;40m��\033[m.. "
+	 "�ثe�J���G\n"
+	 "(a)�p�� $5   (b)���֤k $25   (c)�i�h    $30  (d)�j�� $40  "
+	 "(e)���s $80\n"
+	 "(f)���N $50  (g)��     $15   (h)�����p�s$35  (i)���� $17  "
+	 "(j)�c�] $100\n"
+	 "(k)�Ԫ� $85  (l)����   $200  (m)���^�E  $200\n"
+	 "[0]�ۤv $0\n");
+    getdata_str(6, 0, "�п�ܧA�n�i���ʪ��G", buf, 3, LCECHO, "0");
+
+    buf[0] -= 'a';
+    if(buf[0]<0 || buf[0]>NUM_KINDS-1)
+	return 0;
+    
+    mychicken->type = buf[0];
+    
+    reload_money();
+    price = egg_price[(int)mychicken->type];
+    if(cuser.money < price) {
+	prints("\n �������R�J�J,�J�J�n %d ��", price);
+	refresh();
+	return 0;
+    }
+    vice(price,"�d���J");
+    while(strlen(mychicken->name)<3)
+	getdata(8, 0, "���e���Ӧn�W�r�G", mychicken->name, 18, DOECHO);
+
+    now = time(NULL);
+    sprintf(buf,"\033[31m%s \033[m�i�F�@���s\033[33m %s \033[m�� "
+	    "\033[32m%s\033[m  �� %s",cuser.userid,
+	    mychicken->name,chicken_type[(int)mychicken->type],ctime(&now));
+    log_file(CHICKENLOG,buf);
+    mychicken->lastvisit = mychicken->birthday = mychicken->cbirth = now;
+    mychicken->food   =   0;
+    mychicken->weight = time_change[(int)mychicken->type][WEIGHT]/3;
+    mychicken->clean  =   0;
+    mychicken->run    = time_change[(int)mychicken->type][RUN];
+    mychicken->attack = time_change[(int)mychicken->type][ATTACK];
+    mychicken->book   = time_change[(int)mychicken->type][BOOK];
+    mychicken->happy  = time_change[(int)mychicken->type][HAPPY];
+    mychicken->satis  = time_change[(int)mychicken->type][SATIS];
+    mychicken->temperament = time_change[(int)mychicken->type][TEMPERAMENT];
+    mychicken->tiredstrong = 0;
+    mychicken->sick   =   0;
+    mychicken->hp     = time_change[(int)mychicken->type][WEIGHT];
+    mychicken->hp_max = time_change[(int)mychicken->type][WEIGHT];
+    mychicken->mm     =   0;
+    mychicken->mm_max =   0;
+    return 1;
+}
+
+int show_file(char *filename, int y, int lines, int mode) {
+    FILE *fp;
+    char buf[256];
+    
+    if(y >= 0)
+	move(y,0);
+    clrtoline(lines + y);
+    if((fp=fopen(filename,"r"))) {
+	while(fgets(buf,256,fp) && lines--)
+	    outs(Ptt_prints(buf,mode));
+	fclose(fp);
+    } else
+	return 0;
+    return 1;
+}
+
+static void show_chicken_stat(chicken_t *thechicken) {
+    struct tm *ptime;
+    
+    ptime = localtime(&thechicken->birthday);
+    prints(" Name :\033[33m%s\033[m (\033[32m%s\033[m)%*s�ͤ�  "
+	   ":\033[31m%02d\033[m�~\033[31m%2d\033[m��\033[31m%2d\033[m�� "
+	   "(\033[32m%s %d��\033[m)\n"
+	   " ��:\033[33m%5d/%-5d\033[m �k:\033[33m%5d/%-5d\033[m �����O:"
+	   "\033[33m%-7d\033[m �ӱ�  :\033[33m%-7d\033[m ���� :\033[33m%-7d"
+	   "\033[m \n"
+	   " �ּ� :\033[33m%-7d\033[m ���N :\033[33m%-7d\033[m �h��  :"
+	   "\033[33m%-7d\033[m ���  :\033[33m%-7d \033[m�魫 :"
+	   "\033[33m%-5.2f\033[m \n"
+	   " �f�� :\033[33m%-7d\033[m ���b :\033[33m%-7d\033[m ����  :"
+	   "\033[33m%-7d\033[m �j�ɤY:\033[33m%-7d\033[m �ī~ :\033[33m%-7d"
+	   "\033[m \n",
+	   thechicken->name,  chicken_type[(int)thechicken->type],
+	   15 - strlen(thechicken->name), "",
+	   ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday,
+	   cage[age > 16 ? 16 : age], age, thechicken->hp, thechicken->hp_max, 
+	   thechicken->mm, thechicken->mm_max,
+	   thechicken->attack, thechicken->run, thechicken->book,
+	   thechicken->happy, thechicken->satis, thechicken->tiredstrong,
+	   thechicken->temperament,
+	   ((float)(thechicken->hp_max+(thechicken->weight/50))) / 100,
+	   thechicken->sick, thechicken->clean, thechicken->food,
+	   thechicken->oo, thechicken->medicine);
+}
+
+#define CHICKEN_PIC "etc/chickens"
+extern char *BBSName;
+
+void show_chicken_data(chicken_t *thechicken, chicken_t *pkchicken) {
+    char buf[1024];
+    age = ((time(NULL) - thechicken->cbirth)/ (60*60*24));
+    if(age < 0) {
+        thechicken->birthday = thechicken->cbirth = time(NULL)-10*(60*60*24);
+        age = 10;
+    }
+    /*Ptt:debug*/
+    thechicken->type %= NUM_KINDS;
+    clear();
+    showtitle(pkchicken ? "��tt������" : "��tt�i����", BBSName);
+    move(1,0);
+    
+    show_chicken_stat(thechicken);
+    
+    sprintf(buf, CHICKEN_PIC "/%c%d", thechicken->type + 'a',
+	    age > 16 ? 16 : age);
+    show_file(buf, 5, 14, NO_RELOAD);
+    
+    move(18,0);
+    
+    if(thechicken->sick)
+	outs("�ͯf�F...");
+    if(thechicken->sick > thechicken->hp / 5)
+	outs("\033[5;31m���...�f��!!\033[m");
+
+    if(thechicken->clean > 150)
+	outs("\033[31m�S��Sż��..\033[m");
+    else if(thechicken->clean > 80)
+	outs("���Iż..");
+    else if(thechicken->clean < 20)
+	outs("\033[32m�ܰ��b..\033[m");
+
+    if(thechicken->weight > thechicken->hp_max*4)
+	outs("\033[31m�ֹ����F!.\033[m");
+    else if(thechicken->weight > thechicken->hp_max*3)
+	outs("\033[32m���ʹ�..\033[m");
+    else if(thechicken->weight < (thechicken->hp_max / 4))
+	outs("\033[31m�־j���F!..\033[m");
+    else if(thechicken->weight < (thechicken->hp_max / 2))
+	outs("�j�F..");
+
+    if(thechicken->tiredstrong > thechicken->hp * 1.7)
+	outs("\033[31m�ֱo���g�F...\033[m");
+    else if(thechicken->tiredstrong > thechicken->hp)
+	outs("�֤F..");
+    else if(thechicken->tiredstrong < thechicken->hp / 4)
+	outs("\033[32m��O����...\033[m");
+
+    if(thechicken->hp < thechicken->hp_max / 4)
+	outs("\033[31m��O�κ�..�a�a�@��..\033[m");
+    if(thechicken->happy > 500)
+	outs("\033[32m�ܧּ�..\033[m");
+    else if(thechicken->happy < 100)
+	outs("���ּ�..");
+    if(thechicken->satis > 500)
+	outs("\033[32m�ܺ���..\033[m");
+    else if(thechicken->satis < 50)
+	outs("������..");
+
+    if(pkchicken) {
+	outs("\n");
+	show_chicken_stat(pkchicken);
+	outs("[���N��] ������� [q] ���] [o] �Y�j�ɤY");
+    }
+}
+
+static void ch_eat() {
+    if(mychicken->food) {
+	mychicken->weight += time_change[(int)mychicken->type][WEIGHT] +
+	    mychicken->hp_max/5 ;
+	mychicken->tiredstrong +=
+	    time_change[(int)mychicken->type][TIREDSTRONG] / 2;
+	mychicken->hp_max++;
+	mychicken->happy += 5;
+	mychicken->satis += 7;
+	mychicken->food--;
+	move(10, 10);
+	
+	show_file(CHICKEN_PIC "/eat", 5, 14, NO_RELOAD);
+	pressanykey();
+    }
+}
+
+static void ch_clean() {
+    mychicken->clean = 0;
+    mychicken->tiredstrong +=
+	time_change[(int)mychicken->type][TIREDSTRONG] / 3;
+    show_file(CHICKEN_PIC "/clean", 5, 14, NO_RELOAD);
+    pressanykey();
+}
+
+static void ch_guess() {
+    char *guess[3] = {"�ŤM", "���Y", "��"}, me, ch, win;
+    
+    mychicken->happy += time_change[(int)mychicken->type][HAPPY]*1.5;
+    mychicken->satis += time_change[(int)mychicken->type][SATIS];
+    mychicken->tiredstrong += time_change[(int)mychicken->type][TIREDSTRONG];
+    mychicken->attack += time_change[(int)mychicken->type][ATTACK]/4;
+    move(20,0);
+    clrtobot();
+    outs("�A�n�X[\033[32m1\033[m]\033[33m�ŤM\033[m(\033[32m2\033[m)"
+	 "\033[33m���Y\033[m(\033[32m3\033[m)\033[33m��\033[m:\n");
+    me = igetch();
+    me -= '1';
+    if(me > 2 || me < 0)
+	me = 0;
+    win = (int)(3.0 * rand()/(RAND_MAX + 1.0)) - 1;
+    ch  = (me + win + 3)%3;
+    prints("%s:%s !      %s:%s !.....%s",
+	   cuser.userid, guess[(int)me], mychicken->name, guess[(int)ch],
+	   win==0 ? "����" : win < 0 ? "�C..Ĺ�F :D!!" : "��..�ڿ�F :~");
+    pressanykey();
+}
+
+static void ch_book() {
+    mychicken->book += time_change[(int)mychicken->type][BOOK];
+    mychicken->tiredstrong += time_change[(int)mychicken->type][TIREDSTRONG];
+    show_file(CHICKEN_PIC "/read", 5, 14, NO_RELOAD);
+    pressanykey();
+}
+
+static void ch_kiss() {
+    mychicken->happy += time_change[(int)mychicken->type][HAPPY];
+    mychicken->satis += time_change[(int)mychicken->type][SATIS];
+    mychicken->tiredstrong +=
+	time_change[(int)mychicken->type][TIREDSTRONG] / 2;
+    show_file(CHICKEN_PIC "/kiss", 5, 14, NO_RELOAD);
+    pressanykey();
+}
+
+static void ch_hit() {
+    mychicken->attack += time_change[(int)mychicken->type][ATTACK];
+    mychicken->run += time_change[(int)mychicken->type][RUN];
+    mychicken->mm_max += time_change[(int)mychicken->type][MM_MAX]/15;
+    mychicken->weight -= mychicken->hp_max / 15 ;
+    mychicken->hp -= (int)((float)time_change[(int)mychicken->type][HP_MAX] *
+			   rand()/(RAND_MAX+1.0)) / 2 + 1;
+    
+    if(mychicken->book > 2)
+	mychicken->book -= 2;
+    if(mychicken->happy > 2)
+	mychicken->happy -= 2;
+    if(mychicken->satis > 2)
+	mychicken->satis -= 2;
+    mychicken->tiredstrong += time_change[(int)mychicken->type][TIREDSTRONG];
+    show_file(CHICKEN_PIC "/hit", 5, 14, NO_RELOAD);
+    pressanykey();
+}
+
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+
+void ch_buyitem(int money, char *picture, int *item) {
+    int num = 0;
+    char buf[5];
+    
+    getdata_str(b_lines - 1, 0, "�n�R�h�֥��O:", buf, 4, DOECHO, "1");
+    num = atoi(buf);
+    if(num < 1)
+	return;
+    reload_money();
+    if(cuser.money > money*num) {
+	*item += num;
+	vice(money*num,"�ʶR�d��,��L����");
+	show_file(picture, 5, 14, NO_RELOAD);
+    } else {
+	move(b_lines-1,0);
+	clrtoeol();
+	outs("�{������ !!!");
+    }
+    pressanykey();
+}
+
+static void ch_eatoo() {
+    if(mychicken->oo > 0) {
+	mychicken->oo--;
+	mychicken->tiredstrong = 0;
+	if(mychicken->happy > 5)
+	    mychicken->happy -= 5;
+	show_file(CHICKEN_PIC "/oo", 5, 14, NO_RELOAD);
+	pressanykey();
+    }
+}
+
+static void ch_eatmedicine() {
+    if(mychicken->medicine > 0) {
+	mychicken->medicine--;
+	mychicken->sick = 0;
+	if(mychicken->hp_max > 10)
+	    mychicken->hp_max -= 3;
+	mychicken->hp = mychicken->hp_max;
+	if(mychicken->happy>10)
+	    mychicken->happy -= 10;
+	show_file(CHICKEN_PIC "/medicine", 5, 14, NO_RELOAD);
+	pressanykey();
+    }
+}
+
+static void ch_kill() {
+    char buf[150],ans[4];
+    
+    sprintf(buf, "��i�o%s�n�Q�@ 100 ��, �O�_�n��i?(y/N)",
+	    chicken_type[(int)mychicken->type]);
+    getdata_str(23, 0, buf, ans, 3, DOECHO, "N");
+    if(ans[0] == 'y') {
+	time_t now = time(NULL);
+	
+	vice(100,"��i�d���O");
+	more(CHICKEN_PIC "/deadth",YEA);
+	sprintf(buf, "\033[31m%s \033[m�� \033[33m%s\033[m\033[32m %s "
+		"\033[m�_�F �� %s", cuser.userid,
+		mychicken->name, chicken_type[(int)mychicken->type], ctime(&now));
+	log_file(CHICKENLOG, buf);
+	mychicken->name[0]=0;
+    }
+}
+
+static int ch_sell() {
+/*
+  int money = (mychicken->weight - time_change[(int)mychicken->type][WEIGHT])
+  *(food_price[(int)mychicken->type])/4 + 
+  (
+  + ((mychicken->clean / time_change[(int)mychicken->type][CLEAN])
+  + (mychicken->run / time_change[(int)mychicken->type][RUN])
+  + (mychicken->attack / time_change[(int)mychicken->type][ATTACK])
+  + (mychicken->book / time_change[(int)mychicken->type][BOOK])
+  + (mychicken->happy / time_change[(int)mychicken->type][HAPPY])
+  + (mychicken->satis / time_change[(int)mychicken->type][SATIS])
+  + (mychicken->temperament / time_change[(int)mychicken->type][TEMPERAMENT])
+  - (mychicken->tiredstrong / time_change[(int)mychicken->type][TIREDSTRONG])
+  - (mychicken->sick / time_change[(int)mychicken->type][SICK])
+  + (mychicken->hp / time_change[(int)mychicken->type][HP_MAX])
+  + (mychicken->mm / time_change[(int)mychicken->type][MM_MAX])
+  + 7 - abs(age - 7)) * 3
+  ;
+*/
+    int money = (age * food_price[(int)mychicken->type] * 3       
+		 + (mychicken->hp_max * 10 + mychicken->weight) /
+		 time_change[(int)mychicken->type][HP_MAX]) * 3 / 2 - 
+	mychicken->sick;                                         
+    char buf[150],ans[4];
+    time_t now = time(NULL);
+    
+    if(money < 0)
+	money =0 ;
+    else if(money > MAX_CHICKEN_MONEY)
+	money = MAX_CHICKEN_MONEY; //�������
+    if(mychicken->type == 1 || mychicken->type == 7) {
+	outs("\n\033[31m ��..�˷R��..�c��H�f�O�|�Ǫk����..\033[m");
+	pressanykey();
+	return 0;
+    }
+    if(age < 5) {
+	outs("\n �٥����~�����");
+	pressanykey();
+	return 0;
+    }
+    if(age > 30) {
+	outs("\n\033[31m �o..�ӦѨS�H�n�F\033[m");
+	pressanykey();
+	return 0;
+    }
+    
+    sprintf(buf, "�o��%d��%s�i�H�� %d ��, �O�_�n��?(y/N)", age,
+	    chicken_type[(int)mychicken->type], money);
+    getdata_str(23, 0, buf, ans, 3, DOECHO, "N");
+    if(ans[0]=='y') {
+	sprintf(buf, "\033[31m%s\033[m �� \033[33m%s\033[m "
+		"\033[32m%s\033[m �� \033[36m%d\033[m ��F �� %s",
+		cuser.userid, mychicken->name,
+		chicken_type[(int)mychicken->type],money,ctime(&now));
+	log_file(CHICKENLOG, buf);
+	mychicken->lastvisit = mychicken->name[0]=0;
+	passwd_update(usernum, &cuser);
+	more(CHICKEN_PIC "/sell",YEA);
+	demoney(money);
+	return 1;
+    }
+    return 0;
+}
+
+static void geting_old(int *hp, int *weight, int diff, int age) {
+    float ex = 0.9;
+    
+    if(age > 70)
+	ex = 0.1;
+    else if(age > 30)
+	ex = 0.5;
+    else if(age > 20)
+	ex = 0.7;
+
+    diff /= 60*6;
+    while(diff--) {
+	*hp *= ex;
+	*weight *= ex;
+    }
+}
+
+/* �̮ɶ��ܰʪ���� */
+void time_diff(chicken_t *thechicken) {
+    int diff;
+    int theage = ((time(NULL) - thechicken->cbirth)/ (60 * 60 * 24));
+    
+    thechicken->type %=  NUM_KINDS ;
+    diff = (time(NULL)-thechicken->lastvisit)/60;
+
+    if((diff) < 1)
+	return;
+
+    if(theage > 13 ) /* �Ѧ� */
+	geting_old(&thechicken->hp_max, &thechicken->weight, diff, age);
+    
+    thechicken->lastvisit = time(NULL);
+    thechicken->weight -= thechicken->hp_max * diff / 540;          /* �魫 */
+    if(thechicken->weight < 1) {
+	thechicken->sick -= thechicken->weight / 10; /* �j�o�f��W�� */
+	thechicken->weight =1;
+    }
+    
+    /* �M��� */    
+    thechicken->clean += diff * time_change[(int)thechicken->type][CLEAN] / 30;
+    
+    /* �ּ֫� */
+    thechicken->happy -= diff / 60;
+    if(thechicken->happy < 0)
+	thechicken->happy=0;
+    thechicken->attack -=
+	time_change[(int)thechicken->type][ATTACK] * diff / (60 * 32);
+    if(thechicken->attack < 0)
+	thechicken->attack = 0;
+    /* �����O */
+    thechicken->run -= time_change[(int)thechicken->type][RUN] * diff / (60 * 32);
+    /* �ӱ� */
+    if(thechicken->run < 0)
+	thechicken->run = 0;
+    thechicken->book -= time_change[(int)thechicken->type][BOOK]*diff/ (60*32);
+    /* ���� */
+    if(thechicken->book < 0)
+	thechicken->book = 0;
+    /* ��� */
+    thechicken->temperament++;
+    
+    thechicken->satis -= diff / 60 / 3 * time_change[(int)thechicken->type][SATIS];
+    /* ���N�� */
+    if(thechicken->satis < 0)
+	thechicken->satis = 0;
+
+    /* ż�f�� */
+    if(mychicken->clean > 1000)
+        mychicken->sick += (mychicken->clean - 400) / 10;
+
+    if(thechicken->weight > 1)
+	thechicken->sick -= diff / 60;
+    /* �f����@ */
+    if(thechicken->sick < 0)
+	thechicken->sick = 0;
+    thechicken->tiredstrong -= diff *
+	time_change[(int)thechicken->type][TIREDSTRONG] / 4;
+    /* �h�� */
+    if(thechicken->tiredstrong < 0)
+	thechicken->tiredstrong = 0;
+    /* hp_max */
+    if(thechicken->hp >= thechicken->hp_max/2)
+	thechicken->hp_max +=
+	    time_change[(int)thechicken->type][HP_MAX]*diff/ (60*12);
+    /* hp���@ */
+    if(!thechicken->sick)
+	thechicken->hp +=
+	    time_change[(int)thechicken->type][HP_MAX]*diff/ (60*6);
+    if(thechicken->hp>thechicken->hp_max)
+	thechicken->hp = thechicken->hp_max;
+    /* mm_max */
+    if(thechicken->mm >= thechicken->mm_max/2)
+	thechicken->mm_max +=
+	    time_change[(int)thechicken->type][MM_MAX]*diff/ (60*8);
+    /* mm���@ */
+    if(!thechicken->sick)
+	thechicken->mm += diff;
+    if(thechicken->mm>thechicken->mm_max)
+	thechicken->mm = thechicken->mm_max;
+}
+
+static void check_sick() {
+    /* ż�f�� */
+    if(mychicken->tiredstrong > mychicken->hp * 0.3 && mychicken->clean > 150)
+        mychicken->sick += (mychicken->clean - 150) / 10;
+    /* �֯f�� */
+    if(mychicken->tiredstrong > mychicken->hp*1.3)
+        mychicken->sick += time_change[(int)mychicken->type][SICK];
+    /* �f��ӭ��ٰ��ƴ�hp */
+    if(mychicken->sick > mychicken->hp / 5) {
+        mychicken->hp -= (mychicken->sick - mychicken->hp / 5)/4;
+        if(mychicken->hp < 0 )
+	    mychicken->hp = 0;
+    }
+}
+
+static int deadtype(chicken_t *thechicken) {
+    int i;
+    char buf[150];
+    time_t now = time(NULL);
+    
+    if(thechicken->hp <= 0)      /* hp�κ� */    
+	i = 1;
+    else if(thechicken->tiredstrong > thechicken->hp * 3 )  /* �޳ҹL�� */ 
+	i = 2;
+    else if(thechicken->weight > thechicken->hp_max*5)      /* �έD�L�� */
+	i = 3;
+    else if(thechicken->weight == 1 &&
+	    thechicken->sick > thechicken->hp_max / 4)
+	i = 4;			  	  	  /* �j���F */  
+    else if(thechicken->satis <= 0)		/* �ܤ����N */
+	i = 5;
+    else
+	return 0;
+
+    if(thechicken == mychicken) {
+	sprintf(buf,"\033[31m%s\033[m �үk�R��\033[33m %s\033[32m %s "
+		"\033[m���F �� %s",
+		cuser.userid, thechicken->name,
+		chicken_type[(int)thechicken->type],
+		ctime(&now));
+	log_file(CHICKENLOG, buf);
+	mychicken->name[0] = 0;
+	passwd_update(usernum, &cuser);
+    }           
+    return i;
+}
+
+int showdeadth(int type) {
+    switch(type) {
+    case 1:
+	more(CHICKEN_PIC "/nohp",YEA);
+	break;
+    case 2:
+	more(CHICKEN_PIC "/tootired",YEA);
+	break;
+    case 3:
+	more(CHICKEN_PIC "/toofat",YEA);
+	break;
+    case 4:
+	more(CHICKEN_PIC "/nofood",YEA);
+	break;
+    case 5:
+	more(CHICKEN_PIC "/nosatis", YEA);
+	break;
+    default:
+	return 0;
+    }
+    more(CHICKEN_PIC "/deadth",YEA);
+    return type;
+}
+
+int isdeadth(chicken_t *thechicken) {
+    int i;
+    
+    if(!(i = deadtype(thechicken)))
+	return 0;
+    return showdeadth(i);
+}
+
+static void ch_changename() {
+    char buf[150], newname[20] = "";
+    time_t now = time(NULL);
+    
+    getdata_str(b_lines - 1, 0, "��..��Ӧn�W�r�a:", newname, 18, DOECHO,
+                mychicken->name);
+    
+    if(strlen(newname) >= 3 && strcmp(newname,mychicken->name)) {
+	sprintf(buf, "\033[31m%s\033[m ��k�R��\033[33m %s\033[32m %s "
+		"\033[m��W��\033[33m %s\033[m �� %s",
+		cuser.userid, mychicken->name,
+		chicken_type[(int)mychicken->type],
+		newname, ctime(&now));
+	strcpy(mychicken->name, newname);
+	log_file(CHICKENLOG,buf);
+    }
+}
+
+static int select_menu() {
+    char ch;
+    
+    reload_money();
+    move(19,0);
+    prints("\033[44;37m �� :\033[33m %-10d                                  "
+	   "                       \033[m\n"
+	   "\033[33m(\033[37m1\033[33m)�M�z (\033[37m2\033[33m)�Y�� "
+	   "(\033[37m3\033[33m)�q�� (\033[37m4\033[33m)��� "
+	   "(\033[37m5\033[33m)�˥L (\033[37m6\033[33m)���L "
+	   "(\033[37m7\033[33m)�R%s$%d (\033[37m8\033[33m)�Y�ɤY\n"
+	   "(\033[37m9\033[33m)�Y�f�� (\033[37mo\033[33m)�R�j�ɤY$100 "
+	   "(\033[37mm\033[33m)�R��$10 (\033[37mk\033[33m)��i "
+	   "(\033[37ms\033[33m)�汼 (\033[37mn\033[33m)��W "
+	   "(\033[37mq\033[33m)���}:\033[m",
+	   cuser.money,
+	   /*chicken_food[(int)mychicken->type],
+	     chicken_type[(int)mychicken->type],
+	     chicken_type[(int)mychicken->type],*/
+	   chicken_food[(int)mychicken->type],
+	   food_price[(int)mychicken->type]);
+    do {
+	switch(ch = igetch()) {
+	case '1':
+	    ch_clean();
+	    check_sick();
+	    break;
+	case '2':
+	    ch_eat();
+	    check_sick();
+	    break;
+	case '3':
+	    ch_guess();
+	    check_sick();
+	    break;
+	case '4':
+	    ch_book();
+	    check_sick();
+	    break;
+	case '5':
+	    ch_kiss();
+	    break;
+	case '6':
+	    ch_hit();
+	    check_sick();
+	    break;
+	case '7':
+	    ch_buyitem(food_price[(int)mychicken->type], CHICKEN_PIC "/food",
+		       &mychicken->food);
+	    break;
+	case '8':
+	    ch_eatoo();
+	    break;
+	case '9':
+	    ch_eatmedicine();
+	    break;
+	case 'O':
+	case 'o':
+	    ch_buyitem(100, CHICKEN_PIC "/buyoo", &mychicken->oo);
+	    break;
+	case 'M':
+	case 'm':
+	    ch_buyitem(10, CHICKEN_PIC "/buymedicine", &mychicken->medicine);
+	    break;
+	case 'N':
+	case 'n':
+	    ch_changename();
+	    break;
+	case 'K':
+	case 'k':
+	    ch_kill();
+	    return 0;
+	case 'S':
+	case 's':
+	    if(!ch_sell()) break;
+	case 'Q':
+	case 'q':
+	    return 0;
+        }
+    } while(ch < ' ' || ch>'z');
+    return 1;
+}
+
+static int recover_chicken(chicken_t *thechicken) {
+    char buf[200];
+    int price = egg_price[(int)thechicken->type],
+	money = price + (rand() % price);
+    
+    if(time(NULL) - thechicken->lastvisit > (60 * 60 * 24 * 7))
+	return 0;
+    outmsg("\033[33;44m���F�ɦu��\033[37;45m �O�`�� �ڬO�����A�� \033[m");
+    bell();
+    igetch();
+    outmsg("\033[33;44m���F�ɦu��\033[37;45m �A�L�k���ڤ��y �]���ڬO�t�F, "
+	   "�̪�ʿ��Q�ȥ~�� \033[m");
+    bell();
+    igetch();
+    sprintf(buf, "\033[33;44m���F�ɦu��\033[37;45m "
+	    "�A���@�ӭ訫���[��%s�n�۴��^�Ӷ�? �u�n%d���� \033[m", 
+	    chicken_type[(int)thechicken->type], price*2);
+    outmsg(buf);
+    bell();
+    getdata_str(21, 0, "    ��ܡG(N:�|�H��/y:��������)", buf, 3, LCECHO, "N");
+    if(buf[0] == 'y' || buf[0] == 'Y') {
+        reload_money();
+        if(cuser.money < price*2) {
+	    outmsg("\033[33;44m���F�ɦu��\033[37;45m ���� ���S�a�� "
+		   "�S�����p�� �֥h�w���a \033[m");
+	    bell();
+	    igetch();
+	    return 0;
+        }
+        strcpy(thechicken->name, "[�ߦ^�Ӫ�]");
+        thechicken->hp = thechicken->hp_max;
+        thechicken->sick = 0;
+        thechicken->satis = 2;
+        vice(money,"�F�ɦu��");
+        sprintf(buf, "\033[33;44m���F�ɦu��\033[37;45m OK�F �O�o���L�I�F�� "
+		"���M�i�ॢ�� ���b�ڤ]����Ptt ���A%d�N�n \033[m", money);
+        outmsg(buf);
+        bell();
+        igetch();
+        return 1;
+    }
+    outmsg("\033[33;44m���F�ɦu��\033[37;45m ���M���ڧ|�H! �o�~�Y�R�u���ȿ� "
+	   "���D�ڦA�ӧ�A �A�A�]�S���|�F \033[m");
+    bell();
+    igetch();
+    thechicken->lastvisit = 0;
+    passwd_update(usernum, &cuser);
+    return 0;
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+
+int chicken_main() {
+    lockreturn0(CHICKEN, LOCK_MULTI);
+    
+    reload_chicken();
+    age = ((time(NULL) - mychicken->cbirth)/ (60*60*24));
+    if(!mychicken->name[0] && !recover_chicken(mychicken) && !new_chicken()) {
+	unlockutmpmode();
+	return 0;
+    }
+    
+    do {
+	time_diff(mychicken);
+	if(isdeadth(mychicken))
+	    break;
+	show_chicken_data(mychicken, NULL);
+    } while(select_menu());
+    reload_money();
+    passwd_update(usernum, &cuser);
+    unlockutmpmode();
+    return 0;
+}
+
+extern userinfo_t *currutmp;
+extern struct utmpfile_t *utmpshm;
+
+int chickenpk(int fd) {
+    char mateid[IDLEN + 1], data[200], buf[200];
+    int ch = 0;
+
+    userinfo_t *uin = &utmpshm->uinfo[currutmp->destuip];
+    userec_t ouser;
+    chicken_t *ochicken = &ouser.mychicken;
+    int r, attmax, i, datac, duid = currutmp->destuid, catched=0, count=0;
+
+    lockreturn0(CHICKEN, LOCK_MULTI);
+    
+    strcpy(mateid, currutmp->mateid); /*���⪺id��local buffer�O��*/
+    
+    getuser(mateid) ;
+    memcpy(&ouser, &xuser, sizeof(userec_t));
+    reload_chicken();
+    if(!ochicken->name[0] || !mychicken->name[0]) {
+	outmsg("���@��S���d��");  /* Ptt:����page�ɧ��d���汼 */
+	bell();
+	refresh();
+	add_io(0, 0);
+	close(fd);
+	unlockutmpmode();
+	sleep(1);
+	return 0;
+    }
+    
+    show_chicken_data(ochicken, mychicken);
+    add_io(fd, 3);     /* ��fd�[��igetch�ʵ� */
+    while(1) {
+	r = rand();
+	ch = igetkey();
+	getuser(mateid) ;
+	memcpy(&ouser, &xuser, sizeof(userec_t));
+	reload_chicken();
+	show_chicken_data(ochicken, mychicken);    
+	time_diff(mychicken);
+	
+	i = mychicken->attack* mychicken->hp / mychicken->hp_max;
+	for(attmax=2; (i = i*9/10); attmax++);
+
+	if(ch == I_OTHERDATA) {
+	    count =0;
+	    datac = recv(fd, data, sizeof(data), 0);
+	    if(datac <= 1)
+		break;
+	    move(17,0);
+	    outs(data+1);
+	    switch(data[0]) {
+	    case 'c':
+		catched=1;
+		move(16,0);
+		outs("�n��L����?(y/N)");
+		break;
+	    case 'd':
+		move(16,0);
+		outs("��~�ˤU�F!!");
+		break;
+	    }
+	    if(data[0] == 'd' || data[0]=='q' || data[0]=='l')
+		break;
+	    continue;
+	} else if(currutmp->turn) {
+	    count = 0;
+	    currutmp->turn = 0;
+	    uin->turn = 1;
+	    mychicken->tiredstrong ++;
+	    switch(ch) {
+	    case 'y':
+                if(catched == 1) {
+		    sprintf(data, "l�� %s ���]�F\n",
+			    ochicken->name);
+		}
+                break;
+	    case 'n':
+                catched =0;
+	    default:
+	    case 'k':
+                r = r % (attmax + 2);
+                if(r) {
+		    sprintf(data, "M%s %s%s %s �ˤF %d �I\n", mychicken->name,
+			    damage_degree[r/3>15 ? 15:r/3],
+			    attack_type[(int)mychicken->type],
+			    ochicken->name, r);
+		    ochicken->hp-=r;
+		} else
+		    sprintf(data, "M%s ı�o��n�X���L��\n", mychicken->name);
+                break;
+	    case 'o':
+                if(mychicken->oo > 0) {
+		    mychicken->oo--;
+		    mychicken->hp += 300;
+		    if(mychicken->hp > mychicken->hp_max)
+                        mychicken->hp = mychicken->hp_max;
+		    mychicken->tiredstrong = 0;
+		    sprintf(data, "M%s �Y�F���j�ɤY�ɥR��O\n",
+			    mychicken->name);
+                } else
+		    sprintf(data, "M%s �Q�Y�j�ɤY, �i�O�S���j�ɤY�i�Y\n",
+			    mychicken->name);
+                break;
+	    case 'q':
+                if(r % (mychicken->run+1) > r % (ochicken->run+1))
+		    sprintf(data, "q%s ���]�F\n",
+			    mychicken->name);
+                else
+		    sprintf(data, "c%s �Q���], ���Q %s ���F\n",
+			    mychicken->name, ochicken->name);
+                break;
+	    }
+	    if(deadtype(ochicken)) {
+		strtok(data,"\n");
+		strcpy(buf, data);
+                sprintf(data, "d%s , %s �Q %s �����F\n",
+                        buf + 1, ochicken->name, mychicken->name);
+	    }
+	    move(17,0);
+	    outs(data+1);
+	    i = strlen(data) +1;
+	    passwd_update(duid, &ouser);
+	    passwd_update(usernum, &cuser);
+	    send(fd, data, i, 0); 
+	    if(data[0]=='q' || data[0]=='d')
+		break;
+	} else {
+	    move(17, 0);
+	    if(count++ > 30)
+		break;
+	}
+    }
+    add_io(0, 0);      /* ��igetch���@�^ */
+    pressanykey();
+    close(fd);
+    if(!showdeadth(deadtype(mychicken)));
+    unlockutmpmode();
+    return 0;
+}
diff --git a/mbbsd/dark.c b/mbbsd/dark.c
new file mode 100644
index 00000000..52741617
--- /dev/null
+++ b/mbbsd/dark.c
@@ -0,0 +1,456 @@
+/* $Id: dark.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+#define RED   1
+#define BLACK 0
+typedef short int sint;
+
+typedef struct item {
+    short int color,value,die,out;
+} item;
+
+typedef struct cur{
+    short int y,x,end;
+} cur;
+
+static item brd[4][8];                 
+static cur  curr;                            /*   6 �� bytes */
+extern userinfo_t *currutmp; 
+
+static char *rname[]={"�L","��","�X","��","��","�K","��"};
+static char *bname[]={"��","�]","��","��","�H","�h","�N"};
+
+static sint cury[]={3,5,7,9}, curx[]={5,9,13,17,21,25,29,33};
+static sint rcount,bcount,cont,fix;              /* cont:�O�_�i�s�Y */
+static sint my=0,mx=0,mly=-1,mlx=-1;             /* ���ʪ��y�� �� ��l���y�� */
+
+static sint cur_eaty,cur_eatx;			 /* �Y������l���q�X�y�� */
+static void brdswap(sint y,sint x,sint ly,sint lx) {
+    memcpy(&brd[y][x],&brd[ly][lx],sizeof(item));
+    brd[ly][lx].die=1;
+    brd[ly][lx].color=-1;                  /* �S�o��color */
+    brd[ly][lx].value=-1;
+}
+
+static void pprints(sint y,sint x,char* s) {
+    move(y,x);
+    clrtoeol();
+    prints("%s",s);
+}
+
+static sint Is_win(item att, item det, sint y, sint x, sint ly, sint lx) {
+    sint i,c=0,min,max;
+    if(att.value == 1)   /* �� */
+    {
+	if(y!=ly && x!=lx) return 0;
+	if((abs(ly-y)==1 && brd[y][x].die==0)||
+	   (abs(lx-x)==1 && brd[y][x].die==0))
+	    return 0;
+	if(y==ly){
+	    if(x>lx) {max=x;min=lx;}
+	    else {max=lx;min=x;}
+	    for(i=min+1;i<max;i++)
+		if(brd[y][i].die==0) c++;
+	}else if(x==lx){
+	    if(y>ly) {max=y;min=ly;}
+	    else {max=ly;min=y;}
+	    for(i=min+1;i<max;i++)
+		if(brd[i][x].die==0) c++;
+	}
+	if(c != 1) return 0;
+	if(det.die == 1) return 0;
+	return 1;
+    }
+    /* �D�� */
+    if( ((abs(ly-y)==1&&x==lx) || (abs(lx-x)==1&&ly==y)) && brd[y][x].out==1 )
+    {
+	if(att.value == 0 && det.value == 6) return 1;
+	else if(att.value == 6 && det.value == 0) return 0;
+	else if(att.value >= det.value) return 1;
+	else return 0;
+    }
+    return 0;
+}
+
+static sint Is_move(sint y,sint x, sint ly, sint lx) {
+    if(brd[y][x].die==1 && ((abs(ly-y)==1&&x==lx) || (abs(lx-x)==1&&ly==y)))
+	return 1;
+    return 0;
+}
+
+static void brd_rand() {
+    sint y,x,index;
+    sint tem[32];
+    sint value[32]={0,0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,
+		    0,0,0,0,0,1,1,2,2,3,3,4,4,5,5,6};
+
+    bzero(brd, sizeof(brd));
+    bzero(tem, sizeof(tem));
+    bzero(&curr, sizeof(curr));
+    srand(getpid()%2731+time(NULL)%3219);
+    for(y=0;y<4;y++)
+	for(x=0;x<8;x++)
+	    while(1) {
+		index=rand()%32;
+		if(tem[index]) continue;
+		brd[y][x].color=(index>15)?0:1;
+		brd[y][x].value=value[index];
+		tem[index]=1;
+		break;
+	    }
+}
+
+static void brd_prints() {
+    clear();
+    move(1,0);
+    outs("
+   �~�w�s�w�s�w�s�w�s�w�s�w�s�w�s�w��
+   �x���x���x���x���x���x���x���x���x
+   �u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t
+   �x���x���x���x���x���x���x���x���x
+   �u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t
+   �x���x���x���x���x���x���x���x���x
+   �u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t
+   �x���x���x���x���x���x���x���x���x
+   ���w�r�w�r�w�r�w�r�w�r�w�r�w�r�w��
+   ");
+}
+
+static void draw_line(sint y, sint f) {
+    sint i;
+    char  buf[1024],tmp[256];
+
+    *buf = 0;
+    *tmp = 0;
+    strcpy(buf,"\033[43;30m");
+    for(i=0; i<8; i++)
+    {
+	if(brd[y][i].die==1)
+	    sprintf(tmp,"�x  ");
+	else if(brd[y][i].out==0)
+	    sprintf(tmp,"�x��");
+	else {
+	    sprintf(tmp, "�x\033[%s1;%dm%s\033[m\033[43;30m",
+		    (f==i)?"1;47;":"",(brd[y][i].color)?31:34,
+		    (brd[y][i].color)?rname[brd[y][i].value]:
+		    bname[brd[y][i].value]);
+	}
+	strcat(buf,tmp);
+    }
+    strcat(buf,"�x\033[m");
+
+    move(cury[y],3);
+    clrtoeol();
+    prints("%s",buf);
+}
+
+static void redraw() {
+    sint i=0;
+    for(;i<4;i++)
+	draw_line(i,-1);
+}
+
+static sint playing(sint fd, sint color,sint ch,sint *b, userinfo_t *uin) {
+    curr.end = 0;
+    move(cury[my],curx[mx]);
+
+    if(fix) {
+	if(ch=='s') {
+	    fix=0; *b=0; return 0;
+	} else {
+	    draw_line(mly,-1);
+	}
+    }
+
+    switch(ch) {
+    case KEY_LEFT:
+	if(mx == 0) mx=7;
+	else mx--;
+	move(cury[my],curx[mx]);
+	*b=-1;
+	break;
+    case KEY_RIGHT:
+	if(mx==7) mx=0;
+	else mx++;
+	move(cury[my],curx[mx]);
+	*b=-1;
+	break;
+    case KEY_UP:
+	if(my==0) my=3;
+	else my--;
+	move(cury[my],curx[mx]);
+	*b=-1;
+	break;
+    case KEY_DOWN:
+	if(my==3) my=0;
+	else my++;
+	move(cury[my],curx[mx]);
+	*b=-1;
+	break;
+    case 'q':case 'Q':
+	if(!color) bcount=0;
+	else rcount=0;
+	*b=0;
+	return -2;
+    case 'p':case 'P':
+	return -3;
+    case 'c':
+	return -4;
+    case 'g':
+	return -5;
+    case 's':                     /* ½�}�Ѥl �άO��ܴѤl */
+	/* ��ܴѤl */
+	if(brd[my][mx].out==1)
+	{
+	    if(brd[my][mx].color != color)
+	    {
+		*b=-1;
+		break;
+	    }
+	    if(mly<0)  /*�i�H���*/
+	    {
+		mly=my;mlx=mx;
+		draw_line(my,mx);
+		*b=-1;
+		break;
+	    }
+	    else if(mly == my && mlx == mx) /*����F*/
+	    {
+		mly=-1;mlx=-1;
+		draw_line(my,-1);
+	    }else
+	    {
+		draw_line(mly,-1);
+		mly=my;mlx=mx;
+		if(brd[mly][mlx].value == 1) fix=1;
+		draw_line(my,mx);
+	    }
+	    *b=-1;
+	    break;
+	}
+	/* ½�}�Ѥl */
+	if(mly >=0 ){ *b=-1; break;}   /*���ӴN�O½�}��*/
+	/* �M�w�@�}�l���C�� */
+	if(currutmp->color=='.'){
+	    if(uin->color!='1' && uin->color!='0')
+		currutmp->color=(brd[my][mx].color)?'1':'0';
+	    else
+		currutmp->color=(uin->color=='0')?'1':'0';}
+	brd[my][mx].out=1;
+	draw_line(my,-1);
+	move(cury[my],curx[mx]);
+	*b=0;
+	break;
+    case 'u':
+	move(0,0);clrtoeol();
+	prints("%s��%s cont=%d",(brd[my][mx].color == RED)?"��":"��",rname[brd[my][mx].value],cont);
+	*b=-1;
+	break;
+    case '\r':                       /* �Y or ����  ly��lx�����j��0*/
+    case '\n':
+	if(
+	    mly >= 0                               /* �n����l */
+	    &&
+	    brd[mly][mlx].color != brd[my][mx].color  /* �P�⤣�ಾ�ʤ]����Y */
+	    &&
+	    (Is_move(my,mx,mly,mlx) || Is_win(brd[mly][mlx],brd[my][mx],my,mx,mly,mlx))
+	    )
+	{
+	    if(fix && brd[my][mx].value<0)
+	    {
+		*b=-1;return 0;
+	    }
+	    if(brd[my][mx].value>=0&&brd[my][mx].die==0)
+	    {
+		if(!color) bcount--;
+		else rcount--;
+		move(cur_eaty,cur_eatx);
+		prints("%s",(color)?bname[brd[my][mx].value]:rname[brd[my][mx].value]);
+		if(cur_eatx>=26)
+		{ cur_eatx=5;cur_eaty++; }
+		else
+		    cur_eatx+=3;
+	    }
+	    brdswap(my,mx,mly,mlx);
+	    draw_line(mly,-1);
+	    draw_line( my,-1);
+	    if(fix==1) *b=-1;
+	    else { mly=-1;mlx=-1;*b=0; }
+	}
+	else *b=-1;
+	break;
+    default:
+	*b=-1;
+    }
+  
+    if(!rcount)
+	return -1;
+    else if(!bcount)
+	return -1;
+    if(*b == -1) return 0;
+    curr.y = my;curr.x = mx; curr.end=(!*b)?1:0;
+    send(fd,&curr,sizeof(curr),0);
+    send(fd,&brd,sizeof(brd),0);
+    return 0;
+}
+
+int main_dark(int fd,userinfo_t *uin) {
+    sint end=0,ch=1,go_on,i=0,cont=0;
+    char buf[16];
+    *buf=0;fix=0;
+    currutmp->color='.';   // '.' �����٨S�M�w�C��
+    rcount=16;bcount=16;   // initialize
+    cur_eaty=18,cur_eatx=5;
+    brd_prints();
+    if(currutmp->turn)
+    {
+	brd_rand();
+	send(fd,&brd,sizeof(brd),0);
+	pprints(21,0,"   ���A�O����");
+	pprints(22,0,"   ������A�U�F");
+    }else
+    {
+	recv(fd,&brd,sizeof(brd),0);
+	pprints(21,0,"   ���A�O���");
+    }
+    move(12,3);
+    prints("%s[0��0��]����.%s[0��0��]",currutmp->userid,currutmp->mateid);
+    outs("
+                                                �����\\���������������
+                                                �� ��������: ����
+                                                �� ��:       ��l,½�l
+                                                �� enter:    �Y��,���
+�@�w�g�ѨM��:�@�@                               �� ��:       �X��
+                                       �@�@     �� ��:       �{��
+                                                �� ��:       ����");
+
+    if(currutmp->turn) move(cury[0],curx[0]);
+
+    add_io(fd, 0);
+    while(end<=0)
+    {
+	if(uin->turn=='w' || currutmp->turn=='w') { end=-1; break; }
+
+	ch = igetkey();
+	if(ch == I_OTHERDATA)
+	{
+	    ch=recv(fd,&curr,sizeof(curr),0);
+	    if(ch!=sizeof(curr))
+	    {
+		if(uin->turn=='e') { end=-3;break; }
+		else if(uin->turn!='w') { end=-1; currutmp->turn='w'; break; }
+		end=-1; break;
+	    }
+
+	    if(curr.end==-3)      pprints(23,30,"\033[33m�n�D�X��\033[m");
+	    else if(curr.end==-4) pprints(23,30,"\033[33m�n�D����\033[m");
+	    else if(curr.end==-5) pprints(23,30,"\033[33m�n�D�s�Y\033[m");
+	    else pprints(23,30,"");
+
+	    recv(fd,&brd,sizeof(brd),0);
+	    my=curr.y;mx=curr.x;
+	    redraw();
+	    if(curr.end)
+		pprints(22,0,"   ������A�U�F");
+	    move(cury[my],curx[mx]);
+	}else
+	{
+	    if(currutmp->turn=='p')
+	    {
+		if(ch=='y') { end=-3; currutmp->turn='e'; break; }
+		else { pprints(23,30,""); *buf=0; currutmp->turn=(uin->turn)?0:1; }
+	    }else if(currutmp->turn=='c')
+	    {
+		if(ch=='y') { currutmp->color=(currutmp->color=='1')?'0':'1';
+		uin->color=(uin->color=='1')?'0':'1';
+		pprints(21,0,(currutmp->color=='1')?"   \033[1;33m���A�������\033[m":"   \033[1;33m���A���¦��\033[m");
+		}
+		else { pprints(23,30,""); currutmp->turn=(uin->turn)?0:1; }
+	    }else if(currutmp->turn=='g')
+	    {
+		if(ch=='y') {
+		    cont=1;
+		    pprints(21,0,"   \033[1;33m���A�������\033[m �i�s�Y");
+		}
+		else { pprints(23,30,""); currutmp->turn=(uin->turn)?0:1; }
+	    }
+
+	    if(currutmp->turn==1)
+	    {
+		if(uin->turn=='g') { cont=1;uin->turn=(currutmp->turn)?0:1; pprints(21,10,"�i�s�Y"); }
+		end=playing(fd,currutmp->color-'0',ch,&go_on,uin);
+
+		if(end == -1) { currutmp->turn='w';break; }
+		else if(end == -2) { uin->turn='w';break; }
+		else if(end == -3) {
+		    uin->turn='p';curr.end=-3;
+		    send(fd,&curr,sizeof(curr),0);
+		    send(fd,&brd,sizeof(buf),0);
+		    continue;
+		}
+		else if(end == -4) {
+		    if(currutmp->color!='1'&&currutmp->color!='0')
+			continue;
+		    uin->turn='c';i=0;curr.end=-4;
+		    send(fd,&curr,sizeof(curr),0);
+		    send(fd,&brd,sizeof(buf),0);
+		    continue;
+		}
+		else if(end == -5) {
+		    uin->turn='g';curr.end=-5;
+		    send(fd,&curr,sizeof(curr),0);
+		    send(fd,&brd,sizeof(buf),0);
+		    continue;
+		}
+		if(!i && currutmp->color=='1')
+		{ pprints(21,0,"   \033[1;33m���A�������\033[m");i++;move(cury[my],curx[mx]); }
+		if(!i && currutmp->color=='0')
+		{ pprints(21,0,"   \033[1;33m���A���¦��\033[m");i++;move(cury[my],curx[mx]); }
+
+		if(uin->turn == 'e') { end=-3; break; }
+		if(go_on < 0) continue;
+
+		move(22,0);clrtoeol();
+		prints("   ������%s�U �O�ȧO�� �L��ԣ��",currutmp->mateid);
+		currutmp->turn = 0;
+		uin->turn = 1;
+	    }else
+	    {
+		if(ch == 'q'){uin->turn='w';break;}
+		move(22,0);clrtoeol();
+		prints("   ������%s�U �O�ȧO�� �L��ԣ��",currutmp->mateid);
+	    }
+	}
+    }
+
+    switch(end)
+    {
+    case -1:
+    case -2:
+        if(currutmp->turn=='w'){ move(22,0);clrtoeol();prints("�A�F.. �u�O����~~");}
+        else {move(22,0);clrtoeol();prints("�鱼�F��.....�U�����L�n��!!");}
+        break;
+    case -3:
+        pprints(22,0,"�X�ѭ�!! �U���b�����U�a ^_^");
+        break;
+    default:
+        add_io(0,0);
+        close(fd);
+        pressanykey();
+        return 0;
+    }
+    add_io(0,0);
+    close(fd);
+    pressanykey();
+    return 0;
+}
diff --git a/mbbsd/descrypt.c b/mbbsd/descrypt.c
new file mode 100644
index 00000000..3bb0a5e5
--- /dev/null
+++ b/mbbsd/descrypt.c
@@ -0,0 +1,616 @@
+/* $Id: descrypt.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+
+/*
+ * FreeSec: libcrypt for NetBSD
+ *
+ * Copyright (c) 1994 David Burren
+ * All rights reserved.
+ *
+ * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet
+ *	crypt.c should now *only* export crypt(), in order to make
+ *	binaries of libcrypt exportable from the USA
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the author nor the names of other contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/secure/lib/libcrypt/crypt.c,v 1.11 1999/08/28 01:30:24 peter Exp $
+ *
+ * This is an original implementation of the DES and the crypt(3) interfaces
+ * by David Burren <davidb@werj.com.au>.
+ *
+ * An excellent reference on the underlying algorithm (and related
+ * algorithms) is:
+ *
+ *	B. Schneier, Applied Cryptography: protocols, algorithms,
+ *	and source code in C, John Wiley & Sons, 1994.
+ *
+ * Note that in that book's description of DES the lookups for the initial,
+ * pbox, and final permutations are inverted (this has been brought to the
+ * attention of the author).  A list of errata for this book has been
+ * posted to the sci.crypt newsgroup by the author and is available for FTP.
+ *
+ * ARCHITECTURE ASSUMPTIONS:
+ *	This code assumes that u_longs are 32 bits.  It will probably not
+ *	operate on 64-bit machines without modifications.
+ *	It is assumed that the 8-byte arrays passed by reference can be
+ *	addressed as arrays of u_longs (ie. the CPU is not picky about
+ *	alignment).
+ */
+
+#ifndef HAVE_DES_CRYPT
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <string.h>
+
+static unsigned char IP[64] = {
+    58, 50, 42, 34, 26, 18, 10,  2, 60, 52, 44, 36, 28, 20, 12,  4,
+    62, 54, 46, 38, 30, 22, 14,  6, 64, 56, 48, 40, 32, 24, 16,  8,
+    57, 49, 41, 33, 25, 17,  9,  1, 59, 51, 43, 35, 27, 19, 11,  3,
+    61, 53, 45, 37, 29, 21, 13,  5, 63, 55, 47, 39, 31, 23, 15,  7
+};
+
+static unsigned char inv_key_perm[64];
+static unsigned char u_key_perm[56];
+static unsigned char key_perm[56] = {
+    57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
+    10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
+    63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
+    14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
+};
+
+static unsigned char key_shifts[16] = {
+    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+static unsigned char inv_comp_perm[56];
+static unsigned char comp_perm[48] = {
+    14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
+    23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
+    41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+    44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ *	No E box is used, as it's replaced by some ANDs, shifts, and ORs.
+ */
+
+static unsigned char u_sbox[8][64];
+static unsigned char sbox[8][64] = {
+    {
+	14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
+	0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
+	4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
+	15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13
+    },
+    {
+	15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
+	3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
+	0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
+	13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9
+    },
+    {
+	10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
+	13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
+	13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
+	1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12
+    },
+    {
+	7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
+	13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
+	10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
+	3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14
+    },
+    {
+	2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
+	14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
+	4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
+	11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3
+    },
+    {
+	12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
+	10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
+	9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
+	4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13
+    },
+    {
+	4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
+	13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
+	1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
+	6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12
+    },
+    {
+	13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
+	1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
+	7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
+	2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
+    }
+};
+
+static unsigned char un_pbox[32];
+static unsigned char pbox[32] = {
+    16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+    2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+static unsigned long bits32[32] = {
+    0x80000000, 0x40000000, 0x20000000, 0x10000000,
+    0x08000000, 0x04000000, 0x02000000, 0x01000000,
+    0x00800000, 0x00400000, 0x00200000, 0x00100000,
+    0x00080000, 0x00040000, 0x00020000, 0x00010000,
+    0x00008000, 0x00004000, 0x00002000, 0x00001000,
+    0x00000800, 0x00000400, 0x00000200, 0x00000100,
+    0x00000080, 0x00000040, 0x00000020, 0x00000010,
+    0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+static unsigned char bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+static unsigned long saltbits;
+static long	old_salt;
+static unsigned long *bits28, *bits24;
+static unsigned char init_perm[64], final_perm[64];
+static unsigned long en_keysl[16], en_keysr[16];
+static unsigned long de_keysl[16], de_keysr[16];
+static int des_initialised = 0;
+static unsigned char m_sbox[4][4096];
+static unsigned long psbox[4][256];
+static unsigned long ip_maskl[8][256], ip_maskr[8][256];
+static unsigned long fp_maskl[8][256], fp_maskr[8][256];
+static unsigned long key_perm_maskl[8][128], key_perm_maskr[8][128];
+static unsigned long comp_maskl[8][128], comp_maskr[8][128];
+static unsigned long old_rawkey0, old_rawkey1;
+
+static unsigned char	ascii64[] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+/*	  0000000000111111111122222222223333333333444444444455555555556666 */
+/*	  0123456789012345678901234567890123456789012345678901234567890123 */
+
+static int ascii_to_bin(char ch) {
+    if(ch > 'z')
+	return 0;
+    if(ch >= 'a')
+	return ch - 'a' + 38;
+    if(ch > 'Z')
+	return 0;
+    if(ch >= 'A')
+	return ch - 'A' + 12;
+    if(ch > '9')
+	return 0;
+    if(ch >= '.')
+	return ch - '.';
+    return 0;
+}
+
+static void des_init() {
+    int	i, j, b, k, inbit, obit;
+    unsigned long *p, *il, *ir, *fl, *fr;
+	
+    old_rawkey0 = old_rawkey1 = 0L;
+    saltbits = 0L;
+    old_salt = 0L;
+    bits24 = (bits28 = bits32 + 4) + 4;
+	
+    /*
+     * Invert the S-boxes, reordering the input bits.
+     */
+    for(i = 0; i < 8; i++)
+	for(j = 0; j < 64; j++) {
+	    b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf);
+	    u_sbox[i][j] = sbox[i][b];
+	}
+	
+    /*
+     * Convert the inverted S-boxes into 4 arrays of 8 bits.
+     * Each will handle 12 bits of the S-box input.
+     */
+    for(b = 0; b < 4; b++)
+	for(i = 0; i < 64; i++)
+	    for(j = 0; j < 64; j++)
+		m_sbox[b][(i << 6) | j] =
+		    (u_sbox[(b << 1)][i] << 4) |
+		    u_sbox[(b << 1) + 1][j];
+
+    /*
+     * Set up the initial & final permutations into a useful form, and
+     * initialise the inverted key permutation.
+     */
+    for(i = 0; i < 64; i++) {
+	init_perm[final_perm[i] = IP[i] - 1] = i;
+	inv_key_perm[i] = 255;
+    }
+
+    /*
+     * Invert the key permutation and initialise the inverted key
+     * compression permutation.
+     */
+    for(i = 0; i < 56; i++) {
+	u_key_perm[i] = key_perm[i] - 1;
+	inv_key_perm[key_perm[i] - 1] = i;
+	inv_comp_perm[i] = 255;
+    }
+
+    /*
+     * Invert the key compression permutation.
+     */
+    for(i = 0; i < 48; i++) {
+	inv_comp_perm[comp_perm[i] - 1] = i;
+    }
+
+    /*
+     * Set up the OR-mask arrays for the initial and final permutations,
+     * and for the key initial and compression permutations.
+     */
+    for(k = 0; k < 8; k++) {
+	for(i = 0; i < 256; i++) {
+	    *(il = &ip_maskl[k][i]) = 0L;
+	    *(ir = &ip_maskr[k][i]) = 0L;
+	    *(fl = &fp_maskl[k][i]) = 0L;
+	    *(fr = &fp_maskr[k][i]) = 0L;
+	    for(j = 0; j < 8; j++) {
+		inbit = 8 * k + j;
+		if(i & bits8[j]) {
+		    if((obit = init_perm[inbit]) < 32)
+			*il |= bits32[obit];
+		    else
+			*ir |= bits32[obit-32];
+		    if ((obit = final_perm[inbit]) < 32)
+			*fl |= bits32[obit];
+		    else
+			*fr |= bits32[obit - 32];
+		}
+	    }
+	}
+	for(i = 0; i < 128; i++) {
+	    *(il = &key_perm_maskl[k][i]) = 0L;
+	    *(ir = &key_perm_maskr[k][i]) = 0L;
+	    for(j = 0; j < 7; j++) {
+		inbit = 8 * k + j;
+		if(i & bits8[j + 1]) {
+		    if((obit = inv_key_perm[inbit]) == 255)
+			continue;
+		    if(obit < 28)
+			*il |= bits28[obit];
+		    else
+			*ir |= bits28[obit - 28];
+		}
+	    }
+	    *(il = &comp_maskl[k][i]) = 0L;
+	    *(ir = &comp_maskr[k][i]) = 0L;
+	    for(j = 0; j < 7; j++) {
+		inbit = 7 * k + j;
+		if(i & bits8[j + 1]) {
+		    if((obit=inv_comp_perm[inbit]) == 255)
+			continue;
+		    if(obit < 24)
+			*il |= bits24[obit];
+		    else
+			*ir |= bits24[obit - 24];
+		}
+	    }
+	}
+    }
+	
+    /*
+     * Invert the P-box permutation, and convert into OR-masks for
+     * handling the output of the S-box arrays setup above.
+     */
+    for(i = 0; i < 32; i++)
+	un_pbox[pbox[i] - 1] = i;
+
+    for(b = 0; b < 4; b++)
+	for(i = 0; i < 256; i++) {
+	    *(p = &psbox[b][i]) = 0L;
+	    for (j = 0; j < 8; j++) {
+		if (i & bits8[j])
+		    *p |= bits32[un_pbox[8 * b + j]];
+	    }
+	}
+	
+    des_initialised = 1;
+}
+
+static void setup_salt(long salt) {
+    unsigned long obit, saltbit;
+    int	i;
+	
+    if (salt == old_salt)
+	return;
+    old_salt = salt;
+	
+    saltbits = 0L;
+    saltbit = 1;
+    obit = 0x800000;
+    for (i = 0; i < 24; i++) {
+	if (salt & saltbit)
+	    saltbits |= obit;
+	saltbit <<= 1;
+	obit >>= 1;
+    }
+}
+
+static int des_setkey(const char *key) {
+    unsigned long k0, k1, rawkey0, rawkey1;
+    int	shifts, round;
+
+    if(!des_initialised)
+	des_init();
+	
+    rawkey0 = ntohl(*(unsigned long *) key);
+    rawkey1 = ntohl(*(unsigned long *) (key + 4));
+	
+    if((rawkey0 | rawkey1)
+       && rawkey0 == old_rawkey0
+       && rawkey1 == old_rawkey1) {
+	/*
+	 * Already setup for this key.
+	 * This optimisation fails on a zero key (which is weak and
+	 * has bad parity anyway) in order to simplify the starting
+	 * conditions.
+	 */
+	return 0;
+    }
+    old_rawkey0 = rawkey0;
+    old_rawkey1 = rawkey1;
+	
+    /*
+     *	Do key permutation and split into two 28-bit subkeys.
+     */
+    k0 = key_perm_maskl[0][rawkey0 >> 25]
+	| key_perm_maskl[1][(rawkey0 >> 17) & 0x7f]
+	| key_perm_maskl[2][(rawkey0 >> 9) & 0x7f]
+	| key_perm_maskl[3][(rawkey0 >> 1) & 0x7f]
+	| key_perm_maskl[4][rawkey1 >> 25]
+	| key_perm_maskl[5][(rawkey1 >> 17) & 0x7f]
+	| key_perm_maskl[6][(rawkey1 >> 9) & 0x7f]
+	| key_perm_maskl[7][(rawkey1 >> 1) & 0x7f];
+    k1 = key_perm_maskr[0][rawkey0 >> 25]
+	| key_perm_maskr[1][(rawkey0 >> 17) & 0x7f]
+	| key_perm_maskr[2][(rawkey0 >> 9) & 0x7f]
+	| key_perm_maskr[3][(rawkey0 >> 1) & 0x7f]
+	| key_perm_maskr[4][rawkey1 >> 25]
+	| key_perm_maskr[5][(rawkey1 >> 17) & 0x7f]
+	| key_perm_maskr[6][(rawkey1 >> 9) & 0x7f]
+	| key_perm_maskr[7][(rawkey1 >> 1) & 0x7f];
+    /*
+     *	Rotate subkeys and do compression permutation.
+     */
+    shifts = 0;
+    for(round = 0; round < 16; round++) {
+	unsigned long t0, t1;
+		
+	shifts += key_shifts[round];
+		
+	t0 = (k0 << shifts) | (k0 >> (28 - shifts));
+	t1 = (k1 << shifts) | (k1 >> (28 - shifts));
+		
+	de_keysl[15 - round] =
+	    en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f]
+	    | comp_maskl[1][(t0 >> 14) & 0x7f]
+	    | comp_maskl[2][(t0 >> 7) & 0x7f]
+	    | comp_maskl[3][t0 & 0x7f]
+	    | comp_maskl[4][(t1 >> 21) & 0x7f]
+	    | comp_maskl[5][(t1 >> 14) & 0x7f]
+	    | comp_maskl[6][(t1 >> 7) & 0x7f]
+	    | comp_maskl[7][t1 & 0x7f];
+		
+	de_keysr[15 - round] = en_keysr[round] =
+	    comp_maskr[0][(t0 >> 21) & 0x7f]
+	    | comp_maskr[1][(t0 >> 14) & 0x7f]
+	    | comp_maskr[2][(t0 >> 7) & 0x7f]
+	    | comp_maskr[3][t0 & 0x7f]
+	    | comp_maskr[4][(t1 >> 21) & 0x7f]
+	    | comp_maskr[5][(t1 >> 14) & 0x7f]
+	    | comp_maskr[6][(t1 >> 7) & 0x7f]
+	    | comp_maskr[7][t1 & 0x7f];
+    }
+    return 0;
+}
+
+static int do_des(unsigned long l_in, unsigned long r_in, unsigned long *l_out,
+		  unsigned long *r_out, int count) {
+    /*
+	 *	l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format.
+	 */
+    unsigned long l, r, *kl, *kr, *kl1, *kr1;
+    unsigned long f, r48l, r48r;
+    int	round;
+
+    if(count == 0) {
+	return 1;
+    } else if(count > 0) {
+	/*
+		 * Encrypting
+		 */
+	kl1 = en_keysl;
+	kr1 = en_keysr;
+    } else {
+	/*
+		 * Decrypting
+		 */
+	count = -count;
+	kl1 = de_keysl;
+	kr1 = de_keysr;
+    }
+	
+    /*
+	 *	Do initial permutation (IP).
+	 */
+    l = ip_maskl[0][l_in >> 24]
+	| ip_maskl[1][(l_in >> 16) & 0xff]
+	| ip_maskl[2][(l_in >> 8) & 0xff]
+	| ip_maskl[3][l_in & 0xff]
+	| ip_maskl[4][r_in >> 24]
+	| ip_maskl[5][(r_in >> 16) & 0xff]
+	| ip_maskl[6][(r_in >> 8) & 0xff]
+	| ip_maskl[7][r_in & 0xff];
+    r = ip_maskr[0][l_in >> 24]
+	| ip_maskr[1][(l_in >> 16) & 0xff]
+	| ip_maskr[2][(l_in >> 8) & 0xff]
+	| ip_maskr[3][l_in & 0xff]
+	| ip_maskr[4][r_in >> 24]
+	| ip_maskr[5][(r_in >> 16) & 0xff]
+	| ip_maskr[6][(r_in >> 8) & 0xff]
+	| ip_maskr[7][r_in & 0xff];
+	
+    while(count--) {
+	/*
+	 * Do each round.
+	 */
+	kl = kl1;
+	kr = kr1;
+	round = 16;
+	while(round--) {
+	    /*
+	     * Expand R to 48 bits (simulate the E-box).
+	     */
+	    r48l = ((r & 0x00000001) << 23)
+		| ((r & 0xf8000000) >> 9)
+		| ((r & 0x1f800000) >> 11)
+		| ((r & 0x01f80000) >> 13)
+		| ((r & 0x001f8000) >> 15);
+
+	    r48r = ((r & 0x0001f800) << 7)
+		| ((r & 0x00001f80) << 5)
+		| ((r & 0x000001f8) << 3)
+		| ((r & 0x0000001f) << 1)
+		| ((r & 0x80000000) >> 31);
+	    /*
+	     * Do salting for crypt() and friends, and
+	     * XOR with the permuted key.
+	     */
+	    f = (r48l ^ r48r) & saltbits;
+	    r48l ^= f ^ *kl++;
+	    r48r ^= f ^ *kr++;
+	    /*
+	     * Do sbox lookups (which shrink it back to 32 bits)
+	     * and do the pbox permutation at the same time.
+	     */
+	    f = psbox[0][m_sbox[0][r48l >> 12]]
+		| psbox[1][m_sbox[1][r48l & 0xfff]]
+		| psbox[2][m_sbox[2][r48r >> 12]]
+		| psbox[3][m_sbox[3][r48r & 0xfff]];
+	    /*
+	     * Now that we've permuted things, complete f().
+	     */
+	    f ^= l;
+	    l = r;
+	    r = f;
+	}
+	r = l;
+	l = f;
+    }
+    /*
+	 * Do final permutation (inverse of IP).
+	 */
+    *l_out = fp_maskl[0][l >> 24]
+	| fp_maskl[1][(l >> 16) & 0xff]
+	| fp_maskl[2][(l >> 8) & 0xff]
+	| fp_maskl[3][l & 0xff]
+	| fp_maskl[4][r >> 24]
+	| fp_maskl[5][(r >> 16) & 0xff]
+	| fp_maskl[6][(r >> 8) & 0xff]
+	| fp_maskl[7][r & 0xff];
+    *r_out = fp_maskr[0][l >> 24]
+	| fp_maskr[1][(l >> 16) & 0xff]
+	| fp_maskr[2][(l >> 8) & 0xff]
+	| fp_maskr[3][l & 0xff]
+	| fp_maskr[4][r >> 24]
+	| fp_maskr[5][(r >> 16) & 0xff]
+	| fp_maskr[6][(r >> 8) & 0xff]
+	| fp_maskr[7][r & 0xff];
+    return 0;
+}
+
+char *crypt(char *key, char *setting) {
+    unsigned long count, salt, l, r0, r1, keybuf[2];
+    unsigned char *p, *q;
+    static unsigned char output[21];
+	
+    if(!des_initialised)
+	des_init();
+    /*
+     * Copy the key, shifting each character up by one bit
+     * and padding with zeros.
+     */
+    q = (unsigned char *)keybuf;
+    while(q - (unsigned char *)keybuf - 8) {
+	if((*q++ = *key << 1))
+	    key++;
+    }
+    if(des_setkey((unsigned char *)keybuf))
+	return NULL;
+	
+    /*
+     * "old"-style:
+     *	setting - 2 bytes of salt
+     *	key - up to 8 characters
+     */
+    count = 25;
+
+    salt = (ascii_to_bin(setting[1]) << 6)
+	|  ascii_to_bin(setting[0]);
+
+    output[0] = setting[0];
+    /*
+     * If the encrypted password that the salt was extracted from
+     * is only 1 character long, the salt will be corrupted.  We
+     * need to ensure that the output string doesn't have an extra
+     * NUL in it!
+     */
+    output[1] = setting[1] ? setting[1] : output[0];
+
+    p = output + 2;
+
+    setup_salt(salt);
+    /*
+     * Do it.
+     */
+    if(do_des(0L, 0L, &r0, &r1, count))
+	return NULL;
+    /*
+     * Now encode the result...
+     */
+    l = (r0 >> 8);
+    *p++ = ascii64[(l >> 18) & 0x3f];
+    *p++ = ascii64[(l >> 12) & 0x3f];
+    *p++ = ascii64[(l >> 6) & 0x3f];
+    *p++ = ascii64[l & 0x3f];
+
+    l = (r0 << 16) | ((r1 >> 16) & 0xffff);
+    *p++ = ascii64[(l >> 18) & 0x3f];
+    *p++ = ascii64[(l >> 12) & 0x3f];
+    *p++ = ascii64[(l >> 6) & 0x3f];
+    *p++ = ascii64[l & 0x3f];
+
+    l = r1 << 2;
+    *p++ = ascii64[(l >> 12) & 0x3f];
+    *p++ = ascii64[(l >> 6) & 0x3f];
+    *p++ = ascii64[l & 0x3f];
+    *p = 0;
+
+    return output;
+}
+#endif
diff --git a/mbbsd/dice.c b/mbbsd/dice.c
new file mode 100644
index 00000000..d64ce63c
--- /dev/null
+++ b/mbbsd/dice.c
@@ -0,0 +1,447 @@
+/* $Id: dice.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+#define DICE_TXT   BBSHOME "/etc/dice.txt"
+#define DICE_DATA  BBSHOME "/etc/dice.data"
+#define DICE_WIN   BBSHOME "/etc/windice.log"
+#define DICE_LOST  BBSHOME "/etc/lostdice.log"
+
+#define B_MAX    500
+#define B_MIN    10
+#define B_COMMON 1
+#define B_TIMES  5
+#define B_THIRD  3
+
+extern int usernum;
+static int flag[100], value[100];
+
+typedef struct dicedata_t {
+    int mybet;
+    int mymoney;
+} dicedata_t;
+
+static void set_bingo(int bet[]) {
+    int i, j = 0, k = 0, m = 0;
+    
+    for(i = 0; i < 3; i++)
+	for(j = 2; j > i; j--)
+	    if(bet[j] < bet[j - 1]) {
+		m = bet[j];
+		bet[j] = bet[j - 1];
+		bet[j - 1]=m;
+	    }
+    
+    for(i = 0; i < 100; i++)
+	flag[i] = 0;
+    
+    for(i = 0; i < 3; i++)
+	flag[bet[i]]++;
+    j = bet[0] + bet[1] + bet[2];
+
+    if((abs(bet[1] - bet[0]) == 1 && abs(bet[2] - bet[0]) == 2) ||
+       (abs(bet[2] - bet[0]) == 1 && abs(bet[1] - bet[0]) == 2))
+	flag[66] = B_TIMES;
+    
+    if(j < 10){
+	flag[7] = B_COMMON;
+	for(i = 0; i < 3; i++)
+	    if(bet[i] == 4)
+		flag[74] = B_TIMES;
+    } else if(j > 11) {
+	flag[8] = B_COMMON;
+	for(i = 0; i < 3; i++)
+	    if(bet[i] == 3)
+		flag[83]=B_TIMES;
+    } else
+	flag[11] = B_THIRD;
+    
+    for(i = 0; i < 3; i++)
+	for(j = i; j < 3; j++) {
+	    m = bet[i];
+	    k = bet[j];
+	    if(m != k)
+		flag[m * 10 + k] = B_TIMES;
+	}
+}
+
+static int bingo(int mybet) {
+    return flag[mybet];
+}
+
+int IsNum(char *a, int n) {
+    int i;
+
+    for(i = 0; i < n; i++)
+	if (a[i] > '9' || a[i] < '0' )
+	    return 0;
+    return 1;
+}
+
+int IsSNum(char *a) {
+    int i;
+    
+    for(i = 0; a[i]; i++)
+	if(a[i] > '9' || a[i] < '0')
+	    return 0;
+    return 1;
+}
+
+static void show_data(void) {
+    move(0, 0);
+    prints("\033[31m       �z�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�w�w�w�w�w�w�w�w�w�{\033[m\n");
+    prints("\033[45;37m���v�@\033[m\033[31m �x \033[33m[1]��@�I [2]��G�I "
+	   "[3]��T�I [4]��|�I [5]�㤭�I [6]�㤻�I    \033[31m  �x\033[m\n");
+    prints("\033[31m       �x \033[33m[7]��p   [8]��j                    "
+	   "                          \033[31m  �x\033[m\n");
+    prints("\033[31m       �x                                              "
+	   "                    �x\033[m\n");
+    prints("\033[45;37m�߲v�T\033[m\033[31m �x \033[33m[11]�㤤(�`�I�Ƶ���11"
+	   "��10)                                     \033[31m  �x\033[m\n");
+    prints("\033[31m       �x                                              "
+	   "                    �x\033[m\n");
+    prints("\033[45;37m�߲v��\033[m\033[31m �x \033[33m[74]��p�B�|�I [83]��"
+	   "�j�B�T�I [66]��s��                       \033[31m  �x\033[m\n");
+    prints("\033[31m       �x                                              "
+	   "                    �x\033[m\n");
+    prints("\033[31m       �x \033[33m[12]��@�G�I [13]��@�T�I [14]��@�|�I"
+	   " [15]��@���I [16]��@���I\033[31m �x\033[m\n");
+    prints("\033[31m       �x \033[33m[23]��G�T�I [24]��G�|�I [25]��G���I"
+	   " [26]��G���I [34]��T�|�I\033[31m �x\033[m\n");
+    prints("\033[31m       �x \033[33m[35]��T���I [36]��T���I [45]��|���I"
+	   " [46]��|���I [56]�㤭���I\033[31m �x\033[m\n");
+    prints("\033[31m       �|�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�w�w�w�w�w�w�w�w�}\033[m\n");
+}
+
+static void show_count(int index, int money) {
+    int i = 0, count = 2, j, k;
+    
+    value[index] += money;
+    move(14,0);
+    clrtoline(18);
+    for(i = 1, j = 13; i <= 8; i++, count += 12) {
+	if(i == 6) {
+	    j = 14;
+	    count = 2;
+	}
+	move(j,count);
+	prints("[%2d]:%d ", i, value[i]);
+    }
+    
+    count = 2;
+    i = 15;
+    for(j = 1; j <= 5; j++)
+	for(k = j + 1; k <= 6; k++, count += 12) {
+	    if(j == 2 && k == 4) {
+		i = 16;
+		count = 2;
+	    } else if(j==4 && k==5) {
+		i = 17;
+		count = 2;
+	    }
+	    move(i,count);
+	    prints("[%d%d]:%d ", j, k, value[j * 10 + k]);
+	}
+    
+    move(18,2);
+    prints("[11]:%d",value[11]);
+    move(18,14);
+    prints("[66]:%d",value[66]);
+    move(18,26);
+    prints("[74]:%d",value[74]);
+    move(18,38);
+    prints("[83]:%d",value[83]);
+}
+
+static int check_index(int index) {
+    int i,tp[] = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 23, 24, 25,
+		  26, 34, 35, 36, 45, 46, 56, 66, 74, 83};
+    if(index < 0 || index > 100)
+	return 0;
+    for(i = 0; i < 27; i++)
+	if(index == tp[i])
+	    return 1;
+    return 0;
+}
+
+extern userec_t cuser;
+
+static int del(int total, dicedata_t *table) {
+    int index, money;
+    char data[10];
+    int i;
+    
+    while(1) {
+	do {
+	    move(22,0);
+	    clrtoeol();
+	    getdata(21, 0, "��J�h�諸�Ʀr(��q���}): ", data, 3, LCECHO);
+	    if(data[0] == 'q' || data[0] == 'Q')
+		return 0;
+	} while(!IsNum(data,strlen(data)));
+	
+	index = atoi(data);
+	for(i = 0; i < total; i++) {
+	    if(table[i].mybet == index){
+		do {
+		    getdata(21, 0, "�h�ֿ�: ", data, 10, LCECHO);
+		} while(!IsNum(data,strlen(data)));
+		money = atoi(data);
+		if(money>table[i].mymoney) {
+		    move(22,0);
+		    clrtoeol();
+		    prints("��������");
+		    i--;
+		    continue;
+		}
+		demoney(money);
+		move(19,0);
+		clrtoeol();
+		prints("�A�{�b�� %u Ptt$��", cuser.money);
+		table[i].mymoney -= money;
+		show_count(index, -money);
+		break;
+	    }
+	}
+    }
+    return 0;
+}
+
+static int IsLegal(char *data) {
+    int money = atoi(data);
+    if(IsNum(data,strlen(data)) && money<=B_MAX && money>=B_MIN)
+	return money;
+    return 0;
+}
+
+static void show_output(int bet[]) {
+    int i, j = 10;
+    
+    move(12,0);
+    clrtoline(17);
+    /* �Ȯɭ��� �]�����Uclrtoline�ǩǪ� */
+    for(i = 13; i <= 18; i++) {
+	move(i,0);
+	prints("                               ");
+    }
+    move(12,0);
+    prints("\033[1;31m        �z�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�{\033[m\n\n\n\n\n\n");
+    prints("\033[1;31m        �|�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�}\033[m");
+    for(i = 0; i < 3; i++, j += 25) {
+	switch(bet[i]) {
+	case 1:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x        �x\033[m");
+	    move(15, j);prints("\033[37m�x   ��   �x\033[m");
+	    move(16, j);prints("\033[37m�x        �x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	case 2:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x      ���x\033[m");
+	    move(15, j);prints("\033[37m�x        �x\033[m");
+	    move(16, j);prints("\033[37m�x��      �x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	case 3:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x      ���x\033[m");
+	    move(15, j);prints("\033[37m�x   ��   �x\033[m");
+	    move(16, j);prints("\033[37m�x��      �x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	case 4:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x��    ���x\033[m");
+	    move(15, j);prints("\033[37m�x        �x\033[m");
+	    move(16, j);prints("\033[37m�x��    ���x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	case 5:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x��    ���x\033[m");
+	    move(15, j);prints("\033[37m�x   ��   �x\033[m");
+	    move(16, j);prints("\033[37m�x��    ���x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	case 6:
+	    move(13, j);prints("\033[37m�~�w�w�w�w��\033[m");
+	    move(14, j);prints("\033[37m�x��    ���x\033[m");
+	    move(15, j);prints("\033[37m�x��    ���x\033[m");
+	    move(16, j);prints("\033[37m�x��    ���x\033[m");
+	    move(17, j);prints("\033[37m���w�w�w�w��\033[m");
+	    break;
+	}
+    }
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+
+int dice_main(void) {
+    char input[10],data[256], ch;
+    dicedata_t table[256];
+    int bet[3], index, money = 0, i, ya = 0, j, total, sig = 0;
+    FILE *winfp/* , *lostfp */;
+    
+    more(DICE_TXT, NA);
+    reload_money();
+    if(cuser.money < 10){
+	move(19,0);
+	prints("\033[1;37m�W�L�Q���A�Ӫ��a~~\033[m");
+	pressanykey();
+	return 0;
+    }
+    
+    lockreturn0(DICE, LOCK_MULTI);
+    winfp = fopen(DICE_WIN,"a");
+    /*lostfp = fopen(DICE_LOST,"a");*/
+    if(!winfp /*|| !lostfp*/)
+	return 0;
+
+    do {
+	total = 0; i = 0;
+	ch = 'y';
+	clear();
+	show_data();
+	for(j = 0; j < 3; j++)
+	    bet[j] = rand() % 6 + 1;
+	
+	for(j = 0; j < 100; j++)
+	    value[j] = 0;
+
+	while(1) {
+	    move(19,0);
+	    prints("\033[1;32m�A�{�b��\033[1;31m %u \033[1;32mPtt$��\033[m",
+		   cuser.money);
+	    getdata(20, 0, "\033[1;37m�Ʀr:�[�� d:�h�� s:�}�l�����}\033[m: ",
+		    input, 5, LCECHO);
+	    reload_money();
+	    if(input[0] != 's' && input[0] != 'd' && cuser.money < 10) {
+		move(21, 0);
+		clrtoeol();
+		prints("\033[1;37m�W�L�Q���~���~\033[m");
+		continue;
+	    }
+	    if(input[0] == 'd' || input[0] == 'D') {
+		del(i, table);
+		continue;
+	    }
+	    if(input[0] == 's' || input[0] == 'S')
+		break;
+	    
+	    if(!IsNum(input,strlen(input)))
+		continue;
+	    
+	    index=atoi(input);
+	    if(check_index(index) == 0)
+		continue;
+/*��J����loop*/
+	    while(1) {
+		if(cuser.money < 10)
+		    break;
+		getdata(21, 0, "\033[1;32m��h�ֿ��O\033[1;37m(�j��10 �p��500)"
+			"\033[m: ", input, 9, LCECHO);
+		if(!(money = IsLegal(input))||input[0] == '0')
+		    continue;
+		reload_money(); 
+		if(money > cuser.money)
+		    continue;
+		for(j = 0, sig = 0; j < i; j++)
+		    if(table[j].mybet == index) {
+			if(table[j].mymoney == B_MAX)
+			    sig = 2;
+			else if(table[j].mymoney+money>B_MAX) {
+			    sig = 1;
+			    break;
+			} else {
+			    vice(money,"��l");
+			    table[j].mymoney += money;
+			    j = -1;
+			    break;
+			}
+		    }
+		if(sig == 2)
+		    break;
+		if(sig == 1)
+		    continue;
+		if(j != -1) {
+		    bzero((char*)&table[i], sizeof(dicedata_t));
+		    table[i].mybet = index;
+		    table[i++].mymoney = money;
+		    vice(money,"��l");
+		}
+		break;
+	    }
+	    reload_money(); 
+	    move(19,0);
+	    prints("\033[1;32m�A�{�b�� \033[1;31m%u\033[1;32m Ptt$��",
+		   cuser.money);
+	    if(sig != 2)
+		show_count(index, money);
+	}
+	
+	if(i == 0) {
+	    fclose(winfp);
+	    /*fclose(lostfp);*/
+	    unlockutmpmode();
+	    return 0;
+	}
+	
+	show_output(bet);
+	set_bingo(bet);
+
+	for(j = 0; j < i; j++) {
+	    if(table[j].mymoney <= 0)
+		continue;
+	    ya = bingo(table[j].mybet);
+	    if(ya == 0) {
+		/*sprintf(data, "%-15s ��F %-8d $", cuser.userid,
+			table[j].mymoney);
+		fprintf(lostfp, "%s\n", data);*/
+		continue;
+	    }
+	    demoney(table[j].mymoney * ya + table[j].mymoney);
+	    total += table[j].mymoney * ya;
+		if (table[j].mymoney * ya > 500){   /* �W�L500�����~��log ���io */
+	    	sprintf(data, "%-15s ��%-2d�ﶵ%-8d���� ���F%d�� �b��:%-8d\n",
+		    	cuser.userid,table[j].mybet,
+		   	    table[j].mymoney, ya, table[j].mymoney * ya);
+	    	fputs(data,winfp);
+	    }
+	    ya = 0;
+	}
+
+	if(total > 0) {
+	    move(21,0);
+	    prints("\033[1;32m�A�F \033[1;31m%d\033[1;32m Ptt$ ��~~"
+		   "                    \033[m", total);
+	} else {
+	    move(21,0);
+	    clrtoeol();
+	    prints("\033[1;32m�u�i�� �U���A�ӸI�I�B��a\033[m");
+	}
+	
+	move(19,0);
+	clrtoeol();
+	prints("\033[1;32m�A�{�b�� \033[1;31m%u\033[1;32m Ptt$��\033[m",
+	       cuser.money);
+	
+	getdata(23, 0, "\033[1;32m�~���[\033[1;37my/n\033[1;32m]\033[m: ",
+		input, 2, LCECHO);
+    } while(input[0] != 'n' && input[0] != 'N');
+    fclose(winfp);
+    /*fclose(lostfp);*/
+    unlockutmpmode();
+    return 0;
+}
diff --git a/mbbsd/edit.c b/mbbsd/edit.c
new file mode 100644
index 00000000..19f437af
--- /dev/null
+++ b/mbbsd/edit.c
@@ -0,0 +1,2256 @@
+/* $Id: edit.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "config.h"
+#include "common.h"
+#include "modes.h"
+#include "perm.h"
+#include "proto.h"
+
+#define WRAPMARGIN (511)
+
+typedef struct textline_t {
+    struct textline_t *prev;
+    struct textline_t *next;
+    int len;
+    char data[WRAPMARGIN + 1];
+} textline_t;
+
+extern int current_font_type;
+extern char *str_author1;
+extern char *str_author2;
+extern int t_lines, t_columns;  /* Screen size / width */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern char quote_file[80];
+extern char quote_user[80];
+extern int curredit;
+extern unsigned int currbrdattr;
+extern char currboard[];        /* name of currently selected board */
+extern char *str_reply;
+extern char *str_post1;
+extern char *str_post2;
+extern char *BBSName;
+extern char fromhost[];
+extern unsigned int currstat;
+extern crosspost_t postrecord;
+extern userinfo_t *currutmp;
+extern int KEY_ESC_arg;
+extern char reset_color[];
+extern char trans_buffer[256];
+
+#define KEEP_EDITING    -2
+#define BACKUP_LIMIT    100
+#define SCR_WIDTH       80 
+
+enum {
+    NOBODY, MANAGER, SYSOP
+};
+
+static textline_t *firstline = NULL;
+static textline_t *lastline = NULL;
+static textline_t *currline = NULL;
+static textline_t *blockline = NULL;
+static textline_t *top_of_win = NULL;
+static textline_t *deleted_lines = NULL;
+
+extern int local_article;
+extern char real_name[20];
+static char line[WRAPMARGIN  + 2];
+static int ifuseanony=0;
+static int currpnt, currln, totaln;
+static int curr_window_line;
+static int redraw_everything;
+static int insert_character;
+static int my_ansimode;
+static int raw_mode;
+static int edit_margin;
+static int blockln  = -1;
+static int blockpnt;
+static int prevln = -1;
+static int prevpnt;
+static int line_dirty;
+static int indent_mode;
+static int insert_c = ' ';
+
+static char fp_bak[] = "bak";
+
+char save_title[STRLEN];
+
+/* �O����޲z�P�s��B�z */
+static void indigestion(i) {
+    fprintf(stderr, "�Y������ %d\n", i);
+}
+
+/* Thor: ansi �y���ഫ  for color �s��Ҧ� */
+static int ansi2n(int ansix, textline_t * line) {
+    register char *data, *tmp;
+    register char ch;
+    
+    data = tmp = line->data;
+    
+    while(*tmp) {
+	if(*tmp == KEY_ESC) {
+	    while((ch = *tmp) && !isalpha(ch))
+		tmp++;
+	    if(ch)
+		tmp++;
+	    continue;
+	}
+	if(ansix <= 0)
+	    break;
+	tmp++;
+	ansix--;
+    }
+    return tmp - data;
+}
+
+static int n2ansi(int nx, textline_t * line) {
+    register int ansix = 0;
+    register char *tmp,*nxp;
+    register char ch;
+    
+    tmp = nxp = line->data;
+    nxp += nx;
+    
+    while(*tmp) {
+	if(*tmp == KEY_ESC) {
+	    while((ch = *tmp) && !isalpha(ch))
+		tmp++;
+	    if(ch)
+		tmp++;
+	    continue;
+	}
+	if(tmp >= nxp)
+	    break;
+	tmp++;
+	ansix++;
+    }
+    return ansix;
+}
+
+/* �ù��B�z�G���U�T���B��ܽs�褺�e */
+static void edit_msg() {
+    static char *edit_mode[2] = {"���N", "���J"};
+    register int n = currpnt;
+    
+    if(my_ansimode)                      /* Thor: �@ ansi �s�� */
+	n = n2ansi(n, currline);
+    n++;
+    move(b_lines, 0);
+    clrtoeol();
+    prints("\033[%sm �s��峹 \033[31;47m (Ctrl-Z)\033[30m���U���� "
+	   "\033[31;47m(^G)\033[30m���J�Ϥ�w \033[31m(^X,^Q)"
+	   "\033[30m���}��%s�x%c%c%c%c�� %3d:%3d  \033[m",
+	   "37;44",
+	   edit_mode[insert_character],
+	   my_ansimode ? 'A' : 'a', indent_mode ? 'I' : 'i',
+	   'P' , raw_mode ? 'R' : 'r',
+	   currln + 1, n);
+}
+
+static textline_t *back_line(textline_t *pos, int num) {
+    while(num-- > 0) {
+	register textline_t *item;
+	
+	if(pos && (item = pos->prev)) {
+	    pos = item;
+	    currln--;
+	}
+    }
+    return pos;
+}
+
+static textline_t *forward_line(textline_t *pos, int num) {
+    while(num-- > 0) {
+	register textline_t *item;
+
+	if(pos && (item = pos->next)) {
+	    pos = item;
+	    currln++;
+	}
+    }
+    return pos;
+}
+
+static int getlineno() {
+    int cnt = 0;
+    textline_t *p = currline;
+
+    while(p && (p != top_of_win)) {
+	cnt++;
+	p = p->prev;
+    }
+    return cnt;
+}
+
+static char *killsp(char *s) {
+    while(*s == ' ')
+	s++;
+    return s;
+}
+
+static textline_t *alloc_line() {
+    register textline_t *p;
+    
+    if((p = (textline_t *)malloc(sizeof(textline_t)))) {
+	memset(p, 0, sizeof(textline_t));
+	return p;
+    }
+    
+    indigestion(13);
+    abort_bbs(0);
+    return NULL;
+}
+
+/* append p after line in list. keeps up with last line */
+static void append(textline_t *p, textline_t *line) {
+    register textline_t *n;
+    
+    if((p->next = n = line->next))
+	n->prev = p;
+    else
+	lastline = p;
+    line->next = p;
+    p->prev = line;
+}
+
+/*
+   delete_line deletes 'line' from the list,
+   and maintains the lastline, and firstline pointers.
+*/
+
+static void delete_line(textline_t *line) {
+    register textline_t *p = line->prev;
+    register textline_t *n = line->next;
+    
+    if(!p && !n) {
+	line->data[0] = line->len = 0;
+	return;
+    }
+    if(n)
+	n->prev = p;
+    else
+	lastline = p;
+    if(p)
+	p->next = n;
+    else
+	firstline = n;
+    strcat(line->data, "\n");
+    line->prev = deleted_lines;
+    deleted_lines = line;
+    totaln--;
+}
+
+static int ask(char *prompt) {
+    int ch;
+
+    move (0, 0);
+    clrtoeol ();
+    standout ();
+    prints ("%s", prompt);
+    standend ();
+    ch = igetkey ();
+    move (0, 0);
+    clrtoeol ();
+    return (ch);
+}
+
+static int indent_spcs() {
+    textline_t* p;
+    int spcs;
+    
+    if(!indent_mode)
+	return 0;
+    
+    for(p = currline; p; p = p->prev) {
+	for(spcs = 0; p->data[spcs] == ' '; ++spcs);
+	if (p->data[spcs])
+	    return spcs;
+    }
+    return 0;
+}
+
+/* split 'line' right before the character pos */
+static void split(textline_t *line, int pos) {
+    if(pos <= line->len) {
+	register textline_t *p = alloc_line();
+	register char *ptr;
+	int spcs = indent_spcs();
+	
+	totaln++;
+	
+	p->len = line->len - pos + spcs;
+	line->len = pos;
+	
+	memset(p->data, ' ', spcs);
+	p->data[spcs] = 0;
+	strcat(p->data, (ptr = line->data + pos));
+	ptr[0] = '\0';
+	append(p, line);
+	if(line == currline && pos <= currpnt) {
+	    currline = p;
+	    if(pos == currpnt)
+		currpnt = spcs;
+	    else
+		currpnt -= pos;
+	    curr_window_line++;
+	    currln++;
+	}
+	redraw_everything = YEA;
+    }
+}
+
+static void insert_char(int ch) {
+    register textline_t *p = currline;
+    register int i = p->len;
+    register char *s;
+    int wordwrap = YEA;
+    
+    if(currpnt > i) {
+	indigestion(1);
+	return;
+    }
+    if(currpnt < i && !insert_character) {
+	p->data[currpnt++] = ch;
+	/* Thor: ansi �s��, �i�Hoverwrite, ���\�� ansi code */
+	if(my_ansimode)
+	    currpnt = ansi2n(n2ansi(currpnt, p),p);
+    } else {
+	while(i >= currpnt) {
+	    p->data[i + 1] = p->data[i];
+	    i--;
+	}
+	p->data[currpnt++] = ch;
+	i = ++(p->len);
+    }
+    if(i < WRAPMARGIN)
+	return;
+    s = p->data + (i - 1);
+    while(s != p->data && *s == ' ')
+	s--;
+    while(s != p->data && *s != ' ')
+	s--;
+    if(s == p->data) {
+	wordwrap = NA;
+	s = p->data + (i - 2);
+    }
+    split(p, (s - p->data) + 1);
+    p = p->next;
+    i = p->len;
+    if(wordwrap && i >= 1) {
+	if(p->data[i - 1] != ' ') {
+	    p->data[i] = ' ';
+	    p->data[i + 1] = '\0';
+	    p->len++;
+	}
+    }
+}
+
+static void insert_string(char *str) {
+    int ch;
+    
+    while((ch = *str++)) {
+	if(isprint2(ch) || ch == '\033')
+	    insert_char(ch);
+	else if(ch == '\t') {
+	    do {
+		insert_char(' ');
+	    } while(currpnt & 0x7);
+	} else if(ch == '\n')
+	    split(currline, currpnt);
+    }
+}
+
+static int undelete_line() {
+    textline_t* p = deleted_lines;
+    textline_t* currline0 = currline;
+    textline_t* top_of_win0 = top_of_win;
+    int currpnt0 = currpnt;
+    int currln0 = currln;
+    int curr_window_line0 = curr_window_line;
+    int indent_mode0 = indent_mode;
+
+    if(!deleted_lines)
+	return 0;
+    
+    indent_mode = 0;
+    insert_string(deleted_lines->data);
+    indent_mode = indent_mode0;
+    deleted_lines = deleted_lines->prev;
+    free(p);
+    
+    currline = currline0;
+    top_of_win = top_of_win0;
+    currpnt = currpnt0;
+    currln = currln0;
+    curr_window_line = curr_window_line0;
+    return 0;
+}
+
+/*
+  1) lines were joined and one was deleted
+  2) lines could not be joined
+  3) next line is empty
+  returns false if:
+  1) Some of the joined line wrapped
+*/
+static int join(textline_t *line) {
+    register textline_t *n;
+    register int ovfl;
+    
+    if(!(n = line->next))
+	return YEA;
+    if(!*killsp(n->data))
+	return YEA;
+    
+    ovfl = line->len + n->len - WRAPMARGIN;
+    if(ovfl < 0) {
+	strcat(line->data, n->data);
+	line->len += n->len;
+	delete_line(n);
+	return YEA;
+    } else {
+	register char *s;
+	
+	s = n->data + n->len - ovfl - 1;
+	while(s != n->data && *s == ' ')
+	    s--;
+	while(s != n->data && *s != ' ')
+	    s--;
+	if(s == n->data)
+	    return YEA;
+	split(n, (s - n->data) + 1);
+	if(line->len + n->len >= WRAPMARGIN) {
+	    indigestion(0);
+	    return YEA;
+	}
+	join(line);
+	n = line->next;
+	ovfl = n->len - 1;
+	if(ovfl >= 0 && ovfl < WRAPMARGIN - 2) {
+	    s = &(n->data[ovfl]);
+	    if(*s != ' ') {
+		strcpy(s, " ");
+		n->len++;
+	    }
+	}
+	return NA;
+    }
+}
+
+static void delete_char() {
+    register int len;
+    
+    if((len = currline->len)) {
+	register int i;
+	register char *s;
+	
+	if(currpnt >= len) {
+	    indigestion(1);
+	    return;
+	}
+	for(i = currpnt, s = currline->data + i; i != len; i++, s++)
+	    s[0] = s[1];
+	currline->len--;
+    }
+}
+
+static void load_file(FILE *fp) {
+    int indent_mode0 = indent_mode;
+
+    indent_mode = 0;
+    while(fgets(line, WRAPMARGIN + 2, fp))
+	insert_string(line);
+    fclose(fp);
+    indent_mode = indent_mode0;
+}
+
+/* �Ȧs�� */
+char *ask_tmpbuf(int y) {
+    static char fp_buf[10] = "buf.0";
+    static char msg[] = "�п�ܼȦs�� (0-9)[0]: ";
+    
+    msg[19] = fp_buf[4];
+    do {
+	if(!getdata(y, 0, msg, fp_buf + 4, 4, DOECHO))
+	    fp_buf[4] = msg[19];
+    } while(fp_buf[4] < '0' || fp_buf[4] > '9');
+    return fp_buf;
+}
+
+static void read_tmpbuf(int n) {
+    FILE *fp;
+    char fp_tmpbuf[80];
+    char tmpfname[] = "buf.0";
+    char *tmpf;
+    char ans[4] = "y";
+    
+    if(0 <= n && n <= 9) {
+	tmpfname[4] = '0' + n;
+	tmpf = tmpfname;
+    } else {
+	tmpf = ask_tmpbuf(3);
+	n = tmpf[4] - '0';
+    }
+    
+    setuserfile(fp_tmpbuf, tmpf);
+    if(n != 0 && n != 5 && more(fp_tmpbuf, NA) != -1)
+	getdata(b_lines - 1, 0, "�T�wŪ�J��(Y/N)?[Y]", ans, 4, LCECHO);
+    if(*ans != 'n' && (fp = fopen(fp_tmpbuf, "r"))) {
+	prevln = currln;
+	prevpnt = currpnt;
+	load_file(fp);
+	while(curr_window_line >= b_lines) {
+	    curr_window_line--;
+	    top_of_win = top_of_win->next;
+	}
+    }
+}
+
+static void write_tmpbuf() {
+    FILE *fp;
+    char fp_tmpbuf[80], ans[4];
+    textline_t *p;
+
+    setuserfile(fp_tmpbuf, ask_tmpbuf(3));
+    if(dashf(fp_tmpbuf)) {
+	more(fp_tmpbuf, NA);
+	getdata(b_lines - 1, 0, "�Ȧs�ɤw����� (A)���[ (W)�мg (Q)�����H[A] ",
+		ans, 4, LCECHO);
+	
+	if(ans[0] == 'q')
+	    return;
+    }
+    
+    if((fp = fopen(fp_tmpbuf, (ans[0] == 'w' ? "w" : "a+")))) {
+	for(p = firstline; p; p = p->next) {
+            if(p->next || p->data[0])
+		fprintf(fp, "%s\n", p->data);
+	}
+	fclose(fp);
+    }
+}
+
+static void erase_tmpbuf() {
+    char fp_tmpbuf[80];
+    char ans[4] = "n";
+    
+    setuserfile(fp_tmpbuf, ask_tmpbuf(3));
+    if(more(fp_tmpbuf, NA) != -1)
+	getdata(b_lines - 1, 0, "�T�w�R����(Y/N)?[N]",  ans, 4, LCECHO);
+    if(*ans == 'y')
+	unlink(fp_tmpbuf);
+}
+
+/* �s�边�۰ʳƥ� */
+void auto_backup() {
+    if(currline) {
+	FILE *fp;
+	textline_t *p, *v;
+	char bakfile[64];
+	int count = 0;
+	
+	setuserfile(bakfile, fp_bak);
+	if((fp = fopen(bakfile, "w"))) {
+	    for(p = firstline; p != NULL  && count < 512; p = v,count++) {
+		v = p->next;
+		fprintf(fp, "%s\n", p->data);
+		free(p);
+	    }
+	    fclose(fp);
+	}
+	currline = NULL;
+    }
+}
+
+void restore_backup() {
+    char bakfile[80], buf[80];
+    
+    setuserfile(bakfile, fp_bak);
+    if(dashf(bakfile)) {
+	stand_title("�s�边�۰ʴ_��");
+	getdata(1, 0, "�z���@�g�峹�|�������A(S)�g�J�Ȧs�� (Q)��F�H[S] ",
+		buf, 4, LCECHO);
+	if(buf[0] != 'q') {
+	    setuserfile(buf, ask_tmpbuf(3));
+	    Rename(bakfile, buf);
+	} else
+	    unlink(bakfile);
+    }
+}
+
+/* �ޥΤ峹 */
+static int garbage_line(char *str) {
+    int qlevel = 0;
+    
+    while(*str == ':' || *str == '>') {
+	if(*(++str) == ' ')
+	    str++;
+	if(qlevel++ >= 1)
+	    return 1;
+    }
+    while(*str == ' ' || *str == '\t')
+	str++;
+    if(qlevel >= 1) {
+	if(!strncmp(str, "�� ", 3) || !strncmp(str, "==>", 3) ||
+	   strstr(str, ") ����:\n"))
+	    return 1;
+    }
+    return (*str == '\n');
+}
+
+static void do_quote() {
+    int op;
+    char buf[256];
+
+    getdata(b_lines - 1, 0, "�аݭn�ޥέ���(Y/N/All/Repost)�H[Y] ",
+	    buf, 3, LCECHO);
+    op = buf[0];
+    
+    if(op != 'n') {
+	FILE *inf;
+	
+	if((inf = fopen(quote_file, "r"))) {
+	    char *ptr;
+	    int indent_mode0 = indent_mode;
+	    
+	    fgets(buf, 256, inf);
+	    if((ptr = strrchr(buf, ')')))
+		ptr[1] = '\0';
+	    else if((ptr = strrchr(buf, '\n')))
+		ptr[0] = '\0';
+	    
+	    if((ptr = strchr(buf, ':'))) {
+		char *str;
+		
+		while(*(++ptr) == ' ');
+
+		/* ����o�ϡA���o author's address */
+		if((curredit & EDIT_BOTH) && (str = strchr(quote_user, '.'))) {
+		    strcpy(++str, ptr);
+		    str = strchr(str, ' ');
+		    str[0] = '\0';
+		}
+	    } else
+		ptr = quote_user;
+	    
+	    indent_mode = 0;
+	    insert_string("�� �ޭz�m");
+	    insert_string(ptr);
+	    insert_string("�n���ʨ��G\n");
+	    
+	    if(op != 'a')           /* �h�� header */
+		while(fgets(buf, 256, inf) && buf[0] != '\n');
+
+	    if(op == 'a')
+		while(fgets(buf, 256, inf)) {
+		    insert_char(':');
+		    insert_char(' ');
+		    insert_string(Ptt_prints(buf,STRIP_ALL));
+		}
+	    else if(op == 'r')
+		while(fgets(buf, 256, inf))
+		    insert_string(Ptt_prints(buf,NO_RELOAD));
+	    else {
+		if(curredit & EDIT_LIST)       /* �h�� mail list �� header */
+		    while (fgets(buf, 256, inf) && (!strncmp(buf, "�� ", 3)));
+		while(fgets(buf, 256, inf)) {
+		    if(!strcmp(buf, "--\n"))
+			break;
+		    if(!garbage_line(buf)) {
+			insert_char(':');
+			insert_char(' ');
+			insert_string(Ptt_prints(buf,STRIP_ALL));
+		    }
+		}
+	    }
+	    indent_mode = indent_mode0;
+	    fclose(inf);
+	}
+    }
+}
+
+/* �f�d user �ި����ϥ� */
+static int check_quote() {
+    register textline_t *p = firstline;
+    register char *str;
+    int post_line;
+    int included_line;
+    
+    post_line = included_line = 0;
+    while(p) {
+	if(!strcmp(str = p->data, "--"))
+	    break;
+	if(str[1] == ' ' && ((str[0] == ':') || (str[0] == '>')))
+	    included_line++;
+	else {
+	    while(*str == ' ' || *str == '\t')
+		str++;
+	    if(*str)
+		post_line++;
+	}
+	p = p->next;
+    }
+    
+    if((included_line >> 2) > post_line) {
+	move(4, 0);
+	outs("���g�峹���ި���ҶW�L 80%�A�бz���ǷL���ץ��G\n\n"
+	     "\033[1;33m1) �W�[�@�Ǥ峹 ��  2) �R�������n���ި�\033[m");
+	{
+	    char ans[4];
+	    
+	    getdata(12, 12, "(E)�~��s�� (W)�j��g�J�H[E] ", ans, 4, LCECHO);
+	    if(ans[0] == 'w')
+		return 0;
+	}
+	return 1;
+    }
+    return 0;
+}
+
+/* �ɮ׳B�z�GŪ�ɡB�s�ɡB���D�Bñ�W�� */
+static void read_file(char *fpath) {
+    FILE *fp;
+    
+    if((fp = fopen(fpath, "r")) == NULL) {
+	if((fp = fopen(fpath, "w+"))) {
+	    fclose(fp);
+	    return;
+	}
+	indigestion(4);
+	abort_bbs(0);
+    }
+    load_file(fp);
+}
+
+extern userec_t cuser;
+
+void write_header(FILE *fp) {
+    time_t now = time(0);
+
+    if(curredit & EDIT_MAIL || curredit & EDIT_LIST) {
+	fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid,
+#if defined(REALINFO) && defined(MAIL_REALNAMES)
+		cuser.realname
+#else
+		cuser.username
+#endif
+	    );
+    } else {
+	char *ptr;
+	struct {
+	    char author[IDLEN + 1];
+	    char board[IDLEN + 1];
+	    char title[66];
+	    time_t date;              /* last post's date */
+	    int number;               /* post number */
+	} postlog;
+	
+	strcpy(postlog.author, cuser.userid);
+	ifuseanony=0;
+#ifdef HAVE_ANONYMOUS
+	if(currbrdattr& BRD_ANONYMOUS) {   
+	    int defanony = (currbrdattr & BRD_DEFAULTANONYMOUS);
+	    if(defanony) 
+		getdata(3, 0, "�п�J�A�Q�Ϊ�ID�A�]�i������[Enter]�A"
+			"�άO��[r]�ίu�W�G", real_name, 12, DOECHO);
+	    else
+		getdata(3, 0, "�п�J�A�Q�Ϊ�ID�A�]�i������[Enter]�ϥέ�ID�G",
+			real_name, 12, DOECHO);
+	    if(!real_name[0] && defanony) {
+		strcpy(real_name, "Anonymous");
+		strcpy(postlog.author, real_name);
+		ifuseanony = 1;
+	    } else {
+		if(!strcmp("r",real_name) || (!defanony && !real_name[0]))
+		    sprintf(postlog.author,"%s",cuser.userid);
+		else {
+		    sprintf(postlog.author,"%s.",real_name);
+		    ifuseanony=1;
+		}
+	    }
+	}
+#endif
+	strcpy(postlog.board, currboard);
+	ptr = save_title;
+	if(!strncmp(ptr, str_reply, 4))
+	    ptr += 4;
+	strncpy(postlog.title, ptr, 65);
+	postlog.date = now;
+	postlog.number = 1;
+	append_record(".post", (fileheader_t *)&postlog, sizeof(postlog));
+#ifdef HAVE_ANONYMOUS
+	if(currbrdattr & BRD_ANONYMOUS) {
+	    int defanony = (currbrdattr & BRD_DEFAULTANONYMOUS);
+	    
+	    fprintf(fp, "%s %s (%s) %s %s\n", str_author1, postlog.author ,
+		    (((!strcmp(real_name,"r") && defanony) ||
+		      (!real_name[0] && (!defanony))) ? cuser.username :
+		     "�q�q�ڬO�� ? ^o^"), 
+		    local_article ? str_post2 : str_post1, currboard);
+	} else {
+	    fprintf(fp, "%s %s (%s) %s %s\n", str_author1, cuser.userid,
+#if defined(REALINFO) && defined(POSTS_REALNAMES)
+		    cuser.realname,
+#else
+		    cuser.username,
+#endif
+		    local_article ? str_post2 : str_post1, currboard);
+	}
+#else   /* HAVE_ANONYMOUS */
+	fprintf(fp, "%s %s (%s) %s %s\n", str_author1, cuser.userid,
+#if defined(REALINFO) && defined(POSTS_REALNAMES)
+		cuser.realname,
+#else
+		cuser.username,
+#endif
+		local_article ? str_post2 : str_post1, currboard);
+#endif  /* HAVE_ANONYMOUS */
+
+    }
+    save_title[72] = '\0';
+    fprintf(fp, "���D: %s\n�ɶ�: %s\n", save_title, ctime(&now));
+}
+
+void addsignature(FILE *fp, int ifuseanony) {
+    FILE *fs;
+    int i;
+    char buf[WRAPMARGIN + 1];
+    char fpath[STRLEN];
+
+    static char msg[] = "�п��ñ�W�� (1-9, 0=���[)[0]: ";
+    char ch;
+
+    if(!strcmp(cuser.userid,STR_GUEST)) {
+	fprintf(fp, "\n--\n�� �o�H�� :" BBSNAME "(" MYHOSTNAME
+		") \n�� From: %s\n", fromhost);
+	return;
+    }
+    if(!ifuseanony) {
+	i = showsignature(fpath);
+	msg[27] = ch = '0' | (cuser.uflag & SIG_FLAG);
+	getdata(0, 0, msg, buf, 4, DOECHO);
+	
+	if(ch != buf[0] && buf[0] >= '0' && buf[0] <= '9') {
+	    ch = buf[0];
+	    cuser.uflag = (cuser.uflag & ~SIG_FLAG) | (ch & SIG_FLAG);
+	}
+	
+	if(ch != '0') {
+	    fpath[i] = ch;
+	    if((fs = fopen(fpath, "r"))) {
+		fputs("\n--\n", fp);
+		for(i = 0; i < MAX_SIGLINES &&
+			fgets(buf, sizeof(buf), fs); i++)
+		    fputs(buf, fp);
+		fclose(fs);
+	    }
+	}
+    }
+#ifdef HAVE_ORIGIN
+#ifdef HAVE_ANONYMOUS
+    if(ifuseanony)
+	fprintf(fp, "\n--\n�� �o�H��: " BBSNAME "(" MYHOSTNAME
+		") \n�� From: %s\n", "�ʦW�ѨϪ��a");
+    else {
+	char temp[32];
+	
+	strncpy(temp, fromhost, 31);
+	temp[32] = '\0';
+	fprintf(fp, "\n--\n�� �o�H��: " BBSNAME "(" MYHOSTNAME
+		") \n�� From: %s\n", temp);
+    }
+#else
+    strncpy (temp,fromhost,15);
+    fprintf(fp, "\n--\n�� �o�H��: " BBSNAME "(" MYHOSTNAME
+	    ") \n�� From: %s\n", temp);
+#endif
+#endif
+}
+
+static int
+write_file(char *fpath, int saveheader, int *islocal) {
+    time_t now;
+    struct tm *ptime;
+    FILE *fp = NULL;
+    textline_t *p, *v;
+    char ans[TTLEN], *msg;
+    int aborted = 0, line = 0, checksum[3], sum = 0, po = 1;
+
+    stand_title("�ɮ׳B�z");
+    if(currstat == SMAIL)
+	msg = "[S]�x�s (A)��� (T)����D (E)�~�� (R/W/D)Ū�g�R�Ȧs�ɡH";
+    else if(local_article)
+	msg = "[L]�����H�� (S)�x�s (A)��� (T)����D (E)�~�� "
+	    "(R/W/D)Ū�g�R�Ȧs�ɡH";
+    else
+	msg = "[S]�x�s (L)�����H�� (A)��� (T)����D (E)�~�� "
+	    "(R/W/D)Ū�g�R�Ȧs�ɡH";
+    getdata(1, 0, msg, ans, 3, LCECHO);
+    
+    switch(ans[0]) {
+    case 'a':
+	outs("�峹\033[1m �S�� \033[m�s�J");
+	safe_sleep(1);
+	aborted = -1;
+	break;	
+    case 'r':
+	read_tmpbuf(-1);
+    case 'e':
+	return KEEP_EDITING;
+    case 'w':
+	write_tmpbuf();
+	return KEEP_EDITING;
+    case 'd':
+	erase_tmpbuf();
+	return KEEP_EDITING;
+    case 't':
+	move(3, 0);
+	prints("�¼��D�G%s", save_title);
+	strcpy(ans,save_title);
+	if(getdata_buf(4, 0, "�s���D�G", ans, TTLEN, DOECHO))
+	    strcpy(save_title, ans);
+	return KEEP_EDITING;
+    case 's':
+	if(!HAS_PERM(PERM_LOGINOK)) {
+	    local_article = 1;
+	    move(2, 0);
+	    prints("�z�|���q�L�����T�{�A�u�� Local Save�C\n");
+	    pressanykey();
+	} else
+	    local_article = 0;
+	break;
+    case 'l':
+	local_article = 1;
+    }
+
+    if(!aborted) {
+	if(saveheader && !(curredit & EDIT_MAIL) && check_quote())
+	    return KEEP_EDITING;
+	
+	if(!*fpath) {
+	    sethomepath(fpath, cuser.userid);
+	    strcpy(fpath, tempnam(fpath, "ve_"));
+	}
+	
+	if((fp = fopen(fpath, "w")) == NULL) {
+	    indigestion(5);
+	    abort_bbs(0);
+	}
+	if(saveheader)
+	    write_header(fp);
+    }
+
+    for(p = firstline; p; p = v) {
+	v = p->next;
+	if(!aborted) {
+	    msg = p->data;
+	    if(v || msg[0]) {
+		trim(msg);
+		
+		line++;
+		if(currstat == POSTING && po) {
+		    saveheader = str_checksum(msg);
+		    if(saveheader) {
+			if(postrecord.checksum[po] == saveheader) {
+			    po++;
+			    if(po > 3) {
+				postrecord.times++;
+				po =0;
+			    }
+			} else
+			    po = 1;
+			if(currstat == POSTING && line >= totaln/2 &&
+			   sum < 3) {
+			    checksum[sum++] = saveheader;
+			}
+		    }
+		}
+#ifdef SUPPORT_GB
+		if(current_font_type == TYPE_GB)
+                 {
+		  fprintf(fp, "%s\n", hc_convert_str(msg, HC_GBtoBIG, HC_DO_SINGLE));
+                 }     
+		else
+#endif
+ 		  fprintf(fp, "%s\n", msg);
+	    }
+	}
+	free(p);
+    }
+    currline = NULL;
+    
+    if(postrecord.times > MAX_CROSSNUM - 1)
+	anticrosspost();
+    
+    if(po && sum == 3) {
+	memcpy(&postrecord.checksum[1], checksum, sizeof(int) * 3);
+	postrecord.times  =0;
+    }
+    if(!aborted) {
+	if(islocal)
+	    *islocal = (local_article == 1);
+	if(currstat == POSTING || currstat == SMAIL)
+	    addsignature(fp,ifuseanony);
+        else if(currstat == REEDIT
+#ifndef ALL_REEDIT_LOG
+		&& strcmp(currboard, "SYSOP") == 0
+#endif
+		)
+	  {
+	    time(&now);
+	    ptime = localtime(&now);
+            fprintf(fp,
+                "�� �s��: %-15s �Ӧ�: %-20s (%02d/%02d %02d:%02d)\n",
+		  cuser.userid, fromhost,
+		  ptime->tm_mon+1,ptime->tm_mday,ptime->tm_hour,ptime->tm_min);
+	  }
+	    
+	fclose(fp);
+	if(local_article && (currstat == POSTING))
+	    return 0;
+	return 0;
+    }
+    return aborted;
+}
+
+
+static void display_buffer() {
+    register textline_t *p;
+    register int i;
+    int inblock;
+    char buf[WRAPMARGIN + 2];
+    int min, max;
+    
+    if(currpnt > blockpnt) {
+	min = blockpnt;
+	max = currpnt;
+    } else {
+	min = currpnt;
+	max = blockpnt;
+    }
+
+    for(p = top_of_win, i = 0; i < b_lines; i++) {
+	move(i, 0);
+	clrtoeol();
+	if(blockln >= 0 &&
+	   ((blockln <= currln && blockln <= (currln - curr_window_line + i) &&
+            (currln - curr_window_line + i) <= currln) ||
+	    (currln <= (currln - curr_window_line + i) &&
+            (currln - curr_window_line + i) <= blockln))) {
+	    outs("\033[7m");
+	    inblock = 1;
+	} else
+	    inblock = 0;
+	if(p) {
+	    if(my_ansimode)
+		if(currln == blockln && p == currline && max > min) {
+		    outs("\033[m");
+		    strncpy(buf, p->data, min);
+		    buf[min] = 0;
+		    outs(buf);
+		    outs("\033[7m");
+		    strncpy(buf, p->data + min, max - min);
+		    buf[max - min] = 0;
+		    outs(buf);
+		    outs("\033[m");
+		    outs(p->data + max);
+		} else
+		    outs(p->data);
+	    else if(currln == blockln && p == currline && max > min) {
+		outs("\033[m");
+		    strncpy(buf, p->data, min);
+		    buf[min] = 0;
+		    edit_outs(buf);
+		    outs("\033[7m");
+		    strncpy(buf, p->data + min, max - min);
+		    buf[max - min] = 0;
+		    edit_outs(buf);
+		    outs("\033[m");
+		    edit_outs(p->data + max);
+		} else
+		    edit_outs(&p->data[edit_margin]);
+	    p = p->next;
+	    if(inblock)
+		outs("\033[m");
+	} else
+	    outch('~');
+    }
+    edit_msg();
+}
+
+static void goto_line(int lino) {
+    char buf[10];
+    
+    if(lino > 0 ||
+       (getdata(b_lines - 1, 0, "���ܲĴX��:", buf, 10, DOECHO) &&
+	sscanf(buf, "%d", &lino) && lino > 0)) {
+	textline_t* p;
+	
+	prevln = currln;
+	prevpnt = currpnt;
+	p = firstline;
+	currln = lino - 1;
+	
+	while(--lino && p->next)
+	    p = p->next;
+	
+	if(p)
+	    currline = p;
+	else {
+	    currln = totaln;
+	    currline = lastline;
+	}
+	currpnt = 0;
+	if(currln < 11) {
+	    top_of_win = firstline;
+	    curr_window_line = currln;
+	} else {
+	    int i;
+
+	    curr_window_line = 11;
+	    for(i = curr_window_line; i; i--)
+		p = p->prev;
+            top_of_win = p;
+	}
+    }
+    redraw_everything = YEA;
+}
+
+char *strcasestr(const char* big, const char* little) {
+    char* ans = (char*)big;
+    int len = strlen(little);
+    char* endptr = (char*)big + strlen(big) - len;
+    
+    while(ans <= endptr)
+	if(!strncasecmp(ans, little, len))
+	    return ans;
+	else
+	    ans++;
+    return 0;
+}
+
+/*
+  mode:
+  0: prompt
+  1: forward
+  -1: backward
+*/
+static void search_str(int mode) {
+    static char str[80];
+    typedef char* (*FPTR)();
+    static FPTR fptr;
+    char ans[4] = "n";
+    
+    if(!mode) {
+	if(getdata_buf(b_lines - 1, 0,"[�j�M]����r:",str, 65, DOECHO))
+	    if(*str) {
+		if(getdata(b_lines - 1, 0, "�Ϥ��j�p�g(Y/N/Q)? [N] ",
+			   ans, 4, LCECHO) && *ans == 'y')
+		    fptr = strstr;
+		else
+		    fptr = strcasestr;
+	    }
+    }
+    
+    if(*str && *ans != 'q') {
+	textline_t* p;
+	char *pos = NULL;
+	int lino;
+	
+	if(mode >= 0) {
+	    for(lino = currln, p = currline; p; p = p->next, lino++)
+		if((pos = fptr(p->data + (lino == currln ? currpnt + 1 : 0),
+			       str)) && (lino != currln ||
+					 pos - p->data != currpnt))
+		    break;
+	} else {
+	    for(lino = currln, p = currline; p; p = p->prev, lino--)
+		if((pos = fptr(p->data, str)) &&
+		   (lino != currln || pos - p->data != currpnt))
+		    break;
+	}
+	if(pos) {
+	    prevln = currln;
+	    prevpnt = currpnt;
+	    currline = p;
+	    currln = lino;
+	    currpnt = pos - p->data;
+	    if(lino < 11) {
+		top_of_win = firstline;
+		curr_window_line = currln;
+	    } else {
+		int i;
+
+		curr_window_line = 11;
+		for(i = curr_window_line; i; i--)
+		    p = p->prev;
+		top_of_win = p;
+	    }
+	    redraw_everything = YEA;
+	}
+    }
+    if(!mode)
+	redraw_everything = YEA;
+}
+
+static void match_paren() {
+    static char parens[] = "()[]{}";
+    int type;
+    int parenum = 0;
+    char *ptype;
+    textline_t* p;
+    int lino;
+    int c, i = 0;
+
+    if(!(ptype = strchr(parens, currline->data[currpnt])))
+	return;
+    
+    type = (ptype - parens) / 2;
+    parenum += ((ptype - parens) % 2) ? -1 : 1;
+    
+    if(parenum > 0) {
+	for(lino = currln, p = currline; p; p = p->next, lino++) {
+	    lino = lino;
+	    for(i = (lino == currln) ? currpnt + 1 : 0;
+		i < strlen(p->data); i++)
+		if(p->data[i] == '/' && p->data[++i] == '*') {
+		    ++i;
+		    while(1) {
+			while(i < strlen(p->data) - 1 &&
+			      !(p->data[i] == '*' && p->data[i + 1] == '/'))
+			    i++;
+			if(i >= strlen(p->data) - 1 && p->next) {
+			    p = p->next;
+			    ++lino;
+			    i = 0;
+			} else
+			    break;
+		    }
+		} else if((c = p->data[i]) == '\'' || c == '"') {
+		    while(1) {
+			while(i < (int)(strlen(p->data) - 1))
+			    if(p->data[++i] == '\\' && i < strlen(p->data) - 2)
+				++i;
+			    else if(p->data[i] == c)
+				goto end_quote;
+			if(i >= strlen(p->data) - 1 && p->next) {
+			    p = p->next;
+			    ++lino;
+			    i = -1;
+			} else
+			    break;
+		    }
+end_quote:
+		    ;
+		} else if((ptype = strchr(parens, p->data[i])) &&
+			  (ptype - parens) / 2 == type)
+		    if(!(parenum += ((ptype - parens) % 2) ? -1 : 1))
+			goto p_outscan;
+	}
+    } else {
+	for(lino = currln, p = currline; p; p = p->prev, lino--)
+	    for(i = (lino == currln) ? currpnt - 1 : strlen(p->data) - 1;
+		 i >= 0; i--)
+		if(p->data[i] == '/' && p->data[--i] == '*' && i > 0) {
+		    --i;
+		    while(1) {
+			while(i > 0 &&
+			      !(p->data[i] == '*' && p->data[i - 1] == '/'))
+			    i--;
+			if(i <= 0 && p->prev) {
+			    p = p->prev;
+			    --lino;
+			    i = strlen(p->data) - 1;
+			} else
+			    break;
+		    }
+		} else if((c = p->data[i]) == '\'' || c == '"') {
+		    while(1) {
+			while(i > 0)
+			    if(i > 1 && p->data[i - 2] == '\\')
+				i -= 2;
+			    else if((p->data[--i]) == c)
+				goto begin_quote;
+			if(i <= 0 && p->prev) {
+			    p = p->prev;
+			    --lino;
+			    i = strlen(p->data);
+			} else
+			    break;
+		    }
+begin_quote:
+		    ;
+		} else if((ptype = strchr(parens, p->data[i])) &&
+			  (ptype - parens) / 2 == type)
+		    if(!(parenum += ((ptype - parens) % 2) ? -1 : 1))
+			goto p_outscan;
+    }
+p_outscan:
+    if(!parenum) {
+	int top = currln - curr_window_line;
+	int bottom = currln - curr_window_line + b_lines - 1;
+	
+	currpnt = i;
+	currline = p;
+	curr_window_line += lino - currln;
+	currln = lino;
+	
+	if(lino < top || lino > bottom) {
+	    if(lino < 11) {
+		top_of_win = firstline;
+		curr_window_line = currln;
+	    } else {
+		int i;
+
+		curr_window_line = 11;
+		for(i = curr_window_line; i; i--)
+		    p = p->prev;
+		top_of_win = p;
+	    }
+	    redraw_everything = YEA;
+	}
+    }
+}
+
+static void block_del(int hide) {
+    if(blockln < 0) {
+	blockln = currln;
+	blockpnt = currpnt;
+	blockline = currline;
+    } else {
+	char fp_tmpbuf[80];
+	FILE* fp;
+	textline_t *begin, *end, *p;
+	char tmpfname[10] = "buf.0";
+	char ans[6] = "w+n";
+
+	move(b_lines - 1, 0);
+	clrtoeol();
+	if(hide == 1)
+	    tmpfname[4] = 'q';
+	else if(!hide && !getdata(b_lines - 1, 0, "��϶����ܼȦs�� "
+				  "(0:Cut, 5:Copy, 6-9, q: Cancel)[0] ",
+				  tmpfname + 4, 4, LCECHO))
+	    tmpfname[4] = '0';
+	if(tmpfname[4] < '0' || tmpfname[4] > '9')
+	    tmpfname[4] = 'q';
+	if('1' <= tmpfname[4] && tmpfname[4] <= '9') {
+	    setuserfile(fp_tmpbuf, tmpfname);
+	    if(tmpfname[4] != '5' && dashf(fp_tmpbuf)) {
+		more(fp_tmpbuf, NA);
+		getdata(b_lines - 1, 0, "�Ȧs�ɤw����� (A)���[ (W)�мg "
+			"(Q)�����H[W] ", ans, 4, LCECHO);
+		if(*ans == 'q')
+		    tmpfname[4] = 'q';
+		else if(*ans != 'a')
+		    *ans = 'w';
+	    }
+	    if(tmpfname[4] != '5') {
+		getdata(b_lines - 1, 0, "�R���϶�(Y/N)?[N] ",
+			ans + 2, 4, LCECHO);
+		if(ans[2] != 'y')
+		    ans[2] = 'n';
+	    }
+	} else if(hide != 3)
+	    ans[2] = 'y';
+	
+	tmpfname[5] = ans[1] = ans[3] = 0;
+	if(tmpfname[4] != 'q') {
+	    if(currln >= blockln) {
+		begin = blockline;
+		end = currline;
+		if(ans[2] == 'y' && !(begin == end && currpnt != blockpnt)) {
+		    curr_window_line -= (currln - blockln);
+		    if(curr_window_line < 0) {
+			curr_window_line = 0;
+			if(end->next)
+			    (top_of_win = end->next)->prev = begin->prev;
+			else
+			    top_of_win = (lastline = begin->prev);
+		    }
+		    currln -= (currln - blockln);
+		}
+	    } else {
+		begin = currline;
+		end = blockline;
+	    }
+	    if(ans[2] == 'y' && !(begin == end && currpnt != blockpnt)) {
+		if(begin->prev)
+		    begin->prev->next = end->next;
+		else if(end->next)
+		    top_of_win = firstline = end->next;
+		else {
+		    currline = top_of_win = firstline =
+			lastline = alloc_line();
+		    currln = curr_window_line = edit_margin = 0;
+		}
+		
+		if(end->next)
+		    (currline = end->next)->prev = begin->prev;
+		else if(begin->prev) {
+		    currline = (lastline = begin->prev);
+		    currln--;
+		    if(curr_window_line > 0)
+			curr_window_line--;
+		}
+	    }
+	    
+	    setuserfile(fp_tmpbuf, tmpfname);
+	    if((fp = fopen(fp_tmpbuf, ans))) {
+		if(begin == end && currpnt != blockpnt) {
+		    char buf[WRAPMARGIN + 2];
+		    
+		    if(currpnt > blockpnt) {
+			strcpy(buf, begin->data + blockpnt);
+			buf[currpnt - blockpnt] = 0;
+		    } else {
+			strcpy(buf, begin->data + currpnt);
+			buf[blockpnt - currpnt] = 0;
+		    }
+		    fputs(buf, fp);
+		} else {
+		    for(p = begin; p != end; p = p->next)
+			fprintf(fp, "%s\n", p->data);
+		    fprintf(fp, "%s\n", end->data);
+		}
+		fclose(fp);
+	    }
+	    
+	    if(ans[2] == 'y') {
+		if(begin == end && currpnt != blockpnt) {
+		    int min, max;
+		    
+		    if(currpnt > blockpnt) {
+			min = blockpnt;
+			max = currpnt;
+		    } else {
+			min = currpnt;
+			max = blockpnt;
+		    }
+		    strcpy(begin->data + min, begin->data + max);
+		    begin->len -= max - min;
+		    currpnt = min;
+		} else {
+		    for(p = begin; p != end; totaln--)
+			free((p = p->next)->prev);
+		    free(end);
+		    totaln--;
+		    currpnt = 0;
+		}
+	    }
+	}
+	blockln = -1;
+	redraw_everything = YEA;
+    }
+}
+
+static void block_shift_left() {
+    textline_t *begin, *end, *p;
+    
+    if(currln >= blockln) {
+	begin = blockline;
+	end = currline;
+    } else {
+	begin = currline;
+	end = blockline;
+    }
+    p = begin;
+    while(1) {
+	if(p->len) {
+	    strcpy(p->data, p->data + 1);
+	    --p->len;
+	}
+	if(p == end)
+	    break;
+	else
+	    p = p->next;
+    }
+    if(currpnt > currline->len)
+	currpnt = currline->len;
+    redraw_everything = YEA;
+}
+
+static void block_shift_right() {
+    textline_t *begin, *end, *p;
+
+    if(currln >= blockln) {
+	begin = blockline;
+	end = currline;
+    } else {
+	begin = currline;
+	end = blockline;
+    }
+    p = begin;
+    while(1) {
+	if(p->len < WRAPMARGIN) {
+	    int i = p->len + 1;
+	    
+	    while(i--)
+		p->data[i + 1] = p->data[i];
+	    p->data[0] = insert_character ? ' ' : insert_c;
+	    ++p->len;
+	}
+	if(p == end)
+	    break;
+	else
+	    p = p->next;
+    }
+    if(currpnt > currline->len)
+	currpnt = currline->len;
+    redraw_everything = YEA;
+}
+
+static void transform_to_color(char* line) {
+    while(line[0] && line[1])
+	if(line[0] == '*' && line[1] == '[') {
+	    line[0] = KEY_ESC;
+	    line += 2;
+	} else
+	    ++line;
+}
+
+static void block_color() {
+    textline_t *begin, *end, *p;
+    
+    if(currln >= blockln) {
+	begin = blockline;
+	end = currline;
+    } else {
+	begin = currline;
+	end = blockline;
+    }
+    p = begin;
+    while(1) {
+	transform_to_color(p->data);
+	if(p == end)
+	    break;
+	else
+	    p = p->next;
+    }
+    block_del(1);
+}
+
+/* �s��B�z�G�D�{���B��L�B�z */
+int vedit(char *fpath, int saveheader, int *islocal) {
+    FILE *fp1;
+    char last = 0, buf[200];   /* the last key you press */
+    int ch, foo;
+    int lastindent = -1;
+    int last_margin;
+    int mode0 = currutmp->mode;
+    int destuid0 = currutmp->destuid;
+    unsigned int money=0;
+    unsigned short int interval=0;
+    time_t now=0,th;
+    
+    textline_t* firstline0 = firstline;
+    textline_t* lastline0 = lastline;
+    textline_t* currline0 = currline;
+    textline_t* blockline0 = blockline;
+    textline_t* top_of_win0 = top_of_win;
+    int local_article0 = local_article;
+    int currpnt0 = currpnt;
+    int currln0 = currln;
+    int totaln0 = totaln;
+    int curr_window_line0 = curr_window_line;
+    int insert_character0 = insert_character;
+    int my_ansimode0 = my_ansimode;
+    int edit_margin0 = edit_margin;
+    int blockln0 = blockln, count=0, tin=0;
+    
+    currutmp->mode = EDITING;
+    currutmp->destuid = currstat;
+    insert_character = redraw_everything = 1;
+    prevln = blockln = -1;
+    
+    line_dirty = currpnt = totaln = my_ansimode = 0;
+    currline = top_of_win = firstline = lastline = alloc_line();
+    
+    if(*fpath)
+	read_file(fpath);
+
+    if(*quote_file) {
+	do_quote();
+	*quote_file = '\0';
+	if(quote_file[79] == 'L')
+	    local_article = 1;
+    }
+    
+    currline = firstline;
+    currpnt = currln = curr_window_line = edit_margin = last_margin = 0;
+    
+    while(1) {
+	if(redraw_everything || blockln >= 0) {
+	    display_buffer();
+	    redraw_everything = NA;
+	}
+	if(my_ansimode)
+	    ch = n2ansi(currpnt, currline);
+	else
+	    ch = currpnt - edit_margin;
+	move(curr_window_line, ch);
+	if(!line_dirty && strcmp(line, currline->data))
+	    strcpy(line, currline->data);
+	ch = igetkey();
+												/* jochang debug */
+	if((interval = (unsigned short int)((th = currutmp->lastact) - now))) {
+	    now = th;
+	    if((char)ch != last) {
+		money++;
+		last = (char)ch;
+	    }
+        }
+	if(interval && interval == tin)
+	  count++;
+        else
+	 {
+	  count=0;
+	  tin = interval;
+	 }
+        /* �s��240��interval�@�� , �����O�b�İ] */
+	if(count >= 240) {
+	    sprintf(buf, "\033[1;33;46m%s\033[37m�b\033[37;45m%s"
+		    "\033[37m�O�H�k�ȿ� , %s\033[m", cuser.userid,
+		    currboard,ctime(&now));
+	    log_file ("etc/illegal_money",buf);
+	    money = 0 ;
+	    post_violatelaw(cuser.userid, "Ptt �t��ĵ��", "�H�k�ȿ�", "�������k�ұo");
+	    mail_violatelaw(cuser.userid, "Ptt �t��ĵ��", "�H�k�ȿ�", "�������k�ұo");
+//	    demoney(10000);
+//	    abort_bbs(0);
+	}
+
+	if(raw_mode)
+	    switch (ch) {
+	    case Ctrl('S'):
+	    case Ctrl('Q'):
+	    case Ctrl('T'):
+		continue;
+		break;
+	    }
+	if(ch < 0x100 && isprint2(ch)) {
+	    insert_char(ch);
+	    lastindent = -1;
+	    line_dirty = 1;
+	} else {
+	    if(ch == Ctrl('P') || ch == KEY_UP || ch == KEY_DOWN ||
+	       ch == Ctrl('N')) {
+		if(lastindent == -1)
+		    lastindent = currpnt;
+	    } else
+		lastindent = -1;
+	    if(ch == KEY_ESC)
+		switch(KEY_ESC_arg) {
+		case ',':
+		    ch = Ctrl(']');
+		    break;
+		case '.':
+		    ch = Ctrl('T');
+		    break;
+		case 'v':
+		    ch = KEY_PGUP;
+		    break;
+		case 'a':
+		case 'A':
+		    ch = Ctrl('V');
+		    break;
+		case 'X':
+		    ch = Ctrl('X');
+		    break;
+		case 'q':
+		    ch = Ctrl('Q');
+		    break;
+		case 'o':
+		    ch = Ctrl('O');
+		    break;
+		case '-':
+		    ch = Ctrl('_');
+		    break;
+		case 's':
+		    ch = Ctrl('S');
+		    break;
+		}
+
+	    switch(ch) {
+	    case Ctrl('X'):           /* Save and exit */
+		foo = write_file(fpath, saveheader, islocal);
+		if(foo != KEEP_EDITING) {
+		    currutmp->mode = mode0;
+		    currutmp->destuid = destuid0;
+		    firstline = firstline0;
+		    lastline = lastline0;
+		    currline = currline0;
+		    blockline = blockline0;
+		    top_of_win = top_of_win0;
+		    local_article = local_article0;
+		    currpnt = currpnt0;
+		    currln = currln0;
+		    totaln = totaln0;
+		    curr_window_line = curr_window_line0;
+		    insert_character = insert_character0;
+		    my_ansimode = my_ansimode0;
+		    edit_margin = edit_margin0;
+		    blockln = blockln0;
+		    if(!foo)
+			return money;
+		    else
+			return foo;
+		}
+		line_dirty = 1;
+		redraw_everything = YEA;
+		break;
+	    case Ctrl('W'):
+		if(blockln >= 0)
+		    block_del(2);
+		line_dirty = 1;
+		break;
+	    case Ctrl('Q'):           /* Quit without saving */
+		ch = ask("���������x�s (Y/N)? [N]: ");
+		if(ch == 'y' || ch == 'Y') {
+		    currutmp->mode = mode0;
+		    currutmp->destuid = destuid0;
+		    firstline = firstline0;
+		    lastline = lastline0;
+		    currline = currline0;
+		    blockline = blockline0;
+		    top_of_win = top_of_win0;
+		    local_article = local_article0;
+		    currpnt = currpnt0;
+		    currln = currln0;
+		    totaln = totaln0;
+		    curr_window_line = curr_window_line0;
+		    insert_character = insert_character0;
+		    my_ansimode = my_ansimode0;
+		    edit_margin = edit_margin0;
+		    blockln = blockln0;
+		    return -1;
+		}
+		line_dirty = 1;
+		redraw_everything = YEA;
+		break;
+	    case Ctrl('C'):
+		ch = insert_character;
+		insert_character = redraw_everything = YEA;
+		if(!my_ansimode)
+		    insert_string(reset_color);
+		else {
+		    char ans[4];
+		    move(b_lines - 2, 55);
+		    outs("\033[1;33;40mB\033[41mR\033[42mG\033[43mY\033[44mL"
+			 "\033[45mP\033[46mC\033[47mW\033[m");
+		    if(getdata(b_lines - 1, 0,
+			       "�п�J  �G��/�e��/�I��[���`�զr�©�][0wb]�G",
+			       ans, 4, LCECHO)) {
+			char t[] = "BRGYLPCW";
+			char color[15];
+			char *tmp, *apos = ans;
+			int fg, bg;
+			
+			strcpy(color, "\033[");
+			if(isdigit(*apos)) {
+			    sprintf(color, "%s%c", color, *(apos++));
+			    if(*apos)
+				sprintf(color, "%s;", color);
+			}
+			if(*apos) {
+			    if((tmp = strchr(t, toupper(*(apos++)))))
+				fg = tmp - t + 30;
+			    else
+				fg = 37;
+			    sprintf(color, "%s%d", color, fg);
+			}
+			if(*apos) {
+			    if((tmp = strchr(t, toupper(*(apos++)))))
+				bg = tmp - t + 40;
+			    else
+				bg = 40;
+			    sprintf(color, "%s;%d", color, bg);
+			}
+			sprintf(color, "%sm", color);
+			insert_string(color);
+		    } else
+			insert_string(reset_color);
+		}
+		insert_character = ch;
+		line_dirty = 1;
+		break;
+	    case KEY_ESC:
+		line_dirty = 0;
+		switch(KEY_ESC_arg) {
+		case 'U':
+		    t_users();
+		    redraw_everything = YEA;
+		    line_dirty = 1;
+		    break;
+		case 'i':
+		    t_idle();
+		    redraw_everything = YEA;
+		    line_dirty = 1;
+		    break;
+		case 'n':
+		    search_str(1);
+		    break;
+		case 'p':
+		    search_str(-1);
+		    break;
+		case 'L':
+		case 'J':
+		    goto_line(0);
+		    break;
+		case ']':
+		    match_paren();
+		    break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+		    read_tmpbuf(KEY_ESC_arg - '0');
+		    redraw_everything = YEA;
+		    break;
+		case 'l':                       /* block delete */
+		case ' ':
+		    block_del(0);
+		    line_dirty = 1;
+		    break;
+		case 'u':
+		    if(blockln >= 0)
+			block_del(1);
+		    line_dirty = 1;
+		    break;
+		case 'c':
+		    if(blockln >= 0)
+			block_del(3);
+		    line_dirty = 1;
+		    break;
+		case 'y':
+		    undelete_line();
+		    break;
+		case 'R':
+		    raw_mode ^= 1;
+		    line_dirty = 1;
+		    break;
+		case 'I':
+		    indent_mode ^= 1;
+		    line_dirty = 1;
+		    break;
+		case 'j':
+		    if(blockln >= 0)
+			block_shift_left();
+		    else if(currline->len) {
+			int currpnt0 = currpnt;
+			currpnt = 0;
+			delete_char();
+			currpnt = (currpnt0 <= currline->len) ? currpnt0 : 
+			    currpnt0 - 1;
+			if(my_ansimode)
+			    currpnt = ansi2n(n2ansi(currpnt, currline),
+					     currline);
+		    }
+		    line_dirty = 1;
+		    break;
+		case 'k':
+		    if(blockln >= 0)
+			block_shift_right();
+		    else {
+			int currpnt0 = currpnt;
+			
+			currpnt = 0;
+			insert_char(' ');
+			currpnt = currpnt0;
+		    }
+		    line_dirty = 1;
+		    break;
+		case 'f':
+		    while(currpnt < currline->len &&
+			  isalnum(currline->data[++currpnt]));
+		    while(currpnt < currline->len &&
+			  isspace(currline->data[++currpnt]));
+		    line_dirty = 1;
+		    break;
+		case 'b':
+		    while(currpnt && isalnum(currline->data[--currpnt]));
+		    while(currpnt && isspace(currline->data[--currpnt]));
+		    line_dirty = 1;
+		    break;
+		case 'd':
+		    while(currpnt < currline->len) {
+			delete_char();
+			if(!isalnum(currline->data[currpnt]))
+			    break;
+		    }
+		    while(currpnt < currline->len) {
+			delete_char();
+			if(!isspace(currline->data[currpnt]))
+			    break;
+		    }
+		    line_dirty = 1;
+		    break;
+		default:
+		    line_dirty = 1;
+		}
+		break;
+	    case Ctrl('_'):
+		if(strcmp(line, currline->data)) {
+		    char buf[WRAPMARGIN];
+		    
+		    strcpy(buf, currline->data);
+		    strcpy(currline->data, line);
+		    strcpy(line, buf);
+		    currline->len = strlen(currline->data);
+		    currpnt = 0;
+		    line_dirty = 1;
+		}
+		break;
+	    case Ctrl('S'):
+		search_str(0);
+		break;
+	    case Ctrl('U'):
+		insert_char('\033');
+		line_dirty = 1;
+		break;
+	    case Ctrl('V'):                   /* Toggle ANSI color */
+		my_ansimode ^= 1;
+		if(my_ansimode && blockln >= 0)
+		    block_color();
+		clear();
+		redraw_everything = YEA;
+		line_dirty = 1;
+		break;
+	    case Ctrl('I'):
+		do {
+		    insert_char(' ');
+		} while(currpnt & 0x7);
+		line_dirty = 1;
+		break;
+	    case '\r':
+	    case '\n':
+		split(currline, currpnt);
+		line_dirty = 0;
+		break;
+	    case Ctrl('G'):
+	    {
+		unsigned int currstat0 = currstat;
+		setutmpmode(EDITEXP);
+		a_menu("�s�軲�U��", "etc/editexp",
+		       (HAS_PERM(PERM_SYSOP) ? SYSOP : NOBODY));
+		currstat = currstat0;
+	    }
+	    if(trans_buffer[0]) {
+		if((fp1 = fopen(trans_buffer, "r"))) {
+		    int indent_mode0 = indent_mode;
+
+		    indent_mode = 0;
+		    prevln = currln;
+		    prevpnt = currpnt;
+		    while(fgets(line, WRAPMARGIN + 2, fp1)) {
+			if(!strncmp(line,"�@��:",5) ||
+			   !strncmp(line,"���D:",5) ||
+			   !strncmp(line,"�ɶ�:",5))
+			    continue;
+			insert_string(line);
+		    }
+		    fclose(fp1);
+		    indent_mode = indent_mode0;
+		    while(curr_window_line >= b_lines) {
+			curr_window_line--;
+			top_of_win = top_of_win->next;
+		    }
+		}
+	    }
+	    redraw_everything = YEA;
+	    line_dirty = 1;
+	    break;
+	    case Ctrl('Z'):  /* Help */
+		more("etc/ve.hlp",YEA);
+		redraw_everything = YEA;
+		line_dirty = 1;
+		break;
+	    case Ctrl('L'):
+		clear();
+		redraw_everything = YEA;
+		line_dirty = 1;
+		break;
+	    case KEY_LEFT:
+		if(currpnt) {
+		    if(my_ansimode)
+			currpnt = n2ansi(currpnt, currline);
+		    currpnt--;
+		    if(my_ansimode)
+			currpnt = ansi2n(currpnt, currline);
+		    line_dirty = 1;
+		} else if(currline->prev) {
+		    curr_window_line--;
+		    currln--;
+		    currline = currline->prev;
+		    currpnt = currline->len;
+		    line_dirty = 0;
+		}
+		break;
+	    case KEY_RIGHT:
+		if(currline->len != currpnt) {
+		    if(my_ansimode)
+			currpnt = n2ansi(currpnt, currline);
+		    currpnt++;
+		    if(my_ansimode)
+			currpnt = ansi2n(currpnt, currline);
+		    line_dirty = 1;
+		} else if(currline->next) {
+		    currpnt = 0;
+		    curr_window_line++;
+		    currln++;
+		    currline = currline->next;
+		    line_dirty = 0;
+		}
+		break;
+	    case KEY_UP:
+	    case Ctrl('P'):
+		if(currline->prev) {
+		    if(my_ansimode)
+			ch = n2ansi(currpnt,currline);
+		    curr_window_line--;
+		    currln--;
+		    currline = currline->prev;
+		    if(my_ansimode)
+			currpnt = ansi2n(ch , currline);
+		    else
+			currpnt = (currline->len > lastindent) ? lastindent :
+			currline->len;
+		    line_dirty = 0;
+		}
+		break;
+	    case KEY_DOWN:
+	    case Ctrl('N'):
+		if(currline->next) {
+		    if(my_ansimode)
+			ch = n2ansi(currpnt,currline);
+		    currline = currline->next;
+		    curr_window_line++;
+		    currln++;
+		    if(my_ansimode)
+			currpnt = ansi2n(ch , currline);
+		    else
+			currpnt = (currline->len > lastindent) ? lastindent :
+			currline->len;
+		    line_dirty = 0;
+		}
+		break;
+	    case Ctrl('B'):
+	    case KEY_PGUP:
+		redraw_everything = currln;
+		top_of_win = back_line(top_of_win, 22);
+		currln = redraw_everything;
+		currline = back_line(currline, 22);
+		curr_window_line = getlineno();
+		if(currpnt > currline->len)
+		    currpnt = currline->len;
+		redraw_everything = YEA;
+		line_dirty = 0;
+		break;
+	    case KEY_PGDN:
+	    case Ctrl('F'):
+		redraw_everything = currln;
+		top_of_win = forward_line(top_of_win, 22);
+		currln = redraw_everything;
+		currline = forward_line(currline, 22);
+		curr_window_line = getlineno();
+		if(currpnt > currline->len)
+		    currpnt = currline->len;
+		redraw_everything = YEA;
+		line_dirty = 0;
+		break;
+	    case KEY_END:
+	    case Ctrl('E'):
+		currpnt = currline->len;
+		line_dirty = 1;
+		break;
+	    case Ctrl(']'):   /* start of file */
+		prevln = currln;
+		prevpnt = currpnt;
+		currline = top_of_win = firstline;
+		currpnt = currln = curr_window_line = 0;
+		redraw_everything = YEA;
+		line_dirty = 0;
+		break;
+	    case Ctrl('T'):           /* tail of file */
+		prevln = currln;
+		prevpnt = currpnt;
+		top_of_win = back_line(lastline, 23);
+		currline = lastline;
+		curr_window_line = getlineno();
+		currln = totaln;
+		redraw_everything = YEA;
+		currpnt = 0;
+		line_dirty = 0;
+		break;
+	    case KEY_HOME:
+	    case Ctrl('A'):
+		currpnt = 0;
+		line_dirty = 1;
+		break;
+	    case KEY_INS:             /* Toggle insert/overwrite */
+	    case Ctrl('O'):
+		if(blockln >= 0 && insert_character) {
+		    char ans[4];
+
+		    getdata(b_lines - 1, 0,
+			    "�϶��L�եk�����J�r��(�w�]���ťզr��)",
+			    ans, 4, LCECHO);
+		    insert_c = (*ans) ? *ans : ' ';
+		}
+		insert_character ^= 1;
+		line_dirty = 1;
+		break;
+	    case Ctrl('H'):
+	    case '\177':              /* backspace */
+		line_dirty = 1;
+		if(my_ansimode) {
+		    my_ansimode = 0;
+		    clear();
+		    redraw_everything = YEA;
+		} else {
+		    if(currpnt == 0) {
+			textline_t *p;
+
+			if(!currline->prev)
+			    break;
+			line_dirty = 0;
+			curr_window_line--;
+			currln--;
+			currline = currline->prev;
+			currpnt = currline->len;
+			redraw_everything = YEA;
+			if(*killsp(currline->next->data) == '\0') {
+			    delete_line(currline->next);
+			    break;
+			}
+			p = currline;
+			while(!join(p))	{
+			    p = p->next;
+			    if(p == NULL) {
+				indigestion(2);
+				abort_bbs(0);
+			    }
+			}
+			break;
+		    }
+		    currpnt--;
+		    delete_char();
+		}
+		break;
+	    case Ctrl('D'):
+	    case KEY_DEL:             /* delete current character */
+		line_dirty = 1;
+		if(currline->len == currpnt) {
+		    textline_t *p = currline;
+		    
+		    while(!join(p)) {
+			p = p->next;
+			if(p == NULL) {
+			    indigestion(2);
+			    abort_bbs(0);
+			}
+		    }
+		    line_dirty = 0;
+		    redraw_everything = YEA;
+		} else {
+		    delete_char();
+		    if(my_ansimode)
+			currpnt = ansi2n(n2ansi(currpnt, currline), currline);
+		}
+		break;
+	    case Ctrl('Y'):           /* delete current line */
+		currline->len = currpnt = 0;
+	    case Ctrl('K'):           /* delete to end of line */
+		if(currline->len == 0) {
+		    textline_t *p = currline->next;
+		    if(!p) {
+			p = currline->prev;
+			if(!p)
+			    break;
+			if(curr_window_line > 0) {
+			    curr_window_line--;
+			    currln--;
+			}
+		    }
+		    if(currline == top_of_win)
+			top_of_win = p;
+		    delete_line(currline);
+		    currline = p;
+		    redraw_everything = YEA;
+		    line_dirty = 0;
+		    break;
+		}
+		if(currline->len == currpnt) {
+		    textline_t *p = currline;
+
+		    while(!join(p)) {
+			p = p->next;
+			if(p == NULL) {
+			    indigestion(2);
+			    abort_bbs(0);
+			}
+		    }
+		    redraw_everything = YEA;
+		    line_dirty = 0;
+		    break;
+		}
+		currline->len = currpnt;
+		currline->data[currpnt] = '\0';
+		line_dirty = 1;
+		break;
+	    }
+	    if(currln < 0)
+		currln = 0;
+	    if(curr_window_line < 0) {
+		curr_window_line = 0;
+		if(!top_of_win->prev)
+		    indigestion(6);
+		else {
+		    top_of_win = top_of_win->prev;
+		    rscroll();
+		}
+	    }
+	    if(curr_window_line == b_lines) {
+		curr_window_line = t_lines - 2;
+		if(!top_of_win->next)
+		    indigestion(7);
+		else {
+		    top_of_win = top_of_win->next;
+		    move(b_lines, 0);
+		    clrtoeol();
+		    scroll();
+		}
+	    }
+	}
+	edit_margin = currpnt < SCR_WIDTH - 1 ? 0 : currpnt / 72 * 72;
+	
+	if(!redraw_everything) {
+	    if(edit_margin != last_margin) {
+		last_margin = edit_margin;
+		redraw_everything = YEA;
+	    } else {
+		move(curr_window_line, 0);
+		clrtoeol();
+		if(my_ansimode)
+		    outs(currline->data);
+		else
+		    edit_outs(&currline->data[edit_margin]);
+		edit_msg();
+	    }
+	}
+    }
+}
diff --git a/mbbsd/friend.c b/mbbsd/friend.c
new file mode 100644
index 00000000..3d2167ae
--- /dev/null
+++ b/mbbsd/friend.c
@@ -0,0 +1,509 @@
+/* $Id: friend.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "proto.h"
+
+extern char currboard[];	/* name of currently selected board */
+extern char *fn_overrides;
+extern userinfo_t *currutmp;
+extern char *fn_reject;
+extern int usernum;
+extern char *str_space;
+extern char *msg_uid;
+
+/* ------------------------------------- */
+/* �S�O�W��                              */
+/* ------------------------------------- */
+
+/* Ptt ��L�S�O�W�檺�ɦW */
+static char special_list[] = "list.0";
+static char special_des[] = "ldes.0";
+
+/* �S�O�W�檺�W�� */
+static unsigned int friend_max[8] = {
+    MAX_FRIEND,
+    MAX_REJECT,
+    MAX_LOGIN_INFO,
+    MAX_POST_INFO,
+    MAX_NAMELIST,
+    MAX_NAMELIST,
+    MAX_NAMELIST,
+    MAX_NAMELIST
+};
+/* ���M�n�͸��a�H�W�泣�O * 2 ���O�@���̦hload��shm�u�঳128 */
+
+/* Ptt �U�دS�O�W�檺�ɦW */
+char *friend_file[8] = {
+    FN_OVERRIDES,
+    FN_REJECT,
+    "alohaed",
+    "postlist",
+    "",
+    FN_CANVOTE,
+    FN_WATER,
+    FN_VISABLE
+};
+
+/* Ptt �U�دS�O�W�檺�ɭz */
+static char *friend_desc[8] = {
+    "�ͽ˴y�z�G",
+    "�c�δc���G",
+    "",
+    "",
+    "�y�z�@�U�G",
+    "�벼�̴y�z�G",
+    "�c�δc���G",
+    "�ݪO�n�ʹy�z"
+};
+
+/* Ptt �U�دS�O�W�檺����ԭz */
+static char *friend_list[8] = {
+    "�n�ͦW��",
+    "�a�H�W��",
+    "�W�u�q��",
+    "�s�峹�q��",
+    "�䥦�S�O�W��",
+    "�p�H�벼�W��",
+    "�ݪO�T�n�W��",
+    "�ݪO�n�ͦW��"
+};
+
+static void setfriendfile(char *fpath, int type) {
+    if (type <= 4)		/* user list Ptt */
+	setuserfile(fpath, friend_file[type]);
+    else			/* board list */
+	setbfile(fpath, currboard, friend_file[type]);
+}
+
+static int friend_count(char *fname) {
+    FILE *fp;
+    int count = 0;
+    char buf[200];
+
+#if 0
+    if ((fp = fopen(fname, "r")))
+	while (fgets(buf, 200, fp))
+	    count++;
+#endif
+
+/*rocker.011018: �ѰO���ɤF... */
+    if ((fp = fopen(fname, "r")))
+    {
+      while (fgets(buf, 200, fp)) count++;
+      fclose (fp);
+    }
+    
+    return count;
+}
+
+void friend_add(char *uident, int type) {
+    char fpath[80];
+
+    setfriendfile(fpath, type);
+    if (friend_count(fpath) > friend_max[type])
+	return;
+
+    if ((uident[0] > ' ') && !belong(fpath, uident))
+    {
+	FILE *fp;
+	char buf[40] = "";
+	char t_uident[IDLEN + 1];
+
+    /* Thor: avoid uident run away when get data */
+	strcpy(t_uident, uident);
+
+	if (type != FRIEND_ALOHA && type != FRIEND_POST)
+	    getdata(2, 0, friend_desc[type], buf, 40, DOECHO);
+
+	if ((fp = fopen(fpath, "a")))
+	{
+	    flock(fileno(fp), LOCK_EX);
+	    fprintf(fp, "%-13s%s\n", t_uident, buf);
+	    flock(fileno(fp), LOCK_UN);
+	    fclose(fp);
+	}
+    }
+}
+
+static void friend_special() {
+    char genbuf[70], i, fname[70];
+
+    friend_file[FRIEND_SPECIAL] = special_list;
+    for (i = 0; i <= 9; i++)
+    {
+	sprintf(genbuf, "  (\033[36m%d\033[m)  .. ", i);
+	special_des[5] = i + '0';
+	setuserfile(fname, special_des);
+	if (dashf(fname))
+	{
+	/* no NULL check?? */
+	    FILE *fp = fopen(fname, "r");
+
+	    fgets(genbuf + 15, 40, fp);
+	    genbuf[47] = 0;
+	}
+	move(i + 12, 0);
+	clrtoeol();
+	outs(genbuf);
+    }
+    getdata(22, 0, "�п�ܲĴX���S�O�W�� (0~9)[0]?", genbuf, 3, LCECHO);
+    if (genbuf[0] >= '0' && genbuf[0] <= '9')
+    {
+	special_list[5] = genbuf[0];
+	special_des[5] = genbuf[0];
+    }
+    else
+    {
+	special_list[5] = '0';
+	special_des[5] = '0';
+    }
+}
+
+static void friend_append(int type, int count) {
+    char fpath[80], i, j, buf[80], sfile[80];
+    FILE *fp, *fp1;
+
+    setfriendfile(fpath, type);
+
+    do
+    {
+	move(2, 0);
+	clrtobot();
+	outs("�n�ޤJ���@�ӦW��?\n");
+	for (i = 0, j = 0; i <= 7; i++)
+	{
+	    if (i == type)
+		continue;
+	    j++;
+	    if (i <= 4)
+		sprintf(buf, "  (%d) %-s\n", j, friend_list[(int) i]);
+	    else
+		sprintf(buf, "  (%d) %s ���� %s\n", j, currboard,
+			friend_list[(int) i]);
+	    outs(buf);
+	}
+	outs("  (S) ��ܨ�L�ݪO���S�O�W��");
+	getdata(11, 0, "�� �� ����[Enter] ���:", buf, 3, LCECHO);
+	if (!buf[0])
+	    return;
+	if (buf[0] == 's')
+	    Select();
+    }
+    while (buf[0] < '1' || buf[0] > '9');
+
+    j = buf[0] - '1';
+    if (j >= type)
+	j++;
+    if (j == FRIEND_SPECIAL)
+	friend_special();
+
+    setfriendfile(sfile, j);
+
+    fp = fopen(sfile, "r");
+    while (fgets(buf, 80, fp) && count <= friend_max[type])
+    {
+	char the_id[15];
+
+	sscanf(buf, "%s", the_id);
+	if (!belong(fpath, the_id))
+	{
+	    if ((fp1 = fopen(fpath, "a")))
+	    {
+		flock(fileno(fp1), LOCK_EX);
+		fputs(buf, fp1);
+		flock(fileno(fp1), LOCK_UN);
+		fclose(fp1);
+	    }
+	}
+    }
+    fclose(fp);
+}
+
+void friend_delete(char *uident, int type) {
+    FILE *fp, *nfp;
+    char fn[80], fnnew[80];
+    char genbuf[200];
+
+    setfriendfile(fn, type);
+
+    sprintf(fnnew, "%s-", fn);
+    if ((fp = fopen(fn, "r")) && (nfp = fopen(fnnew, "w")))
+    {
+	int length = strlen(uident);
+
+	while (fgets(genbuf, STRLEN, fp))
+	    if ((genbuf[0] > ' ') && strncmp(genbuf, uident, length))
+		fputs(genbuf, nfp);
+	fclose(fp);
+	fclose(nfp);
+	Rename(fnnew, fn);
+    }
+}
+
+static void friend_editdesc(char *uident, int type) {
+    FILE *fp, *nfp;
+    char fnnew[200], genbuf[200], fn[200];
+    setfriendfile(fn, type);
+    sprintf(fnnew, "%s-", fn);
+    if ((fp = fopen(fn, "r")) && (nfp = fopen(fnnew, "w")))
+    {
+	int length = strlen(uident);
+
+	while (fgets(genbuf, STRLEN, fp))
+	{
+	    if ((genbuf[0] > ' ') && strncmp(genbuf, uident, length))
+		fputs(genbuf, nfp);
+	    else if (!strncmp(genbuf, uident, length))
+	    {
+		char buf[50] = "";
+		getdata(2, 0, "�ק�y�z�G", buf, 40, DOECHO);
+		fprintf(nfp, "%-13s%s\n", uident, buf);
+	    }
+	}
+	fclose(fp);
+	fclose(nfp);
+	Rename(fnnew, fn);
+    }
+}
+
+void friend_load() {
+    FILE *fp;
+    int myfriends[MAX_FRIEND];
+    int myrejects[MAX_REJECT];
+    int friendcount, rejectedcount;
+    char genbuf[200];
+
+    memset(myfriends, 0, sizeof(myfriends));
+    friendcount = 0;
+    setuserfile(genbuf, fn_overrides);
+    if ((fp = fopen(genbuf, "r")))
+    {
+	int unum;
+
+	while (fgets(genbuf, STRLEN, fp) && friendcount < MAX_FRIEND - 1)
+	    if (strtok(genbuf, str_space))
+		if ((unum = searchuser(genbuf)))
+		    myfriends[friendcount++] = unum;
+	fclose(fp);
+    }
+    memcpy(currutmp->friend, myfriends, sizeof(myfriends));
+
+    memset(myrejects, 0, sizeof(myrejects));
+    rejectedcount = 0;
+    setuserfile(genbuf, fn_reject);
+    if ((fp = fopen(genbuf, "r")))
+    {
+	int unum;
+
+	while (fgets(genbuf, STRLEN, fp) && rejectedcount < MAX_REJECT - 1)
+	    if (strtok(genbuf, str_space))
+		if ((unum = searchuser(genbuf)))
+		    myrejects[rejectedcount++] = unum;
+	fclose(fp);
+    }
+    memcpy(currutmp->reject, myrejects, sizeof(myrejects));
+    if(currutmp->friendtotal) logout_friend_online();
+    login_friend_online();
+}
+
+extern userec_t cuser;
+
+static void friend_water(char *message, int type) { /* �s����y added by Ptt */
+    char fpath[80], line[80], userid[IDLEN + 1];
+    FILE *fp;
+
+    setfriendfile(fpath, type);
+    if ((fp = fopen(fpath, "r")))
+	while(fgets(line, 80, fp)) {
+	    userinfo_t *uentp;
+	    int tuid;
+	    
+	    sscanf(line, "%s", userid);
+	    if((tuid = searchuser(userid)) && tuid != usernum &&
+	       (uentp = (userinfo_t *) search_ulist(tuid)) &&
+               isvisible_uid(tuid))
+		my_write(uentp->pid, message, uentp->userid, 1);
+	}
+    fclose(fp);
+}
+
+void friend_edit(int type) {
+    char fpath[80], line[80], uident[20];
+    int count, column, dirty;
+    FILE *fp;
+    char genbuf[200];
+
+    if (type == FRIEND_SPECIAL)
+	friend_special();
+    setfriendfile(fpath, type);
+
+    if (type == FRIEND_ALOHA || type == FRIEND_POST)
+    {
+	if (dashf(fpath))
+	{
+	    sprintf(genbuf, "/bin/cp %s %s.old", fpath, fpath);
+	    system(genbuf);
+	}
+    }
+
+    dirty = 0;
+    while (1)
+    {
+	stand_title(friend_list[type]);
+	move(0, 40);
+	sprintf(line, "(�W��W��:%d�ӤH)", friend_max[type]);
+	outs(line);
+	count = 0;
+	CreateNameList();
+
+	if ((fp = fopen(fpath, "r")))
+	{
+	    move(3, 0);
+	    column = 0;
+	    while (fgets(genbuf, STRLEN, fp))
+	    {
+		if (genbuf[0] <= ' ')
+		    continue;
+		strtok(genbuf, str_space);
+		AddNameList(genbuf);
+		prints("%-13s", genbuf);
+		count++;
+		if (++column > 5)
+		{
+		    column = 0;
+		    outc('\n');
+		}
+	    }
+	    fclose(fp);
+	}
+	getdata(1, 0, (count ?
+		       "(A)�W�[(D)�R��(E)�ק�(P)�ޤJ(L)�ԲӦC�X"
+		       "(K)�R����ӦW��(W)����y(Q)�����H[Q]" :
+		       "(A)�W�[ (P)�ޤJ��L�W�� (Q)�����H[Q]"),
+		uident, 3, LCECHO);
+	if (*uident == 'a')
+	{
+	    move(1, 0);
+	    usercomplete(msg_uid, uident);
+	    if (uident[0] && searchuser(uident) && !InNameList(uident))
+	    {
+		friend_add(uident, type);
+		dirty = 1;
+	    }
+	}
+	else if (*uident == 'p')
+	{
+	    friend_append(type, count);
+	    dirty = 1;
+	}
+	else if (*uident == 'e' && count)
+	{
+	    move(1, 0);
+	    namecomplete(msg_uid, uident);
+	    if (uident[0] && InNameList(uident))
+	    {
+		friend_editdesc(uident, type);
+	    }
+	}
+	else if (*uident == 'd' && count)
+	{
+	    move(1, 0);
+	    namecomplete(msg_uid, uident);
+	    if (uident[0] && InNameList(uident))
+	    {
+		friend_delete(uident, type);
+		dirty = 1;
+	    }
+	}
+	else if (*uident == 'l' && count)
+	    more(fpath, YEA);
+	else if (*uident == 'k' && count)
+	{
+	    getdata(2, 0, "����W��N�|�Q�R��,�z�T�w�� (a/N)?", uident, 3,
+		    LCECHO);
+	    if (*uident == 'a')
+		unlink(fpath);
+	    dirty = 1;
+	}
+	else if (*uident == 'w' && count)
+	{
+	    if (!getdata(0, 0, "�s����y:", uident, 60, DOECHO))
+		continue;
+	    if (getdata(0, 0, "�T�w��X�s����y? [Y]", line, 4, LCECHO) &&
+		*line == 'n')
+		continue;
+	    friend_water(uident, type);
+	}
+	else
+	    break;
+    }
+    if (dirty)
+    {
+	move(2, 0);
+	outs("��s��Ƥ�..�еy��.....");
+	refresh();
+	if (type == FRIEND_ALOHA || type == FRIEND_POST)
+	{
+	    sprintf(genbuf, "%s.old", fpath);
+	    if ((fp = fopen(genbuf, "r")))
+	    {
+		while (fgets(line, 80, fp))
+		{
+		    sscanf(line, "%s", uident);
+		    sethomefile(genbuf, uident,
+			  type == FRIEND_ALOHA ? "aloha" : "postnotify");
+		    del_distinct(genbuf, cuser.userid);
+		}
+		fclose(fp);
+	    }
+	    sprintf(genbuf, "%s", fpath);
+	    if ((fp = fopen(genbuf, "r")))
+	    {
+		while (fgets(line, 80, fp))
+		{
+		    sscanf(line, "%s", uident);
+		    sethomefile(genbuf, uident,
+			  type == FRIEND_ALOHA ? "aloha" : "postnotify");
+		    add_distinct(genbuf, cuser.userid);
+		}
+		fclose(fp);
+	    }
+	}
+	else if (type == FRIEND_SPECIAL)
+	{
+	    genbuf[0] = 0;
+	    setuserfile(line, special_des);
+	    if ((fp = fopen(line, "r")))
+	    {
+		fgets(genbuf, 30, fp);
+		fclose(fp);
+	    }
+	    getdata_buf(2, 0, " �Ь����S�O�W����@��²�u�W��:", genbuf, 30,
+			DOECHO);
+	    if ((fp = fopen(line, "w")))
+	    {
+		fprintf(fp, "%s", genbuf);
+		fclose(fp);
+	    }
+	}
+	friend_load();
+    }
+}
+
+int t_override() {
+    friend_edit(FRIEND_OVERRIDE);
+    return 0;
+}
+
+int t_reject() {
+    friend_edit(FRIEND_REJECT);
+    return 0;
+}
diff --git a/mbbsd/gamble.c b/mbbsd/gamble.c
new file mode 100644
index 00000000..23867450
--- /dev/null
+++ b/mbbsd/gamble.c
@@ -0,0 +1,361 @@
+/* $Id: gamble.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+extern int usernum;
+
+#ifndef _BBS_UTIL_C_
+extern userec_t cuser;
+extern int b_lines;
+
+#define MAX_ITEM	8	//�̤j �䶵(item) �Ӽ�
+#define MAX_ITEM_LEN	30	//�̤j �C�@�䶵�W�r����
+#define MAX_SUBJECT_LEN 650	//8*81 = 648 �̤j �D�D���� 
+
+static char betname[MAX_ITEM][MAX_ITEM_LEN];
+static int currbid;
+
+int post_msg(char* bname, char* title, char *msg, char* author)
+{
+    FILE *fp;
+    int bid;
+    fileheader_t fhdr;
+    time_t now = time(0);
+    char genbuf[256];
+                
+    /* �b bname ���o���s�峹 */
+    sprintf(genbuf, "boards/%s", bname);
+    stampfile(genbuf, &fhdr);
+    fp = fopen(genbuf,"w");
+    
+    if(!fp)
+      return -1;
+    
+    fprintf(fp, "�@��: %s �ݪO: %s\n���D: %s \n",author,bname,title);
+    fprintf(fp, "�ɶ�: %s\n", ctime(&now));
+                                                
+    /* �峹�����e */
+    fprintf(fp, "%s", msg);
+    fclose(fp);
+                                                                        
+    /* �N�ɮץ[�J�C�� */
+    strcpy(fhdr.title, title);
+    strcpy(fhdr.owner, author);
+    setbdir(genbuf,bname);
+    if(append_record(genbuf, &fhdr, sizeof(fhdr))!=-1)
+      if((bid = getbnum(bname)) > 0)
+          setbtotal(bid);
+    return 0;
+}
+
+int post_file(char* bname,  char* title, char *filename, char* author)
+{
+ int size=dashs(filename);
+ char *msg;
+ FILE *fp;
+
+ if(size<=0) return -1;
+ if(!(fp=fopen(filename,"r")) ) return -1;
+ msg= (char *)malloc(size);
+ fread(msg,1,size,fp);
+ size= post_msg(bname, title, msg, author);
+ fclose(fp);
+ free(msg);
+ return size;
+}
+
+
+static int load_ticket_record(char *direct, int ticket[])
+{
+   char buf[256];
+   int i, total=0;
+   FILE *fp;
+   sprintf(buf,"%s/"FN_TICKET_RECORD,direct);
+   if(!(fp=fopen(buf,"r"))) return 0;
+   for(i=0;i<MAX_ITEM && fscanf(fp, "%9d ",&ticket[i]); i++) 
+      total=total+ticket[i]; 
+   fclose(fp);
+   return total;
+}
+
+static int show_ticket_data(char *direct, int *price, boardheader_t *bh) {
+    int i,count, total = 0, end=0,
+        ticket[MAX_ITEM] = {0, 0, 0, 0, 0, 0, 0, 0};
+    FILE *fp;
+    char genbuf[256];
+
+    clear();
+    if (bh)
+      {
+        sprintf(genbuf,"%s ��L", bh->brdname);
+        showtitle(genbuf, BBSNAME);
+      }	
+    else
+        showtitle("Ptt��L", BBSNAME);
+    move(2, 0);
+    sprintf(genbuf, "%s/"FN_TICKET_ITEMS, direct);
+    if(!(fp = fopen(genbuf,"r")))
+    {
+      prints("\n�ثe�èS���|���L\n");
+      sprintf(genbuf, "%s/"FN_TICKET_OUTCOME, direct);
+      if(more(genbuf, NA));
+      return 0;
+    }
+    fgets(genbuf,MAX_ITEM_LEN,fp);
+    *price=atoi(genbuf);
+    for(count=0; fgets(betname[count],MAX_ITEM_LEN,fp)&&count<MAX_ITEM; count++)
+	strtok(betname[count],"\r\n");
+    fclose(fp);
+
+    prints("\033[32m���W:\033[m 1.�i�ʶR�H�U���P�������m���C�C�i�n�� \033[32m%d\033[m ���C\n"
+         "      2.%s\n"
+         "      3.�}���ɥu���@�رm������, ���ʶR�ӱm����, �h�i���ʶR���i�Ƨ���"
+         "�`����C\n"
+         "      4.�C�������Ѩt�Ω�� 5% ���|��%s�C\n\n"
+         "\033[32m%s:\033[m", *price,
+         bh?"����L�Ѫ��D�t�d�|��åB�M�w�}���ɶ����G, ��������, �@��A��C":
+             "�t�ΨC�� 2:00 11:00 16:00 21:00 �}���C",
+	 bh?", �䤤 2% �����}�����D":"",
+         bh?"���D�ۭq�W�h�λ���":"�e�X���}�����G");
+
+
+    sprintf(genbuf, "%s/"FN_TICKET, direct);
+    if(!dashf(genbuf))
+	{
+	  sprintf(genbuf, "%s/"FN_TICKET_END, direct);
+	  end=1;
+	}
+    show_file(genbuf, 8, -1, NO_RELOAD);
+    move(15,0);
+    prints("\033[1;32m�ثe�U�`���p:\033[m\n");
+
+    total=load_ticket_record(direct, ticket);
+    
+    prints("\033[33m");
+    for(i = 0 ; i<count; i++)
+      {
+	prints("%d.%-8s: %-7d", i+1, betname[i], ticket[i]);
+	if(i==3) prints("\n");
+      }
+    prints("\033[m\n\033[42m �U�`�`���B:\033[31m %d �� \033[m",total*(*price));
+    if(end)
+     {
+       prints("\n��L�w�g����U�`\n");
+       return -count;
+     }
+    return count;
+}
+
+static void append_ticket_record(char *direct, int ch, int n, int count) {
+    FILE *fp;
+    int ticket[8] = {0,0,0,0,0,0,0,0}, i;
+    char genbuf[256];
+    sprintf(genbuf, "%s/"FN_TICKET_USER, direct);
+    
+    if((fp = fopen(genbuf,"a"))) {
+	fprintf(fp, "%s %d %d\n", cuser.userid, ch, n);
+	fclose(fp);
+    }
+    load_ticket_record(direct, ticket);
+    ticket[ch] += n;
+    sprintf(genbuf, "%s/" FN_TICKET_RECORD, direct);
+    if((fp = fopen(genbuf,"w"))) {
+        for(i=0; i<count; i++)
+           fprintf(fp,"%d ", ticket[i]);
+	fclose(fp);
+    }
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+int ticket(int bid)
+{
+    int ch, n, price, count, end=0;
+    char path[128],fn_ticket[128];
+    boardheader_t *bh=NULL; 
+    
+    if(bid)
+      {
+       bh=getbcache(bid);
+       setbpath(path, bh->brdname);   
+       setbfile(fn_ticket, bh->brdname, FN_TICKET);   
+       currbid = bid;
+      }
+    else
+       strcpy(path,"etc/");
+
+    lockreturn0(TICKET, LOCK_MULTI);
+    while(1) {
+	count=show_ticket_data(path, &price, bh);
+        if(count<=0) 
+        {
+	   pressanykey();
+           break;
+        } 
+        move(20, 0);
+        reload_money();
+	prints("\033[44m��: %-10d  \033[m\n\033[1m�п�ܭn�ʶR������(1~%d)"
+        "[Q:���}]\033[m:", cuser.money,count);
+	ch = igetch();
+	/*-- 
+	  Tim011127
+	  ���F����CS���D ���O�o���٤��৹���ѨM�o���D,        
+	  �Yuser�q�L�ˬd�U�h, ��n���D�}��, �٬O�|�y��user���o������
+	  �ܦ��i��]��U����L�������h, �]�ܦ��i��Q���D�s�}��L�ɬ~��
+	  ���L�o��ܤ֥i�H���쪺�O, ���h�u�|���@����ƬO����
+	--*/
+        if(bid && !dashf(fn_ticket))
+        {
+           move(b_lines-1,0);
+           prints("�z!! �@����...���D�w�g����U�`�F ������P");
+	   pressanykey();
+	   break;
+        }
+
+        if(ch=='q' || ch == 'Q')
+	    break;
+        ch-='1';
+	if(end || ch >= count || ch < 0)
+	    continue;
+        n=0;
+        ch_buyitem(price, "etc/buyticket", &n);
+	if(n > 0)
+		append_ticket_record(path,ch,n,count);
+      }	
+    unlockutmpmode();
+    return 0;
+}
+
+int openticket(int bid) {
+    char path[128],buf[256],outcome[128];
+    int i, money=0, count, bet, price, total = 0, ticket[8]={0,0,0,0,0,0,0,0};
+    boardheader_t *bh=getbcache(bid);
+    time_t now = time(NULL);
+    FILE  *fp, *fp1;
+
+    setbpath(path, bh->brdname); 
+    count=-show_ticket_data(path, &price, bh);
+    if(count==0)
+      {
+         setbfile(buf,bh->brdname,FN_TICKET_END);
+         unlink(buf); //Ptt: ��bug
+         return 0;
+      }
+    lockreturn0(TICKET, LOCK_MULTI);
+    do
+    {
+     do
+     {
+       getdata(20, 0, "\033[1m��ܤ��������X(0:����)\033[m:", buf, 3, LCECHO);
+       bet=atoi(buf);
+       move(0,0);
+       clrtoeol();
+     } while(bet<0 || bet>count);
+     if(bet==0) 
+        {unlockutmpmode(); return 0;}
+     getdata(21, 0, "\033[1m�A���T�{���������X\033[m:", buf, 3, LCECHO);
+    }while(bet!=atoi(buf));
+
+    bet -= 1; //�ন�x�}��index
+
+    total=load_ticket_record(path, ticket);
+    setbfile(buf,bh->brdname,FN_TICKET_END);
+    if(!(fp1 = fopen(buf,"r")))
+      {
+         unlockutmpmode();
+         return 0; 
+      }
+        // �٨S�}���������� �u�nmv�@���N�n 
+    money=total*price;
+    demoney(money*0.02);
+    mail_redenvelop("[������Y]", cuser.userid, money*0.02, 'n');
+    money = ticket[bet] ?  money*0.95/ticket[bet]:9999999; 
+    setbfile(outcome,bh->brdname,FN_TICKET_OUTCOME);
+    if((fp = fopen(outcome, "w")))
+    {  
+      fprintf(fp,"��L����\n");
+      while(fgets(buf,256,fp1))
+         {
+	   buf[255]=0;
+	   fprintf(fp,"%s",buf);
+         }
+      fprintf(fp,"�U�`���p\n");
+
+      fprintf(fp, "\033[33m");
+      for(i = 0 ; i<count; i++)
+      {
+        fprintf(fp, "%d.%-8s: %-7d",i+1,betname[i], ticket[i]);
+        if(i==3) fprintf(fp,"\n");
+      }
+      fprintf(fp, "\033[m\n");
+                                        
+      fprintf(fp, "\n\n�}���ɶ��G %s \n\n"
+             "�}�����G�G %s \n\n"
+             "�Ҧ����B�G %d �� \n"
+             "������ҡG %d�i/%d�i  (%f)\n"
+             "�C�i�����m���i�o %d �T�޹� \n\n",
+             Cdatelite(&now), betname[bet], total*price, ticket[bet], total,
+             (float) ticket[bet] / total, money);
+             
+      fprintf(fp, "%s ��L�}�X:%s �Ҧ����B:%d �� ����/�i:%d �� ���v:%1.2f\n",
+              Cdatelite(&now), betname[bet], total*price, money,
+              total? (float)ticket[bet] / total:0);
+              
+      fclose(fp);
+    }
+    fclose(fp1); 
+
+    setbfile(buf, bh->brdname, FN_TICKET_END);
+    unlink(buf);
+/*
+    if(fork())
+      {
+        more(outcome,YEA);
+        unlockutmpmode();
+        return 0;
+      }
+*/
+    sprintf(buf, "[���i] %s ��L�}��", bh->brdname);
+    post_file(bh->brdname, buf, outcome, "[�䯫]");
+    post_file("Record", buf+7, outcome, "[�������l]");
+    /*
+      �H�U�O�����ʧ@
+    */
+    setbfile(buf, bh->brdname, FN_TICKET_USER);
+    if (ticket[bet] && (fp = fopen(buf, "r")))  
+    {
+        int mybet, uid;
+        char userid[IDLEN];
+        
+        while (fscanf(fp, "%s %d %d\n", userid, &mybet, &i) != EOF)
+        {
+           if (mybet == bet)
+           {
+                printf("���� %-15s�R�F%9d �i %s, ��o %d �T�޹�\n"
+                       ,userid, i, betname[mybet], money * i);                    
+                if((uid=getuser(userid))==0) continue;
+                deumoney(uid, money * i);
+                sprintf(buf, "%s ������! $ %d", bh->brdname,  money * i);
+                mail_id(userid, buf, outcome, "Ptt���");
+            }
+        }   
+    }
+    setbfile(buf, bh->brdname, FN_TICKET_RECORD); 
+    unlink(buf);
+    setbfile(buf, bh->brdname, FN_TICKET_USER); 
+    unlink(buf);
+    return 0;
+}
+
+int ticket_main() {
+    ticket(0);
+    return 0;
+}
+#endif
diff --git a/mbbsd/gomo.c b/mbbsd/gomo.c
new file mode 100644
index 00000000..06062ce4
--- /dev/null
+++ b/mbbsd/gomo.c
@@ -0,0 +1,417 @@
+/* $Id: gomo.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "gomo.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern int usernum;
+extern userinfo_t *currutmp;
+
+char ku[BRDSIZ][BRDSIZ];
+static char *chess[] = { "��", "��" };
+static int tick, lastcount, mylasttick, hislasttick;
+
+unsigned char  *pat, *adv;
+
+typedef struct {
+    char x;
+    char y;
+} Horder_t;
+
+static Horder_t *v, pool[225];
+
+static void HO_init() {
+    memset(pool, 0, sizeof(pool));
+    v = pool;
+    pat = pat_gomoku;
+    adv = adv_gomoku;
+    memset(ku, 0, sizeof(ku));
+}
+
+static void HO_add(Horder_t *mv) {
+    *v++ = *mv;
+}
+
+static void HO_undo(Horder_t *mv) {
+    char *str = "�z�s�{�u�q�t�|�r�}";
+    int n1, n2, loc;
+
+    *mv = *(--v);
+    ku[(int)mv->x][(int)mv->y] = BBLANK;
+    BGOTO(mv->x, mv->y);
+    n1 = (mv->x == 0) ? 0 : (mv->x == 14) ? 2 : 1;
+    n2 = (mv->y == 14)? 0 : (mv->y == 0) ? 2 : 1;
+    loc= 2 * ( n2 * 3 + n1);
+    prints("%.2s", str + loc);
+}
+
+extern userec_t cuser;
+
+static void HO_log(char *user) {
+    int i;
+    FILE *log;
+    char buf[80];
+    char buf1[80];
+    char title[80];
+    Horder_t *ptr = pool;
+    extern screenline_t *big_picture;
+    fileheader_t mymail;
+
+    sprintf(buf, "home/%c/%s/F.%d", cuser.userid[0], cuser.userid,
+	    rand() & 65535);
+    log = fopen(buf, "w");
+    
+    for(i = 1; i < 17; i++)
+	fprintf(log, "%.*s\n", big_picture[i].len, big_picture[i].data);
+    
+    i = 0;
+    do {
+	fprintf(log, "[%2d]%s ==> %c%d%c", i + 1, chess[i % 2],
+	 	'A' + ptr->x, ptr->y + 1, (i % 2) ? '\n' : '\t');
+	i++;
+    } while( ++ptr < v);
+    fclose(log);
+    
+    sethomepath(buf1, cuser.userid);
+    stampfile(buf1, &mymail);
+    
+    mymail.savemode = 'H';        /* hold-mail flag */
+    mymail.filemode = FILE_READ;
+    strcpy(mymail.owner, "[��.��.��]");
+    sprintf(mymail.title, "\033[37;41m����\033[m %s VS %s",
+	    cuser.userid, user);
+    sethomedir(title, cuser.userid);
+    Rename(buf, buf1);
+    append_record(title, &mymail, sizeof(mymail));  
+    
+    unlink(buf);
+}
+
+static int countgomo() {
+    Horder_t *ptr;
+    int i;
+    
+    ptr = pool;
+    i = 0;
+    do {
+	i++;
+    } while(++ptr < v);
+    return i;
+}
+
+static int chkmv(Horder_t *mv, int color, int limit) {
+    char *xtype[] = {"\033[1;31m���T\033[m", "\033[1;31m���T\033[m",
+		     "\033[1;31m���|\033[m", "\033[1;31m���|\033[m",
+		     "\033[1;31m���|\033[m", "\033[1;31m�|�T\033[m",
+		     "\033[1;31m���T\033[m", "\033[1;31m���|\033[m",
+		     "\033[1;31m���|\033[m", "\033[1;31m�s��\033[m",
+		     "\033[1;31m�s��\033[m"};
+    int rule = getstyle(mv->x, mv->y, color, limit);
+    if(rule > 1 && rule < 13) {
+	move(15, 40);
+	outs(xtype[rule - 2]);
+	bell();
+    }
+    return chkwin(rule, limit);
+}
+
+static int gomo_key(int fd, int ch, Horder_t *mv) {
+    if( ch >= 'a' && ch <= 'o') {
+        char pbuf[4], vx, vy;
+	
+        *pbuf = ch;
+	if(fd)
+	    add_io(0, 0);
+        oldgetdata(17, 0, "�������w��m :", pbuf, 4, DOECHO);
+        if(fd)
+	    add_io(fd, 0);
+        vx = pbuf[0] - 'a';
+        vy = atoi(pbuf + 1) - 1;
+        if(vx >= 0 && vx < 15 && vy >= 0 && vy < 15 &&
+	   ku[(int)vx][(int)vy] == BBLANK) {
+	    mv->x = vx;
+	    mv->y = vy;
+	    return 1;
+        }
+    } else {
+        switch(ch) {
+	case KEY_RIGHT:
+            mv->x = (mv->x == BRDSIZ - 1) ? mv->x : mv->x + 1;
+            break;
+	case KEY_LEFT:
+            mv->x = (mv->x == 0 ) ? 0 : mv->x - 1;
+            break;
+	case KEY_UP:
+            mv->y = (mv->y == BRDSIZ - 1) ? mv->y : mv->y + 1;
+            break;
+	case KEY_DOWN:
+            mv->y = (mv->y == 0 ) ? 0 : mv->y - 1;
+            break;
+	case ' ':
+	case '\r':
+            if(ku[(int)mv->x][(int)mv->y] == BBLANK)
+		return 1;
+        }
+    }
+    return 0;
+}
+
+extern userec_t xuser;
+
+static int reload_gomo() {
+    passwd_query(usernum, &xuser);
+    cuser.five_win = xuser.five_win;
+    cuser.five_lose = xuser.five_lose;
+    cuser.five_tie = xuser.five_tie;
+    return 0;
+}   
+
+int gomoku(int fd) {
+    Horder_t mv;
+    int me, he, win, ch;
+    int hewantpass, iwantpass;
+    userinfo_t *my = currutmp;
+    
+    HO_init();
+    me = !(my->turn) + 1;
+    he = my->turn + 1;
+    win = 1;
+    tick=time(0) + MAX_TIME;
+    lastcount = MAX_TIME;
+    setutmpmode(M_FIVE);
+    clear();
+    
+    prints("\033[1;46m  ���l�ѹ��  \033[45m%30s VS %-30s\033[m",
+	   cuser.userid, my->mateid);
+    show_file("etc/@five", 1, -1, ONLY_COLOR);
+    move(11, 40);
+    prints("�ڬO %s", me == BBLACK ? "���� ��, ���T��" : "��� ��");
+    move(16, 40);
+    prints("\033[1;33m%s", cuser.userid);
+    move(17, 40);
+    prints("\033[1;33m%s", my->mateid);
+    
+    reload_gomo();
+    move(16, 60);
+    prints("\033[1;31m%d\033[37m�� \033[34m%d\033[37m�� \033[36m%d\033[37m�M"
+	   "\033[m", cuser.five_win, cuser.five_lose, cuser.five_tie);
+    
+    getuser(my->mateid);  
+    move(17, 60);  
+    prints("\033[1;31m%d\033[37m�� \033[34m%d\033[37m�� \033[36m%d\033[37m"
+	   "�M\033[m", xuser.five_win, xuser.five_lose, xuser.five_tie);
+    
+    cuser.five_lose++;
+    /* �@�i�ӥ��[�@���ѳ�, Ĺ�F��A���^�h, �קK�ֿ�F�c�N�_�u */
+    passwd_update(usernum, &cuser);
+    
+    add_io(fd, 0);
+    
+    hewantpass = iwantpass = 0;
+    mv.x = mv.y = 7;
+    move(18, 40);
+    prints("%s�ɶ��ٳ�%d:%02d\n", my->turn ? "�A��" : "���",
+	   MAX_TIME / 60, MAX_TIME % 60);      
+    for(;;) {
+	move(13, 40);
+	outs(my->turn ? "����ۤv�U�F!": "���ݹ��U�l..");
+	if(lastcount != tick-time(0)) {
+	    lastcount = tick-time(0);
+	    move(18, 40);
+	    prints("%s�ɶ��ٳ�%d:%02d\n", my->turn ? "�A��" : "���",
+		   lastcount / 60, lastcount % 60);
+	    if(lastcount <= 0 && my->turn) {
+		move(19, 40);
+		outs("�ɶ��w��, �A��F");
+		my->five_lose++;
+		send(fd, '\0', 1, 0);
+		break;
+	    }
+	    if(lastcount <= -5 && !my->turn) {
+		move(19, 40);
+		outs("���Ӥ[�S�U, �AĹ�F!");
+		win = 1;
+		cuser.five_lose--;
+		cuser.five_win++;
+		my->five_win++;
+		passwd_update(usernum, &cuser);
+		mv.x = mv.y = -2;
+		send(fd, &mv, sizeof(Horder_t), 0);
+		mv = *(v - 1);		
+		break;    		
+	    }
+	}
+	move(14, 40);
+	if(hewantpass) {
+	    outs("\033[1;32m�M�ѭn�D!\033[m");
+	    bell();
+	} else
+	    clrtoeol();
+	BGOTOCUR(mv.x, mv.y);
+	ch = igetkey();
+	if(ch != I_OTHERDATA)
+	    iwantpass = 0;
+	if(ch == 'q') {
+	    if(countgomo() < 10) {
+		cuser.five_lose--;		 	
+		passwd_update(usernum, &cuser);
+	    }
+	    send(fd, '\0', 1, 0);
+	    break;
+	} else if(ch == 'u' && !my->turn && v > pool) {
+	    mv.x = mv.y = -1;
+	    ch = send(fd, &mv, sizeof(Horder_t), 0);
+	    if(ch == sizeof(Horder_t)) {
+      		HO_undo(&mv);
+		tick = mylasttick;
+		my->turn = 1;
+		continue;
+	    } else 
+		break;
+	}
+	if(ch == 'p') {
+	    if(my->turn ) {
+		if(iwantpass == 0) {
+		    iwantpass = 1;
+		    mv.x = mv.y = -2;
+		    send(fd, &mv, sizeof(Horder_t), 0);
+		    mv = *(v - 1);
+		}
+		continue;
+	    } else if(hewantpass) {
+		win = 0;
+		cuser.five_lose--;
+		cuser.five_tie++;
+		my->five_tie++;
+		passwd_update(usernum, &cuser);
+		mv.x=mv.y=-2;
+		send(fd, &mv, sizeof(Horder_t), 0);
+		mv = *(v - 1);
+		break;
+	    }
+	}
+	
+	if(ch == I_OTHERDATA) {
+	    ch = recv(fd, &mv, sizeof(Horder_t), 0);
+	    if(ch != sizeof(Horder_t)) {
+		lastcount=tick-time(0);
+		if(lastcount >=0) {
+		    win = 1;
+		    cuser.five_lose--;
+		    if(countgomo() >=10) {
+			cuser.five_win++;
+			my->five_win++;
+		    }
+		    passwd_update(usernum, &cuser);
+		    outmsg("���{��F!!");
+		    break;
+		} else {
+		    win = 0;
+		    outmsg("�A�W�L�ɶ����U�l, ��F!");
+		    my->five_lose++;
+		    break;
+		}
+	    } else if(mv.x == -2 && mv.y == -2) {
+		if(iwantpass == 1) {
+		    win = 0;
+		    cuser.five_lose--;
+		    cuser.five_tie++;
+		    my->five_tie++;
+		    passwd_update(usernum, &cuser);
+		    break;
+		} else {
+		    hewantpass = 1;
+		    mv = *(v - 1);
+		    continue;
+		}
+	    }
+	    if(my->turn && mv.x == -1 && mv.y == -1) {
+		outmsg("��讬��");
+		tick = hislasttick;
+		HO_undo(&mv);
+		my->turn = 0;
+		continue;
+	    }
+	    
+	    if(!my->turn) {
+		win = chkmv(&mv, he, he == BBLACK);
+		HO_add(&mv);
+		hislasttick = tick;
+		tick = time(0) + MAX_TIME;
+		ku[(int)mv.x][(int)mv.y] = he;
+		bell();
+		BGOTO(mv.x, mv.y);
+		outs(chess[he - 1]);
+		
+		if(win) {
+		    outmsg(win == 1 ? "����F!" : "���T��");
+		    if(win != 1) {
+			cuser.five_lose--;
+			cuser.five_win++;
+			my->five_win++;
+			passwd_update(usernum, &cuser);
+		    } else
+		    	my->five_lose++;
+		    win = -win;
+		    break;
+		}
+		my->turn = 1;
+	    }
+	    continue;
+	}
+	
+	if(my->turn) {
+	    if(gomo_key(fd, ch, &mv))
+		my->turn = 0;
+	    else
+		continue;
+	    
+	    if(!my->turn) {
+		HO_add(&mv);
+		BGOTO(mv.x, mv.y);
+		outs(chess[me - 1]);
+		win = chkmv( &mv, me, me == BBLACK);
+		ku[(int)mv.x][(int)mv.y] = me;
+		mylasttick = tick;
+		tick = time(0) + MAX_TIME;   /*�˼�*/
+		lastcount = MAX_TIME;
+		if(send(fd, &mv, sizeof(Horder_t), 0) != sizeof(Horder_t))
+		    break;
+		if(win) {
+		    outmsg(win == 1 ? "���o~~" : "�T���F" );
+		    if(win == 1) {
+			cuser.five_lose--;
+			cuser.five_win++;
+			my->five_win++;
+			passwd_update(usernum, &cuser);
+		    } else 
+		    	my->five_lose++;
+		    break;
+		}
+		move(15, 40);
+		clrtoeol();
+	    }
+	}
+    }
+    add_io(0, 0);
+    close(fd);
+    
+    igetch();
+    if(v > pool) { 
+	char ans[4];
+	
+	getdata(19 , 0, "�n�O�d����������(y/N)", ans, 4, LCECHO);
+	if(*ans == 'y')
+	    HO_log(my->mateid);
+    }
+    return 0;
+}
diff --git a/mbbsd/gomo1.c b/mbbsd/gomo1.c
new file mode 100644
index 00000000..953bad2d
--- /dev/null
+++ b/mbbsd/gomo1.c
@@ -0,0 +1,136 @@
+/* $Id: gomo1.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "proto.h"
+
+#define QCAST   int (*)(const void *, const void *)
+
+#define BBLANK (0)  /* �ť� */
+#define BBLACK (1)  /* �¤l, ���� */
+#define BWHITE (2)  /* �դl, ��� */
+#ifndef BRDSIZ
+#define BRDSIZ (15) /* �ѽL����j�p */
+#endif
+
+extern char ku[BRDSIZ][BRDSIZ];
+
+/* pattern and advance map */
+extern unsigned char *pat, *adv;
+
+static int intrevcmp(const void *a, const void *b) {
+    return (*(int *)b - *(int *)a);
+}
+
+/* x,y: 0..BRDSIZ-1 ; color: CBLACK,CWHITE ; dx,dy: -1,0,+1 */
+static int gomo_getindex(int x, int y, int color, int dx, int dy) {
+    int i, k, n;
+    for(n = -1, i = 0, k = 1; i < 5; i++, k <<= 1) {
+	x += dx;
+	y += dy;
+	
+	if((x < 0) || (x >= BRDSIZ) || ( y < 0) || ( y >= BRDSIZ)) {
+	    n += k;
+	    break;
+	} else if(ku[x][y] != BBLANK) {
+	    n += k;
+	    if(ku[x][y] != color)
+		break;
+	}
+    }
+    
+    if(i >= 5)
+	n += k;
+
+    return n;
+}
+
+int chkwin(int style, int limit) {
+    if(style == 0x0c)
+	return 1 /* style */;
+    else if(limit == 0) {
+	if(style == 0x0b)
+	    return 1 /* style */;
+	return 0;
+    }
+    if((style < 0x0c) && (style > 0x07))
+	return -1 /* -style */;
+    return 0;
+}
+
+/* x,y: 0..BRDSIZ-1 ; color: CBLACK,CWHITE ; limit:1,0 ; dx,dy: 0,1 */
+static int dirchk(int x, int y, int color, int limit, int dx, int dy) {
+    int le, ri, loc, style;
+
+    le = gomo_getindex(x, y, color, -dx, -dy);
+    ri = gomo_getindex(x, y, color, dx,  dy);
+
+    loc = (le > ri) ? (((le * (le + 1)) >> 1) + ri) :
+	(((ri * (ri + 1)) >> 1) + le);
+    
+    style = pat[loc];
+    
+    if(limit == 0)
+	return (style & 0x0f);
+
+    style >>= 4;
+    
+    if((style == 3) || (style == 2)) {
+	int i, n, tmp, nx, ny;
+	
+	n = adv[loc >> 1];
+	
+	((loc & 1) == 0) ? (n >>= 4) : (n &= 0x0f);
+	
+	ku[x][y] = color;
+	
+	for(i = 0; i < 2; i++) {
+	    if((tmp = (i == 0) ? (-(n >> 2)):(n & 3)) != 0) {
+		nx = x + (le > ri ? 1 : -1) * tmp * dx;
+		ny = y + (le > ri ? 1 : -1) * tmp * dy;
+		
+		if((dirchk(nx, ny, color, 0, dx, dy) == 0x06) &&
+		   (chkwin(getstyle(nx, ny, color, limit), limit) >= 0))
+		    break;
+	    }
+	}
+	if(i >= 2)
+	    style = 0;
+	ku[x][y] = BBLANK;
+    }
+    return style;
+}
+
+/* �ҥ~=F ���~=E ���l=D �s��=C �s��=B ���|=A �|�|=9 �T�T=8 */
+/* �|�T=7 ���|=6 �_�|=5 ���|=4 ���T=3 �_�T=2 �O�d=1 �L��=0 */
+
+/* x,y: 0..BRDSIZ-1 ; color: CBLACK,CWHITE ; limit: 1,0 */
+int getstyle(int x, int y, int color, int limit) {
+    int i, j, dir[4], style;
+
+    if((x < 0) || (x >= BRDSIZ) || ( y < 0) || (y >= BRDSIZ))
+	return 0x0f;
+    if(ku[x][y] != BBLANK)
+	return 0x0d;
+
+    for(i = 0; i < 4; i++)
+	dir[i] = dirchk(x, y, color, limit, i ? (i>>1) : -1, i ? (i&1) : 1);
+
+    qsort(dir, 4, sizeof(int), (QCAST)intrevcmp);
+
+    if((style = dir[0]) >= 2) {
+	for(i = 1, j = 6 + (limit ? 1 : 0); i < 4; i++) {
+	    if((style > j) || (dir[i] < 2))
+		break;
+	    if(dir[i] > 3)
+		style = 9;
+	    else if((style < 7) && (style > 3))
+		style = 7;
+	    else
+		style = 8;
+	}
+    }
+    return style;
+}
diff --git a/mbbsd/guess.c b/mbbsd/guess.c
new file mode 100644
index 00000000..27a337f7
--- /dev/null
+++ b/mbbsd/guess.c
@@ -0,0 +1,364 @@
+/* $Id: guess.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *BBSName;
+extern int usernum;
+#define LOGPASS BBSHOME "/etc/winguess.log"
+
+static void show_table(char TABLE[], char ifcomputer) {
+    int i;
+    
+    move(0, 35);
+    prints("\033[1;44;33m  �i �q�Ʀr �j  \033[m");
+    move(8, 1);
+    prints("\033[1;44;36m��   �e   ��   �v\033[m\n");
+    prints("\033[1;33m=================\033[m\n");
+    if(ifcomputer) {
+	prints("�q��: 2 ��\n");
+	prints("��q��: 0 ��\n");
+    } else {
+	for(i = 1; i <= 6; i++)
+	    prints("��%d��, %02d��\n",i,TABLE[i]);
+    }
+    prints("\033[33m=================\033[m");
+}
+
+extern userec_t cuser;
+
+static unsigned long int get_money(void) {
+    int money, i;
+    char data[20];
+    
+    move(1, 0);
+    prints("�z�ثe��:%d Ptt$", cuser.money);
+    do {
+	getdata(2, 0, "�n��h��(5-10��q���}): ", data, 9, LCECHO);
+	money = strlen(data);
+	if(data[0] == 'q' || data[0] == 'Q') {
+	    unlockutmpmode();
+	    return 0;
+	}
+	for(i = 0; i < money; i++)
+	    if(data[i]<'0' || data[i]>'9') {
+		money = -1;
+		break;
+	    }
+        if(money != -1){
+	    money = atol(data);
+	    reload_money();
+	    if(money > cuser.money || money <= 4 || money > 10 ||
+	       money < 1)
+		money = -1;
+        }
+    } while(money == -1);
+    move(1,0);
+    clrtoeol();
+    reload_money();
+    prints("�z�ثe��:%d Ptt$", cuser.money - money);
+    return money;
+}
+
+static int check_data(char *str) {
+    int i, j;
+    
+    if(strlen(str) != 4)
+	return -1;
+    for(i = 0; i < 4; i++)
+	if(str[i] < '0' || str[i] > '9')
+	    return -1;
+    for(i = 0; i < 4; i++)
+	for(j = i + 1; j < 4; j++)
+	    if(str[i] == str[j])
+		return -1;
+    return 1;
+}
+
+static char *get_data(int count) {
+    static char data[5];
+    while(1) {
+	getdata(6, 0, "��J�|��Ʀr(������): ", data, 5, LCECHO);
+	if(check_data(data) == 1)
+	    break;
+    }
+    return data;
+}
+
+static int guess_play(char *data, char *answer, int count) {
+    int A_num = 0, B_num = 0;
+    int i, j;
+    
+    for(i = 0; i < 4; i++) {
+	if(data[i] == answer[i])
+	    A_num++;
+	for(j = 0; j < 4; j++)
+	    if(i == j)
+		continue;
+	    else if(data[i] == answer[j]) {
+		B_num++;
+		break;
+	    }
+    }
+    if(A_num == 4)
+	return 1;
+    move(count + 8,55);
+    prints("%s => \033[1;32m%dA %dB\033[m", data, A_num, B_num);
+    return 0;
+}
+
+static int result(int correct, int number) {
+    char a = 0, b = 0, i, j;
+    char n1[5], n2[5];
+    
+    sprintf(n1, "%04d",correct);
+    sprintf(n2, "%04d",number);
+    for(i = 0; i < 4; i++)
+	for(j = 0; j < 4; j++)
+	    if(n1[(int)i] == n2[(int)j])
+		b++;
+    for(i = 0; i < 4; i++)
+	if(n1[(int)i] == n2[(int)i]) {
+	    b--;
+	    a++;
+	}
+    return 10 * a + b;
+}
+
+static int legal(int number) {
+    char i, j;
+    char temp[5];
+    
+    sprintf(temp, "%04d", number);
+    for(i = 0; i < 4; i++)
+	for(j = i + 1; j < 4; j++)
+	    if(temp[(int)i] == temp[(int)j])
+		return 0;
+    return 1;
+}
+
+static void initcomputer(char flag[]) {
+    int i;
+    
+    for(i = 0; i < 10000; i++)
+	if(legal(i))
+	    flag[i] = 1;
+	else 
+	    flag[i] = 0;
+}
+
+static int computer(int correct, int total, char flag[], int n[]) {
+    int guess;
+    static int j;
+    int k,i;
+    char data[5];
+    
+    if(total == 1) {
+	do {
+	    guess = rand() % 10000;
+	} while(!legal(guess));
+    } else 
+	guess = n[rand() % j];
+    k = result(correct, guess);
+    if(k == 40) {
+	move(total + 8, 25);
+	sprintf(data, "%04d", guess);
+	prints("%s => �q���F!!", data);
+	return 1;
+    } else {
+	move(total + 8, 25);
+	sprintf(data, "%04d", guess);
+	prints("%s => \033[1;32m%dA %dB\033[m", data, k / 10, k % 10);
+    }
+    j = 0;
+    for(i = 0; i < 10000; i++)
+	if(flag[i]) {
+	    if(result(i, guess) != k)
+		flag[i] = 0;
+	    else 
+		n[j++] = i;
+	}
+    return 0;
+}
+
+static void Diff_Random(char *answer) {
+    register int i = 0, j, k;
+    
+    while(i < 4) {
+	k = rand() % 10 + '0';
+	for(j = 0; j < i; j++)
+	    if(k == answer[j])
+		break;
+	if(j == i) {
+	    answer[j] = k;
+	    i++;
+	}
+    }
+    answer[4] = 0;
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+
+int guess_main() {
+    unsigned long int money;
+    char computerwin = 0,youwin = 0;
+    int count = 0,c_count = 0;
+    char ifcomputer;
+    char answer[5];
+    int *n = NULL;
+    char yournum[5];
+    char *flag = NULL;
+    static char TABLE[]={0,10,8,4,2,1,0,0,0,0,0};    
+    FILE *file;
+
+    clear();
+    showtitle("�q�Ʀr", BBSName);
+    lockreturn0(GUESSNUM, LOCK_MULTI);
+
+    reload_money();
+    if(cuser.money < 5) {
+	clear();
+	move(12, 35);
+	prints("�������� �ܤ֭n 5 Ptt$");
+	unlockutmpmode();
+	pressanykey();
+	return 1;
+    }
+    if((money = get_money()) == 0)
+	return 1;         
+    vice(money,"�q�Ʀr");
+    
+    Diff_Random(answer);
+    move(2, 0);
+    clrtoeol();
+    prints("�z�U�` :%d Ptt$", money);
+    
+    getdata_str(4, 0, "�z�n�M�q�����ɶ�? <y/n>[y]:", &ifcomputer, 2,
+		LCECHO, "y");
+    if(ifcomputer == 'y') {
+	ifcomputer = 1;
+	show_table(TABLE, 1);
+    } else {
+	ifcomputer = 0;
+	show_table(TABLE, 0);
+    }
+    if(ifcomputer) {
+	do {
+	    getdata(5, 0, "�п�J�z�n���q���q���Ʀr: ", yournum, 5, LCECHO);
+	} while(!legal(atoi(yournum)));
+	move(8, 25);
+	prints("�q���q");
+	flag = malloc(sizeof(char) * 10000);
+	n = malloc(sizeof(int) * 1500);
+	initcomputer(flag);
+    }
+    move(8, 55);
+    prints("�A�q");
+    while(((!computerwin || !youwin) && count <10 && (ifcomputer)) ||
+	  (!ifcomputer && count < 10 && !youwin)) {
+	if(!computerwin && ifcomputer) {
+	    ++c_count;
+	    if(computer(atoi(yournum), c_count, flag, n))
+      		computerwin = 1;
+	}
+	move(20, 55);
+	prints("�� %d �����| ", count + 1);
+	if(!youwin) {
+	    ++count;
+	    if(guess_play(get_data(count),answer,count))
+      		youwin=1;
+	}
+    }
+    move(17, 35);
+    if(ifcomputer) {
+	free(flag);
+	free(n);
+	if(count > c_count) {
+	    prints("�A�鵹�q���F");
+	    move(18, 35);
+	    prints("�A�ߤF %d ", money);
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "�q����%d���q��, ", c_count);
+		if(youwin)
+		    fprintf(file, "%s ��%d���q��, ", cuser.userid, count);
+		else
+		    fprintf(file, "%s �S�q��, ", cuser.userid);				
+		fprintf(file,"�q���Ȩ��F%s %ld Ptt$\n", cuser.userid, money);
+		fclose(file);
+	    }
+	    unlockutmpmode();
+	    pressanykey();
+	    return 1;                           	 	   
+	} else if(count < c_count) {
+	    prints("�u�F�`, ���A�Ȩ��o");
+	    move(18,35);prints("�A�Ȩ��F %d ",money*2);
+	    demoney(money*2);
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "id: %s, ��%d���q��, �q����%d���q��, "
+			"�F�q�� %ld Ptt$\n", cuser.userid, count,
+			c_count, money * 2);
+		fclose(file);
+	    }
+	    unlockutmpmode();
+	    pressanykey();
+	    return 1;
+	} else {
+	    prints("�u�F�`, �M�q����������F, ���^����%d\n", money);
+	    demoney(money);
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "id: %s �M�q�������F����\n", cuser.userid);
+		fclose(file);
+	    }
+	    unlockutmpmode();
+	    pressanykey();
+	    return 1;
+	}
+    }
+    if(youwin) {
+	demoney(TABLE[count]*money);
+	if(count < 5) {
+	    prints("�u�F�`, ���Q�A�Ȩ��F");
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "id: %s, ��%d���q��, �F %ld Ptt$\n",
+			cuser.userid, count, TABLE[count] * money);
+		fclose(file);
+	    }
+	} else if(count > 5) {
+	    prints("��, �Ӧh���~�q�X�ӤF");
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "id: %s, ��%d���~�q��, �ߤF %ld Ptt$\n",
+			cuser.userid, count, money);
+		fclose(file);
+	    }
+	}
+	else {
+	    prints("�����q�X��, �٧A�����a");
+	    move(18,35);
+	    clrtoeol();
+	    prints("�A���^�F%d Ptt$\n", money);
+	    if((file = fopen(LOGPASS,"a"))) {
+		fprintf(file, "id: %s, ��%d���q��, ���^�F���� %ld Ptt$\n",
+			cuser.userid, count, money);
+		fclose(file);
+	    }
+	}
+	unlockutmpmode();
+	pressanykey();
+	return 1;
+    }
+    move(17,35);
+    prints("�K�K �зǵ��׬O %s ", answer);
+    move(18,35);
+    prints("�U���A�ӧa");
+    if((file = fopen(BBSHOME "/etc/loseguess.log","a"))) {
+	fprintf(file,"id: %s ��F %ld Ptt$\n",cuser.userid,money);
+	fclose(file);
+    }
+    return 1;
+}
diff --git a/mbbsd/indict.c b/mbbsd/indict.c
new file mode 100644
index 00000000..407b5ae9
--- /dev/null
+++ b/mbbsd/indict.c
@@ -0,0 +1,184 @@
+/* $Id: indict.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+#define REFER "etc/dicts"
+
+extern userec_t cuser;
+char dict[41],database[41];
+
+static void addword(char word[]) {
+    char buf[150],temp[150],a[3];
+    FILE *fp = fopen(database,"r+");
+
+    fgets(buf,130,fp);
+    fseek(fp,0,2);
+    if(HAVE_PERM(PERM_LOGINOK)) {
+	clear();
+	move(4,0);
+	outs(" \033[31mĵ�i\033[m:�Y�W�N��g����ƱN\033[36m��id\033[m�B��\n");
+	sprintf(temp, "\n��J�d�Q\n:\033[33m%s\033[m", buf);
+	outs(temp);
+	outs("\n�Ш̤W�C�d�ҿ�J�@����(����enter���)\n");
+	getdata(10, 0, ":", buf, 65, DOECHO);
+	if(buf[0]) {
+	    getdata(13, 0, "�T�w�s�W?(Y/n)", a, 2, LCECHO);
+	    if(a[0] != 'n')
+		fprintf(fp, "%-65s[%s]\n", buf, cuser.userid);
+	}
+    }
+    fclose(fp);
+    clear();
+}
+
+static int choose_dict(void) {
+    int c;
+    FILE *fp;
+    char buf[10][21], data[10][21], cho[130];
+
+    move(12, 0);
+    clrtobot();
+    outs("                        "
+	 "�� \033[45;33m�r��� �� �n�d���@���H\033[m ��");
+
+    if((fp = fopen(REFER, "r"))) {
+	for(c = 0; fscanf(fp, "%s %s", buf[c], data[c]) != EOF; c++ ) {
+            sprintf(cho,"\n                     "
+		    "(\033[36m%d\033[m) %-20s�j�r��",c+1,buf[c]);
+            outs(cho);
+	}
+	
+	getdata(22, 14, "          �� �п�ܡA[Enter]���}�G", cho, 3, LCECHO);
+	cho[0] -= '1';
+	if(cho[1])
+	    cho[0] = (cho[0] + 1) * 10 + (cho[1] - '1');
+	
+	if(cho[0] >= 0 && cho[0] < c) {
+	    strcpy(dict, buf[(int)cho[0]]);
+	    strcpy(database, data[(int)cho[0]]);
+	    return 1;
+	} else
+	    return 0;
+    }
+    return 0;
+}
+
+static char *lower(char str[]) {
+    int c;
+    static char temp[200];
+
+    strcpy(temp,str);
+    for(c = 0; temp[c] !=0; c++)
+	if(temp[c] >= 'A' && temp[c] <= 'Z')
+	    temp[c] += 'a' - 'A';
+    return temp;
+}
+
+int use_dict() {
+    FILE *fp;
+    char lang[150], word[80] = "";
+    char j, f, buf[120], sys[] = "|\033[31me\033[m:�s�y�r��";
+    int i = 0;
+    
+    setutmpmode(DICT);
+    if(!HAS_PERM(PERM_SYSOP))
+	sys[0]=0;
+    
+    clear();
+    
+    sprintf(buf,"\033[45m                           ��\033[1;44;33m"
+	    "  %-14s\033[3;45m ��                              ", dict);
+    strcpy(&buf[100],"\033[m\n");
+    for(;;) {
+	move(0, 0);
+	sprintf(lang, "  �п�J����r��(%s) �Ϋ��O(h,t,a)\n", dict);
+	outs(lang);
+	sprintf(lang, "[\033[32m<����r>\033[m|\033[32mh\033[m:help|\033[32m"
+		"t\033[m:�Ҧ����|\033[32ma\033[m:�s�W���%s]\n:", sys);
+	outs(lang);
+	getdata(2, 0, ":", word, 18, DOECHO);
+	outs("��Ʒj�M���еy��....");
+	strcpy(word,lower(word));
+	if(word[0] == 0)
+	    return 0;
+	clear();
+	move(4, 0);
+	outs(buf);
+	if(strlen(word) == 1) {
+	    if(word[0] == 'a') {
+		clear();
+		move(4,0);
+		outs(buf);
+		addword(word);
+		continue;
+	    } else if(word[0] == 't')
+		word[0] = 0;
+	    else if(word[0] == 'h') {
+		more("etc/dict.hlp",YEA);
+		clear();
+		continue;
+	    } else if(word[0]=='e') {
+		vedit(database,NA, NULL);
+		clear();
+		continue;
+	    } else {
+		outs("�r��ӵu,�п�J�h�@�I����r");
+		continue;
+	    }
+	}
+
+	if((fp = fopen(database,"r"))) {
+	    i = 0;
+	    while(fgets(lang,150,fp) != NULL) {
+		if(lang[65] == '[') {
+		    lang[65] = 0;
+		    f = 1;
+		} else
+		    f = 0;
+		if(strstr(lower(lang),word)) {
+		    if(f == 1)
+			lang[65] = '[';
+		    outs(lang);
+		    i++;
+		    if(!((i+1)%17)) {
+			move(23, 0);
+			outs("\033[45m                               "
+			     "���N���~��  Q:���}                             "
+			     "\033[m ");
+			j = igetch();
+                        if(j == 'q')
+			    break;
+                        else {
+			    clear();
+			    move(4,0);
+			    outs(buf);
+                        }
+		    }
+		}
+	    }
+	}
+	fclose(fp);
+	if(i == 0) {
+	    getdata(5, 0, "�S�o�Ӹ�ƭC,�s�W��?(y/N)", lang, 3, LCECHO);
+	    if(lang[0] == 'y') {
+		clear();
+		move(4,0);
+		outs(buf);
+		addword(word);
+	    }
+	}
+    }
+}
+
+int x_dict() {
+    if(choose_dict())
+	use_dict();
+    return 0;
+}
diff --git a/mbbsd/io.c b/mbbsd/io.c
new file mode 100644
index 00000000..e3d03b9b
--- /dev/null
+++ b/mbbsd/io.c
@@ -0,0 +1,611 @@
+/* $Id: io.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "modes.h"
+#include "common.h"
+#include "proto.h"
+
+#if defined(linux)
+#define OBUFSIZE  2048
+#define IBUFSIZE  128
+#else
+#define OBUFSIZE  4096
+#define IBUFSIZE  256
+#endif
+
+extern int current_font_type;
+extern char *fn_proverb;
+extern userinfo_t *currutmp;
+extern unsigned int currstat;
+extern pid_t currpid;
+extern int errno;
+extern screenline_t *big_picture;
+extern int t_lines, t_columns;  /* Screen size / width */
+extern int curr_idle_timeout;
+extern water_t water[6], *swater[5], *water_which;
+extern char water_usies;
+
+static char outbuf[OBUFSIZE], inbuf[IBUFSIZE];
+static int obufsize = 0, ibufsize = 0;
+static int icurrchar = 0;
+
+/* ----------------------------------------------------- */
+/* �w����ܰʺA�ݪO                                      */
+/* ----------------------------------------------------- */
+extern userec_t cuser;
+
+static void hit_alarm_clock() {
+    if(HAS_PERM(PERM_NOTIMEOUT) || PERM_HIDE(currutmp) || currstat == MAILALL)
+	return;
+//    if(time(0) - currutmp->lastact > IDLE_TIMEOUT - 2) {
+    if(time(0) - currutmp->lastact > curr_idle_timeout - 2) {
+	clear();
+	if(currpid > 0) kill(currpid, SIGHUP);
+    }
+//    alarm(IDLE_TIMEOUT);
+    alarm(curr_idle_timeout);
+}
+
+void init_alarm() {
+    signal(SIGALRM, (void (*)(int))hit_alarm_clock);
+//    alarm(IDLE_TIMEOUT);
+    alarm(curr_idle_timeout);
+}
+
+/* ----------------------------------------------------- */
+/* output routines                                       */
+/* ----------------------------------------------------- */
+
+void oflush() {
+    if(obufsize) {
+	write(1, outbuf, obufsize);
+	obufsize = 0;
+    }
+}
+
+void init_buf()
+{
+
+   memset(inbuf,0,IBUFSIZE);
+}
+void output(char *s, int len) {
+    /* Invalid if len >= OBUFSIZE */
+
+    if(obufsize + len > OBUFSIZE) {
+	write(1, outbuf, obufsize);
+	obufsize = 0;
+    }
+    memcpy(outbuf + obufsize, s, len);
+    obufsize += len;
+}
+
+int ochar(int c) {
+    if(obufsize > OBUFSIZE - 1) {
+	write(1, outbuf, obufsize);
+	obufsize = 0;
+    }
+    outbuf[obufsize++] = c;
+    return 0;
+}
+
+/* ----------------------------------------------------- */
+/* input routines                                        */
+/* ----------------------------------------------------- */
+
+static int i_newfd = 0;
+static struct timeval i_to, *i_top = NULL;
+static int (*flushf) () = NULL;
+
+void add_io(int fd, int timeout) {
+    i_newfd = fd;
+    if(timeout) {
+	i_to.tv_sec = timeout;
+	i_to.tv_usec = 16384;  /* Ptt: �令16384 �קK������for loop�Ycpu time
+				  16384 ���C��64�� */
+	i_top = &i_to;
+    } else
+	i_top = NULL;
+}
+
+int num_in_buf() {
+    return icurrchar - ibufsize;
+}
+
+int watermode = -1; 
+/* Ptt ���y�^�U�Ϊ��Ѽ� */
+/* watermode = -1  �S�b�^���y
+             = 0   �b�^�W�@�����y  (Ctrl-R)
+	     > 0   �b�^�e n �����y (Ctrl-R Ctrl-R) */
+
+/*
+	dogetch() is not reentrant-safe. SIGUSR[12] might happen at any time,
+	and dogetch() might be called again, and then ibufsize/icurrchar/inbuf
+	might be inconsistent.
+	We try to not segfault here... 
+*/
+
+static int dogetch() {
+    int len;
+
+    if(ibufsize <= icurrchar) {
+
+	if(flushf)
+	    (*flushf)();
+
+	refresh();
+
+	if(i_newfd) {
+	
+	    struct timeval timeout;
+	    fd_set readfds;
+			
+	    if(i_top) timeout=*i_top; /* copy it because select() might change it */
+			
+	    FD_ZERO(&readfds);
+	    FD_SET(0, &readfds);
+	    FD_SET(i_newfd, &readfds);
+
+	    /* jochang: modify first argument of select from FD_SETSIZE */
+	    /* since we are only waiting input from fd 0 and i_newfd(>0) */
+		
+	    while((len = select(i_newfd+1, &readfds, NULL, NULL, i_top?&timeout:NULL))<0)
+	    {
+		if(errno != EINTR)
+		    abort_bbs(0);
+		    /* raise(SIGHUP); */
+	    }
+
+	    if(len == 0)
+		return I_TIMEOUT;
+
+	    if(i_newfd && FD_ISSET(i_newfd, &readfds))
+		return I_OTHERDATA;
+	}
+
+	while((len = read(0, inbuf, IBUFSIZE)) <= 0) {
+	    if(len == 0 || errno != EINTR)
+	    	abort_bbs(0);
+		/* raise(SIGHUP); */
+	}
+	ibufsize = len;
+	icurrchar = 0;
+    }
+
+    if(currutmp) 
+	currutmp->lastact = time(0);
+    return inbuf[icurrchar++];
+}
+
+static int water_which_flag=0;
+int igetch() {
+    register int ch;
+    while((ch = dogetch())) {
+	switch(ch) {
+	case Ctrl('L'):
+	    redoscr();
+	    continue;
+	case Ctrl('U'):
+	    if(currutmp != NULL &&  currutmp->mode != EDITING
+	       && currutmp->mode !=  LUSERS && currutmp->mode) {
+
+		screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t));
+		int y, x, my_newfd;
+
+		getyx(&y, &x);
+		memcpy(screen0, big_picture, t_lines * sizeof(screenline_t));
+		my_newfd = i_newfd;
+		i_newfd = 0;
+		t_users();
+		i_newfd = my_newfd;
+		memcpy(big_picture, screen0, t_lines * sizeof(screenline_t));
+		move(y, x);
+		free(screen0);
+		redoscr();
+		continue;
+	    } else
+		return (ch);
+        case KEY_TAB:
+	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) )
+		if( currutmp != NULL && watermode > 0 ){
+		    watermode = (watermode + water_which->count)
+			% water_which->count + 1;
+		    t_display_new();
+		    continue;
+		}
+	    return ch;
+	    break;
+
+        case Ctrl('R'):
+	    if(currutmp == NULL)
+		return (ch);
+	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){
+		if( watermode > 0 ){
+		    watermode = (watermode + water_which->count)
+			% water_which->count + 1;
+		    t_display_new();
+		    continue;
+		}
+		else if( currutmp->mode == 0 &&
+			 (currutmp->chatid[0]==2 || currutmp->chatid[0]==3) &&
+			 water_which->count != 0 && watermode == 0) {
+		    /* �ĤG���� Ctrl-R */
+		    watermode = 1;
+		    t_display_new();
+		    continue;
+		}
+		else if(currutmp->msgs[0].pid) {
+		    /* �Ĥ@���� Ctrl-R (�������Q��L���y) */
+		    screenline_t *screen0;
+		    int y, x, my_newfd;
+		    screen0 = calloc(t_lines, sizeof(screenline_t));
+		    getyx(&y, &x);
+		    memcpy(screen0, big_picture, t_lines*sizeof(screenline_t));
+		    
+		    /* �p�G���btalk���ܥ����B�z���e�L�Ӫ��ʥ] (���hselect) */
+		    my_newfd = i_newfd;
+		    i_newfd = 0;
+		    show_last_call_in(0);
+		    watermode = 0;
+		    my_write(currutmp->msgs[0].pid, "���y��L�h �G ",
+			     currutmp->msgs[0].userid, 0);
+		    i_newfd = my_newfd;
+		
+		    /* �٭�ù� */
+		    memcpy(big_picture, screen0, t_lines*sizeof(screenline_t));
+		    move(y, x);
+		    free(screen0);
+		    redoscr();
+		    continue;
+		}
+		else
+		    return ch;
+	    }
+
+	    if( currutmp->msgs[0].pid && 
+		WATERMODE(WATER_OFO) && watermode == -1 ){
+		int y, x, my_newfd;
+		screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t));
+		memcpy(screen0, big_picture, t_lines * sizeof(screenline_t));
+		getyx(&y, &x);		
+		my_newfd = i_newfd;
+		i_newfd = 0;
+		my_write2();
+		memcpy(big_picture, screen0, t_lines * sizeof(screenline_t));
+		i_newfd = my_newfd;
+		move(y, x);
+		free(screen0);
+		redoscr();
+		continue;
+	    }
+
+	    return ch;
+        case '\n':   /* Ptt�� \n���� */
+	    continue;
+        case Ctrl('T'):
+	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){
+		if(watermode > 0) {
+		    if(watermode>1)
+			watermode--;
+		    else
+			watermode = water_which->count;	
+		    t_display_new();
+		    continue;
+		}
+	    }
+	    return (ch);
+
+        case Ctrl('E'):
+	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){
+		if(watermode >0){
+		    if( water_which_flag == (int)water_usies )
+			water_which_flag = 0;
+		    else
+			water_which_flag =
+		                   (water_which_flag+1) % (int)(water_usies+1);
+		    if(water_which_flag==0)
+			water_which = &water[0];
+		    else
+			water_which = swater[water_which_flag-1];
+		    watermode = 1;
+		    t_display_new();
+		    continue;
+		}
+	    }
+	    return ch;
+
+        case Ctrl('W'):
+	    if(watermode >0)
+	      {
+		water_which_flag=(water_which_flag+water_usies)%(water_usies+1);
+		if(water_which_flag==0)
+			water_which = &water[0];
+		else
+			water_which = swater[water_which_flag-1];
+		watermode = 1;
+		t_display_new();
+		continue;
+	      }
+	    else return ch;
+        default:
+	    return ch;
+	}
+    }
+    return 0;
+}
+
+int oldgetdata(int line, int col, char *prompt, char *buf, int len, int echo) {
+    register int ch, i;
+    int clen;
+    int x = col, y = line;
+    extern unsigned char scr_cols;
+#define MAXLASTCMD 12
+    static char lastcmd[MAXLASTCMD][80];
+
+    strip_ansi(buf, buf, STRIP_ALL);
+
+    if(prompt) {
+
+	move(line, col);
+
+	clrtoeol();
+
+	outs(prompt);
+
+	x += strip_ansi(NULL,prompt,0);
+    }
+
+    if(!echo) {
+	len--;
+	clen = 0;
+	while((ch = igetch()) != '\r') {
+	    if(ch == '\177' || ch == Ctrl('H')) {
+		if(!clen) {
+		    bell();
+		    continue;
+		}
+		clen--;
+		if(echo) {
+		    ochar(Ctrl('H'));
+		    ochar(' ');
+		    ochar(Ctrl('H'));
+		}
+		continue;
+	    }
+// Ptt
+#ifdef BIT8
+	    if(!isprint2(ch))
+#else
+		if(!isprint(ch))
+#endif
+		{
+		    if(echo)
+			bell();
+		    continue;
+		}
+
+	    if(clen >= len)  {
+		if(echo)
+		    bell();
+		continue;
+	    }
+	    buf[clen++] = ch;
+	    if(echo)
+		ochar(ch);
+	}
+	buf[clen] = '\0';
+	outc('\n');
+	oflush();
+    } else {
+	int cmdpos = -1;
+	int currchar = 0;
+
+	standout();
+	for(clen = len--; clen; clen--)
+	    outc(' ');
+	standend();
+	buf[len] = 0;
+	move(y, x);
+	edit_outs(buf);
+	clen = currchar = strlen(buf);
+
+	while(move(y, x + currchar), (ch = igetkey()) != '\r') {
+	    switch(ch) {
+	    case KEY_DOWN:
+	    case Ctrl('N'):
+                buf[clen] = '\0';  /* Ptt */
+                strncpy(lastcmd[cmdpos], buf, 79);
+                cmdpos += MAXLASTCMD - 2;
+	    case Ctrl('P'):
+	    case KEY_UP:
+                if(ch == KEY_UP || ch == Ctrl('P')) {
+		    buf[clen] = '\0';  /* Ptt */
+		    strncpy(lastcmd[cmdpos], buf, 79);
+                }
+                cmdpos++;
+                cmdpos %= MAXLASTCMD;
+                strncpy(buf, lastcmd[cmdpos], len);
+                buf[len] = 0;
+
+                move(y, x);                   /* clrtoeof */
+                for(i = 0; i <= clen; i++)
+		    outc(' ');
+                move(y, x);
+                edit_outs(buf);
+                clen = currchar = strlen(buf);
+                break;
+	    case KEY_LEFT:
+                if(currchar)
+		    --currchar;
+                break;
+	    case KEY_RIGHT:
+                if(buf[currchar])
+		    ++currchar;
+                break;
+	    case '\177':
+	    case Ctrl('H'):
+                if(currchar) {
+		    currchar--;
+		    clen--;
+		    for(i = currchar; i <= clen; i++)
+			buf[i] = buf[i + 1];
+		    move(y, x + clen);
+		    outc(' ');
+		    move(y, x);
+		    edit_outs(buf);
+                }
+                break;
+	    case Ctrl('Y'):
+                currchar = 0;
+	    case Ctrl('K'):
+                buf[currchar] = 0;
+                move(y, x + currchar);
+                for(i = currchar; i < clen; i++)
+		    outc(' ');
+                clen = currchar;
+                break;
+	    case Ctrl('D'):
+                if(buf[currchar]) {
+		    clen--;
+		    for(i = currchar; i <= clen; i++)
+			buf[i] = buf[i + 1];
+		    move(y, x + clen);
+		    outc(' ');
+		    move(y, x);
+		    edit_outs(buf);
+                }
+                break;
+	    case Ctrl('A'):
+                currchar = 0;
+                break;
+	    case Ctrl('E'):
+                currchar = clen;
+                break;
+	    default:
+                if(isprint2(ch) && clen < len && x + clen < scr_cols) {
+		    for(i = clen + 1; i > currchar;i--)
+			buf[i] = buf[i - 1];
+		    buf[currchar] = ch;
+		    move(y, x + currchar);
+		    edit_outs(buf + currchar);
+		    currchar++;
+		    clen++;
+		}
+                break;
+	    }/* end case */
+	}  /* end while */
+
+	if(clen > 1)
+	    for(cmdpos = MAXLASTCMD - 1; cmdpos; cmdpos--) {
+                strcpy(lastcmd[cmdpos], lastcmd[cmdpos - 1]);
+                strncpy(lastcmd[0], buf, len);
+	    }
+	if(echo)
+	    outc('\n');
+	refresh();
+    }
+    if((echo == LCECHO) && ((ch = buf[0]) >= 'A') && (ch <= 'Z'))
+	buf[0] = ch | 32;
+#ifdef SUPPORT_GB    
+    if(echo == DOECHO &&  current_font_type == TYPE_GB)
+    {
+	strcpy(buf,hc_convert_str(buf, HC_GBtoBIG, HC_DO_SINGLE));
+    }
+#endif
+    return clen;
+}
+
+/* Ptt */
+int getdata_buf(int line, int col, char *prompt, char *buf, int len, int echo) {
+    return oldgetdata(line, col, prompt, buf, len, echo);
+}
+
+char
+getans(char *prompt)
+{
+  char ans[5];
+
+  getdata(t_lines-1, 0, prompt, ans, 4, LCECHO);
+  return ans[0];
+}
+
+int getdata_str(int line, int col, char *prompt, char *buf, int len, int echo, char *defaultstr) {
+    strncpy(buf, defaultstr, len);
+    
+    buf[len] = 0;
+    return oldgetdata(line, col, prompt, buf, len, echo);
+}
+
+int getdata(int line, int col, char *prompt, char *buf, int len, int echo) {
+    buf[0] = 0;
+    return oldgetdata(line, col, prompt, buf, len, echo);
+}
+
+int
+rget(int x,char *prompt)
+{
+  register int ch;
+
+  move(x,0);
+  clrtobot(); 
+  outs(prompt);
+  refresh();
+
+  ch = igetch();
+  if( ch >= 'A' && ch <= 'Z') ch |= 32;
+
+   return ch;
+}
+
+
+int KEY_ESC_arg;
+
+int igetkey() {
+    int mode;
+    int ch, last;
+
+    mode = last = 0;
+    while(1) {
+	ch = igetch();
+	if(mode == 0) {
+	    if(ch == KEY_ESC)
+		mode = 1;
+	    else
+		return ch;              /* Normal Key */
+	} else if (mode == 1) {         /* Escape sequence */
+	    if(ch == '[' || ch == 'O')
+		mode = 2;
+	    else if(ch == '1' || ch == '4')
+		mode = 3;
+	    else {
+		KEY_ESC_arg = ch;
+		return KEY_ESC;
+	    }
+	} else if(mode == 2) {          /* Cursor key */
+	    if(ch >= 'A' && ch <= 'D')
+		return KEY_UP + (ch - 'A');
+	    else if(ch >= '1' && ch <= '6')
+		mode = 3;
+	    else
+		return ch;
+	} else if (mode == 3) {         /* Ins Del Home End PgUp PgDn */
+	    if(ch == '~')
+		return KEY_HOME + (last - '1');
+	    else
+		return ch;
+	}
+	last = ch;
+    }
+}
+
diff --git a/mbbsd/kaede.c b/mbbsd/kaede.c
new file mode 100644
index 00000000..c0bd5103
--- /dev/null
+++ b/mbbsd/kaede.c
@@ -0,0 +1,95 @@
+/* $Id: kaede.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "proto.h"
+
+extern struct utmpfile_t *utmpshm;
+extern userec_t cuser;
+
+char *Ptt_prints(char *str, int mode) {
+    char *po , strbuf[256];
+
+    while((po = strstr(str, "\033*s"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%s%s", str, cuser.userid, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*t"))) {
+	time_t now = time(0);
+
+	po[0] = 0;
+	sprintf(strbuf, "%s%s", str, Cdate(&now));
+	str[strlen(strbuf)-1] = 0;
+	strcat(strbuf, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*u"))) {
+	int attempts;
+
+	attempts = utmpshm->number;
+	po[0] = 0;
+	sprintf(strbuf, "%s%d%s", str, attempts, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*b"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%d/%d%s", str, cuser.month, cuser.day, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*l"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%d%s", str, cuser.numlogins, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*p"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%d%s", str, cuser.numposts, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*n"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%s%s", str, cuser.username, po + 3);
+	strcpy(str, strbuf);
+    }
+    while((po = strstr(str, "\033*m"))) {
+	po[0] = 0;
+	sprintf(strbuf, "%s%d%s", str, cuser.money, po + 3);
+	strcpy(str, strbuf);
+    }
+    strip_ansi(str, str ,mode);
+    return str;
+}
+
+int Rename(char* src, char* dst) {
+    if(rename(src, dst) == 0)
+	return 0;    
+    return -1;
+}
+
+int Link(char* src, char* dst) {
+    char cmd[200];
+    
+    if(strcmp(src, BBSHOME "/home") == 0)
+    	return 1;
+    if(link(src, dst) == 0)
+	return 0;
+    
+    sprintf(cmd, "/bin/cp -R %s %s", src, dst);
+    return system(cmd);
+}
+
+char *my_ctime(const time_t *t) {
+    struct tm *tp;
+    static char ans[100];
+
+    tp = localtime(t);
+    sprintf(ans, "%02d/%02d/%02d %02d:%02d:%02d", (tp->tm_year % 100),
+	    tp->tm_mon + 1,tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
+    return ans;
+}
diff --git a/mbbsd/lovepaper.c b/mbbsd/lovepaper.c
new file mode 100644
index 00000000..52e8cbd5
--- /dev/null
+++ b/mbbsd/lovepaper.c
@@ -0,0 +1,120 @@
+/* $Id: lovepaper.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "proto.h"
+
+#define DATA "etc/lovepaper.dat"
+
+extern userec_t cuser;
+
+int x_love() {
+    char buf1[200], save_title[TTLEN + 1];
+    char receiver[61], path[STRLEN] = "home/";
+    int x, y = 0, tline = 0, poem = 0;
+    FILE *fp, *fpo;
+    time_t timenow;
+    struct tm *gtime;
+    fileheader_t mhdr;
+    
+    setutmpmode(LOVE);
+    time(&timenow);
+    gtime = localtime(&timenow);
+    sprintf(buf1,"%c/%s/love%d%d",
+	    cuser.userid[0], cuser.userid,gtime->tm_sec,gtime->tm_min);
+    strcat(path,buf1);
+    move(1,0);
+    clrtobot();
+    
+    outs("\n�w��ϥα��Ѳ��;� v0.00 �� \n");
+    outs("�������H�Ҿ�����,��Ѩt�����A���a.\n������ : �ݱ����Ǫk.\n");
+    
+    if(!getdata(7, 0, "���H�H�G", receiver, 60, DOECHO)) return 0;
+    if(receiver[0] && !(searchuser(receiver) &&
+			getdata(8, 0, "�D  �D�G", save_title,
+				TTLEN, DOECHO))) {
+	move(10, 0);
+	outs("���H�H�ΥD�D�����T, ���ѵL�k�ǻ�. ");
+	pressanykey();
+	return 0;
+    }
+    
+    fpo = fopen(path, "w");
+    fprintf(fpo, "\n");
+    if((fp = fopen(DATA, "r"))) {
+	while(fgets(buf1,100, fp)) {
+	    switch(buf1[0]) {
+	    case '#':
+		break;
+	    case '@':
+		if(!strncmp(buf1, "@begin", 6) || !strncmp(buf1, "@end", 4))
+		    tline=3;
+		else if(!strncmp(buf1,"@poem",5)) {
+		    poem = 1;
+		    tline = 1;
+		    fprintf(fpo, "\n\n");
+		} else
+		    tline=2;
+		break;
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	    case '8':
+	    case '9':
+		sscanf(buf1,"%d",&x);
+		y = (rand() % (x - 1)) * tline; 
+		break;
+	    default:
+		if(!poem) {
+		    if(y > 0)
+			y = y - 1;
+		    else {
+			if(tline > 0) {
+			    fprintf(fpo, "%s", buf1);
+			    tline--;
+			}
+		    }
+		} else {
+		    if(buf1[0] == '$')
+			y--;
+		    else if(y == 0)
+			fprintf(fpo,"%s",buf1);
+		}
+	    } 
+
+	}
+	
+	fclose(fp);
+	fclose(fpo);
+	if(vedit(path, YEA, NULL) == -1) {
+	    unlink(path);
+	    clear();
+	    outs("\n\n ���H����\n");
+	    pressanykey();
+	    return -2;
+	}
+	sethomepath(buf1, receiver);
+	stampfile(buf1, &mhdr);
+	Rename(path, buf1);
+	strncpy(mhdr.title, save_title, TTLEN);
+	strcpy(mhdr.owner, cuser.userid);
+	mhdr.savemode = '\0';
+	sethomedir(path, receiver );
+	if(append_record(path, &mhdr, sizeof(mhdr)) == -1)
+	    return -1;
+	hold_mail(buf1, receiver);
+	return 1;
+    }
+    return 0;
+}
diff --git a/mbbsd/mail.c b/mbbsd/mail.c
new file mode 100644
index 00000000..e480abb2
--- /dev/null
+++ b/mbbsd/mail.c
@@ -0,0 +1,1675 @@
+/* $Id: mail.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+
+extern int TagNum;
+extern int b_lines;               /* Screen bottom line number: t_lines-1 */
+extern char save_title[];         /* used by editor when inserting */
+extern int curredit;
+extern char *err_uid;
+extern char *msg_cancel;
+extern char *msg_uid;
+extern char *fn_overrides;
+extern char quote_file[80];
+extern char quote_user[80];
+extern char *fn_notes;
+extern char *msg_mailer;
+extern char *msg_sure_ny;
+extern char *BBSName;
+extern char currtitle[44];
+extern unsigned char currfmode;               /* current file mode */
+extern char *msg_del_ny;
+extern char currfile[FNLEN];
+extern int currmode;
+extern char currboard[];        /* name of currently selected board */
+extern char *str_space;
+extern char *str_author1;
+extern char *str_author2;
+extern userinfo_t *currutmp;
+extern unsigned int currstat;
+extern pid_t currpid;
+extern int usernum;
+extern char *str_mail_address;
+extern userec_t cuser;
+
+char currmaildir[32];
+static char msg_cc[] = "\033[32m[�s�զW��]\033[m\n";
+static char listfile[] = "list.0";
+static int mailkeep = 0, mailsum = 0;
+static int mailsumlimit = 0,mailmaxkeep = 0;
+
+int setforward() {
+    char buf[80], ip[50] = "", yn[4];
+    FILE *fp;
+    
+    sethomepath(buf, cuser.userid);
+    strcat(buf,"/.forward");
+    if((fp = fopen(buf,"r"))) {
+	fscanf(fp,"%s",ip);
+	fclose(fp);
+    }
+    getdata_buf(b_lines - 1, 0, "�п�J�H�c�۰���H��email�a�}:",
+		ip, 41, DOECHO);
+    if(ip[0] && ip[0] != ' ') {
+	getdata(b_lines, 0, "�T�w�}�Ҧ۰���H�\\��?(Y/n)", yn, 3,
+		LCECHO);
+	if(yn[0] != 'n' && (fp = fopen(buf, "w"))) {
+	    move(b_lines,0);
+	    clrtoeol();
+	    fprintf(fp,"%s",ip);
+	    fclose(fp);
+	    outs("�]�w����!");
+	    refresh();
+	    return 0;
+	}
+    }
+    move(b_lines,0);
+    clrtoeol();
+    outs("�����۰���H!");
+    unlink(buf);
+    refresh();
+    return 0;
+}
+
+int built_mail_index() {
+    char genbuf[128];
+    
+    getdata(b_lines, 0,
+	    "���ثH�c?(ĵ�i:�нT�w�H�c�����D�ɤ~�ϥ�)(y/N)", genbuf, 3,
+            LCECHO);
+    if(genbuf[0] != 'y') return 0;  
+
+    sprintf(genbuf, BBSHOME "/bin/buildir " BBSHOME "/home/%c/%s",
+	    cuser.userid[0], cuser.userid);
+    move(22,0);
+    prints("\033[1;31m�w�g�B�z����!! �Ѧh���K �q�Э��~\033[m");pressanykey();
+    system(genbuf);
+    return 0;
+}
+
+int mailalert(char *userid)
+{
+    userinfo_t *uentp=NULL;
+    int n,tuid,i;     
+
+    if((tuid=searchuser(userid))==0) return -1;
+
+    n=count_logins(tuid, 0);
+    for(i=1;i<=n;i++)
+       if((uentp = (userinfo_t *)search_ulistn(tuid, i)))
+         uentp->mailalert=1;
+    return 0;
+}
+
+int mail_muser(userec_t muser, char *title, char *filename) {
+    return mail_id(muser.userid, title, filename, cuser.userid);
+}
+
+/* Heat: ��id�ӱH�H,���e�hlink�dzƦn���ɮ� */
+int mail_id(char* id, char *title, char *filename, char *owner) {
+    fileheader_t mhdr;
+    char genbuf[128];
+    sethomepath(genbuf, id);
+    if(stampfile(genbuf, &mhdr))
+        return 0;
+    strcpy(mhdr.owner, owner);
+    strncpy(mhdr.title, title, TTLEN);
+    mhdr.savemode = 0;
+    mhdr.filemode = 0;
+    Link(filename, genbuf);
+    sethomedir(genbuf,id);
+    append_record(genbuf, &mhdr, sizeof(mhdr));
+    mailalert(id);
+    return 0;
+}    
+          
+int invalidaddr(char *addr) {
+    if(*addr == '\0')
+	return 1;                   /* blank */
+    while(*addr) {
+	if(not_alnum(*addr) && !strchr("[].%!@:-_;", *addr))
+	    return 1;
+	addr++;
+    }
+    return 0;
+}
+
+int m_internet() {
+    char receiver[60];
+    
+    getdata(20, 0, "���H�H�G", receiver, 60, DOECHO);
+    if(strchr(receiver, '@') && !invalidaddr(receiver) &&
+       getdata(21, 0, "�D  �D�G", save_title, TTLEN, DOECHO))
+	do_send(receiver, save_title);
+    else {
+	move(22, 0);
+	outs("���H�H�ΥD�D�����T, �Э��s������O");
+	pressanykey();
+    }
+    return 0;
+}
+
+void m_init() {
+    sethomedir(currmaildir, cuser.userid);
+}
+
+int chkmailbox() {
+    if(!HAVE_PERM(PERM_SYSOP) && !HAVE_PERM(PERM_MAILLIMIT)) {
+        int max_keepmail = MAX_KEEPMAIL;
+        if ( HAS_PERM(PERM_SYSSUBOP) || HAS_PERM(PERM_SMG) ||
+           HAS_PERM(PERM_PRG) || HAS_PERM(PERM_ACTION) || HAS_PERM(PERM_PAINT))
+          {
+            mailsumlimit = 700;
+            max_keepmail = 500;
+          }
+	else if(HAS_PERM(PERM_BM))
+          {
+	    mailsumlimit = 500;
+            max_keepmail = 300;
+	  }
+	else if(HAS_PERM(PERM_LOGINOK))
+	    mailsumlimit = 200;
+	else
+	    mailsumlimit = 50;
+	mailsumlimit += cuser.exmailbox * 10;
+	mailmaxkeep = max_keepmail + cuser.exmailbox;
+	m_init();
+	if((mailkeep = get_num_records(currmaildir, sizeof(fileheader_t))) >
+	   mailmaxkeep) {
+	    move(b_lines, 0);
+	    clrtoeol();
+	    bell();
+	    prints("�z�O�s�H��ƥ� %d �W�X�W�� %d, �о�z",
+		   mailkeep, mailmaxkeep);
+	    bell();
+	    refresh();
+	    igetch();
+	    return mailkeep;
+	}
+	if((mailsum = get_sum_records(currmaildir, sizeof(fileheader_t))) >
+	   mailsumlimit) {
+	    move(b_lines, 0);
+	    clrtoeol();
+	    bell();
+	    prints("�z�O�s�H��e�q %d(k)�W�X�W�� %d(k), �о�z",
+		   mailsum, mailsumlimit);
+	    bell();
+	    refresh();
+	    igetch();
+	    return mailkeep;
+	}
+    }
+    return 0;
+}
+
+static void do_hold_mail(char *fpath, char *receiver, char *holder) {
+    char buf[80], title[128];
+    
+    fileheader_t mymail;
+    
+    sethomepath(buf, holder);
+    stampfile(buf, &mymail);
+    
+    mymail.savemode = 'H';        /* hold-mail flag */
+    mymail.filemode = FILE_READ;
+    strcpy(mymail.owner, "[��.��.��]");
+    if(receiver) {
+	sprintf(title, "(%s) %s", receiver, save_title);
+	strncpy(mymail.title, title, TTLEN);
+    } else
+	strcpy(mymail.title, save_title);
+    
+    sethomedir(title, holder);
+    
+    unlink(buf);
+    Link(fpath, buf);
+    /* Ptt: append_record->do_append */
+    do_append(title, &mymail, sizeof(mymail));
+}
+
+extern userec_t xuser;
+
+void hold_mail(char *fpath, char *receiver) {
+    char buf[4];
+    
+    getdata(b_lines - 1, 0, "�w���Q�H�X�A�O�_�ۦs���Z(Y/N)�H[N] ",
+	    buf, 4, LCECHO);
+    
+    if(buf[0] == 'y')
+	do_hold_mail(fpath, receiver, cuser.userid);
+}
+
+int do_send(char *userid, char *title) {
+    fileheader_t mhdr;
+    char fpath[STRLEN];
+    char receiver[IDLEN];
+    char genbuf[200];
+    int internet_mail, i;
+    
+    if(strchr(userid, '@'))
+	internet_mail = 1;
+    else {
+	internet_mail = 0;
+	if(!getuser(userid))
+	    return -1;
+	if(!(xuser.userlevel & PERM_READMAIL))
+	    return -3;
+	
+	if(!title)
+	    getdata(2, 0, "�D�D�G", save_title, TTLEN, DOECHO);
+	curredit |= EDIT_MAIL;
+	curredit &= ~EDIT_ITEM;
+    }
+    
+    setutmpmode(SMAIL);
+    
+    fpath[0] = '\0';
+    
+    if(internet_mail) {
+	int res, ch;
+
+	if(vedit(fpath, NA, NULL) == -1) {
+	    unlink(fpath);
+	    clear();
+	    return -2;
+	}
+	clear();
+	prints("�H��Y�N�H�� %s\n���D���G%s\n�T�w�n�H�X��? (Y/N) [Y]",
+	       userid, title);
+	ch = igetch();
+	switch(ch) {
+	case 'N':
+	case 'n':
+	    outs("N\n�H��w����");
+	    res = -2;
+	    break;
+	default:
+	    outs("Y\n�еy��, �H��ǻ���...\n");
+	    res =
+#ifndef USE_BSMTP
+		bbs_sendmail(fpath, title, userid);
+#else
+            bsmtp(fpath, title, userid,0);
+#endif
+	    hold_mail(fpath, userid);
+	}
+	unlink(fpath);
+	return res;
+    } else {
+	strcpy(receiver, userid);
+	sethomepath(genbuf, userid);
+	stampfile(genbuf, &mhdr);
+	strcpy(mhdr.owner, cuser.userid);
+	strncpy(mhdr.title, save_title, TTLEN);
+	mhdr.savemode = '\0';
+	if(vedit(genbuf, YEA, NULL) == -1) {
+	    unlink(genbuf);
+	    clear();
+	    return -2;
+	}
+	clear();
+	sethomefile(fpath, userid, FN_OVERRIDES);
+        i=belong(fpath, cuser.userid);
+        sethomefile(fpath, userid, FN_REJECT);
+
+        if(i || !belong(fpath, cuser.userid)) //Ptt:��belong���I�Q�� 
+           {
+	    sethomedir(fpath, userid);
+     	    if(append_record(fpath, &mhdr, sizeof(mhdr)) == -1)
+	        return -1;
+            mailalert(userid);
+	   }
+	hold_mail(genbuf, userid);
+	return 0;
+    }
+}
+
+void my_send(char *uident) {
+    switch(do_send(uident, NULL)) {
+    case -1:
+	outs(err_uid);
+	break;
+    case -2:
+	outs(msg_cancel);
+	break;
+    case -3:
+	prints("�ϥΪ� [%s] �L�k���H", uident);
+	break;
+    }
+    pressanykey();
+}
+
+int m_send() {
+    char uident[40];
+
+    stand_title("�Bť������");
+    usercomplete(msg_uid, uident);
+    showplans(uident);
+    if(uident[0])
+	my_send(uident);
+    return 0;
+}
+
+/* �s�ձH�H�B�^�H : multi_send, multi_reply */
+extern struct word_t *toplev;
+
+static void multi_list(int *reciper) {
+    char uid[16];
+    char genbuf[200];
+
+    while(1) {
+	stand_title("�s�ձH�H�W��");
+	ShowNameList(3, 0, msg_cc);
+	getdata(1, 0,
+		"(I)�ޤJ�n�� (O)�ޤJ�W�u�q�� (N)�ޤJ�s�峹�q�� "
+		"(0-9)�ޤJ��L�S�O�W��\n"
+		"(A)�W�[     (D)�R��         (M)�T�{�H�H�W��   (Q)���� �H[M]",
+		genbuf, 4, LCECHO);
+	switch(genbuf[0]) {
+	case 'a':
+	    while(1) {
+		move(1, 0);
+		usercomplete("�п�J�n�W�[���N��(�u�� ENTER �����s�W): ", uid);
+		if(uid[0] == '\0')
+		    break;
+		
+		move(2, 0);
+		clrtoeol();
+		
+		if(!searchuser(uid))
+		    outs(err_uid);
+		else if(!InNameList(uid)) {
+		    AddNameList(uid);
+		    (*reciper)++;
+		}
+		ShowNameList(3, 0, msg_cc);
+	    }
+	    break;
+	case 'd':
+	    while(*reciper) {
+		move(1, 0);
+		namecomplete("�п�J�n�R�����N��(�u�� ENTER �����R��): ", uid);
+		if(uid[0] == '\0')
+		    break;
+		if(RemoveNameList(uid))
+		    (*reciper)--;
+		ShowNameList(3, 0, msg_cc);
+	    }
+	    break;
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	    listfile[5] = genbuf[0];
+	    genbuf[0] = '1';
+	case 'i':
+	    setuserfile(genbuf, genbuf[0] == '1' ? listfile : fn_overrides);
+	    ToggleNameList(reciper, genbuf, msg_cc);
+	    break;
+	case 'o':
+	    setuserfile(genbuf, "alohaed");
+	    ToggleNameList(reciper, genbuf, msg_cc);
+	    break;
+	case 'n':
+	    setuserfile(genbuf, "postlist");
+	    ToggleNameList(reciper, genbuf, msg_cc);
+	    break;
+	case 'q':
+	    *reciper = 0;
+	    return;
+	default:
+	    return;
+	}
+    }
+}
+
+static void multi_send(char *title) {
+    FILE *fp;
+    struct word_t *p;
+    fileheader_t mymail;
+    char fpath[TTLEN], *ptr;
+    int reciper, listing;
+    char genbuf[256];
+    
+    CreateNameList();
+    listing = reciper = 0;
+    if(*quote_file) {
+	AddNameList(quote_user);
+	reciper = 1;
+	fp = fopen(quote_file, "r");
+	while(fgets(genbuf, 256, fp)) {
+	    if(strncmp(genbuf, "�� ", 3)) {
+		if(listing)
+		    break;
+	    } else {
+		if(listing) {
+		    strtok(ptr = genbuf + 3, " \n\r");
+		    do {
+			if(searchuser(ptr) && !InNameList(ptr) &&
+			   strcmp(cuser.userid, ptr)) {
+			    AddNameList(ptr);
+			    reciper++;
+			}
+		    } while((ptr = (char *)strtok(NULL, " \n\r")));
+		} else if(!strncmp(genbuf + 3, "[�q�i]", 6))
+		    listing = 1;
+	    }
+	}
+	ShowNameList(3, 0, msg_cc);
+    }
+    
+    multi_list(&reciper);
+    move(1, 0);
+    clrtobot();
+    
+    if(reciper) {
+	setutmpmode(SMAIL);
+	if(title)
+	    do_reply_title(2, title);
+	else {
+	    getdata(2, 0, "�D�D�G", fpath, 64, DOECHO);
+	    sprintf(save_title, "[�q�i] %s", fpath);
+	}
+	
+	setuserfile(fpath, fn_notes);
+	
+	if((fp = fopen(fpath, "w"))) {
+	    fprintf(fp, "�� [�q�i] �@ %d �H����", reciper);
+	    listing = 80;
+
+	    for(p = toplev; p; p = p->next) {
+		reciper = strlen(p->word) + 1;
+		if(listing + reciper > 75) {
+		    listing = reciper;
+		    fprintf(fp, "\n��");
+		} else
+		    listing += reciper;
+		
+		fprintf(fp, " %s", p->word);
+	    }
+	    memset(genbuf, '-', 75);
+	    genbuf[75] = '\0';
+	    fprintf(fp, "\n%s\n\n", genbuf);
+	    fclose(fp);
+	}
+	
+	curredit |= EDIT_LIST;
+
+	if(vedit(fpath, YEA, NULL) == -1) {
+	    unlink(fpath);
+	    curredit = 0;
+	    outs(msg_cancel);
+	    pressanykey();
+	    return;
+	}
+	
+	stand_title("�H�H��...");
+	refresh();
+	
+	listing = 80;
+	
+	for(p = toplev; p; p = p->next) {
+	    reciper = strlen(p->word) + 1;
+	    if(listing + reciper > 75) {
+		listing = reciper;
+		outc('\n');
+	    } else {
+		listing += reciper;
+		outc(' ');
+	    }
+	    outs(p->word);
+	    if(searchuser(p->word) && strcmp(STR_GUEST, p->word) )
+		sethomepath(genbuf, p->word);
+	    else
+		continue;
+	    stampfile(genbuf, &mymail);
+	    unlink(genbuf);
+	    Link(fpath, genbuf);
+	    
+	    strcpy(mymail.owner, cuser.userid);
+	    strcpy(mymail.title, save_title);
+	    mymail.savemode = 'M';    /* multi-send flag */
+	    sethomedir(genbuf, p->word);
+	    if(append_record(genbuf, &mymail, sizeof(mymail)) == -1)
+		outs(err_uid);
+	    mailalert(p->word);
+	}
+	hold_mail(fpath, NULL);
+	unlink(fpath);
+	curredit = 0;
+    } else
+	outs(msg_cancel);
+    pressanykey();
+}
+
+static int multi_reply(int ent, fileheader_t *fhdr, char *direct) {
+    if(fhdr->savemode != 'M')
+	return mail_reply(ent, fhdr, direct);
+
+    stand_title("�s�զ^�H");
+    strcpy(quote_user, fhdr->owner);
+    setuserfile(quote_file, fhdr->filename);
+    multi_send(fhdr->title);
+    return 0;
+}
+
+int mail_list() {
+    stand_title("�s�է@�~");
+    multi_send(NULL);
+    return 0;
+}
+
+int mail_all() {
+    FILE *fp;
+    fileheader_t mymail;
+    char fpath[TTLEN];
+    char genbuf[200];
+    extern struct uhash_t *uhash;
+    int i, unum;
+    char *userid;
+    
+    stand_title("���Ҧ��ϥΪ̪��t�γq�i");
+    setutmpmode(SMAIL);
+    getdata(2, 0, "�D�D�G", fpath, 64, DOECHO);
+    sprintf(save_title, "[�t�γq�i]\033[1;32m %s\033[m", fpath);
+    
+    setuserfile(fpath, fn_notes);
+    
+    if((fp = fopen(fpath, "w"))) {
+	fprintf(fp, "�� [\033[1m�t�γq�i\033[m] �o�O�ʵ��Ҧ��ϥΪ̪��H\n");
+	fprintf(fp, "-----------------------------------------------------"
+		"----------------------\n");
+	fclose(fp);
+    }
+    
+    *quote_file = 0;
+    
+    curredit |= EDIT_MAIL;
+    curredit &= ~EDIT_ITEM;
+    if(vedit(fpath, YEA, NULL) == -1) {
+	curredit = 0;
+	unlink(fpath);
+	outs(msg_cancel);
+	pressanykey();
+	return 0;
+    }
+    curredit = 0;
+    
+    setutmpmode(MAILALL);
+    stand_title("�H�H��...");
+    
+    sethomepath(genbuf, cuser.userid);
+    stampfile(genbuf, &mymail);
+    unlink(genbuf);
+    Link(fpath, genbuf);
+    unlink(fpath);
+    strcpy(fpath, genbuf);
+    
+    strcpy(mymail.owner, cuser.userid);  /*���� ID*/
+    strcpy(mymail.title, save_title);
+    mymail.savemode = 0;
+    
+    sethomedir(genbuf, cuser.userid);
+    if(append_record(genbuf, &mymail, sizeof(mymail)) == -1)
+	outs(err_uid);
+    
+    for(unum = uhash->number, i = 0; i < unum; i++) {
+	if(bad_user_id(uhash->userid[i]))
+	    continue; /* Ptt */
+	
+	userid = uhash->userid[i];
+	if(strcmp(userid,STR_GUEST) && strcmp(userid, "new") &&
+	   strcmp(userid, cuser.userid)) {
+	    sethomepath(genbuf, userid);
+	    stampfile(genbuf, &mymail);
+	    unlink(genbuf);
+	    Link(fpath, genbuf);
+	    
+	    strcpy(mymail.owner, cuser.userid);
+	    strcpy(mymail.title, save_title);
+	    mymail.savemode = 0;
+	    /* mymail.filemode |= FILE_MARKED; Ptt ���i�令���|mark */
+	    sethomedir(genbuf, userid);
+	    if(append_record(genbuf, &mymail, sizeof(mymail)) == -1)
+		outs(err_uid);
+	    sprintf(genbuf, "%*s %5d / %5d", IDLEN + 1, userid, i + 1, unum);
+	    outmsg(genbuf);
+	    refresh();
+	}
+    }
+    return 0;
+}
+
+int mail_mbox() {
+    char cmd[100];
+    fileheader_t fhdr;
+
+    sprintf(cmd, "/tmp/%s.uu", cuser.userid);
+    sprintf(fhdr.title, "%s �p�H���", cuser.userid);
+    doforward(cmd, &fhdr, 'Z');
+    return 0;
+}
+
+static int m_forward(int ent, fileheader_t *fhdr, char *direct) {
+    char uid[STRLEN];
+
+    stand_title("��F�H��");
+    usercomplete(msg_uid, uid);
+    if(uid[0] == '\0')
+	return FULLUPDATE;
+
+    strcpy(quote_user, fhdr->owner);
+    setuserfile(quote_file, fhdr->filename);
+    sprintf(save_title, "%.64s (fwd)", fhdr->title);
+    move(1, 0);
+    clrtobot();
+    prints("��H��: %s\n��  �D: %s\n", uid, save_title);
+
+    switch(do_send(uid, save_title)) {
+    case -1:
+	outs(err_uid);
+	break;
+    case -2:
+	outs(msg_cancel);
+	break;
+    case -3:
+	prints("�ϥΪ� [%s] �L�k���H", uid);
+	break;
+    }
+    pressanykey();
+    return FULLUPDATE;
+}
+
+static int delmsgs[128];
+static int delcnt;
+static int mrd;
+
+static int read_new_mail(fileheader_t *fptr) {
+    static int idc;
+    char done = NA, delete_it;
+    char fname[256];
+    char genbuf[4];
+    
+    if(fptr == NULL) {
+	delcnt = 0;
+	idc = 0;
+	return 0;
+    }
+    idc++;
+    if(fptr->filemode)
+	return 0;
+    clear();
+    move(10, 0);
+    prints("�z�nŪ�Ӧ�[%s]���T��(%s)�ܡH", fptr->owner, fptr->title);
+    getdata(11, 0, "�бz�T�w(Y/N/Q)?[Y] ", genbuf, 3, DOECHO);
+    if(genbuf[0] == 'q')
+	return QUIT;
+    if(genbuf[0] == 'n')
+	return 0;
+    
+    setuserfile(fname, fptr->filename);
+    fptr->filemode |= FILE_READ;
+    if(substitute_record(currmaildir, fptr, sizeof(*fptr), idc))
+	return -1;
+    
+    mrd = 1;
+    delete_it = NA;
+    while(!done) {
+	int more_result = more(fname, YEA);
+
+	switch(more_result) {
+	case 1:
+	    return READ_PREV;
+	case 2:
+	    return RELATE_PREV;
+	case 3:
+	    return READ_NEXT;
+	case 4:
+	    return RELATE_NEXT;
+	case 5:
+	    return RELATE_FIRST;
+	case 6:
+	    return 0;
+	case 7:
+	    mail_reply(idc, fptr, currmaildir);
+	    return FULLUPDATE;
+	case 8:
+	    multi_reply(idc, fptr, currmaildir);
+	    return FULLUPDATE;
+	}
+	move(b_lines, 0);
+	clrtoeol();
+	outs(msg_mailer);
+	refresh();
+	
+	switch(egetch()) {
+	case 'r':
+	case 'R':
+	    mail_reply(idc, fptr, currmaildir);
+	    break;
+	case 'x':
+	    m_forward(idc, fptr, currmaildir);
+	    break;
+	case 'y':
+	    multi_reply(idc, fptr, currmaildir);
+	    break;
+	case 'd':
+	case 'D':
+	    delete_it = YEA;
+	default:
+	    done = YEA;
+	}
+    }
+    if(delete_it) {
+	clear();
+	prints("�R���H��m%s�n", fptr->title);
+	getdata(1, 0, msg_sure_ny, genbuf, 2, LCECHO);
+	if(genbuf[0] == 'y') {
+	    unlink(fname);
+	    delmsgs[delcnt++] = idc;
+	}
+    }
+    clear();
+    return 0;
+}
+
+int m_new() {
+    clear();
+    mrd = 0;
+    setutmpmode(RMAIL);
+    read_new_mail(NULL);
+    clear();
+    curredit |= EDIT_MAIL;
+    curredit &= ~EDIT_ITEM;
+    if(apply_record(currmaildir, read_new_mail, sizeof(fileheader_t)) == -1) {
+	outs("�S���s�H��F");
+	pressanykey();
+	return -1;
+    }
+    curredit = 0;
+    if(delcnt) {
+	while(delcnt--)
+	    delete_record(currmaildir, sizeof(fileheader_t), delmsgs[delcnt]);
+    }
+    outs(mrd ? "�H�w�\\��" : "�S���s�H��F");
+    pressanykey();
+    return -1;
+}
+
+static void mailtitle() {
+    char buf[256] = "";
+
+    showtitle("\0�l����", BBSName);
+    sprintf(buf,"[��]���}[����]���[��]�\\Ū�H�� [R]�^�H [x]��F "
+	 "[y]�s�զ^�H [O]���~�H:%s [h]�D�U\n\033[7m"
+	 "�s��   �� ��  �@ ��          �H  ��  ��  �D     \033[32m",
+	 HAS_PERM(PERM_NOOUTMAIL)? "\033[31m��\033[m":"�}");
+    outs(buf);
+    buf[0]=0;
+    if(mailsumlimit) {
+	sprintf(buf,"(�e�q:%d/%dk %d/%d�g)", mailsum, mailsumlimit,
+		mailkeep, mailmaxkeep);
+    }
+    sprintf(buf, "%s%*s\033[m", buf, 29 - (int) strlen(buf), "");
+    outs(buf);
+}
+
+static void maildoent(int num, fileheader_t *ent) {
+    char *title, *mark, color, type = "+ Mm"[ent->filemode];
+
+    if (TagNum && !Tagger(atoi(ent->filename + 2), 0, TAG_NIN))
+            type = 'D';     
+
+    title = subject(mark = ent->title);
+    if(title == mark) {
+	color = '1';
+	mark = "��";
+    } else {
+	color = '3';
+	mark = "R:";
+    }
+
+    if(strncmp(currtitle, title, 40))
+	prints("%5d %c %-7s%-15.14s%s %.46s\n", num, type,
+	       ent->date, ent->owner, mark, title);
+    else
+	prints("%5d %c %-7s%-15.14s\033[1;3%cm%s %.46s\033[0m\n", num, type,
+	       ent->date, ent->owner, color, mark, title);
+}
+
+#ifdef POSTBUG
+extern int bug_possible;
+#endif
+
+
+static int m_idle(int ent, fileheader_t *fhdr, char *direct) {
+    t_idle();
+    return FULLUPDATE;
+}
+
+static int mail_del(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[200];
+
+    if(fhdr->filemode & FILE_MARKED)
+	return DONOTHING;
+
+    getdata(1, 0, msg_del_ny, genbuf, 3, LCECHO);
+    if(genbuf[0] == 'y') {
+	strcpy(currfile, fhdr->filename);
+	if(!delete_file(direct, sizeof(*fhdr), ent, cmpfilename)) {
+	    setdirpath(genbuf, direct, fhdr->filename);
+	    unlink(genbuf);
+	    if((currmode & MODE_SELECT)) {
+		int now;
+		
+		sethomedir(genbuf, cuser.userid);
+		now = getindex(genbuf, fhdr->filename, sizeof(fileheader_t));
+		delete_file(genbuf, sizeof(fileheader_t), now, cmpfilename);
+	    }
+	    return DIRCHANGED;
+	}
+    }
+    return FULLUPDATE;
+}
+
+static int mail_read(int ent, fileheader_t *fhdr, char *direct) {
+    char buf[64];
+    char done, delete_it, replied;
+
+    clear();
+    setdirpath(buf, direct, fhdr->filename);
+    strncpy(currtitle, subject(fhdr->title), 40);
+    done = delete_it = replied = NA;
+    while(!done) {
+	int more_result = more(buf, YEA);
+	
+	if(more_result != -1) {
+	    fhdr->filemode |= FILE_READ;
+	    if((currmode & MODE_SELECT)) {
+		int now;
+		
+		now = getindex(currmaildir, fhdr->filename,
+			       sizeof(fileheader_t));
+		substitute_record(currmaildir, fhdr, sizeof(*fhdr), now);
+		substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+	    }
+	    else
+		substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent);
+	}
+	switch(more_result) {
+	case 1:
+	    return READ_PREV;
+	case 2:
+	    return RELATE_PREV;
+	case 3:
+	    return READ_NEXT;
+	case 4:
+	    return RELATE_NEXT;
+	case 5:
+	    return RELATE_FIRST;
+	case 6:
+	    return FULLUPDATE;
+	case 7:
+	    mail_reply(ent, fhdr, direct);
+	    return FULLUPDATE;
+	case 8:
+	    multi_reply(ent, fhdr, direct);
+	    return FULLUPDATE;
+	}
+	move(b_lines, 0);
+	clrtoeol();
+	refresh();
+	outs(msg_mailer);
+	
+	switch(egetch()) {
+	case 'r':
+	case 'R':
+	    replied = YEA;
+	    mail_reply(ent, fhdr, direct);
+	    break;
+	case 'x':
+	    m_forward(ent, fhdr, direct);
+	    break;
+	case 'y':
+	    multi_reply(ent, fhdr, direct);
+	    break;
+	case 'd':
+	    delete_it = YEA;
+	default:
+	    done = YEA;
+	}
+    }
+    if(delete_it)
+	mail_del(ent, fhdr, direct);
+    else {
+	fhdr->filemode |= FILE_READ;
+#ifdef POSTBUG
+	if(replied)
+	    bug_possible = YEA;
+#endif
+	if((currmode & MODE_SELECT)) {
+	    int now;
+	    
+	    now = getindex(currmaildir, fhdr->filename, sizeof(fileheader_t));
+	    substitute_record(currmaildir, fhdr, sizeof(*fhdr), now);
+	    substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+	} else
+	    substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent);
+#ifdef POSTBUG
+	bug_possible = NA;
+#endif
+    }
+    return FULLUPDATE;
+}
+
+/* in boards/mail �^�H����@�̡A��H����i */
+int mail_reply(int ent, fileheader_t *fhdr, char *direct) {
+    char uid[STRLEN];
+    char *t;
+    FILE *fp;
+    char genbuf[512];
+
+    stand_title("�^  �H");
+
+    /* �P�_�O boards �� mail */
+    if(curredit & EDIT_MAIL)
+	setuserfile(quote_file, fhdr->filename);
+    else
+	setbfile(quote_file, currboard, fhdr->filename);
+
+    /* find the author */
+    strcpy(quote_user, fhdr->owner);
+    if(strchr(quote_user, '.')) {
+	genbuf[0] = '\0';
+	if((fp = fopen(quote_file, "r"))) {
+	    fgets(genbuf, 512, fp);
+	    fclose(fp);
+	}
+	
+	t = strtok(genbuf, str_space);
+	if(!strcmp(t, str_author1) || !strcmp(t, str_author2))
+	    strcpy(uid, strtok(NULL, str_space));
+	else {
+	    outs("���~: �䤣��@�̡C");
+	    pressanykey();
+	    return FULLUPDATE;
+	}
+    } else
+	strcpy(uid, quote_user);
+    
+    /* make the title */
+    do_reply_title(3, fhdr->title);
+    prints("\n���H�H: %s\n��  �D: %s\n", uid, save_title);
+    
+    /* edit, then send the mail */
+    ent = curredit;
+    switch(do_send(uid, save_title)) {
+    case -1:
+	outs(err_uid);
+	break;
+    case -2:
+	outs(msg_cancel);
+	break;
+    case -3:
+	prints("�ϥΪ� [%s] �L�k���H", uid);
+	break;
+    }
+    curredit = ent;
+    pressanykey();
+    return FULLUPDATE;
+}
+
+static int mail_edit(int ent, fileheader_t *fhdr, char *direct) {
+    char genbuf[200];
+
+    if(!HAS_PERM(PERM_SYSOP) && 
+       strcmp(cuser.userid, fhdr->owner) &&
+       strcmp("[��.��.��]", fhdr->owner))
+	return DONOTHING;
+
+    setdirpath(genbuf, direct, fhdr->filename);
+    vedit(genbuf, NA, NULL);
+    return FULLUPDATE;
+}
+
+static int mail_nooutmail(int ent, fileheader_t *fhdr, char *direct)
+{
+    cuser.userlevel ^= PERM_NOOUTMAIL;
+    passwd_update(usernum, &cuser);
+    return FULLUPDATE;
+
+}
+
+static int mail_mark(int ent, fileheader_t *fhdr, char *direct) {
+    fhdr->filemode ^= FILE_MARKED;
+    
+    if((currmode & MODE_SELECT)) {
+	int now;
+
+	now = getindex(currmaildir, fhdr->filename, sizeof(fileheader_t));
+	substitute_record(currmaildir, fhdr, sizeof(*fhdr), now);
+	substitute_record(direct, fhdr, sizeof(*fhdr), ent);
+    } else
+	substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent);
+    return PART_REDRAW;
+}
+
+/* help for mail reading */
+static char *mail_help[] = {
+    "\0�q�l�H�c�ާ@����",
+    "\01�򥻩R�O",
+    "(p)(��)    �e�@�g�峹",
+    "(n)(��)    �U�@�g�峹",
+    "(P)(PgUp)  �e�@��",
+    "(N)(PgDn)  �U�@��",
+    "(##)(cr)   ����� ## ��",
+    "($)        ����̫�@��",
+    "\01�i���R�O",
+    "(r)(��)/(R)Ū�H / �^�H",
+    "(O)        ����/�}�� ���~�H����J",
+    "(c/z)      ���J���H��i�J�p�H�H��/�i�J�p�H�H��",
+    "(x/X)      ��F�H��/����峹���L�ݪO",
+    "(y)        �s�զ^�H",
+    "(F)        �N�H�ǰe�^�z���q�l�H�c      (u)���y��z�H�^�H�c",
+    "(d)        �������H",
+    "(D)        �������w�d�򪺫H",
+    "(m)        �N�H�аO�A�H���Q�M��",
+    "(^G)       �ߧY���ثH�c (�H�c���l�ɥ�)",
+    "(t)        �аO���R���H��",
+    "(^D)       �R���w�аO�H��",
+    NULL
+};
+
+static int m_help() {
+    show_help(mail_help);
+    return FULLUPDATE;
+}
+
+static int mail_cross_post(int ent, fileheader_t *fhdr, char *direct) {
+    char xboard[20], fname[80], xfpath[80], xtitle[80], inputbuf[10];
+    fileheader_t xfile;
+    FILE *xptr;
+    int author = 0;
+    char genbuf[200];
+    char genbuf2[4];
+    
+    make_blist();
+    move(2, 0);
+    clrtoeol();
+    move(3, 0);
+    clrtoeol();
+    move(1, 0);
+    namecomplete("������峹��ݪO�G", xboard);
+    if(*xboard == '\0' || !haspostperm(xboard))
+	return FULLUPDATE;
+    
+    ent = 1;
+    if(HAS_PERM(PERM_SYSOP) || !strcmp(fhdr->owner, cuser.userid)) {
+	getdata(2, 0, "(1)������ (2)������榡�H[1] ",
+		genbuf, 3, DOECHO);
+	if(genbuf[0] != '2') {
+	    ent = 0;
+	    getdata(2, 0, "�O�d��@�̦W�ٶ�?[Y] ", inputbuf, 3, DOECHO);
+	    if(inputbuf[0] != 'n' && inputbuf[0] != 'N')
+		author = 1;
+	}
+    }
+    
+    if(ent)
+	sprintf(xtitle, "[���]%.66s", fhdr->title);
+    else
+	strcpy(xtitle, fhdr->title);
+    
+    sprintf(genbuf, "�ĥέ���D�m%.60s�n��?[Y] ", xtitle);
+    getdata(2, 0, genbuf, genbuf2, 4, LCECHO);
+    if(*genbuf2 == 'n')
+	if(getdata(2, 0, "���D�G", genbuf, TTLEN, DOECHO))
+	    strcpy(xtitle, genbuf);
+    
+    getdata(2, 0, "(S)�s�� (L)���� (Q)�����H[Q] ", genbuf, 3, LCECHO);
+    if(genbuf[0] == 'l' || genbuf[0] == 's') {
+	int currmode0 = currmode;
+
+	currmode = 0;
+	setbpath(xfpath, xboard);
+	stampfile(xfpath, &xfile);
+	if(author)
+	    strcpy(xfile.owner, fhdr->owner);
+	else
+	    strcpy(xfile.owner, cuser.userid);
+	strcpy(xfile.title, xtitle);
+	if(genbuf[0] == 'l') {
+	    xfile.savemode = 'L';
+	    xfile.filemode = FILE_LOCAL;
+	} else
+	    xfile.savemode = 'S';
+	
+	setuserfile(fname, fhdr->filename);
+	if(ent) {
+	    xptr = fopen(xfpath, "w");
+	    
+	    strcpy(save_title, xfile.title);
+	    strcpy(xfpath, currboard);
+	    strcpy(currboard, xboard);
+	    write_header(xptr);
+	    strcpy(currboard, xfpath);
+	    
+	    fprintf(xptr, "�� [��������� %s �H�c]\n\n", cuser.userid);
+	    
+	    b_suckinfile(xptr, fname);
+	    addsignature(xptr,0);
+	    fclose(xptr);
+	} else {
+	    unlink(xfpath);
+	    Link(fname, xfpath);
+	}
+	
+	setbdir(fname, xboard);
+	append_record(fname, &xfile, sizeof(xfile));
+	setbtotal(getbnum(xboard));
+	if(!xfile.filemode)
+	    outgo_post(&xfile, xboard);
+	cuser.numposts++;
+	passwd_update(usernum, &cuser);
+	outs("�峹�������");
+	pressanykey();
+	currmode = currmode0;
+    }
+    return FULLUPDATE;
+}
+
+int mail_man() {
+    char buf[64],buf1[64];
+    if (HAS_PERM(PERM_MAILLIMIT)) {
+	int mode0 = currutmp->mode;
+	int stat0 = currstat;
+	
+	sethomeman(buf, cuser.userid);
+	sprintf(buf1, "%s ���H��", cuser.userid);
+	a_menu(buf1, buf, 1);
+	currutmp->mode = mode0;
+	currstat = stat0;
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+static int mail_cite(int ent, fileheader_t *fhdr, char *direct) {
+    char fpath[256];
+    char title[TTLEN + 1];
+    static char xboard[20];
+    char buf[20];
+    boardheader_t *bp;
+
+    setuserfile(fpath, fhdr->filename);
+    strcpy(title, "�� ");
+    strncpy(title+3, fhdr->title, TTLEN-3);
+    title[TTLEN] = '\0';
+    a_copyitem(fpath, title, 0, 1);
+
+    if(cuser.userlevel >= PERM_BM) {
+	move(2, 0);
+	clrtoeol();
+	move(3, 0);
+	clrtoeol();
+	move(1, 0);
+	make_blist();
+	namecomplete("��J�ݪ��W�� (����Enter�i�J�p�H�H��)�G", buf);
+	if(*buf)
+	    strcpy(xboard, buf);
+	if(*xboard && (bp = getbcache(getbnum(xboard)))) {
+	    setapath(fpath, xboard);
+	    setutmpmode(ANNOUNCE);
+	    a_menu(xboard, fpath, HAS_PERM(PERM_ALLBOARD) ? 2 :
+		   is_BM(bp->BM) ? 1 : 0);
+	} else {
+	    mail_man();
+	}
+	return FULLUPDATE;
+    } else {
+	mail_man();
+        return FULLUPDATE;
+    }
+}
+
+static int mail_save(int ent, fileheader_t *fhdr, char *direct) {
+    char fpath[256];
+    char title[TTLEN+1];
+
+    if(HAS_PERM(PERM_MAILLIMIT)) {
+	setuserfile(fpath, fhdr->filename);
+	strcpy(title, "�� ");
+	strncpy(title + 3, fhdr->title, TTLEN - 3);
+	title[TTLEN] = '\0';
+	a_copyitem(fpath, title, fhdr->owner, 1);
+	sethomeman(fpath, cuser.userid);
+	a_menu(cuser.userid, fpath, 1);
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+#ifdef OUTJOBSPOOL
+static int mail_waterball(int ent, fileheader_t *fhdr, char *direct)
+{
+    static  char address[60], cmode = 1;
+    char    fname[500], genbuf[200];
+    FILE    *fp;
+    int     now;
+
+    if(!address[0])
+	strcpy(address, cuser.email);
+    if(address[0]) {
+	sprintf(genbuf, "�H�� [%s] ��(Y/N/Q)�H[Y] ", address);
+	getdata(b_lines - 2, 0, genbuf, fname, 3, LCECHO);
+	if(fname[0] == 'q') { outmsg("�����B�z"); return 1; }
+	if(fname[0] == 'n')
+	    address[0] = '\0';
+    }
+    
+    if(!address[0]) {
+	getdata(b_lines - 2, 0, "�п�J�l��a�}�G", fname, 60, DOECHO);
+	if(fname[0] && strchr(fname, '.')) {
+	    strcpy(address, fname);
+	} else {
+	    outmsg("�����B�z");
+	    return 1;
+	}
+    }
+    if(invalidaddr(address))
+	return -2;
+    
+    //    sprintf(fname, "%d\n", cmode);
+    getdata(b_lines - 1, 0, "�ϥμҦ�(0/1)? [1]", fname, 3, LCECHO);
+    cmode = (fname[0] != '0' && fname[0] != '1') ? 1 : fname[0] - '0';
+    
+    now = time(NULL);
+    sprintf(fname, BBSHOME "/jobspool/water.src.%s-%d",
+	    cuser.userid, now);
+    sprintf(genbuf, "cp " BBSHOME "/home/%c/%s/%s %s",
+	    cuser.userid[0], cuser.userid, fhdr->filename, fname);
+    system(genbuf);
+    /* dirty code ;x */
+    sprintf(fname, BBSHOME "/jobspool/water.des.%s-%d",
+	    cuser.userid, now);
+    fp = fopen(fname, "wt");
+    fprintf(fp, "%s\n%s\n%d\n", cuser.userid, address, cmode);
+    fclose(fp);
+    return FULLUPDATE;
+}
+#endif
+static struct onekey_t mail_comms[] = {
+    {'z', mail_man},
+    {'c', mail_cite},
+    {'s', mail_save},
+    {'d', mail_del},
+    {'D', del_range},
+    {'r', mail_read},
+    {'R', mail_reply},
+    {'E', mail_edit},
+    {'m', mail_mark},
+    {'O', mail_nooutmail},
+    {'T', edit_title},
+    {'x', m_forward},
+    {'X', mail_cross_post},
+    {Ctrl('G'), built_mail_index}, /* �׫H�c */
+    {'y', multi_reply},
+    {Ctrl('I'), m_idle},
+    {'h', m_help},
+#ifdef OUTJOBSPOOL
+    {'u', mail_waterball},
+#endif
+    {'\0', NULL}
+};
+
+int m_read() {
+    if(get_num_records(currmaildir, sizeof(fileheader_t))) {
+	curredit = EDIT_MAIL;
+	curredit &= ~EDIT_ITEM;
+	i_read(RMAIL, currmaildir, mailtitle, maildoent, mail_comms,  -1);
+	curredit = 0;
+        currutmp->mailalert = load_mailalert(cuser.userid);
+	return 0;
+    } else {
+	outs("�z�S���ӫH");
+	return XEASY;
+    }
+}
+
+/* �H�����H */
+static int send_inner_mail(char *fpath, char *title, char *receiver) {
+    char genbuf[256];
+    fileheader_t mymail;
+    
+    if(!searchuser(receiver))
+	return -2;
+    sethomepath(genbuf, receiver);
+    stampfile(genbuf, &mymail);
+    if(!strcmp(receiver, cuser.userid)) {
+	strcpy(mymail.owner, "[" BBSNAME "]");
+	mymail.filemode = FILE_READ;
+    } else
+	strcpy(mymail.owner, cuser.userid);
+    strncpy(mymail.title, title, TTLEN);
+    unlink(genbuf);
+    Link(fpath, genbuf);
+    sethomedir(genbuf, receiver);
+    return do_append(genbuf, &mymail, sizeof(mymail));    
+}
+
+#include <netdb.h>
+#include <pwd.h>
+#include <time.h>
+
+#ifndef USE_BSMTP
+static int bbs_sendmail(char *fpath, char *title, char *receiver) {
+    static int configured = 0;
+    static char myhostname[STRLEN];
+    static char myusername[20];
+    struct hostent *hbuf;
+    struct passwd *pbuf;
+    char *ptr;
+    char genbuf[256];
+    FILE *fin, *fout;
+
+    /* ���~�d�I */
+    if((ptr = strchr(receiver, ';'))) {
+	struct tm *ptime;
+	time_t now;
+	
+	*ptr = '\0';
+    }
+    
+    if((ptr = strstr(receiver, str_mail_address)) || !strchr(receiver,'@')) {
+	char hacker[20];
+	int len;
+
+	if(strchr(receiver,'@')) {
+            len = ptr - receiver;
+            memcpy(hacker, receiver, len);
+            hacker[len] = '\0';
+        } else
+            strcpy(hacker,receiver);
+	return send_inner_mail(fpath, title, hacker);
+    }
+    
+    /* setup the hostname and username */
+    if(!configured) {
+	/* get host name */
+	hbuf = gethostbyname("localhost");
+	if(hbuf)
+	    strncpy(myhostname, hbuf->h_name, STRLEN);
+
+	/* get bbs uident */
+	pbuf = getpwuid(getuid());
+	if(pbuf)
+	    strncpy(myusername, pbuf->pw_name, 20);
+	if(hbuf && pbuf)
+	    configured = 1;
+	else
+	    return -1;
+    }
+    
+    /* Running the sendmail */
+    if(fpath == NULL) {
+	sprintf(genbuf, "/usr/sbin/sendmail %s > /dev/null", receiver);
+	fin = fopen("etc/confirm", "r");
+    } else {
+	sprintf(genbuf, "/usr/sbin/sendmail -f %s%s %s > /dev/null",
+		cuser.userid, str_mail_address, receiver);
+	fin = fopen(fpath, "r");
+    }
+    fout = popen(genbuf, "w");
+    if(fin == NULL || fout == NULL)
+	return -1;
+    
+    if(fpath)
+	fprintf(fout, "Reply-To: %s%s\nFrom: %s%s\n",
+		cuser.userid, str_mail_address, cuser.userid,
+		str_mail_address);
+    fprintf(fout, "To: %s\nSubject: %s\n", receiver, title);
+    fprintf(fout, "X-Disclaimer: " BBSNAME "�糧�H���e�����t�d�C\n\n");
+    
+    while(fgets(genbuf, 255, fin)) {
+	if(genbuf[0] == '.' && genbuf[1] == '\n')
+	    fputs(". \n", fout);
+	else
+	    fputs(genbuf, fout);
+    }
+    fclose(fin);
+    fprintf(fout, ".\n");
+    pclose(fout);
+    return 0;
+}
+#else /* USE_BSMTP */
+
+int bsmtp(char *fpath, char *title, char *rcpt, int method) {
+    char buf[80], *ptr;
+    time_t chrono;
+    MailQueue mqueue;
+
+    /* check if the mail is a inner mail */
+    if((ptr = strstr(rcpt, str_mail_address)) || !strchr(rcpt, '@')) {
+	char hacker[20];
+	int len;
+
+	if(strchr(rcpt,'@')) {
+            len = ptr - rcpt;
+            memcpy(hacker, rcpt, len);
+            hacker[len] = '\0';
+        } else
+            strcpy(hacker, rcpt);
+	return send_inner_mail(fpath, title, hacker);
+    }
+    
+    chrono = time(NULL);
+    if(method != MQ_JUSTIFY) { /* �{�ҫH */
+	/* stamp the queue file */
+	strcpy(buf, "out/");
+	for(;;) {
+	    sprintf(buf + 4,"M.%ld.A", ++chrono);
+	    if(!dashf(buf)) {
+		Link(fpath, buf);
+		break;
+	    }
+	}
+	
+	fpath = buf;
+
+	strcpy(mqueue.filepath, fpath);
+	strcpy(mqueue.subject, title);
+    }
+    /* setup mail queue */    
+    mqueue.mailtime = chrono;
+    mqueue.method = method;
+    strcpy(mqueue.sender, cuser.userid);
+    strcpy(mqueue.username, cuser.username);
+    strcpy(mqueue.rcpt, rcpt);
+    if(do_append("out/.DIR", (fileheader_t *)&mqueue, sizeof(mqueue)) < 0)
+	return 0;
+    return chrono;
+}
+#endif /* USE_BSMTP */
+
+int doforward(char *direct, fileheader_t *fh, int mode) {
+    static char address[60];
+    char fname[500];
+    int return_no;
+    char genbuf[200];
+    
+    if(!address[0])
+	strcpy(address, cuser.email);
+
+    if(address[0]) {
+	sprintf(genbuf, "�T�w��H�� [%s] ��(Y/N/Q)�H[Y] ", address);
+	getdata(b_lines - 1, 0, genbuf, fname, 3, LCECHO);
+	
+	if(fname[0] == 'q') {
+	    outmsg("������H");
+	    return 1;
+	}
+	if(fname[0] == 'n')
+	    address[0] = '\0';
+    }
+    
+    if(!address[0]) {
+	do{
+	    getdata(b_lines - 1, 0, "�п�J��H�a�}�G", fname, 60, DOECHO);
+	    if(fname[0]) {
+		if(strchr(fname, '.'))
+		    strcpy(address, fname);
+		else
+		    sprintf(address, "%s.bbs@%s", fname, MYHOSTNAME);
+	    } else {
+		outmsg("������H");
+		return 1;
+	    }
+	}while(mode=='Z' && strstr(address, MYHOSTNAME));
+    }
+    if(invalidaddr(address))
+	return -2;
+    
+    sprintf(fname, "����H�� %s, �еy��...", address);
+    outmsg(fname);
+    move(b_lines - 1, 0);
+    refresh();
+    
+    /* �l�ܨϥΪ� */
+    if(HAS_PERM(PERM_LOGUSER)) {
+	time_t now = time(NULL);
+	char msg[200];
+	
+	sprintf(msg, "%s mailforward to %s at %s",
+		cuser.userid, address, Cdate(&now));
+	log_user(msg);
+    }
+    
+    if(mode == 'Z') {
+	sprintf(fname, TAR_PATH " cfz - home/%c/%s | "
+		"/usr/bin/uuencode %s.tgz > %s",
+		cuser.userid[0], cuser.userid, cuser.userid, direct);
+	system(fname);
+	strcpy(fname, direct);
+    } else if(mode == 'U') {
+	char tmp_buf[128];
+
+	sprintf(fname, "/tmp/bbs.uu%05d", currpid);
+	sprintf(tmp_buf, "/usr/bin/uuencode %s/%s uu.%05d > %s",
+		direct, fh->filename, currpid, fname);
+	system(tmp_buf);
+    } else if (mode == 'F'){
+	char tmp_buf[128];
+	
+	sprintf(fname, "/tmp/bbs.f%05d", currpid);
+	sprintf(tmp_buf, "cp %s/%s %s",direct,fh->filename,fname);
+	system(tmp_buf);
+    } else
+	return -1;
+    
+    return_no =
+#ifndef USE_BSMTP
+	bbs_sendmail(fname, fh->title, address);
+#else
+    bsmtp(fname, fh->title, address,mode);
+#endif
+    unlink(fname);
+    return (return_no);
+}
+
+int load_mailalert(char *userid) {
+    struct stat st;
+    char maildir[256];
+    int fd;
+    register int numfiles;
+    fileheader_t my_mail;
+
+    sethomedir(maildir, userid); 
+    if(!HAS_PERM(PERM_BASIC))
+	return 0;
+    if(stat(maildir, &st) < 0)
+	return 0;
+    numfiles = st.st_size / sizeof(fileheader_t);
+    if(numfiles <= 0)
+	return 0;
+    
+    /* �ݬݦ��S���H���٨SŪ�L�H�q�ɧ��^�Y�ˬd�A�IJv���� */    
+    if((fd = open(maildir, O_RDONLY)) > 0) {
+	lseek(fd, st.st_size - sizeof(fileheader_t), SEEK_SET);
+	while(numfiles--) {
+	    read(fd, &my_mail, sizeof(fileheader_t));
+	    if(!(my_mail.filemode & FILE_READ)) {
+		close(fd);
+		return 1;
+	    }
+	    lseek(fd, -(off_t)2 * sizeof(fileheader_t), SEEK_CUR);
+	}
+	close(fd);
+    }
+    return 0;
+}
+
+#ifdef  EMAIL_JUSTIFY
+static void mail_justify(userec_t muser) {
+    fileheader_t mhdr;
+    char title[128], buf1[80];
+    FILE* fp;
+    
+    sethomepath(buf1, muser.userid);
+    stampfile(buf1, &mhdr);
+    unlink(buf1);
+    strcpy(mhdr.owner, cuser.userid);
+    strncpy(mhdr.title, "[�f�ֳq�L]", TTLEN);
+    mhdr.savemode = 0;
+    mhdr.filemode = 0;
+
+    if(valid_ident(muser.email) && !invalidaddr(muser.email)) {
+	char title[80], *ptr;
+	unsigned short checksum;            /* 16-bit is enough */
+	char ch;
+	
+	checksum = searchuser(muser.userid);
+	ptr = muser.email;
+	while((ch = *ptr++)) {
+	    if(ch <= ' ')
+		break;
+	    if(ch >= 'A' && ch <= 'Z')
+		ch |= 0x20;
+	    checksum = (checksum << 1) ^ ch;
+	}
+	
+	sprintf(title, "[PTT BBS]To %s(%d:%d) [User Justify]",
+		muser.userid, getuser(muser.userid) + MAGIC_KEY, checksum);
+	if(
+#ifndef USE_BSMTP
+	    bbs_sendmail(NULL, title, muser.email)
+#else
+	    bsmtp(NULL, title, muser.email, MQ_JUSTIFY);
+#endif
+	    < 0)
+	    Link("etc/bademail", buf1);
+	else
+	    Link("etc/replyemail", buf1);
+    } else
+	Link("etc/bademail", buf1);
+    sethomedir(title, muser.userid);
+    append_record(title, &mhdr, sizeof(mhdr));
+}
+#endif /* EMAIL_JUSTIFY */
diff --git a/mbbsd/mbbsd.c b/mbbsd/mbbsd.c
new file mode 100644
index 00000000..c4849da5
--- /dev/null
+++ b/mbbsd/mbbsd.c
@@ -0,0 +1,1465 @@
+/* $Id: mbbsd.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+#ifdef FreeBSD
+  #include <machine/limits.h>
+#else
+  #include <limits.h>
+#endif
+
+#define SOCKET_QLEN 4
+#define TH_LOW 100
+#define TH_HIGH 120
+
+extern int t_lines, t_columns;	/* Screen size / width */
+extern int b_lines;		/* Screen bottom line number: t_lines-1 */
+extern userinfo_t *currutmp;
+extern int curr_idle_timeout;
+
+static void do_aloha (char *hello);
+
+static jmp_buf byebye;
+
+int talkrequest = NA;
+
+static char remoteusername[40] = "?";
+
+extern struct fromcache_t *fcache;
+extern struct utmpfile_t *utmpshm;
+extern int fcache_semid;
+
+static unsigned char enter_uflag;
+static int use_shell_login_mode = 0;
+
+char fromhost[STRLEN] = "\0";
+
+static struct sockaddr_in xsin;
+
+/* set signal handler, which won't be reset once signal comes */
+static void
+signal_restart (int signum, void (*handler) (int))
+{
+    struct sigaction act;
+    act.sa_handler = handler;
+    memset (&(act.sa_mask), 0, sizeof (sigset_t));
+    act.sa_flags = 0;
+    sigaction (signum, &act, NULL);
+}
+
+static void
+start_daemon ()
+{
+    int n;
+    char buf[80];
+    
+    /*
+     * More idiot speed-hacking --- the first time conversion makes the C
+     * library open the files containing the locale definition and time zone.
+     * If this hasn't happened in the parent process, it happens in the
+     * children, once per connection --- and it does add up.
+     */
+    time_t dummy = time (NULL);
+    struct tm *dummy_time = localtime (&dummy);
+    
+    strftime (buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time);
+    
+    if ((n = fork ())){
+	exit (0);
+    }
+    
+    /* rocker.011018: it's a good idea to close all unexcept fd!! */
+    n = getdtablesize ();
+    while (n)
+	close (--n);
+    /* rocker.011018: we don't need to remember original tty, 
+       so request a new session id */
+    setsid ();
+    
+    /* rocker.011018: after new session, 
+       we should insure the process is clean daemon */
+    if ((n = fork ())){
+	exit (0);
+    }
+}
+
+static void
+reapchild (int sig)
+{
+    int state, pid;
+    
+    while ((pid = waitpid (-1, &state, WNOHANG | WUNTRACED)) > 0)
+	;
+}
+
+#define BANNER \
+"�i" BBSNAME "�j�� �x�j�y��� ��(" MYHOSTNAME ") �մT(" MYIP ") "
+/*
+#define BANNER \
+"�i" BBSNAME "�j�� �x�j�y��� ��(" MYHOSTNAME ")\r\n"\
+"  �մT(" MYIP ") "
+*/
+/* check load and print approriate banner string in buf */
+static int
+chkload (char *buf)
+{
+    char cpu_load[30];
+    int i;
+    
+    i = cpuload (cpu_load);
+    
+    sprintf (buf, BANNER" �t�έt��\r\n %s %s \r\n", cpu_load,
+	     (i > MAX_CPULOAD ? "�A���t���q�A�еy��A�� "
+	      "(�ЧQ��port 3000~3010�s�u)" : ""));
+#ifdef INSCREEN
+    strcpy(buf, (i > MAX_CPULOAD ? BANNER
+		 "���t���q�A�еy��A��(�ЧQ��port 3000~3010�s�u)" : ""));
+#else
+    sprintf(buf, BANNER "%s\r\n",
+	    (i > MAX_CPULOAD ? "���t���q�A�еy��A��(�ЧQ��port 3000~3010�s�u)":""));
+#endif
+    if (i > MAX_CPULOAD)
+	return 1;
+    else if (i > MAX_CPULOAD / 2)
+	curr_idle_timeout = 10 * 60;
+    else
+	curr_idle_timeout = 30 * 60;
+    
+    return 0;
+}
+
+extern userec_t cuser;
+
+void
+log_user (char *msg)
+{
+    char filename[200];
+    
+    sprintf (filename, BBSHOME "/home/%c/%s/USERLOG",
+	     cuser.userid[0], cuser.userid);
+    log_file (filename, msg);
+}
+
+extern time_t login_start_time;
+
+void
+log_usies (char *mode, char *mesg)
+{
+    char genbuf[200];
+    time_t now = time (0);
+    
+    if (!mesg)
+	sprintf (genbuf, cuser.userid[0] ? "%s %s %-12s Stay:%d (%s)" :
+		 "%s %s %s Stay:%d (%s)",
+		 Cdate (&now), mode, cuser.userid,
+		 (int)(now - login_start_time) / 60, cuser.username);
+    else
+	sprintf (genbuf, cuser.userid[0] ? "%s %s %-12s %s" : "%s %s %s%s",
+		 Cdate (&now), mode, cuser.userid, mesg);
+    log_file (FN_USIES, genbuf);
+    
+    /* �l�ܨϥΪ� */
+    if (HAS_PERM (PERM_LOGUSER))
+	log_user (genbuf);
+}
+
+static void
+setflags (int mask, int value)
+{
+    if (value)
+	cuser.uflag |= mask;
+    else
+	cuser.uflag &= ~mask;
+}
+
+extern int usernum;
+extern int currmode;
+
+void
+u_exit (char *mode)
+{
+    //userec_t xuser;
+    int diff = (time (0) - login_start_time) / 60;
+
+    reload_money(); 
+    auto_backup ();
+    save_brdbuf();
+    setflags (PAGER_FLAG, currutmp->pager != 1);
+    setflags (CLOAK_FLAG, currutmp->invisible);
+    
+    cuser.invisible = currutmp->invisible;
+    cuser.pager = currutmp->pager;
+    cuser.mind  = currutmp->mind; 
+    if (!(HAS_PERM (PERM_SYSOP) && HAS_PERM (PERM_DENYPOST)) &&
+	!currutmp->invisible )
+	do_aloha ("<<�U���q��>> -- �ڨ��o�I");
+    
+    purge_utmp (currutmp);
+    if ((cuser.uflag != enter_uflag) || (currmode & MODE_DIRTY) || diff)
+	{
+	    if (!diff && cuser.numlogins)
+		cuser.numlogins = --cuser.numlogins;
+	                                        /* Leeym �W�����d�ɶ���� */
+	}
+    passwd_update (usernum, &cuser);
+    log_usies (mode, NULL);
+}
+
+static void
+system_abort ()
+{
+    if (currmode)
+	u_exit ("ABORT");
+    
+    clear ();
+    refresh ();
+    fprintf (stdout, "���¥��{, �O�o�`�ӳ� !\n");
+    exit (0);
+}
+
+void
+abort_bbs (int sig)
+{
+    if (currmode)
+	u_exit ("AXXED");
+    exit (0);
+}
+
+static void
+abort_bbs_debug (int sig)
+{
+    static int reentrant = 0;
+    
+    if (!reentrant){
+	reentrant = 1;
+	if (currmode)
+	    u_exit ("AXXED");
+	setproctitle("debug me!(%d)",sig);
+	sleep(3600); /* wait 60 mins for debug */
+    }
+    exit (0);
+}
+
+/* �n�� BBS �{�� */
+static void
+mysrand ()
+{
+    srand (time (NULL) + currutmp->pid);  /* �ɶ��� pid �� rand �� seed */
+}
+
+extern userec_t xuser;
+
+int
+dosearchuser (char *userid)
+{
+    if ((usernum = getuser (userid)))
+	memcpy (&cuser, &xuser, sizeof (cuser));
+    else
+	memset (&cuser, 0, sizeof (cuser));
+    return usernum;
+}
+
+static void
+talk_request ()
+{
+    bell ();
+    bell ();
+    if (currutmp->msgcount){
+	char buf[200];
+	time_t now = time (0);
+	
+	sprintf (buf, "\033[33;41m��%s\033[34;47m [%s] %s \033[0m",
+		 utmpshm->uinfo[currutmp->destuip].userid, my_ctime (&now),
+		 (currutmp->sig == 2)? "���n�����s���I(��Ctrl-U,l�d�ݼ��T�O��)"
+		 : "�I�s�B�I�s�Ať��Ц^��");
+	move (0, 0);
+	clrtoeol ();
+	outs (buf);
+	refresh ();
+    }
+    else{
+	unsigned char mode0 = currutmp->mode;
+	char c0 = currutmp->chatid[0];
+	screenline_t *screen0 = calloc (t_lines, sizeof (screenline_t));
+	extern screenline_t *big_picture;
+	
+	currutmp->mode = 0;
+	currutmp->chatid[0] = 1;
+	memcpy (screen0, big_picture, t_lines * sizeof (screenline_t));
+	talkreply ();
+	currutmp->mode = mode0;
+	currutmp->chatid[0] = c0;
+	memcpy (big_picture, screen0, t_lines * sizeof (screenline_t));
+	free (screen0);
+	redoscr ();
+    }
+}
+
+extern char *fn_writelog;
+FILE *fp_writelog = NULL;
+
+void
+show_last_call_in (int save)
+{
+    char buf[200];
+    sprintf (buf, "\033[1;33;46m��%s\033[37;45m %s \033[m",
+	     currutmp->msgs[0].userid, currutmp->msgs[0].last_call_in);
+    move (b_lines, 0);
+    clrtoeol ();
+    refresh ();
+    outmsg (buf);
+    
+    if (save){
+	char genbuf[200];
+	time_t now;
+	if (!fp_writelog){
+	    sethomefile (genbuf, cuser.userid, fn_writelog);
+	    fp_writelog = fopen (genbuf, "a");
+	}
+	if (fp_writelog){
+	    time (&now);
+	    fprintf (fp_writelog, "%s \033[0m[%s]\n", buf, Cdatelite (&now));
+	}
+    }
+}
+
+extern  unsigned int currstat;
+water_t water[6], *swater[6], *water_which=&water[0];
+char    water_usies=0;
+extern  int watermode;
+static int add_history_water(water_t *w, msgque_t *msg)
+{
+    memcpy(&w->msg[w->top], msg, sizeof(msgque_t));
+    w->top++;
+    w->top %= WATERMODE(WATER_OFO) ? 5 : MAX_REVIEW;
+    
+    if (w->count < MAX_REVIEW)
+	w->count++;
+    
+    return w->count;
+}
+
+static int
+add_history(msgque_t *msg)
+{
+    int     i,j;
+    water_t *tmp;
+    add_history_water(&water[0], msg);
+    for(i = 0 ; i < 5 && swater[i] ; i++ )
+	if( swater[i]->pid == msg->pid )
+	    break;
+    if( i != 5 ){
+	if( !swater[i] ){
+	    water_usies = i + 1;
+	    swater[i] = &water[i + 1];
+	    strcpy(swater[i]->userid, msg->userid); 
+	    swater[i]->pid = msg->pid;
+	}
+	tmp = swater[i];
+    }
+    else{
+	tmp = swater[4];
+	memset(swater[4], 0, sizeof (water_t));	
+	strcpy(swater[4]->userid, msg->userid); 
+	swater[4]->pid = msg->pid;
+	i = 4;
+    }
+    
+    for( j = i ; j > 0 ; j-- )
+	swater[j] = swater[j - 1];
+    swater[0] = tmp;
+    add_history_water(swater[0], msg);
+    
+    if(WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){
+	if( watermode > 0 &&
+	    (water_which == swater[0] || water_which == &water[0]) ){
+	    if (watermode < water_which->count)
+		watermode++;
+	    t_display_new();
+	}
+    }
+    return i;
+}
+
+static void
+write_request (int sig)
+{
+  struct tm *ptime;
+  time_t now;
+
+  time (&now);
+  ptime = localtime (&now);
+
+  if (currutmp->pager != 0 &&
+      cuser.userlevel != 0 &&
+      currutmp->msgcount != 0 &&
+      currutmp->mode != TALK &&
+      currutmp->mode != EDITING &&
+      currutmp->mode != CHATING &&
+      currutmp->mode != PAGE &&
+      currutmp->mode != IDLE &&
+      currutmp->mode != MAILALL && currutmp->mode != MONITOR)
+    {
+      int i;
+      char c0 = currutmp->chatid[0];
+      int currstat0 = currstat;
+      unsigned char mode0 = currutmp->mode;
+
+      currutmp->mode = 0;
+      currutmp->chatid[0] = 2;
+      currstat = XMODE;
+
+      do
+	{
+	  bell ();
+	  show_last_call_in (1);
+	  igetch ();
+	  currutmp->msgcount--;
+	  if (currutmp->msgcount >= MAX_MSGS)
+	    {
+	      /* this causes chaos... jochang */
+	      raise (SIGFPE);
+	    }
+
+          add_history(&currutmp->msgs[0]);
+	  for (i = 0; i < currutmp->msgcount; i++)
+	    currutmp->msgs[i] = currutmp->msgs[i + 1];
+	}
+      while (currutmp->msgcount);
+      currutmp->chatid[0] = c0;
+      currutmp->mode = mode0;
+      currstat = currstat0;
+    }
+  else
+    {
+      bell ();
+      show_last_call_in (1);
+      add_history(&currutmp->msgs[0]);
+
+      refresh ();
+      currutmp->msgcount = 0;
+    }
+}
+
+#if 0
+static void
+write_request (int sig)
+{
+    int     i, mtimemin, wu;
+    static  char    inlock = 0;
+    if( inlock ) /* �p�G�w�g�i�ӤF (���ܤW�Ӥ��y�٨S���B�z��,
+		    �s�����y�S�i��) �h��������ƪ��� return   */
+	return;
+    inlock = 1;
+    do{
+	for( wu = 0 ; wu < 5 ; ++wu )
+	    if( water[wu].pid == currutmp->msgs[0].pid )
+		break;
+	if( wu == 5 ){
+	    for( i = 0, mtimemin = INT_MAX ; i < 5 ; ++i )
+		if( water[i].pid == 0 ){
+		    ++water_usies;
+		    wu = i;
+		    break;
+		}
+		else if( water[i].mtime < mtimemin ){
+		    mtimemin = water[i].mtime;
+		    wu = i;
+		}
+	    water[wu].pid = currutmp->msgs[0].pid;
+	    strcpy(water[wu].userid, currutmp->msgs[0].userid);
+            water[wu].msgtop = 0;
+            for( i = 0 ; i < 5 ; ++i )
+                water[wu].msg[i][0] = 0;
+	}
+	water[wu].mtime = time(NULL);
+	strncpy(water[wu].msg[ (int)water[wu].msgtop ],
+		currutmp->msgs[0].last_call_in, 64);
+	++water[wu].msgtop;
+	water[wu].msgtop %= 5;
+	
+	bell ();
+	show_last_call_in (1);
+	refresh();
+
+	if( watermode == 0 ){ /* in waterball selection mode 
+	    if( wu != 0 ){
+		water_scr(water_which, 0);
+		qsort(water, 5, sizeof(water_t), cmpwatermtime);
+		for( i = 0 ; i < 5 ; ++i )
+		    if( water[i].pid == 0 )
+			break;
+		    else
+			water_scr(i, 0);
+		water_scr(water_which = 0, 1);
+		refresh();
+	    } */
+	}
+	--currutmp->msgcount;
+	for( i = 0 ; i < currutmp->msgcount - 1 ; ++i )
+	    currutmp->msgs[i] = currutmp->msgs[i + 1];
+    } while( currutmp->msgcount > 0 );
+    inlock = 0;
+}
+#endif
+
+static void
+multi_user_check ()
+{
+    register userinfo_t *ui;
+    register pid_t pid;
+    char genbuf[3];
+    
+    if (HAS_PERM (PERM_SYSOP))
+	return;			/* don't check sysops */
+    
+    if (cuser.userlevel){
+	if (!(ui = (userinfo_t *) search_ulist (usernum)))
+	    return;			/* user isn't logged in */
+	
+	pid = ui->pid;
+	if (!pid /*|| (kill(pid, 0) == -1) */ )
+	    return;			/* stale entry in utmp file */
+	
+	getdata (b_lines - 1, 0, "�z�Q�R����L���ƪ� login (Y/N)�ܡH[Y] ",
+		 genbuf, 3, LCECHO);
+	
+	if (genbuf[0] != 'n'){
+	    if (pid > 0)
+		kill (pid, SIGHUP);
+	    log_usies ("KICK ", cuser.username);
+	}
+	else{
+	    if (search_ulistn(usernum, 3)!=NULL)
+		system_abort ();	/* Goodbye(); */
+	}
+    }
+    else{
+	/* allow multiple guest user */
+	if (search_ulistn(usernum, 100)!=NULL){
+	    outs ("\n��p�A�ثe�w���Ӧh guest, �еy��A�աC\n");
+	    pressanykey ();
+	    oflush ();
+	    exit (1);
+	}
+    }
+}
+
+/* bad login */
+static char str_badlogin[] = "logins.bad";
+
+static void
+logattempt (char *uid, char type)
+{
+    char fname[40];
+    int fd, len;
+    char genbuf[200];
+    
+    sprintf (genbuf, "%c%-12s[%s] %s@%s\n", type, uid,
+	     Cdate (&login_start_time), remoteusername, fromhost);
+    len = strlen (genbuf);
+    if ((fd = open (str_badlogin, O_WRONLY | O_CREAT | O_APPEND, 0644)) > 0){
+	write (fd, genbuf, len);
+	close (fd);
+    }
+    if (type == '-'){
+	sprintf (genbuf, "[%s] %s\n", Cdate (&login_start_time), fromhost);
+	len = strlen (genbuf);
+	sethomefile (fname, uid, str_badlogin);
+	if ((fd = open (fname, O_WRONLY | O_CREAT | O_APPEND, 0644)) > 0){
+	    write (fd, genbuf, len);
+	    close (fd);
+	}
+    }
+}
+
+extern char *str_new;
+extern char *err_uid;
+
+static void
+login_query ()
+{
+    char uid[IDLEN + 1], passbuf[PASSLEN];
+    int attempts;
+    char genbuf[200];
+    extern struct utmpfile_t *utmpshm;
+    resolve_utmp ();
+    attach_uhash ();
+    attempts = utmpshm->number;
+    show_file ("etc/Welcome", 1, -1, NO_RELOAD);
+    output ("1", 1);
+    if (attempts >= MAX_ACTIVE){
+	outs ("�ѩ�H�ƤӦh�A�бz�y��A�ӡC\n");
+	refresh ();
+	exit (1);
+    }
+    
+  /* hint */
+
+  attempts = 0;
+  while (1){
+      if (attempts++ >= LOGINATTEMPTS){
+	  more ("etc/goodbye", NA);
+	  pressanykey ();
+	  exit (1);
+      }
+      getdata (20, 0, "�п�J�N���A�ΥH[guest]���[�A�H[new]���U�G",
+	       uid, IDLEN + 1, DOECHO);
+      if (strcasecmp (uid, str_new) == 0){
+#ifdef LOGINASNEW
+	  new_register ();
+	  break;
+#else
+	  outs ("���t�Υثe�L�k�H new ���U, �Х� guest �i�J\n");
+	  continue;
+#endif
+      }
+      else if (uid[0] == '\0' || !dosearchuser (uid)){
+	  outs (err_uid);
+      }
+      else if (strcmp (uid, STR_GUEST)){
+	  getdata (21, 0, MSG_PASSWD, passbuf, PASSLEN, NOECHO);
+	  passbuf[8] = '\0';
+	  
+	  if (!checkpasswd (cuser.passwd, passbuf)
+	      /* || (HAS_PERM(PERM_SYSOP) && !use_shell_login_mode) */ ){
+	      logattempt (cuser.userid, '-');
+	      outs (ERR_PASSWD);
+	  }
+	  else{
+	      logattempt (cuser.userid, ' ');
+	      if (strcasecmp ("SYSOP", cuser.userid) == 0)
+		  cuser.userlevel = PERM_BASIC | PERM_CHAT | PERM_PAGE |
+		      PERM_POST | PERM_LOGINOK | PERM_MAILLIMIT |
+		      PERM_CLOAK | PERM_SEECLOAK | PERM_XEMPT |
+		      PERM_DENYPOST | PERM_BM | PERM_ACCOUNTS |
+		      PERM_CHATROOM | PERM_BOARD | PERM_SYSOP | PERM_BBSADM;
+	      break;
+	  }
+      }
+      else{ /* guest */
+	  cuser.userlevel = 0;
+	  cuser.uflag = COLOR_FLAG | PAGER_FLAG | BRDSORT_FLAG | MOVIE_FLAG;
+	  break;
+      }
+  }
+  multi_user_check ();
+  sethomepath (genbuf, cuser.userid);
+  mkdir (genbuf, 0755);
+}
+
+void
+add_distinct (char *fname, char *line)
+{
+    FILE *fp;
+    int n = 0;
+    
+    if ((fp = fopen (fname, "a+"))){
+	char buffer[80];
+	char tmpname[100];
+	FILE *fptmp;
+	
+	strcpy (tmpname, fname);
+	strcat (tmpname, "_tmp");
+	if (!(fptmp = fopen (tmpname, "w"))){
+	    fclose (fp);
+	    return;
+	}
+	rewind (fp);
+	while (fgets (buffer, 80, fp)){
+	    char *p = buffer + strlen (buffer) - 1;
+	    
+	    if (p[-1] == '\n' || p[-1] == '\r')
+		p[-1] = 0;
+	    if (!strcmp (buffer, line))
+		break;
+	    sscanf (buffer + strlen (buffer) + 2, "%d", &n);
+	    fprintf (fptmp, "%s%c#%d\n", buffer, 0, n);
+	}
+	
+	if (feof (fp))
+	    fprintf (fptmp, "%s%c#1\n", line, 0);
+	else{
+	    sscanf (buffer + strlen (buffer) + 2, "%d", &n);
+	    fprintf (fptmp, "%s%c#%d\n", buffer, 0, n + 1);
+	    while (fgets (buffer, 80, fp)){
+		sscanf (buffer + strlen (buffer) + 2, "%d", &n);
+		fprintf (fptmp, "%s%c#%d\n", buffer, 0, n);
+	    }
+	}
+	fclose (fp);
+	fclose (fptmp);
+	unlink (fname);
+	rename (tmpname, fname);
+    }
+}
+
+void
+del_distinct (char *fname, char *line)
+{
+    FILE *fp;
+    int n = 0;
+    
+    if ((fp = fopen (fname, "r"))){
+	char buffer[80];
+	char tmpname[100];
+	FILE *fptmp;
+	
+	strcpy (tmpname, fname);
+	strcat (tmpname, "_tmp");
+	if (!(fptmp = fopen (tmpname, "w"))){
+	    fclose (fp);
+	    return;
+	}
+	rewind (fp);
+	while (fgets (buffer, 80, fp)){
+	    char *p = buffer + strlen (buffer) - 1;
+	    
+	    if (p[-1] == '\n' || p[-1] == '\r')
+		p[-1] = 0;
+	    if (!strcmp (buffer, line))
+		break;
+	    sscanf (buffer + strlen (buffer) + 2, "%d", &n);
+	    fprintf (fptmp, "%s%c#%d\n", buffer, 0, n);
+	}
+	
+	if (!feof (fp))
+	    while (fgets (buffer, 80, fp)){
+		sscanf (buffer + strlen (buffer) + 2, "%d", &n);
+		fprintf (fptmp, "%s%c#%d\n", buffer, 0, n);
+	    }
+	fclose (fp);
+	fclose (fptmp);
+	unlink (fname);
+	rename (tmpname, fname);
+    }
+}
+
+#ifdef WHERE
+static int
+where (char *from)
+{
+    register int i = 0, count = 0, j;
+    
+    for (j = 0; j < fcache->top; j++){
+	char *token = strtok (fcache->domain[j], "&");
+	
+	i = 0;
+	count = 0;
+	while (token){
+	    if (strstr (from, token))
+		count++;
+	    token = strtok (NULL, "&");
+	    i++;
+	}
+	if (i == count)
+	    break;
+    }
+    if (i != count)
+	return 0;
+    return j;
+}
+#endif
+
+static void
+check_BM ()
+{
+    int i;
+    boardheader_t *bhdr;
+    extern boardheader_t *bcache;
+    extern int numboards;
+    
+    cuser.userlevel &= ~PERM_BM;
+    for (i = 0, bhdr = bcache; i < numboards && !is_BM (bhdr->BM); i++, bhdr++)
+	;
+}
+
+extern pid_t currpid;
+extern crosspost_t postrecord;
+
+static void
+setup_utmp (int mode)
+{
+    userinfo_t uinfo;
+    /*
+      char buf[80];
+      char remotebuf[1024];
+      time_t now = time(NULL);
+    */
+    memset (&uinfo, 0, sizeof (uinfo));
+    uinfo.pid = currpid = getpid ();
+    uinfo.uid = usernum;
+    uinfo.mode = currstat = mode;
+    uinfo.msgcount = 0;
+    uinfo.mailalert = load_mailalert (cuser.userid);
+    if (!(cuser.numlogins % 20) && cuser.userlevel & PERM_BM)
+	check_BM ();		/* Ptt �۰ʨ��U��¾�O�D�v�O */
+    
+    uinfo.userlevel = cuser.userlevel;
+    uinfo.sex = cuser.sex % 8;
+    uinfo.lastact = time (NULL);
+    
+    postrecord.times = 0;		/* �p��crosspost�� */
+    
+    strcpy (uinfo.userid, cuser.userid);
+    strcpy (uinfo.realname, cuser.realname);
+    strcpy (uinfo.username, cuser.username);
+    strncpy (uinfo.from, fromhost, 23);
+    
+    uinfo.five_win = cuser.five_win;
+    uinfo.five_lose = cuser.five_lose;
+    uinfo.five_tie = cuser.five_tie;
+    
+    uinfo.invisible = cuser.invisible % 2;
+    uinfo.pager = cuser.pager%5;
+    uinfo.mind  = cuser.mind; 
+    uinfo.brc_id = 0;
+#ifdef WHERE
+    uinfo.from_alias = where (fromhost);
+#else
+    uinfo.from_alias = 0;
+#endif
+#ifndef FAST_LOGIN
+    setuserfile (buf, "remoteuser");
+    
+    strcpy (remotebuf, fromhost);
+    strcat (remotebuf, ctime (&now));
+    remotebuf[strlen (remotebuf) - 1] = 0;
+    add_distinct (buf, remotebuf);
+#endif
+    if (enter_uflag & CLOAK_FLAG)
+	uinfo.invisible = YEA;
+    getnewutmpent (&uinfo);
+#ifndef _BBS_UTIL_C_
+    friend_load ();
+#endif
+}
+
+extern char margs[];
+extern char *str_sysop;
+extern char *loginview_file[NUMVIEWFILE][2];
+
+static void
+user_login ()
+{
+    char ans[4], i;
+    char genbuf[200];
+    struct tm *ptime, *tmp;
+    time_t now;
+    int a;
+    /*** Heat:�s�i��
+	 char *ADV[] = {
+	 "7/17 @LIVE �üu, ��Y�J �� �J�����n�e�� ptt ���R�Ϊ�!",
+	 "�����Ա��Ь� PttAct �O!!",
+	 }; ***/
+    
+    log_usies ("ENTER", fromhost);
+    setproctitle ("%s: %s", margs, cuser.userid);
+    resolve_garbage ();
+    resolve_fcache ();
+    resolve_boards ();  
+    memset( &water[0],0,sizeof(water_t) * 6);
+    strcpy(water[0].userid, " ���� ");
+    /* ��l�� uinfo�Bflag�Bmode */
+    setup_utmp (LOGIN);
+    mysrand ();		/* ��l��: random number �W�[user��ɶ����t�� */
+    currmode = MODE_STARTED;
+    enter_uflag = cuser.uflag;
+    
+    /* get local time */
+    time (&now);
+    ptime = localtime (&now);
+    tmp = localtime (&cuser.lastlogin);
+    if ((a = utmpshm->number) > fcache->max_user){
+	fcache->max_user = a;
+	fcache->max_time = now;
+    }
+    init_brdbuf(); 
+    brc_initial (DEFAULT_BOARD);
+    set_board ();
+    /* �e���B�z�}�l */
+    if (!(HAS_PERM (PERM_SYSOP) && HAS_PERM (PERM_DENYPOST)) && !currutmp->invisible )
+	do_aloha ("<<�W���q��>> -- �ڨӰաI");
+    if (ptime->tm_mday == cuser.day && ptime->tm_mon + 1 == cuser.month){
+	more ("etc/Welcome_birth", NA);
+	currutmp->birth = 1;
+    }
+    else{
+#ifdef MULTI_WELCOME_LOGIN
+	char    buf[80];
+	int     nScreens;
+	for( nScreens = 0 ; nScreens < 10 ; ++nScreens ){
+	    sprintf(buf, "etc/Welcome_login.%d", nScreens);
+	    if( access(buf, 0) < 0 )
+		break;
+	}
+	printf("%d\n", nScreens);
+	if( nScreens ==  0 ){ // multi screen error?
+	    more ("etc/Welcome_login", NA);
+	}
+	else{
+	    sprintf(buf, "etc/Welcome_login.%d", (int)login_start_time % nScreens);
+	    more (buf, NA);
+	}
+#else
+      more ("etc/Welcome_login", NA);
+#endif
+//      pressanykey();
+//    more("etc/CSIE_Week", NA);
+      currutmp->birth = 0;
+    }
+    
+    if (cuser.userlevel){/* not guest */
+	move (t_lines - 4, 0);
+	prints ("      �w��z�� \033[1;33m%d\033[0;37m �׫��X�����A"
+		"�W���z�O�q \033[1;33m%s\033[0;37m �s�������A\n"
+		"     �ڰO�o���ѬO \033[1;33m%s\033[0;37m�C\n",
+		++cuser.numlogins, cuser.lasthost, Cdate (&cuser.lastlogin));
+	pressanykey ();
+	
+	if (currutmp->birth && tmp->tm_mday != ptime->tm_mday){
+	    more ("etc/birth.post", YEA);
+	    brc_initial ("WhoAmI");
+	    set_board ();
+	    do_post ();
+	}
+	setuserfile (genbuf, str_badlogin);
+	if (more (genbuf, NA) != -1){
+	    getdata (b_lines - 1, 0, "�z�n�R���H�W���~���ժ��O����(Y/N)?[Y]",
+		     ans, 3, LCECHO);
+	    if (*ans != 'n')
+		unlink (genbuf);
+	}
+	check_register ();
+	strncpy (cuser.lasthost, fromhost, 16);
+	cuser.lasthost[15] = '\0';
+	restore_backup ();
+    }
+    else if (!strcmp (cuser.userid, STR_GUEST)){
+	char *nick[13] = {
+	    "���l", "����", "����", "�_�S�~", "½����",
+	    "��", "�B��", "�c�l", "�����", "�]��",
+	    "�K��", "�Ҩ�", "�j���k"
+	};
+	char *name[13] = {
+	    "�j�����l", "�x�M��", "���", "�i�f�i��", "���a����",
+	    "��", "������", "AIR Jordon", "����Q�븹", "����",
+	    "SASAYA����", "�n�J", "���|�J��������"
+	};
+	char *addr[13] = {
+	    "�Ѱ�ֶ�", "�j��", "��q�p�]��", "����", "�������G",
+	    "����", "�쥻��", "NIKE", "Ĭ�p", "�k�K618��",
+	    "�R����", "�ѤW", "�Ŧ�����G"
+	};
+	i = login_start_time % 13;
+	sprintf (cuser.username, "����}�Ӫ�%s", nick[(int) i]);
+	sprintf (currutmp->username, cuser.username);
+	sprintf (cuser.realname, name[(int) i]);
+	sprintf (currutmp->realname, cuser.realname);
+	sprintf (cuser.address, addr[(int) i]);
+	cuser.sex = i % 8;
+	currutmp->pager = 2;
+	pressanykey ();
+    }
+    else
+	pressanykey ();
+    
+    if (!PERM_HIDE (currutmp))
+	cuser.lastlogin = login_start_time;
+    
+    passwd_update (usernum, &cuser);
+    
+    for (i = 0; i < NUMVIEWFILE; i++)
+	if ((cuser.loginview >> i) & 1)
+	    more (loginview_file[(int) i][0], YEA);
+    
+    
+}
+
+static void
+do_aloha (char *hello)
+{
+    FILE *fp;
+    char userid[80];
+    char genbuf[200];
+    
+    setuserfile (genbuf, "aloha");
+    if ((fp = fopen (genbuf, "r"))){
+	sprintf (genbuf, hello);
+	while (fgets (userid, 80, fp)){
+	    userinfo_t *uentp;
+	    int tuid;
+	    
+	    if ((tuid = searchuser (userid)) && tuid != usernum &&
+		(uentp = (userinfo_t *) search_ulist (tuid)) &&
+		isvisible(uentp, currutmp)){
+		my_write (uentp->pid, genbuf, uentp->userid, 2);
+	    }
+	}
+	fclose (fp);
+    }
+}
+
+static void
+do_term_init ()
+{
+    term_init ();
+    initscr ();
+}
+
+extern char *fn_register;
+extern int showansi;
+
+static void
+start_client ()
+{
+    extern struct commands_t cmdlist[];
+#if FORCE_PROCESS_REGISTER_FORM
+    int nreg;
+#endif
+    
+    /* system init */
+    nice (2);			/*  Ptt: lower priority */
+    login_start_time = time (0);
+    currmode = 0;
+    
+    signal (SIGHUP, abort_bbs);
+    signal (SIGTERM, abort_bbs);
+    signal (SIGPIPE, abort_bbs);
+    
+    signal (SIGINT, abort_bbs_debug);
+    signal (SIGQUIT, abort_bbs_debug);
+    signal (SIGILL, abort_bbs_debug);
+    signal (SIGABRT, abort_bbs_debug);
+    signal (SIGFPE, abort_bbs_debug);
+    signal (SIGBUS, abort_bbs_debug);
+    signal (SIGSEGV, abort_bbs_debug);
+    
+    signal_restart (SIGUSR1, talk_request);
+    signal_restart (SIGUSR2, write_request);
+    
+    dup2 (0, 1);
+    
+    do_term_init ();
+    signal (SIGALRM, abort_bbs);
+    alarm (600);
+    login_query ();		/* Ptt �[�Wlogin time out */
+    user_login ();
+    m_init ();
+    
+#if FORCE_PROCESS_REGISTER_FORM
+    if (HAS_PERM (PERM_SYSOP) && (nreg = dashs (fn_register) / 163) > 100){
+	char cpu_load[30];
+	if (cpuload (cpu_load) > MAX_CPULOAD * 2 / 3)
+	    /* DickG: �ھڥثe�� load �ӨM�w�n�f�֪��ƥ� */
+	    scan_register_form (fn_register, 1, nreg / 20);	
+	else
+	    scan_register_form (fn_register, 1, nreg / 10);
+    }
+#endif
+    if (HAVE_PERM (PERM_SYSOP | PERM_BM))
+	b_closepolls ();
+    if (!(cuser.uflag & COLOR_FLAG))
+	showansi = 0;
+#ifdef DOTIMEOUT
+    /* init_alarm(); */// cause strange logout with saving post.
+    signal (SIGALRM, SIG_IGN);
+#else
+    signal (SIGALRM, SIG_IGN);
+#endif
+    if (chkmailbox ())
+	m_read ();
+    
+    domenu (MMENU, "�D�\\���", (currutmp->mailalert ? 'M' : 'C'), cmdlist);
+}
+
+/* FSA (finite state automata) for telnet protocol */
+static void
+telnet_init ()
+{
+    static char svr[] = {
+	IAC, DO, TELOPT_TTYPE,
+	IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE,
+	IAC, WILL, TELOPT_ECHO,
+	IAC, WILL, TELOPT_SGA
+    };
+    char *cmd;
+    int n, len, rset;
+    struct timeval to;
+    char buf[64];
+    for (n = 0, cmd = svr; n < 4; n++){
+	len = (n == 1 ? 6 : 3);
+	write (0, cmd, len);
+	cmd += len;
+	to.tv_sec = 3;
+	to.tv_usec = 0;
+	rset=1;
+	if (select (1, (fd_set *) & rset, NULL, NULL, &to) > 0)
+	    recv(0, buf, sizeof (buf),0);
+    }
+}
+
+/* ���o remote user name �H�P�w����                */
+/*
+ * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
+ * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote
+ * host to look up the owner of a connection. The information should not be
+ * used for authentication purposes. This routine intercepts alarm signals.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; }
+#define RFC931_TIMEOUT   10
+#define RFC931_PORT     113	/* Semi-well-known port */
+#define ANY_PORT        0	/* Any old port will do */
+
+/* timeout - handle timeouts */
+static void
+timeout (int sig)
+{
+    longjmp (byebye, sig);
+}
+
+static void
+getremotename (struct sockaddr_in *from, char *rhost, char *rname)
+{
+
+    /* get remote host name */
+    
+#ifdef FAST_LOGIN
+    strcpy (rhost, (char *) inet_ntoa (from->sin_addr));
+#else
+    struct sockaddr_in our_sin;
+    struct sockaddr_in rmt_sin;
+    unsigned rmt_port, rmt_pt;
+    unsigned our_port, our_pt;
+    FILE *fp;
+    char buffer[512], user[80], *cp;
+    int s;
+    static struct hostent *hp;
+    
+    
+    hp = NULL;
+    if (setjmp (byebye) == 0){
+	signal (SIGALRM, timeout);
+	alarm (3);
+	hp = gethostbyaddr ((char *) &from->sin_addr, sizeof (struct in_addr),
+			    from->sin_family);
+	alarm (0);
+    }
+    strcpy (rhost, hp ? hp->h_name : (char *) inet_ntoa (from->sin_addr));
+    
+/*
+ * Use one unbuffered stdio stream for writing to and for reading from the
+ * RFC931 etc. server. This is done because of a bug in the SunOS 4.1.x
+ * stdio library. The bug may live in other stdio implementations, too.
+ * When we use a single, buffered, bidirectional stdio stream ("r+" or "w+"
+ * mode) we read our own output. Such behaviour would make sense with
+ * resources that support random-access operations, but not with sockets.
+ */
+
+    s = sizeof (our_sin);
+    if (getsockname (0, (struct sockaddr *) &our_sin, &s) < 0)
+	return;
+    
+    if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+	return;
+    
+    if (!(fp = fdopen (s, "r+"))){
+	close (s);
+	return;
+    }
+    /* Set up a timer so we won't get stuck while waiting for the server. */
+    if (setjmp (byebye) == 0){
+	signal (SIGALRM, timeout);
+	alarm (RFC931_TIMEOUT);
+	
+/*
+ * Bind the local and remote ends of the query socket to the same IP
+ * addresses as the connection under investigation. We go through all
+ * this trouble because the local or remote system might have more than
+ * one network address. The RFC931 etc. client sends only port numbers;
+ * the server takes the IP addresses from the query socket.
+ */
+	our_pt = ntohs (our_sin.sin_port);
+	our_sin.sin_port = htons (ANY_PORT);
+	
+	rmt_sin = *from;
+	rmt_pt = ntohs (rmt_sin.sin_port);
+	rmt_sin.sin_port = htons (RFC931_PORT);
+	
+	setbuf (fp, (char *) 0);
+	s = fileno (fp);
+	
+	if (bind (s, (struct sockaddr *) &our_sin, sizeof (our_sin)) >= 0 &&
+	    connect (s, (struct sockaddr *) &rmt_sin, sizeof (rmt_sin)) >= 0){
+/*
+ * Send query to server. Neglect the risk that a 13-byte write would
+ * have to be fragmented by the local system and cause trouble with
+ * buggy System V stdio libraries.
+ */
+	    fprintf (fp, "%u,%u\r\n", rmt_pt, our_pt);
+	    fflush (fp);
+/*
+ * Read response from server. Use fgets()/sscanf() so we can work
+ * around System V stdio libraries that incorrectly assume EOF when a
+ * read from a socket returns less than requested.
+ */
+	    if (fgets (buffer, sizeof (buffer), fp) && !ferror (fp)
+		&& !feof (fp)
+		&& sscanf (buffer, "%u , %u : USERID :%*[^:]:%79s", &rmt_port,
+			   &our_port, user) == 3 && rmt_pt == rmt_port
+		&& our_pt == our_port){
+
+/*
+ * Strip trailing carriage return. It is part of the protocol, not
+ * part of the data.
+ */
+		if ((cp = (char *) strchr (user, '\r')))
+		    *cp = 0;
+		strcpy (rname, user);
+	    }
+	}
+      alarm (0);
+    }
+    fclose (fp);
+#endif
+}
+
+static int
+bind_port (int port)
+{
+    int sock, on;
+    
+    sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    
+    on = 1;
+    setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on));
+    setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
+    
+    on = 0;
+    setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &on, sizeof (on));
+    
+    xsin.sin_port = htons (port);
+    if (bind (sock, (struct sockaddr *) &xsin, sizeof xsin) < 0){
+	syslog (LOG_INFO, "bbsd bind_port can't bind to %d", port);
+	exit (1);
+    }
+    if (listen (sock, SOCKET_QLEN) < 0){
+	syslog (LOG_INFO, "bbsd bind_port can't listen to %d", port);
+	exit (1);
+    }
+    return sock;
+}
+
+
+/*******************************************************/
+
+
+static void shell_login (int argc, char *argv[], char *envp[]);
+static void daemon_login (int argc, char *argv[], char *envp[]);
+static int check_ban_and_load (int fd);
+#ifdef SUPPORT_GB
+extern int current_font_type;
+#endif
+
+int
+main (int argc, char *argv[], char *envp[])
+{
+    /* avoid SIGPIPE */
+    signal (SIGPIPE, SIG_IGN);
+    
+    /* avoid erroneous signal from other mbbsd */
+    signal (SIGUSR1, SIG_IGN);
+    signal (SIGUSR2, SIG_IGN);
+    
+    /* check if invoked as "bbs" */
+    if (argc == 3)
+	shell_login (argc, argv, envp);
+    else
+	daemon_login (argc, argv, envp);
+    
+    return 0;
+}
+
+static void
+shell_login (int argc, char *argv[], char *envp[])
+{
+    
+    /* Give up root privileges: no way back from here */
+    setgid (BBSGID);
+    setuid (BBSUID);
+    chdir (BBSHOME);
+    
+    /* mmap passwd file */
+    if (passwd_mmap ())
+	exit (1);
+    
+    use_shell_login_mode = 1;
+    initsetproctitle (argc, argv, envp);
+    
+    /* copy fromindent: Standard input:1138: Error:Unexpected end of file
+       the original "bbs" */
+    if (argc > 1){
+	strcpy (fromhost, argv[1]);
+	if (argc > 3)
+	    strcpy (remoteusername, argv[3]);
+    }
+    
+    close (2);
+    /* don't close fd 1, at least init_tty need it */
+    
+    init_tty ();
+    if (check_ban_and_load (0)){
+	exit (0);
+    }
+    start_client ();
+}
+
+static void
+daemon_login (int argc, char *argv[], char *envp[])
+{
+    int msock, csock;		/* socket for Master and Child */
+    FILE *fp;
+    int listen_port = 23;
+    int len_of_sock_addr;
+    char buf[256];
+    
+    /* setup standalone */
+
+    start_daemon();
+    
+    signal_restart(SIGCHLD, reapchild);
+    
+    /* choose port */
+    if(argc == 1)
+	listen_port = 3006;
+    else if(argc >= 2)
+	listen_port = atoi(argv[1]);
+
+    sprintf(margs, "%s %d ", argv[0],listen_port);
+
+    /* port binding */
+    xsin.sin_family = AF_INET;
+    msock = bind_port(listen_port);
+    if(msock<0) {
+	syslog(LOG_INFO, "mbbsd bind_port failed.\n");
+	exit(1);
+    }
+    
+
+    initsetproctitle(argc, argv, envp);
+    setproctitle("%s: listening ", margs);
+    
+    /* Give up root privileges: no way back from here */
+    setgid(BBSGID);
+    setuid(BBSUID);
+    chdir(BBSHOME);
+    
+    /* mmap passwd file */
+    if(passwd_mmap())
+    {
+	exit(1);
+    }
+    sprintf(buf, "run/mbbsd.%d.pid", listen_port);
+    if((fp = fopen(buf, "w"))) {
+	fprintf(fp, "%d\n", getpid());
+	fclose(fp);
+    }
+    
+    /* main loop */
+    for(;;) {
+	len_of_sock_addr = sizeof(xsin);
+	csock = accept(msock, (struct sockaddr *)&xsin, &len_of_sock_addr);
+
+	if(csock < 0) {
+	    if(errno!=EINTR) sleep(1);
+	    continue;
+	}
+	
+	if(check_ban_and_load(csock))
+	{
+	    close(csock);
+	    continue;
+	}
+	
+	if(fork()==0)
+	    break;
+	else
+	    close(csock);
+
+    }	
+    /* here is only child running */
+	
+    setproctitle("%s: ...login wait... ", margs);
+    close(msock);
+    dup2(csock, 0);
+    close(csock);
+
+    getremotename(&xsin, fromhost, remoteusername);
+    telnet_init();
+    start_client();
+    close(0);
+    close(1);
+}
+
+/* check if we're banning login and if the load is too high.
+   if login is permitted, return 0;
+   else return -1;
+   approriate message is output to fd.
+*/
+static int check_ban_and_load(int fd)
+{
+    FILE *fp;
+    static char buf[256];
+    static time_t chkload_time = 0;
+    static int overload = 0;	/* overload or banned, update every 1 sec  */
+    static int banned = 0;
+    
+    if((time(0) - chkload_time) > 1) {
+	overload = chkload(buf);
+	banned = !access(BBSHOME "/BAN",R_OK) &&
+	    (strcmp(fromhost, "localhost") != 0);
+	chkload_time = time(0);
+    }
+
+    write(fd, buf, strlen(buf));
+
+    if(banned && (fp = fopen(BBSHOME "/BAN", "r"))) {
+	while(fgets(buf, 256, fp))
+	    write(fd, buf, strlen(buf));
+	fclose(fp);
+    }
+
+    if(banned || overload)
+	return -1;
+
+#ifdef INSCREEN
+    write(fd, INSCREEN, strlen(INSCREEN));
+#endif
+
+    return 0;
+}
diff --git a/mbbsd/menu.c b/mbbsd/menu.c
new file mode 100644
index 00000000..d48670b3
--- /dev/null
+++ b/mbbsd/menu.c
@@ -0,0 +1,596 @@
+/* $Id: menu.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+extern int usernum;
+extern int talkrequest;
+extern char *fn_register;
+extern char currboard[];        /* name of currently selected board */
+extern int currmode;
+extern unsigned int currstat;
+extern char reset_color[];
+extern userinfo_t *currutmp;
+extern char *BBSName;
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+
+/* help & menu processring */
+static int refscreen = NA;
+extern char *boardprefix;
+extern struct utmpfile_t *utmpshm;
+
+int egetch() {
+    int rval;
+
+    while(1) {
+        rval = igetkey();
+        if(talkrequest) {
+            talkreply();
+            refscreen = YEA;
+            return rval;
+        }
+        if(rval != Ctrl('L'))
+            return rval;
+        redoscr();
+    }
+}
+
+extern userec_t cuser;
+extern char *fn_board;
+extern char board_hidden_status;
+
+void showtitle(char *title, char *mid) {
+    char    buf[40], numreg[50];
+    int     nreg, spc = 0, pad, bid;
+    boardheader_t   bh;
+    static  char    lastboard[16] = {0};
+
+    spc = strlen(mid);
+    if(title[0] == 0)
+        title++;
+    else if(currutmp->mailalert) {
+        mid = "\033[41;5m   �l�t�ӫ��a�o   " TITLE_COLOR;
+        spc = 22;
+    } else if(HAS_PERM(PERM_SYSOP) && (nreg = dashs(fn_register)/163) > 10) {
+        /* �W�L�Q�ӤH���f�� */
+        sprintf(numreg, "\033[41;5m  ��%03d/%03d���f��  " TITLE_COLOR,
+		nreg,
+		(int)dashs("register.new.tmp") / 163);
+        mid = numreg;
+        spc = 22;
+    }
+    spc = 66 - strlen(title) - spc - strlen(currboard);
+    if(spc < 0)
+        spc = 0;
+    pad = 1 - (spc & 1);
+    memset(buf, ' ', spc >>= 1);
+    buf[spc] = '\0';
+    
+    clear();
+    prints(TITLE_COLOR "�i%s�j%s\033[33m%s%s%s\033[3%s�m",
+           title, buf, mid, buf, " " + pad,
+           currmode & MODE_SELECT ? "6m�t�C" : currmode & MODE_ETC ? "5m��L" :
+           currmode & MODE_DIGEST ? "2m��K" : "7m�ݪO");
+
+    if( strcmp(currboard, lastboard) ){ /* change board */
+	if( currboard[0] != 0 &&
+	    (bid = getbnum(currboard)) > 0 &&
+	    (get_record(fn_board, &bh, sizeof(bh), bid) != -1) ){
+	    board_hidden_status = ((bh.brdattr & BRD_HIDE) &&
+				   (bh.brdattr & BRD_POSTMASK));
+	    strncpy(lastboard, currboard, sizeof(lastboard));
+	}
+    }
+    
+    if( board_hidden_status )
+	prints("\033[32m%s", currboard);
+    else
+	prints("%s", currboard);
+    prints("\033[3%dm�n\033[0m\n", currmode & MODE_SELECT ? 6 :
+	   currmode & MODE_ETC ? 5 : currmode & MODE_DIGEST ? 2 : 7);
+}
+
+/* �ʵe�B�z */
+#define FILMROW 11
+static unsigned char menu_row = 12;
+static unsigned char menu_column = 20;
+static char mystatus[160];
+
+static int u_movie() {
+    cuser.uflag ^= MOVIE_FLAG;
+    return 0;
+}
+
+void movie(int i) {
+    extern struct pttcache_t *ptt;
+    static short history[MAX_HISTORY];
+    static char myweek[] = "�Ѥ@�G�T�|����";
+    const char *msgs[] = {"����", "���}", "�ޱ�", "����","�n��"};
+    time_t now = time(NULL);
+    struct tm *ptime = localtime(&now);
+
+    if((currstat != CLASS) && (cuser.uflag & MOVIE_FLAG) &&
+       !ptt->busystate && ptt->max_film > 0) {
+        if(currstat == PSALE) {
+            i = PSALE;
+            reload_money();
+        } else {
+            do {
+                if(!i)
+                    i = 1 + (int)(((float)ptt->max_film *
+                                   rand()) / (RAND_MAX + 1.0));
+
+                for(now = ptt->max_history; now >= 0; now--)
+                    if(i == history[now]) {
+                        i = 0;
+                        break;
+                    }
+            } while(i == 0);
+        }
+
+        memcpy(history, &history[1], ptt->max_history * sizeof(short));
+        history[ptt->max_history] = now = i;
+
+        if(i == 999)       /* Goodbye my friend */
+            i = 0;
+
+        move(1, 0);
+        clrtoline(1 + FILMROW); /* �M���W���� */
+        Jaky_outs(ptt->notes[i], 11); /* �u�L11��N�n */
+        outs(reset_color);
+    }
+    i = ptime->tm_wday << 1;
+    sprintf(mystatus, "\033[34;46m[%d/%d �P��%c%c %d:%02d]\033[1;33;45m%-14s"
+            "\033[30;47m �ثe�{�̦� \033[31m%d\033[30m�H, �ڬO\033[31m%-12s"
+            "\033[30m[����]\033[31m%s\033[0m",
+            ptime->tm_mon + 1, ptime->tm_mday, myweek[i], myweek[i + 1],
+            ptime->tm_hour, ptime->tm_min, currutmp->birth ?
+            "�ͤ�n�Ыȭ�" : ptt->today_is,
+            utmpshm->number, cuser.userid, msgs[currutmp->pager]);
+    outmsg(mystatus);
+    refresh();
+}
+
+static int show_menu(commands_t *p) {
+    register int n = 0;
+    register char *s;
+    const char *state[4]={"�Υ\\��", "�w�h��", "�۩w��", "SHUTUP"};
+    char buf[80];
+
+    movie(currstat);
+
+    move(menu_row, 0);
+    while((s = p[n].desc)) {
+        if(HAS_PERM(p[n].level)) {
+            sprintf(buf, s + 2, state[cuser.proverb % 4]);
+            prints("%*s  (\033[1;36m%c\033[0m)%s\n", menu_column, "", s[1],
+                   buf);
+        }
+        n++;
+    }
+    return n - 1;
+}
+
+void domenu(int cmdmode, char *cmdtitle, int cmd, commands_t cmdtable[]) {
+    int lastcmdptr;
+    int n, pos, total, i;
+    int err;
+    int chkmailbox();
+    static char cmd0[LOGIN];
+
+    if(cmd0[cmdmode])
+        cmd = cmd0[cmdmode];
+
+    setutmpmode(cmdmode);
+
+    showtitle(cmdtitle, BBSName);
+
+    total = show_menu(cmdtable);
+
+    outmsg(mystatus);
+    lastcmdptr = pos = 0;
+
+    do {
+        i = -1;
+        switch(cmd) {
+        case Ctrl('C'):
+            cal();
+            i = lastcmdptr;
+            refscreen = YEA;
+            break;
+        case Ctrl('I'):
+            t_idle();
+            refscreen = YEA;
+            i = lastcmdptr;
+            break;
+        case Ctrl('N'):
+            New();
+            refscreen = YEA;
+            i = lastcmdptr;
+            break;
+        case Ctrl('A'):
+            if(mail_man() == FULLUPDATE)
+                refscreen = YEA;
+            i = lastcmdptr;
+            break;
+        case KEY_DOWN:
+            i = lastcmdptr;
+        case KEY_HOME:
+        case KEY_PGUP:
+            do {
+                if(++i > total)
+                    i = 0;
+            } while(!HAS_PERM(cmdtable[i].level));
+            break;
+        case KEY_END:
+        case KEY_PGDN:
+            i = total;
+            break;
+        case KEY_UP:
+            i = lastcmdptr;
+            do {
+                if(--i < 0)
+                    i = total;
+            } while(!HAS_PERM(cmdtable[i].level));
+            break;
+        case KEY_LEFT:
+        case 'e':
+        case 'E':
+            if(cmdmode == MMENU)
+                cmd = 'G';
+            else if((cmdmode == MAIL) && chkmailbox())
+                cmd = 'R';
+            else
+                return;
+        default:
+            if((cmd == 's'  || cmd == 'r') &&
+               (currstat == MMENU || currstat == TMENU || currstat == XMENU)) {
+                if(cmd == 's')
+                    ReadSelect();
+                else
+                    Read();
+                refscreen = YEA;
+                i = lastcmdptr;
+                break;
+            }
+
+            if(cmd == '\n' || cmd == '\r' || cmd == KEY_RIGHT) {
+                move(b_lines, 0);
+                clrtoeol();
+
+                currstat = XMODE;
+
+                if((err = (*cmdtable[lastcmdptr].cmdfunc) ()) == QUIT)
+                    return;
+                currutmp->mode = currstat = cmdmode;
+
+                if(err == XEASY) {
+                    refresh();
+                    safe_sleep(1);
+                } else if(err != XEASY + 1 || err == FULLUPDATE)
+                    refscreen = YEA;
+
+                if(err != -1)
+                    cmd = cmdtable[lastcmdptr].desc[0];
+                else
+                    cmd = cmdtable[lastcmdptr].desc[1];
+                cmd0[cmdmode] = cmdtable[lastcmdptr].desc[0];
+            }
+
+            if(cmd >= 'a' && cmd <= 'z')
+                cmd &= ~0x20;
+            while(++i <= total)
+                if(cmdtable[i].desc[1] == cmd)
+                    break;
+        }
+
+        if(i > total || !HAS_PERM(cmdtable[i].level))
+            continue;
+
+        if(refscreen) {
+            showtitle(cmdtitle, BBSName);
+
+            show_menu(cmdtable);
+
+            outmsg(mystatus);
+            refscreen = NA;
+        }
+        cursor_clear(menu_row + pos, menu_column);
+        n = pos = -1;
+        while(++n <= (lastcmdptr = i))
+            if(HAS_PERM(cmdtable[n].level))
+                pos++;
+
+        cursor_show(menu_row + pos, menu_column);
+    } while(((cmd = egetch()) != EOF) || refscreen);
+
+    abort_bbs(0);
+}
+/* INDENT OFF */
+
+/* administrator's maintain menu */
+static commands_t adminlist[] = {
+    {m_user, PERM_ACCOUNTS,           "UUser          �ϥΪ̸��"},
+    {search_user_bypwd, PERM_SYSOP,   "SSearch User   �S���j�M�ϥΪ�"},
+    {search_user_bybakpwd,PERM_SYSOP, "OOld User data �d�\\�ƥ��ϥΪ̸��"},
+    {m_board, PERM_SYSOP,             "BBoard         �]�w�ݪO"},
+    {m_register, PERM_SYSOP,          "RRegister      �f�ֵ��U����"},
+    {cat_register, PERM_SYSOP,        "CCatregister   �L�k�f�֮ɥΪ�"},
+    {x_file, PERM_SYSOP|PERM_VIEWSYSOP,     "XXfile         �s��t���ɮ�"},
+    {give_money, PERM_SYSOP|PERM_VIEWSYSOP, "GGivemoney     ���]��"},
+#ifdef  HAVE_MAILCLEAN
+    {m_mclean, PERM_SYSOP,            "MMail Clean    �M�z�ϥΪ̭ӤH�H�c"},
+#endif
+#ifdef  HAVE_REPORT
+    {m_trace, PERM_SYSOP,             "TTrace         �]�w�O�_�O��������T"},
+#endif
+    {NULL, 0, NULL}
+};
+
+/* mail menu */
+static commands_t maillist[] = {
+    {m_new, PERM_READMAIL,      "RNew           �\\Ū�s�i�l��"},
+    {m_read, PERM_READMAIL,     "RRead          �h�\\��Ū�H���"},
+    {m_send, PERM_BASIC,        "RSend          �����H�H"},
+    {main_bbcall, PERM_LOGINOK, "BBBcall        \033[1;31m�q�ܯ���\033[m"},
+    {x_love, PERM_LOGINOK,      "PPaper         \033[1;32m���Ѳ��;�\033[m "},
+    {mail_list, PERM_BASIC,     "RMail List     �s�ձH�H"},
+    {setforward, PERM_LOGINOK,"FForward       \033[32m�]�w�H�c�۰���H\033[m"},
+    {m_sysop, 0,                "YYes, sir!     �ԴA����"},
+    {m_internet, PERM_INTERNET, "RInternet      �H�H�� Internet"},
+    {mail_mbox, PERM_INTERNET,  "RZip UserHome  ��Ҧ��p�H��ƥ��]�^�h"},
+    {built_mail_index, PERM_LOGINOK, "SSavemail      ��H��Ϧ^��"},
+    {mail_all, PERM_SYSOP,      "RAll           �H�H���Ҧ��ϥΪ�"},
+    {NULL, 0, NULL}
+};
+
+/* Talk menu */
+static commands_t talklist[] = {
+    {t_users, 0,            "UUsers         ������Ѥ�U"},
+    {t_pager, PERM_BASIC,   "PPager         �����I�s��"},
+    {t_idle, 0,             "IIdle          �o�b"},
+    {t_query, 0,            "QQuery         �d�ߺ���"},
+    {t_qchicken, 0,         "WWatch Pet     �d���d��"},
+    {t_talk, PERM_PAGE,     "TTalk          ��H���"},
+    {t_chat, PERM_CHAT,     "CChat          ��a���{����h"},
+#ifdef HAVE_MUD
+    {x_mud, 0,              "VVrChat        \033[1;32m������~��Ѽs��\033[m"},
+#endif
+    {t_display, 0,          "DDisplay       ��ܤW�X�����T"},
+    {NULL, 0, NULL}
+};
+
+/* name menu */
+static int t_aloha() {
+    friend_edit(FRIEND_ALOHA);
+    return 0;
+}
+
+static int t_special() {
+    friend_edit(FRIEND_SPECIAL);
+    return 0;
+}
+
+static commands_t namelist[] = {
+    {t_override, PERM_LOGINOK,"OOverRide      �n�ͦW��"},
+    {t_reject, PERM_LOGINOK,  "BBlack         �a�H�W��"},
+    {t_aloha,PERM_LOGINOK,    "AALOHA         �W���q���W��"},
+#ifdef POSTNOTIFY
+    {t_post,PERM_LOGINOK,     "NNewPost       �s�峹�q���W��"},
+#endif
+    {t_special,PERM_LOGINOK,  "SSpecial       ��L�S�O�W��"},
+    {NULL, 0, NULL}
+};
+
+/* User menu */
+static commands_t userlist[] = {
+    {u_info, PERM_LOGINOK,          "IInfo          �]�w�ӤH��ƻP�K�X"},
+    {calendar, PERM_LOGINOK,          "CCalendar      �ӤH��ƾ�"},
+    {u_editcalendar, PERM_LOGINOK,    "CCalendarEdit  �s��ӤH��ƾ�"},
+    {u_loginview, PERM_LOGINOK,     "LLogin View    ��ܶi���e��"},
+    {u_ansi, 0, "AANSI          ���� ANSI \033[36m�m\033[35m��\033[37m/"
+     "\033[30;47m��\033[1;37m��\033[m�ҥ�"},
+    {u_movie, 0,                    "MMovie         �����ʵe�ҥ�"},
+#ifdef  HAVE_SUICIDE
+    {u_kill, PERM_BASIC,            "IKill          �۱��I�I"},
+#endif
+    {u_editplan, PERM_LOGINOK,      "QQueryEdit     �s��W����"},
+    {u_editsig, PERM_LOGINOK,       "SSignature     �s��ñ�W��"},
+#if HAVE_FREECLOAK
+    {u_cloak, PERM_LOGINOK,           "CCloak         �����N"},
+#else
+    {u_cloak, PERM_CLOAK,           "CCloak         �����N"},
+#endif
+    {u_register, PERM_BASIC,        "RRegister      ��g�m���U�ӽг�n"},
+    {u_list, PERM_SYSOP,            "UUsers         �C�X���U�W��"},
+    {NULL, 0, NULL}
+};
+
+/* XYZ tool menu */
+static commands_t xyzlist[] = {
+#ifdef  HAVE_LICENSE
+    {x_gpl, 0,       "LLicense       GNU �ϥΰ���"},
+#endif
+#ifdef HAVE_INFO
+    {x_program, 0,   "PProgram       ���{���������P���v�ŧi"},
+#endif
+    {x_boardman,0,   "MMan Boards    �m�ݪ���ذϱƦ�]�n"},
+//  {x_boards,0,     "HHot Boards    �m�ݪ��H��Ʀ�]�n"},
+    {x_history, 0,   "HHistory       �m�ڭ̪������n"},
+    {x_note, 0,      "NNote          �m�IJ��W���y�����n"},
+    {x_login,0,      "SSystem        �m�t���n���i�n"},
+    {x_week, 0,      "WWeek          �m���g���Q�j�������D�n"},
+    {x_issue, 0,     "IIssue         �m����Q�j�������D�n"},
+    {x_today, 0,     "TToday         �m����W�u�H���έp�n"},
+    {x_yesterday, 0, "YYesterday     �m�Q��W�u�H���έp�n"},
+    {x_user100 ,0,   "UUsers         �m�ϥΪ̦ʤj�Ʀ�]�n"},
+    {x_birth, 0,     "BBirthday      �m����جP�j�[�n"},
+    {p_sysinfo, 0,   "XXload         �m�d�ݨt�έt���n"},
+    {NULL, 0, NULL}
+};
+
+/* Ptt money menu */
+static commands_t moneylist[] = {
+    {p_give, 0,         "00Give        ����L�H��"},
+    {save_violatelaw, 0,"11ViolateLaw  ú�@��"},
+#if !HAVE_FREECLOAK
+    {p_cloak, 0,        "22Cloak       ���� ����/�{��   $19  /��"},
+#endif
+    {p_from, 0,         "33From        �Ȯɭק�G�m     $49  /��"},
+    {ordersong,0,       "44OSong       �ڮ�ʺA�I�q��   $200 /��"},
+    {p_exmail, 0,       "55Exmail      �ʶR�H�c         $1000/��"},
+    {NULL, 0, NULL}
+};
+
+static int p_money() {
+    domenu(PSALE, "��tt�q�c��", '0', moneylist);
+    return 0;
+};
+
+static commands_t jceelist[] = {
+    {x_90,PERM_LOGINOK,	     "0090 JCEE     �i90�Ǧ~�פj���p�۬d�]�t�Ρj"},
+    {x_89,PERM_LOGINOK,	     "1189 JCEE     �i89�Ǧ~�פj���p�۬d�]�t�Ρj"},
+    {x_88,PERM_LOGINOK,      "2288 JCEE     �i88�Ǧ~�פj���p�۬d�]�t�Ρj"},
+    {x_87,PERM_LOGINOK,      "3387 JCEE     �i87�Ǧ~�פj���p�۬d�]�t�Ρj"},
+    {x_86,PERM_LOGINOK,      "4486 JCEE     �i86�Ǧ~�פj���p�۬d�]�t�Ρj"},
+    {NULL, 0, NULL}
+};
+
+static int m_jcee() {
+    domenu(JCEE, "��tt�d�]�t��", '0', jceelist);
+    return 0;
+}
+
+static int forsearch();
+static int playground();
+
+/* Ptt Play menu */
+static commands_t playlist[] = {
+#if HAVE_JCEE
+    {m_jcee, PERM_LOGINOK,   "JJCEE        �i �j���p�Ҭd�]�t�� �j"},
+#endif
+    {note, PERM_LOGINOK,     "NNote        �i ���y���� �j"},
+    {x_weather,0 ,           "WWeather     �i ��H�w�� �j"},
+    {x_stock,0 ,             "SStock       �i �ѥ��污 �j"},
+#ifdef HAVE_BIG2
+    {x_big2, 0,              "BBig2        �i �����j�ѤG �j"},
+#endif
+#ifdef HAVE_MJ
+    {x_mj, PERM_LOGINOK,     "QQkmj        �i �������±N �j"},
+#endif
+#ifdef  HAVE_BRIDGE
+    {x_bridge, PERM_LOGINOK, "OOkBridge    �i ���P�v�� �j"},
+#endif
+#ifdef HAVE_GOPHER
+    {x_gopher, PERM_LOGINOK, "GGopher      �i �a����Ʈw �j"},
+#endif
+#ifdef HAVE_TIN
+    {x_tin, PERM_LOGINOK,    "NNEWS        �i ���ڷs�D �j"},
+#endif
+#ifdef BBSDOORS
+    {x_bbsnet, PERM_LOGINOK, "BBBSNet      �i ��L BBS�� �j"},
+#endif
+#ifdef HAVE_WWW
+    {x_www, PERM_LOGINOK,    "WWWW Browser �i �L�L�L �j"},
+#endif
+    {forsearch,PERM_LOGINOK, "SSearchEngine�i\033[1;35m ��tt�j�M�� \033[m�j"},
+    {topsong,PERM_LOGINOK,   "TTop Songs   �i\033[1;32m�ڮ��I�q�Ʀ�]\033[m�j"},
+    {p_money,PERM_LOGINOK,   "PPay         �i\033[1;31m ��tt�q�c�� \033[m�j"},
+    {chicken_main,PERM_LOGINOK, "CChicken     "
+     "�i\033[1;34m ��tt�i���� \033[m�j"},
+    {playground,PERM_LOGINOK, "AAmusement   �i\033[1;33m ��tt�C�ֳ� \033[m�j"},
+    {NULL, 0, NULL}
+};
+
+static commands_t plist[] = {
+
+/*    {p_ticket_main, PERM_LOGINOK,"00Pre         �i �`�ξ� �j"},
+      {alive, PERM_LOGINOK,        "00Alive       �i  �q����  �j"},
+*/
+    {ticket_main, PERM_LOGINOK,  "11Gamble      �i ��tt��� �j"},
+    {guess_main, PERM_LOGINOK,   "22Guess number�i �q�Ʀr   �j"},
+    {othello_main, PERM_LOGINOK, "33Othello     �i �¥մ�   �j"},
+//    {dice_main, PERM_LOGINOK,    "44Dice        �i ����l   �j"},
+    {vice_main, PERM_LOGINOK,    "44Vice        �i �o����� �j"},
+    {g_card_jack, PERM_LOGINOK,  "55Jack        �i �³ǧJ �j"},
+    {g_ten_helf, PERM_LOGINOK,   "66Tenhalf     �i �Q�I�b �j"},
+    {card_99, PERM_LOGINOK,      "77Nine        �i �E�Q�E �j"},
+    {NULL, 0, NULL}
+};
+
+static int playground() {
+    domenu(AMUSE, "��tt�C�ֳ�",'1',plist);
+    return 0;
+}
+
+static commands_t slist[] = {
+    {x_dict,0,                   "11Dictionary  "
+     "�i\033[1;33m ����j�r�� \033[m�j"},
+    {main_railway, PERM_LOGINOK,  "33Railway     "
+     "�i\033[1;32m �������d�� \033[m�j"},
+    {NULL, 0, NULL}
+};
+
+static int forsearch() {
+    domenu(SREG, "��tt�j�M��", '1', slist);
+    return 0;
+}
+
+/* main menu */
+
+static int admin() {
+    domenu(ADMIN, "�t��@", 'X', adminlist);
+    return 0;
+}
+
+static int Mail() {
+    domenu(MAIL, "�q�l�l��", 'R', maillist);
+    return 0;
+}
+
+static int Talk() {
+    domenu(TMENU, "��ѻ���", 'U', talklist);
+    return 0;
+}
+
+static int User() {
+    domenu(UMENU, "�ӤH�]�w", 'A', userlist);
+    return 0;
+}
+
+static int Xyz() {
+    domenu(XMENU, "�u��{��", 'M', xyzlist);
+    return 0;
+}
+
+static int Play_Play() {
+    domenu(PMENU, "�����C�ֳ�", 'A', playlist);
+    return 0;
+}
+
+static int Name_Menu() {
+    domenu(NMENU, "�զ⮣��", 'O', namelist);
+    return 0;
+}
+
+commands_t cmdlist[] = {
+    {admin,PERM_SYSOP|PERM_VIEWSYSOP, "00Admin       �i �t��@�� �j"},
+    {Announce, 0,                     "AAnnounce     �i ��ؤ��G�� �j"},
+    {Boards, 0,                       "FFavorite     �i �� �� �̷R �j"},
+    {root_board, 0,                   "CClass        �i ���հQ�װ� �j"},
+    {Mail, PERM_BASIC,                "MMail         �i �p�H�H��� �j"},
+    {Talk, 0,                         "TTalk         �i �𶢲�Ѱ� �j"},
+    {User, 0,                         "UUser         �i �ӤH�]�w�� �j"},
+    {Xyz, 0,                          "XXyz          �i �t�Τu��� �j"},
+    {Play_Play,0,                     "PPlay         �i �C�ֳ�/�j�Ǭd�]�j"},
+    {Name_Menu,PERM_LOGINOK,          "NNamelist     �i �s�S�O�W�� �j"},
+    {Goodbye, 0,                      "GGoodbye       ���}�A�A���K�K"},
+    {NULL, 0, NULL}
+};
diff --git a/mbbsd/more.c b/mbbsd/more.c
new file mode 100644
index 00000000..f7755874
--- /dev/null
+++ b/mbbsd/more.c
@@ -0,0 +1,931 @@
+/* $Id: more.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "perm.h"
+#include "proto.h"
+
+extern int showansi;
+extern int t_lines, t_columns;  /* Screen size / width */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern char *str_author1;
+extern char *str_author2;
+extern char *str_post1;
+extern char *str_post2;
+extern char *msg_seperator;
+extern char reset_color[];
+
+#define MORE_BUFSIZE	4096
+#define MORE_WINSIZE	4096
+#define STR_ANSICODE    "[0123456789;,"
+
+static int more_base, more_size, more_head;
+static unsigned char more_pool[MORE_BUFSIZE];
+
+
+#define MAXPATHLEN 256
+static char *more_help[] = {
+    "\0�\\Ū�峹�\\����ϥλ���",
+    "\01��в��ʥ\\����",
+    "(��)                  �W���@��",
+    "(��)(Enter)           �U���@��",
+    "(^B)(PgUp)(BackSpace) �W���@��",
+    "(��)(PgDn)(Space)     �U���@��",
+    "(0)(g)(Home)          �ɮ׶}�Y",
+    "($)(G) (End)          �ɮ׵���",
+    "\01��L�\\����",
+    "(/)                   �j�M�r��",
+    "(n/N)                 ���ƥ�/�ϦV�j�M",
+    "(TAB)                 URL�s��",
+    "(Ctrl-T)              �s��Ȧs��",
+    "(:/f/b)               ���ܬY��/�U/�W�g",
+    "(F/B)                 ���ܦP�@�j�M�D�D�U/�W�g",
+    "(a/A)                 ���ܦP�@�@�̤U/�W�g",
+    "([/])                 �D�D���\\Ū �W/�U",
+    "(t)                   �D�D���`�Ǿ\\Ū",
+    "(Ctrl-C)              �p�p���",
+    "(q)(��)               ����",
+    "(h)(H)(?)             ���U�����e��",
+    NULL
+};
+
+int beep = 0;
+
+static void
+more_goto (int fd, off_t off)
+{ 
+  int base = more_base;
+
+  if (off < base || off >= base + more_size)
+  { 
+    more_base = base = off & (-MORE_WINSIZE);
+    lseek (fd, base, SEEK_SET);
+    more_size = read (fd, more_pool, MORE_BUFSIZE);
+  }
+  more_head = off - base;
+}
+
+static int
+more_readln (int fd, unsigned char *buf)
+{
+  int ch;
+
+  unsigned char *data, *tail, *cc;
+  int len, bytes, in_ansi;
+  int size, head, ansilen;
+
+  len = bytes = in_ansi = ansilen = 0;
+  tail = buf + ANSILINELEN - 1;
+  size = more_size;
+  head = more_head;
+  data = &more_pool[head];
+
+  do
+  {
+    if (head >= size)
+    {
+      more_base += size;
+      data = more_pool;
+      more_size = size = read (fd, data, MORE_BUFSIZE);
+      if (size == 0)
+        break;
+      head = 0;
+    }
+
+    ch = *data++;
+    head++;
+    bytes++;
+    if (ch == '\n')
+    { 
+      break;
+    }
+    if (ch == '\t')
+    { 
+      do
+      { 
+        *buf++ = ' ';
+      }
+      while ((++len & 7) && len < 80);
+    }
+    else if (ch == '\033')
+    { 
+      if (atoi (data + 1) > 47)
+      { 
+        if ((cc = strchr (data + 1, 'm')) != NULL)
+        { 
+          ch = cc - data + 1;
+
+          data += ch;
+          head += ch;
+          bytes += ch;
+        }
+      }
+      else
+      { 
+        if (showansi)
+          *buf++ = ch;
+        in_ansi = 1;
+      }
+    }
+    else if (in_ansi)
+    {
+      if (showansi)
+        *buf++ = ch;
+      if (!strchr (STR_ANSICODE, ch))
+        in_ansi = 0;
+    }
+    else if (isprint2 (ch))
+    { 
+      len++;
+      *buf++ = ch;
+    }
+  }
+  while (len < 80 && buf < tail);
+  *buf = '\0';
+  more_head = head;
+  return bytes;
+}
+
+/* not used 
+static int readln(FILE *fp, char *buf) {
+    register int ch, i, len, bytes, in_ansi;
+
+    len = bytes = in_ansi = i = 0;
+    while(len < 80 && i < ANSILINELEN && (ch = getc(fp)) != EOF) {
+	bytes++;
+	if(ch == '\n')
+	    break;
+	else if(ch == '\t')
+	    do {
+		buf[i++] = ' ';
+	    } while((++len & 7) && len < 80);
+	else if(ch == '\a')
+	    beep = 1;
+	else if(ch == '\033') {
+	    if(showansi)
+		buf[i++] = ch;
+	    in_ansi = 1;
+	} else if(in_ansi) {
+	    if(showansi)
+		buf[i++] = ch;
+	    if(!strchr("[0123456789;,", ch))
+		in_ansi = 0;
+	} else if(isprint2(ch)) {
+	    len++;
+	    buf[i++] = ch;
+	}
+    }
+    buf[i] = '\0';
+    return bytes;
+}
+*/
+
+extern userec_t cuser;
+
+static int more_web(char *fpath, int promptend);
+
+int more(char *fpath, int promptend) {
+    extern char* strcasestr();
+    static char *head[4] = {"�@��", "���D", "�ɶ�" ,"��H"};
+    char *ptr, *word = NULL, buf[ANSILINELEN + 1], *ch1;
+    struct stat st;
+
+/* rocker */
+    //FILE *fp;
+    int fd, fsize;
+
+    unsigned int pagebreak[MAX_PAGES], pageno, lino = 0;
+    int line, ch, viewed, pos, numbytes;
+    int header = 0;
+    int local = 0;
+    char search_char0=0;
+    static char search_str[81]="";
+    typedef char* (*FPTR)();
+    static FPTR fptr;
+    int searching = 0;
+    int scrollup = 0;
+    char *printcolor[3]= {"44","33;45","0;34;46"}, color =0;
+    char *http[80]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+		    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+    /* Ptt */
+    char pagemode = 0;
+    char pagecount = 0;
+
+    memset(pagebreak, 0, sizeof(pagebreak));
+    if(*search_str)
+	search_char0 = *search_str;
+    *search_str = 0;
+
+    
+    fd = open (fpath, O_RDONLY, 0600); 
+    if (fd < 0) return -1;
+    
+    if(fstat(fd, &st) || ((fsize = st.st_size) <= 0) || S_ISDIR (st.st_mode))
+     {
+        close(fd); //Ptt
+	return -1;
+     }
+    pagebreak[0] = pageno = viewed = line = pos = 0;
+    clear();
+
+/* rocker */
+
+   more_base = more_head = more_size = 0;
+    
+    while((numbytes = more_readln(fd, buf)) || (line == t_lines)) {
+	if(scrollup) {
+	    rscroll();
+	    move(0, 0);
+	}
+	if(numbytes) {              /* �@���ƳB�z */
+	    if(!viewed) {             /* begin of file */
+		if(showansi) {          /* header processing */
+		    if(!strncmp(buf, str_author1, LEN_AUTHOR1)) {
+			line = 3;
+			word = buf + LEN_AUTHOR1;
+			local = 1;
+		    } else if(!strncmp(buf, str_author2, LEN_AUTHOR2)) {
+			line = 4;
+			word = buf + LEN_AUTHOR2;
+		    }
+		    
+		    while(pos < line) {
+			if(!pos && ((ptr = strstr(word, str_post1)) ||
+				    (ptr = strstr(word, str_post2)))) {
+			    ptr[-1] = '\0';
+			    prints("\033[47;34m %s \033[44;37m%-53.53s"
+				   "\033[47;34m %.4s \033[44;37m%-13s\033[m\n",
+				   head[0], word, ptr, ptr + 5);
+			} else if (pos < 4)
+			    prints("\033[47;34m %s \033[44;37m%-72.72s"
+				   "\033[m\n", head[pos], word);
+			
+			viewed += numbytes;
+			numbytes = more_readln(fd, buf);
+			
+			/* �Ĥ@��Ӫ��F */			
+			if(!pos && viewed > 79) {
+                            /* �ĤG�椣�O [��....] */
+			    if(memcmp( buf, head[1], 2)) { 
+				/* Ū�U�@��i�ӳB�z */
+				viewed += numbytes;
+				numbytes = more_readln(fd, buf);
+			    }
+			}
+			pos++;
+		    }
+		    if(pos) {
+			header = 1;
+			
+			prints("\033[36m%s\033[m\n", msg_seperator);
+			line = pos = 4;
+		    }
+		}
+		lino = pos;
+		word = NULL;
+	    }
+
+	    /* ���B�z�ޥΪ� & �ި� */
+	    if((buf[1] == ' ') && (buf[0] == ':' || buf[0] == '>'))
+		word = "\033[36m";
+	    else if(!strncmp(buf, "��", 2) || !strncmp(buf, "==>", 3))
+		word = "\033[32m";
+	    
+	    ch1 = buf;
+	    while(1) {
+		int i;
+		char e,*ch2;
+
+		if((ch2 = strstr(ch1, "http://")))
+		    ;
+		else if((ch2 = strstr(ch1,"gopher://")))
+		    ;
+		else if((ch2 = strstr(ch1,"mailto:")))
+		    ;
+		else
+		    break;
+		for(e = 0; ch2[(int)e] != ' ' && ch2[(int)e] != '\n' &&
+			ch2[(int)e] != '\0' && ch2[(int)e] != '"' &&
+			ch2[(int)e] != ';' && ch2[(int)e] != ']'; e++);
+		for(i = 0; http[i] && i < 80; i++)
+		    if(!strncmp(http[i], ch2, e) && http[(int)e] == 0)
+			break;
+		if(!http[i]) {
+		    http[i] = (char *)malloc(e + 1);
+		    strncpy(http[i], ch2, e);
+		    http[i][(int)e] = 0;
+		    pagecount++;
+                }
+		ch1 = &ch2[7];
+	    }
+	    if(word)
+		outs(word);
+	    {
+		char msg[500], *pos;
+		
+		if(*search_str && (pos = fptr(buf, search_str))) {
+		    char SearchStr[81];
+		    char buf1[100], *pos1;
+		    
+		    strncpy(SearchStr, pos, strlen(search_str));
+		    SearchStr[strlen(search_str)] = 0;
+		    searching = 0;
+		    sprintf(msg, "%.*s\033[7m%s\033[m", pos - buf, buf,
+			    SearchStr);
+		    while((pos = fptr(pos1 = pos + strlen(search_str),
+				      search_str))) {
+			sprintf(buf1, "%.*s\033[7m%s\033[m", pos - pos1,
+				pos1, SearchStr);
+			strcat(msg, buf1);
+		    }
+		    strcat(msg, pos1);
+		    outs(Ptt_prints(msg,NO_RELOAD));
+		} else
+		    outs(Ptt_prints(buf,NO_RELOAD));
+	    }
+	    if(word) {
+		outs("\033[m");
+		word = NULL;
+	    }
+	    outch('\n');
+	    
+	    if(beep) {
+		bell();
+		beep = 0;
+	    }
+
+	    if(line < b_lines)       /* �@����Ū�� */
+		line++;
+	    
+	    if(line == b_lines && searching == -1) {
+		if(pageno > 0)
+		    more_goto(fd, viewed = pagebreak[--pageno]);
+		else
+		    searching = 0;
+		lino = pos = line = 0;
+		clear();
+		continue;
+	    }
+	    
+	    if(scrollup) {
+		move(line = b_lines, 0);
+		clrtoeol();
+		for(pos = 1; pos < b_lines; pos++)
+		    viewed += more_readln(fd, buf);
+	    } else if(pos == b_lines)  /* ���ʿù� */
+		scroll();
+	    else
+		pos++;
+
+	    if(!scrollup && ++lino >= b_lines && pageno < MAX_PAGES - 1) {
+		pagebreak[++pageno] = viewed;
+		lino = 1;
+	    }
+
+	    if(scrollup) {
+		lino = scrollup;
+		scrollup = 0;
+	    }
+	    viewed += numbytes;       /* �֭pŪ�L��� */
+	} else
+	    line = b_lines;           /* end of END */
+	
+	if(promptend &&
+	   ((!searching && line == b_lines) || viewed == fsize)) {
+	    /* Kaede ��n 100% �ɤ��� */
+	    move(b_lines, 0);
+	    if(viewed == fsize) {
+		if(searching == 1)
+		    searching = 0;
+		color = 0;
+	    } else if(pageno == 1 && lino == 1) {
+		if(searching == -1)
+		    searching = 0;
+		color = 1;
+	    } else
+		color = 2;
+
+	    prints("\033[m\033[%sm  �s�� P.%d(%d%%)  %s  %-30.30s%s",
+		   printcolor[(int)color], 
+		   pageno, 
+		   (int)((viewed * 100) / fsize),
+		   pagemode ? "\033[30;47m" : "\033[31;47m",
+		   pagemode ? http[pagemode-1] : "(h)\033[30m�D�U  \033[31m����[PgUp][",
+		   pagemode ? "\033[31m[TAB]\033[30m���� \033[31m[Enter]\033[30m��w \033[31m��\033[30m���\033[m" : "PgDn][Home][End]\033[30m����  \033[31m��[q]\033[30m����   \033[m");
+	    
+	    
+	    while(line == b_lines || (line > 0 && viewed == fsize)) {
+		switch((ch = egetch())) {
+		case ':':
+		{
+		    char buf[10];
+		    int i = 0;
+		    
+		    getdata(b_lines - 1, 0, "Goto Page: ", buf, 5, DOECHO);
+		    sscanf(buf, "%d", &i);
+		    if(0 < i && i <  MAX_PAGES && (i == 1 || pagebreak[i - 1]))
+			pageno = i - 1;
+		    else if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+		}
+		case '/':
+		{
+		    char ans[4] = "n";
+		    
+		    *search_str = search_char0;
+		    getdata_buf(b_lines - 1, 0,"[�j�M]����r:", search_str,
+				40, DOECHO);
+		    if(*search_str) {
+			searching = 1;
+			if(getdata(b_lines - 1, 0, "�Ϥ��j�p�g(Y/N/Q)? [N] ",
+				   ans, 4, LCECHO) && *ans == 'y')
+			    fptr = strstr;
+			else
+			    fptr = strcasestr;
+		    }
+		    if(*ans == 'q')
+			searching = 0;
+		    if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+		}
+		case 'n':
+		    if(*search_str) {
+			searching = 1;
+			if(pageno)
+			    pageno--;
+			lino = line = 0;
+		    }
+		    break;
+		case 'N':
+		    if(*search_str) {
+			searching = -1;
+			if(pageno)
+			    pageno--;
+			lino = line = 0;
+		    }
+		    break;
+		case 'r':
+		case 'R':
+		case 'Y':
+		    close(fd);
+		    return 7;
+		case 'y':
+		    close(fd);
+		    return 8;
+		case 'A':
+		    close(fd);
+		    return 9;
+		case 'a':
+		    close(fd);
+		    return 10;
+		case 'F':
+		    close(fd);
+		    return 11;
+		case 'B':
+		    close(fd);
+		    return 12;
+		case KEY_LEFT:
+		    if(pagemode) {
+			pagemode = 0;
+			*search_str = 0;
+			if(pageno)
+			    pageno--;
+			lino = line = 0;
+			break;
+		    }
+		    close(fd);
+		    return 6;
+		case 'q':
+		    close(fd);
+		    return 0;
+		case 'b':
+		    close(fd);
+		    return 1;
+		case 'f':
+		    close(fd);
+		    return 3;
+		case ']':       /* Kaede ���F�D�D�\Ū��K */
+		    close(fd);
+		    return 4;
+		case '[':       /* Kaede ���F�D�D�\Ū��K */
+		    close(fd);
+		    return 2;
+		case '=':       /* Kaede ���F�D�D�\Ū��K */
+		    close(fd);
+		    return 5;
+		case Ctrl('F'):
+		case KEY_PGDN:
+		    line = 1;
+		    break;
+		case 't':
+		    if(viewed == fsize) {
+			close(fd);
+			return 4;
+		    }
+		    line = 1;
+		    break;
+		case ' ':
+		    if(viewed == fsize) {
+			close(fd);
+			return 3;
+		    }
+		    line = 1;
+		    break;
+		case KEY_RIGHT:
+		    if(viewed == fsize) {
+			close(fd);
+			return 0;
+		    }
+		    line = 1;
+		    break;
+		case '\r':
+		case '\n':
+		    if(pagemode) {
+			more_web(http[pagemode-1],YEA);
+			pagemode = 0;
+			*search_str = 0;
+			if(pageno)
+			    pageno--;
+			lino = line = 0;
+			break;
+		    }
+		case KEY_DOWN:
+		    if(viewed == fsize ||
+		       (promptend == 2 && (ch == '\r' || ch == '\n'))) {
+			close(fd);
+			return 3;
+		    }
+		    line = t_lines - 2;
+		    break;
+		case '$':
+		case 'G':
+		case KEY_END:
+		    line = t_lines;
+		    break;
+		case '0':
+		case 'g':
+		case KEY_HOME:
+		    pageno = line = 0;
+		    break;
+		case 'h':
+		case 'H':
+		case '?':
+		    /* Kaede Buggy ... */
+		    show_help(more_help);
+		    if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+		case 'E':
+		    if(HAS_PERM(PERM_SYSOP) && strcmp(fpath, "etc/ve.hlp")) {
+			close(fd);
+			vedit(fpath, NA, NULL);
+			return 0;
+		    }
+		    break;
+		case Ctrl('C'):
+		    cal();
+		    if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+
+		case Ctrl('T'):
+		    getdata(b_lines - 2, 0, "��o�g�峹���J��Ȧs�ɡH[y/N] ",
+			    buf, 4, LCECHO);
+		    if(buf[0] == 'y') {
+			char tmpbuf[128];
+			
+			setuserfile(tmpbuf, ask_tmpbuf(b_lines - 1));
+			sprintf(buf, "cp -f %s %s", fpath, tmpbuf);
+			system(buf);
+		    }
+		    if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+#if 0
+		case Ctrl('I'):
+		    if(!pagecount)
+			break;
+		    pagemode = (pagemode % pagecount) + 1;
+		    strncpy(search_str,http[pagemode-1],80);
+		    search_str[80] =0;
+		    fptr = strstr;
+		    if(pageno)
+			pageno--;
+		    lino = line = 0;
+		    break;
+#endif
+		case KEY_UP:
+		    line = -1;
+		    break;
+		case Ctrl('B'):
+		case KEY_PGUP:
+		    if(pageno > 1) {
+			if(lino < 2)
+			    pageno -= 2;
+			else
+			    pageno--;
+			lino = line = 0;
+		    } else if(pageno && lino > 1)
+			pageno = line = 0;
+		    break;
+		case Ctrl('H'):
+		    if(pageno > 1) {
+			if(lino < 2)
+			    pageno -= 2;
+			else
+			    pageno--;
+			lino = line = 0;
+		    } else if(pageno && lino > 1)
+			pageno = line = 0;
+		    else {
+			close(fd);
+			return 1;
+		    }
+		}
+	    }
+	    
+	    if(line > 0) {
+		move(b_lines, 0);
+		clrtoeol();
+		refresh();
+	    } else if(line < 0) {                      /* Line scroll up */
+		if(pageno <= 1) {
+		    if(lino == 1 || !pageno) {
+			close(fd);
+			return 1;
+		    }
+		    if(header && lino <= 5) {
+			more_goto(fd, viewed = pagebreak[scrollup = lino =
+						    pageno = 0] = 0);
+			clear();
+		    }
+		}
+		if(pageno && lino > 1 + local) {
+		    line =  (lino - 2) - local;
+		    if(pageno > 1 && viewed == fsize)
+			line += local;
+		    scrollup = lino - 1;
+		    more_goto(fd, viewed = pagebreak[pageno - 1]);
+		    while(line--)
+			viewed += more_readln(fd, buf);
+		} else if(pageno > 1) {
+		    scrollup = b_lines - 1;
+		    line = (b_lines - 2) - local;
+		    more_goto(fd, viewed = pagebreak[--pageno - 1]);
+		    while(line--)
+			viewed += more_readln(fd, buf);
+		}
+		line = pos = 0;
+	    } else {
+		pos = 0;
+		more_goto (fd, viewed = pagebreak[pageno]);
+		move(0,0);
+		clear();
+	    }
+	}
+    }
+
+    close(fd);
+    if(promptend) {
+	pressanykey();
+	clear();
+    } else
+	outs(reset_color);
+    return 0;
+}
+
+static int more_web(char *fpath, int promptend) {
+    char *ch, *ch1 = NULL;
+    char *hostname = fpath,userfile[MAXPATHLEN],file[MAXPATHLEN]="/";
+    char genbuf[200];
+    time_t dtime;
+#if !defined(USE_LYNX) && defined(USE_PROXY)
+    int a;
+    FILE *fp;
+    struct hostent *h;
+    struct sockaddr_in sin;
+#endif
+
+    if((ch = strstr(fpath, "mailto:"))) {
+        if(!HAS_PERM(PERM_LOGINOK)) {
+	    move(b_lines - 1,0);
+	    outs("\033[41m �z���v�������L�k�ϥ�internet mail... \033[m");
+	    refresh();
+	    return 0;
+	}
+        if(!invalidaddr(&ch[7]) &&
+	   getdata(b_lines - 1, 0, "[�H�H]�D�D�G", genbuf, 40, DOECHO))
+	    do_send(&ch[7], genbuf);
+	else {
+	    move(b_lines - 1,0);
+	    outs("\033[41m ���H�Hemail �� ���D ���~... \033[m");
+	    refresh();
+	}
+        return 0;
+    }
+    if((ch = strstr(fpath, "gopher://"))) {
+	item_t item;
+
+	strcpy(item.X.G.server, &ch[9]);
+	strcpy(item.X.G.path, "1/");
+	item.X.G.port = 70;
+	gem(fpath , &item, 0);
+        return 0;
+    }
+    if((ch = strstr(fpath, "http://")))
+	hostname=&ch[7];
+    if((ch = strchr(hostname, '/'))) {
+        *ch = 0;
+        if(&ch1[1])
+	    strcat(file,&ch[1]);
+    }
+    if(file[strlen(file) - 1] == '/')
+	strcat(file,"index.html");
+    move(b_lines-1,0);
+    clrtoeol();
+#ifdef USE_PROXY
+    sprintf(genbuf, "\033[33;44m ���b�s��%s.(proxy:%s).....�еy��....\033[m",
+	    hostname, PROXYSERVER);
+#else
+    sprintf(genbuf, "\033[33;44m ���b�s��%s......�еy��....\033[m", hostname);
+#endif
+    outs(genbuf);
+    refresh();
+
+#ifdef LOCAL_PROXY
+/* ���� local disk �� proxy */
+    time(&dtime);
+    sprintf(userfile,"hproxy/%s%s",hostname,file);
+    if(dashf(userfile) && (dtime - dasht(userfile)) <  HPROXYDAY * 24 * 60
+       && more(userfile,promptend)) {
+        return 1;
+    }
+    ch=userfile - 1;
+    while((ch1 = strchr(ch + 1,'/'))) {
+        *ch1 = 0;
+        if(!dashd(ch))
+	    mkdir(ch+1,0755);
+        chdir(ch+1);
+        *ch1 = '/';
+        ch = ch1;
+    }
+    chdir(BBSHOME);
+#endif
+
+#ifndef USE_LYNX
+#ifdef USE_PROXY
+    if(!(h = gethostbyname(PROXYSERVER))) {
+	outs("\033[33;44m �䤣��o��proxy server!..\033[m");
+	refresh();
+	return;
+    }
+    ()memset((char *)&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+
+    if(h == NULL)
+	sin.sin_addr.s_addr = inet_addr(PROXYSERVER);
+    else
+	()memcpy(&sin.sin_addr.s_addr, h->h_addr, h->h_length);
+
+    sin.sin_port = htons((ushort)PROXYPORT);  /* HTTP port */
+    a = socket(AF_INET, SOCK_STREAM, 0);
+    if((connect(a, (struct sockaddr *) & sin, sizeof sin)) < 0) {
+	outs("\033[1;44m �s����proxy����ڵ� ! \033[m");
+	refresh();
+	return;
+    }
+    sprintf(genbuf,"GET http://%s/%s HTTP/1.1\n",hostname,file);
+#else
+    if(!(h = gethostbyname(hostname))) {
+	outs("\033[33;44m �䤣��o��server!..\033[m");
+	refresh();
+	return;
+    }
+    ()memset((char *) &sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    
+    if(h == NULL)
+	sin.sin_addr.s_addr = inet_addr(hostname);
+    else
+	()memcpy(&sin.sin_addr.s_addr, h->h_addr, h->h_length);
+    
+    sin.sin_port =  htons((ushort)80);  
+    a = socket(AF_INET, SOCK_STREAM, 0);
+    if((connect(a, (struct sockaddr *) & sin, sizeof sin)) < 0) {
+	outs("\033[1;44m �s������ڵ� ! \033[m");
+	refresh();
+	return;
+    }
+    sprintf(genbuf, "GET %s\n", file);
+#endif
+
+    for(i = strlen(file); file[i - 1] != '/' && i > 0 ; i--);
+    file[i] = 0;
+
+    i = strlen(genbuf);
+    write(a, genbuf, i);
+
+#define BLANK   001
+#define ISPRINT 002
+#define PRE     004
+#define CENTER  010
+    if((fp = fopen(userfile,"w"))) {
+	int flag = 2, c;
+	char path[MAXPATHLEN];
+	unsigned char j, k;
+
+	while((i = read(a,genbuf,200))) {
+	    if(i < 0)
+		return;
+	    genbuf[i]=0;
+	    
+	    for(j = 0, k = 0; genbuf[j] && j < i; j++) {
+		if((flag & ISPRINT) && genbuf[j] == '<')
+		    flag |= BLANK;
+		else if((flag & ISPRINT) && genbuf[j] == '>')
+		    flag &= ~BLANK;
+		else {
+		    if(!(flag & BLANK)) {
+			if(j != k && (genbuf[j] != '\n' || flag & PRE))
+			    genbuf[k++] = genbuf[j];
+		    } else {
+			switch(char_lower(genbuf[j])) {
+			case 'a':
+			    break;
+			case 'b':
+			    if(genbuf[j + 1] == 'r' && genbuf[j + 2] == '>') 
+				genbuf[k++] = '\n';
+			    break;
+			case 'h':
+			    if(genbuf[j + 1] == 'r' && 
+			       (genbuf[j + 2] == '>' ||
+				genbuf[j + 2] == 's')) {
+				strncpy(&genbuf[k], "\n--\n", 4);
+				k += 4;
+			    }
+			    break;
+			case 'l':
+			    if(genbuf[j + 1] == 'i' && genbuf[j + 2]=='>') {
+				strncpy(&genbuf[k], "\n�� ", 4);
+				k += 4;
+			    }
+			    break;
+			case 'p':
+			    if(genbuf[j + 1]=='>') {
+				genbuf[k++] = '\n';
+				genbuf[k++] = '\n';
+			    } else if(genbuf[j + 1] == 'r' &&
+				      genbuf[j + 2] == 'e')
+				flag ^= PRE;
+			    break;
+			case 't':
+			    if(genbuf[j + 1] == 'd' && genbuf[j + 2]=='>') {
+				strncpy(&genbuf[k], "\n-\n", 3);
+				k += 3;
+			    }
+			    break;
+			}
+		    }
+		    if((genbuf[j] & 0x80) && (flag & ISPRINT))
+			flag &= ~ISPRINT;
+		    else
+			flag |= ISPRINT;
+                }
+	    }
+	    genbuf[k]=0;
+	    fputs(genbuf, fp);
+	}
+	fclose(fp);
+	close(a);
+	return more(userfile, promptend);
+    }
+    return 0;
+#else  /* use lynx dump */
+    sprintf(genbuf, "lynx -dump http://%s%s > %s", hostname, file, userfile);
+    system(genbuf);
+    return more(userfile, promptend);
+#endif
+}
diff --git a/mbbsd/name.c b/mbbsd/name.c
new file mode 100644
index 00000000..2f84a1fe
--- /dev/null
+++ b/mbbsd/name.c
@@ -0,0 +1,473 @@
+/* $Id: name.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+extern char *str_space;
+extern int p_lines;             /* a Page of Screen line numbers: tlines-4 */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+
+word_t *toplev = NULL;
+static word_t *current = NULL;
+static char *msg_more = "\033[7m-- More --\033[m";
+
+typedef char (*arrptr)[];
+/* name complete for user ID */
+
+static int UserMaxLen(char cwlist[][IDLEN + 1], int cwnum, int morenum,
+		      int count) {
+    int len, max = 0;
+    
+    while(count-- > 0 && morenum < cwnum) {
+	len = strlen(cwlist[morenum++]);
+	if (len > max)
+	    max = len;
+    }
+    return max;
+}
+
+static int UserSubArray(char cwbuf[][IDLEN + 1], char cwlist[][IDLEN + 1],
+			int cwnum, int key, int pos) {
+    int key2, num = 0;
+    int n, ch;
+
+    key = chartoupper(key);
+    if(key >= 'A' && key <= 'Z')
+	key2 = key | 0x20;
+    else
+	key2 = key ;
+
+    for(n = 0; n < cwnum; n++) {
+	ch = cwlist[n][pos];
+	if(ch == key || ch == key2)
+	    strcpy(cwbuf[num++], cwlist[n]);
+    }
+    return num;
+}
+
+static void FreeNameList() {
+    word_t *p, *temp;
+    
+    for(p = toplev; p; p = temp) {
+	temp = p->next;
+	free(p->word);
+	free(p);
+    }
+}
+
+void CreateNameList() {
+    if(toplev)
+	FreeNameList();
+    toplev = current = NULL;
+}
+
+void AddNameList(char *name) {
+    word_t *node;
+    
+    node = (word_t *)malloc(sizeof(word_t));
+    node->next = NULL;
+    node->word = (char *)malloc(strlen(name) + 1);
+    strcpy(node->word, name);
+
+    if(toplev)
+	current = current->next = node;
+    else
+	current = toplev = node;
+}
+
+int RemoveNameList(char *name) {
+    word_t *curr, *prev = NULL;
+
+    for(curr = toplev; curr; curr = curr->next) {
+	if(!strcmp(curr->word, name)) {
+	    if(prev == NULL)
+		toplev = curr->next;
+	    else
+		prev->next = curr->next;
+
+	    if(curr == current)
+		current = prev;
+	    free(curr->word);
+	    free(curr);
+	    return 1;
+	}
+	prev = curr;
+    }
+    return 0;
+}
+
+int InNameList(char *name) {
+    word_t *p;
+
+    for(p = toplev; p; p = p->next)
+	if(!strcmp(p->word, name))
+	    return 1;
+    return 0;
+}
+
+void ShowNameList(int row, int column, char *prompt) {
+    word_t *p;
+    
+    move(row, column);
+    clrtobot();
+    outs(prompt);
+
+    column = 80;
+    for(p = toplev; p; p = p->next) {
+	row = strlen(p->word) + 1;
+	if(column + row > 76) {
+	    column = row;
+	    outc('\n');
+	} else {
+	    column += row;
+	    outc(' ');
+	}
+	outs(p->word);
+    }
+}
+
+void ToggleNameList(int *reciper, char *listfile, char *msg) {
+    FILE *fp;
+    char genbuf[200];
+
+    if((fp = fopen(listfile, "r"))) {
+	while(fgets(genbuf, STRLEN, fp)) {
+	    strtok(genbuf, str_space);
+	    if(!InNameList(genbuf)) {
+		AddNameList(genbuf);
+		(*reciper)++;
+	    } else {
+		RemoveNameList(genbuf);
+		(*reciper)--;
+	    }
+	}
+	fclose(fp);
+	ShowNameList(3, 0, msg);
+    }
+}
+
+static int NumInList(word_t *list) {
+    register int i;
+
+    for(i = 0; list; i++)
+	list = list->next;
+    return i;
+}
+
+int chkstr(char *otag, char *tag, char *name) {
+    char ch, *oname = name;
+
+    while(*tag) {
+	ch = *name++;
+	if(*tag != chartoupper(ch))
+	    return 0;
+	tag++;
+    }
+    if(*tag && *name == '\0')
+	strcpy(otag, oname);
+    return 1;
+}
+
+static word_t *GetSubList(char *tag, word_t *list) {
+    word_t *wlist, *wcurr;
+    char tagbuf[STRLEN];
+    int n;
+
+    wlist = wcurr = NULL;
+    for(n = 0; tag[n]; n++)
+	tagbuf[n] = chartoupper(tag[n]);
+    tagbuf[n] = '\0';
+
+    while(list) {
+	if(chkstr(tag, tagbuf, list->word)) {
+	    register word_t *node;
+
+	    node = (word_t *)malloc(sizeof(word_t));
+	    node->word = list->word;
+	    node->next = NULL;
+	    if(wlist)
+		wcurr->next = node;
+	    else
+		wlist = node;
+	    wcurr = node;
+	}
+	list = list->next;
+    }
+    return wlist;
+}
+
+static void ClearSubList(word_t *list) {
+    struct word_t *tmp_list;
+
+    while(list) {
+	tmp_list = list->next;
+	free(list);
+	list = tmp_list;
+    }
+}
+
+static int MaxLen(word_t *list, int count) {
+    int len = strlen(list->word);
+    int t;
+
+    while(list && count) {
+	if((t = strlen(list->word)) > len)
+	    len = t;
+	list = list->next;
+	count--;
+    }
+    return len;
+}
+
+void namecomplete(char *prompt, char *data) {
+    char *temp;
+    word_t *cwlist, *morelist;
+    int x, y, origx, origy;
+    int ch;
+    int count = 0;
+    int clearbot = NA;
+    
+    if(toplev == NULL)
+	AddNameList("");
+    cwlist = GetSubList("", toplev);
+    morelist = NULL;
+    temp = data;
+    
+    outs(prompt);
+    clrtoeol();
+    getyx(&y, &x);
+    getyx(&origy, &origx);
+    standout();
+    prints("%*s", IDLEN + 1, "");
+    standend();
+    move(y, x);
+    refresh();
+    
+    while((ch = igetch()) != EOF) {
+	if(ch == '\n' || ch == '\r') {
+	    *temp = '\0';
+	    outc('\n');
+	    if(NumInList(cwlist) == 1)
+		strcpy(data, cwlist->word);
+	    ClearSubList(cwlist);
+	    break;
+	}
+	if(ch == ' ') {
+	    int col, len;
+	    
+	    if(NumInList(cwlist) == 1) {
+		strcpy(data, cwlist->word);
+		move(y, x);
+		outs(data + count);
+		count = strlen(data);
+		temp = data + count;
+		getyx(&y, &x);
+		continue;
+	    }
+	    clearbot = YEA;
+	    col = 0;
+	    if(!morelist)
+		morelist = cwlist;
+	    len = MaxLen(morelist, p_lines);
+	    move(2, 0);
+	    clrtobot();
+	    printdash("������T�@����");
+	    while(len + col < 80) {
+		int i;
+		
+		for(i = p_lines; (morelist) && (i > 0); i--) {
+		    move(3 + (p_lines - i), col);
+		    outs(morelist->word);
+		    morelist = morelist->next;
+		}
+		col += len + 2;
+		if(!morelist)
+		    break;
+		len = MaxLen(morelist, p_lines);
+	    }
+	    if(morelist) {
+		move(b_lines, 0);
+		outs(msg_more);
+	    }
+	    move(y, x);
+	    continue;
+	}
+	if(ch == '\177' || ch == '\010') {
+	    if(temp == data)
+		continue;
+	    temp--;
+	    count--;
+	    *temp = '\0';
+	    ClearSubList(cwlist);
+	    cwlist = GetSubList(data, toplev);
+	    morelist = NULL;
+	    x--;
+	    move(y, x);
+	    outc(' ');
+	    move(y, x);
+	    continue;
+	}
+	
+	if(count < STRLEN && isprint(ch)) {
+	    word_t *node;
+
+	    *temp++ = ch;
+	    count++;
+	    *temp = '\0';
+	    node = GetSubList(data, cwlist);
+	    if(node == NULL) {
+		temp--;
+		*temp = '\0';
+		count--;
+		continue;
+	    }
+	    ClearSubList(cwlist);
+	    cwlist = node;
+	    morelist = NULL;
+	    move(y, x);
+	    outc(ch);
+	    x++;
+	}
+    }
+    if(ch == EOF)
+	/* longjmp(byebye, -1); */
+	raise(SIGHUP);	/* jochang: don't know if this is necessary... */
+    outc('\n');
+    refresh();
+    if(clearbot) {
+	move(2, 0);
+	clrtobot();
+    }
+    if(*data) {
+	move(origy, origx);
+	outs(data);
+	outc('\n');
+    }
+}
+
+void usercomplete(char *prompt, char *data) {
+    char *temp;
+    char *cwbuf, *cwlist;
+    int cwnum, x, y, origx, origy;
+    int clearbot = NA, count = 0, morenum = 0;
+    char ch;
+
+    cwbuf = malloc(MAX_USERS * (IDLEN + 1));
+    cwlist = u_namearray((arrptr)cwbuf, &cwnum, "");
+    temp = data;
+    
+    outs(prompt);
+    clrtoeol();
+    getyx(&y, &x);
+    getyx(&origy, &origx);
+    standout();
+    prints("%*s", IDLEN + 1, "");
+    standend();
+    move(y, x);
+    while((ch = igetch()) != EOF) {
+	if(ch == '\n' || ch == '\r') {
+	    int i;
+	    char *ptr;
+	    
+	    *temp = '\0';
+	    outc('\n');
+	    ptr = (char *)cwlist;
+	    for(i = 0; i < cwnum; i++) {
+		if(strncasecmp(data, ptr, IDLEN + 1) == 0)
+		    strcpy(data, ptr);
+		ptr += IDLEN + 1;
+	    }
+	    break;
+	} else if(ch == ' ') {
+	    int col, len;
+	    
+	    if(cwnum == 1) {
+		strcpy(data, (char *)cwlist);
+		move(y, x);
+		outs(data + count);
+		count = strlen(data);
+		temp = data + count;
+		getyx(&y, &x);
+		continue;
+	    }
+	    clearbot = YEA;
+	    col = 0;
+	    len = UserMaxLen((arrptr)cwlist, cwnum, morenum, p_lines);
+	    move(2, 0);
+	    clrtobot();
+	    printdash("�ϥΪ̥N���@����");
+	    while(len + col < 79) {
+		int i;
+		
+		for(i = 0; morenum < cwnum && i < p_lines; i++)	{
+		    move(3 + i, col);
+		    prints("%s ", cwlist + (IDLEN + 1) * morenum++);
+		}
+		col += len + 2;
+		if(morenum >= cwnum)
+		    break;
+		len = UserMaxLen((arrptr)cwlist, cwnum, morenum, p_lines);
+	    }
+	    if(morenum < cwnum) {
+		move(b_lines, 0);
+		outs(msg_more);
+	    } else
+		morenum = 0;
+	    move(y, x);
+	    continue;
+	} else if(ch == '\177' || ch == '\010') {
+	    if(temp == data)
+		continue;
+	    temp--;
+	    count--;
+	    *temp = '\0';
+	    cwlist = u_namearray((arrptr)cwbuf, &cwnum, data);
+	    morenum = 0;
+	    x--;
+	    move(y, x);
+	    outc(' ');
+	    move(y, x);
+	    continue;
+	} else if(count < STRLEN && isprint(ch)) {
+	    int n;
+	    
+	    *temp++ = ch;
+	    *temp = '\0';
+	    n = UserSubArray((arrptr)cwbuf, (arrptr)cwlist, cwnum, ch, count);
+	    if(n == 0) {
+		temp--;
+		*temp = '\0';
+		continue;
+	    }
+	    cwlist = cwbuf;
+	    count++;
+	    cwnum = n;
+	    morenum = 0;
+	    move(y, x);
+	    outc(ch);
+	    x++;
+	}
+    }
+    free(cwbuf);
+    if(ch == EOF)
+	/* longjmp(byebye, -1); */
+	raise(SIGHUP);	/* jochang: don't know if this is necessary */
+    outc('\n');
+    refresh();
+    if(clearbot) {
+	move(2, 0);
+	clrtobot();
+    }
+    if(*data) {
+	move(origy, origx);
+	outs(data);
+	outc('\n');
+    }
+}
diff --git a/mbbsd/osdep.c b/mbbsd/osdep.c
new file mode 100644
index 00000000..967a4db0
--- /dev/null
+++ b/mbbsd/osdep.c
@@ -0,0 +1,79 @@
+/* $Id: osdep.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#if defined(linux)
+int cpuload(char *str) {
+    double l[3] = {-1, -1, -1};
+    FILE *fp;
+    
+    if((fp = fopen("/proc/loadavg", "r"))) {
+	if(fscanf(fp, "%lf %lf %lf",  &l[0], &l[1], &l[2]) != 3)
+	    l[0] = -1;
+	fclose(fp);
+    }
+    if(str) {
+	if(l[0] != -1)
+	    sprintf(str, " %.2f %.2f %.2f", l[0], l[1], l[2]);
+	else
+	    strcpy(str, " (unknown) ");
+    }
+    return (int)l[0];
+}
+
+double swapused(long *total, long *used) {
+    double percent = -1;
+    char buf[101];
+    FILE *fp;
+	
+    if((fp = fopen("/proc/meminfo","r"))) {
+	while(fgets(buf, 100, fp) && buf[0] != 'S');
+	if(sscanf(buf + 6, "%ld %ld", total, used) == 2)
+	    if(*total != 0)
+		percent = (double)*used / (double)*total;
+	fclose(fp);
+    }
+    return percent;
+}
+
+#elif __FreeBSD__ >=4 
+
+#include <kvm.h>
+
+int cpuload(char *str) {
+    double l[3] = {-1, -1, -1};
+    if(getloadavg(l, 3) != 3)
+	l[0] = -1;
+    
+    if(str) {
+	if(l[0] != -1)
+	    sprintf(str, " %.2f %.2f %.2f", l[0], l[1], l[2]);
+	else
+	    strcpy(str, " (unknown) ");
+    }
+    return (int)l[0];
+}
+
+double swapused(long *total, long *used) {
+    double percent = -1;
+    kvm_t *kd;
+    struct kvm_swap swapinfo;
+    int pagesize;
+	
+    kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
+    if(kd) {
+	if(kvm_getswapinfo(kd, &swapinfo, 1, 0) == 0) {
+	    pagesize = getpagesize();
+	    *total = swapinfo.ksw_total * pagesize;
+	    *used = swapinfo.ksw_used * pagesize;
+	    if(*total != 0)
+		percent = (double)*used / (double)*total;
+	}
+	kvm_close(kd);
+    }
+    return percent;
+}
+#endif
diff --git a/mbbsd/othello.c b/mbbsd/othello.c
new file mode 100644
index 00000000..47b8cef3
--- /dev/null
+++ b/mbbsd/othello.c
@@ -0,0 +1,541 @@
+/* $Id: othello.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *BBSName;
+
+#define LOGFILE "etc/othello.log"
+#define SECRET "etc/othello.secret"
+#define NR_TABLE 2
+
+#define true 1
+#define false 0
+#define STARTX 3
+#define STARTY 20
+#define NONE_CHESS "  "
+#define WHITE_CHESS "��"
+#define BLACK_CHESS "��"
+#define HINT_CHESS "��"
+#define NONE  0
+#define HINT  1
+#define BLACK 2
+#define WHITE 3
+
+#define INVERT(COLOR) (((COLOR))==WHITE?BLACK:WHITE)
+
+static char nowx = 3, nowy = 3;
+static char *CHESS_TYPE[] = {NONE_CHESS, HINT_CHESS, BLACK_CHESS, WHITE_CHESS};
+static char DIRX[] = {-1,-1,-1, 0, 1, 1, 1, 0};
+static char DIRY[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+static char number[2];
+
+static char pass = 0;
+static char if_hint = 0;
+static int think, which_table;
+
+static char nowboard[10][10]=
+{{-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, -1}};
+static char init_table[NR_TABLE+1][5][5] = {
+    {{ 0, 0, 0, 0, 0},
+     { 0,30,-3, 2, 2},
+     { 0,-3,-3,-1,-1},
+     { 0, 2,-1, 1, 1},
+     { 0, 2,-1, 1, 0}},
+	 
+    {{ 0, 0, 0, 0, 0},
+     { 0,70, 5,20,30},
+     { 0, 5,-5, 3, 3},
+     { 0,20, 3, 5, 5},
+     { 0,30, 3, 5, 5}},
+	 
+    {{ 0, 0, 0, 0, 0},
+     { 0, 5, 2, 2, 2},
+     { 0, 2, 1, 1, 1},
+     { 0, 2, 1, 1, 1},
+     { 0, 2, 1, 1, 1}}
+};
+
+static char table[NR_TABLE + 1][10][10];
+static void print_chess(int x, int y, char chess) {
+    move(STARTX - 1 + x * 2, STARTY - 2 + y * 4);
+    if(chess != HINT || if_hint == 1)
+	prints(CHESS_TYPE[(int)chess]);
+    else
+	prints(CHESS_TYPE[NONE]);
+    refresh();
+}
+
+extern userec_t cuser;
+
+static void printboard() {
+    int i;
+    
+    move(STARTX, STARTY);
+    prints("�z�w�s�w�s�w�s�w�s�w�s�w�s�w�s�w�{");
+    for(i = 0; i < 7; i++) {
+	move(STARTX + 1 + i * 2, STARTY);
+	prints ("�x  �x  �x  �x  �x  �x  �x  �x  �x");
+	move(STARTX + 2 + i * 2, STARTY);
+	prints ("�u�w�q�w�q�w�q�w�q�w�q�w�q�w�q�w�t");
+    }
+    move(STARTX + 1 + i * 2, STARTY);
+    prints("�x  �x  �x  �x  �x  �x  �x  �x  �x");
+    move(STARTX + 2 + i * 2, STARTY);
+    prints("�|�w�r�w�r�w�r�w�r�w�r�w�r�w�r�w�}");
+    print_chess(4, 4, WHITE);
+    print_chess(5, 5, WHITE);
+    print_chess(4, 5, BLACK);
+    print_chess(5, 4, BLACK);
+    move(3, 56);
+    prints("(��)%s",cuser.userid);
+    move(3, 72);
+    prints(": 02");
+    move(4, 56);
+    prints("(��)�q��        : 02");
+    move(6, 56);
+    prints("��  �i�H�U���B");
+    move(7, 56);
+    prints("[q] �h�X");
+    move(8, 56);
+    prints("[h] �}��/���� ����");
+    move(9,56);
+    prints("[Enter][Space] �U��");
+    move(10, 56);
+    prints("�W:��, i");
+    move(11, 56);
+    prints("�U:��, k");
+    move(12, 56);
+    prints("��:��, j");
+    move(13, 56);
+    prints("�k:��, l");
+}
+
+static int get_key(char nowx, char nowy) {
+    int ch;
+    
+    move(STARTX - 1 + nowx * 2, STARTY - 1 + nowy * 4);
+    ch = igetkey();
+    move(STARTX - 1 + nowx * 2, STARTY - 2 + nowy * 4);
+    if(nowboard[(int)nowx][(int)nowy] != HINT || if_hint==1)
+	outs(CHESS_TYPE[(int)nowboard[(int)nowx][(int)nowy]]);
+    else
+	outs(CHESS_TYPE[NONE]);
+    return ch;
+}
+
+static int eatline(int i, int j, char color, int dir, char chessboard[][10]) {
+    int tmpx,tmpy;
+    char tmpchess;
+    
+    tmpx = i + DIRX[dir];
+    tmpy = j + DIRY[dir];
+    tmpchess = chessboard[tmpx][tmpy];
+    if(tmpchess == -1)
+	return false;
+    if(tmpchess != INVERT(color))
+	return false;
+		
+    tmpx += DIRX[dir];
+    tmpy += DIRY[dir];
+    tmpchess = chessboard[tmpx][tmpy];
+    while(tmpchess != -1) {
+	if(tmpchess < BLACK)
+	    return false;
+	if(tmpchess == color) {
+	    while(i != tmpx || j != tmpy) {
+		chessboard[i][j] = color;
+		i += DIRX[dir];
+		j += DIRY[dir];
+	    }
+	    return true;
+	}
+	tmpx += DIRX[dir];
+	tmpy += DIRY[dir];
+	tmpchess = chessboard[tmpx][tmpy];
+    }
+    return false;
+}
+
+static int if_can_put(int x, int y, char color, char chessboard[][10]) {
+    int i, temp, checkx, checky;
+    
+    if(chessboard[x][y]<BLACK)
+	for(i = 0; i < 8; i++) {
+	    checkx = x + DIRX[i];
+	    checky = y + DIRY[i];
+	    temp = chessboard[checkx][checky];
+	    if(temp < BLACK)
+		continue;
+	    if(temp != color)
+		while(chessboard[checkx += DIRX[i]][checky += DIRY[i]] > HINT)
+		    if(chessboard[checkx][checky] == color)
+			return true;
+	}
+    return false;
+}
+
+static int get_hint(char color) {
+    int i, j, temp = 0;
+    
+    for(i = 1; i <= 8; i++)
+	for(j = 1; j <= 8; j++) {
+	    if(nowboard[i][j] == HINT)
+		nowboard[i][j] = NONE;
+	    if(if_can_put(i, j, color, nowboard)) {
+		nowboard[i][j] = HINT;
+		temp++;
+	    }
+	    print_chess(i, j, nowboard[i][j]);
+	}
+    return temp;
+}
+
+static void eat(int x, int y, int color, char chessboard[][10]) {
+    int k;
+    
+    for(k = 0; k < 8; k++)
+	eatline(x, y, color, k, chessboard);
+}
+
+static void end_of_game(int quit) {
+    FILE *fp,*fp1;	
+    char *opponent[] = {"","CD-65","","����","�p��","","�j�H","�M�a"};	
+    
+    move(STARTX - 1, 30);
+    prints ("                         ");
+    move(22, 35);
+    fp = fopen(LOGFILE, "a");
+    if(!quit) {
+	fp1 = fopen(SECRET, "a");
+	if(fp1) {
+	    fprintf(fp1, "%d,%d,%s,%02d,%02d\n", think, which_table,
+		    cuser.userid, number[0], number[1]);	
+	    fclose(fp1);
+	}
+    }
+    
+    if(quit) {
+	if(number[0] == 2 && number[1] == 2) {
+	    if(fp)
+		fclose(fp);
+	    return;
+	}
+	fprintf(fp, "�b%s�Ť�, %s�{�}��k\n", opponent[think], cuser.userid);
+	if(fp)
+	    fclose(fp);			
+	return;
+    }	
+    if(number[0] > number[1]) {
+	prints("�A�F�q��%02d�l", number[0] - number[1]);
+	if(think == 6 && number[0] - number[1] >= 50)
+	    demoney(200);
+	if(think == 7 && number[0] - number[1] >= 40)
+	    demoney(200);
+	if(fp)
+	    fprintf(fp, "�b%s�Ť�, %s�H %02d:%02d Ĺ�F�q��%02d�l\n",
+		    opponent[think], cuser.userid, number[0], number[1],
+		    number[0] - number[1]);
+    } else if(number[1] > number[0]) {
+	prints("�q���F�A%02d�l", number[1] - number[0]);
+	if(fp) {
+	    fprintf(fp, "�b%s�Ť�, ", opponent[think]);
+	    if(number[1] - number[0] > 20)
+		fprintf(fp, "�q���H %02d:%02d �G�q%s %02d�l\n", number[1],
+			number[0], cuser.userid, number[1] - number[0]);
+	    else
+		fprintf(fp, "�q���H %02d:%02d �F%s %02d�l\n", number[1],
+			number[0], cuser.userid, number[1] - number[0]);
+	}				
+    } else {
+	prints("�A�M�q����������!!");
+	if(fp)
+	    fprintf(fp, "�b%s�Ť�, %s�M�q���H %02d:%02d �����F����\n",
+		    opponent[think], cuser.userid, number[1], number[0]);
+    }
+    if(fp)
+	fclose(fp);
+    move(1,1);
+    igetkey();
+}
+
+static void othello_redraw() {
+    int i, j;
+    
+    for(i = 1; i <= 8; i++)
+	for(j = 1; j <= 8; j++)
+	    print_chess(i, j, nowboard[i][j]);
+}
+
+static int player(char color) {
+    int ch;
+    
+    if(get_hint(color)) {
+	while(true) {
+	    ch = get_key(nowx,nowy);
+	    switch(ch) {
+	    case 'J':
+	    case 'j':
+	    case KEY_LEFT:
+		nowy--;
+		break;
+	    case 'L':
+	    case 'l':
+	    case KEY_RIGHT:
+		nowy++;
+		break;
+	    case 'I':
+	    case 'i':
+	    case KEY_UP:
+		nowx--;
+		break;
+	    case 'K':
+	    case 'k':
+	    case KEY_DOWN:
+		nowx++;
+		break;	
+	    case ' ':
+	    case '\r':
+		if(nowboard[(int)nowx][(int)nowy] != HINT)
+		    break;
+		pass = 0;
+		nowboard[(int)nowx][(int)nowy] = color;
+		eat(nowx, nowy, color, nowboard);
+		print_chess(nowx, nowy, color);
+		return true;
+	    case 'q':
+		end_of_game(1);
+		return false;
+	    case 'H':
+	    case 'h':
+		if_hint = if_hint^1;
+		othello_redraw();
+		break;
+	    }
+	    if(nowx == 9)
+		nowx=1;
+	    if(nowx == 0)
+		nowx=8;
+	    if(nowy == 9)
+		nowy=1;
+	    if(nowy == 0)
+		nowy=8;
+	}
+    } else {
+	pass++;
+	if(pass == 1) {
+	    move(23, 34);
+	    prints("�A���ݩ��o�@�B!!");
+	    igetch();
+	    move(28,23);
+	    prints("                             ");
+	} else {
+	    end_of_game(0);
+	    return false;
+	}
+    }
+    return 0;
+}
+
+static void init() {
+    int i, j, i1, j1;
+    
+    nowx = 4;
+    nowy = 4;
+    number[0] = number[1] = 2;
+    for(i = 1; i <= 8; i++)
+	for(j = 1;j <= 8; j++) {
+	    i1 = 4.5 - abs(4.5 - i);
+	    j1 = 4.5 - abs(4.5 - j);
+	    table[0][i][j] = init_table[0][i1][j1];
+	    table[1][i][j] = init_table[1][i1][j1];
+	}
+    for(i = 1; i <= 8; i++)
+	for(j = 1; j <= 8; j++)
+	    nowboard[i][j] = NONE;
+    nowboard[4][4] = nowboard[5][5] = WHITE;
+    nowboard[4][5] = nowboard[5][4] = BLACK;
+}
+
+static void report() {
+    int i, j;
+    
+    number[0] = number[1] = 0;
+    for(i = 1; i <= 8; i++)
+	for(j = 1; j <= 8; j++)
+	    if(nowboard[i][j] == BLACK)
+		number[0]++;
+	    else if(nowboard[i][j] == WHITE)
+		number[1]++;
+    move(3, 60);
+    prints("%s", cuser.userid);
+    move(3, 72);
+    prints(": %02d", number[0]);
+    move(4, 60);
+    prints("�q��        : %02d", number[1]);
+}
+
+static int EVL(char chessboard[][10], int color, int table_number) {
+    int points = 0,a,b;
+    for(a = 1; a <= 8; a++)
+	for(b = 1; b <= 8; b++)
+	    if(chessboard[a][b] > HINT) {
+		if(chessboard[a][b] == BLACK)
+		    points += table[table_number][a][b];
+		else
+		    points -= table[table_number][a][b];
+	    }
+    return ((color == BLACK) ? points : -points);
+}
+
+static int alphabeta(int alpha, int beta, int level, char chessboard[][10],
+	      int thinkstep, int color, int table) {
+    int i, j, k, flag = 1;
+    char tempboard[10][10];
+    if(level == thinkstep+1)
+	return EVL(chessboard, (level & 1 ? color : ((color - 2) ^ 1) + 2),
+		   table);
+    for(i = 1; i <= 8; i++) {
+	for(j = 1; j <= 8; j++) {
+	    if(if_can_put(i, j, color, chessboard)) {
+		flag = 0;
+		memcpy(tempboard, chessboard, sizeof(char) * 100);
+		eat(i, j, color, tempboard);
+		
+		k = alphabeta(alpha, beta, level + 1, tempboard, thinkstep,
+			      ((color - 2) ^ 1) + 2, table);
+		if(((level & 1) && k > alpha))
+		    alpha = k;
+		else if(!(level & 1) && k < beta)
+		    beta = k;
+		if(alpha >= beta)
+		    break;
+	    }
+	}
+    }
+    if(flag)
+	return EVL(chessboard, color, table);
+    return ((level & 1) ? alpha : beta);
+}
+
+static int Computer(int thinkstep, int table) {
+    int i, j, maxi = 0, maxj = 0, level = 1;
+    char chessboard[10][10];
+    int alpha = -10000, k;
+    if((number[0] + number[1]) > 44)
+	table = NR_TABLE;
+    for(i = 1; i <= 8; i++)
+	for(j = 1; j <= 8; j++) {
+	    if(if_can_put(i,j,WHITE,nowboard)) {
+		memcpy(chessboard, nowboard, sizeof(char) * 100);
+		eat(i, j, WHITE, chessboard);
+		k = alphabeta(alpha, 10000, level + 1, chessboard, thinkstep,
+			      BLACK, table);
+		if(k > alpha) {
+		    alpha = k;
+		    maxi = i;
+		    maxj = j;
+		}
+	    }
+	}
+    if(alpha != -10000) {
+	eat(maxi, maxj, WHITE, nowboard);
+	pass = 0;
+	nowx = maxi;
+	nowy = maxj;
+    } else {
+	move(23, 30);
+	prints("�q�����o�@�B��!!");
+	pass++;
+	if(pass == 2) {
+	    move(23, 24);
+	    prints("                               ");
+	    end_of_game(0);
+	    return false;
+	}
+	igetch();
+	move(23, 24);
+	prints("                                  ");	
+    }
+    return true;
+}
+
+static int choose() {
+    char thinkstep[2];
+    
+    move(2, 0);
+    prints("������:");
+    move(5, 0);
+    prints("(1) CD-65\n");		   /* �Q 1 �B */
+    prints("(2) ����\n");		   /* �Q 3 �B */
+    prints("(3) �p��\n");		   /* �Q 4 �B */
+    do {
+	getdata(4, 0, "�п�ܤ@�ӹ�H�M�z�若:(1~5)", thinkstep, 2, LCECHO);
+    } while(thinkstep[0] < '1' || thinkstep[0] > '3');
+    clear();
+    switch(thinkstep[0]) {
+    case '2':
+	thinkstep[0] = '3';
+	break;
+    case '3':
+	thinkstep[0] = '4';
+	break;
+    default:
+	thinkstep[0] = '1';
+	break;
+    }
+    return atoi(thinkstep);
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+
+int othello_main() {
+    lockreturn0(OTHELLO, LOCK_MULTI);
+    clear();
+    init();
+    think = choose();
+    showtitle("�¥մ�", BBSName);	
+    printboard();
+    which_table = rand() % NR_TABLE;
+    while(true) {
+	move(STARTX - 1, 30);
+	prints("����A�U�F...");
+	if(!player(BLACK))
+	    break;
+	report();
+	othello_redraw();
+	if(number[0] + number[1] == 64) {
+	    end_of_game(0);
+	    break;
+	}
+	move(STARTX - 1, 30);
+	prints("�q����Ҥ�...");
+	refresh();
+	if(!Computer(think, which_table))
+	    break;
+	report();
+	othello_redraw();
+	if(number[0] + number[1] == 64) {
+	    end_of_game(0);
+	    break;
+	}
+    }
+    more(LOGFILE, YEA);
+    unlockutmpmode();
+    return 1;
+}
diff --git a/mbbsd/page.c b/mbbsd/page.c
new file mode 100644
index 00000000..c77ef421
--- /dev/null
+++ b/mbbsd/page.c
@@ -0,0 +1,130 @@
+/* $Id: page.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "proto.h"
+
+#define hpressanykey(a) {move(22, 0); prints(a); pressanykey();}      
+static void filt_railway(char* fpath) {
+    char buf[256], tmppath[32];
+    FILE* fp = fopen(fpath, "w"), *tp;
+
+    sprintf(tmppath, "%s.railway", fpath);
+    if(!fp || !(tp = fopen(tmppath, "r")))
+	return;
+
+    while(fgets(buf, 255, tp)) {
+	if(strstr(buf, "INLINE"))
+	    continue;
+	if(strstr(buf, "LINK"))
+	    break;
+	fprintf(fp, "%s", buf);
+    }
+    fclose(fp);
+    fclose(tp);
+    unlink(tmppath);
+}
+
+extern userec_t cuser;
+
+int main_railway() {
+    fileheader_t mhdr;
+    char genbuf[200];
+    int from, to, time_go, time_reach;
+    char tt[2], type[2];
+    char command[256], buf[8];
+    char *addr[]= {
+	"��", "�K��", "�C��", "����", "����", "�n��", "�Q�s", "�x�_", "�U��",
+	"�O��", "��L", "�s��", "�a�q", "���", "���c", "���c", "�H��", "����",
+	"��f", "�s��", "�˥_", "�s��", "���s", "�T��", "�˫n", "�y��", "�״I",
+	"�ͤ�", "�j�s", "���s", "�s��", "�ըF��", "�s�H", "�q�]", "�b��",
+	"��n", "�j��", "�O����", "�M��", "�F��", "�s��", "�j�{", "�l��",
+	"�]��", "�n��", "���r", "�T�q", "�ӿ�", "���w", "�Z��", "�׭�", "��l",
+	"�x��", "�Q��", "���\\", "����", "���", "���L", "�ùt", "���Y",
+	"�Ф�", "�G��", "�L��", "�ۺh", "�椻", "��n", "���t", "�j�L",
+	"����", "�Ÿq",	"���W", "�n�t", "���", "�s��", "�h��", "�L����",
+	"����", "�ުL",	"����", "�s��", "�ñd", "�x�n", "�O�w", "���w",
+	"�j��", "����", "���s",	"���Y", "����", "����", "����", "��s",
+	"�E����", "�̪F", NULL, NULL
+    };
+    
+    setutmpmode(RAIL_WAY);
+    clear();
+    move(0,25);
+    prints("\033[1;37;45m �����d�ߨt�� \033[1;44;33m�@��:Heat\033[m");
+    move(1,0);
+    outs("\033[1;33m
+ 1.��    16.���c     31.�s��     46.���r     61.��    76.�L����   91.����
+ 2.�K��    17.�H��     32.�ըF��   47.�T�q     62.�G��    77.����     92.��s
+ 3.�C��    18.����     33.�s�H     48.�ӿ�     63.�L��    78.�ުL     93.�E����
+ 4.����    19.��f     34.�q�]     49.���w     64.�ۺh    79.����     94.�̪F
+ 5.����    20.�s��     35.�b��     50.�Z��     65.�椻    80.�s��
+ 6.�n��    21.�˥_     36.��n     51.�׭�     66.��n    81.�ñd
+ 7.�Q�s    22.�s��     37.�j��     52.��l     67.���t    82.�x�n
+ 8.�x�_    23.���s     38.�O����   53.�x��     68.�j�L    83.�O�w
+ 9.�U��    24.�T��     39.�M��     54.�Q��     69.����    84.���w
+10.�O��    25.�˫n     40.�F��     55.���\\     70.�Ÿq    85.�j��
+11.��L    26.�y��     41.�s��     56.����     71.���W    86.����
+12.�s��    27.�״I     42.�j�{     57.���     72.�n�t    87.���s
+13.�a�q    28.�ͤ�     43.�l��     58.���L     73.���    88.���Y
+14.���    29.�j�s     44.�]��     59.�ùt     74.�s��    89.����
+15.���c    30.���s     45.�n��     60.���Y     75.�h��    90.����\033[m");
+
+    getdata(17, 0, "\033[1;35m�A�T�w�n�j�M��?[y/n]:\033[m", buf, 2, LCECHO);
+    if(buf[0] != 'y' && buf[0] != 'Y')
+	return 0;
+    while(1)
+	if(getdata(18, 0, "\033[1;35m�п�J�_��(1-94):\033[m", buf, 3, LCECHO) &&
+	   (from = atoi(buf)) >= 1 && from <= 94)
+	    break;
+    while(1)
+	if(getdata(18, 40, "\033[1;35m�п�J�ت��a(1-94):\033[m",
+		   buf, 3, LCECHO) &&
+	   (to = atoi(buf)) >= 1 && to <= 94)
+	    break;
+    while(1)
+	if(getdata(19, 0, "\033[1;35m�п�J�ɶ��Ϭq(0-23) ��:\033[m",
+		   buf,3,LCECHO) &&
+	   (time_go = atoi(buf)) >= 0 && time_go <= 23)
+	    break;
+    while(1)
+	if(getdata(19, 40, "\033[1;35m��:\033[m", buf, 3, LCECHO) &&
+	   (time_reach=atoi(buf)) >= 0 && time_reach <= 23)
+	    break;
+    while(1)
+	if(getdata(20, 0, "\033[1;35m�Q�d�� 1:�︹�֨�  2:���q����\033[m",
+		   type,2,LCECHO) && (type[0] == '1' || type[0] == '2'))
+	    break;
+    while(1)
+	if(getdata(21, 0, "\033[1;35m���d�� 1:�X�o�ɶ�  2:��F�ɶ�\033[m",
+		   tt, 2, LCECHO) &&
+	   (tt[0]=='1' || tt[0]=='2'))
+	    break;
+    sethomepath(genbuf, cuser.userid);
+    stampfile(genbuf, &mhdr);
+    strcpy(mhdr.owner, "Ptt�j�M��");
+    strncpy(mhdr.title, "�����ɨ�j�M���G", TTLEN);
+    mhdr.savemode = '\0';
+
+    sprintf(command,"echo \"from-station=%s&to-station=%s"
+	    "&from-time=%02d00&to-time=%02d00&tt=%s&type=%s\" | "
+	    "lynx -dump -post_data "
+	    "\"http://www.railway.gov.tw/cgi-bin/timetk.cgi\" > %s.railway",
+	    addr[from - 1], addr[to - 1], time_go, time_reach,
+	    (tt[0] == '1') ? "start" : "arriv",
+	    (type[0] == '1') ? "fast" : "slow", genbuf);
+   
+    system(command);
+    filt_railway(genbuf);
+    sethomedir(genbuf, cuser.userid);
+    if(append_record(genbuf, &mhdr, sizeof(mhdr)) == -1)
+	return -1;
+    hpressanykey("\033[1;31m�ڭ̷|��j�M���G�ܧִN�H���A��  ^_^\033[m");
+    return 0;
+}
diff --git a/mbbsd/passwd.c b/mbbsd/passwd.c
new file mode 100644
index 00000000..28a31119
--- /dev/null
+++ b/mbbsd/passwd.c
@@ -0,0 +1,138 @@
+/* $Id: passwd.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *fn_passwd;
+
+static userec_t *passwd_image = NULL;
+static int passwd_image_size;
+static int semid = -1;
+
+#ifndef SEM_R
+#define SEM_R 0400
+#endif
+
+#ifndef SEM_A
+#define SEM_A 0200
+#endif
+
+#ifndef __FreeBSD__
+union semun {
+        int     val;            /* value for SETVAL */
+        struct  semid_ds *buf;  /* buffer for IPC_STAT & IPC_SET */
+        u_short *array;         /* array for GETALL & SETALL */
+        struct seminfo *__buf;  /* buffer for IPC_INFO */
+};
+#endif
+
+int passwd_mmap() {
+    int fd;
+    
+    fd = open(fn_passwd, O_RDWR);
+    if(fd > 0) 
+    {
+	struct stat st;
+	
+	fstat(fd, &st);
+	passwd_image_size = st.st_size;
+	passwd_image = mmap(NULL, passwd_image_size,
+			    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if(passwd_image == (userec_t *)-1) {
+	    perror("mmap");
+	    return -1;
+	}
+/* rocker 011018: after success get mmap, close file descript */
+	close (fd);
+	
+	semid = semget(PASSWDSEM_KEY, 1, SEM_R | SEM_A | IPC_CREAT | IPC_EXCL);
+	if(semid == -1) {
+	    if(errno == EEXIST) {
+		semid = semget(PASSWDSEM_KEY, 1, SEM_R | SEM_A);
+		if(semid == -1) {
+		    perror("semget");
+		    exit(1);
+		}
+	    } else {
+		perror("semget");
+		exit(1);
+	    }
+	} else {
+	    union semun s;
+	    
+	    s.val = 1;
+	    if(semctl(semid, 0, SETVAL, s) == -1) {
+		perror("semctl");
+		exit(1);
+	    }
+	}
+    } else {
+	perror(fn_passwd);
+	return -1;
+    }
+    return 0;
+}
+
+extern int usernum;
+int passwd_update_money(int num) {
+    int money;
+    if(num < 1 || num > MAX_USERS)
+        return -1;
+    money = moneyof(num);
+    memcpy(&passwd_image[num - 1].money, &money, sizeof(int));
+    return 0;
+}   
+
+int passwd_update(int num, userec_t *buf) {
+    if(num < 1 || num > MAX_USERS)
+	return -1;
+    buf->money = moneyof(num);
+    memcpy(&passwd_image[num - 1], buf, sizeof(userec_t));
+    return 0;
+}
+
+int passwd_query(int num, userec_t *buf) {
+    if(num < 1 || num > MAX_USERS)
+	return -1;
+    memcpy(buf, &passwd_image[num - 1], sizeof(userec_t));
+    return 0;
+}
+
+int passwd_apply(int (*fptr)(userec_t *)) {
+    int i;
+
+    for(i = 0; i < MAX_USERS; i++)
+	if((*fptr)(&passwd_image[i]) == QUIT)
+	    return QUIT;
+    return 0;
+}
+
+void passwd_lock() {
+    struct sembuf buf = { 0, -1, SEM_UNDO };
+    
+    if(semop(semid, &buf, 1)) {
+	perror("semop");
+	exit(1);
+    }
+}
+
+void passwd_unlock() {
+    struct sembuf buf = { 0, 1, SEM_UNDO };
+    
+    if(semop(semid, &buf, 1)) {
+	perror("semop");
+	exit(1);
+    }
+}
diff --git a/mbbsd/read.c b/mbbsd/read.c
new file mode 100644
index 00000000..b92f95e7
--- /dev/null
+++ b/mbbsd/read.c
@@ -0,0 +1,998 @@
+/* $Id: read.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "perm.h"
+#include "proto.h"
+
+#define MAXPATHLEN 256
+
+extern int p_lines;             /* a Page of Screen line numbers: tlines-4 */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern char currowner[IDLEN + 2];
+extern char currtitle[44];
+extern char currauthor[IDLEN + 2];
+extern char *str_reply;
+extern char *msg_fwd_ok;
+extern char *msg_fwd_err1;
+extern char *msg_fwd_err2;
+extern int currmode;
+extern unsigned int currstat;
+extern char currboard[];        /* name of currently selected board */
+extern int KEY_ESC_arg;
+extern int curredit;
+extern char *msg_mailer;
+extern int currbid;
+extern bcache_t *brdshm;  
+
+char currdirect[64];
+static fileheader_t *headers = NULL;
+static int last_line;
+static int hit_thread;
+
+/* rocker.011018: add new tag */
+
+extern int rget();
+extern char getans();
+extern void touchdircache();
+extern int get_fileheader_cache();
+
+/* rocker.011018: �s��tag�覡 */
+
+#define MAXTAGS	256
+
+#include <sys/mman.h>
+
+typedef struct
+{ 
+  time_t chrono;
+  int recno;
+}      TagItem;
+
+
+/* ----------------------------------------------------- */
+/* Tag List ����                                         */
+/* ----------------------------------------------------- */
+
+  
+int TagNum;                     /* tag's number */
+TagItem TagList[MAXTAGS];       /* ascending list */
+
+void
+UnTagger (int locus)
+{
+  if (locus > TagNum) return;
+
+  TagNum--;
+
+  if (TagNum > locus)
+    memcpy(&TagList[locus], &TagList[locus + 1],
+      (TagNum - locus) * sizeof(TagItem));
+}
+
+int
+Tagger(time_t chrono, int recno, int mode)
+{ 
+  int head, tail, posi = 0, comp;
+  
+  for (head = 0, tail = TagNum - 1, comp = 1; head <= tail;)
+  {
+    posi = (head + tail) >> 1;
+    comp = TagList[posi].chrono - chrono;
+    if (!comp)
+    { 
+      break;
+    }
+    else if (comp < 0)
+    { 
+      head = posi + 1;
+    }
+    else
+    { 
+      tail = posi - 1;
+    }
+  }
+
+  if (mode == TAG_NIN)
+  { 
+    if (!comp && recno)         /* �����Y�ԡG�s recno �@�_��� */
+      comp = recno - TagList[posi].recno;
+    return comp;
+
+  }
+
+  if (!comp)
+  { 
+    if (mode != TAG_TOGGLE)
+      return NA;
+
+    TagNum--;
+    memcpy(&TagList[posi], &TagList[posi + 1],
+      (TagNum - posi) * sizeof(TagItem));
+  }
+  else if (TagNum < MAXTAGS)
+  { 
+    TagItem *tagp, buf[MAXTAGS];
+
+    tail = (TagNum - head) * sizeof(TagItem);
+    tagp = &TagList[head];
+    memcpy(buf, tagp, tail);
+    tagp->chrono = chrono;
+    tagp->recno = recno;
+    memcpy(++tagp, buf, tail);
+    TagNum++;
+  }
+  else
+  { 
+    bell();
+    return 0;                   /* full */
+  }
+  return YEA;
+}
+
+
+void
+EnumTagName( char *fname, int locus)
+{ 
+  sprintf(fname, "M.%d.A", (int) TagList[locus].chrono);
+}
+
+void
+EnumTagFhdr(fileheader_t *fhdr, char *direct, int locus)
+{ 
+  get_record(direct, fhdr, sizeof(fileheader_t), TagList[locus].recno);
+}
+
+/* -1 : ���� */
+/* 0 : single article */
+/* ow: whole tag list */
+
+int
+AskTag(char *msg)
+{ 
+  char buf[80];
+  int num;
+
+  num = TagNum;
+  sprintf(buf, "�� %s A)�峹 T)�аO Q)uit?", msg);
+  switch (rget(b_lines-1, buf))
+  {
+  case 'q':
+    num = -1;
+    break;
+  case 'a':
+    num = 0;
+  }
+  return num;
+}
+
+
+#include <sys/mman.h>
+
+#define BATCH_SIZE      65536
+
+char *
+f_map (char *fpath, int *fsize)
+{
+  int fd, size;
+  struct stat st;
+  
+  if ((fd = open(fpath, O_RDONLY)) < 0)
+    return (char *) -1;
+
+  if (fstat(fd, &st) || !S_ISREG(st.st_mode) || (size = st.st_size) <= 0)
+  { 
+    close(fd);
+    return (char *) -1;
+  }
+
+  fpath = (char *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+  close(fd);
+  *fsize = size;
+  return fpath;
+}
+
+
+static int
+TagThread(char *direct)
+{ 
+  int fsize, count;
+  char *title, *fimage;
+  fileheader_t *head, *tail;
+
+  fimage = f_map(direct, &fsize);
+  if ( fimage == (char *) -1)
+    return DONOTHING;
+
+  head = (fileheader_t *) fimage;
+  tail = (fileheader_t *) (fimage + fsize);
+  count = 0;
+  do
+  { 
+    count++;
+    title = subject(head->title);
+    if (!strncmp( currtitle, title,TTLEN))
+    {   
+        if (!Tagger(atoi (head->filename + 2), count, TAG_INSERT))
+          break;
+    }
+  } while (++head < tail);
+
+  munmap(fimage, fsize);
+  return FULLUPDATE;
+}
+
+
+int
+TagPruner(int bid)
+{ 
+  if (TagNum && ((currstat != READING) || (currmode & MODE_BOARD)))
+  { 
+    if(getans("�R���Ҧ��аO[N]?") != 'y')
+      return FULLUPDATE;
+    delete_range(currdirect, 0, 0);
+    TagNum = 0;
+    if(bid>0);
+       setbtotal(bid);	
+    return NEWDIRECT;
+  }
+  return DONOTHING;
+}
+
+
+/* ----------------------------------------------------- */
+/* cursor & reading record position control              */
+/* ----------------------------------------------------- */
+keeploc_t *getkeep(char *s, int def_topline, int def_cursline) {
+    static struct keeploc_t *keeplist = NULL;
+    struct keeploc_t *p;
+    void *malloc();
+
+    if(def_cursline >= 0)
+	for(p = keeplist; p; p = p->next) {
+	    if(!strcmp(s, p->key)) {
+		if(p->crs_ln < 1)
+		    p->crs_ln = 1;
+		return p;
+	    }
+	}
+    else
+	def_cursline = -def_cursline;
+    p = (keeploc_t *)malloc(sizeof(keeploc_t));
+    p->key = (char *)malloc(strlen(s) + 1);
+    strcpy(p->key, s);
+    p->top_ln = def_topline;
+    p->crs_ln = def_cursline;
+    p->next = keeplist;
+    return (keeplist = p);
+}
+
+void fixkeep(char *s, int first) {
+    keeploc_t *k;
+    
+    k = getkeep(s, 1, 1);
+    if(k->crs_ln >= first) {
+	k->crs_ln = (first == 1 ? 1 : first - 1);
+	k->top_ln = (first < 11 ? 1 : first - 10);
+    }
+}
+
+/* calc cursor pos and show cursor correctly */
+static int cursor_pos(keeploc_t *locmem, int val, int from_top) {
+    int top;
+    
+    if(val > last_line) {
+	bell();
+	val = last_line;
+    }
+    if(val <= 0) {
+	bell();
+	val = 1;
+    }
+
+    top = locmem->top_ln;
+    if(val >= top && val < top + p_lines) {
+	cursor_clear(3 + locmem->crs_ln - top, 0);
+	locmem->crs_ln = val;
+	cursor_show(3 + val - top, 0);
+	return DONOTHING;
+    }
+    locmem->top_ln = val - from_top;
+    if(locmem->top_ln <= 0)
+	locmem->top_ln = 1;
+    locmem->crs_ln = val;
+    return PARTUPDATE;
+}
+
+static int move_cursor_line(keeploc_t *locmem, int mode) {
+    int top, crs;
+    int reload = 0;
+
+    top = locmem->top_ln;
+    crs = locmem->crs_ln;
+    if(mode == READ_PREV) {
+	if(crs <= top) {
+	    top -= p_lines - 1;
+	    if(top < 1)
+		top = 1;
+	    reload = 1;
+	}
+	if(--crs < 1) {
+	    crs = 1;
+	    reload = -1;
+	}
+    } else if(mode == READ_NEXT) {
+	if(crs >= top + p_lines - 1) {
+	    top += p_lines - 1;
+	    reload = 1;
+	}
+	if(++crs > last_line) {
+	    crs = last_line;
+	    reload = -1;
+	}
+    }
+    locmem->top_ln = top;
+    locmem->crs_ln = crs;
+    return reload;
+}
+
+static int thread(keeploc_t *locmem, int stype) {
+    static char a_ans[32], t_ans[32];
+    char ans[32], s_pmt[64];
+    register char *tag, *query = NULL;
+    register int now, pos, match, near = 0;
+    fileheader_t fh;
+    int circulate_flag = 1;  /* circulate at end or begin */
+
+    match = hit_thread = 0;
+    now = pos = locmem->crs_ln;
+    if(stype == 'A') {
+	if(!*currowner)
+	    return DONOTHING;
+	str_lower(a_ans, currowner);
+	query = a_ans;
+	circulate_flag = 0;
+	stype = 0;
+    } else if(stype == 'a') {
+	if(!*currowner)
+	    return DONOTHING;
+	str_lower(a_ans, currowner);
+	query = a_ans;
+	circulate_flag = 0;
+	stype = RS_FORWARD;
+    } else if(stype == '/') {
+	if(!*t_ans)
+	    return DONOTHING;
+	query = t_ans;
+	circulate_flag = 0;
+	stype = RS_TITLE | RS_FORWARD;
+    } else if(stype == '?') {
+	if(!*t_ans)
+	    return DONOTHING;
+	circulate_flag = 0;
+	query = t_ans;
+	stype = RS_TITLE;
+    } else if(stype & RS_RELATED) {
+	tag = headers[pos - locmem->top_ln].title;
+	if(stype & RS_CURRENT) {
+	    if(stype & RS_FIRST) {
+		if(!strncmp(currtitle, tag, 40))
+		    return DONOTHING;
+		near = 0;
+	    }
+	    query = currtitle;
+	} else {
+	    query = subject(tag);
+	    if(stype & RS_FIRST) {
+		if(query == tag)
+		    return DONOTHING;
+		near = 0;
+	    }
+	}
+    } else if(!(stype & RS_THREAD)) {
+	query = (stype & RS_TITLE) ? t_ans : a_ans;
+	if(!*query && query == a_ans) {
+	    if(*currowner)
+		strcpy(a_ans, currowner);
+	    else if (*currauthor)
+		strcpy(a_ans, currauthor);
+	}
+	sprintf(s_pmt, "%s�j�M%s [%s] ",(stype & RS_FORWARD) ? "����":"���e",
+		(stype & RS_TITLE) ? "���D" : "�@��", query);
+	getdata(b_lines - 1, 0, s_pmt, ans, 30, DOECHO);
+	if(*ans)
+	    strcpy(query, ans);
+	else if(*query == '\0')
+	    return DONOTHING;
+    }
+
+    tag = fh.owner;
+
+    do {
+	if(!circulate_flag || stype & RS_RELATED) {
+	    if(stype & RS_FORWARD) {
+		if(++now > last_line)
+		    return DONOTHING;
+	    } else {
+		if(--now <= 0) {
+		    if((stype & RS_FIRST) && (near)) {
+			hit_thread = 1;
+			return cursor_pos(locmem, near, 10);
+		    }
+		    return DONOTHING;
+		}
+	    }
+	} else {
+	    if(stype & RS_FORWARD) {
+		if(++now > last_line)
+		    now = 1;
+	    } else if(--now <= 0)
+		now = last_line;
+	}
+	
+	get_record(currdirect, &fh, sizeof(fileheader_t), now);
+	
+	if(fh.owner[0] == '-')
+	    continue;
+	
+	if(stype & RS_THREAD) {
+	    if(strncasecmp(fh.title, str_reply, 3)) {
+		hit_thread = 1;
+		return cursor_pos(locmem, now, 10);
+	    }
+	    continue;
+	}
+	
+	if(stype & RS_TITLE)
+	    tag = subject(fh.title);
+	
+	if(((stype & RS_RELATED) && !strncmp(tag, query, 40)) ||
+	   (!(stype & RS_RELATED) && ((query == currowner) ?
+				      !strcmp(tag, query) :
+				      strstr_lower(tag, query)))) {
+	    if((stype & RS_FIRST) && tag != fh.title) {
+		near = now;
+		continue;
+	    }
+	    
+	    hit_thread = 1;
+	    match = cursor_pos(locmem, now, 10);
+	    if((!(stype & RS_CURRENT)) &&
+	       (stype & RS_RELATED) &&
+	       strncmp(currtitle, query, 40)) {
+		strncpy(currtitle, query, 40);
+		match = PARTUPDATE;
+	    }
+	    break;
+	}
+    } while(now != pos);
+    
+    return match;
+}
+
+
+#ifdef INTERNET_EMAIL
+static void mail_forward(fileheader_t *fhdr, char *direct, int mode) {
+    int i;
+    char buf[STRLEN];
+    char *p;
+    
+    strncpy(buf, direct, sizeof(buf));
+    if((p = strrchr(buf, '/')))
+	*p = '\0';
+    switch(i = doforward(buf, fhdr, mode)) {
+    case 0:
+	outmsg(msg_fwd_ok);
+	break;
+    case -1:
+	outmsg(msg_fwd_err1);
+	break;
+    case -2:
+	outmsg(msg_fwd_err2);
+	break;
+    default:
+	break;
+    }
+    refresh();
+    sleep(1);
+}
+#endif
+
+extern userec_t cuser;
+
+static int select_read(keeploc_t *locmem, int sr_mode) {
+    register char *tag,*query,*temp;
+    fileheader_t fh;
+    char fpath[80], genbuf[MAXPATHLEN], buf3[5];
+    char static t_ans[TTLEN+1]="";
+    char static a_ans[IDLEN+1]="";
+    int fd, fr, size = sizeof(fileheader_t);
+    struct stat st;
+/* rocker.011018: make a reference number for process article */
+    int reference = 0;
+
+    if((currmode & MODE_SELECT))
+	return -1;
+    if(sr_mode == RS_TITLE)
+	query = subject(headers[locmem->crs_ln - locmem->top_ln].title);
+    else if(sr_mode == RS_NEWPOST)
+    {
+	strcpy(buf3, "Re: ");
+	query = buf3;
+    }
+    else
+    {
+	char buff[80];
+
+	query = (sr_mode == RS_RELATED) ? t_ans : a_ans;
+	sprintf(buff, "�j�M%s [%s] ",
+		(sr_mode == RS_RELATED) ? "���D" : "�@��", query);
+	getdata(b_lines, 0,buff, query, 30, DOECHO);
+	if(!(*query))
+	    return DONOTHING;
+    }
+
+    if((fd = open(currdirect, O_RDONLY, 0)) != -1) {
+	sprintf(genbuf,"SR.%s",cuser.userid);
+	if(currstat==RMAIL)
+	    sethomefile(fpath,cuser.userid,genbuf);
+	else
+	    setbfile(fpath,currboard,genbuf);
+	if(((fr = open(fpath,O_WRONLY | O_CREAT | O_TRUNC,0600)) != -1)) {
+	    switch(sr_mode) {
+	    case RS_TITLE:
+		while(read(fd,&fh,size) == size) {
+		    ++reference;
+		    tag = subject(fh.title);
+		    if(!strncmp(tag, query, 40))
+	            {
+			fh.money = reference | FHR_REFERENCE;
+			write(fr,&fh,size);
+		    }
+		}
+		break;
+	    case RS_RELATED:
+		while(read(fd,&fh,size) == size) {
+		    ++reference;
+		    tag = fh.title;
+		    if(strcasestr(tag,query))
+		    {
+			fh.money = reference | FHR_REFERENCE;
+			write(fr,&fh,size);
+		    }
+		}
+		break;
+	    case RS_NEWPOST:
+		while(read(fd, &fh, size) == size) {
+		    ++reference;
+		    tag = fh.title;
+		    temp = strstr(tag, query);
+		    if(temp == NULL || temp != tag)
+		    {
+			write(fr, &fh, size);
+			fh.money = reference | FHR_REFERENCE;
+		    }
+		}
+	    case RS_AUTHOR:
+		while(read(fd,&fh,size) == size) {
+		    ++reference;
+		    tag = fh.owner;
+		    if(strcasestr(tag,query))
+		    {
+			write(fr,&fh,size);
+			fh.money = reference | FHR_REFERENCE;
+		    }
+		}
+		break;
+	    }
+	    fstat(fr,&st);
+	    close(fr);
+	}
+	close(fd);
+	if(st.st_size) {
+	    currmode |= MODE_SELECT;
+	    strcpy(currdirect,fpath);
+	}
+    }
+    return st.st_size;
+}
+
+extern userec_t xuser;
+
+static int i_read_key(onekey_t *rcmdlist, keeploc_t *locmem, int ch, int bid) {
+    int i, mode = DONOTHING;
+    
+    switch(ch) {
+    case 'q':
+    case 'e':
+    case KEY_LEFT:
+	return (currmode & MODE_SELECT) ? board_select() :
+	    (currmode & MODE_ETC) ? board_etc() :
+		(currmode & MODE_DIGEST) ? board_digest() : DOQUIT;
+    case Ctrl('L'):
+	redoscr();
+	break;
+/*
+    case Ctrl('C'):
+	cal();
+	return FULLUPDATE;
+	break;
+*/
+    case KEY_ESC:
+	if(KEY_ESC_arg == 'i') {
+	    t_idle();
+	    return FULLUPDATE;
+	}
+	break;
+    case Ctrl('H'):
+	if(select_read(locmem, RS_NEWPOST))
+	    return NEWDIRECT;
+	else
+	    return READ_REDRAW;
+    case 'a':
+    case 'A':
+	if(select_read(locmem,RS_AUTHOR))
+	    return NEWDIRECT;
+	else
+	    return READ_REDRAW;
+    case '/':
+    case '?':
+	if(select_read(locmem,RS_RELATED))
+	    return NEWDIRECT;
+	else
+	    return READ_REDRAW;
+    case 'S':
+	if(select_read(locmem,RS_TITLE))
+	    return NEWDIRECT;
+	else
+	    return READ_REDRAW;
+	/* quick search title first */
+    case '=':
+	return thread(locmem, RELATE_FIRST);
+    case '\\':
+	return thread(locmem, CURSOR_FIRST);
+	/* quick search title forword */
+    case ']':
+	return thread(locmem, RELATE_NEXT);
+    case '+':
+	return thread(locmem, CURSOR_NEXT);
+	/* quick search title backword */
+    case '[':
+	return thread(locmem, RELATE_PREV);
+    case '-':
+	return thread(locmem, CURSOR_PREV);
+    case '<':
+    case ',':
+	return thread(locmem, THREAD_PREV);
+    case '.':
+    case '>':
+	return thread(locmem, THREAD_NEXT);
+    case 'p':
+    case 'k':
+    case KEY_UP:
+	return cursor_pos(locmem, locmem->crs_ln - 1, p_lines - 2);
+    case 'n':
+    case 'j':
+    case KEY_DOWN:
+	return cursor_pos(locmem, locmem->crs_ln + 1, 1);
+    case ' ':
+    case KEY_PGDN:
+    case 'N':
+    case Ctrl('F'):
+	if(last_line >= locmem->top_ln + p_lines) {
+	    if(last_line > locmem->top_ln + p_lines)
+		locmem->top_ln += p_lines;
+	    else
+		locmem->top_ln += p_lines - 1;
+	    locmem->crs_ln = locmem->top_ln;
+	    return PARTUPDATE;
+	}
+	cursor_clear(3 + locmem->crs_ln - locmem->top_ln, 0);
+	locmem->crs_ln = last_line;
+	cursor_show(3 + locmem->crs_ln - locmem->top_ln, 0);
+	break;
+    case KEY_PGUP:
+    case Ctrl('B'):
+    case 'P':
+	if(locmem->top_ln > 1) {
+	    locmem->top_ln -= p_lines;
+	    if(locmem->top_ln <= 0)
+		locmem->top_ln = 1;
+	    locmem->crs_ln = locmem->top_ln;
+	    return PARTUPDATE;
+	}
+	break;
+    case KEY_END:
+    case '$':
+	if(last_line >= locmem->top_ln + p_lines) {
+	    locmem->top_ln = last_line - p_lines + 1;
+	    if(locmem->top_ln <= 0)
+		locmem->top_ln = 1;
+	    locmem->crs_ln = last_line;
+	    return PARTUPDATE;
+	}
+	cursor_clear(3 + locmem->crs_ln - locmem->top_ln, 0);
+	locmem->crs_ln = last_line;
+	cursor_show(3 + locmem->crs_ln - locmem->top_ln, 0);
+	break;
+    case 'F':
+    case 'U':
+	if(HAS_PERM(PERM_FORWARD)) {
+	    mail_forward(&headers[locmem->crs_ln - locmem->top_ln],
+			 currdirect, ch /*== 'U'*/);
+	    /*by CharlieL*/
+	    return FULLUPDATE;
+	}
+	break;
+    case Ctrl('Q'):
+	return my_query(headers[locmem->crs_ln - locmem->top_ln].owner);
+    case Ctrl('S'):
+	if(HAS_PERM(PERM_ACCOUNTS)) {
+	    int id;
+	    userec_t muser;
+
+	    strcpy(currauthor, headers[locmem->crs_ln - locmem->top_ln].owner);
+	    stand_title("�ϥΪ̳]�w");
+	    move(1, 0);
+	    if((id = getuser(headers[locmem->crs_ln - locmem->top_ln].owner))){
+		memcpy(&muser, &xuser, sizeof(muser));
+		user_display(&muser, 1);
+		uinfo_query(&muser, 1, id);
+	    }
+	    return FULLUPDATE;
+	}
+	break;
+
+/* rocker.011018: �ĥηs��tag�Ҧ� */
+    case 't':
+/* rocker.011112: �ѨM�Aselect mode�аO�峹�����D */
+      if (Tagger(atoi(headers[locmem->crs_ln - locmem->top_ln].filename + 2), 
+	(currmode & MODE_SELECT) ?
+	  (headers[locmem->crs_ln - locmem->top_ln].money & ~FHR_REFERENCE) :
+           locmem->crs_ln, TAG_TOGGLE))
+        return  POS_NEXT;
+      return DONOTHING;
+
+    case Ctrl('C'):
+      if (TagNum)
+      {
+        TagNum = 0;
+        return FULLUPDATE;
+      }
+      return DONOTHING;
+
+    case Ctrl('T'):
+      return TagThread(currdirect);
+    case Ctrl('D'):
+      return TagPruner(bid);
+    case '\n':
+    case '\r':
+    case 'l':
+    case KEY_RIGHT:
+	ch = 'r';
+    default:
+	for(i = 0; rcmdlist[i].fptr; i++) {
+	    if(rcmdlist[i].key == ch) {
+		mode = (*(rcmdlist[i].fptr))(locmem->crs_ln,
+		     &headers[locmem->crs_ln -
+		     locmem->top_ln], currdirect);
+		break;
+	    }
+	    if(rcmdlist[i].key == 'h')
+		if(currmode & (MODE_ETC | MODE_DIGEST))
+		    return DONOTHING;
+	}
+    }
+    return mode;
+}
+
+void i_read(int cmdmode, char *direct, void (*dotitle)(), void (*doentry)(), onekey_t *rcmdlist, int bidcache) {
+    keeploc_t *locmem = NULL;
+    int recbase = 0, mode, ch;
+    int num = 0, entries = 0;
+    int i;
+    int jump = 0;
+    char genbuf[4];
+    char currdirect0[64];
+    int last_line0 = last_line;
+    int hit_thread0 = hit_thread;
+    fileheader_t *headers0 = headers;
+
+    strcpy(currdirect0 ,currdirect);
+#define FHSZ    sizeof(fileheader_t)
+//  Ptt:�o��headers �i�H�w��ݪO���̫�60�g��cache 
+    headers = (fileheader_t *)calloc(p_lines, FHSZ);
+    strcpy(currdirect, direct);
+    mode = NEWDIRECT;
+
+/* rocker.011018: �[�J�s��tag���� */
+    TagNum = 0;
+
+    do {
+	/* �̾� mode ��� fileheader */
+	setutmpmode(cmdmode);
+	switch(mode) {
+	case NEWDIRECT:             /* �Ĥ@�����J���ؿ� */
+	case DIRCHANGED:
+            if(bidcache>0  && !(currmode & (MODE_SELECT| MODE_DIGEST)) )
+                 last_line=getbtotal(currbid);
+            else
+                 last_line= get_num_records(currdirect, FHSZ);
+
+	    if(mode == NEWDIRECT) {
+		if(last_line == 0) {
+		    if(curredit & EDIT_ITEM) {
+			outs("�S�����~");
+			refresh();
+			goto return_i_read;
+		    } else if(curredit & EDIT_MAIL) {
+			outs("�S���ӫH");
+			refresh();
+			goto return_i_read;
+		    } else if(currmode & MODE_ETC) {
+			board_etc(); /* Kaede */
+			outmsg("�|�������䥦�峹");
+			refresh();
+		    } else if(currmode & MODE_DIGEST) {
+			board_digest(); /* Kaede */
+			outmsg("�|��������K");
+			refresh();
+		    } else if(currmode & MODE_SELECT) {
+			board_select(); /* Leeym */
+			outmsg("�S�����t�C���峹");
+			refresh();
+		    } else {
+			getdata(b_lines - 1, 0,
+				"�ݪO�s���� (P)�o���峹 (Q)���}�H[Q] ",
+				genbuf, 4, LCECHO);
+			if(genbuf[0] == 'p')
+			    do_post();
+			goto return_i_read;
+		    }
+		}
+		num = last_line - p_lines + 1;
+		locmem = getkeep(currdirect, num < 1 ? 1 : num, last_line);
+	    }
+	    recbase = -1;
+	    
+	case FULLUPDATE:
+	    (*dotitle)();
+	    
+	case PARTUPDATE:
+	    if(last_line < locmem->top_ln + p_lines) {
+		if(bidcache>0 && !(currmode & (MODE_SELECT| MODE_DIGEST)))
+		    num=getbtotal(currbid);
+                else
+		    num = get_num_records(currdirect, FHSZ);
+		
+		if(last_line != num) {
+		    last_line = num;
+		    recbase = -1;
+		}
+	    }
+	    
+	    if(last_line == 0)
+		goto return_i_read;
+	    else if(recbase != locmem->top_ln) {
+		recbase = locmem->top_ln;
+		if(recbase > last_line)	{
+		    recbase = (last_line - p_lines) >> 1;
+		    if(recbase < 1)
+			recbase = 1;
+		    locmem->top_ln = recbase;
+		}
+                if(bidcache>0 && !(currmode & (MODE_SELECT| MODE_DIGEST))
+				 && (last_line - recbase) < DIRCACHESIZE )
+		  entries = get_fileheader_cache(currbid, currdirect, headers,
+			      recbase, p_lines);
+		else
+		  entries = get_records(currdirect, headers, FHSZ, recbase,
+				      p_lines);
+	    }
+	    if(locmem->crs_ln > last_line)
+		locmem->crs_ln = last_line;
+	    move(3, 0);
+	    clrtobot();
+	case PART_REDRAW:
+	    move(3, 0);
+	    for (i = 0; i < entries; i++)
+		(*doentry) (locmem->top_ln + i, &headers[i]);
+	case READ_REDRAW:
+	    outmsg(curredit & EDIT_ITEM ?
+		   "\033[44m �p�H���� \033[30;47m �~��? \033[m" : 
+		   curredit & EDIT_MAIL ? msg_mailer : MSG_POSTER);
+	    break;
+	case READ_PREV:
+	case READ_NEXT:
+	case RELATE_PREV:
+	case RELATE_NEXT:
+	case RELATE_FIRST:
+	case POS_NEXT:
+	case 'A':
+	case 'a':
+	case '/':
+	case '?':
+	    jump = 1;
+	    break;
+	}
+
+	/* Ū����L�A�[�H�B�z�A�]�w mode */
+	if(!jump) {
+	    cursor_show(3 + locmem->crs_ln - locmem->top_ln, 0);
+	    ch = egetch();
+	    mode = DONOTHING;
+	} else
+	    ch = ' ';
+
+	if(mode == POS_NEXT) {
+	    mode = cursor_pos(locmem, locmem->crs_ln + 1, 1);
+	    if(mode == DONOTHING)
+		mode = PART_REDRAW;
+	    jump = 0;
+	} else if(ch >= '0' && ch <= '9') {
+	    if((i = search_num(ch, last_line)) != -1)
+		mode = cursor_pos(locmem, i + 1, 10);
+	} else {
+	    if(!jump)
+		mode = i_read_key(rcmdlist, locmem, ch, currbid);
+	    while(mode == READ_NEXT || mode == READ_PREV ||
+		  mode == RELATE_FIRST || mode == RELATE_NEXT ||
+		  mode == RELATE_PREV || mode == THREAD_NEXT ||
+		  mode == THREAD_PREV || mode == 'A' || mode == 'a' ||
+		  mode == '/' || mode == '?') {
+		int reload;
+		
+		if(mode == READ_NEXT || mode == READ_PREV)
+		    reload = move_cursor_line(locmem, mode);
+		else {
+		    reload = thread(locmem, mode);
+		    if(!hit_thread) {
+			mode = FULLUPDATE;
+			break;
+		    }
+		}
+		
+		if(reload == -1) {
+		    mode = FULLUPDATE;
+		    break;
+		} else if(reload) {
+		    recbase = locmem->top_ln;
+
+                    if(bidcache>0 && !(currmode &(MODE_SELECT| MODE_DIGEST))  
+				&& last_line-recbase<DIRCACHESIZE )
+                       entries = get_fileheader_cache(currbid, currdirect,
+				  headers, recbase, p_lines);
+                    else
+                       entries = get_records(currdirect, headers, FHSZ, recbase,
+                                      p_lines);        
+
+		    if(entries <= 0) {
+			last_line = -1;
+			break;
+		    }
+		}
+		num = locmem->crs_ln - locmem->top_ln;
+		if(headers[num].owner[0] != '-')
+		    mode = i_read_key(rcmdlist, locmem, ch, bidcache);
+	    }
+	}
+    } while(mode != DOQUIT);
+#undef  FHSZ
+
+ return_i_read:
+    free(headers);
+    last_line = last_line0;
+    hit_thread = hit_thread0;
+    headers = headers0;
+    strcpy(currdirect ,currdirect0);
+    return;
+}
diff --git a/mbbsd/record.c b/mbbsd/record.c
new file mode 100644
index 00000000..59bc6a75
--- /dev/null
+++ b/mbbsd/record.c
@@ -0,0 +1,536 @@
+/* $Id: record.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "proto.h"
+
+#undef  HAVE_MMAP
+#define BUFSIZE 512
+
+extern char *str_reply;
+
+static void PttLock(int fd, int size, int mode) {
+    static struct flock lock_it;
+    int ret;
+
+    lock_it.l_whence = SEEK_CUR;        /* from current point */
+    lock_it.l_start = 0;                /* -"- */
+    lock_it.l_len = size;               /* length of data */
+    lock_it.l_type = mode;              /* set exclusive/write lock */
+    lock_it.l_pid = 0;                  /* pid not actually interesting */
+    while((ret = fcntl(fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR);
+}
+
+#define safewrite       write
+
+int get_num_records(char *fpath, int size) {
+    struct stat st;
+    if(stat(fpath, &st) == -1)
+	return 0;
+    return st.st_size / size;
+}
+
+int get_sum_records(char* fpath, int size) {
+    struct stat st;
+    long ans = 0;
+    FILE* fp;
+    fileheader_t fhdr;
+    char buf[200], *p;
+
+    if(!(fp = fopen(fpath, "r")))
+	return -1;
+    
+    strcpy(buf, fpath);
+    p = strrchr(buf, '/') + 1;
+    
+    while(fread(&fhdr, size, 1, fp) == 1) {
+	strcpy(p, fhdr.filename);
+	if(stat(buf, &st) == 0 && S_ISREG(st.st_mode) && st.st_nlink == 1)
+	    ans += st.st_size;
+    }
+    fclose(fp);
+    return ans / 1024;
+}
+
+int get_record(char *fpath, void *rptr, int size, int id) {
+    int fd = -1;
+    
+    if(id < 1 || (fd = open(fpath, O_RDONLY, 0)) != -1) {
+	if(lseek(fd, (off_t)(size * (id - 1)), SEEK_SET) != -1) {
+	    if(read(fd, rptr, size) == size) {
+		close(fd);
+		return 0;
+	    }
+	}
+	close(fd);
+    }
+    return -1;
+}
+
+int get_records(char *fpath, void *rptr, int size, int id, int number) {
+    int fd;
+    
+    if(id < 1 || (fd = open(fpath, O_RDONLY, 0)) == -1)
+	return -1;
+
+    if(lseek(fd, (off_t)(size * (id - 1)), SEEK_SET) == -1) {
+	close(fd);
+	return 0;
+    }
+    if((id = read(fd, rptr, size * number)) == -1) {
+	close(fd);
+	return -1;
+    }
+    close(fd);
+    return id / size;
+}
+
+int substitute_record(char *fpath, void *rptr, int size, int id) {
+    int fd;
+
+    if(id < 1 || (fd = open(fpath, O_WRONLY | O_CREAT, 0644)) == -1)
+	return -1;
+    
+    lseek(fd, (off_t) (size * (id - 1)), SEEK_SET);
+    PttLock(fd, size, F_WRLCK);
+    safewrite(fd, rptr, size);
+    PttLock(fd, size, F_UNLCK);
+    close(fd);
+    
+    return 0;
+}
+
+/* rocker.011022: �קKlock�ɶ}�Үɤ����`�_�u,�y���ä[lock */
+static int
+force_open (char *fname)
+{
+  int fd;
+  time_t expire;
+
+  expire = time(NULL) - 3600; /* lock �s�b�W�L�@�Ӥp�ɴN�O�����D! */
+  
+  if (dasht (fname) < expire) return -1;
+  unlink(fname);
+  fd = open (fname, O_WRONLY|O_TRUNC, 0644);
+
+  return fd;
+}
+
+
+#if !defined(_BBS_UTIL_C_)
+/* new/old/lock file processing */
+typedef struct nol_t {
+    char newfn[256];
+    char oldfn[256];
+    char lockfn[256];
+} nol_t;
+
+static void nolfilename(nol_t *n, char *fpath) {
+    sprintf(n->newfn, "%s.new", fpath);
+    sprintf(n->oldfn, "%s.old", fpath);
+    sprintf(n->lockfn, "%s.lock", fpath);
+}
+
+int delete_record(char fpath[], int size, int id) {
+    nol_t my;
+    char abuf[BUFSIZE];
+    int fdr, fdw, fd;
+    int count;
+    
+    nolfilename(&my, fpath);
+    if((fd = open(my.lockfn, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1)
+	return -1;
+
+    flock(fd, LOCK_EX);
+
+    if((fdr = open(fpath, O_RDONLY, 0)) == -1) {
+	move(10,10);
+	outs("delete_record failed!!! (open)");
+	pressanykey();
+	flock(fd, LOCK_UN);
+	close(fd);
+	return -1;
+    }
+
+    if(
+   	((fdw = open(my.newfn, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1) && 
+       	((fdw = force_open (my.newfn)) == -1)) {
+	flock(fd, LOCK_UN);
+	close(fd);
+	close(fdr);
+	return -1;
+    }
+    count = 1;
+    while(read(fdr, abuf, size) == size) {
+	if(id != count++ && (safewrite(fdw, abuf, size) == -1)) {
+	    unlink(my.newfn);
+	    close(fdr);
+	    close(fdw);
+	    flock(fd, LOCK_UN);
+	    close(fd);
+	    return -1;
+	}
+    }
+    close(fdr);
+    close(fdw);
+    if(Rename(fpath, my.oldfn) == -1 || Rename(my.newfn, fpath) == -1) {
+	flock(fd, LOCK_UN);
+	close(fd);
+	return -1;
+    }
+    flock(fd, LOCK_UN);
+    close(fd);
+    return 0;
+}
+
+static char *title_body(char *title) {
+    if(!strncasecmp(title, str_reply, 3)) {
+	title += 3;
+	if(*title == ' ')
+	    title++;
+    }
+    return title;
+}
+
+int delete_range(char *fpath, int id1, int id2) {
+    fileheader_t fhdr;
+    nol_t my;
+    char fullpath[STRLEN], *t;
+    int fdr, fdw, fd;
+    int count;
+    extern int Tagger();
+    
+    nolfilename(&my, fpath);
+    
+    if((fd = open(my.lockfn, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1)
+	return -1;
+
+    flock(fd, LOCK_EX);
+
+    if((fdr = open(fpath, O_RDONLY, 0)) == -1) {
+	flock(fd, LOCK_UN);
+	close(fd);
+	return -1;
+    }
+    
+    if(
+	((fdw = open(my.newfn, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1) &&
+	((fdw = force_open (my.newfn)) == -1)) {
+	close(fdr);
+	flock(fd, LOCK_UN);
+	close(fd);
+	return -1;
+    }
+    
+    count = 1;
+    strcpy(fullpath, fpath);
+    t = strrchr(fullpath, '/') + 1;
+
+    while(read(fdr, &fhdr, sizeof(fileheader_t)) == sizeof(fileheader_t)) 
+    {
+	strcpy(t, fhdr.filename);
+
+/* rocker.011018: add new tag delete */
+      if (
+	(fhdr.filemode & FILE_MARKED) ||        /* �аO */
+        (fhdr.filemode & FILE_DIGEST)  ||        /* ��K */
+        (id1 && (count < id1 || count > id2)) ||  /* range */
+        (!id1 && Tagger(atoi (t + 2), count, TAG_NIN))) /* TagList */
+	 {
+	    if((safewrite(fdw, &fhdr, sizeof(fileheader_t)) == -1)) {
+		close(fdr);
+		close(fdw);
+		unlink(my.newfn);
+		flock(fd, LOCK_UN);
+		close(fd);
+		return -1;
+	    }
+	  }
+	 else
+          {
+            //if(dashd(fullpath))
+	        unlink(fullpath);
+	  }
+	++count;
+    }
+    close(fdr);
+    close(fdw);
+    if(Rename(fpath, my.oldfn) == -1 || Rename(my.newfn, fpath) == -1) {
+	flock(fd, LOCK_UN);
+	close(fd);
+	return -1;
+    }
+    flock(fd, LOCK_UN);
+    close(fd);
+    return 0;
+}
+
+int search_rec(char* dirname, int (*filecheck)()) {
+    fileheader_t fhdr;
+    FILE *fp;
+    int ans = 0;
+    
+    if(!(fp = fopen(dirname, "r")))
+	return 0;
+    
+    while(fread(&fhdr, sizeof(fhdr), 1, fp)) {
+	ans++;
+	if((*filecheck) (&fhdr)) {
+	    fclose(fp);
+	    return ans;
+	}
+    }
+    fclose(fp);
+    return 0;
+}
+
+int delete_files(char* dirname, int (*filecheck)(), int record) {
+    fileheader_t fhdr;
+    FILE *fp, *fptmp;
+    int ans = 0;
+    char tmpfname[128];
+    char genbuf[256];
+    char deleted[256];
+    fileheader_t delfh;
+    char deletedDIR[] = "boards/deleted/.DIR";
+    
+    strcpy(deleted, "boards/deleted");
+  
+    if(!(fp = fopen(dirname, "r")))
+	return ans;
+    
+    strcpy(tmpfname, dirname);
+    strcat(tmpfname, "_tmp");
+    
+    if(!(fptmp = fopen(tmpfname, "w"))) {
+	fclose(fp);
+	return ans;
+    }
+    
+    while(fread(&fhdr, sizeof(fhdr), 1, fp)){
+	if((*filecheck)(&fhdr)) {
+	    ans++;
+	    setdirpath(genbuf, dirname, fhdr.filename);
+	    if (record){
+		deleted[14] = '\0';
+		stampfile(deleted, &delfh);
+		strcpy(delfh.owner, fhdr.owner);
+		strcpy(delfh.title, fhdr.title);
+		Link(genbuf, deleted);
+		append_record(deletedDIR, &delfh, sizeof(delfh));
+	    }
+	    unlink(genbuf);
+	} else
+	    fwrite(&fhdr, sizeof(fhdr), 1, fptmp);
+    }
+  
+    fclose(fp);
+    fclose(fptmp);
+    unlink(dirname);
+    Rename(tmpfname, dirname);
+  
+    return ans;
+}
+
+int delete_file(char *dirname, int size, int ent, int (*filecheck)()) {
+    char abuf[BUFSIZE];
+    int fd;
+    struct stat st;
+    long numents;
+    
+    if(ent < 1 || (fd = open(dirname, O_RDWR)) == -1)
+	return -1;
+    flock(fd, LOCK_EX);
+    fstat(fd, &st);
+    numents = ((long) st.st_size) / size;
+    if(((long) st.st_size) % size)
+	fprintf(stderr, "align err\n");
+    if(lseek(fd, (off_t) size * (ent - 1), SEEK_SET) != -1) {
+	if(read(fd, abuf, size) == size){
+	    if((*filecheck) (abuf)) {
+		int i;
+		
+		for(i = ent; i < numents; i++) {
+		    if(lseek(fd, (off_t)((i) * size), SEEK_SET) == -1 ||
+		       read(fd, abuf, size) != size 		     ||
+		       lseek(fd, (off_t)(i - 1) * size, SEEK_SET) == -1)
+			break;
+		    if(safewrite(fd, abuf, size) != size)
+			break;
+		}
+		ftruncate(fd, (off_t) size * (numents - 1));
+		flock(fd, LOCK_UN);
+		close(fd);
+		return 0;
+	    }
+	}
+    }
+    lseek(fd, 0, SEEK_SET);
+    ent = 1;
+    while(read(fd, abuf, size) == size) {
+	if((*filecheck)(abuf)) {
+	    int i;
+	    
+	    for(i = ent; i < numents; i++) {
+		if(lseek(fd, (off_t) (i + 1) * size, SEEK_SET) == -1 ||
+		   read(fd, abuf, size) != size ||
+		   lseek(fd, (off_t) (i) * size, SEEK_SET) == -1 ||
+		   safewrite(fd, abuf, size) != size)
+		    break;
+	    }
+	    ftruncate(fd, (off_t) size * (numents - 1));
+	    flock(fd, LOCK_UN);
+	    close(fd);
+	    return 0;
+	}
+	ent++;
+    }
+    flock(fd, LOCK_UN);
+    close(fd);
+    return -1;
+}
+
+#endif                          /* !defined(_BBS_UTIL_C_) */
+
+int apply_record(char *fpath, int (*fptr)(), int size) {
+    char abuf[BUFSIZE];
+    FILE* fp;
+    
+    if(!(fp = fopen(fpath, "r")))
+	return -1;
+    
+    while(fread(abuf, 1, size, fp) == size)
+	if((*fptr) (abuf) == QUIT) {
+	    fclose(fp);
+	    return QUIT;
+	}
+    fclose(fp);
+    return 0;
+}
+
+/* mail / post �ɡA�̾ڮɶ��إ��ɮסA�[�W�l�W */
+int stampfile(char *fpath, fileheader_t *fh) {
+    register char *ip = fpath;
+    time_t dtime;
+    struct tm *ptime;
+    int fp = 0;
+
+    if(access(fpath, X_OK | R_OK | W_OK))
+	mkdir(fpath, 0755);
+
+    time(&dtime);
+    while (*(++ip));
+    *ip++ = '/';
+    do {
+	sprintf(ip, "M.%ld.A", ++dtime );
+	if(fp == -1 && errno != EEXIST)
+	    return -1;
+    } while((fp = open(fpath, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1);
+    close(fp);
+    memset(fh, 0, sizeof(fileheader_t));
+    strcpy(fh->filename, ip);
+    ptime = localtime(&dtime);
+    sprintf(fh->date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
+    return 0;
+}
+
+void stampdir(char *fpath, fileheader_t *fh) {
+    register char *ip = fpath;
+    time_t dtime;
+    struct tm *ptime;
+    
+    if(access(fpath, X_OK | R_OK | W_OK))
+	mkdir(fpath, 0755);
+    
+    time(&dtime);
+    while(*(++ip));
+    *ip++ = '/';
+    do {
+	sprintf(ip, "D%lX", ++dtime & 07777);
+    } while(mkdir(fpath, 0755) == -1);
+    memset(fh, 0, sizeof(fileheader_t));
+    strcpy(fh->filename, ip);
+    ptime = localtime(&dtime);
+    sprintf(fh->date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
+}
+
+void stamplink(char *fpath, fileheader_t *fh) {
+    register char *ip = fpath;
+    time_t dtime;
+    struct tm *ptime;
+
+    if(access(fpath, X_OK | R_OK | W_OK))
+	mkdir(fpath, 0755);
+
+    time(&dtime);
+    while(*(++ip));
+    *ip++ = '/';
+    do {
+	sprintf(ip, "S%lX", ++dtime );
+    } while(symlink("temp", fpath) == -1);
+    memset(fh, 0, sizeof(fileheader_t));
+    strcpy(fh->filename, ip);
+    ptime = localtime(&dtime);
+    sprintf(fh->date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
+}
+
+int do_append(char *fpath, fileheader_t *record, int size) {
+    int fd;
+    
+    if((fd = open(fpath, O_WRONLY | O_CREAT, 0644)) == -1) {
+	perror("open");
+	return -1;
+    }
+    flock(fd, LOCK_EX);
+    lseek(fd, 0, SEEK_END);
+    
+    safewrite(fd, record, size);
+    
+    flock(fd, LOCK_UN);
+    close(fd);
+    return 0;
+}
+
+int append_record(char *fpath, fileheader_t *record, int size) {
+#if !defined(_BBS_UTIL_C_)
+    int m,n;
+    if(get_num_records(fpath, sizeof(fileheader_t)) <= MAX_KEEPMAIL * 2) {
+	FILE *fp;
+	char buf[512],address[200];
+
+	for(n = strlen(fpath) - 1 ; fpath[n] != '/' && n > 0; n--);
+	strncpy(buf, fpath, n + 1);
+	buf[n + 1] = 0;
+	for(m = strlen(buf) - 2 ; buf[m] != '/' && m > 0 ; m--);
+	strcat(buf, ".forward");
+	if((fp = fopen(buf,"r"))) {
+	    fscanf(fp,"%s",address);
+	    fclose(fp);
+	    if(buf[0] != 0 && buf[0] != ' ') {
+		buf[n + 1] = 0;
+		strcat(buf, record->filename);
+		do_append(fpath,record,size);
+#ifndef  USE_BSMTP
+		bbs_sendmail(buf,record->title,address);
+#else
+		bsmtp(buf, record->title, address, 0);
+#endif
+		return 0;
+	    }
+	}
+    }
+#endif 
+    
+    do_append(fpath,record,size);
+    
+    return 0;
+}
diff --git a/mbbsd/register.c b/mbbsd/register.c
new file mode 100644
index 00000000..e9c25be5
--- /dev/null
+++ b/mbbsd/register.c
@@ -0,0 +1,339 @@
+/* $Id: register.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#define _XOPEN_SOURCE
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "proto.h"
+
+extern char *str_new;
+extern char *msg_uid;
+extern int t_lines, t_columns;  /* Screen size / width */
+extern char *str_mail_address;
+
+/* password encryption */
+static char pwbuf[14];
+
+char *genpasswd(char *pw) {
+    if(pw[0]) {
+	char saltc[2], c;
+	int i;
+	
+	i = 9 * getpid();
+	saltc[0] = i & 077;
+	saltc[1] = (i >> 6) & 077;
+	
+	for(i = 0; i < 2; i++) {
+	    c = saltc[i] + '.';
+	    if(c > '9')
+		c += 7;
+	    if(c > 'Z')
+		c += 6;
+	    saltc[i] = c;
+	}
+	strcpy(pwbuf, pw);
+	return crypt(pwbuf, saltc);
+    }
+    return "";
+}
+
+int checkpasswd(char *passwd, char *test) {
+    char *pw;
+    
+    strncpy(pwbuf, test, 14);
+    pw = crypt(pwbuf, passwd);
+    return (!strncmp(pw, passwd, 14));
+}
+
+/* �ˬd user ���U���p */
+int bad_user_id(char *userid) {
+    int len, i;
+    len = strlen(userid);
+
+    if(len < 2)
+	return 1;
+
+    if (not_alpha(userid[0]))
+	return 1;
+    for (i=1; i<len; i++)	//DickG:�ץ��F�u��� userid �Ĥ@�Ӧr���� bug
+	if(not_alnum(userid[i]))
+            return 1;
+    
+    if(strcasecmp(userid, str_new) == 0)
+	return 1;
+    
+    /*   while((ch = *(++userid)))
+	 if(not_alnum(ch))
+	 return 1;*/
+    return 0;
+}
+
+/* -------------------------------- */
+/* New policy for allocate new user */
+/* (a) is the worst user currently  */
+/* (b) is the object to be compared */
+/* -------------------------------- */
+static int compute_user_value(userec_t *urec, time_t clock) {
+    int value;
+    
+    /* if (urec) has XEMPT permission, don't kick it */
+    if((urec->userid[0] == '\0') || (urec->userlevel & PERM_XEMPT)
+       /*|| (urec->userlevel & PERM_LOGINOK)*/
+       || !strcmp(STR_GUEST,urec->userid))
+	return 999999;
+    value = (clock - urec->lastlogin) / 60;       /* minutes */
+    
+    /* new user should register in 30 mins */
+    if(strcmp(urec->userid, str_new) == 0)
+	return 30 - value;
+#if 0    
+    if (!urec->numlogins)         /* �� login ���\�̡A���O�d */
+	return -1;
+    if (urec->numlogins <= 3)     /* #login �֩�T�̡A�O�d 20 �� */
+	return 20 * 24 * 60 - value;
+#endif
+    /* ���������U�̡A�O�d 15 �� */
+    /* �@�뱡�p�A�O�d 120 �� */
+    return (urec->userlevel & PERM_LOGINOK ? 120 : 15) * 24 * 60 - value;
+}
+
+int check_and_expire_account(int uid,userec_t *urec)
+{
+    userec_t zerorec;
+    time_t now=time(NULL); 
+    char genbuf[200],genbuf2[200];
+    int val;
+    if((val = compute_user_value(urec, now)) < 0) {
+        sprintf(genbuf, "#%d %-12s %15.15s %d %d %d",
+              uid, urec->userid, ctime(&(urec->lastlogin)) + 4,
+              urec->numlogins, urec->numposts, val);
+        if(val > -1 * 60 * 24 * 365) {
+	    memset(&zerorec, 0, sizeof(zerorec));
+            log_usies("CLEAN", genbuf);
+            sprintf(genbuf, "home/%c/%s", urec->userid[0],
+                            urec->userid);
+            sprintf(genbuf2, "tmp/%s", urec->userid);
+            if(dashd(genbuf) && Rename(genbuf, genbuf2)) {
+                     sprintf(genbuf, "/bin/rm -fr home/%c/%s >/dev/null 2>&1",
+                                   urec->userid[0],urec->userid);
+		     system(genbuf);
+	 	  } 
+	    passwd_update(uid, &zerorec);
+	    remove_from_uhash(uid - 1);
+	    add_to_uhash(uid - 1, "");
+          }
+        else
+          {
+		   val=0;
+                   log_usies("DATED", genbuf);
+          }
+	}
+    return val;
+}
+
+extern char *fn_passwd;
+
+int getnewuserid() {
+    char genbuf[50];
+    static char *fn_fresh = ".fresh";
+    userec_t utmp,zerorec;
+    time_t clock;
+    struct stat st;
+    int fd, i;
+    
+    memset(&zerorec, 0, sizeof(zerorec));
+    clock = time(NULL);
+    
+    /* Lazy method : ����M�w�g�M�����L���b�� */
+    if((i = searchnewuser(0)) == 0) {
+	/* �C 1 �Ӥp�ɡA�M�z user �b���@�� */
+	if((stat(fn_fresh, &st) == -1) || (st.st_mtime < clock - 3600)) {
+	    if((fd = open(fn_fresh, O_RDWR | O_CREAT, 0600)) == -1)
+		return -1;
+	    write(fd, ctime(&clock), 25);
+	    close(fd);
+	    log_usies("CLEAN", "dated users");
+	    
+	    fprintf(stdout, "�M��s�b����, �еy�ݤ���...\n\r");
+	    
+	    if((fd = open(fn_passwd, O_RDWR | O_CREAT, 0600)) == -1)
+		return -1;
+	    
+	    /* ����o������n�q 2 �}�l... Ptt:�]��SYSOP�b1 */
+ 	    for(i = 2; i <= MAX_USERS; i++) {
+		passwd_query(i, &utmp);
+		check_and_expire_account(i,&utmp);
+	    }
+	}
+    }
+    
+    passwd_lock();
+    i = searchnewuser(1);
+    if((i <= 0) || (i > MAX_USERS)) {
+	passwd_unlock();
+	if(more("etc/user_full", NA) == -1)
+	    fprintf(stdout, "��p�A�ϥΪ̱b���w�g���F�A�L�k���U�s���b��\n\r");
+	safe_sleep(2);
+	exit(1);
+    }
+    
+    sprintf(genbuf, "uid %d", i);
+    log_usies("APPLY", genbuf);
+    
+    strcpy(zerorec.userid, str_new);
+    zerorec.lastlogin = clock;
+    passwd_update(i, &zerorec);
+    setuserid(i, zerorec.userid);
+    passwd_unlock();
+    return i;
+}
+
+void new_register() {
+    extern userec_t xuser;
+    userec_t newuser;
+    char passbuf[STRLEN];
+    int allocid, try, id;
+
+    memset(&newuser, 0, sizeof(newuser));
+    more("etc/register", NA);
+    try = 0;
+    while(1) {
+	if(++try >= 6) {
+	    outs("\n�z���տ��~����J�Ӧh�A�ФU���A�ӧa\n");
+	    refresh();
+	    
+	    pressanykey();
+	    oflush();
+	    exit(1);
+	}
+	getdata(17, 0, msg_uid, newuser.userid, IDLEN + 1, DOECHO);
+
+	if(bad_user_id(newuser.userid))
+	    outs("�L�k�����o�ӥN���A�Шϥέ^��r���A�åB���n�]�t�Ů�\n");
+	else if ((id=getuser(newuser.userid)) &&
+		  (id=check_and_expire_account(id,&xuser))>=0)
+	   {
+	    if(id==999999)
+		outs("���N���w�g���H�ϥ� �O��������");
+	    else
+		{
+                 sprintf(passbuf,"���N���w�g���H�ϥ� �٦�%d�Ѥ~�L�� \n",id/(60*24));
+	         outs(passbuf);
+	        }
+	   }
+	else
+	    break;
+    }
+
+    try = 0;
+    while(1) {
+	if(++try >= 6) {
+	    outs("\n�z���տ��~����J�Ӧh�A�ФU���A�ӧa\n");
+	    refresh();
+	    
+	    pressanykey();
+	    oflush();
+	    exit(1);
+	}
+	if((getdata(19, 0, "�г]�w�K�X�G", passbuf, PASSLEN, NOECHO) < 3) ||
+	   !strcmp(passbuf, newuser.userid)) {
+	    outs("�K�X��²��A���D�J�I�A�ܤ֭n 4 �Ӧr�A�Э��s��J\n");
+	    continue;
+	}
+	strncpy(newuser.passwd, passbuf, PASSLEN);
+	getdata(20, 0, "���ˬd�K�X�G", passbuf, PASSLEN, NOECHO);
+	if(strncmp(passbuf, newuser.passwd, PASSLEN)) {
+	    outs("�K�X��J���~, ���s��J�K�X.\n");
+	    continue;
+	}
+	passbuf[8] = '\0';
+	strncpy(newuser.passwd, genpasswd(passbuf), PASSLEN);
+	break;
+    }
+    newuser.userlevel = PERM_DEFAULT;
+    newuser.uflag = COLOR_FLAG | BRDSORT_FLAG | MOVIE_FLAG;
+    newuser.firstlogin = newuser.lastlogin = time(NULL);
+    newuser.money = 0;
+    newuser.pager = 1;
+    allocid = getnewuserid();
+    if(allocid > MAX_USERS || allocid <= 0) {
+	fprintf(stderr, "�����H�f�w�F���M�I\n");
+	exit(1);
+    }
+    
+    if(passwd_update(allocid, &newuser) == -1) {
+	fprintf(stderr, "�Ⱥ��F�A�A���I\n");
+	exit(1);
+    }
+    setuserid(allocid, newuser.userid);
+    if(!dosearchuser(newuser.userid)) {
+	fprintf(stderr, "�L�k�إ߱b��\n");
+	exit(1);
+    }
+}
+
+extern userec_t cuser;
+
+void check_register() {
+    char *ptr = NULL;
+    
+    stand_title("�иԲӶ�g�ӤH���");
+    
+    while(strlen(cuser.username) < 2)
+	getdata(2, 0, "�︹�ʺ١G", cuser.username, 24, DOECHO);
+
+    for(ptr = cuser.username; *ptr; ptr++) {
+	if (*ptr == 9)              /* TAB convert */
+	    *ptr = ' ';
+    }
+    while(strlen(cuser.realname) < 4)
+	getdata(4, 0, "�u��m�W�G", cuser.realname, 20, DOECHO);
+    
+    while(strlen(cuser.address) < 8)
+	getdata(6, 0, "�p���a�}�G", cuser.address, 50, DOECHO);
+
+    
+    if(!strchr(cuser.email, '@')) {
+	bell();
+	move(t_lines - 4, 0);
+	prints("�� ���F�z���v�q�A�ж�g�u�ꪺ E-mail address�A "
+	       "�H��T�{�դU�����A\n"
+	       "�榡�� \033[44muser@domain_name\033[0m �� \033[44muser"
+	       "@\\[ip_number\\]\033[0m�C\n\n"
+	       "�� �p�G�z�u���S�� E-mail�A������ [return] �Y�i�C");
+
+	do {
+	    getdata(8, 0, "�q�l�H�c�G", cuser.email, 50, DOECHO);
+	    if(!cuser.email[0])
+		sprintf(cuser.email, "%s%s", cuser.userid, str_mail_address);
+	} while(!strchr(cuser.email, '@'));
+
+    }
+    if(!HAS_PERM(PERM_SYSOP) && !HAS_PERM(PERM_LOGINOK)) {
+	/* �^�йL�����{�ҫH��A�δ��g E-mail post �L */
+	clear();
+	move(9,3);
+	prints("�иԶ�g\033[32m���U�ӽг�\033[m�A"
+	       "�q�i�����H��o�i���ϥ��v�O�C\n\n\n\n");
+	u_register();
+    }
+
+#ifdef NEWUSER_LIMIT
+    if(!(cuser.userlevel & PERM_LOGINOK) && !HAS_PERM(PERM_SYSOP)) {
+	if(cuser.lastlogin - cuser.firstlogin < 3 * 86400)
+	    cuser.userlevel &= ~PERM_POST;
+	more("etc/newuser", YEA);
+    }
+#endif
+}
diff --git a/mbbsd/screen.c b/mbbsd/screen.c
new file mode 100644
index 00000000..46ad5b38
--- /dev/null
+++ b/mbbsd/screen.c
@@ -0,0 +1,559 @@
+/* $Id: screen.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+extern int t_lines, t_columns;  /* Screen size / width */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern int p_lines;             /* a Page of Screen line numbers: tlines-4 */
+extern int showansi;
+
+extern char *clearbuf;
+extern char *cleolbuf;
+extern char *scrollrev;
+extern char *strtstandout;
+extern char *endstandout;
+extern int clearbuflen;
+extern int cleolbuflen;
+extern int scrollrevlen;
+extern int strtstandoutlen;
+extern int endstandoutlen;
+extern int automargins;
+#ifdef SUPPORT_GB    
+static int current_font_type=TYPE_BIG5;
+static int gbinited=0;
+#endif
+#define SCR_WIDTH       80 
+#define o_clear()     output(clearbuf,clearbuflen)
+#define o_cleol()     output(cleolbuf,cleolbuflen)
+#define o_scrollrev() output(scrollrev,scrollrevlen)
+#define o_standup()   output(strtstandout,strtstandoutlen)
+#define o_standdown() output(endstandout,endstandoutlen)
+
+unsigned char scr_lns, scr_cols;
+static unsigned char cur_ln = 0, cur_col = 0;
+static unsigned char docls, downfrom = 0;
+static unsigned char standing = NA;
+static char roll = 0;
+static int scrollcnt, tc_col, tc_line;
+
+screenline_t *big_picture = NULL;
+
+#define MODIFIED (1)            /* if line has been modifed, screen output */
+#define STANDOUT (2)            /* if this line has a standout region */
+
+int tputs(const char *str, int affcnt, int (*putc)(int));
+
+void initscr() {
+    if(!big_picture) {
+	scr_lns = t_lines;
+	scr_cols = t_columns = ANSILINELEN;
+	/* scr_cols = MIN(t_columns, ANSILINELEN); */
+	big_picture = (screenline_t *) calloc(scr_lns, sizeof(screenline_t));
+	docls = YEA;
+    }
+}
+
+void move(int y, int x) {
+    cur_col = x;
+    cur_ln = y;
+}
+
+void getyx(int *y, int *x) {
+    *y = cur_ln;
+    *x = cur_col;
+}
+
+static void rel_move(int was_col, int was_ln, int new_col, int new_ln) {
+    if(new_ln >= t_lines || new_col >= t_columns)
+	return;
+
+    tc_col = new_col;
+    tc_line = new_ln;
+    if(new_col == 0) {
+	if(new_ln == was_ln) {
+	    if(was_col)
+		ochar('\r');
+	    return;
+	} else if(new_ln == was_ln + 1) {
+	    ochar('\n');
+	    if(was_col)
+		ochar('\r');
+	    return;
+	}
+    }
+
+    if(new_ln == was_ln) {
+	if(was_col == new_col)
+	    return;
+
+	if(new_col == was_col - 1) {
+	    ochar(Ctrl('H'));
+	    return;
+	}
+    }
+    do_move(new_col, new_ln);
+}
+
+static void standoutput(char *buf, int ds, int de, int sso, int eso) {
+    int st_start, st_end;
+    
+    if(eso <= ds || sso >= de) {
+	output(buf + ds, de - ds);
+    } else {
+	st_start = MAX(sso, ds);
+	st_end = MIN(eso, de);
+	if(sso > ds)
+	    output(buf + ds, sso - ds);
+	o_standup();
+	output(buf + st_start, st_end - st_start);
+	o_standdown();
+	if(de > eso)
+	    output(buf + eso, de - eso);
+    }
+}
+
+void redoscr() {
+    register screenline_t *bp;
+    register int i, j, len;
+
+    o_clear();
+    for(tc_col = tc_line = i = 0, j = roll; i < scr_lns; i++, j++) {
+	if(j >= scr_lns)
+	    j = 0;
+	bp = &big_picture[j];
+	if((len = bp->len)) {
+	    rel_move(tc_col, tc_line, 0, i);
+	    if(bp->mode & STANDOUT)
+		standoutput(bp->data, 0, len, bp->sso, bp->eso);
+	    else
+		output(bp->data, len);
+	    tc_col += len;
+	    if(tc_col >= t_columns) {
+		if (automargins)
+		    tc_col = t_columns - 1;
+		else {
+		    tc_col -= t_columns;
+		    tc_line++;
+		    if(tc_line >= t_lines)
+			tc_line = b_lines;
+		}
+	    }
+	    bp->mode &= ~(MODIFIED);
+	    bp->oldlen = len;
+	}
+    }
+    rel_move(tc_col, tc_line, cur_col, cur_ln);
+    docls = scrollcnt = 0;
+    oflush();
+}
+
+void refresh() {
+    register screenline_t *bp = big_picture;
+    register int i, j, len;
+    extern int automargins;
+    extern int scrollrevlen;
+    if(num_in_buf())
+	return;
+
+    if((docls) || (abs(scrollcnt) >= (scr_lns - 3))) {
+	redoscr();
+	return;
+    }
+
+    if(scrollcnt < 0) {
+	if(!scrollrevlen) {
+	    redoscr();
+	    return;
+	}
+	rel_move(tc_col, tc_line, 0, 0);
+	do {
+	    o_scrollrev();
+	} while(++scrollcnt);
+    } else if (scrollcnt > 0) {
+	rel_move(tc_col, tc_line, 0, b_lines);
+	do {
+	    ochar('\n');
+	} while(--scrollcnt);
+    }
+
+    for(i = 0, j = roll; i < scr_lns; i++, j++) {
+	if(j >= scr_lns)
+	    j = 0;
+	bp = &big_picture[j];
+	len = bp->len;
+	if(bp->mode & MODIFIED && bp->smod < len) {
+	    bp->mode &= ~(MODIFIED);
+	    if(bp->emod >= len)
+		bp->emod = len - 1;
+	    rel_move(tc_col, tc_line, bp->smod, i);
+
+	    if(bp->mode & STANDOUT)
+		standoutput(bp->data, bp->smod, bp->emod + 1,
+			    bp->sso, bp->eso);
+	    else
+		output(&bp->data[bp->smod], bp->emod - bp->smod + 1);
+	    tc_col = bp->emod + 1;
+	    if(tc_col >= t_columns) {
+		if(automargins)	{
+		    tc_col -= t_columns;
+		    if(++tc_line >= t_lines)
+			tc_line = b_lines;
+		} else
+		    tc_col = t_columns - 1;
+	    }
+	}
+	
+	if(bp->oldlen > len) {
+	    rel_move(tc_col, tc_line, len, i);
+	    o_cleol();
+	}
+	bp->oldlen = len;
+
+    }
+
+    rel_move(tc_col, tc_line, cur_col, cur_ln);
+
+    oflush();
+}
+
+void clear() {
+    register screenline_t *slp;
+
+    register int i;
+
+    docls = YEA;
+    cur_col = cur_ln = roll = downfrom = i = 0;
+    do {
+	slp = &big_picture[i];
+	slp->mode = slp->len = slp->oldlen = 0;
+    } while(++i < scr_lns);
+}
+
+void clrtoeol() {
+    register screenline_t *slp;
+    register int ln;
+
+    standing = NA;
+    if((ln = cur_ln + roll) >= scr_lns)
+	ln -= scr_lns;
+    slp = &big_picture[ln];
+    if(cur_col <= slp->sso)
+	slp->mode &= ~STANDOUT;
+
+    if(cur_col > slp->oldlen) {
+	for(ln = slp->len; ln <= cur_col; ln++)
+	    slp->data[ln] = ' ';
+    }
+
+    if(cur_col < slp->oldlen) {
+	for(ln = slp->len; ln >= cur_col; ln--)
+	    slp->data[ln] = ' ';
+    }
+	
+    slp->len = cur_col;
+}
+
+void clrtoline(int line) {
+    register screenline_t *slp;
+    register int i, j;
+
+    for(i = cur_ln, j = i + roll; i < line; i++, j++) {
+	if(j >= scr_lns)
+	    j -= scr_lns;
+	slp = &big_picture[j];
+	slp->mode = slp->len = 0;
+	if(slp->oldlen)
+	    slp->oldlen = 255;
+    }
+}     
+
+void clrtobot() {
+    clrtoline(scr_lns);
+}
+
+void outch(unsigned char c) {
+    register screenline_t *slp;
+    register int i;
+
+    if((i = cur_ln + roll) >= scr_lns)
+	i -= scr_lns;
+    slp = &big_picture[i];
+ 
+    if(c == '\n' || c == '\r') {
+	if(standing) {
+	    slp->eso = MAX(slp->eso, cur_col);
+	    standing = NA;
+	}
+	if((i = cur_col - slp->len) > 0)
+	    memset(&slp->data[slp->len], ' ', i + 1);
+	slp->len = cur_col;
+	cur_col = 0;
+	if(cur_ln < scr_lns)
+	    cur_ln++;
+	return;
+    }
+/*
+  else if(c != '\033' && !isprint2(c))
+  {
+  c = '*'; //substitute a '*' for non-printable 
+  }
+*/
+    if(cur_col >= slp->len) {
+	for(i = slp->len; i < cur_col; i++)
+	    slp->data[i] = ' ';
+	slp->data[cur_col] = '\0';
+	slp->len = cur_col + 1;
+    }
+
+    if(slp->data[cur_col] != c) {
+	slp->data[cur_col] = c;
+	if((slp->mode & MODIFIED) != MODIFIED)
+	    slp->smod = slp->emod = cur_col;
+	slp->mode |= MODIFIED;
+	if(cur_col > slp->emod)
+	    slp->emod = cur_col;
+	if(cur_col < slp->smod)
+	    slp->smod = cur_col;
+    }
+
+    if (++cur_col >= scr_cols)
+    {
+	if (standing && (slp->mode & STANDOUT))
+	{
+	    standing = 0;
+	    slp->eso = MAX(slp->eso, cur_col);
+	}
+	cur_col = 0;
+	if (cur_ln < scr_lns)
+	    cur_ln++;
+    }             
+
+}
+
+static void parsecolor(char *buf) {
+    char *val;
+    char data[24];
+    
+    data[0] = '\0';
+    val = (char *)strtok(buf, ";");
+
+    while(val) {
+	if(atoi(val) < 30) {
+	    if(data[0])
+		strcat(data, ";");
+	    strcat(data, val);
+	}
+	val = (char *) strtok(NULL, ";");
+    }
+    strcpy(buf, data);
+}
+
+#define NORMAL (00)
+#define ESCAPE (01)
+#define VTKEYS (02)
+
+void outc(unsigned char ch) {
+    if(showansi)
+	outch(ch);
+    else {
+	static char buf[24];
+	static int p = 0;
+	static int mode = NORMAL;
+	int i;
+
+	switch(mode) {
+	case NORMAL:
+	    if(ch == '\033')
+		mode = ESCAPE;
+	    else
+		outch(ch);
+	    return;
+	case ESCAPE:
+	    if(ch == '[')
+		mode = VTKEYS;
+	    else {
+		mode = NORMAL;
+		outch('');
+		outch(ch);
+	    }
+	    return;
+	case VTKEYS:
+	    if(ch == 'm') {
+		buf[p++] = '\0';
+		parsecolor(buf);
+	    } else if((p < 24) && (not_alpha(ch))) {
+		buf[p++] = ch;
+		return;
+	    }
+	    if(buf[0]) {
+		outch('');
+		outch('[');
+
+		for(i = 0; (p = buf[i]); i++)
+		    outch(p);
+		outch(ch);
+	    }
+	    p = 0;
+	    mode = NORMAL;
+	}
+    }
+}
+
+static void do_outs(char *str) {
+    while(*str)
+    {
+        outc(*str++);
+    }
+}          
+#ifdef SUPPORT_GB    
+static void gb_init()
+{
+    if(current_font_type == TYPE_GB)
+    {
+	hc_readtab(BBSHOME"/etc/hc.tab");
+    }
+    gbinited = 1;
+}
+
+static void gb_outs(char *str)
+{
+    do_outs(hc_convert_str(str, HC_BIGtoGB, HC_DO_SINGLE));
+}             
+#endif
+int edit_outs(char *text) {
+    register int column = 0;
+    register char ch;
+#ifdef SUPPORT_GB    
+    if(current_font_type == TYPE_GB) 
+	text = hc_convert_str(text, HC_BIGtoGB, HC_DO_SINGLE);
+#endif
+    while((ch = *text++) && (++column < SCR_WIDTH))
+        outch(ch == 27 ? '*' : ch);
+
+    return 0;
+}                  
+
+void outs(char *str) {
+#ifdef SUPPORT_GB    
+    if(current_font_type == TYPE_BIG5)
+#endif
+	do_outs(str);
+#ifdef SUPPORT_GB    
+    else
+    {
+	if(!gbinited) gb_init();
+	gb_outs(str);
+    }
+#endif
+}
+
+
+/* Jaky */
+void  Jaky_outs(char *str, int line) {
+#ifdef SUPPORT_GB    
+    if(current_font_type == TYPE_GB)
+	str = hc_convert_str(str, HC_BIGtoGB, HC_DO_SINGLE);    
+#endif
+    while(*str && line) {
+	outc(*str);
+	if(*str=='\n')
+	    line--;
+	str++;
+    }
+}
+
+void outmsg(char *msg) {
+    move(b_lines, 0);
+    clrtoeol();
+#ifdef SUPPORT_GB    
+    if(current_font_type == TYPE_GB)
+	msg = hc_convert_str(msg, HC_BIGtoGB, HC_DO_SINGLE);    
+#endif
+    while(*msg)
+	outc(*msg++);
+}
+
+void prints(char *fmt, ...) {
+    va_list args;
+    char buff[1024];
+
+    va_start(args, fmt);
+    vsprintf(buff, fmt, args);
+    va_end(args);
+    outs(buff);
+}
+
+void mprints(int y, int x, char *str) {
+    move(y, x);
+    clrtoeol();
+    prints(str);
+} 
+
+void scroll() {
+    scrollcnt++;
+    if(++roll >= scr_lns)
+	roll = 0;
+    move(b_lines, 0);
+    clrtoeol();
+}
+
+void rscroll() {
+    scrollcnt--;
+    if(--roll < 0)
+	roll = b_lines;
+    move(0, 0);
+    clrtoeol();
+}
+
+void region_scroll_up(int top, int bottom) {
+    int i;
+
+    if(top > bottom) {
+	i = top; 
+	top = bottom;
+	bottom = i;
+    }
+
+    if(top < 0 || bottom >= scr_lns)
+	return;
+
+    for(i = top; i < bottom; i++)
+	big_picture[i] = big_picture[i + 1];
+    memset(big_picture + i, 0, sizeof(*big_picture));
+    memset(big_picture[i].data, ' ', scr_cols);
+    save_cursor();
+    change_scroll_range(top, bottom);
+    do_move(0, bottom);
+    scroll_forward();
+    change_scroll_range(0, scr_lns - 1);
+    restore_cursor();
+    refresh();
+}
+
+void standout() {
+    if(!standing && strtstandoutlen) {
+	register screenline_t *slp;
+
+	slp = &big_picture[((cur_ln + roll) % scr_lns)];
+	standing = YEA;
+	slp->sso = slp->eso = cur_col;
+	slp->mode |= STANDOUT;
+    }
+}
+
+void standend() {
+    if(standing && strtstandoutlen) {
+	register screenline_t *slp;
+
+	slp = &big_picture[((cur_ln + roll) % scr_lns)];
+	standing = NA;
+	slp->eso = MAX(slp->eso, cur_col);
+    }
+}
diff --git a/mbbsd/stuff.c b/mbbsd/stuff.c
new file mode 100644
index 00000000..9218b7f0
--- /dev/null
+++ b/mbbsd/stuff.c
@@ -0,0 +1,524 @@
+/* $Id: stuff.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "perm.h"
+#include "proto.h"
+
+extern int currmode;
+extern char *fn_mandex;
+extern char *str_reply;
+extern char *str_space;
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern userec_t cuser;
+
+/* ----------------------------------------------------- */
+/* set file path for boards/user home                    */
+/* ----------------------------------------------------- */
+static char *str_home_file = "home/%c/%s/%s";
+static char *str_board_file = "boards/%s/%s";
+
+#define STR_DOTDIR  ".DIR"
+static char *str_dotdir = STR_DOTDIR;
+
+void setcalfile(char *buf, char *userid) {
+    sprintf(buf, "home/%c/%s/calendar", userid[0], userid);
+}
+
+void sethomepath(char *buf, char *userid) {
+    sprintf(buf, "home/%c/%s", userid[0], userid);
+}
+
+void sethomedir(char *buf, char *userid) {
+    sprintf(buf, str_home_file, userid[0], userid, str_dotdir);
+}
+
+void sethomeman(char *buf, char *userid) {
+    sprintf(buf, str_home_file, userid[0], userid, "man");
+}
+
+void sethomefile(char *buf, char *userid, char *fname) {
+    sprintf(buf, str_home_file, userid[0], userid, fname);
+}
+
+void setuserfile(char *buf, char *fname) {
+    sprintf(buf, str_home_file, cuser.userid[0], cuser.userid, fname);
+}
+
+void setapath(char *buf, char *boardname) {
+    sprintf(buf, "man/boards/%s", boardname);
+}
+
+void setadir(char *buf, char *path) {
+    sprintf(buf, "%s/%s", path, str_dotdir);
+}
+
+void setbpath(char *buf, char *boardname) {
+    sprintf(buf, "boards/%s", boardname);
+}
+
+void setbdir(char *buf, char *boardname) {
+    sprintf(buf, str_board_file, boardname,
+	    currmode & MODE_ETC ? ".ETC" :
+	    (currmode & MODE_DIGEST ? fn_mandex : str_dotdir));
+}
+
+void setbfile(char *buf, char *boardname, char *fname) {
+    sprintf(buf, str_board_file, boardname, fname);
+}
+
+void setdirpath(char *buf, char *direct, char *fname) {
+    strcpy(buf, direct);
+    direct = strrchr(buf, '/');
+    strcpy(direct + 1, fname);
+}
+
+char *subject(char *title) {
+    if(!strncasecmp(title, str_reply, 3)) {
+	title += 3;
+	if(*title == ' ')
+	    title++;
+    }
+    return title;
+}
+
+/* ----------------------------------------------------- */
+/* �r���ഫ�ˬd���                                      */
+/* ----------------------------------------------------- */
+int str_checksum(char *str) {
+    int n = 1;
+    if(strlen(str) < 6)
+	return 0;
+    while(*str)
+        n += *(str++) * (n);
+    return n;
+}
+
+void str_lower(char *t, char *s) {
+    register unsigned char ch;
+
+    do {
+	ch = *s++;
+	*t++ = char_lower(ch);
+    } while(ch);
+}
+
+int strstr_lower(char *str, char *tag) {
+    char buf[STRLEN];
+    
+    str_lower(buf, str);
+    return (int)strstr(buf, tag);
+}
+
+void trim(char *buf) {            /* remove trailing space */
+    char *p = buf;
+
+    while(*p)
+	p++;
+    while(--p >= buf) {
+	if(*p == ' ')
+	    *p = '\0';
+	else
+	    break;
+    }
+}
+
+/* ----------------------------------------------------- */
+/* �r���ˬd��ơG�^��B�Ʀr�B�ɦW�BE-mail address        */
+/* ----------------------------------------------------- */
+int isprint2(char ch) {
+    return ((ch & 0x80) ? 1 : isprint(ch));
+    //return 1;
+}
+
+int not_alpha(char ch) {
+    return (ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z');
+}
+
+int not_alnum(char ch) {
+    return (ch < '0' || (ch > '9' && ch < 'A') ||
+	    (ch > 'Z' && ch < 'a') || ch > 'z');
+}
+
+int invalid_pname(char *str) {
+    char *p1, *p2, *p3;
+    
+    p1 = str;
+    while(*p1) {
+	if(!(p2 = strchr(p1, '/')))
+	    p2 = str + strlen(str);
+	if(p1 + 1 > p2 || p1 + strspn(p1, ".") == p2)
+	    return 1;
+	for(p3 = p1; p3 < p2; p3++)
+	    if(not_alnum(*p3) && !strchr("@[]-._", *p3))
+		return 1;
+	p1 = p2 + (*p2 ? 1 : 0);
+    }
+    return 0;
+}
+
+int valid_ident(char *ident) {
+    static char *invalid[] = {"unknown@", "root@", "gopher@", "bbs@",
+			      "@bbs", "guest@", "@ppp", "@slip", NULL};
+    char buf[128];
+    int i;
+    
+    str_lower(buf, ident);
+    for(i = 0; invalid[i]; i++)
+	if(strstr(buf, invalid[i]))
+	    return 0;
+    return 1;
+}
+
+int is_uBM(char *list, char *id) {
+    register int len;
+
+    if(list[0] == '[')
+	list++;
+    if(list[0] > ' ') {
+	len = strlen(id);
+	do {
+	    if(!strncasecmp(list, id, len)) {
+		list += len;
+		if((*list == 0) || (*list == '/') ||
+		   (*list == ']') || (*list == ' '))
+		    return 1;
+	    }
+	    if((list = strchr(list,'/')) != NULL)
+		list++;
+	    else
+		break;
+	} while(1);
+    }
+    return 0;
+}
+
+int is_BM(char *list) {
+    if(is_uBM(list,cuser.userid)) {
+	cuser.userlevel |= PERM_BM; /* Ptt �۰ʥ[�WBM���v�Q */
+	return 1;
+    }
+    return 0;
+}
+
+int userid_is_BM(char *userid, char *list) {
+    register int ch, len;
+    
+    ch = list[0];
+    if((ch > ' ') && (ch < 128)) {
+	len = strlen(userid);
+	do {
+	    if(!strncasecmp(list, userid, len)) {
+		ch = list[len];
+		if((ch == 0) || (ch == '/') || (ch == ']'))
+		    return 1;
+	    }
+	    while((ch = *list++)) {
+		if(ch == '/')
+		    break;
+	    }
+	} while(ch);
+    }
+    return 0;
+}
+
+/* ----------------------------------------------------- */
+/* �ɮ��ˬd��ơG�ɮסB�ؿ��B�ݩ�                        */
+/* ----------------------------------------------------- */
+off_t dashs(char *fname) {
+    struct stat st;
+    
+    if(!stat(fname, &st))
+        return st.st_size;
+    else
+        return -1;
+}
+
+long dasht(char *fname) {
+    struct stat st;
+    
+    if(!stat(fname, &st))
+        return st.st_mtime;
+    else
+        return -1;
+}
+
+int dashl(char *fname) {
+    struct stat st;
+    
+    return (lstat(fname, &st) == 0 && S_ISLNK(st.st_mode));
+}
+
+int dashf(char *fname) {
+    struct stat st;
+    
+    return (stat(fname, &st) == 0 && S_ISREG(st.st_mode));
+}
+
+int dashd(char *fname) {
+    struct stat st;
+
+    return (stat(fname, &st) == 0 && S_ISDIR(st.st_mode));
+}
+
+int belong(char *filelist, char *key) {
+    FILE *fp;
+    int rc = 0;
+    
+    if((fp = fopen(filelist, "r"))) {
+	char buf[STRLEN], *ptr;
+	
+	while(fgets(buf, STRLEN, fp)) {
+	    if((ptr = strtok(buf, str_space)) && !strcasecmp(ptr, key)) {
+		rc = 1;
+		break;
+	    }
+	}
+	fclose(fp);
+    }
+    return rc;
+}
+
+char *Cdate(time_t *clock) {
+    static char foo[32];
+    struct tm *mytm = localtime(clock);
+    
+    strftime(foo, 32, "%m/%d/%Y %T %a", mytm);
+    return foo;
+}
+
+char *Cdatelite(time_t *clock) {
+    static char foo[32];
+    struct tm *mytm = localtime(clock);
+    
+    strftime(foo, 32, "%m/%d/%Y %T", mytm);
+    return foo;
+}
+
+char *Cdatedate(time_t *clock){
+    static char foo[32];
+    struct tm *mytm = localtime(clock);
+    
+    strftime(foo, 32, "%m/%d/%Y", mytm);
+    return foo;
+}
+
+static void capture_screen() {
+    char fname[200];
+    FILE* fp;
+    extern screenline_t *big_picture;
+    extern unsigned char scr_lns;
+    int i;
+
+    getdata(b_lines - 2, 0, "��o�ӵe�����J��Ȧs�ɡH[y/N] ",
+	    fname, 4, LCECHO);
+    if(fname[0] != 'y' ) return;
+    
+    setuserfile(fname, ask_tmpbuf(b_lines - 1));
+    if((fp = fopen(fname, "w"))) {
+	for(i = 0; i < scr_lns; i++)
+	    fprintf(fp, "%.*s\n", big_picture[i].len, big_picture[i].data);
+	fclose(fp);
+    }
+}
+
+void pressanykey() {
+    int ch;
+    
+    outmsg("\033[37;45;1m                        "
+	   "�� �� \033[33m(Space/Return)\033[37m �~�� ��"
+	   "       \033[33m(^T)\033[37m �s�Ȧs��   \033[m");
+    do {
+	ch = igetkey();
+	
+	if(ch == Ctrl('T')) {
+	    capture_screen();
+	    break;
+	}
+    } while((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n'));
+    move(b_lines, 0);
+    clrtoeol();
+    refresh();
+}
+
+int vmsg (const char *fmt, ...)
+{
+  va_list ap;
+  char msg[80] = {0};
+  int ch;
+
+  va_start (ap, fmt);
+  vsprintf (msg, fmt, ap);
+  va_end (ap);
+
+  move (b_lines, 0);
+  clrtoeol ();
+
+  if (*msg)
+    prints ("\033[1;36;44m �� %-55.54s \033[33;46m \033[200m\033[1431m\033[506m[�����N���~��]\033[201m \033[m", msg);
+  else
+    outs ("\033[46;1m                        \033[37m"
+      "\033[200m\033[1431m\033[506m�� �� \033[33m(Space/Return)\033[37m �~�� ��\033[201m"
+      "                       \033[m");
+
+  do {
+	ch = igetkey();
+	
+	if(ch == Ctrl('T')) {
+	    capture_screen();
+	    break;
+	}
+    } while((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n'));
+
+  
+  move (b_lines, 0);
+  clrtoeol ();
+  refresh ();
+  return ch;
+}
+
+void bell() {
+    char c;
+    
+    c = Ctrl('G');
+    write(1, &c, 1);
+}
+
+int search_num(int ch, int max) {
+    int clen = 1;
+    int x, y;
+    extern unsigned char scr_cols;
+    char genbuf[10];
+    
+    outmsg("\033[7m ���ܲĴX���G\033[m");
+    outc(ch);
+    genbuf[0] = ch;
+    getyx(&y, &x);
+    x--;
+    while((ch = igetch()) != '\r') {
+	if(ch == 'q' || ch == 'e')
+	    return -1;
+	if(ch == '\n')
+	    break;
+	if(ch == '\177' || ch == Ctrl('H')) {
+	    if(clen == 0) {
+		bell();
+		continue;
+	    }
+	    clen--;
+	    move(y, x + clen);
+	    outc(' ');
+	    move(y, x + clen);
+	    continue;
+	}
+	if(!isdigit(ch)) {
+	    bell();
+	    continue;
+	}
+	if(x + clen >= scr_cols || clen >= 6) {
+	    bell();
+	    continue;
+	}
+	genbuf[clen++] = ch;
+	outc(ch);
+    }
+    genbuf[clen] = '\0';
+    move(b_lines, 0);
+    clrtoeol();
+    if(genbuf[0] == '\0')
+	return -1;
+    clen = atoi(genbuf);
+    if(clen == 0)
+	return 0;
+    if(clen > max)
+	return max;
+    return clen - 1;
+}
+
+void stand_title(char *title) {
+    clear();
+    prints("\033[1;37;46m�i %s �j\033[m\n", title);
+}
+
+void cursor_show(int row, int column) {
+    move(row, column);
+    outs(STR_CURSOR);
+    move(row, column + 1);
+}
+
+void cursor_clear(int row, int column) {
+    move(row, column);
+    outs(STR_UNCUR);
+}
+
+int cursor_key(int row, int column) {
+    int ch;
+    
+    cursor_show(row, column);
+    ch = egetch();
+    move(row, column);
+    outs(STR_UNCUR);
+    return ch;
+}
+
+void printdash(char *mesg) {
+    int head = 0, tail;
+    
+    if(mesg)
+	head = (strlen(mesg) + 1) >> 1;
+    
+    tail = head;
+    
+    while(head++ < 38)
+	outch('-');
+    
+    if(tail) {
+	outch(' ');
+	outs(mesg);
+	outch(' ');
+    }
+    
+    while(tail++ < 38)
+	outch('-');
+    outch('\n');
+}
+
+int log_file(char *filename,char *buf) {
+    FILE *fp;
+
+    if((fp = fopen(filename, "a" )) != NULL ) {
+        fputs( buf, fp );
+        if(!strchr(buf,'\n'))
+	    fputc('\n',fp);
+        fclose( fp );
+        return 0;
+    }
+    else
+	return -1;
+}
+
+void show_help(char *helptext[]) {
+    char *str;
+    int i;
+    
+    clear();
+    for(i = 0; (str = helptext[i]); i++) {
+	if(*str == '\0')
+	    prints("\033[1m�i %s �j\033[0m\n", str + 1);
+	else if(*str == '\01')
+	    prints("\n\033[36m�i %s �j\033[m\n", str + 1);
+	else
+	    prints("        %s\n", str);
+    }
+    pressanykey();
+}
diff --git a/mbbsd/syspost.c b/mbbsd/syspost.c
new file mode 100644
index 00000000..b7aefe10
--- /dev/null
+++ b/mbbsd/syspost.c
@@ -0,0 +1,102 @@
+/* $Id: syspost.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "proto.h"
+
+extern char *str_permid[];
+extern userec_t cuser;
+
+void post_change_perm(int oldperm, int newperm, char *sysopid, char *userid) {
+    FILE *fp;
+    fileheader_t fhdr;
+    time_t now = time(0);
+    char genbuf[200], reason[30];
+    int i, flag=0;
+    
+    strcpy(genbuf, "boards/Security");
+    stampfile(genbuf, &fhdr);
+    if(!(fp = fopen(genbuf,"w")))
+	return;
+    
+    fprintf(fp, "�@��: [�t�Φw����] �ݪO: Security\n"
+	    "���D: [���w���i] �����ק��v�����i\n"
+	    "�ɶ�: %s\n", ctime(&now));
+    for(i = 5; i < NUMPERMS; i++) {
+        if(((oldperm >> i) & 1) != ((newperm >> i) & 1)) {
+            fprintf (fp, "   ����\033[1;32m%s%s%s%s\033[m���v��\n",
+		     sysopid,
+		     (((oldperm >> i) & 1) ? "\033[1;33m����":"\033[1;33m�}��"),
+		     userid, str_permid[i]);
+            flag++;
+        }
+    }
+    
+    if(flag) {
+	clrtobot();
+	clear();
+	while(!getdata_str(5, 0, "�п�J�z�ѥH�ܭt�d�G",
+			   reason, 60, DOECHO, "�ݪ����D:"));
+	fprintf(fp, "\n   \033[1;37m����%s�ק��v���z�ѬO�G%s\033[m",
+		cuser.userid, reason);
+	fclose(fp);
+	
+	sprintf(fhdr.title, "[���w���i] ����%s�ק�%s�v�����i",
+		cuser.userid, userid);
+	strcpy(fhdr.owner, "[�t�Φw����]");
+	append_record("boards/Security/.DIR", &fhdr, sizeof(fhdr));
+    }
+}
+
+void post_violatelaw(char* crime, char* police, char* reason, char* result){
+    char genbuf[200];
+    fileheader_t fhdr;
+    time_t now;
+    FILE *fp;            
+    strcpy(genbuf, "boards/Security");
+    stampfile(genbuf, &fhdr);
+    if(!(fp = fopen(genbuf,"w")))
+        return;
+    now = time(NULL);
+    fprintf(fp, "�@��: [Ptt�k�|] �ݪO: Security\n"
+	    "���D: [���i] %-20s �H�k�P�M���i\n"
+	    "�ɶ�: %s\n"
+	    "\033[1;32m%s\033[m�P�M�G\n     \033[1;32m%s\033[m"
+	    "�]\033[1;35m%s\033[m�欰�A\n�H�ϥ������W�A�B�H\033[1;35m%s\033[m�A�S�����i",
+	    crime, ctime(&now), police, crime, reason, result);
+    fclose(fp);
+    sprintf(fhdr.title, "[���i] %-20s �H�k�P�M���i", crime);
+    strcpy(fhdr.owner, "[Ptt�k�|]");
+    append_record("boards/Security/.DIR", &fhdr, sizeof(fhdr));
+    
+    strcpy(genbuf, "boards/ViolateLaw");
+    stampfile(genbuf, &fhdr);
+    if(!(fp = fopen(genbuf,"w")))
+        return;
+    now = time(NULL);
+    fprintf(fp, "�@��: [Ptt�k�|] �ݪO: ViolateLaw\n"
+	    "���D: [���i] %-20s �H�k�P�M���i\n"
+	    "�ɶ�: %s\n"
+	    "\033[1;32m%s\033[m�P�M�G\n     \033[1;32m%s\033[m"
+	    "�]\033[1;35m%s\033[m�欰�A\n�H�ϥ������W�A�B�H\033[1;35m%s\033[m�A�S�����i",
+	    crime, ctime(&now), police, crime, reason, result);
+    fclose(fp);
+    sprintf(fhdr.title, "[���i] %-20s �H�k�P�M���i", crime);
+    strcpy(fhdr.owner, "[Ptt�k�|]");
+    
+    append_record("boards/ViolateLaw/.DIR", &fhdr, sizeof(fhdr));
+                                 
+}
+
+void post_newboard(char* bgroup, char* bname, char* bms){
+    char genbuf[256], title[128];
+    sprintf(title, "[�s������] %s", bname);
+    sprintf(genbuf, "%s �}�F�@�ӷs�� %s : %s\n\n�s�����D�� %s\n\n����*^_^*\n",
+	     cuser.userid, bname, bgroup, bms);
+    post_msg("Record", title,  genbuf, "[�t��]");  
+}
diff --git a/mbbsd/talk.c b/mbbsd/talk.c
new file mode 100644
index 00000000..3012b2d2
--- /dev/null
+++ b/mbbsd/talk.c
@@ -0,0 +1,2663 @@
+/* $Id: talk.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+
+#define QCAST   int (*)(const void *, const void *)
+
+extern userinfo_t *currutmp;
+extern char *ModeTypeTable[MAX_MODES];
+extern char *fn_overrides;
+extern int usernum;
+extern char *msg_sure_ny;
+extern char *msg_cancel;
+extern unsigned int currstat;
+extern char *fn_writelog;
+extern FILE *fp_writelog;
+extern pid_t currpid;
+extern int b_lines;		/* Screen bottom line number: t_lines-1 */
+extern int t_lines, t_columns;	/* Screen size / width */
+extern char *fn_talklog;
+extern char currauthor[IDLEN + 2];
+extern char *msg_usr_left;
+extern char *msg_uid;
+extern char *BBSName;
+extern int p_lines;		/* a Page of Screen line numbers: tlines-4 */
+extern char fromhost[];
+extern char *err_uid;
+extern int talkrequest;
+extern char *msg_shortulist;
+extern char *msg_nobody;
+extern boardheader_t *bcache;
+extern int curr_idle_timeout;
+extern userec_t cuser;
+extern userec_t xuser;
+
+
+static char *IdleTypeTable[] = {
+    "���b��b��", "���H�ӹq", "�V����", "�����P��", "�������A", "�ڦb���"
+};
+static char *sig_des[] = {
+    "����", "���", "", "�U��", "�H��", "�t��"
+};
+
+#define MAX_SHOW_MODE 3
+#define M_INT 15		/* monitor mode update interval */
+#define P_INT 20		/* interval to check for page req. in
+				 * talk/chat */
+#define BOARDFRI  1
+
+typedef struct talkwin_t {
+    int curcol, curln;
+    int sline, eline;
+} talkwin_t;
+
+typedef struct pickup_t {
+    userinfo_t *ui;
+    time_t idle;
+    int friend;
+} pickup_t;
+
+extern int bind( /* int,struct sockaddr *, int */ );
+extern char *getuserid();
+extern struct utmpfile_t *utmpshm;
+extern int watermode;
+extern water_t water[6], *swater[5], *water_which;
+extern char *friend_file[8], water_usies;
+
+/* �O�� friend �� user number */
+//#define PICKUP_WAYS     7		//�����k�h�u��
+#define PICKUP_WAYS     6
+
+static int pickup_way = 0;
+static char *fcolor[11] = {
+    "", "\033[36m", "\033[32m", "\033[1;32m",
+    "\033[33m", "\033[1;33m", "\033[1;37m", "\033[1;37m",
+    "\033[31m", "\033[1;35m", "\033[1;36m"
+};
+static char save_page_requestor[40];
+static char page_requestor[40];
+static char description[30];
+static FILE *flog;
+
+
+char *modestring(userinfo_t * uentp, int simple) {
+    static char modestr[40];
+    static char *notonline = "���b���W";
+    register int mode = uentp->mode;
+    register char *word;
+    int fri_stat;
+
+/* for debugging */
+    if (mode >= MAX_MODES)
+    {
+	syslog(LOG_WARNING, "what!? mode = %d", mode);
+	word = ModeTypeTable[mode % MAX_MODES];
+    }
+    else
+	word = ModeTypeTable[mode];
+    fri_stat = friend_stat(currutmp, uentp);
+    if (!(HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_SEECLOAK)) &&
+	(
+	    (uentp->invisible || (fri_stat & HRM)) &&
+	    !((fri_stat & HFM) && (fri_stat & HRM))
+	    )
+	)
+	return notonline;
+    else if (mode == EDITING)
+    {
+	sprintf(modestr, "E:%s",
+		ModeTypeTable[uentp->destuid < EDITING ? uentp->destuid :
+			     EDITING]);
+	word = modestr;
+    }
+    else if (!mode && *uentp->chatid == 1)
+    {
+	if (!simple)
+	    sprintf(modestr, "�^�� %s", getuserid(uentp->destuid));
+	else
+	    sprintf(modestr, "�^���I�s");
+    }
+    else if (!mode && *uentp->chatid == 2)
+	if (uentp->msgcount < 10)
+	{
+	    char *cnum[10] =
+	    {"", "�@", "��", "�T", "�|", "��", "��", "�C",
+	     "�K", "�E"};
+	    sprintf(modestr, "��%s�����y", cnum[uentp->msgcount]);
+	}
+	else
+	    sprintf(modestr, "����F @_@");
+    else if (!mode && *uentp->chatid == 3)
+	sprintf(modestr, "���y�dzƤ�");
+    else if (!mode)
+	return (uentp->destuid == 6) ? uentp->chatid :
+	IdleTypeTable[(0 <= uentp->destuid && uentp->destuid < 6) ?
+		     uentp->destuid : 0];
+    else if (simple)
+	return word;
+    else if (uentp->in_chat && mode == CHATING)
+	sprintf(modestr, "%s (%s)", word, uentp->chatid);
+    else if (mode == TALK)
+    {
+	if (!isvisible_uid(uentp->destuid))/* Leeym ���(����)���� */
+	    sprintf(modestr, "%s", "��� �Ů�");/* Leeym �j�a�ۤv�o���a�I */
+	else
+	    sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));
+    }
+    else if (mode == M_FIVE)
+    {
+	if (!isvisible_uid(uentp->destuid))
+	    sprintf(modestr, "%s", "���l�� ��");
+	else
+	    sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));
+    }
+    else if (mode == CHC)
+    {
+	if (isvisible_uid(uentp->destuid))
+	    sprintf(modestr, "%s", "�U�H��");
+	else
+	    sprintf(modestr, "�U�H�� %s", getuserid(uentp->destuid));
+    }
+    else if (mode != PAGE && mode != TQUERY)
+	return word;
+    else
+	sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));
+
+    return (modestr);
+}
+
+int set_friend_bit(userinfo_t * me, userinfo_t * ui) {
+    int unum, *myfriends, hit=0, n;
+
+/* �P�_���O�_���ڪ��B�� ? */
+    unum = ui->uid;
+    myfriends = me->friend;
+    while ((n = *myfriends++))
+    {
+	if (unum == n)
+	{
+	    hit = IFH;
+	    break;
+	}
+    }
+
+/* �P�_�ڬO�_����誺�B�� ? */
+    myfriends = ui->friend;
+    while ((unum = *myfriends++))
+    {
+	if (unum == me->uid)
+	{
+	    hit |= HFM;
+	    break;
+	}
+    }
+
+/* �P�_���O�_���ڪ����H ? */
+
+    unum = ui->uid;
+    myfriends = me->reject;
+    while ((n = *myfriends++))
+    {
+	if (unum == n)
+	{
+	    hit |= IRH;
+	    break;
+	}
+    }
+
+/* �P�_�ڬO�_����誺���H ? */
+    myfriends = ui->reject;
+    while ((unum = *myfriends++))
+    {
+	if (unum == me->uid)
+	{
+	    hit |= HRM;
+	    break;
+	}
+    }
+    return hit;
+}
+int reverse_friend_stat(int stat)
+{
+     int stat1=0;
+     if(stat & IFH)
+          stat1 |=HFM;
+     if(stat & IRH)
+          stat1 |=HRM;
+     if(stat & HFM)
+          stat1 |=IFH;
+     if(stat & HRM)
+          stat1 |=IRH;   
+     if(stat & IBH)
+          stat1 |=IBH;   
+     return stat1; 
+}
+
+int login_friend_online(){
+   userinfo_t *uentp;
+   int i, stat, stat1;
+   int offset=(int) (currutmp - &utmpshm->uinfo[0]);
+   for (i=0;i<utmpshm->number && currutmp->friendtotal<MAX_FRIEND; i++)
+     {
+       uentp = (utmpshm->sorted[utmpshm->currsorted][0][i]);
+       if(uentp && uentp->uid && (stat=set_friend_bit(currutmp,uentp)))
+             {
+		  stat1=reverse_friend_stat(stat);
+                  stat <<= 24;
+                  stat |= (int) (uentp - &utmpshm->uinfo[0]);
+	          currutmp->friend_online[currutmp->friendtotal++]=stat; 
+		  if(uentp!=currutmp && uentp->friendtotal<MAX_FRIEND)
+	             {
+		        stat1 <<= 24;
+		        stat1 |= offset;
+			uentp->friend_online[uentp->friendtotal++]=stat1;
+	             }
+	     }
+     } 
+  return 0;
+}
+
+int logout_friend_online(){
+    int i, j, k;
+    int offset=(int) (currutmp - &utmpshm->uinfo[0]);
+    userinfo_t *ui;
+    while(currutmp->friendtotal)
+	{
+	  i = currutmp->friendtotal-1;
+	  j = (currutmp->friend_online[i] & 0xFFFFFF);
+          currutmp->friend_online[i]=0;
+	  ui = &utmpshm->uinfo[j]; 
+          if(ui->pid && ui!=currutmp)
+	  {
+            for(k=0; k<ui->friendtotal && 
+		 (int)(ui->friend_online[k] & 0xFFFFFF) !=offset; k++);
+            if(k<ui->friendtotal)
+	     {
+	      ui->friendtotal--;
+	      ui->friend_online[k]=ui->friend_online[ui->friendtotal];
+	      ui->friend_online[ui->friendtotal]=0;
+	     }
+	  }
+          currutmp->friendtotal--;
+          currutmp->friend_online[currutmp->friendtotal]=0;
+	}
+    return 0;
+}
+
+
+int friend_stat(userinfo_t *me, userinfo_t * ui)
+{
+  int i, j, hit=0;
+/* �ݪO�n�� */
+  if (me->brc_id && ui->brc_id == me->brc_id)
+    {
+      hit = IBH;
+    }
+  for(i=0;me->friend_online[i];i++)
+    {
+       j = (me->friend_online[i] & 0xFFFFFF);
+       if(ui == &utmpshm->uinfo[j])
+        {
+           hit |= me->friend_online[i] >>24;
+	   break;
+        }
+    }
+  if (PERM_HIDE(ui))
+        return hit & ST_FRIEND;   
+  return hit;
+}
+
+int isvisible_stat(userinfo_t * me, userinfo_t * uentp, int fri_stat) {
+    if (uentp->userid[0] == 0)
+	return 0;
+
+    if (PERM_HIDE(uentp) && !(PERM_HIDE(me)))/* ��赵�����ΦӧA�S�� */
+	return 0;
+    else if ((me->userlevel & PERM_SYSOP) ||
+             ((fri_stat & HRM) && (fri_stat & HFM)))							/* �����ݪ�������H */
+	return 1;
+
+    if (uentp->invisible && !(me->userlevel & PERM_SEECLOAK)) return 0;
+
+    return (fri_stat & HRM) ? 0 : 1;
+}
+
+int isvisible(userinfo_t * me, userinfo_t * uentp) {
+   return isvisible_stat(currutmp, uentp, friend_stat(me, uentp));
+}
+
+int isvisible_uid(int tuid){
+    userinfo_t *uentp;
+
+    if(!tuid || !(uentp = search_ulist(tuid)))
+       return 1;
+    return isvisible(currutmp, uentp);
+}
+
+/* �u��ʧ@ */
+static void my_kick(userinfo_t * uentp) {
+    char genbuf[200];
+
+    getdata(1, 0, msg_sure_ny, genbuf, 4, LCECHO);
+    clrtoeol();
+    if (genbuf[0] == 'y')
+    {
+	sprintf(genbuf, "%s (%s)", uentp->userid, uentp->username);
+	log_usies("KICK ", genbuf);
+	if((uentp->pid <= 0 || kill(uentp->pid, SIGHUP) == -1) && (errno == ESRCH))
+	    purge_utmp(uentp);
+	outs("��X�h�o");
+    }
+    else
+	outs(msg_cancel);
+    pressanykey();
+}
+
+static void chicken_query(char *userid) {
+    char buf[100];
+
+    if (getuser(userid))
+    {
+	if (xuser.mychicken.name[0])
+	{
+	    time_diff(&(xuser.mychicken));
+	    if (!isdeadth(&(xuser.mychicken)))
+	    {
+		show_chicken_data(&(xuser.mychicken), NULL);
+		sprintf(buf, "\n\n�H�W�O %s ���d�����..", userid);
+		outs(buf);
+	    }
+	}
+	else
+	{
+	    move(1, 0);
+	    clrtobot();
+	    sprintf(buf, "\n\n%s �èS���i�d��..", userid);
+	    outs(buf);
+	}
+	pressanykey();
+    }
+}
+
+int my_query(char *uident) {
+    userec_t muser;
+    int tuid, i, fri_stat=0;
+    unsigned long int j;
+    userinfo_t *uentp;
+    static const char *money[10] =
+    {"�ťx���v", "���h", "�M�H", "���q", "�p�d",
+     "�p�I", "���I", "�j�I��", "�I�i�İ�", "�񺸻\\��"},
+    *sex[8] =
+            {MSG_BIG_BOY, MSG_BIG_GIRL,
+             MSG_LITTLE_BOY, MSG_LITTLE_GIRL,
+             MSG_MAN, MSG_WOMAN, MSG_PLANT, MSG_MIME};
+
+
+    if ((tuid = getuser(uident)))
+    {
+	memcpy(&muser, &xuser, sizeof(muser));
+	move(1, 0);
+	clrtobot();
+	move(1, 0);
+	setutmpmode(TQUERY);
+	currutmp->destuid = tuid;
+
+	j = muser.money;
+	for (i = 0; i < 10 && j > 10; i++)
+	    j /= 10;
+	prints("�m�עҼʺ١n%s(%s)%*s�m�g�٪��p�n%s\n",
+	       muser.userid,
+	       muser.username,
+	       26 - strlen(muser.userid) - strlen(muser.username), "",
+	       money[i]);
+	prints("�m�W�����ơn%d��", muser.numlogins);
+	move(2, 40);
+	prints("�m�峹�g�ơn%d�g\n", muser.numposts);
+
+	if((uentp = (userinfo_t *) search_ulist(tuid)))
+           fri_stat=friend_stat(currutmp, uentp); 
+	prints("\033[1;33m�m�ثe�ʺA�n%-28.28s\033[m",
+	     (uentp && isvisible_stat(currutmp, uentp, fri_stat)) ?
+	       modestring(uentp, 0) : "���b���W");
+
+	outs(((uentp && uentp->mailalert) || load_mailalert(muser.userid))
+	    ? "�m�p�H�H�c�n���s�i�H���٨S��\n" :
+	      "�m�p�H�H�c�n�Ҧ��H�󳣬ݹL�F\n");
+	prints("�m�W���W���n%-28.28s�m�W���G�m�n%s\n",
+	       Cdate(&muser.lastlogin),
+	       (muser.lasthost[0] ? muser.lasthost : "(����)"));
+        if ((uentp && fri_stat&HFM) || HAS_PERM(PERM_SYSOP))
+	   prints("�m ��  �O �n%-28.28s�m�p���]���n%ld �Ȩ�\n",
+	  	      sex[muser.sex % 8],
+		      muser.money);
+	prints("�m���l�Ѿ��Z�n%3d �� %3d �� %3d �M      "
+	       "�m�H�Ѿ��Z�n%3d �� %3d �� %3d �M",
+	       muser.five_win, muser.five_lose, muser.five_tie,
+	       muser.chc_win, muser.chc_lose, muser.chc_tie);
+	showplans(uident);
+	pressanykey();
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+static char t_last_write[200] = "";
+
+void water_scr(water_t **currwater, int which, char type)
+{
+    if( type == 1 ){
+	int    i;
+	int    colors[] = {33, 37, 33, 37, 33};
+	move(8 + which, 28);prints(" ");
+	move(8 + which, 28);
+	prints("\033[1;37;45m    %-14s \033[0m", currwater[which]->userid);
+	for( i = 0 ; i < 5 ; ++i ){
+	    move(16 + i, 4);
+	    prints(" ");
+	    move(16 + i, 4);
+	    if( currwater[which]->msg[ (currwater[which]->top-i+4) % 5 ].last_call_in[0] != 0 )
+		prints("\033[0m   \033[1;%d;44m��%-64s\033[0m   \n",
+		       colors[i],
+		       currwater[which]->msg[ (currwater[which]->top-i+4) % 5 ].last_call_in);
+	    else
+		prints("\033[0m�@\n");
+	}
+	move(0, 0);prints(" ");
+	move(0, 0);
+	prints("\033[0m���� %s:", currwater[which]->userid);
+	clrtoeol();
+	move(0, strlen(currwater[which]->userid) + 6);
+    }
+    else{
+	move(8 + which, 28);
+	prints("123456789012345678901234567890");
+	//	refresh();
+	move(8 + which, 28);
+	prints("\033[1;37;44m    %-13s�@\033[0m", currwater[which]->userid);
+	//	refresh();
+    }
+}
+
+void my_write2(void)
+{
+    int     i, ch, currstat0, currwater_usies;
+    char    genbuf[256], msg[80], done = 0, c0, which;
+    water_t *tw, *currwater[5];
+    unsigned        char    mode0;
+
+    watermode = 0;
+    currstat0 = currstat;
+    c0 = currutmp->chatid[0];
+    mode0 = currutmp->mode;
+    currutmp->mode = 0;
+    currutmp->chatid[0] = 3;
+    currstat = XMODE;
+
+    // init screen
+    memcpy(currwater, swater, sizeof(water_t*) * 5);
+    currwater_usies = water_usies;
+    move(7, 28);
+    prints("\033[1;33;46m �� ���y������H ��\033[0m");
+    for( i = 0 ; i < 5 ; ++i )
+	if( currwater[i] == NULL || currwater[i]->pid == 0 )
+	    break;
+	else
+	    water_scr(currwater, i, 0);
+    move(15, 4);
+    prints("\033[0m \033[1;35m��\033[1;36m�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w\033[1;35m��\033[0m ");
+    move(22, 4);
+    prints(" \033[1;35m��\033[1;36m�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	   "�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w\033[1;35m��\033[0m ");
+    move(21, 4);prints(" ");
+    move(21, 4);
+    prints("\033[0m   \033[1;37;46m%-66s\033[0m   \n", t_last_write);
+    water_scr(currwater, 0, 1);
+    refresh();
+
+    which = 0;
+    do{
+	switch( (ch = igetkey()) ){
+	case Ctrl('T'):
+	case KEY_UP:
+	    if( currwater_usies != 1 ){
+		water_scr(currwater, which, 0);
+		which = (which - 1 + currwater_usies) % currwater_usies;
+		water_scr(currwater, which, 1);
+		refresh();
+	    }
+	    break;
+
+	case KEY_DOWN:
+	case Ctrl('R'):
+	    if( currwater_usies != 1 ){
+		water_scr(currwater, which, 0);
+		which = (which + 1 + currwater_usies) % currwater_usies;
+		water_scr(currwater, which, 1);
+		refresh();
+	    }
+	    break;
+
+	case KEY_LEFT:
+	    done = 1;
+	    break;
+
+	default:
+	    done = 1;
+	    watermode = 1;
+	    tw = currwater[(int)which];
+
+	    if( ch != '\r' && ch != '\n' ){
+		msg[0] = ch, msg[1] = 0;
+	    }
+	    else
+		msg[0] = 0;
+	    move(0, 0);prints("\033[m"); clrtoeol();
+	    refresh();
+	    sprintf(genbuf, "���� %s:", tw->userid);
+	    if( !oldgetdata(0, 0, genbuf, msg,
+			    80-strlen(tw->userid)-6, DOECHO) )
+		break;
+
+	    my_write(tw->pid, msg, tw->userid, 4);
+	    break;
+	}
+    } while( !done );
+
+    watermode = -1;
+    currstat = currstat0;
+    currutmp->chatid[0] = c0;
+    currutmp->mode = mode0;
+}
+
+/* 
+   �Q�I�s���ɾ�:
+   1. ��s�դ��y flag = 1 (pre-edit)
+   2. �^���y     flag = 0
+   3. �W��aloha  flag = 2 (pre-edit)
+   4. �s��       flag = 3 if SYSOP, otherwise flag = 1 (pre-edit)
+   5. ����y     flag = 0
+   6. my_write2  flag = 4 (pre-edit) but confirm
+*/
+int my_write(pid_t pid, char *prompt, char *id, int flag) {
+    int len, currstat0 = currstat, fri_stat;
+    char msg[80], destid[IDLEN + 1];
+    char genbuf[200], buf[200], c0 = currutmp->chatid[0];
+    unsigned char mode0 = currutmp->mode;
+    time_t now;
+    struct tm *ptime;
+    userinfo_t *uin;
+    uin = (userinfo_t *)search_ulist_pid(pid);
+    strcpy(destid, id);
+    
+    if(!uin && !(flag == 0 && water_which->count> 0)) {
+      outmsg("\033[1;33;41m�V�|! ���w���]�F(���b���W)! \033[37m~>_<~\033[m");
+	clrtoeol();
+	refresh();
+	watermode = -1;
+	return 0;
+    }
+    currutmp->mode = 0;
+    currutmp->chatid[0] = 3;
+    currstat = XMODE;
+    
+    time(&now);
+    ptime = localtime(&now);
+    
+    if(flag == 0) {
+	/* �@����y */
+	watermode = 0;
+	if(!(len = getdata(0, 0, prompt, msg, 56, DOECHO))) {
+	    outmsg("\033[1;33;42m��F! ��A�@��...\033[m");
+	    clrtoeol();
+	    refresh();
+	    currutmp->chatid[0] = c0;
+	    currutmp->mode = mode0;
+	    currstat = currstat0;
+	    watermode = -1;
+	    return 0;
+	}
+	
+	if(watermode > 0) {
+	    int i;
+	    
+	    i = (water_which->top- watermode + MAX_REVIEW) % MAX_REVIEW;
+	    uin = (userinfo_t *)search_ulist_pid(water_which->msg[i].pid);
+	    strcpy(destid, water_which->msg[i].userid);
+	}
+    } else {
+	/* pre-edit �����y */
+	strcpy(msg, prompt);
+	len = strlen(msg);
+    }
+    
+    watermode = -1;
+    strip_ansi(msg, msg, 0);
+    if(uin && *uin->userid && (flag == 0 || flag == 4)) {
+	sprintf(buf, "�ᵹ %s : %s [Y/n]?", uin->userid, msg);
+	getdata(0, 0, buf, genbuf, 3, LCECHO);
+	if(genbuf[0] == 'n') {
+	    outmsg("\033[1;33;42m��F! ��A�@��...\033[m");
+	    clrtoeol();
+	    refresh();
+	    currutmp->chatid[0] = c0;
+	    currutmp->mode = mode0;
+	    currstat = currstat0;
+	    watermode = -1;
+	    return 0;
+	}
+    }
+    
+    if(!uin || !*uin->userid || strcasecmp(destid, uin->userid)) {
+	outmsg("\033[1;33;41m�V�|! ���w���]�F(���b���W)! \033[37m~>_<~\033[m");
+	clrtoeol();
+	refresh();
+	currutmp->chatid[0] = c0;
+	currutmp->mode = mode0;
+	currstat = currstat0;
+	return 0;
+    }
+    
+    fri_stat=friend_stat(currutmp, uin); 
+    time(&now);
+    if(flag != 2) { /* aloha �����y���Φs�U�� */
+	/* �s��ۤv�����y�� */
+	if(!fp_writelog)
+	{
+ 	  sethomefile(genbuf, cuser.userid, fn_writelog);
+	  fp_writelog = fopen(genbuf, "a");
+	}
+	if(fp_writelog) {
+	    fprintf(fp_writelog, "To %s: %s [%s]\n", 
+		    uin->userid, msg, Cdatelite(&now));
+		snprintf(t_last_write, 66, "To %s: %s", uin->userid, msg);
+	}
+    }
+    
+    if(flag == 3 && uin->msgcount) {
+	/* ���� */
+	uin->destuip = currutmp - &utmpshm->uinfo[0];
+	uin->sig = 2;
+	if(uin->pid > 0) kill(uin->pid, SIGUSR1);
+    } else if(flag != 2 &&
+	      !HAS_PERM(PERM_SYSOP) &&
+	      (uin->pager == 3 || 
+	       uin->pager == 2 || 
+	       (uin->pager == 4 &&
+		!(fri_stat & HFM))))
+	outmsg("\033[1;33;41m�V�|! ��訾���F! \033[37m~>_<~\033[m");
+    else {
+	if(uin->msgcount < MAX_MSGS) {
+	    unsigned char pager0 = uin->pager;
+	    
+	    uin->pager = 2;
+	    uin->msgs[uin->msgcount].pid = currpid;
+	    strcpy(uin->msgs[uin->msgcount].userid, cuser.userid);
+	    strcpy(uin->msgs[uin->msgcount++].last_call_in, msg);
+	    uin->pager = pager0;
+	} else if (flag != 2)
+	    outmsg("\033[1;33;41m�V�|! ��褣��F! (����Ӧh���y) \033[37m@_@\033[m");
+	
+	if(uin->msgcount == 1 && (uin->pid <= 0 || kill(uin->pid, SIGUSR2) == -1) && flag != 2)
+	    outmsg("\033[1;33;41m�V�|! �S����! \033[37m~>_<~\033[m");
+	else if(uin->msgcount == 1 && flag != 2)
+	    outmsg("\033[1;33;44m���y�{�L�h�F! \033[37m*^o^*\033[m");
+	else if(uin->msgcount > 1 && uin->msgcount < MAX_MSGS && flag != 2)
+	    outmsg("\033[1;33;44m�A�ɤW�@��! \033[37m*^o^*\033[m");
+    }
+    
+    clrtoeol();
+    refresh();
+    
+    currutmp->chatid[0] = c0;
+    currutmp->mode = mode0;
+    currstat = currstat0;
+    return 1;
+}
+void t_display_new() {
+    static int t_display_new_flag=0;
+    int i, off=2;
+    if (t_display_new_flag)
+	return;
+    else
+	t_display_new_flag = 1;
+
+    if( WATERMODE(WATER_ORIG) )
+	water_which = &water[0];
+    else
+	off =3;
+
+    if (water[0].count && watermode > 0){
+	move(1, 0);
+	outs("�w�w�w�w�w�w�w���w�y�w�^�w�U�w�w�w");
+	outs(WATERMODE(WATER_ORIG)                           ?
+	     "�w�w�w�w�w�w��[Ctrl-R Ctrl-T]������w�w�w�w�w" :
+	     "��[Ctrl-R Ctrl-T Ctrl-E Ctrl-W ]������w�w�w�w");
+	if( WATERMODE(WATER_NEW) ){
+	    move(2, 0);
+	    clrtoeol();
+	    for (i = 0; i<6 ; i++){
+		if(i>0)
+		    prints("%s%-13.13s\033[m",
+			   swater[i-1]==water_which?"\033[1;33;47m ":
+			   " ", 
+			   swater[i-1] ? swater[i-1]->userid:"");
+		else
+		    prints("%s ����  \033[m",
+			   water_which==&water[0]?"\033[1;33;45m ":
+			   " ");
+	    }
+	}
+	
+	for (i = 0; i < water_which->count; i++){
+	    int a = (water_which->top - i - 1 + MAX_REVIEW) % MAX_REVIEW,
+		len = 75-strlen(water_which->msg[a].last_call_in)
+		-strlen(water_which->msg[a].userid);
+	    if(len<0) len=0;
+	    
+	    move(i + (WATERMODE(WATER_ORIG)?2:3), 0);
+	    clrtoeol();
+	    if (watermode - 1 != i)
+		prints("\033[1;33;46m %s \033[37;45m %s \033[m%*s",
+		       water_which->msg[a].userid,
+		       water_which->msg[a].last_call_in, len,
+		       "");
+	    else
+		prints("\033[1;44m>\033[1;33;47m%s "
+		       "\033[37;45m %s \033[m%*s",
+		       water_which->msg[a].userid, 
+		       water_which->msg[a].last_call_in,
+		       len,"");
+	}
+	
+	if (t_last_write[0]){
+	    move(i + off, 0);
+	    clrtoeol();
+	    prints(t_last_write);
+	    i++;
+	}
+	move(i + off, 0);
+	outs("�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	     "�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w");
+	if( WATERMODE(WATER_NEW))
+	 while( i++ <= water[0].count ) {
+	    move(i + off, 0);
+	    clrtoeol();
+	}
+    }
+
+    t_display_new_flag = 0;
+}
+#if 0
+void t_display_new() {
+    int i, which=water_which;
+    char buf[200];
+
+    if (t_display_new_flag)
+	return;
+    else
+	t_display_new_flag = 1;
+
+    if (oldmsg_count && watermode > 0)
+    {
+	move(1, 0);
+	outs(
+	    "�w�w�w�w�w�w�w���w�y�w�^�w�U�w�w�w�w�w�w�w�w�w"
+	    "��[Ctrl-R Ctrl-T]������w�w�w�w�w");
+	move(2, 0);
+	prints(" |");
+        for (i = 0; i<5 && water[i].pid != 0; i++)
+            prints(" %s%13.13s \033[m|",which==i?"\033[1;33m":"", 
+			      water[i].userid); 
+
+	for( i = 0 ; i < 5 &&
+	     water[which].msg[ (water[which].msgtop-i+4) % 5 ][0] != 0; ++i ){
+            move(3 + i, 0);
+	    if (watermode - 1 != i) 
+		    sprintf(buf, "\033[1;33;46m %s \033[37;45m %s \033[m",
+                            water[which].userid,
+			    water[which].msg[ (water[which].msgtop-i+4) % 5 ]);
+	    else
+		sprintf(buf, "\033[1;44m>\033[1;33;47m%s \033[37;45m %s \033[m",
+                            water[which].userid,
+			    water[which].msg[ (water[which].msgtop-i+4) % 5 ]);
+	}
+	/*
+	for (i = 0; i < oldmsg_count; i++)
+	{
+	    int a = (water[water_which].top - i - 1 + MAX_REVIEW) % MAX_REVIEW;
+
+	    move(i + 3, 0);
+	    clrtoeol();
+	    if (watermode - 1 != i)
+		sprintf(buf, "\033[1;33;46m %s \033[37;45m %s \033[m",
+			oldmsg[a].userid, oldmsg[a].last_call_in);
+	    else
+		sprintf(buf, "\033[1;44m>\033[1;33;47m%s "
+			"\033[37;45m %s \033[m",
+			oldmsg[a].userid, oldmsg[a].last_call_in);
+	    outs(buf);
+	}
+*/
+	if (t_last_write[0])
+	{
+	    move(i + 3, 0);
+	    clrtoeol();
+	    outs(t_last_write);
+	    i++;
+	}
+	move(i + 3, 0);
+	outs("�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	     "�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w");
+    }
+    t_display_new_flag = 0;
+}
+#endif
+
+int t_display() {
+    char genbuf[200], ans[4];
+    if(fp_writelog)
+	{ fclose(fp_writelog); fp_writelog=NULL;}
+    setuserfile(genbuf, fn_writelog);
+    if (more(genbuf, YEA) != -1)
+    {
+	getdata(b_lines - 1, 0, "�M��(C) ���ܳƧѿ�(M) �O�d(R) (C/M/R)?[R]",
+		ans, 3, LCECHO);
+	if (*ans == 'm')
+	{
+	    fileheader_t mymail;
+	    char title[128], buf[80];
+
+	    sethomepath(buf, cuser.userid);
+	    stampfile(buf, &mymail);
+
+	    mymail.savemode = 'H';	/* hold-mail flag */
+	    mymail.filemode = FILE_READ;
+	    strcpy(mymail.owner, "[��.��.��]");
+	    strcpy(mymail.title, "���u\033[37;41m�O��\033[m");
+	    sethomedir(title, cuser.userid);
+	    Rename(genbuf, buf);
+	    append_record(title, &mymail, sizeof(mymail));
+	}
+	else if (*ans == 'c')
+	    unlink(genbuf);
+	return FULLUPDATE;
+    }
+    return DONOTHING;
+}
+
+static void do_talk_nextline(talkwin_t * twin) {
+    twin->curcol = 0;
+    if (twin->curln < twin->eline)
+	++(twin->curln);
+    else
+	region_scroll_up(twin->sline, twin->eline);
+    move(twin->curln, twin->curcol);
+}
+
+static void do_talk_char(talkwin_t * twin, int ch) {
+    extern screenline_t *big_picture;
+    screenline_t *line;
+    int i;
+    char ch0, buf[81];
+
+    if (isprint2(ch))
+    {
+	ch0 = big_picture[twin->curln].data[twin->curcol];
+	if (big_picture[twin->curln].len < 79)
+	    move(twin->curln, twin->curcol);
+	else
+	    do_talk_nextline(twin);
+	outc(ch);
+	++(twin->curcol);
+	line = big_picture + twin->curln;
+	if (twin->curcol < line->len)
+	{			/* insert */
+	    ++(line->len);
+	    memcpy(buf, line->data + twin->curcol, 80);
+	    save_cursor();
+	    do_move(twin->curcol, twin->curln);
+	    ochar(line->data[twin->curcol] = ch0);
+	    for (i = twin->curcol + 1; i < line->len; i++)
+		ochar(line->data[i] = buf[i - twin->curcol - 1]);
+	    restore_cursor();
+	}
+	line->data[line->len] = 0;
+	return;
+    }
+
+    switch (ch)
+    {
+    case Ctrl('H'):
+    case '\177':
+	if (twin->curcol == 0)
+	    return;
+	line = big_picture + twin->curln;
+	--(twin->curcol);
+	if (twin->curcol < line->len)
+	{
+	    --(line->len);
+	    save_cursor();
+	    do_move(twin->curcol, twin->curln);
+	    for (i = twin->curcol; i < line->len; i++)
+		ochar(line->data[i] = line->data[i + 1]);
+	    line->data[i] = 0;
+	    ochar(' ');
+	    restore_cursor();
+	}
+	move(twin->curln, twin->curcol);
+	return;
+    case Ctrl('D'):
+	line = big_picture + twin->curln;
+	if (twin->curcol < line->len)
+	{
+	    --(line->len);
+	    save_cursor();
+	    do_move(twin->curcol, twin->curln);
+	    for (i = twin->curcol; i < line->len; i++)
+		ochar(line->data[i] = line->data[i + 1]);
+	    line->data[i] = 0;
+	    ochar(' ');
+	    restore_cursor();
+	}
+	return;
+    case Ctrl('G'):
+	bell();
+	return;
+    case Ctrl('B'):
+	if (twin->curcol > 0)
+	{
+	    --(twin->curcol);
+	    move(twin->curln, twin->curcol);
+	}
+	return;
+    case Ctrl('F'):
+	if (twin->curcol < 79)
+	{
+	    ++(twin->curcol);
+	    move(twin->curln, twin->curcol);
+	}
+	return;
+    case KEY_TAB:
+	twin->curcol += 8;
+	if (twin->curcol > 80)
+	    twin->curcol = 80;
+	move(twin->curln, twin->curcol);
+	return;
+    case Ctrl('A'):
+	twin->curcol = 0;
+	move(twin->curln, twin->curcol);
+	return;
+    case Ctrl('K'):
+	clrtoeol();
+	return;
+    case Ctrl('Y'):
+	twin->curcol = 0;
+	move(twin->curln, twin->curcol);
+	clrtoeol();
+	return;
+    case Ctrl('E'):
+	twin->curcol = big_picture[twin->curln].len;
+	move(twin->curln, twin->curcol);
+	return;
+    case Ctrl('M'):
+    case Ctrl('J'):
+	line = big_picture + twin->curln;
+	strncpy(buf, line->data, line->len);
+	buf[line->len] = 0;
+	do_talk_nextline(twin);
+	break;
+    case Ctrl('P'):
+	line = big_picture + twin->curln;
+	strncpy(buf, line->data, line->len);
+	buf[line->len] = 0;
+	if (twin->curln > twin->sline)
+	{
+	    --(twin->curln);
+	    move(twin->curln, twin->curcol);
+	}
+	break;
+    case Ctrl('N'):
+	line = big_picture + twin->curln;
+	strncpy(buf, line->data, line->len);
+	buf[line->len] = 0;
+	if (twin->curln < twin->eline)
+	{
+	    ++(twin->curln);
+	    move(twin->curln, twin->curcol);
+	}
+	break;
+    }
+    trim(buf);
+    if (*buf)
+	fprintf(flog, "%s%s: %s%s\n",
+		(twin->eline == b_lines - 1) ? "\033[1;35m" : "",
+		(twin->eline == b_lines - 1) ?
+		getuserid(currutmp->destuid) : cuser.userid, buf,
+		(ch == Ctrl('P')) ? "\033[37;45m(Up)\033[m" : "\033[m");
+}
+
+static void do_talk(int fd) {
+    struct talkwin_t mywin, itswin;
+    char mid_line[128], data[200];
+    int i, datac, ch;
+    int im_leaving = 0;
+    FILE *log;
+    struct tm *ptime;
+    time_t now;
+    char genbuf[200], fpath[100];
+
+    time(&now);
+    ptime = localtime(&now);
+
+    sethomepath(fpath, cuser.userid);
+    strcpy(fpath, tempnam(fpath, "talk_"));
+    flog = fopen(fpath, "w");
+
+    setuserfile(genbuf, fn_talklog);
+
+    if ((log = fopen(genbuf, "w")))
+	fprintf(log, "[%d/%d %d:%02d] & %s\n",
+		ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour,
+		ptime->tm_min, save_page_requestor);
+    setutmpmode(TALK);
+
+    ch = 58 - strlen(save_page_requestor);
+    sprintf(genbuf, "%s�i%s", cuser.userid, cuser.username);
+    i = ch - strlen(genbuf);
+    if (i >= 0)
+	i = (i >> 1) + 1;
+    else
+    {
+	genbuf[ch] = '\0';
+	i = 1;
+    }
+    memset(data, ' ', i);
+    data[i] = '\0';
+
+    sprintf(mid_line, "\033[1;46;37m  �ͤѻ��a  \033[45m%s%s�j"
+	    " �P  %s%s\033[0m", data, genbuf, save_page_requestor, data);
+
+    memset(&mywin, 0, sizeof(mywin));
+    memset(&itswin, 0, sizeof(itswin));
+
+    i = b_lines >> 1;
+    mywin.eline = i - 1;
+    itswin.curln = itswin.sline = i + 1;
+    itswin.eline = b_lines - 1;
+
+    clear();
+    move(i, 0);
+    outs(mid_line);
+    move(0, 0);
+
+    add_io(fd, 0);
+
+    while (1)
+    {
+	ch = igetkey();
+	if (ch == I_OTHERDATA)
+	{
+	    datac = recv(fd, data, sizeof(data), 0);
+	    if (datac <= 0)
+		break;
+	    for (i = 0; i < datac; i++)
+		do_talk_char(&itswin, data[i]);
+	}
+	else
+	{
+	    if (ch == Ctrl('C'))
+	    {
+		if (im_leaving)
+		    break;
+		move(b_lines, 0);
+		clrtoeol();
+		outs("�A���@�� Ctrl-C �N��������͸��o�I");
+		im_leaving = 1;
+		continue;
+	    }
+	    if (im_leaving)
+	    {
+		move(b_lines, 0);
+		clrtoeol();
+		im_leaving = 0;
+	    }
+	    switch (ch)
+	    {
+	    case KEY_LEFT:	/* ��2byte����אּ�@byte */
+		ch = Ctrl('B');
+		break;
+	    case KEY_RIGHT:
+		ch = Ctrl('F');
+		break;
+	    case KEY_UP:
+		ch = Ctrl('P');
+		break;
+	    case KEY_DOWN:
+		ch = Ctrl('N');
+		break;
+	    }
+	    data[0] = (char) ch;
+	    if (send(fd, data, 1, 0) != 1)
+		break;
+	    if (log)
+		fprintf(log, "%c", (ch == Ctrl('M')) ? '\n' : (char) *data);
+	    do_talk_char(&mywin, *data);
+	}
+    }
+    if (log)
+	fclose(log);
+
+    add_io(0, 0);
+    close(fd);
+
+    if (flog)
+    {
+	char ans[4];
+	extern screenline_t *big_picture;
+	extern unsigned char scr_lns;
+	int i;
+
+	time(&now);
+	fprintf(flog, "\n\033[33;44m���O�e�� [%s] ...     \033[m\n",
+		Cdatelite(&now));
+	for (i = 0; i < scr_lns; i++)
+	    fprintf(flog, "%.*s\n", big_picture[i].len, big_picture[i].data);
+	fclose(flog);
+	more(fpath, NA);
+	getdata(b_lines - 1, 0, "�M��(C) ���ܳƧѿ�(M). (C/M)?[C]",
+		ans, 4, LCECHO);
+	if (*ans == 'm')
+	{
+	    fileheader_t mymail;
+	    char title[128];
+
+	    sethomepath(genbuf, cuser.userid);
+	    stampfile(genbuf, &mymail);
+	    mymail.savemode = 'H';	/* hold-mail flag */
+	    mymail.filemode = FILE_READ;
+	    strcpy(mymail.owner, "[��.��.��]");
+	    sprintf(mymail.title, "��ܰO�� \033[1;36m(%s)\033[m",
+		    getuserid(currutmp->destuid));
+	    sethomedir(title, cuser.userid);
+	    Rename(fpath, genbuf);
+	    append_record(title, &mymail, sizeof(mymail));
+	}
+	else
+	    unlink(fpath);
+	flog = 0;
+    }
+    setutmpmode(XINFO);
+}
+
+#define lockreturn(unmode, state) if(lockutmpmode(unmode, state)) return 
+
+static void my_talk(userinfo_t * uin, int fri_stat) {
+    int sock, msgsock, length, ch, error = 0;
+    struct sockaddr_in server;
+    pid_t pid;
+    char c;
+    char genbuf[4];
+
+    unsigned char mode0 = currutmp->mode;
+
+    ch = uin->mode;
+    strcpy(currauthor, uin->userid);
+
+    if (ch == EDITING || ch == TALK || ch == CHATING || ch == PAGE ||
+	ch == MAILALL || ch == MONITOR || ch == M_FIVE || ch == CHC ||
+	(!ch && (uin->chatid[0] == 1 || uin->chatid[0] == 3)) ||
+	uin->lockmode == M_FIVE || uin->lockmode == CHC)
+    {
+	outs("�H�a�b����");
+    }
+    else if (!HAS_PERM(PERM_SYSOP) &&
+	     (
+		 ((fri_stat& HRM) && !(fri_stat& HFM)) ||
+		 ((!uin->pager) && !(fri_stat & HFM))
+		 )
+	)
+    {
+	outs("��������I�s���F");
+    }
+    else if (!HAS_PERM(PERM_SYSOP) &&
+	     (
+		 ((fri_stat & HRM) && !(fri_stat& HFM)) ||
+		 uin->pager == 2
+		 )
+	)
+    {
+	outs("���ޱ��I�s���F");
+    }
+    else if (!HAS_PERM(PERM_SYSOP) &&
+	     !(fri_stat & HFM) && uin->pager == 4)
+    {
+	outs("���u�����n�ͪ��I�s");
+    }
+    else if (!(pid = uin->pid) /*|| (kill(pid, 0) == -1) */ )
+    {
+//      resetutmpent();
+	outs(msg_usr_left);
+    }
+    else
+    {
+	showplans(uin->userid);
+	getdata(2, 0, "�n�M�L(�o) (T)�ͤ�(F)�U���l��(P)���d��"
+		"(C)�U�H��(D)�U�t��(N)�S�Ƨ���H�F?[N] ", genbuf, 4, LCECHO);
+	switch (*genbuf)
+	{
+	case 'y':
+	case 't':
+	    uin->sig = SIG_TALK;
+	    break;
+	case 'f':
+	    lockreturn(M_FIVE, LOCK_THIS);
+	    uin->sig = SIG_GOMO;
+	    break;
+	case 'c':
+	    lockreturn(CHC, LOCK_THIS);
+	    uin->sig = SIG_CHC;
+	    break;
+	case 'd':
+	    uin->sig = SIG_DARK;
+	    break;
+	case 'p':
+	    reload_chicken();
+	    getuser(uin->userid);
+	    if (uin->lockmode == CHICKEN || currutmp->lockmode == CHICKEN)
+		error = 1;
+	    if (!cuser.mychicken.name[0] || !xuser.mychicken.name[0])
+		error = 2;
+	    if (error)
+	    {
+		outmsg(error == 2 ? "�ëD��H���i�d��" :
+		       "���@�誺�d�����b�ϥΤ�");
+		bell();
+		refresh();
+		sleep(1);
+		return;
+	    }
+	    uin->sig = SIG_PK;
+	    break;
+	default:
+	    return;
+	}
+
+	uin->turn = 1;
+	currutmp->turn = 0;
+	strcpy(uin->mateid, currutmp->userid);
+	strcpy(currutmp->mateid, uin->userid);
+
+	sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (sock < 0)
+	{
+	    perror("sock err");
+	    unlockutmpmode();
+	    return;
+	}
+	server.sin_family = PF_INET;
+	server.sin_addr.s_addr = INADDR_ANY;
+	server.sin_port = 0;
+	if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
+	{
+	    close(sock);
+	    perror("bind err");
+	    unlockutmpmode();
+	    return;
+	}
+	length = sizeof(server);
+	if (getsockname(sock, (struct sockaddr *) &server, &length) < 0)
+	{
+	    close(sock);
+	    perror("sock name err");
+	    unlockutmpmode();
+	    return;
+	}
+	currutmp->sockactive = YEA;
+	currutmp->sockaddr = server.sin_port;
+	currutmp->destuid = uin->uid;
+	setutmpmode(PAGE);
+	uin->destuip = currutmp - &utmpshm->uinfo[0];
+	if(pid > 0) kill(pid, SIGUSR1);
+	clear();
+	prints("���I�s %s.....\n��J Ctrl-D ����....", uin->userid);
+
+	listen(sock, 1);
+	add_io(sock, 5);
+	while (1)
+	{
+	    ch = igetch();
+	    if (ch == I_TIMEOUT)
+	    {
+		ch = uin->mode;
+		if (!ch && uin->chatid[0] == 1 &&
+		    uin->destuip == currutmp - &utmpshm->uinfo[0])
+		{
+		    bell();
+		    outmsg("���^����...");
+		    refresh();
+		}
+		else if (ch == EDITING || ch == TALK || ch == CHATING ||
+			 ch == PAGE || ch == MAILALL || ch == MONITOR ||
+			 ch == M_FIVE || ch == CHC ||
+			 (!ch && (uin->chatid[0] == 1 ||
+				  uin->chatid[0] == 3)))
+		{
+		    add_io(0, 0);
+		    close(sock);
+		    currutmp->sockactive = currutmp->destuid = 0;
+		    outmsg("�H�a�b����");
+		    pressanykey();
+		    unlockutmpmode();
+		    return;
+		}
+		else
+		{
+#ifdef linux
+		    add_io(sock, 20);	/* added for linux... achen */
+#endif
+		    move(0, 0);
+		    outs("�A");
+		    bell();
+
+		    uin->destuip = currutmp - &utmpshm->uinfo[0];
+		    if(pid <= 0 || kill(pid, SIGUSR1) == -1)
+		    {
+#ifdef linux
+			add_io(sock, 20);	/* added 4 linux... achen */
+#endif
+			outmsg(msg_usr_left);
+			refresh();
+			pressanykey();
+			unlockutmpmode();
+			return;
+		    }
+		    continue;
+		}
+	    }
+
+	    if (ch == I_OTHERDATA)
+		break;
+
+	    if (ch == '\004')
+	    {
+		add_io(0, 0);
+		close(sock);
+		currutmp->sockactive = currutmp->destuid = 0;
+		unlockutmpmode();
+		return;
+	    }
+	}
+
+	msgsock = accept(sock, (struct sockaddr *) 0, (int *) 0);
+	if (msgsock == -1)
+	{
+	    perror("accept");
+	    unlockutmpmode();
+	    return;
+	}
+	add_io(0, 0);
+	close(sock);
+	currutmp->sockactive = NA;
+	read(msgsock, &c, sizeof c);
+
+	if (c == 'y')
+	{
+	    sprintf(save_page_requestor, "%s (%s)",
+		    uin->userid, uin->username);
+	    /* gomo */
+	    switch (uin->sig)
+	    {
+	    case SIG_DARK:
+		main_dark(msgsock, uin);
+		break;
+	    case SIG_PK:
+		chickenpk(msgsock);
+		break;
+	    case SIG_GOMO:
+		gomoku(msgsock);
+		break;
+	    case SIG_CHC:
+		chc(msgsock);
+		break;
+	    case SIG_TALK:
+	    default:
+		do_talk(msgsock);
+	    }
+	}
+	else
+	{
+	    move(9, 9);
+	    outs("�i�^���j ");
+	    switch (c)
+	    {
+	    case 'a':
+		outs("�ڲ{�b�ܦ��A�е��@�|��A call �ڡA�n�ܡH");
+		break;
+	    case 'b':
+		prints("�藍�_�A�ڦ��Ʊ������A %s....", sig_des[uin->sig]);
+		break;
+	    case 'd':
+		outs("�ڭn�����o..�U���A��a..........");
+		break;
+	    case 'c':
+		outs("�Ф��n�n�ڦn�ܡH");
+		break;
+	    case 'e':
+		outs("��ڦ��ƶܡH�Х��ӫH��....");
+		break;
+	    case 'f':
+	    {
+		char msgbuf[60];
+
+		read(msgsock, msgbuf, 60);
+		prints("�藍�_�A�ڲ{�b�����A %s�A�]��\n", sig_des[uin->sig]);
+		move(10, 18);
+		outs(msgbuf);
+	    }
+	    break;
+	    case '1':
+		prints("%s�H����100�Ȩ��..", sig_des[uin->sig]);
+		break;
+	    case '2':
+		prints("%s�H����1000�Ȩ��..", sig_des[uin->sig]);
+		break;
+	    default:
+		prints("�ڲ{�b���Q %s ��.....:)", sig_des[uin->sig]);
+	    }
+	    close(msgsock);
+	}
+    }
+    currutmp->mode = mode0;
+    currutmp->destuid = 0;
+    unlockutmpmode();
+    pressanykey();
+}
+
+/* ��榡��Ѥ��� */
+#define US_PICKUP       1234
+#define US_RESORT       1233
+#define US_ACTION       1232
+#define US_REDRAW       1231
+
+static void t_showhelp() {
+    clear();
+
+    outs("\033[36m�i �𶢲�Ѩϥλ��� �j\033[m\n\n"
+	 "(��)(e)         �������}             (h)             �ݨϥλ���\n"
+	 "(��)/(��)(n)    �W�U����             (TAB)           �����ƧǤ覡\n"
+	 "(PgUp)(^B)      �W�����             ( )(PgDn)(^F)   �U�����\n"
+	 "(Hm)/($)(Ed)    ��/��                (S)             "
+	 "�ӷ�/�n�ʹy�z/���Z ����\n"
+	 "(m)             �H�H                 (q/c)           "
+	 "�d�ߺ���/�d��\n"
+	 "(r)             �\\Ū�H��            (l)             �ݤW�����T\n"
+	 "(f)             ����/�n�ͦC��        (�Ʀr)          ���ܸӨϥΪ�\n"
+	 "(p)             �����I�s��           (g/i)           ����/�����߱�\n"
+	 "(a/d/o)         �n�� �W�[/�R��/�ק�  (/)(s)          ����ID/�ʺٷj�M");
+
+    if (HAS_PERM(PERM_PAGE))
+    {
+	outs("\n\n\033[36m�i ��ͱM���� �j\033[m\n\n"
+	     "(��)(t)(Enter)  ��L���o���\n"
+	     "(w)             ���u Call in\n"
+	     "(b)             ��n�ͼs�� (�@�w�n�b�n�ͦC����)\n"
+	     "(^R)            �Y�ɦ^�� (���H Call in �A��)");
+    }
+
+    if (HAS_PERM(PERM_SYSOP))
+    {
+	outs("\n\n\033[36m�i �����M���� �j\033[m\n\n");
+	if (HAS_PERM(PERM_SYSOP))
+	    outs("(u)/(H)         �]�w�ϥΪ̸��/�������μҦ�\n");
+	outs("(R)/(K)         �d�ߨϥΪ̪��u��m�W/���a�J��X�h\n");
+    }
+    pressanykey();
+}
+
+static int listcuent(userinfo_t * uentp) {
+    if((!uentp->invisible || HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_SEECLOAK)))
+	AddNameList(uentp->userid);
+    return 0;
+}
+
+static void creat_list() {
+    CreateNameList();
+    apply_ulist(listcuent);
+}
+
+static int search_pickup(int num, int actor, pickup_t pklist[]) {
+    char genbuf[IDLEN + 2];
+
+    move(1, 0);
+    creat_list();
+    namecomplete(msg_uid, genbuf);
+    if (genbuf[0])
+    {
+	int n = (num + 1) % actor;
+	while (n != num)
+	{
+	    if (!strcasecmp(pklist[n].ui->userid, genbuf))
+		return n;
+	    if (++n >= actor)
+		n = 0;
+	}
+    }
+    return -1;
+}
+
+/* Kaede show friend description */
+static char *friend_descript(char *uident) {
+    static char *space_buf = "                    ";
+    static char desc_buf[80];
+    char fpath[80], name[IDLEN + 2], *desc, *ptr;
+    int len, flag;
+    FILE *fp;
+    char genbuf[200];
+
+    setuserfile(fpath, friend_file[0]);
+
+    if ((fp = fopen(fpath, "r")))
+    {
+	sprintf(name, "%s ", uident);
+	len = strlen(name);
+	desc = genbuf + 13;
+
+	while ((flag = (int) fgets(genbuf, STRLEN, fp)))
+	{
+	    if (!memcmp(genbuf, name, len))
+	    {
+		if ((ptr = strchr(desc, '\n')))
+		    ptr[0] = '\0';
+		if (desc)
+		    break;
+	    }
+	}
+	fclose(fp);
+	if (desc && flag)
+	    strcpy(desc_buf, desc);
+	else
+	    return space_buf;
+
+	return desc_buf;
+    }
+    else
+	return space_buf;
+}
+
+static char *descript(int show_mode, userinfo_t * uentp, time_t diff,
+		      fromcache_t * fcache) {
+    switch (show_mode)
+    {
+    case 1:
+	return friend_descript(uentp->userid);
+    case 0:
+	return (((uentp->pager != 2 && uentp->pager != 3 && diff) ||
+		 HAS_PERM(PERM_SYSOP)) ?
+#ifdef WHERE
+		uentp->from_alias ? fcache->replace[uentp->from_alias] :
+		uentp->from
+#else
+		uentp->from
+#endif
+		: "*");
+    case 2:
+	sprintf(description, "%3d/%3d/%3d", uentp->five_win,
+		uentp->five_lose, uentp->five_tie);
+	description[20] = 0;
+	return description;
+    default:
+	syslog(LOG_WARNING, "damn!!! what's wrong?? show_mode = %d",
+	       show_mode);
+	return "";
+    }
+}
+
+static int pickup_user_cmp(time_t now, int sortedway, int cmp_fri, 
+    pickup_t pklist[], int *bfriends_number, int *ifh_number,
+    int *hfm_number,int *irh_number, char *keyword)
+{
+    int i, fri_stat, is_friend, count=0, diff;
+    userinfo_t *uentp;
+    for (i=0;i<utmpshm->number;i++)
+    {
+        uentp = (utmpshm->sorted[utmpshm->currsorted][sortedway][i]);
+	if (!uentp || !uentp->pid) continue;
+	fri_stat = friend_stat(currutmp, uentp);
+	if(uentp->uid==currutmp->uid)
+	    fri_stat = fri_stat|IFH|HFM;
+        is_friend = (fri_stat & IRH) && !(fri_stat & IFH) ? 0 :
+		    fri_stat & ST_FRIEND;
+	if (!isvisible_stat(currutmp, uentp, fri_stat) || 
+                  ((cmp_fri==1 && !is_friend) ||
+                  (cmp_fri==-1 && is_friend)) ||
+                  (keyword[0] && !strcasestr(uentp->username,keyword))
+		  ) continue;
+	if (bfriends_number && fri_stat & IBH) (*bfriends_number)++;
+	if (ifh_number && fri_stat & IFH) (*ifh_number)++;
+	if (hfm_number && fri_stat & HFM) (*hfm_number)++;
+	if (irh_number && fri_stat & IRH) (*irh_number)++;
+#ifdef SHOW_IDLE_TIME
+	diff = now - uentp->lastact;
+#ifdef DOTIMEOUT
+	    /* prevent fault /dev mount from kicking out users */
+	if ((diff > curr_idle_timeout + 10) &&
+		(diff < 60 * 60 * 24 * 5))
+	    {
+ 	     if ((uentp->pid <= 0 || kill(uentp->pid, SIGHUP) == -1) &&
+		    (errno == ESRCH))
+		    purge_utmp(uentp);
+		continue;
+	    }
+#endif
+	pklist[count].idle = diff;
+#endif
+	pklist[count].friend = fri_stat;
+	pklist[count].ui = uentp;
+	count++;
+    }
+    return count;
+}
+
+static int cmputmpfriend(const void *i, const void *j)
+{
+    if((((pickup_t*)j)->friend&ST_FRIEND)==(((pickup_t*)i)->friend&ST_FRIEND))
+	return strcasecmp( ((pickup_t*)i)->ui->userid,
+			   ((pickup_t*)j)->ui->userid);
+    else
+	return (((pickup_t*)j)->friend&ST_FRIEND) -
+	       (((pickup_t*)i)->friend&ST_FRIEND);
+} 
+
+static void pickup_user() {
+    static int real_name = 0;
+    static int show_mode = 0;
+    static int show_uid = 0;
+    static int show_board = 0;
+    static int show_pid = 0;
+    static int num = 0;
+    char genbuf[200];
+
+#ifdef WHERE
+    extern struct fromcache_t *fcache;
+#endif
+
+    register userinfo_t *uentp;
+    register pid_t pid0 = 0;	/* Ptt �w�� */
+    register int id0 = 0;	/* US_PICKUP�ɪ���Х� */
+    register int state = US_PICKUP, ch;
+    register int actor = 0, head, foot;
+    int fri_stat, bfriends_number, ifh_number, irh_number, hfm_number;
+    int savemode = currstat;
+    int i, sortedway;			/* �u�Oloop���� */
+    time_t diff, freshtime;
+    pickup_t pklist[USHM_SIZE];	/* parameter Ptt�� */
+/* num : �{�b����� */
+/* foot: �������}�} */
+    char buf[20],keyword[13]="";		/* actor:�@���h��user */
+    char pagerchar[5] = "* -Wf";
+    char *msg_pickup_way[PICKUP_WAYS] =
+    {
+        "��! �B��",
+	"���ͥN��",
+	"���ͰʺA",
+	"�o�b�ɶ�",
+	"�Ӧۦ��",
+	"���l��  ",
+//	"�k�h�u��"
+    };
+    char *MODE_STRING[MAX_SHOW_MODE] =
+    {
+	"�G�m",
+	"�n�ʹy�z",
+	"���l�Ѿ��Z"
+    };
+    char
+	*Mind[] =
+    {"   ",
+     "^-^", "^_^", "Q_Q", "@_@", "/_\\", "=_=", "-_-", "-.-", ">_<",
+     "-_+", "!_!", "o_o", "z_Z", "O_O", "O.O", "$_$", "^*^", "O_<",
+     "��!", "��!", "�s!", "��!", ":) ", ":( ", ":~ ", ":q ", ":O ",
+     ":D ", ":p ", ";) ", ":> ", ";> ", ":< ", ":)~", ":D~", ">< ",
+     "^^;", "^^|", "��;",  NULL};
+     
+    while (1) 
+    {
+	if (utmpshm->uptime > freshtime || state == US_PICKUP ||
+            state ==US_RESORT)
+	{ 
+	    state = US_PICKUP;
+	    time(&freshtime);
+	    ifh_number=hfm_number=irh_number=bfriends_number = actor = ch = 0;
+            if(pickup_way==0)
+	        sortedway=0;
+            else
+		sortedway=pickup_way-1;
+
+	    //qsort(pklist,actor,sizeof(pickup_t),cmputmpfriend);
+            if(pickup_way==0 || (cuser.uflag & FRIEND_FLAG))
+              {
+                actor=pickup_user_cmp(freshtime, sortedway, 1, 
+                       pklist, &bfriends_number, &ifh_number, &hfm_number,
+			NULL,keyword);
+                if(sortedway==0)
+		   qsort(pklist,actor,sizeof(pickup_t),cmputmpfriend);
+                if(!(cuser.uflag & FRIEND_FLAG))
+                   actor=pickup_user_cmp(freshtime, sortedway, -1, 
+                              pklist+actor, NULL, NULL, NULL, &irh_number,
+			      keyword);
+              }
+            else
+              {
+                actor=pickup_user_cmp(freshtime, sortedway, 0, 
+                       pklist, &bfriends_number, &ifh_number, &hfm_number,
+		       &irh_number, keyword);
+              }
+
+
+	    if (!actor)
+	    {
+                if(keyword[0])
+		{
+			mprints(b_lines-1,0,
+		  		"�j�M�������H !!");
+			keyword[0]=0;
+			pressanykey();
+			continue;
+		}
+		getdata(b_lines - 1, 0,
+			"�A���B���٨S�W���A�n�ݬݤ@����Ͷ�(Y/N)�H[Y]",
+			genbuf, 4, LCECHO);
+		if (genbuf[0] != 'n')
+		{
+		    cuser.uflag &= ~FRIEND_FLAG;
+		    continue;
+		}
+		return;
+	    }
+	}
+	if (state >= US_ACTION)
+	{
+	    showtitle((cuser.uflag & FRIEND_FLAG) ? "�n�ͦC��" : "�𶢲��",
+		      BBSName);
+	    prints("  �ƧǡG[%s] �W���H�ơG%-4d\033[1;32m�ڪ��B�͡G%-3d"
+		   "\033[33m�P�ڬ��͡G%-3d\033[36m�O�͡G%-4d\033[31m�a�H�G"
+		   "%-2d\033[m\n"
+		   "\033[7m  %s P%c�N��         %-17s%-17s%-13s%-10s\033[m\n",
+		   msg_pickup_way[pickup_way], actor, ifh_number,
+		   hfm_number,  bfriends_number, irh_number,
+		   show_uid ? "UID" : "No.", 
+		  (HAS_PERM(PERM_SEECLOAK) || HAS_PERM(PERM_SYSOP)) ? 'C' : ' ',
+		   real_name ? "�m�W" : "�ʺ�",
+		   MODE_STRING[show_mode],
+		   show_board ? "Board" : "�ʺA",
+		   show_pid ? "       PID" : "�Ƶ�  �o�b"
+		);
+	}
+	else
+	{
+	    move(3, 0);
+	    clrtobot();
+	}
+	if (pid0)
+	    for (ch = 0; ch < actor; ch++)
+	    {
+		if (pid0 == (pklist[ch].ui)->pid &&
+		    id0 == 256 * pklist[ch].ui->userid[0] +
+		    pklist[ch].ui->userid[1])
+		{
+		    num = ch;
+		}
+	    }
+	if (num < 0)
+	    num = 0;
+	else if (num >= actor)
+	    num = actor - 1;
+	head = (num / p_lines) * p_lines;
+	foot = head + p_lines;
+	if (foot > actor)
+	    foot = actor;
+	for (ch = head; ch < foot; ch++)
+	{
+	    uentp = pklist[ch].ui;
+
+	    if (!uentp->pid)
+	    {
+	        prints("%5d   < ������..>\n",ch);
+		continue;
+	    }
+#ifdef SHOW_IDLE_TIME
+	    diff = pklist[ch].idle;
+	    if (diff > 59990) diff = 59990;   /* Doma: �H�K�@�j�ꪺ�o�b�ɶ� */
+	    if (diff > 0)
+		sprintf(buf, "%3ld'%02ld", diff / 60, diff % 60);
+	    else
+		buf[0] = '\0';
+#else
+	    buf[0] = '\0';
+#endif
+            i = pklist[ch].friend;
+#ifdef SHOWPID
+	    if (show_pid)
+		sprintf(buf, "%6d", uentp->pid);
+#endif
+	    if (PERM_HIDE(uentp))
+		state = 9;
+	    else if(currutmp == uentp)
+		state =10;
+	    else if(i & IRH && !(i & IFH))
+	        state = 8;
+	    else
+		state =(i&ST_FRIEND)>>2;
+	    diff = uentp->pager & !(i&HRM);
+	    prints("%5d %c%c%s%-13s%-17.16s\033[m%-17.16s%-13.13s"
+			     "\33[33m%-4.4s\33[m%s\n",
+#ifdef SHOWUID
+		   show_uid ? uentp->uid :
+#endif
+		   (ch + 1),
+		   (i & HRM) ? 'X' :
+		   pagerchar[uentp->pager % 5],
+		   (uentp->invisible ? ')' : ' '),
+		      fcolor[state],
+		   /* %s */
+		   uentp->userid,
+
+		   /* %-13s �ʺ� */
+#ifdef REALINFO
+		   real_name ? uentp->realname :
+#endif
+		   uentp->username,
+		   /* %-17.16s �G�m */
+		   descript(show_mode, uentp, diff, fcache),
+
+		   /* %-17.16s �ݪO */
+#ifdef SHOWBOARD
+		   show_board ? (uentp->brc_id == 0 ? "" :
+				 bcache[uentp->brc_id - 1].brdname) :
+#endif
+		   /* %-13.13s */
+		   modestring(uentp, 0),
+		   /* %4s �Ƶ� */
+		   ((uentp->userlevel & PERM_VIOLATELAW) ? "�q�r" :
+		    (uentp->birth ? "�جP" :
+		     Mind[uentp->mind])),
+		   /* %s �o�b */
+		   buf);
+	}
+	if (state == US_PICKUP)
+	    continue;
+
+	move(b_lines, 0);
+	outs("\033[31;47m(TAB/f)\033[30m�Ƨ�/�n�� \033[31m(t)\033[30m��� "
+	     "\033[31m(a/d/o)\033[30m��� \033[31m(q)\033[30m�d�� "
+	     "\033[31m(w)\033[30m���y \033[31m(m)\033[30m�H�H \033[31m(h)"
+	     "\033[30m�u�W���U \033[m");
+	state = 0;
+	while (!state)
+	{
+	    ch = cursor_key(num + 3 - head, 0);
+	    if (ch == KEY_RIGHT || ch == '\n' || ch == '\r')
+		ch = 't';
+
+	    switch (ch)
+	    {
+	    case KEY_LEFT:
+	    case 'e':
+	    case 'E':
+		if(!keyword[0]) return;
+		keyword[0]=0;
+	        state = US_PICKUP;
+		break;
+
+	    case KEY_TAB:
+		pickup_way = (pickup_way + 1) % PICKUP_WAYS;
+		state = US_RESORT;
+		num = 0;
+		break;
+
+	    case KEY_DOWN:
+	    case 'n':
+	    case 'j':
+		if (++num < actor)
+		{
+		    if (num >= foot)
+			state = US_REDRAW;
+		    break;
+		}
+	    case '0':
+	    case KEY_HOME:
+		num = 0;
+		if (head)
+		    state = US_REDRAW;
+		break;
+	    case 'H':
+		if (HAS_PERM(PERM_SYSOP))
+		{
+		    currutmp->userlevel ^= PERM_DENYPOST;
+		    state = US_REDRAW;
+		}
+		break;
+	    case 'D':
+		if (HAS_PERM(PERM_SYSOP))
+		{
+		    char buf[100];
+
+		    sprintf(buf, "�N�� [%s]�G", currutmp->userid);
+		    if (!getdata(1, 0, buf, currutmp->userid, IDLEN + 1,
+				 DOECHO))
+			strcpy(currutmp->userid, cuser.userid);
+		    state = US_REDRAW;
+		}
+		break;
+	    case 'F':
+		if (HAS_PERM(PERM_SYSOP))
+		{
+		    char buf[100];
+
+		    sprintf(buf, "�G�m [%s]�G", currutmp->from);
+		    if (!getdata(1, 0, buf, currutmp->from, 17, DOECHO))
+			strncpy(currutmp->from, fromhost, 23);
+		    state = US_REDRAW;
+		}
+		break;
+	    case 'C':
+#if !HAVE_FREECLOAK
+		if (HAS_PERM(PERM_CLOAK))
+#endif
+		{
+		    currutmp->invisible ^= 1;
+		    state = US_REDRAW;
+		}
+		break;
+	    case ' ':
+	    case KEY_PGDN:
+	    case Ctrl('F'):
+		if (foot < actor)
+		{
+		    num += p_lines;
+		    state = US_REDRAW;
+		    break;
+		}
+		if (head)
+		    num = 0;
+		state = US_PICKUP;
+		break;
+	    case KEY_UP:
+	    case 'k':
+		if (--num < head)
+		{
+		    if (num < 0)
+		    {
+			num = actor - 1;
+			if (actor == foot)
+			    break;
+		    }
+		    state = US_REDRAW;
+		}
+		break;
+	    case KEY_PGUP:
+	    case Ctrl('B'):
+	    case 'P':
+		if (head)
+		{
+		    num -= p_lines;
+		    state = US_REDRAW;
+		    break;
+		}
+
+	    case KEY_END:
+	    case '$':
+		num = actor - 1;
+		if (foot < actor)
+		    state = US_REDRAW;
+		break;
+
+	    case '/':
+	        getdata_buf(b_lines-1,0,"�п�J�ʺ�����r:",keyword, 12,
+				 DOECHO);
+		state = US_PICKUP;
+	        break;
+	    case 's':
+		if ((i = search_pickup(num, actor, pklist)) >= 0)
+		    num = i;
+		state = US_ACTION;
+		break;
+
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	    case '8':
+	    case '9':
+	    {		/* Thor: �i�H���Ʀr����ӤH */
+		int tmp;
+		if ((tmp = search_num(ch, actor - 1)) >= 0)
+		    num = tmp;
+		state = US_REDRAW;
+	    }
+	    break;
+#ifdef REALINFO
+	    case 'R':		/* ��ܯu��m�W */
+		if (HAS_PERM(PERM_SYSOP))
+		    real_name ^= 1;
+		state = US_PICKUP;
+		break;
+#endif
+#ifdef SHOWUID
+	    case 'U':
+		if (HAS_PERM(PERM_SYSOP))
+		    show_uid ^= 1;
+		state = US_PICKUP;
+		break;
+#endif
+#ifdef  SHOWBOARD
+	    case 'Y':
+		if (HAS_PERM(PERM_SYSOP))
+		    show_board ^= 1;
+		state = US_PICKUP;
+		break;
+#endif
+#ifdef  SHOWPID
+	    case '#':
+		if (HAS_PERM(PERM_SYSOP))
+		    show_pid ^= 1;
+		state = US_PICKUP;
+		break;
+#endif
+
+	    case 'b':		/* broadcast */
+		if (cuser.uflag & FRIEND_FLAG || HAS_PERM(PERM_SYSOP))
+		{
+		    int actor_pos = actor;
+		    char ans[4];
+
+		    state = US_PICKUP;
+		    if (!getdata(0, 0, "�s���T��:", genbuf, 60, DOECHO))
+			break;
+		    if (getdata(0, 0, "�T�w�s��? [Y]", ans, 4, LCECHO) &&
+			*ans == 'n')
+			break;
+		    while (actor_pos)
+		    {
+			uentp = pklist[--actor_pos].ui;
+		        fri_stat = pklist[actor_pos].friend; 
+			if (uentp->pid &&
+			    currpid != uentp->pid &&
+			    uentp->pid > 0 && kill(uentp->pid, 0) != -1 &&
+			    (HAS_PERM(PERM_SYSOP) ||
+			     (uentp->pager != 3 &&
+			      (uentp->pager != 4 || fri_stat & HFM))))
+			    my_write(uentp->pid, genbuf, uentp->userid, HAS_PERM(PERM_SYSOP) ? 3 : 1);
+		    }
+		}
+		break;
+	    case 'S':		/* ��ܦn�ʹy�z */
+		show_mode = (++show_mode) % MAX_SHOW_MODE;
+		state = US_PICKUP;
+		break;
+	    case 'u':		/* �u�W�ק��� */
+	    case 'K':		/* ���a�J��X�h */
+		if (!HAS_PERM(PERM_ACCOUNTS))
+		    continue;
+		state = US_ACTION;
+		break;
+	    case 'i':
+		state = US_ACTION;
+		break;
+	    case Ctrl('S'):
+		state = US_ACTION;
+		break;
+	    case 't':
+	    case 'w':
+		if (!(cuser.userlevel & PERM_LOGINOK))
+		    continue;
+		state = US_ACTION;
+		break;
+	    case 'a':
+	    case 'd':
+	    case 'o':
+	    case 'f':
+	    case 'g':
+		if (!HAS_PERM(PERM_LOGINOK))
+		    /* ���U�~�� Friend */
+		    break;
+		if (ch == 'f')
+		{
+		    cuser.uflag ^= FRIEND_FLAG;
+		    state = US_PICKUP;
+		    break;
+		}
+		state = US_ACTION;
+		break;
+	    case 'q':
+	    case 'c':
+	    case 'm':
+	    case 'r':
+	    case 'l':
+		/* guest �u�� query */
+		if (!cuser.userlevel && ch != 'q' && ch != 'l')
+		    break;
+	    case 'h':
+		state = US_ACTION;
+		break;
+	    case 'p':
+		if (HAS_PERM(PERM_BASIC))
+		{
+		    t_pager();
+		    state = US_REDRAW;
+		}
+		break;
+	    case 'W':
+		{
+		    int     tmp;
+		    char    *wm[3] = {"�@��", "�i��", "����"};
+		    tmp = cuser.uflag2 & WATER_MASK;
+		    cuser.uflag2 -= tmp;
+		    tmp = (tmp + 1) % 3;
+		    cuser.uflag2 |= tmp;
+		    prints("������ %s ���y�Ҧ�", wm[tmp]);
+		    refresh();
+		    sleep(1);
+		    state = US_REDRAW;
+		}
+	    default:		/* refresh screen */
+		state = US_REDRAW;
+	    }
+	}
+
+	if (state != US_ACTION)
+	{
+	    pid0 = 0;
+	    continue;
+	}
+
+	/* Ptt decide cur */
+	uentp = pklist[num].ui;
+        fri_stat = friend_stat(currutmp, uentp);
+	pid0 = uentp->pid;
+	id0 = 256 * uentp->userid[0] + uentp->userid[1];
+
+	if (ch == 'w')
+	{
+	    if ((uentp->pid != currpid) &&
+		(HAS_PERM(PERM_SYSOP) ||
+		 (uentp->pager != 3 &&
+		  (fri_stat & HFM || uentp->pager != 4))))
+	    {
+		cursor_show(num + 3 - head, 0);
+		sprintf(genbuf, "Call-In %s �G", uentp->userid);
+		my_write(uentp->pid, genbuf, uentp->userid, 0);
+	    }
+	}
+	else if (ch == 'l')
+	{			/* Thor: �� Last call in */
+	    t_display();
+	}
+	else
+	{
+	    switch (ch)
+	    {
+	    case 'r':
+		m_read();
+		break;
+	    case 'g':		/* give money */
+		move(b_lines - 2, 0);
+		if (strcmp(uentp->userid, cuser.userid))
+		{
+		    sprintf(genbuf, "�n�� %s �h�ֿ��O?  ", uentp->userid);
+		    outs(genbuf);
+		    if (getdata(b_lines - 1, 0, "[�Ȧ���b]:", genbuf, 7,
+				LCECHO))
+		    {
+			clrtoeol();
+			if ((ch = atoi(genbuf)) <= 0)
+			    break;
+			reload_money();
+			if (ch > cuser.money)
+			    outs("\033[41m �{������~~\033[m");
+			else
+			{
+			    deumoney(uentp->uid, ch);
+			    sprintf(genbuf, "\033[44m ��..�ٳѤU %d ��.."
+				    "\033[m", demoney(-1*ch));
+			    outs(genbuf);
+			    sprintf(genbuf, "%s\t��%s\t%d\t%s", cuser.userid,
+				    uentp->userid, ch,
+				    ctime(&currutmp->lastact));
+			    log_file(FN_MONEY, genbuf);
+			    mail_redenvelop(cuser.userid, uentp->userid, ch, 'Y');
+			}
+		    }
+		    else
+		    {
+			clrtoeol();
+			outs("\033[41m �������! \033[m");
+		    }
+		}
+		else
+		    outs("\033[33m �ۤv���ۤv? �A��..\033[m");
+		refresh();
+		sleep(1);
+		break;
+#ifdef SHOWMIND
+	    case 'i':
+		move(3,0);
+		clrtobot();
+		for (i = 1; Mind[i]!=NULL; i++)
+		{
+		    move(5+(i-1)/7,((i-1)%7)*10);
+		    prints("%2d: %s",i,Mind[i]);
+		}
+		getdata(b_lines - 1, 0, "�A�{�b���߱� 0:�L q���� [q]:",
+				genbuf, 3, LCECHO);
+		if (genbuf[0] && genbuf[0] != 'q')
+	               currutmp->mind=atoi(genbuf)%i;
+		state = US_REDRAW;
+		break;
+#endif
+	    case 'a':
+		friend_add(uentp->userid, FRIEND_OVERRIDE);
+		friend_load();
+		state = US_PICKUP;
+		break;
+	    case 'd':
+		friend_delete(uentp->userid, FRIEND_OVERRIDE);
+		friend_load();
+		state = US_PICKUP;
+		break;
+	    case 'o':
+		t_override();
+		state = US_PICKUP;
+		break;
+	    case 'K':
+		if (uentp->pid > 0 && kill(uentp->pid, 0) != -1)
+		{
+		    move(1, 0);
+		    clrtobot();
+		    move(2, 0);
+		    my_kick(uentp);
+		    state = US_PICKUP;
+		}
+		break;
+	    case 'm':
+		stand_title("�H  �H");
+		prints("[�H�H] ���H�H�G%s", uentp->userid);
+		my_send(uentp->userid);
+		break;
+	    case 'q':
+		strcpy(currauthor, uentp->userid);
+		my_query(uentp->userid);
+		break;
+	    case 'c':
+		chicken_query(uentp->userid);
+		break;
+	    case 'u':		/* Thor: �i�u�W�d�ݤέק�ϥΪ� */
+	    {
+		int id;
+		userec_t muser;
+
+		strcpy(currauthor, uentp->userid);
+		stand_title("�ϥΪ̳]�w");
+		move(1, 0);
+		if ((id = getuser(uentp->userid)))
+		{
+		    memcpy(&muser, &xuser, sizeof(muser));
+		    user_display(&muser, 1);
+		    uinfo_query(&muser, 1, id);
+		}
+	    }
+	    break;
+
+	    case 'h':		/* Thor: �� Help */
+		t_showhelp();
+		break;
+
+	    case 't':
+		if (uentp->pid != currpid &&
+		    (strcmp(uentp->userid, cuser.userid)))
+		{
+		    move(1, 0);
+		    clrtobot();
+		    move(3, 0);
+		    my_talk(uentp, fri_stat);
+		    state = US_PICKUP;
+		}
+		break;
+	    }
+	}
+	setutmpmode(savemode);
+    }
+}
+
+int t_users() {
+    int destuid0 = currutmp->destuid;
+    int mode0 = currutmp->mode;
+    int stat0 = currstat;
+
+    setutmpmode(LUSERS);
+    pickup_user();
+    currutmp->mode = mode0;
+    currutmp->destuid = destuid0;
+    currstat = stat0;
+    return 0;
+}
+
+int t_pager() {
+    currutmp->pager = (currutmp->pager + 1) % 5;
+    return 0;
+}
+
+int t_idle() {
+    int destuid0 = currutmp->destuid;
+    int mode0 = currutmp->mode;
+    int stat0 = currstat;
+    char genbuf[20];
+    char buf[80], passbuf[PASSLEN];
+
+    setutmpmode(IDLE);
+    getdata(b_lines - 1, 0, "�z�ѡG[0]�o�b (1)���q�� (2)�V�� (3)���O�� "
+	    "(4)�˦� (5)ù�� (6)��L (Q)�S�ơH", genbuf, 3, DOECHO);
+    if (genbuf[0] == 'q' || genbuf[0] == 'Q')
+    {
+	currutmp->mode = mode0;
+	currstat = stat0;
+	return 0;
+    }
+    else if (genbuf[0] >= '1' && genbuf[0] <= '6')
+	currutmp->destuid = genbuf[0] - '0';
+    else
+	currutmp->destuid = 0;
+
+    if (currutmp->destuid == 6)
+	if (!cuser.userlevel ||
+	    !getdata(b_lines - 1, 0, "�o�b���z�ѡG", currutmp->chatid, 11,
+		     DOECHO))
+	    currutmp->destuid = 0;
+    do
+    {
+	move(b_lines - 2, 0);
+	clrtoeol();
+	sprintf(buf, "(��w�ù�)�o�b��]: %s", (currutmp->destuid != 6) ?
+		IdleTypeTable[currutmp->destuid] : currutmp->chatid);
+	outs(buf);
+	refresh();
+	getdata(b_lines - 1, 0, MSG_PASSWD, passbuf, PASSLEN, NOECHO);
+	passbuf[8] = '\0';
+    }
+    while (!checkpasswd(cuser.passwd, passbuf) &&
+	   strcmp(STR_GUEST, cuser.userid));
+
+    currutmp->mode = mode0;
+    currutmp->destuid = destuid0;
+    currstat = stat0;
+
+    return 0;
+}
+
+int t_qchicken() {
+    char uident[STRLEN];
+
+    stand_title("�d���d��");
+    usercomplete(msg_uid, uident);
+    if (uident[0])
+	chicken_query(uident);
+    return 0;
+}
+
+int t_query() {
+    char uident[STRLEN];
+
+    stand_title("�d�ߺ���");
+    usercomplete(msg_uid, uident);
+    if (uident[0])
+	my_query(uident);
+    return 0;
+}
+
+int t_talk() {
+    char uident[16];
+    int tuid, unum, ucount;
+    userinfo_t *uentp;
+    char genbuf[4];
+/*
+    if (count_ulist() <= 1)
+    {
+	outs("�ثe�u�W�u���z�@�H�A���ܽЪB�ͨӥ��{�i" BBSNAME "�j�a�I");
+	return XEASY;
+    }
+*/
+    stand_title("���}�ܧX�l");
+    creat_list();
+    namecomplete(msg_uid, uident);
+    if (uident[0] == '\0')
+	return 0;
+
+    move(3, 0);
+    if (!(tuid = searchuser(uident)) || tuid == usernum)
+    {
+	outs(err_uid);
+	pressanykey();
+	return 0;
+    }
+
+/* multi-login check */
+    unum = 1;
+    while ((ucount = count_logins(tuid, 0)) > 1)
+    {
+	outs("(0) ���Q talk �F...\n");
+	count_logins(tuid, 1);
+	getdata(1, 33, "�п�ܤ@�Ӳ�ѹ�H [0]�G", genbuf, 4, DOECHO);
+	unum = atoi(genbuf);
+	if (unum == 0)
+	    return 0;
+	move(3, 0);
+	clrtobot();
+	if (unum > 0 && unum <= ucount)
+	    break;
+    }
+
+    if ((uentp = (userinfo_t *) search_ulistn(tuid, unum)))
+	my_talk(uentp, friend_stat(currutmp, uentp));
+
+    return 0;
+}
+
+/* ���H�Ӧ���l�F�A�^���I�s�� */
+static userinfo_t *uip;
+void talkreply() {
+    struct hostent *h;
+    char buf[4];
+    struct sockaddr_in sin;
+    char genbuf[200];
+    int a, sig = currutmp->sig;
+
+    talkrequest = NA;
+    uip = &utmpshm->uinfo[currutmp->destuip];
+    sprintf(page_requestor, "%s (%s)", uip->userid, uip->username);
+    currutmp->destuid = uip->uid;
+    currstat = XMODE;		/* �קK�X�{�ʵe */
+
+    clear();
+
+    prints("\n\n");
+    prints("       (Y) ���ڭ� %s �a�I"
+	   "     (A) �ڲ{�b�ܦ��A�е��@�|��A call ��\n", sig_des[sig]);
+    prints("       (N) �ڲ{�b���Q %s"
+	   "      (B) �藍�_�A�ڦ��Ʊ������A %s\n",
+	   sig_des[sig], sig_des[sig]);
+    prints("       (C) �Ф��n�n�ڦn�ܡH"
+	   "     (D) �ڭn�����o..�U���A��a.......\n");
+    prints("       (E) ���ƶܡH�Х��ӫH"
+	   "     (F) \033[1;33m�ڦۤv��J�z�Ѧn�F...\033[m\n");
+    prints("       (1) %s�H����100�Ȩ��"
+	   "  (2) %s�H����1000�Ȩ��..\n\n", sig_des[sig], sig_des[sig]);
+
+    getuser(uip->userid);
+    currutmp->msgs[0].pid = uip->pid;
+    strcpy(currutmp->msgs[0].userid, uip->userid);
+    strcpy(currutmp->msgs[0].last_call_in, "�I�s�B�I�s�Ať��Ц^�� (Ctrl-R)");
+    prints("���Ӧ� [%s]�A�@�W�� %d ���A�峹 %d �g\n",
+	   uip->from, xuser.numlogins, xuser.numposts);
+    showplans(uip->userid);
+    show_last_call_in(0);
+
+    sprintf(genbuf, "�A�Q�� %s %s�ڡH�п��(Y/N/A/B/C/D/E/F/1/2)[N] ",
+	    page_requestor, sig_des[sig]);
+    getdata(0, 0, genbuf, buf, 4, LCECHO);
+
+    if (uip->mode != PAGE)
+    {
+	sprintf(genbuf, "%s�w����I�s�A��Enter�~��...", page_requestor);
+	getdata(0, 0, genbuf, buf, 4, LCECHO);
+	return;
+    }
+    currutmp->msgcount = 0;
+    strcpy(save_page_requestor, page_requestor);
+    memset(page_requestor, 0, sizeof(page_requestor));
+    if (!(h = gethostbyname("localhost")))
+    {
+	perror("gethostbyname");
+	return;
+    }
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = h->h_addrtype;
+    memcpy(&sin.sin_addr, h->h_addr, h->h_length);
+    sin.sin_port = uip->sockaddr;
+    a = socket(sin.sin_family, SOCK_STREAM, 0);
+    if ((connect(a, (struct sockaddr *) &sin, sizeof(sin))))
+    {
+	perror("connect err");
+	return;
+    }
+    if (!buf[0] || !strchr("yabcdef12", buf[0]))
+	buf[0] = 'n';
+    write(a, buf, 1);
+    if (buf[0] == 'f' || buf[0] == 'F')
+    {
+	if (!getdata(b_lines, 0, "���઺��]�G", genbuf, 60, DOECHO))
+	    strcpy(genbuf, "���i�D�A�� !! ^o^");
+	write(a, genbuf, 60);
+    }
+    uip->destuip = currutmp - &utmpshm->uinfo[0];
+    if (buf[0] == 'y')
+	switch (sig)
+	{
+	case SIG_DARK:
+	    main_dark(a, uip);
+	    break;
+	case SIG_PK:
+	    chickenpk(a);
+	    break;
+	case SIG_GOMO:
+	    gomoku(a);
+	    break;
+	case SIG_CHC:
+	    chc(a);
+	    break;
+	case SIG_TALK:
+	default:
+	    do_talk(a);
+	}
+    else
+	close(a);
+    clear();
+}
+
+/* ���ͰʺA²�� */
+/* not used
+static int shortulist(userinfo_t * uentp) {
+    static int lineno, fullactive, linecnt;
+    static int moreactive, page, num;
+    char uentry[50];
+    int state;
+
+    if (!lineno)
+    {
+	lineno = 3;
+	page = moreactive ? (page + p_lines * 3) : 0;
+	linecnt = num = moreactive = 0;
+	move(1, 70);
+	prints("Page: %d", page / (p_lines) / 3 + 1);
+	move(lineno, 0);
+    }
+
+    if (uentp == NULL)
+    {
+	int finaltally;
+
+	clrtoeol();
+	move(++lineno, 0);
+	clrtobot();
+	finaltally = fullactive;
+	lineno = fullactive = 0;
+	return finaltally;
+    }
+    if ((!HAS_PERM(PERM_SYSOP) &&
+	 !HAS_PERM(PERM_SEECLOAK) &&
+	 uentp->invisible) ||
+	((friend_stat(currutmp, uentp) & HRM) &&
+	 !HAS_PERM(PERM_SYSOP)))
+    {
+	if (lineno >= b_lines)
+	    return 0;
+	if (num++ < page)
+	    return 0;
+	memset(uentry, ' ', 25);
+	uentry[25] = '\0';
+    }
+    else
+    {
+	fullactive++;
+	if (lineno >= b_lines)
+	{
+	    moreactive = 1;
+	    return 0;
+	}
+	if (num++ < page)
+	    return 0;
+
+        state = (currutmp == uentp) ? 10 :
+                     (friend_stat(currutmp,uentp)&ST_FRIEND)>>2;
+
+	if (PERM_HIDE(uentp))
+	    state = 9;
+
+	sprintf(uentry, "%s%-13s%c%-10s%s ", fcolor[state],
+		uentp->userid, uentp->invisible ? '#' : ' ',
+		modestring(uentp, 1), state ? "\033[0m" : "");
+    }
+    if (++linecnt < 3)
+    {
+	strcat(uentry, "�x");
+	outs(uentry);
+    }
+    else
+    {
+	outs(uentry);
+	linecnt = 0;
+	clrtoeol();
+	move(++lineno, 0);
+    }
+    return 0;
+}
+*/
diff --git a/mbbsd/term.c b/mbbsd/term.c
new file mode 100644
index 00000000..61a0128d
--- /dev/null
+++ b/mbbsd/term.c
@@ -0,0 +1,144 @@
+/* $Id: term.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+int tgetent(const char *bp, char *name);
+char *tgetstr(const char *id, char **area);
+int tgetflag(const char *id);
+int tgetnum(const char *id);
+int tputs(const char *str, int affcnt, int (*putc)(int));
+char *tparm(const char *str, ...);
+char *tgoto(const char *cap, int col, int row);
+
+static struct termios tty_state, tty_new;
+
+/* ----------------------------------------------------- */
+/* basic tty control                                     */
+/* ----------------------------------------------------- */
+void init_tty() {
+    if(tcgetattr(1, &tty_state) < 0) {
+	syslog(LOG_ERR, "tcgetattr(): %m");
+	return;
+    }
+    memcpy(&tty_new, &tty_state, sizeof(tty_new));
+    tty_new.c_lflag &= ~(ICANON | ECHO | ISIG);
+/*    tty_new.c_cc[VTIME] = 0;
+    tty_new.c_cc[VMIN] = 1; */
+    tcsetattr(1, TCSANOW, &tty_new);
+    system("stty raw -echo");
+}
+
+/* ----------------------------------------------------- */
+/* init tty control code                                 */
+/* ----------------------------------------------------- */
+
+
+#define TERMCOMSIZE (40)
+
+char *clearbuf = "\33[H\33[J";
+int clearbuflen = 6;
+
+char *cleolbuf = "\33[K";
+int cleolbuflen = 3;
+
+char *scrollrev = "\33M";
+int scrollrevlen = 2;
+
+char *strtstandout = "\33[7m";
+int strtstandoutlen = 4;
+
+char *endstandout = "\33[m";
+int endstandoutlen = 3;
+
+int t_lines = 24;
+int b_lines = 23;
+int p_lines = 20;
+int t_columns = 80;
+
+int automargins = 1;
+
+static char *outp;
+static int *outlp;
+
+
+static int outcf(int ch) {
+    if(*outlp < TERMCOMSIZE) {
+	(*outlp)++;
+	*outp++ = ch;
+    }
+    return 0;
+}
+
+extern screenline_t *big_picture;
+
+static void term_resize(int sig) {
+    struct winsize newsize;
+    screenline_t *new_picture;
+
+    signal(SIGWINCH, SIG_IGN);	/* Don't bother me! */
+    ioctl(0, TIOCGWINSZ, &newsize);
+    if(newsize.ws_row > t_lines) {
+	new_picture = (screenline_t *)calloc(newsize.ws_row,
+					     sizeof(screenline_t));
+	if(new_picture == NULL) {
+	    syslog(LOG_ERR, "calloc(): %m");
+	    return;
+	}
+	free(big_picture);
+	big_picture = new_picture;
+    }
+
+    t_lines=newsize.ws_row;
+    b_lines=t_lines-1;
+    p_lines=t_lines-4;
+
+    signal(SIGWINCH, term_resize);
+}
+
+int term_init() {
+    signal(SIGWINCH, term_resize);
+    return YEA;
+}
+
+char term_buf[32];
+
+void do_move(int destcol, int destline) {
+    char buf[16], *p;
+    
+    sprintf(buf, "\33[%d;%dH", destline + 1, destcol + 1);
+    for(p = buf; *p; p++)
+	ochar(*p);
+}
+
+void save_cursor() {
+    ochar('\33');
+    ochar('7');
+}
+
+void restore_cursor() {
+    ochar('\33');
+    ochar('8');
+}
+
+void change_scroll_range(int top, int bottom) {
+    char buf[16], *p;
+    
+    sprintf(buf, "\33[%d;%dr", top + 1, bottom + 1);
+    for(p = buf; *p; p++)
+	ochar(*p);
+}
+
+void scroll_forward() {
+    ochar('\33');
+    ochar('D');
+}
diff --git a/mbbsd/toolkit.c b/mbbsd/toolkit.c
new file mode 100644
index 00000000..81a0d6f0
--- /dev/null
+++ b/mbbsd/toolkit.c
@@ -0,0 +1,14 @@
+/* $Id: toolkit.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <ctype.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+
+unsigned StringHash(unsigned char *s) {
+    unsigned int v=0;
+    while(*s) {
+	v = (v << 8) | (v >> 24);
+	v ^= toupper(*s++);	/* note this is case insensitive */
+    }
+    return (v * 2654435769UL) >> (32 - HASH_BITS);
+}
diff --git a/mbbsd/topsong.c b/mbbsd/topsong.c
new file mode 100644
index 00000000..d3a65447
--- /dev/null
+++ b/mbbsd/topsong.c
@@ -0,0 +1,79 @@
+/* $Id: topsong.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "proto.h"
+
+#define MAX_SONGS 300
+#define QCAST     int (*)(const void *, const void *)
+
+typedef struct songcmp_t {
+    char name[100];
+    char cname[100];
+    long int count;
+} songcmp_t;
+
+static long int totalcount=0;
+
+static int count_cmp(songcmp_t *b, songcmp_t *a) {
+    return (a->count - b->count);
+}
+
+int topsong() {
+    more(FN_TOPSONG,YEA);
+    return 0;
+}
+     
+static int strip_blank(char *cbuf, char *buf) {
+    for(; *buf; buf++)
+	if(*buf != ' ')
+	    *cbuf++ = *buf;	  
+    *cbuf = 0;
+    return 0;
+}
+
+void sortsong() {
+    FILE *fo, *fp = fopen(BBSHOME "/" FN_USSONG, "r");
+    songcmp_t songs[MAX_SONGS + 1];
+    int n;
+    char buf[256], cbuf[256];
+
+    memset(songs , 0, sizeof(songs));
+    if(!fp) return;
+    if(!(fo = fopen(FN_TOPSONG,"w"))) {
+	fclose(fp);
+	return;
+    }
+    
+    totalcount = 0;
+    while(fgets(buf, 200, fp)) {
+	strtok(buf, "\n\r");
+	strip_blank(cbuf, buf);
+	if(!cbuf[0] || !isprint2(cbuf[0]))
+	    continue;
+	
+	for(n = 0; n < MAX_SONGS && songs[n].name[0]; n++)
+	    if(!strcmp(songs[n].cname,cbuf))
+		break;
+	strcpy(songs[n].name, buf);
+	strcpy(songs[n].cname, cbuf);
+	songs[n].count++;
+	totalcount++;
+    }
+    qsort(songs, MAX_SONGS, sizeof(songcmp_t), (QCAST)count_cmp);
+    fprintf(fo,
+	    "    \033[36m�w�w\033[37m�W��\033[36m�w�w�w�w�w�w\033[37m�q"
+	    "  �W\033[36m�w�w�w�w�w�w�w�w�w�w�w\033[37m����\033[36m"
+	    "�w�w\033[32m�@%ld��\033[36m�w�w\033[m\n", totalcount);
+    for(n = 0; n < 100 && songs[n].name[0]; n++) {
+        fprintf(fo, "      %5d. %-38.38s %4ld \033[32m[%.2f]\033[m\n", n + 1,
+		songs[n].name, songs[n].count,
+		(float)songs[n].count/totalcount);
+    }
+    fclose(fp);
+    fclose(fo);
+}
diff --git a/mbbsd/uptime b/mbbsd/uptime
new file mode 100644
index 00000000..4f8281fb
--- /dev/null
+++ b/mbbsd/uptime
@@ -0,0 +1,23 @@
+cache.c:    if(fcache->busystate)
+cache.c:	fcache->busystate = 1;
+cache.c:	bzero(fcache->domain, sizeof fcache->domain);
+cache.c:	    fcache->top=0;
+cache.c:		    sscanf(buf,"%s",fcache->domain[fcache->top]);
+cache.c:		    po = buf + strlen(fcache->domain[fcache->top]);
+cache.c:		    strncpy(fcache->replace[fcache->top],po,49);
+cache.c:		    fcache->replace[fcache->top]
+cache.c:			[strlen(fcache->replace[fcache->top])-1] = 0;
+cache.c:		    (fcache->top)++;
+cache.c:		    if(fcache->top == MAX_FROM)
+cache.c:	fcache->max_user=0;
+cache.c:	fcache->uptime = fcache->touchtime;
+cache.c:	fcache->busystate = 0;
+cache.c:	if(fcache->touchtime == 0)
+cache.c:	    fcache->touchtime = 1;
+cache.c:    while(fcache->uptime < fcache->touchtime)
+mbbsd.c:    for (j = 0; j < fcache->top; j++){
+mbbsd.c:	char *token = strtok (fcache->domain[j], "&");
+mbbsd.c:    if ((a = utmpshm->number) > fcache->max_user){
+mbbsd.c:	fcache->max_user = a;
+mbbsd.c:	fcache->max_time = now;
+talk.c:		uentp->from_alias ? fcache->replace[uentp->from_alias] :
diff --git a/mbbsd/user.c b/mbbsd/user.c
new file mode 100644
index 00000000..41aacfa9
--- /dev/null
+++ b/mbbsd/user.c
@@ -0,0 +1,980 @@
+/* $Id: user.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "perm.h"
+#include "modes.h"
+#include "proto.h"
+
+extern int numboards;
+extern boardheader_t *bcache;
+extern char *loginview_file[NUMVIEWFILE][2];
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern time_t login_start_time;
+extern char *msg_uid;
+extern int usernum;
+extern char *msg_sure_ny;
+extern userinfo_t *currutmp;
+extern int showansi;
+extern char reset_color[];
+extern char *fn_proverb;
+extern char *fn_plans;
+extern char *msg_del_ok;
+extern char *fn_register;
+extern char *msg_nobody;
+extern userec_t cuser;
+extern userec_t xuser;
+
+static char *sex[8] = {
+    MSG_BIG_BOY, MSG_BIG_GIRL, MSG_LITTLE_BOY, MSG_LITTLE_GIRL,
+    MSG_MAN, MSG_WOMAN, MSG_PLANT, MSG_MIME
+};
+
+int u_loginview() {
+    int i;
+    unsigned int pbits = cuser.loginview;
+    char choice[5];
+    
+    clear();
+    move(4,0);
+    for(i = 0; i < NUMVIEWFILE ; i++)
+	prints("    %c. %-20s %-15s \n", 'A' + i,
+	       loginview_file[i][1],((pbits >> i) & 1 ? "��" : "��"));
+    
+    clrtobot();
+    while(getdata(b_lines - 1, 0, "�� [A-N] �����]�w�A�� [Return] �����G",
+		  choice, 3, LCECHO)) {
+	i = choice[0] - 'a';
+	if(i >= NUMVIEWFILE || i < 0)
+	    bell();
+	else {
+	    pbits ^= (1 << i);
+	    move( i + 4 , 28 );
+	    prints((pbits >> i) & 1 ? "��" : "��");
+	}
+    }
+    
+    if(pbits != cuser.loginview) {
+	cuser.loginview = pbits ;
+	passwd_update(usernum, &cuser);
+    }
+    return 0;
+}
+
+void user_display(userec_t *u, int real) {
+    int diff = 0;
+    char genbuf[200];
+    
+    clrtobot();
+    prints(
+	   "        \033[30;41m�r�s�r�s�r�s\033[m  \033[1;30;45m    �� �� ��"
+	   " �� ��        "
+	   "     \033[m  \033[30;41m�r�s�r�s�r�s\033[m\n");
+    prints("                �N���ʺ�: %s(%s)\n"
+	   "                �u��m�W: %s\n"
+	   "                �~�����}: %s\n"
+	   "                �q�l�H�c: %s\n"
+	   "                ��    �O: %s\n"
+	   "                �Ȧ�b��: %ld �Ȩ�\n",
+	   u->userid, u->username, u->realname, u->address, u->email,
+	   sex[u->sex % 8], u->money);
+    
+    sethomedir(genbuf, u->userid);
+    prints("                �p�H�H�c: %d ��  (�ʶR�H�c: %d ��)\n"
+	   "                �����Ҹ�: %s\n"
+	   "                ������X: %010d\n"
+	   "                ��    ��: %02i/%02i/%02i\n"
+	   "                �p���W�r: %s\n",
+	   get_num_records(genbuf, sizeof(fileheader_t)),
+	   u->exmailbox, u->ident, u->mobile,
+	   u->month, u->day, u->year % 100, u->mychicken.name);
+    prints("                ���U���: %s", ctime(&u->firstlogin));
+    prints("                �e�����{: %s", ctime(&u->lastlogin));
+    prints("                �e���I�q: %s", ctime(&u->lastsong));
+    prints("                �W���峹: %d �� / %d �g\n",
+	   u->numlogins, u->numposts);
+
+    if(real) {
+	strcpy(genbuf, "bTCPRp#@XWBA#VSM0123456789ABCDEF");
+	for(diff = 0; diff < 32; diff++)
+	    if(!(u->userlevel & (1 << diff)))
+		genbuf[diff] = '-';
+	prints("                �{�Ҹ��: %s\n"
+	       "                user�v��: %s\n",
+	       u->justify, genbuf);
+    } else {
+	diff = (time(0) - login_start_time) / 60;
+	prints("                ���d����: %d �p�� %2d ��\n",
+	       diff / 60, diff % 60);
+    }
+    
+    /* Thor: �Q�ݬݳo�� user �O���Ǫ������D */
+    if(u->userlevel >= PERM_BM) {
+	int i;
+	boardheader_t *bhdr;
+	
+	outs("                ����O�D: ");
+	
+	for(i = 0, bhdr = bcache; i < numboards; i++, bhdr++) {
+	    if(is_uBM(bhdr->BM,u->userid)) {
+		outs(bhdr->brdname);
+		outc(' ');
+	    }
+	}
+	outc('\n');
+    }
+    outs("        \033[30;41m�r�s�r�s�r�s�r�s�r�s�r�s�r�s�r�s�r�s�r�s�r�s�r"
+	 "�s�r�s�r�s�r�s\033[m");
+    
+    outs((u->userlevel & PERM_LOGINOK) ?
+	 "\n�z�����U�{�Ǥw�g�����A�w��[�J����" :
+	 "\n�p�G�n���@�v���A�аѦҥ������G���z���U");
+    
+#ifdef NEWUSER_LIMIT
+    if((u->lastlogin - u->firstlogin < 3 * 86400) && !HAS_PERM(PERM_POST))
+	outs("\n�s��W���A�T�ѫ�}���v��");
+#endif
+}
+
+void mail_violatelaw(char* crime, char* police, char* reason, char* result){
+    char genbuf[200];
+    fileheader_t fhdr;
+    time_t now;
+    FILE *fp;            
+    sprintf(genbuf, "home/%c/%s", crime[0], crime);
+    stampfile(genbuf, &fhdr);
+    if(!(fp = fopen(genbuf,"w")))
+        return;
+    now = time(NULL);
+    fprintf(fp, "�@��: [Ptt�k�|]\n"
+	    "���D: [���i] �H�k�P�M���i\n"
+	    "�ɶ�: %s\n"
+	    "\033[1;32m%s\033[m�P�M�G\n     \033[1;32m%s\033[m"
+	    "�]\033[1;35m%s\033[m�欰�A\n�H�ϥ������W�A�B�H\033[1;35m%s\033[m�A�S���q��"
+	    "\n�Ш� PttLaw �d�߬����k�W��T�A�è� Play-Pay-ViolateLaw ú��@��",
+	    ctime(&now), police, crime, reason, result);
+    fclose(fp);
+    sprintf(fhdr.title, "[���i] �H�k�P�M���i");
+    strcpy(fhdr.owner, "[Ptt�k�|]");
+    sprintf(genbuf, "home/%c/%s/.DIR", crime[0], crime);
+    append_record(genbuf, &fhdr, sizeof(fhdr));
+}
+
+static void violate_law(userec_t *u, int unum){
+    char ans[4], ans2[4];
+    char reason[128];
+    move(1,0);
+    clrtobot();
+    move(2,0);
+    prints("(1)Cross-post (2)�õo�s�i�H (3)�õo�s��H\n");
+    prints("(4)���Z���W�ϥΪ� (8)��L�H�@��B�m�欰\n(9)�� id �欰\n");
+    getdata(5, 0, "(0)����",
+            ans, 3, DOECHO);
+    switch(ans[0]){
+    case '1':
+	sprintf(reason, "%s", "Cross-post");
+	break;
+    case '2':
+	sprintf(reason, "%s", "�õo�s�i�H");
+	break;
+    case '3':
+	sprintf(reason, "%s", "�õo�s��H");
+	break;
+    case '4':
+        while(!getdata(7, 0, "�п�J�Q���|�z�ѥH�ܭt�d�G", reason, 50, DOECHO));
+        strcat(reason, "[���Z���W�ϥΪ�]");     
+        break;
+    case '8':   
+    case '9':
+        while(!getdata(6, 0, "�п�J�z�ѥH�ܭt�d�G", reason, 50, DOECHO));        
+        break;
+    default:
+	return;
+    }
+    getdata(7, 0, msg_sure_ny, ans2, 3, LCECHO);
+    if(*ans2 != 'y')
+	return;      
+    if (ans[0]=='9'){
+	char src[STRLEN], dst[STRLEN];
+	sprintf(src, "home/%c/%s", u->userid[0], u->userid);
+	sprintf(dst, "tmp/%s", u->userid);
+	Rename(src, dst);
+	log_usies("KILL", u->userid);
+	post_violatelaw(u->userid, cuser.userid, reason, "�尣 ID");       
+	u->userid[0] = '\0';
+	setuserid(unum, u->userid);
+	passwd_update(unum, u);
+    }
+    else{
+        u->userlevel |= PERM_VIOLATELAW;
+        u->vl_count ++;
+	passwd_update(unum, u);
+        post_violatelaw(u->userid, cuser.userid, reason, "�@��B��");
+        mail_violatelaw(u->userid, cuser.userid, reason, "�@��B��");
+    }                 
+    pressanykey();
+}
+
+extern char* str_permid[];
+
+void uinfo_query(userec_t *u, int real, int unum) {
+    userec_t x;
+    register int i = 0, fail, mail_changed;
+    char ans[4], buf[STRLEN], *p;
+    char genbuf[200], reason[50];
+    unsigned long int money = 0;
+    fileheader_t fhdr;
+    int flag = 0, temp = 0, money_change = 0;
+    time_t now;
+    
+    FILE *fp;
+    
+    fail = mail_changed = 0;
+    
+    memcpy(&x, u, sizeof(userec_t));
+    getdata(b_lines - 1, 0, real ?
+	    "(1)����(2)�]�K�X(3)�]�v��(4)��b��(5)��ID"
+	    "(6)��/�_���d��(7)�f�P [0]���� " :
+	    "�п�� (1)�ק��� (2)�]�w�K�X ==> [0]���� ",
+	    ans, 3, DOECHO);
+    
+    if(ans[0] > '2' && !real)
+	ans[0] = '0';
+    
+    if(ans[0] == '1' || ans[0] == '3') {
+	clear();
+	i = 2;
+	move(i++, 0);
+	outs(msg_uid);
+	outs(x.userid);
+    }
+    
+    switch(ans[0]) {
+    case '7':
+	violate_law(&x, unum);
+	return;
+    case '1':
+	move(0, 0);
+	outs("�гv���ק�C");
+	
+	getdata_buf(i++, 0," �� ��  �G",x.username, 24, DOECHO);
+	if(real) {
+	    getdata_buf(i++, 0, "�u��m�W�G", x.realname, 20, DOECHO);
+	    getdata_buf(i++, 0, "�����Ҹ��G", x.ident, 11, DOECHO);
+	    getdata_buf(i++, 0, "�~���a�}�G", x.address, 50, DOECHO);
+	}
+	sprintf(buf, "%010d", x.mobile);
+	getdata_buf(i++, 0, "������X�G", buf, 11, LCECHO);
+        x.mobile=atoi(buf);
+	getdata_str(i++, 0, "�q�l�H�c[�ܰʭn���s�{��]�G", buf, 50, DOECHO,
+		    x.email);
+	if(strcmp(buf,x.email) && strchr(buf, '@')) {
+	    strcpy(x.email,buf);
+	    mail_changed = 1 - real;
+	}
+	
+	sprintf(genbuf, "%i", (u->sex + 1) % 8);
+	getdata_str(i++, 0, "�ʧO (1)���� (2)�j�� (3)���} (4)���� (5)���� "
+		    "(6)���� (7)�Ӫ� (8)�q���G",
+		    buf, 3, DOECHO,genbuf);
+	if(buf[0] >= '1' && buf[0] <= '8')
+	    x.sex = (buf[0] - '1') % 8;
+	else
+	    x.sex = u->sex % 8;
+	
+	while(1) {
+	    int len;
+	    
+	    sprintf(genbuf, "%02i/%02i/%02i",
+		    u->month, u->day, u->year % 100);
+	    len = getdata_str(i, 0, "�ͤ� ���/���/�褸�G", buf, 9,
+			      DOECHO,genbuf);
+	    if(len && len != 8)
+		continue;
+	    if(!len) {
+		x.month = u->month;
+		x.day = u->day;
+		x.year = u->year;
+	    } else if(len == 8) {
+		x.month = (buf[0] - '0') * 10 + (buf[1] - '0');
+		x.day   = (buf[3] - '0') * 10 + (buf[4] - '0');
+		x.year  = (buf[6] - '0') * 10 + (buf[7] - '0');
+	    } else
+		continue;
+	    if(!real && (x.month > 12 || x.month < 1 || x.day > 31 ||
+			 x.day < 1 || x.year > 90 || x.year < 40))
+		continue;
+	    i++;
+	    break;
+	}
+	if(real) {
+	    unsigned long int l;
+	    if(HAS_PERM(PERM_BBSADM)) {
+		sprintf(genbuf, "%d", x.money);
+		if(getdata_str(i++, 0,"�Ȧ�b��G", buf, 10, DOECHO,genbuf))
+		    if((l = atol(buf)) >= 0) {
+			if(l != x.money) {
+			    money_change = 1;
+			    money = x.money;
+			    x.money = l;
+			}
+		    }
+	    }
+	    sprintf(genbuf, "%d", x.exmailbox);
+	    if(getdata_str(i++, 0,"�ʶR�H�c�ơG", buf, 4, DOECHO,genbuf))
+		if((l = atol(buf)) >= 0)
+		    x.exmailbox = (int)l;
+	    
+	    getdata_buf(i++, 0, "�{�Ҹ�ơG", x.justify, 44, DOECHO);
+	    getdata_buf(i++, 0, "�̪���{�����G", x.lasthost, 16, DOECHO);
+	    
+	    sprintf(genbuf, "%d", x.numlogins);
+	    if(getdata_str(i++, 0,"�W�u���ơG", buf, 10, DOECHO,genbuf))
+		if((fail = atoi(buf)) >= 0)
+		    x.numlogins = fail;
+	    
+	    sprintf(genbuf,"%d", u->numposts);
+	    if(getdata_str(i++, 0, "�峹�ƥءG", buf, 10, DOECHO,genbuf))
+		if((fail = atoi(buf)) >= 0)
+		    x.numposts = fail;
+	    sprintf(genbuf, "%d", u->vl_count);
+	    if (getdata_str(i++, 0, "�H�k�O���G", buf, 10, DOECHO, genbuf))
+		if ((fail = atoi(buf)) >= 0)
+		    x.vl_count = fail;
+	    
+	    sprintf(genbuf, "%d/%d/%d", u->five_win, u->five_lose,
+		    u->five_tie);
+	    if(getdata_str(i++, 0, "���l�Ѿ��Z ��/��/�M�G", buf, 16, DOECHO,
+			   genbuf))
+		while(1) {
+		    p = strtok(buf, "/\r\n");
+		    if(!p) break;
+		    x.five_win = atoi(p);
+		    p = strtok(NULL, "/\r\n");
+		    if(!p) break;
+		    x.five_lose = atoi(p);
+		    p = strtok(NULL, "/\r\n");
+		    if(!p) break;
+		    x.five_tie = atoi(p);
+		    break;
+		}
+	    sprintf(genbuf, "%d/%d/%d", u->chc_win, u->chc_lose, u->chc_tie);
+	    if(getdata_str(i++, 0, "�H�Ѿ��Z ��/��/�M�G", buf, 16, DOECHO,
+			   genbuf))
+		while(1) {
+		    p = strtok(buf, "/\r\n");
+		    if(!p) break;
+		    x.chc_win = atoi(p);
+		    p = strtok(NULL, "/\r\n");
+		    if(!p) break;
+		    x.chc_lose = atoi(p);
+		    p = strtok(NULL, "/\r\n");
+		    if(!p) break;
+		    x.chc_tie = atoi(p);
+		    break;
+		}
+	    fail = 0;
+	}
+	break;
+	
+    case '2':
+	i = 19;
+	if(!real) {
+	    if(!getdata(i++, 0, "�п�J��K�X�G", buf, PASSLEN, NOECHO) ||
+	       !checkpasswd(u->passwd, buf)) {
+		outs("\n\n�z��J���K�X�����T\n");
+		fail++;
+		break;
+	    }
+	}
+	else{
+	    char witness[3][32];
+	    time_t now = time(NULL);
+	    for(i=0;i<3;i++){
+		if(!getdata(19+i, 0, "�п�J��U�ҩ����ϥΪ̡G", witness[i], 32, DOECHO)){
+		    outs("\n����J�h�L�k���\n");
+		    fail++;
+		    break;
+		}
+		else if (!getuser(witness[i])){
+		    outs("\n�d�L���ϥΪ�\n");
+		    fail++;
+		    break;
+		}
+		else if (now - xuser.firstlogin < 6*30*24*60*60){
+		    outs("\n���U���W�L�b�~�A���s��J\n");
+		    i--;
+		}
+	    }
+	    if (i < 3)
+		break;
+	    else
+		i = 20;
+	}    
+	
+	if(!getdata(i++, 0, "�г]�w�s�K�X�G", buf, PASSLEN, NOECHO)) {
+	    outs("\n\n�K�X�]�w����, �~��ϥ��±K�X\n");
+	    fail++;
+	    break;
+	}
+	strncpy(genbuf, buf, PASSLEN);
+	
+	getdata(i++, 0, "���ˬd�s�K�X�G", buf, PASSLEN, NOECHO);
+	if(strncmp(buf, genbuf, PASSLEN)) {
+	    outs("\n\n�s�K�X�T�{����, �L�k�]�w�s�K�X\n");
+	    fail++;
+	    break;
+	}
+	buf[8] = '\0';
+	strncpy(x.passwd, genpasswd(buf), PASSLEN);
+	if (real)
+	    x.userlevel &= (!PERM_LOGINOK);
+	break;
+	
+    case '3':
+	i = setperms(x.userlevel, str_permid);
+	if(i == x.userlevel)
+	    fail++;
+	else {
+	    flag=1;
+	    temp=x.userlevel;
+	    x.userlevel = i;
+	}
+	break;
+	
+    case '4':
+	i = QUIT;
+	break;
+	
+    case '5':
+	if (getdata_str(b_lines - 3, 0, "�s���ϥΪ̥N���G", genbuf, IDLEN + 1,
+			DOECHO,x.userid)) {
+	    if(searchuser(genbuf)) {
+		outs("���~! �w�g���P�� ID ���ϥΪ�");
+		fail++;
+	    } else
+		strcpy(x.userid, genbuf);
+	}
+	break;
+    case '6':
+	if(x.mychicken.name[0])
+	    x.mychicken.name[0] = 0;
+	else
+	    strcpy(x.mychicken.name,"[��]");
+	break;
+    default:
+	return;
+    }
+
+    if(fail) {
+	pressanykey();
+	return;
+    }
+    
+    getdata(b_lines - 1, 0, msg_sure_ny, ans, 3, LCECHO);
+    if(*ans == 'y') {
+	if(flag)
+	    post_change_perm(temp,i,cuser.userid,x.userid);
+	if(strcmp(u->userid, x.userid)) {
+	    char src[STRLEN], dst[STRLEN];
+	    
+	    sethomepath(src, u->userid);
+	    sethomepath(dst, x.userid);
+	    Rename(src, dst);
+	    setuserid(unum, x.userid);
+	}
+	memcpy(u, &x, sizeof(x));
+	if(mail_changed) {	
+#ifdef EMAIL_JUSTIFY
+	    x.userlevel &= ~PERM_LOGINOK;
+	    mail_justify();
+#endif
+	}
+	
+	if(i == QUIT) {
+	    char src[STRLEN], dst[STRLEN];
+	    
+	    sprintf(src, "home/%c/%s", x.userid[0], x.userid);
+	    sprintf(dst, "tmp/%s", x.userid);
+	    if(Rename(src, dst)) {
+		sprintf(genbuf, "/bin/rm -fr %s >/dev/null 2>&1", src);
+/* do not remove
+   system(genbuf);
+*/
+	    }	
+	    log_usies("KILL", x.userid);
+	    x.userid[0] = '\0';
+	    setuserid(unum, x.userid);
+	} else
+	    log_usies("SetUser", x.userid);
+        if(money_change)
+              	setumoney(unum,x.money);
+	passwd_update(unum, &x);
+	now = time(0);
+	if(money_change) {
+	    strcpy(genbuf, "boards/Security");
+	    stampfile(genbuf, &fhdr);	
+	    if(!(fp = fopen(genbuf,"w")))
+		return;
+	    
+	    now = time(NULL);
+	    fprintf(fp, "�@��: [�t�Φw����] �ݪO: Security\n"
+		    "���D: [���w���i] �����ק�������i\n"
+		    "�ɶ�: %s\n"
+		    "   ����\033[1;32m%s\033[m��\033[1;32m%s\033[m"
+		    "�����q\033[1;35m%ld\033[m�令\033[1;35m%d\033[m",
+		    ctime(&now), cuser.userid, x.userid, money, x.money);
+	    
+	    clrtobot ();
+	    clear();
+	    while(!getdata(5, 0, "�п�J�z�ѥH�ܭt�d�G", reason, 60, DOECHO));
+	    
+	    fprintf(fp, "\n   \033[1;37m����%s�ק���z�ѬO�G%s\033[m",
+		    cuser.userid, reason);
+	    fclose(fp);
+	    sprintf(fhdr.title, "[���w���i] ����%s�ק�%s�����i", cuser.userid,
+		    x.userid);
+	    strcpy(fhdr.owner, "[�t�Φw����]");
+	    append_record("boards/Security/.DIR", &fhdr, sizeof(fhdr));
+	}
+    }
+}
+
+int u_info() {
+    move(2, 0);
+    user_display(&cuser, 0);
+    uinfo_query(&cuser, 0, usernum);
+    strcpy(currutmp->realname, cuser.realname);
+    strcpy(currutmp->username, cuser.username);
+    return 0;
+}
+
+int u_ansi() {
+    showansi ^= 1;
+    cuser.uflag ^= COLOR_FLAG;
+    outs(reset_color);
+    return 0;
+}
+
+int u_cloak() {
+    outs((currutmp->invisible ^= 1) ? MSG_CLOAKED : MSG_UNCLOAK);
+    return XEASY;
+}
+
+int u_switchproverb() {
+/*  char *state[4]={"�Υ\\��","�w�h��","�۩w��","SHUTUP"}; */
+    char buf[100];
+
+    cuser.proverb =(cuser.proverb +1) %4;
+    setuserfile(buf,fn_proverb);
+    if(cuser.proverb==2 && dashd(buf)) {
+	FILE *fp = fopen(buf,"a");
+	
+	fprintf(fp,"�y�k�ʪ��A��[�۩w��]�n�O�o�]�y�k�ʪ����e��!!");
+	fclose(fp);
+    }
+    passwd_update(usernum, &cuser);
+    return 0;
+}
+
+int u_editproverb() {
+    char buf[100];
+  
+    setutmpmode(PROVERB);
+    setuserfile(buf,fn_proverb);
+    move(1,0);
+    clrtobot();
+    outs("\n\n �Ф@��@��̧���J�Q�t�δ����A�����e,\n"
+	 " �x�s��O�o�⪬�A�]�� [�۩w��] �~���@��\n"
+	 " �y�k�ʳ̦h100��");
+    pressanykey();
+    vedit(buf,NA, NULL);
+    return 0;
+}
+
+void showplans(char *uid) {
+    char genbuf[200];
+    
+    sethomefile(genbuf, uid, fn_plans);
+    if(!show_file(genbuf, 7, MAX_QUERYLINES, ONLY_COLOR))
+	prints("�m�ӤH�W���n%s �ثe�S���W��", uid);
+}
+
+int showsignature(char *fname) {
+    FILE *fp;
+    char buf[256];
+    int i, j;
+    char ch;
+
+    clear();
+    move(2, 0);
+    setuserfile(fname, "sig.0");
+    j = strlen(fname) - 1;
+
+    for(ch = '1'; ch <= '9'; ch++) {
+	fname[j] = ch;
+	if((fp = fopen(fname, "r"))) {
+	    prints("\033[36m�i ñ�W��.%c �j\033[m\n", ch);
+	    for(i = 0; i++ < MAX_SIGLINES && fgets(buf, 256, fp); outs(buf));
+	    fclose(fp);
+	}
+    }
+    return j;
+}
+
+int u_editsig() {
+    int aborted;
+    char ans[4];
+    int j;
+    char genbuf[200];
+    
+    j = showsignature(genbuf);
+    
+    getdata(0, 0, "ñ�W�� (E)�s�� (D)�R�� (Q)�����H[Q] ", ans, 4, LCECHO);
+    
+    aborted = 0;
+    if(ans[0] == 'd')
+	aborted = 1;
+    if(ans[0] == 'e')
+	aborted = 2;
+    
+    if(aborted) {
+	if(!getdata(1, 0, "�п��ñ�W��(1-9)�H[1] ", ans, 4, DOECHO))
+	    ans[0] = '1';
+	if(ans[0] >= '1' && ans[0] <= '9') {
+	    genbuf[j] = ans[0];
+	    if(aborted == 1) {
+		unlink(genbuf);
+		outs(msg_del_ok);
+	    } else {
+		setutmpmode(EDITSIG);
+		aborted = vedit(genbuf, NA, NULL);
+		if(aborted != -1)
+		    outs("ñ�W�ɧ�s����");
+	    }
+	}
+	pressanykey();
+    }
+    return 0;
+}
+
+int u_editplan() {
+    char genbuf[200];
+    
+    getdata(b_lines - 1, 0, "�W�� (D)�R�� (E)�s�� [Q]�����H[Q] ",
+	    genbuf, 3, LCECHO);
+    
+    if(genbuf[0] == 'e') {
+	int aborted;
+	
+	setutmpmode(EDITPLAN);
+	setuserfile(genbuf, fn_plans);
+	aborted = vedit(genbuf, NA, NULL);
+	if(aborted != -1)
+	    outs("�W����s����");
+	pressanykey();
+	return 0;
+    } else if(genbuf[0] == 'd') {
+	setuserfile(genbuf, fn_plans);
+	unlink(genbuf);
+	outmsg("�W���R������");
+    }
+    return 0;
+}
+
+int u_editcalendar() {
+    char genbuf[200];
+    
+    getdata(b_lines - 1, 0, "��ƾ� (D)�R�� (E)�s�� [Q]�����H[Q] ",
+	    genbuf, 3, LCECHO);
+    
+    if(genbuf[0] == 'e') {
+	int aborted;
+	
+	setutmpmode(EDITPLAN);
+	setcalfile(genbuf, cuser.userid);
+	aborted = vedit(genbuf, NA, NULL);
+	if(aborted != -1)
+	    outs("��ƾ��s����");
+	pressanykey();
+	return 0;
+    } else if(genbuf[0] == 'd') {
+	setcalfile(genbuf, cuser.userid);
+	unlink(genbuf);
+	outmsg("��ƾ�R������");
+    }
+    return 0;
+}
+
+/* �ϥΪ̶�g���U���� */
+static void getfield(int line, char *info, char *desc, char *buf, int len) {
+    char prompt[STRLEN];
+    char genbuf[200];
+
+    sprintf(genbuf, "����]�w�G%-30.30s (%s)", buf, info);
+    move(line, 2);
+    outs(genbuf);
+    sprintf(prompt, "%s�G", desc);
+    if(getdata_str(line + 1, 2, prompt, genbuf, len, DOECHO, buf))
+	strcpy(buf, genbuf);
+    move(line, 2);
+    prints("%s�G%s", desc, buf);
+    clrtoeol();
+}
+
+static int removespace(char* s){
+    int i, index;
+
+    for(i=0, index=0;s[i];i++){
+	if (s[i] != ' ')
+	    s[index++] = s[i];
+    }
+    s[index] = '\0';
+    return index;
+}
+
+int u_register() {
+    char rname[20], addr[50], ident[11], mobile[20];
+    char phone[20], career[40], email[50],birthday[9],sex_is[2],year,mon,day;
+    char ans[3], *ptr;
+    FILE *fn;
+    time_t now;
+    char genbuf[200];
+    
+    if(cuser.userlevel & PERM_LOGINOK) {
+	outs("�z�������T�{�w�g�����A���ݶ�g�ӽЪ�");
+	return XEASY;
+    }
+    if((fn = fopen(fn_register, "r"))) {
+	while(fgets(genbuf, STRLEN, fn)) {
+	    if((ptr = strchr(genbuf, '\n')))
+		*ptr = '\0';
+	    if(strncmp(genbuf, "uid: ", 5) == 0 &&
+	       strcmp(genbuf + 5, cuser.userid) == 0) {
+		fclose(fn);
+		outs("�z�����U�ӽг�|�b�B�z���A�Э@�ߵ���");
+		return XEASY;
+	    }
+	}
+	fclose(fn);
+    }
+    
+    getdata(b_lines - 1, 0, "�z�T�w�n��g���U���(Y/N)�H[N] ", ans, 3, LCECHO);
+    if(ans[0] != 'y')
+	return FULLUPDATE;
+    
+    move(2, 0);
+    clrtobot();
+    strcpy(ident, cuser.ident);
+    strcpy(rname, cuser.realname);
+    strcpy(addr, cuser.address);
+    strcpy(email, cuser.email);
+    sprintf(mobile,"0%09d",cuser.mobile);
+    sprintf(birthday, "%02i/%02i/%02i",
+	    cuser.month, cuser.day, cuser.year % 100);
+    sex_is[0]=(cuser.sex % 8)+'1';sex_is[1]=0;
+    career[0] = phone[0] = '\0';
+    while(1) {
+	clear();
+	move(1, 0);
+	prints("%s(%s) �z�n�A�оڹ��g�H�U�����:",
+	       cuser.userid, cuser.username);
+        do{
+	    getfield(3, "D120908396", "�����Ҹ�", ident, 11);
+        }while(removespace(ident)<10 || !isalpha(ident[0]));
+        do{
+	    getfield(5, "�ХΤ���", "�u��m�W", rname, 20);
+        }while(!removespace(rname) || isalpha(rname[0]));
+        do{ 
+	    getfield(7, "�Ǯըt�ũγ��¾��", "�A�ȳ��", career, 40);
+        }while(!removespace(career));
+        do{
+	    getfield(9, "�]�A��ǩΪ��P���X", "�ثe���}", addr, 50);
+        }while(!(addr[0]));
+        do{
+	    getfield(11, "�]�A���~�����ϰ�X", "�s���q��", phone, 20);
+        }while(!removespace(phone));
+	getfield(13, "�u��J�Ʀr �p:0912345678", "������X", mobile, 20);
+	while(1) {
+	    int len;
+	    
+	    getfield(15,"���/���/�褸 �p:09/27/76","�ͤ�",birthday,9);
+	    len = strlen(birthday);
+	    if(!len) {
+		sprintf(birthday, "%02i/%02i/%02i",
+			cuser.month, cuser.day, cuser.year % 100);
+		mon = cuser.month;
+		day = cuser.day;
+		year = cuser.year;
+	    } else if(len == 8) {
+		mon = (birthday[0] - '0') * 10 + (birthday[1] - '0');
+		day = (birthday[3] - '0') * 10 + (birthday[4] - '0');
+		year = (birthday[6] - '0') * 10 + (birthday[7] - '0');
+	    } else
+		continue;
+	    if(mon > 12 || mon < 1 || day > 31 || day < 1 || year > 90 ||
+	       year < 40)
+		continue;
+	    break;
+	}
+	getfield(17, "1.���� 2.�j�� ", "�ʧO", sex_is, 2);
+	getfield(19, "�����{�ҥ�", "E-Mail Address", email, 50);
+	
+	getdata(b_lines - 1, 0, "�H�W��ƬO�_���T(Y/N)�H(Q)�������U [N] ",
+		ans, 3, LCECHO);
+	if(ans[0] == 'q')
+	    return 0;
+	if(ans[0] == 'y')
+	    break;
+    }
+    strcpy(cuser.ident, ident);
+    strcpy(cuser.realname, rname);
+    strcpy(cuser.address, addr);
+    strcpy(cuser.email, email);
+    cuser.mobile = atoi(mobile);
+    cuser.sex= (sex_is[0] - '1') % 8;
+    cuser.month = mon;
+    cuser.day = day;
+    cuser.year = year;
+    if((fn = fopen(fn_register, "a"))) {
+	now = time(NULL);
+	trim(career);
+	trim(addr);
+	trim(phone);
+	fprintf(fn, "num: %d, %s", usernum, ctime(&now));
+	fprintf(fn, "uid: %s\n", cuser.userid);
+	fprintf(fn, "ident: %s\n", ident);
+	fprintf(fn, "name: %s\n", rname);
+	fprintf(fn, "career: %s\n", career);
+	fprintf(fn, "addr: %s\n", addr);
+	fprintf(fn, "phone: %s\n", phone);
+	fprintf(fn, "mobile: %s\n", mobile);
+	fprintf(fn, "email: %s\n", email);
+	fprintf(fn, "----\n");
+	fclose(fn);
+    }
+    clear();
+    move(9,3);
+    prints("�̫�Post�@�g\033[32m�ۧڤ��Ф峹\033[m���j�a�a�A"
+	   "�i�D�Ҧ��Ѱ��Y\033[31m�ڨӰ�^$�C\\n\n\n\n");
+    pressanykey();
+    cuser.userlevel |= PERM_POST;
+    brc_initial("WhoAmI");
+    set_board();
+    do_post();
+    cuser.userlevel &= ~PERM_POST;
+    return 0;
+}
+
+/* �C�X�Ҧ����U�ϥΪ� */
+extern struct uhash_t *uhash;
+static int usercounter, totalusers, showrealname;
+static ushort u_list_special;
+
+static int u_list_CB(userec_t *uentp) {
+    static int i;
+    char permstr[8], *ptr;
+    register int level;
+    
+    if(uentp == NULL) {
+	move(2, 0);
+	clrtoeol();
+	prints("\033[7m  �ϥΪ̥N��   %-25s   �W��  �峹  %s  "
+	       "�̪���{���     \033[0m\n",
+	       showrealname ? "�u��m�W" : "�︹�ʺ�",
+	       HAS_PERM(PERM_SEEULEVELS) ? "����" : "");
+	i = 3;
+	return 0;
+    }
+    
+    if(bad_user_id(uentp->userid))
+	return 0;
+    
+    if((uentp->userlevel & ~(u_list_special)) == 0)
+	return 0;
+    
+    if(i == b_lines) {
+	prints("\033[34;46m  �w��� %d/%d �H(%d%%)  \033[31;47m  "
+	       "(Space)\033[30m �ݤU�@��  \033[31m(Q)\033[30m ���}  \033[m",
+	       usercounter, totalusers, usercounter * 100 / totalusers);
+	i = igetch();
+	if(i == 'q' || i == 'Q')
+	    return QUIT;
+	i = 3;
+    }
+    if(i == 3) {
+	move(3, 0);
+	clrtobot();
+    }
+
+    level = uentp->userlevel;
+    strcpy(permstr, "----");
+    if(level & PERM_SYSOP)
+	permstr[0] = 'S';
+    else if(level & PERM_ACCOUNTS)
+	permstr[0] = 'A';
+    else if(level & PERM_DENYPOST)
+	permstr[0] = 'p';
+
+    if(level & (PERM_BOARD))
+	permstr[1] = 'B';
+    else if(level & (PERM_BM))
+	permstr[1] = 'b';
+
+    if(level & (PERM_XEMPT))
+	permstr[2] = 'X';
+    else if(level & (PERM_LOGINOK))
+	permstr[2] = 'R';
+
+    if(level & (PERM_CLOAK | PERM_SEECLOAK))
+	permstr[3] = 'C';
+    
+    ptr = (char *)Cdate(&uentp->lastlogin);
+    ptr[18] = '\0';
+    prints("%-14s %-27.27s%5d %5d  %s  %s\n",
+	   uentp->userid,
+	   showrealname ? uentp->realname : uentp->username,
+	   uentp->numlogins, uentp->numposts,
+	   HAS_PERM(PERM_SEEULEVELS) ? permstr : "", ptr);
+    usercounter++;
+    i++;
+    return 0;
+}
+
+int u_list() {
+    char genbuf[3];
+    
+    setutmpmode(LAUSERS);
+    showrealname = u_list_special = usercounter = 0;
+    totalusers = uhash->number;
+    if(HAS_PERM(PERM_SEEULEVELS)) {
+	getdata(b_lines - 1, 0, "�[�� [1]�S������ (2)�����H",
+		genbuf, 3, DOECHO);
+	if(genbuf[0] != '2')
+	    u_list_special = PERM_BASIC | PERM_CHAT | PERM_PAGE | PERM_POST | PERM_LOGINOK | PERM_BM;
+    }
+    if(HAS_PERM(PERM_CHATROOM) || HAS_PERM(PERM_SYSOP)) {
+	getdata(b_lines - 1, 0, "��� [1]�u��m�W (2)�ʺ١H",
+		genbuf, 3, DOECHO);
+	if(genbuf[0] != '2')
+	    showrealname = 1;
+    }
+    u_list_CB(NULL);
+    if(passwd_apply(u_list_CB) == -1) {
+	outs(msg_nobody);
+	return XEASY;
+    }
+    move(b_lines, 0);
+    clrtoeol();
+    prints("\033[34;46m  �w��� %d/%d ���ϥΪ�(�t�ήe�q�L�W��)  "
+	   "\033[31;47m  (�����N���~��)  \033[m", usercounter, totalusers);
+    egetch();
+    return 0;
+}
diff --git a/mbbsd/var.c b/mbbsd/var.c
new file mode 100644
index 00000000..7b07f63b
--- /dev/null
+++ b/mbbsd/var.c
@@ -0,0 +1,268 @@
+/* $Id: var.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+
+char *str_permid[] = {
+    "���v�O",                   /* PERM_BASIC */
+    "�i�J��ѫ�",                 /* PERM_CHAT */
+    "��H���",                   /* PERM_PAGE */
+    "�o���峹",                   /* PERM_POST */
+    "���U�{�ǻ{��",               /* PERM_LOGINOK */
+    "�H��L�W��",                 /* PERM_MAILLIMIT */
+    "�����N",                     /* PERM_CLOAK */
+    "�ݨ��Ԫ�",                   /* PERM_SEECLOAK */
+    "�ä[�O�d�b��",               /* PERM_XEMPT */
+    "���������N",                 /* PERM_DENYPOST */
+    "�O�D",                       /* PERM_BM */
+    "�b���`��",                   /* PERM_ACCOUNTS */
+    "��ѫ��`��",                 /* PERM_CHATCLOAK */
+    "�ݪO�`��",                   /* PERM_BOARD */
+    "����",                       /* PERM_SYSOP */
+    "BBSADM",                     /* PERM_POSTMARK */
+    "���C�J�Ʀ�]",               /* PERM_NOTOP */
+    "�H�k�q�r��",                 /* PERM_VIOLATELAW */
+    "���������~���H",             /* PERM_ */
+    "�S�Q��",                     /* PERM_ */
+    "��ı����",                   /* PERM_VIEWSYSOP */
+    "�[��ϥΪ̦���",             /* PERM_LOGUSER */
+    "��ذ��`��z�v",             /* PERM_Announce */
+    "������",                     /* PERM_RELATION */
+    "�S�Ȳ�",                     /* PERM_SMG */
+    "�{����",                     /* PERM_PRG */
+    "���ʲ�",                     /* PERM_ACTION */
+    "���u��",                     /* PERM_PAINT */
+    "�ߪk��",                     /* PERM_LAW */
+    "�p�ժ�",                     /* PERM_SYSSUBOP */
+    "�@�ťD��",                   /* PERM_LSYSOP */
+    "�ޢ���"                      /* PERM_PTT */  
+};
+
+char *str_permboard[] = {
+    "���i Zap",                   /* BRD_NOZAP */
+    "���C�J�έp",                 /* BRD_NOCOUNT */
+    "����H",                     /* BRD_NOTRAN */
+    "�s�ժ�",                     /* BRD_GROUP */
+    "���ê�",                     /* BRD_HIDE */
+    "����(���ݳ]�w)",        /* BRD_POSTMASK */
+    "�ΦW��",                     /* BRD_ANONYMOUS */
+    "�w�]�ΦW��",                 /* BRD_DEFAULTANONYMOUS */
+    "�H�k��i���ݪ�",             /* BRD_BAD */
+    "�s�p�M�άݪ�",               /* BRD_VOTEBOARD */
+    "�S�Q��",
+    "�S�Q��", 
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��", 
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��", 
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��", 
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��",
+    "�S�Q��", 
+};
+
+int usernum;
+pid_t currpid;                  /* current process ID */
+unsigned int currstat;
+int currmode = 0;
+int curredit = 0;
+int showansi = 1;
+int curr_idle_timeout = IDLE_TIMEOUT;
+time_t login_start_time;
+userec_t cuser;                   /* current user structure */
+userec_t xuser;                   /* lookup user structure */
+char quote_file[80] = "\0";
+char quote_user[80] = "\0";
+time_t paste_time;
+char paste_title[STRLEN];
+char paste_path[256];
+int paste_level;
+char currtitle[40] = "\0";
+char vetitle[40] = "\0";
+char currowner[IDLEN + 2] = "\0";
+char currauthor[IDLEN + 2] = "\0";
+char currfile[FNLEN];           /* current file name @ bbs.c mail.c */
+unsigned char currfmode;               /* current file mode */
+char currboard[IDLEN + 2];
+int currbid;
+unsigned int currbrdattr;
+char currBM[IDLEN * 3 + 10];
+char reset_color[4] = "\033[m";
+char margs[64] = "\0";           /*  main argv list*/
+crosspost_t postrecord;           /* anti cross post */
+
+/* global string variables */
+/* filename */
+
+char *fn_passwd = FN_PASSWD;
+char *fn_board = FN_BOARD;
+char *fn_note_ans = FN_NOTE_ANS;
+char *fn_register = "register.new";
+char *fn_plans = "plans";
+char *fn_writelog = "writelog";
+char *fn_talklog = "talklog";
+char *fn_overrides = FN_OVERRIDES;
+char *fn_reject = FN_REJECT;
+char *fn_canvote = FN_CANVOTE;
+char *fn_notes = "notes";
+char *fn_water = FN_WATER;
+char *fn_visable = FN_VISABLE;
+char *fn_mandex = "/.Names";
+char *fn_proverb = "proverb";
+
+/* are descript in userec.loginview */
+
+char *loginview_file[NUMVIEWFILE][2] = {
+    {FN_NOTE_ANS       ,"�IJ��W���y���O"},
+    {FN_TOPSONG        ,"�I�q�Ʀ�]"    },
+    {"etc/topusr"      ,"�Q�j�Ʀ�]"    },
+    {"etc/topusr100"   ,"�ʤj�Ʀ�]"   },
+    {"etc/birth.today" ,"����جP"     },
+    {"etc/weather.tmp" ,"�Ѯ�ֳ�"     },
+    {"etc/stock.tmp"   ,"�ѥ��ֳ�"     },
+    {"etc/day"         ,"����Q�j���D"  },
+    {"etc/week"        ,"�@�g���Q�j���D"},
+    {"etc/today"       ,"���ѤW���H��"  },
+    {"etc/yesterday"   ,"�Q��W���H��"  },
+    {"etc/history"     ,"���v�W������"  },
+    {"etc/topboardman" ,"��ذϱƦ�]"  },
+    {"etc/topboard.tmp","�ݪO�H��Ʀ�]"}
+};
+
+/* message */
+char *msg_seperator = MSG_SEPERATOR;
+char *msg_mailer = MSG_MAILER;
+char *msg_shortulist = MSG_SHORTULIST;
+
+char *msg_cancel = MSG_CANCEL;
+char *msg_usr_left = MSG_USR_LEFT;
+char *msg_nobody = MSG_NOBODY;
+
+char *msg_sure_ny = MSG_SURE_NY;
+char *msg_sure_yn = MSG_SURE_YN;
+
+char *msg_bid = MSG_BID;
+char *msg_uid = MSG_UID;
+
+char *msg_del_ok = MSG_DEL_OK;
+char *msg_del_ny = MSG_DEL_NY;
+
+char *msg_fwd_ok = MSG_FWD_OK;
+char *msg_fwd_err1 = MSG_FWD_ERR1;
+char *msg_fwd_err2 = MSG_FWD_ERR2;
+
+char *err_board_update = ERR_BOARD_UPDATE;
+char *err_bid = ERR_BID;
+char *err_uid = ERR_UID;
+char *err_filename = ERR_FILENAME;
+
+char *str_mail_address = "." BBSUSER "@" MYHOSTNAME;
+char *str_new = "new";
+char *str_reply = "Re: ";
+char *str_space = " \t\n\r";
+char *str_sysop = "SYSOP";
+char *str_author1 = STR_AUTHOR1;
+char *str_author2 = STR_AUTHOR2;
+char *str_post1 = STR_POST1;
+char *str_post2 = STR_POST2;
+char *BBSName = BBSNAME;
+
+/* #define MAX_MODES 78 */
+/* MAX_MODES is defined in common.h */
+
+char *ModeTypeTable[MAX_MODES] = {
+    "�o�b",                       /* IDLE */
+    "�D���",                     /* MMENU */
+    "�t��@",                   /* ADMIN */
+    "�l����",                   /* MAIL */
+    "��Ϳ��",                   /* TMENU */
+    "�ϥΪ̿��",                 /* UMENU */
+    "XYZ ���",                   /* XMENU */
+    "�����ݪO",                   /* CLASS */
+    "Play���",                   /* PMENU */
+    "�s�S�O�W��",                 /* NMENU */
+    "��tt�q�c��",                 /* PSALE */
+    "�o���峹",                   /* POSTING */
+    "�ݪO�C��",                   /* READBRD */
+    "�\\Ū�峹",                  /* READING */
+    "�s�峹�C��",                 /* READNEW */
+    "��ܬݪO",                   /* SELECT */
+    "Ū�H",                       /* RMAIL */
+    "�g�H",                       /* SMAIL */
+    "��ѫ�",                     /* CHATING */
+    "��L",                       /* XMODE */
+    "�M��n��",                   /* FRIEND */
+    "�W�u�ϥΪ�",                 /* LAUSERS */
+    "�ϥΪ̦W��",                 /* LUSERS */
+    "�l�ܯ���",                   /* MONITOR */
+    "�I�s",                       /* PAGE */
+    "�d��",                       /* TQUERY */
+    "���",                       /* TALK  */
+    "�s�W����",                   /* EDITPLAN */
+    "�sñ�W��",                   /* EDITSIG */
+    "�벼��",                     /* VOTING */
+    "�]�w���",                   /* XINFO */
+    "�H������",                   /* MSYSOP */
+    "�L�L�L",                     /* WWW */
+    "���j�ѤG",                   /* BIG2 */
+    "�^��",                       /* REPLY */
+    "�Q���y����",                 /* HIT */
+    "���y�dzƤ�",                 /* DBACK */
+    "���O��",                     /* NOTE */
+    "�s��峹",                   /* EDITING */
+    "�o�t�γq�i",                 /* MAILALL */
+    "�N���",                     /* MJ */
+    "�q���ܤ�",                   /* P_FRIEND */
+    "�W���~��",                   /* LOGIN */
+    "�d�r��",                     /* DICT */
+    "�����P",                     /* BRIDGE */
+    "���ɮ�",                     /* ARCHIE */
+    "���a��",                     /* GOPHER */
+    "��News",                     /* NEWS */
+    "���Ѳ��;�",                 /* LOVE */
+    "�s�y���U��",  		  /* EDITEXP */
+    "�ӽ�IP��}",		  /* IPREG */
+    "���޿줽��", 		  /* NetAdm */
+    "������~�{",  		  /* DRINK */
+    "�p���",                     /* CAL */
+    "�s�y�y�k��",		  /* PROVERB */
+    "���G��",                     /* ANNOUNCE */
+    "��y����",                   /* EDNOTE */
+    "�^�~½Ķ��",   		  /* CDICT */
+    "�˵��ۤv���~",		  /* LOBJ */
+    "�I�q",                       /* OSONG */
+    "���b���p��",                 /* CHICKEN */
+    "���m��",                     /* TICKET */
+    "�q�Ʀr",                     /* GUESSNUM */ 
+    "�C�ֳ�",			  /* AMUSE */
+    "�¥մ�",			  /* OTHELLO */
+    "����l",                     /* DICE*/
+    "�o�����",                   /* VICE */  
+    "�G�G��ing",                  /* BBCALL */
+    "ú�@��",                     /* CROSSPOST */
+    "���l��",                     /* M_FIVE */
+    "21�Iing",                    /* JACK_CARD */
+    "10�I�bing",                  /* TENHALF */ 
+    "�W�ŤE�Q�E",                 /* CARD_99 */
+    "�����d��",                   /* RAIL_WAY */
+    "�j�M���",                    /* SREG */
+    "�U�H��",                      /* CHC */
+    "�U�t�X",			   /* DARK */
+    "NBA�j�q��"                    /* TMPJACK */
+    "��tt�d�]�t��",                 /* JCEE */
+    "���s�峹"                    /* REEDIT */
+};
diff --git a/mbbsd/vice.c b/mbbsd/vice.c
new file mode 100644
index 00000000..46f695d9
--- /dev/null
+++ b/mbbsd/vice.c
@@ -0,0 +1,151 @@
+/* $Id: vice.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+extern int usernum;
+
+#define VICE_PLAY   BBSHOME "/etc/vice/vice.play"
+#define VICE_DATA   "vice.new"
+#define VICE_BINGO  BBSHOME "/etc/vice.bingo"
+#define VICE_SHOW   BBSHOME "/etc/vice.show"
+#define VICE_LOST   BBSHOME "/etc/vice/vice.lost"
+#define VICE_WIN    BBSHOME "/etc/vice/vice.win"
+#define VICE_END    BBSHOME "/etc/vice/vice.end"
+#define VICE_NO     BBSHOME "/etc/vice/vice.no"
+#define MAX_NO_PICTURE   2
+#define MAX_WIN_PICTURE  2
+#define MAX_LOST_PICTURE 3
+#define MAX_END_PICTURE  5
+
+
+
+static int vice_load(char tbingo[6][15]) {
+    FILE *fb = fopen(VICE_BINGO, "r");
+    char buf[16], *ptr;
+    int i = 0;
+    if(!fb) return -1; bzero((char*)tbingo, sizeof(tbingo));
+    while(i < 6 && fgets(buf, 15, fb)) {
+        if((ptr = strchr(buf, '\n')))
+            *ptr = 0;
+        strcpy(tbingo[i++], buf);
+    }
+    fclose(fb);
+    return 0;
+}
+
+static int check(char tbingo[6][15], char *data) {
+    int i = 0, j;
+
+    if(!strcmp(data, tbingo[0]))
+        return 8;
+
+    for(j = 8; j > 0; j--)
+        for(i = 1; i < 6; i++)
+            if(!strncmp(data+8-j, tbingo[i]+8-j,j))
+                return j - 1;
+    return 0;
+}
+// Ptt: showfile ran_showfile more �T�̭n��X
+static int ran_showfile(int y, int x, char *filename, int maxnum) {
+    FILE *fs;
+    char buf[512];
+
+    bzero(buf, sizeof(char) * 512);
+    sprintf(buf, "%s%d", filename, rand() % maxnum + 1);
+    if(!(fs = fopen(buf, "r"))) {
+        move(10,10);
+        prints("can't open file: %s", buf);
+        return 0;
+    }
+
+    move(y, x);
+
+    while(fgets(buf, 511, fs))
+        prints("%s", buf);
+
+    fclose(fs);
+    return 1;
+}
+
+static int ran_showmfile(char *filename, int maxnum) {
+    char buf[256];
+
+    sprintf(buf, "%s%d", filename, rand() % maxnum + 1);
+    return more(buf, YEA);
+}
+
+extern userec_t cuser;
+
+int vice_main() {
+    FILE *fd;
+    char tbingo[6][15];
+    char buf_data[256]
+	, serial[16], ch[2], *ptr;
+    int TABLE[] = {0,10,200,1000,4000,10000,40000,100000,200000};
+    int total = 0, money, i = 4, j = 0;
+
+    setuserfile(buf_data, VICE_DATA);
+    if(!dashf(buf_data)) {
+        ran_showmfile(VICE_NO, MAX_NO_PICTURE);
+        return 0;
+    }
+    if(vice_load(tbingo)<0) return -1;
+    clear();
+    ran_showfile(0, 0, VICE_PLAY, 1);
+    ran_showfile(10, 0, VICE_SHOW, 1);
+
+    if(!(fd = fopen(buf_data, "r")))
+        return 0;
+    j = 0;
+    i = 0;
+    move(10,24);
+    clrtoeol();
+    prints("�o�@�����o�����X");
+    while(fgets(serial, 15, fd)) {
+        if((ptr = strchr(serial,'\r')))
+            *ptr = 0;
+        if(j == 0)
+            i++;
+        move(10 + i, 24 + j);
+        prints("%s", serial);
+        j += 9;
+        j %= 45;
+    }
+    getdata(8, 0, "��'c'�}�l����F(�άO���N�����})): ", ch, 2, LCECHO);
+    if(ch[0] != 'c' || lockutmpmode(VICE, LOCK_MULTI)){
+            fclose(fd);
+            return 0;
+        }
+
+    showtitle("�o�����", BBSNAME);
+    rewind(fd);
+    while(fgets(serial, 15, fd)) {
+        if((ptr = strchr(serial,'\n')))
+            *ptr = 0;
+        money = TABLE[check(tbingo,serial)];
+        total += money;
+        prints("%s ���F %d\n", serial, money);
+    }
+    pressanykey();
+    if(total > 0) {
+        ran_showmfile(VICE_WIN, MAX_WIN_PICTURE);
+        move(22,0);
+        clrtoeol();
+        prints("�������o�����F %d ����\n", total);
+        demoney(total);
+    } else
+        ran_showmfile(VICE_LOST, MAX_LOST_PICTURE);
+
+    fclose(fd);
+    unlink(buf_data);
+    pressanykey();
+    unlockutmpmode();
+    return 0;
+}
diff --git a/mbbsd/vote.c b/mbbsd/vote.c
new file mode 100644
index 00000000..62916c99
--- /dev/null
+++ b/mbbsd/vote.c
@@ -0,0 +1,1068 @@
+/* $Id: vote.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "perm.h"
+#include "proto.h"
+
+static int total;
+extern int numboards;
+extern boardheader_t *bcache;     /* Thor: for speed up */
+extern char *err_board_update;
+extern char *fn_board;
+extern char *msg_seperator;
+extern int t_lines, t_columns;  /* Screen size / width */
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern int currmode;
+extern int usernum;
+extern char currboard[];        /* name of currently selected board */
+extern userec_t cuser;
+
+static char STR_bv_control[] = "control";  /* �벼��� �ﶵ */
+static char STR_bv_desc[] = "desc";        /* �벼�ت� */
+static char STR_bv_ballots[] = "ballots";
+static char STR_bv_flags[] = "flags";
+static char STR_bv_comments[] = "comments"; /* �벼�̪��طN */
+static char STR_bv_limited[] = "limited"; /* �p�H�벼 */
+static char STR_bv_title[] = "vtitle";
+
+static char STR_bv_results[] = "results";
+
+static char STR_new_control[] = "control0";  /* �벼��� �ﶵ */
+static char STR_new_desc[] = "desc0";        /* �벼�ت� */
+static char STR_new_ballots[] = "ballots0";
+static char STR_new_flags[] = "flags0";
+static char STR_new_comments[] = "comments0"; /* �벼�̪��طN */
+static char STR_new_limited[] = "limited0"; /* �p�H�벼 */
+static char STR_new_title[] = "vtitle0";
+
+int strip_ansi(char *buf, char *str, int mode) {
+    register int ansi, count = 0;
+
+    for(ansi = 0; *str /*&& *str != '\n' */; str++) {
+	if(*str == 27) {
+	    if(mode) {
+		if(buf)
+		    *buf++ = *str;
+		count++;
+	    }
+	    ansi = 1;
+	} else if(ansi && strchr("[;1234567890mfHABCDnsuJKc=n", *str)) {
+	    if((mode == NO_RELOAD && !strchr("c=n", *str)) ||
+	       (mode == ONLY_COLOR && strchr("[;1234567890m", *str))) {
+		if(buf)
+		    *buf++ = *str;
+		count++;
+	    }
+	    if(strchr("mHn ", *str))
+		ansi = 0;
+	} else {
+	    ansi =0;
+	    if(buf)
+		*buf++ = *str;
+	    count++;
+	}
+    }
+    if(buf)
+	*buf = '\0';
+    return count;
+}
+
+void b_suckinfile(FILE *fp, char *fname) {
+    FILE *sfp;
+
+    if((sfp = fopen(fname, "r"))) {
+	char inbuf[256];
+
+	while(fgets(inbuf, sizeof(inbuf), sfp))
+	    fputs(inbuf, fp);
+	fclose(sfp);
+    }
+}
+
+static void b_count(char *buf, int counts[]) {
+    char inchar;
+    int fd;
+
+    memset(counts, 0, 31 * sizeof(counts[0]));
+    total = 0;
+    if((fd = open(buf, O_RDONLY)) != -1) {
+	flock(fd, LOCK_EX);         /* Thor: ����h�H�P�ɺ� */
+	while(read(fd, &inchar, 1) == 1) {
+	    counts[(int)(inchar - 'A')]++;
+	    total++;
+	}
+	flock(fd, LOCK_UN);
+	close(fd);
+    }
+}
+
+
+static int b_nonzeroNum(char *buf) {
+    int i = 0;
+    char inchar;
+    int fd;
+
+    if((fd = open(buf, O_RDONLY)) != -1) {
+	while(read(fd, &inchar, 1) == 1)
+	    if(inchar)
+		i++;
+	close(fd);
+    }
+    return i;
+}
+
+static void vote_report(char *bname, char *fname, char *fpath) {
+    register char *ip;
+    time_t dtime;
+    int fd, bid;
+    fileheader_t header;
+
+    ip = fpath;
+    while(*(++ip));
+    *ip++ = '/';
+
+    /* get a filename by timestamp */
+
+    dtime = time(0);
+    for(;;) {
+	sprintf(ip, "M.%ld.A", ++dtime);
+	fd = open(fpath, O_CREAT | O_EXCL | O_WRONLY, 0644);
+	if(fd >= 0)
+	    break;
+	dtime++;
+    }
+    close(fd);
+    
+    log_usies("TESTfn", fname);
+    log_usies("TESTfp", fpath);
+    
+    unlink(fpath);
+    link(fname, fpath);
+    
+    /* append record to .DIR */
+    
+    memset(&header, 0, sizeof(fileheader_t));
+    strcpy(header.owner, "[�������l]");
+    sprintf(header.title, "[%s] �ݪO �ﱡ����", bname);
+    {
+	register struct tm *ptime = localtime(&dtime);
+	
+	sprintf(header.date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
+    }
+    strcpy(header.filename, ip);
+
+    strcpy(ip, ".DIR");
+    if((fd = open(fpath, O_WRONLY | O_CREAT, 0644)) >= 0) {
+	flock(fd, LOCK_EX);
+	lseek(fd, 0, SEEK_END);
+	write(fd, &header, sizeof(fileheader_t));
+	flock(fd, LOCK_UN);
+	close(fd);
+        if((bid = getbnum(bname)) > 0)
+    	   setbtotal(bid);
+
+    }
+   
+}
+
+static void b_result_one(boardheader_t *fh, int ind) {
+    FILE *cfp, *tfp, *frp, *xfp;
+    char *bname ;
+    char buf[STRLEN];
+    char inbuf[80];
+    int counts[31];
+    int num ;
+    int junk;
+    char b_control[64];
+    char b_newresults[64];
+    char b_report[64];
+    time_t closetime, now;
+
+    fh->bvote--;
+
+    if(fh->bvote==0)
+        fh->bvote=2;
+    else if(fh->bvote==2)
+        fh->bvote=1;
+
+    if(ind) {
+	sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, ind);
+	sprintf(STR_new_control, "%s%d", STR_bv_control, ind);
+	sprintf(STR_new_desc, "%s%d", STR_bv_desc, ind);
+	sprintf(STR_new_flags, "%s%d", STR_bv_flags, ind);
+	sprintf(STR_new_comments, "%s%d", STR_bv_comments, ind);
+	sprintf(STR_new_limited, "%s%d", STR_bv_limited, ind);
+	sprintf(STR_new_title, "%s%d", STR_bv_title, ind);
+    } else {
+	strcpy(STR_new_ballots, STR_bv_ballots);
+	strcpy(STR_new_control, STR_bv_control);
+	strcpy(STR_new_desc, STR_bv_desc);
+	strcpy(STR_new_flags, STR_bv_flags);
+	strcpy(STR_new_comments, STR_bv_comments);
+	strcpy(STR_new_limited, STR_bv_limited);
+	strcpy(STR_new_title, STR_bv_title);
+    }
+
+    bname = fh->brdname;
+
+    setbfile(buf, bname, STR_new_control);
+    cfp = fopen(buf,"r");
+    fscanf(cfp, "%d\n%lu\n", &junk, &closetime);
+    fclose(cfp);
+
+    setbfile(b_control, bname, "tmp");
+    if(rename(buf, b_control) == -1)
+	return;
+    setbfile(buf, bname, STR_new_flags);
+    num = b_nonzeroNum(buf);
+    unlink(buf);
+    setbfile(buf, bname, STR_new_ballots);
+    b_count(buf, counts);
+    unlink(buf);
+
+    setbfile(b_newresults, bname, "newresults");
+    if((tfp = fopen(b_newresults, "w")) == NULL)
+	return;
+
+    now = time(NULL);
+    setbfile(buf, bname, STR_new_title);
+
+    if((xfp=fopen(buf,"r"))) {
+	fgets(inbuf, sizeof(inbuf), xfp);
+	fprintf(tfp, "%s\n�� �벼�W��: %s\n\n", msg_seperator, inbuf);
+    }
+    
+    fprintf(tfp, "%s\n�� �벼�����: %s\n\n�� �����D�شy�z:\n\n",
+	    msg_seperator, ctime(&closetime));
+    fh->vtime = now;
+    
+    setbfile(buf, bname, STR_new_desc);
+    
+    b_suckinfile(tfp, buf);
+    unlink(buf);
+    
+    if((cfp = fopen(b_control, "r"))) {
+	fgets(inbuf, sizeof(inbuf), cfp);
+	fgets(inbuf, sizeof(inbuf), cfp);
+	fprintf(tfp, "\n���벼���G:(�@�� %d �H�벼,�C�H�̦h�i�� %d ��)\n",
+		num, junk);
+	while(fgets(inbuf, sizeof(inbuf), cfp)) {
+	    inbuf[(strlen(inbuf) - 1)] = '\0';
+	    num = counts[inbuf[0] - 'A'];
+	    fprintf(tfp, "    %-42s %3d ��   %02.2f%%\n", inbuf + 3, num,
+		    (float)(num*100)/(float)(total));
+	}
+	fclose(cfp);
+    }
+    unlink(b_control);
+    
+    fprintf(tfp, "%s\n�� �ϥΪ̫�ij�G\n\n", msg_seperator);
+    setbfile(buf, bname, STR_new_comments);
+    b_suckinfile(tfp, buf);
+    unlink(buf);
+    
+    fprintf(tfp, "%s\n�� �`���� = %d ��\n\n", msg_seperator, total);
+    fclose(tfp);
+    
+    setbfile(b_report, bname, "report");
+    if((frp = fopen(b_report, "w"))) {
+	b_suckinfile(frp, b_newresults);
+	fclose(frp);
+    }
+    sprintf(inbuf, "boards/%s", bname);
+    vote_report(bname, b_report, inbuf);
+    if(!(fh->brdattr &BRD_NOCOUNT)) {
+	sprintf(inbuf, "boards/%s", "Record");
+	vote_report(bname, b_report, inbuf);
+    }
+    unlink(b_report);
+    
+    tfp = fopen(b_newresults, "a");
+    setbfile(buf, bname, STR_bv_results);
+    b_suckinfile(tfp, buf);
+    fclose(tfp);
+    Rename(b_newresults, buf);
+}
+
+static void b_result(boardheader_t *fh) {
+    FILE *cfp;
+    time_t closetime, now;
+    int i;
+    char buf[STRLEN];
+    char temp[STRLEN];
+
+    now = time(NULL);
+    for(i = 0; i < 9; i++) {
+	if(i)
+	    sprintf(STR_new_control, "%s%d", STR_bv_control, i);
+        else
+	    strcpy(STR_new_control, STR_bv_control);
+
+        setbfile(buf, fh->brdname, STR_new_control);
+        cfp = fopen(buf,"r");
+        if (!cfp)
+            continue;
+        fgets(temp,sizeof(temp),cfp);
+        fscanf(cfp, "%lu\n", &closetime);
+        fclose(cfp);
+        if(closetime < now)
+	    b_result_one(fh,i);
+    }
+}
+
+static int b_close(boardheader_t *fh) {
+    time_t now;
+    now = time(NULL);
+
+    if(fh->bvote == 2) {
+	if(fh->vtime < now - 3 * 86400) {
+	    fh->bvote = 0;
+	    return 1;
+	}
+	else
+	    return 0;
+    }
+    b_result(fh);
+    return 1;
+}
+
+int b_closepolls() {
+    static char *fn_vote_polling = ".polling";
+    boardheader_t *fhp;
+    FILE *cfp;
+    time_t now;
+    int pos, dirty;
+    time_t last;
+    char timebuf[100];
+
+    now = time(NULL);
+/* Edited by CharlieL for can't auto poll bug */
+
+    if((cfp = fopen(fn_vote_polling,"r"))) {
+        fgets(timebuf,100*sizeof(char),cfp);
+        sscanf(timebuf, "%lu", &last);
+        fclose(cfp);
+        if(last + 3600 >= now)
+            return 0;
+    }
+
+    if((cfp = fopen(fn_vote_polling, "w")) == NULL)
+	return 0;
+    fprintf(cfp, "%lu\n%s\n", now, ctime(&now));
+    fclose(cfp);
+
+    dirty = 0;
+    for(fhp = bcache, pos = 1; pos <= numboards; fhp++, pos++) {
+	if(fhp->bvote && b_close(fhp)) {
+	    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
+		outs(err_board_update);
+	    dirty = 1;
+	}
+    }
+    if(dirty) /* vote flag changed */
+	reset_board(pos);
+
+    return 0;
+}
+
+static int vote_view(char *bname, int index) {
+    boardheader_t *fhp;
+    FILE* fp;
+    char buf[STRLEN], genbuf[STRLEN], inbuf[STRLEN];
+    struct stat stbuf;
+    int fd, num = 0, i, pos, counts[31];
+    time_t closetime;
+
+    if(index) {
+	sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, index);
+	sprintf(STR_new_control, "%s%d", STR_bv_control, index);
+	sprintf(STR_new_desc, "%s%d", STR_bv_desc, index);
+	sprintf(STR_new_flags, "%s%d", STR_bv_flags, index);
+	sprintf(STR_new_comments, "%s%d", STR_bv_comments, index);
+	sprintf(STR_new_limited, "%s%d", STR_bv_limited, index);
+	sprintf(STR_new_title, "%s%d", STR_bv_title, index);
+    } else {
+	strcpy(STR_new_ballots, STR_bv_ballots);
+	strcpy(STR_new_control, STR_bv_control);
+	strcpy(STR_new_desc, STR_bv_desc);
+	strcpy(STR_new_flags, STR_bv_flags);
+	strcpy(STR_new_comments, STR_bv_comments);
+	strcpy(STR_new_limited, STR_bv_limited);
+	strcpy(STR_new_title, STR_bv_title);
+    }
+
+    setbfile(buf, bname, STR_new_ballots);
+    if((fd = open(buf, O_RDONLY)) > 0) {
+	fstat(fd, &stbuf);
+	close(fd);
+    } else
+	stbuf.st_size = 0;
+    
+    setbfile(buf, bname, STR_new_title);
+    move(0, 0);
+    clrtobot();
+    
+    if((fp = fopen(buf, "r"))) {
+	fgets(inbuf, sizeof(inbuf), fp);
+	prints("\n�벼�W��: %s", inbuf);
+    }
+    
+    setbfile(buf, bname, STR_new_control);
+    fp = fopen(buf, "r");
+    fgets(inbuf, sizeof(inbuf), fp);
+    fscanf(fp, "%lu\n", &closetime);
+
+    prints("\n�� �w���벼����: �C�H�̦h�i�� %d ��,�ثe�@�� %d ��,\n"
+	   "�����벼�N������ %s", atoi(inbuf),  stbuf.st_size,
+	   ctime(&closetime));
+    
+    /* Thor: �}�� ���� �w�� */
+    setbfile(buf, bname, STR_new_flags);
+    num = b_nonzeroNum(buf);
+    
+    setbfile(buf, bname, STR_new_ballots);
+    b_count(buf, counts);
+    
+    prints("�@�� %d �H�벼\n", num);
+    total = 0;
+    
+    while(fgets(inbuf, sizeof(inbuf), fp)) {
+	inbuf[(strlen(inbuf) - 1)] = '\0';
+	inbuf[30] = '\0';         /* truncate */
+	i = inbuf[0] - 'A';
+	num = counts[i];
+	move(i % 15 + 6, i / 15 * 40);
+	prints("  %-32s%3d ��", inbuf, num);
+	total += num;
+    }
+    fclose(fp);
+    pos = getbnum(bname);
+    fhp = bcache + pos - 1;
+    move(t_lines - 3, 0);
+    prints("�� �ثe�`���� = %d ��", total);
+    getdata(b_lines - 1, 0, "(A)�����벼 (B)�����}�� (C)�~��H[C] ", genbuf,
+	    4, LCECHO);
+    if(genbuf[0] == 'a') {
+	setbfile(buf, bname, STR_new_control);
+	unlink(buf);
+	setbfile(buf, bname, STR_new_flags);
+	unlink(buf);
+	setbfile(buf, bname, STR_new_ballots);
+	unlink(buf);
+	setbfile(buf, bname, STR_new_desc);
+	unlink(buf);
+	setbfile(buf, bname, STR_new_limited);
+	unlink(buf);
+	setbfile(buf,bname, STR_new_title);
+	unlink(buf);
+	
+	if(fhp->bvote)
+	    fhp->bvote--;
+	if (fhp->bvote == 2)
+	    fhp->bvote = 1;
+	
+	if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
+	    outs(err_board_update);
+	reset_board(pos);
+    } else if(genbuf[0] == 'b') {
+	b_result_one(fhp,index);
+	if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
+	    outs(err_board_update);
+	
+	reset_board(pos);
+    }
+    return FULLUPDATE;
+}
+
+static int vote_view_all(char *bname) {
+    int i;
+    int x = -1;
+    FILE *fp, *xfp;
+    char buf[STRLEN], genbuf[STRLEN];
+    char inbuf[80];
+    
+    strcpy(STR_new_control, STR_bv_control);
+    strcpy(STR_new_title, STR_bv_title);
+    setbfile(buf, bname, STR_new_control);
+    move(0, 0);
+    if((fp=fopen(buf,"r"))) {
+	prints("(0) ");
+	x = 0;
+	fclose(fp);
+	
+	setbfile(buf, bname, STR_new_title);
+	if((xfp=fopen(buf,"r")))
+	    fgets(inbuf, sizeof(inbuf), xfp);
+	else
+	    strcpy(inbuf, "�L���D");
+	prints("%s\n", inbuf);
+	fclose(xfp);
+    }
+
+    for(i = 1; i < 9; i++) {
+	sprintf(STR_new_control, "%s%d", STR_bv_control, i);
+	sprintf(STR_new_title, "%s%d", STR_bv_title, i);
+	setbfile(buf, bname, STR_new_control);
+	if((fp=fopen(buf,"r"))) {
+	    prints("(%d) ", i);
+	    x = i;
+	    fclose(fp);
+
+	    setbfile(buf, bname, STR_new_title);
+	    if((xfp=fopen(buf,"r")))
+		fgets(inbuf, sizeof(inbuf), xfp);
+	    else
+		strcpy(inbuf, "�L���D");
+	    prints("%s\n", inbuf);
+	    fclose(xfp);
+	}
+    }
+
+    if(x < 0)
+	return FULLUPDATE;
+    sprintf(buf, "�n�ݴX���벼 [%d] ", x);
+    
+    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);
+
+    if(genbuf[0] < '0' || genbuf[0] > '8')
+	genbuf[0] = '0'+x;
+
+    if(genbuf[0] != '0')
+	sprintf(STR_new_control, "%s%c", STR_bv_control, genbuf[0]);
+    else
+	strcpy(STR_new_control, STR_bv_control);
+    
+    setbfile(buf, bname, STR_new_control);
+
+    if((fp=fopen(buf,"r"))) {
+	fclose(fp);
+	return vote_view(bname, genbuf[0] - '0');
+    }
+    else
+	return FULLUPDATE;
+}
+
+static int vote_maintain(char *bname) {
+    FILE *fp = NULL;
+    char inbuf[STRLEN], buf[STRLEN];
+    int num = 0, aborted, pos, x, i;
+    time_t closetime;
+    boardheader_t *fhp;
+    char genbuf[4];
+
+    if(!(currmode & MODE_BOARD))
+	return 0;
+    if((pos = getbnum(bname)) <= 0)
+	return 0;
+
+    stand_title("�|��벼");
+    fhp = bcache + pos - 1;
+
+/* CharlieL */
+    if(fhp->bvote != 2 && fhp->bvote !=0) {
+	getdata(b_lines - 1, 0,
+		"(V)�[��ثe�벼 (M)�|��s�벼 (A)�����Ҧ��벼 (Q)�~�� [Q]",
+		genbuf, 4, LCECHO);
+	if(genbuf[0] == 'v')
+	    return vote_view_all(bname);
+	else if(genbuf[0] == 'a') {
+	    fhp->bvote=0;
+	    
+	    setbfile(buf, bname, STR_bv_control);
+	    unlink(buf);
+	    setbfile(buf, bname, STR_bv_flags);
+	    unlink(buf);
+	    setbfile(buf, bname, STR_bv_ballots);
+	    unlink(buf);
+	    setbfile(buf, bname, STR_bv_desc);
+	    unlink(buf);
+	    setbfile(buf, bname, STR_bv_limited);
+	    unlink(buf);
+	    setbfile(buf, bname, STR_bv_title);
+	    unlink(buf);
+	    
+	    for(i = 1; i < 9; i++) {
+		sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, i);
+		sprintf(STR_new_control, "%s%d", STR_bv_control, i);
+		sprintf(STR_new_desc, "%s%d", STR_bv_desc, i);
+		sprintf(STR_new_flags, "%s%d", STR_bv_flags, i);
+		sprintf(STR_new_comments, "%s%d", STR_bv_comments, i);
+		sprintf(STR_new_limited, "%s%d", STR_bv_limited, i);
+		sprintf(STR_new_title, "%s%d", STR_bv_title, i);
+		
+		setbfile(buf, bname, STR_new_control);
+		unlink(buf);
+		setbfile(buf, bname, STR_new_flags);
+		unlink(buf);
+		setbfile(buf, bname, STR_new_ballots);
+		unlink(buf);
+		setbfile(buf, bname, STR_new_desc);
+		unlink(buf);
+		setbfile(buf, bname, STR_new_limited);
+		unlink(buf);
+		setbfile(buf, bname, STR_new_title);
+		unlink(buf);
+	    }
+	    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
+		outs(err_board_update);
+	    
+	    return FULLUPDATE;
+	} else if(genbuf[0] != 'm' || fhp->bvote > 10)
+	    return FULLUPDATE;
+    }
+
+    strcpy(STR_new_control, STR_bv_control);
+    setbfile(buf,bname, STR_new_control);
+    x = 0;
+    while(x < 9 && (fp = fopen(buf,"r")) != NULL) {
+	fclose(fp);
+        x++;
+        sprintf(STR_new_control, "%s%d", STR_bv_control, x);
+	setbfile(buf, bname, STR_new_control);
+    }
+    if(fp)
+	fclose(fp);
+    if(x >=9)
+        return FULLUPDATE;
+    if(x) {
+	sprintf(STR_new_ballots, "%s%d", STR_bv_ballots,x);
+	sprintf(STR_new_control, "%s%d", STR_bv_control,x);
+	sprintf(STR_new_desc, "%s%d", STR_bv_desc,x);
+	sprintf(STR_new_flags, "%s%d", STR_bv_flags,x);
+	sprintf(STR_new_comments, "%s%d", STR_bv_comments,x);
+	sprintf(STR_new_limited, "%s%d", STR_bv_limited,x);
+	sprintf(STR_new_title, "%s%d", STR_bv_title,x);
+    } else {
+	strcpy(STR_new_ballots, STR_bv_ballots);
+	strcpy(STR_new_control, STR_bv_control);
+	strcpy(STR_new_desc, STR_bv_desc);
+	strcpy(STR_new_flags, STR_bv_flags);
+	strcpy(STR_new_comments, STR_bv_comments);
+	strcpy(STR_new_limited, STR_bv_limited);
+	strcpy(STR_new_title, STR_bv_title);
+    }
+    clear();
+    move(0,0);
+    prints("�� %d ���벼\n", x);
+    setbfile(buf, bname, STR_new_title);
+    getdata(4, 0, "�п�J�벼�W��", inbuf, 30, LCECHO);
+    if(inbuf[0]=='\0')
+	strcpy(inbuf,"�����W��");
+    fp = fopen(buf, "w");
+    fprintf(fp, "%s", inbuf);
+    fclose(fp);
+    
+    prints("��������}�l�s�覹�� [�벼�v��]");
+    pressanykey();
+    setbfile(buf, bname, STR_new_desc);
+    aborted = vedit(buf, NA, NULL);
+    if(aborted== -1) {
+	clear();
+	outs("���������벼");
+	pressanykey();
+	return FULLUPDATE;
+    }
+    aborted = 0;
+    setbfile(buf, bname, STR_new_flags);
+    unlink(buf);
+    
+    getdata(4, 0,
+	    "�O�_���w�벼�̦W��G(y)�s�y�i�벼�H���W��[n]����H�ҥi�벼:[N]",
+	    inbuf, 2, LCECHO);
+    setbfile(buf, bname, STR_new_limited);
+    if(inbuf[0] == 'y') {
+	fp = fopen(buf, "w");
+	fprintf(fp,"�����벼�]��");
+	fclose(fp);
+	friend_edit(FRIEND_CANVOTE);
+    } else {
+	if(dashf(buf))
+	    unlink(buf);
+    }
+    clear();
+    getdata(0, 0, "�����벼�i��X�� (�@��Q��)�H", inbuf, 4, DOECHO);
+
+    closetime = atoi(inbuf);
+    if(closetime <= 0)
+	closetime = 1;
+    else if(closetime >10)
+	closetime = 10;
+    
+    closetime = closetime * 86400 + time(NULL);
+    setbfile(buf, bname, STR_new_control);
+    fp = fopen(buf, "w");
+    fprintf(fp, "00\n%lu\n", closetime);
+
+    outs("\n�Ш̧ǿ�J�ﶵ, �� ENTER �����]�w");
+    num = 0;
+    while(!aborted) {
+	sprintf(buf, "%c) ", num + 'A');
+	getdata((num % 15) + 2, (num / 15) * 40, buf, inbuf, 36, DOECHO);
+	if(*inbuf) {
+	    fprintf(fp, "%1c) %s\n", (num+'A'), inbuf);
+	    num++;
+	}
+	if((*inbuf == '\0' && num >= 1) || num == 30)
+	    aborted = 1;
+    }
+    sprintf(buf, "�аݨC�H�̦h�i��X���H([1]��%d): ", num);
+    
+    getdata(t_lines-3, 0, buf, inbuf, 3, DOECHO);
+    
+    if(atoi(inbuf) <= 0 || atoi(inbuf) > num)
+	strcpy(inbuf,"1");
+    
+    rewind(fp);
+    fprintf(fp, "%2d\n", MAX(1, atoi(inbuf)));
+    fclose(fp);
+    
+    if(fhp->bvote == 2)
+        fhp->bvote = 0;
+    else if(fhp->bvote == 1)
+        fhp->bvote = 2;
+    else if(fhp->bvote == 2)
+        fhp->bvote = 1;
+    
+    fhp->bvote ++;
+    
+    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
+	outs(err_board_update);
+    reset_board(pos);
+    outs("�}�l�벼�F�I");
+    
+    return FULLUPDATE;
+}
+
+static int vote_flag(char *bname, int index, char val) {
+    char buf[256], flag;
+    int fd, num, size;
+
+    if(index)
+	sprintf(STR_new_flags, "%s%d", STR_bv_flags, index);
+    else
+	strcpy(STR_new_flags, STR_bv_flags);
+    
+    num = usernum - 1;
+    setbfile(buf, bname, STR_new_flags);
+    if((fd = open(buf, O_RDWR | O_CREAT, 0600)) == -1)
+	return -1;
+    size = lseek(fd, 0, SEEK_END);
+    memset(buf, 0, sizeof(buf));
+    while(size <= num) {
+	write(fd, buf, sizeof(buf));
+	size += sizeof(buf);
+    }
+    lseek(fd, num, SEEK_SET);
+    read(fd, &flag, 1);
+    if(flag == 0 && val != 0) {
+	lseek(fd, num, SEEK_SET);
+	write(fd, &val, 1);
+    }
+    close(fd);
+    return flag;
+}
+
+static int same(char compare, char list[], int num) {
+    int n;
+    int rep = 0;
+
+    for(n = 0; n < num; n++) {
+	if(compare == list[n])
+	    rep = 1;
+	if(rep == 1)
+	    list[n] = list[n + 1];
+    }
+    return rep;
+}
+
+static int user_vote_one(char *bname, int ind) {
+    FILE* cfp,*fcm;
+    char buf[STRLEN];
+    boardheader_t *fhp;
+    int pos = 0, i = 0, count = 0, tickets, fd;
+    char inbuf[80], choices[31], vote[4], chosen[31];
+    time_t closetime;
+
+    if(ind) {
+	sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, ind);
+	sprintf(STR_new_control, "%s%d", STR_bv_control, ind);
+	sprintf(STR_new_desc, "%s%d", STR_bv_desc, ind);
+	sprintf(STR_new_flags, "%s%d", STR_bv_flags, ind);
+	sprintf(STR_new_comments, "%s%d", STR_bv_comments, ind);
+	sprintf(STR_new_limited, "%s%d", STR_bv_limited, ind);
+    } else {
+	strcpy(STR_new_ballots, STR_bv_ballots);
+	strcpy(STR_new_control, STR_bv_control);
+	strcpy(STR_new_desc, STR_bv_desc);
+	strcpy(STR_new_flags, STR_bv_flags);
+	strcpy(STR_new_comments, STR_bv_comments);
+	strcpy(STR_new_limited, STR_bv_limited);
+    }
+
+    setbfile(buf, bname, STR_new_control);
+    cfp = fopen(buf,"r");
+    if(!cfp)
+        return FULLUPDATE;
+
+    setbfile(buf, bname, STR_new_limited); /* Ptt */
+    if(dashf(buf)) {
+	setbfile(buf, bname, FN_CANVOTE);
+	if(!belong(buf, cuser.userid)) {
+	    fclose(cfp);
+	    outs("\n\n�藍�_! �o�O�p�H�벼..�A�èS�����ܭ�!");
+	    pressanykey();
+	    return FULLUPDATE;
+	} else {
+	    outs("\n\n���ߧA���ܦ����p�H�벼....<�����N���˵��������ܦW��>");
+	    pressanykey();
+	    more(buf, YEA);
+	}
+    }
+    if(vote_flag(bname, ind, '\0')) {
+	outs("\n\n�����벼�A�A�w��L�F�I");
+	pressanykey();
+	return FULLUPDATE;
+    }
+    
+    setutmpmode(VOTING);
+    setbfile(buf, bname, STR_new_desc);
+    more(buf, YEA);
+
+    stand_title("�벼�c");
+    if((pos = getbnum(bname)) <= 0)
+	return 0;
+
+    fhp = bcache + pos - 1;
+    fgets(inbuf, sizeof(inbuf), cfp);
+    tickets = atoi(inbuf);
+    fscanf(cfp,"%lu\n", &closetime);
+
+    prints("�벼�覡�G�T�w�n�z����ܫ�A��J��N�X(A, B, C...)�Y�i�C\n"
+	   "�����벼�A�i�H�� %1d ���C"
+	   "�� 0 �����벼 , 1 �����벼\n"
+	   "�����벼�N������G%s \n",
+	   tickets, ctime(&closetime));
+    move(5, 0);
+    memset(choices, 0, sizeof(choices));
+    memset(chosen , 0, sizeof(chosen));
+
+    while(fgets(inbuf, sizeof(inbuf), cfp)) {
+	move((count % 15) + 5, (count / 15) * 40);
+	prints( " %s", strtok(inbuf, "\n\0"));
+	choices[count++] = inbuf[0];
+    }
+    fclose(cfp);
+
+    while(1) {
+	vote[0] = vote[1] = '\0';
+	move(t_lines - 2, 0);
+	prints("�A�٥i�H�� %2d ��", tickets - i);
+	getdata(t_lines - 4, 0, "��J�z�����: ", vote, 3, DOECHO);
+	*vote = toupper(*vote);
+	if(vote[0] == '0' || (!vote[0] && !i)) {
+	    outs("�O���A�ӧ��!!");
+	    break;
+	} else if(vote[0] == '1' && i)
+	    ;
+	else if(!vote[0])
+	    continue;
+	else if(index(choices, vote[0]) == NULL) /* �L�� */
+	    continue;
+	else if(same(vote[0], chosen, i)) {
+	    move(((vote[0] - 'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
+	    prints(" ");
+	    i--;
+	    continue;
+	} else {
+	    if(i == tickets)
+		continue;
+	    chosen[i] = vote[0];
+	    move(((vote[0]-'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
+	    prints("*");
+	    i++;
+	    continue;
+	}
+	
+	if(vote_flag(bname, ind, vote[0]) != 0)
+	    prints("���Ч벼! �����p���C");
+	else {
+	    setbfile(buf, bname, STR_new_ballots);
+	    if((fd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0600)) == 0)
+		outs("�L�k��J���o\n");
+	    else {
+		struct stat statb;
+		char buf[3], mycomments[3][74], b_comments[80];
+		
+		for(i = 0; i < 3; i++)
+		    strcpy(mycomments[i], "\n");
+
+		flock(fd, LOCK_EX);
+		for(count = 0; count < 31; count++) {
+		    if(chosen[count])
+			write(fd, &chosen[count], 1);
+		}
+		flock(fd, LOCK_UN);
+		fstat(fd, &statb);
+		close(fd);
+		getdata(b_lines - 2, 0,
+			"�z��o���벼�������_�Q���N���ܡH(y/n)[N]",
+			buf, 3 ,DOECHO);
+		if(buf[0] == 'Y' || buf[0] == 'y'){
+		    do {
+			move(5,0);clrtobot();
+			outs("�аݱz��o���벼�������_�Q���N���H"
+			     "�̦h�T��A��[Enter]����");
+			for(i = 0; (i < 3) &&
+				getdata(7 + i, 0, "�G", mycomments[i], 74,
+					DOECHO); i++);
+			getdata(b_lines-2,0, "(S)�x�s (E)���s�ӹL "
+				"(Q)�����H[S]", buf, 3, LCECHO);
+		    } while(buf[0] == 'E' || buf[0] == 'e');
+		    if(buf[0] == 'Q' || buf[0] == 'q')
+			break;
+		    setbfile(b_comments, bname, STR_new_comments);
+		    if(mycomments[0])
+			if((fcm = fopen(b_comments, "a"))){
+			    fprintf(fcm, 
+		                     "\033[36m���ϥΪ�\033[1;36m %s "
+			             "\033[;36m����ij�G\033[m\n",
+				    cuser.userid);
+			    for(i = 0; i < 3; i++)
+				fprintf(fcm, "    %s\n", mycomments[i]);
+			    fprintf(fcm, "\n");
+			    fclose(fcm);
+		        }
+		}
+		move(b_lines - 1 ,0);
+		prints("�w�����벼�I\n");
+	    }
+	}
+	break;
+    }
+    pressanykey();
+    return FULLUPDATE;
+}
+
+static int user_vote(char *bname) {
+    int pos;
+    boardheader_t *fhp;
+    char buf[STRLEN];
+    FILE* fp,*xfp;
+    int i, x = -1;
+    char genbuf[STRLEN];
+    char inbuf[80];
+    
+    if((pos = getbnum(bname)) <= 0)
+	return 0;
+    
+    fhp = bcache + pos - 1;
+    
+    move(0,0);
+    clrtobot();
+    
+    if(fhp->bvote == 2 || fhp->bvote == 0) {
+	outs("\n\n�ثe�èS������벼�|��C");
+	pressanykey();
+	return FULLUPDATE;
+    }
+    
+    if(!HAS_PERM(PERM_LOGINOK)) {
+	outs("\n�藍�_! �z�����G�Q��, �٨S���벼�v��!");
+	pressanykey();
+	return FULLUPDATE;
+    }
+    
+    strcpy(STR_new_control, STR_bv_control);
+    strcpy(STR_new_title, STR_bv_title);
+    setbfile(buf, bname, STR_new_control);
+    move(0, 0);
+    if((fp = fopen(buf, "r"))) {
+	prints("(0) ");
+	x = 0;
+	fclose(fp);
+
+	setbfile(buf, bname, STR_new_title);
+	if((xfp = fopen(buf,"r")))
+	    fgets(inbuf, sizeof(inbuf), xfp);
+	else
+	    strcpy(inbuf, "�L���D");
+	prints("%s\n", inbuf);
+	fclose(xfp);
+    }
+    
+    for(i = 1; i < 9; i++) {
+	sprintf(STR_new_control, "%s%d", STR_bv_control, i);
+	sprintf(STR_new_title, "%s%d", STR_bv_title, i);
+	setbfile(buf, bname, STR_new_control);
+	if((fp = fopen(buf, "r"))) {
+	    prints("(%d) ", i);
+	    x = i;
+	    fclose(fp);
+	    
+	    setbfile(buf, bname, STR_new_title);
+	    if((xfp = fopen(buf, "r")))
+		fgets(inbuf, sizeof(inbuf), xfp);
+	    else
+		strcpy(inbuf, "�L���D");
+	    prints("%s\n", inbuf);
+	    fclose(xfp);
+	}
+    }
+    
+    if(x < 0)
+	return FULLUPDATE;
+    
+    sprintf(buf, "�n��X���벼 [%d] ", x);
+    
+    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);
+    
+    if(genbuf[0] < '0' || genbuf[0] > '8')
+        genbuf[0] = x + '0';
+    
+    if(genbuf[0] != '0')
+	sprintf(STR_new_control, "%s%c", STR_bv_control, genbuf[0]);
+    else
+	strcpy(STR_new_control, STR_bv_control);
+    
+    setbfile(buf, bname, STR_new_control);
+
+    if((fp = fopen(buf, "r"))){
+	fclose(fp);
+	
+	return user_vote_one(bname, genbuf[0] - '0');
+    } else
+	return FULLUPDATE;
+}
+
+static int vote_results(char *bname) {
+    char buf[STRLEN];
+
+    setbfile(buf, bname, STR_bv_results);
+    if(more(buf, YEA) == -1)
+	outs("\n�ثe�S������벼�����G�C");
+    return FULLUPDATE;
+}
+
+int b_vote_maintain() {
+    return vote_maintain(currboard);
+}
+
+int b_vote() {
+    return user_vote(currboard);
+}
+
+int b_results() {
+    return vote_results(currboard);
+}
diff --git a/mbbsd/voteboard.c b/mbbsd/voteboard.c
new file mode 100644
index 00000000..1286d986
--- /dev/null
+++ b/mbbsd/voteboard.c
@@ -0,0 +1,447 @@
+/* $Id: voteboard.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "modes.h"
+#include "common.h"
+#include "perm.h"
+#include "proto.h"
+
+#define VOTEBOARD "NewBoard"
+
+extern char currboard[];
+extern int currbid;
+extern boardheader_t *bcache;
+extern int currmode;
+extern userec_t cuser;
+
+void do_voteboardreply(fileheader_t *fhdr){
+    char genbuf[1024];
+    char reason[60];
+    char fpath[80];
+    char oldfpath[80];
+    char opnion[10];
+    char *ptr;
+    FILE *fo, *fp;
+    fileheader_t votefile;
+    int len;
+    int i, j;
+    int fd;
+    time_t endtime, now = time(NULL);
+    int hastime = 0;
+   
+
+    clear();
+    if(!(currmode & MODE_POST)) {
+	move(5, 10);
+	outs("�藍�_�A�z�ثe�L�k�b���o���峹�I");
+	pressanykey();
+	return;
+    }   
+    
+    setbpath(fpath, currboard);
+    stampfile(fpath, &votefile);
+
+    setbpath(oldfpath, currboard);
+   
+    strcat(oldfpath, "/");
+    strcat(oldfpath, fhdr->filename);
+   
+    fp = fopen(oldfpath, "r");
+   
+    len = strlen(cuser.userid);
+   
+    while(fgets(genbuf, 1024, fp)){
+	if (!strncmp(genbuf, "�s�p�����ɶ�", 12)){
+	    hastime = 1;
+	    ptr = strchr(genbuf, '(');
+	    sscanf(ptr+1, "%ld", &endtime);
+	    if (endtime < now){
+		prints("�s�p�ɶ��w�L");
+		pressanykey();
+		fclose(fp);
+		return;
+	    }
+	}
+	if (!strncmp(genbuf+4, cuser.userid, len)){
+	    move(5, 10);
+	    prints("�z�w�g�s�p�L���g�F");
+	    opnion[0] = 'n';
+	    getdata(7, 0, "�n�ק�z���e���s�p�ܡH(Y/N) [N]", opnion, 3, LCECHO);
+	    if (opnion[0] != 'y'){
+		fclose(fp);
+		return;
+	    }
+	    strcpy(reason, genbuf+19);
+	}
+    }
+    fclose(fp);                  
+   
+    if((fd = open(oldfpath, O_RDONLY)) == -1)
+	return;
+    flock(fd, LOCK_EX);
+   
+    fo = fopen(fpath, "w");
+   
+    if (!fo)
+	return;
+    i = 0;
+    while(fo){
+	j = 0;
+	do{
+	    if (read(fd, genbuf+j, 1)<=0){
+		flock(fd, LOCK_UN);
+		close(fd);
+		fclose(fo);
+		unlink(fpath);
+		return;
+	    }
+	    j++;
+	}while(genbuf[j-1] !='\n');
+	genbuf[j] = '\0';        
+	i++;
+	if (!strncmp("----------", genbuf, 10))
+	    break;
+	if (i > 3)
+	    prints(genbuf);
+	fprintf(fo, "%s", genbuf);    
+    }
+    if (!hastime){
+	now += 14*24*60*60;
+	fprintf(fo, "�s�p�����ɶ�: (%ld)%s", now, ctime(&now));
+	now -= 14*24*60*60;
+    }
+   
+    fprintf(fo, "%s", genbuf);
+
+    do{
+	if (!getdata(18, 0, "�аݱz (Y)��� (N)�Ϲ� �o��ij�D�G", opnion, 3, LCECHO)){
+	    flock(fd, LOCK_UN);
+	    close(fd);           
+	    fclose(fo);
+	    unlink(fpath);
+	    return;
+	}
+    }while(opnion[0] != 'y' && opnion[0] != 'n');      
+   
+    if (!getdata(20, 0, "�аݱz�P�o��ij�D�����Y�γs�p�z�Ѭ���G", reason, 40, DOECHO)){
+	flock(fd, LOCK_UN);
+	close(fd);           
+	fclose(fo);
+	unlink(fpath);
+	return;   
+    }
+   
+    i = 0;
+   
+    while(fo){
+	i++;
+	j = 0;
+	do{
+	    if (read(fd, genbuf+j, 1)<=0){
+		flock(fd, LOCK_UN);
+		close(fd);
+		fclose(fo);
+		unlink(fpath);
+		return;
+	    }      
+	    j++;
+	}while(genbuf[j-1] !='\n');   
+	genbuf[j] = '\0';
+	if (!strncmp("----------", genbuf, 10))
+	    break;
+	if (strncmp(genbuf+4, cuser.userid, len))
+	    fprintf(fo, "%3d.%s", i, genbuf+4);
+	else
+	    i--;
+    }
+    if (opnion[0] == 'y')
+	fprintf(fo, "%3d.%-15s%-34s �ӷ�:%s\n", i, cuser.userid, reason,cuser.lasthost);
+    i = 0;
+    fprintf(fo, "%s", genbuf);
+    while(fo){
+	i++;
+	j = 0;
+	do{
+	    if (!read(fd, genbuf+j, 1))
+		break;
+	    j++;
+	}while(genbuf[j-1] !='\n');
+	genbuf[j] = '\0';
+	if (j <= 3)
+	    break;         
+	if (strncmp(genbuf+4, cuser.userid, len))
+	    fprintf(fo, "%3d.%s", i, genbuf+4);
+	else
+	    i--;
+    }
+    if (opnion[0] == 'n')
+	fprintf(fo, "%3d.%-15s%-34s �ӷ�:%s\n", i, cuser.userid, reason,cuser.lasthost);
+    flock(fd, LOCK_UN);  
+    close(fd);
+    fclose(fo);
+    unlink(oldfpath);
+    rename(fpath, oldfpath);
+}
+
+int do_voteboard() {
+    fileheader_t votefile;
+    char topic[100];
+    char title[80];
+    char genbuf[1024];
+    char fpath[80];
+    FILE* fp;
+    int temp, i;
+    time_t now = time(NULL);
+
+    clear();
+    if(!(currmode & MODE_POST)) {
+	move(5, 10);
+	outs("�藍�_�A�z�ثe�L�k�b���o���峹�I");
+	pressanykey();
+	return FULLUPDATE;
+    }       
+    
+    move(0, 0);
+    clrtobot();
+    prints("�z���b�ϥ� PTT ���s�p�t��\n");
+    prints("���s�p�t�αN�߰ݱz�@�ǰ��D�A�Фp�ߦ^���~��}�l�s�p\n");
+    prints("���N���X�s�p�ת̡A�N�Q�C�J���t�Τ����w��ϥΪ̳�\n");
+    pressanykey();
+    move(0, 0);
+    clrtobot();    
+    prints("(1)�ӽзs�� (2)�o���ª� (3)�s�p���D (4)�}�K���D\n");
+    if (!strcmp(currboard, VOTEBOARD))
+	prints("(5)�s�p�p�ժ� (6)�}�K�p�ժ� ");
+    if (!strcmp(currboard, VOTEBOARD) && HAS_PERM(PERM_SYSOP))
+	prints("(7)��������");
+    prints("(8)�ӽзs�s��");
+       
+    do{       
+	getdata(3, 0, "�п�J�s�p���O�G", topic, 3, DOECHO);
+	temp = atoi(topic);
+    }while(temp <= 0 && temp >= 9);
+    
+    switch(temp){
+    case 1:    
+	do{
+	    if (!getdata(4, 0, "�п�J�ݪ��^��W�١G", topic, IDLEN+1, DOECHO))
+		return FULLUPDATE;
+	    else if (invalid_brdname(topic))
+		outs("���O���T���ݪ��W��");
+	    else if (getbnum(topic) > 0)
+		outs("���W�٤w�g�s�b");
+	    else
+		break;
+	}while(temp > 0);          
+	sprintf(title, "[�ӽзs��] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s","�ӽзs��", "�^��W��: ", topic, "����W��: ");
+       
+	if (!getdata(5, 0, "�п�J�ݪ�����W�١G", topic, 20, DOECHO))
+	    return FULLUPDATE;
+	strcat(genbuf, topic);
+	strcat(genbuf, "\n�ݪ����O: ");
+	if (!getdata(6, 0, "�п�J�ݪ����O�G", topic, 20, DOECHO))
+	    return FULLUPDATE;
+	strcat(genbuf, topic);       
+	strcat(genbuf, "\n���D�W��: ");
+	getdata(7, 0, "�п�J���D�W��G", topic, IDLEN * 3 + 3, DOECHO);
+	strcat(genbuf, topic);
+	strcat(genbuf, "\n�ӽЭ�]: \n");
+	outs("�п�J�ӽЭ�](�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;
+    case 2:
+	do{
+	    if (!getdata(4, 0, "�п�J�ݪ��^��W�١G", topic, IDLEN+1, DOECHO))
+		return FULLUPDATE;
+	    else if (getbnum(topic) <= 0)
+		outs("���W�٨ä��s�b");
+	    else
+		break;
+	}while(temp > 0);          
+	sprintf(title, "[�o���ª�] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n","�o���ª�", "�^��W��: ", topic);
+	strcat(genbuf, "\n�o����]: \n");
+	outs("�п�J�o����](�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+          
+	break;
+    case 3:    
+	do{
+	    if (!getdata(4, 0, "�п�J�ݪ��^��W�١G", topic, IDLEN+1, DOECHO))
+		return FULLUPDATE;
+	    else if (getbnum(topic) <= 0)
+		outs("���W�٨ä��s�b");
+	    else
+		break;
+	}while(temp > 0);          
+	sprintf(title, "[�s�p���D] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s%s","�s�p���D", "�^��W��: ", topic, "�ӽ� ID : ", cuser.userid);
+	strcat(genbuf, "\n�ӽЬF��: \n");
+	outs("�п�J�ӽЬF��(�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;       
+	break;       
+    case 4:    
+	do{
+	    if (!getdata(4, 0, "�п�J�ݪ��^��W�١G", topic, IDLEN+1, DOECHO))
+		return FULLUPDATE;
+	    else if ((i = getbnum(topic)) <= 0)
+		outs("���W�٨ä��s�b");
+	    else
+		break;
+	}while(temp > 0);          
+	sprintf(title, "[�}�K���D] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s","�}�K���D", "�^��W��: ", topic, "���D ID : ");
+	do{
+	    if (!getdata(6, 0, "�п�J���DID�G", topic, IDLEN + 1, DOECHO))
+		return FULLUPDATE;
+	    else if (!userid_is_BM(topic, bcache[i-1].BM))
+		outs("���O�Ӫ������D");
+	    else
+		break;
+	}while(temp > 0);
+	strcat(genbuf, topic);
+	strcat(genbuf, "\n�}�K��]: \n");
+	outs("�п�J�}�K��](�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;       
+    case 5:    
+	if (!getdata(4, 0, "�п�J�p�դ��^��W�١G", topic, 30, DOECHO))
+	    return FULLUPDATE;
+	sprintf(title, "[�s�p�p�ժ�] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s%s","�s�p�p�ժ�", "�p�զW��: ", topic, "�ӽ� ID : ", cuser.userid);
+	strcat(genbuf, "\n�ӽЬF��: \n");
+	outs("�п�J�ӽЬF��(�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;
+    case 6:
+           
+	if (!getdata(4, 0, "�п�J�p�դ��^��W�١G", topic, 30, DOECHO))
+	    return FULLUPDATE;
+	sprintf(title, "[�}�K�p�ժ�] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s","�}�K�p�ժ�", "�p�զW��: ", topic, "�p�ժ� ID : ");
+	if (!getdata(6, 0, "�п�J�p�ժ�ID�G", topic, IDLEN + 1, DOECHO))
+	    return FULLUPDATE;
+	strcat(genbuf, topic);
+	strcat(genbuf, "\n�}�K��]: \n");
+	outs("�п�J�}�K��](�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;       
+    case 7:    
+	if (!HAS_PERM(PERM_SYSOP))
+	    return FULLUPDATE;
+	if (!getdata(4, 0, "�п�J����D�D�G", topic, 30, DOECHO))
+	    return FULLUPDATE;
+	sprintf(title, "%s %s", "[��������]", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n","��������", "����D�D: ", topic);
+	strcat(genbuf, "\n�����]: \n");
+	outs("�п�J�����](�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;              
+    case 8:
+	if(!getdata(4, 0, "�п�J�s�դ��^��W�١G", topic, 30, DOECHO))
+	    return FULLUPDATE;
+	sprintf(title, "[�ӽзs�s��] %s", topic);
+	sprintf(genbuf, "%s\n\n%s%s\n%s%s","�ӽиs��", "�s�զW��: ", topic, "�ӽ� ID : ", cuser.userid);
+	strcat(genbuf, "\n�ӽЬF��: \n");
+	outs("�п�J�ӽЬF��(�ܦh����)�A�n�M����g���M���|�֭��");
+	for(i= 8;i<13;i++){
+	    if (!getdata(i, 0, "�G", topic, 60, DOECHO))
+		break;
+	    strcat(genbuf, topic);
+	    strcat(genbuf, "\n");
+	}
+	if (i==8)
+	    return FULLUPDATE;
+	break;
+    default:
+	return FULLUPDATE;
+    }
+    strcat(genbuf, "�s�p�����ɶ�: ");
+    now += 14*24*60*60;    
+    sprintf(topic, "(%ld)", now);
+    strcat(genbuf, topic);
+    strcat(genbuf, ctime(&now));
+    now -= 14*24*60*60;
+    strcat(genbuf, "----------���----------\n");
+    strcat(genbuf, "----------�Ϲ�----------\n");    
+    outs("�}�l�s�p��");
+    setbpath(fpath, currboard);
+    stampfile(fpath, &votefile);
+    
+    if (!(fp = fopen(fpath, "w"))){
+	outs("�}�ɥ��ѡA�еy�ԭ��Ӥ@��");
+	return FULLUPDATE;
+    }
+    fprintf(fp, "%s%s %s%s\n%s%s\n%s%s", "�@��: ", cuser.userid,
+	    "�ݪO: ", currboard, 
+	    "���D: ", title,
+	    "�ɶ�: ", ctime(&now));
+    fprintf(fp, "%s\n", genbuf);
+    fclose(fp);
+    strcpy(votefile.owner, cuser.userid);
+    strcpy(votefile.title, title);
+    votefile.savemode = 'S';
+    setbdir(genbuf, currboard);
+    if(append_record(genbuf, &votefile, sizeof(votefile)) != -1)
+	setbtotal(currbid);
+    do_voteboardreply(&votefile);
+    return FULLUPDATE;
+}
diff --git a/mbbsd/xyz.c b/mbbsd/xyz.c
new file mode 100644
index 00000000..786add30
--- /dev/null
+++ b/mbbsd/xyz.c
@@ -0,0 +1,448 @@
+/* $Id: xyz.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *fn_note_ans;
+extern int b_lines;             /* Screen bottom line number: t_lines-1 */
+extern char *BBSName;
+extern char fromhost[];
+extern userinfo_t *currutmp;
+extern int curr_idle_timeout;
+extern userec_t cuser;
+
+/* �U�زέp�ά�����T�C�� */
+/* Ptt90�~�פj���p�۬d�]�t��  */
+int x_90() {
+    extern char dict[21], database[41];
+    strcpy(dict, "(90)����Ҹ�/�m�W/�Ǯ�/��t/����");
+    strcpy(database, "etc/90");
+    use_dict();
+    return 0;
+}
+
+/* Ptt89�~�פj���p�۬d�]�t��  */
+int x_89() {
+    extern char dict[21], database[41];
+    strcpy(dict, "(89)����Ҹ�/�m�W/�Ǯ�/��t/����");
+    strcpy(database, "etc/89");
+    use_dict();
+    return 0;
+}
+/* Ptt88�~�פj���p�۬d�]�t��  */
+int x_88() {
+    extern char dict[21], database[41];
+
+    strcpy(dict, "(88)����Ҹ�/�m�W/�Ǯ�/��t/����");
+    strcpy(database, "etc/88");
+    use_dict();
+    return 0;
+}    
+/* Ptt87�~�פj���p�۬d�]�t��  */
+int x_87() {
+    extern char dict[21], database[41];
+
+    strcpy(dict, "(87)����Ҹ�/�m�W/�Ǯ�/��t");
+    strcpy(database, "etc/87");
+    use_dict();
+    return 0;
+}    
+
+/* Ptt86�~�פj���p�۬d�]�t��  */
+int x_86() {
+    extern char dict[21], database[41];
+
+    strcpy(dict, "(86)����Ҹ�/�m�W/�Ǯ�/��t");
+    strcpy(database, "etc/86");
+    use_dict();
+    return 0;
+}
+
+int x_boardman() {
+    more("etc/topboardman", YEA);
+    return 0;
+}
+
+int x_user100() {
+    more("etc/topusr100", YEA);
+    return 0;
+}
+
+int x_history() {
+    more("etc/history", YEA);
+    return 0;
+}
+
+#ifdef HAVE_X_BOARDS
+static int x_boards() {
+    more("etc/topboard.tmp", YEA);
+    return 0;
+}
+#endif
+
+int x_birth() {
+    more("etc/birth.today", YEA);
+    return 0;
+}
+
+int x_weather() {
+    more("etc/weather.tmp", YEA);
+    return 0;
+}
+
+int x_stock() {
+    more("etc/stock.tmp", YEA);
+    return 0;
+}
+
+int x_note() {
+    more(fn_note_ans, YEA);
+    return 0;
+}
+
+int x_issue() {
+    more("etc/day", YEA);
+    return 0;
+}
+
+int x_week() {
+    more("etc/week", YEA);
+    return 0;
+}
+
+int x_today() {
+    more("etc/today", YEA);
+    return 0;
+}
+
+int x_yesterday() {
+    more("etc/yesterday", YEA);
+    return 0;
+}
+
+int x_login() {
+    more("etc/Welcome_login", YEA);
+    return 0;
+}
+
+#ifdef HAVE_INFO
+static int x_program() {
+    more("etc/version", YEA);
+    return 0;
+}
+#endif
+
+#ifdef HAVE_LICENSE
+static int x_gpl() {
+    more("etc/GPL", YEA);
+    return 0;
+}
+#endif
+
+/* ���} BBS �� */
+int note() {
+    static char *fn_note_tmp = "note.tmp";
+    static char *fn_note_dat = "note.dat";
+    int total = 0, i, collect, len;
+    struct stat st;
+    char buf[256], buf2[80];
+    int fd, fx;
+    FILE *fp, *foo;
+
+    typedef struct notedata_t {
+	time_t date;
+	char userid[IDLEN + 1];
+	char username[19];
+	char buf[3][80];
+    } notedata_t;
+    notedata_t myitem;
+
+    if(cuser.money < 5) {
+	outmsg("\033[1;41m �u�r! �n�뤭�Ȥ~��d��...�S���C..\033[m");
+	clrtoeol();
+	refresh();
+	return 0;
+    }
+
+    setutmpmode(EDNOTE);
+    do {
+	myitem.buf[0][0] = myitem.buf[1][0] = myitem.buf[2][0] = '\0';
+	move(12, 0);
+	clrtobot();
+	outs("\n�뤭��... ��... �Яd�� (�ܦh�T��)�A��[Enter]����");
+	for(i = 0; (i < 3) && getdata(16 + i, 0, "�G", myitem.buf[i], 78,
+				      DOECHO) && *myitem.buf[i]; i++);
+	getdata(b_lines - 1, 0, "(S)�x�s (E)���s�ӹL (Q)�����H[S] ",
+		buf, 3, LCECHO);
+
+	if(buf[0] == 'q' || (i == 0 && *buf != 'e'))
+	    return 0;
+    } while(buf[0] == 'e');
+    demoney(-5);
+    strcpy(myitem.userid, cuser.userid);
+    strncpy(myitem.username, cuser.username, 18);
+    myitem.username[18] = '\0';
+    time(&(myitem.date));
+
+    /* begin load file */
+    if((foo = fopen(".note", "a")) == NULL)
+	return 0;
+
+    if((fp = fopen(fn_note_ans, "w")) == NULL)
+	return 0;
+
+    if((fx = open(fn_note_tmp, O_WRONLY | O_CREAT, 0644)) <= 0)
+	return 0;
+
+    if((fd = open(fn_note_dat, O_RDONLY)) == -1)
+	total = 1;
+    else if(fstat(fd, &st) != -1) {
+	total = st.st_size / sizeof(notedata_t) + 1;
+	if (total > MAX_NOTE)
+	    total = MAX_NOTE;
+    }
+
+    fputs("\033[1;31;44m��s�w�w�w�w�w�w�w�w�w�w�w�w�w�w�t"
+	  "\033[37m�IJ��W���O\033[31m�u�w�w�w�w�w�w�w�w�w�w�w�w�w�w�s��"
+	  "\033[m\n", fp);
+    collect = 1;
+
+    while(total) {
+	sprintf(buf, "\033[1;31m�~�t\033[32m %s \033[37m(%s)",
+		myitem.userid, myitem.username);
+	len = strlen(buf);
+
+	for(i = len ; i < 73; i++)
+	    strcat(buf, " ");
+	sprintf(buf2, " \033[1;36m%.14s\033[31m   �u��\033[m\n",
+		Cdate(&(myitem.date)));
+	strcat(buf, buf2);
+	fputs(buf, fp);
+	if(collect)
+	    fputs(buf, foo);
+	for(i = 0 ; i < 3 && *myitem.buf[i]; i++) {
+            fprintf(fp, "\033[1;31m�x\033[m%-74.74s\033[1;31m�x\033[m\n",
+		    myitem.buf[i]);
+            if(collect)
+		fprintf(foo, "\033[1;31m�x\033[m%-74.74s\033[1;31m�x\033[m\n",
+			myitem.buf[i]);
+        }
+	fputs("\033[1;31m���s�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	      "�w�w�w�w�w�w�w�w�w�w�w�w�s��\033[m\n",fp);
+
+	if(collect) {
+	    fputs("\033[1;31m���s�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+		  "�w�w�w�w�w�w�w�w�w�w�w�w�w�w�s��\033[m\n", foo);
+	    fclose(foo);
+	    collect = 0;
+	}
+	
+	write(fx, &myitem, sizeof(myitem));
+	
+	if(--total)
+	    read(fd, (char *) &myitem, sizeof(myitem));
+    }
+    fputs("\033[1;31;44m��r�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w"
+	  "�w�w�w�w�w�w�w�w�w�w�w�w�r��\033[m\n",fp);
+    fclose(fp);
+    close(fd);
+    close(fx);
+    Rename(fn_note_tmp, fn_note_dat);
+    more(fn_note_ans, YEA);
+    return 0;
+}
+
+static void mail_sysop() {
+    FILE *fp;
+    char genbuf[200];
+    
+    if((fp = fopen("etc/sysop", "r"))) {
+	int i, j;
+	char *ptr;
+	
+	typedef struct sysoplist_t {
+	    char userid[IDLEN + 1];
+	    char duty[40];
+	} sysoplist_t;
+	sysoplist_t sysoplist[9];
+	
+	j = 0;
+	while(fgets(genbuf, 128, fp)) {
+	    if((ptr = strchr(genbuf, '\n'))) {
+		*ptr = '\0';
+		if((ptr = strchr(genbuf, ':'))) {
+		    *ptr = '\0';
+		    do {
+			i = *++ptr;
+		    } while(i == ' ' || i == '\t');
+		    if(i) {
+			strcpy(sysoplist[j].userid, genbuf);
+			strcpy(sysoplist[j++].duty, ptr);
+		    }
+		}
+	    }
+	}
+	
+	move(12, 0);
+	clrtobot();
+	prints("%16s   %-18s�v�d����\n\n", "�s��", "���� ID");
+
+	for(i = 0; i < j; i++)
+	    prints("%15d.   \033[1;%dm%-16s%s\033[0m\n",
+		   i + 1, 31 + i % 7, sysoplist[i].userid, sysoplist[i].duty);
+	prints("%-14s0.   \033[1;%dm���}\033[0m", "", 31 + j % 7);
+	getdata(b_lines - 1, 0, "                   �п�J�N�X[0]�G",
+		genbuf, 4, DOECHO);
+	i = genbuf[0] - '0' - 1;
+	if(i >= 0 && i < j) {
+	    clear();
+	    do_send(sysoplist[i].userid, NULL);
+	}
+    }
+}
+
+int m_sysop() {
+    setutmpmode(MSYSOP);
+    mail_sysop();
+    return 0;
+}
+
+int Goodbye() {
+    extern void movie();
+    char genbuf[100];
+
+    getdata(b_lines - 1, 0, "�z�T�w�n���}�i " BBSNAME " �j��(Y/N)�H[N] ",
+	    genbuf, 3, LCECHO);
+
+    if(*genbuf != 'y')
+	return 0;
+
+    movie(999);
+    if(cuser.userlevel) {
+	getdata(b_lines - 1, 0,
+		"(G)�H���ӳu (M)���گ��� (N)�IJ��W���y�����H[G] ",
+		genbuf, 3, LCECHO);
+	if(genbuf[0] == 'm')
+	    mail_sysop();
+	else if(genbuf[0] == 'n')
+	    note();
+    }
+    
+    clear();
+    prints("\033[1;36m�˷R�� \033[33m%s(%s)\033[36m�A�O�ѤF�A�ץ��{\033[45;33m"
+	   " %s \033[40;36m�I\n�H�U�O�z�b���������U���:\033[0m\n",
+	   cuser.userid, cuser.username, BBSName);
+    user_display(&cuser, 0);
+    pressanykey();
+    
+    more("etc/Logout",NA);
+    pressanykey();
+    u_exit("EXIT ");
+    return QUIT; 
+}
+
+/* �䴩�~���{�� : tin�Bgopher�Bwww�Bbbsnet�Bgame�Bcsh */
+#define LOOKFIRST       (0)
+#define LOOKLAST        (1)
+#define QUOTEMODE       (2)
+#define MAXCOMSZ        (1024)
+#define MAXARGS         (40)
+#define MAXENVS         (20)
+#define BINDIR          BBSHOME"/bin/"
+
+#define MAXPATHLEN 256
+
+#ifdef HAVE_TIN
+static int x_tin() {
+    clear();
+    return exec_cmd(NEWS, YEA, "bin/tin.sh", "TIN");
+}
+#endif
+
+#ifdef HAVE_GOPHER
+static int x_gopher() {
+    clear();
+    return exec_cmd(GOPHER, YEA, "bin/gopher.sh", "GOPHER");
+}
+#endif
+
+#ifdef HAVE_WWW
+static int x_www() {
+    return exec_cmd(WWW, NA, "bin/www.sh", "WWW");
+}
+#endif
+
+#ifdef HAVE_IRC
+static int x_irc() {
+    return exec_cmd(XMODE, NA, "bin/irc.sh", "IRC");
+}
+#endif
+
+#ifdef HAVE_ARCHIE
+static int x_archie() {
+    char buf[STRLEN], ans[4];
+    char genbuf1[100], genbuf2[200];
+    char *s;
+
+    setutmpmode(ARCHIE);
+    clear();
+    outs("\n�w����{�i\033[1;33;44m" BBSNAME "\033[m�j�ϥ� "
+	 "\033[32mARCHIE\033[m �\\��\n");
+    outs("\n���\\��N���z�C�X�b���� FTP ���s���z���M�䪺�ɮ�.\n");
+    outs("\n�п�J���j�M���r��, �Ϊ����� <ENTER> �����C\n");
+    outs("\n                            coder by Harimau\n");
+    outs("                              modified by Leeym\n");
+    getdata(13,0,"�j�M�r��G",buf,20,DOECHO,0);
+    if(buf[0]=='\0') {
+	prints("\n�����j�M.....\n");
+	pressanykey();
+	return;
+    }
+
+    for(s = buf; *s != '\0'; s++) {
+        if(isspace(*s)) {
+            prints("\n�@���u��j�M�@�Ӧr���, ����ӳg�߳�!!");
+            pressanykey();
+            return;
+	}
+    }
+    bbssetenv("ARCHIESTRING", buf);
+    exec_cmd(ARCHIE, YEA, "bin/archie.sh", ARCHIE);
+    log_usies("ARCHIE", "");
+    strcpy(genbuf1, buf);
+    sprintf(buf, BBSHOME "/tmp/archie.%s", cuser.userid);
+    if(dashf(buf)) {
+	getdata(0, 0, "�n�N���G�H�^�H�c��(Y/N)�H[N]", ans, 3, DOECHO,0);
+	if(*ans == 'y') {
+	    fileheader_t mhdr;
+	    char title[128], buf1[80];
+	    FILE* fp;
+	    
+	    sethomepath(buf1, cuser.userid);
+	    stampfile(buf1, &mhdr);
+	    strcpy(mhdr.owner, cuser.userid);
+	    sprintf(genbuf2, "Archie �j�M�ɮ�: %s ���G", genbuf1);
+	    strcpy(mhdr.title, genbuf2);
+	    mhdr.savemode = 0;
+	    mhdr.filemode = 0;
+	    sethomedir(title, cuser.userid);
+	    append_record(title, &mhdr, sizeof(mhdr));
+	    Link(buf, buf1);
+	}
+	more( buf, YEA);
+	unlink (buf);
+    }
+}
+#endif                          /* HAVE_ARCHIE */
-- 
cgit v1.2.3